def _gather_press(self) -> None: # pylint: disable=cyclic-import from bastd.ui.gather import GatherWindow self._save_state() ba.containerwidget(edit=self._root_widget, transition='out_left') ba.app.ui.set_main_menu_window( GatherWindow(origin_widget=self._gather_button).get_root_widget())
def _back(self) -> None: # pylint: disable=cyclic-import if self._is_main_menu: from bastd.ui.mainmenu import MainMenuWindow self._save_state() ba.app.ui.set_main_menu_window( MainMenuWindow(transition='in_left').get_root_widget()) ba.containerwidget(edit=self._root_widget, transition=self._transition_out) else: from bastd.ui.gather import GatherWindow self._save_state() ba.app.ui.set_main_menu_window( GatherWindow(transition='in_left').get_root_widget()) ba.containerwidget(edit=self._root_widget, transition=self._transition_out)
def _on_ok_press(self) -> None: # Disallow if our playlist has disappeared. if not self._does_target_playlist_exist(): return # Disallow if we have no unlocked games. if not self._have_at_least_one_owned: ba.playsound(ba.getsound('error')) ba.screenmessage(ba.Lstr(resource='playlistNoValidGamesErrorText'), color=(1, 0, 0)) return cfg = ba.app.config cfg[self._pvars.config_name + ' Playlist Selection'] = self._playlist # Head back to the gather window in playlist-select mode # or start the game in regular mode. if self._selecting_mode: from bastd.ui.gather import GatherWindow if self._sessiontype is ba.FreeForAllSession: typename = 'ffa' elif self._sessiontype is ba.DualTeamSession: typename = 'teams' else: raise RuntimeError('Only teams and ffa currently supported') cfg['Private Party Host Session Type'] = typename ba.playsound(ba.getsound('gunCocking')) ba.app.ui.set_main_menu_window( GatherWindow(transition='in_right').get_root_widget()) self._transition_out(transition='out_left') if self._delegate is not None: self._delegate.on_play_options_window_run_game() else: _ba.fade_screen(False, endcall=self._run_selected_playlist) _ba.lock_all_input() self._transition_out(transition='out_left') if self._delegate is not None: self._delegate.on_play_options_window_run_game() cfg.commit()
def on_transition_in(self) -> None: super().on_transition_in() random.seed(123) self._logo_node: Optional[ba.Node] = None self._custom_logo_tex_name: Optional[str] = None self._word_actors: List[ba.Actor] = [] app = ba.app # FIXME: We shouldn't be doing things conditionally based on whether # the host is VR mode or not (clients may differ in that regard). # Any differences need to happen at the engine level so everyone # sees things in their own optimal way. vr_mode = ba.app.vr_mode if not ba.app.toolbar_test: color = ((1.0, 1.0, 1.0, 1.0) if vr_mode else (0.5, 0.6, 0.5, 0.6)) # FIXME: Need a node attr for vr-specific-scale. scale = (0.9 if (app.ui.uiscale is ba.UIScale.SMALL or vr_mode) else 0.7) self.my_name = ba.NodeActor( ba.newnode('text', attrs={ 'v_attach': 'bottom', 'h_align': 'center', 'color': color, 'flatness': 1.0, 'shadow': 1.0 if vr_mode else 0.5, 'scale': scale, 'position': (0, 10), 'vr_depth': -10, 'text': '\xa9 2011-2020 Eric Froemling' })) # Throw up some text that only clients can see so they know that the # host is navigating menus while they're just staring at an # empty-ish screen. tval = ba.Lstr(resource='hostIsNavigatingMenusText', subs=[('${HOST}', _ba.get_account_display_string())]) self._host_is_navigating_text = ba.NodeActor( ba.newnode('text', attrs={ 'text': tval, 'client_only': True, 'position': (0, -200), 'flatness': 1.0, 'h_align': 'center' })) if not ba.app.main_menu_did_initial_transition and hasattr( self, 'my_name'): assert self.my_name.node ba.animate(self.my_name.node, 'opacity', {2.3: 0, 3.0: 1.0}) # FIXME: We shouldn't be doing things conditionally based on whether # the host is vr mode or not (clients may not be or vice versa). # Any differences need to happen at the engine level so everyone sees # things in their own optimal way. vr_mode = app.vr_mode uiscale = app.ui.uiscale # In cases where we're doing lots of dev work lets always show the # build number. force_show_build_number = False if not ba.app.toolbar_test: if app.debug_build or app.test_build or force_show_build_number: if app.debug_build: text = ba.Lstr(value='${V} (${B}) (${D})', subs=[ ('${V}', app.version), ('${B}', str(app.build_number)), ('${D}', ba.Lstr(resource='debugText')), ]) else: text = ba.Lstr(value='${V} (${B})', subs=[ ('${V}', app.version), ('${B}', str(app.build_number)), ]) else: text = ba.Lstr(value='${V}', subs=[('${V}', app.version)]) scale = 0.9 if (uiscale is ba.UIScale.SMALL or vr_mode) else 0.7 color = (1, 1, 1, 1) if vr_mode else (0.5, 0.6, 0.5, 0.7) self.version = ba.NodeActor( ba.newnode( 'text', attrs={ 'v_attach': 'bottom', 'h_attach': 'right', 'h_align': 'right', 'flatness': 1.0, 'vr_depth': -10, 'shadow': 1.0 if vr_mode else 0.5, 'color': color, 'scale': scale, 'position': (-260, 10) if vr_mode else (-10, 10), 'text': text })) if not ba.app.main_menu_did_initial_transition: assert self.version.node ba.animate(self.version.node, 'opacity', {2.3: 0, 3.0: 1.0}) # Show the iircade logo on our iircade build. if app.iircade_mode: img = ba.NodeActor( ba.newnode('image', attrs={ 'texture': ba.gettexture('iircadeLogo'), 'attach': 'center', 'scale': (250, 250), 'position': (0, 0), 'tilt_translate': 0.21, 'absolute_scale': True })).autoretain() imgdelay = 0.0 if app.main_menu_did_initial_transition else 1.0 ba.animate(img.node, 'opacity', { imgdelay + 1.5: 0.0, imgdelay + 2.5: 1.0 }) # Throw in test build info. self.beta_info = self.beta_info_2 = None if app.test_build and not (app.demo_mode or app.arcade_mode): pos = ((230, 125) if (app.demo_mode or app.arcade_mode) else (230, 35)) self.beta_info = ba.NodeActor( ba.newnode('text', attrs={ 'v_attach': 'center', 'h_align': 'center', 'color': (1, 1, 1, 1), 'shadow': 0.5, 'flatness': 0.5, 'scale': 1, 'vr_depth': -60, 'position': pos, 'text': ba.Lstr(resource='testBuildText') })) if not ba.app.main_menu_did_initial_transition: assert self.beta_info.node ba.animate(self.beta_info.node, 'opacity', {1.3: 0, 1.8: 1.0}) model = ba.getmodel('thePadLevel') trees_model = ba.getmodel('trees') bottom_model = ba.getmodel('thePadLevelBottom') color_texture = ba.gettexture('thePadLevelColor') trees_texture = ba.gettexture('treesColor') bgtex = ba.gettexture('menuBG') bgmodel = ba.getmodel('thePadBG') # Load these last since most platforms don't use them. vr_bottom_fill_model = ba.getmodel('thePadVRFillBottom') vr_top_fill_model = ba.getmodel('thePadVRFillTop') gnode = self.globalsnode gnode.camera_mode = 'rotate' tint = (1.14, 1.1, 1.0) gnode.tint = tint gnode.ambient_color = (1.06, 1.04, 1.03) gnode.vignette_outer = (0.45, 0.55, 0.54) gnode.vignette_inner = (0.99, 0.98, 0.98) self.bottom = ba.NodeActor( ba.newnode('terrain', attrs={ 'model': bottom_model, 'lighting': False, 'reflection': 'soft', 'reflection_scale': [0.45], 'color_texture': color_texture })) self.vr_bottom_fill = ba.NodeActor( ba.newnode('terrain', attrs={ 'model': vr_bottom_fill_model, 'lighting': False, 'vr_only': True, 'color_texture': color_texture })) self.vr_top_fill = ba.NodeActor( ba.newnode('terrain', attrs={ 'model': vr_top_fill_model, 'vr_only': True, 'lighting': False, 'color_texture': bgtex })) self.terrain = ba.NodeActor( ba.newnode('terrain', attrs={ 'model': model, 'color_texture': color_texture, 'reflection': 'soft', 'reflection_scale': [0.3] })) self.trees = ba.NodeActor( ba.newnode('terrain', attrs={ 'model': trees_model, 'lighting': False, 'reflection': 'char', 'reflection_scale': [0.1], 'color_texture': trees_texture })) self.bgterrain = ba.NodeActor( ba.newnode('terrain', attrs={ 'model': bgmodel, 'color': (0.92, 0.91, 0.9), 'lighting': False, 'background': True, 'color_texture': bgtex })) self._ts = 0.86 self._language: Optional[str] = None self._update_timer = ba.Timer(1.0, self._update, repeat=True) self._update() # Hopefully this won't hitch but lets space these out anyway. _ba.add_clean_frame_callback(ba.WeakCall(self._start_preloads)) random.seed() # On the main menu, also show our news. class News: """Wrangles news display.""" def __init__(self, activity: ba.Activity): self._valid = True self._message_duration = 10.0 self._message_spacing = 2.0 self._text: Optional[ba.NodeActor] = None self._activity = weakref.ref(activity) # If we're signed in, fetch news immediately. # Otherwise wait until we are signed in. self._fetch_timer: Optional[ba.Timer] = ba.Timer( 1.0, ba.WeakCall(self._try_fetching_news), repeat=True) self._try_fetching_news() # We now want to wait until we're signed in before fetching news. def _try_fetching_news(self) -> None: if _ba.get_account_state() == 'signed_in': self._fetch_news() self._fetch_timer = None def _fetch_news(self) -> None: ba.app.main_menu_last_news_fetch_time = time.time() # UPDATE - We now just pull news from MRVs. news = _ba.get_account_misc_read_val('n', None) if news is not None: self._got_news(news) def _change_phrase(self) -> None: from bastd.actor.text import Text # If our news is way out of date, lets re-request it; # otherwise, rotate our phrase. assert ba.app.main_menu_last_news_fetch_time is not None if time.time() - ba.app.main_menu_last_news_fetch_time > 600.0: self._fetch_news() self._text = None else: if self._text is not None: if not self._phrases: for phr in self._used_phrases: self._phrases.insert(0, phr) val = self._phrases.pop() if val == '__ACH__': vrmode = app.vr_mode Text(ba.Lstr(resource='nextAchievementsText'), color=((1, 1, 1, 1) if vrmode else (0.95, 0.9, 1, 0.4)), host_only=True, maxwidth=200, position=(-300, -35), h_align=Text.HAlign.RIGHT, transition=Text.Transition.FADE_IN, scale=0.9 if vrmode else 0.7, flatness=1.0 if vrmode else 0.6, shadow=1.0 if vrmode else 0.5, h_attach=Text.HAttach.CENTER, v_attach=Text.VAttach.TOP, transition_delay=1.0, transition_out_delay=self._message_duration ).autoretain() achs = [ a for a in app.achievements if not a.complete ] if achs: ach = achs.pop( random.randrange(min(4, len(achs)))) ach.create_display( -180, -35, 1.0, outdelay=self._message_duration, style='news') if achs: ach = achs.pop( random.randrange(min(8, len(achs)))) ach.create_display( 180, -35, 1.25, outdelay=self._message_duration, style='news') else: spc = self._message_spacing keys = { spc: 0.0, spc + 1.0: 1.0, spc + self._message_duration - 1.0: 1.0, spc + self._message_duration: 0.0 } assert self._text.node ba.animate(self._text.node, 'opacity', keys) # {k: v # for k, v in list(keys.items())}) self._text.node.text = val def _got_news(self, news: str) -> None: # Run this stuff in the context of our activity since we # need to make nodes and stuff.. should fix the serverget # call so it. activity = self._activity() if activity is None or activity.expired: return with ba.Context(activity): self._phrases: List[str] = [] # Show upcoming achievements in non-vr versions # (currently too hard to read in vr). self._used_phrases = ( ['__ACH__'] if not ba.app.vr_mode else []) + [s for s in news.split('<br>\n') if s != ''] self._phrase_change_timer = ba.Timer( (self._message_duration + self._message_spacing), ba.WeakCall(self._change_phrase), repeat=True) scl = 1.2 if (ba.app.ui.uiscale is ba.UIScale.SMALL or ba.app.vr_mode) else 0.8 color2 = ((1, 1, 1, 1) if ba.app.vr_mode else (0.7, 0.65, 0.75, 1.0)) shadow = (1.0 if ba.app.vr_mode else 0.4) self._text = ba.NodeActor( ba.newnode('text', attrs={ 'v_attach': 'top', 'h_attach': 'center', 'h_align': 'center', 'vr_depth': -20, 'shadow': shadow, 'flatness': 0.8, 'v_align': 'top', 'color': color2, 'scale': scl, 'maxwidth': 900.0 / scl, 'position': (0, -10) })) self._change_phrase() if not (app.demo_mode or app.arcade_mode) and not app.toolbar_test: self._news = News(self) # Bring up the last place we were, or start at the main menu otherwise. with ba.Context('ui'): from bastd.ui import specialoffer if bool(False): uicontroller = ba.app.ui.controller assert uicontroller is not None uicontroller.show_main_menu() else: main_menu_location = ba.app.ui.get_main_menu_location() # When coming back from a kiosk-mode game, jump to # the kiosk start screen. if ba.app.demo_mode or ba.app.arcade_mode: # pylint: disable=cyclic-import from bastd.ui.kiosk import KioskWindow ba.app.ui.set_main_menu_window( KioskWindow().get_root_widget()) # ..or in normal cases go back to the main menu else: if main_menu_location == 'Gather': # pylint: disable=cyclic-import from bastd.ui.gather import GatherWindow ba.app.ui.set_main_menu_window( GatherWindow(transition=None).get_root_widget()) elif main_menu_location == 'Watch': # pylint: disable=cyclic-import from bastd.ui.watch import WatchWindow ba.app.ui.set_main_menu_window( WatchWindow(transition=None).get_root_widget()) elif main_menu_location == 'Team Game Select': # pylint: disable=cyclic-import from bastd.ui.playlist.browser import ( PlaylistBrowserWindow) ba.app.ui.set_main_menu_window( PlaylistBrowserWindow( sessiontype=ba.DualTeamSession, transition=None).get_root_widget()) elif main_menu_location == 'Free-for-All Game Select': # pylint: disable=cyclic-import from bastd.ui.playlist.browser import ( PlaylistBrowserWindow) ba.app.ui.set_main_menu_window( PlaylistBrowserWindow( sessiontype=ba.FreeForAllSession, transition=None).get_root_widget()) elif main_menu_location == 'Coop Select': # pylint: disable=cyclic-import from bastd.ui.coop.browser import CoopBrowserWindow ba.app.ui.set_main_menu_window( CoopBrowserWindow( transition=None).get_root_widget()) else: # pylint: disable=cyclic-import from bastd.ui.mainmenu import MainMenuWindow ba.app.ui.set_main_menu_window( MainMenuWindow(transition=None).get_root_widget()) # attempt to show any pending offers immediately. # If that doesn't work, try again in a few seconds # (we may not have heard back from the server) # ..if that doesn't work they'll just have to wait # until the next opportunity. if not specialoffer.show_offer(): def try_again() -> None: if not specialoffer.show_offer(): # Try one last time.. ba.timer(2.0, specialoffer.show_offer, timetype=ba.TimeType.REAL) ba.timer(2.0, try_again, timetype=ba.TimeType.REAL) ba.app.main_menu_did_initial_transition = True