def refreshStats(self): liveplayers={} nextMap='' currentMap='' global stats for i in _ba.get_game_roster(): try: liveplayers[i['account_id']]={'name':i['players'][0]['name_full'],'client_id':i['client_id'],'device_id':i['display_string']} except: liveplayers[i['account_id']]={'name':"<in-lobby>",'clientid':i['client_id'],'device_id':i['display_string']} try: nextMap=_ba.get_foreground_host_session().get_next_game_description().evaluate() current_game_spec=_ba.get_foreground_host_session()._current_game_spec gametype: Type[GameActivity] =current_game_spec['resolved_type'] currentMap=gametype.get_settings_display_string(current_game_spec).evaluate() except: pass minigame={'current':currentMap,'next':nextMap} # system={'cpu':p.cpu_percent(),'ram':p.virtual_memory().percent} #system={'cpu':80,'ram':34} # stats['system']=system stats['roster']=liveplayers stats['chats']=_ba.get_chat_messages() stats['playlist']=minigame
def return_to_main_menu_session_gracefully(self) -> None: """Attempt to cleanly get back to the main menu.""" # pylint: disable=cyclic-import from ba import _benchmark from ba._general import Call from bastd.mainmenu import MainMenuSession _ba.app.main_window = None if isinstance(_ba.get_foreground_host_session(), MainMenuSession): # It may be possible we're on the main menu but the screen is faded # so fade back in. _ba.fade_screen(True) return _benchmark.stop_stress_test() # Stop stress-test if in progress. # If we're in a host-session, tell them to end. # This lets them tear themselves down gracefully. host_session: Optional[ba.Session] = _ba.get_foreground_host_session() if host_session is not None: # Kick off a little transaction so we'll hopefully have all the # latest account state when we get back to the menu. _ba.add_transaction({ 'type': 'END_SESSION', 'sType': str(type(host_session)) }) _ba.run_transactions() host_session.end() # Otherwise just force the issue. else: _ba.pushcall(Call(_ba.new_host_session, MainMenuSession))
def QuickAccess(msg,client_id): if msg.startswith(","): acid="" teamid=0 for i in _ba.get_foreground_host_session().sessionplayers: if i.inputdevice.client_id==client_id: teamid=i.sessionteam.id for i in _ba.get_foreground_host_session().sessionplayers: if teamid==i.sessionteam.id and i.inputdevice.client_id!=client_id: _ba.screenmessage(i.getname(True)+":"+msg[1:],clients=[i.inputdevice.client_id],color=(0.3,0.6,0.3)) return None;
def _put_log() -> None: try: sessionname = str(_ba.get_foreground_host_session()) except Exception: sessionname = 'unavailable' try: activityname = str(_ba.get_foreground_host_activity()) except Exception: activityname = 'unavailable' info = { 'log': _ba.getlog(), 'version': app.version, 'build': app.build_number, 'userAgentString': app.user_agent_string, 'session': sessionname, 'activity': activityname, 'fatal': 0, 'userRanCommands': _ba.has_user_run_commands(), 'time': _ba.time(TimeType.REAL), 'userModded': _ba.has_user_mods(), 'newsShow': _ba.get_news_show(), } def response(data: Any) -> None: # A non-None response means success; lets # take note that we don't need to report further # log info this run if data is not None: app.log_have_new = False _ba.mark_log_sent() master_server_post('bsLog', info, response)
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]
def clientid_to_myself(clientid): """Return Player Index Of Self Player""" session = _ba.get_foreground_host_session() for i in range(len(session.sessionplayers)): if session.sessionplayers[i].inputdevice.client_id == clientid: return int(session.sessionplayers[i].id)
def set_custom_effect(arguments): try: session = _ba.get_foreground_host_session() for i in session.sessionplayers: if i.inputdevice.client_id == int(arguments[1]): roles = pdata.set_effect(arguments[0], i.get_account_id()) except: return
def __init__(self, transition: Optional[str] = 'in_right'): # pylint: disable=cyclic-import import threading from bastd.mainmenu import MainMenuSession self._in_game = not isinstance(_ba.get_foreground_host_session(), MainMenuSession) # Preload some modules we use in a background thread so we won't # have a visual hitch when the user taps them. threading.Thread(target=self._preload_modules).start() if not self._in_game: ba.set_analytics_screen('Main Menu') self._show_remote_app_info_on_first_launch() # Make a vanilla container; we'll modify it to our needs in refresh. super().__init__(root_widget=ba.containerwidget( transition=transition, toolbar_visibility='menu_minimal_no_back' if self. _in_game else 'menu_minimal_no_back')) # Grab this stuff in case it changes. self._is_demo = ba.app.demo_mode self._is_arcade = ba.app.arcade_mode self._is_iircade = ba.app.iircade_mode self._tdelay = 0.0 self._t_delay_inc = 0.02 self._t_delay_play = 1.7 self._p_index = 0 self._use_autoselect = True self._button_width = 200.0 self._button_height = 45.0 self._width = 100.0 self._height = 100.0 self._demo_menu_button: Optional[ba.Widget] = None self._gather_button: Optional[ba.Widget] = None self._start_button: Optional[ba.Widget] = None self._watch_button: Optional[ba.Widget] = None self._gc_button: Optional[ba.Widget] = None self._how_to_play_button: Optional[ba.Widget] = None self._credits_button: Optional[ba.Widget] = None self._settings_button: Optional[ba.Widget] = None self._store_char_tex = self._get_store_char_tex() self._refresh() self._restore_state() # Keep an eye on a few things and refresh if they change. self._account_state = _ba.get_account_state() self._account_state_num = _ba.get_account_state_num() self._account_type = (_ba.get_account_type() if self._account_state == 'signed_in' else None) self._refresh_timer = ba.Timer(1.0, ba.WeakCall(self._check_refresh), repeat=True, timetype=ba.TimeType.REAL)
def _reset_stress_test(args: Dict[str, Any]) -> None: from ba._general import Call from ba._enums import TimeType _ba.set_stress_testing(False, args['player_count']) _ba.screenmessage('Resetting stress test...') session = _ba.get_foreground_host_session() assert session is not None session.end() _ba.timer(1.0, Call(start_stress_test, args), timetype=TimeType.REAL)
def remove(arguments): if arguments == [] or arguments == ['']: return elif arguments[0] == 'all': session = _ba.get_foreground_host_session() for i in session.sessionplayers: i.remove_from_game() else: try: session = _ba.get_foreground_host_session() for i in session.sessionplayers: if i.inputdevice.client_id == int(arguments[0]): i.remove_from_game() except: return
def add_role_to_player(arguments): try: session = _ba.get_foreground_host_session() for i in session.sessionplayers: if i.inputdevice.client_id == int(arguments[1]): roles = pdata.add_player_role(arguments[0], i.get_account_id()) except: return
def handle_hit(msg, hp, dmg, hit_by, msg_pos): #Check if not msg.hit_type: return #Record Out Data dmg = dmg / 10 if hit_by is not None: hit_by_id = None hit_by_id = hit_by.node.playerID if hit_by_id is not None: hit_by_account_id = None for c in _ba.get_foreground_host_session().sessionplayers: if (c.activityplayer) and (c.activityplayer.node.playerID == hit_by_id): hit_by_account_id = c.get_account_id() if hit_by_account_id in damage_data: damage_data[hit_by_account_id] += float(dmg) else: damage_data[hit_by_account_id] = float(dmg) #Send Screen Texts in enabled if our_settings['enableHitTexts']: try: if hp <= 0: PopupText("Rest In Peace !", color=(1, 0.2, 0.2), scale=1.6, position=msg_pos).autoretain() else: if dmg >= 800: PopupText("#PRO !", color=(1, 0.2, 0.2), scale=1.6, position=msg_pos).autoretain() elif dmg >= 600 and dmg < 800: PopupText("GOOD ONE!", color=(1, 0.3, 0.1), scale=1.6, position=msg_pos).autoretain() elif dmg >= 400 and dmg < 600: PopupText("OH! YEAH", color=(1, 0.5, 0.2), scale=1.6, position=msg_pos).autoretain() elif dmg >= 200 and dmg < 400: PopupText("WTF!", color=(0.7, 0.4, 0.2), scale=1.6, position=msg_pos).autoretain() elif dmg > 0 and dmg < 200: PopupText("!!!", color=(1, 1, 1), scale=1.6, position=msg_pos).autoretain() except: pass return
def set_playlist_inline(playlist, newPLaylistType): session = _ba.get_foreground_host_session() if (isinstance(session, DualTeamSession) or isinstance(session, CoopSession)) and newPLaylistType == 'ffa': #_ba.get_foreground_host_activity().end_game() _ba.get_foreground_host_session().end() _thread.start_new_thread(withDelay, ( FreeForAllSession, playlist, )) elif (isinstance(session, FreeForAllSession) or isinstance(session, CoopSession)) and newPLaylistType == "teams": _ba.get_foreground_host_session().end() _thread.start_new_thread(withDelay, ( DualTeamSession, playlist, )) else: updatePlaylist(playlist)
def setTeamCharacter(): if not _setting["sameCharacterForTeam"]: return used = [] teams = _ba.get_foreground_host_session().sessionteams if len(teams) < 10: for team in teams: character = getRandomCharacter(used) used.append(character) team.name = character team.customdata["character"] = character
def updatePlaylist(playlist): session = _ba.get_foreground_host_session() content = ba._playlist.filter_playlist( playlist, sessiontype=type(session), add_resolved_type=True, ) playlist = ba._multiteamsession.ShuffleList(content, shuffle=False) session._playlist = playlist set_next_map(session, playlist.pull_next())
def movePlayers(fromTeam,toTeam,count): session=_ba.get_foreground_host_session() fromTeam=session.sessionteams[fromTeam] toTeam=session.sessionteams[toTeam] for i in range(0,count): player=fromTeam.players.pop() print("moved"+player.get_account_id()) broadCastShiftMsg(player.get_account_id()) player.setdata(team=toTeam,character=player.character,color=toTeam.color,highlight=player.highlight) iconinfo=player.get_icon_info() player.set_icon_info(iconinfo['texture'],iconinfo['tint_texture'],toTeam.color,player.highlight) toTeam.players.append(player)
def on_player_join(): session = _ba.get_foreground_host_session() if len(session.sessionplayers)>1: return if isinstance(session,DualTeamSession): if settings["coopModeWithLessPlayers"]["enable"] and len(session.sessionplayers) < settings["coopModeWithLessPlayers"]["minPlayerToExitCoop"]: playlist.setPlaylist('coop') # this not usefull now ., leave it here for now elif isinstance(session,CoopSession): if len(session.sessionplayers) >= settings["coopModeWithLessPlayers"]["minPlayerToExitCoop"]: playlist.setPlaylist('default')
def get_roles_of_player(arguments, clientid): try: session = _ba.get_foreground_host_session() roles = [] reply = "" for i in session.sessionplayers: if i.inputdevice.client_id == int(arguments[0]): roles = pdata.get_player_roles(i.get_account_id()) print(roles) for role in roles: reply = reply + role + "," send(reply, clientid) except: return
def handle_party_invite(name: str, invite_id: str) -> None: """Handle an incoming party invitation.""" from bastd import mainmenu from bastd.ui import confirm ba.playsound(ba.getsound('fanfare')) # if we're not in the main menu, just print the invite # (don't want to screw up an in-progress game) in_game = not isinstance(_ba.get_foreground_host_session(), mainmenu.MainMenuSession) if in_game: ba.screenmessage(ba.Lstr( value='${A}\n${B}', subs=[('${A}', ba.Lstr(resource='gatherWindow.partyInviteText', subs=[('${NAME}', name)])), ('${B}', ba.Lstr( resource='gatherWindow.partyInviteGooglePlayExtraText')) ]), color=(0.5, 1, 0)) else: def do_accept(inv_id: str) -> None: _ba.accept_party_invitation(inv_id) conf = confirm.ConfirmWindow( ba.Lstr(resource='gatherWindow.partyInviteText', subs=[('${NAME}', name)]), ba.Call(do_accept, invite_id), width=500, height=150, color=(0.75, 1.0, 0.0), ok_text=ba.Lstr(resource='gatherWindow.partyInviteAcceptText'), cancel_text=ba.Lstr(resource='gatherWindow.partyInviteIgnoreText')) # FIXME: Ugly. # Let's store the invite-id away on the confirm window so we know if # we need to kill it later. # noinspection PyTypeHints conf.party_invite_id = invite_id # type: ignore # store a weak-ref so we can get at this later ba.app.invite_confirm_windows.append(weakref.ref(conf)) # go ahead and prune our weak refs while we're here. ba.app.invite_confirm_windows = [ w for w in ba.app.invite_confirm_windows if w() is not None ]
def __init__(self, transition: Optional[str] = 'in_right'): # pylint: disable=cyclic-import from bastd import mainmenu self._in_game = not isinstance(_ba.get_foreground_host_session(), mainmenu.MainMenuSession) if not self._in_game: ba.set_analytics_screen('Main Menu') self._show_remote_app_info_on_first_launch() # Make a vanilla container; we'll modify it to our needs in refresh. super().__init__(root_widget=ba.containerwidget( transition=transition, toolbar_visibility='menu_minimal_no_back' if self. _in_game else 'menu_minimal_no_back')) self._is_kiosk = ba.app.kiosk_mode self._tdelay = 0.0 self._t_delay_inc = 0.02 self._t_delay_play = 1.7 self._p_index = 0 self._use_autoselect = True self._button_width = 200.0 self._button_height = 45.0 self._width = 100.0 self._height = 100.0 self._demo_menu_button: Optional[ba.Widget] = None self._gather_button: Optional[ba.Widget] = None self._start_button: Optional[ba.Widget] = None self._watch_button: Optional[ba.Widget] = None self._gc_button: Optional[ba.Widget] = None self._how_to_play_button: Optional[ba.Widget] = None self._credits_button: Optional[ba.Widget] = None self._store_char_tex = self._get_store_char_tex() self._refresh() self._restore_state() # Keep an eye on a few things and refresh if they change. self._account_state = _ba.get_account_state() self._account_state_num = _ba.get_account_state_num() self._account_type = (_ba.get_account_type() if self._account_state == 'signed_in' else None) self._refresh_timer = ba.Timer(1.0, ba.WeakCall(self._check_refresh), repeat=True, timetype=ba.TimeType.REAL)
def list(clientid): """Returns The List Of Players Clientid and index""" p = u'{0:^16}{1:^15}{2:^10}' seprator = '\n______________________________\n' list = p.format('Name', 'Client ID' , 'Player ID')+seprator session = _ba.get_foreground_host_session() for index, player in enumerate(session.sessionplayers): list += p.format(player.getname(icon = False), player.inputdevice.client_id, index)+"\n" send(list, clientid)
def accountid_request(arguments, clientid, accountid): """Returns The Account Id Of Players""" if arguments == [] or arguments == ['']: send(f"Your account id is {accountid} ", clientid) else: try: session = _ba.get_foreground_host_session() player = session.sessionplayers[int(arguments[0])] name = player.getname(full=True, icon=True) accountid = player.get_account_id() send(f" {name}'s account id is '{accountid}' ", clientid) except: return
def __init__(self): modifyspaz.setTeamCharacter() data = setti['textonmap'] left = data['bottom left watermark'] top = data['top watermark'] nextMap="" try: nextMap=_ba.get_foreground_host_session().get_next_game_description().evaluate() except: pass self.index = 0 self.highlights = data['center highlights']["msg"] self.left_watermark(left) self.top_message(top) self.nextGame(nextMap) if setti["leaderboard"]["enable"]: self.leaderBoard() self.timer = ba.timer(8, ba.Call(self.highlights_), repeat=True)
def _on_party_member_press(self, client_id: int, is_host: bool, widget: ba.Widget) -> None: # if we're the host, pop up 'kick' options for all non-host members if _ba.get_foreground_host_session() is not None: kick_str = ba.Lstr(resource='kickText') else: # kick-votes appeared in build 14248 if (_ba.get_connection_to_host_info().get('build_number', 0) < 14248): return kick_str = ba.Lstr(resource='kickVoteText') popup.PopupMenuWindow( position=widget.get_screen_space_center(), scale=2.3 if ba.app.small_ui else 1.65 if ba.app.med_ui else 1.23, choices=['kick'], choices_display=[kick_str], current_choice='kick', delegate=self) self._popup_type = 'partyMemberPress' self._popup_party_member_client_id = client_id self._popup_party_member_is_host = is_host
def balanceTeams(): session = _ba.get_foreground_host_session() if settings["coopModeWithLessPlayers"]["enable"] and len(session.sessionplayers) < settings["coopModeWithLessPlayers"]["minPlayerToExitCoop"]: playlist.setPlaylist('coop') return if not isinstance(session,DualTeamSession) or len(session.sessionplayers)<4 or len(session.sessionteams)!=2: return teamASize=0 teamBSize=0 for player in session.sessionplayers: if player.sessionteam.id==0: teamASize+=1 else: teamBSize+=1 if abs(teamBSize-teamASize)>=0: if teamBSize> teamASize and teamBSize!=0: movePlayers(1,0,abs(teamBSize-teamASize)-1) elif teamASize>teamBSize and teamASize!=0: movePlayers(0,1,abs(teamBSize-teamASize)-1)
def getTeamInfo(self): data = {} session = _ba.get_foreground_host_session() data['sessionType'] = type(session).__name__ teams = session.sessionteams for team in teams: data[team.id] = { 'name': team.name.evaluate(), 'color': list(team.color), 'score': team.customdata['score'], 'players': [] } for player in team.players: teamplayer = { 'name': player.getname(), 'device_id': player.inputdevice.get_account_name(True), 'inGame': player.in_game, 'character': player.character, 'account_id': player.get_account_id() } data[team.id]['players'].append(teamplayer) return data
def call_after_ad(call: Callable[[], Any]) -> None: """Run a call after potentially showing an ad.""" # pylint: disable=too-many-statements # pylint: disable=too-many-branches # pylint: disable=too-many-locals from ba._account import have_pro from ba._enums import TimeType import time app = _ba.app show = True # No ads without net-connections, etc. if not _ba.can_show_ad(): show = False if have_pro(): show = False # Pro disables interstitials. try: session = _ba.get_foreground_host_session() assert session is not None is_tournament = session.tournament_id is not None except Exception: is_tournament = False if is_tournament: show = False # Never show ads during tournaments. if show: interval: Optional[float] launch_count = app.config.get('launchCount', 0) # If we're seeing short ads we may want to space them differently. interval_mult = (_ba.get_account_misc_read_val( 'ads.shortIntervalMult', 1.0) if app.last_ad_was_short else 1.0) if app.ad_amt is None: if launch_count <= 1: app.ad_amt = _ba.get_account_misc_read_val( 'ads.startVal1', 0.99) else: app.ad_amt = _ba.get_account_misc_read_val( 'ads.startVal2', 1.0) interval = None else: # So far we're cleared to show; now calc our ad-show-threshold and # see if we should *actually* show (we reach our threshold faster # the longer we've been playing). base = 'ads' if _ba.has_video_ads() else 'ads2' min_lc = _ba.get_account_misc_read_val(base + '.minLC', 0.0) max_lc = _ba.get_account_misc_read_val(base + '.maxLC', 5.0) min_lc_scale = (_ba.get_account_misc_read_val( base + '.minLCScale', 0.25)) max_lc_scale = (_ba.get_account_misc_read_val( base + '.maxLCScale', 0.34)) min_lc_interval = (_ba.get_account_misc_read_val( base + '.minLCInterval', 360)) max_lc_interval = (_ba.get_account_misc_read_val( base + '.maxLCInterval', 300)) if launch_count < min_lc: lc_amt = 0.0 elif launch_count > max_lc: lc_amt = 1.0 else: lc_amt = ((float(launch_count) - min_lc) / (max_lc - min_lc)) incr = (1.0 - lc_amt) * min_lc_scale + lc_amt * max_lc_scale interval = ((1.0 - lc_amt) * min_lc_interval + lc_amt * max_lc_interval) app.ad_amt += incr assert app.ad_amt is not None if app.ad_amt >= 1.0: app.ad_amt = app.ad_amt % 1.0 app.attempted_first_ad = True # After we've reached the traditional show-threshold once, # try again whenever its been INTERVAL since our last successful show. elif (app.attempted_first_ad and (app.last_ad_completion_time is None or (interval is not None and _ba.time(TimeType.REAL) - app.last_ad_completion_time > (interval * interval_mult)))): # Reset our other counter too in this case. app.ad_amt = 0.0 else: show = False # If we're *still* cleared to show, actually tell the system to show. if show: # As a safety-check, set up an object that will run # the completion callback if we've returned and sat for 10 seconds # (in case some random ad network doesn't properly deliver its # completion callback). class _Payload: def __init__(self, pcall: Callable[[], Any]): self._call = pcall self._ran = False def run(self, fallback: bool = False) -> None: """Run the fallback call (and issues a warning about it).""" if not self._ran: if fallback: print(( 'ERROR: relying on fallback ad-callback! ' 'last network: ' + app.last_ad_network + ' (set ' + str(int(time.time() - app.last_ad_network_set_time)) + 's ago); purpose=' + app.last_ad_purpose)) _ba.pushcall(self._call) self._ran = True payload = _Payload(call) with _ba.Context('ui'): _ba.timer(5.0, lambda: payload.run(fallback=True), timetype=TimeType.REAL) show_ad('between_game', on_completion_call=payload.run) else: _ba.pushcall(call) # Just run the callback without the ad.
def _refresh_in_game( self, positions: List[Tuple[float, float, float]]) -> Tuple[float, float, float]: # pylint: disable=too-many-branches # pylint: disable=too-many-locals # pylint: disable=too-many-statements custom_menu_entries: List[Dict[str, Any]] = [] session = _ba.get_foreground_host_session() if session is not None: try: custom_menu_entries = session.get_custom_menu_entries() for cme in custom_menu_entries: if (not isinstance(cme, dict) or 'label' not in cme or not isinstance(cme['label'], (str, ba.Lstr)) or 'call' not in cme or not callable(cme['call'])): raise ValueError('invalid custom menu entry: ' + str(cme)) except Exception: custom_menu_entries = [] ba.print_exception( f'Error getting custom menu entries for {session}') self._width = 250.0 self._height = 250.0 if self._input_player else 180.0 if (self._is_demo or self._is_arcade) and self._input_player: self._height -= 40 if not self._have_settings_button: self._height -= 50 if self._connected_to_remote_player: # In this case we have a leave *and* a disconnect button. self._height += 50 self._height += 50 * (len(custom_menu_entries)) uiscale = ba.app.ui.uiscale ba.containerwidget( edit=self._root_widget, size=(self._width, self._height), scale=(2.15 if uiscale is ba.UIScale.SMALL else 1.6 if uiscale is ba.UIScale.MEDIUM else 1.0)) h = 125.0 v = (self._height - 80.0 if self._input_player else self._height - 60) h_offset = 0 d_h_offset = 0 v_offset = -50 for _i in range(6 + len(custom_menu_entries)): positions.append((h, v, 1.0)) v += v_offset h += h_offset h_offset += d_h_offset self._start_button = None ba.app.pause() # Player name if applicable. if self._input_player: player_name = self._input_player.getname() h, v, scale = positions[self._p_index] v += 35 ba.textwidget(parent=self._root_widget, position=(h - self._button_width / 2, v), size=(self._button_width, self._button_height), color=(1, 1, 1, 0.5), scale=0.7, h_align='center', text=ba.Lstr(value=player_name)) else: player_name = '' h, v, scale = positions[self._p_index] self._p_index += 1 btn = ba.buttonwidget(parent=self._root_widget, position=(h - self._button_width / 2, v), size=(self._button_width, self._button_height), scale=scale, label=ba.Lstr(resource=self._r + '.resumeText'), autoselect=self._use_autoselect, on_activate_call=self._resume) ba.containerwidget(edit=self._root_widget, cancel_button=btn) # Add any custom options defined by the current game. for entry in custom_menu_entries: h, v, scale = positions[self._p_index] self._p_index += 1 # Ask the entry whether we should resume when we call # it (defaults to true). resume = bool(entry.get('resume_on_call', True)) if resume: call = ba.Call(self._resume_and_call, entry['call']) else: call = ba.Call(entry['call'], ba.WeakCall(self._resume)) ba.buttonwidget(parent=self._root_widget, position=(h - self._button_width / 2, v), size=(self._button_width, self._button_height), scale=scale, on_activate_call=call, label=entry['label'], autoselect=self._use_autoselect) # Add a 'leave' button if the menu-owner has a player. if ((self._input_player or self._connected_to_remote_player) and not (self._is_demo or self._is_arcade)): h, v, scale = positions[self._p_index] self._p_index += 1 btn = ba.buttonwidget(parent=self._root_widget, position=(h - self._button_width / 2, v), size=(self._button_width, self._button_height), scale=scale, on_activate_call=self._leave, label='', autoselect=self._use_autoselect) if (player_name != '' and player_name[0] != '<' and player_name[-1] != '>'): txt = ba.Lstr(resource=self._r + '.justPlayerText', subs=[('${NAME}', player_name)]) else: txt = ba.Lstr(value=player_name) ba.textwidget(parent=self._root_widget, position=(h, v + self._button_height * (0.64 if player_name != '' else 0.5)), size=(0, 0), text=ba.Lstr(resource=self._r + '.leaveGameText'), scale=(0.83 if player_name != '' else 1.0), color=(0.75, 1.0, 0.7), h_align='center', v_align='center', draw_controller=btn, maxwidth=self._button_width * 0.9) ba.textwidget(parent=self._root_widget, position=(h, v + self._button_height * 0.27), size=(0, 0), text=txt, color=(0.75, 1.0, 0.7), h_align='center', v_align='center', draw_controller=btn, scale=0.45, maxwidth=self._button_width * 0.9) return h, v, scale
def _refresh(self) -> None: # pylint: disable=too-many-branches # pylint: disable=too-many-locals # pylint: disable=too-many-statements from bastd.ui.confirm import QuitWindow from bastd.ui.store.button import StoreButton # Clear everything that was there. children = self._root_widget.get_children() for child in children: child.delete() self._tdelay = 0.0 self._t_delay_inc = 0.0 self._t_delay_play = 0.0 self._button_width = 200.0 self._button_height = 45.0 self._r = 'mainMenu' app = ba.app self._have_quit_button = (app.ui.uiscale is ba.UIScale.LARGE or (app.platform == 'windows' and app.subplatform == 'oculus')) self._have_store_button = not self._in_game self._have_settings_button = ( (not self._in_game or not app.toolbar_test) and not (self._is_demo or self._is_arcade or self._is_iircade)) self._input_device = input_device = _ba.get_ui_input_device() self._input_player = input_device.player if input_device else None self._connected_to_remote_player = ( input_device.is_connected_to_remote_player() if input_device else False) positions: List[Tuple[float, float, float]] = [] self._p_index = 0 if self._in_game: h, v, scale = self._refresh_in_game(positions) else: h, v, scale = self._refresh_not_in_game(positions) if self._have_settings_button: h, v, scale = positions[self._p_index] self._p_index += 1 self._settings_button = ba.buttonwidget( parent=self._root_widget, position=(h - self._button_width * 0.5 * scale, v), size=(self._button_width, self._button_height), scale=scale, autoselect=self._use_autoselect, label=ba.Lstr(resource=self._r + '.settingsText'), transition_delay=self._tdelay, on_activate_call=self._settings) # Scattered eggs on easter. if _ba.get_account_misc_read_val('easter', False) and not self._in_game: icon_size = 34 ba.imagewidget(parent=self._root_widget, position=(h - icon_size * 0.5 - 15, v + self._button_height * scale - icon_size * 0.24 + 1.5), transition_delay=self._tdelay, size=(icon_size, icon_size), texture=ba.gettexture('egg3'), tilt_scale=0.0) self._tdelay += self._t_delay_inc if self._in_game: h, v, scale = positions[self._p_index] self._p_index += 1 # If we're in a replay, we have a 'Leave Replay' button. if _ba.is_in_replay(): ba.buttonwidget(parent=self._root_widget, position=(h - self._button_width * 0.5 * scale, v), scale=scale, size=(self._button_width, self._button_height), autoselect=self._use_autoselect, label=ba.Lstr(resource='replayEndText'), on_activate_call=self._confirm_end_replay) elif _ba.get_foreground_host_session() is not None: ba.buttonwidget( parent=self._root_widget, position=(h - self._button_width * 0.5 * scale, v), scale=scale, size=(self._button_width, self._button_height), autoselect=self._use_autoselect, label=ba.Lstr(resource=self._r + '.endGameText'), on_activate_call=self._confirm_end_game) # Assume we're in a client-session. else: ba.buttonwidget( parent=self._root_widget, position=(h - self._button_width * 0.5 * scale, v), scale=scale, size=(self._button_width, self._button_height), autoselect=self._use_autoselect, label=ba.Lstr(resource=self._r + '.leavePartyText'), on_activate_call=self._confirm_leave_party) self._store_button: Optional[ba.Widget] if self._have_store_button: this_b_width = self._button_width h, v, scale = positions[self._p_index] self._p_index += 1 sbtn = self._store_button_instance = StoreButton( parent=self._root_widget, position=(h - this_b_width * 0.5 * scale, v), size=(this_b_width, self._button_height), scale=scale, on_activate_call=ba.WeakCall(self._on_store_pressed), sale_scale=1.3, transition_delay=self._tdelay) self._store_button = store_button = sbtn.get_button() uiscale = ba.app.ui.uiscale icon_size = (55 if uiscale is ba.UIScale.SMALL else 55 if uiscale is ba.UIScale.MEDIUM else 70) ba.imagewidget( parent=self._root_widget, position=(h - icon_size * 0.5, v + self._button_height * scale - icon_size * 0.23), transition_delay=self._tdelay, size=(icon_size, icon_size), texture=ba.gettexture(self._store_char_tex), tilt_scale=0.0, draw_controller=store_button) self._tdelay += self._t_delay_inc else: self._store_button = None self._quit_button: Optional[ba.Widget] if not self._in_game and self._have_quit_button: h, v, scale = positions[self._p_index] self._p_index += 1 self._quit_button = quit_button = ba.buttonwidget( parent=self._root_widget, autoselect=self._use_autoselect, position=(h - self._button_width * 0.5 * scale, v), size=(self._button_width, self._button_height), scale=scale, label=ba.Lstr(resource=self._r + ('.quitText' if 'Mac' in ba.app.user_agent_string else '.exitGameText')), on_activate_call=self._quit, transition_delay=self._tdelay) # Scattered eggs on easter. if _ba.get_account_misc_read_val('easter', False): icon_size = 30 ba.imagewidget(parent=self._root_widget, position=(h - icon_size * 0.5 + 25, v + self._button_height * scale - icon_size * 0.24 + 1.5), transition_delay=self._tdelay, size=(icon_size, icon_size), texture=ba.gettexture('egg1'), tilt_scale=0.0) ba.containerwidget(edit=self._root_widget, cancel_button=quit_button) self._tdelay += self._t_delay_inc else: self._quit_button = None # If we're not in-game, have no quit button, and this is android, # we want back presses to quit our activity. if (not self._in_game and not self._have_quit_button and ba.app.platform == 'android'): def _do_quit() -> None: QuitWindow(swish=True, back=True) ba.containerwidget(edit=self._root_widget, on_cancel_call=_do_quit) # Add speed-up/slow-down buttons for replays. # (ideally this should be part of a fading-out playback bar like most # media players but this works for now). if _ba.is_in_replay(): b_size = 50.0 b_buffer = 10.0 t_scale = 0.75 uiscale = ba.app.ui.uiscale if uiscale is ba.UIScale.SMALL: b_size *= 0.6 b_buffer *= 1.0 v_offs = -40 t_scale = 0.5 elif uiscale is ba.UIScale.MEDIUM: v_offs = -70 else: v_offs = -100 self._replay_speed_text = ba.textwidget( parent=self._root_widget, text=ba.Lstr(resource='watchWindow.playbackSpeedText', subs=[('${SPEED}', str(1.23))]), position=(h, v + v_offs + 7 * t_scale), h_align='center', v_align='center', size=(0, 0), scale=t_scale) # Update to current value. self._change_replay_speed(0) # Keep updating in a timer in case it gets changed elsewhere. self._change_replay_speed_timer = ba.Timer( 0.25, ba.WeakCall(self._change_replay_speed, 0), timetype=ba.TimeType.REAL, repeat=True) btn = ba.buttonwidget(parent=self._root_widget, position=(h - b_size - b_buffer, v - b_size - b_buffer + v_offs), button_type='square', size=(b_size, b_size), label='', autoselect=True, on_activate_call=ba.Call( self._change_replay_speed, -1)) ba.textwidget( parent=self._root_widget, draw_controller=btn, text='-', position=(h - b_size * 0.5 - b_buffer, v - b_size * 0.5 - b_buffer + 5 * t_scale + v_offs), h_align='center', v_align='center', size=(0, 0), scale=3.0 * t_scale) btn = ba.buttonwidget( parent=self._root_widget, position=(h + b_buffer, v - b_size - b_buffer + v_offs), button_type='square', size=(b_size, b_size), label='', autoselect=True, on_activate_call=ba.Call(self._change_replay_speed, 1)) ba.textwidget( parent=self._root_widget, draw_controller=btn, text='+', position=(h + b_size * 0.5 + b_buffer, v - b_size * 0.5 - b_buffer + 5 * t_scale + v_offs), h_align='center', v_align='center', size=(0, 0), scale=3.0 * t_scale)
def _refresh(self) -> None: # pylint: disable=too-many-locals from ba.internal import (PlayerProfilesChangedMessage, get_player_profile_colors, get_player_profile_icon) old_selection = self._selected_profile # Delete old. while self._profile_widgets: self._profile_widgets.pop().delete() try: self._profiles = ba.app.config['Player Profiles'] except Exception: self._profiles = {} assert self._profiles is not None items = list(self._profiles.items()) items.sort(key=lambda x: x[0].lower()) index = 0 account_name: Optional[str] if _ba.get_account_state() == 'signed_in': account_name = _ba.get_account_display_string() else: account_name = None widget_to_select = None for p_name, _ in items: if p_name == '__account__' and account_name is None: continue color, _highlight = get_player_profile_colors(p_name) scl = 1.1 txtw = ba.textwidget( parent=self._columnwidget, position=(0, 32), size=((self._width - 40) / scl, 28), text=ba.Lstr( value=account_name if p_name == '__account__' else get_player_profile_icon(p_name) + p_name), h_align='left', v_align='center', on_select_call=ba.WeakCall(self._select, p_name, index), maxwidth=self._scroll_width * 0.92, corner_scale=scl, color=ba.safecolor(color, 0.4), always_highlight=True, on_activate_call=ba.Call(self._edit_button.activate), selectable=True) if index == 0: ba.widget(edit=txtw, up_widget=self._back_button) ba.widget(edit=txtw, show_buffer_top=40, show_buffer_bottom=40) self._profile_widgets.append(txtw) # Select/show this one if it was previously selected # (but defer till after this loop since our height is # still changing). if p_name == old_selection: widget_to_select = txtw index += 1 if widget_to_select is not None: ba.columnwidget(edit=self._columnwidget, selected_child=widget_to_select, visible_child=widget_to_select) # If there's a team-chooser in existence, tell it the profile-list # has probably changed. session = _ba.get_foreground_host_session() if session is not None: session.handlemessage(PlayerProfilesChangedMessage())