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 shared = SharedObjects.get() 91 self._lastpoweruptype: str | None = None 92 self.model = ba.getmodel('powerup') 93 self.model_simple = ba.getmodel('powerupSimple') 94 self.tex_bomb = ba.gettexture('powerupBomb') 95 self.tex_punch = ba.gettexture('powerupPunch') 96 self.tex_ice_bombs = ba.gettexture('powerupIceBombs') 97 self.tex_sticky_bombs = ba.gettexture('powerupStickyBombs') 98 self.tex_shield = ba.gettexture('powerupShield') 99 self.tex_impact_bombs = ba.gettexture('powerupImpactBombs') 100 self.tex_health = ba.gettexture('powerupHealth') 101 self.tex_land_mines = ba.gettexture('powerupLandMines') 102 self.tex_curse = ba.gettexture('powerupCurse') 103 self.health_powerup_sound = ba.getsound('healthPowerup') 104 self.powerup_sound = ba.getsound('powerup01') 105 self.powerdown_sound = ba.getsound('powerdown01') 106 self.drop_sound = ba.getsound('boxDrop') 107 108 # Material for powerups. 109 self.powerup_material = ba.Material() 110 111 # Material for anyone wanting to accept powerups. 112 self.powerup_accept_material = ba.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 # We don't wanna be picked up. 124 self.powerup_material.add_actions( 125 conditions=('they_have_material', shared.pickup_material), 126 actions=('modify_part_collision', 'collide', False), 127 ) 128 129 self.powerup_material.add_actions( 130 conditions=('they_have_material', shared.footing_material), 131 actions=('impact_sound', self.drop_sound, 0.5, 0.1), 132 ) 133 134 self._powerupdist: list[str] = [] 135 for powerup, freq in get_default_powerup_distribution(): 136 for _i in range(int(freq)): 137 self._powerupdist.append(powerup) 138 139 def get_random_powerup_type(self, 140 forcetype: str | None = None, 141 excludetypes: list[str] | None = None) -> str: 142 """Returns a random powerup type (string). 143 144 See ba.Powerup.poweruptype for available type values. 145 146 There are certain non-random aspects to this; a 'curse' powerup, 147 for instance, is always followed by a 'health' powerup (to keep things 148 interesting). Passing 'forcetype' forces a given returned type while 149 still properly interacting with the non-random aspects of the system 150 (ie: forcing a 'curse' powerup will result 151 in the next powerup being health). 152 """ 153 if excludetypes is None: 154 excludetypes = [] 155 if forcetype: 156 ptype = forcetype 157 else: 158 # If the last one was a curse, make this one a health to 159 # provide some hope. 160 if self._lastpoweruptype == 'curse': 161 ptype = 'health' 162 else: 163 while True: 164 ptype = self._powerupdist[random.randint( 165 0, 166 len(self._powerupdist) - 1)] 167 if ptype not in excludetypes: 168 break 169 self._lastpoweruptype = ptype 170 return ptype 171 172 @classmethod 173 def get(cls) -> PowerupBoxFactory: 174 """Return a shared ba.PowerupBoxFactory object, creating if needed.""" 175 activity = ba.getactivity() 176 if activity is None: 177 raise ba.ContextError('No current activity.') 178 factory = activity.customdata.get(cls._STORENAME) 179 if factory is None: 180 factory = activity.customdata[cls._STORENAME] = PowerupBoxFactory() 181 assert isinstance(factory, PowerupBoxFactory) 182 return factory 183 184 185class PowerupBox(ba.Actor): 186 """A box that grants a powerup. 187 188 category: Gameplay Classes 189 190 This will deliver a ba.PowerupMessage to anything that touches it 191 which has the ba.PowerupBoxFactory.powerup_accept_material applied. 192 """ 193 194 poweruptype: str 195 """The string powerup type. This can be 'triple_bombs', 'punch', 196 'ice_bombs', 'impact_bombs', 'land_mines', 'sticky_bombs', 'shield', 197 'health', or 'curse'.""" 198 199 node: ba.Node 200 """The 'prop' ba.Node representing this box.""" 201 202 def __init__(self, 203 position: Sequence[float] = (0.0, 1.0, 0.0), 204 poweruptype: str = 'triple_bombs', 205 expire: bool = True): 206 """Create a powerup-box of the requested type at the given position. 207 208 see ba.Powerup.poweruptype for valid type strings. 209 """ 210 211 super().__init__() 212 shared = SharedObjects.get() 213 factory = PowerupBoxFactory.get() 214 self.poweruptype = poweruptype 215 self._powersgiven = False 216 217 if poweruptype == 'triple_bombs': 218 tex = factory.tex_bomb 219 elif poweruptype == 'punch': 220 tex = factory.tex_punch 221 elif poweruptype == 'ice_bombs': 222 tex = factory.tex_ice_bombs 223 elif poweruptype == 'impact_bombs': 224 tex = factory.tex_impact_bombs 225 elif poweruptype == 'land_mines': 226 tex = factory.tex_land_mines 227 elif poweruptype == 'sticky_bombs': 228 tex = factory.tex_sticky_bombs 229 elif poweruptype == 'shield': 230 tex = factory.tex_shield 231 elif poweruptype == 'health': 232 tex = factory.tex_health 233 elif poweruptype == 'curse': 234 tex = factory.tex_curse 235 else: 236 raise ValueError('invalid poweruptype: ' + str(poweruptype)) 237 238 if len(position) != 3: 239 raise ValueError('expected 3 floats for position') 240 241 self.node = ba.newnode( 242 'prop', 243 delegate=self, 244 attrs={ 245 'body': 'box', 246 'position': position, 247 'model': factory.model, 248 'light_model': factory.model_simple, 249 'shadow_size': 0.5, 250 'color_texture': tex, 251 'reflection': 'powerup', 252 'reflection_scale': [1.0], 253 'materials': (factory.powerup_material, 254 shared.object_material) 255 }) # yapf: disable 256 257 # Animate in. 258 curve = ba.animate(self.node, 'model_scale', {0: 0, 0.14: 1.6, 0.2: 1}) 259 ba.timer(0.2, curve.delete) 260 261 if expire: 262 ba.timer(DEFAULT_POWERUP_INTERVAL - 2.5, 263 ba.WeakCall(self._start_flashing)) 264 ba.timer(DEFAULT_POWERUP_INTERVAL - 1.0, 265 ba.WeakCall(self.handlemessage, ba.DieMessage())) 266 267 def _start_flashing(self) -> None: 268 if self.node: 269 self.node.flashing = True 270 271 def handlemessage(self, msg: Any) -> Any: 272 assert not self.expired 273 274 if isinstance(msg, ba.PowerupAcceptMessage): 275 factory = PowerupBoxFactory.get() 276 assert self.node 277 if self.poweruptype == 'health': 278 ba.playsound(factory.health_powerup_sound, 279 3, 280 position=self.node.position) 281 ba.playsound(factory.powerup_sound, 3, position=self.node.position) 282 self._powersgiven = True 283 self.handlemessage(ba.DieMessage()) 284 285 elif isinstance(msg, _TouchedMessage): 286 if not self._powersgiven: 287 node = ba.getcollision().opposingnode 288 node.handlemessage( 289 ba.PowerupMessage(self.poweruptype, sourcenode=self.node)) 290 291 elif isinstance(msg, ba.DieMessage): 292 if self.node: 293 if msg.immediate: 294 self.node.delete() 295 else: 296 ba.animate(self.node, 'model_scale', {0: 1, 0.1: 0}) 297 ba.timer(0.1, self.node.delete) 298 299 elif isinstance(msg, ba.OutOfBoundsMessage): 300 self.handlemessage(ba.DieMessage()) 301 302 elif isinstance(msg, ba.HitMessage): 303 # Don't die on punches (that's annoying). 304 if msg.hit_type != 'punch': 305 self.handlemessage(ba.DieMessage()) 306 else: 307 return super().handlemessage(msg) 308 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 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 # 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(self, 141 forcetype: str | None = None, 142 excludetypes: list[str] | None = None) -> str: 143 """Returns a random powerup type (string). 144 145 See ba.Powerup.poweruptype for available type values. 146 147 There are certain non-random aspects to this; a 'curse' powerup, 148 for instance, is always followed by a 'health' powerup (to keep things 149 interesting). Passing 'forcetype' forces a given returned type while 150 still properly interacting with the non-random aspects of the system 151 (ie: forcing a 'curse' powerup will result 152 in the next powerup being health). 153 """ 154 if excludetypes is None: 155 excludetypes = [] 156 if forcetype: 157 ptype = forcetype 158 else: 159 # If the last one was a curse, make this one a health to 160 # provide some hope. 161 if self._lastpoweruptype == 'curse': 162 ptype = 'health' 163 else: 164 while True: 165 ptype = self._powerupdist[random.randint( 166 0, 167 len(self._powerupdist) - 1)] 168 if ptype not in excludetypes: 169 break 170 self._lastpoweruptype = ptype 171 return ptype 172 173 @classmethod 174 def get(cls) -> PowerupBoxFactory: 175 """Return a shared ba.PowerupBoxFactory object, creating if needed.""" 176 activity = ba.getactivity() 177 if activity is None: 178 raise ba.ContextError('No current activity.') 179 factory = activity.customdata.get(cls._STORENAME) 180 if factory is None: 181 factory = activity.customdata[cls._STORENAME] = PowerupBoxFactory() 182 assert isinstance(factory, PowerupBoxFactory) 183 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 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 # 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)
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.
140 def get_random_powerup_type(self, 141 forcetype: str | None = None, 142 excludetypes: list[str] | None = None) -> str: 143 """Returns a random powerup type (string). 144 145 See ba.Powerup.poweruptype for available type values. 146 147 There are certain non-random aspects to this; a 'curse' powerup, 148 for instance, is always followed by a 'health' powerup (to keep things 149 interesting). Passing 'forcetype' forces a given returned type while 150 still properly interacting with the non-random aspects of the system 151 (ie: forcing a 'curse' powerup will result 152 in the next powerup being health). 153 """ 154 if excludetypes is None: 155 excludetypes = [] 156 if forcetype: 157 ptype = forcetype 158 else: 159 # If the last one was a curse, make this one a health to 160 # provide some hope. 161 if self._lastpoweruptype == 'curse': 162 ptype = 'health' 163 else: 164 while True: 165 ptype = self._powerupdist[random.randint( 166 0, 167 len(self._powerupdist) - 1)] 168 if ptype not in excludetypes: 169 break 170 self._lastpoweruptype = ptype 171 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).
173 @classmethod 174 def get(cls) -> PowerupBoxFactory: 175 """Return a shared ba.PowerupBoxFactory object, creating if needed.""" 176 activity = ba.getactivity() 177 if activity is None: 178 raise ba.ContextError('No current activity.') 179 factory = activity.customdata.get(cls._STORENAME) 180 if factory is None: 181 factory = activity.customdata[cls._STORENAME] = PowerupBoxFactory() 182 assert isinstance(factory, PowerupBoxFactory) 183 return factory
Return a shared ba.PowerupBoxFactory object, creating if needed.
186class PowerupBox(ba.Actor): 187 """A box that grants a powerup. 188 189 category: Gameplay Classes 190 191 This will deliver a ba.PowerupMessage to anything that touches it 192 which has the ba.PowerupBoxFactory.powerup_accept_material applied. 193 """ 194 195 poweruptype: str 196 """The string powerup type. This can be 'triple_bombs', 'punch', 197 'ice_bombs', 'impact_bombs', 'land_mines', 'sticky_bombs', 'shield', 198 'health', or 'curse'.""" 199 200 node: ba.Node 201 """The 'prop' ba.Node representing this box.""" 202 203 def __init__(self, 204 position: Sequence[float] = (0.0, 1.0, 0.0), 205 poweruptype: str = 'triple_bombs', 206 expire: bool = True): 207 """Create a powerup-box of the requested type at the given position. 208 209 see ba.Powerup.poweruptype for valid type strings. 210 """ 211 212 super().__init__() 213 shared = SharedObjects.get() 214 factory = PowerupBoxFactory.get() 215 self.poweruptype = poweruptype 216 self._powersgiven = False 217 218 if poweruptype == 'triple_bombs': 219 tex = factory.tex_bomb 220 elif poweruptype == 'punch': 221 tex = factory.tex_punch 222 elif poweruptype == 'ice_bombs': 223 tex = factory.tex_ice_bombs 224 elif poweruptype == 'impact_bombs': 225 tex = factory.tex_impact_bombs 226 elif poweruptype == 'land_mines': 227 tex = factory.tex_land_mines 228 elif poweruptype == 'sticky_bombs': 229 tex = factory.tex_sticky_bombs 230 elif poweruptype == 'shield': 231 tex = factory.tex_shield 232 elif poweruptype == 'health': 233 tex = factory.tex_health 234 elif poweruptype == 'curse': 235 tex = factory.tex_curse 236 else: 237 raise ValueError('invalid poweruptype: ' + str(poweruptype)) 238 239 if len(position) != 3: 240 raise ValueError('expected 3 floats for position') 241 242 self.node = ba.newnode( 243 'prop', 244 delegate=self, 245 attrs={ 246 'body': 'box', 247 'position': position, 248 'model': factory.model, 249 'light_model': factory.model_simple, 250 'shadow_size': 0.5, 251 'color_texture': tex, 252 'reflection': 'powerup', 253 'reflection_scale': [1.0], 254 'materials': (factory.powerup_material, 255 shared.object_material) 256 }) # yapf: disable 257 258 # Animate in. 259 curve = ba.animate(self.node, 'model_scale', {0: 0, 0.14: 1.6, 0.2: 1}) 260 ba.timer(0.2, curve.delete) 261 262 if expire: 263 ba.timer(DEFAULT_POWERUP_INTERVAL - 2.5, 264 ba.WeakCall(self._start_flashing)) 265 ba.timer(DEFAULT_POWERUP_INTERVAL - 1.0, 266 ba.WeakCall(self.handlemessage, ba.DieMessage())) 267 268 def _start_flashing(self) -> None: 269 if self.node: 270 self.node.flashing = True 271 272 def handlemessage(self, msg: Any) -> Any: 273 assert not self.expired 274 275 if isinstance(msg, ba.PowerupAcceptMessage): 276 factory = PowerupBoxFactory.get() 277 assert self.node 278 if self.poweruptype == 'health': 279 ba.playsound(factory.health_powerup_sound, 280 3, 281 position=self.node.position) 282 ba.playsound(factory.powerup_sound, 3, position=self.node.position) 283 self._powersgiven = True 284 self.handlemessage(ba.DieMessage()) 285 286 elif isinstance(msg, _TouchedMessage): 287 if not self._powersgiven: 288 node = ba.getcollision().opposingnode 289 node.handlemessage( 290 ba.PowerupMessage(self.poweruptype, sourcenode=self.node)) 291 292 elif isinstance(msg, ba.DieMessage): 293 if self.node: 294 if msg.immediate: 295 self.node.delete() 296 else: 297 ba.animate(self.node, 'model_scale', {0: 1, 0.1: 0}) 298 ba.timer(0.1, self.node.delete) 299 300 elif isinstance(msg, ba.OutOfBoundsMessage): 301 self.handlemessage(ba.DieMessage()) 302 303 elif isinstance(msg, ba.HitMessage): 304 # Don't die on punches (that's annoying). 305 if msg.hit_type != 'punch': 306 self.handlemessage(ba.DieMessage()) 307 else: 308 return super().handlemessage(msg) 309 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.
203 def __init__(self, 204 position: Sequence[float] = (0.0, 1.0, 0.0), 205 poweruptype: str = 'triple_bombs', 206 expire: bool = True): 207 """Create a powerup-box of the requested type at the given position. 208 209 see ba.Powerup.poweruptype for valid type strings. 210 """ 211 212 super().__init__() 213 shared = SharedObjects.get() 214 factory = PowerupBoxFactory.get() 215 self.poweruptype = poweruptype 216 self._powersgiven = False 217 218 if poweruptype == 'triple_bombs': 219 tex = factory.tex_bomb 220 elif poweruptype == 'punch': 221 tex = factory.tex_punch 222 elif poweruptype == 'ice_bombs': 223 tex = factory.tex_ice_bombs 224 elif poweruptype == 'impact_bombs': 225 tex = factory.tex_impact_bombs 226 elif poweruptype == 'land_mines': 227 tex = factory.tex_land_mines 228 elif poweruptype == 'sticky_bombs': 229 tex = factory.tex_sticky_bombs 230 elif poweruptype == 'shield': 231 tex = factory.tex_shield 232 elif poweruptype == 'health': 233 tex = factory.tex_health 234 elif poweruptype == 'curse': 235 tex = factory.tex_curse 236 else: 237 raise ValueError('invalid poweruptype: ' + str(poweruptype)) 238 239 if len(position) != 3: 240 raise ValueError('expected 3 floats for position') 241 242 self.node = ba.newnode( 243 'prop', 244 delegate=self, 245 attrs={ 246 'body': 'box', 247 'position': position, 248 'model': factory.model, 249 'light_model': factory.model_simple, 250 'shadow_size': 0.5, 251 'color_texture': tex, 252 'reflection': 'powerup', 253 'reflection_scale': [1.0], 254 'materials': (factory.powerup_material, 255 shared.object_material) 256 }) # yapf: disable 257 258 # Animate in. 259 curve = ba.animate(self.node, 'model_scale', {0: 0, 0.14: 1.6, 0.2: 1}) 260 ba.timer(0.2, curve.delete) 261 262 if expire: 263 ba.timer(DEFAULT_POWERUP_INTERVAL - 2.5, 264 ba.WeakCall(self._start_flashing)) 265 ba.timer(DEFAULT_POWERUP_INTERVAL - 1.0, 266 ba.WeakCall(self.handlemessage, ba.DieMessage()))
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'.
272 def handlemessage(self, msg: Any) -> Any: 273 assert not self.expired 274 275 if isinstance(msg, ba.PowerupAcceptMessage): 276 factory = PowerupBoxFactory.get() 277 assert self.node 278 if self.poweruptype == 'health': 279 ba.playsound(factory.health_powerup_sound, 280 3, 281 position=self.node.position) 282 ba.playsound(factory.powerup_sound, 3, position=self.node.position) 283 self._powersgiven = True 284 self.handlemessage(ba.DieMessage()) 285 286 elif isinstance(msg, _TouchedMessage): 287 if not self._powersgiven: 288 node = ba.getcollision().opposingnode 289 node.handlemessage( 290 ba.PowerupMessage(self.poweruptype, sourcenode=self.node)) 291 292 elif isinstance(msg, ba.DieMessage): 293 if self.node: 294 if msg.immediate: 295 self.node.delete() 296 else: 297 ba.animate(self.node, 'model_scale', {0: 1, 0.1: 0}) 298 ba.timer(0.1, self.node.delete) 299 300 elif isinstance(msg, ba.OutOfBoundsMessage): 301 self.handlemessage(ba.DieMessage()) 302 303 elif isinstance(msg, ba.HitMessage): 304 # Don't die on punches (that's annoying). 305 if msg.hit_type != 'punch': 306 self.handlemessage(ba.DieMessage()) 307 else: 308 return super().handlemessage(msg) 309 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