Ejemplo n.º 1
0
 def _handle_splat(self) -> None:
     node = ba.get_collision_info('opposing_node')
     if (node is not self.owner
             and ba.time() - self._last_sticky_sound_time > 1.0):
         self._last_sticky_sound_time = ba.time()
         assert self.node
         ba.playsound(get_factory().sticky_impact_sound,
                      2.0,
                      position=self.node.position)
 def _handle_splat(self) -> None:
     node = ba.getcollision().opposingnode
     if (node is not self.owner
             and ba.time() - self._last_sticky_sound_time > 1.0):
         self._last_sticky_sound_time = ba.time()
         assert self.node
         ba.playsound(BombFactory.get().sticky_impact_sound,
                      2.0,
                      position=self.node.position)
Ejemplo n.º 3
0
    def update_scores(self) -> None:
        """ update scoreboard and check for winners """
        # FIXME: tidy this up
        # pylint: disable=too-many-nested-blocks
        have_scoring_team = False
        win_score = self._score_to_win
        for team in [self.teams[0], self._bot_team]:
            assert team is not None
            assert self._scoreboard is not None
            self._scoreboard.set_team_value(team, team.gamedata['score'],
                                            win_score)
            if team.gamedata['score'] >= win_score:
                if not have_scoring_team:
                    self.scoring_team = team
                    if team is self._bot_team:
                        self.continue_or_end_game()
                    else:
                        ba.setmusic(ba.MusicType.VICTORY)

                        # Completion achievements.
                        assert self._bot_team is not None
                        if self._preset in ['rookie', 'rookie_easy']:
                            self._award_achievement('Rookie Football Victory',
                                                    sound=False)
                            if self._bot_team.gamedata['score'] == 0:
                                self._award_achievement(
                                    'Rookie Football Shutout', sound=False)
                        elif self._preset in ['pro', 'pro_easy']:
                            self._award_achievement('Pro Football Victory',
                                                    sound=False)
                            if self._bot_team.gamedata['score'] == 0:
                                self._award_achievement('Pro Football Shutout',
                                                        sound=False)
                        elif self._preset in ['uber', 'uber_easy']:
                            self._award_achievement('Uber Football Victory',
                                                    sound=False)
                            if self._bot_team.gamedata['score'] == 0:
                                self._award_achievement(
                                    'Uber Football Shutout', sound=False)
                            if (not self._player_has_dropped_bomb
                                    and not self._player_has_punched):
                                self._award_achievement('Got the Moves',
                                                        sound=False)
                        self._bots.stop_moving()
                        self.show_zoom_message(ba.Lstr(resource='victoryText'),
                                               scale=1.0,
                                               duration=4.0)
                        self.celebrate(10.0)
                        assert self._starttime_ms is not None
                        self._final_time_ms = int(
                            ba.time(timeformat=ba.TimeFormat.MILLISECONDS) -
                            self._starttime_ms)
                        self._time_text_timer = None
                        assert (self._time_text_input is not None
                                and self._time_text_input.node)
                        self._time_text_input.node.timemax = (
                            self._final_time_ms)

                        # FIXME: Does this still need to be deferred?
                        ba.pushcall(ba.Call(self.do_end, 'victory'))
Ejemplo n.º 4
0
    def _drop_powerups(self,
                       standard_points: bool = False,
                       force_first: str = None) -> None:
        """Generic powerup drop."""

        # If its been a minute since our last wave finished emerging, stop
        # giving out land-mine powerups. (prevents players from waiting
        # around for them on purpose and filling the map up)
        if ba.time() - self._last_wave_end_time > 60.0:
            extra_excludes = ['land_mines']
        else:
            extra_excludes = []

        if standard_points:
            points = self.map.powerup_spawn_points
            for i in range(len(points)):
                ba.timer(
                    1.0 + i * 0.5,
                    ba.Call(self._drop_powerup, i,
                            force_first if i == 0 else None))
        else:
            pos = (self._powerup_center[0] + random.uniform(
                -1.0 * self._powerup_spread[0], 1.0 * self._powerup_spread[0]),
                   self._powerup_center[1],
                   self._powerup_center[2] + random.uniform(
                       -self._powerup_spread[1], self._powerup_spread[1]))

            # drop one random one somewhere..
            assert self._exclude_powerups is not None
            PowerupBox(
                position=pos,
                poweruptype=PowerupBoxFactory.get().get_random_powerup_type(
                    excludetypes=self._exclude_powerups +
                    extra_excludes)).autoretain()
Ejemplo n.º 5
0
 def on_punch_press(self) -> None:
     """
     Called to 'press punch' on this spaz;
     used for player or AI connections.
     """
     if not self.node or self.frozen or self.node.knockout > 0.0:
         return
     t_ms = ba.time(timeformat=ba.TimeFormat.MILLISECONDS)
     assert isinstance(t_ms, int)
     if t_ms - self.last_punch_time_ms >= self._punch_cooldown:
         if self.punch_callback is not None:
             self.punch_callback(self)
         self._punched_nodes = set()  # Reset this.
         self.last_punch_time_ms = t_ms
         self.node.color = ((0 + random.random() * 6.5),
                            (0 + random.random() * 6.5),
                            (0 + random.random() * 6.5))
         self.node.highlight = ((0 + random.random() * 6.5),
                                (0 + random.random() * 6.5),
                                (0 + random.random() * 6.5))
         ba.emitfx(position=self.node.position,
                   velocity=self.node.velocity,
                   count=35,
                   scale=0.5,
                   spread=2,
                   chunk_type='spark')
         self.node.punch_pressed = True
         if not self.node.hold_node:
             ba.timer(
                 0.1,
                 ba.WeakCall(self._safe_play_sound,
                             SpazFactory.get().swish_sound, 0.8))
     self._turbo_filter_add_press('punch')
Ejemplo n.º 6
0
    def _update(self) -> None:
        """Periodic updating."""

        now = ba.time(ba.TimeType.REAL)

        self._update_currency_ui()

        if self._state.sub_tab is SubTabType.HOST:

            # If we're not signed in, just refresh to show that.
            if (_ba.get_account_state() != 'signed_in'
                    and self._showing_not_signed_in_screen):
                self._refresh_sub_tab()
            else:

                # Query an updated state periodically.
                if (self._last_hosting_state_query_time is None
                        or now - self._last_hosting_state_query_time > 15.0):
                    self._debug_server_comm('querying private party state')
                    if _ba.get_account_state() == 'signed_in':
                        _ba.add_transaction(
                            {'type': 'PRIVATE_PARTY_QUERY'},
                            callback=ba.WeakCall(
                                self._hosting_state_idle_response),
                        )
                        _ba.run_transactions()
                    else:
                        self._hosting_state_idle_response(None)
                    self._last_hosting_state_query_time = now
Ejemplo n.º 7
0
    def stop(self,
             endtime: Union[int, float] = None,
             timeformat: ba.TimeFormat = ba.TimeFormat.SECONDS) -> None:
        """End the timer.

        If 'endtime' is not None, it is used when calculating
        the final display time; otherwise the current time is used.

        'timeformat' applies to endtime and can be SECONDS or MILLISECONDS
        """
        if endtime is None:
            endtime = ba.time(timeformat=ba.TimeFormat.MILLISECONDS)
            timeformat = ba.TimeFormat.MILLISECONDS

        if self._starttime is None:
            print('Warning: OnScreenTimer.stop() called without start() first')
        else:
            endtime_ms: int
            if timeformat is ba.TimeFormat.SECONDS:
                endtime_ms = int(endtime * 1000)
            elif timeformat is ba.TimeFormat.MILLISECONDS:
                assert isinstance(endtime, int)
                endtime_ms = endtime
            else:
                raise ValueError(f'invalid timeformat: {timeformat}')

            self.inputnode.timemax = endtime_ms - self._starttime
Ejemplo n.º 8
0
 def start(self) -> None:
     """Start the timer."""
     tval = ba.time(timeformat=ba.TimeFormat.MILLISECONDS)
     assert isinstance(tval, int)
     self._starttime = tval
     self.inputnode.time1 = self._starttime
     ba.sharedobj('globals').connectattr('time', self.inputnode, 'time2')
Ejemplo n.º 9
0
    def _ping_parties_periodically(self) -> None:
        now = ba.time(ba.TimeType.REAL)

        # Go through our existing public party entries firing off pings
        # for any that have timed out.
        for party in list(self._parties.values()):
            if party.next_ping_time <= now and ba.app.ping_thread_count < 15:

                # Crank the interval up for high-latency or non-responding
                # parties to save us some useless work.
                mult = 1
                if party.ping_responses == 0:
                    if party.ping_attempts > 4:
                        mult = 10
                    elif party.ping_attempts > 2:
                        mult = 5
                if party.ping is not None:
                    mult = (10 if party.ping > 300 else
                            5 if party.ping > 150 else 2)

                interval = party.ping_interval * mult
                if DEBUG_SERVER_COMMUNICATION:
                    print(f'pinging #{party.index} cur={party.ping} '
                          f'interval={interval} '
                          f'({party.ping_responses}/{party.ping_attempts})')

                party.next_ping_time = now + party.ping_interval * mult
                party.ping_attempts += 1

                PingThread(party.address, party.port,
                           ba.WeakCall(self._ping_callback)).start()
 def check(self):
     current = ba.time(ba.TimeType.REAL,
                       timeformat=ba.TimeFormat.MILLISECONDS)
     for player in _ba.get_foreground_host_session().sessionplayers:
         last_input = int(player.inputdevice.get_last_input_time())
         afk_time = int((current - last_input) / 1000)
         if afk_time in range(INGAME_TIME, INGAME_TIME + 20):
             self.warn_player(
                 player.get_account_id(), "Press any button within " +
                 str(INGAME_TIME + 20 - afk_time) + " secs")
         if afk_time > INGAME_TIME + 20:
             player.remove_from_game()
     if LOBBY_KICK:
         current_players = []
         for player in _ba.get_game_roster():
             if player['client_id'] != -1 and len(player['players']) == 0:
                 current_players.append(player['client_id'])
                 if player['client_id'] not in self.lobbies:
                     self.lobbies[player['client_id']] = current
                 lobby_afk = int(
                     (current - self.lobbies[player['client_id']]) / 1000)
                 if lobby_afk in range(INLOBBY_TIME, INLOBBY_TIME + 10):
                     _ba.screenmessage("Join game within " +
                                       str(INLOBBY_TIME + 10 - lobby_afk) +
                                       " secs",
                                       color=(1, 0, 0),
                                       transient=True,
                                       clients=[player['client_id']])
                 if lobby_afk > INLOBBY_TIME + 10:
                     _ba.disconnect_client(player['client_id'], 0)
         # clean the lobbies dict
         temp = self.lobbies.copy()
         for clid in temp:
             if clid not in current_players:
                 del self.lobbies[clid]
Ejemplo n.º 11
0
    def handlemessage(self, msg: Any) -> Any:
        if isinstance(msg, ba.PlayerDiedMessage):

            # Augment standard behavior.
            super().handlemessage(msg)
            player: Player = msg.getplayer(Player)

            player.lives -= 1
            if player.lives < 0:
                ba.print_error(
                    "Got lives < 0 in Elim; this shouldn't happen. duo: True")
                player.lives = 0

            # If we have any icons, update their state.
            for icon in player.icons:
                icon.handle_player_died()

            # Play big death sound on our last death
            # or for every one in solo mode.
            if player.lives == 0:
                ba.playsound(SpazFactory.get().single_player_death_sound)

            # If we hit zero lives, we're dead (and our team might be too).
            if player.lives == 0:
                # If the whole team is now dead, mark their survival time.
                if self._get_total_team_lives(player.team) == 0:
                    assert self._start_time is not None
                    player.team.survival_seconds = int(ba.time() -
                                                       self._start_time)

            # In solo, put ourself at the back of the spawn order.
            if self._duo_mode:
                player.team.spawn_order.remove(player)
                player.team.spawn_order.append(player)
Ejemplo n.º 12
0
    def _on_pay_with_ad_press(self) -> None:
        from ba.internal import show_ad_2

        # If we're already entering, ignore.
        if self._entering:
            return

        if not self._have_valid_data:
            ba.screenmessage(ba.Lstr(resource='tournamentCheckingStateText'),
                             color=(1, 0, 0))
            ba.playsound(ba.getsound('error'))
            return

        # Deny if it looks like the tourney has ended.
        if self._seconds_remaining == 0:
            ba.screenmessage(ba.Lstr(resource='tournamentEndedText'),
                             color=(1, 0, 0))
            ba.playsound(ba.getsound('error'))
            return

        cur_time = ba.time(ba.TimeType.REAL)
        if cur_time - self._last_ad_press_time > 5.0:
            self._last_ad_press_time = cur_time
            show_ad_2('tournament_entry',
                      on_completion_call=ba.WeakCall(self._on_ad_complete))
Ejemplo n.º 13
0
    def handlemessage(self, msg: Any) -> Any:
        if isinstance(msg, PlayerSpazDeathMessage):

            # Augment standard behavior.
            super().handlemessage(msg)

            curtime = ba.time()

            # Record the player's moment of death.
            PlayerData.get(msg.spaz.player).death_time = curtime

            # In co-op mode, end the game the instant everyone dies
            # (more accurate looking).
            # In teams/ffa, allow a one-second fudge-factor so we can
            # get more draws if players die basically at the same time.
            if isinstance(self.session, ba.CoopSession):
                # Teams will still show up if we check now.. check in
                # the next cycle.
                ba.pushcall(self._check_end_game)

                # Also record this for a final setting of the clock.
                self._last_player_death_time = curtime
            else:
                ba.timer(1.0, self._check_end_game)

        else:
            # Default handler:
            super().handlemessage(msg)
Ejemplo n.º 14
0
    def end_game(self) -> None:

        # Stop our on-screen timer so players can see what they got.
        assert self._timer is not None
        self._timer.stop()

        results = ba.TeamGameResults()

        # If we won, set our score to the elapsed time
        # (there should just be 1 team here since this is co-op).
        # ..if we didn't win, leave scores as default (None) which means
        # we lost.
        if self._won:
            curtime = ba.time(timeformat=ba.TimeFormat.MILLISECONDS)
            assert isinstance(curtime, int)
            starttime = self._timer.getstarttime(
                timeformat=ba.TimeFormat.MILLISECONDS)
            assert isinstance(starttime, int)
            elapsed_time_ms = curtime - starttime
            ba.cameraflash()
            ba.playsound(self._winsound)
            for team in self.teams:
                for player in team.players:
                    if player.actor:
                        player.actor.handlemessage(ba.CelebrateMessage())
                results.set_team_score(team, elapsed_time_ms)

        # Ends the activity.
        self.end(results)
Ejemplo n.º 15
0
    def __init__(self, settings: dict):
        settings['map'] = 'Tower D'
        super().__init__(settings)
        shared = SharedObjects.get()
        self._preset = Preset(settings.get('preset', 'pro'))

        self._player_death_sound = ba.getsound('playerDeath')
        self._new_wave_sound = ba.getsound('scoreHit01')
        self._winsound = ba.getsound('score')
        self._cashregistersound = ba.getsound('cashRegister')
        self._bad_guy_score_sound = ba.getsound('shieldDown')
        self._heart_tex = ba.gettexture('heart')
        self._heart_model_opaque = ba.getmodel('heartOpaque')
        self._heart_model_transparent = ba.getmodel('heartTransparent')

        self._a_player_has_been_killed = False
        self._spawn_center = self._map_type.defs.points['spawn1'][0:3]
        self._tntspawnpos = self._map_type.defs.points['tnt_loc'][0:3]
        self._powerup_center = self._map_type.defs.boxes['powerup_region'][0:3]
        self._powerup_spread = (
            self._map_type.defs.boxes['powerup_region'][6] * 0.5,
            self._map_type.defs.boxes['powerup_region'][8] * 0.5)

        self._score_region_material = ba.Material()
        self._score_region_material.add_actions(
            conditions=('they_have_material', shared.player_material),
            actions=(
                ('modify_part_collision', 'collide', True),
                ('modify_part_collision', 'physical', False),
                ('call', 'at_connect', self._handle_reached_end),
            ))

        self._last_wave_end_time = ba.time()
        self._player_has_picked_up_powerup = False
        self._scoreboard: Optional[Scoreboard] = None
        self._game_over = False
        self._wavenum = 0
        self._can_end_wave = True
        self._score = 0
        self._time_bonus = 0
        self._score_region: Optional[ba.Actor] = None
        self._dingsound = ba.getsound('dingSmall')
        self._dingsoundhigh = ba.getsound('dingSmallHigh')
        self._exclude_powerups: Optional[List[str]] = None
        self._have_tnt: Optional[bool] = None
        self._waves: Optional[List[Wave]] = None
        self._bots = SpazBotSet()
        self._tntspawner: Optional[TNTSpawner] = None
        self._lives_bg: Optional[ba.NodeActor] = None
        self._start_lives = 10
        self._lives = self._start_lives
        self._lives_text: Optional[ba.NodeActor] = None
        self._flawless = True
        self._time_bonus_timer: Optional[ba.Timer] = None
        self._time_bonus_text: Optional[ba.NodeActor] = None
        self._time_bonus_mult: Optional[float] = None
        self._wave_text: Optional[ba.NodeActor] = None
        self._flawless_bonus: Optional[int] = None
        self._wave_update_timer: Optional[ba.Timer] = None
Ejemplo n.º 16
0
 def start(self) -> None:
     """Start the timer."""
     tval = ba.time(timeformat=ba.TimeFormat.MILLISECONDS)
     assert isinstance(tval, int)
     self._starttime_ms = tval
     self.inputnode.time1 = self._starttime_ms
     ba.getactivity().globalsnode.connectattr('time', self.inputnode,
                                              'time2')
Ejemplo n.º 17
0
 def _update(self) -> None:
     remaining = int(round(self._respawn_time - ba.time()))
     if remaining > 0:
         assert self._text is not None
         if self._text.node:
             self._text.node.text = str(remaining)
     else:
         self._visible = False
         self._image = self._text = self._timer = self._name = None
    def _handle_flag_entered_base(self, team: Team) -> None:
        try:
            flag = ba.getcollision().opposingnode.getdelegate(
                StickyStormCTFFlag, True)
        except ba.NotFoundError:
            # Don't think this should logically ever happen.
            print(
                'Error getting StickyStormCTFFlag in entering-base callback.')
            return

        if flag.team is team:
            team.home_flag_at_base = True

            # If the enemy flag is already here, score!
            if team.enemy_flag_at_base:
                self._score(team)
        else:
            team.enemy_flag_at_base = True
            if team.home_flag_at_base:
                # Award points to whoever was carrying the enemy flag.
                player = flag.last_player_to_hold
                if player and player.team is team:
                    assert self.stats
                    self.stats.player_scored(player, 50, big_message=True)

                # Update score and reset flags.
                self._score(team)

            # If the home-team flag isn't here, print a message to that effect.
            else:
                # Don't want slo-mo affecting this
                curtime = ba.time(ba.TimeType.BASE)
                if curtime - self._last_home_flag_notice_print_time > 5.0:
                    self._last_home_flag_notice_print_time = curtime
                    bpos = team.base_pos
                    tval = ba.Lstr(resource='ownFlagAtYourBaseWarning')
                    tnode = ba.newnode('text',
                                       attrs={
                                           'text':
                                           tval,
                                           'in_world':
                                           True,
                                           'scale':
                                           0.013,
                                           'color': (1, 1, 0, 1),
                                           'h_align':
                                           'center',
                                           'position':
                                           (bpos[0], bpos[1] + 3.2, bpos[2])
                                       })
                    ba.timer(5.1, tnode.delete)
                    ba.animate(tnode, 'scale', {
                        0.0: 0,
                        0.2: 0.013,
                        4.8: 0.013,
                        5.0: 0
                    })
Ejemplo n.º 19
0
    def _handle_flag_entered_base(self, team: ba.Team) -> None:
        node = ba.get_collision_info('opposing_node')
        assert isinstance(node, (ba.Node, type(None)))
        flag = CTFFlag.from_node(node)
        if not flag:
            print('Unable to get flag in _handle_flag_entered_base')
            return

        if flag.team is team:
            team.gamedata['home_flag_at_base'] = True

            # If the enemy flag is already here, score!
            if team.gamedata['enemy_flag_at_base']:
                self._score(team)
        else:
            team.gamedata['enemy_flag_at_base'] = True
            if team.gamedata['home_flag_at_base']:
                # Award points to whoever was carrying the enemy flag.
                player = flag.last_player_to_hold
                if player and player.team is team:
                    assert self.stats
                    self.stats.player_scored(player, 50, big_message=True)

                # Update score and reset flags.
                self._score(team)

            # If the home-team flag isn't here, print a message to that effect.
            else:
                # Don't want slo-mo affecting this
                curtime = ba.time(ba.TimeType.BASE)
                if curtime - self._last_home_flag_notice_print_time > 5.0:
                    self._last_home_flag_notice_print_time = curtime
                    bpos = team.gamedata['base_pos']
                    tval = ba.Lstr(resource='ownFlagAtYourBaseWarning')
                    tnode = ba.newnode('text',
                                       attrs={
                                           'text':
                                           tval,
                                           'in_world':
                                           True,
                                           'scale':
                                           0.013,
                                           'color': (1, 1, 0, 1),
                                           'h_align':
                                           'center',
                                           'position':
                                           (bpos[0], bpos[1] + 3.2, bpos[2])
                                       })
                    ba.timer(5.1, tnode.delete)
                    ba.animate(tnode, 'scale', {
                        0.0: 0,
                        0.2: 0.013,
                        4.8: 0.013,
                        5.0: 0
                    })
Ejemplo n.º 20
0
    def __init__(self, data: dict[str, Any]):
        self._dialog_id = data['dialogID']
        txt = ba.Lstr(translate=('serverResponses', data['text']),
                      subs=data.get('subs', [])).evaluate()
        txt = txt.strip()
        txt_scale = 1.5
        txt_height = (_ba.get_string_height(txt, suppress_warning=True) *
                      txt_scale)
        self._width = 500
        self._height = 130 + min(200, txt_height)
        uiscale = ba.app.ui.uiscale
        super().__init__(root_widget=ba.containerwidget(
            size=(self._width, self._height),
            transition='in_scale',
            scale=(1.8 if uiscale is ba.UIScale.SMALL else
                   1.35 if uiscale is ba.UIScale.MEDIUM else 1.0)))
        self._starttime = ba.time(ba.TimeType.REAL, ba.TimeFormat.MILLISECONDS)

        ba.playsound(ba.getsound('swish'))
        ba.textwidget(parent=self._root_widget,
                      position=(self._width * 0.5,
                                70 + (self._height - 70) * 0.5),
                      size=(0, 0),
                      color=(1.0, 3.0, 1.0),
                      scale=txt_scale,
                      h_align='center',
                      v_align='center',
                      text=txt,
                      maxwidth=self._width * 0.85,
                      max_height=(self._height - 110))
        show_cancel = data.get('showCancel', True)
        self._cancel_button: Optional[ba.Widget]
        if show_cancel:
            self._cancel_button = ba.buttonwidget(
                parent=self._root_widget,
                position=(30, 30),
                size=(160, 60),
                autoselect=True,
                label=ba.Lstr(resource='cancelText'),
                on_activate_call=self._cancel_press)
        else:
            self._cancel_button = None
        self._ok_button = ba.buttonwidget(
            parent=self._root_widget,
            position=((self._width - 182) if show_cancel else
                      (self._width * 0.5 - 80), 30),
            size=(160, 60),
            autoselect=True,
            label=ba.Lstr(resource='okText'),
            on_activate_call=self._ok_press)
        ba.containerwidget(edit=self._root_widget,
                           cancel_button=self._cancel_button,
                           start_button=self._ok_button,
                           selected_child=self._ok_button)
Ejemplo n.º 21
0
 def _ok_press(self) -> None:
     if ba.time(ba.TimeType.REAL,
                ba.TimeFormat.MILLISECONDS) - self._starttime < 1000:
         ba.playsound(ba.getsound('error'))
         return
     _ba.add_transaction({
         'type': 'DIALOG_RESPONSE',
         'dialogID': self._dialog_id,
         'response': 1
     })
     ba.containerwidget(edit=self._root_widget, transition='out_scale')
Ejemplo n.º 22
0
    def _type_char(self, char: str) -> None:
        ba.playsound(self._click_sound)
        if char.isspace():
            if (ba.time(ba.TimeType.REAL) - self._last_space_press <
                    self._double_space_interval):
                self._last_space_press = 0
                self._next_keyboard()
                self._del()  # We typed unneeded space around 1s ago.
                return
            self._last_space_press = ba.time(ba.TimeType.REAL)

        # Operate in unicode so we don't do anything funky like chop utf-8
        # chars in half.
        txt = cast(str, ba.textwidget(query=self._text_field))
        txt += char
        ba.textwidget(edit=self._text_field, text=txt)

        # If we were caps, go back only if not Shift is pressed twice.
        if self._mode == 'caps' and not self._double_press_shift:
            self._mode = 'normal'
        self._refresh()
Ejemplo n.º 23
0
    def wavedash(self) -> None:
        if not self.node:
            return

        isMoving = abs(self.node.move_up_down) >= 0.5 or abs(
            self.node.move_left_right) >= 0.5

        if self._dead or not self.grounded or not isMoving:
            return

        if self.node.knockout > 0.0 or self.frozen or self.node.hold_node:
            return

        t_ms = ba.time(timeformat=ba.TimeFormat.MILLISECONDS)
        assert isinstance(t_ms, int)

        if t_ms - self.last_wavedash_time_ms >= self._wavedash_cooldown:

            move = [self.node.move_left_right, -self.node.move_up_down]
            vel = [self.node.velocity[0], self.node.velocity[2]]

            move_length = math.hypot(move[0], move[1])
            vel_length = math.hypot(vel[0], vel[1])
            if vel_length < 1.25: return
            move_norm = [m / move_length for m in move]
            vel_norm = [v / vel_length for v in vel]
            dot = sum(x * y for x, y in zip(move_norm, vel_norm))
            turn_power = min(round(math.acos(dot) / math.pi, 2) * 1.3, 1)
            if turn_power < 0.2: return

            boost_power = math.sqrt(math.pow(vel[0], 2) +
                                    math.pow(vel[1], 2)) * 1.2
            boost_power = min(pow(boost_power, 4), 160)
            #print(boost_power * turn_power)

            self.last_wavedash_time_ms = t_ms

            # FX
            ba.emitfx(position=self.node.position,
                      velocity=(vel[0] * 0.5, -1, vel[1] * 0.5),
                      chunk_type='sweat',
                      count=8,
                      scale=boost_power / 160 * turn_power,
                      spread=0.25)

            # Boost itself
            pos = self.node.position
            for i in range(6):
                self.node.handlemessage('impulse', pos[0],
                                        -0.1 + pos[1] + i * 0.1, pos[2], 0, 0,
                                        0, boost_power * turn_power,
                                        boost_power * turn_power, 0, 0,
                                        move[0], 0, move[1])
Ejemplo n.º 24
0
    def buy(self, item: str) -> None:
        """Attempt to purchase the provided item."""
        from ba.internal import (get_available_sale_time,
                                 get_store_item_name_translated)
        from bastd.ui import account
        from bastd.ui.confirm import ConfirmWindow
        from bastd.ui import getcurrency

        # Prevent pressing buy within a few seconds of the last press
        # (gives the buttons time to disable themselves and whatnot).
        curtime = ba.time(ba.TimeType.REAL)
        if self._last_buy_time is not None and (curtime -
                                                self._last_buy_time) < 2.0:
            ba.playsound(ba.getsound('error'))
        else:
            if _ba.get_account_state() != 'signed_in':
                account.show_sign_in_prompt()
            else:
                self._last_buy_time = curtime

                # Pro is an actual IAP; the rest are ticket purchases.
                if item == 'pro':
                    ba.playsound(ba.getsound('click01'))

                    # Purchase either pro or pro_sale depending on whether
                    # there is a sale going on.
                    self._do_purchase_check('pro' if get_available_sale_time(
                        'extras') is None else 'pro_sale')
                else:
                    price = _ba.get_account_misc_read_val(
                        'price.' + item, None)
                    our_tickets = _ba.get_account_ticket_count()
                    if price is not None and our_tickets < price:
                        ba.playsound(ba.getsound('error'))
                        getcurrency.show_get_tickets_prompt()
                    else:

                        def do_it() -> None:
                            self._do_purchase_check(item,
                                                    is_ticket_purchase=True)

                        ba.playsound(ba.getsound('swish'))
                        ConfirmWindow(
                            ba.Lstr(resource='store.purchaseConfirmText',
                                    subs=[
                                        ('${ITEM}',
                                         get_store_item_name_translated(item))
                                    ]),
                            width=400,
                            height=120,
                            action=do_it,
                            ok_text=ba.Lstr(resource='store.purchaseText',
                                            fallback_resource='okText'))
Ejemplo n.º 25
0
    def end_game(self) -> None:
        cur_time = ba.time()
        assert self._timer is not None
        start_time = self._timer.getstarttime()

        # Mark death-time as now for any still-living players
        # and award players points for how long they lasted.
        # (these per-player scores are only meaningful in team-games)
        for team in self.teams:
            for player in team.players:
                playerdata = PlayerData.get(player)
                survived = False

                # Throw an extra fudge factor in so teams that
                # didn't die come out ahead of teams that did.
                if playerdata.death_time is None:
                    survived = True
                    playerdata.death_time = cur_time + 1

                # Award a per-player score depending on how many seconds
                # they lasted (per-player scores only affect teams mode;
                # everywhere else just looks at the per-team score).
                score = int(playerdata.death_time - self._timer.getstarttime())
                if survived:
                    score += 50  # A bit extra for survivors.
                self.stats.player_scored(player, score, screenmessage=False)

        # Stop updating our time text, and set the final time to match
        # exactly when our last guy died.
        self._timer.stop(endtime=self._last_player_death_time)

        # Ok now calc game results: set a score for each team and then tell
        # the game to end.
        results = ba.TeamGameResults()

        # Remember that 'free-for-all' mode is simply a special form
        # of 'teams' mode where each player gets their own team, so we can
        # just always deal in teams and have all cases covered.
        for team in self.teams:

            # Set the team score to the max time survived by any player on
            # that team.
            longest_life = 0.0
            for player in team.players:
                playerdata = PlayerData.get(player)
                assert playerdata.death_time is not None
                longest_life = max(longest_life,
                                   playerdata.death_time - start_time)

            # Submit the score value in milliseconds.
            results.set_team_score(team, int(1000.0 * longest_life))

        self.end(results=results)
Ejemplo n.º 26
0
    def _on_cancel(self) -> None:

        # Don't allow canceling for several seconds after poking an enter
        # button if it looks like we're waiting on a purchase or entering
        # the tournament.
        if ((ba.time(ba.TimeType.REAL, ba.TimeFormat.MILLISECONDS) -
             self._last_ticket_press_time < 6000) and
            (_ba.have_outstanding_transactions()
             or _ba.get_purchased(self._purchase_name) or self._entering)):
            ba.playsound(ba.getsound('error'))
            return
        self._transition_out()
Ejemplo n.º 27
0
    def _on_pay_with_tickets_press(self) -> None:
        from bastd.ui import getcurrency

        # If we're already entering, ignore.
        if self._entering:
            return

        if not self._have_valid_data:
            ba.screenmessage(ba.Lstr(resource='tournamentCheckingStateText'),
                             color=(1, 0, 0))
            ba.playsound(ba.getsound('error'))
            return

        # If we don't have a price.
        if self._purchase_price is None:
            ba.screenmessage(ba.Lstr(resource='tournamentCheckingStateText'),
                             color=(1, 0, 0))
            ba.playsound(ba.getsound('error'))
            return

        # Deny if it looks like the tourney has ended.
        if self._seconds_remaining == 0:
            ba.screenmessage(ba.Lstr(resource='tournamentEndedText'),
                             color=(1, 0, 0))
            ba.playsound(ba.getsound('error'))
            return

        # Deny if we don't have enough tickets.
        ticket_count: Optional[int]
        try:
            ticket_count = _ba.get_account_ticket_count()
        except Exception:
            # FIXME: should add a ba.NotSignedInError we can use here.
            ticket_count = None
        ticket_cost = self._purchase_price
        if ticket_count is not None and ticket_count < ticket_cost:
            getcurrency.show_get_tickets_prompt()
            ba.playsound(ba.getsound('error'))
            return

        cur_time = ba.time(ba.TimeType.REAL, ba.TimeFormat.MILLISECONDS)
        self._last_ticket_press_time = cur_time
        assert isinstance(ticket_cost, int)
        _ba.in_game_purchase(self._purchase_name, ticket_cost)

        self._entering = True
        _ba.add_transaction({
            'type': 'ENTER_TOURNAMENT',
            'fee': self._fee,
            'tournamentID': self._tournament_id
        })
        _ba.run_transactions()
        self._launch()
 def updatepartylist():
     self._internet_join_last_refresh_time = now
     self._first_public_party_list_rebuild_time = ba.time(
         ba.TimeType.REAL) + 1
     app = ba.app
     _ba.add_transaction(
         {
             'type': 'PUBLIC_PARTY_QUERY',
             'proto': app.protocol_version,
             'lang': app.language
         },
         callback=ba.WeakCall(self._on_public_party_query_result))
     _ba.run_transactions()
Ejemplo n.º 29
0
            def _update(self):
                if not self.target:
                    del self  # commit suicide because we have no goal in our existing :(
                    return
                d = ba.Vec3(self.target.position) - ba.Vec3(self.position)

                if d.length() < 0.1:
                    self._blast()
                    del self
                    return

                d = d.normalized() * 0.04

                from math import sin, cos

                self.position = (self.position[0] + d.x +
                                 sin(ba.time() * 2) * 0.03,
                                 self.position[1] + d.y, self.position[2] +
                                 d.z + cos(ba.time() * 2) * 0.03)
                self._sparkle()

                ba.timer(0.001, ba.WeakCall(self._update))
Ejemplo n.º 30
0
    def _process_pending_party_infos(self) -> None:
        starttime = time.time()

        # We want to do this in small enough pieces to not cause UI hitches.
        chunksize = 30
        parties_in = self._pending_party_infos[:chunksize]
        self._pending_party_infos = self._pending_party_infos[chunksize:]
        for party_in in parties_in:
            addr = party_in['a']
            assert isinstance(addr, str)
            port = party_in['p']
            assert isinstance(port, int)
            party_key = f'{addr}_{port}'
            party = self._parties.get(party_key)
            if party is None:
                # If this party is new to us, init it.
                party = PartyEntry(address=addr,
                                   next_ping_time=ba.time(ba.TimeType.REAL) +
                                   0.001 * party_in['pd'],
                                   index=self._next_entry_index)
                self._parties[party_key] = party
                self._parties_sorted.append((party_key, party))
                self._party_lists_dirty = True
                self._next_entry_index += 1
                assert isinstance(party.address, str)
                assert isinstance(party.next_ping_time, float)

            # Now, new or not, update its values.
            party.queue = party_in.get('q')
            assert isinstance(party.queue, (str, type(None)))
            party.port = port
            party.name = party_in['n']
            assert isinstance(party.name, str)
            party.size = party_in['s']
            assert isinstance(party.size, int)
            party.size_max = party_in['sm']
            assert isinstance(party.size_max, int)

            # Server provides this in milliseconds; we use seconds.
            party.ping_interval = 0.001 * party_in['pi']
            assert isinstance(party.ping_interval, float)
            party.stats_addr = party_in['sa']
            assert isinstance(party.stats_addr, (str, type(None)))

            # Make sure the party's UI gets updated.
            party.clean_display_index = None

        if DEBUG_PROCESSING and parties_in:
            print(f'Processed {len(parties_in)} raw party infos in'
                  f' {time.time()-starttime:.5f}s.')