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