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 self.pending_promo_codes:
                    _ba.screenmessage(Lstr(resource='signInForPromoCodeText'),
                                      color=(1, 0, 0))
                    _ba.playsound(_ba.getsound('error'))
Example #2
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'))
Example #3
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'))
Example #4
0
 def handle_deep_link(self, url: str) -> None:
     """Handle a deep link URL."""
     from ba._language import Lstr
     appname = _ba.appname()
     if url.startswith(f'{appname}://code/'):
         code = url.replace(f'{appname}://code/', '')
         self.accounts.add_pending_promo_code(code)
     else:
         _ba.screenmessage(Lstr(resource='errorText'), color=(1, 0, 0))
         _ba.playsound(_ba.getsound('error'))
    def spawn_player_spaz(self,
                          player: PlayerType,
                          position: Sequence[float] = (0, 0, 0),
                          angle: float = None) -> MyPlayerSpaz:
        """Create and wire up a ba.PlayerSpaz for the provided ba.Player."""
        # pylint: disable=too-many-locals
        # pylint: disable=cyclic-import

        name = player.getname()
        color = player.color
        highlight = player.highlight

        light_color = _math.normalized_color(color)
        display_color = _ba.safecolor(color, target_intensity=0.75)
        spaz = MyPlayerSpaz(color=color,
                            highlight=highlight,
                            character=player.character,
                            player=player)

        player.actor = spaz
        assert spaz.node
        if position is None:
            # In teams-mode get our team-start-location.
            if isinstance(self.session, DualTeamSession):
                position = (self.map.get_start_position(player.team.id))
            else:
                # Otherwise do free-for-all spawn locations.
                position = self.map.get_ffa_start_position(self.players)
        # If this is co-op and we're on Courtyard or Runaround, add the
        # material that allows us to collide with the player-walls.
        # FIXME: Need to generalize this.
        if isinstance(self.session, CoopSession) and self.map.getname() in [
                'Courtyard', 'Tower D'
        ]:
            mat = self.map.preloaddata['collide_with_wall_material']
            assert isinstance(spaz.node.materials, tuple)
            assert isinstance(spaz.node.roller_materials, tuple)
            spaz.node.materials += (mat, )
            spaz.node.roller_materials += (mat, )

        spaz.node.name = name
        spaz.node.name_color = display_color
        spaz.connect_controls_to_player()
        # Move to the stand position and add a flash of light.
        spaz.handlemessage(
            StandMessage(
                position,
                angle if angle is not None else random.uniform(0, 360)))
        _ba.playsound(self._spawn_sound, 1, position=spaz.node.position)
        light = _ba.newnode('light', attrs={'color': light_color})
        spaz.node.connectattr('position', light, 'position')
        animate(light, 'intensity', {0: 0, 0.25: 1, 0.5: 0})
        _ba.timer(0.5, light.delete)
        return spaz
Example #6
0
def party_icon_activate(origin: Sequence[float]) -> None:
    import weakref
    from bastd.ui.party import PartyWindow
    app = _ba.app
    _ba.playsound(_ba.getsound('swish'))

    # If it exists, dismiss it; otherwise make a new one.
    if app.ui.party_window is not None and app.ui.party_window() is not None:
        app.ui.party_window().close()
    else:
        app.ui.party_window = weakref.ref(PartyWindow(origin=origin))
Example #7
0
    def on_player_leave(self, sessionplayer: ba.SessionPlayer) -> None:
        """Called when a previously-accepted ba.SessionPlayer leaves."""

        if sessionplayer not in self.sessionplayers:
            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.sessionplayers.remove(sessionplayer)
Example #8
0
def _request_storage_permission() -> bool:
    """If needed, requests storage permission from the user (& return true)."""
    from ba._language 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
    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)
Example #10
0
 def show_post_purchase_message(self) -> None:
     """(internal)"""
     from ba._language import Lstr
     from ba._generated.enums import TimeType
     cur_time = _ba.time(TimeType.REAL)
     if (self.last_post_purchase_message_time is None
             or cur_time - self.last_post_purchase_message_time > 3.0):
         self.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'))
Example #11
0
def handle_scan_results(results: ScanResults) -> None:
    """Called in the game thread with results of a completed scan."""
    from ba import _lang

    # 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 != '':
        _ba.screenmessage(_lang.Lstr(resource='scanScriptsErrorText'),
                          color=(1, 0, 0))
        _ba.playsound(_ba.getsound('error'))
        if results.warnings != '':
            _ba.log(results.warnings, to_server=False)
        if results.errors != '':
            _ba.log(results.errors)
Example #12
0
def show_user_scripts() -> None:
    """Open or nicely print the location of the user-scripts directory."""
    from ba import _lang
    from ba._enums import Permission
    app = _ba.app

    # First off, if we need permission for this, ask for it.
    if not _ba.have_permission(Permission.STORAGE):
        _ba.playsound(_ba.getsound('error'))
        _ba.screenmessage(_lang.Lstr(resource='storagePermissionAccessText'),
                          color=(1, 0, 0))
        _ba.request_permission(Permission.STORAGE)
        return

    # Secondly, if the dir doesn't exist, attempt to make it.
    if not os.path.exists(app.python_directory_user):
        os.makedirs(app.python_directory_user)

    # On android, attempt to write a file in their user-scripts dir telling
    # them about modding. This also has the side-effect of allowing us to
    # media-scan that dir so it shows up in android-file-transfer, since it
    # doesn't seem like there's a way to inform the media scanner of an empty
    # directory, which means they would have to reboot their device before
    # they can see it.
    if app.platform == 'android':
        try:
            usd: Optional[str] = app.python_directory_user
            if usd is not None and os.path.isdir(usd):
                file_name = usd + '/about_this_folder.txt'
                with open(file_name, 'w') as outfile:
                    outfile.write('You can drop files in here to mod the game.'
                                  '  See settings/advanced'
                                  ' in the game for more info.')
                _ba.android_media_scan_file(file_name)
        except Exception:
            from ba import _error
            _error.print_exception('error writing about_this_folder stuff')

    # On a few platforms we try to open the dir in the UI.
    if app.platform in ['mac', 'windows']:
        _ba.open_dir_externally(app.python_directory_user)

    # Otherwise we just print a pretty version of it.
    else:
        _ba.screenmessage(get_human_readable_user_scripts_path())
 def _continue_choice(self, do_continue: bool) -> None:
     self._is_waiting_for_continue = False
     if self.has_ended():
         return
     with _ba.Context(self):
         if do_continue:
             _ba.playsound(_ba.getsound('shieldUp'))
             _ba.playsound(_ba.getsound('cashRegister'))
             _ba.add_transaction({
                 'type': 'CONTINUE',
                 'cost': self._continue_cost
             })
             _ba.run_transactions()
             self._continue_cost = (
                 self._continue_cost * self._continue_cost_mult +
                 self._continue_cost_offset)
             self.on_continue()
         else:
             self.end_game()
Example #14
0
    def resume(self) -> None:
        """Resume the game due to a user request or menu closing.

        If there's a foreground host-activity that's currently paused, tell it
        to resume.
        """

        # FIXME: Shouldn't be touching scene stuff here;
        #  should just pass the request on to the host-session.
        activity = _ba.get_foreground_host_activity()
        if activity is not None:
            with _ba.Context(activity):
                globs = activity.globalsnode
                if globs.paused:
                    _ba.playsound(_ba.getsound('refWhistle'))
                    globs.paused = False

                    # FIXME: This should not be an actor attr.
                    activity.paused_text = None
Example #15
0
    def handlemessage(self, msg: Any) -> Any:
        """Standard generic message handler."""

        if isinstance(msg, ChangeMessage):
            self._handle_repeat_message_attack()

            # If we've been removed from the lobby, ignore this stuff.
            if self._dead:
                from ba import _error
                _error.print_error('chooser got ChangeMessage after dying')
                return

            if not self._text_node:
                from ba import _error
                _error.print_error('got ChangeMessage after nodes died')
                return

            if msg.what == 'team':
                teams = self.lobby.teams
                if len(teams) > 1:
                    _ba.playsound(self._swish_sound)
                self._selected_team_index = (
                    (self._selected_team_index + msg.value) % len(teams))
                self._update_text()
                self.update_position()
                self._update_icon()

            elif msg.what == 'profileindex':
                if len(self._profilenames) == 1:

                    # This should be pretty hard to hit now with
                    # automatic local accounts.
                    _ba.playsound(_ba.getsound('error'))
                else:

                    # Pick the next player profile and assign our name
                    # and character based on that.
                    _ba.playsound(self._deek_sound)
                    self._profileindex = ((self._profileindex + msg.value) %
                                          len(self._profilenames))
                    self.update_from_player_profiles()

            elif msg.what == 'character':
                _ba.playsound(self._click_sound)
                # update our index in our local list of characters
                self.character_index = ((self.character_index + msg.value) %
                                        len(self.character_names))
                self._update_text()
                self._update_icon()

            elif msg.what == 'ready':
                self._handle_ready_msg(bool(msg.value))
        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
            # a current position for them.
            our_pos: Optional[ba.Vec3] = None
            if self._sessionplayer:
                if self._sessionplayer.activityplayer is not None:
                    try:
                        our_pos = self._sessionplayer.activityplayer.position
                    except NotFoundError:
                        pass
            if our_pos is None:
                return

            # Jitter position a bit since these often come in clusters.
            our_pos = _ba.Vec3(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))
Example #17
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'))
Example #18
0
    def _on_player_ready(self, chooser: ba.Chooser) -> None:
        """Called when a ba.Player has checked themself ready."""
        lobby = chooser.lobby
        activity = self._activity_weak()

        # This happens sometimes. That seems like it shouldn't be happening;
        # when would we have a session and a chooser with players but no
        # active activity?
        if activity is None:
            print('_on_player_ready called with no activity.')
            return

        # In joining-activities, we wait till all choosers are ready
        # and then create all players at once.
        if activity.is_joining_activity:
            if not lobby.check_all_ready():
                return
            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'))

        # Otherwise just add players on the fly.
        else:
            self._add_chosen_player(chooser)
            lobby.remove_chooser(chooser.getplayer())
Example #19
0
    def _handle_ready_msg(self, ready: bool) -> None:
        force_team_switch = False

        # Team auto-balance kicks us to another team if we try to
        # join the team with the most players.
        if not self._ready:
            if _ba.app.config.get('Auto Balance Teams', False):
                lobby = self.lobby
                sessionteams = lobby.sessionteams
                if len(sessionteams) > 1:

                    # First, calc how many players are on each team
                    # ..we need to count both active players and
                    # choosers that have been marked as ready.
                    team_player_counts = {}
                    for sessionteam in sessionteams:
                        team_player_counts[sessionteam.id] = len(
                            sessionteam.players)
                    for chooser in lobby.choosers:
                        if chooser.ready:
                            team_player_counts[chooser.sessionteam.id] += 1
                    largest_team_size = max(team_player_counts.values())
                    smallest_team_size = (min(team_player_counts.values()))

                    # Force switch if we're on the biggest sessionteam
                    # and there's a smaller one available.
                    if (largest_team_size != smallest_team_size
                            and team_player_counts[self.sessionteam.id] >=
                            largest_team_size):
                        force_team_switch = True

        # Either force switch teams, or actually for realsies do the set-ready.
        if force_team_switch:
            _ba.playsound(self._errorsound)
            self.handlemessage(ChangeMessage('team', 1))
        else:
            _ba.playsound(self._punchsound)
            self._set_ready(ready)
    def on_player_request(self, player: ba.SessionPlayer) -> bool:
        """Called when a new ba.Player wants to join the Session.

        This should return True or False to accept/reject.
        """

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

            if len(self.sessionplayers) >= 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.inputdevice.client_id],
                                  transient=True)
                return False

        _ba.playsound(_ba.getsound('dripity'))
        return True
Example #21
0
def in_progress_message() -> None:
    from ba._language import Lstr
    _ba.playsound(_ba.getsound('error'))
    _ba.screenmessage(Lstr(resource='getTicketsWindow.inProgressText'),
                      color=(1, 0, 0))
Example #22
0
def gear_vr_controller_warning() -> None:
    from ba._language import Lstr
    _ba.playsound(_ba.getsound('error'))
    _ba.screenmessage(Lstr(resource='usesExternalControllerText'),
                      color=(1, 0, 0))
Example #23
0
def purchase_already_in_progress_error() -> None:
    from ba._language import Lstr
    _ba.playsound(_ba.getsound('error'))
    _ba.screenmessage(Lstr(resource='store.purchaseAlreadyInProgressText'),
                      color=(1, 0, 0))
Example #24
0
    def on_player_leave(self, player: ba.Player) -> None:
        """Called when a previously-accepted ba.Player leaves the session."""
        # pylint: disable=too-many-statements
        # pylint: disable=too-many-branches
        # pylint: disable=cyclic-import
        from ba._freeforallsession import FreeForAllSession
        from ba._lang import Lstr
        from ba import _error

        # Remove them from the game rosters.
        if player in self.players:

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

            team: Optional[ba.Team]

            # The player will have no team if they are still in the lobby.
            try:
                team = player.team
            except _error.TeamNotFoundError:
                team = None

            activity = self._activity_weak()

            # If he had no team, he's in the lobby.
            # If we have a current activity with a lobby, ask them to
            # remove him.
            if team is None:
                with _ba.Context(self):
                    try:
                        self.lobby.remove_chooser(player)
                    except Exception:
                        _error.print_exception(
                            'Error in Lobby.remove_chooser()')

            # *If* they were actually in the game, announce their departure.
            if team is not None:
                _ba.screenmessage(
                    Lstr(resource='playerLeftText',
                         subs=[('${PLAYER}', player.get_name(full=True))]))

            # Remove him from his team and session lists.
            # (he may not be on the team list since player are re-added to
            # team lists every activity)
            if team is not None and player in team.players:

                # Testing; can remove this eventually.
                if isinstance(self, FreeForAllSession):
                    if len(team.players) != 1:
                        _error.print_error('expected 1 player in FFA team')
                team.players.remove(player)

            # Remove player from any current activity.
            if activity is not None and player in activity.players:
                activity.players.remove(player)

                # Run the activity callback unless its been expired.
                if not activity.is_expired():
                    try:
                        with _ba.Context(activity):
                            activity.on_player_leave(player)
                    except Exception:
                        _error.print_exception(
                            'exception in on_player_leave for activity',
                            activity)
                else:
                    _error.print_error('expired activity in on_player_leave;'
                                       " shouldn't happen")

                player.set_activity(None)
                player.set_node(None)

                # Reset the player; this will remove its actor-ref and clear
                # its calls/etc
                try:
                    with _ba.Context(activity):
                        player.reset()
                except Exception:
                    _error.print_exception(
                        'exception in player.reset in'
                        ' on_player_leave for player', player)

            # If we're a non-team session, remove the player's team completely.
            if not self._use_teams and team is not None:

                # If the team's in an activity, call its on_team_leave
                # callback.
                if activity is not None and team in activity.teams:
                    activity.teams.remove(team)

                    if not activity.is_expired():
                        try:
                            with _ba.Context(activity):
                                activity.on_team_leave(team)
                        except Exception:
                            _error.print_exception(
                                'exception in on_team_leave for activity',
                                activity)
                    else:
                        _error.print_error(
                            'expired activity in on_player_leave p2'
                            "; shouldn't happen")

                    # Clear the team's game-data (so dying stuff will
                    # have proper context).
                    try:
                        with _ba.Context(activity):
                            team.reset_gamedata()
                    except Exception:
                        _error.print_exception(
                            'exception clearing gamedata for team:', team,
                            'for player:', player, 'in activity:', activity)

                # Remove the team from the session.
                self.teams.remove(team)
                try:
                    with _ba.Context(self):
                        self.on_team_leave(team)
                except Exception:
                    _error.print_exception(
                        'exception in on_team_leave for session', self)

                # Clear the team's session-data (so dying stuff will
                # have proper context).
                try:
                    with _ba.Context(self):
                        team.reset_sessiondata()
                except Exception:
                    _error.print_exception(
                        'exception clearing sessiondata for team:', team,
                        'in session:', self)

            # Now remove them from the session list.
            self.players.remove(player)

        else:
            print('ERROR: Session.on_player_leave called'
                  ' for player not in our list.')
Example #25
0
def play_gong_sound() -> None:
    _ba.playsound(_ba.getsound('gong'))
Example #26
0
def temporarily_unavailable_message() -> None:
    from ba._language import Lstr
    _ba.playsound(_ba.getsound('error'))
    _ba.screenmessage(
        Lstr(resource='getTicketsWindow.unavailableTemporarilyText'),
        color=(1, 0, 0))
Example #27
0
def purchase_not_valid_error() -> None:
    from ba._language import Lstr
    _ba.playsound(_ba.getsound('error'))
    _ba.screenmessage(Lstr(resource='store.purchaseNotValidError',
                           subs=[('${EMAIL}', '*****@*****.**')]),
                      color=(1, 0, 0))
Example #28
0
    def show_completion_banner(self, sound: bool = True) -> None:
        """Create the banner/sound for an acquired achievement announcement."""
        from ba import _account
        from ba import _gameutils
        from bastd.actor.text import Text
        from bastd.actor.image import Image
        from ba._general import WeakCall
        from ba._lang import Lstr
        from ba._messages import DieMessage
        from ba._enums import TimeType, SpecialChar
        app = _ba.app
        app.last_achievement_display_time = _ba.time(TimeType.REAL)

        # Just piggy-back onto any current activity
        # (should we use the session instead?..)
        activity = _ba.getactivity(doraise=False)

        # If this gets called while this achievement is occupying a slot
        # already, ignore it. (probably should never happen in real
        # life but whatevs).
        if self._completion_banner_slot is not None:
            return

        if activity is None:
            print('show_completion_banner() called with no current activity!')
            return

        if sound:
            _ba.playsound(_ba.getsound('achievement'), host_only=True)
        else:
            _ba.timer(
                0.5,
                lambda: _ba.playsound(_ba.getsound('ding'), host_only=True))

        in_time = 0.300
        out_time = 3.5

        base_vr_depth = 200

        # Find the first free slot.
        i = 0
        while True:
            if i not in app.achievement_completion_banner_slots:
                app.achievement_completion_banner_slots.add(i)
                self._completion_banner_slot = i

                # Remove us from that slot when we close.
                # Use a real-timer in the UI context so the removal runs even
                # if our activity/session dies.
                with _ba.Context('ui'):
                    _ba.timer(in_time + out_time,
                              self._remove_banner_slot,
                              timetype=TimeType.REAL)
                break
            i += 1
        assert self._completion_banner_slot is not None
        y_offs = 110 * self._completion_banner_slot
        objs: List[ba.Actor] = []
        obj = Image(_ba.gettexture('shadow'),
                    position=(-30, 30 + y_offs),
                    front=True,
                    attach=Image.Attach.BOTTOM_CENTER,
                    transition=Image.Transition.IN_BOTTOM,
                    vr_depth=base_vr_depth - 100,
                    transition_delay=in_time,
                    transition_out_delay=out_time,
                    color=(0.0, 0.1, 0, 1),
                    scale=(1000, 300)).autoretain()
        objs.append(obj)
        assert obj.node
        obj.node.host_only = True
        obj = Image(_ba.gettexture('light'),
                    position=(-180, 60 + y_offs),
                    front=True,
                    attach=Image.Attach.BOTTOM_CENTER,
                    vr_depth=base_vr_depth,
                    transition=Image.Transition.IN_BOTTOM,
                    transition_delay=in_time,
                    transition_out_delay=out_time,
                    color=(1.8, 1.8, 1.0, 0.0),
                    scale=(40, 300)).autoretain()
        objs.append(obj)
        assert obj.node
        obj.node.host_only = True
        obj.node.premultiplied = True
        combine = _ba.newnode('combine', owner=obj.node, attrs={'size': 2})
        _gameutils.animate(
            combine, 'input0', {
                in_time: 0,
                in_time + 0.4: 30,
                in_time + 0.5: 40,
                in_time + 0.6: 30,
                in_time + 2.0: 0
            })
        _gameutils.animate(
            combine, 'input1', {
                in_time: 0,
                in_time + 0.4: 200,
                in_time + 0.5: 500,
                in_time + 0.6: 200,
                in_time + 2.0: 0
            })
        combine.connectattr('output', obj.node, 'scale')
        _gameutils.animate(obj.node,
                           'rotate', {
                               0: 0.0,
                               0.35: 360.0
                           },
                           loop=True)
        obj = Image(self.get_icon_texture(True),
                    position=(-180, 60 + y_offs),
                    attach=Image.Attach.BOTTOM_CENTER,
                    front=True,
                    vr_depth=base_vr_depth - 10,
                    transition=Image.Transition.IN_BOTTOM,
                    transition_delay=in_time,
                    transition_out_delay=out_time,
                    scale=(100, 100)).autoretain()
        objs.append(obj)
        assert obj.node
        obj.node.host_only = True

        # Flash.
        color = self.get_icon_color(True)
        combine = _ba.newnode('combine', owner=obj.node, attrs={'size': 3})
        keys = {
            in_time: 1.0 * color[0],
            in_time + 0.4: 1.5 * color[0],
            in_time + 0.5: 6.0 * color[0],
            in_time + 0.6: 1.5 * color[0],
            in_time + 2.0: 1.0 * color[0]
        }
        _gameutils.animate(combine, 'input0', keys)
        keys = {
            in_time: 1.0 * color[1],
            in_time + 0.4: 1.5 * color[1],
            in_time + 0.5: 6.0 * color[1],
            in_time + 0.6: 1.5 * color[1],
            in_time + 2.0: 1.0 * color[1]
        }
        _gameutils.animate(combine, 'input1', keys)
        keys = {
            in_time: 1.0 * color[2],
            in_time + 0.4: 1.5 * color[2],
            in_time + 0.5: 6.0 * color[2],
            in_time + 0.6: 1.5 * color[2],
            in_time + 2.0: 1.0 * color[2]
        }
        _gameutils.animate(combine, 'input2', keys)
        combine.connectattr('output', obj.node, 'color')

        obj = Image(_ba.gettexture('achievementOutline'),
                    model_transparent=_ba.getmodel('achievementOutline'),
                    position=(-180, 60 + y_offs),
                    front=True,
                    attach=Image.Attach.BOTTOM_CENTER,
                    vr_depth=base_vr_depth,
                    transition=Image.Transition.IN_BOTTOM,
                    transition_delay=in_time,
                    transition_out_delay=out_time,
                    scale=(100, 100)).autoretain()
        assert obj.node
        obj.node.host_only = True

        # Flash.
        color = (2, 1.4, 0.4, 1)
        combine = _ba.newnode('combine', owner=obj.node, attrs={'size': 3})
        keys = {
            in_time: 1.0 * color[0],
            in_time + 0.4: 1.5 * color[0],
            in_time + 0.5: 6.0 * color[0],
            in_time + 0.6: 1.5 * color[0],
            in_time + 2.0: 1.0 * color[0]
        }
        _gameutils.animate(combine, 'input0', keys)
        keys = {
            in_time: 1.0 * color[1],
            in_time + 0.4: 1.5 * color[1],
            in_time + 0.5: 6.0 * color[1],
            in_time + 0.6: 1.5 * color[1],
            in_time + 2.0: 1.0 * color[1]
        }
        _gameutils.animate(combine, 'input1', keys)
        keys = {
            in_time: 1.0 * color[2],
            in_time + 0.4: 1.5 * color[2],
            in_time + 0.5: 6.0 * color[2],
            in_time + 0.6: 1.5 * color[2],
            in_time + 2.0: 1.0 * color[2]
        }
        _gameutils.animate(combine, 'input2', keys)
        combine.connectattr('output', obj.node, 'color')
        objs.append(obj)

        objt = Text(Lstr(value='${A}:',
                         subs=[('${A}', Lstr(resource='achievementText'))]),
                    position=(-120, 91 + y_offs),
                    front=True,
                    v_attach=Text.VAttach.BOTTOM,
                    vr_depth=base_vr_depth - 10,
                    transition=Text.Transition.IN_BOTTOM,
                    flatness=0.5,
                    transition_delay=in_time,
                    transition_out_delay=out_time,
                    color=(1, 1, 1, 0.8),
                    scale=0.65).autoretain()
        objs.append(objt)
        assert objt.node
        objt.node.host_only = True

        objt = Text(self.display_name,
                    position=(-120, 50 + y_offs),
                    front=True,
                    v_attach=Text.VAttach.BOTTOM,
                    transition=Text.Transition.IN_BOTTOM,
                    vr_depth=base_vr_depth,
                    flatness=0.5,
                    transition_delay=in_time,
                    transition_out_delay=out_time,
                    flash=True,
                    color=(1, 0.8, 0, 1.0),
                    scale=1.5).autoretain()
        objs.append(objt)
        assert objt.node
        objt.node.host_only = True

        objt = Text(_ba.charstr(SpecialChar.TICKET),
                    position=(-120 - 170 + 5, 75 + y_offs - 20),
                    front=True,
                    v_attach=Text.VAttach.BOTTOM,
                    h_align=Text.HAlign.CENTER,
                    v_align=Text.VAlign.CENTER,
                    transition=Text.Transition.IN_BOTTOM,
                    vr_depth=base_vr_depth,
                    transition_delay=in_time,
                    transition_out_delay=out_time,
                    flash=True,
                    color=(0.5, 0.5, 0.5, 1),
                    scale=3.0).autoretain()
        objs.append(objt)
        assert objt.node
        objt.node.host_only = True

        objt = Text('+' + str(self.get_award_ticket_value()),
                    position=(-120 - 180 + 5, 80 + y_offs - 20),
                    v_attach=Text.VAttach.BOTTOM,
                    front=True,
                    h_align=Text.HAlign.CENTER,
                    v_align=Text.VAlign.CENTER,
                    transition=Text.Transition.IN_BOTTOM,
                    vr_depth=base_vr_depth,
                    flatness=0.5,
                    shadow=1.0,
                    transition_delay=in_time,
                    transition_out_delay=out_time,
                    flash=True,
                    color=(0, 1, 0, 1),
                    scale=1.5).autoretain()
        objs.append(objt)
        assert objt.node
        objt.node.host_only = True

        # Add the 'x 2' if we've got pro.
        if _account.have_pro():
            objt = Text('x 2',
                        position=(-120 - 180 + 45, 80 + y_offs - 50),
                        v_attach=Text.VAttach.BOTTOM,
                        front=True,
                        h_align=Text.HAlign.CENTER,
                        v_align=Text.VAlign.CENTER,
                        transition=Text.Transition.IN_BOTTOM,
                        vr_depth=base_vr_depth,
                        flatness=0.5,
                        shadow=1.0,
                        transition_delay=in_time,
                        transition_out_delay=out_time,
                        flash=True,
                        color=(0.4, 0, 1, 1),
                        scale=0.9).autoretain()
            objs.append(objt)
            assert objt.node
            objt.node.host_only = True

        objt = Text(self.description_complete,
                    position=(-120, 30 + y_offs),
                    front=True,
                    v_attach=Text.VAttach.BOTTOM,
                    transition=Text.Transition.IN_BOTTOM,
                    vr_depth=base_vr_depth - 10,
                    flatness=0.5,
                    transition_delay=in_time,
                    transition_out_delay=out_time,
                    color=(1.0, 0.7, 0.5, 1.0),
                    scale=0.8).autoretain()
        objs.append(objt)
        assert objt.node
        objt.node.host_only = True

        for actor in objs:
            _ba.timer(out_time + 1.000,
                      WeakCall(actor.handlemessage, DieMessage()))
    def _show_tip(self) -> None:
        # pylint: disable=too-many-locals
        from ba._gameutils import animate, GameTip
        from ba._enums import SpecialChar

        # If there's any tips left on the list, display one.
        if self.tips:
            tip = self.tips.pop(random.randrange(len(self.tips)))
            tip_title = Lstr(value='${A}:',
                             subs=[('${A}', Lstr(resource='tipText'))])
            icon: Optional[ba.Texture] = None
            sound: Optional[ba.Sound] = None
            if isinstance(tip, GameTip):
                icon = tip.icon
                sound = tip.sound
                tip = tip.text
                assert isinstance(tip, str)

            # Do a few substitutions.
            tip_lstr = Lstr(translate=('tips', tip),
                            subs=[('${PICKUP}',
                                   _ba.charstr(SpecialChar.TOP_BUTTON))])
            base_position = (75, 50)
            tip_scale = 0.8
            tip_title_scale = 1.2
            vrmode = _ba.app.vr_mode

            t_offs = -350.0
            tnode = _ba.newnode('text',
                                attrs={
                                    'text':
                                    tip_lstr,
                                    'scale':
                                    tip_scale,
                                    'maxwidth':
                                    900,
                                    'position': (base_position[0] + t_offs,
                                                 base_position[1]),
                                    'h_align':
                                    'left',
                                    'vr_depth':
                                    300,
                                    'shadow':
                                    1.0 if vrmode else 0.5,
                                    'flatness':
                                    1.0 if vrmode else 0.5,
                                    'v_align':
                                    'center',
                                    'v_attach':
                                    'bottom'
                                })
            t2pos = (base_position[0] + t_offs - (20 if icon is None else 82),
                     base_position[1] + 2)
            t2node = _ba.newnode('text',
                                 owner=tnode,
                                 attrs={
                                     'text': tip_title,
                                     'scale': tip_title_scale,
                                     'position': t2pos,
                                     'h_align': 'right',
                                     'vr_depth': 300,
                                     'shadow': 1.0 if vrmode else 0.5,
                                     'flatness': 1.0 if vrmode else 0.5,
                                     'maxwidth': 140,
                                     'v_align': 'center',
                                     'v_attach': 'bottom'
                                 })
            if icon is not None:
                ipos = (base_position[0] + t_offs - 40, base_position[1] + 1)
                img = _ba.newnode('image',
                                  attrs={
                                      'texture': icon,
                                      'position': ipos,
                                      'scale': (50, 50),
                                      'opacity': 1.0,
                                      'vr_depth': 315,
                                      'color': (1, 1, 1),
                                      'absolute_scale': True,
                                      'attach': 'bottomCenter'
                                  })
                animate(img, 'opacity', {0: 0, 1.0: 1, 4.0: 1, 5.0: 0})
                _ba.timer(5.0, img.delete)
            if sound is not None:
                _ba.playsound(sound)

            combine = _ba.newnode('combine',
                                  owner=tnode,
                                  attrs={
                                      'input0': 1.0,
                                      'input1': 0.8,
                                      'input2': 1.0,
                                      'size': 4
                                  })
            combine.connectattr('output', tnode, 'color')
            combine.connectattr('output', t2node, 'color')
            animate(combine, 'input3', {0: 0, 1.0: 1, 4.0: 1, 5.0: 0})
            _ba.timer(5.0, tnode.delete)
Example #30
0
def error_message() -> None:
    from ba._language import Lstr
    _ba.playsound(_ba.getsound('error'))
    _ba.screenmessage(Lstr(resource='errorText'), color=(1, 0, 0))