def _sync(self): ba.screenmessage('Syncing...') threading.Thread(target=self._sync_target).start()
def _no_replay_selected_error(self) -> None: ba.screenmessage(ba.Lstr(resource=self._r + '.noReplaySelectedErrorText'), color=(1, 0, 0)) ba.playsound(ba.getsound('error'))
def do_toggle(value: bool) -> None: ba.screenmessage( ba.Lstr(resource='settingsWindowAdvanced.mustRestartText'), color=(1, 1, 0)) ba.playsound(ba.getsound('gunCocking')) _ba.set_low_level_config_value('enablexinput', not value)
def _show_restart_needed(self, value: Any) -> None: del value # Unused. ba.screenmessage(ba.Lstr(resource=self._r + '.mustRestartText'), color=(1, 1, 0))
"""Define a simple example plugin.""" # ba_meta require api 6 from __future__ import annotations from typing import TYPE_CHECKING import random from don import tintColor import don import ba ba.screenmessage('Spaz Mods is created by PCModder for FRIENDS to use', color=(3, -4, 8)) #created by PCModder full rights to me. :) ba.screenmessage('Use don.py to use specific settings', color=(3, -4, 8)) #I believe now i am gone with the wind from _ba import chatmessage as cmsg, get_foreground_host_activity import _ba from bastd.actor.spaz import Spaz, SpazFactory, BombDiedMessage, CurseExplodeMessage, PunchHitMessage, PickupMessage import bastd.actor.spaz from bastd.actor.popuptext import PopupText from bastd.gameutils import SharedObjects from bastd.actor import bomb as stdbomb if TYPE_CHECKING: from typing import Any, Type, Optional, Tuple, List, Dict # ba_meta export plugin class spazMOD(ba.Plugin):
def __init__(self, gametype: type[ba.GameActivity], sessiontype: type[ba.Session], config: Optional[dict[str, Any]], completion_call: Callable[[Optional[dict[str, Any]]], Any], default_selection: str = None, transition: str = 'in_right', edit_info: dict[str, Any] = None): # pylint: disable=too-many-branches # pylint: disable=too-many-statements # pylint: disable=too-many-locals from ba.internal import (get_unowned_maps, get_filtered_map_name, get_map_class, get_map_display_string) self._gametype = gametype self._sessiontype = sessiontype # If we're within an editing session we get passed edit_info # (returning from map selection window, etc). if edit_info is not None: self._edit_info = edit_info # ..otherwise determine whether we're adding or editing a game based # on whether an existing config was passed to us. else: if config is None: self._edit_info = {'editType': 'add'} else: self._edit_info = {'editType': 'edit'} self._r = 'gameSettingsWindow' valid_maps = gametype.get_supported_maps(sessiontype) if not valid_maps: ba.screenmessage(ba.Lstr(resource='noValidMapsErrorText')) raise Exception('No valid maps') self._settings_defs = gametype.get_available_settings(sessiontype) self._completion_call = completion_call # To start with, pick a random map out of the ones we own. unowned_maps = get_unowned_maps() valid_maps_owned = [m for m in valid_maps if m not in unowned_maps] if valid_maps_owned: self._map = valid_maps[random.randrange(len(valid_maps_owned))] # Hmmm.. we own none of these maps.. just pick a random un-owned one # I guess.. should this ever happen? else: self._map = valid_maps[random.randrange(len(valid_maps))] is_add = (self._edit_info['editType'] == 'add') # If there's a valid map name in the existing config, use that. try: if (config is not None and 'settings' in config and 'map' in config['settings']): filtered_map_name = get_filtered_map_name( config['settings']['map']) if filtered_map_name in valid_maps: self._map = filtered_map_name except Exception: ba.print_exception('Error getting map for editor.') if config is not None and 'settings' in config: self._settings = config['settings'] else: self._settings = {} self._choice_selections: dict[str, int] = {} uiscale = ba.app.ui.uiscale width = 720 if uiscale is ba.UIScale.SMALL else 620 x_inset = 50 if uiscale is ba.UIScale.SMALL else 0 height = (365 if uiscale is ba.UIScale.SMALL else 460 if uiscale is ba.UIScale.MEDIUM else 550) spacing = 52 y_extra = 15 y_extra2 = 21 map_tex_name = (get_map_class(self._map).get_preview_texture_name()) if map_tex_name is None: raise Exception('no map preview tex found for' + self._map) map_tex = ba.gettexture(map_tex_name) top_extra = 20 if uiscale is ba.UIScale.SMALL else 0 super().__init__(root_widget=ba.containerwidget( size=(width, height + top_extra), transition=transition, scale=(2.19 if uiscale is ba.UIScale.SMALL else 1.35 if uiscale is ba.UIScale.MEDIUM else 1.0), stack_offset=(0, -17) if uiscale is ba.UIScale.SMALL else (0, 0))) btn = ba.buttonwidget( parent=self._root_widget, position=(45 + x_inset, height - 82 + y_extra2), size=(180, 70) if is_add else (180, 65), label=ba.Lstr(resource='backText') if is_add else ba.Lstr( resource='cancelText'), button_type='back' if is_add else None, autoselect=True, scale=0.75, text_scale=1.3, on_activate_call=ba.Call(self._cancel)) ba.containerwidget(edit=self._root_widget, cancel_button=btn) add_button = ba.buttonwidget( parent=self._root_widget, position=(width - (193 + x_inset), height - 82 + y_extra2), size=(200, 65), scale=0.75, text_scale=1.3, label=ba.Lstr(resource=self._r + '.addGameText') if is_add else ba.Lstr( resource='doneText')) if ba.app.ui.use_toolbars: pbtn = _ba.get_special_widget('party_button') ba.widget(edit=add_button, right_widget=pbtn, up_widget=pbtn) ba.textwidget(parent=self._root_widget, position=(-8, height - 70 + y_extra2), size=(width, 25), text=gametype.get_display_string(), color=ba.app.ui.title_color, maxwidth=235, scale=1.1, h_align='center', v_align='center') map_height = 100 scroll_height = map_height + 10 # map select and margin # Calc our total height we'll need scroll_height += spacing * len(self._settings_defs) scroll_width = width - (86 + 2 * x_inset) self._scrollwidget = ba.scrollwidget(parent=self._root_widget, position=(44 + x_inset, 35 + y_extra), size=(scroll_width, height - 116), highlight=False, claims_left_right=True, claims_tab=True, selection_loops_to_parent=True) self._subcontainer = ba.containerwidget(parent=self._scrollwidget, size=(scroll_width, scroll_height), background=False, claims_left_right=True, claims_tab=True, selection_loops_to_parent=True) v = scroll_height - 5 h = -40 # Keep track of all the selectable widgets we make so we can wire # them up conveniently. widget_column: list[list[ba.Widget]] = [] # Map select button. ba.textwidget(parent=self._subcontainer, position=(h + 49, v - 63), size=(100, 30), maxwidth=110, text=ba.Lstr(resource='mapText'), h_align='left', color=(0.8, 0.8, 0.8, 1.0), v_align='center') ba.imagewidget( parent=self._subcontainer, size=(256 * 0.7, 125 * 0.7), position=(h + 261 - 128 + 128.0 * 0.56, v - 90), texture=map_tex, model_opaque=ba.getmodel('level_select_button_opaque'), model_transparent=ba.getmodel('level_select_button_transparent'), mask_texture=ba.gettexture('mapPreviewMask')) map_button = btn = ba.buttonwidget( parent=self._subcontainer, size=(140, 60), position=(h + 448, v - 72), on_activate_call=ba.Call(self._select_map), scale=0.7, label=ba.Lstr(resource='mapSelectText')) widget_column.append([btn]) ba.textwidget(parent=self._subcontainer, position=(h + 363 - 123, v - 114), size=(100, 30), flatness=1.0, shadow=1.0, scale=0.55, maxwidth=256 * 0.7 * 0.8, text=get_map_display_string(self._map), h_align='center', color=(0.6, 1.0, 0.6, 1.0), v_align='center') v -= map_height for setting in self._settings_defs: value = setting.default value_type = type(value) # Now, if there's an existing value for it in the config, # override with that. try: if (config is not None and 'settings' in config and setting.name in config['settings']): value = value_type(config['settings'][setting.name]) except Exception: ba.print_exception() # Shove the starting value in there to start. self._settings[setting.name] = value name_translated = self._get_localized_setting_name(setting.name) mw1 = 280 mw2 = 70 # Handle types with choices specially: if isinstance(setting, ba.ChoiceSetting): for choice in setting.choices: if len(choice) != 2: raise ValueError( "Expected 2-member tuples for 'choices'; got: " + repr(choice)) if not isinstance(choice[0], str): raise TypeError( 'First value for choice tuple must be a str; got: ' + repr(choice)) if not isinstance(choice[1], value_type): raise TypeError( 'Choice type does not match default value; choice:' + repr(choice) + '; setting:' + repr(setting)) if value_type not in (int, float): raise TypeError( 'Choice type setting must have int or float default; ' 'got: ' + repr(setting)) # Start at the choice corresponding to the default if possible. self._choice_selections[setting.name] = 0 for index, choice in enumerate(setting.choices): if choice[1] == value: self._choice_selections[setting.name] = index break v -= spacing ba.textwidget(parent=self._subcontainer, position=(h + 50, v), size=(100, 30), maxwidth=mw1, text=name_translated, h_align='left', color=(0.8, 0.8, 0.8, 1.0), v_align='center') txt = ba.textwidget( parent=self._subcontainer, position=(h + 509 - 95, v), size=(0, 28), text=self._get_localized_setting_name(setting.choices[ self._choice_selections[setting.name]][0]), editable=False, color=(0.6, 1.0, 0.6, 1.0), maxwidth=mw2, h_align='right', v_align='center', padding=2) btn1 = ba.buttonwidget(parent=self._subcontainer, position=(h + 509 - 50 - 1, v), size=(28, 28), label='<', autoselect=True, on_activate_call=ba.Call( self._choice_inc, setting.name, txt, setting, -1), repeat=True) btn2 = ba.buttonwidget(parent=self._subcontainer, position=(h + 509 + 5, v), size=(28, 28), label='>', autoselect=True, on_activate_call=ba.Call( self._choice_inc, setting.name, txt, setting, 1), repeat=True) widget_column.append([btn1, btn2]) elif isinstance(setting, (ba.IntSetting, ba.FloatSetting)): v -= spacing min_value = setting.min_value max_value = setting.max_value increment = setting.increment ba.textwidget(parent=self._subcontainer, position=(h + 50, v), size=(100, 30), text=name_translated, h_align='left', color=(0.8, 0.8, 0.8, 1.0), v_align='center', maxwidth=mw1) txt = ba.textwidget(parent=self._subcontainer, position=(h + 509 - 95, v), size=(0, 28), text=str(value), editable=False, color=(0.6, 1.0, 0.6, 1.0), maxwidth=mw2, h_align='right', v_align='center', padding=2) btn1 = ba.buttonwidget(parent=self._subcontainer, position=(h + 509 - 50 - 1, v), size=(28, 28), label='-', autoselect=True, on_activate_call=ba.Call( self._inc, txt, min_value, max_value, -increment, value_type, setting.name), repeat=True) btn2 = ba.buttonwidget(parent=self._subcontainer, position=(h + 509 + 5, v), size=(28, 28), label='+', autoselect=True, on_activate_call=ba.Call( self._inc, txt, min_value, max_value, increment, value_type, setting.name), repeat=True) widget_column.append([btn1, btn2]) elif value_type == bool: v -= spacing ba.textwidget(parent=self._subcontainer, position=(h + 50, v), size=(100, 30), text=name_translated, h_align='left', color=(0.8, 0.8, 0.8, 1.0), v_align='center', maxwidth=mw1) txt = ba.textwidget( parent=self._subcontainer, position=(h + 509 - 95, v), size=(0, 28), text=ba.Lstr(resource='onText') if value else ba.Lstr( resource='offText'), editable=False, color=(0.6, 1.0, 0.6, 1.0), maxwidth=mw2, h_align='right', v_align='center', padding=2) cbw = ba.checkboxwidget(parent=self._subcontainer, text='', position=(h + 505 - 50 - 5, v - 2), size=(200, 30), autoselect=True, textcolor=(0.8, 0.8, 0.8), value=value, on_value_change_call=ba.Call( self._check_value_change, setting.name, txt)) widget_column.append([cbw]) else: raise Exception() # Ok now wire up the column. try: # pylint: disable=unsubscriptable-object prev_widgets: Optional[list[ba.Widget]] = None for cwdg in widget_column: if prev_widgets is not None: # Wire our rightmost to their rightmost. ba.widget(edit=prev_widgets[-1], down_widget=cwdg[-1]) ba.widget(cwdg[-1], up_widget=prev_widgets[-1]) # Wire our leftmost to their leftmost. ba.widget(edit=prev_widgets[0], down_widget=cwdg[0]) ba.widget(cwdg[0], up_widget=prev_widgets[0]) prev_widgets = cwdg except Exception: ba.print_exception( 'Error wiring up game-settings-select widget column.') ba.buttonwidget(edit=add_button, on_activate_call=ba.Call(self._add)) ba.containerwidget(edit=self._root_widget, selected_child=add_button, start_button=add_button) if default_selection == 'map': ba.containerwidget(edit=self._root_widget, selected_child=self._scrollwidget) ba.containerwidget(edit=self._subcontainer, selected_child=map_button)
def _duplicate_playlist(self) -> None: # pylint: disable=too-many-branches # pylint: disable=cyclic-import from ba.internal import have_pro_options from bastd.ui import purchase if not have_pro_options(): purchase.PurchaseWindow(items=['pro']) return if self._selected_playlist_name is None: return plst: Optional[List[Dict[str, Any]]] if self._selected_playlist_name == '__default__': plst = self._pvars.get_default_list_call() else: plst = ba.app.config[self._config_name_full].get( self._selected_playlist_name) if plst is None: ba.playsound(ba.getsound('error')) return # clamp at our max playlist number if len(ba.app.config[self._config_name_full]) > self._max_playlists: ba.screenmessage( ba.Lstr(translate=('serverResponses', 'Max number of playlists reached.')), color=(1, 0, 0)) ba.playsound(ba.getsound('error')) return copy_text = ba.Lstr(resource='copyOfText').evaluate() # get just 'Copy' or whatnot copy_word = copy_text.replace('${NAME}', '').strip() # find a valid dup name that doesn't exist test_index = 1 base_name = self._get_playlist_display_name( self._selected_playlist_name).evaluate() # If it looks like a copy, strip digits and spaces off the end. if copy_word in base_name: while base_name[-1].isdigit() or base_name[-1] == ' ': base_name = base_name[:-1] while True: if copy_word in base_name: test_name = base_name else: test_name = copy_text.replace('${NAME}', base_name) if test_index > 1: test_name += ' ' + str(test_index) if test_name not in ba.app.config[self._config_name_full]: break test_index += 1 _ba.add_transaction({ 'type': 'ADD_PLAYLIST', 'playlistType': self._pvars.config_name, 'playlistName': test_name, 'playlist': copy.deepcopy(plst) }) _ba.run_transactions() ba.playsound(ba.getsound('gunCocking')) self._refresh(select_playlist=test_name)
def _host_copy_press(self) -> None: assert self._hostingstate.party_code is not None ba.clipboard_set_text(self._hostingstate.party_code) ba.screenmessage(ba.Lstr(resource='gatherWindow.copyCodeConfirmText'))
def _ok(self) -> None: ba.containerwidget(edit=self._root_widget, transition='out_left') _ba.set_telnet_access_enabled(True) ba.screenmessage(ba.Lstr(resource='telnetAccessGrantedText'))
def __init__(self, transition: str = 'in_right', origin_widget: ba.Widget = None): # pylint: disable=too-many-locals app = ba.app # If they provided an origin-widget, scale up from that. scale_origin: Optional[tuple[float, float]] if origin_widget is not None: self._transition_out = 'out_scale' scale_origin = origin_widget.get_screen_space_center() transition = 'in_scale' else: self._transition_out = 'out_right' scale_origin = None uiscale = ba.app.ui.uiscale self._width = 870.0 if uiscale is ba.UIScale.SMALL else 670.0 x_inset = 100 if uiscale is ba.UIScale.SMALL else 0 self._height = (390.0 if uiscale is ba.UIScale.SMALL else 450.0 if uiscale is ba.UIScale.MEDIUM else 520.0) top_extra = 10 if uiscale is ba.UIScale.SMALL else 0 super().__init__(root_widget=ba.containerwidget( size=(self._width, self._height + top_extra), transition=transition, toolbar_visibility='menu_minimal', scale_origin_stack_offset=scale_origin, scale=(2.06 if uiscale is ba.UIScale.SMALL else 1.4 if uiscale is ba.UIScale.MEDIUM else 1.0), stack_offset=(0, -25) if uiscale is ba.UIScale.SMALL else (0, 0))) self._scroll_width = self._width - (100 + 2 * x_inset) self._scroll_height = self._height - 115.0 self._sub_width = self._scroll_width * 0.95 self._sub_height = 724.0 if app.ui.use_toolbars and uiscale is ba.UIScale.SMALL: ba.containerwidget(edit=self._root_widget, on_cancel_call=self._do_back) self._back_button = None else: self._back_button = ba.buttonwidget( parent=self._root_widget, position=(53 + x_inset, self._height - 60), size=(140, 60), scale=0.8, autoselect=True, label=ba.Lstr(resource='backText'), button_type='back', on_activate_call=self._do_back) ba.containerwidget(edit=self._root_widget, cancel_button=self._back_button) self._title_text = ba.textwidget(parent=self._root_widget, position=(0, self._height - 52), size=(self._width, 25), text=ba.Lstr(resource='pluginsText'), color=app.ui.title_color, h_align='center', v_align='top') if self._back_button is not None: ba.buttonwidget(edit=self._back_button, button_type='backSmall', size=(60, 60), label=ba.charstr(ba.SpecialChar.BACK)) self._scrollwidget = ba.scrollwidget(parent=self._root_widget, position=(50 + x_inset, 50), simple_culling_v=20.0, highlight=False, size=(self._scroll_width, self._scroll_height), selection_loops_to_parent=True) ba.widget(edit=self._scrollwidget, right_widget=self._scrollwidget) self._subcontainer = ba.columnwidget(parent=self._scrollwidget, selection_loops_to_parent=True) if ba.app.meta.metascan is None: ba.screenmessage('Still scanning plugins; please try again.', color=(1, 0, 0)) ba.playsound(ba.getsound('error')) pluglist = ba.app.plugins.potential_plugins plugstates: dict[str, dict] = ba.app.config.setdefault('Plugins', {}) assert isinstance(plugstates, dict) for i, availplug in enumerate(pluglist): active = availplug.class_path in ba.app.plugins.active_plugins plugstate = plugstates.setdefault(availplug.class_path, {}) checked = plugstate.get('enabled', False) assert isinstance(checked, bool) check = ba.checkboxwidget( parent=self._subcontainer, text=availplug.display_name, value=checked, maxwidth=self._scroll_width - 100, size=(self._scroll_width - 40, 50), on_value_change_call=ba.Call(self._check_value_changed, availplug), textcolor=((0.8, 0.3, 0.3) if not availplug.available else (0, 1, 0) if active else (0.6, 0.6, 0.6))) # Make sure we scroll all the way to the end when using # keyboard/button nav. ba.widget(edit=check, show_buffer_top=40, show_buffer_bottom=40) # Keep last from looping to back button when down is pressed. if i == len(pluglist) - 1: ba.widget(edit=check, down_widget=check) ba.containerwidget(edit=self._root_widget, selected_child=self._scrollwidget) self._restore_state()
def _handle_race_point_collide(self) -> None: # FIXME: Tidy this up. # pylint: disable=too-many-statements # pylint: disable=too-many-branches # pylint: disable=too-many-nested-blocks collision = ba.getcollision() try: region = collision.sourcenode.getdelegate(RaceRegion, True) player = collision.opposingnode.getdelegate(PlayerSpaz, True).getplayer( Player, True) except ba.NotFoundError: return last_region = player.last_region this_region = region.index if last_region != this_region: # If a player tries to skip regions, smite them. # Allow a one region leeway though (its plausible players can get # blown over a region, etc). if this_region > last_region + 2: if player.is_alive(): assert player.actor player.actor.handlemessage(ba.DieMessage()) ba.screenmessage(ba.Lstr( translate=('statements', 'Killing ${NAME} for' ' skipping part of the track!'), subs=[('${NAME}', player.getname(full=True))]), color=(1, 0, 0)) else: # If this player is in first, note that this is the # front-most race-point. if player.rank == 0: self._front_race_region = this_region player.last_region = this_region if last_region >= len(self._regions) - 2 and this_region == 0: team = player.team player.lap = min(self._laps, player.lap + 1) # In teams mode with all-must-finish on, the team lap # value is the min of all team players. # Otherwise its the max. if isinstance(self.session, ba.DualTeamSession ) and self._entire_team_must_finish: team.lap = min(p.lap for p in team.players) else: team.lap = max(p.lap for p in team.players) # A player is finishing. if player.lap == self._laps: # In teams mode, hand out points based on the order # players come in. if isinstance(self.session, ba.DualTeamSession): assert self._team_finish_pts is not None if self._team_finish_pts > 0: self.stats.player_scored(player, self._team_finish_pts, screenmessage=False) self._team_finish_pts -= 25 # Flash where the player is. self._flash_player(player, 1.0) player.finished = True assert player.actor player.actor.handlemessage( ba.DieMessage(immediate=True)) # Makes sure noone behind them passes them in rank # while finishing. player.distance = 9999.0 # If the whole team has finished the race. if team.lap == self._laps: ba.playsound(self._score_sound) player.team.finished = True assert self._timer is not None elapsed = ba.time() - self._timer.getstarttime() self._last_team_time = player.team.time = elapsed self._check_end_game() # Team has yet to finish. else: ba.playsound(self._swipsound) # They've just finished a lap but not the race. else: ba.playsound(self._swipsound) self._flash_player(player, 0.3) # Print their lap number over their head. try: assert isinstance(player.actor, PlayerSpaz) mathnode = ba.newnode('math', owner=player.actor.node, attrs={ 'input1': (0, 1.9, 0), 'operation': 'add' }) player.actor.node.connectattr( 'torso_position', mathnode, 'input2') tstr = ba.Lstr(resource='lapNumberText', subs=[('${CURRENT}', str(player.lap + 1)), ('${TOTAL}', str(self._laps)) ]) txtnode = ba.newnode('text', owner=mathnode, attrs={ 'text': tstr, 'in_world': True, 'color': (1, 1, 0, 1), 'scale': 0.015, 'h_align': 'center' }) mathnode.connectattr('output', txtnode, 'position') ba.animate(txtnode, 'scale', { 0.0: 0, 0.2: 0.019, 2.0: 0.019, 2.2: 0 }) ba.timer(2.3, mathnode.delete) except Exception: ba.print_exception('Error printing lap.')
def _print_already_own(self, charname: str) -> None: ba.screenmessage(ba.Lstr(resource=self._r + '.alreadyOwnText', subs=[('${NAME}', charname)]), color=(1, 0, 0)) ba.playsound(ba.getsound('error'))
def _no_favorite_selected_error(self) -> None: ba.screenmessage(ba.Lstr(resource='nothingIsSelectedErrorText'), color=(1, 0, 0)) ba.playsound(ba.getsound('error'))
def _handle_race_point_collide(self) -> None: # FIXME: Tidy this up. # pylint: disable=too-many-statements # pylint: disable=too-many-branches # pylint: disable=too-many-nested-blocks region_node, playernode = ba.get_collision_info( 'source_node', 'opposing_node') try: player = playernode.getdelegate().getplayer() except Exception: player = None region = region_node.getdelegate() if not player or not region: return assert isinstance(player, ba.Player) assert isinstance(region, RaceRegion) last_region = player.gamedata['last_region'] this_region = region.index if last_region != this_region: # If a player tries to skip regions, smite them. # Allow a one region leeway though (its plausible players can get # blown over a region, etc). if this_region > last_region + 2: if player.is_alive(): assert player.actor player.actor.handlemessage(ba.DieMessage()) ba.screenmessage(ba.Lstr( translate=('statements', 'Killing ${NAME} for' ' skipping part of the track!'), subs=[('${NAME}', player.get_name(full=True))]), color=(1, 0, 0)) else: # If this player is in first, note that this is the # front-most race-point. if player.gamedata['rank'] == 0: self._front_race_region = this_region player.gamedata['last_region'] = this_region if last_region >= len(self._regions) - 2 and this_region == 0: team = player.team player.gamedata['lap'] = min(self.settings_raw['Laps'], player.gamedata['lap'] + 1) # In teams mode with all-must-finish on, the team lap # value is the min of all team players. # Otherwise its the max. if isinstance( self.session, ba.DualTeamSession ) and self.settings_raw.get('Entire Team Must Finish'): team.gamedata['lap'] = min( [p.gamedata['lap'] for p in team.players]) else: team.gamedata['lap'] = max( [p.gamedata['lap'] for p in team.players]) # A player is finishing. if player.gamedata['lap'] == self.settings_raw['Laps']: # In teams mode, hand out points based on the order # players come in. if isinstance(self.session, ba.DualTeamSession): assert self._team_finish_pts is not None if self._team_finish_pts > 0: self.stats.player_scored(player, self._team_finish_pts, screenmessage=False) self._team_finish_pts -= 25 # Flash where the player is. self._flash_player(player, 1.0) player.gamedata['finished'] = True assert player.actor player.actor.handlemessage( ba.DieMessage(immediate=True)) # Makes sure noone behind them passes them in rank # while finishing. player.gamedata['distance'] = 9999.0 # If the whole team has finished the race. if team.gamedata['lap'] == self.settings_raw['Laps']: ba.playsound(self._score_sound) player.team.gamedata['finished'] = True assert self._timer is not None cur_time = ba.time( timeformat=ba.TimeFormat.MILLISECONDS) start_time = self._timer.getstarttime( timeformat=ba.TimeFormat.MILLISECONDS) self._last_team_time = ( player.team.gamedata['time']) = (cur_time - start_time) self._check_end_game() # Team has yet to finish. else: ba.playsound(self._swipsound) # They've just finished a lap but not the race. else: ba.playsound(self._swipsound) self._flash_player(player, 0.3) # Print their lap number over their head. try: assert isinstance(player.actor, PlayerSpaz) mathnode = ba.newnode('math', owner=player.actor.node, attrs={ 'input1': (0, 1.9, 0), 'operation': 'add' }) player.actor.node.connectattr( 'torso_position', mathnode, 'input2') tstr = ba.Lstr( resource='lapNumberText', subs=[('${CURRENT}', str(player.gamedata['lap'] + 1)), ('${TOTAL}', str(self.settings_raw['Laps']))]) txtnode = ba.newnode('text', owner=mathnode, attrs={ 'text': tstr, 'in_world': True, 'color': (1, 1, 0, 1), 'scale': 0.015, 'h_align': 'center' }) mathnode.connectattr('output', txtnode, 'position') ba.animate(txtnode, 'scale', { 0.0: 0, 0.2: 0.019, 2.0: 0.019, 2.2: 0 }) ba.timer(2.3, mathnode.delete) except Exception as exc: print('Exception printing lap:', exc)
def __init__(self): ba.screenmessage('Server connector by Mr.Smoothy', color=(0, 1, 0)) if _ba.env().get("build_number",0) >= 20258: enableeeeee() else:print("only work on 1.5.29 and above ")
def _no_saved_party_selected_error(self) -> None: ba.screenmessage(ba.Lstr(resource="No Server Selected"), color=(1, 0, 0)) ba.playsound(ba.getsound('error'))