This commit is contained in:
Eric Froemling 2020-06-04 21:20:25 -07:00
parent 690ca375af
commit 431e6e80bf
16 changed files with 210 additions and 196 deletions

View File

@ -35,6 +35,7 @@
<w>actionhero</w> <w>actionhero</w>
<w>activityname</w> <w>activityname</w>
<w>activityplayer</w> <w>activityplayer</w>
<w>activityteam</w>
<w>activitytypes</w> <w>activitytypes</w>
<w>activityutils</w> <w>activityutils</w>
<w>actorclass</w> <w>actorclass</w>
@ -2178,6 +2179,7 @@
<w>yapf</w> <w>yapf</w>
<w>yapfconfig</w> <w>yapfconfig</w>
<w>yinyang</w> <w>yinyang</w>
<w>yoffs</w>
<w>ypos</w> <w>ypos</w>
<w>yres</w> <w>yres</w>
<w>yscl</w> <w>yscl</w>

View File

@ -557,7 +557,7 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
sessionplayer.resetinput() sessionplayer.resetinput()
sessionteam = sessionplayer.sessionteam sessionteam = sessionplayer.sessionteam
assert sessionplayer in sessionteam.players assert sessionplayer in sessionteam.players
team = sessionteam.gameteam team = sessionteam.activityteam
assert team is not None assert team is not None
sessionplayer.setactivity(self) sessionplayer.setactivity(self)
with _ba.Context(self): with _ba.Context(self):
@ -587,7 +587,7 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
player: Any = sessionplayer.activityplayer player: Any = sessionplayer.activityplayer
assert isinstance(player, self._playertype) assert isinstance(player, self._playertype)
team: Any = sessionplayer.sessionteam.gameteam team: Any = sessionplayer.sessionteam.activityteam
assert isinstance(team, self._teamtype) assert isinstance(team, self._teamtype)
assert player in team.players assert player in team.players
@ -629,7 +629,7 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
assert not self.expired assert not self.expired
with _ba.Context(self): with _ba.Context(self):
sessionteam.gameteam = team = self.create_team(sessionteam) sessionteam.activityteam = team = self.create_team(sessionteam)
team.postinit(sessionteam) team.postinit(sessionteam)
self.teams.append(team) self.teams.append(team)
try: try:
@ -643,9 +643,9 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
(internal) (internal)
""" """
assert not self.expired assert not self.expired
assert sessionteam.gameteam is not None assert sessionteam.activityteam is not None
team: Any = sessionteam.gameteam team: Any = sessionteam.activityteam
assert isinstance(team, self._teamtype) assert isinstance(team, self._teamtype)
assert team in self.teams assert team in self.teams
@ -663,7 +663,7 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
except Exception: except Exception:
print_exception(f'Error on leave for {team} in {self}.') print_exception(f'Error on leave for {team} in {self}.')
sessionteam.gameteam = None sessionteam.activityteam = None
# Add the team to a list to keep it around for a while. This is # Add the team to a list to keep it around for a while. This is
# to discourage logic from firing on team object death, which # to discourage logic from firing on team object death, which
@ -854,7 +854,7 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
try: try:
sessionteam = team.sessionteam sessionteam = team.sessionteam
sessionteam.gameteam = None sessionteam.activityteam = None
except SessionTeamNotFoundError: except SessionTeamNotFoundError:
# It is expected that Team objects may last longer than # It is expected that Team objects may last longer than
# the SessionTeam they came from (game objects may hold # the SessionTeam they came from (game objects may hold

View File

@ -346,7 +346,7 @@ class CoopSession(Session):
# Generic team games. # Generic team games.
if isinstance(results, GameResults): if isinstance(results, GameResults):
playerinfos = results.playerinfos playerinfos = results.playerinfos
score = results.get_team_score(results.sessionteams[0]) score = results.get_sessionteam_score(results.sessionteams[0])
fail_message = None fail_message = None
score_order = ('decreasing' score_order = ('decreasing'
if results.lower_is_better else 'increasing') if results.lower_is_better else 'increasing')

View File

@ -50,7 +50,6 @@ class GameResults:
""" """
def __init__(self) -> None: def __init__(self) -> None:
"""Instantiate a results instance."""
self._game_set = False self._game_set = False
self._scores: Dict[int, Tuple[ReferenceType[ba.SessionTeam], self._scores: Dict[int, Tuple[ReferenceType[ba.SessionTeam],
Optional[int]]] = {} Optional[int]]] = {}
@ -76,7 +75,7 @@ class GameResults:
self._scoretype = scoreconfig.scoretype self._scoretype = scoreconfig.scoretype
def set_team_score(self, team: ba.Team, score: Optional[int]) -> None: def set_team_score(self, team: ba.Team, score: Optional[int]) -> None:
"""Set the score for a given ba.Team. """Set the score for a given team.
This can be a number or None. This can be a number or None.
(see the none_is_winner arg in the constructor) (see the none_is_winner arg in the constructor)
@ -84,7 +83,8 @@ class GameResults:
sessionteam = team.sessionteam sessionteam = team.sessionteam
self._scores[sessionteam.id] = (weakref.ref(sessionteam), score) self._scores[sessionteam.id] = (weakref.ref(sessionteam), score)
def get_team_score(self, sessionteam: ba.SessionTeam) -> Optional[int]: def get_sessionteam_score(self,
sessionteam: ba.SessionTeam) -> Optional[int]:
"""Return the score for a given ba.SessionTeam.""" """Return the score for a given ba.SessionTeam."""
for score in list(self._scores.values()): for score in list(self._scores.values()):
if score[0]() is sessionteam: if score[0]() is sessionteam:
@ -106,12 +106,13 @@ class GameResults:
teams.append(team) teams.append(team)
return teams return teams
def has_score_for_team(self, sessionteam: ba.SessionTeam) -> bool: def has_score_for_sessionteam(self, sessionteam: ba.SessionTeam) -> bool:
"""Return whether there is a score for a given team.""" """Return whether there is a score for a given session-team."""
return any(s[0]() is sessionteam for s in self._scores.values()) return any(s[0]() is sessionteam for s in self._scores.values())
def get_team_score_str(self, sessionteam: ba.SessionTeam) -> ba.Lstr: def get_sessionteam_score_str(self,
"""Return the score for the given ba.Team as an Lstr. sessionteam: ba.SessionTeam) -> ba.Lstr:
"""Return the score for the given session-team as an Lstr.
(properly formatted for the score type.) (properly formatted for the score type.)
""" """
@ -169,7 +170,7 @@ class GameResults:
return self._lower_is_better return self._lower_is_better
@property @property
def winning_team(self) -> Optional[ba.SessionTeam]: def winning_sessionteam(self) -> Optional[ba.SessionTeam]:
"""The winning ba.SessionTeam if there is exactly one, or else None.""" """The winning ba.SessionTeam if there is exactly one, or else None."""
if not self._game_set: if not self._game_set:
raise RuntimeError("Can't get winners until game is set.") raise RuntimeError("Can't get winners until game is set.")

View File

@ -262,12 +262,12 @@ class MultiTeamSession(Session):
_ba.timer(delay, Call(_ba.playsound, _ba.getsound('boxingBell'))) _ba.timer(delay, Call(_ba.playsound, _ba.getsound('boxingBell')))
if announce_winning_team: if announce_winning_team:
winning_team = results.winning_team winning_sessionteam = results.winning_sessionteam
if winning_team is not None: if winning_sessionteam is not None:
# Have all players celebrate. # Have all players celebrate.
celebrate_msg = CelebrateMessage(duration=10.0) celebrate_msg = CelebrateMessage(duration=10.0)
assert winning_team.gameteam is not None assert winning_sessionteam.activityteam is not None
for player in winning_team.gameteam.players: for player in winning_sessionteam.activityteam.players:
if player.actor: if player.actor:
player.actor.handlemessage(celebrate_msg) player.actor.handlemessage(celebrate_msg)
cameraflash() cameraflash()
@ -278,11 +278,11 @@ class MultiTeamSession(Session):
else: else:
wins_resource = 'winsTeamText' wins_resource = 'winsTeamText'
wins_text = Lstr(resource=wins_resource, wins_text = Lstr(resource=wins_resource,
subs=[('${NAME}', winning_team.name)]) subs=[('${NAME}', winning_sessionteam.name)])
activity.show_zoom_message( activity.show_zoom_message(
wins_text, wins_text,
scale=0.85, scale=0.85,
color=normalized_color(winning_team.color), color=normalized_color(winning_sessionteam.color),
) )

View File

@ -81,6 +81,7 @@ class Player(Generic[TeamType]):
actor: Optional[ba.Actor] actor: Optional[ba.Actor]
color: Sequence[float] color: Sequence[float]
highlight: Sequence[float] highlight: Sequence[float]
_team: TeamType _team: TeamType
_sessionplayer: ba.SessionPlayer _sessionplayer: ba.SessionPlayer
_nodeactor: Optional[ba.NodeActor] _nodeactor: Optional[ba.NodeActor]
@ -118,7 +119,7 @@ class Player(Generic[TeamType]):
self.character = sessionplayer.character self.character = sessionplayer.character
self.color = sessionplayer.color self.color = sessionplayer.color
self.highlight = sessionplayer.highlight self.highlight = sessionplayer.highlight
self._team = cast(TeamType, sessionplayer.sessionteam.gameteam) self._team = cast(TeamType, sessionplayer.sessionteam.activityteam)
assert self._team is not None assert self._team is not None
self._customdata = {} self._customdata = {}
self._expired = False self._expired = False

View File

@ -306,7 +306,7 @@ class Session:
# Remove their Team from the Activity. # Remove their Team from the Activity.
if activity is not None: if activity is not None:
if sessionteam.gameteam in activity.teams: if sessionteam.activityteam in activity.teams:
activity.remove_team(sessionteam) activity.remove_team(sessionteam)
else: else:
print('Team not found in Activity in on_player_leave.') print('Team not found in Activity in on_player_leave.')

View File

@ -85,7 +85,7 @@ class SessionTeam:
self.color = tuple(color) self.color = tuple(color)
self.players = [] self.players = []
self.customdata = {} self.customdata = {}
self.gameteam: Optional[Team] = None self.activityteam: Optional[Team] = None
def leave(self) -> None: def leave(self) -> None:
"""(internal)""" """(internal)"""

View File

@ -194,12 +194,15 @@ def uicleanupcheck(obj: Any, widget: ba.Widget) -> None:
if not isinstance(widget, _ba.Widget): if not isinstance(widget, _ba.Widget):
raise TypeError('widget arg is not a ba.Widget') raise TypeError('widget arg is not a ba.Widget')
def foobar() -> None: if bool(False):
"""Just testing."""
if DEBUG_UI_CLEANUP_CHECKS: def foobar() -> None:
print('uicleanupcheck widget dying...') """Just testing."""
if DEBUG_UI_CLEANUP_CHECKS:
print('uicleanupcheck widget dying...')
widget.add_delete_callback(foobar)
widget.add_delete_callback(foobar)
_ba.app.uicleanupchecks.append( _ba.app.uicleanupchecks.append(
UICleanupCheck(obj=weakref.ref(obj), UICleanupCheck(obj=weakref.ref(obj),
widget=widget, widget=widget,

View File

@ -88,16 +88,16 @@ class MultiTeamScoreScreenActivity(ScoreScreenActivity):
def _get_prec_score(p_rec: ba.PlayerRecord) -> Optional[int]: def _get_prec_score(p_rec: ba.PlayerRecord) -> Optional[int]:
if is_free_for_all and results is not None: if is_free_for_all and results is not None:
assert isinstance(results, ba.GameResults) assert isinstance(results, ba.GameResults)
assert p_rec.team.gameteam is not None assert p_rec.team.activityteam is not None
val = results.get_team_score(p_rec.team) val = results.get_sessionteam_score(p_rec.team)
return val return val
return p_rec.accumscore return p_rec.accumscore
def _get_prec_score_str(p_rec: ba.PlayerRecord) -> Union[str, ba.Lstr]: def _get_prec_score_str(p_rec: ba.PlayerRecord) -> Union[str, ba.Lstr]:
if is_free_for_all and results is not None: if is_free_for_all and results is not None:
assert isinstance(results, ba.GameResults) assert isinstance(results, ba.GameResults)
assert p_rec.team.gameteam is not None assert p_rec.team.activityteam is not None
val = results.get_team_score_str(p_rec.team) val = results.get_sessionteam_score_str(p_rec.team)
assert val is not None assert val is not None
return val return val
return str(p_rec.accumscore) return str(p_rec.accumscore)
@ -139,18 +139,18 @@ class MultiTeamScoreScreenActivity(ScoreScreenActivity):
# Just want living player entries. # Just want living player entries.
player_records = [p[2] for p in player_records_scores if p[2]] player_records = [p[2] for p in player_records_scores if p[2]]
v_offs = -140.0 + spacing * len(player_records) * 0.5 voffs = -140.0 + spacing * len(player_records) * 0.5
def _txt(x_offs: float, def _txt(xoffs: float,
y_offs: float, yoffs: float,
text: ba.Lstr, text: ba.Lstr,
h_align: Text.HAlign = Text.HAlign.RIGHT, h_align: Text.HAlign = Text.HAlign.RIGHT,
extrascale: float = 1.0, extrascale: float = 1.0,
maxwidth: Optional[float] = 120.0) -> None: maxwidth: Optional[float] = 120.0) -> None:
Text(text, Text(text,
color=(0.5, 0.5, 0.6, 0.5), color=(0.5, 0.5, 0.6, 0.5),
position=(ts_h_offs + x_offs * scale, position=(ts_h_offs + xoffs * scale,
ts_v_offset + (v_offs + y_offs + 4.0) * scale), ts_v_offset + (voffs + yoffs + 4.0) * scale),
h_align=h_align, h_align=h_align,
v_align=Text.VAlign.CENTER, v_align=Text.VAlign.CENTER,
scale=0.8 * scale * extrascale, scale=0.8 * scale * extrascale,
@ -193,7 +193,7 @@ class MultiTeamScoreScreenActivity(ScoreScreenActivity):
maxwidth: float = 70.0) -> None: maxwidth: float = 70.0) -> None:
Text(text, Text(text,
position=(ts_h_offs + x_offs * scale, position=(ts_h_offs + x_offs * scale,
ts_v_offset + (v_offs + 15) * scale), ts_v_offset + (voffs + 15) * scale),
scale=scale, scale=scale,
color=(1.0, 0.9, 0.5, 1.0) if highlight else color=(1.0, 0.9, 0.5, 1.0) if highlight else
(0.5, 0.5, 0.6, 0.5), (0.5, 0.5, 0.6, 0.5),
@ -205,10 +205,10 @@ class MultiTeamScoreScreenActivity(ScoreScreenActivity):
for playerrec in player_records: for playerrec in player_records:
tdelay += 0.05 tdelay += 0.05
v_offs -= spacing voffs -= spacing
Image(playerrec.get_icon(), Image(playerrec.get_icon(),
position=(ts_h_offs - 12 * scale, position=(ts_h_offs - 12 * scale,
ts_v_offset + (v_offs + 15.0) * scale), ts_v_offset + (voffs + 15.0) * scale),
scale=(30.0 * scale, 30.0 * scale), scale=(30.0 * scale, 30.0 * scale),
transition=Image.Transition.IN_LEFT, transition=Image.Transition.IN_LEFT,
transition_delay=tdelay).autoretain() transition_delay=tdelay).autoretain()
@ -216,7 +216,7 @@ class MultiTeamScoreScreenActivity(ScoreScreenActivity):
maxwidth=160, maxwidth=160,
scale=0.75 * scale, scale=0.75 * scale,
position=(ts_h_offs + 10.0 * scale, position=(ts_h_offs + 10.0 * scale,
ts_v_offset + (v_offs + 15) * scale), ts_v_offset + (voffs + 15) * scale),
h_align=Text.HAlign.LEFT, h_align=Text.HAlign.LEFT,
v_align=Text.VAlign.CENTER, v_align=Text.VAlign.CENTER,
color=ba.safecolor(playerrec.team.color + (1, )), color=ba.safecolor(playerrec.team.color + (1, )),

View File

@ -61,7 +61,7 @@ class TeamSeriesVictoryScoreScreenActivity(MultiTeamScoreScreenActivity):
self._show_up_next = False self._show_up_next = False
self._custom_continue_message = sval self._custom_continue_message = sval
super().on_begin() super().on_begin()
winning_team = self.settings_raw['winner'] winning_sessionteam = self.settings_raw['winner']
# Pause a moment before playing victory music. # Pause a moment before playing victory music.
ba.timer(0.6, ba.WeakCall(self._play_victory_music)) ba.timer(0.6, ba.WeakCall(self._play_victory_music))
@ -171,7 +171,7 @@ class TeamSeriesVictoryScoreScreenActivity(MultiTeamScoreScreenActivity):
if not self._is_ffa: if not self._is_ffa:
mvp, mvp_name = None, None mvp, mvp_name = None, None
for entry in player_entries: for entry in player_entries:
if entry[2].team == winning_team: if entry[2].team == winning_sessionteam:
mvp = entry[2] mvp = entry[2]
mvp_name = entry[1] mvp_name = entry[1]
break break

View File

@ -177,7 +177,8 @@ class MeteorShowerGame(ba.TeamGameActivity[Player, Team]):
else: else:
# Default handler: # Default handler:
super().handlemessage(msg) return super().handlemessage(msg)
return None
def _check_end_game(self) -> None: def _check_end_game(self) -> None:
living_team_count = 0 living_team_count = 0

View File

@ -532,16 +532,13 @@ class RaceGame(ba.TeamGameActivity[Player, Team]):
self._race_started = True self._race_started = True
def _update_player_order(self) -> None: def _update_player_order(self) -> None:
# FIXME: tidy this up
# Calc all player distances. # Calc all player distances.
for player in self.players: for player in self.players:
pos: Optional[ba.Vec3] pos: Optional[ba.Vec3]
try: try:
assert isinstance(player.actor, PlayerSpaz) pos = player.position
assert player.actor.node except ba.NotFoundError:
pos = ba.Vec3(player.actor.node.position)
except Exception:
pos = None pos = None
if pos is not None: if pos is not None:
r_index = player.last_region r_index = player.last_region
@ -560,14 +557,11 @@ class RaceGame(ba.TeamGameActivity[Player, Team]):
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: plr[1].rank = i
plr[1].rank = i if plr[1].actor:
if plr[1].actor: 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 ''
except Exception:
ba.print_exception('error updating player orders')
def _spawn_bomb(self) -> None: def _spawn_bomb(self) -> None:
if self._front_race_region is None: if self._front_race_region is None:
@ -730,7 +724,8 @@ class RaceGame(ba.TeamGameActivity[Player, Team]):
def handlemessage(self, msg: Any) -> Any: def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, ba.PlayerDiedMessage): if isinstance(msg, ba.PlayerDiedMessage):
super().handlemessage(msg) # Augment default behavior. # Augment default behavior.
super().handlemessage(msg)
player = msg.getplayer(Player) player = msg.getplayer(Player)
if not player.finished: if not player.finished:
self.respawn_player(player, respawn_time=1) self.respawn_player(player, respawn_time=1)

View File

@ -38,6 +38,7 @@ class PlaylistTypeVars:
from ba.internal import (get_default_teams_playlist, from ba.internal import (get_default_teams_playlist,
get_default_free_for_all_playlist) get_default_free_for_all_playlist)
self.sessiontype: Type[ba.Session] self.sessiontype: Type[ba.Session]
if issubclass(sessiontype, ba.DualTeamSession): if issubclass(sessiontype, ba.DualTeamSession):
play_mode_name = ba.Lstr(resource='playModes.teamsText', play_mode_name = ba.Lstr(resource='playModes.teamsText',
fallback_resource='teamsText') fallback_resource='teamsText')
@ -47,6 +48,7 @@ class PlaylistTypeVars:
self.window_title_name = ba.Lstr(resource='playModes.teamsText', self.window_title_name = ba.Lstr(resource='playModes.teamsText',
fallback_resource='teamsText') fallback_resource='teamsText')
self.sessiontype = ba.DualTeamSession self.sessiontype = ba.DualTeamSession
elif issubclass(sessiontype, ba.FreeForAllSession): elif issubclass(sessiontype, ba.FreeForAllSession):
play_mode_name = ba.Lstr(resource='playModes.freeForAllText', play_mode_name = ba.Lstr(resource='playModes.freeForAllText',
fallback_resource='freeForAllText') fallback_resource='freeForAllText')
@ -57,6 +59,7 @@ class PlaylistTypeVars:
resource='playModes.freeForAllText', resource='playModes.freeForAllText',
fallback_resource='freeForAllText') fallback_resource='freeForAllText')
self.sessiontype = ba.FreeForAllSession self.sessiontype = ba.FreeForAllSession
else: else:
raise TypeError('playlist type vars undefined for session type: ' + raise TypeError('playlist type vars undefined for session type: ' +
str(sessiontype)) str(sessiontype))

View File

@ -42,7 +42,7 @@ class PlaylistBrowserWindow(ba.Window):
origin_widget: ba.Widget = None): origin_widget: ba.Widget = None):
# pylint: disable=too-many-statements # pylint: disable=too-many-statements
# pylint: disable=cyclic-import # pylint: disable=cyclic-import
from bastd.ui import playlist from bastd.ui.playlist import PlaylistTypeVars
# If they provided an origin-widget, scale up from that. # If they provided an origin-widget, scale up from that.
scale_origin: Optional[Tuple[float, float]] scale_origin: Optional[Tuple[float, float]]
@ -62,8 +62,8 @@ class PlaylistBrowserWindow(ba.Window):
ba.app.main_window = 'Free-for-All Game Select' ba.app.main_window = 'Free-for-All Game Select'
ba.set_analytics_screen('FreeForAll Window') ba.set_analytics_screen('FreeForAll Window')
else: else:
raise TypeError(f'invalid sessiontype: {sessiontype}') raise TypeError(f'Invalid sessiontype: {sessiontype}.')
self._pvars = playlist.PlaylistTypeVars(sessiontype) self._pvars = PlaylistTypeVars(sessiontype)
self._sessiontype = sessiontype self._sessiontype = sessiontype
@ -74,39 +74,45 @@ class PlaylistBrowserWindow(ba.Window):
# On new installations, go ahead and create a few playlists # On new installations, go ahead and create a few playlists
# besides the hard-coded default one: # besides the hard-coded default one:
if not _ba.get_account_misc_val('madeStandardPlaylists', False): if not _ba.get_account_misc_val('madeStandardPlaylists', False):
# yapf: disable
_ba.add_transaction({ _ba.add_transaction({
'type': 'ADD_PLAYLIST', 'type':
'playlistType': 'Free-for-All', 'ADD_PLAYLIST',
'playlistType':
'Free-for-All',
'playlistName': 'playlistName':
ba.Lstr(resource='singleGamePlaylistNameText' ba.Lstr(resource='singleGamePlaylistNameText'
).evaluate().replace( ).evaluate().replace(
'${GAME}', '${GAME}',
ba.Lstr( ba.Lstr(translate=('gameNames',
translate=('gameNames', 'Death Match')).evaluate()),
'Death Match')).evaluate()),
'playlist': [ 'playlist': [
{'type': 'bs_death_match.DeathMatchGame', {
'settings': { 'type': 'bs_death_match.DeathMatchGame',
'Epic Mode': False, 'settings': {
'Kills to Win Per Player': 10, 'Epic Mode': False,
'Respawn Times': 1.0, 'Kills to Win Per Player': 10,
'Time Limit': 300, 'Respawn Times': 1.0,
'map': 'Doom Shroom'} 'Time Limit': 300,
'map': 'Doom Shroom'
}
},
{
'type': 'bs_death_match.DeathMatchGame',
'settings': {
'Epic Mode': False,
'Kills to Win Per Player': 10,
'Respawn Times': 1.0,
'Time Limit': 300,
'map': 'Crag Castle'
}
}, },
{'type': 'bs_death_match.DeathMatchGame',
'settings': {
'Epic Mode': False,
'Kills to Win Per Player': 10,
'Respawn Times': 1.0,
'Time Limit': 300,
'map': 'Crag Castle'}
}
] ]
}) })
_ba.add_transaction({ _ba.add_transaction({
'type': 'ADD_PLAYLIST', 'type':
'playlistType': 'Team Tournament', 'ADD_PLAYLIST',
'playlistType':
'Team Tournament',
'playlistName': 'playlistName':
ba.Lstr( ba.Lstr(
resource='singleGamePlaylistNameText' resource='singleGamePlaylistNameText'
@ -115,92 +121,102 @@ class PlaylistBrowserWindow(ba.Window):
ba.Lstr(translate=('gameNames', ba.Lstr(translate=('gameNames',
'Capture the Flag')).evaluate()), 'Capture the Flag')).evaluate()),
'playlist': [ 'playlist': [
{'type': 'bs_capture_the_flag.CTFGame', {
'settings': { 'type': 'bs_capture_the_flag.CTFGame',
'map': 'Bridgit', 'settings': {
'Score to Win': 3, 'map': 'Bridgit',
'Flag Idle Return Time': 30, 'Score to Win': 3,
'Flag Touch Return Time': 0, 'Flag Idle Return Time': 30,
'Respawn Times': 1.0, 'Flag Touch Return Time': 0,
'Time Limit': 600, 'Respawn Times': 1.0,
'Epic Mode': False} 'Time Limit': 600,
'Epic Mode': False
}
}, },
{'type': 'bs_capture_the_flag.CTFGame', {
'settings': { 'type': 'bs_capture_the_flag.CTFGame',
'map': 'Roundabout', 'settings': {
'Score to Win': 2, 'map': 'Roundabout',
'Flag Idle Return Time': 30, 'Score to Win': 2,
'Flag Touch Return Time': 0, 'Flag Idle Return Time': 30,
'Respawn Times': 1.0, 'Flag Touch Return Time': 0,
'Time Limit': 600, 'Respawn Times': 1.0,
'Epic Mode': False} 'Time Limit': 600,
'Epic Mode': False
}
},
{
'type': 'bs_capture_the_flag.CTFGame',
'settings': {
'map': 'Tip Top',
'Score to Win': 2,
'Flag Idle Return Time': 30,
'Flag Touch Return Time': 3,
'Respawn Times': 1.0,
'Time Limit': 300,
'Epic Mode': False
}
}, },
{'type': 'bs_capture_the_flag.CTFGame',
'settings': {
'map': 'Tip Top',
'Score to Win': 2,
'Flag Idle Return Time': 30,
'Flag Touch Return Time': 3,
'Respawn Times': 1.0,
'Time Limit': 300,
'Epic Mode': False}
}
] ]
}) })
_ba.add_transaction({ _ba.add_transaction({
'type': 'ADD_PLAYLIST', 'type':
'playlistType': 'Team Tournament', 'ADD_PLAYLIST',
'playlistType':
'Team Tournament',
'playlistName': 'playlistName':
ba.Lstr(translate=('playlistNames', ba.Lstr(translate=('playlistNames', 'Just Sports')
'Just Sports')).evaluate(), ).evaluate(),
'playlist': [ 'playlist': [
{'type': 'bs_hockey.HockeyGame', {
'settings': { 'type': 'bs_hockey.HockeyGame',
'Time Limit': 0, 'settings': {
'map': 'Hockey Stadium', 'Time Limit': 0,
'Score to Win': 1, 'map': 'Hockey Stadium',
'Respawn Times': 1.0} 'Score to Win': 1,
'Respawn Times': 1.0
}
},
{
'type': 'bs_football.FootballTeamGame',
'settings': {
'Time Limit': 0,
'map': 'Football Stadium',
'Score to Win': 21,
'Respawn Times': 1.0
}
}, },
{'type': 'bs_football.FootballTeamGame',
'settings': {
'Time Limit': 0,
'map': 'Football Stadium',
'Score to Win': 21,
'Respawn Times': 1.0}
}
] ]
}) })
_ba.add_transaction({ _ba.add_transaction({
'type': 'ADD_PLAYLIST', 'type':
'playlistType': 'Free-for-All', 'ADD_PLAYLIST',
'playlistType':
'Free-for-All',
'playlistName': 'playlistName':
ba.Lstr(translate=('playlistNames', ba.Lstr(translate=('playlistNames', 'Just Epic')
'Just Epic')).evaluate(), ).evaluate(),
'playlist': [ 'playlist': [{
{'type': 'bs_elimination.EliminationGame', 'type': 'bs_elimination.EliminationGame',
'settings': { 'settings': {
'Time Limit': 120, 'Time Limit': 120,
'map': 'Tip Top', 'map': 'Tip Top',
'Respawn Times': 1.0, 'Respawn Times': 1.0,
'Lives Per Player': 1, 'Lives Per Player': 1,
'Epic Mode': 1} 'Epic Mode': 1
} }
] }]
}) })
_ba.add_transaction({ _ba.add_transaction({
'type': 'SET_MISC_VAL', 'type': 'SET_MISC_VAL',
'name': 'madeStandardPlaylists', 'name': 'madeStandardPlaylists',
'value': True 'value': True
}) })
# yapf: enable
_ba.run_transactions() _ba.run_transactions()
# Get the current selection (if any). # Get the current selection (if any).
try: self._selected_playlist = ba.app.config.get(self._pvars.config_name +
self._selected_playlist = ba.app.config[self._pvars.config_name + ' Playlist Selection')
' Playlist Selection']
except Exception:
self._selected_playlist = None
self._width = 900 if ba.app.small_ui else 800 self._width = 900 if ba.app.small_ui else 800
x_inset = 50 if ba.app.small_ui else 0 x_inset = 50 if ba.app.small_ui else 0
@ -270,12 +286,13 @@ class PlaylistBrowserWindow(ba.Window):
self._config_name_full = self._pvars.config_name + ' Playlists' self._config_name_full = self._pvars.config_name + ' Playlists'
self._last_config = None self._last_config = None
# update now and once per second.. (this should do our initial refresh) # Update now and once per second.
# (this should do our initial refresh)
self._update()
self._update_timer = ba.Timer(1.0, self._update_timer = ba.Timer(1.0,
ba.WeakCall(self._update), ba.WeakCall(self._update),
timetype=ba.TimeType.REAL, timetype=ba.TimeType.REAL,
repeat=True) repeat=True)
self._update()
def _refresh(self) -> None: def _refresh(self) -> None:
# FIXME: Should tidy this up. # FIXME: Should tidy this up.
@ -292,13 +309,13 @@ class PlaylistBrowserWindow(ba.Window):
self._save_state() self._save_state()
self._subcontainer.delete() self._subcontainer.delete()
# make sure config exists # Make sure config exists.
if self._config_name_full not in ba.app.config: if self._config_name_full not in ba.app.config:
ba.app.config[self._config_name_full] = {} ba.app.config[self._config_name_full] = {}
items = list(ba.app.config[self._config_name_full].items()) items = list(ba.app.config[self._config_name_full].items())
# make sure everything is unicode # Make sure everything is unicode.
items = [(i[0].decode(), i[1]) if not isinstance(i[0], str) else i items = [(i[0].decode(), i[1]) if not isinstance(i[0], str) else i
for i in items] for i in items]
@ -507,7 +524,7 @@ class PlaylistBrowserWindow(ba.Window):
v -= scl * 130.0 v -= scl * 130.0
except Exception: except Exception:
ba.print_exception('error listing playlist maps') ba.print_exception('Error listing playlist maps.')
if not map_images: if not map_images:
ba.textwidget(parent=self._subcontainer, ba.textwidget(parent=self._subcontainer,
@ -563,43 +580,39 @@ class PlaylistBrowserWindow(ba.Window):
def _on_playlist_press(self, button: ba.Widget, def _on_playlist_press(self, button: ba.Widget,
playlist_name: str) -> None: playlist_name: str) -> None:
# pylint: disable=cyclic-import # pylint: disable=cyclic-import
from bastd.ui import playoptions from bastd.ui.playoptions import PlayOptionsWindow
# Make sure the target playlist still exists. # Make sure the target playlist still exists.
try: exists = (playlist_name == '__default__'
exists = (playlist_name == '__default__' or playlist_name or playlist_name in ba.app.config.get(
in ba.app.config[self._config_name_full]) self._config_name_full, {}))
except Exception:
exists = False
if not exists: if not exists:
return return
self._save_state() self._save_state()
playoptions.PlayOptionsWindow( PlayOptionsWindow(sessiontype=self._sessiontype,
sessiontype=self._sessiontype, scale_origin=button.get_screen_space_center(),
scale_origin=button.get_screen_space_center(), playlist=playlist_name,
playlist=playlist_name, delegate=self)
delegate=self)
def _on_customize_press(self) -> None: def _on_customize_press(self) -> None:
# pylint: disable=cyclic-import # pylint: disable=cyclic-import
from bastd.ui.playlist import customizebrowser as cb from bastd.ui.playlist.customizebrowser import (
PlaylistCustomizeBrowserWindow)
self._save_state() self._save_state()
ba.containerwidget(edit=self._root_widget, transition='out_left') ba.containerwidget(edit=self._root_widget, transition='out_left')
ba.app.main_menu_window = (cb.PlaylistCustomizeBrowserWindow( ba.app.main_menu_window = (PlaylistCustomizeBrowserWindow(
origin_widget=self._customize_button, origin_widget=self._customize_button,
sessiontype=self._sessiontype).get_root_widget()) sessiontype=self._sessiontype).get_root_widget())
def _on_back_press(self) -> None: def _on_back_press(self) -> None:
# pylint: disable=cyclic-import # pylint: disable=cyclic-import
from bastd.ui import play from bastd.ui.play import PlayWindow
# Store our selected playlist if that's changed. # Store our selected playlist if that's changed.
if self._selected_playlist is not None: if self._selected_playlist is not None:
try: prev_sel = ba.app.config.get(self._pvars.config_name +
prev_sel = ba.app.config[self._pvars.config_name + ' Playlist Selection')
' Playlist Selection']
except Exception:
prev_sel = None
if self._selected_playlist != prev_sel: if self._selected_playlist != prev_sel:
cfg = ba.app.config cfg = ba.app.config
cfg[self._pvars.config_name + cfg[self._pvars.config_name +
@ -609,7 +622,7 @@ class PlaylistBrowserWindow(ba.Window):
self._save_state() self._save_state()
ba.containerwidget(edit=self._root_widget, ba.containerwidget(edit=self._root_widget,
transition=self._transition_out) transition=self._transition_out)
ba.app.main_menu_window = (play.PlayWindow( ba.app.main_menu_window = (PlayWindow(
transition='in_left').get_root_widget()) transition='in_left').get_root_widget())
def _save_state(self) -> None: def _save_state(self) -> None:
@ -628,14 +641,11 @@ class PlaylistBrowserWindow(ba.Window):
raise Exception('unrecognized selected widget') raise Exception('unrecognized selected widget')
ba.app.window_states[self.__class__.__name__] = sel_name ba.app.window_states[self.__class__.__name__] = sel_name
except Exception: except Exception:
ba.print_exception('error saving state for', self.__class__) ba.print_exception(f'Error saving state for {self}.')
def _restore_state(self) -> None: def _restore_state(self) -> None:
try: try:
try: sel_name = ba.app.window_states.get(self.__class__.__name__)
sel_name = ba.app.window_states[self.__class__.__name__]
except Exception:
sel_name = None
if sel_name == 'Back': if sel_name == 'Back':
sel = self._back_button sel = self._back_button
elif sel_name == 'Scroll': elif sel_name == 'Scroll':
@ -649,4 +659,4 @@ class PlaylistBrowserWindow(ba.Window):
sel = self._scrollwidget sel = self._scrollwidget
ba.containerwidget(edit=self._root_widget, selected_child=sel) ba.containerwidget(edit=self._root_widget, selected_child=sel)
except Exception: except Exception:
ba.print_exception('error restoring state for', self.__class__) ba.print_exception(f'Error restoring state for {self}.')

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-06-03 for Ballistica version 1.5.0 build 20045</em></h4> <h4><em>last updated on 2020-06-04 for Ballistica version 1.5.0 build 20045</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>
@ -2719,7 +2719,7 @@ Results for a completed game.</p>
<a href="#method_ba_Activity__end">ba.Activity.end</a>() call.</p> <a href="#method_ba_Activity__end">ba.Activity.end</a>() call.</p>
<h3>Attributes:</h3> <h3>Attributes:</h3>
<h5><a href="#attr_ba_GameResults__lower_is_better">lower_is_better</a>, <a href="#attr_ba_GameResults__playerinfos">playerinfos</a>, <a href="#attr_ba_GameResults__score_label">score_label</a>, <a href="#attr_ba_GameResults__scoretype">scoretype</a>, <a href="#attr_ba_GameResults__sessionteams">sessionteams</a>, <a href="#attr_ba_GameResults__winnergroups">winnergroups</a>, <a href="#attr_ba_GameResults__winning_team">winning_team</a></h5> <h5><a href="#attr_ba_GameResults__lower_is_better">lower_is_better</a>, <a href="#attr_ba_GameResults__playerinfos">playerinfos</a>, <a href="#attr_ba_GameResults__score_label">score_label</a>, <a href="#attr_ba_GameResults__scoretype">scoretype</a>, <a href="#attr_ba_GameResults__sessionteams">sessionteams</a>, <a href="#attr_ba_GameResults__winnergroups">winnergroups</a>, <a href="#attr_ba_GameResults__winning_sessionteam">winning_sessionteam</a></h5>
<dl> <dl>
<dt><h4><a name="attr_ba_GameResults__lower_is_better">lower_is_better</a></h4></dt><dd> <dt><h4><a name="attr_ba_GameResults__lower_is_better">lower_is_better</a></h4></dt><dd>
<p><span>bool</span></p> <p><span>bool</span></p>
@ -2751,39 +2751,37 @@ Results for a completed game.</p>
<p>Get an ordered list of winner groups.</p> <p>Get an ordered list of winner groups.</p>
</dd> </dd>
<dt><h4><a name="attr_ba_GameResults__winning_team">winning_team</a></h4></dt><dd> <dt><h4><a name="attr_ba_GameResults__winning_sessionteam">winning_sessionteam</a></h4></dt><dd>
<p><span>Optional[<a href="#class_ba_SessionTeam">ba.SessionTeam</a>]</span></p> <p><span>Optional[<a href="#class_ba_SessionTeam">ba.SessionTeam</a>]</span></p>
<p>The winning <a href="#class_ba_SessionTeam">ba.SessionTeam</a> if there is exactly one, or else None.</p> <p>The winning <a href="#class_ba_SessionTeam">ba.SessionTeam</a> if there is exactly one, or else None.</p>
</dd> </dd>
</dl> </dl>
<h3>Methods:</h3> <h3>Methods:</h3>
<h5><a href="#method_ba_GameResults____init__">&lt;constructor&gt;</a>, <a href="#method_ba_GameResults__get_team_score">get_team_score()</a>, <a href="#method_ba_GameResults__get_team_score_str">get_team_score_str()</a>, <a href="#method_ba_GameResults__has_score_for_team">has_score_for_team()</a>, <a href="#method_ba_GameResults__set_game">set_game()</a>, <a href="#method_ba_GameResults__set_team_score">set_team_score()</a></h5> <h5><a href="#method_ba_GameResults____init__">&lt;constructor&gt;</a>, <a href="#method_ba_GameResults__get_sessionteam_score">get_sessionteam_score()</a>, <a href="#method_ba_GameResults__get_sessionteam_score_str">get_sessionteam_score_str()</a>, <a href="#method_ba_GameResults__has_score_for_sessionteam">has_score_for_sessionteam()</a>, <a href="#method_ba_GameResults__set_game">set_game()</a>, <a href="#method_ba_GameResults__set_team_score">set_team_score()</a></h5>
<dl> <dl>
<dt><h4><a name="method_ba_GameResults____init__">&lt;constructor&gt;</a></dt></h4><dd> <dt><h4><a name="method_ba_GameResults____init__">&lt;constructor&gt;</a></dt></h4><dd>
<p><span>ba.GameResults()</span></p> <p><span>ba.GameResults()</span></p>
<p>Instantiate a results instance.</p>
</dd> </dd>
<dt><h4><a name="method_ba_GameResults__get_team_score">get_team_score()</a></dt></h4><dd> <dt><h4><a name="method_ba_GameResults__get_sessionteam_score">get_sessionteam_score()</a></dt></h4><dd>
<p><span>get_team_score(self, sessionteam: <a href="#class_ba_SessionTeam">ba.SessionTeam</a>) -&gt; Optional[int]</span></p> <p><span>get_sessionteam_score(self, sessionteam: <a href="#class_ba_SessionTeam">ba.SessionTeam</a>) -&gt; Optional[int]</span></p>
<p>Return the score for a given <a href="#class_ba_SessionTeam">ba.SessionTeam</a>.</p> <p>Return the score for a given <a href="#class_ba_SessionTeam">ba.SessionTeam</a>.</p>
</dd> </dd>
<dt><h4><a name="method_ba_GameResults__get_team_score_str">get_team_score_str()</a></dt></h4><dd> <dt><h4><a name="method_ba_GameResults__get_sessionteam_score_str">get_sessionteam_score_str()</a></dt></h4><dd>
<p><span>get_team_score_str(self, sessionteam: <a href="#class_ba_SessionTeam">ba.SessionTeam</a>) -&gt; <a href="#class_ba_Lstr">ba.Lstr</a></span></p> <p><span>get_sessionteam_score_str(self, sessionteam: <a href="#class_ba_SessionTeam">ba.SessionTeam</a>) -&gt; <a href="#class_ba_Lstr">ba.Lstr</a></span></p>
<p>Return the score for the given <a href="#class_ba_Team">ba.Team</a> as an Lstr.</p> <p>Return the score for the given session-team as an Lstr.</p>
<p>(properly formatted for the score type.)</p> <p>(properly formatted for the score type.)</p>
</dd> </dd>
<dt><h4><a name="method_ba_GameResults__has_score_for_team">has_score_for_team()</a></dt></h4><dd> <dt><h4><a name="method_ba_GameResults__has_score_for_sessionteam">has_score_for_sessionteam()</a></dt></h4><dd>
<p><span>has_score_for_team(self, sessionteam: <a href="#class_ba_SessionTeam">ba.SessionTeam</a>) -&gt; bool</span></p> <p><span>has_score_for_sessionteam(self, sessionteam: <a href="#class_ba_SessionTeam">ba.SessionTeam</a>) -&gt; bool</span></p>
<p>Return whether there is a score for a given team.</p> <p>Return whether there is a score for a given session-team.</p>
</dd> </dd>
<dt><h4><a name="method_ba_GameResults__set_game">set_game()</a></dt></h4><dd> <dt><h4><a name="method_ba_GameResults__set_game">set_game()</a></dt></h4><dd>
@ -2795,7 +2793,7 @@ Results for a completed game.</p>
<dt><h4><a name="method_ba_GameResults__set_team_score">set_team_score()</a></dt></h4><dd> <dt><h4><a name="method_ba_GameResults__set_team_score">set_team_score()</a></dt></h4><dd>
<p><span>set_team_score(self, team: <a href="#class_ba_Team">ba.Team</a>, score: Optional[int]) -&gt; None</span></p> <p><span>set_team_score(self, team: <a href="#class_ba_Team">ba.Team</a>, score: Optional[int]) -&gt; None</span></p>
<p>Set the score for a given <a href="#class_ba_Team">ba.Team</a>.</p> <p>Set the score for a given team.</p>
<p>This can be a number or None. <p>This can be a number or None.
(see the none_is_winner arg in the constructor)</p> (see the none_is_winner arg in the constructor)</p>