diff --git a/assets/src/ba_data/python/ba/_activity.py b/assets/src/ba_data/python/ba/_activity.py
index 87bbca24..4fe4b7b1 100644
--- a/assets/src/ba_data/python/ba/_activity.py
+++ b/assets/src/ba_data/python/ba/_activity.py
@@ -698,7 +698,6 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
'ba.Activity.on_begin() never got called for ' + str(self) +
'; did you forget to call it in your on_begin override?')
- # noinspection PyUnresolvedReferences
def _setup_player_and_team_types(self) -> None:
"""Pull player and team types from our typing.Generic params."""
diff --git a/assets/src/ba_data/python/ba/_gameutils.py b/assets/src/ba_data/python/ba/_gameutils.py
index acc8969a..214c837a 100644
--- a/assets/src/ba_data/python/ba/_gameutils.py
+++ b/assets/src/ba_data/python/ba/_gameutils.py
@@ -197,7 +197,6 @@ def animate(node: ba.Node,
if _ba.app.test_build and not suppress_format_warning:
for item in items:
# (PyCharm seems to think item is a float, not a tuple)
- # noinspection PyUnresolvedReferences
_ba.time_format_check(timeformat, item[0])
curve = _ba.newnode('animcurve',
@@ -223,7 +222,6 @@ def animate(node: ba.Node,
# get disconnected.
if not loop:
# (PyCharm seems to think item is a float, not a tuple)
- # noinspection PyUnresolvedReferences
_ba.timer(int(mult * items[-1][0]) + 1000,
curve.delete,
timeformat=TimeFormat.MILLISECONDS)
@@ -264,7 +262,6 @@ def animate_array(node: ba.Node,
if _ba.app.test_build and not suppress_format_warning:
for item in items:
# (PyCharm seems to think item is a float, not a tuple)
- # noinspection PyUnresolvedReferences
_ba.time_format_check(timeformat, item[0])
if timeformat is TimeFormat.SECONDS:
@@ -291,7 +288,6 @@ def animate_array(node: ba.Node,
# curve after its done its job.
if not loop:
# (PyCharm seems to think item is a float, not a tuple)
- # noinspection PyUnresolvedReferences
_ba.timer(int(mult * items[-1][0]) + 1000,
curve.delete,
timeformat=TimeFormat.MILLISECONDS)
@@ -303,7 +299,6 @@ def animate_array(node: ba.Node,
# once we get disconnected.
if not loop:
# (PyCharm seems to think item is a float, not a tuple)
- # noinspection PyUnresolvedReferences
_ba.timer(int(mult * items[-1][0]) + 1000,
combine.delete,
timeformat=TimeFormat.MILLISECONDS)
diff --git a/assets/src/ba_data/python/ba/_netutils.py b/assets/src/ba_data/python/ba/_netutils.py
index afeffd15..4087874b 100644
--- a/assets/src/ba_data/python/ba/_netutils.py
+++ b/assets/src/ba_data/python/ba/_netutils.py
@@ -114,7 +114,6 @@ class ServerCallThread(threading.Thread):
_ba.set_thread_name('BA_ServerCallThread')
# Seems pycharm doesn't know about urllib.parse.
- # noinspection PyUnresolvedReferences
parse = urllib.parse
if self._request_type == 'get':
response = urllib.request.urlopen(
diff --git a/assets/src/ba_data/python/ba/_powerup.py b/assets/src/ba_data/python/ba/_powerup.py
index 7dc69357..dbef03fb 100644
--- a/assets/src/ba_data/python/ba/_powerup.py
+++ b/assets/src/ba_data/python/ba/_powerup.py
@@ -31,7 +31,6 @@ if TYPE_CHECKING:
@dataclass
class PowerupMessage:
- # noinspection PyUnresolvedReferences
"""A message telling an object to accept a powerup.
Category: Message Classes
diff --git a/assets/src/ba_data/python/bastd/activity/multiteamscore.py b/assets/src/ba_data/python/bastd/activity/multiteamscore.py
index b58d43dd..8fd34a3e 100644
--- a/assets/src/ba_data/python/bastd/activity/multiteamscore.py
+++ b/assets/src/ba_data/python/bastd/activity/multiteamscore.py
@@ -115,8 +115,6 @@ class MultiTeamScoreScreenActivity(ScoreScreenActivity):
def _get_player_score_set_entry(
player: ba.SessionPlayer) -> Optional[ba.PlayerRecord]:
for p_rec in valid_players:
- # PyCharm incorrectly thinks valid_players is a List[str]
- # noinspection PyUnresolvedReferences
if p_rec[1].player is player:
return p_rec[1]
return None
diff --git a/assets/src/ba_data/python/bastd/actor/scoreboard.py b/assets/src/ba_data/python/bastd/actor/scoreboard.py
index 52ff65c8..6b3c2374 100644
--- a/assets/src/ba_data/python/bastd/actor/scoreboard.py
+++ b/assets/src/ba_data/python/bastd/actor/scoreboard.py
@@ -53,7 +53,7 @@ class _Entry:
self._flash_timer: Optional[ba.Timer] = None
self._flash_counter: Optional[int] = None
self._flash_colors: Optional[bool] = None
- self._score: Optional[int] = None
+ self._score: Optional[float] = None
safe_team_color = ba.safecolor(team.color, target_intensity=1.0)
@@ -258,8 +258,8 @@ class _Entry:
self._set_flash_colors(not self._flash_colors)
def set_value(self,
- score: int,
- max_score: int = None,
+ score: float,
+ max_score: float = None,
countdown: bool = False,
flash: bool = True,
show_value: bool = True) -> None:
@@ -360,8 +360,8 @@ class Scoreboard:
def set_team_value(self,
team: ba.Team,
- score: int,
- max_score: int = None,
+ score: float,
+ max_score: float = None,
countdown: bool = False,
flash: bool = True,
show_value: bool = True) -> None:
@@ -373,7 +373,8 @@ class Scoreboard:
if '_scoreboard_entry' in team.gamedata:
raise Exception('existing _EntryProxy found')
team.gamedata['_scoreboard_entry'] = _EntryProxy(self, team)
- # now set the entry..
+
+ # Now set the entry.
self._entries[team.id].set_value(score=score,
max_score=max_score,
countdown=countdown,
diff --git a/assets/src/ba_data/python/bastd/game/assault.py b/assets/src/ba_data/python/bastd/game/assault.py
index cf4821a8..9f532b4e 100644
--- a/assets/src/ba_data/python/bastd/game/assault.py
+++ b/assets/src/ba_data/python/bastd/game/assault.py
@@ -31,6 +31,7 @@ from typing import TYPE_CHECKING
import ba
from bastd.actor.playerspaz import PlayerSpaz
from bastd.actor.flag import Flag
+from bastd.actor.scoreboard import Scoreboard
if TYPE_CHECKING:
from typing import Any, Type, List, Dict, Sequence, Union
@@ -85,7 +86,6 @@ class AssaultGame(ba.TeamGameActivity[Player, Team]):
return ba.getmaps('team_flag')
def __init__(self, settings: Dict[str, Any]):
- from bastd.actor.scoreboard import Scoreboard
super().__init__(settings)
self._scoreboard = Scoreboard()
self._last_score_time = 0.0
diff --git a/assets/src/ba_data/python/bastd/game/capturetheflag.py b/assets/src/ba_data/python/bastd/game/capturetheflag.py
index f2f6e540..84dc7276 100644
--- a/assets/src/ba_data/python/bastd/game/capturetheflag.py
+++ b/assets/src/ba_data/python/bastd/game/capturetheflag.py
@@ -62,8 +62,7 @@ class CTFFlag(stdflag.Flag):
def reset_return_times(self) -> None:
"""Clear flag related times in the activity."""
- self.time_out_respawn_time = int(
- self.activity.settings_raw['Flag Idle Return Time'])
+ self.time_out_respawn_time = int(self.activity.flag_idle_return_time)
self.touch_return_time = float(self.activity.flag_touch_return_time)
@property
@@ -167,21 +166,17 @@ class CaptureTheFlagGame(ba.TeamGameActivity[Player, Team]):
self._all_bases_material = ba.Material()
self._last_home_flag_notice_print_time = 0.0
self._score_to_win = int(settings['Score to Win'])
- self._flag_touch_return_time = float(
- settings['Flag Touch Return Time'])
self._epic_mode = bool(settings['Epic Mode'])
self._time_limit = float(settings['Time Limit'])
+ self.flag_touch_return_time = float(settings['Flag Touch Return Time'])
+ self.flag_idle_return_time = float(settings['Flag Idle return Time'])
+
# Base class overrides
self.slow_motion = self._epic_mode
self.default_music = (ba.MusicType.EPIC if self._epic_mode else
ba.MusicType.FLAG_CATCHER)
- @property
- def flag_touch_return_time(self) -> float:
- """How long a flag must be touched for to return it to base."""
- return self._flag_touch_return_time
-
def get_instance_description(self) -> Union[str, Sequence]:
if self._score_to_win == 1:
return 'Steal the enemy flag.'
@@ -440,7 +435,7 @@ class CaptureTheFlagGame(ba.TeamGameActivity[Player, Team]):
def _award_players_touching_own_flag(self, team: Team) -> None:
for player in team.players:
if player.touching_own_flag > 0:
- return_score = 10 + 5 * int(self._flag_touch_return_time)
+ return_score = 10 + 5 * int(self.flag_touch_return_time)
self.stats.player_scored(player,
return_score,
screenmessage=False)
@@ -468,7 +463,7 @@ class CaptureTheFlagGame(ba.TeamGameActivity[Player, Team]):
# If return-time is zero, just kill it immediately.. otherwise keep
# track of touches and count down.
- if float(self._flag_touch_return_time) <= 0.0:
+ if float(self.flag_touch_return_time) <= 0.0:
assert team.flag is not None
if not team.home_flag_at_base and team.flag.held_count == 0:
diff --git a/assets/src/ba_data/python/bastd/game/conquest.py b/assets/src/ba_data/python/bastd/game/conquest.py
index 11430f35..7c76c8e3 100644
--- a/assets/src/ba_data/python/bastd/game/conquest.py
+++ b/assets/src/ba_data/python/bastd/game/conquest.py
@@ -33,6 +33,7 @@ from bastd.actor.flag import Flag
if TYPE_CHECKING:
from typing import Any, Optional, Type, List, Dict, Sequence, Union
+ from bastd.actor.respawnicon import RespawnIcon
class ConquestFlag(Flag):
@@ -57,10 +58,31 @@ class ConquestFlag(Flag):
class Player(ba.Player['Team']):
"""Our player type for this game."""
+ @property
+ def respawn_timer(self) -> Optional[ba.Timer]:
+ """Type safe access to standard respawn timer."""
+ return self.gamedata.get('respawn_timer', None)
+
+ @respawn_timer.setter
+ def respawn_timer(self, value: Optional[ba.Timer]) -> None:
+ self.gamedata['respawn_timer'] = value
+
+ @property
+ def respawn_icon(self) -> Optional[RespawnIcon]:
+ """Type safe access to standard respawn icon."""
+ return self.gamedata.get('respawn_icon', None)
+
+ @respawn_icon.setter
+ def respawn_icon(self, value: Optional[RespawnIcon]) -> None:
+ self.gamedata['respawn_icon'] = value
+
class Team(ba.Team[Player]):
"""Our team type for this game."""
+ def __init__(self) -> None:
+ self.flags_held = 0
+
# ba_meta export game
class ConquestGame(ba.TeamGameActivity[Player, Team]):
@@ -96,13 +118,18 @@ class ConquestGame(ba.TeamGameActivity[Player, Team]):
def __init__(self, settings: Dict[str, Any]):
from bastd.actor.scoreboard import Scoreboard
super().__init__(settings)
- if self.settings_raw['Epic Mode']:
- self.slow_motion = True
self._scoreboard = Scoreboard()
self._score_sound = ba.getsound('score')
self._swipsound = ba.getsound('swip')
self._extraflagmat = ba.Material()
self._flags: List[ConquestFlag] = []
+ self._epic_mode = bool(settings['Epic Mode'])
+ self._time_limit = float(settings['Time Limit'])
+
+ # Base class overrides.
+ self.slow_motion = self._epic_mode
+ self.default_music = (ba.MusicType.EPIC
+ if self._epic_mode else ba.MusicType.GRAND_ROMP)
# We want flags to tell us they've been hit but not react physically.
self._extraflagmat.add_actions(
@@ -116,27 +143,20 @@ class ConquestGame(ba.TeamGameActivity[Player, Team]):
def get_instance_description_short(self) -> Union[str, Sequence]:
return 'secure all ${ARG1} flags', len(self.map.flag_points)
- def on_transition_in(self) -> None:
- self.default_music = (ba.MusicType.EPIC
- if self.settings_raw['Epic Mode'] else
- ba.MusicType.GRAND_ROMP)
- super().on_transition_in()
-
def on_team_join(self, team: Team) -> None:
if self.has_begun():
self._update_scores()
- team.gamedata['flags_held'] = 0
def on_player_join(self, player: Player) -> None:
- player.gamedata['respawn_timer'] = None
+ player.respawn_timer = None
# Only spawn if this player's team has a flag currently.
- if player.team.gamedata['flags_held'] > 0:
+ if player.team.flags_held > 0:
self.spawn_player(player)
def on_begin(self) -> None:
super().on_begin()
- self.setup_standard_time_limit(self.settings_raw['Time Limit'])
+ self.setup_standard_time_limit(self._time_limit)
self.setup_standard_powerup_drops()
# Set up flags with marker lights.
@@ -177,27 +197,27 @@ class ConquestGame(ba.TeamGameActivity[Player, Team]):
def _update_scores(self) -> None:
for team in self.teams:
- team.gamedata['flags_held'] = 0
+ team.flags_held = 0
for flag in self._flags:
if flag.team is not None:
- flag.team.gamedata['flags_held'] += 1
+ flag.team.flags_held += 1
for team in self.teams:
# If a team finds themselves with no flags, cancel all
# outstanding spawn-timers.
- if team.gamedata['flags_held'] == 0:
+ if team.flags_held == 0:
for player in team.players:
- player.gamedata['respawn_timer'] = None
- player.gamedata['respawn_icon'] = None
- if team.gamedata['flags_held'] == len(self._flags):
+ player.respawn_timer = None
+ player.respawn_icon = None
+ if team.flags_held == len(self._flags):
self.end_game()
- self._scoreboard.set_team_value(team, team.gamedata['flags_held'],
+ self._scoreboard.set_team_value(team, team.flags_held,
len(self._flags))
def end_game(self) -> None:
results = ba.TeamGameResults()
for team in self.teams:
- results.set_team_score(team, team.gamedata['flags_held'])
+ results.set_team_score(team, team.flags_held)
self.end(results=results)
def _flash_flag(self, flag: ConquestFlag, length: float = 1.0) -> None:
@@ -239,7 +259,7 @@ class ConquestGame(ba.TeamGameActivity[Player, Team]):
if (otherplayer.team is flag.team
and otherplayer.actor is not None
and not otherplayer.is_alive()
- and otherplayer.gamedata['respawn_timer'] is None):
+ and otherplayer.respawn_timer is None):
self.spawn_player(otherplayer)
def handlemessage(self, msg: Any) -> Any:
@@ -249,10 +269,10 @@ class ConquestGame(ba.TeamGameActivity[Player, Team]):
# Respawn only if this team has a flag.
player = msg.getplayer(Player)
- if player.team.gamedata['flags_held'] > 0:
+ if player.team.flags_held > 0:
self.respawn_player(player)
else:
- player.gamedata['respawn_timer'] = None
+ player.respawn_timer = None
else:
super().handlemessage(msg)
diff --git a/assets/src/ba_data/python/bastd/game/easteregghunt.py b/assets/src/ba_data/python/bastd/game/easteregghunt.py
index 96ea4ef9..48f29b64 100644
--- a/assets/src/ba_data/python/bastd/game/easteregghunt.py
+++ b/assets/src/ba_data/python/bastd/game/easteregghunt.py
@@ -34,6 +34,7 @@ from bastd.actor.playerspaz import PlayerSpaz
from bastd.actor.spazbot import BotSet, BouncyBot, SpazBotDeathMessage
from bastd.actor.onscreencountdown import OnScreenCountdown
from bastd.actor.scoreboard import Scoreboard
+from bastd.actor.respawnicon import RespawnIcon
if TYPE_CHECKING:
from typing import Any, Type, Dict, List, Tuple, Optional
@@ -42,10 +43,17 @@ if TYPE_CHECKING:
class Player(ba.Player['Team']):
"""Our player type for this game."""
+ def __init__(self) -> None:
+ self.respawn_timer: Optional[ba.Timer] = None
+ self.respawn_icon: Optional[RespawnIcon] = None
+
class Team(ba.Team[Player]):
"""Our team type for this game."""
+ def __init__(self) -> None:
+ self.score = 0
+
# ba_meta export game
class EasterEggHuntGame(ba.TeamGameActivity[Player, Team]):
@@ -88,15 +96,10 @@ class EasterEggHuntGame(ba.TeamGameActivity[Player, Team]):
self._countdown: Optional[OnScreenCountdown] = None
self._bots: Optional[BotSet] = None
- # Called when our game is transitioning in but not ready to start.
- # ..we can go ahead and set our music and whatnot.
-
- def on_transition_in(self) -> None:
+ # Base class overrides
self.default_music = ba.MusicType.FORWARD_MARCH
- super().on_transition_in()
def on_team_join(self, team: Team) -> None:
- team.gamedata['score'] = 0
if self.has_begun():
self._update_scoreboard()
@@ -142,7 +145,7 @@ class EasterEggHuntGame(ba.TeamGameActivity[Player, Team]):
player = (spaz.getplayer()
if hasattr(spaz, 'getplayer') else None)
if player and egg:
- player.team.gamedata['score'] += 1
+ player.team.score += 1
# Displays a +1 (and adds to individual player score in
# teams mode).
@@ -201,7 +204,6 @@ class EasterEggHuntGame(ba.TeamGameActivity[Player, Team]):
# Respawn dead players.
if isinstance(msg, ba.PlayerDiedMessage):
- from bastd.actor import respawnicon
# Augment standard behavior.
super().handlemessage(msg)
@@ -213,10 +215,9 @@ class EasterEggHuntGame(ba.TeamGameActivity[Player, Team]):
# Respawn them shortly.
assert self.initial_player_info is not None
respawn_time = 2.0 + len(self.initial_player_info) * 1.0
- player.gamedata['respawn_timer'] = ba.Timer(
+ player.respawn_timer = ba.Timer(
respawn_time, ba.Call(self.spawn_player_if_exists, player))
- player.gamedata['respawn_icon'] = respawnicon.RespawnIcon(
- player, respawn_time)
+ player.respawn_icon = RespawnIcon(player, respawn_time)
# Whenever our evil bunny dies, respawn him and spew some eggs.
elif isinstance(msg, SpazBotDeathMessage):
@@ -235,12 +236,12 @@ class EasterEggHuntGame(ba.TeamGameActivity[Player, Team]):
def _update_scoreboard(self) -> None:
for team in self.teams:
- self._scoreboard.set_team_value(team, team.gamedata['score'])
+ self._scoreboard.set_team_value(team, team.score)
def end_game(self) -> None:
results = ba.TeamGameResults()
for team in self.teams:
- results.set_team_score(team, team.gamedata['score'])
+ results.set_team_score(team, team.score)
self.end(results)
diff --git a/assets/src/ba_data/python/bastd/game/elimination.py b/assets/src/ba_data/python/bastd/game/elimination.py
index b38eff49..cf8eb951 100644
--- a/assets/src/ba_data/python/bastd/game/elimination.py
+++ b/assets/src/ba_data/python/bastd/game/elimination.py
@@ -161,6 +161,12 @@ class Icon(ba.Actor):
if lives == 0:
ba.timer(0.6, self.update_for_lives)
+ def handlemessage(self, msg: Any) -> Any:
+ if isinstance(msg, ba.DieMessage):
+ self.node.delete()
+ return None
+ return super().handlemessage(msg)
+
class Player(ba.Player['Team']):
"""Our player type for this game."""
diff --git a/assets/src/ba_data/python/bastd/game/football.py b/assets/src/ba_data/python/bastd/game/football.py
index bfb77ec1..7e9dbca5 100644
--- a/assets/src/ba_data/python/bastd/game/football.py
+++ b/assets/src/ba_data/python/bastd/game/football.py
@@ -69,10 +69,17 @@ class FootballFlag(stdflag.Flag):
class Player(ba.Player['Team']):
"""Our player type for this game."""
+ def __init__(self) -> None:
+ self.respawn_timer: Optional[ba.Timer] = None
+ self.respawn_icon: Optional[RespawnIcon] = None
+
class Team(ba.Team[Player]):
"""Our team type for this game."""
+ def __init__(self) -> None:
+ self.score = 0
+
# ba_meta export game
class FootballTeamGame(ba.TeamGameActivity[Player, Team]):
@@ -130,11 +137,13 @@ class FootballTeamGame(ba.TeamGameActivity[Player, Team]):
self._flag: Optional[FootballFlag] = None
self._flag_respawn_timer: Optional[ba.Timer] = None
self._flag_respawn_light: Optional[ba.NodeActor] = None
+ self._score_to_win = int(settings['Score to Win'])
+ self._time_limit = float(settings['Time Limit'])
def get_instance_description(self) -> Union[str, Sequence]:
- touchdowns = self.settings_raw['Score to Win'] / 7
+ touchdowns = self._score_to_win / 7
- # NOTE: if use just touchdowns = self.settings_raw['Score to Win'] // 7
+ # NOTE: if use just touchdowns = self._score_to_win // 7
# and we will need to score, for example, 27 points,
# we will be required to score 3 (not 4) goals ..
touchdowns = math.ceil(touchdowns)
@@ -143,7 +152,7 @@ class FootballTeamGame(ba.TeamGameActivity[Player, Team]):
return 'Score a touchdown.'
def get_instance_description_short(self) -> Union[str, Sequence]:
- touchdowns = self.settings_raw['Score to Win'] / 7
+ touchdowns = self._score_to_win / 7
touchdowns = math.ceil(touchdowns)
if touchdowns > 1:
return 'score ${ARG1} touchdowns', touchdowns
@@ -155,7 +164,7 @@ class FootballTeamGame(ba.TeamGameActivity[Player, Team]):
def on_begin(self) -> None:
super().on_begin()
- self.setup_standard_time_limit(self.settings_raw['Time Limit'])
+ self.setup_standard_time_limit(self._time_limit)
self.setup_standard_powerup_drops()
self._flag_spawn_pos = (self.map.get_flag_position(None))
self._spawn_flag()
@@ -182,7 +191,6 @@ class FootballTeamGame(ba.TeamGameActivity[Player, Team]):
ba.playsound(self._chant_sound)
def on_team_join(self, team: Team) -> None:
- team.gamedata['score'] = 0
self._update_scoreboard()
def _kill_flag(self) -> None:
@@ -203,7 +211,7 @@ class FootballTeamGame(ba.TeamGameActivity[Player, Team]):
break
for team in self.teams:
if team.id == i:
- team.gamedata['score'] += 7
+ team.score += 7
# Tell all players to celebrate.
for player in team.players:
@@ -218,8 +226,8 @@ class FootballTeamGame(ba.TeamGameActivity[Player, Team]):
self.stats.player_scored(self._flag.last_holding_player,
50,
big_message=True)
- # end game if we won
- if team.gamedata['score'] >= self.settings_raw['Score to Win']:
+ # End the game if we won.
+ if team.score >= self._score_to_win:
self.end_game()
ba.playsound(self._score_sound)
ba.playsound(self._cheer_sound)
@@ -242,15 +250,14 @@ class FootballTeamGame(ba.TeamGameActivity[Player, Team]):
def end_game(self) -> None:
results = ba.TeamGameResults()
for team in self.teams:
- results.set_team_score(team, team.gamedata['score'])
+ results.set_team_score(team, team.score)
self.end(results=results, announce_delay=0.8)
def _update_scoreboard(self) -> None:
- win_score = self.settings_raw['Score to Win']
assert self._scoreboard is not None
for team in self.teams:
- self._scoreboard.set_team_value(team, team.gamedata['score'],
- win_score)
+ self._scoreboard.set_team_value(team, team.score,
+ self._score_to_win)
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, stdflag.FlagPickedUpMessage):
@@ -348,7 +355,7 @@ class FootballCoopGame(ba.CoopGameActivity[Player, Team]):
def __init__(self, settings: Dict[str, Any]):
settings['map'] = 'Football Stadium'
super().__init__(settings)
- self._preset = self.settings_raw.get('preset', 'rookie')
+ self._preset = settings.get('preset', 'rookie')
# Load some media we need.
self._cheer_sound = ba.getsound('cheer')
@@ -506,7 +513,7 @@ class FootballCoopGame(ba.CoopGameActivity[Player, Team]):
color=(0.5, 0.4, 0.4))
for team in [self.teams[0], self._bot_team]:
- team.gamedata['score'] = 0
+ team.score = 0
self.update_scores()
@@ -664,7 +671,7 @@ class FootballCoopGame(ba.CoopGameActivity[Player, Team]):
for team in [self.teams[0], self._bot_team]:
assert team is not None
if team.id == i:
- team.gamedata['score'] += 7
+ team.score += 7
# Tell all players (or bots) to celebrate.
if i == 0:
@@ -677,11 +684,11 @@ class FootballCoopGame(ba.CoopGameActivity[Player, Team]):
# If the good guys scored, add more enemies.
if i == 0:
- if self.teams[0].gamedata['score'] == 7:
+ if self.teams[0].score == 7:
assert self._bot_types_7 is not None
for bottype in self._bot_types_7:
self._spawn_bot(bottype)
- elif self.teams[0].gamedata['score'] == 14:
+ elif self.teams[0].score == 14:
assert self._bot_types_14 is not None
for bottype in self._bot_types_14:
self._spawn_bot(bottype)
@@ -717,7 +724,7 @@ class FootballCoopGame(ba.CoopGameActivity[Player, Team]):
def on_continue(self) -> None:
# Subtract one touchdown from the bots and get them moving again.
assert self._bot_team is not None
- self._bot_team.gamedata['score'] -= 7
+ self._bot_team.score -= 7
self._bots.start_moving()
self.update_scores()
@@ -730,9 +737,8 @@ class FootballCoopGame(ba.CoopGameActivity[Player, Team]):
for team in [self.teams[0], self._bot_team]:
assert team is not None
assert self._scoreboard is not None
- self._scoreboard.set_team_value(team, team.gamedata['score'],
- win_score)
- if team.gamedata['score'] >= win_score:
+ self._scoreboard.set_team_value(team, team.score, win_score)
+ if team.score >= win_score:
if not have_scoring_team:
self.scoring_team = team
if team is self._bot_team:
@@ -745,19 +751,19 @@ class FootballCoopGame(ba.CoopGameActivity[Player, Team]):
if self._preset in ['rookie', 'rookie_easy']:
self._award_achievement('Rookie Football Victory',
sound=False)
- if self._bot_team.gamedata['score'] == 0:
+ if self._bot_team.score == 0:
self._award_achievement(
'Rookie Football Shutout', sound=False)
elif self._preset in ['pro', 'pro_easy']:
self._award_achievement('Pro Football Victory',
sound=False)
- if self._bot_team.gamedata['score'] == 0:
+ if self._bot_team.score == 0:
self._award_achievement('Pro Football Shutout',
sound=False)
elif self._preset in ['uber', 'uber_easy']:
self._award_achievement('Uber Football Victory',
sound=False)
- if self._bot_team.gamedata['score'] == 0:
+ if self._bot_team.score == 0:
self._award_achievement(
'Uber Football Shutout', sound=False)
if (not self._player_has_dropped_bomb
@@ -808,9 +814,9 @@ class FootballCoopGame(ba.CoopGameActivity[Player, Team]):
respawn_time = 2.0 + len(self.initial_player_info) * 1.0
# Respawn them shortly.
- player.gamedata['respawn_timer'] = ba.Timer(
+ player.respawn_timer = ba.Timer(
respawn_time, ba.Call(self.spawn_player_if_exists, player))
- player.gamedata['respawn_icon'] = RespawnIcon(player, respawn_time)
+ player.respawn_icon = RespawnIcon(player, respawn_time)
# Augment standard behavior.
super().handlemessage(msg)
diff --git a/assets/src/ba_data/python/bastd/game/race.py b/assets/src/ba_data/python/bastd/game/race.py
index 0bd2bac9..2036da7d 100644
--- a/assets/src/ba_data/python/bastd/game/race.py
+++ b/assets/src/ba_data/python/bastd/game/race.py
@@ -71,11 +71,21 @@ class Player(ba.Player['Team']):
def __init__(self) -> None:
self.distance_txt: Optional[ba.Node] = None
+ self.last_region = 0
+ self.lap = 0
+ self.distance = 0.0
+ self.finished = False
+ self.rank: Optional[int] = None
class Team(ba.Team[Player]):
"""Our team type for this game."""
+ def __init__(self) -> None:
+ self.time: Optional[float] = None
+ self.lap = 0
+ self.finished = False
+
# ba_meta export game
class RaceGame(ba.TeamGameActivity[Player, Team]):
@@ -137,8 +147,6 @@ class RaceGame(ba.TeamGameActivity[Player, Team]):
self._race_started = False
super().__init__(settings)
self._scoreboard = Scoreboard()
- if self.settings_raw['Epic Mode']:
- self.slow_motion = True
self._score_sound = ba.getsound('score')
self._swipsound = ba.getsound('swip')
self._last_team_time: Optional[float] = None
@@ -157,6 +165,18 @@ class RaceGame(ba.TeamGameActivity[Player, Team]):
self._player_order_update_timer: Optional[ba.Timer] = None
self._start_lights: Optional[List[ba.Node]] = None
self._bomb_spawn_timer: Optional[ba.Timer] = None
+ self._laps = int(settings['Laps'])
+ self._entire_team_must_finish = bool(
+ settings.get('Entire Team Must Finish', False))
+ self._time_limit = float(settings['Time Limit'])
+ self._mine_spawning = int(settings['Mine Spawning'])
+ self._bomb_spawning = int(settings['Bomb Spawning'])
+ self._epic_mode = bool(settings['Epic Mode'])
+
+ # Base class overrides.
+ self.slow_motion = self._epic_mode
+ self.default_music = (ba.MusicType.EPIC_RACE
+ if self._epic_mode else ba.MusicType.RACE)
def get_instance_description(self) -> Union[str, Sequence]:
if (isinstance(self.session, ba.DualTeamSession)
@@ -175,11 +195,7 @@ class RaceGame(ba.TeamGameActivity[Player, Team]):
return 'run 1 lap'
def on_transition_in(self) -> None:
- self.default_music = (ba.MusicType.EPIC_RACE
- if self.settings_raw['Epic Mode'] else
- ba.MusicType.RACE)
super().on_transition_in()
-
pts = self.map.get_def_points('race_point')
mat = self.race_region_material = ba.Material()
mat.add_actions(conditions=('they_have_material',
@@ -222,7 +238,7 @@ class RaceGame(ba.TeamGameActivity[Player, Team]):
assert isinstance(player, Player)
assert isinstance(region, RaceRegion)
- last_region = player.gamedata['last_region']
+ last_region = player.last_region
this_region = region.index
if last_region != this_region:
@@ -242,29 +258,25 @@ class RaceGame(ba.TeamGameActivity[Player, Team]):
else:
# If this player is in first, note that this is the
# front-most race-point.
- if player.gamedata['rank'] == 0:
+ if player.rank == 0:
self._front_race_region = this_region
- player.gamedata['last_region'] = this_region
+ player.last_region = this_region
if last_region >= len(self._regions) - 2 and this_region == 0:
team = player.team
- player.gamedata['lap'] = min(self.settings_raw['Laps'],
- player.gamedata['lap'] + 1)
+ player.lap = min(self._laps, player.lap + 1)
# In teams mode with all-must-finish on, the team lap
# value is the min of all team players.
# Otherwise its the max.
- if isinstance(
- self.session, ba.DualTeamSession
- ) and self.settings_raw.get('Entire Team Must Finish'):
- team.gamedata['lap'] = min(
- [p.gamedata['lap'] for p in team.players])
+ if isinstance(self.session, ba.DualTeamSession
+ ) and self._entire_team_must_finish:
+ team.lap = min([p.lap for p in team.players])
else:
- team.gamedata['lap'] = max(
- [p.gamedata['lap'] for p in team.players])
+ team.lap = max([p.lap for p in team.players])
# A player is finishing.
- if player.gamedata['lap'] == self.settings_raw['Laps']:
+ if player.lap == self._laps:
# In teams mode, hand out points based on the order
# players come in.
@@ -278,27 +290,22 @@ class RaceGame(ba.TeamGameActivity[Player, Team]):
# Flash where the player is.
self._flash_player(player, 1.0)
- player.gamedata['finished'] = True
+ player.finished = True
assert player.actor
player.actor.handlemessage(
ba.DieMessage(immediate=True))
# Makes sure noone behind them passes them in rank
# while finishing.
- player.gamedata['distance'] = 9999.0
+ player.distance = 9999.0
# If the whole team has finished the race.
- if team.gamedata['lap'] == self.settings_raw['Laps']:
+ if team.lap == self._laps:
ba.playsound(self._score_sound)
- player.team.gamedata['finished'] = True
+ player.team.finished = True
assert self._timer is not None
- cur_time = ba.time(
- timeformat=ba.TimeFormat.MILLISECONDS)
- start_time = self._timer.getstarttime(
- timeformat=ba.TimeFormat.MILLISECONDS)
- self._last_team_time = (
- player.team.gamedata['time']) = (cur_time -
- start_time)
+ elapsed = ba.time() - self._timer.getstarttime()
+ self._last_team_time = player.team.time = elapsed
self._check_end_game()
# Team has yet to finish.
@@ -321,12 +328,11 @@ class RaceGame(ba.TeamGameActivity[Player, Team]):
})
player.actor.node.connectattr(
'torso_position', mathnode, 'input2')
- tstr = ba.Lstr(
- resource='lapNumberText',
- subs=[('${CURRENT}',
- str(player.gamedata['lap'] + 1)),
- ('${TOTAL}',
- str(self.settings_raw['Laps']))])
+ tstr = ba.Lstr(resource='lapNumberText',
+ subs=[('${CURRENT}',
+ str(player.lap + 1)),
+ ('${TOTAL}', str(self._laps))
+ ])
txtnode = ba.newnode('text',
owner=mathnode,
attrs={
@@ -348,19 +354,8 @@ class RaceGame(ba.TeamGameActivity[Player, Team]):
print('Exception printing lap:', exc)
def on_team_join(self, team: Team) -> None:
- team.gamedata['time'] = None
- team.gamedata['lap'] = 0
- team.gamedata['finished'] = False
self._update_scoreboard()
- def on_player_join(self, player: Player) -> None:
- player.gamedata['last_region'] = 0
- player.gamedata['lap'] = 0
- player.gamedata['distance'] = 0.0
- player.gamedata['finished'] = False
- player.gamedata['rank'] = None
- super().on_player_join(player)
-
def on_player_leave(self, player: Player) -> None:
super().on_player_leave(player)
@@ -368,20 +363,20 @@ class RaceGame(ba.TeamGameActivity[Player, Team]):
# is on (otherwise in teams mode everyone could just leave except the
# leading player to win).
if (isinstance(self.session, ba.DualTeamSession)
- and self.settings_raw.get('Entire Team Must Finish')):
+ and self._entire_team_must_finish):
ba.screenmessage(ba.Lstr(
translate=('statements',
'${TEAM} is disqualified because ${PLAYER} left'),
subs=[('${TEAM}', player.team.name),
('${PLAYER}', player.get_name(full=True))]),
color=(1, 1, 0))
- player.team.gamedata['finished'] = True
- player.team.gamedata['time'] = None
- player.team.gamedata['lap'] = 0
+ player.team.finished = True
+ player.team.time = None
+ player.team.lap = 0
ba.playsound(ba.getsound('boo'))
for otherplayer in player.team.players:
- otherplayer.gamedata['lap'] = 0
- otherplayer.gamedata['finished'] = True
+ otherplayer.lap = 0
+ otherplayer.finished = True
try:
if otherplayer.actor is not None:
otherplayer.actor.handlemessage(ba.DieMessage())
@@ -393,28 +388,26 @@ class RaceGame(ba.TeamGameActivity[Player, Team]):
def _update_scoreboard(self) -> None:
for team in self.teams:
- distances = [
- player.gamedata['distance'] for player in team.players
- ]
+ distances = [player.distance for player in team.players]
if not distances:
- teams_dist = 0
+ teams_dist = 0.0
else:
if (isinstance(self.session, ba.DualTeamSession)
- and self.settings_raw.get('Entire Team Must Finish')):
+ and self._entire_team_must_finish):
teams_dist = min(distances)
else:
teams_dist = max(distances)
self._scoreboard.set_team_value(
team,
teams_dist,
- self.settings_raw['Laps'],
- flash=(teams_dist >= float(self.settings_raw['Laps'])),
+ self._laps,
+ flash=(teams_dist >= float(self._laps)),
show_value=False)
def on_begin(self) -> None:
from bastd.actor.onscreentimer import OnScreenTimer
super().on_begin()
- self.setup_standard_time_limit(self.settings_raw['Time Limit'])
+ self.setup_standard_time_limit(self._time_limit)
self.setup_standard_powerup_drops()
self._team_finish_pts = 100
@@ -434,16 +427,15 @@ class RaceGame(ba.TeamGameActivity[Player, Team]):
}))
self._timer = OnScreenTimer()
- if self.settings_raw['Mine Spawning'] != 0:
+ if self._mine_spawning != 0:
self._race_mines = [
RaceMine(point=p, mine=None)
for p in self.map.get_def_points('race_mine')
]
if self._race_mines:
- self._race_mine_timer = ba.Timer(
- 0.001 * self.settings_raw['Mine Spawning'],
- self._update_race_mine,
- repeat=True)
+ self._race_mine_timer = ba.Timer(0.001 * self._mine_spawning,
+ self._update_race_mine,
+ repeat=True)
self._scoreboard_timer = ba.Timer(0.25,
self._update_scoreboard,
@@ -516,16 +508,15 @@ class RaceGame(ba.TeamGameActivity[Player, Team]):
try:
assert isinstance(player.actor, PlayerSpaz)
player.actor.connect_controls_to_player()
- except Exception as exc:
- print('Exception in race player connects:', exc)
+ except Exception:
+ ba.print_exception('Error in race player connects')
assert self._timer is not None
self._timer.start()
- if self.settings_raw['Bomb Spawning'] != 0:
- self._bomb_spawn_timer = ba.Timer(
- 0.001 * self.settings_raw['Bomb Spawning'],
- self._spawn_bomb,
- repeat=True)
+ if self._bomb_spawning != 0:
+ self._bomb_spawn_timer = ba.Timer(0.001 * self._bomb_spawning,
+ self._spawn_bomb,
+ repeat=True)
self._race_started = True
@@ -542,7 +533,7 @@ class RaceGame(ba.TeamGameActivity[Player, Team]):
except Exception:
pos = None
if pos is not None:
- r_index = player.gamedata['last_region']
+ r_index = player.last_region
rg1 = self._regions[r_index]
r1pt = ba.Vec3(rg1.pos[:3])
rg2 = self._regions[0] if r_index == len(
@@ -550,20 +541,17 @@ class RaceGame(ba.TeamGameActivity[Player, Team]):
r2pt = ba.Vec3(rg2.pos[:3])
r2dist = (pos - r2pt).length()
amt = 1.0 - (r2dist / (r2pt - r1pt).length())
- amt = player.gamedata['lap'] + (r_index + amt) * (
- 1.0 / len(self._regions))
- player.gamedata['distance'] = amt
+ amt = player.lap + (r_index + amt) * (1.0 / len(self._regions))
+ player.distance = amt
# Sort players by distance and update their ranks.
- p_list = [(player.gamedata['distance'], player)
- for player in self.players]
+ p_list = [(player.distance, player) for player in self.players]
p_list.sort(reverse=True, key=lambda x: x[0])
for i, plr in enumerate(p_list):
try:
- plr[1].gamedata['rank'] = i
+ plr[1].rank = i
if plr[1].actor:
- # noinspection PyUnresolvedReferences
node = plr[1].distance_txt
if node:
node.text = str(i + 1) if plr[1].is_alive() else ''
@@ -626,13 +614,13 @@ class RaceGame(ba.TeamGameActivity[Player, Team]):
ba.timer(0.95, ba.Call(self._make_mine, m_index))
def spawn_player(self, player: Player) -> ba.Actor:
- if player.team.gamedata['finished']:
+ if player.team.finished:
# FIXME: This is not type-safe!
# This call is expected to always return an Actor!
# Perhaps we need something like can_spawn_player()...
# noinspection PyTypeChecker
return None # type: ignore
- pos = self._regions[player.gamedata['last_region']].pos
+ pos = self._regions[player.last_region].pos
# Don't use the full region so we're less likely to spawn off a cliff.
region_scale = 0.8
@@ -674,17 +662,14 @@ class RaceGame(ba.TeamGameActivity[Player, Team]):
def _check_end_game(self) -> None:
# If there's no teams left racing, finish.
- teams_still_in = len(
- [t for t in self.teams if not t.gamedata['finished']])
+ teams_still_in = len([t for t in self.teams if not t.finished])
if teams_still_in == 0:
self.end_game()
return
# Count the number of teams that have completed the race.
- teams_completed = len([
- t for t in self.teams
- if t.gamedata['finished'] and t.gamedata['time'] is not None
- ])
+ teams_completed = len(
+ [t for t in self.teams if t.finished and t.time is not None])
if teams_completed > 0:
session = self.session
@@ -709,22 +694,21 @@ class RaceGame(ba.TeamGameActivity[Player, Team]):
# Stop updating our time text, and set it to show the exact last
# finish time if we have one. (so users don't get upset if their
- # final time differs from what they see onscreen by a tiny bit)
+ # final time differs from what they see onscreen by a tiny amount)
assert self._timer is not None
if self._timer.has_started():
- cur_time = self._timer.getstarttime(
- timeformat=ba.TimeFormat.MILLISECONDS)
self._timer.stop(
endtime=None if self._last_team_time is None else (
- cur_time + self._last_team_time))
+ self._timer.getstarttime() + self._last_team_time))
results = ba.TeamGameResults()
for team in self.teams:
- if team.gamedata['time'] is not None:
- results.set_team_score(team, team.gamedata['time'])
- # If game have ended before we
- # get any result, use 'fail' screen
+ if team.time is not None:
+ # We store time in seconds, but pass a score in milliseconds.
+ results.set_team_score(team, int(team.time * 1000.0))
+ else:
+ results.set_team_score(team, None)
# We don't announce a winner in ffa mode since its probably been a
# while since the first place guy crossed the finish line so it seems
@@ -738,10 +722,7 @@ class RaceGame(ba.TeamGameActivity[Player, Team]):
# Augment default behavior.
super().handlemessage(msg)
player = msg.getplayer(Player)
- if not player:
- ba.print_error('got no player in PlayerDiedMessage')
- return
- if not player.gamedata['finished']:
+ if not player.finished:
self.respawn_player(player, respawn_time=1)
else:
super().handlemessage(msg)
diff --git a/assets/src/ba_data/python/bastd/maps.py b/assets/src/ba_data/python/bastd/maps.py
index aa467779..1e5158c6 100644
--- a/assets/src/ba_data/python/bastd/maps.py
+++ b/assets/src/ba_data/python/bastd/maps.py
@@ -35,7 +35,6 @@ if TYPE_CHECKING:
class HockeyStadium(ba.Map):
"""Stadium map used for ice hockey games."""
- # noinspection PyUnresolvedReferences
from bastd.mapdata import hockey_stadium as defs
name = 'Hockey Stadium'
@@ -183,7 +182,6 @@ class FootballStadium(ba.Map):
class Bridgit(ba.Map):
"""Map with a narrow bridge in the middle."""
- # noinspection PyUnresolvedReferences
from bastd.mapdata import bridgit as defs
name = 'Bridgit'
@@ -278,7 +276,6 @@ class Bridgit(ba.Map):
class BigG(ba.Map):
"""Large G shaped map for racing"""
- # noinspection PyUnresolvedReferences
from bastd.mapdata import big_g as defs
name = 'Big G'
@@ -375,7 +372,6 @@ class BigG(ba.Map):
class Roundabout(ba.Map):
"""CTF map featuring two platforms and a long way around between them"""
- # noinspection PyUnresolvedReferences
from bastd.mapdata import roundabout as defs
name = 'Roundabout'
@@ -469,7 +465,6 @@ class Roundabout(ba.Map):
class MonkeyFace(ba.Map):
"""Map sorta shaped like a monkey face; teehee!"""
- # noinspection PyUnresolvedReferences
from bastd.mapdata import monkey_face as defs
name = 'Monkey Face'
@@ -563,7 +558,6 @@ class MonkeyFace(ba.Map):
class ZigZag(ba.Map):
"""A very long zig-zaggy map"""
- # noinspection PyUnresolvedReferences
from bastd.mapdata import zig_zag as defs
name = 'Zigzag'
@@ -657,7 +651,6 @@ class ZigZag(ba.Map):
class ThePad(ba.Map):
"""A simple square shaped map with a raised edge."""
- # noinspection PyUnresolvedReferences
from bastd.mapdata import the_pad as defs
name = 'The Pad'
@@ -738,7 +731,6 @@ class ThePad(ba.Map):
class DoomShroom(ba.Map):
"""A giant mushroom. Of doom."""
- # noinspection PyUnresolvedReferences
from bastd.mapdata import doom_shroom as defs
name = 'Doom Shroom'
@@ -831,7 +823,6 @@ class DoomShroom(ba.Map):
class LakeFrigid(ba.Map):
"""An icy lake fit for racing."""
- # noinspection PyUnresolvedReferences
from bastd.mapdata import lake_frigid as defs
name = 'Lake Frigid'
@@ -912,7 +903,6 @@ class LakeFrigid(ba.Map):
class TipTop(ba.Map):
"""A pointy map good for king-of-the-hill-ish games."""
- # noinspection PyUnresolvedReferences
from bastd.mapdata import tip_top as defs
name = 'Tip Top'
@@ -984,7 +974,6 @@ class TipTop(ba.Map):
class CragCastle(ba.Map):
"""A lovely castle map."""
- # noinspection PyUnresolvedReferences
from bastd.mapdata import crag_castle as defs
name = 'Crag Castle'
@@ -1185,7 +1174,6 @@ class TowerD(ba.Map):
class HappyThoughts(ba.Map):
"""Flying map."""
- # noinspection PyUnresolvedReferences
from bastd.mapdata import happy_thoughts as defs
name = 'Happy Thoughts'
@@ -1291,7 +1279,6 @@ class HappyThoughts(ba.Map):
class StepRightUp(ba.Map):
"""Wide stepped map good for CTF or Assault."""
- # noinspection PyUnresolvedReferences
from bastd.mapdata import step_right_up as defs
name = 'Step Right Up'
diff --git a/assets/src/ba_data/python/bastd/ui/colorpicker.py b/assets/src/ba_data/python/bastd/ui/colorpicker.py
index 53856e28..eb070aaf 100644
--- a/assets/src/ba_data/python/bastd/ui/colorpicker.py
+++ b/assets/src/ba_data/python/bastd/ui/colorpicker.py
@@ -268,7 +268,6 @@ class ColorPickerExact(popup.PopupWindow):
# color to the delegate, so start doing that...
self._update_for_color()
- # noinspection PyUnresolvedReferences
def _update_for_color(self) -> None:
if not self.root_widget:
return
diff --git a/docs/ba_module.md b/docs/ba_module.md
index dc9024c2..12352ff5 100644
--- a/docs/ba_module.md
+++ b/docs/ba_module.md
@@ -1,5 +1,5 @@
-last updated on 2020-05-20 for Ballistica version 1.5.0 build 20025
+last updated on 2020-05-21 for Ballistica version 1.5.0 build 20025
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 let me know. Happy modding!
diff --git a/tools/snippets b/tools/snippets
index b5c0405e..09733841 100755
--- a/tools/snippets
+++ b/tools/snippets
@@ -37,7 +37,6 @@ from typing import TYPE_CHECKING
# Pull in some standard snippets we want to expose.
# pylint: disable=unused-import
-# noinspection PyUnresolvedReferences
from efrotools.snippets import (
PROJROOT, snippets_main, formatcode, formatscripts, formatmakefile,
cpplint, pylint, runpylint, mypy, runmypy, dmypy, tool_config_install,