def handlemessage(self, msg: Any) -> Any: if isinstance(msg, ba.PlayerScoredMessage): self._score += msg.score self._update_scores() elif isinstance(msg, ba.PlayerDiedMessage): # Augment standard behavior. super().handlemessage(msg) self._a_player_has_been_killed = True # Respawn them shortly. player = msg.getplayer(Player) assert self.initialplayerinfos is not None respawn_time = 2.0 + len(self.initialplayerinfos) * 1.0 player.respawn_timer = ba.Timer( respawn_time, ba.Call(self.spawn_player_if_exists, player)) player.respawn_icon = RespawnIcon(player, respawn_time) elif isinstance(msg, SpazBotDiedMessage): if msg.how is ba.DeathType.REACHED_GOAL: return None pts, importance = msg.spazbot.get_death_points(msg.how) if msg.killerplayer is not None: target: Optional[Sequence[float]] try: assert msg.spazbot is not None assert msg.spazbot.node target = msg.spazbot.node.position except Exception: ba.print_exception() target = None try: if msg.killerplayer: self.stats.player_scored(msg.killerplayer, pts, target=target, kill=True, screenmessage=False, importance=importance) ba.playsound(self._dingsound if importance == 1 else self._dingsoundhigh, volume=0.6) except Exception: ba.print_exception('Error on SpazBotDiedMessage') # Normally we pull scores from the score-set, but if there's no # player lets be explicit. else: self._score += pts self._update_scores() else: return super().handlemessage(msg) return None
def _refresh_account_name_text(self) -> None: if self._account_name_text is None: return try: name_str = _ba.get_account_display_string() except Exception: ba.print_exception() name_str = '??' ba.textwidget(edit=self._account_name_text, text=name_str)
def gamepad_configure_callback(event: Dict[str, Any]) -> None: """Respond to a gamepad button press during config selection.""" from ba.internal import get_remote_app_name from bastd.ui.settings import gamepad # Ignore all but button-presses. if event['type'] not in ['BUTTONDOWN', 'HATMOTION']: return _ba.release_gamepad_input() try: ba.containerwidget(edit=ba.app.main_menu_window, transition='out_left') except Exception: ba.print_exception('Error transitioning out main_menu_window.') ba.playsound(ba.getsound('activateBeep')) ba.playsound(ba.getsound('swish')) inputdevice = event['input_device'] assert isinstance(inputdevice, ba.InputDevice) if inputdevice.allows_configuring: ba.app.main_menu_window = ( gamepad.GamepadSettingsWindow(inputdevice).get_root_widget()) else: width = 700 height = 200 button_width = 100 uiscale = ba.app.uiscale ba.app.main_menu_window = dlg = (ba.containerwidget( scale=(1.7 if uiscale is ba.UIScale.SMALL else 1.4 if uiscale is ba.UIScale.MEDIUM else 1.0), size=(width, height), transition='in_right')) device_name = inputdevice.name if device_name == 'iDevice': msg = ba.Lstr(resource='bsRemoteConfigureInAppText', subs=[('${REMOTE_APP_NAME}', get_remote_app_name())]) else: msg = ba.Lstr(resource='cantConfigureDeviceText', subs=[('${DEVICE}', device_name)]) ba.textwidget(parent=dlg, position=(0, height - 80), size=(width, 25), text=msg, scale=0.8, h_align='center', v_align='top') def _ok() -> None: from bastd.ui.settings import controls ba.containerwidget(edit=dlg, transition='out_right') ba.app.main_menu_window = (controls.ControlsSettingsWindow( transition='in_left').get_root_widget()) ba.buttonwidget(parent=dlg, position=((width - button_width) / 2, 20), size=(button_width, 60), label=ba.Lstr(resource='okText'), on_activate_call=_ok)
def __del__(self) -> None: try: ba.app.ui.have_party_queue_window = False _ba.add_transaction({ 'type': 'PARTY_QUEUE_REMOVE', 'q': self._queue_id }) _ba.run_transactions() except Exception: ba.print_exception('Error removing self from party queue.')
def _run_selected_playlist(self) -> None: _ba.unlock_all_input() try: _ba.new_host_session(self._sessiontype) except Exception: from bastd import mainmenu ba.print_exception('exception running session', self._sessiontype) # Drop back into a main menu session. _ba.new_host_session(mainmenu.MainMenuSession)
def _update_bots(self) -> None: assert self._bot_update_interval is not None self._bot_update_interval = max(0.5, self._bot_update_interval * 0.98) self._bot_update_timer = ba.Timer(self._bot_update_interval, ba.WeakCall(self._update_bots)) botspawnpts: list[Sequence[float]] = [[-5.0, 5.5, -4.14], [0.0, 5.5, -4.14], [5.0, 5.5, -4.14]] dists = [0.0, 0.0, 0.0] playerpts: list[Sequence[float]] = [] for player in self.players: try: if player.is_alive(): assert isinstance(player.actor, PlayerSpaz) assert player.actor.node playerpts.append(player.actor.node.position) except Exception: ba.print_exception('Error updating bots.') for i in range(3): for playerpt in playerpts: dists[i] += abs(playerpt[0] - botspawnpts[i][0]) dists[i] += random.random() * 5.0 # Minor random variation. if dists[0] > dists[1] and dists[0] > dists[2]: spawnpt = botspawnpts[0] elif dists[1] > dists[2]: spawnpt = botspawnpts[1] else: spawnpt = botspawnpts[2] spawnpt = (spawnpt[0] + 3.0 * (random.random() - 0.5), spawnpt[1], 2.0 * (random.random() - 0.5) + spawnpt[2]) # Normalize our bot type total and find a random number within that. total = 0.0 for spawninfo in self._bot_spawn_types.values(): total += spawninfo.spawnrate randval = random.random() * total # Now go back through and see where this value falls. total = 0 bottype: Optional[type[SpazBot]] = None for spawntype, spawninfo in self._bot_spawn_types.items(): total += spawninfo.spawnrate if randval <= total: bottype = spawntype break spawn_time = 1.0 assert bottype is not None self._bots.spawn_bot(bottype, pos=spawnpt, spawn_time=spawn_time) # After every spawn we adjust our ratios slightly to get more # difficult. for spawninfo in self._bot_spawn_types.values(): spawninfo.spawnrate += spawninfo.increase spawninfo.increase += spawninfo.dincrease
def _run_selected_playlist(self) -> None: # pylint: disable=cyclic-import _ba.unlock_all_input() try: _ba.new_host_session(self._sessiontype) except Exception: from bastd import mainmenu ba.print_exception(f'Error running session {self._sessiontype}.') # Drop back into a main menu session. _ba.new_host_session(mainmenu.MainMenuSession)
def _refresh_tickets_text(self) -> None: if self._tickets_text is None: return try: tc_str = str(_ba.get_v1_account_ticket_count()) except Exception: ba.print_exception() tc_str = '-' ba.textwidget(edit=self._tickets_text, text=ba.Lstr(resource=self._r + '.ticketsText', subs=[('${COUNT}', tc_str)]))
def _save_state(self) -> None: try: sel = self._root_widget.get_selected_child() if sel == self._back_button: sel_name = 'Back' elif sel == self._scrollwidget: sel_name = 'Scroll' else: raise Exception('unrecognized selection') ba.app.window_states[self.__class__.__name__] = sel_name except Exception: ba.print_exception('exception saving state for', self.__class__)
def _restore_state(self) -> None: try: sel_name = ba.app.ui.window_states.get(type(self)) if sel_name == 'Back': sel = self._back_button elif sel_name == 'Scroll': sel = self._scrollwidget else: sel = self._back_button ba.containerwidget(edit=self._root_widget, selected_child=sel) except Exception: ba.print_exception(f'Error restoring state for {self}.')
def _save_state(self) -> None: try: sel = self._root_widget.get_selected_child() if sel == self._back_button: sel_name = 'Back' elif sel == self._scrollwidget: sel_name = 'Scroll' else: raise ValueError('unrecognized selection') ba.app.ui.window_states[type(self)] = sel_name except Exception: ba.print_exception(f'Error saving state for {self}.')
def do_it() -> None: try: # reset to normal speed _ba.set_replay_speed_exponent(0) _ba.fade_screen(True) assert self._my_replay_selected is not None _ba.new_replay_session(_ba.get_replays_dir() + '/' + self._my_replay_selected) except Exception: ba.print_exception('exception running replay session') # drop back into a fresh main menu session # in case we half-launched or something.. from bastd import mainmenu _ba.new_host_session(mainmenu.MainMenuSession)
def _restore_state(self) -> None: # pylint: disable=too-many-branches try: sel_name = ba.app.ui.window_states.get(type(self), {}).get('sel_name') if sel_name == 'Back': sel = self._back_button else: ba.containerwidget(edit=self._root_widget, selected_child=self._scrollwidget) if sel_name == 'VRTest': sel = self._vr_test_button elif sel_name == 'NetTest': sel = self._net_test_button elif sel_name == 'PromoCode': sel = self._promo_code_button elif sel_name == 'Benchmarks': sel = self._benchmarks_button elif sel_name == 'KickIdlePlayers': sel = self._kick_idle_players_check_box.widget elif sel_name == 'DisableCameraShake': sel = self._disable_camera_shake_check_box.widget elif (sel_name == 'AlwaysUseInternalKeyboard' and self._always_use_internal_keyboard_check_box is not None): sel = self._always_use_internal_keyboard_check_box.widget elif (sel_name == 'DisableGyro' and self._disable_gyro_check_box is not None): sel = self._disable_gyro_check_box.widget elif (sel_name == 'Languages' and self._language_popup is not None): sel = self._language_popup.get_button() elif sel_name == 'TranslationEditor': sel = self._translation_editor_button elif sel_name == 'ShowUserMods': sel = self._show_user_mods_button elif sel_name == 'Plugins': sel = self._plugins_button elif sel_name == 'ModdingGuide': sel = self._modding_guide_button elif sel_name == 'LangInform': sel = self._language_inform_checkbox else: sel = None if sel is not None: ba.containerwidget(edit=self._subcontainer, selected_child=sel, visible_child=sel) except Exception: ba.print_exception(f'Error restoring state for {self.__class__}')
def _delete_replay(self, replay: str) -> None: try: _ba.increment_analytics_count('Replay delete') os.remove((_ba.get_replays_dir() + '/' + replay).encode('utf-8')) self._refresh_my_replays() ba.playsound(ba.getsound('shieldDown')) if replay == self._my_replay_selected: self._my_replay_selected = None except Exception: ba.print_exception("exception deleting replay '" + replay + "'") ba.playsound(ba.getsound('error')) ba.screenmessage(ba.Lstr(resource=self._r + '.replayDeleteErrorText'), color=(1, 0, 0))
def _uninstall_target(): try: bap.uninstall(self.pkginfo.name) except Exception as e: ba.print_exception() ba.pushcall(ba.Call(ba.screenmessage, f'Error: {e}', color=(1, 0, 0)), from_other_thread=True) else: ba.pushcall(ba.Call(ba.screenmessage, 'Done', color=(0, 1, 0)), from_other_thread=True) if self._parent: self._parent._push_refresh()
def _launch(self) -> None: if self._launched: return self._launched = True launched = False # If they gave us an existing activity, just restart it. if self._tournament_activity is not None: try: ba.timer(0.1, lambda: ba.playsound(ba.getsound('cashRegister')), timetype=ba.TimeType.REAL) with ba.Context(self._tournament_activity): self._tournament_activity.end({'outcome': 'restart'}, force=True) ba.timer(0.3, self._transition_out, timetype=ba.TimeType.REAL) launched = True ba.screenmessage(ba.Lstr(translate=('serverResponses', 'Entering tournament...')), color=(0, 1, 0)) # We can hit exceptions here if _tournament_activity ends before # our restart attempt happens. # In this case we'll fall back to launching a new session. # This is not ideal since players will have to rejoin, etc., # but it works for now. except Exception: ba.print_exception('Error restarting tournament activity.') # If we had no existing activity (or were unable to restart it) # launch a new session. if not launched: ba.timer(0.1, lambda: ba.playsound(ba.getsound('cashRegister')), timetype=ba.TimeType.REAL) ba.timer( 1.0, lambda: ba.app.launch_coop_game( self._tournament_info['game'], args={ 'min_players': self._tournament_info['minPlayers'], 'max_players': self._tournament_info['maxPlayers'], 'tournament_id': self._tournament_id }), timetype=ba.TimeType.REAL) ba.timer(0.7, self._transition_out, timetype=ba.TimeType.REAL) ba.screenmessage(ba.Lstr(translate=('serverResponses', 'Entering tournament...')), color=(0, 1, 0))
def _save_state(self) -> None: # pylint: disable=too-many-branches try: sel = self._root_widget.get_selected_child() if sel == self._scrollwidget: sel = self._subcontainer.get_selected_child() if sel == self._vr_test_button: sel_name = 'VRTest' elif sel == self._net_test_button: sel_name = 'NetTest' elif sel == self._promo_code_button: sel_name = 'PromoCode' elif sel == self._benchmarks_button: sel_name = 'Benchmarks' elif sel == self._kick_idle_players_check_box.widget: sel_name = 'KickIdlePlayers' elif sel == self._disable_camera_shake_check_box.widget: sel_name = 'DisableCameraShake' elif (self._always_use_internal_keyboard_check_box is not None and sel == self._always_use_internal_keyboard_check_box.widget): sel_name = 'AlwaysUseInternalKeyboard' elif (self._disable_gyro_check_box is not None and sel == self._disable_gyro_check_box.widget): sel_name = 'DisableGyro' elif (self._language_popup is not None and sel == self._language_popup.get_button()): sel_name = 'Languages' elif sel == self._translation_editor_button: sel_name = 'TranslationEditor' elif sel == self._show_user_mods_button: sel_name = 'ShowUserMods' elif sel == self._plugins_button: sel_name = 'Plugins' elif sel == self._modding_guide_button: sel_name = 'ModdingGuide' elif sel == self._language_inform_checkbox: sel_name = 'LangInform' else: raise ValueError(f'unrecognized selection \'{sel}\'') elif sel == self._back_button: sel_name = 'Back' else: raise ValueError(f'unrecognized selection \'{sel}\'') ba.app.ui.window_states[self.__class__.__name__] = { 'sel_name': sel_name } except Exception: ba.print_exception(f'Error saving state for {self.__class__}')
def _restore_state(self) -> None: try: try: sel_name = ba.app.window_states[self.__class__.__name__] except Exception: sel_name = None if sel_name == 'Back': sel = self._back_button elif sel_name == 'Scroll': sel = self._scrollwidget else: sel = self._back_button ba.containerwidget(edit=self._root_widget, selected_child=sel) except Exception: ba.print_exception('error restoring state for', self.__class__)
def _save_state(self) -> None: try: sel = self._root_widget.get_selected_child() if sel == self._new_button: sel_name = 'New' elif sel == self._edit_button: sel_name = 'Edit' elif sel == self._delete_button: sel_name = 'Delete' elif sel == self._scrollwidget: sel_name = 'Scroll' else: sel_name = 'Back' ba.app.window_states[self.__class__.__name__] = sel_name except Exception: ba.print_exception('error saving state for', self.__class__)
def _save_state(self) -> None: try: sel = self._root_widget.get_selected_child() if sel == self._teams_button: sel_name = 'Team Games' elif self._coop_button is not None and sel == self._coop_button: sel_name = 'Co-op Games' elif sel == self._free_for_all_button: sel_name = 'Free-for-All Games' elif sel == self._back_button: sel_name = 'Back' else: raise ValueError(f'unrecognized selection {sel}') ba.app.ui.window_states[type(self)] = sel_name except Exception: ba.print_exception(f'Error saving state for {self}.')
def run(self) -> None: try: # FIXME: Update this to work with IPv6 at some point. import socket sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.connect(('8.8.8.8', 80)) val = sock.getsockname()[0] sock.close() ba.pushcall(ba.Call(self._call, val), from_other_thread=True) except Exception as exc: # Ignore expected network errors; log others. import errno if isinstance(exc, OSError) and exc.errno == errno.ENETUNREACH: pass else: ba.print_exception()
def _reset_progress(self) -> None: try: from ba.internal import getcampaign # FIXME: This would need to happen server-side these days. if self._can_reset_achievements: ba.app.config['Achievements'] = {} _ba.reset_achievements() campaign = getcampaign('Default') campaign.reset() # also writes the config.. campaign = getcampaign('Challenges') campaign.reset() # also writes the config.. except Exception: ba.print_exception('Error resetting co-op campaign progress.') ba.playsound(ba.getsound('shieldDown')) self._refresh()
def _restore_state(self) -> None: try: sel_name = ba.app.window_states.get(self.__class__.__name__) if sel_name == 'Team Games': sel = self._teams_button elif sel_name == 'Co-op Games': sel = self._coop_button elif sel_name == 'Free-for-All Games': sel = self._free_for_all_button elif sel_name == 'Back': sel = self._back_button else: sel = self._coop_button ba.containerwidget(edit=self._root_widget, selected_child=sel) except Exception: ba.print_exception(f'Error restoring state for {self}.')
def _save_state(self) -> None: try: sel = self._root_widget.get_selected_child() if sel == self._teams_button: sel_name = 'Team Games' elif sel == self._coop_button: sel_name = 'Co-op Games' elif sel == self._free_for_all_button: sel_name = 'Free-for-All Games' elif sel == self._back_button: sel_name = 'Back' else: raise Exception("unrecognized selected widget") ba.app.window_states[self.__class__.__name__] = sel_name except Exception: ba.print_exception('error saving state for', self.__class__)
def _save_state(self) -> None: try: sel = self._root_widget.get_selected_child() if sel == self._new_button: sel_name = 'New' elif sel == self._edit_button: sel_name = 'Edit' elif sel == self._delete_button: sel_name = 'Delete' elif sel == self._scrollwidget: sel_name = 'Scroll' else: sel_name = 'Back' ba.app.ui.window_states[type(self)] = sel_name except Exception: ba.print_exception(f'Error saving state for {self}.')
def run(self) -> None: try: # FIXME: Update this to work with IPv6 at some point. import socket sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.connect(('8.8.8.8', 80)) val = sock.getsockname()[0] sock.close() ba.pushcall(ba.Call(self._call, val), from_other_thread=True) except Exception as exc: from efro.error import is_udp_network_error # Ignore expected network errors; log others. if is_udp_network_error(exc): pass else: ba.print_exception()
def handlemessage(self, msg: Any) -> Any: if isinstance(msg, stdflag.FlagPickedUpMessage): assert isinstance(msg.flag, FootballFlag) try: player = msg.node.getdelegate().getplayer() if player: msg.flag.last_holding_player = player msg.flag.held_count += 1 except Exception: ba.print_exception('exception in Football FlagPickedUpMessage;' " this shouldn't happen") elif isinstance(msg, stdflag.FlagDroppedMessage): assert isinstance(msg.flag, FootballFlag) msg.flag.held_count -= 1 # Respawn dead players if they're still in the game. elif isinstance(msg, playerspaz.PlayerSpazDeathMessage): # Augment standard behavior. super().handlemessage(msg) self.respawn_player(msg.spaz.player) # Respawn dead flags. elif isinstance(msg, stdflag.FlagDeathMessage): if not self.has_ended(): self._flag_respawn_timer = ba.Timer(3.0, self._spawn_flag) self._flag_respawn_light = ba.NodeActor( ba.newnode('light', attrs={ 'position': self._flag_spawn_pos, 'height_attenuated': False, 'radius': 0.15, 'color': (1.0, 1.0, 0.3) })) assert self._flag_respawn_light.node ba.animate(self._flag_respawn_light.node, 'intensity', { 0.0: 0, 0.25: 0.15, 0.5: 0 }, loop=True) ba.timer(3.0, self._flag_respawn_light.node.delete) else: # Augment standard behavior. super().handlemessage(msg)
def run(self) -> None: ba.app.ping_thread_count += 1 sock: Optional[socket.socket] = None try: import socket from ba.internal import get_ip_address_type socket_type = get_ip_address_type(self._address) sock = socket.socket(socket_type, socket.SOCK_DGRAM) sock.connect((self._address, self._port)) accessible = False starttime = time.time() # Send a few pings and wait a second for # a response. sock.settimeout(1) for _i in range(3): sock.send(b'\x0b') result: Optional[bytes] try: # 11: BA_PACKET_SIMPLE_PING result = sock.recv(10) except Exception: result = None if result == b'\x0c': # 12: BA_PACKET_SIMPLE_PONG accessible = True break time.sleep(1) ping = (time.time() - starttime) * 1000.0 ba.pushcall(ba.Call(self._call, self._address, self._port, ping if accessible else None), from_other_thread=True) except Exception as exc: from efro.error import is_udp_network_error if is_udp_network_error(exc): pass else: ba.print_exception('Error on gather ping', once=True) finally: try: if sock is not None: sock.close() except Exception: ba.print_exception('Error on gather ping cleanup', once=True) ba.app.ping_thread_count -= 1
def _restore_state(self) -> None: try: sel_name = ba.app.window_states.get(self.__class__.__name__) if sel_name == 'Back': sel = self._back_button elif sel_name == 'Scroll': sel = self._scrollwidget elif sel_name == 'Customize': sel = self._scrollwidget ba.containerwidget(edit=self._subcontainer, selected_child=self._customize_button, visible_child=self._customize_button) else: sel = self._scrollwidget ba.containerwidget(edit=self._root_widget, selected_child=sel) except Exception: ba.print_exception(f'Error restoring state for {self}.')
def _rename_my_replay(self, replay: str) -> None: new_name = None try: if not self._my_replay_rename_text: return new_name_raw = cast( str, ba.textwidget(query=self._my_replay_rename_text)) new_name = new_name_raw + '.brp' # Ignore attempts to change it to what it already is # (or what it looks like to the user). if (replay != new_name and self._get_replay_display_name(replay) != new_name_raw): old_name_full = (_ba.get_replays_dir() + '/' + replay).encode('utf-8') new_name_full = (_ba.get_replays_dir() + '/' + new_name).encode('utf-8') # False alarm; ba.textwidget can return non-None val. # pylint: disable=unsupported-membership-test if os.path.exists(new_name_full): ba.playsound(ba.getsound('error')) ba.screenmessage( ba.Lstr(resource=self._r + '.replayRenameErrorAlreadyExistsText'), color=(1, 0, 0)) elif any(char in new_name_raw for char in ['/', '\\', ':']): ba.playsound(ba.getsound('error')) ba.screenmessage(ba.Lstr(resource=self._r + '.replayRenameErrorInvalidName'), color=(1, 0, 0)) else: _ba.increment_analytics_count('Replay rename') os.rename(old_name_full, new_name_full) self._refresh_my_replays() ba.playsound(ba.getsound('gunCocking')) except Exception: ba.print_exception( f"Error renaming replay '{replay}' to '{new_name}'.") ba.playsound(ba.getsound('error')) ba.screenmessage( ba.Lstr(resource=self._r + '.replayRenameErrorText'), color=(1, 0, 0), ) ba.containerwidget(edit=self._my_replays_rename_window, transition='out_scale')