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)
class Player(ba._player.Player[ForwardRef('Team')]):
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.

Player()
30    def __init__(self) -> None:
31        self.respawn_timer: ba.Timer | None = None
32        self.respawn_icon: RespawnIcon | None = None
class Team(ba._team.Team[bastd.game.easteregghunt.Player]):
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.

Team()
38    def __init__(self) -> None:
39        self.score = 0
Inherited Members
ba._team.Team
manual_init
customdata
on_expire
sessionteam
class EasterEggHuntGame(ba._teamgame.TeamGameActivity[bastd.game.easteregghunt.Player, bastd.game.easteregghunt.Team]):
 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.

EasterEggHuntGame(settings: dict)
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.

name: str | None = 'Easter Egg Hunt'
description: str | None = 'Gather eggs!'
available_settings: list[ba._settings.Setting] | None = [BoolSetting(name='Pro Mode', default=False)]
scoreconfig: ba._score.ScoreConfig | None = ScoreConfig(label='Score', scoretype=<ScoreType.POINTS: 'p'>, lower_is_better=False, none_is_winner=False, version='')
@classmethod
def get_supported_maps(cls, sessiontype: type[ba._session.Session]) -> list[str]:
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.

@classmethod
def supports_session_type(cls, sessiontype: type[ba._session.Session]) -> bool:
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.

def on_team_join(self, team: bastd.game.easteregghunt.Team) -> None:
87    def on_team_join(self, team: Team) -> None:
88        if self.has_begun():
89            self._update_scoreboard()

Called when a new ba.Team joins the Activity.

(including the initial set of Teams)

def on_begin(self) -> None:
 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.

def spawn_player(self, player: bastd.game.easteregghunt.Player) -> ba._actor.Actor:
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().

def handlemessage(self, msg: Any) -> Any:
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.

def end_game(self) -> None:
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
class Egg(ba._actor.Actor):
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.

Egg(position: tuple[float, float, float] = (0.0, 1.0, 0.0))
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.

def exists(self) -> bool:
255    def exists(self) -> bool:
256        return bool(self.node)

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.

def handlemessage(self, msg: Any) -> Any:
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