bastd.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 ba 11from bastd.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 ba.Powerups. 25 26 Category: **Gameplay Classes** 27 28 A single instance of this is shared between all powerups 29 and can be retrieved via ba.Powerup.get_factory(). 30 """ 31 32 model: ba.Model 33 """The ba.Model of the powerup box.""" 34 35 model_simple: ba.Model 36 """A simpler ba.Model of the powerup box, for use in shadows, etc.""" 37 38 tex_bomb: ba.Texture 39 """Triple-bomb powerup ba.Texture.""" 40 41 tex_punch: ba.Texture 42 """Punch powerup ba.Texture.""" 43 44 tex_ice_bombs: ba.Texture 45 """Ice bomb powerup ba.Texture.""" 46 47 tex_sticky_bombs: ba.Texture 48 """Sticky bomb powerup ba.Texture.""" 49 50 tex_shield: ba.Texture 51 """Shield powerup ba.Texture.""" 52 53 tex_impact_bombs: ba.Texture 54 """Impact-bomb powerup ba.Texture.""" 55 56 tex_health: ba.Texture 57 """Health powerup ba.Texture.""" 58 59 tex_land_mines: ba.Texture 60 """Land-mine powerup ba.Texture.""" 61 62 tex_curse: ba.Texture 63 """Curse powerup ba.Texture.""" 64 65 health_powerup_sound: ba.Sound 66 """ba.Sound played when a health powerup is accepted.""" 67 68 powerup_sound: ba.Sound 69 """ba.Sound played when a powerup is accepted.""" 70 71 powerdown_sound: ba.Sound 72 """ba.Sound that can be used when powerups wear off.""" 73 74 powerup_material: ba.Material 75 """ba.Material applied to powerup boxes.""" 76 77 powerup_accept_material: ba.Material 78 """Powerups will send a ba.PowerupMessage to anything they touch 79 that has this ba.Material applied.""" 80 81 _STORENAME = ba.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 ba.internal import get_default_powerup_distribution 90 91 shared = SharedObjects.get() 92 self._lastpoweruptype: str | None = None 93 self.model = ba.getmodel('powerup') 94 self.model_simple = ba.getmodel('powerupSimple') 95 self.tex_bomb = ba.gettexture('powerupBomb') 96 self.tex_punch = ba.gettexture('powerupPunch') 97 self.tex_ice_bombs = ba.gettexture('powerupIceBombs') 98 self.tex_sticky_bombs = ba.gettexture('powerupStickyBombs') 99 self.tex_shield = ba.gettexture('powerupShield') 100 self.tex_impact_bombs = ba.gettexture('powerupImpactBombs') 101 self.tex_health = ba.gettexture('powerupHealth') 102 self.tex_land_mines = ba.gettexture('powerupLandMines') 103 self.tex_curse = ba.gettexture('powerupCurse') 104 self.health_powerup_sound = ba.getsound('healthPowerup') 105 self.powerup_sound = ba.getsound('powerup01') 106 self.powerdown_sound = ba.getsound('powerdown01') 107 self.drop_sound = ba.getsound('boxDrop') 108 109 # Material for powerups. 110 self.powerup_material = ba.Material() 111 112 # Material for anyone wanting to accept powerups. 113 self.powerup_accept_material = ba.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 ba.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 ba.PowerupBoxFactory object, creating if needed.""" 179 activity = ba.getactivity() 180 if activity is None: 181 raise ba.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(ba.Actor): 190 """A box that grants a powerup. 191 192 category: Gameplay Classes 193 194 This will deliver a ba.PowerupMessage to anything that touches it 195 which has the ba.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: ba.Node 204 """The 'prop' ba.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 ba.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 = ba.newnode( 248 'prop', 249 delegate=self, 250 attrs={ 251 'body': 'box', 252 'position': position, 253 'model': factory.model, 254 'light_model': factory.model_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 ) # yapf: disable 262 263 # Animate in. 264 curve = ba.animate(self.node, 'model_scale', {0: 0, 0.14: 1.6, 0.2: 1}) 265 ba.timer(0.2, curve.delete) 266 267 if expire: 268 ba.timer( 269 DEFAULT_POWERUP_INTERVAL - 2.5, 270 ba.WeakCall(self._start_flashing), 271 ) 272 ba.timer( 273 DEFAULT_POWERUP_INTERVAL - 1.0, 274 ba.WeakCall(self.handlemessage, ba.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, ba.PowerupAcceptMessage): 285 factory = PowerupBoxFactory.get() 286 assert self.node 287 if self.poweruptype == 'health': 288 ba.playsound( 289 factory.health_powerup_sound, 3, position=self.node.position 290 ) 291 ba.playsound(factory.powerup_sound, 3, position=self.node.position) 292 self._powersgiven = True 293 self.handlemessage(ba.DieMessage()) 294 295 elif isinstance(msg, _TouchedMessage): 296 if not self._powersgiven: 297 node = ba.getcollision().opposingnode 298 node.handlemessage( 299 ba.PowerupMessage(self.poweruptype, sourcenode=self.node) 300 ) 301 302 elif isinstance(msg, ba.DieMessage): 303 if self.node: 304 if msg.immediate: 305 self.node.delete() 306 else: 307 ba.animate(self.node, 'model_scale', {0: 1, 0.1: 0}) 308 ba.timer(0.1, self.node.delete) 309 310 elif isinstance(msg, ba.OutOfBoundsMessage): 311 self.handlemessage(ba.DieMessage()) 312 313 elif isinstance(msg, ba.HitMessage): 314 # Don't die on punches (that's annoying). 315 if msg.hit_type != 'punch': 316 self.handlemessage(ba.DieMessage()) 317 else: 318 return super().handlemessage(msg) 319 return None
24class PowerupBoxFactory: 25 """A collection of media and other resources used by ba.Powerups. 26 27 Category: **Gameplay Classes** 28 29 A single instance of this is shared between all powerups 30 and can be retrieved via ba.Powerup.get_factory(). 31 """ 32 33 model: ba.Model 34 """The ba.Model of the powerup box.""" 35 36 model_simple: ba.Model 37 """A simpler ba.Model of the powerup box, for use in shadows, etc.""" 38 39 tex_bomb: ba.Texture 40 """Triple-bomb powerup ba.Texture.""" 41 42 tex_punch: ba.Texture 43 """Punch powerup ba.Texture.""" 44 45 tex_ice_bombs: ba.Texture 46 """Ice bomb powerup ba.Texture.""" 47 48 tex_sticky_bombs: ba.Texture 49 """Sticky bomb powerup ba.Texture.""" 50 51 tex_shield: ba.Texture 52 """Shield powerup ba.Texture.""" 53 54 tex_impact_bombs: ba.Texture 55 """Impact-bomb powerup ba.Texture.""" 56 57 tex_health: ba.Texture 58 """Health powerup ba.Texture.""" 59 60 tex_land_mines: ba.Texture 61 """Land-mine powerup ba.Texture.""" 62 63 tex_curse: ba.Texture 64 """Curse powerup ba.Texture.""" 65 66 health_powerup_sound: ba.Sound 67 """ba.Sound played when a health powerup is accepted.""" 68 69 powerup_sound: ba.Sound 70 """ba.Sound played when a powerup is accepted.""" 71 72 powerdown_sound: ba.Sound 73 """ba.Sound that can be used when powerups wear off.""" 74 75 powerup_material: ba.Material 76 """ba.Material applied to powerup boxes.""" 77 78 powerup_accept_material: ba.Material 79 """Powerups will send a ba.PowerupMessage to anything they touch 80 that has this ba.Material applied.""" 81 82 _STORENAME = ba.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 ba.internal import get_default_powerup_distribution 91 92 shared = SharedObjects.get() 93 self._lastpoweruptype: str | None = None 94 self.model = ba.getmodel('powerup') 95 self.model_simple = ba.getmodel('powerupSimple') 96 self.tex_bomb = ba.gettexture('powerupBomb') 97 self.tex_punch = ba.gettexture('powerupPunch') 98 self.tex_ice_bombs = ba.gettexture('powerupIceBombs') 99 self.tex_sticky_bombs = ba.gettexture('powerupStickyBombs') 100 self.tex_shield = ba.gettexture('powerupShield') 101 self.tex_impact_bombs = ba.gettexture('powerupImpactBombs') 102 self.tex_health = ba.gettexture('powerupHealth') 103 self.tex_land_mines = ba.gettexture('powerupLandMines') 104 self.tex_curse = ba.gettexture('powerupCurse') 105 self.health_powerup_sound = ba.getsound('healthPowerup') 106 self.powerup_sound = ba.getsound('powerup01') 107 self.powerdown_sound = ba.getsound('powerdown01') 108 self.drop_sound = ba.getsound('boxDrop') 109 110 # Material for powerups. 111 self.powerup_material = ba.Material() 112 113 # Material for anyone wanting to accept powerups. 114 self.powerup_accept_material = ba.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 ba.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 ba.PowerupBoxFactory object, creating if needed.""" 180 activity = ba.getactivity() 181 if activity is None: 182 raise ba.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 ba.Powerups.
Category: Gameplay Classes
A single instance of this is shared between all powerups and can be retrieved via ba.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 ba.internal import get_default_powerup_distribution 91 92 shared = SharedObjects.get() 93 self._lastpoweruptype: str | None = None 94 self.model = ba.getmodel('powerup') 95 self.model_simple = ba.getmodel('powerupSimple') 96 self.tex_bomb = ba.gettexture('powerupBomb') 97 self.tex_punch = ba.gettexture('powerupPunch') 98 self.tex_ice_bombs = ba.gettexture('powerupIceBombs') 99 self.tex_sticky_bombs = ba.gettexture('powerupStickyBombs') 100 self.tex_shield = ba.gettexture('powerupShield') 101 self.tex_impact_bombs = ba.gettexture('powerupImpactBombs') 102 self.tex_health = ba.gettexture('powerupHealth') 103 self.tex_land_mines = ba.gettexture('powerupLandMines') 104 self.tex_curse = ba.gettexture('powerupCurse') 105 self.health_powerup_sound = ba.getsound('healthPowerup') 106 self.powerup_sound = ba.getsound('powerup01') 107 self.powerdown_sound = ba.getsound('powerdown01') 108 self.drop_sound = ba.getsound('boxDrop') 109 110 # Material for powerups. 111 self.powerup_material = ba.Material() 112 113 # Material for anyone wanting to accept powerups. 114 self.powerup_accept_material = ba.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 ba.PowerupMessage to anything they touch that has this ba.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 ba.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 ba.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 ba.PowerupBoxFactory object, creating if needed.""" 180 activity = ba.getactivity() 181 if activity is None: 182 raise ba.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 ba.PowerupBoxFactory object, creating if needed.
190class PowerupBox(ba.Actor): 191 """A box that grants a powerup. 192 193 category: Gameplay Classes 194 195 This will deliver a ba.PowerupMessage to anything that touches it 196 which has the ba.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: ba.Node 205 """The 'prop' ba.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 ba.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 = ba.newnode( 249 'prop', 250 delegate=self, 251 attrs={ 252 'body': 'box', 253 'position': position, 254 'model': factory.model, 255 'light_model': factory.model_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 ) # yapf: disable 263 264 # Animate in. 265 curve = ba.animate(self.node, 'model_scale', {0: 0, 0.14: 1.6, 0.2: 1}) 266 ba.timer(0.2, curve.delete) 267 268 if expire: 269 ba.timer( 270 DEFAULT_POWERUP_INTERVAL - 2.5, 271 ba.WeakCall(self._start_flashing), 272 ) 273 ba.timer( 274 DEFAULT_POWERUP_INTERVAL - 1.0, 275 ba.WeakCall(self.handlemessage, ba.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, ba.PowerupAcceptMessage): 286 factory = PowerupBoxFactory.get() 287 assert self.node 288 if self.poweruptype == 'health': 289 ba.playsound( 290 factory.health_powerup_sound, 3, position=self.node.position 291 ) 292 ba.playsound(factory.powerup_sound, 3, position=self.node.position) 293 self._powersgiven = True 294 self.handlemessage(ba.DieMessage()) 295 296 elif isinstance(msg, _TouchedMessage): 297 if not self._powersgiven: 298 node = ba.getcollision().opposingnode 299 node.handlemessage( 300 ba.PowerupMessage(self.poweruptype, sourcenode=self.node) 301 ) 302 303 elif isinstance(msg, ba.DieMessage): 304 if self.node: 305 if msg.immediate: 306 self.node.delete() 307 else: 308 ba.animate(self.node, 'model_scale', {0: 1, 0.1: 0}) 309 ba.timer(0.1, self.node.delete) 310 311 elif isinstance(msg, ba.OutOfBoundsMessage): 312 self.handlemessage(ba.DieMessage()) 313 314 elif isinstance(msg, ba.HitMessage): 315 # Don't die on punches (that's annoying). 316 if msg.hit_type != 'punch': 317 self.handlemessage(ba.DieMessage()) 318 else: 319 return super().handlemessage(msg) 320 return None
A box that grants a powerup.
category: Gameplay Classes
This will deliver a ba.PowerupMessage to anything that touches it which has the ba.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 ba.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 = ba.newnode( 249 'prop', 250 delegate=self, 251 attrs={ 252 'body': 'box', 253 'position': position, 254 'model': factory.model, 255 'light_model': factory.model_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 ) # yapf: disable 263 264 # Animate in. 265 curve = ba.animate(self.node, 'model_scale', {0: 0, 0.14: 1.6, 0.2: 1}) 266 ba.timer(0.2, curve.delete) 267 268 if expire: 269 ba.timer( 270 DEFAULT_POWERUP_INTERVAL - 2.5, 271 ba.WeakCall(self._start_flashing), 272 ) 273 ba.timer( 274 DEFAULT_POWERUP_INTERVAL - 1.0, 275 ba.WeakCall(self.handlemessage, ba.DieMessage()), 276 )
Create a powerup-box of the requested type at the given position.
see ba.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, ba.PowerupAcceptMessage): 286 factory = PowerupBoxFactory.get() 287 assert self.node 288 if self.poweruptype == 'health': 289 ba.playsound( 290 factory.health_powerup_sound, 3, position=self.node.position 291 ) 292 ba.playsound(factory.powerup_sound, 3, position=self.node.position) 293 self._powersgiven = True 294 self.handlemessage(ba.DieMessage()) 295 296 elif isinstance(msg, _TouchedMessage): 297 if not self._powersgiven: 298 node = ba.getcollision().opposingnode 299 node.handlemessage( 300 ba.PowerupMessage(self.poweruptype, sourcenode=self.node) 301 ) 302 303 elif isinstance(msg, ba.DieMessage): 304 if self.node: 305 if msg.immediate: 306 self.node.delete() 307 else: 308 ba.animate(self.node, 'model_scale', {0: 1, 0.1: 0}) 309 ba.timer(0.1, self.node.delete) 310 311 elif isinstance(msg, ba.OutOfBoundsMessage): 312 self.handlemessage(ba.DieMessage()) 313 314 elif isinstance(msg, ba.HitMessage): 315 # Don't die on punches (that's annoying). 316 if msg.hit_type != 'punch': 317 self.handlemessage(ba.DieMessage()) 318 else: 319 return super().handlemessage(msg) 320 return None
General message handling; can be passed any message object.
Inherited Members
- ba._actor.Actor
- autoretain
- on_expire
- expired
- exists
- is_alive
- activity
- getactivity