Source code for bascenev1lib.mainmenu

# Released under the MIT License. See LICENSE for details.
#
"""Session and Activity for displaying the main menu bg."""

from __future__ import annotations

import time
import random
import weakref
from typing import TYPE_CHECKING, override

import bascenev1 as bs
import bauiv1 as bui

if TYPE_CHECKING:
    from typing import Any






[docs] class NewsDisplay: """Wrangles news display.""" def __init__(self, activity: bs.Activity): self._valid = True self._message_duration = 10.0 self._message_spacing = 2.0 self._text: bs.NodeActor | None = None self._activity = weakref.ref(activity) self._phrases: list[str] = [] self._used_phrases: list[str] = [] self._phrase_change_timer: bs.Timer | None = None # If we're signed in, fetch news immediately. Otherwise wait # until we are signed in. self._fetch_timer: bs.Timer | None = bs.Timer( 1.0, bs.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: plus = bui.app.plus assert plus is not None if plus.get_v1_account_state() == 'signed_in': self._fetch_news() self._fetch_timer = None def _fetch_news(self) -> None: plus = bui.app.plus assert plus is not None assert bs.app.classic is not None bs.app.classic.main_menu_last_news_fetch_time = time.time() # UPDATE - We now just pull news from MRVs. news = plus.get_v1_account_misc_read_val('n', None) if news is not None: self._got_news(news) def _change_phrase(self) -> None: from bascenev1lib.actor.text import Text app = bs.app assert app.classic is not None # If our news is way out of date, lets re-request it; otherwise, # rotate our phrase. assert app.classic.main_menu_last_news_fetch_time is not None if time.time() - app.classic.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.env.vr Text( bs.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.classic.ach.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 bs.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 activity.context: self._phrases.clear() # Show upcoming achievements in non-vr versions (currently # too hard to read in vr). self._used_phrases = (['__ACH__'] if not bs.app.env.vr else []) + [ s for s in news.split('<br>\n') if s != '' ] self._phrase_change_timer = bs.Timer( (self._message_duration + self._message_spacing), bs.WeakCall(self._change_phrase), repeat=True, ) assert bs.app.classic is not None scl = ( 1.2 if (bs.app.ui_v1.uiscale is bs.UIScale.SMALL or bs.app.env.vr) else 0.8 ) color2 = (1, 1, 1, 1) if bs.app.env.vr else (0.7, 0.65, 0.75, 1.0) shadow = 1.0 if bs.app.env.vr else 0.4 self._text = bs.NodeActor( bs.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()
def _preload1() -> None: """Pre-load some assets a second or two into the main menu. Helps avoid hitches later on. """ for mname in [ 'plasticEyesTransparent', 'playerLineup1Transparent', 'playerLineup2Transparent', 'playerLineup3Transparent', 'playerLineup4Transparent', 'angryComputerTransparent', 'scrollWidgetShort', 'windowBGBlotch', ]: bs.getmesh(mname) for tname in ['playerLineup', 'lock']: bs.gettexture(tname) for tex in [ 'iconRunaround', 'iconOnslaught', 'medalComplete', 'medalBronze', 'medalSilver', 'medalGold', 'characterIconMask', ]: bs.gettexture(tex) bs.gettexture('bg') from bascenev1lib.actor.powerupbox import PowerupBoxFactory PowerupBoxFactory.get() bui.apptimer(0.1, _preload2) def _preload2() -> None: # FIXME: Could integrate these loads with the classes that use them # so they don't have to redundantly call the load # (even if the actual result is cached). for mname in ['powerup', 'powerupSimple']: bs.getmesh(mname) for tname in [ 'powerupBomb', 'powerupSpeed', 'powerupPunch', 'powerupIceBombs', 'powerupStickyBombs', 'powerupShield', 'powerupImpactBombs', 'powerupHealth', ]: bs.gettexture(tname) for sname in [ 'powerup01', 'boxDrop', 'boxingBell', 'scoreHit01', 'scoreHit02', 'dripity', 'spawn', 'gong', ]: bs.getsound(sname) from bascenev1lib.actor.bomb import BombFactory BombFactory.get() bui.apptimer(0.1, _preload3) def _preload3() -> None: from bascenev1lib.actor.spazfactory import SpazFactory for mname in ['bomb', 'bombSticky', 'impactBomb']: bs.getmesh(mname) for tname in [ 'bombColor', 'bombColorIce', 'bombStickyColor', 'impactBombColor', 'impactBombColorLit', ]: bs.gettexture(tname) for sname in ['freeze', 'fuse01', 'activateBeep', 'warnBeep']: bs.getsound(sname) SpazFactory.get() bui.apptimer(0.2, _preload4) def _preload4() -> None: for tname in ['bar', 'meter', 'null', 'flagColor', 'achievementOutline']: bs.gettexture(tname) for mname in ['frameInset', 'meterTransparent', 'achievementOutline']: bs.getmesh(mname) for sname in ['metalHit', 'metalSkid', 'refWhistle', 'achievement']: bs.getsound(sname) from bascenev1lib.actor.flag import FlagFactory FlagFactory.get()