def get_settings_display_string(cls, config: Dict[str, Any]) -> ba.Lstr:
        """Given a game config dict, return a short description for it.

        This is used when viewing game-lists or showing what game
        is up next in a series.
        """
        name = cls.get_display_string(config['settings'])

        # In newer configs, map is in settings; it used to be in the
        # config root.
        if 'map' in config['settings']:
            sval = Lstr(value='${NAME} @ ${MAP}',
                        subs=[('${NAME}', name),
                              ('${MAP}',
                               _map.get_map_display_string(
                                   _map.get_filtered_map_name(
                                       config['settings']['map'])))])
        elif 'map' in config:
            sval = Lstr(value='${NAME} @ ${MAP}',
                        subs=[('${NAME}', name),
                              ('${MAP}',
                               _map.get_map_display_string(
                                   _map.get_filtered_map_name(config['map'])))
                              ])
        else:
            print('invalid game config - expected map entry under settings')
            sval = Lstr(value='???')
        return sval
Ejemplo n.º 2
0
    def _update_text(self) -> None:
        assert self._text_node is not None
        if self._ready:

            # Once we're ready, we've saved the name, so lets ask the system
            # for it so we get appended numbers and stuff.
            text = Lstr(value=self._sessionplayer.getname(full=True))
            text = Lstr(value='${A} (${B})',
                        subs=[('${A}', text),
                              ('${B}', Lstr(resource='readyText'))])
        else:
            text = Lstr(value=self._getname(full=True))

        can_switch_teams = len(self.lobby.sessionteams) > 1

        # Flash as we're coming in.
        fin_color = _ba.safecolor(self.get_color()) + (1, )
        if not self._inited:
            animate_array(self._text_node, 'color', 4, {
                0.15: fin_color,
                0.25: (2, 2, 2, 1),
                0.35: fin_color
            })
        else:

            # Blend if we're in teams mode; switch instantly otherwise.
            if can_switch_teams:
                animate_array(self._text_node, 'color', 4, {
                    0: self._text_node.color,
                    0.1: fin_color
                })
            else:
                self._text_node.color = fin_color

        self._text_node.text = text
Ejemplo n.º 3
0
    def get_team_score_str(self, team: ba.Team) -> ba.Lstr:
        """Return the score for the given ba.Team as an Lstr.

        (properly formatted for the score type.)
        """
        from ba._gameutils import timestring
        from ba._lang import Lstr
        from ba._enums import TimeFormat
        from ba._score import ScoreType
        if not self._game_set:
            raise RuntimeError("Can't get team-score-str until game is set.")
        for score in list(self._scores.values()):
            if score[0]() is team.sessionteam:
                if score[1] is None:
                    return Lstr(value='-')
                if self._score_type is ScoreType.SECONDS:
                    return timestring(score[1] * 1000,
                                      centi=False,
                                      timeformat=TimeFormat.MILLISECONDS)
                if self._score_type is ScoreType.MILLISECONDS:
                    return timestring(score[1],
                                      centi=True,
                                      timeformat=TimeFormat.MILLISECONDS)
                return Lstr(value=str(score[1]))
        return Lstr(value='-')
Ejemplo n.º 4
0
def handle_scan_results(results: ScanResults) -> None:
    """Called in the game thread with results of a completed scan."""

    from ba._lang import Lstr
    from ba._plugin import PotentialPlugin

    # Warnings generally only get printed locally for users' benefit
    # (things like out-of-date scripts being ignored, etc.)
    # Errors are more serious and will get included in the regular log
    # warnings = results.get('warnings', '')
    # errors = results.get('errors', '')
    if results.warnings != '' or results.errors != '':
        import textwrap
        _ba.screenmessage(Lstr(resource='scanScriptsErrorText'),
                          color=(1, 0, 0))
        _ba.playsound(_ba.getsound('error'))
        if results.warnings != '':
            _ba.log(textwrap.indent(results.warnings, 'Warning (meta-scan): '),
                    to_server=False)
        if results.errors != '':
            _ba.log(textwrap.indent(results.errors, 'Error (meta-scan): '))

    # Handle plugins.
    config_changed = False
    found_new = False
    plugstates: Dict[str, Dict] = _ba.app.config.setdefault('Plugins', {})
    assert isinstance(plugstates, dict)

    # Create a potential-plugin for each class we found in the scan.
    for class_path in results.plugins:
        _ba.app.potential_plugins.append(
            PotentialPlugin(display_name=Lstr(value=class_path),
                            class_path=class_path,
                            available=True))
        if class_path not in plugstates:
            plugstates[class_path] = {'enabled': False}
            config_changed = True
            found_new = True

    # Also add a special one for any plugins set to load but *not* found
    # in the scan (this way they will show up in the UI so we can disable them)
    for class_path, plugstate in plugstates.items():
        enabled = plugstate.get('enabled', False)
        assert isinstance(enabled, bool)
        if enabled and class_path not in results.plugins:
            _ba.app.potential_plugins.append(
                PotentialPlugin(display_name=Lstr(value=class_path),
                                class_path=class_path,
                                available=False))

    _ba.app.potential_plugins.sort(key=lambda p: p.class_path)

    if found_new:
        _ba.screenmessage(Lstr(resource='pluginsDetectedText'),
                          color=(0, 1, 0))
        _ba.playsound(_ba.getsound('ding'))

    if config_changed:
        _ba.app.config.commit()
Ejemplo n.º 5
0
 def description_complete(self) -> ba.Lstr:
     """Get a ba.Lstr for the Achievement's description when completed."""
     from ba._lang import Lstr, get_resource
     if 'descriptionComplete' in get_resource('achievements')[self._name]:
         return Lstr(resource='achievements.' + self._name +
                     '.descriptionComplete')
     return Lstr(resource='achievements.' + self._name +
                 '.descriptionFullComplete')
Ejemplo n.º 6
0
 def description_full_complete(self) -> ba.Lstr:
     """Get a ba.Lstr for the Achievement's full desc. when completed."""
     from ba._lang import Lstr
     return Lstr(
         resource='achievements.' + self._name + '.descriptionFullComplete',
         subs=[('${LEVEL}',
                Lstr(translate=('coopLevelNames',
                                ACH_LEVEL_NAMES.get(self._name, '?'))))])
Ejemplo n.º 7
0
        def _apply(name2: Lstr, score2: int, showpoints2: bool,
                   color2: Tuple[float, float, float, float], scale2: float,
                   sound2: Optional[ba.Sound]) -> None:
            from bastd.actor.popuptext import PopupText

            # Only award this if they're still alive and we can get
            # their pos.
            if self._player is not None and self._player.node:
                our_pos = self._player.node.position
            else:
                return

            # Jitter position a bit since these often come in clusters.
            our_pos = (our_pos[0] + (random.random() - 0.5) * 2.0,
                       our_pos[1] + (random.random() - 0.5) * 2.0,
                       our_pos[2] + (random.random() - 0.5) * 2.0)
            activity = self.getactivity()
            if activity is not None:
                PopupText(Lstr(
                    value=(('+' + str(score2) + ' ') if showpoints2 else '') +
                    '${N}',
                    subs=[('${N}', name2)]),
                          color=color2,
                          scale=scale2,
                          position=our_pos).autoretain()
            if sound2:
                _ba.playsound(sound2)

            self.score += score2
            self.accumscore += score2

            # Inform a running game of the score.
            if score2 != 0 and activity is not None:
                activity.handlemessage(PlayerScoredMessage(score=score2))
Ejemplo n.º 8
0
    def on_player_request(self, player: ba.Player) -> bool:
        """Called when a new ba.Player wants to join the Session.

        This should return True or False to accept/reject.
        """
        from ba._lang import Lstr

        # Limit player counts *unless* we're in a stress test.
        if _ba.app.stress_test_reset_timer is None:

            if len(self.players) >= self.max_players:

                # Print a rejection message *only* to the client trying to
                # join (prevents spamming everyone else in the game).
                _ba.playsound(_ba.getsound('error'))
                _ba.screenmessage(
                    Lstr(resource='playerLimitReachedText',
                         subs=[('${COUNT}', str(self.max_players))]),
                    color=(0.8, 0.0, 0.0),
                    clients=[player.get_input_device().client_id],
                    transient=True)
                return False

        _ba.playsound(_ba.getsound('dripity'))
        return True
Ejemplo n.º 9
0
    def _on_player_ready(self, chooser: ba.Chooser) -> None:
        """Called when a ba.Player has checked themself ready."""
        from ba._lang import Lstr
        lobby = chooser.lobby
        activity = self._activity_weak()

        # In joining activities, we wait till all choosers are ready
        # and then create all players at once.
        if activity is not None and activity.is_joining_activity:
            if lobby.check_all_ready():
                choosers = lobby.get_choosers()
                min_players = self.min_players
                if len(choosers) >= min_players:
                    for lch in lobby.get_choosers():
                        self._add_chosen_player(lch)
                    lobby.remove_all_choosers()

                    # Get our next activity going.
                    self._complete_end_activity(activity, {})
                else:
                    _ba.screenmessage(Lstr(resource='notEnoughPlayersText',
                                           subs=[('${COUNT}', str(min_players))
                                                 ]),
                                      color=(1, 1, 0))
                    _ba.playsound(_ba.getsound('error'))
            else:
                return

        # Otherwise just add players on the fly.
        else:
            self._add_chosen_player(chooser)
            lobby.remove_chooser(chooser.getplayer())
Ejemplo n.º 10
0
 def _standard_time_limit_tick(self) -> None:
     from ba._gameutils import animate
     assert self._standard_time_limit_time is not None
     self._standard_time_limit_time -= 1
     if self._standard_time_limit_time <= 10:
         if self._standard_time_limit_time == 10:
             assert self._standard_time_limit_text is not None
             assert self._standard_time_limit_text.node
             self._standard_time_limit_text.node.scale = 1.3
             self._standard_time_limit_text.node.position = (-30, -45)
             cnode = _ba.newnode('combine',
                                 owner=self._standard_time_limit_text.node,
                                 attrs={'size': 4})
             cnode.connectattr('output',
                               self._standard_time_limit_text.node, 'color')
             animate(cnode, 'input0', {0: 1, 0.15: 1}, loop=True)
             animate(cnode, 'input1', {0: 1, 0.15: 0.5}, loop=True)
             animate(cnode, 'input2', {0: 0.1, 0.15: 0.0}, loop=True)
             cnode.input3 = 1.0
         _ba.playsound(_ba.getsound('tick'))
     if self._standard_time_limit_time <= 0:
         self._standard_time_limit_timer = None
         self.end_game()
         node = _ba.newnode('text',
                            attrs={
                                'v_attach': 'top',
                                'h_attach': 'center',
                                'h_align': 'center',
                                'color': (1, 0.7, 0, 1),
                                'position': (0, -90),
                                'scale': 1.2,
                                'text': Lstr(resource='timeExpiredText')
                            })
         _ba.playsound(_ba.getsound('refWhistle'))
         animate(node, 'scale', {0.0: 0.0, 0.1: 1.4, 0.15: 1.2})
Ejemplo n.º 11
0
 def player_was_killed(self,
                       player: ba.Player,
                       killed: bool = False,
                       killer: ba.Player = None) -> None:
     """Should be called when a player is killed."""
     from ba._lang import Lstr
     name = player.get_name()
     prec = self._player_records[name]
     prec.streak = 0
     if killed:
         prec.accum_killed_count += 1
         prec.killed_count += 1
     try:
         if killed and _ba.getactivity().announce_player_deaths:
             if killer == player:
                 _ba.screenmessage(Lstr(resource='nameSuicideText',
                                        subs=[('${NAME}', name)]),
                                   top=True,
                                   color=player.color,
                                   image=player.get_icon())
             elif killer is not None:
                 if killer.team == player.team:
                     _ba.screenmessage(Lstr(resource='nameBetrayedText',
                                            subs=[('${NAME}',
                                                   killer.get_name()),
                                                  ('${VICTIM}', name)]),
                                       top=True,
                                       color=killer.color,
                                       image=killer.get_icon())
                 else:
                     _ba.screenmessage(Lstr(resource='nameKilledText',
                                            subs=[('${NAME}',
                                                   killer.get_name()),
                                                  ('${VICTIM}', name)]),
                                       top=True,
                                       color=killer.color,
                                       image=killer.get_icon())
             else:
                 _ba.screenmessage(Lstr(resource='nameDiedText',
                                        subs=[('${NAME}', name)]),
                                   top=True,
                                   color=player.color,
                                   image=player.get_icon())
     except Exception:
         from ba import _error
         _error.print_exception('error announcing kill')
Ejemplo n.º 12
0
    def get_description_display_string(
            cls, sessiontype: Type[ba.Session]) -> ba.Lstr:
        """Return a translated version of get_description().

        Sub-classes should override get_description(); not this.
        """
        description = cls.get_description(sessiontype)
        return Lstr(translate=('gameDescriptions', description))
Ejemplo n.º 13
0
def ui_remote_press() -> None:
    """Handle a press by a remote device that is only usable for nav."""
    from ba._lang import Lstr

    # Can be called without a context; need a context for getsound.
    with _ba.Context('ui'):
        _ba.screenmessage(Lstr(resource='internal.controllerForMenusOnlyText'),
                          color=(1, 0, 0))
        _ba.playsound(_ba.getsound('error'))
Ejemplo n.º 14
0
 def _execute_shutdown(self) -> None:
     from ba._lang import Lstr
     if self._executing_shutdown:
         return
     self._executing_shutdown = True
     timestrval = time.strftime('%c')
     if self._shutdown_reason is ShutdownReason.RESTARTING:
         _ba.screenmessage(Lstr(resource='internal.serverRestartingText'),
                           color=(1, 0.5, 0.0))
         print(f'{Clr.SBLU}Exiting for server-restart'
               f' at {timestrval}{Clr.RST}')
     else:
         _ba.screenmessage(Lstr(resource='internal.serverShuttingDownText'),
                           color=(1, 0.5, 0.0))
         print(f'{Clr.SBLU}Exiting for server-shutdown'
               f' at {timestrval}{Clr.RST}')
     with _ba.Context('ui'):
         _ba.timer(2.0, _ba.quit, timetype=TimeType.REAL)
Ejemplo n.º 15
0
                def check_pending_codes() -> None:
                    """(internal)"""

                    # If we're still not signed in and have pending codes,
                    # inform the user that they need to sign in to use them.
                    if _ba.app.pending_promo_codes:
                        _ba.screenmessage(
                            Lstr(resource='signInForPromoCodeText'),
                            color=(1, 0, 0))
                        _ba.playsound(_ba.getsound('error'))
Ejemplo n.º 16
0
    def get_display_string(cls, settings: Optional[Dict] = None) -> ba.Lstr:
        """Return a descriptive name for this game/settings combo.

        Subclasses should override getname(); not this.
        """
        name = Lstr(translate=('gameNames', cls.getname()))

        # A few substitutions for 'Epic', 'Solo' etc. modes.
        # FIXME: Should provide a way for game types to define filters of
        #  their own and should not rely on hard-coded settings names.
        if settings is not None:
            if 'Solo Mode' in settings and settings['Solo Mode']:
                name = Lstr(resource='soloNameFilterText',
                            subs=[('${NAME}', name)])
            if 'Epic Mode' in settings and settings['Epic Mode']:
                name = Lstr(resource='epicNameFilterText',
                            subs=[('${NAME}', name)])

        return name
Ejemplo n.º 17
0
def handle_account_gained_tickets(count: int) -> None:
    """Called when the current account has been awarded tickets.

    (internal)
    """
    from ba._lang import Lstr
    _ba.screenmessage(Lstr(resource='getTicketsWindow.receivedTicketsText',
                           subs=[('${COUNT}', str(count))]),
                      color=(0, 1, 0))
    _ba.playsound(_ba.getsound('cashRegister'))
Ejemplo n.º 18
0
    def do_remove_in_game_ads_message(self) -> None:
        """(internal)"""
        from ba._lang import Lstr
        from ba._enums import TimeType

        # Print this message once every 10 minutes at most.
        tval = _ba.time(TimeType.REAL)
        if (self.last_in_game_ad_remove_message_show_time is None or
            (tval - self.last_in_game_ad_remove_message_show_time > 60 * 10)):
            self.last_in_game_ad_remove_message_show_time = tval
            with _ba.Context('ui'):
                _ba.timer(
                    1.0,
                    lambda: _ba.screenmessage(Lstr(
                        resource='removeInGameAdsText',
                        subs=[('${PRO}',
                               Lstr(resource='store.bombSquadProNameText')),
                              ('${APP_NAME}', Lstr(resource='titleText'))]),
                                              color=(1, 1, 0)),
                    timetype=TimeType.REAL)
Ejemplo n.º 19
0
def _request_storage_permission() -> bool:
    """If needed, requests storage permission from the user (& return true)."""
    from ba._lang import Lstr
    from ba._enums import Permission
    if not _ba.have_permission(Permission.STORAGE):
        _ba.playsound(_ba.getsound('error'))
        _ba.screenmessage(Lstr(resource='storagePermissionAccessText'),
                          color=(1, 0, 0))
        _ba.timer(1.0, lambda: _ba.request_permission(Permission.STORAGE))
        return True
    return False
Ejemplo n.º 20
0
    def on_player_leave(self, sessionplayer: ba.SessionPlayer) -> None:
        """Called when a previously-accepted ba.SessionPlayer leaves."""

        if sessionplayer not in self.players:
            print('ERROR: Session.on_player_leave called'
                  ' for player not in our list.')
            return

        _ba.playsound(_ba.getsound('playerLeft'))

        activity = self._activity_weak()

        if not sessionplayer.in_game:

            # Ok, the player is still in the lobby; simply remove them.
            with _ba.Context(self):
                try:
                    self.lobby.remove_chooser(sessionplayer)
                except Exception:
                    print_exception('Error in Lobby.remove_chooser().')
        else:
            # Ok, they've already entered the game. Remove them from
            # teams/activities/etc.
            sessionteam = sessionplayer.sessionteam
            assert sessionteam is not None

            _ba.screenmessage(
                Lstr(resource='playerLeftText',
                     subs=[('${PLAYER}', sessionplayer.getname(full=True))]))

            # Remove them from their SessionTeam.
            if sessionplayer in sessionteam.players:
                sessionteam.players.remove(sessionplayer)
            else:
                print('SessionPlayer not found in SessionTeam'
                      ' in on_player_leave.')

            # Grab their activity-specific player instance.
            player = sessionplayer.activityplayer
            assert isinstance(player, (Player, type(None)))

            # Remove them from any current Activity.
            if activity is not None:
                if player in activity.players:
                    activity.remove_player(sessionplayer)
                else:
                    print('Player not found in Activity in on_player_leave.')

            # If we're a non-team session, remove their team too.
            if not self.use_teams:
                self._remove_player_team(sessionteam, activity)

        # Now remove them from the session list.
        self.players.remove(sessionplayer)
Ejemplo n.º 21
0
    def _tournament_time_limit_tick(self) -> None:
        from ba._gameutils import animate
        assert self._tournament_time_limit is not None
        self._tournament_time_limit -= 1
        if self._tournament_time_limit <= 10:
            if self._tournament_time_limit == 10:
                assert self._tournament_time_limit_title_text is not None
                assert self._tournament_time_limit_title_text.node
                assert self._tournament_time_limit_text is not None
                assert self._tournament_time_limit_text.node
                self._tournament_time_limit_title_text.node.scale = 1.0
                self._tournament_time_limit_text.node.scale = 1.3
                self._tournament_time_limit_title_text.node.position = (80, 85)
                self._tournament_time_limit_text.node.position = (80, 60)
                cnode = _ba.newnode(
                    'combine',
                    owner=self._tournament_time_limit_text.node,
                    attrs={'size': 4})
                cnode.connectattr('output',
                                  self._tournament_time_limit_title_text.node,
                                  'color')
                cnode.connectattr('output',
                                  self._tournament_time_limit_text.node,
                                  'color')
                animate(cnode, 'input0', {0: 1, 0.15: 1}, loop=True)
                animate(cnode, 'input1', {0: 1, 0.15: 0.5}, loop=True)
                animate(cnode, 'input2', {0: 0.1, 0.15: 0.0}, loop=True)
                cnode.input3 = 1.0
            _ba.playsound(_ba.getsound('tick'))
        if self._tournament_time_limit <= 0:
            self._tournament_time_limit_timer = None
            self.end_game()
            tval = Lstr(resource='tournamentTimeExpiredText',
                        fallback_resource='timeExpiredText')
            node = _ba.newnode('text',
                               attrs={
                                   'v_attach': 'top',
                                   'h_attach': 'center',
                                   'h_align': 'center',
                                   'color': (1, 0.7, 0, 1),
                                   'position': (0, -200),
                                   'scale': 1.6,
                                   'text': tval
                               })
            _ba.playsound(_ba.getsound('refWhistle'))
            animate(node, 'scale', {0: 0.0, 0.1: 1.4, 0.15: 1.2})

        # Normally we just connect this to time, but since this is a bit of a
        # funky setup we just update it manually once per second.
        assert self._tournament_time_limit_text_input is not None
        assert self._tournament_time_limit_text_input.node
        self._tournament_time_limit_text_input.node.time2 = (
            self._tournament_time_limit * 1000)
Ejemplo n.º 22
0
 def _execute_shutdown(self) -> None:
     from ba._lang import Lstr
     if self._executing_shutdown:
         return
     self._executing_shutdown = True
     timestrval = time.strftime('%c')
     if self._shutdown_reason is ShutdownReason.RESTARTING:
         # FIXME: Should add a server-screen-message call.
         # (so we could send this an an Lstr)
         _ba.chat_message(
             Lstr(resource='internal.serverRestartingText').evaluate())
         print(f'{Clr.SBLU}Exiting for server-restart'
               f' at {timestrval}{Clr.RST}')
     else:
         # FIXME: Should add a server-screen-message call.
         # (so we could send this an an Lstr)
         print(f'{Clr.SBLU}Exiting for server-shutdown'
               f' at {timestrval}{Clr.RST}')
         _ba.chat_message(
             Lstr(resource='internal.serverShuttingDownText').evaluate())
     with _ba.Context('ui'):
         _ba.timer(2.0, _ba.quit, timetype=TimeType.REAL)
Ejemplo n.º 23
0
    def handle_deep_link(self, url: str) -> None:
        """Handle a deep link URL."""
        from ba._lang import Lstr
        from ba._enums import TimeType
        appname = _ba.appname()
        if url.startswith(f'{appname}://code/'):
            code = url.replace(f'{appname}://code/', '')

            # If we're not signed in, queue up the code to run the next time we
            # are and issue a warning if we haven't signed in within the next
            # few seconds.
            if _ba.get_account_state() != 'signed_in':

                def check_pending_codes() -> None:
                    """(internal)"""

                    # If we're still not signed in and have pending codes,
                    # inform the user that they need to sign in to use them.
                    if _ba.app.pending_promo_codes:
                        _ba.screenmessage(
                            Lstr(resource='signInForPromoCodeText'),
                            color=(1, 0, 0))
                        _ba.playsound(_ba.getsound('error'))

                _ba.app.pending_promo_codes.append(code)
                _ba.timer(6.0, check_pending_codes, timetype=TimeType.REAL)
                return
            _ba.screenmessage(Lstr(resource='submittingPromoCodeText'),
                              color=(0, 1, 0))
            _ba.add_transaction({
                'type': 'PROMO_CODE',
                'expire_time': time.time() + 5,
                'code': code
            })
            _ba.run_transactions()
        else:
            _ba.screenmessage(Lstr(resource='errorText'), color=(1, 0, 0))
            _ba.playsound(_ba.getsound('error'))
Ejemplo n.º 24
0
def show_post_purchase_message() -> None:
    """(internal)"""
    from ba._lang import Lstr
    from ba._enums import TimeType
    app = _ba.app
    cur_time = _ba.time(TimeType.REAL)
    if (app.last_post_purchase_message_time is None
            or cur_time - app.last_post_purchase_message_time > 3.0):
        app.last_post_purchase_message_time = cur_time
        with _ba.Context('ui'):
            _ba.screenmessage(Lstr(resource='updatingAccountText',
                                   fallback_resource='purchasingText'),
                              color=(0, 1, 0))
            _ba.playsound(_ba.getsound('click01'))
Ejemplo n.º 25
0
 def _calc_map_name(self, settings: dict) -> str:
     map_name: str
     if 'map' in settings:
         map_name = settings['map']
     else:
         # If settings doesn't specify a map, pick a random one from the
         # list of supported ones.
         unowned_maps = _map.get_unowned_maps()
         valid_maps: List[str] = [
             m for m in self.get_supported_maps(type(self.session))
             if m not in unowned_maps
         ]
         if not valid_maps:
             _ba.screenmessage(Lstr(resource='noValidMapsErrorText'))
             raise Exception('No valid maps')
         map_name = valid_maps[random.randrange(len(valid_maps))]
     return map_name
Ejemplo n.º 26
0
    def announce_game_results(self,
                              activity: ba.GameActivity,
                              results: ba.GameResults,
                              delay: float,
                              announce_winning_team: bool = True) -> None:
        """Show basic game result at the end of a game.

        (before transitioning to a score screen).
        This will include a zoom-text of 'BLUE WINS'
        or whatnot, along with a possible audio
        announcement of the same.
        """
        # pylint: disable=cyclic-import
        # pylint: disable=too-many-locals
        from ba._math import normalized_color
        from ba._general import Call
        from ba._gameutils import cameraflash
        from ba._lang import Lstr
        from ba._freeforallsession import FreeForAllSession
        from ba._messages import CelebrateMessage
        _ba.timer(delay, Call(_ba.playsound, _ba.getsound('boxingBell')))

        if announce_winning_team:
            winning_sessionteam = results.winning_sessionteam
            if winning_sessionteam is not None:
                # Have all players celebrate.
                celebrate_msg = CelebrateMessage(duration=10.0)
                assert winning_sessionteam.activityteam is not None
                for player in winning_sessionteam.activityteam.players:
                    if player.actor:
                        player.actor.handlemessage(celebrate_msg)
                cameraflash()

                # Some languages say "FOO WINS" different for teams vs players.
                if isinstance(self, FreeForAllSession):
                    wins_resource = 'winsPlayerText'
                else:
                    wins_resource = 'winsTeamText'
                wins_text = Lstr(resource=wins_resource,
                                 subs=[('${NAME}', winning_sessionteam.name)])
                activity.show_zoom_message(
                    wins_text,
                    scale=0.85,
                    color=normalized_color(winning_sessionteam.color),
                )
Ejemplo n.º 27
0
    def _getname(self, full: bool = False) -> str:
        name_raw = name = self._profilenames[self._profileindex]
        clamp = False
        if name == '_random':
            try:
                name = (
                    self._sessionplayer.inputdevice.get_default_player_name())
            except Exception:
                print_exception('Error getting _random chooser name.')
                name = 'Invalid'
            clamp = not full
        elif name == '__account__':
            try:
                name = self._sessionplayer.inputdevice.get_account_name(full)
            except Exception:
                print_exception('Error getting account name for chooser.')
                name = 'Invalid'
            clamp = not full
        elif name == '_edit':
            # Explicitly flattening this to a str; it's only relevant on
            # the host so that's ok.
            name = (Lstr(
                resource='createEditPlayerText',
                fallback_resource='editProfileWindow.titleNewText').evaluate())
        else:
            # If we have a regular profile marked as global with an icon,
            # use it (for full only).
            if full:
                try:
                    if self._profiles[name_raw].get('global', False):
                        icon = (self._profiles[name_raw]['icon']
                                if 'icon' in self._profiles[name_raw] else
                                _ba.charstr(SpecialChar.LOGO))
                        name = icon + name
                except Exception:
                    print_exception('Error applying global icon.')
            else:
                # We now clamp non-full versions of names so there's at
                # least some hope of reading them in-game.
                clamp = True

        if clamp:
            if len(name) > 10:
                name = name[:10] + '...'
        return name
Ejemplo n.º 28
0
    def __init__(self, lobby: ba.Lobby):
        from ba._nodeactor import NodeActor
        from ba._general import WeakCall
        self._state = 0
        self._press_to_punch: Union[str, ba.Lstr] = _ba.charstr(
            SpecialChar.LEFT_BUTTON)
        self._press_to_bomb: Union[str, ba.Lstr] = _ba.charstr(
            SpecialChar.RIGHT_BUTTON)
        self._joinmsg = Lstr(resource='pressAnyButtonToJoinText')
        can_switch_teams = (len(lobby.sessionteams) > 1)

        # If we have a keyboard, grab keys for punch and pickup.
        # FIXME: This of course is only correct on the local device;
        #  Should change this for net games.
        keyboard = _ba.getinputdevice('Keyboard', '#1', doraise=False)
        if keyboard is not None:
            self._update_for_keyboard(keyboard)

        flatness = 1.0 if _ba.app.vr_mode else 0.0
        self._text = NodeActor(
            _ba.newnode('text',
                        attrs={
                            'position': (0, -40),
                            'h_attach': 'center',
                            'v_attach': 'top',
                            'h_align': 'center',
                            'color': (0.7, 0.7, 0.95, 1.0),
                            'flatness': flatness,
                            'text': self._joinmsg
                        }))

        if _ba.app.kiosk_mode:
            self._messages = [self._joinmsg]
        else:
            msg1 = Lstr(resource='pressToSelectProfileText',
                        subs=[
                            ('${BUTTONS}', _ba.charstr(SpecialChar.UP_ARROW) +
                             ' ' + _ba.charstr(SpecialChar.DOWN_ARROW))
                        ])
            msg2 = Lstr(resource='pressToOverrideCharacterText',
                        subs=[('${BUTTONS}', Lstr(resource='bombBoldText'))])
            msg3 = Lstr(value='${A} < ${B} >',
                        subs=[('${A}', msg2), ('${B}', self._press_to_bomb)])
            self._messages = (([
                Lstr(
                    resource='pressToSelectTeamText',
                    subs=[('${BUTTONS}', _ba.charstr(SpecialChar.LEFT_ARROW) +
                           ' ' + _ba.charstr(SpecialChar.RIGHT_ARROW))],
                )
            ] if can_switch_teams else []) + [msg1] + [msg3] + [self._joinmsg])

        self._timer = _ba.Timer(4.0, WeakCall(self._update), repeat=True)
Ejemplo n.º 29
0
 def display_name(self) -> ba.Lstr:
     """Return a ba.Lstr for this Achievement's name."""
     from ba._lang import Lstr
     name: Union[ba.Lstr, str]
     try:
         if self._level_name != '':
             from ba._campaign import getcampaign
             campaignname, campaign_level = self._level_name.split(':')
             name = getcampaign(campaignname).getlevel(
                 campaign_level).displayname
         else:
             name = ''
     except Exception:
         from ba import _error
         name = ''
         _error.print_exception()
     return Lstr(resource='achievements.' + self._name + '.name',
                 subs=[('${LEVEL}', name)])
Ejemplo n.º 30
0
    def _handle_server_restarts(self) -> bool:
        """Handle automatic restarts/shutdowns in server mode.

        Returns True if an action was taken; otherwise default action
        should occur (starting next round, etc).
        """
        # pylint: disable=cyclic-import

        # FIXME: Move server stuff to its own module.
        if self._allow_server_restart and _ba.app.server_config_dirty:
            from ba import _server
            from ba._lang import Lstr
            from ba._general import Call
            from ba._enums import TimeType
            if _ba.app.server_config.get('quit', False):
                if not self._kicked_off_server_shutdown:
                    if _ba.app.server_config.get(
                            'quit_reason') == 'restarting':
                        # FIXME: Should add a server-screen-message call
                        #  or something.
                        _ba.chat_message(
                            Lstr(resource='internal.serverRestartingText').
                            evaluate())
                        print(('Exiting for server-restart at ' +
                               time.strftime('%c')))
                    else:
                        print(('Exiting for server-shutdown at ' +
                               time.strftime('%c')))
                    with _ba.Context('ui'):
                        _ba.timer(2.0, _ba.quit, timetype=TimeType.REAL)
                    self._kicked_off_server_shutdown = True
                    return True
            else:
                if not self._kicked_off_server_restart:
                    print(('Running updated server config at ' +
                           time.strftime('%c')))
                    with _ba.Context('ui'):
                        _ba.timer(1.0,
                                  Call(_ba.pushcall,
                                       _server.launch_server_session),
                                  timetype=TimeType.REAL)
                    self._kicked_off_server_restart = True
                    return True
        return False