def _switch_to_score_screen(self, results: ba.GameResults) -> None: # pylint: disable=cyclic-import from bastd.activity.drawscore import DrawScoreScreenActivity from bastd.activity.dualteamscore import ( TeamVictoryScoreScreenActivity) from bastd.activity.multiteamvictory import ( TeamSeriesVictoryScoreScreenActivity) winnergroups = results.winnergroups # If everyone has the same score, call it a draw. if len(winnergroups) < 2: self.setactivity(_ba.newactivity(DrawScoreScreenActivity)) else: winner = winnergroups[0].teams[0] winner.customdata['score'] += 1 # If a team has won, show final victory screen. if winner.customdata['score'] >= (self._series_length - 1) / 2 + 1: self.setactivity( _ba.newactivity(TeamSeriesVictoryScoreScreenActivity, {'winner': winner})) else: self.setactivity( _ba.newactivity(TeamVictoryScoreScreenActivity, {'winner': winner}))
def _update_on_deck_game_instances(self) -> None: # pylint: disable=cyclic-import from ba._gameactivity import GameActivity # Instantiate levels we may be running soon to let them load in the bg. # Build an instance for the current level. assert self.campaign is not None level = self.campaign.getlevel(self.campaign_level_name) gametype = level.gametype settings = level.get_settings() # Make sure all settings the game expects are present. neededsettings = gametype.get_available_settings(type(self)) for setting in neededsettings: if setting.name not in settings: settings[setting.name] = setting.default newactivity = _ba.newactivity(gametype, settings) assert isinstance(newactivity, GameActivity) self._current_game_instance: GameActivity = newactivity # Find the next level and build an instance for it too. levels = self.campaign.levels level = self.campaign.getlevel(self.campaign_level_name) nextlevel: Optional[ba.Level] if level.index < len(levels) - 1: nextlevel = levels[level.index + 1] else: nextlevel = None if nextlevel: gametype = nextlevel.gametype settings = nextlevel.get_settings() # Make sure all settings the game expects are present. neededsettings = gametype.get_available_settings(type(self)) for setting in neededsettings: if setting.name not in settings: settings[setting.name] = setting.default # We wanna be in the activity's context while taking it down. newactivity = _ba.newactivity(gametype, settings) assert isinstance(newactivity, GameActivity) self._next_game_instance = newactivity self._next_game_level_name = nextlevel.name else: self._next_game_instance = None self._next_game_level_name = None # Special case: # If our current level is 'onslaught training', instantiate # our tutorial so its ready to go. (if we haven't run it yet). if (self.campaign_level_name == 'Onslaught Training' and self._tutorial_activity is None and not self._ran_tutorial_activity): from bastd.tutorial import TutorialActivity self._tutorial_activity = _ba.newactivity(TutorialActivity)
def _switch_to_score_screen(self, results: ba.GameResults) -> None: # pylint: disable=cyclic-import from efro.util import asserttype from bastd.activity.drawscore import DrawScoreScreenActivity from bastd.activity.multiteamvictory import ( TeamSeriesVictoryScoreScreenActivity) from bastd.activity.freeforallvictory import ( FreeForAllVictoryScoreScreenActivity) winners = results.winnergroups # If there's multiple players and everyone has the same score, # call it a draw. if len(self.sessionplayers) > 1 and len(winners) < 2: self.setactivity( _ba.newactivity(DrawScoreScreenActivity, {'results': results})) else: # Award different point amounts based on number of players. point_awards = self.get_ffa_point_awards() for i, winner in enumerate(winners): for team in winner.teams: points = (point_awards[i] if i in point_awards else 0) team.customdata['previous_score'] = ( team.customdata['score']) team.customdata['score'] += points series_winners = [ team for team in self.sessionteams if team.customdata['score'] >= self._ffa_series_length ] series_winners.sort( reverse=True, key=lambda t: asserttype(t.customdata['score'], int)) if (len(series_winners) == 1 or (len(series_winners) > 1 and series_winners[0].customdata['score'] != series_winners[1].customdata['score'])): self.setactivity( _ba.newactivity(TeamSeriesVictoryScoreScreenActivity, {'winner': series_winners[0]})) else: self.setactivity( _ba.newactivity(FreeForAllVictoryScoreScreenActivity, {'results': results}))
def __init__(self) -> None: # print('FIXME: BENCHMARK SESSION WOULD CALC DEPS.') depsets: Sequence[ba.DependencySet] = [] super().__init__(depsets) # Store old graphics settings. self._old_quality = _ba.app.config.resolve('Graphics Quality') cfg = _ba.app.config cfg['Graphics Quality'] = 'Low' cfg.apply() self.benchmark_type = 'cpu' self.setactivity(_ba.newactivity(tutorial.TutorialActivity))
def __init__(self) -> None: """Instantiate a co-op mode session.""" # pylint: disable=cyclic-import from ba._campaign import getcampaign from bastd.activity.coopjoin import CoopJoinActivity _ba.increment_analytics_count('Co-op session start') app = _ba.app # If they passed in explicit min/max, honor that. # Otherwise defer to user overrides or defaults. if 'min_players' in app.coop_session_args: min_players = app.coop_session_args['min_players'] else: min_players = 1 if 'max_players' in app.coop_session_args: max_players = app.coop_session_args['max_players'] else: max_players = app.config.get('Coop Game Max Players', 4) # print('FIXME: COOP SESSION WOULD CALC DEPS.') depsets: Sequence[ba.DependencySet] = [] super().__init__(depsets, team_names=TEAM_NAMES, team_colors=TEAM_COLORS, min_players=min_players, max_players=max_players) # Tournament-ID if we correspond to a co-op tournament (otherwise None) self.tournament_id: Optional[str] = ( app.coop_session_args.get('tournament_id')) self.campaign = getcampaign(app.coop_session_args['campaign']) self.campaign_level_name: str = app.coop_session_args['level'] self._ran_tutorial_activity = False self._tutorial_activity: Optional[ba.Activity] = None self._custom_menu_ui: list[dict[str, Any]] = [] # Start our joining screen. self.setactivity(_ba.newactivity(CoopJoinActivity)) self._next_game_instance: Optional[ba.GameActivity] = None self._next_game_level_name: Optional[str] = None self._update_on_deck_game_instances()
def _launch_end_session_activity(self) -> None: """(internal)""" from ba._activitytypes import EndSessionActivity from ba._enums import TimeType with _ba.Context(self): curtime = _ba.time(TimeType.REAL) if self._ending: # Ignore repeats unless its been a while. assert self._launch_end_session_activity_time is not None since_last = (curtime - self._launch_end_session_activity_time) if since_last < 30.0: return print_error( '_launch_end_session_activity called twice (since_last=' + str(since_last) + ')') self._launch_end_session_activity_time = curtime self.setactivity(_ba.newactivity(EndSessionActivity)) self._wants_to_end = False self._ending = True # Prevent further actions.
def on_activity_end(self, activity: ba.Activity, results: Any) -> None: """Method override for co-op sessions. Jumps between co-op games and score screens. """ # pylint: disable=too-many-branches # pylint: disable=too-many-locals # pylint: disable=too-many-statements # pylint: disable=cyclic-import from ba._activitytypes import JoinActivity, TransitionActivity from ba._language import Lstr from ba._general import WeakCall from ba._coopgame import CoopGameActivity from ba._gameresults import GameResults from ba._score import ScoreType from ba._player import PlayerInfo from bastd.tutorial import TutorialActivity from bastd.activity.coopscore import CoopScoreScreen app = _ba.app # If we're running a TeamGameActivity we'll have a GameResults # as results. Otherwise its an old CoopGameActivity so its giving # us a dict of random stuff. if isinstance(results, GameResults): outcome = 'defeat' # This can't be 'beaten'. else: outcome = '' if results is None else results.get('outcome', '') # If we're running with a gui and at any point we have no # in-game players, quit out of the session (this can happen if # someone leaves in the tutorial for instance). if not _ba.app.headless_mode: active_players = [p for p in self.sessionplayers if p.in_game] if not active_players: self.end() return # If we're in a between-round activity or a restart-activity, # hop into a round. if (isinstance(activity, (JoinActivity, CoopScoreScreen, TransitionActivity))): if outcome == 'next_level': if self._next_game_instance is None: raise RuntimeError() assert self._next_game_level_name is not None self.campaign_level_name = self._next_game_level_name next_game = self._next_game_instance else: next_game = self._current_game_instance # Special case: if we're coming from a joining-activity # and will be going into onslaught-training, show the # tutorial first. if (isinstance(activity, JoinActivity) and self.campaign_level_name == 'Onslaught Training' and not (app.demo_mode or app.arcade_mode)): if self._tutorial_activity is None: raise RuntimeError('Tutorial not preloaded properly.') self.setactivity(self._tutorial_activity) self._tutorial_activity = None self._ran_tutorial_activity = True self._custom_menu_ui = [] # Normal case; launch the next round. else: # Reset stats for the new activity. self.stats.reset() for player in self.sessionplayers: # Skip players that are still choosing a team. if player.in_game: self.stats.register_sessionplayer(player) self.stats.setactivity(next_game) # Now flip the current activity.. self.setactivity(next_game) if not (app.demo_mode or app.arcade_mode): if self.tournament_id is not None: self._custom_menu_ui = [{ 'label': Lstr(resource='restartText'), 'resume_on_call': False, 'call': WeakCall(self._on_tournament_restart_menu_press) }] else: self._custom_menu_ui = [{ 'label': Lstr(resource='restartText'), 'call': WeakCall(self.restart) }] # If we were in a tutorial, just pop a transition to get to the # actual round. elif isinstance(activity, TutorialActivity): self.setactivity(_ba.newactivity(TransitionActivity)) else: playerinfos: list[ba.PlayerInfo] # Generic team games. if isinstance(results, GameResults): playerinfos = results.playerinfos score = results.get_sessionteam_score(results.sessionteams[0]) fail_message = None score_order = ('decreasing' if results.lower_is_better else 'increasing') if results.scoretype in (ScoreType.SECONDS, ScoreType.MILLISECONDS): scoretype = 'time' # ScoreScreen wants hundredths of a second. if score is not None: if results.scoretype is ScoreType.SECONDS: score *= 100 elif results.scoretype is ScoreType.MILLISECONDS: score //= 10 else: raise RuntimeError('FIXME') else: if results.scoretype is not ScoreType.POINTS: print(f'Unknown ScoreType:' f' "{results.scoretype}"') scoretype = 'points' # Old coop-game-specific results; should migrate away from these. else: playerinfos = results.get('playerinfos') score = results['score'] if 'score' in results else None fail_message = (results['fail_message'] if 'fail_message' in results else None) score_order = (results['score_order'] if 'score_order' in results else 'increasing') activity_score_type = (activity.get_score_type() if isinstance( activity, CoopGameActivity) else None) assert activity_score_type is not None scoretype = activity_score_type # Validate types. if playerinfos is not None: assert isinstance(playerinfos, list) assert (isinstance(i, PlayerInfo) for i in playerinfos) # Looks like we were in a round - check the outcome and # go from there. if outcome == 'restart': # This will pop up back in the same round. self.setactivity(_ba.newactivity(TransitionActivity)) else: self.setactivity( _ba.newactivity( CoopScoreScreen, { 'playerinfos': playerinfos, 'score': score, 'fail_message': fail_message, 'score_order': score_order, 'score_type': scoretype, 'outcome': outcome, 'campaign': self.campaign, 'level': self.campaign_level_name })) # No matter what, get the next 2 levels ready to go. self._update_on_deck_game_instances()
def __init__(self) -> None: """Set up playlists and launches a ba.Activity to accept joiners.""" # pylint: disable=cyclic-import from ba import _playlist from bastd.activity.multiteamjoin import MultiTeamJoinActivity app = _ba.app cfg = app.config if self.use_teams: team_names = cfg.get('Custom Team Names', DEFAULT_TEAM_NAMES) team_colors = cfg.get('Custom Team Colors', DEFAULT_TEAM_COLORS) else: team_names = None team_colors = None # print('FIXME: TEAM BASE SESSION WOULD CALC DEPS.') depsets: Sequence[ba.DependencySet] = [] super().__init__(depsets, team_names=team_names, team_colors=team_colors, min_players=1, max_players=self.get_max_players()) self._series_length = app.teams_series_length self._ffa_series_length = app.ffa_series_length show_tutorial = cfg.get('Show Tutorial', True) self._tutorial_activity_instance: Optional[ba.Activity] if show_tutorial: from bastd.tutorial import TutorialActivity # Get this loading. self._tutorial_activity_instance = _ba.newactivity( TutorialActivity) else: self._tutorial_activity_instance = None self._playlist_name = cfg.get(self._playlist_selection_var, '__default__') self._playlist_randomize = cfg.get(self._playlist_randomize_var, False) # Which game activity we're on. self._game_number = 0 playlists = cfg.get(self._playlists_var, {}) if (self._playlist_name != '__default__' and self._playlist_name in playlists): # Make sure to copy this, as we muck with it in place once we've # got it and we don't want that to affect our config. playlist = copy.deepcopy(playlists[self._playlist_name]) else: if self.use_teams: playlist = _playlist.get_default_teams_playlist() else: playlist = _playlist.get_default_free_for_all_playlist() # Resolve types and whatnot to get our final playlist. playlist_resolved = _playlist.filter_playlist(playlist, sessiontype=type(self), add_resolved_type=True) if not playlist_resolved: raise RuntimeError('Playlist contains no valid games.') self._playlist = ShuffleList(playlist_resolved, shuffle=self._playlist_randomize) # Get a game on deck ready to go. self._current_game_spec: Optional[dict[str, Any]] = None self._next_game_spec: dict[str, Any] = self._playlist.pull_next() self._next_game: type[ba.GameActivity] = ( self._next_game_spec['resolved_type']) # Go ahead and instantiate the next game we'll # use so it has lots of time to load. self._instantiate_next_game() # Start in our custom join screen. self.setactivity(_ba.newactivity(MultiTeamJoinActivity))
def on_activity_end(self, activity: ba.Activity, results: Any) -> None: # pylint: disable=cyclic-import from bastd.tutorial import TutorialActivity from bastd.activity.multiteamvictory import ( TeamSeriesVictoryScoreScreenActivity) from ba._activitytypes import (TransitionActivity, JoinActivity, ScoreScreenActivity) # If we have a tutorial to show, that's the first thing we do no # matter what. if self._tutorial_activity_instance is not None: self.setactivity(self._tutorial_activity_instance) self._tutorial_activity_instance = None # If we're leaving the tutorial activity, pop a transition activity # to transition us into a round gracefully (otherwise we'd snap from # one terrain to another instantly). elif isinstance(activity, TutorialActivity): self.setactivity(_ba.newactivity(TransitionActivity)) # If we're in a between-round activity or a restart-activity, hop # into a round. elif isinstance( activity, (JoinActivity, TransitionActivity, ScoreScreenActivity)): # If we're coming from a series-end activity, reset scores. if isinstance(activity, TeamSeriesVictoryScoreScreenActivity): self.stats.reset() self._game_number = 0 for team in self.sessionteams: team.customdata['score'] = 0 # Otherwise just set accum (per-game) scores. else: self.stats.reset_accum() next_game = self._next_game_instance self._current_game_spec = self._next_game_spec self._next_game_spec = self._playlist.pull_next() self._game_number += 1 # Instantiate the next now so they have plenty of time to load. self._instantiate_next_game() # (Re)register all players and wire stats to our next activity. for player in self.sessionplayers: # ..but only ones who have been placed on a team # (ie: no longer sitting in the lobby). try: has_team = (player.sessionteam is not None) except NotFoundError: has_team = False if has_team: self.stats.register_sessionplayer(player) self.stats.setactivity(next_game) # Now flip the current activity. self.setactivity(next_game) # If we're leaving a round, go to the score screen. else: self._switch_to_score_screen(results)
def _instantiate_next_game(self) -> None: self._next_game_instance = _ba.newactivity( self._next_game_spec['resolved_type'], self._next_game_spec['settings'])