def start(self) -> None: """Start the timer.""" tval = ba.time(timeformat=ba.TimeFormat.MILLISECONDS) assert isinstance(tval, int) self._starttime = tval self.inputnode.time1 = self._starttime ba.sharedobj('globals').connectattr('time', self.inputnode, 'time2')
def __init__(self) -> None: """Instantiate a PowerupBoxFactory. You shouldn't need to do this; call ba.Powerup.get_factory() to get a shared instance. """ from ba.internal import get_default_powerup_distribution self._lastpoweruptype: Optional[str] = None self.model = ba.getmodel('powerup') self.model_simple = ba.getmodel('powerupSimple') self.tex_bomb = ba.gettexture('powerupBomb') self.tex_punch = ba.gettexture('powerupPunch') self.tex_ice_bombs = ba.gettexture('powerupIceBombs') self.tex_sticky_bombs = ba.gettexture('powerupStickyBombs') self.tex_shield = ba.gettexture('powerupShield') self.tex_impact_bombs = ba.gettexture('powerupImpactBombs') self.tex_health = ba.gettexture('powerupHealth') self.tex_land_mines = ba.gettexture('powerupLandMines') self.tex_curse = ba.gettexture('powerupCurse') self.health_powerup_sound = ba.getsound('healthPowerup') self.powerup_sound = ba.getsound('powerup01') self.powerdown_sound = ba.getsound('powerdown01') self.drop_sound = ba.getsound('boxDrop') # Material for powerups. self.powerup_material = ba.Material() # Material for anyone wanting to accept powerups. self.powerup_accept_material = ba.Material() # Pass a powerup-touched message to applicable stuff. self.powerup_material.add_actions( conditions=('they_have_material', self.powerup_accept_material), actions=(('modify_part_collision', 'collide', True), ('modify_part_collision', 'physical', False), ('message', 'our_node', 'at_connect', _TouchedMessage()))) # We don't wanna be picked up. self.powerup_material.add_actions( conditions=('they_have_material', ba.sharedobj('pickup_material')), actions=('modify_part_collision', 'collide', False)) self.powerup_material.add_actions( conditions=('they_have_material', ba.sharedobj('footing_material')), actions=('impact_sound', self.drop_sound, 0.5, 0.1)) self._powerupdist: List[str] = [] for powerup, freq in get_default_powerup_distribution(): for _i in range(int(freq)): self._powerupdist.append(powerup)
def __init__(self, settings: Dict[str, Any]): from bastd.actor.scoreboard import Scoreboard from bastd.actor import powerupbox super().__init__(settings) self._scoreboard = Scoreboard() self._cheer_sound = ba.getsound("cheer") self._chant_sound = ba.getsound("crowdChant") self._foghorn_sound = ba.getsound("foghorn") self._swipsound = ba.getsound("swip") self._whistle_sound = ba.getsound("refWhistle") self.puck_model = ba.getmodel("puck") self.puck_tex = ba.gettexture("puckColor") self._puck_sound = ba.getsound("metalHit") self.puck_material = ba.Material() self.puck_material.add_actions(actions=(("modify_part_collision", "friction", 0.5))) self.puck_material.add_actions( conditions=("they_have_material", ba.sharedobj('pickup_material')), actions=("modify_part_collision", "collide", False)) self.puck_material.add_actions( conditions=(("we_are_younger_than", 100), 'and', ("they_have_material", ba.sharedobj('object_material'))), actions=("modify_node_collision", "collide", False)) self.puck_material.add_actions( conditions=("they_have_material", ba.sharedobj('footing_material')), actions=("impact_sound", self._puck_sound, 0.2, 5)) # Keep track of which player last touched the puck self.puck_material.add_actions( conditions=("they_have_material", ba.sharedobj('player_material')), actions=(("call", "at_connect", self._handle_puck_player_collide), )) # We want the puck to kill powerups; not get stopped by them self.puck_material.add_actions( conditions=("they_have_material", powerupbox.get_factory().powerup_material), actions=(("modify_part_collision", "physical", False), ("message", "their_node", "at_connect", ba.DieMessage()))) self._score_region_material = ba.Material() self._score_region_material.add_actions( conditions=("they_have_material", self.puck_material), actions=(("modify_part_collision", "collide", True), ("modify_part_collision", "physical", False), ("call", "at_connect", self._handle_score))) self._puck_spawn_pos: Optional[Sequence[float]] = None self._score_regions: Optional[List[ba.NodeActor]] = None self._puck: Optional[Puck] = None
def on_begin(self) -> None: super().on_begin() self.setup_standard_time_limit(self._time_limit) self.setup_standard_powerup_drops() self._flag_spawn_pos = self.map.get_flag_position(None) self.project_flag_stand(self._flag_spawn_pos) self._set_chosen_one_player(None) pos = self._flag_spawn_pos ba.timer(1.0, call=self._tick, repeat=True) mat = self._reset_region_material = ba.Material() mat.add_actions(conditions=('they_have_material', ba.sharedobj('player_material')), actions=(('modify_part_collision', 'collide', True), ('modify_part_collision', 'physical', False), ('call', 'at_connect', ba.WeakCall(self._handle_reset_collide)))) self._reset_region = ba.newnode('region', attrs={ 'position': (pos[0], pos[1] + 0.75, pos[2]), 'scale': (0.5, 0.5, 0.5), 'type': 'sphere', 'materials': [mat] })
def __init__(self, settings: Dict[str, Any]): from bastd.actor.scoreboard import Scoreboard super().__init__(settings) self._scoreboard = Scoreboard() self._swipsound = ba.getsound('swip') self._tick_sound = ba.getsound('tick') self._countdownsounds = { 10: ba.getsound('announceTen'), 9: ba.getsound('announceNine'), 8: ba.getsound('announceEight'), 7: ba.getsound('announceSeven'), 6: ba.getsound('announceSix'), 5: ba.getsound('announceFive'), 4: ba.getsound('announceFour'), 3: ba.getsound('announceThree'), 2: ba.getsound('announceTwo'), 1: ba.getsound('announceOne') } self._flag_pos: Optional[Sequence[float]] = None self._flag_state: Optional[int] = None self._flag: Optional[stdflag.Flag] = None self._flag_light: Optional[ba.Node] = None self._scoring_team: Optional[ReferenceType[ba.Team]] = None self._flag_region_material = ba.Material() self._flag_region_material.add_actions( conditions=('they_have_material', ba.sharedobj('player_material')), actions=(('modify_part_collision', 'collide', True), ('modify_part_collision', 'physical', False), ('call', 'at_connect', ba.Call(self._handle_player_flag_region_collide, True)), ('call', 'at_disconnect', ba.Call(self._handle_player_flag_region_collide, False))))
def on_begin(self) -> None: super().on_begin() self.setup_standard_time_limit(self.settings['Time Limit']) self.setup_standard_powerup_drops() self._flag_pos = self.map.get_flag_position(None) ba.timer(1.0, self._tick, repeat=True) self._flag_state = self.FLAG_NEW self.project_flag_stand(self._flag_pos) self._flag = stdflag.Flag(position=self._flag_pos, touchable=False, color=(1, 1, 1)) self._flag_light = ba.newnode('light', attrs={ 'position': self._flag_pos, 'intensity': 0.2, 'height_attenuated': False, 'radius': 0.4, 'color': (0.2, 0.2, 0.2) }) # Flag region. flagmats = [ self._flag_region_material, ba.sharedobj('region_material') ] ba.newnode('region', attrs={ 'position': self._flag_pos, 'scale': (1.8, 1.8, 1.8), 'type': 'sphere', 'materials': flagmats }) self._update_flag_state()
def __init__(self, position: Sequence[float] = (0.0, 1.0, 0.0)): super().__init__() activity = self.getactivity() # Spawn just above the provided point. self._spawn_pos = (position[0], position[1] + 1.0, position[2]) self.last_players_to_touch: Dict[int, ba.Player] = {} self.scored = False assert activity is not None assert isinstance(activity, HockeyGame) pmats = [ba.sharedobj('object_material'), activity.puck_material] self.node = ba.newnode("prop", delegate=self, attrs={ 'model': activity.puck_model, 'color_texture': activity.puck_tex, 'body': 'puck', 'reflection': 'soft', 'reflection_scale': [0.2], 'shadow_size': 1.0, 'is_area_of_interest': True, 'position': self._spawn_pos, 'materials': pmats }) ba.animate(self.node, "model_scale", {0: 0, 0.2: 1.3, 0.26: 1})
def __init__(self, position: Tuple[float, float, float] = (0.0, 1.0, 0.0)): super().__init__() activity = self.activity assert isinstance(activity, EasterEggHuntGame) # Spawn just above the provided point. self._spawn_pos = (position[0], position[1] + 1.0, position[2]) ctex = (activity.egg_tex_1, activity.egg_tex_2, activity.egg_tex_3)[random.randrange(3)] mats = [ba.sharedobj('object_material'), activity.egg_material] self.node = ba.newnode("prop", delegate=self, attrs={ 'model': activity.egg_model, 'color_texture': ctex, 'body': 'capsule', 'reflection': 'soft', 'model_scale': 0.5, 'bodyScale': 0.6, 'density': 4.0, 'reflection_scale': [0.15], 'shadow_size': 0.6, 'position': self._spawn_pos, 'materials': mats })
def start(self) -> None: """Start the timer.""" globalsnode = ba.sharedobj('globals') globalsnode.connectattr('time', self.inputnode, 'time1') self.inputnode.time2 = (globalsnode.time + (self._timeremaining + 1) * 1000) self._timer = ba.Timer(1.0, self._update, repeat=True)
def on_transition_in(self) -> None: super().on_transition_in() pts = self.map.get_def_points('race_point') mat = self.race_region_material = ba.Material() mat.add_actions(conditions=('they_have_material', ba.sharedobj('player_material')), actions=(('modify_part_collision', 'collide', True), ('modify_part_collision', 'physical', False), ('call', 'at_connect', self._handle_race_point_collide))) for rpt in pts: self._regions.append(RaceRegion(rpt, len(self._regions)))
def __init__(self, settings: Dict[str, Any]): from bastd.actor.scoreboard import Scoreboard super().__init__(settings) if self.settings_raw['Epic Mode']: self.slow_motion = True self._scoreboard = Scoreboard() self._score_sound = ba.getsound('score') self._swipsound = ba.getsound('swip') self._extraflagmat = ba.Material() self._flags: List[ConquestFlag] = [] # We want flags to tell us they've been hit but not react physically. self._extraflagmat.add_actions( conditions=('they_have_material', ba.sharedobj('player_material')), actions=(('modify_part_collision', 'collide', True), ('call', 'at_connect', self._handle_flag_player_collide)))
def on_begin(self) -> None: from bastd.actor.flag import Flag super().on_begin() self.setup_standard_time_limit(self.settings_raw['Time Limit']) self.setup_standard_powerup_drops() for team in self.teams: mat = self._base_region_materials[team.get_id()] = ba.Material() mat.add_actions(conditions=('they_have_material', ba.sharedobj('player_material')), actions=(('modify_part_collision', 'collide', True), ('modify_part_collision', 'physical', False), ('call', 'at_connect', ba.Call(self._handle_base_collide, team)))) # Create a score region and flag for each team. for team in self.teams: team.gamedata['base_pos'] = self.map.get_flag_position( team.get_id()) ba.newnode('light', attrs={ 'position': team.gamedata['base_pos'], 'intensity': 0.6, 'height_attenuated': False, 'volume_intensity_scale': 0.1, 'radius': 0.1, 'color': team.color }) self.project_flag_stand(team.gamedata['base_pos']) team.gamedata['flag'] = Flag(touchable=False, position=team.gamedata['base_pos'], color=team.color) basepos = team.gamedata['base_pos'] ba.newnode('region', owner=team.gamedata['flag'].node, attrs={ 'position': (basepos[0], basepos[1] + 0.75, basepos[2]), 'scale': (0.5, 0.5, 0.5), 'type': 'sphere', 'materials': [self._base_region_materials[team.get_id()]] })
def __init__(self) -> None: """Instantiate a FlagFactory. You shouldn't need to do this; call bastd.actor.flag.get_factory() to get a shared instance. """ self.flagmaterial = ba.Material() self.flagmaterial.add_actions( conditions=(('we_are_younger_than', 100), 'and', ('they_have_material', ba.sharedobj('object_material'))), actions=('modify_node_collision', 'collide', False)) self.flagmaterial.add_actions( conditions=('they_have_material', ba.sharedobj('footing_material')), actions=(('message', 'our_node', 'at_connect', 'footing', 1), ('message', 'our_node', 'at_disconnect', 'footing', -1))) self.impact_sound = ba.getsound('metalHit') self.skid_sound = ba.getsound('metalSkid') self.flagmaterial.add_actions( conditions=('they_have_material', ba.sharedobj('footing_material')), actions=(('impact_sound', self.impact_sound, 2, 5), ('skid_sound', self.skid_sound, 2, 5))) self.no_hit_material = ba.Material() self.no_hit_material.add_actions( conditions=(('they_have_material', ba.sharedobj('pickup_material')), 'or', ('they_have_material', ba.sharedobj('attack_material'))), actions=('modify_part_collision', 'collide', False)) # We also don't want anything moving it. self.no_hit_material.add_actions( conditions=(('they_have_material', ba.sharedobj('object_material')), 'or', ('they_dont_have_material', ba.sharedobj('footing_material'))), actions=(('modify_part_collision', 'collide', False), ('modify_part_collision', 'physical', False))) self.flag_texture = ba.gettexture('flagColor')
def __init__(self, settings: Dict[str, Any]): from bastd.actor.scoreboard import Scoreboard super().__init__(settings) self._last_player_death_time = None self._scoreboard = Scoreboard() self.egg_model = ba.getmodel('egg') self.egg_tex_1 = ba.gettexture('eggTex1') self.egg_tex_2 = ba.gettexture('eggTex2') self.egg_tex_3 = ba.gettexture('eggTex3') self._collect_sound = ba.getsound('powerup01') self._pro_mode = settings.get('Pro Mode', False) self._max_eggs = 1.0 self.egg_material = ba.Material() self.egg_material.add_actions( conditions=("they_have_material", ba.sharedobj('player_material')), actions=(("call", "at_connect", self._on_egg_player_collide), )) self._eggs: List[Egg] = [] self._update_timer: Optional[ba.Timer] = None self._countdown: Optional[OnScreenCountdown] = None self._bots: Optional[spazbot.BotSet] = None
def __init__(self, settings: Dict[str, Any]): from bastd.actor.scoreboard import Scoreboard super().__init__(settings) self._scoreboard = Scoreboard() self._score_sound = ba.getsound('score') self._swipsound = ba.getsound('swip') self._extraflagmat = ba.Material() self._flags: List[ConquestFlag] = [] self._epic_mode = bool(settings['Epic Mode']) self._time_limit = float(settings['Time Limit']) # Base class overrides. self.slow_motion = self._epic_mode self.default_music = (ba.MusicType.EPIC if self._epic_mode else ba.MusicType.GRAND_ROMP) # We want flags to tell us they've been hit but not react physically. self._extraflagmat.add_actions( conditions=('they_have_material', ba.sharedobj('player_material')), actions=(('modify_part_collision', 'collide', True), ('call', 'at_connect', self._handle_flag_player_collide)))
def __init__(self, settings: Dict[str, Any]): super().__init__(settings) self._last_player_death_time = None self._scoreboard = Scoreboard() self.egg_model = ba.getmodel('egg') self.egg_tex_1 = ba.gettexture('eggTex1') self.egg_tex_2 = ba.gettexture('eggTex2') self.egg_tex_3 = ba.gettexture('eggTex3') self._collect_sound = ba.getsound('powerup01') self._pro_mode = settings.get('Pro Mode', False) self._max_eggs = 1.0 self.egg_material = ba.Material() self.egg_material.add_actions( conditions=('they_have_material', ba.sharedobj('player_material')), actions=(('call', 'at_connect', self._on_egg_player_collide), )) self._eggs: List[Egg] = [] self._update_timer: Optional[ba.Timer] = None self._countdown: Optional[OnScreenCountdown] = None self._bots: Optional[BotSet] = None # Base class overrides self.default_music = ba.MusicType.FORWARD_MARCH
def create_team(self, sessionteam: ba.SessionTeam) -> Team: base_pos = self.map.get_flag_position(sessionteam.id) ba.newnode('light', attrs={ 'position': base_pos, 'intensity': 0.6, 'height_attenuated': False, 'volume_intensity_scale': 0.1, 'radius': 0.1, 'color': sessionteam.color }) self.project_flag_stand(base_pos) flag = Flag(touchable=False, position=base_pos, color=sessionteam.color) team = Team(base_pos=base_pos, flag=flag) mat = self._base_region_materials[sessionteam.id] = ba.Material() mat.add_actions( conditions=('they_have_material', ba.sharedobj('player_material')), actions=( ('modify_part_collision', 'collide', True), ('modify_part_collision', 'physical', False), ('call', 'at_connect', ba.Call(self._handle_base_collide, team)), ), ) ba.newnode( 'region', owner=flag.node, attrs={ 'position': (base_pos[0], base_pos[1] + 0.75, base_pos[2]), 'scale': (0.5, 0.5, 0.5), 'type': 'sphere', 'materials': [self._base_region_materials[sessionteam.id]] }) return team
def __init__(self, settings: Dict[str, Any]): super().__init__(settings) self._scoreboard = Scoreboard() self._swipsound = ba.getsound('swip') self._tick_sound = ba.getsound('tick') self._countdownsounds = { 10: ba.getsound('announceTen'), 9: ba.getsound('announceNine'), 8: ba.getsound('announceEight'), 7: ba.getsound('announceSeven'), 6: ba.getsound('announceSix'), 5: ba.getsound('announceFive'), 4: ba.getsound('announceFour'), 3: ba.getsound('announceThree'), 2: ba.getsound('announceTwo'), 1: ba.getsound('announceOne') } self._flag_pos: Optional[Sequence[float]] = None self._flag_state: Optional[FlagState] = None self._flag: Optional[Flag] = None self._flag_light: Optional[ba.Node] = None self._scoring_team: Optional[ReferenceType[Team]] = None self._hold_time = int(settings['Hold Time']) self._time_limit = float(settings['Time Limit']) self._flag_region_material = ba.Material() self._flag_region_material.add_actions( conditions=('they_have_material', ba.sharedobj('player_material')), actions=( ('modify_part_collision', 'collide', True), ('modify_part_collision', 'physical', False), ('call', 'at_connect', ba.Call(self._handle_player_flag_region_collide, True)), ('call', 'at_disconnect', ba.Call(self._handle_player_flag_region_collide, False)), )) # Base class overrides. self.default_music = ba.MusicType.SCARY
def __init__(self, position: Sequence[float] = (0.0, 1.0, 0.0), velocity: Sequence[float] = (0.0, 0.0, 0.0), bomb_type: str = 'normal', blast_radius: float = 2.0, source_player: ba.Player = None, owner: ba.Node = None): """Create a new Bomb. bomb_type can be 'ice','impact','land_mine','normal','sticky', or 'tnt'. Note that for impact or land_mine bombs you have to call arm() before they will go off. """ super().__init__() factory = get_factory() if bomb_type not in ('ice', 'impact', 'land_mine', 'normal', 'sticky', 'tnt'): raise ValueError('invalid bomb type: ' + bomb_type) self.bomb_type = bomb_type self._exploded = False self.texture_sequence: Optional[ba.Node] = None if self.bomb_type == 'sticky': self._last_sticky_sound_time = 0.0 self.blast_radius = blast_radius if self.bomb_type == 'ice': self.blast_radius *= 1.2 elif self.bomb_type == 'impact': self.blast_radius *= 0.7 elif self.bomb_type == 'land_mine': self.blast_radius *= 0.7 elif self.bomb_type == 'tnt': self.blast_radius *= 1.45 self._explode_callbacks: List[Callable[[Bomb, Blast], Any]] = [] # the player this came from self.source_player = source_player # by default our hit type/subtype is our own, but we pick up types of # whoever sets us off so we know what caused a chain reaction self.hit_type = 'explosion' self.hit_subtype = self.bomb_type # if no owner was provided, use an unconnected node ref # (nevermind; trying to use None in these type cases instead) # if owner is None: # owner = ba.Node(None) # the node this came from self.owner = owner # adding footing-materials to things can screw up jumping and flying # since players carrying those things # and thus touching footing objects will think they're on solid # ground.. perhaps we don't wanna add this even in the tnt case?.. materials: Tuple[ba.Material, ...] if self.bomb_type == 'tnt': materials = (factory.bomb_material, ba.sharedobj('footing_material'), ba.sharedobj('object_material')) else: materials = (factory.bomb_material, ba.sharedobj('object_material')) if self.bomb_type == 'impact': materials = materials + (factory.impact_blast_material, ) elif self.bomb_type == 'land_mine': materials = materials + (factory.land_mine_no_explode_material, ) if self.bomb_type == 'sticky': materials = materials + (factory.sticky_material, ) else: materials = materials + (factory.normal_sound_material, ) if self.bomb_type == 'land_mine': fuse_time = None self.node = ba.newnode('prop', delegate=self, attrs={ 'position': position, 'velocity': velocity, 'model': factory.land_mine_model, 'light_model': factory.land_mine_model, 'body': 'landMine', 'shadow_size': 0.44, 'color_texture': factory.land_mine_tex, 'reflection': 'powerup', 'reflection_scale': [1.0], 'materials': materials }) elif self.bomb_type == 'tnt': fuse_time = None self.node = ba.newnode('prop', delegate=self, attrs={ 'position': position, 'velocity': velocity, 'model': factory.tnt_model, 'light_model': factory.tnt_model, 'body': 'crate', 'shadow_size': 0.5, 'color_texture': factory.tnt_tex, 'reflection': 'soft', 'reflection_scale': [0.23], 'materials': materials }) elif self.bomb_type == 'impact': fuse_time = 20.0 self.node = ba.newnode('prop', delegate=self, attrs={ 'position': position, 'velocity': velocity, 'body': 'sphere', 'model': factory.impact_bomb_model, 'shadow_size': 0.3, 'color_texture': factory.impact_tex, 'reflection': 'powerup', 'reflection_scale': [1.5], 'materials': materials }) self.arm_timer = ba.Timer( 0.2, ba.WeakCall(self.handlemessage, ArmMessage())) self.warn_timer = ba.Timer( fuse_time - 1.7, ba.WeakCall(self.handlemessage, WarnMessage())) else: fuse_time = 3.0 if self.bomb_type == 'sticky': sticky = True model = factory.sticky_bomb_model rtype = 'sharper' rscale = 1.8 else: sticky = False model = factory.bomb_model rtype = 'sharper' rscale = 1.8 if self.bomb_type == 'ice': tex = factory.ice_tex elif self.bomb_type == 'sticky': tex = factory.sticky_tex else: tex = factory.regular_tex self.node = ba.newnode('bomb', delegate=self, attrs={ 'position': position, 'velocity': velocity, 'model': model, 'shadow_size': 0.3, 'color_texture': tex, 'sticky': sticky, 'owner': owner, 'reflection': rtype, 'reflection_scale': [rscale], 'materials': materials }) sound = ba.newnode('sound', owner=self.node, attrs={ 'sound': factory.fuse_sound, 'volume': 0.25 }) self.node.connectattr('position', sound, 'position') ba.animate(self.node, 'fuse_length', {0.0: 1.0, fuse_time: 0.0}) # Light the fuse!!! if self.bomb_type not in ('land_mine', 'tnt'): assert fuse_time is not None ba.timer(fuse_time, ba.WeakCall(self.handlemessage, ExplodeMessage())) ba.animate(self.node, 'model_scale', {0: 0, 0.2: 1.3, 0.26: 1})
def __init__(self, position: Sequence[float] = (0.0, 1.0, 0.0), velocity: Sequence[float] = (0.0, 0.0, 0.0), blast_radius: float = 2.0, blast_type: str = 'normal', source_player: ba.Player = None, hit_type: str = 'explosion', hit_subtype: str = 'normal'): """Instantiate with given values.""" # bah; get off my lawn! # pylint: disable=too-many-locals # pylint: disable=too-many-statements super().__init__() factory = get_factory() self.blast_type = blast_type self.source_player = source_player self.hit_type = hit_type self.hit_subtype = hit_subtype self.radius = blast_radius # set our position a bit lower so we throw more things upward rmats = (factory.blast_material, ba.sharedobj('attack_material')) self.node = ba.newnode('region', delegate=self, attrs={ 'position': (position[0], position[1] - 0.1, position[2]), 'scale': (self.radius, self.radius, self.radius), 'type': 'sphere', 'materials': rmats }) ba.timer(0.05, self.node.delete) # throw in an explosion and flash evel = (velocity[0], max(-1.0, velocity[1]), velocity[2]) explosion = ba.newnode('explosion', attrs={ 'position': position, 'velocity': evel, 'radius': self.radius, 'big': (self.blast_type == 'tnt') }) if self.blast_type == 'ice': explosion.color = (0, 0.05, 0.4) ba.timer(1.0, explosion.delete) if self.blast_type != 'ice': ba.emitfx(position=position, velocity=velocity, count=int(1.0 + random.random() * 4), emit_type='tendrils', tendril_type='thin_smoke') ba.emitfx(position=position, velocity=velocity, count=int(4.0 + random.random() * 4), emit_type='tendrils', tendril_type='ice' if self.blast_type == 'ice' else 'smoke') ba.emitfx(position=position, emit_type='distortion', spread=1.0 if self.blast_type == 'tnt' else 2.0) # and emit some shrapnel.. if self.blast_type == 'ice': def emit() -> None: ba.emitfx(position=position, velocity=velocity, count=30, spread=2.0, scale=0.4, chunk_type='ice', emit_type='stickers') # looks better if we delay a bit ba.timer(0.05, emit) elif self.blast_type == 'sticky': def emit() -> None: ba.emitfx(position=position, velocity=velocity, count=int(4.0 + random.random() * 8), spread=0.7, chunk_type='slime') ba.emitfx(position=position, velocity=velocity, count=int(4.0 + random.random() * 8), scale=0.5, spread=0.7, chunk_type='slime') ba.emitfx(position=position, velocity=velocity, count=15, scale=0.6, chunk_type='slime', emit_type='stickers') ba.emitfx(position=position, velocity=velocity, count=20, scale=0.7, chunk_type='spark', emit_type='stickers') ba.emitfx(position=position, velocity=velocity, count=int(6.0 + random.random() * 12), scale=0.8, spread=1.5, chunk_type='spark') # looks better if we delay a bit ba.timer(0.05, emit) elif self.blast_type == 'impact': # regular bomb shrapnel def emit() -> None: ba.emitfx(position=position, velocity=velocity, count=int(4.0 + random.random() * 8), scale=0.8, chunk_type='metal') ba.emitfx(position=position, velocity=velocity, count=int(4.0 + random.random() * 8), scale=0.4, chunk_type='metal') ba.emitfx(position=position, velocity=velocity, count=20, scale=0.7, chunk_type='spark', emit_type='stickers') ba.emitfx(position=position, velocity=velocity, count=int(8.0 + random.random() * 15), scale=0.8, spread=1.5, chunk_type='spark') # looks better if we delay a bit ba.timer(0.05, emit) else: # regular or land mine bomb shrapnel def emit() -> None: if self.blast_type != 'tnt': ba.emitfx(position=position, velocity=velocity, count=int(4.0 + random.random() * 8), chunk_type='rock') ba.emitfx(position=position, velocity=velocity, count=int(4.0 + random.random() * 8), scale=0.5, chunk_type='rock') ba.emitfx(position=position, velocity=velocity, count=30, scale=1.0 if self.blast_type == 'tnt' else 0.7, chunk_type='spark', emit_type='stickers') ba.emitfx(position=position, velocity=velocity, count=int(18.0 + random.random() * 20), scale=1.0 if self.blast_type == 'tnt' else 0.8, spread=1.5, chunk_type='spark') # tnt throws splintery chunks if self.blast_type == 'tnt': def emit_splinters() -> None: ba.emitfx(position=position, velocity=velocity, count=int(20.0 + random.random() * 25), scale=0.8, spread=1.0, chunk_type='splinter') ba.timer(0.01, emit_splinters) # every now and then do a sparky one if self.blast_type == 'tnt' or random.random() < 0.1: def emit_extra_sparks() -> None: ba.emitfx(position=position, velocity=velocity, count=int(10.0 + random.random() * 20), scale=0.8, spread=1.5, chunk_type='spark') ba.timer(0.02, emit_extra_sparks) # looks better if we delay a bit ba.timer(0.05, emit) lcolor = ((0.6, 0.6, 1.0) if self.blast_type == 'ice' else (1, 0.3, 0.1)) light = ba.newnode('light', attrs={ 'position': position, 'volume_intensity_scale': 10.0, 'color': lcolor }) scl = random.uniform(0.6, 0.9) scorch_radius = light_radius = self.radius if self.blast_type == 'tnt': light_radius *= 1.4 scorch_radius *= 1.15 scl *= 3.0 iscale = 1.6 ba.animate( light, 'intensity', { 0: 2.0 * iscale, scl * 0.02: 0.1 * iscale, scl * 0.025: 0.2 * iscale, scl * 0.05: 17.0 * iscale, scl * 0.06: 5.0 * iscale, scl * 0.08: 4.0 * iscale, scl * 0.2: 0.6 * iscale, scl * 2.0: 0.00 * iscale, scl * 3.0: 0.0 }) ba.animate( light, 'radius', { 0: light_radius * 0.2, scl * 0.05: light_radius * 0.55, scl * 0.1: light_radius * 0.3, scl * 0.3: light_radius * 0.15, scl * 1.0: light_radius * 0.05 }) ba.timer(scl * 3.0, light.delete) # make a scorch that fades over time scorch = ba.newnode('scorch', attrs={ 'position': position, 'size': scorch_radius * 0.5, 'big': (self.blast_type == 'tnt') }) if self.blast_type == 'ice': scorch.color = (1, 1, 1.5) ba.animate(scorch, 'presence', {3.000: 1, 13.000: 0}) ba.timer(13.0, scorch.delete) if self.blast_type == 'ice': ba.playsound(factory.hiss_sound, position=light.position) lpos = light.position ba.playsound(factory.random_explode_sound(), position=lpos) ba.playsound(factory.debris_fall_sound, position=lpos) ba.camerashake(intensity=5.0 if self.blast_type == 'tnt' else 1.0) # tnt is more epic.. if self.blast_type == 'tnt': ba.playsound(factory.random_explode_sound(), position=lpos) def _extra_boom() -> None: ba.playsound(factory.random_explode_sound(), position=lpos) ba.timer(0.25, _extra_boom) def _extra_debris_sound() -> None: ba.playsound(factory.debris_fall_sound, position=lpos) ba.playsound(factory.wood_debris_fall_sound, position=lpos) ba.timer(0.4, _extra_debris_sound)
def __init__(self) -> None: """Instantiate a BombFactory. You shouldn't need to do this; call bastd.actor.bomb.get_factory() to get a shared instance. """ self.bomb_model = ba.getmodel('bomb') self.sticky_bomb_model = ba.getmodel('bombSticky') self.impact_bomb_model = ba.getmodel('impactBomb') self.land_mine_model = ba.getmodel('landMine') self.tnt_model = ba.getmodel('tnt') self.regular_tex = ba.gettexture('bombColor') self.ice_tex = ba.gettexture('bombColorIce') self.sticky_tex = ba.gettexture('bombStickyColor') self.impact_tex = ba.gettexture('impactBombColor') self.impact_lit_tex = ba.gettexture('impactBombColorLit') self.land_mine_tex = ba.gettexture('landMine') self.land_mine_lit_tex = ba.gettexture('landMineLit') self.tnt_tex = ba.gettexture('tnt') self.hiss_sound = ba.getsound('hiss') self.debris_fall_sound = ba.getsound('debrisFall') self.wood_debris_fall_sound = ba.getsound('woodDebrisFall') self.explode_sounds = (ba.getsound('explosion01'), ba.getsound('explosion02'), ba.getsound('explosion03'), ba.getsound('explosion04'), ba.getsound('explosion05')) self.freeze_sound = ba.getsound('freeze') self.fuse_sound = ba.getsound('fuse01') self.activate_sound = ba.getsound('activateBeep') self.warn_sound = ba.getsound('warnBeep') # set up our material so new bombs don't collide with objects # that they are initially overlapping self.bomb_material = ba.Material() self.normal_sound_material = ba.Material() self.sticky_material = ba.Material() self.bomb_material.add_actions( conditions=((('we_are_younger_than', 100), 'or', ('they_are_younger_than', 100)), 'and', ('they_have_material', ba.sharedobj('object_material'))), actions=('modify_node_collision', 'collide', False)) # we want pickup materials to always hit us even if we're currently not # colliding with their node (generally due to the above rule) self.bomb_material.add_actions( conditions=('they_have_material', ba.sharedobj('pickup_material')), actions=('modify_part_collision', 'use_node_collide', False)) self.bomb_material.add_actions(actions=('modify_part_collision', 'friction', 0.3)) self.land_mine_no_explode_material = ba.Material() self.land_mine_blast_material = ba.Material() self.land_mine_blast_material.add_actions( conditions=(('we_are_older_than', 200), 'and', ('they_are_older_than', 200), 'and', ('eval_colliding', ), 'and', (('they_dont_have_material', self.land_mine_no_explode_material), 'and', (('they_have_material', ba.sharedobj('object_material')), 'or', ('they_have_material', ba.sharedobj('player_material'))))), actions=('message', 'our_node', 'at_connect', ImpactMessage())) self.impact_blast_material = ba.Material() self.impact_blast_material.add_actions( conditions=(('we_are_older_than', 200), 'and', ('they_are_older_than', 200), 'and', ('eval_colliding', ), 'and', (('they_have_material', ba.sharedobj('footing_material')), 'or', ('they_have_material', ba.sharedobj('object_material')))), actions=('message', 'our_node', 'at_connect', ImpactMessage())) self.blast_material = ba.Material() self.blast_material.add_actions( conditions=(('they_have_material', ba.sharedobj('object_material'))), actions=(('modify_part_collision', 'collide', True), ('modify_part_collision', 'physical', False), ('message', 'our_node', 'at_connect', ExplodeHitMessage()))) self.dink_sounds = (ba.getsound('bombDrop01'), ba.getsound('bombDrop02')) self.sticky_impact_sound = ba.getsound('stickyImpact') self.roll_sound = ba.getsound('bombRoll01') # collision sounds self.normal_sound_material.add_actions( conditions=('they_have_material', ba.sharedobj('footing_material')), actions=(('impact_sound', self.dink_sounds, 2, 0.8), ('roll_sound', self.roll_sound, 3, 6))) self.sticky_material.add_actions(actions=(('modify_part_collision', 'stiffness', 0.1), ('modify_part_collision', 'damping', 1.0))) self.sticky_material.add_actions( conditions=(('they_have_material', ba.sharedobj('player_material')), 'or', ('they_have_material', ba.sharedobj('footing_material'))), actions=('message', 'our_node', 'at_connect', SplatMessage()))
def __init__(self, position: Sequence[float] = (0.0, 1.0, 0.0), poweruptype: str = 'triple_bombs', expire: bool = True): """Create a powerup-box of the requested type at the given position. see ba.Powerup.poweruptype for valid type strings. """ super().__init__() factory = get_factory() self.poweruptype = poweruptype self._powersgiven = False if poweruptype == 'triple_bombs': tex = factory.tex_bomb elif poweruptype == 'punch': tex = factory.tex_punch elif poweruptype == 'ice_bombs': tex = factory.tex_ice_bombs elif poweruptype == 'impact_bombs': tex = factory.tex_impact_bombs elif poweruptype == 'land_mines': tex = factory.tex_land_mines elif poweruptype == 'sticky_bombs': tex = factory.tex_sticky_bombs elif poweruptype == 'shield': tex = factory.tex_shield elif poweruptype == 'health': tex = factory.tex_health elif poweruptype == 'curse': tex = factory.tex_curse else: raise ValueError('invalid poweruptype: ' + str(poweruptype)) if len(position) != 3: raise ValueError('expected 3 floats for position') self.node = ba.newnode( 'prop', delegate=self, attrs={ 'body': 'box', 'position': position, 'model': factory.model, 'light_model': factory.model_simple, 'shadow_size': 0.5, 'color_texture': tex, 'reflection': 'powerup', 'reflection_scale': [1.0], 'materials': (factory.powerup_material, ba.sharedobj('object_material')) }) # yapf: disable # Animate in. curve = ba.animate(self.node, 'model_scale', {0: 0, 0.14: 1.6, 0.2: 1}) ba.timer(0.2, curve.delete) if expire: ba.timer(DEFAULT_POWERUP_INTERVAL - 2.5, ba.WeakCall(self._start_flashing)) ba.timer(DEFAULT_POWERUP_INTERVAL - 1.0, ba.WeakCall(self.handlemessage, ba.DieMessage()))
def __init__(self, position: Sequence[float] = (0.0, 1.0, 0.0), color: Sequence[float] = (1.0, 1.0, 1.0), materials: Sequence[ba.Material] = None, touchable: bool = True, dropped_timeout: int = None): """Instantiate a flag. If 'touchable' is False, the flag will only touch terrain; useful for things like king-of-the-hill where players should not be moving the flag around. 'materials can be a list of extra ba.Materials to apply to the flag. If 'dropped_timeout' is provided (in seconds), the flag will die after remaining untouched for that long once it has been moved from its initial position. """ super().__init__() self._initial_position: Optional[Sequence[float]] = None self._has_moved = False factory = get_factory() if materials is None: materials = [] elif not isinstance(materials, list): # In case they passed a tuple or whatnot. materials = list(materials) if not touchable: materials = [factory.no_hit_material] + materials finalmaterials = ( [ba.sharedobj('object_material'), factory.flagmaterial] + materials) self.node = ba.newnode("flag", attrs={ 'position': (position[0], position[1] + 0.75, position[2]), 'color_texture': factory.flag_texture, 'color': color, 'materials': finalmaterials }, delegate=self) if dropped_timeout is not None: dropped_timeout = int(dropped_timeout) self._dropped_timeout = dropped_timeout self._counter: Optional[ba.Node] if self._dropped_timeout is not None: self._count = self._dropped_timeout self._tick_timer = ba.Timer(1.0, call=ba.WeakCall(self._tick), repeat=True) self._counter = ba.newnode('text', owner=self.node, attrs={ 'in_world': True, 'color': (1, 1, 1, 0.7), 'scale': 0.015, 'shadow': 0.5, 'flatness': 1.0, 'h_align': 'center' }) else: self._counter = None self._held_count = 0 self._score_text: Optional[ba.Node] = None self._score_text_hide_timer: Optional[ba.Timer] = None
def __init__(self) -> None: """Instantiate a factory object.""" self.impact_sounds_medium = (ba.getsound('impactMedium'), ba.getsound('impactMedium2')) self.impact_sounds_hard = (ba.getsound('impactHard'), ba.getsound('impactHard2'), ba.getsound('impactHard3')) self.impact_sounds_harder = (ba.getsound('bigImpact'), ba.getsound('bigImpact2')) self.single_player_death_sound = ba.getsound('playerDeath') self.punch_sound = ba.getsound('punch01') self.punch_sound_strong = (ba.getsound('punchStrong01'), ba.getsound('punchStrong02')) self.punch_sound_stronger = ba.getsound('superPunch') self.swish_sound = ba.getsound('punchSwish') self.block_sound = ba.getsound('block') self.shatter_sound = ba.getsound('shatter') self.splatter_sound = ba.getsound('splatter') self.spaz_material = ba.Material() self.roller_material = ba.Material() self.punch_material = ba.Material() self.pickup_material = ba.Material() self.curse_material = ba.Material() footing_material = ba.sharedobj('footing_material') object_material = ba.sharedobj('object_material') player_material = ba.sharedobj('player_material') region_material = ba.sharedobj('region_material') # send footing messages to spazzes so they know when they're on solid # ground. # eww this should really just be built into the spaz node self.roller_material.add_actions( conditions=('they_have_material', footing_material), actions=(('message', 'our_node', 'at_connect', 'footing', 1), ('message', 'our_node', 'at_disconnect', 'footing', -1))) self.spaz_material.add_actions( conditions=('they_have_material', footing_material), actions=(('message', 'our_node', 'at_connect', 'footing', 1), ('message', 'our_node', 'at_disconnect', 'footing', -1))) # punches self.punch_material.add_actions( conditions=('they_are_different_node_than_us', ), actions=(('modify_part_collision', 'collide', True), ('modify_part_collision', 'physical', False), ('message', 'our_node', 'at_connect', basespaz.PunchHitMessage()))) # pickups self.pickup_material.add_actions( conditions=(('they_are_different_node_than_us', ), 'and', ('they_have_material', object_material)), actions=(('modify_part_collision', 'collide', True), ('modify_part_collision', 'physical', False), ('message', 'our_node', 'at_connect', basespaz.PickupMessage()))) # curse self.curse_material.add_actions( conditions=(('they_are_different_node_than_us', ), 'and', ('they_have_material', player_material)), actions=('message', 'our_node', 'at_connect', basespaz.CurseExplodeMessage())) self.foot_impact_sounds = (ba.getsound('footImpact01'), ba.getsound('footImpact02'), ba.getsound('footImpact03')) self.foot_skid_sound = ba.getsound('skid01') self.foot_roll_sound = ba.getsound('scamper01') self.roller_material.add_actions( conditions=('they_have_material', footing_material), actions=(('impact_sound', self.foot_impact_sounds, 1, 0.2), ('skid_sound', self.foot_skid_sound, 20, 0.3), ('roll_sound', self.foot_roll_sound, 20, 3.0))) self.skid_sound = ba.getsound('gravelSkid') self.spaz_material.add_actions( conditions=('they_have_material', footing_material), actions=(('impact_sound', self.foot_impact_sounds, 20, 6), ('skid_sound', self.skid_sound, 2.0, 1), ('roll_sound', self.skid_sound, 2.0, 1))) self.shield_up_sound = ba.getsound('shieldUp') self.shield_down_sound = ba.getsound('shieldDown') self.shield_hit_sound = ba.getsound('shieldHit') # we don't want to collide with stuff we're initially overlapping # (unless its marked with a special region material) self.spaz_material.add_actions( conditions=((('we_are_younger_than', 51), 'and', ('they_are_different_node_than_us', )), 'and', ('they_dont_have_material', region_material)), actions=('modify_node_collision', 'collide', False)) self.spaz_media: Dict[str, Any] = {} # lets load some basic rules (allows them to be tweaked from the # master server) self.shield_decay_rate = _ba.get_account_misc_read_val('rsdr', 10.0) self.punch_cooldown = _ba.get_account_misc_read_val('rpc', 400) self.punch_cooldown_gloves = (_ba.get_account_misc_read_val( 'rpcg', 300)) self.punch_power_scale = _ba.get_account_misc_read_val('rpp', 1.2) self.punch_power_scale_gloves = (_ba.get_account_misc_read_val( 'rppg', 1.4)) self.max_shield_spillover_damage = (_ba.get_account_misc_read_val( 'rsms', 500))
def on_begin(self) -> None: # FIXME: Split this up a bit. # pylint: disable=too-many-statements from bastd.actor import controlsguide super().on_begin() # Show controls help in kiosk mode. if ba.app.kiosk_mode: controlsguide.ControlsGuide(delay=3.0, lifespan=10.0, bright=True).autoretain() assert self.initial_player_info is not None abot: Type[spazbot.SpazBot] bbot: Type[spazbot.SpazBot] cbot: Type[spazbot.SpazBot] if self._preset in ['rookie', 'rookie_easy']: self._exclude_powerups = ['curse'] self._have_tnt = False abot = (spazbot.BrawlerBotLite if self._preset == 'rookie_easy' else spazbot.BrawlerBot) self._bot_types_initial = [abot] * len(self.initial_player_info) bbot = (spazbot.BomberBotLite if self._preset == 'rookie_easy' else spazbot.BomberBot) self._bot_types_7 = ( [bbot] * (1 if len(self.initial_player_info) < 3 else 2)) cbot = (spazbot.BomberBot if self._preset == 'rookie_easy' else spazbot.TriggerBot) self._bot_types_14 = ( [cbot] * (1 if len(self.initial_player_info) < 3 else 2)) elif self._preset == 'tournament': self._exclude_powerups = [] self._have_tnt = True self._bot_types_initial = ( [spazbot.BrawlerBot] * (1 if len(self.initial_player_info) < 2 else 2)) self._bot_types_7 = ( [spazbot.TriggerBot] * (1 if len(self.initial_player_info) < 3 else 2)) self._bot_types_14 = ( [spazbot.ChargerBot] * (1 if len(self.initial_player_info) < 4 else 2)) elif self._preset in ['pro', 'pro_easy', 'tournament_pro']: self._exclude_powerups = ['curse'] self._have_tnt = True self._bot_types_initial = [spazbot.ChargerBot] * len( self.initial_player_info) abot = (spazbot.BrawlerBot if self._preset == 'pro' else spazbot.BrawlerBotLite) typed_bot_list: List[Type[spazbot.SpazBot]] = [] self._bot_types_7 = ( typed_bot_list + [abot] + [spazbot.BomberBot] * (1 if len(self.initial_player_info) < 3 else 2)) bbot = (spazbot.TriggerBotPro if self._preset == 'pro' else spazbot.TriggerBot) self._bot_types_14 = ( [bbot] * (1 if len(self.initial_player_info) < 3 else 2)) elif self._preset in ['uber', 'uber_easy']: self._exclude_powerups = [] self._have_tnt = True abot = (spazbot.BrawlerBotPro if self._preset == 'uber' else spazbot.BrawlerBot) bbot = (spazbot.TriggerBotPro if self._preset == 'uber' else spazbot.TriggerBot) typed_bot_list_2: List[Type[spazbot.SpazBot]] = [] self._bot_types_initial = (typed_bot_list_2 + [spazbot.StickyBot] + [abot] * len(self.initial_player_info)) self._bot_types_7 = ( [bbot] * (1 if len(self.initial_player_info) < 3 else 2)) self._bot_types_14 = ( [spazbot.ExplodeyBot] * (1 if len(self.initial_player_info) < 3 else 2)) else: raise Exception() self.setup_low_life_warning_sound() self._drop_powerups(standard_points=True) ba.timer(4.0, self._start_powerup_drops) # Make a bogus team for our bots. bad_team_name = self.get_team_display_string('Bad Guys') self._bot_team = ba.Team(1, bad_team_name, (0.5, 0.4, 0.4)) for team in [self.teams[0], self._bot_team]: team.gamedata['score'] = 0 self.update_scores() # Time display. starttime_ms = ba.time(timeformat=ba.TimeFormat.MILLISECONDS) assert isinstance(starttime_ms, int) self._starttime_ms = starttime_ms self._time_text = ba.NodeActor( ba.newnode('text', attrs={ 'v_attach': 'top', 'h_attach': 'center', 'h_align': 'center', 'color': (1, 1, 0.5, 1), 'flatness': 0.5, 'shadow': 0.5, 'position': (0, -50), 'scale': 1.3, 'text': '' })) self._time_text_input = ba.NodeActor( ba.newnode('timedisplay', attrs={'showsubseconds': True})) ba.sharedobj('globals').connectattr('time', self._time_text_input.node, 'time2') assert self._time_text_input.node assert self._time_text.node self._time_text_input.node.connectattr('output', self._time_text.node, 'text') # Our TNT spawner (if applicable). if self._have_tnt: self._tntspawner = stdbomb.TNTSpawner(position=(0, 1, -1)) self._bots = spazbot.BotSet() self._bot_spawn_timer = ba.Timer(1.0, self._update_bots, repeat=True) for bottype in self._bot_types_initial: self._spawn_bot(bottype)
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.interface_type == '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 interface_type = app.interface_type # 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 (interface_type == '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}) # Throw in test build info. self.beta_info = self.beta_info_2 = None if app.test_build and not app.kiosk_mode: pos = (230, 125) if app.kiosk_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 = ba.sharedobj('globals') 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.is_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.interface_type == '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.kiosk_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.uicontroller assert uicontroller is not None uicontroller.show_main_menu() else: main_window = ba.app.main_window # When coming back from a kiosk-mode game, jump to # the kiosk start screen. if ba.app.kiosk_mode: # pylint: disable=cyclic-import from bastd.ui.kiosk import KioskWindow ba.app.main_menu_window = KioskWindow().get_root_widget() # ..or in normal cases go back to the main menu else: main_window = ba.app.main_window if main_window == 'Gather': # pylint: disable=cyclic-import from bastd.ui.gather import GatherWindow ba.app.main_menu_window = (GatherWindow( transition=None).get_root_widget()) elif main_window == 'Watch': # pylint: disable=cyclic-import from bastd.ui.watch import WatchWindow ba.app.main_menu_window = WatchWindow( transition=None).get_root_widget() elif main_window == 'Team Game Select': # pylint: disable=cyclic-import from bastd.ui.playlist.browser import ( PlaylistBrowserWindow) ba.app.main_menu_window = PlaylistBrowserWindow( sessiontype=ba.TeamsSession, transition=None).get_root_widget() elif main_window == 'Free-for-All Game Select': # pylint: disable=cyclic-import from bastd.ui.playlist.browser import ( PlaylistBrowserWindow) ba.app.main_menu_window = PlaylistBrowserWindow( sessiontype=ba.FreeForAllSession, transition=None).get_root_widget() elif main_window == 'Coop Select': # pylint: disable=cyclic-import from bastd.ui.coop.browser import CoopBrowserWindow ba.app.main_menu_window = CoopBrowserWindow( transition=None).get_root_widget() else: # pylint: disable=cyclic-import from bastd.ui.mainmenu import MainMenuWindow ba.app.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