bascenev1lib.actor.powerupbox
Defines Actor(s).
1# Released under the MIT License. See LICENSE for details. 2# 3"""Defines Actor(s).""" 4 5from __future__ import annotations 6 7import random 8from typing import TYPE_CHECKING 9 10import bascenev1 as bs 11from bascenev1lib.gameutils import SharedObjects 12 13if TYPE_CHECKING: 14 from typing import Any, Sequence 15 16DEFAULT_POWERUP_INTERVAL = 8.0 17 18 19class _TouchedMessage: 20 pass 21 22 23class PowerupBoxFactory: 24 """A collection of media and other resources used by bs.Powerups. 25 26 Category: **Gameplay Classes** 27 28 A single instance of this is shared between all powerups 29 and can be retrieved via bs.Powerup.get_factory(). 30 """ 31 32 mesh: bs.Mesh 33 """The bs.Mesh of the powerup box.""" 34 35 mesh_simple: bs.Mesh 36 """A simpler bs.Mesh of the powerup box, for use in shadows, etc.""" 37 38 tex_bomb: bs.Texture 39 """Triple-bomb powerup bs.Texture.""" 40 41 tex_punch: bs.Texture 42 """Punch powerup bs.Texture.""" 43 44 tex_ice_bombs: bs.Texture 45 """Ice bomb powerup bs.Texture.""" 46 47 tex_sticky_bombs: bs.Texture 48 """Sticky bomb powerup bs.Texture.""" 49 50 tex_shield: bs.Texture 51 """Shield powerup bs.Texture.""" 52 53 tex_impact_bombs: bs.Texture 54 """Impact-bomb powerup bs.Texture.""" 55 56 tex_health: bs.Texture 57 """Health powerup bs.Texture.""" 58 59 tex_land_mines: bs.Texture 60 """Land-mine powerup bs.Texture.""" 61 62 tex_curse: bs.Texture 63 """Curse powerup bs.Texture.""" 64 65 health_powerup_sound: bs.Sound 66 """bs.Sound played when a health powerup is accepted.""" 67 68 powerup_sound: bs.Sound 69 """bs.Sound played when a powerup is accepted.""" 70 71 powerdown_sound: bs.Sound 72 """bs.Sound that can be used when powerups wear off.""" 73 74 powerup_material: bs.Material 75 """bs.Material applied to powerup boxes.""" 76 77 powerup_accept_material: bs.Material 78 """Powerups will send a bs.PowerupMessage to anything they touch 79 that has this bs.Material applied.""" 80 81 _STORENAME = bs.storagename() 82 83 def __init__(self) -> None: 84 """Instantiate a PowerupBoxFactory. 85 86 You shouldn't need to do this; call Powerup.get_factory() 87 to get a shared instance. 88 """ 89 from bascenev1 import get_default_powerup_distribution 90 91 shared = SharedObjects.get() 92 self._lastpoweruptype: str | None = None 93 self.mesh = bs.getmesh('powerup') 94 self.mesh_simple = bs.getmesh('powerupSimple') 95 self.tex_bomb = bs.gettexture('powerupBomb') 96 self.tex_punch = bs.gettexture('powerupPunch') 97 self.tex_ice_bombs = bs.gettexture('powerupIceBombs') 98 self.tex_sticky_bombs = bs.gettexture('powerupStickyBombs') 99 self.tex_shield = bs.gettexture('powerupShield') 100 self.tex_impact_bombs = bs.gettexture('powerupImpactBombs') 101 self.tex_health = bs.gettexture('powerupHealth') 102 self.tex_land_mines = bs.gettexture('powerupLandMines') 103 self.tex_curse = bs.gettexture('powerupCurse') 104 self.health_powerup_sound = bs.getsound('healthPowerup') 105 self.powerup_sound = bs.getsound('powerup01') 106 self.powerdown_sound = bs.getsound('powerdown01') 107 self.drop_sound = bs.getsound('boxDrop') 108 109 # Material for powerups. 110 self.powerup_material = bs.Material() 111 112 # Material for anyone wanting to accept powerups. 113 self.powerup_accept_material = bs.Material() 114 115 # Pass a powerup-touched message to applicable stuff. 116 self.powerup_material.add_actions( 117 conditions=('they_have_material', self.powerup_accept_material), 118 actions=( 119 ('modify_part_collision', 'collide', True), 120 ('modify_part_collision', 'physical', False), 121 ('message', 'our_node', 'at_connect', _TouchedMessage()), 122 ), 123 ) 124 125 # We don't wanna be picked up. 126 self.powerup_material.add_actions( 127 conditions=('they_have_material', shared.pickup_material), 128 actions=('modify_part_collision', 'collide', False), 129 ) 130 131 self.powerup_material.add_actions( 132 conditions=('they_have_material', shared.footing_material), 133 actions=('impact_sound', self.drop_sound, 0.5, 0.1), 134 ) 135 136 self._powerupdist: list[str] = [] 137 for powerup, freq in get_default_powerup_distribution(): 138 for _i in range(int(freq)): 139 self._powerupdist.append(powerup) 140 141 def get_random_powerup_type( 142 self, 143 forcetype: str | None = None, 144 excludetypes: list[str] | None = None, 145 ) -> str: 146 """Returns a random powerup type (string). 147 148 See bs.Powerup.poweruptype for available type values. 149 150 There are certain non-random aspects to this; a 'curse' powerup, 151 for instance, is always followed by a 'health' powerup (to keep things 152 interesting). Passing 'forcetype' forces a given returned type while 153 still properly interacting with the non-random aspects of the system 154 (ie: forcing a 'curse' powerup will result 155 in the next powerup being health). 156 """ 157 if excludetypes is None: 158 excludetypes = [] 159 if forcetype: 160 ptype = forcetype 161 else: 162 # If the last one was a curse, make this one a health to 163 # provide some hope. 164 if self._lastpoweruptype == 'curse': 165 ptype = 'health' 166 else: 167 while True: 168 ptype = self._powerupdist[ 169 random.randint(0, len(self._powerupdist) - 1) 170 ] 171 if ptype not in excludetypes: 172 break 173 self._lastpoweruptype = ptype 174 return ptype 175 176 @classmethod 177 def get(cls) -> PowerupBoxFactory: 178 """Return a shared bs.PowerupBoxFactory object, creating if needed.""" 179 activity = bs.getactivity() 180 if activity is None: 181 raise bs.ContextError('No current activity.') 182 factory = activity.customdata.get(cls._STORENAME) 183 if factory is None: 184 factory = activity.customdata[cls._STORENAME] = PowerupBoxFactory() 185 assert isinstance(factory, PowerupBoxFactory) 186 return factory 187 188 189class PowerupBox(bs.Actor): 190 """A box that grants a powerup. 191 192 category: Gameplay Classes 193 194 This will deliver a bs.PowerupMessage to anything that touches it 195 which has the bs.PowerupBoxFactory.powerup_accept_material applied. 196 """ 197 198 poweruptype: str 199 """The string powerup type. This can be 'triple_bombs', 'punch', 200 'ice_bombs', 'impact_bombs', 'land_mines', 'sticky_bombs', 'shield', 201 'health', or 'curse'.""" 202 203 node: bs.Node 204 """The 'prop' bs.Node representing this box.""" 205 206 def __init__( 207 self, 208 position: Sequence[float] = (0.0, 1.0, 0.0), 209 poweruptype: str = 'triple_bombs', 210 expire: bool = True, 211 ): 212 """Create a powerup-box of the requested type at the given position. 213 214 see bs.Powerup.poweruptype for valid type strings. 215 """ 216 217 super().__init__() 218 shared = SharedObjects.get() 219 factory = PowerupBoxFactory.get() 220 self.poweruptype = poweruptype 221 self._powersgiven = False 222 223 if poweruptype == 'triple_bombs': 224 tex = factory.tex_bomb 225 elif poweruptype == 'punch': 226 tex = factory.tex_punch 227 elif poweruptype == 'ice_bombs': 228 tex = factory.tex_ice_bombs 229 elif poweruptype == 'impact_bombs': 230 tex = factory.tex_impact_bombs 231 elif poweruptype == 'land_mines': 232 tex = factory.tex_land_mines 233 elif poweruptype == 'sticky_bombs': 234 tex = factory.tex_sticky_bombs 235 elif poweruptype == 'shield': 236 tex = factory.tex_shield 237 elif poweruptype == 'health': 238 tex = factory.tex_health 239 elif poweruptype == 'curse': 240 tex = factory.tex_curse 241 else: 242 raise ValueError('invalid poweruptype: ' + str(poweruptype)) 243 244 if len(position) != 3: 245 raise ValueError('expected 3 floats for position') 246 247 self.node = bs.newnode( 248 'prop', 249 delegate=self, 250 attrs={ 251 'body': 'box', 252 'position': position, 253 'mesh': factory.mesh, 254 'light_mesh': factory.mesh_simple, 255 'shadow_size': 0.5, 256 'color_texture': tex, 257 'reflection': 'powerup', 258 'reflection_scale': [1.0], 259 'materials': (factory.powerup_material, shared.object_material), 260 }, 261 ) 262 263 # Animate in. 264 curve = bs.animate(self.node, 'mesh_scale', {0: 0, 0.14: 1.6, 0.2: 1}) 265 bs.timer(0.2, curve.delete) 266 267 if expire: 268 bs.timer( 269 DEFAULT_POWERUP_INTERVAL - 2.5, 270 bs.WeakCall(self._start_flashing), 271 ) 272 bs.timer( 273 DEFAULT_POWERUP_INTERVAL - 1.0, 274 bs.WeakCall(self.handlemessage, bs.DieMessage()), 275 ) 276 277 def _start_flashing(self) -> None: 278 if self.node: 279 self.node.flashing = True 280 281 def handlemessage(self, msg: Any) -> Any: 282 assert not self.expired 283 284 if isinstance(msg, bs.PowerupAcceptMessage): 285 factory = PowerupBoxFactory.get() 286 assert self.node 287 if self.poweruptype == 'health': 288 factory.health_powerup_sound.play( 289 3, position=self.node.position 290 ) 291 292 factory.powerup_sound.play(3, position=self.node.position) 293 self._powersgiven = True 294 self.handlemessage(bs.DieMessage()) 295 296 elif isinstance(msg, _TouchedMessage): 297 if not self._powersgiven: 298 node = bs.getcollision().opposingnode 299 node.handlemessage( 300 bs.PowerupMessage(self.poweruptype, sourcenode=self.node) 301 ) 302 303 elif isinstance(msg, bs.DieMessage): 304 if self.node: 305 if msg.immediate: 306 self.node.delete() 307 else: 308 bs.animate(self.node, 'mesh_scale', {0: 1, 0.1: 0}) 309 bs.timer(0.1, self.node.delete) 310 311 elif isinstance(msg, bs.OutOfBoundsMessage): 312 self.handlemessage(bs.DieMessage()) 313 314 elif isinstance(msg, bs.HitMessage): 315 # Don't die on punches (that's annoying). 316 if msg.hit_type != 'punch': 317 self.handlemessage(bs.DieMessage()) 318 else: 319 return super().handlemessage(msg) 320 return None
24class PowerupBoxFactory: 25 """A collection of media and other resources used by bs.Powerups. 26 27 Category: **Gameplay Classes** 28 29 A single instance of this is shared between all powerups 30 and can be retrieved via bs.Powerup.get_factory(). 31 """ 32 33 mesh: bs.Mesh 34 """The bs.Mesh of the powerup box.""" 35 36 mesh_simple: bs.Mesh 37 """A simpler bs.Mesh of the powerup box, for use in shadows, etc.""" 38 39 tex_bomb: bs.Texture 40 """Triple-bomb powerup bs.Texture.""" 41 42 tex_punch: bs.Texture 43 """Punch powerup bs.Texture.""" 44 45 tex_ice_bombs: bs.Texture 46 """Ice bomb powerup bs.Texture.""" 47 48 tex_sticky_bombs: bs.Texture 49 """Sticky bomb powerup bs.Texture.""" 50 51 tex_shield: bs.Texture 52 """Shield powerup bs.Texture.""" 53 54 tex_impact_bombs: bs.Texture 55 """Impact-bomb powerup bs.Texture.""" 56 57 tex_health: bs.Texture 58 """Health powerup bs.Texture.""" 59 60 tex_land_mines: bs.Texture 61 """Land-mine powerup bs.Texture.""" 62 63 tex_curse: bs.Texture 64 """Curse powerup bs.Texture.""" 65 66 health_powerup_sound: bs.Sound 67 """bs.Sound played when a health powerup is accepted.""" 68 69 powerup_sound: bs.Sound 70 """bs.Sound played when a powerup is accepted.""" 71 72 powerdown_sound: bs.Sound 73 """bs.Sound that can be used when powerups wear off.""" 74 75 powerup_material: bs.Material 76 """bs.Material applied to powerup boxes.""" 77 78 powerup_accept_material: bs.Material 79 """Powerups will send a bs.PowerupMessage to anything they touch 80 that has this bs.Material applied.""" 81 82 _STORENAME = bs.storagename() 83 84 def __init__(self) -> None: 85 """Instantiate a PowerupBoxFactory. 86 87 You shouldn't need to do this; call Powerup.get_factory() 88 to get a shared instance. 89 """ 90 from bascenev1 import get_default_powerup_distribution 91 92 shared = SharedObjects.get() 93 self._lastpoweruptype: str | None = None 94 self.mesh = bs.getmesh('powerup') 95 self.mesh_simple = bs.getmesh('powerupSimple') 96 self.tex_bomb = bs.gettexture('powerupBomb') 97 self.tex_punch = bs.gettexture('powerupPunch') 98 self.tex_ice_bombs = bs.gettexture('powerupIceBombs') 99 self.tex_sticky_bombs = bs.gettexture('powerupStickyBombs') 100 self.tex_shield = bs.gettexture('powerupShield') 101 self.tex_impact_bombs = bs.gettexture('powerupImpactBombs') 102 self.tex_health = bs.gettexture('powerupHealth') 103 self.tex_land_mines = bs.gettexture('powerupLandMines') 104 self.tex_curse = bs.gettexture('powerupCurse') 105 self.health_powerup_sound = bs.getsound('healthPowerup') 106 self.powerup_sound = bs.getsound('powerup01') 107 self.powerdown_sound = bs.getsound('powerdown01') 108 self.drop_sound = bs.getsound('boxDrop') 109 110 # Material for powerups. 111 self.powerup_material = bs.Material() 112 113 # Material for anyone wanting to accept powerups. 114 self.powerup_accept_material = bs.Material() 115 116 # Pass a powerup-touched message to applicable stuff. 117 self.powerup_material.add_actions( 118 conditions=('they_have_material', self.powerup_accept_material), 119 actions=( 120 ('modify_part_collision', 'collide', True), 121 ('modify_part_collision', 'physical', False), 122 ('message', 'our_node', 'at_connect', _TouchedMessage()), 123 ), 124 ) 125 126 # We don't wanna be picked up. 127 self.powerup_material.add_actions( 128 conditions=('they_have_material', shared.pickup_material), 129 actions=('modify_part_collision', 'collide', False), 130 ) 131 132 self.powerup_material.add_actions( 133 conditions=('they_have_material', shared.footing_material), 134 actions=('impact_sound', self.drop_sound, 0.5, 0.1), 135 ) 136 137 self._powerupdist: list[str] = [] 138 for powerup, freq in get_default_powerup_distribution(): 139 for _i in range(int(freq)): 140 self._powerupdist.append(powerup) 141 142 def get_random_powerup_type( 143 self, 144 forcetype: str | None = None, 145 excludetypes: list[str] | None = None, 146 ) -> str: 147 """Returns a random powerup type (string). 148 149 See bs.Powerup.poweruptype for available type values. 150 151 There are certain non-random aspects to this; a 'curse' powerup, 152 for instance, is always followed by a 'health' powerup (to keep things 153 interesting). Passing 'forcetype' forces a given returned type while 154 still properly interacting with the non-random aspects of the system 155 (ie: forcing a 'curse' powerup will result 156 in the next powerup being health). 157 """ 158 if excludetypes is None: 159 excludetypes = [] 160 if forcetype: 161 ptype = forcetype 162 else: 163 # If the last one was a curse, make this one a health to 164 # provide some hope. 165 if self._lastpoweruptype == 'curse': 166 ptype = 'health' 167 else: 168 while True: 169 ptype = self._powerupdist[ 170 random.randint(0, len(self._powerupdist) - 1) 171 ] 172 if ptype not in excludetypes: 173 break 174 self._lastpoweruptype = ptype 175 return ptype 176 177 @classmethod 178 def get(cls) -> PowerupBoxFactory: 179 """Return a shared bs.PowerupBoxFactory object, creating if needed.""" 180 activity = bs.getactivity() 181 if activity is None: 182 raise bs.ContextError('No current activity.') 183 factory = activity.customdata.get(cls._STORENAME) 184 if factory is None: 185 factory = activity.customdata[cls._STORENAME] = PowerupBoxFactory() 186 assert isinstance(factory, PowerupBoxFactory) 187 return factory
A collection of media and other resources used by bs.Powerups.
Category: Gameplay Classes
A single instance of this is shared between all powerups and can be retrieved via bs.Powerup.get_factory().
84 def __init__(self) -> None: 85 """Instantiate a PowerupBoxFactory. 86 87 You shouldn't need to do this; call Powerup.get_factory() 88 to get a shared instance. 89 """ 90 from bascenev1 import get_default_powerup_distribution 91 92 shared = SharedObjects.get() 93 self._lastpoweruptype: str | None = None 94 self.mesh = bs.getmesh('powerup') 95 self.mesh_simple = bs.getmesh('powerupSimple') 96 self.tex_bomb = bs.gettexture('powerupBomb') 97 self.tex_punch = bs.gettexture('powerupPunch') 98 self.tex_ice_bombs = bs.gettexture('powerupIceBombs') 99 self.tex_sticky_bombs = bs.gettexture('powerupStickyBombs') 100 self.tex_shield = bs.gettexture('powerupShield') 101 self.tex_impact_bombs = bs.gettexture('powerupImpactBombs') 102 self.tex_health = bs.gettexture('powerupHealth') 103 self.tex_land_mines = bs.gettexture('powerupLandMines') 104 self.tex_curse = bs.gettexture('powerupCurse') 105 self.health_powerup_sound = bs.getsound('healthPowerup') 106 self.powerup_sound = bs.getsound('powerup01') 107 self.powerdown_sound = bs.getsound('powerdown01') 108 self.drop_sound = bs.getsound('boxDrop') 109 110 # Material for powerups. 111 self.powerup_material = bs.Material() 112 113 # Material for anyone wanting to accept powerups. 114 self.powerup_accept_material = bs.Material() 115 116 # Pass a powerup-touched message to applicable stuff. 117 self.powerup_material.add_actions( 118 conditions=('they_have_material', self.powerup_accept_material), 119 actions=( 120 ('modify_part_collision', 'collide', True), 121 ('modify_part_collision', 'physical', False), 122 ('message', 'our_node', 'at_connect', _TouchedMessage()), 123 ), 124 ) 125 126 # We don't wanna be picked up. 127 self.powerup_material.add_actions( 128 conditions=('they_have_material', shared.pickup_material), 129 actions=('modify_part_collision', 'collide', False), 130 ) 131 132 self.powerup_material.add_actions( 133 conditions=('they_have_material', shared.footing_material), 134 actions=('impact_sound', self.drop_sound, 0.5, 0.1), 135 ) 136 137 self._powerupdist: list[str] = [] 138 for powerup, freq in get_default_powerup_distribution(): 139 for _i in range(int(freq)): 140 self._powerupdist.append(powerup)
Instantiate a PowerupBoxFactory.
You shouldn't need to do this; call Powerup.get_factory() to get a shared instance.
Powerups will send a bs.PowerupMessage to anything they touch that has this bs.Material applied.
142 def get_random_powerup_type( 143 self, 144 forcetype: str | None = None, 145 excludetypes: list[str] | None = None, 146 ) -> str: 147 """Returns a random powerup type (string). 148 149 See bs.Powerup.poweruptype for available type values. 150 151 There are certain non-random aspects to this; a 'curse' powerup, 152 for instance, is always followed by a 'health' powerup (to keep things 153 interesting). Passing 'forcetype' forces a given returned type while 154 still properly interacting with the non-random aspects of the system 155 (ie: forcing a 'curse' powerup will result 156 in the next powerup being health). 157 """ 158 if excludetypes is None: 159 excludetypes = [] 160 if forcetype: 161 ptype = forcetype 162 else: 163 # If the last one was a curse, make this one a health to 164 # provide some hope. 165 if self._lastpoweruptype == 'curse': 166 ptype = 'health' 167 else: 168 while True: 169 ptype = self._powerupdist[ 170 random.randint(0, len(self._powerupdist) - 1) 171 ] 172 if ptype not in excludetypes: 173 break 174 self._lastpoweruptype = ptype 175 return ptype
Returns a random powerup type (string).
See bs.Powerup.poweruptype for available type values.
There are certain non-random aspects to this; a 'curse' powerup, for instance, is always followed by a 'health' powerup (to keep things interesting). Passing 'forcetype' forces a given returned type while still properly interacting with the non-random aspects of the system (ie: forcing a 'curse' powerup will result in the next powerup being health).
177 @classmethod 178 def get(cls) -> PowerupBoxFactory: 179 """Return a shared bs.PowerupBoxFactory object, creating if needed.""" 180 activity = bs.getactivity() 181 if activity is None: 182 raise bs.ContextError('No current activity.') 183 factory = activity.customdata.get(cls._STORENAME) 184 if factory is None: 185 factory = activity.customdata[cls._STORENAME] = PowerupBoxFactory() 186 assert isinstance(factory, PowerupBoxFactory) 187 return factory
Return a shared bs.PowerupBoxFactory object, creating if needed.
190class PowerupBox(bs.Actor): 191 """A box that grants a powerup. 192 193 category: Gameplay Classes 194 195 This will deliver a bs.PowerupMessage to anything that touches it 196 which has the bs.PowerupBoxFactory.powerup_accept_material applied. 197 """ 198 199 poweruptype: str 200 """The string powerup type. This can be 'triple_bombs', 'punch', 201 'ice_bombs', 'impact_bombs', 'land_mines', 'sticky_bombs', 'shield', 202 'health', or 'curse'.""" 203 204 node: bs.Node 205 """The 'prop' bs.Node representing this box.""" 206 207 def __init__( 208 self, 209 position: Sequence[float] = (0.0, 1.0, 0.0), 210 poweruptype: str = 'triple_bombs', 211 expire: bool = True, 212 ): 213 """Create a powerup-box of the requested type at the given position. 214 215 see bs.Powerup.poweruptype for valid type strings. 216 """ 217 218 super().__init__() 219 shared = SharedObjects.get() 220 factory = PowerupBoxFactory.get() 221 self.poweruptype = poweruptype 222 self._powersgiven = False 223 224 if poweruptype == 'triple_bombs': 225 tex = factory.tex_bomb 226 elif poweruptype == 'punch': 227 tex = factory.tex_punch 228 elif poweruptype == 'ice_bombs': 229 tex = factory.tex_ice_bombs 230 elif poweruptype == 'impact_bombs': 231 tex = factory.tex_impact_bombs 232 elif poweruptype == 'land_mines': 233 tex = factory.tex_land_mines 234 elif poweruptype == 'sticky_bombs': 235 tex = factory.tex_sticky_bombs 236 elif poweruptype == 'shield': 237 tex = factory.tex_shield 238 elif poweruptype == 'health': 239 tex = factory.tex_health 240 elif poweruptype == 'curse': 241 tex = factory.tex_curse 242 else: 243 raise ValueError('invalid poweruptype: ' + str(poweruptype)) 244 245 if len(position) != 3: 246 raise ValueError('expected 3 floats for position') 247 248 self.node = bs.newnode( 249 'prop', 250 delegate=self, 251 attrs={ 252 'body': 'box', 253 'position': position, 254 'mesh': factory.mesh, 255 'light_mesh': factory.mesh_simple, 256 'shadow_size': 0.5, 257 'color_texture': tex, 258 'reflection': 'powerup', 259 'reflection_scale': [1.0], 260 'materials': (factory.powerup_material, shared.object_material), 261 }, 262 ) 263 264 # Animate in. 265 curve = bs.animate(self.node, 'mesh_scale', {0: 0, 0.14: 1.6, 0.2: 1}) 266 bs.timer(0.2, curve.delete) 267 268 if expire: 269 bs.timer( 270 DEFAULT_POWERUP_INTERVAL - 2.5, 271 bs.WeakCall(self._start_flashing), 272 ) 273 bs.timer( 274 DEFAULT_POWERUP_INTERVAL - 1.0, 275 bs.WeakCall(self.handlemessage, bs.DieMessage()), 276 ) 277 278 def _start_flashing(self) -> None: 279 if self.node: 280 self.node.flashing = True 281 282 def handlemessage(self, msg: Any) -> Any: 283 assert not self.expired 284 285 if isinstance(msg, bs.PowerupAcceptMessage): 286 factory = PowerupBoxFactory.get() 287 assert self.node 288 if self.poweruptype == 'health': 289 factory.health_powerup_sound.play( 290 3, position=self.node.position 291 ) 292 293 factory.powerup_sound.play(3, position=self.node.position) 294 self._powersgiven = True 295 self.handlemessage(bs.DieMessage()) 296 297 elif isinstance(msg, _TouchedMessage): 298 if not self._powersgiven: 299 node = bs.getcollision().opposingnode 300 node.handlemessage( 301 bs.PowerupMessage(self.poweruptype, sourcenode=self.node) 302 ) 303 304 elif isinstance(msg, bs.DieMessage): 305 if self.node: 306 if msg.immediate: 307 self.node.delete() 308 else: 309 bs.animate(self.node, 'mesh_scale', {0: 1, 0.1: 0}) 310 bs.timer(0.1, self.node.delete) 311 312 elif isinstance(msg, bs.OutOfBoundsMessage): 313 self.handlemessage(bs.DieMessage()) 314 315 elif isinstance(msg, bs.HitMessage): 316 # Don't die on punches (that's annoying). 317 if msg.hit_type != 'punch': 318 self.handlemessage(bs.DieMessage()) 319 else: 320 return super().handlemessage(msg) 321 return None
A box that grants a powerup.
category: Gameplay Classes
This will deliver a bs.PowerupMessage to anything that touches it which has the bs.PowerupBoxFactory.powerup_accept_material applied.
207 def __init__( 208 self, 209 position: Sequence[float] = (0.0, 1.0, 0.0), 210 poweruptype: str = 'triple_bombs', 211 expire: bool = True, 212 ): 213 """Create a powerup-box of the requested type at the given position. 214 215 see bs.Powerup.poweruptype for valid type strings. 216 """ 217 218 super().__init__() 219 shared = SharedObjects.get() 220 factory = PowerupBoxFactory.get() 221 self.poweruptype = poweruptype 222 self._powersgiven = False 223 224 if poweruptype == 'triple_bombs': 225 tex = factory.tex_bomb 226 elif poweruptype == 'punch': 227 tex = factory.tex_punch 228 elif poweruptype == 'ice_bombs': 229 tex = factory.tex_ice_bombs 230 elif poweruptype == 'impact_bombs': 231 tex = factory.tex_impact_bombs 232 elif poweruptype == 'land_mines': 233 tex = factory.tex_land_mines 234 elif poweruptype == 'sticky_bombs': 235 tex = factory.tex_sticky_bombs 236 elif poweruptype == 'shield': 237 tex = factory.tex_shield 238 elif poweruptype == 'health': 239 tex = factory.tex_health 240 elif poweruptype == 'curse': 241 tex = factory.tex_curse 242 else: 243 raise ValueError('invalid poweruptype: ' + str(poweruptype)) 244 245 if len(position) != 3: 246 raise ValueError('expected 3 floats for position') 247 248 self.node = bs.newnode( 249 'prop', 250 delegate=self, 251 attrs={ 252 'body': 'box', 253 'position': position, 254 'mesh': factory.mesh, 255 'light_mesh': factory.mesh_simple, 256 'shadow_size': 0.5, 257 'color_texture': tex, 258 'reflection': 'powerup', 259 'reflection_scale': [1.0], 260 'materials': (factory.powerup_material, shared.object_material), 261 }, 262 ) 263 264 # Animate in. 265 curve = bs.animate(self.node, 'mesh_scale', {0: 0, 0.14: 1.6, 0.2: 1}) 266 bs.timer(0.2, curve.delete) 267 268 if expire: 269 bs.timer( 270 DEFAULT_POWERUP_INTERVAL - 2.5, 271 bs.WeakCall(self._start_flashing), 272 ) 273 bs.timer( 274 DEFAULT_POWERUP_INTERVAL - 1.0, 275 bs.WeakCall(self.handlemessage, bs.DieMessage()), 276 )
Create a powerup-box of the requested type at the given position.
see bs.Powerup.poweruptype for valid type strings.
The string powerup type. This can be 'triple_bombs', 'punch', 'ice_bombs', 'impact_bombs', 'land_mines', 'sticky_bombs', 'shield', 'health', or 'curse'.
282 def handlemessage(self, msg: Any) -> Any: 283 assert not self.expired 284 285 if isinstance(msg, bs.PowerupAcceptMessage): 286 factory = PowerupBoxFactory.get() 287 assert self.node 288 if self.poweruptype == 'health': 289 factory.health_powerup_sound.play( 290 3, position=self.node.position 291 ) 292 293 factory.powerup_sound.play(3, position=self.node.position) 294 self._powersgiven = True 295 self.handlemessage(bs.DieMessage()) 296 297 elif isinstance(msg, _TouchedMessage): 298 if not self._powersgiven: 299 node = bs.getcollision().opposingnode 300 node.handlemessage( 301 bs.PowerupMessage(self.poweruptype, sourcenode=self.node) 302 ) 303 304 elif isinstance(msg, bs.DieMessage): 305 if self.node: 306 if msg.immediate: 307 self.node.delete() 308 else: 309 bs.animate(self.node, 'mesh_scale', {0: 1, 0.1: 0}) 310 bs.timer(0.1, self.node.delete) 311 312 elif isinstance(msg, bs.OutOfBoundsMessage): 313 self.handlemessage(bs.DieMessage()) 314 315 elif isinstance(msg, bs.HitMessage): 316 # Don't die on punches (that's annoying). 317 if msg.hit_type != 'punch': 318 self.handlemessage(bs.DieMessage()) 319 else: 320 return super().handlemessage(msg) 321 return None
General message handling; can be passed any message object.
Inherited Members
- bascenev1._actor.Actor
- autoretain
- on_expire
- expired
- exists
- is_alive
- activity
- getactivity