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