This commit is contained in:
Eric Froemling 2020-05-23 12:07:00 -07:00
parent 374ce7ee11
commit e6755d9be8
19 changed files with 261 additions and 250 deletions

View File

@ -1012,7 +1012,7 @@ class GameActivity(Activity[PlayerType, TeamType]):
def spawn_player_spaz(self, def spawn_player_spaz(self,
player: PlayerType, player: PlayerType,
position: Sequence[float] = (0, 0, 0), position: Sequence[float] = (0, 0, 0),
angle: float = None) -> PlayerSpaz[PlayerType]: angle: float = None) -> PlayerSpaz:
"""Create and wire up a ba.PlayerSpaz for the provided ba.Player.""" """Create and wire up a ba.PlayerSpaz for the provided ba.Player."""
# pylint: disable=too-many-locals # pylint: disable=too-many-locals
# pylint: disable=cyclic-import # pylint: disable=cyclic-import

View File

@ -110,7 +110,7 @@ class TeamGameActivity(GameActivity[PlayerType, TeamType]):
def spawn_player_spaz(self, def spawn_player_spaz(self,
player: PlayerType, player: PlayerType,
position: Sequence[float] = None, position: Sequence[float] = None,
angle: float = None) -> PlayerSpaz[PlayerType]: angle: float = None) -> PlayerSpaz:
""" """
Method override; spawns and wires up a standard ba.PlayerSpaz for Method override; spawns and wires up a standard ba.PlayerSpaz for
a ba.Player. a ba.Player.

View File

@ -22,13 +22,14 @@
from __future__ import annotations from __future__ import annotations
from typing import TYPE_CHECKING, Generic, TypeVar from typing import TYPE_CHECKING, TypeVar, overload
import ba import ba
from bastd.actor.spaz import Spaz from bastd.actor.spaz import Spaz
if TYPE_CHECKING: if TYPE_CHECKING:
from typing import Any, Sequence, Tuple, Optional from typing import Any, Sequence, Tuple, Optional, Type
from typing_extensions import Literal
PlayerType = TypeVar('PlayerType', bound=ba.Player) PlayerType = TypeVar('PlayerType', bound=ba.Player)
TeamType = TypeVar('TeamType', bound=ba.Team) TeamType = TypeVar('TeamType', bound=ba.Team)
@ -50,7 +51,7 @@ class PlayerSpazHurtMessage:
self.spaz = spaz self.spaz = spaz
class PlayerSpaz(Spaz, Generic[PlayerType]): class PlayerSpaz(Spaz):
"""A ba.Spaz subclass meant to be controlled by a ba.Player. """A ba.Spaz subclass meant to be controlled by a ba.Player.
category: Gameplay Classes category: Gameplay Classes
@ -64,7 +65,7 @@ class PlayerSpaz(Spaz, Generic[PlayerType]):
""" """
def __init__(self, def __init__(self,
player: PlayerType, player: ba.Player,
color: Sequence[float] = (1.0, 1.0, 1.0), color: Sequence[float] = (1.0, 1.0, 1.0),
highlight: Sequence[float] = (0.5, 0.5, 0.5), highlight: Sequence[float] = (0.5, 0.5, 0.5),
character: str = 'Spaz', character: str = 'Spaz',
@ -81,31 +82,51 @@ class PlayerSpaz(Spaz, Generic[PlayerType]):
source_player=player, source_player=player,
start_invincible=True, start_invincible=True,
powerups_expire=powerups_expire) powerups_expire=powerups_expire)
self.last_player_attacked_by: Optional[PlayerType] = None self.last_player_attacked_by: Optional[ba.Player] = None
self.last_attacked_time = 0.0 self.last_attacked_time = 0.0
self.last_attacked_type: Optional[Tuple[str, str]] = None self.last_attacked_type: Optional[Tuple[str, str]] = None
self.held_count = 0 self.held_count = 0
self.last_player_held_by: Optional[PlayerType] = None self.last_player_held_by: Optional[ba.Player] = None
self._player = player self._player = player
self._drive_player_position() self._drive_player_position()
@property # @property
def player(self) -> PlayerType: # def player(self, playertype: Type[PlayerType]) -> PlayerType:
"""The ba.Player associated with this Spaz. # """The ba.Player associated with this Spaz.
If the player no longer exists, raises an ba.PlayerNotFoundError. # If the player no longer exists, raises an ba.PlayerNotFoundError.
""" # """
if not self._player: # player = self._player
raise ba.PlayerNotFoundError() # assert isinstance(player, playertype)
return self._player # if not player:
# raise ba.PlayerNotFoundError()
# return player
def getplayer(self) -> Optional[PlayerType]: @overload
def getplayer(self,
playertype: Type[PlayerType],
doraise: Literal[False] = False) -> Optional[PlayerType]:
...
@overload
def getplayer(self, playertype: Type[PlayerType],
doraise: Literal[True]) -> PlayerType:
...
def getplayer(self,
playertype: Type[PlayerType],
doraise: bool = False) -> Optional[PlayerType]:
"""Get the ba.Player associated with this Spaz. """Get the ba.Player associated with this Spaz.
Note that this may return None if the player has left. By default this will return None if the Player no longer exists.
If you are logically certain that the Player still exists, pass
doraise=False to get a non-optional return type.
""" """
# Return None in the case of a no-longer-valid reference. player: Any = self._player
return self._player if self._player else None assert isinstance(player, playertype)
if not player.exists() and doraise:
raise ba.PlayerNotFoundError()
return player if player.exists() else None
def connect_controls_to_player(self, def connect_controls_to_player(self,
enable_jump: bool = True, enable_jump: bool = True,
@ -120,7 +141,7 @@ class PlayerSpaz(Spaz, Generic[PlayerType]):
but can be selectively limited by passing False but can be selectively limited by passing False
to specific arguments. to specific arguments.
""" """
player = self.getplayer() player = self.getplayer(ba.Player)
assert player assert player
# Reset any currently connected player and/or the player we're # Reset any currently connected player and/or the player we're
@ -232,6 +253,7 @@ class PlayerSpaz(Spaz, Generic[PlayerType]):
activity = self._activity() activity = self._activity()
player = self.getplayer(ba.Player, doraise=False)
if not killed: if not killed:
killerplayer = None killerplayer = None
else: else:
@ -254,7 +276,7 @@ class PlayerSpaz(Spaz, Generic[PlayerType]):
# ok, call it a suicide unless we're in co-op # ok, call it a suicide unless we're in co-op
if (activity is not None and not isinstance( if (activity is not None and not isinstance(
activity.session, ba.CoopSession)): activity.session, ba.CoopSession)):
killerplayer = self.getplayer() killerplayer = player
else: else:
killerplayer = None killerplayer = None
@ -263,9 +285,9 @@ class PlayerSpaz(Spaz, Generic[PlayerType]):
assert killerplayer is None or killerplayer assert killerplayer is None or killerplayer
# Only report if both the player and the activity still exist. # Only report if both the player and the activity still exist.
if killed and activity is not None and self.getplayer(): if killed and activity is not None and player:
activity.handlemessage( activity.handlemessage(
ba.PlayerDiedMessage(self.player, killed, killerplayer, ba.PlayerDiedMessage(player, killed, killerplayer,
msg.how)) msg.how))
super().handlemessage(msg) # Augment standard behavior. super().handlemessage(msg) # Augment standard behavior.
@ -279,7 +301,7 @@ class PlayerSpaz(Spaz, Generic[PlayerType]):
self.last_attacked_type = (msg.hit_type, msg.hit_subtype) self.last_attacked_type = (msg.hit_type, msg.hit_subtype)
super().handlemessage(msg) # Augment standard behavior. super().handlemessage(msg) # Augment standard behavior.
activity = self._activity() activity = self._activity()
if activity is not None: if activity is not None and self._player.exists():
activity.handlemessage(PlayerSpazHurtMessage(self)) activity.handlemessage(PlayerSpazHurtMessage(self))
else: else:
super().handlemessage(msg) super().handlemessage(msg)

View File

@ -187,12 +187,10 @@ class AssaultGame(ba.TeamGameActivity[Player, Team]):
if not isinstance(actor, PlayerSpaz): if not isinstance(actor, PlayerSpaz):
return return
player = actor.getplayer() player = actor.getplayer(Player)
if not player or not player.actor: if not player or not player.actor:
return return
assert isinstance(player, Player)
# If its another team's player, they scored. # If its another team's player, they scored.
player_team = player.team player_team = player.team
if player_team is not team: if player_team is not team:

View File

@ -170,7 +170,7 @@ class CaptureTheFlagGame(ba.TeamGameActivity[Player, Team]):
self._time_limit = float(settings['Time Limit']) self._time_limit = float(settings['Time Limit'])
self.flag_touch_return_time = float(settings['Flag Touch Return Time']) self.flag_touch_return_time = float(settings['Flag Touch Return Time'])
self.flag_idle_return_time = float(settings['Flag Idle return Time']) self.flag_idle_return_time = float(settings['Flag Idle Return Time'])
# Base class overrides # Base class overrides
self.slow_motion = self._epic_mode self.slow_motion = self._epic_mode
@ -448,7 +448,7 @@ class CaptureTheFlagGame(ba.TeamGameActivity[Player, Team]):
delegate = node.getdelegate() delegate = node.getdelegate()
if not isinstance(delegate, PlayerSpaz): if not isinstance(delegate, PlayerSpaz):
return None return None
return delegate.getplayer() return delegate.getplayer(Player)
def _handle_hit_own_flag(self, team: Team, val: int) -> None: def _handle_hit_own_flag(self, team: Team, val: int) -> None:
""" """
@ -508,10 +508,10 @@ class CaptureTheFlagGame(ba.TeamGameActivity[Player, Team]):
def spawn_player_spaz(self, def spawn_player_spaz(self,
player: Player, player: Player,
position: Sequence[float] = None, position: Sequence[float] = None,
angle: float = None) -> PlayerSpaz[Player]: angle: float = None) -> PlayerSpaz:
"""Intercept new spazzes and add our team material for them.""" """Intercept new spazzes and add our team material for them."""
spaz = super().spawn_player_spaz(player, position, angle) spaz = super().spawn_player_spaz(player, position, angle)
player = spaz.player player = spaz.getplayer(Player, doraise=True)
team: Team = player.team team: Team = player.team
player.touching_own_flag = 0 player.touching_own_flag = 0
no_physical_mats: List[ba.Material] = [ no_physical_mats: List[ba.Material] = [

View File

@ -30,6 +30,7 @@ from typing import TYPE_CHECKING
import ba import ba
from bastd.actor.flag import Flag from bastd.actor.flag import Flag
from bastd.actor.playerspaz import PlayerSpaz from bastd.actor.playerspaz import PlayerSpaz
from bastd.actor.scoreboard import Scoreboard
if TYPE_CHECKING: if TYPE_CHECKING:
from typing import Any, Type, List, Dict, Optional, Sequence, Union from typing import Any, Type, List, Dict, Optional, Sequence, Union
@ -94,7 +95,6 @@ class ChosenOneGame(ba.TeamGameActivity[Player, Team]):
return ba.getmaps('keep_away') return ba.getmaps('keep_away')
def __init__(self, settings: Dict[str, Any]): def __init__(self, settings: Dict[str, Any]):
from bastd.actor.scoreboard import Scoreboard
super().__init__(settings) super().__init__(settings)
self._scoreboard = Scoreboard() self._scoreboard = Scoreboard()
self._chosen_one_player: Optional[Player] = None self._chosen_one_player: Optional[Player] = None
@ -179,7 +179,7 @@ class ChosenOneGame(ba.TeamGameActivity[Player, Team]):
return return
delegate = ba.get_collision_info('opposing_node').getdelegate() delegate = ba.get_collision_info('opposing_node').getdelegate()
if isinstance(delegate, PlayerSpaz): if isinstance(delegate, PlayerSpaz):
player = ba.playercast_o(Player, delegate.getplayer()) player = delegate.getplayer(Player)
if player is not None and player.is_alive(): if player is not None and player.is_alive():
self._set_chosen_one_player(player) self._set_chosen_one_player(player)

View File

@ -30,6 +30,7 @@ from typing import TYPE_CHECKING
import ba import ba
from bastd.actor.flag import Flag from bastd.actor.flag import Flag
from bastd.actor.scoreboard import Scoreboard
if TYPE_CHECKING: if TYPE_CHECKING:
from typing import Any, Optional, Type, List, Dict, Sequence, Union from typing import Any, Optional, Type, List, Dict, Sequence, Union
@ -116,7 +117,6 @@ class ConquestGame(ba.TeamGameActivity[Player, Team]):
return ba.getmaps('conquest') return ba.getmaps('conquest')
def __init__(self, settings: Dict[str, Any]): def __init__(self, settings: Dict[str, Any]):
from bastd.actor.scoreboard import Scoreboard
super().__init__(settings) super().__init__(settings)
self._scoreboard = Scoreboard() self._scoreboard = Scoreboard()
self._score_sound = ba.getsound('score') self._score_sound = ba.getsound('score')

View File

@ -29,6 +29,7 @@ from typing import TYPE_CHECKING
import ba import ba
from bastd.actor.playerspaz import PlayerSpaz from bastd.actor.playerspaz import PlayerSpaz
from bastd.actor.scoreboard import Scoreboard
if TYPE_CHECKING: if TYPE_CHECKING:
from typing import Any, Type, List, Dict, Tuple, Union, Sequence, Optional from typing import Any, Type, List, Dict, Tuple, Union, Sequence, Optional
@ -101,7 +102,6 @@ class DeathMatchGame(ba.TeamGameActivity[Player, Team]):
return ba.getmaps('melee') return ba.getmaps('melee')
def __init__(self, settings: Dict[str, Any]): def __init__(self, settings: Dict[str, Any]):
from bastd.actor.scoreboard import Scoreboard
super().__init__(settings) super().__init__(settings)
self._scoreboard = Scoreboard() self._scoreboard = Scoreboard()
self._score_to_win: Optional[int] = None self._score_to_win: Optional[int] = None

View File

@ -142,8 +142,7 @@ class EasterEggHuntGame(ba.TeamGameActivity[Player, Team]):
assert isinstance(egg, Egg) assert isinstance(egg, Egg)
spaz = playernode.getdelegate() spaz = playernode.getdelegate()
assert isinstance(spaz, PlayerSpaz) assert isinstance(spaz, PlayerSpaz)
player = (spaz.getplayer() player = spaz.getplayer(Player)
if hasattr(spaz, 'getplayer') else None)
if player and egg: if player and egg:
player.team.score += 1 player.team.score += 1

View File

@ -29,6 +29,7 @@ from typing import TYPE_CHECKING
import ba import ba
from bastd.actor.spaz import get_factory from bastd.actor.spaz import get_factory
from bastd.actor.scoreboard import Scoreboard
if TYPE_CHECKING: if TYPE_CHECKING:
from typing import (Any, Tuple, Dict, Type, List, Sequence, Optional, from typing import (Any, Tuple, Dict, Type, List, Sequence, Optional,
@ -237,7 +238,6 @@ class EliminationGame(ba.TeamGameActivity[Player, Team]):
return ba.getmaps('melee') return ba.getmaps('melee')
def __init__(self, settings: Dict[str, Any]): def __init__(self, settings: Dict[str, Any]):
from bastd.actor.scoreboard import Scoreboard
super().__init__(settings) super().__init__(settings)
self._scoreboard = Scoreboard() self._scoreboard = Scoreboard()
self._start_time: Optional[float] = None self._start_time: Optional[float] = None

View File

@ -28,13 +28,15 @@ from __future__ import annotations
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
import ba import ba
from bastd.actor.scoreboard import Scoreboard
from bastd.actor import powerupbox
if TYPE_CHECKING: if TYPE_CHECKING:
from typing import Any, Sequence, Dict, Type, List, Optional, Union from typing import Any, Sequence, Dict, Type, List, Optional, Union
class PuckDeathMessage: class PuckDeathMessage:
"""Inform an object that a puck has died.""" """Inform something that a puck has died."""
def __init__(self, puck: Puck): def __init__(self, puck: Puck):
self.puck = puck self.puck = puck
@ -145,8 +147,6 @@ class HockeyGame(ba.TeamGameActivity[Player, Team]):
return ba.getmaps('hockey') return ba.getmaps('hockey')
def __init__(self, settings: Dict[str, Any]): def __init__(self, settings: Dict[str, Any]):
from bastd.actor.scoreboard import Scoreboard
from bastd.actor import powerupbox
super().__init__(settings) super().__init__(settings)
self._scoreboard = Scoreboard() self._scoreboard = Scoreboard()
self._cheer_sound = ba.getsound('cheer') self._cheer_sound = ba.getsound('cheer')

View File

@ -30,6 +30,7 @@ from typing import TYPE_CHECKING
import ba import ba
from bastd.actor.playerspaz import PlayerSpaz from bastd.actor.playerspaz import PlayerSpaz
from bastd.actor.scoreboard import Scoreboard
from bastd.actor.flag import (Flag, FlagDroppedMessage, FlagDeathMessage, from bastd.actor.flag import (Flag, FlagDroppedMessage, FlagDeathMessage,
FlagPickedUpMessage) FlagPickedUpMessage)
@ -89,7 +90,6 @@ class KeepAwayGame(ba.TeamGameActivity[Player, Team]):
return ba.getmaps('keep_away') return ba.getmaps('keep_away')
def __init__(self, settings: Dict[str, Any]): def __init__(self, settings: Dict[str, Any]):
from bastd.actor.scoreboard import Scoreboard
super().__init__(settings) super().__init__(settings)
self._scoreboard = Scoreboard() self._scoreboard = Scoreboard()
self._swipsound = ba.getsound('swip') self._swipsound = ba.getsound('swip')

View File

@ -250,7 +250,7 @@ class KingOfTheHillGame(ba.TeamGameActivity[Player, Team]):
delegate = ba.get_collision_info('opposing_node').getdelegate() delegate = ba.get_collision_info('opposing_node').getdelegate()
if not isinstance(delegate, PlayerSpaz): if not isinstance(delegate, PlayerSpaz):
return return
player = ba.playercast_o(Player, delegate.getplayer()) player = delegate.getplayer(Player)
if not player: if not player:
return return

View File

@ -29,7 +29,7 @@ import random
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
import ba import ba
from bastd.actor import spazbot from bastd.actor.spazbot import BotSet, ChargerBot, SpazBotDeathMessage
from bastd.actor.onscreentimer import OnScreenTimer from bastd.actor.onscreentimer import OnScreenTimer
if TYPE_CHECKING: if TYPE_CHECKING:
@ -76,7 +76,7 @@ class NinjaFightGame(ba.TeamGameActivity[Player, Team]):
self._winsound = ba.getsound('score') self._winsound = ba.getsound('score')
self._won = False self._won = False
self._timer: Optional[OnScreenTimer] = None self._timer: Optional[OnScreenTimer] = None
self._bots = spazbot.BotSet() self._bots = BotSet()
# Called when our game is transitioning in but not ready to begin; # Called when our game is transitioning in but not ready to begin;
# we can go ahead and start creating stuff, playing music, etc. # we can go ahead and start creating stuff, playing music, etc.
@ -100,27 +100,27 @@ class NinjaFightGame(ba.TeamGameActivity[Player, Team]):
# Spawn some baddies. # Spawn some baddies.
ba.timer( ba.timer(
1.0, lambda: self._bots.spawn_bot( 1.0, lambda: self._bots.spawn_bot(
spazbot.ChargerBot, pos=(3, 3, -2), spawn_time=3.0)) ChargerBot, pos=(3, 3, -2), spawn_time=3.0))
ba.timer( ba.timer(
2.0, lambda: self._bots.spawn_bot( 2.0, lambda: self._bots.spawn_bot(
spazbot.ChargerBot, pos=(-3, 3, -2), spawn_time=3.0)) ChargerBot, pos=(-3, 3, -2), spawn_time=3.0))
ba.timer( ba.timer(
3.0, lambda: self._bots.spawn_bot( 3.0, lambda: self._bots.spawn_bot(
spazbot.ChargerBot, pos=(5, 3, -2), spawn_time=3.0)) ChargerBot, pos=(5, 3, -2), spawn_time=3.0))
ba.timer( ba.timer(
4.0, lambda: self._bots.spawn_bot( 4.0, lambda: self._bots.spawn_bot(
spazbot.ChargerBot, pos=(-5, 3, -2), spawn_time=3.0)) ChargerBot, pos=(-5, 3, -2), spawn_time=3.0))
# Add some extras for multiplayer or pro mode. # Add some extras for multiplayer or pro mode.
assert self.initial_player_info is not None assert self.initial_player_info is not None
if len(self.initial_player_info) > 2 or is_pro: if len(self.initial_player_info) > 2 or is_pro:
ba.timer( ba.timer(
5.0, lambda: self._bots.spawn_bot( 5.0, lambda: self._bots.spawn_bot(
spazbot.ChargerBot, pos=(0, 3, -5), spawn_time=3.0)) ChargerBot, pos=(0, 3, -5), spawn_time=3.0))
if len(self.initial_player_info) > 3 or is_pro: if len(self.initial_player_info) > 3 or is_pro:
ba.timer( ba.timer(
6.0, lambda: self._bots.spawn_bot( 6.0, lambda: self._bots.spawn_bot(
spazbot.ChargerBot, pos=(0, 3, 1), spawn_time=3.0)) ChargerBot, pos=(0, 3, 1), spawn_time=3.0))
# Called for each spawning player. # Called for each spawning player.
def spawn_player(self, player: Player) -> ba.Actor: def spawn_player(self, player: Player) -> ba.Actor:
@ -150,7 +150,7 @@ class NinjaFightGame(ba.TeamGameActivity[Player, Team]):
self.respawn_player(msg.getplayer(Player)) self.respawn_player(msg.getplayer(Player))
# A spaz-bot has died. # A spaz-bot has died.
elif isinstance(msg, spazbot.SpazBotDeathMessage): elif isinstance(msg, SpazBotDeathMessage):
# Unfortunately the bot-set will always tell us there are living # Unfortunately the bot-set will always tell us there are living
# bots if we ask here (the currently-dying bot isn't officially # bots if we ask here (the currently-dying bot isn't officially
# marked dead yet) ..so lets push a call into the event loop to # marked dead yet) ..so lets push a call into the event loop to

View File

@ -30,17 +30,28 @@ import random
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
import ba import ba
from bastd.actor import bomb as stdbomb from bastd.actor.bomb import TNTSpawner
from bastd.actor import playerspaz, spazbot from bastd.actor.playerspaz import PlayerSpazHurtMessage
from bastd.actor.scoreboard import Scoreboard
from bastd.actor.spazbot import (
SpazBotDeathMessage, BotSet, ChargerBot, StickyBot, BomberBot,
BomberBotLite, BrawlerBot, BrawlerBotLite, TriggerBot, BomberBotStaticLite,
TriggerBotStatic, BomberBotProStatic, TriggerBotPro, ExplodeyBot,
BrawlerBotProShielded, ChargerBotProShielded, BomberBotPro,
TriggerBotProShielded, BrawlerBotPro, BomberBotProShielded)
if TYPE_CHECKING: if TYPE_CHECKING:
from typing import Any, Type, Dict, Optional, List, Tuple, Union, Sequence from typing import Any, Type, Dict, Optional, List, Tuple, Union, Sequence
from bastd.actor.scoreboard import Scoreboard from bastd.actor.spazbot import SpazBot
class Player(ba.Player['Team']): class Player(ba.Player['Team']):
"""Our player type for this game.""" """Our player type for this game."""
def __init__(self) -> None:
self.has_been_hurt = False
self.respawn_wave = 0
class Team(ba.Team[Player]): class Team(ba.Team[Player]):
"""Our team type for this game.""" """Our team type for this game."""
@ -114,8 +125,8 @@ class OnslaughtGame(ba.CoopGameActivity[Player, Team]):
self._have_tnt = False self._have_tnt = False
self._excludepowerups: Optional[List[str]] = None self._excludepowerups: Optional[List[str]] = None
self._waves: Optional[List[Dict[str, Any]]] = None self._waves: Optional[List[Dict[str, Any]]] = None
self._tntspawner: Optional[stdbomb.TNTSpawner] = None self._tntspawner: Optional[TNTSpawner] = None
self._bots: Optional[spazbot.BotSet] = None self._bots: Optional[BotSet] = None
self._powerup_drop_timer: Optional[ba.Timer] = None self._powerup_drop_timer: Optional[ba.Timer] = None
self._time_bonus_timer: Optional[ba.Timer] = None self._time_bonus_timer: Optional[ba.Timer] = None
self._time_bonus_text: Optional[ba.NodeActor] = None self._time_bonus_text: Optional[ba.NodeActor] = None
@ -127,17 +138,13 @@ class OnslaughtGame(ba.CoopGameActivity[Player, Team]):
self._tnt_kills = 0 self._tnt_kills = 0
def on_transition_in(self) -> None: def on_transition_in(self) -> None:
from bastd.actor.scoreboard import Scoreboard
super().on_transition_in() super().on_transition_in()
session = ba.getsession()
# Show special landmine tip on rookie preset. # Show special landmine tip on rookie preset.
if self._preset in ['rookie', 'rookie_easy']: if self._preset in ['rookie', 'rookie_easy']:
# Show once per session only (then we revert to regular tips). # Show once per session only (then we revert to regular tips).
if not hasattr(ba.getsession(), if not getattr(session, '_g_showed_onslaught_landmine_tip', False):
'_g_showed_onslaught_land_mine_tip'): setattr(session, '_g_showed_onslaught_landmine_tip', True)
# pylint: disable=protected-access
ba.getsession( # type: ignore
)._g_showed_onslaught_land_mine_tip = True
self.tips = [{ self.tips = [{
'tip': 'Land-mines are a good way' 'tip': 'Land-mines are a good way'
' to stop speedy enemies.', ' to stop speedy enemies.',
@ -148,10 +155,8 @@ class OnslaughtGame(ba.CoopGameActivity[Player, Team]):
# Show special tnt tip on pro preset. # Show special tnt tip on pro preset.
if self._preset in ['pro', 'pro_easy']: if self._preset in ['pro', 'pro_easy']:
# Show once per session only (then we revert to regular tips). # Show once per session only (then we revert to regular tips).
if not hasattr(ba.getsession(), '_g_showed_onslaught_tnt_tip'): if not getattr(session, '_g_showed_onslaught_tnt_tip', False):
# pylint: disable=protected-access setattr(session, '_g_showed_onslaught_tnt_tip', True)
ba.getsession( # type: ignore
)._g_showed_onslaught_tnt_tip = True
self.tips = [{ self.tips = [{
'tip': 'Take out a group of enemies by\n' 'tip': 'Take out a group of enemies by\n'
'setting off a bomb near a TNT box.', 'setting off a bomb near a TNT box.',
@ -162,10 +167,8 @@ class OnslaughtGame(ba.CoopGameActivity[Player, Team]):
# Show special curse tip on uber preset. # Show special curse tip on uber preset.
if self._preset in ['uber', 'uber_easy']: if self._preset in ['uber', 'uber_easy']:
# Show once per session only (then we revert to regular tips). # Show once per session only (then we revert to regular tips).
if not hasattr(ba.getsession(), '_g_showed_onslaught_curse_tip'): if not getattr(session, '_g_showed_onslaught_curse_tip', False):
# pylint: disable=protected-access setattr(session, '_g_showed_onslaught_curse_tip', True)
ba.getsession( # type: ignore
)._g_showed_onslaught_curse_tip = True
self.tips = [{ self.tips = [{
'tip': 'Curse boxes turn you into a ticking time bomb.\n' 'tip': 'Curse boxes turn you into a ticking time bomb.\n'
'The only cure is to quickly grab a health-pack.', 'The only cure is to quickly grab a health-pack.',
@ -203,38 +206,38 @@ class OnslaughtGame(ba.CoopGameActivity[Player, Team]):
self._waves = [ self._waves = [
{'base_angle': 195, {'base_angle': 195,
'entries': [ 'entries': [
{'type': spazbot.BomberBotLite, 'spacing': 5}, {'type': BomberBotLite, 'spacing': 5},
] * player_count}, ] * player_count},
{'base_angle': 130, {'base_angle': 130,
'entries': [ 'entries': [
{'type': spazbot.BrawlerBotLite, 'spacing': 5}, {'type': BrawlerBotLite, 'spacing': 5},
] * player_count}, ] * player_count},
{'base_angle': 195, {'base_angle': 195,
'entries': [ 'entries': [
{'type': spazbot.BomberBotLite, 'spacing': 10}, {'type': BomberBotLite, 'spacing': 10},
] * (player_count + 1)}, ] * (player_count + 1)},
{'base_angle': 130, {'base_angle': 130,
'entries': [ 'entries': [
{'type': spazbot.BrawlerBotLite, 'spacing': 10}, {'type': BrawlerBotLite, 'spacing': 10},
] * (player_count + 1)}, ] * (player_count + 1)},
{'base_angle': 130, {'base_angle': 130,
'entries': [ 'entries': [
{'type': spazbot.BrawlerBotLite, 'spacing': 5} {'type': BrawlerBotLite, 'spacing': 5}
if player_count > 1 else None, if player_count > 1 else None,
{'type': spazbot.BrawlerBotLite, 'spacing': 5}, {'type': BrawlerBotLite, 'spacing': 5},
{'type': None, 'spacing': 30}, {'type': None, 'spacing': 30},
{'type': spazbot.BomberBotLite, 'spacing': 5} {'type': BomberBotLite, 'spacing': 5}
if player_count > 3 else None, if player_count > 3 else None,
{'type': spazbot.BomberBotLite, 'spacing': 5}, {'type': BomberBotLite, 'spacing': 5},
{'type': None, 'spacing': 30}, {'type': None, 'spacing': 30},
{'type': spazbot.BrawlerBotLite, 'spacing': 5}, {'type': BrawlerBotLite, 'spacing': 5},
{'type': spazbot.BrawlerBotLite, 'spacing': 5} {'type': BrawlerBotLite, 'spacing': 5}
if player_count > 2 else None, if player_count > 2 else None,
]}, ]},
{'base_angle': 195, {'base_angle': 195,
'entries': [ 'entries': [
{'type': spazbot.TriggerBot, 'spacing': 90}, {'type': TriggerBot, 'spacing': 90},
{'type': spazbot.TriggerBot, 'spacing': 90} {'type': TriggerBot, 'spacing': 90}
if player_count > 1 else None, if player_count > 1 else None,
]}, ]},
] # yapf: disable ] # yapf: disable
@ -244,60 +247,60 @@ class OnslaughtGame(ba.CoopGameActivity[Player, Team]):
self._excludepowerups = ['curse'] self._excludepowerups = ['curse']
self._waves = [ self._waves = [
{'entries': [ {'entries': [
{'type': spazbot.ChargerBot, 'point': 'left_upper_more'} {'type': ChargerBot, 'point': 'left_upper_more'}
if player_count > 2 else None, if player_count > 2 else None,
{'type': spazbot.ChargerBot, 'point': 'left_upper'}, {'type': ChargerBot, 'point': 'left_upper'},
]}, ]},
{'entries': [ {'entries': [
{'type': spazbot.BomberBotStaticLite, {'type': BomberBotStaticLite,
'point': 'turret_top_right'}, 'point': 'turret_top_right'},
{'type': spazbot.BrawlerBotLite, 'point': 'right_upper'}, {'type': BrawlerBotLite, 'point': 'right_upper'},
{'type': spazbot.BrawlerBotLite, 'point': 'right_lower'} {'type': BrawlerBotLite, 'point': 'right_lower'}
if player_count > 1 else None, if player_count > 1 else None,
{'type': spazbot.BomberBotStaticLite, {'type': BomberBotStaticLite,
'point': 'turret_bottom_right'} 'point': 'turret_bottom_right'}
if player_count > 2 else None, if player_count > 2 else None,
]}, ]},
{'entries': [ {'entries': [
{'type': spazbot.BomberBotStaticLite, {'type': BomberBotStaticLite,
'point': 'turret_bottom_left'}, 'point': 'turret_bottom_left'},
{'type': spazbot.TriggerBot, 'point': 'left'}, {'type': TriggerBot, 'point': 'left'},
{'type': spazbot.TriggerBot, 'point': 'left_lower'} {'type': TriggerBot, 'point': 'left_lower'}
if player_count > 1 else None, if player_count > 1 else None,
{'type': spazbot.TriggerBot, 'point': 'left_upper'} {'type': TriggerBot, 'point': 'left_upper'}
if player_count > 2 else None, if player_count > 2 else None,
]}, ]},
{'entries': [ {'entries': [
{'type': spazbot.BrawlerBotLite, 'point': 'top_right'}, {'type': BrawlerBotLite, 'point': 'top_right'},
{'type': spazbot.BrawlerBot, 'point': 'top_half_right'} {'type': BrawlerBot, 'point': 'top_half_right'}
if player_count > 1 else None, if player_count > 1 else None,
{'type': spazbot.BrawlerBotLite, 'point': 'top_left'}, {'type': BrawlerBotLite, 'point': 'top_left'},
{'type': spazbot.BrawlerBotLite, 'point': 'top_half_left'} {'type': BrawlerBotLite, 'point': 'top_half_left'}
if player_count > 2 else None, if player_count > 2 else None,
{'type': spazbot.BrawlerBot, 'point': 'top'}, {'type': BrawlerBot, 'point': 'top'},
{'type': spazbot.BomberBotStaticLite, {'type': BomberBotStaticLite,
'point': 'turret_top_middle'}, 'point': 'turret_top_middle'},
]}, ]},
{'entries': [ {'entries': [
{'type': spazbot.TriggerBotStatic, {'type': TriggerBotStatic,
'point': 'turret_bottom_left'}, 'point': 'turret_bottom_left'},
{'type': spazbot.TriggerBotStatic, {'type': TriggerBotStatic,
'point': 'turret_bottom_right'}, 'point': 'turret_bottom_right'},
{'type': spazbot.TriggerBot, 'point': 'bottom'}, {'type': TriggerBot, 'point': 'bottom'},
{'type': spazbot.TriggerBot, 'point': 'bottom_half_right'} {'type': TriggerBot, 'point': 'bottom_half_right'}
if player_count > 1 else None, if player_count > 1 else None,
{'type': spazbot.TriggerBot, 'point': 'bottom_half_left'} {'type': TriggerBot, 'point': 'bottom_half_left'}
if player_count > 2 else None, if player_count > 2 else None,
]}, ]},
{'entries': [ {'entries': [
{'type': spazbot.BomberBotStaticLite, {'type': BomberBotStaticLite,
'point': 'turret_top_left'}, 'point': 'turret_top_left'},
{'type': spazbot.BomberBotStaticLite, {'type': BomberBotStaticLite,
'point': 'turret_top_right'}, 'point': 'turret_top_right'},
{'type': spazbot.ChargerBot, 'point': 'bottom'}, {'type': ChargerBot, 'point': 'bottom'},
{'type': spazbot.ChargerBot, 'point': 'bottom_half_left'} {'type': ChargerBot, 'point': 'bottom_half_left'}
if player_count > 1 else None, if player_count > 1 else None,
{'type': spazbot.ChargerBot, 'point': 'bottom_half_right'} {'type': ChargerBot, 'point': 'bottom_half_right'}
if player_count > 2 else None, if player_count > 2 else None,
]}, ]},
] # yapf: disable ] # yapf: disable
@ -308,78 +311,78 @@ class OnslaughtGame(ba.CoopGameActivity[Player, Team]):
self._waves = [ self._waves = [
{'base_angle': -50, {'base_angle': -50,
'entries': [ 'entries': [
{'type': spazbot.BrawlerBot, 'spacing': 12} {'type': BrawlerBot, 'spacing': 12}
if player_count > 3 else None, if player_count > 3 else None,
{'type': spazbot.BrawlerBot, 'spacing': 12}, {'type': BrawlerBot, 'spacing': 12},
{'type': spazbot.BomberBot, 'spacing': 6}, {'type': BomberBot, 'spacing': 6},
{'type': spazbot.BomberBot, 'spacing': 6} {'type': BomberBot, 'spacing': 6}
if self._preset == 'pro' else None, if self._preset == 'pro' else None,
{'type': spazbot.BomberBot, 'spacing': 6} {'type': BomberBot, 'spacing': 6}
if player_count > 1 else None, if player_count > 1 else None,
{'type': spazbot.BrawlerBot, 'spacing': 12}, {'type': BrawlerBot, 'spacing': 12},
{'type': spazbot.BrawlerBot, 'spacing': 12} {'type': BrawlerBot, 'spacing': 12}
if player_count > 2 else None, if player_count > 2 else None,
]}, ]},
{'base_angle': 180, {'base_angle': 180,
'entries': [ 'entries': [
{'type': spazbot.BrawlerBot, 'spacing': 6} {'type': BrawlerBot, 'spacing': 6}
if player_count > 3 else None, if player_count > 3 else None,
{'type': spazbot.BrawlerBot, 'spacing': 6} {'type': BrawlerBot, 'spacing': 6}
if self._preset == 'pro' else None, if self._preset == 'pro' else None,
{'type': spazbot.BrawlerBot, 'spacing': 6}, {'type': BrawlerBot, 'spacing': 6},
{'type': spazbot.ChargerBot, 'spacing': 45}, {'type': ChargerBot, 'spacing': 45},
{'type': spazbot.ChargerBot, 'spacing': 45} {'type': ChargerBot, 'spacing': 45}
if player_count > 1 else None, if player_count > 1 else None,
{'type': spazbot.BrawlerBot, 'spacing': 6}, {'type': BrawlerBot, 'spacing': 6},
{'type': spazbot.BrawlerBot, 'spacing': 6} {'type': BrawlerBot, 'spacing': 6}
if self._preset == 'pro' else None, if self._preset == 'pro' else None,
{'type': spazbot.BrawlerBot, 'spacing': 6} {'type': BrawlerBot, 'spacing': 6}
if player_count > 2 else None, if player_count > 2 else None,
]}, ]},
{'base_angle': 0, {'base_angle': 0,
'entries': [ 'entries': [
{'type': spazbot.ChargerBot, 'spacing': 30}, {'type': ChargerBot, 'spacing': 30},
{'type': spazbot.TriggerBot, 'spacing': 30}, {'type': TriggerBot, 'spacing': 30},
{'type': spazbot.TriggerBot, 'spacing': 30}, {'type': TriggerBot, 'spacing': 30},
{'type': spazbot.TriggerBot, 'spacing': 30} {'type': TriggerBot, 'spacing': 30}
if self._preset == 'pro' else None, if self._preset == 'pro' else None,
{'type': spazbot.TriggerBot, 'spacing': 30} {'type': TriggerBot, 'spacing': 30}
if player_count > 1 else None, if player_count > 1 else None,
{'type': spazbot.TriggerBot, 'spacing': 30} {'type': TriggerBot, 'spacing': 30}
if player_count > 3 else None, if player_count > 3 else None,
{'type': spazbot.ChargerBot, 'spacing': 30}, {'type': ChargerBot, 'spacing': 30},
]}, ]},
{'base_angle': 90, {'base_angle': 90,
'entries': [ 'entries': [
{'type': spazbot.StickyBot, 'spacing': 50}, {'type': StickyBot, 'spacing': 50},
{'type': spazbot.StickyBot, 'spacing': 50} {'type': StickyBot, 'spacing': 50}
if self._preset == 'pro' else None, if self._preset == 'pro' else None,
{'type': spazbot.StickyBot, 'spacing': 50}, {'type': StickyBot, 'spacing': 50},
{'type': spazbot.StickyBot, 'spacing': 50} {'type': StickyBot, 'spacing': 50}
if player_count > 1 else None, if player_count > 1 else None,
{'type': spazbot.StickyBot, 'spacing': 50} {'type': StickyBot, 'spacing': 50}
if player_count > 3 else None, if player_count > 3 else None,
]}, ]},
{'base_angle': 0, {'base_angle': 0,
'entries': [ 'entries': [
{'type': spazbot.TriggerBot, 'spacing': 72}, {'type': TriggerBot, 'spacing': 72},
{'type': spazbot.TriggerBot, 'spacing': 72}, {'type': TriggerBot, 'spacing': 72},
{'type': spazbot.TriggerBot, 'spacing': 72} {'type': TriggerBot, 'spacing': 72}
if self._preset == 'pro' else None, if self._preset == 'pro' else None,
{'type': spazbot.TriggerBot, 'spacing': 72}, {'type': TriggerBot, 'spacing': 72},
{'type': spazbot.TriggerBot, 'spacing': 72}, {'type': TriggerBot, 'spacing': 72},
{'type': spazbot.TriggerBot, 'spacing': 36} {'type': TriggerBot, 'spacing': 36}
if player_count > 2 else None, if player_count > 2 else None,
]}, ]},
{'base_angle': 30, {'base_angle': 30,
'entries': [ 'entries': [
{'type': spazbot.ChargerBotProShielded, 'spacing': 50}, {'type': ChargerBotProShielded, 'spacing': 50},
{'type': spazbot.ChargerBotProShielded, 'spacing': 50}, {'type': ChargerBotProShielded, 'spacing': 50},
{'type': spazbot.ChargerBotProShielded, 'spacing': 50} {'type': ChargerBotProShielded, 'spacing': 50}
if self._preset == 'pro' else None, if self._preset == 'pro' else None,
{'type': spazbot.ChargerBotProShielded, 'spacing': 50} {'type': ChargerBotProShielded, 'spacing': 50}
if player_count > 1 else None, if player_count > 1 else None,
{'type': spazbot.ChargerBotProShielded, 'spacing': 50} {'type': ChargerBotProShielded, 'spacing': 50}
if player_count > 2 else None, if player_count > 2 else None,
]} ]}
] # yapf: disable ] # yapf: disable
@ -395,84 +398,84 @@ class OnslaughtGame(ba.CoopGameActivity[Player, Team]):
self._excludepowerups = [] self._excludepowerups = []
self._waves = [ self._waves = [
{'entries': [ {'entries': [
{'type': spazbot.BomberBotProStatic, {'type': BomberBotProStatic,
'point': 'turret_top_middle_left'} 'point': 'turret_top_middle_left'}
if hard else None, if hard else None,
{'type': spazbot.BomberBotProStatic, {'type': BomberBotProStatic,
'point': 'turret_top_middle_right'}, 'point': 'turret_top_middle_right'},
{'type': spazbot.BomberBotProStatic, {'type': BomberBotProStatic,
'point': 'turret_top_left'} 'point': 'turret_top_left'}
if player_count > 2 else None, if player_count > 2 else None,
{'type': spazbot.ExplodeyBot, 'point': 'top_right'}, {'type': ExplodeyBot, 'point': 'top_right'},
{'type': 'delay', 'duration': 4.0}, {'type': 'delay', 'duration': 4.0},
{'type': spazbot.ExplodeyBot, 'point': 'top_left'}, {'type': ExplodeyBot, 'point': 'top_left'},
]}, ]},
{'entries': [ {'entries': [
{'type': spazbot.ChargerBot, 'point': 'left'}, {'type': ChargerBot, 'point': 'left'},
{'type': spazbot.ChargerBot, 'point': 'right'}, {'type': ChargerBot, 'point': 'right'},
{'type': spazbot.ChargerBot, 'point': 'right_upper_more'} {'type': ChargerBot, 'point': 'right_upper_more'}
if player_count > 2 else None, if player_count > 2 else None,
{'type': spazbot.BomberBotProStatic, {'type': BomberBotProStatic,
'point': 'turret_top_left'}, 'point': 'turret_top_left'},
{'type': spazbot.BomberBotProStatic, {'type': BomberBotProStatic,
'point': 'turret_top_right'}, 'point': 'turret_top_right'},
]}, ]},
{'entries': [ {'entries': [
{'type': spazbot.TriggerBotPro, 'point': 'top_right'}, {'type': TriggerBotPro, 'point': 'top_right'},
{'type': spazbot.TriggerBotPro, {'type': TriggerBotPro,
'point': 'right_upper_more'} 'point': 'right_upper_more'}
if player_count > 1 else None, if player_count > 1 else None,
{'type': spazbot.TriggerBotPro, 'point': 'right_upper'}, {'type': TriggerBotPro, 'point': 'right_upper'},
{'type': spazbot.TriggerBotPro, 'point': 'right_lower'} {'type': TriggerBotPro, 'point': 'right_lower'}
if hard else None, if hard else None,
{'type': spazbot.TriggerBotPro, {'type': TriggerBotPro,
'point': 'right_lower_more'} 'point': 'right_lower_more'}
if player_count > 2 else None, if player_count > 2 else None,
{'type': spazbot.TriggerBotPro, 'point': 'bottom_right'}, {'type': TriggerBotPro, 'point': 'bottom_right'},
]}, ]},
{'entries': [ {'entries': [
{'type': spazbot.ChargerBotProShielded, {'type': ChargerBotProShielded,
'point': 'bottom_right'}, 'point': 'bottom_right'},
{'type': spazbot.ChargerBotProShielded, 'point': 'bottom'} {'type': ChargerBotProShielded, 'point': 'bottom'}
if player_count > 2 else None, if player_count > 2 else None,
{'type': spazbot.ChargerBotProShielded, {'type': ChargerBotProShielded,
'point': 'bottom_left'}, 'point': 'bottom_left'},
{'type': spazbot.ChargerBotProShielded, 'point': 'top'} {'type': ChargerBotProShielded, 'point': 'top'}
if hard else None, if hard else None,
{'type': spazbot.BomberBotProStatic, {'type': BomberBotProStatic,
'point': 'turret_top_middle'}, 'point': 'turret_top_middle'},
]}, ]},
{'entries': [ {'entries': [
{'type': spazbot.ExplodeyBot, 'point': 'left_upper'}, {'type': ExplodeyBot, 'point': 'left_upper'},
{'type': 'delay', 'duration': 1.0}, {'type': 'delay', 'duration': 1.0},
{'type': spazbot.BrawlerBotProShielded, {'type': BrawlerBotProShielded,
'point': 'left_lower'}, 'point': 'left_lower'},
{'type': spazbot.BrawlerBotProShielded, {'type': BrawlerBotProShielded,
'point': 'left_lower_more'}, 'point': 'left_lower_more'},
{'type': 'delay', 'duration': 4.0}, {'type': 'delay', 'duration': 4.0},
{'type': spazbot.ExplodeyBot, 'point': 'right_upper'}, {'type': ExplodeyBot, 'point': 'right_upper'},
{'type': 'delay', 'duration': 1.0}, {'type': 'delay', 'duration': 1.0},
{'type': spazbot.BrawlerBotProShielded, {'type': BrawlerBotProShielded,
'point': 'right_lower'}, 'point': 'right_lower'},
{'type': spazbot.BrawlerBotProShielded, {'type': BrawlerBotProShielded,
'point': 'right_upper_more'}, 'point': 'right_upper_more'},
{'type': 'delay', 'duration': 4.0}, {'type': 'delay', 'duration': 4.0},
{'type': spazbot.ExplodeyBot, 'point': 'left'}, {'type': ExplodeyBot, 'point': 'left'},
{'type': 'delay', 'duration': 5.0}, {'type': 'delay', 'duration': 5.0},
{'type': spazbot.ExplodeyBot, 'point': 'right'}, {'type': ExplodeyBot, 'point': 'right'},
]}, ]},
{'entries': [ {'entries': [
{'type': spazbot.BomberBotProStatic, {'type': BomberBotProStatic,
'point': 'turret_top_left'}, 'point': 'turret_top_left'},
{'type': spazbot.BomberBotProStatic, {'type': BomberBotProStatic,
'point': 'turret_top_right'}, 'point': 'turret_top_right'},
{'type': spazbot.BomberBotProStatic, {'type': BomberBotProStatic,
'point': 'turret_bottom_left'}, 'point': 'turret_bottom_left'},
{'type': spazbot.BomberBotProStatic, {'type': BomberBotProStatic,
'point': 'turret_bottom_right'}, 'point': 'turret_bottom_right'},
{'type': spazbot.BomberBotProStatic, {'type': BomberBotProStatic,
'point': 'turret_top_middle_left'} if hard else None, 'point': 'turret_top_middle_left'} if hard else None,
{'type': spazbot.BomberBotProStatic, {'type': BomberBotProStatic,
'point': 'turret_top_middle_right'} if hard else None, 'point': 'turret_top_middle_right'} if hard else None,
] ]
}] # yapf: disable }] # yapf: disable
@ -498,11 +501,11 @@ class OnslaughtGame(ba.CoopGameActivity[Player, Team]):
# Our TNT spawner (if applicable). # Our TNT spawner (if applicable).
if self._have_tnt: if self._have_tnt:
self._tntspawner = stdbomb.TNTSpawner(position=self._tntspawnpos) self._tntspawner = TNTSpawner(position=self._tntspawnpos)
self.setup_low_life_warning_sound() self.setup_low_life_warning_sound()
self._update_scores() self._update_scores()
self._bots = spazbot.BotSet() self._bots = BotSet()
ba.timer(4.0, self._start_updating_waves) ba.timer(4.0, self._start_updating_waves)
def _on_got_scores_to_beat(self, scores: List[Dict[str, Any]]) -> None: def _on_got_scores_to_beat(self, scores: List[Dict[str, Any]]) -> None:
@ -852,11 +855,10 @@ class OnslaughtGame(ba.CoopGameActivity[Player, Team]):
assert self._waves is not None assert self._waves is not None
if (not player.is_alive() and if (not player.is_alive() and
(self._preset in ['endless', 'endless_tournament'] or (self._preset in ['endless', 'endless_tournament'] or
(player.gamedata['respawn_wave'] <= len(self._waves)))): (player.respawn_wave <= len(self._waves)))):
rtxt = ba.Lstr(resource='onslaughtRespawnText', rtxt = ba.Lstr(resource='onslaughtRespawnText',
subs=[('${PLAYER}', player.get_name()), subs=[('${PLAYER}', player.get_name()),
('${WAVE}', ('${WAVE}', str(player.respawn_wave))
str(player.gamedata['respawn_wave']))
]) ])
text = ba.Lstr(value='${A}${B}\n', text = ba.Lstr(value='${A}${B}\n',
subs=[ subs=[
@ -881,7 +883,7 @@ class OnslaughtGame(ba.CoopGameActivity[Player, Team]):
if self._wave > 1 and not self.is_waiting_for_continue(): if self._wave > 1 and not self.is_waiting_for_continue():
for player in self.players: for player in self.players:
if (not player.is_alive() if (not player.is_alive()
and player.gamedata['respawn_wave'] == self._wave): and player.respawn_wave == self._wave):
self.spawn_player(player) self.spawn_player(player)
self._update_player_spawn_info() self._update_player_spawn_info()
self.show_zoom_message(ba.Lstr(value='${A} ${B}', self.show_zoom_message(ba.Lstr(value='${A} ${B}',
@ -907,40 +909,34 @@ class OnslaughtGame(ba.CoopGameActivity[Player, Team]):
if self._preset in ['endless', 'endless_tournament']: if self._preset in ['endless', 'endless_tournament']:
level = self._wave level = self._wave
bot_types2 = [ bot_types2 = [
spazbot.BomberBot, spazbot.BrawlerBot, spazbot.TriggerBot, BomberBot, BrawlerBot, TriggerBot, ChargerBot, BomberBotPro,
spazbot.ChargerBot, spazbot.BomberBotPro, BrawlerBotPro, TriggerBotPro, BomberBotProShielded,
spazbot.BrawlerBotPro, spazbot.TriggerBotPro, ExplodeyBot, ChargerBotProShielded, StickyBot,
spazbot.BomberBotProShielded, spazbot.ExplodeyBot, BrawlerBotProShielded, TriggerBotProShielded
spazbot.ChargerBotProShielded, spazbot.StickyBot,
spazbot.BrawlerBotProShielded, spazbot.TriggerBotProShielded
] ]
if level > 5: if level > 5:
bot_types2 += [ bot_types2 += [
spazbot.ExplodeyBot, ExplodeyBot,
spazbot.TriggerBotProShielded, TriggerBotProShielded,
spazbot.BrawlerBotProShielded, BrawlerBotProShielded,
spazbot.ChargerBotProShielded, ChargerBotProShielded,
] ]
if level > 7: if level > 7:
bot_types2 += [ bot_types2 += [
spazbot.ExplodeyBot, ExplodeyBot,
spazbot.TriggerBotProShielded, TriggerBotProShielded,
spazbot.BrawlerBotProShielded, BrawlerBotProShielded,
spazbot.ChargerBotProShielded, ChargerBotProShielded,
] ]
if level > 10: if level > 10:
bot_types2 += [ bot_types2 += [
spazbot.TriggerBotProShielded, TriggerBotProShielded, TriggerBotProShielded,
spazbot.TriggerBotProShielded, TriggerBotProShielded, TriggerBotProShielded
spazbot.TriggerBotProShielded,
spazbot.TriggerBotProShielded
] ]
if level > 13: if level > 13:
bot_types2 += [ bot_types2 += [
spazbot.TriggerBotProShielded, TriggerBotProShielded, TriggerBotProShielded,
spazbot.TriggerBotProShielded, TriggerBotProShielded, TriggerBotProShielded
spazbot.TriggerBotProShielded,
spazbot.TriggerBotProShielded
] ]
bot_levels = [[b for b in bot_types2 if b.points_mult == 1], bot_levels = [[b for b in bot_types2 if b.points_mult == 1],
@ -1096,7 +1092,7 @@ class OnslaughtGame(ba.CoopGameActivity[Player, Team]):
def add_bot_at_point(self, def add_bot_at_point(self,
point: str, point: str,
spaz_type: Type[spazbot.SpazBot], spaz_type: Type[SpazBot],
spawn_time: float = 1.0) -> None: spawn_time: float = 1.0) -> None:
"""Add a new bot at a specified named point.""" """Add a new bot at a specified named point."""
if self._game_over: if self._game_over:
@ -1107,7 +1103,7 @@ class OnslaughtGame(ba.CoopGameActivity[Player, Team]):
def add_bot_at_angle(self, def add_bot_at_angle(self,
angle: float, angle: float,
spaz_type: Type[spazbot.SpazBot], spaz_type: Type[SpazBot],
spawn_time: float = 1.0) -> None: spawn_time: float = 1.0) -> None:
"""Add a new bot at a specified angle (for circular maps).""" """Add a new bot at a specified angle (for circular maps)."""
if self._game_over: if self._game_over:
@ -1153,11 +1149,9 @@ class OnslaughtGame(ba.CoopGameActivity[Player, Team]):
# pylint: disable=too-many-statements # pylint: disable=too-many-statements
# pylint: disable=too-many-branches # pylint: disable=too-many-branches
if isinstance(msg, playerspaz.PlayerSpazHurtMessage): if isinstance(msg, PlayerSpazHurtMessage):
player = msg.spaz.getplayer() player = msg.spaz.getplayer(Player, doraise=True)
if not player: player.has_been_hurt = True
return
player.gamedata['has_been_hurt'] = True
self._a_player_has_been_hurt = True self._a_player_has_been_hurt = True
elif isinstance(msg, ba.PlayerScoredMessage): elif isinstance(msg, ba.PlayerScoredMessage):
@ -1171,15 +1165,15 @@ class OnslaughtGame(ba.CoopGameActivity[Player, Team]):
# Make note with the player when they can respawn: # Make note with the player when they can respawn:
if self._wave < 10: if self._wave < 10:
player.gamedata['respawn_wave'] = max(2, self._wave + 1) player.respawn_wave = max(2, self._wave + 1)
elif self._wave < 15: elif self._wave < 15:
player.gamedata['respawn_wave'] = max(2, self._wave + 2) player.respawn_wave = max(2, self._wave + 2)
else: else:
player.gamedata['respawn_wave'] = max(2, self._wave + 3) player.respawn_wave = max(2, self._wave + 3)
ba.timer(0.1, self._update_player_spawn_info) ba.timer(0.1, self._update_player_spawn_info)
ba.timer(0.1, self._checkroundover) ba.timer(0.1, self._checkroundover)
elif isinstance(msg, spazbot.SpazBotDeathMessage): elif isinstance(msg, SpazBotDeathMessage):
pts, importance = msg.badguy.get_death_points(msg.how) pts, importance = msg.badguy.get_death_points(msg.how)
if msg.killerplayer is not None: if msg.killerplayer is not None:

View File

@ -32,6 +32,7 @@ from dataclasses import dataclass
import ba import ba
from bastd.actor.bomb import Bomb from bastd.actor.bomb import Bomb
from bastd.actor.playerspaz import PlayerSpaz from bastd.actor.playerspaz import PlayerSpaz
from bastd.actor.scoreboard import Scoreboard
if TYPE_CHECKING: if TYPE_CHECKING:
from typing import (Any, Type, Tuple, List, Sequence, Optional, Dict, from typing import (Any, Type, Tuple, List, Sequence, Optional, Dict,
@ -143,7 +144,6 @@ class RaceGame(ba.TeamGameActivity[Player, Team]):
return ba.getmaps('race') return ba.getmaps('race')
def __init__(self, settings: Dict[str, Any]): def __init__(self, settings: Dict[str, Any]):
from bastd.actor.scoreboard import Scoreboard
self._race_started = False self._race_started = False
super().__init__(settings) super().__init__(settings)
self._scoreboard = Scoreboard() self._scoreboard = Scoreboard()

View File

@ -29,11 +29,14 @@ import random
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
import ba import ba
from bastd.actor.scoreboard import Scoreboard
from bastd.actor.onscreencountdown import OnScreenCountdown
from bastd.actor.bomb import Bomb
from bastd.actor.popuptext import PopupText
if TYPE_CHECKING: if TYPE_CHECKING:
from typing import Any, Type, List, Dict, Optional, Sequence from typing import Any, Type, List, Dict, Optional, Sequence
from bastd.actor.onscreencountdown import OnScreenCountdown from bastd.actor.bomb import Blast
from bastd.actor.bomb import Bomb, Blast
class Player(ba.Player['Team']): class Player(ba.Player['Team']):
@ -80,7 +83,6 @@ class TargetPracticeGame(ba.TeamGameActivity[Player, Team]):
or issubclass(sessiontype, ba.MultiTeamSession)) or issubclass(sessiontype, ba.MultiTeamSession))
def __init__(self, settings: Dict[str, Any]): def __init__(self, settings: Dict[str, Any]):
from bastd.actor.scoreboard import Scoreboard
super().__init__(settings) super().__init__(settings)
self._scoreboard = Scoreboard() self._scoreboard = Scoreboard()
self._targets: List[Target] = [] self._targets: List[Target] = []
@ -98,7 +100,6 @@ class TargetPracticeGame(ba.TeamGameActivity[Player, Team]):
self.update_scoreboard() self.update_scoreboard()
def on_begin(self) -> None: def on_begin(self) -> None:
from bastd.actor.onscreencountdown import OnScreenCountdown
super().on_begin() super().on_begin()
self.update_scoreboard() self.update_scoreboard()
@ -141,9 +142,9 @@ class TargetPracticeGame(ba.TeamGameActivity[Player, Team]):
ypos = random.uniform(-1.0, 1.0) ypos = random.uniform(-1.0, 1.0)
if xpos * xpos + ypos * ypos < 1.0: if xpos * xpos + ypos * ypos < 1.0:
break break
points.append((8.0 * xpos, 2.2, -3.5 + 5.0 * ypos)) points.append(ba.Vec3(8.0 * xpos, 2.2, -3.5 + 5.0 * ypos))
def get_min_dist_from_target(pnt: Sequence[float]) -> float: def get_min_dist_from_target(pnt: ba.Vec3) -> float:
return min((t.get_dist_from_point(pnt) for t in self._targets)) return min((t.get_dist_from_point(pnt) for t in self._targets))
# If we have existing targets, use the point with the highest # If we have existing targets, use the point with the highest
@ -157,7 +158,6 @@ class TargetPracticeGame(ba.TeamGameActivity[Player, Team]):
def _on_spaz_dropped_bomb(self, spaz: ba.Actor, bomb: ba.Actor) -> None: def _on_spaz_dropped_bomb(self, spaz: ba.Actor, bomb: ba.Actor) -> None:
del spaz # Unused. del spaz # Unused.
from bastd.actor.bomb import Bomb
# Wire up this bomb to inform us when it blows up. # Wire up this bomb to inform us when it blows up.
assert isinstance(bomb, Bomb) assert isinstance(bomb, Bomb)
@ -281,14 +281,13 @@ class Target(ba.Actor):
else: else:
super().handlemessage(msg) super().handlemessage(msg)
def get_dist_from_point(self, pos: Sequence[float]) -> float: def get_dist_from_point(self, pos: ba.Vec3) -> float:
"""Given a point, returns distance squared from it.""" """Given a point, returns distance squared from it."""
return (ba.Vec3(pos) - self._position).length() return (pos - self._position).length()
def do_hit_at_position(self, pos: Sequence[float], player: Player) -> bool: def do_hit_at_position(self, pos: Sequence[float], player: Player) -> bool:
"""Handle a bomb hit at the given position.""" """Handle a bomb hit at the given position."""
# pylint: disable=too-many-statements # pylint: disable=too-many-statements
from bastd.actor import popuptext
activity = self.activity activity = self.activity
# Ignore hits if the game is over or if we've already been hit # Ignore hits if the game is over or if we've already been hit
@ -357,10 +356,10 @@ class Target(ba.Actor):
if len(activity.players) > 1: if len(activity.players) > 1:
popupcolor = ba.safecolor(player.color, target_intensity=0.75) popupcolor = ba.safecolor(player.color, target_intensity=0.75)
popupstr += ' ' + player.get_name() popupstr += ' ' + player.get_name()
popuptext.PopupText(popupstr, PopupText(popupstr,
position=self._position, position=self._position,
color=popupcolor, color=popupcolor,
scale=popupscale).autoretain() scale=popupscale).autoretain()
# Give this player's team points and update the score-board. # Give this player's team points and update the score-board.
player.team.score += points player.team.score += points

View File

@ -26,13 +26,13 @@ import random
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
import ba import ba
from bastd.actor import playerspaz
from bastd.actor import spazbot from bastd.actor import spazbot
from bastd.actor.playerspaz import PlayerSpaz
from bastd.actor.bomb import TNTSpawner from bastd.actor.bomb import TNTSpawner
from bastd.actor.scoreboard import Scoreboard
if TYPE_CHECKING: if TYPE_CHECKING:
from typing import Any, Dict, Type, List, Optional, Sequence from typing import Any, Dict, Type, List, Optional, Sequence
from bastd.actor.scoreboard import Scoreboard
class Player(ba.Player['Team']): class Player(ba.Player['Team']):
@ -59,6 +59,8 @@ class TheLastStandGame(ba.CoopGameActivity[Player, Team]):
# And of course the most important part. # And of course the most important part.
slow_motion = True slow_motion = True
default_music = ba.MusicType.EPIC
def __init__(self, settings: Dict[str, Any]): def __init__(self, settings: Dict[str, Any]):
settings['map'] = 'Rampage' settings['map'] = 'Rampage'
super().__init__(settings) super().__init__(settings)
@ -98,8 +100,6 @@ class TheLastStandGame(ba.CoopGameActivity[Player, Team]):
} # yapf: disable } # yapf: disable
def on_transition_in(self) -> None: def on_transition_in(self) -> None:
from bastd.actor.scoreboard import Scoreboard
self.default_music = ba.MusicType.EPIC
super().on_transition_in() super().on_transition_in()
ba.timer(1.3, ba.Call(ba.playsound, self._new_wave_sound)) ba.timer(1.3, ba.Call(ba.playsound, self._new_wave_sound))
self._scoreboard = Scoreboard(label=ba.Lstr(resource='scoreText'), self._scoreboard = Scoreboard(label=ba.Lstr(resource='scoreText'),
@ -198,11 +198,11 @@ class TheLastStandGame(ba.CoopGameActivity[Player, Team]):
for player in self.players: for player in self.players:
try: try:
if player.is_alive(): if player.is_alive():
assert isinstance(player.actor, playerspaz.PlayerSpaz) assert isinstance(player.actor, PlayerSpaz)
assert player.actor.node assert player.actor.node
playerpts.append(player.actor.node.position) playerpts.append(player.actor.node.position)
except Exception as exc: except Exception:
print('ERROR in _update_bots', exc) ba.print_exception('Error updating bots')
for i in range(3): for i in range(3):
for playerpt in playerpts: for playerpt in playerpts:
dists[i] += abs(playerpt[0] - botspawnpts[i][0]) dists[i] += abs(playerpt[0] - botspawnpts[i][0])
@ -244,7 +244,7 @@ class TheLastStandGame(ba.CoopGameActivity[Player, Team]):
spawntype[1][1] += spawntype[1][2] # incr spawn rate incr rate spawntype[1][1] += spawntype[1][2] # incr spawn rate incr rate
def _update_scores(self) -> None: def _update_scores(self) -> None:
# Achievements in default preset only. # Do achievements in default preset only.
score = self._score score = self._score
if self._preset == 'default': if self._preset == 'default':
if score >= 250: if score >= 250:
@ -298,7 +298,6 @@ class TheLastStandGame(ba.CoopGameActivity[Player, Team]):
super().handlemessage(msg) super().handlemessage(msg)
def _on_got_scores_to_beat(self, scores: List[Dict[str, Any]]) -> None: def _on_got_scores_to_beat(self, scores: List[Dict[str, Any]]) -> None:
# FIXME: Unify args.
self._show_standard_scores_to_beat_ui(scores) self._show_standard_scores_to_beat_ui(scores)
def end_game(self) -> None: def end_game(self) -> None:

View File

@ -1,5 +1,5 @@
<!-- THIS FILE IS AUTO GENERATED; DO NOT EDIT BY HAND --> <!-- THIS FILE IS AUTO GENERATED; DO NOT EDIT BY HAND -->
<h4><em>last updated on 2020-05-22 for Ballistica version 1.5.0 build 20026</em></h4> <h4><em>last updated on 2020-05-23 for Ballistica version 1.5.0 build 20026</em></h4>
<p>This page documents the Python classes and functions in the 'ba' module, <p>This page documents the Python classes and functions in the 'ba' module,
which are the ones most relevant to modding in Ballistica. If you come across something you feel should be included here or could be better explained, please <a href="mailto:support@froemling.net">let me know</a>. Happy modding!</p> which are the ones most relevant to modding in Ballistica. If you come across something you feel should be included here or could be better explained, please <a href="mailto:support@froemling.net">let me know</a>. Happy modding!</p>
<hr> <hr>
@ -2450,7 +2450,7 @@ and short description of the game.</p>
</dd> </dd>
<dt><h4><a name="method_ba_GameActivity__spawn_player_spaz">spawn_player_spaz()</a></dt></h4><dd> <dt><h4><a name="method_ba_GameActivity__spawn_player_spaz">spawn_player_spaz()</a></dt></h4><dd>
<p><span>spawn_player_spaz(self, player: PlayerType, position: Sequence[float] = (0, 0, 0), angle: float = None) -&gt; PlayerSpaz[PlayerType]</span></p> <p><span>spawn_player_spaz(self, player: PlayerType, position: Sequence[float] = (0, 0, 0), angle: float = None) -&gt; PlayerSpaz</span></p>
<p>Create and wire up a ba.PlayerSpaz for the provided <a href="#class_ba_Player">ba.Player</a>.</p> <p>Create and wire up a ba.PlayerSpaz for the provided <a href="#class_ba_Player">ba.Player</a>.</p>
@ -4975,7 +4975,7 @@ up until <a href="#method_ba_Activity__on_begin">ba.Activity.on_begin</a>() is c
</dd> </dd>
<dt><h4><a name="method_ba_TeamGameActivity__spawn_player_spaz">spawn_player_spaz()</a></dt></h4><dd> <dt><h4><a name="method_ba_TeamGameActivity__spawn_player_spaz">spawn_player_spaz()</a></dt></h4><dd>
<p><span>spawn_player_spaz(self, player: PlayerType, position: Sequence[float] = None, angle: float = None) -&gt; PlayerSpaz[PlayerType]</span></p> <p><span>spawn_player_spaz(self, player: PlayerType, position: Sequence[float] = None, angle: float = None) -&gt; PlayerSpaz</span></p>
<p>Method override; spawns and wires up a standard ba.PlayerSpaz for <p>Method override; spawns and wires up a standard ba.PlayerSpaz for
a <a href="#class_ba_Player">ba.Player</a>.</p> a <a href="#class_ba_Player">ba.Player</a>.</p>