bastd.game.easteregghunt
Provides an easter egg hunt game.
1# Released under the MIT License. See LICENSE for details. 2# 3"""Provides an easter egg hunt game.""" 4 5# ba_meta require api 7 6# (see https://ballistica.net/wiki/meta-tag-system) 7 8from __future__ import annotations 9 10import random 11from typing import TYPE_CHECKING 12 13import ba 14from bastd.actor.bomb import Bomb 15from bastd.actor.playerspaz import PlayerSpaz 16from bastd.actor.spazbot import SpazBotSet, BouncyBot, SpazBotDiedMessage 17from bastd.actor.onscreencountdown import OnScreenCountdown 18from bastd.actor.scoreboard import Scoreboard 19from bastd.actor.respawnicon import RespawnIcon 20from bastd.gameutils import SharedObjects 21 22if TYPE_CHECKING: 23 from typing import Any 24 25 26class Player(ba.Player['Team']): 27 """Our player type for this game.""" 28 29 def __init__(self) -> None: 30 self.respawn_timer: ba.Timer | None = None 31 self.respawn_icon: RespawnIcon | None = None 32 33 34class Team(ba.Team[Player]): 35 """Our team type for this game.""" 36 37 def __init__(self) -> None: 38 self.score = 0 39 40 41# ba_meta export game 42class EasterEggHuntGame(ba.TeamGameActivity[Player, Team]): 43 """A game where score is based on collecting eggs.""" 44 45 name = 'Easter Egg Hunt' 46 description = 'Gather eggs!' 47 available_settings = [ba.BoolSetting('Pro Mode', default=False)] 48 scoreconfig = ba.ScoreConfig(label='Score', scoretype=ba.ScoreType.POINTS) 49 50 # We're currently hard-coded for one map. 51 @classmethod 52 def get_supported_maps(cls, sessiontype: type[ba.Session]) -> list[str]: 53 return ['Tower D'] 54 55 # We support teams, free-for-all, and co-op sessions. 56 @classmethod 57 def supports_session_type(cls, sessiontype: type[ba.Session]) -> bool: 58 return (issubclass(sessiontype, ba.CoopSession) 59 or issubclass(sessiontype, ba.DualTeamSession) 60 or issubclass(sessiontype, ba.FreeForAllSession)) 61 62 def __init__(self, settings: dict): 63 super().__init__(settings) 64 shared = SharedObjects.get() 65 self._last_player_death_time = None 66 self._scoreboard = Scoreboard() 67 self.egg_model = ba.getmodel('egg') 68 self.egg_tex_1 = ba.gettexture('eggTex1') 69 self.egg_tex_2 = ba.gettexture('eggTex2') 70 self.egg_tex_3 = ba.gettexture('eggTex3') 71 self._collect_sound = ba.getsound('powerup01') 72 self._pro_mode = settings.get('Pro Mode', False) 73 self._max_eggs = 1.0 74 self.egg_material = ba.Material() 75 self.egg_material.add_actions( 76 conditions=('they_have_material', shared.player_material), 77 actions=(('call', 'at_connect', self._on_egg_player_collide), )) 78 self._eggs: list[Egg] = [] 79 self._update_timer: ba.Timer | None = None 80 self._countdown: OnScreenCountdown | None = None 81 self._bots: SpazBotSet | None = None 82 83 # Base class overrides 84 self.default_music = ba.MusicType.FORWARD_MARCH 85 86 def on_team_join(self, team: Team) -> None: 87 if self.has_begun(): 88 self._update_scoreboard() 89 90 # Called when our game actually starts. 91 def on_begin(self) -> None: 92 from bastd.maps import TowerD 93 94 # There's a player-wall on the tower-d level to prevent 95 # players from getting up on the stairs.. we wanna kill that. 96 gamemap = self.map 97 assert isinstance(gamemap, TowerD) 98 gamemap.player_wall.delete() 99 super().on_begin() 100 self._update_scoreboard() 101 self._update_timer = ba.Timer(0.25, self._update, repeat=True) 102 self._countdown = OnScreenCountdown(60, endcall=self.end_game) 103 ba.timer(4.0, self._countdown.start) 104 self._bots = SpazBotSet() 105 106 # Spawn evil bunny in co-op only. 107 if isinstance(self.session, ba.CoopSession) and self._pro_mode: 108 self._spawn_evil_bunny() 109 110 # Overriding the default character spawning. 111 def spawn_player(self, player: Player) -> ba.Actor: 112 spaz = self.spawn_player_spaz(player) 113 spaz.connect_controls_to_player() 114 return spaz 115 116 def _spawn_evil_bunny(self) -> None: 117 assert self._bots is not None 118 self._bots.spawn_bot(BouncyBot, pos=(6, 4, -7.8), spawn_time=10.0) 119 120 def _on_egg_player_collide(self) -> None: 121 if self.has_ended(): 122 return 123 collision = ba.getcollision() 124 125 # Be defensive here; we could be hitting the corpse of a player 126 # who just left/etc. 127 try: 128 egg = collision.sourcenode.getdelegate(Egg, True) 129 player = collision.opposingnode.getdelegate(PlayerSpaz, 130 True).getplayer( 131 Player, True) 132 except ba.NotFoundError: 133 return 134 135 player.team.score += 1 136 137 # Displays a +1 (and adds to individual player score in 138 # teams mode). 139 self.stats.player_scored(player, 1, screenmessage=False) 140 if self._max_eggs < 5: 141 self._max_eggs += 1.0 142 elif self._max_eggs < 10: 143 self._max_eggs += 0.5 144 elif self._max_eggs < 30: 145 self._max_eggs += 0.3 146 self._update_scoreboard() 147 ba.playsound(self._collect_sound, 0.5, position=egg.node.position) 148 149 # Create a flash. 150 light = ba.newnode('light', 151 attrs={ 152 'position': egg.node.position, 153 'height_attenuated': False, 154 'radius': 0.1, 155 'color': (1, 1, 0) 156 }) 157 ba.animate(light, 'intensity', {0: 0, 0.1: 1.0, 0.2: 0}, loop=False) 158 ba.timer(0.200, light.delete) 159 egg.handlemessage(ba.DieMessage()) 160 161 def _update(self) -> None: 162 # Misc. periodic updating. 163 xpos = random.uniform(-7.1, 6.0) 164 ypos = random.uniform(3.5, 3.5) 165 zpos = random.uniform(-8.2, 3.7) 166 167 # Prune dead eggs from our list. 168 self._eggs = [e for e in self._eggs if e] 169 170 # Spawn more eggs if we've got space. 171 if len(self._eggs) < int(self._max_eggs): 172 173 # Occasionally spawn a land-mine in addition. 174 if self._pro_mode and random.random() < 0.25: 175 mine = Bomb(position=(xpos, ypos, zpos), 176 bomb_type='land_mine').autoretain() 177 mine.arm() 178 else: 179 self._eggs.append(Egg(position=(xpos, ypos, zpos))) 180 181 # Various high-level game events come through this method. 182 def handlemessage(self, msg: Any) -> Any: 183 184 # Respawn dead players. 185 if isinstance(msg, ba.PlayerDiedMessage): 186 # Augment standard behavior. 187 super().handlemessage(msg) 188 189 # Respawn them shortly. 190 player = msg.getplayer(Player) 191 assert self.initialplayerinfos is not None 192 respawn_time = 2.0 + len(self.initialplayerinfos) * 1.0 193 player.respawn_timer = ba.Timer( 194 respawn_time, ba.Call(self.spawn_player_if_exists, player)) 195 player.respawn_icon = RespawnIcon(player, respawn_time) 196 197 # Whenever our evil bunny dies, respawn him and spew some eggs. 198 elif isinstance(msg, SpazBotDiedMessage): 199 self._spawn_evil_bunny() 200 assert msg.spazbot.node 201 pos = msg.spazbot.node.position 202 for _i in range(6): 203 spread = 0.4 204 self._eggs.append( 205 Egg(position=(pos[0] + random.uniform(-spread, spread), 206 pos[1] + random.uniform(-spread, spread), 207 pos[2] + random.uniform(-spread, spread)))) 208 else: 209 # Default handler. 210 return super().handlemessage(msg) 211 return None 212 213 def _update_scoreboard(self) -> None: 214 for team in self.teams: 215 self._scoreboard.set_team_value(team, team.score) 216 217 def end_game(self) -> None: 218 results = ba.GameResults() 219 for team in self.teams: 220 results.set_team_score(team, team.score) 221 self.end(results) 222 223 224class Egg(ba.Actor): 225 """A lovely egg that can be picked up for points.""" 226 227 def __init__(self, position: tuple[float, float, float] = (0.0, 1.0, 0.0)): 228 super().__init__() 229 activity = self.activity 230 assert isinstance(activity, EasterEggHuntGame) 231 shared = SharedObjects.get() 232 233 # Spawn just above the provided point. 234 self._spawn_pos = (position[0], position[1] + 1.0, position[2]) 235 ctex = (activity.egg_tex_1, activity.egg_tex_2, 236 activity.egg_tex_3)[random.randrange(3)] 237 mats = [shared.object_material, activity.egg_material] 238 self.node = ba.newnode('prop', 239 delegate=self, 240 attrs={ 241 'model': activity.egg_model, 242 'color_texture': ctex, 243 'body': 'capsule', 244 'reflection': 'soft', 245 'model_scale': 0.5, 246 'body_scale': 0.6, 247 'density': 4.0, 248 'reflection_scale': [0.15], 249 'shadow_size': 0.6, 250 'position': self._spawn_pos, 251 'materials': mats 252 }) 253 254 def exists(self) -> bool: 255 return bool(self.node) 256 257 def handlemessage(self, msg: Any) -> Any: 258 if isinstance(msg, ba.DieMessage): 259 if self.node: 260 self.node.delete() 261 elif isinstance(msg, ba.HitMessage): 262 if self.node: 263 assert msg.force_direction is not None 264 self.node.handlemessage( 265 'impulse', msg.pos[0], msg.pos[1], msg.pos[2], 266 msg.velocity[0], msg.velocity[1], msg.velocity[2], 267 1.0 * msg.magnitude, 1.0 * msg.velocity_magnitude, 268 msg.radius, 0, msg.force_direction[0], 269 msg.force_direction[1], msg.force_direction[2]) 270 else: 271 super().handlemessage(msg)
27class Player(ba.Player['Team']): 28 """Our player type for this game.""" 29 30 def __init__(self) -> None: 31 self.respawn_timer: ba.Timer | None = None 32 self.respawn_icon: RespawnIcon | None = None
Our player type for this game.
Inherited Members
- ba._player.Player
- actor
- on_expire
- team
- customdata
- sessionplayer
- node
- position
- exists
- getname
- is_alive
- get_icon
- assigninput
- resetinput
35class Team(ba.Team[Player]): 36 """Our team type for this game.""" 37 38 def __init__(self) -> None: 39 self.score = 0
Our team type for this game.
Inherited Members
43class EasterEggHuntGame(ba.TeamGameActivity[Player, Team]): 44 """A game where score is based on collecting eggs.""" 45 46 name = 'Easter Egg Hunt' 47 description = 'Gather eggs!' 48 available_settings = [ba.BoolSetting('Pro Mode', default=False)] 49 scoreconfig = ba.ScoreConfig(label='Score', scoretype=ba.ScoreType.POINTS) 50 51 # We're currently hard-coded for one map. 52 @classmethod 53 def get_supported_maps(cls, sessiontype: type[ba.Session]) -> list[str]: 54 return ['Tower D'] 55 56 # We support teams, free-for-all, and co-op sessions. 57 @classmethod 58 def supports_session_type(cls, sessiontype: type[ba.Session]) -> bool: 59 return (issubclass(sessiontype, ba.CoopSession) 60 or issubclass(sessiontype, ba.DualTeamSession) 61 or issubclass(sessiontype, ba.FreeForAllSession)) 62 63 def __init__(self, settings: dict): 64 super().__init__(settings) 65 shared = SharedObjects.get() 66 self._last_player_death_time = None 67 self._scoreboard = Scoreboard() 68 self.egg_model = ba.getmodel('egg') 69 self.egg_tex_1 = ba.gettexture('eggTex1') 70 self.egg_tex_2 = ba.gettexture('eggTex2') 71 self.egg_tex_3 = ba.gettexture('eggTex3') 72 self._collect_sound = ba.getsound('powerup01') 73 self._pro_mode = settings.get('Pro Mode', False) 74 self._max_eggs = 1.0 75 self.egg_material = ba.Material() 76 self.egg_material.add_actions( 77 conditions=('they_have_material', shared.player_material), 78 actions=(('call', 'at_connect', self._on_egg_player_collide), )) 79 self._eggs: list[Egg] = [] 80 self._update_timer: ba.Timer | None = None 81 self._countdown: OnScreenCountdown | None = None 82 self._bots: SpazBotSet | None = None 83 84 # Base class overrides 85 self.default_music = ba.MusicType.FORWARD_MARCH 86 87 def on_team_join(self, team: Team) -> None: 88 if self.has_begun(): 89 self._update_scoreboard() 90 91 # Called when our game actually starts. 92 def on_begin(self) -> None: 93 from bastd.maps import TowerD 94 95 # There's a player-wall on the tower-d level to prevent 96 # players from getting up on the stairs.. we wanna kill that. 97 gamemap = self.map 98 assert isinstance(gamemap, TowerD) 99 gamemap.player_wall.delete() 100 super().on_begin() 101 self._update_scoreboard() 102 self._update_timer = ba.Timer(0.25, self._update, repeat=True) 103 self._countdown = OnScreenCountdown(60, endcall=self.end_game) 104 ba.timer(4.0, self._countdown.start) 105 self._bots = SpazBotSet() 106 107 # Spawn evil bunny in co-op only. 108 if isinstance(self.session, ba.CoopSession) and self._pro_mode: 109 self._spawn_evil_bunny() 110 111 # Overriding the default character spawning. 112 def spawn_player(self, player: Player) -> ba.Actor: 113 spaz = self.spawn_player_spaz(player) 114 spaz.connect_controls_to_player() 115 return spaz 116 117 def _spawn_evil_bunny(self) -> None: 118 assert self._bots is not None 119 self._bots.spawn_bot(BouncyBot, pos=(6, 4, -7.8), spawn_time=10.0) 120 121 def _on_egg_player_collide(self) -> None: 122 if self.has_ended(): 123 return 124 collision = ba.getcollision() 125 126 # Be defensive here; we could be hitting the corpse of a player 127 # who just left/etc. 128 try: 129 egg = collision.sourcenode.getdelegate(Egg, True) 130 player = collision.opposingnode.getdelegate(PlayerSpaz, 131 True).getplayer( 132 Player, True) 133 except ba.NotFoundError: 134 return 135 136 player.team.score += 1 137 138 # Displays a +1 (and adds to individual player score in 139 # teams mode). 140 self.stats.player_scored(player, 1, screenmessage=False) 141 if self._max_eggs < 5: 142 self._max_eggs += 1.0 143 elif self._max_eggs < 10: 144 self._max_eggs += 0.5 145 elif self._max_eggs < 30: 146 self._max_eggs += 0.3 147 self._update_scoreboard() 148 ba.playsound(self._collect_sound, 0.5, position=egg.node.position) 149 150 # Create a flash. 151 light = ba.newnode('light', 152 attrs={ 153 'position': egg.node.position, 154 'height_attenuated': False, 155 'radius': 0.1, 156 'color': (1, 1, 0) 157 }) 158 ba.animate(light, 'intensity', {0: 0, 0.1: 1.0, 0.2: 0}, loop=False) 159 ba.timer(0.200, light.delete) 160 egg.handlemessage(ba.DieMessage()) 161 162 def _update(self) -> None: 163 # Misc. periodic updating. 164 xpos = random.uniform(-7.1, 6.0) 165 ypos = random.uniform(3.5, 3.5) 166 zpos = random.uniform(-8.2, 3.7) 167 168 # Prune dead eggs from our list. 169 self._eggs = [e for e in self._eggs if e] 170 171 # Spawn more eggs if we've got space. 172 if len(self._eggs) < int(self._max_eggs): 173 174 # Occasionally spawn a land-mine in addition. 175 if self._pro_mode and random.random() < 0.25: 176 mine = Bomb(position=(xpos, ypos, zpos), 177 bomb_type='land_mine').autoretain() 178 mine.arm() 179 else: 180 self._eggs.append(Egg(position=(xpos, ypos, zpos))) 181 182 # Various high-level game events come through this method. 183 def handlemessage(self, msg: Any) -> Any: 184 185 # Respawn dead players. 186 if isinstance(msg, ba.PlayerDiedMessage): 187 # Augment standard behavior. 188 super().handlemessage(msg) 189 190 # Respawn them shortly. 191 player = msg.getplayer(Player) 192 assert self.initialplayerinfos is not None 193 respawn_time = 2.0 + len(self.initialplayerinfos) * 1.0 194 player.respawn_timer = ba.Timer( 195 respawn_time, ba.Call(self.spawn_player_if_exists, player)) 196 player.respawn_icon = RespawnIcon(player, respawn_time) 197 198 # Whenever our evil bunny dies, respawn him and spew some eggs. 199 elif isinstance(msg, SpazBotDiedMessage): 200 self._spawn_evil_bunny() 201 assert msg.spazbot.node 202 pos = msg.spazbot.node.position 203 for _i in range(6): 204 spread = 0.4 205 self._eggs.append( 206 Egg(position=(pos[0] + random.uniform(-spread, spread), 207 pos[1] + random.uniform(-spread, spread), 208 pos[2] + random.uniform(-spread, spread)))) 209 else: 210 # Default handler. 211 return super().handlemessage(msg) 212 return None 213 214 def _update_scoreboard(self) -> None: 215 for team in self.teams: 216 self._scoreboard.set_team_value(team, team.score) 217 218 def end_game(self) -> None: 219 results = ba.GameResults() 220 for team in self.teams: 221 results.set_team_score(team, team.score) 222 self.end(results)
A game where score is based on collecting eggs.
63 def __init__(self, settings: dict): 64 super().__init__(settings) 65 shared = SharedObjects.get() 66 self._last_player_death_time = None 67 self._scoreboard = Scoreboard() 68 self.egg_model = ba.getmodel('egg') 69 self.egg_tex_1 = ba.gettexture('eggTex1') 70 self.egg_tex_2 = ba.gettexture('eggTex2') 71 self.egg_tex_3 = ba.gettexture('eggTex3') 72 self._collect_sound = ba.getsound('powerup01') 73 self._pro_mode = settings.get('Pro Mode', False) 74 self._max_eggs = 1.0 75 self.egg_material = ba.Material() 76 self.egg_material.add_actions( 77 conditions=('they_have_material', shared.player_material), 78 actions=(('call', 'at_connect', self._on_egg_player_collide), )) 79 self._eggs: list[Egg] = [] 80 self._update_timer: ba.Timer | None = None 81 self._countdown: OnScreenCountdown | None = None 82 self._bots: SpazBotSet | None = None 83 84 # Base class overrides 85 self.default_music = ba.MusicType.FORWARD_MARCH
Instantiate the Activity.
52 @classmethod 53 def get_supported_maps(cls, sessiontype: type[ba.Session]) -> list[str]: 54 return ['Tower D']
Called by the default ba.GameActivity.create_settings_ui() implementation; should return a list of map names valid for this game-type for the given ba.Session type.
57 @classmethod 58 def supports_session_type(cls, sessiontype: type[ba.Session]) -> bool: 59 return (issubclass(sessiontype, ba.CoopSession) 60 or issubclass(sessiontype, ba.DualTeamSession) 61 or issubclass(sessiontype, ba.FreeForAllSession))
Class method override; returns True for ba.DualTeamSessions and ba.FreeForAllSessions; False otherwise.
Called when a new ba.Team joins the Activity.
(including the initial set of Teams)
92 def on_begin(self) -> None: 93 from bastd.maps import TowerD 94 95 # There's a player-wall on the tower-d level to prevent 96 # players from getting up on the stairs.. we wanna kill that. 97 gamemap = self.map 98 assert isinstance(gamemap, TowerD) 99 gamemap.player_wall.delete() 100 super().on_begin() 101 self._update_scoreboard() 102 self._update_timer = ba.Timer(0.25, self._update, repeat=True) 103 self._countdown = OnScreenCountdown(60, endcall=self.end_game) 104 ba.timer(4.0, self._countdown.start) 105 self._bots = SpazBotSet() 106 107 # Spawn evil bunny in co-op only. 108 if isinstance(self.session, ba.CoopSession) and self._pro_mode: 109 self._spawn_evil_bunny()
Called once the previous ba.Activity has finished transitioning out.
At this point the activity's initial players and teams are filled in and it should begin its actual game logic.
112 def spawn_player(self, player: Player) -> ba.Actor: 113 spaz = self.spawn_player_spaz(player) 114 spaz.connect_controls_to_player() 115 return spaz
Spawn something for the provided ba.Player.
The default implementation simply calls spawn_player_spaz().
183 def handlemessage(self, msg: Any) -> Any: 184 185 # Respawn dead players. 186 if isinstance(msg, ba.PlayerDiedMessage): 187 # Augment standard behavior. 188 super().handlemessage(msg) 189 190 # Respawn them shortly. 191 player = msg.getplayer(Player) 192 assert self.initialplayerinfos is not None 193 respawn_time = 2.0 + len(self.initialplayerinfos) * 1.0 194 player.respawn_timer = ba.Timer( 195 respawn_time, ba.Call(self.spawn_player_if_exists, player)) 196 player.respawn_icon = RespawnIcon(player, respawn_time) 197 198 # Whenever our evil bunny dies, respawn him and spew some eggs. 199 elif isinstance(msg, SpazBotDiedMessage): 200 self._spawn_evil_bunny() 201 assert msg.spazbot.node 202 pos = msg.spazbot.node.position 203 for _i in range(6): 204 spread = 0.4 205 self._eggs.append( 206 Egg(position=(pos[0] + random.uniform(-spread, spread), 207 pos[1] + random.uniform(-spread, spread), 208 pos[2] + random.uniform(-spread, spread)))) 209 else: 210 # Default handler. 211 return super().handlemessage(msg) 212 return None
General message handling; can be passed any message object.
218 def end_game(self) -> None: 219 results = ba.GameResults() 220 for team in self.teams: 221 results.set_team_score(team, team.score) 222 self.end(results)
Tell the game to wrap up and call ba.Activity.end() immediately.
This method should be overridden by subclasses. A game should always be prepared to end and deliver results, even if there is no 'winner' yet; this way things like the standard time-limit (ba.GameActivity.setup_standard_time_limit()) will work with the game.
Inherited Members
- ba._gameactivity.GameActivity
- default_music
- tips
- allow_pausing
- allow_kick_idle_players
- show_kill_points
- create_settings_ui
- getscoreconfig
- getname
- get_display_string
- get_team_display_string
- get_description
- get_description_display_string
- get_available_settings
- get_settings_display_string
- map
- get_instance_display_string
- get_instance_scoreboard_display_string
- get_instance_description
- get_instance_description_short
- on_continue
- is_waiting_for_continue
- continue_or_end_game
- on_player_join
- respawn_player
- spawn_player_if_exists
- setup_standard_powerup_drops
- setup_standard_time_limit
- show_zoom_message
- ba._teamgame.TeamGameActivity
- on_transition_in
- spawn_player_spaz
- end
- ba._activity.Activity
- settings_raw
- teams
- players
- announce_player_deaths
- is_joining_activity
- use_fixed_vr_overlay
- slow_motion
- inherits_slow_motion
- inherits_music
- inherits_vr_camera_offset
- inherits_vr_overlay_center
- inherits_tint
- allow_mid_activity_joins
- transition_time
- can_show_ad_on_death
- globalsnode
- stats
- on_expire
- customdata
- expired
- playertype
- teamtype
- retain_actor
- add_actor_weak_ref
- session
- on_player_leave
- on_team_leave
- on_transition_out
- has_transitioned_in
- has_begun
- has_ended
- is_transitioning_out
- transition_out
- create_player
- create_team
- ba._dependency.DependencyComponent
- dep_is_present
- get_dynamic_deps
225class Egg(ba.Actor): 226 """A lovely egg that can be picked up for points.""" 227 228 def __init__(self, position: tuple[float, float, float] = (0.0, 1.0, 0.0)): 229 super().__init__() 230 activity = self.activity 231 assert isinstance(activity, EasterEggHuntGame) 232 shared = SharedObjects.get() 233 234 # Spawn just above the provided point. 235 self._spawn_pos = (position[0], position[1] + 1.0, position[2]) 236 ctex = (activity.egg_tex_1, activity.egg_tex_2, 237 activity.egg_tex_3)[random.randrange(3)] 238 mats = [shared.object_material, activity.egg_material] 239 self.node = ba.newnode('prop', 240 delegate=self, 241 attrs={ 242 'model': activity.egg_model, 243 'color_texture': ctex, 244 'body': 'capsule', 245 'reflection': 'soft', 246 'model_scale': 0.5, 247 'body_scale': 0.6, 248 'density': 4.0, 249 'reflection_scale': [0.15], 250 'shadow_size': 0.6, 251 'position': self._spawn_pos, 252 'materials': mats 253 }) 254 255 def exists(self) -> bool: 256 return bool(self.node) 257 258 def handlemessage(self, msg: Any) -> Any: 259 if isinstance(msg, ba.DieMessage): 260 if self.node: 261 self.node.delete() 262 elif isinstance(msg, ba.HitMessage): 263 if self.node: 264 assert msg.force_direction is not None 265 self.node.handlemessage( 266 'impulse', msg.pos[0], msg.pos[1], msg.pos[2], 267 msg.velocity[0], msg.velocity[1], msg.velocity[2], 268 1.0 * msg.magnitude, 1.0 * msg.velocity_magnitude, 269 msg.radius, 0, msg.force_direction[0], 270 msg.force_direction[1], msg.force_direction[2]) 271 else: 272 super().handlemessage(msg)
A lovely egg that can be picked up for points.
228 def __init__(self, position: tuple[float, float, float] = (0.0, 1.0, 0.0)): 229 super().__init__() 230 activity = self.activity 231 assert isinstance(activity, EasterEggHuntGame) 232 shared = SharedObjects.get() 233 234 # Spawn just above the provided point. 235 self._spawn_pos = (position[0], position[1] + 1.0, position[2]) 236 ctex = (activity.egg_tex_1, activity.egg_tex_2, 237 activity.egg_tex_3)[random.randrange(3)] 238 mats = [shared.object_material, activity.egg_material] 239 self.node = ba.newnode('prop', 240 delegate=self, 241 attrs={ 242 'model': activity.egg_model, 243 'color_texture': ctex, 244 'body': 'capsule', 245 'reflection': 'soft', 246 'model_scale': 0.5, 247 'body_scale': 0.6, 248 'density': 4.0, 249 'reflection_scale': [0.15], 250 'shadow_size': 0.6, 251 'position': self._spawn_pos, 252 'materials': mats 253 })
Instantiates an Actor in the current ba.Activity.
Returns whether the Actor is still present in a meaningful way.
Note that a dying character should still return True here as long as their corpse is visible; this is about presence, not being 'alive' (see ba.Actor.is_alive() for that).
If this returns False, it is assumed the Actor can be completely deleted without affecting the game; this call is often used when pruning lists of Actors, such as with ba.Actor.autoretain()
The default implementation of this method always return True.
Note that the boolean operator for the Actor class calls this method, so a simple "if myactor" test will conveniently do the right thing even if myactor is set to None.
258 def handlemessage(self, msg: Any) -> Any: 259 if isinstance(msg, ba.DieMessage): 260 if self.node: 261 self.node.delete() 262 elif isinstance(msg, ba.HitMessage): 263 if self.node: 264 assert msg.force_direction is not None 265 self.node.handlemessage( 266 'impulse', msg.pos[0], msg.pos[1], msg.pos[2], 267 msg.velocity[0], msg.velocity[1], msg.velocity[2], 268 1.0 * msg.magnitude, 1.0 * msg.velocity_magnitude, 269 msg.radius, 0, msg.force_direction[0], 270 msg.force_direction[1], msg.force_direction[2]) 271 else: 272 super().handlemessage(msg)
General message handling; can be passed any message object.
Inherited Members
- ba._actor.Actor
- autoretain
- on_expire
- expired
- is_alive
- activity
- getactivity