more game modernizing and bug fixes

This commit is contained in:
Eric Froemling 2020-05-21 01:14:58 -07:00
parent 06600d9eeb
commit 9d7085cb31
19 changed files with 205 additions and 220 deletions

View File

@ -4132,16 +4132,16 @@
"assets/build/windows/x64/python.exe": "https://files.ballistica.net/cache/ba1/25/a7/dc87c1be41605eb6fefd0145144c", "assets/build/windows/x64/python.exe": "https://files.ballistica.net/cache/ba1/25/a7/dc87c1be41605eb6fefd0145144c",
"assets/build/windows/x64/python37.dll": "https://files.ballistica.net/cache/ba1/b9/e4/d912f56e42e9991bcbb4c804cfcb", "assets/build/windows/x64/python37.dll": "https://files.ballistica.net/cache/ba1/b9/e4/d912f56e42e9991bcbb4c804cfcb",
"assets/build/windows/x64/pythonw.exe": "https://files.ballistica.net/cache/ba1/6c/bb/b6f52c306aa4e88061510e96cefe", "assets/build/windows/x64/pythonw.exe": "https://files.ballistica.net/cache/ba1/6c/bb/b6f52c306aa4e88061510e96cefe",
"build/prefab/linux-server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/66/68/37e1f6d2afd5d6a4cbbcebba2f3e", "build/prefab/linux-server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/f7/15/2016db42f7d9d0cd341b29d4cf85",
"build/prefab/linux-server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/dc/d4/f954892306c82ca4d9c74d335c15", "build/prefab/linux-server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/74/7a/5361dbc0c2dae7ab52c5501fc21d",
"build/prefab/linux/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/4d/b8/4cfc2035ec4cdeba78be2aee8aff", "build/prefab/linux/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/04/9f/20f97048d27b7eff224cc280a5c3",
"build/prefab/linux/release/ballisticacore": "https://files.ballistica.net/cache/ba1/3f/98/9edf61a1b38432213e93b9342a4e", "build/prefab/linux/release/ballisticacore": "https://files.ballistica.net/cache/ba1/d2/d1/c023095a1fc0f2683e6214142ea2",
"build/prefab/mac-server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/86/43/f76e498f45bb42f2383986d3c15b", "build/prefab/mac-server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/4e/50/1b6f2eb8ec843e1cf3a96ab0e458",
"build/prefab/mac-server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/a1/d6/a9dd60f83d58eb09b1b4c0771588", "build/prefab/mac-server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/d6/0a/b979206cca7a41baa8c81d8f6225",
"build/prefab/mac/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/d8/4f/9ded4658cf6e8d8d7fdf9477ae86", "build/prefab/mac/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/37/23/4dcfca7889611fd21ece1c39bec5",
"build/prefab/mac/release/ballisticacore": "https://files.ballistica.net/cache/ba1/a1/6b/56d9fa2709eb43be73c00aacb1b5", "build/prefab/mac/release/ballisticacore": "https://files.ballistica.net/cache/ba1/7e/6a/093312688ce930585cf2651860b3",
"build/prefab/windows-server/debug/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/a9/41/2e78f2f5dfa4273ce70fc5a59e0e", "build/prefab/windows-server/debug/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/81/73/63cb8f8ce715a5156bbd3127cee1",
"build/prefab/windows-server/release/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/9e/3d/12c0ba5235b6750ec0f37726de6e", "build/prefab/windows-server/release/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/2c/de/9d59be806cb4a45fa50414276eb1",
"build/prefab/windows/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/8f/9c/edea76ee92634ef9565988c9ef6e", "build/prefab/windows/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/f6/af/3eb4c3046770371ea72717e3442e",
"build/prefab/windows/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/35/86/2aeb9cfac9f7639676e149e1323b" "build/prefab/windows/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/5f/3b/645e268f5f3a74acd23edc46ba2f"
} }

View File

@ -52,7 +52,7 @@
<inspection_tool class="PyTypeCheckerInspection" enabled="false" level="WARNING" enabled_by_default="false" /> <inspection_tool class="PyTypeCheckerInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PyTypeHintsInspection" enabled="false" level="WARNING" enabled_by_default="false" /> <inspection_tool class="PyTypeHintsInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PyUnreachableCodeInspection" enabled="false" level="WARNING" enabled_by_default="false" /> <inspection_tool class="PyUnreachableCodeInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PyUnresolvedReferencesInspection" enabled="true" level="WARNING" enabled_by_default="true"> <inspection_tool class="PyUnresolvedReferencesInspection" enabled="true" level="WARNING" enabled_by_default="false">
<scope name="PyIgnoreUnresolved" level="WARNING" enabled="false"> <scope name="PyIgnoreUnresolved" level="WARNING" enabled="false">
<option name="ignoredIdentifiers"> <option name="ignoredIdentifiers">
<list> <list>

View File

@ -698,7 +698,6 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
'ba.Activity.on_begin() never got called for ' + str(self) + 'ba.Activity.on_begin() never got called for ' + str(self) +
'; did you forget to call it in your on_begin override?') '; did you forget to call it in your on_begin override?')
# noinspection PyUnresolvedReferences
def _setup_player_and_team_types(self) -> None: def _setup_player_and_team_types(self) -> None:
"""Pull player and team types from our typing.Generic params.""" """Pull player and team types from our typing.Generic params."""

View File

@ -197,7 +197,6 @@ def animate(node: ba.Node,
if _ba.app.test_build and not suppress_format_warning: if _ba.app.test_build and not suppress_format_warning:
for item in items: for item in items:
# (PyCharm seems to think item is a float, not a tuple) # (PyCharm seems to think item is a float, not a tuple)
# noinspection PyUnresolvedReferences
_ba.time_format_check(timeformat, item[0]) _ba.time_format_check(timeformat, item[0])
curve = _ba.newnode('animcurve', curve = _ba.newnode('animcurve',
@ -223,7 +222,6 @@ def animate(node: ba.Node,
# get disconnected. # get disconnected.
if not loop: if not loop:
# (PyCharm seems to think item is a float, not a tuple) # (PyCharm seems to think item is a float, not a tuple)
# noinspection PyUnresolvedReferences
_ba.timer(int(mult * items[-1][0]) + 1000, _ba.timer(int(mult * items[-1][0]) + 1000,
curve.delete, curve.delete,
timeformat=TimeFormat.MILLISECONDS) timeformat=TimeFormat.MILLISECONDS)
@ -264,7 +262,6 @@ def animate_array(node: ba.Node,
if _ba.app.test_build and not suppress_format_warning: if _ba.app.test_build and not suppress_format_warning:
for item in items: for item in items:
# (PyCharm seems to think item is a float, not a tuple) # (PyCharm seems to think item is a float, not a tuple)
# noinspection PyUnresolvedReferences
_ba.time_format_check(timeformat, item[0]) _ba.time_format_check(timeformat, item[0])
if timeformat is TimeFormat.SECONDS: if timeformat is TimeFormat.SECONDS:
@ -291,7 +288,6 @@ def animate_array(node: ba.Node,
# curve after its done its job. # curve after its done its job.
if not loop: if not loop:
# (PyCharm seems to think item is a float, not a tuple) # (PyCharm seems to think item is a float, not a tuple)
# noinspection PyUnresolvedReferences
_ba.timer(int(mult * items[-1][0]) + 1000, _ba.timer(int(mult * items[-1][0]) + 1000,
curve.delete, curve.delete,
timeformat=TimeFormat.MILLISECONDS) timeformat=TimeFormat.MILLISECONDS)
@ -303,7 +299,6 @@ def animate_array(node: ba.Node,
# once we get disconnected. # once we get disconnected.
if not loop: if not loop:
# (PyCharm seems to think item is a float, not a tuple) # (PyCharm seems to think item is a float, not a tuple)
# noinspection PyUnresolvedReferences
_ba.timer(int(mult * items[-1][0]) + 1000, _ba.timer(int(mult * items[-1][0]) + 1000,
combine.delete, combine.delete,
timeformat=TimeFormat.MILLISECONDS) timeformat=TimeFormat.MILLISECONDS)

View File

@ -114,7 +114,6 @@ class ServerCallThread(threading.Thread):
_ba.set_thread_name('BA_ServerCallThread') _ba.set_thread_name('BA_ServerCallThread')
# Seems pycharm doesn't know about urllib.parse. # Seems pycharm doesn't know about urllib.parse.
# noinspection PyUnresolvedReferences
parse = urllib.parse parse = urllib.parse
if self._request_type == 'get': if self._request_type == 'get':
response = urllib.request.urlopen( response = urllib.request.urlopen(

View File

@ -31,7 +31,6 @@ if TYPE_CHECKING:
@dataclass @dataclass
class PowerupMessage: class PowerupMessage:
# noinspection PyUnresolvedReferences
"""A message telling an object to accept a powerup. """A message telling an object to accept a powerup.
Category: Message Classes Category: Message Classes

View File

@ -115,8 +115,6 @@ class MultiTeamScoreScreenActivity(ScoreScreenActivity):
def _get_player_score_set_entry( def _get_player_score_set_entry(
player: ba.SessionPlayer) -> Optional[ba.PlayerRecord]: player: ba.SessionPlayer) -> Optional[ba.PlayerRecord]:
for p_rec in valid_players: for p_rec in valid_players:
# PyCharm incorrectly thinks valid_players is a List[str]
# noinspection PyUnresolvedReferences
if p_rec[1].player is player: if p_rec[1].player is player:
return p_rec[1] return p_rec[1]
return None return None

View File

@ -53,7 +53,7 @@ class _Entry:
self._flash_timer: Optional[ba.Timer] = None self._flash_timer: Optional[ba.Timer] = None
self._flash_counter: Optional[int] = None self._flash_counter: Optional[int] = None
self._flash_colors: Optional[bool] = 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) 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) self._set_flash_colors(not self._flash_colors)
def set_value(self, def set_value(self,
score: int, score: float,
max_score: int = None, max_score: float = None,
countdown: bool = False, countdown: bool = False,
flash: bool = True, flash: bool = True,
show_value: bool = True) -> None: show_value: bool = True) -> None:
@ -360,8 +360,8 @@ class Scoreboard:
def set_team_value(self, def set_team_value(self,
team: ba.Team, team: ba.Team,
score: int, score: float,
max_score: int = None, max_score: float = None,
countdown: bool = False, countdown: bool = False,
flash: bool = True, flash: bool = True,
show_value: bool = True) -> None: show_value: bool = True) -> None:
@ -373,7 +373,8 @@ class Scoreboard:
if '_scoreboard_entry' in team.gamedata: if '_scoreboard_entry' in team.gamedata:
raise Exception('existing _EntryProxy found') raise Exception('existing _EntryProxy found')
team.gamedata['_scoreboard_entry'] = _EntryProxy(self, team) team.gamedata['_scoreboard_entry'] = _EntryProxy(self, team)
# now set the entry..
# Now set the entry.
self._entries[team.id].set_value(score=score, self._entries[team.id].set_value(score=score,
max_score=max_score, max_score=max_score,
countdown=countdown, countdown=countdown,

View File

@ -31,6 +31,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.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, Type, List, Dict, Sequence, Union from typing import Any, Type, List, Dict, Sequence, Union
@ -85,7 +86,6 @@ class AssaultGame(ba.TeamGameActivity[Player, Team]):
return ba.getmaps('team_flag') return ba.getmaps('team_flag')
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._last_score_time = 0.0 self._last_score_time = 0.0

View File

@ -62,8 +62,7 @@ class CTFFlag(stdflag.Flag):
def reset_return_times(self) -> None: def reset_return_times(self) -> None:
"""Clear flag related times in the activity.""" """Clear flag related times in the activity."""
self.time_out_respawn_time = int( self.time_out_respawn_time = int(self.activity.flag_idle_return_time)
self.activity.settings_raw['Flag Idle Return Time'])
self.touch_return_time = float(self.activity.flag_touch_return_time) self.touch_return_time = float(self.activity.flag_touch_return_time)
@property @property
@ -167,21 +166,17 @@ class CaptureTheFlagGame(ba.TeamGameActivity[Player, Team]):
self._all_bases_material = ba.Material() self._all_bases_material = ba.Material()
self._last_home_flag_notice_print_time = 0.0 self._last_home_flag_notice_print_time = 0.0
self._score_to_win = int(settings['Score to Win']) 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._epic_mode = bool(settings['Epic Mode'])
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_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
self.default_music = (ba.MusicType.EPIC if self._epic_mode else self.default_music = (ba.MusicType.EPIC if self._epic_mode else
ba.MusicType.FLAG_CATCHER) 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]: def get_instance_description(self) -> Union[str, Sequence]:
if self._score_to_win == 1: if self._score_to_win == 1:
return 'Steal the enemy flag.' 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: def _award_players_touching_own_flag(self, team: Team) -> None:
for player in team.players: for player in team.players:
if player.touching_own_flag > 0: 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, self.stats.player_scored(player,
return_score, return_score,
screenmessage=False) screenmessage=False)
@ -468,7 +463,7 @@ class CaptureTheFlagGame(ba.TeamGameActivity[Player, Team]):
# If return-time is zero, just kill it immediately.. otherwise keep # If return-time is zero, just kill it immediately.. otherwise keep
# track of touches and count down. # 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 assert team.flag is not None
if not team.home_flag_at_base and team.flag.held_count == 0: if not team.home_flag_at_base and team.flag.held_count == 0:

View File

@ -33,6 +33,7 @@ from bastd.actor.flag import Flag
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
from bastd.actor.respawnicon import RespawnIcon
class ConquestFlag(Flag): class ConquestFlag(Flag):
@ -57,10 +58,31 @@ class ConquestFlag(Flag):
class Player(ba.Player['Team']): class Player(ba.Player['Team']):
"""Our player type for this game.""" """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]): class Team(ba.Team[Player]):
"""Our team type for this game.""" """Our team type for this game."""
def __init__(self) -> None:
self.flags_held = 0
# ba_meta export game # ba_meta export game
class ConquestGame(ba.TeamGameActivity[Player, Team]): class ConquestGame(ba.TeamGameActivity[Player, Team]):
@ -96,13 +118,18 @@ class ConquestGame(ba.TeamGameActivity[Player, Team]):
def __init__(self, settings: Dict[str, Any]): def __init__(self, settings: Dict[str, Any]):
from bastd.actor.scoreboard import Scoreboard from bastd.actor.scoreboard import Scoreboard
super().__init__(settings) super().__init__(settings)
if self.settings_raw['Epic Mode']:
self.slow_motion = True
self._scoreboard = Scoreboard() self._scoreboard = Scoreboard()
self._score_sound = ba.getsound('score') self._score_sound = ba.getsound('score')
self._swipsound = ba.getsound('swip') self._swipsound = ba.getsound('swip')
self._extraflagmat = ba.Material() self._extraflagmat = ba.Material()
self._flags: List[ConquestFlag] = [] 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. # We want flags to tell us they've been hit but not react physically.
self._extraflagmat.add_actions( self._extraflagmat.add_actions(
@ -116,27 +143,20 @@ class ConquestGame(ba.TeamGameActivity[Player, Team]):
def get_instance_description_short(self) -> Union[str, Sequence]: def get_instance_description_short(self) -> Union[str, Sequence]:
return 'secure all ${ARG1} flags', len(self.map.flag_points) 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: def on_team_join(self, team: Team) -> None:
if self.has_begun(): if self.has_begun():
self._update_scores() self._update_scores()
team.gamedata['flags_held'] = 0
def on_player_join(self, player: Player) -> None: 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. # 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) self.spawn_player(player)
def on_begin(self) -> None: def on_begin(self) -> None:
super().on_begin() 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.setup_standard_powerup_drops()
# Set up flags with marker lights. # Set up flags with marker lights.
@ -177,27 +197,27 @@ class ConquestGame(ba.TeamGameActivity[Player, Team]):
def _update_scores(self) -> None: def _update_scores(self) -> None:
for team in self.teams: for team in self.teams:
team.gamedata['flags_held'] = 0 team.flags_held = 0
for flag in self._flags: for flag in self._flags:
if flag.team is not None: if flag.team is not None:
flag.team.gamedata['flags_held'] += 1 flag.team.flags_held += 1
for team in self.teams: for team in self.teams:
# If a team finds themselves with no flags, cancel all # If a team finds themselves with no flags, cancel all
# outstanding spawn-timers. # outstanding spawn-timers.
if team.gamedata['flags_held'] == 0: if team.flags_held == 0:
for player in team.players: for player in team.players:
player.gamedata['respawn_timer'] = None player.respawn_timer = None
player.gamedata['respawn_icon'] = None player.respawn_icon = None
if team.gamedata['flags_held'] == len(self._flags): if team.flags_held == len(self._flags):
self.end_game() 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)) len(self._flags))
def end_game(self) -> None: def end_game(self) -> None:
results = ba.TeamGameResults() results = ba.TeamGameResults()
for team in self.teams: 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) self.end(results=results)
def _flash_flag(self, flag: ConquestFlag, length: float = 1.0) -> None: 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 if (otherplayer.team is flag.team
and otherplayer.actor is not None and otherplayer.actor is not None
and not otherplayer.is_alive() and not otherplayer.is_alive()
and otherplayer.gamedata['respawn_timer'] is None): and otherplayer.respawn_timer is None):
self.spawn_player(otherplayer) self.spawn_player(otherplayer)
def handlemessage(self, msg: Any) -> Any: def handlemessage(self, msg: Any) -> Any:
@ -249,10 +269,10 @@ class ConquestGame(ba.TeamGameActivity[Player, Team]):
# Respawn only if this team has a flag. # Respawn only if this team has a flag.
player = msg.getplayer(Player) player = msg.getplayer(Player)
if player.team.gamedata['flags_held'] > 0: if player.team.flags_held > 0:
self.respawn_player(player) self.respawn_player(player)
else: else:
player.gamedata['respawn_timer'] = None player.respawn_timer = None
else: else:
super().handlemessage(msg) super().handlemessage(msg)

View File

@ -34,6 +34,7 @@ from bastd.actor.playerspaz import PlayerSpaz
from bastd.actor.spazbot import BotSet, BouncyBot, SpazBotDeathMessage from bastd.actor.spazbot import BotSet, BouncyBot, SpazBotDeathMessage
from bastd.actor.onscreencountdown import OnScreenCountdown from bastd.actor.onscreencountdown import OnScreenCountdown
from bastd.actor.scoreboard import Scoreboard from bastd.actor.scoreboard import Scoreboard
from bastd.actor.respawnicon import RespawnIcon
if TYPE_CHECKING: if TYPE_CHECKING:
from typing import Any, Type, Dict, List, Tuple, Optional from typing import Any, Type, Dict, List, Tuple, Optional
@ -42,10 +43,17 @@ if TYPE_CHECKING:
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.respawn_timer: Optional[ba.Timer] = None
self.respawn_icon: Optional[RespawnIcon] = None
class Team(ba.Team[Player]): class Team(ba.Team[Player]):
"""Our team type for this game.""" """Our team type for this game."""
def __init__(self) -> None:
self.score = 0
# ba_meta export game # ba_meta export game
class EasterEggHuntGame(ba.TeamGameActivity[Player, Team]): class EasterEggHuntGame(ba.TeamGameActivity[Player, Team]):
@ -88,15 +96,10 @@ class EasterEggHuntGame(ba.TeamGameActivity[Player, Team]):
self._countdown: Optional[OnScreenCountdown] = None self._countdown: Optional[OnScreenCountdown] = None
self._bots: Optional[BotSet] = None self._bots: Optional[BotSet] = None
# Called when our game is transitioning in but not ready to start. # Base class overrides
# ..we can go ahead and set our music and whatnot.
def on_transition_in(self) -> None:
self.default_music = ba.MusicType.FORWARD_MARCH self.default_music = ba.MusicType.FORWARD_MARCH
super().on_transition_in()
def on_team_join(self, team: Team) -> None: def on_team_join(self, team: Team) -> None:
team.gamedata['score'] = 0
if self.has_begun(): if self.has_begun():
self._update_scoreboard() self._update_scoreboard()
@ -142,7 +145,7 @@ class EasterEggHuntGame(ba.TeamGameActivity[Player, Team]):
player = (spaz.getplayer() player = (spaz.getplayer()
if hasattr(spaz, 'getplayer') else None) if hasattr(spaz, 'getplayer') else None)
if player and egg: if player and egg:
player.team.gamedata['score'] += 1 player.team.score += 1
# Displays a +1 (and adds to individual player score in # Displays a +1 (and adds to individual player score in
# teams mode). # teams mode).
@ -201,7 +204,6 @@ class EasterEggHuntGame(ba.TeamGameActivity[Player, Team]):
# Respawn dead players. # Respawn dead players.
if isinstance(msg, ba.PlayerDiedMessage): if isinstance(msg, ba.PlayerDiedMessage):
from bastd.actor import respawnicon
# Augment standard behavior. # Augment standard behavior.
super().handlemessage(msg) super().handlemessage(msg)
@ -213,10 +215,9 @@ class EasterEggHuntGame(ba.TeamGameActivity[Player, Team]):
# Respawn them shortly. # Respawn them shortly.
assert self.initial_player_info is not None assert self.initial_player_info is not None
respawn_time = 2.0 + len(self.initial_player_info) * 1.0 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)) respawn_time, ba.Call(self.spawn_player_if_exists, player))
player.gamedata['respawn_icon'] = respawnicon.RespawnIcon( player.respawn_icon = RespawnIcon(player, respawn_time)
player, respawn_time)
# Whenever our evil bunny dies, respawn him and spew some eggs. # Whenever our evil bunny dies, respawn him and spew some eggs.
elif isinstance(msg, SpazBotDeathMessage): elif isinstance(msg, SpazBotDeathMessage):
@ -235,12 +236,12 @@ class EasterEggHuntGame(ba.TeamGameActivity[Player, Team]):
def _update_scoreboard(self) -> None: def _update_scoreboard(self) -> None:
for team in self.teams: 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: def end_game(self) -> None:
results = ba.TeamGameResults() results = ba.TeamGameResults()
for team in self.teams: for team in self.teams:
results.set_team_score(team, team.gamedata['score']) results.set_team_score(team, team.score)
self.end(results) self.end(results)

View File

@ -161,6 +161,12 @@ class Icon(ba.Actor):
if lives == 0: if lives == 0:
ba.timer(0.6, self.update_for_lives) 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']): class Player(ba.Player['Team']):
"""Our player type for this game.""" """Our player type for this game."""

View File

@ -69,10 +69,17 @@ class FootballFlag(stdflag.Flag):
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.respawn_timer: Optional[ba.Timer] = None
self.respawn_icon: Optional[RespawnIcon] = None
class Team(ba.Team[Player]): class Team(ba.Team[Player]):
"""Our team type for this game.""" """Our team type for this game."""
def __init__(self) -> None:
self.score = 0
# ba_meta export game # ba_meta export game
class FootballTeamGame(ba.TeamGameActivity[Player, Team]): class FootballTeamGame(ba.TeamGameActivity[Player, Team]):
@ -130,11 +137,13 @@ class FootballTeamGame(ba.TeamGameActivity[Player, Team]):
self._flag: Optional[FootballFlag] = None self._flag: Optional[FootballFlag] = None
self._flag_respawn_timer: Optional[ba.Timer] = None self._flag_respawn_timer: Optional[ba.Timer] = None
self._flag_respawn_light: Optional[ba.NodeActor] = 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]: 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, # and we will need to score, for example, 27 points,
# we will be required to score 3 (not 4) goals .. # we will be required to score 3 (not 4) goals ..
touchdowns = math.ceil(touchdowns) touchdowns = math.ceil(touchdowns)
@ -143,7 +152,7 @@ class FootballTeamGame(ba.TeamGameActivity[Player, Team]):
return 'Score a touchdown.' return 'Score a touchdown.'
def get_instance_description_short(self) -> Union[str, Sequence]: 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) touchdowns = math.ceil(touchdowns)
if touchdowns > 1: if touchdowns > 1:
return 'score ${ARG1} touchdowns', touchdowns return 'score ${ARG1} touchdowns', touchdowns
@ -155,7 +164,7 @@ class FootballTeamGame(ba.TeamGameActivity[Player, Team]):
def on_begin(self) -> None: def on_begin(self) -> None:
super().on_begin() 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.setup_standard_powerup_drops()
self._flag_spawn_pos = (self.map.get_flag_position(None)) self._flag_spawn_pos = (self.map.get_flag_position(None))
self._spawn_flag() self._spawn_flag()
@ -182,7 +191,6 @@ class FootballTeamGame(ba.TeamGameActivity[Player, Team]):
ba.playsound(self._chant_sound) ba.playsound(self._chant_sound)
def on_team_join(self, team: Team) -> None: def on_team_join(self, team: Team) -> None:
team.gamedata['score'] = 0
self._update_scoreboard() self._update_scoreboard()
def _kill_flag(self) -> None: def _kill_flag(self) -> None:
@ -203,7 +211,7 @@ class FootballTeamGame(ba.TeamGameActivity[Player, Team]):
break break
for team in self.teams: for team in self.teams:
if team.id == i: if team.id == i:
team.gamedata['score'] += 7 team.score += 7
# Tell all players to celebrate. # Tell all players to celebrate.
for player in team.players: for player in team.players:
@ -218,8 +226,8 @@ class FootballTeamGame(ba.TeamGameActivity[Player, Team]):
self.stats.player_scored(self._flag.last_holding_player, self.stats.player_scored(self._flag.last_holding_player,
50, 50,
big_message=True) big_message=True)
# end game if we won # End the game if we won.
if team.gamedata['score'] >= self.settings_raw['Score to Win']: if team.score >= self._score_to_win:
self.end_game() self.end_game()
ba.playsound(self._score_sound) ba.playsound(self._score_sound)
ba.playsound(self._cheer_sound) ba.playsound(self._cheer_sound)
@ -242,15 +250,14 @@ class FootballTeamGame(ba.TeamGameActivity[Player, Team]):
def end_game(self) -> None: def end_game(self) -> None:
results = ba.TeamGameResults() results = ba.TeamGameResults()
for team in self.teams: 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) self.end(results=results, announce_delay=0.8)
def _update_scoreboard(self) -> None: def _update_scoreboard(self) -> None:
win_score = self.settings_raw['Score to Win']
assert self._scoreboard is not None assert self._scoreboard is not None
for team in self.teams: for team in self.teams:
self._scoreboard.set_team_value(team, team.gamedata['score'], self._scoreboard.set_team_value(team, team.score,
win_score) self._score_to_win)
def handlemessage(self, msg: Any) -> Any: def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, stdflag.FlagPickedUpMessage): if isinstance(msg, stdflag.FlagPickedUpMessage):
@ -348,7 +355,7 @@ class FootballCoopGame(ba.CoopGameActivity[Player, Team]):
def __init__(self, settings: Dict[str, Any]): def __init__(self, settings: Dict[str, Any]):
settings['map'] = 'Football Stadium' settings['map'] = 'Football Stadium'
super().__init__(settings) super().__init__(settings)
self._preset = self.settings_raw.get('preset', 'rookie') self._preset = settings.get('preset', 'rookie')
# Load some media we need. # Load some media we need.
self._cheer_sound = ba.getsound('cheer') self._cheer_sound = ba.getsound('cheer')
@ -506,7 +513,7 @@ class FootballCoopGame(ba.CoopGameActivity[Player, Team]):
color=(0.5, 0.4, 0.4)) color=(0.5, 0.4, 0.4))
for team in [self.teams[0], self._bot_team]: for team in [self.teams[0], self._bot_team]:
team.gamedata['score'] = 0 team.score = 0
self.update_scores() self.update_scores()
@ -664,7 +671,7 @@ class FootballCoopGame(ba.CoopGameActivity[Player, Team]):
for team in [self.teams[0], self._bot_team]: for team in [self.teams[0], self._bot_team]:
assert team is not None assert team is not None
if team.id == i: if team.id == i:
team.gamedata['score'] += 7 team.score += 7
# Tell all players (or bots) to celebrate. # Tell all players (or bots) to celebrate.
if i == 0: if i == 0:
@ -677,11 +684,11 @@ class FootballCoopGame(ba.CoopGameActivity[Player, Team]):
# If the good guys scored, add more enemies. # If the good guys scored, add more enemies.
if i == 0: if i == 0:
if self.teams[0].gamedata['score'] == 7: if self.teams[0].score == 7:
assert self._bot_types_7 is not None assert self._bot_types_7 is not None
for bottype in self._bot_types_7: for bottype in self._bot_types_7:
self._spawn_bot(bottype) 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 assert self._bot_types_14 is not None
for bottype in self._bot_types_14: for bottype in self._bot_types_14:
self._spawn_bot(bottype) self._spawn_bot(bottype)
@ -717,7 +724,7 @@ class FootballCoopGame(ba.CoopGameActivity[Player, Team]):
def on_continue(self) -> None: def on_continue(self) -> None:
# Subtract one touchdown from the bots and get them moving again. # Subtract one touchdown from the bots and get them moving again.
assert self._bot_team is not None assert self._bot_team is not None
self._bot_team.gamedata['score'] -= 7 self._bot_team.score -= 7
self._bots.start_moving() self._bots.start_moving()
self.update_scores() self.update_scores()
@ -730,9 +737,8 @@ class FootballCoopGame(ba.CoopGameActivity[Player, Team]):
for team in [self.teams[0], self._bot_team]: for team in [self.teams[0], self._bot_team]:
assert team is not None assert team is not None
assert self._scoreboard is not None assert self._scoreboard is not None
self._scoreboard.set_team_value(team, team.gamedata['score'], self._scoreboard.set_team_value(team, team.score, win_score)
win_score) if team.score >= win_score:
if team.gamedata['score'] >= win_score:
if not have_scoring_team: if not have_scoring_team:
self.scoring_team = team self.scoring_team = team
if team is self._bot_team: if team is self._bot_team:
@ -745,19 +751,19 @@ class FootballCoopGame(ba.CoopGameActivity[Player, Team]):
if self._preset in ['rookie', 'rookie_easy']: if self._preset in ['rookie', 'rookie_easy']:
self._award_achievement('Rookie Football Victory', self._award_achievement('Rookie Football Victory',
sound=False) sound=False)
if self._bot_team.gamedata['score'] == 0: if self._bot_team.score == 0:
self._award_achievement( self._award_achievement(
'Rookie Football Shutout', sound=False) 'Rookie Football Shutout', sound=False)
elif self._preset in ['pro', 'pro_easy']: elif self._preset in ['pro', 'pro_easy']:
self._award_achievement('Pro Football Victory', self._award_achievement('Pro Football Victory',
sound=False) sound=False)
if self._bot_team.gamedata['score'] == 0: if self._bot_team.score == 0:
self._award_achievement('Pro Football Shutout', self._award_achievement('Pro Football Shutout',
sound=False) sound=False)
elif self._preset in ['uber', 'uber_easy']: elif self._preset in ['uber', 'uber_easy']:
self._award_achievement('Uber Football Victory', self._award_achievement('Uber Football Victory',
sound=False) sound=False)
if self._bot_team.gamedata['score'] == 0: if self._bot_team.score == 0:
self._award_achievement( self._award_achievement(
'Uber Football Shutout', sound=False) 'Uber Football Shutout', sound=False)
if (not self._player_has_dropped_bomb 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_time = 2.0 + len(self.initial_player_info) * 1.0
# Respawn them shortly. # Respawn them shortly.
player.gamedata['respawn_timer'] = ba.Timer( player.respawn_timer = ba.Timer(
respawn_time, ba.Call(self.spawn_player_if_exists, player)) 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. # Augment standard behavior.
super().handlemessage(msg) super().handlemessage(msg)

View File

@ -71,11 +71,21 @@ class Player(ba.Player['Team']):
def __init__(self) -> None: def __init__(self) -> None:
self.distance_txt: Optional[ba.Node] = 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]): class Team(ba.Team[Player]):
"""Our team type for this game.""" """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 # ba_meta export game
class RaceGame(ba.TeamGameActivity[Player, Team]): class RaceGame(ba.TeamGameActivity[Player, Team]):
@ -137,8 +147,6 @@ class RaceGame(ba.TeamGameActivity[Player, Team]):
self._race_started = False self._race_started = False
super().__init__(settings) super().__init__(settings)
self._scoreboard = Scoreboard() self._scoreboard = Scoreboard()
if self.settings_raw['Epic Mode']:
self.slow_motion = True
self._score_sound = ba.getsound('score') self._score_sound = ba.getsound('score')
self._swipsound = ba.getsound('swip') self._swipsound = ba.getsound('swip')
self._last_team_time: Optional[float] = None 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._player_order_update_timer: Optional[ba.Timer] = None
self._start_lights: Optional[List[ba.Node]] = None self._start_lights: Optional[List[ba.Node]] = None
self._bomb_spawn_timer: Optional[ba.Timer] = 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]: def get_instance_description(self) -> Union[str, Sequence]:
if (isinstance(self.session, ba.DualTeamSession) if (isinstance(self.session, ba.DualTeamSession)
@ -175,11 +195,7 @@ class RaceGame(ba.TeamGameActivity[Player, Team]):
return 'run 1 lap' return 'run 1 lap'
def on_transition_in(self) -> None: 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() super().on_transition_in()
pts = self.map.get_def_points('race_point') pts = self.map.get_def_points('race_point')
mat = self.race_region_material = ba.Material() mat = self.race_region_material = ba.Material()
mat.add_actions(conditions=('they_have_material', mat.add_actions(conditions=('they_have_material',
@ -222,7 +238,7 @@ class RaceGame(ba.TeamGameActivity[Player, Team]):
assert isinstance(player, Player) assert isinstance(player, Player)
assert isinstance(region, RaceRegion) assert isinstance(region, RaceRegion)
last_region = player.gamedata['last_region'] last_region = player.last_region
this_region = region.index this_region = region.index
if last_region != this_region: if last_region != this_region:
@ -242,29 +258,25 @@ class RaceGame(ba.TeamGameActivity[Player, Team]):
else: else:
# If this player is in first, note that this is the # If this player is in first, note that this is the
# front-most race-point. # front-most race-point.
if player.gamedata['rank'] == 0: if player.rank == 0:
self._front_race_region = this_region 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: if last_region >= len(self._regions) - 2 and this_region == 0:
team = player.team team = player.team
player.gamedata['lap'] = min(self.settings_raw['Laps'], player.lap = min(self._laps, player.lap + 1)
player.gamedata['lap'] + 1)
# In teams mode with all-must-finish on, the team lap # In teams mode with all-must-finish on, the team lap
# value is the min of all team players. # value is the min of all team players.
# Otherwise its the max. # Otherwise its the max.
if isinstance( if isinstance(self.session, ba.DualTeamSession
self.session, ba.DualTeamSession ) and self._entire_team_must_finish:
) and self.settings_raw.get('Entire Team Must Finish'): team.lap = min([p.lap for p in team.players])
team.gamedata['lap'] = min(
[p.gamedata['lap'] for p in team.players])
else: else:
team.gamedata['lap'] = max( team.lap = max([p.lap for p in team.players])
[p.gamedata['lap'] for p in team.players])
# A player is finishing. # 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 # In teams mode, hand out points based on the order
# players come in. # players come in.
@ -278,27 +290,22 @@ class RaceGame(ba.TeamGameActivity[Player, Team]):
# Flash where the player is. # Flash where the player is.
self._flash_player(player, 1.0) self._flash_player(player, 1.0)
player.gamedata['finished'] = True player.finished = True
assert player.actor assert player.actor
player.actor.handlemessage( player.actor.handlemessage(
ba.DieMessage(immediate=True)) ba.DieMessage(immediate=True))
# Makes sure noone behind them passes them in rank # Makes sure noone behind them passes them in rank
# while finishing. # while finishing.
player.gamedata['distance'] = 9999.0 player.distance = 9999.0
# If the whole team has finished the race. # 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) ba.playsound(self._score_sound)
player.team.gamedata['finished'] = True player.team.finished = True
assert self._timer is not None assert self._timer is not None
cur_time = ba.time( elapsed = ba.time() - self._timer.getstarttime()
timeformat=ba.TimeFormat.MILLISECONDS) self._last_team_time = player.team.time = elapsed
start_time = self._timer.getstarttime(
timeformat=ba.TimeFormat.MILLISECONDS)
self._last_team_time = (
player.team.gamedata['time']) = (cur_time -
start_time)
self._check_end_game() self._check_end_game()
# Team has yet to finish. # Team has yet to finish.
@ -321,12 +328,11 @@ class RaceGame(ba.TeamGameActivity[Player, Team]):
}) })
player.actor.node.connectattr( player.actor.node.connectattr(
'torso_position', mathnode, 'input2') 'torso_position', mathnode, 'input2')
tstr = ba.Lstr( tstr = ba.Lstr(resource='lapNumberText',
resource='lapNumberText', subs=[('${CURRENT}',
subs=[('${CURRENT}', str(player.lap + 1)),
str(player.gamedata['lap'] + 1)), ('${TOTAL}', str(self._laps))
('${TOTAL}', ])
str(self.settings_raw['Laps']))])
txtnode = ba.newnode('text', txtnode = ba.newnode('text',
owner=mathnode, owner=mathnode,
attrs={ attrs={
@ -348,19 +354,8 @@ class RaceGame(ba.TeamGameActivity[Player, Team]):
print('Exception printing lap:', exc) print('Exception printing lap:', exc)
def on_team_join(self, team: Team) -> None: def on_team_join(self, team: Team) -> None:
team.gamedata['time'] = None
team.gamedata['lap'] = 0
team.gamedata['finished'] = False
self._update_scoreboard() 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: def on_player_leave(self, player: Player) -> None:
super().on_player_leave(player) 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 # is on (otherwise in teams mode everyone could just leave except the
# leading player to win). # leading player to win).
if (isinstance(self.session, ba.DualTeamSession) 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( ba.screenmessage(ba.Lstr(
translate=('statements', translate=('statements',
'${TEAM} is disqualified because ${PLAYER} left'), '${TEAM} is disqualified because ${PLAYER} left'),
subs=[('${TEAM}', player.team.name), subs=[('${TEAM}', player.team.name),
('${PLAYER}', player.get_name(full=True))]), ('${PLAYER}', player.get_name(full=True))]),
color=(1, 1, 0)) color=(1, 1, 0))
player.team.gamedata['finished'] = True player.team.finished = True
player.team.gamedata['time'] = None player.team.time = None
player.team.gamedata['lap'] = 0 player.team.lap = 0
ba.playsound(ba.getsound('boo')) ba.playsound(ba.getsound('boo'))
for otherplayer in player.team.players: for otherplayer in player.team.players:
otherplayer.gamedata['lap'] = 0 otherplayer.lap = 0
otherplayer.gamedata['finished'] = True otherplayer.finished = True
try: try:
if otherplayer.actor is not None: if otherplayer.actor is not None:
otherplayer.actor.handlemessage(ba.DieMessage()) otherplayer.actor.handlemessage(ba.DieMessage())
@ -393,28 +388,26 @@ class RaceGame(ba.TeamGameActivity[Player, Team]):
def _update_scoreboard(self) -> None: def _update_scoreboard(self) -> None:
for team in self.teams: for team in self.teams:
distances = [ distances = [player.distance for player in team.players]
player.gamedata['distance'] for player in team.players
]
if not distances: if not distances:
teams_dist = 0 teams_dist = 0.0
else: else:
if (isinstance(self.session, ba.DualTeamSession) 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) teams_dist = min(distances)
else: else:
teams_dist = max(distances) teams_dist = max(distances)
self._scoreboard.set_team_value( self._scoreboard.set_team_value(
team, team,
teams_dist, teams_dist,
self.settings_raw['Laps'], self._laps,
flash=(teams_dist >= float(self.settings_raw['Laps'])), flash=(teams_dist >= float(self._laps)),
show_value=False) show_value=False)
def on_begin(self) -> None: def on_begin(self) -> None:
from bastd.actor.onscreentimer import OnScreenTimer from bastd.actor.onscreentimer import OnScreenTimer
super().on_begin() 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.setup_standard_powerup_drops()
self._team_finish_pts = 100 self._team_finish_pts = 100
@ -434,16 +427,15 @@ class RaceGame(ba.TeamGameActivity[Player, Team]):
})) }))
self._timer = OnScreenTimer() self._timer = OnScreenTimer()
if self.settings_raw['Mine Spawning'] != 0: if self._mine_spawning != 0:
self._race_mines = [ self._race_mines = [
RaceMine(point=p, mine=None) RaceMine(point=p, mine=None)
for p in self.map.get_def_points('race_mine') for p in self.map.get_def_points('race_mine')
] ]
if self._race_mines: if self._race_mines:
self._race_mine_timer = ba.Timer( self._race_mine_timer = ba.Timer(0.001 * self._mine_spawning,
0.001 * self.settings_raw['Mine Spawning'], self._update_race_mine,
self._update_race_mine, repeat=True)
repeat=True)
self._scoreboard_timer = ba.Timer(0.25, self._scoreboard_timer = ba.Timer(0.25,
self._update_scoreboard, self._update_scoreboard,
@ -516,16 +508,15 @@ class RaceGame(ba.TeamGameActivity[Player, Team]):
try: try:
assert isinstance(player.actor, PlayerSpaz) assert isinstance(player.actor, PlayerSpaz)
player.actor.connect_controls_to_player() player.actor.connect_controls_to_player()
except Exception as exc: except Exception:
print('Exception in race player connects:', exc) ba.print_exception('Error in race player connects')
assert self._timer is not None assert self._timer is not None
self._timer.start() self._timer.start()
if self.settings_raw['Bomb Spawning'] != 0: if self._bomb_spawning != 0:
self._bomb_spawn_timer = ba.Timer( self._bomb_spawn_timer = ba.Timer(0.001 * self._bomb_spawning,
0.001 * self.settings_raw['Bomb Spawning'], self._spawn_bomb,
self._spawn_bomb, repeat=True)
repeat=True)
self._race_started = True self._race_started = True
@ -542,7 +533,7 @@ class RaceGame(ba.TeamGameActivity[Player, Team]):
except Exception: except Exception:
pos = None pos = None
if pos is not None: if pos is not None:
r_index = player.gamedata['last_region'] r_index = player.last_region
rg1 = self._regions[r_index] rg1 = self._regions[r_index]
r1pt = ba.Vec3(rg1.pos[:3]) r1pt = ba.Vec3(rg1.pos[:3])
rg2 = self._regions[0] if r_index == len( rg2 = self._regions[0] if r_index == len(
@ -550,20 +541,17 @@ class RaceGame(ba.TeamGameActivity[Player, Team]):
r2pt = ba.Vec3(rg2.pos[:3]) r2pt = ba.Vec3(rg2.pos[:3])
r2dist = (pos - r2pt).length() r2dist = (pos - r2pt).length()
amt = 1.0 - (r2dist / (r2pt - r1pt).length()) amt = 1.0 - (r2dist / (r2pt - r1pt).length())
amt = player.gamedata['lap'] + (r_index + amt) * ( amt = player.lap + (r_index + amt) * (1.0 / len(self._regions))
1.0 / len(self._regions)) player.distance = amt
player.gamedata['distance'] = amt
# Sort players by distance and update their ranks. # Sort players by distance and update their ranks.
p_list = [(player.gamedata['distance'], player) p_list = [(player.distance, player) for player in self.players]
for player in self.players]
p_list.sort(reverse=True, key=lambda x: x[0]) p_list.sort(reverse=True, key=lambda x: x[0])
for i, plr in enumerate(p_list): for i, plr in enumerate(p_list):
try: try:
plr[1].gamedata['rank'] = i plr[1].rank = i
if plr[1].actor: if plr[1].actor:
# noinspection PyUnresolvedReferences
node = plr[1].distance_txt node = plr[1].distance_txt
if node: if node:
node.text = str(i + 1) if plr[1].is_alive() else '' 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)) ba.timer(0.95, ba.Call(self._make_mine, m_index))
def spawn_player(self, player: Player) -> ba.Actor: def spawn_player(self, player: Player) -> ba.Actor:
if player.team.gamedata['finished']: if player.team.finished:
# FIXME: This is not type-safe! # FIXME: This is not type-safe!
# This call is expected to always return an Actor! # This call is expected to always return an Actor!
# Perhaps we need something like can_spawn_player()... # Perhaps we need something like can_spawn_player()...
# noinspection PyTypeChecker # noinspection PyTypeChecker
return None # type: ignore 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. # Don't use the full region so we're less likely to spawn off a cliff.
region_scale = 0.8 region_scale = 0.8
@ -674,17 +662,14 @@ class RaceGame(ba.TeamGameActivity[Player, Team]):
def _check_end_game(self) -> None: def _check_end_game(self) -> None:
# If there's no teams left racing, finish. # If there's no teams left racing, finish.
teams_still_in = len( teams_still_in = len([t for t in self.teams if not t.finished])
[t for t in self.teams if not t.gamedata['finished']])
if teams_still_in == 0: if teams_still_in == 0:
self.end_game() self.end_game()
return return
# Count the number of teams that have completed the race. # Count the number of teams that have completed the race.
teams_completed = len([ teams_completed = len(
t for t in self.teams [t for t in self.teams if t.finished and t.time is not None])
if t.gamedata['finished'] and t.gamedata['time'] is not None
])
if teams_completed > 0: if teams_completed > 0:
session = self.session 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 # 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 # 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 assert self._timer is not None
if self._timer.has_started(): if self._timer.has_started():
cur_time = self._timer.getstarttime(
timeformat=ba.TimeFormat.MILLISECONDS)
self._timer.stop( self._timer.stop(
endtime=None if self._last_team_time is None else ( 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() results = ba.TeamGameResults()
for team in self.teams: for team in self.teams:
if team.gamedata['time'] is not None: if team.time is not None:
results.set_team_score(team, team.gamedata['time']) # We store time in seconds, but pass a score in milliseconds.
# If game have ended before we results.set_team_score(team, int(team.time * 1000.0))
# get any result, use 'fail' screen else:
results.set_team_score(team, None)
# We don't announce a winner in ffa mode since its probably been a # 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 # 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. # Augment default behavior.
super().handlemessage(msg) super().handlemessage(msg)
player = msg.getplayer(Player) player = msg.getplayer(Player)
if not player: if not player.finished:
ba.print_error('got no player in PlayerDiedMessage')
return
if not player.gamedata['finished']:
self.respawn_player(player, respawn_time=1) self.respawn_player(player, respawn_time=1)
else: else:
super().handlemessage(msg) super().handlemessage(msg)

View File

@ -35,7 +35,6 @@ if TYPE_CHECKING:
class HockeyStadium(ba.Map): class HockeyStadium(ba.Map):
"""Stadium map used for ice hockey games.""" """Stadium map used for ice hockey games."""
# noinspection PyUnresolvedReferences
from bastd.mapdata import hockey_stadium as defs from bastd.mapdata import hockey_stadium as defs
name = 'Hockey Stadium' name = 'Hockey Stadium'
@ -183,7 +182,6 @@ class FootballStadium(ba.Map):
class Bridgit(ba.Map): class Bridgit(ba.Map):
"""Map with a narrow bridge in the middle.""" """Map with a narrow bridge in the middle."""
# noinspection PyUnresolvedReferences
from bastd.mapdata import bridgit as defs from bastd.mapdata import bridgit as defs
name = 'Bridgit' name = 'Bridgit'
@ -278,7 +276,6 @@ class Bridgit(ba.Map):
class BigG(ba.Map): class BigG(ba.Map):
"""Large G shaped map for racing""" """Large G shaped map for racing"""
# noinspection PyUnresolvedReferences
from bastd.mapdata import big_g as defs from bastd.mapdata import big_g as defs
name = 'Big G' name = 'Big G'
@ -375,7 +372,6 @@ class BigG(ba.Map):
class Roundabout(ba.Map): class Roundabout(ba.Map):
"""CTF map featuring two platforms and a long way around between them""" """CTF map featuring two platforms and a long way around between them"""
# noinspection PyUnresolvedReferences
from bastd.mapdata import roundabout as defs from bastd.mapdata import roundabout as defs
name = 'Roundabout' name = 'Roundabout'
@ -469,7 +465,6 @@ class Roundabout(ba.Map):
class MonkeyFace(ba.Map): class MonkeyFace(ba.Map):
"""Map sorta shaped like a monkey face; teehee!""" """Map sorta shaped like a monkey face; teehee!"""
# noinspection PyUnresolvedReferences
from bastd.mapdata import monkey_face as defs from bastd.mapdata import monkey_face as defs
name = 'Monkey Face' name = 'Monkey Face'
@ -563,7 +558,6 @@ class MonkeyFace(ba.Map):
class ZigZag(ba.Map): class ZigZag(ba.Map):
"""A very long zig-zaggy map""" """A very long zig-zaggy map"""
# noinspection PyUnresolvedReferences
from bastd.mapdata import zig_zag as defs from bastd.mapdata import zig_zag as defs
name = 'Zigzag' name = 'Zigzag'
@ -657,7 +651,6 @@ class ZigZag(ba.Map):
class ThePad(ba.Map): class ThePad(ba.Map):
"""A simple square shaped map with a raised edge.""" """A simple square shaped map with a raised edge."""
# noinspection PyUnresolvedReferences
from bastd.mapdata import the_pad as defs from bastd.mapdata import the_pad as defs
name = 'The Pad' name = 'The Pad'
@ -738,7 +731,6 @@ class ThePad(ba.Map):
class DoomShroom(ba.Map): class DoomShroom(ba.Map):
"""A giant mushroom. Of doom.""" """A giant mushroom. Of doom."""
# noinspection PyUnresolvedReferences
from bastd.mapdata import doom_shroom as defs from bastd.mapdata import doom_shroom as defs
name = 'Doom Shroom' name = 'Doom Shroom'
@ -831,7 +823,6 @@ class DoomShroom(ba.Map):
class LakeFrigid(ba.Map): class LakeFrigid(ba.Map):
"""An icy lake fit for racing.""" """An icy lake fit for racing."""
# noinspection PyUnresolvedReferences
from bastd.mapdata import lake_frigid as defs from bastd.mapdata import lake_frigid as defs
name = 'Lake Frigid' name = 'Lake Frigid'
@ -912,7 +903,6 @@ class LakeFrigid(ba.Map):
class TipTop(ba.Map): class TipTop(ba.Map):
"""A pointy map good for king-of-the-hill-ish games.""" """A pointy map good for king-of-the-hill-ish games."""
# noinspection PyUnresolvedReferences
from bastd.mapdata import tip_top as defs from bastd.mapdata import tip_top as defs
name = 'Tip Top' name = 'Tip Top'
@ -984,7 +974,6 @@ class TipTop(ba.Map):
class CragCastle(ba.Map): class CragCastle(ba.Map):
"""A lovely castle map.""" """A lovely castle map."""
# noinspection PyUnresolvedReferences
from bastd.mapdata import crag_castle as defs from bastd.mapdata import crag_castle as defs
name = 'Crag Castle' name = 'Crag Castle'
@ -1185,7 +1174,6 @@ class TowerD(ba.Map):
class HappyThoughts(ba.Map): class HappyThoughts(ba.Map):
"""Flying map.""" """Flying map."""
# noinspection PyUnresolvedReferences
from bastd.mapdata import happy_thoughts as defs from bastd.mapdata import happy_thoughts as defs
name = 'Happy Thoughts' name = 'Happy Thoughts'
@ -1291,7 +1279,6 @@ class HappyThoughts(ba.Map):
class StepRightUp(ba.Map): class StepRightUp(ba.Map):
"""Wide stepped map good for CTF or Assault.""" """Wide stepped map good for CTF or Assault."""
# noinspection PyUnresolvedReferences
from bastd.mapdata import step_right_up as defs from bastd.mapdata import step_right_up as defs
name = 'Step Right Up' name = 'Step Right Up'

View File

@ -268,7 +268,6 @@ class ColorPickerExact(popup.PopupWindow):
# color to the delegate, so start doing that... # color to the delegate, so start doing that...
self._update_for_color() self._update_for_color()
# noinspection PyUnresolvedReferences
def _update_for_color(self) -> None: def _update_for_color(self) -> None:
if not self.root_widget: if not self.root_widget:
return return

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-20 for Ballistica version 1.5.0 build 20025</em></h4> <h4><em>last updated on 2020-05-21 for Ballistica version 1.5.0 build 20025</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>

View File

@ -37,7 +37,6 @@ from typing import TYPE_CHECKING
# Pull in some standard snippets we want to expose. # Pull in some standard snippets we want to expose.
# pylint: disable=unused-import # pylint: disable=unused-import
# noinspection PyUnresolvedReferences
from efrotools.snippets import ( from efrotools.snippets import (
PROJROOT, snippets_main, formatcode, formatscripts, formatmakefile, PROJROOT, snippets_main, formatcode, formatscripts, formatmakefile,
cpplint, pylint, runpylint, mypy, runmypy, dmypy, tool_config_install, cpplint, pylint, runpylint, mypy, runmypy, dmypy, tool_config_install,