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}))
Exemple #2
0
    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}))
Exemple #4
0
        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))
Exemple #5
0
    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()
Exemple #6
0
 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.
Exemple #7
0
    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()
Exemple #8
0
    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))
Exemple #9
0
    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)
Exemple #10
0
 def _instantiate_next_game(self) -> None:
     self._next_game_instance = _ba.newactivity(
         self._next_game_spec['resolved_type'],
         self._next_game_spec['settings'])