コード例 #1
0
 def _standard_time_limit_tick(self) -> None:
     from ba._gameutils import animate
     assert self._standard_time_limit_time is not None
     self._standard_time_limit_time -= 1
     if self._standard_time_limit_time <= 10:
         if self._standard_time_limit_time == 10:
             assert self._standard_time_limit_text is not None
             assert self._standard_time_limit_text.node
             self._standard_time_limit_text.node.scale = 1.3
             self._standard_time_limit_text.node.position = (-30, -45)
             cnode = _ba.newnode('combine',
                                 owner=self._standard_time_limit_text.node,
                                 attrs={'size': 4})
             cnode.connectattr('output',
                               self._standard_time_limit_text.node, 'color')
             animate(cnode, 'input0', {0: 1, 0.15: 1}, loop=True)
             animate(cnode, 'input1', {0: 1, 0.15: 0.5}, loop=True)
             animate(cnode, 'input2', {0: 0.1, 0.15: 0.0}, loop=True)
             cnode.input3 = 1.0
         _ba.playsound(_ba.getsound('tick'))
     if self._standard_time_limit_time <= 0:
         self._standard_time_limit_timer = None
         self.end_game()
         node = _ba.newnode('text',
                            attrs={
                                'v_attach': 'top',
                                'h_attach': 'center',
                                'h_align': 'center',
                                'color': (1, 0.7, 0, 1),
                                'position': (0, -90),
                                'scale': 1.2,
                                'text': Lstr(resource='timeExpiredText')
                            })
         _ba.playsound(_ba.getsound('refWhistle'))
         animate(node, 'scale', {0.0: 0.0, 0.1: 1.4, 0.15: 1.2})
コード例 #2
0
    def _tournament_time_limit_tick(self) -> None:
        from ba._gameutils import animate
        assert self._tournament_time_limit is not None
        self._tournament_time_limit -= 1
        if self._tournament_time_limit <= 10:
            if self._tournament_time_limit == 10:
                assert self._tournament_time_limit_title_text is not None
                assert self._tournament_time_limit_title_text.node
                assert self._tournament_time_limit_text is not None
                assert self._tournament_time_limit_text.node
                self._tournament_time_limit_title_text.node.scale = 1.0
                self._tournament_time_limit_text.node.scale = 1.3
                self._tournament_time_limit_title_text.node.position = (80, 85)
                self._tournament_time_limit_text.node.position = (80, 60)
                cnode = _ba.newnode(
                    'combine',
                    owner=self._tournament_time_limit_text.node,
                    attrs={'size': 4})
                cnode.connectattr('output',
                                  self._tournament_time_limit_title_text.node,
                                  'color')
                cnode.connectattr('output',
                                  self._tournament_time_limit_text.node,
                                  'color')
                animate(cnode, 'input0', {0: 1, 0.15: 1}, loop=True)
                animate(cnode, 'input1', {0: 1, 0.15: 0.5}, loop=True)
                animate(cnode, 'input2', {0: 0.1, 0.15: 0.0}, loop=True)
                cnode.input3 = 1.0
            _ba.playsound(_ba.getsound('tick'))
        if self._tournament_time_limit <= 0:
            self._tournament_time_limit_timer = None
            self.end_game()
            tval = Lstr(resource='tournamentTimeExpiredText',
                        fallback_resource='timeExpiredText')
            node = _ba.newnode('text',
                               attrs={
                                   'v_attach': 'top',
                                   'h_attach': 'center',
                                   'h_align': 'center',
                                   'color': (1, 0.7, 0, 1),
                                   'position': (0, -200),
                                   'scale': 1.6,
                                   'text': tval
                               })
            _ba.playsound(_ba.getsound('refWhistle'))
            animate(node, 'scale', {0: 0.0, 0.1: 1.4, 0.15: 1.2})

        # Normally we just connect this to time, but since this is a bit of a
        # funky setup we just update it manually once per second.
        assert self._tournament_time_limit_text_input is not None
        assert self._tournament_time_limit_text_input.node
        self._tournament_time_limit_text_input.node.time2 = (
            self._tournament_time_limit * 1000)
コード例 #3
0
def show_damage_count(damage: str, position: Sequence[float],
                      direction: Sequence[float]) -> None:
    """Pop up a damage count at a position in space.

    Category: Gameplay Functions
    """
    lifespan = 1.0
    app = _ba.app

    # FIXME: Should never vary game elements based on local config.
    #  (connected clients may have differing configs so they won't
    #  get the intended results).
    do_big = app.ui.uiscale is UIScale.SMALL or app.vr_mode
    txtnode = _ba.newnode('text',
                          attrs={
                              'text': damage,
                              'in_world': True,
                              'h_align': 'center',
                              'flatness': 1.0,
                              'shadow': 1.0 if do_big else 0.7,
                              'color': (1, 0.25, 0.25, 1),
                              'scale': 0.015 if do_big else 0.01
                          })
    # Translate upward.
    tcombine = _ba.newnode('combine', owner=txtnode, attrs={'size': 3})
    tcombine.connectattr('output', txtnode, 'position')
    v_vals = []
    pval = 0.0
    vval = 0.07
    count = 6
    for i in range(count):
        v_vals.append((float(i) / count, pval))
        pval += vval
        vval *= 0.5
    p_start = position[0]
    p_dir = direction[0]
    animate(tcombine, 'input0',
            {i[0] * lifespan: p_start + p_dir * i[1]
             for i in v_vals})
    p_start = position[1]
    p_dir = direction[1]
    animate(tcombine, 'input1',
            {i[0] * lifespan: p_start + p_dir * i[1]
             for i in v_vals})
    p_start = position[2]
    p_dir = direction[2]
    animate(tcombine, 'input2',
            {i[0] * lifespan: p_start + p_dir * i[1]
             for i in v_vals})
    animate(txtnode, 'opacity', {0.7 * lifespan: 1.0, lifespan: 0.0})
    _ba.timer(lifespan, txtnode.delete)
コード例 #4
0
    def _play_internal_music(self, musictype: Optional[MusicType]) -> None:

        # Stop any existing music-player playback.
        if self._music_player is not None:
            self._music_player.stop()

        # Stop any existing internal music.
        if self._music_node:
            self._music_node.delete()
            self._music_node = None

        # Start up new internal music.
        if musictype is not None:

            entry = ASSET_SOUNDTRACK_ENTRIES.get(musictype)
            if entry is None:
                print(f"Unknown music: '{musictype}'")
                entry = ASSET_SOUNDTRACK_ENTRIES[MusicType.FLAG_CATCHER]

            self._music_node = _ba.newnode(type='sound',
                                           attrs={
                                               'sound':
                                               _ba.getsound(entry.assetname),
                                               'positional':
                                               False,
                                               'music':
                                               True,
                                               'volume':
                                               entry.volume * 5.0,
                                               'loop':
                                               entry.loop
                                           })
コード例 #5
0
    def pause(self) -> None:
        """Pause the game due to a user request or menu popping up.

        If there's a foreground host-activity that says it's pausable, tell it
        to pause ..we now no longer pause if there are connected clients.
        """
        activity: Optional[ba.Activity] = _ba.get_foreground_host_activity()
        if (activity is not None and activity.allow_pausing
                and not _ba.have_connected_clients()):
            from ba import _gameutils
            from ba._language import Lstr
            from ba._nodeactor import NodeActor

            # FIXME: Shouldn't be touching scene stuff here;
            #  should just pass the request on to the host-session.
            with _ba.Context(activity):
                globs = activity.globalsnode
                if not globs.paused:
                    _ba.playsound(_ba.getsound('refWhistle'))
                    globs.paused = True

                # FIXME: This should not be an attr on Actor.
                activity.paused_text = NodeActor(
                    _ba.newnode('text',
                                attrs={
                                    'text': Lstr(resource='pausedByHostText'),
                                    'client_only': True,
                                    'flatness': 1.0,
                                    'h_align': 'center'
                                }))
コード例 #6
0
    def postinit(self, sessionplayer: ba.SessionPlayer) -> None:
        """Wire up a newly created player.

        (internal)
        """
        from ba._nodeactor import NodeActor

        # Sanity check; if a dataclass is created that inherits from us,
        # it will define an equality operator by default which will break
        # internal game logic. So complain loudly if we find one.
        if type(self).__eq__ is not object.__eq__:
            raise RuntimeError(
                f'Player class {type(self)} defines an equality'
                f' operator (__eq__) which will break internal'
                f' logic. Please remove it.\n'
                f'For dataclasses you can do "dataclass(eq=False)"'
                f' in the class decorator.')

        self.actor = None
        self.character = ''
        self._nodeactor: Optional[ba.NodeActor] = None
        self._sessionplayer = sessionplayer
        self.character = sessionplayer.character
        self.color = sessionplayer.color
        self.highlight = sessionplayer.highlight
        self._team = cast(TeamType, sessionplayer.sessionteam.activityteam)
        assert self._team is not None
        self._customdata = {}
        self._expired = False
        self._postinited = True
        node = _ba.newnode('player', attrs={'playerID': sessionplayer.id})
        self._nodeactor = NodeActor(node)
        sessionplayer.setnode(node)
コード例 #7
0
def _play_internal_music(musictype: Optional[MusicType]) -> None:
    app = _ba.app

    # Stop any existing music-player playback.
    if app.music_player is not None:
        app.music_player.stop()

    # Stop any existing internal music.
    if app.music:
        app.music.delete()
        app.music = None

    # Start up new internal music.
    if musictype is not None:

        # Filenames/volume/loop for our built-in music.
        musicinfos: Dict[MusicType, Tuple[str, float, bool]] = {
            MusicType.MENU: ('menuMusic', 5.0, True),
            MusicType.VICTORY: ('victoryMusic', 6.0, False),
            MusicType.CHAR_SELECT: ('charSelectMusic', 2.0, True),
            MusicType.RUN_AWAY: ('runAwayMusic', 6.0, True),
            MusicType.ONSLAUGHT: ('runAwayMusic', 6.0, True),
            MusicType.KEEP_AWAY: ('runAwayMusic', 6.0, True),
            MusicType.RACE: ('runAwayMusic', 6.0, True),
            MusicType.EPIC_RACE: ('slowEpicMusic', 6.0, True),
            MusicType.SCORES: ('scoresEpicMusic', 3.0, False),
            MusicType.GRAND_ROMP: ('grandRompMusic', 6.0, True),
            MusicType.TO_THE_DEATH: ('toTheDeathMusic', 6.0, True),
            MusicType.CHOSEN_ONE: ('survivalMusic', 4.0, True),
            MusicType.FORWARD_MARCH: ('forwardMarchMusic', 4.0, True),
            MusicType.FLAG_CATCHER: ('flagCatcherMusic', 6.0, True),
            MusicType.SURVIVAL: ('survivalMusic', 4.0, True),
            MusicType.EPIC: ('slowEpicMusic', 6.0, True),
            MusicType.SPORTS: ('sportsMusic', 4.0, True),
            MusicType.HOCKEY: ('sportsMusic', 4.0, True),
            MusicType.FOOTBALL: ('sportsMusic', 4.0, True),
            MusicType.FLYING: ('flyingMusic', 4.0, True),
            MusicType.SCARY: ('scaryMusic', 4.0, True),
            MusicType.MARCHING:
            ('whenJohnnyComesMarchingHomeMusic', 4.0, True),
        }
        musicinfo = musicinfos.get(musictype)
        if musicinfo is None:
            print(f"Unknown music: '{musictype}'")
            filename = 'flagCatcherMusic'
            volume = 6.0
            loop = True
        else:
            filename, volume, loop = musicinfo

        app.music = _ba.newnode(type='sound',
                                attrs={
                                    'sound': _ba.getsound(filename),
                                    'positional': False,
                                    'music': True,
                                    'volume': volume,
                                    'loop': loop
                                })
コード例 #8
0
ファイル: _activity.py プロジェクト: Dmitry450/ballistica
 def create_player_node(self, player: ba.Player) -> ba.Node:
     """Create the 'player' node associated with the provided ba.Player."""
     from ba._nodeactor import NodeActor
     with _ba.Context(self):
         node = _ba.newnode('player', attrs={'playerID': player.get_id()})
         # FIXME: Should add a dedicated slot for this on ba.Player
         #  instead of cluttering up their gamedata dict.
         player.gamedata['_playernode'] = NodeActor(node)
         return node
コード例 #9
0
    def __init__(self, lobby: ba.Lobby):
        from ba._nodeactor import NodeActor
        from ba._general import WeakCall
        self._state = 0
        self._press_to_punch: Union[str,
                                    ba.Lstr] = ('C' if _ba.app.iircade_mode
                                                else _ba.charstr(
                                                    SpecialChar.LEFT_BUTTON))
        self._press_to_bomb: Union[str,
                                   ba.Lstr] = ('B' if _ba.app.iircade_mode else
                                               _ba.charstr(
                                                   SpecialChar.RIGHT_BUTTON))
        self._joinmsg = Lstr(resource='pressAnyButtonToJoinText')
        can_switch_teams = (len(lobby.sessionteams) > 1)

        # If we have a keyboard, grab keys for punch and pickup.
        # FIXME: This of course is only correct on the local device;
        #  Should change this for net games.
        keyboard = _ba.getinputdevice('Keyboard', '#1', doraise=False)
        if keyboard is not None:
            self._update_for_keyboard(keyboard)

        flatness = 1.0 if _ba.app.vr_mode else 0.0
        self._text = NodeActor(
            _ba.newnode('text',
                        attrs={
                            'position': (0, -40),
                            'h_attach': 'center',
                            'v_attach': 'top',
                            'h_align': 'center',
                            'color': (0.7, 0.7, 0.95, 1.0),
                            'flatness': flatness,
                            'text': self._joinmsg
                        }))

        if _ba.app.demo_mode or _ba.app.arcade_mode:
            self._messages = [self._joinmsg]
        else:
            msg1 = Lstr(resource='pressToSelectProfileText',
                        subs=[
                            ('${BUTTONS}', _ba.charstr(SpecialChar.UP_ARROW) +
                             ' ' + _ba.charstr(SpecialChar.DOWN_ARROW))
                        ])
            msg2 = Lstr(resource='pressToOverrideCharacterText',
                        subs=[('${BUTTONS}', Lstr(resource='bombBoldText'))])
            msg3 = Lstr(value='${A} < ${B} >',
                        subs=[('${A}', msg2), ('${B}', self._press_to_bomb)])
            self._messages = (([
                Lstr(
                    resource='pressToSelectTeamText',
                    subs=[('${BUTTONS}', _ba.charstr(SpecialChar.LEFT_ARROW) +
                           ' ' + _ba.charstr(SpecialChar.RIGHT_ARROW))],
                )
            ] if can_switch_teams else []) + [msg1] + [msg3] + [self._joinmsg])

        self._timer = _ba.Timer(4.0, WeakCall(self._update), repeat=True)
コード例 #10
0
ファイル: _activity.py プロジェクト: imayushsaini/ballistica
    def transition_in(self, prev_globals: Optional[ba.Node]) -> None:
        """Called by Session to kick off transition-in.

        (internal)
        """
        assert not self._has_transitioned_in
        self._has_transitioned_in = True

        # Set up the globals node based on our settings.
        with _ba.Context(self):
            glb = self._globalsnode = _ba.newnode('globals')

            # Now that it's going to be front and center,
            # set some global values based on what the activity wants.
            glb.use_fixed_vr_overlay = self.use_fixed_vr_overlay
            glb.allow_kick_idle_players = self.allow_kick_idle_players
            if self.inherits_slow_motion and prev_globals is not None:
                glb.slow_motion = prev_globals.slow_motion
            else:
                glb.slow_motion = self.slow_motion
            if self.inherits_music and prev_globals is not None:
                glb.music_continuous = True  # Prevent restarting same music.
                glb.music = prev_globals.music
                glb.music_count += 1
            if self.inherits_vr_camera_offset and prev_globals is not None:
                glb.vr_camera_offset = prev_globals.vr_camera_offset
            if self.inherits_vr_overlay_center and prev_globals is not None:
                glb.vr_overlay_center = prev_globals.vr_overlay_center
                glb.vr_overlay_center_enabled = (
                    prev_globals.vr_overlay_center_enabled)

            # If they want to inherit tint from the previous self.
            if self.inherits_tint and prev_globals is not None:
                glb.tint = prev_globals.tint
                glb.vignette_outer = prev_globals.vignette_outer
                glb.vignette_inner = prev_globals.vignette_inner

            # Start pruning our various things periodically.
            self._prune_dead_actors()
            self._prune_dead_actors_timer = _ba.Timer(5.17,
                                                      self._prune_dead_actors,
                                                      repeat=True)

            _ba.timer(13.3, self._prune_delay_deletes, repeat=True)

            # Also start our low-level scene running.
            self._activity_data.start()

            try:
                self.on_transition_in()
            except Exception:
                print_exception(f'Error in on_transition_in for {self}.')

        # Tell the C++ layer that this activity is the main one, so it uses
        # settings from our globals, directs various events to us, etc.
        self._activity_data.make_foreground()
コード例 #11
0
    def spawn_player_spaz(self,
                          player: PlayerType,
                          position: Sequence[float] = (0, 0, 0),
                          angle: float = None) -> MyPlayerSpaz:
        """Create and wire up a ba.PlayerSpaz for the provided ba.Player."""
        # pylint: disable=too-many-locals
        # pylint: disable=cyclic-import

        name = player.getname()
        color = player.color
        highlight = player.highlight

        light_color = _math.normalized_color(color)
        display_color = _ba.safecolor(color, target_intensity=0.75)
        spaz = MyPlayerSpaz(color=color,
                            highlight=highlight,
                            character=player.character,
                            player=player)

        player.actor = spaz
        assert spaz.node
        if position is None:
            # In teams-mode get our team-start-location.
            if isinstance(self.session, DualTeamSession):
                position = (self.map.get_start_position(player.team.id))
            else:
                # Otherwise do free-for-all spawn locations.
                position = self.map.get_ffa_start_position(self.players)
        # If this is co-op and we're on Courtyard or Runaround, add the
        # material that allows us to collide with the player-walls.
        # FIXME: Need to generalize this.
        if isinstance(self.session, CoopSession) and self.map.getname() in [
                'Courtyard', 'Tower D'
        ]:
            mat = self.map.preloaddata['collide_with_wall_material']
            assert isinstance(spaz.node.materials, tuple)
            assert isinstance(spaz.node.roller_materials, tuple)
            spaz.node.materials += (mat, )
            spaz.node.roller_materials += (mat, )

        spaz.node.name = name
        spaz.node.name_color = display_color
        spaz.connect_controls_to_player()
        # Move to the stand position and add a flash of light.
        spaz.handlemessage(
            StandMessage(
                position,
                angle if angle is not None else random.uniform(0, 360)))
        _ba.playsound(self._spawn_sound, 1, position=spaz.node.position)
        light = _ba.newnode('light', attrs={'color': light_color})
        spaz.node.connectattr('position', light, 'position')
        animate(light, 'intensity', {0: 0, 0.25: 1, 0.5: 0})
        _ba.timer(0.5, light.delete)
        return spaz
コード例 #12
0
	def top_message(self, text):
		node = _ba.newnode('text',
							attrs={
								'text': text,
								'flatness': 1.0,
								'h_align': 'center',
								'v_attach':'top',
								'scale':0.7,
								'position':(0,-70),
								'color':(1,1,1)
							})
コード例 #13
0
	def left_watermark(self, text):
		node = _ba.newnode('text',
							attrs={
								'text': text,
								'flatness': 1.0,
								'h_align': 'left',
								'v_attach':'bottom',
								'h_attach':'left',
								'scale':0.7,
								'position':(25,67),
								'color':(0.7,0.7,0.7)
							})
コード例 #14
0
	def nextGame(self,text):
		node = _ba.newnode('text',
							attrs={
							    'text':"Next : "+text,
							    'flatness':1.0,
							    'h_align':'right',
							    'v_attach':'bottom',
							    'h_attach':'right',
							    'scale':0.7,
							    'position':(-25,18),
							    'color':(0.5,0.5,0.5)
							})
コード例 #15
0
 def fade_to_red(self) -> None:
     """Fade the screen to red; (such as when the good guys have lost)."""
     from ba import _gameutils
     c_existing = self.globalsnode.tint
     cnode = _ba.newnode('combine',
                         attrs={
                             'input0': c_existing[0],
                             'input1': c_existing[1],
                             'input2': c_existing[2],
                             'size': 3
                         })
     _gameutils.animate(cnode, 'input1', {0: c_existing[1], 2.0: 0})
     _gameutils.animate(cnode, 'input2', {0: c_existing[2], 2.0: 0})
     cnode.connectattr('output', self.globalsnode, 'tint')
コード例 #16
0
 def setup_standard_time_limit(self, duration: float) -> None:
     """
     Create a standard game time-limit given the provided
     duration in seconds.
     This will be displayed at the top of the screen.
     If the time-limit expires, end_game() will be called.
     """
     from ba._nodeactor import NodeActor
     if duration <= 0.0:
         return
     self._standard_time_limit_time = int(duration)
     self._standard_time_limit_timer = _ba.Timer(
         1.0, WeakCall(self._standard_time_limit_tick), repeat=True)
     self._standard_time_limit_text = NodeActor(
         _ba.newnode('text',
                     attrs={
                         'v_attach': 'top',
                         'h_attach': 'center',
                         'h_align': 'left',
                         'color': (1.0, 1.0, 1.0, 0.5),
                         'position': (-25, -30),
                         'flatness': 1.0,
                         'scale': 0.9
                     }))
     self._standard_time_limit_text_input = NodeActor(
         _ba.newnode('timedisplay',
                     attrs={
                         'time2': duration * 1000,
                         'timemin': 0
                     }))
     self.globalsnode.connectattr('time',
                                  self._standard_time_limit_text_input.node,
                                  'time1')
     assert self._standard_time_limit_text_input.node
     assert self._standard_time_limit_text.node
     self._standard_time_limit_text_input.node.connectattr(
         'output', self._standard_time_limit_text.node, 'text')
コード例 #17
0
	def highlights_(self):
		if setti["textonmap"]['center highlights']["randomColor"]:
			color=((0+random.random()*1.0),(0+random.random()*1.0),(0+random.random()*1.0))
		else:
			color=tuple(setti["textonmap"]["center highlights"]["color"])
		node = _ba.newnode('text',
							attrs={
								'text': self.highlights[self.index],
								'flatness': 1.0,
								'h_align': 'center',
								'v_attach':'bottom',
								'scale':1,
								'position':(0,138),
								'color':color
							})

		self.delt = ba.timer(7,node.delete)
		self.index = int((self.index+1)%len(self.highlights))
コード例 #18
0
 def _update_life_warning(self) -> None:
     # Beep continuously if anyone is close to death.
     should_beep = False
     for player in self.players:
         if player.is_alive():
             # FIXME: Should abstract this instead of
             #  reading hitpoints directly.
             if getattr(player.actor, 'hitpoints', 999) < 200:
                 should_beep = True
                 break
     if should_beep and self._life_warning_beep is None:
         from ba._nodeactor import NodeActor
         self._life_warning_beep = NodeActor(
             _ba.newnode('sound',
                         attrs={
                             'sound': self._warn_beeps_sound,
                             'positional': False,
                             'loop': True
                         }))
     if self._life_warning_beep is not None and not should_beep:
         self._life_warning_beep = None
コード例 #19
0
ファイル: _coopgame.py プロジェクト: snow-1711/ballistica
    def _show_standard_scores_to_beat_ui(self,
                                         scores: List[Dict[str, Any]]) -> None:
        from efro.util import asserttype
        from ba._gameutils import timestring, animate
        from ba._nodeactor import NodeActor
        from ba._enums import TimeFormat
        display_type = self.get_score_type()
        if scores is not None:

            # Sort by originating date so that the most recent is first.
            scores.sort(reverse=True, key=lambda s: asserttype(s['time'], int))

            # Now make a display for the most recent challenge.
            for score in scores:
                if score['type'] == 'score_challenge':
                    tval = (score['player'] + ':  ' + timestring(
                        int(score['value']) * 10,
                        timeformat=TimeFormat.MILLISECONDS).evaluate()
                            if display_type == 'time' else str(score['value']))
                    hattach = 'center' if display_type == 'time' else 'left'
                    halign = 'center' if display_type == 'time' else 'left'
                    pos = (20, -70) if display_type == 'time' else (20, -130)
                    txt = NodeActor(
                        _ba.newnode('text',
                                    attrs={
                                        'v_attach': 'top',
                                        'h_attach': hattach,
                                        'h_align': halign,
                                        'color': (0.7, 0.4, 1, 1),
                                        'shadow': 0.5,
                                        'flatness': 1.0,
                                        'position': pos,
                                        'scale': 0.6,
                                        'text': tval
                                    })).autoretain()
                    assert txt.node is not None
                    animate(txt.node, 'scale', {1.0: 0.0, 1.1: 0.7, 1.2: 0.6})
                    break
コード例 #20
0
ファイル: _player.py プロジェクト: ShifengHuGit/ballistica
    def postinit(self, sessionplayer: ba.SessionPlayer) -> None:
        """Wire up a newly created player.

        (internal)
        """
        from ba._nodeactor import NodeActor

        # Sanity check; if a dataclass is created that inherits from us,
        # it will define an equality operator by default which will break
        # internal game logic. So complain loudly if we find one.
        if type(self).__eq__ is not object.__eq__:
            raise RuntimeError(
                f'Player class {type(self)} defines an equality'
                f' operator (__eq__) which will break internal'
                f' logic. Please remove it.\n'
                f'For dataclasses you can do "dataclass(eq=False)"'
                f' in the class decorator.')

        self.actor = None
        self.character = ''
        self._nodeactor: Optional[ba.NodeActor] = None
        self._sessionplayer = sessionplayer
        self.character = sessionplayer.character
        self.color = sessionplayer.color
        self.highlight = sessionplayer.highlight
        self.team = sessionplayer.team.gameteam  # type: ignore
        assert self.team is not None
        self.sessiondata = sessionplayer.sessiondata
        self.gamedata = sessionplayer.gamedata

        # Create our player node in the current activity.
        # Note: do we want to save a few cycles here by managing our player
        # node manually instead of wrapping it in a NodeActor?
        node = _ba.newnode('player', attrs={'playerID': sessionplayer.id})
        self._nodeactor = NodeActor(node)
        sessionplayer.set_node(node)
コード例 #21
0
    def show_completion_banner(self, sound: bool = True) -> None:
        """Create the banner/sound for an acquired achievement announcement."""
        from ba import _account
        from ba import _gameutils
        from bastd.actor.text import Text
        from bastd.actor.image import Image
        from ba._general import WeakCall
        from ba._lang import Lstr
        from ba._messages import DieMessage
        from ba._enums import TimeType, SpecialChar
        app = _ba.app
        app.last_achievement_display_time = _ba.time(TimeType.REAL)

        # Just piggy-back onto any current activity
        # (should we use the session instead?..)
        activity = _ba.getactivity(doraise=False)

        # If this gets called while this achievement is occupying a slot
        # already, ignore it. (probably should never happen in real
        # life but whatevs).
        if self._completion_banner_slot is not None:
            return

        if activity is None:
            print('show_completion_banner() called with no current activity!')
            return

        if sound:
            _ba.playsound(_ba.getsound('achievement'), host_only=True)
        else:
            _ba.timer(
                0.5,
                lambda: _ba.playsound(_ba.getsound('ding'), host_only=True))

        in_time = 0.300
        out_time = 3.5

        base_vr_depth = 200

        # Find the first free slot.
        i = 0
        while True:
            if i not in app.achievement_completion_banner_slots:
                app.achievement_completion_banner_slots.add(i)
                self._completion_banner_slot = i

                # Remove us from that slot when we close.
                # Use a real-timer in the UI context so the removal runs even
                # if our activity/session dies.
                with _ba.Context('ui'):
                    _ba.timer(in_time + out_time,
                              self._remove_banner_slot,
                              timetype=TimeType.REAL)
                break
            i += 1
        assert self._completion_banner_slot is not None
        y_offs = 110 * self._completion_banner_slot
        objs: List[ba.Actor] = []
        obj = Image(_ba.gettexture('shadow'),
                    position=(-30, 30 + y_offs),
                    front=True,
                    attach=Image.Attach.BOTTOM_CENTER,
                    transition=Image.Transition.IN_BOTTOM,
                    vr_depth=base_vr_depth - 100,
                    transition_delay=in_time,
                    transition_out_delay=out_time,
                    color=(0.0, 0.1, 0, 1),
                    scale=(1000, 300)).autoretain()
        objs.append(obj)
        assert obj.node
        obj.node.host_only = True
        obj = Image(_ba.gettexture('light'),
                    position=(-180, 60 + y_offs),
                    front=True,
                    attach=Image.Attach.BOTTOM_CENTER,
                    vr_depth=base_vr_depth,
                    transition=Image.Transition.IN_BOTTOM,
                    transition_delay=in_time,
                    transition_out_delay=out_time,
                    color=(1.8, 1.8, 1.0, 0.0),
                    scale=(40, 300)).autoretain()
        objs.append(obj)
        assert obj.node
        obj.node.host_only = True
        obj.node.premultiplied = True
        combine = _ba.newnode('combine', owner=obj.node, attrs={'size': 2})
        _gameutils.animate(
            combine, 'input0', {
                in_time: 0,
                in_time + 0.4: 30,
                in_time + 0.5: 40,
                in_time + 0.6: 30,
                in_time + 2.0: 0
            })
        _gameutils.animate(
            combine, 'input1', {
                in_time: 0,
                in_time + 0.4: 200,
                in_time + 0.5: 500,
                in_time + 0.6: 200,
                in_time + 2.0: 0
            })
        combine.connectattr('output', obj.node, 'scale')
        _gameutils.animate(obj.node,
                           'rotate', {
                               0: 0.0,
                               0.35: 360.0
                           },
                           loop=True)
        obj = Image(self.get_icon_texture(True),
                    position=(-180, 60 + y_offs),
                    attach=Image.Attach.BOTTOM_CENTER,
                    front=True,
                    vr_depth=base_vr_depth - 10,
                    transition=Image.Transition.IN_BOTTOM,
                    transition_delay=in_time,
                    transition_out_delay=out_time,
                    scale=(100, 100)).autoretain()
        objs.append(obj)
        assert obj.node
        obj.node.host_only = True

        # Flash.
        color = self.get_icon_color(True)
        combine = _ba.newnode('combine', owner=obj.node, attrs={'size': 3})
        keys = {
            in_time: 1.0 * color[0],
            in_time + 0.4: 1.5 * color[0],
            in_time + 0.5: 6.0 * color[0],
            in_time + 0.6: 1.5 * color[0],
            in_time + 2.0: 1.0 * color[0]
        }
        _gameutils.animate(combine, 'input0', keys)
        keys = {
            in_time: 1.0 * color[1],
            in_time + 0.4: 1.5 * color[1],
            in_time + 0.5: 6.0 * color[1],
            in_time + 0.6: 1.5 * color[1],
            in_time + 2.0: 1.0 * color[1]
        }
        _gameutils.animate(combine, 'input1', keys)
        keys = {
            in_time: 1.0 * color[2],
            in_time + 0.4: 1.5 * color[2],
            in_time + 0.5: 6.0 * color[2],
            in_time + 0.6: 1.5 * color[2],
            in_time + 2.0: 1.0 * color[2]
        }
        _gameutils.animate(combine, 'input2', keys)
        combine.connectattr('output', obj.node, 'color')

        obj = Image(_ba.gettexture('achievementOutline'),
                    model_transparent=_ba.getmodel('achievementOutline'),
                    position=(-180, 60 + y_offs),
                    front=True,
                    attach=Image.Attach.BOTTOM_CENTER,
                    vr_depth=base_vr_depth,
                    transition=Image.Transition.IN_BOTTOM,
                    transition_delay=in_time,
                    transition_out_delay=out_time,
                    scale=(100, 100)).autoretain()
        assert obj.node
        obj.node.host_only = True

        # Flash.
        color = (2, 1.4, 0.4, 1)
        combine = _ba.newnode('combine', owner=obj.node, attrs={'size': 3})
        keys = {
            in_time: 1.0 * color[0],
            in_time + 0.4: 1.5 * color[0],
            in_time + 0.5: 6.0 * color[0],
            in_time + 0.6: 1.5 * color[0],
            in_time + 2.0: 1.0 * color[0]
        }
        _gameutils.animate(combine, 'input0', keys)
        keys = {
            in_time: 1.0 * color[1],
            in_time + 0.4: 1.5 * color[1],
            in_time + 0.5: 6.0 * color[1],
            in_time + 0.6: 1.5 * color[1],
            in_time + 2.0: 1.0 * color[1]
        }
        _gameutils.animate(combine, 'input1', keys)
        keys = {
            in_time: 1.0 * color[2],
            in_time + 0.4: 1.5 * color[2],
            in_time + 0.5: 6.0 * color[2],
            in_time + 0.6: 1.5 * color[2],
            in_time + 2.0: 1.0 * color[2]
        }
        _gameutils.animate(combine, 'input2', keys)
        combine.connectattr('output', obj.node, 'color')
        objs.append(obj)

        objt = Text(Lstr(value='${A}:',
                         subs=[('${A}', Lstr(resource='achievementText'))]),
                    position=(-120, 91 + y_offs),
                    front=True,
                    v_attach=Text.VAttach.BOTTOM,
                    vr_depth=base_vr_depth - 10,
                    transition=Text.Transition.IN_BOTTOM,
                    flatness=0.5,
                    transition_delay=in_time,
                    transition_out_delay=out_time,
                    color=(1, 1, 1, 0.8),
                    scale=0.65).autoretain()
        objs.append(objt)
        assert objt.node
        objt.node.host_only = True

        objt = Text(self.display_name,
                    position=(-120, 50 + y_offs),
                    front=True,
                    v_attach=Text.VAttach.BOTTOM,
                    transition=Text.Transition.IN_BOTTOM,
                    vr_depth=base_vr_depth,
                    flatness=0.5,
                    transition_delay=in_time,
                    transition_out_delay=out_time,
                    flash=True,
                    color=(1, 0.8, 0, 1.0),
                    scale=1.5).autoretain()
        objs.append(objt)
        assert objt.node
        objt.node.host_only = True

        objt = Text(_ba.charstr(SpecialChar.TICKET),
                    position=(-120 - 170 + 5, 75 + y_offs - 20),
                    front=True,
                    v_attach=Text.VAttach.BOTTOM,
                    h_align=Text.HAlign.CENTER,
                    v_align=Text.VAlign.CENTER,
                    transition=Text.Transition.IN_BOTTOM,
                    vr_depth=base_vr_depth,
                    transition_delay=in_time,
                    transition_out_delay=out_time,
                    flash=True,
                    color=(0.5, 0.5, 0.5, 1),
                    scale=3.0).autoretain()
        objs.append(objt)
        assert objt.node
        objt.node.host_only = True

        objt = Text('+' + str(self.get_award_ticket_value()),
                    position=(-120 - 180 + 5, 80 + y_offs - 20),
                    v_attach=Text.VAttach.BOTTOM,
                    front=True,
                    h_align=Text.HAlign.CENTER,
                    v_align=Text.VAlign.CENTER,
                    transition=Text.Transition.IN_BOTTOM,
                    vr_depth=base_vr_depth,
                    flatness=0.5,
                    shadow=1.0,
                    transition_delay=in_time,
                    transition_out_delay=out_time,
                    flash=True,
                    color=(0, 1, 0, 1),
                    scale=1.5).autoretain()
        objs.append(objt)
        assert objt.node
        objt.node.host_only = True

        # Add the 'x 2' if we've got pro.
        if _account.have_pro():
            objt = Text('x 2',
                        position=(-120 - 180 + 45, 80 + y_offs - 50),
                        v_attach=Text.VAttach.BOTTOM,
                        front=True,
                        h_align=Text.HAlign.CENTER,
                        v_align=Text.VAlign.CENTER,
                        transition=Text.Transition.IN_BOTTOM,
                        vr_depth=base_vr_depth,
                        flatness=0.5,
                        shadow=1.0,
                        transition_delay=in_time,
                        transition_out_delay=out_time,
                        flash=True,
                        color=(0.4, 0, 1, 1),
                        scale=0.9).autoretain()
            objs.append(objt)
            assert objt.node
            objt.node.host_only = True

        objt = Text(self.description_complete,
                    position=(-120, 30 + y_offs),
                    front=True,
                    v_attach=Text.VAttach.BOTTOM,
                    transition=Text.Transition.IN_BOTTOM,
                    vr_depth=base_vr_depth - 10,
                    flatness=0.5,
                    transition_delay=in_time,
                    transition_out_delay=out_time,
                    color=(1.0, 0.7, 0.5, 1.0),
                    scale=0.8).autoretain()
        objs.append(objt)
        assert objt.node
        objt.node.host_only = True

        for actor in objs:
            _ba.timer(out_time + 1.000,
                      WeakCall(actor.handlemessage, DieMessage()))
コード例 #22
0
    def _show_tip(self) -> None:
        # pylint: disable=too-many-locals
        from ba._gameutils import animate, GameTip
        from ba._enums import SpecialChar

        # If there's any tips left on the list, display one.
        if self.tips:
            tip = self.tips.pop(random.randrange(len(self.tips)))
            tip_title = Lstr(value='${A}:',
                             subs=[('${A}', Lstr(resource='tipText'))])
            icon: Optional[ba.Texture] = None
            sound: Optional[ba.Sound] = None
            if isinstance(tip, GameTip):
                icon = tip.icon
                sound = tip.sound
                tip = tip.text
                assert isinstance(tip, str)

            # Do a few substitutions.
            tip_lstr = Lstr(translate=('tips', tip),
                            subs=[('${PICKUP}',
                                   _ba.charstr(SpecialChar.TOP_BUTTON))])
            base_position = (75, 50)
            tip_scale = 0.8
            tip_title_scale = 1.2
            vrmode = _ba.app.vr_mode

            t_offs = -350.0
            tnode = _ba.newnode('text',
                                attrs={
                                    'text':
                                    tip_lstr,
                                    'scale':
                                    tip_scale,
                                    'maxwidth':
                                    900,
                                    'position': (base_position[0] + t_offs,
                                                 base_position[1]),
                                    'h_align':
                                    'left',
                                    'vr_depth':
                                    300,
                                    'shadow':
                                    1.0 if vrmode else 0.5,
                                    'flatness':
                                    1.0 if vrmode else 0.5,
                                    'v_align':
                                    'center',
                                    'v_attach':
                                    'bottom'
                                })
            t2pos = (base_position[0] + t_offs - (20 if icon is None else 82),
                     base_position[1] + 2)
            t2node = _ba.newnode('text',
                                 owner=tnode,
                                 attrs={
                                     'text': tip_title,
                                     'scale': tip_title_scale,
                                     'position': t2pos,
                                     'h_align': 'right',
                                     'vr_depth': 300,
                                     'shadow': 1.0 if vrmode else 0.5,
                                     'flatness': 1.0 if vrmode else 0.5,
                                     'maxwidth': 140,
                                     'v_align': 'center',
                                     'v_attach': 'bottom'
                                 })
            if icon is not None:
                ipos = (base_position[0] + t_offs - 40, base_position[1] + 1)
                img = _ba.newnode('image',
                                  attrs={
                                      'texture': icon,
                                      'position': ipos,
                                      'scale': (50, 50),
                                      'opacity': 1.0,
                                      'vr_depth': 315,
                                      'color': (1, 1, 1),
                                      'absolute_scale': True,
                                      'attach': 'bottomCenter'
                                  })
                animate(img, 'opacity', {0: 0, 1.0: 1, 4.0: 1, 5.0: 0})
                _ba.timer(5.0, img.delete)
            if sound is not None:
                _ba.playsound(sound)

            combine = _ba.newnode('combine',
                                  owner=tnode,
                                  attrs={
                                      'input0': 1.0,
                                      'input1': 0.8,
                                      'input2': 1.0,
                                      'size': 4
                                  })
            combine.connectattr('output', tnode, 'color')
            combine.connectattr('output', t2node, 'color')
            animate(combine, 'input3', {0: 0, 1.0: 1, 4.0: 1, 5.0: 0})
            _ba.timer(5.0, tnode.delete)
コード例 #23
0
    def _show_info(self) -> None:
        """Show the game description."""
        from ba._gameutils import animate
        from bastd.actor.zoomtext import ZoomText
        name = self.get_instance_display_string()
        ZoomText(name,
                 maxwidth=800,
                 lifespan=2.5,
                 jitter=2.0,
                 position=(0, 180),
                 flash=False,
                 color=(0.93 * 1.25, 0.9 * 1.25, 1.0 * 1.25),
                 trailcolor=(0.15, 0.05, 1.0, 0.0)).autoretain()
        _ba.timer(0.2, Call(_ba.playsound, _ba.getsound('gong')))

        # The description can be either a string or a sequence with args
        # to swap in post-translation.
        desc_in = self.get_instance_description()
        desc_l: Sequence
        if isinstance(desc_in, str):
            desc_l = [desc_in]  # handle simple string case
        else:
            desc_l = desc_in
        if not isinstance(desc_l[0], str):
            raise TypeError('Invalid format for instance description')
        subs = []
        for i in range(len(desc_l) - 1):
            subs.append(('${ARG' + str(i + 1) + '}', str(desc_l[i + 1])))
        translation = Lstr(translate=('gameDescriptions', desc_l[0]),
                           subs=subs)

        # Do some standard filters (epic mode, etc).
        if self.settings_raw.get('Epic Mode', False):
            translation = Lstr(resource='epicDescriptionFilterText',
                               subs=[('${DESCRIPTION}', translation)])
        vrmode = _ba.app.vr_mode
        dnode = _ba.newnode('text',
                            attrs={
                                'v_attach': 'center',
                                'h_attach': 'center',
                                'h_align': 'center',
                                'color': (1, 1, 1, 1),
                                'shadow': 1.0 if vrmode else 0.5,
                                'flatness': 1.0 if vrmode else 0.5,
                                'vr_depth': -30,
                                'position': (0, 80),
                                'scale': 1.2,
                                'maxwidth': 700,
                                'text': translation
                            })
        cnode = _ba.newnode('combine',
                            owner=dnode,
                            attrs={
                                'input0': 1.0,
                                'input1': 1.0,
                                'input2': 1.0,
                                'size': 4
                            })
        cnode.connectattr('output', dnode, 'color')
        keys = {0.5: 0, 1.0: 1.0, 2.5: 1.0, 4.0: 0.0}
        animate(cnode, 'input3', keys)
        _ba.timer(4.0, dnode.delete)
コード例 #24
0
    def _show_scoreboard_info(self) -> None:
        """Create the game info display.

        This is the thing in the top left corner showing the name
        and short description of the game.
        """
        # pylint: disable=too-many-locals
        from ba._freeforallsession import FreeForAllSession
        from ba._gameutils import animate
        from ba._nodeactor import NodeActor
        sb_name = self.get_instance_scoreboard_display_string()

        # The description can be either a string or a sequence with args
        # to swap in post-translation.
        sb_desc_in = self.get_instance_description_short()
        sb_desc_l: Sequence
        if isinstance(sb_desc_in, str):
            sb_desc_l = [sb_desc_in]  # handle simple string case
        else:
            sb_desc_l = sb_desc_in
        if not isinstance(sb_desc_l[0], str):
            raise TypeError('Invalid format for instance description.')

        is_empty = (sb_desc_l[0] == '')
        subs = []
        for i in range(len(sb_desc_l) - 1):
            subs.append(('${ARG' + str(i + 1) + '}', str(sb_desc_l[i + 1])))
        translation = Lstr(translate=('gameDescriptions', sb_desc_l[0]),
                           subs=subs)
        sb_desc = translation
        vrmode = _ba.app.vr_mode
        yval = -34 if is_empty else -20
        yval -= 16
        sbpos = ((15, yval) if isinstance(self.session, FreeForAllSession) else
                 (15, yval))
        self._game_scoreboard_name_text = NodeActor(
            _ba.newnode('text',
                        attrs={
                            'text': sb_name,
                            'maxwidth': 300,
                            'position': sbpos,
                            'h_attach': 'left',
                            'vr_depth': 10,
                            'v_attach': 'top',
                            'v_align': 'bottom',
                            'color': (1.0, 1.0, 1.0, 1.0),
                            'shadow': 1.0 if vrmode else 0.6,
                            'flatness': 1.0 if vrmode else 0.5,
                            'scale': 1.1
                        }))

        assert self._game_scoreboard_name_text.node
        animate(self._game_scoreboard_name_text.node, 'opacity', {
            0: 0.0,
            1.0: 1.0
        })

        descpos = (((17, -44 +
                     10) if isinstance(self.session, FreeForAllSession) else
                    (17, -44 + 10)))
        self._game_scoreboard_description_text = NodeActor(
            _ba.newnode('text',
                        attrs={
                            'text':
                            sb_desc,
                            'maxwidth':
                            480,
                            'position':
                            descpos,
                            'scale':
                            0.7,
                            'h_attach':
                            'left',
                            'v_attach':
                            'top',
                            'v_align':
                            'top',
                            'shadow':
                            1.0 if vrmode else 0.7,
                            'flatness':
                            1.0 if vrmode else 0.8,
                            'color': (1, 1, 1, 1) if vrmode else
                            (0.9, 0.9, 0.9, 1.0)
                        }))

        assert self._game_scoreboard_description_text.node
        animate(self._game_scoreboard_description_text.node, 'opacity', {
            0: 0.0,
            1.0: 1.0
        })
コード例 #25
0
def animate_array(node: ba.Node,
                  attr: str,
                  size: int,
                  keys: dict[float, Sequence[float]],
                  loop: bool = False,
                  offset: float = 0,
                  timetype: ba.TimeType = TimeType.SIM,
                  timeformat: ba.TimeFormat = TimeFormat.SECONDS,
                  suppress_format_warning: bool = False) -> None:
    """Animate an array of values on a target ba.Node.

    Category: Gameplay Functions

    Like ba.animate(), but operates on array attributes.
    """
    # pylint: disable=too-many-locals
    combine = _ba.newnode('combine', owner=node, attrs={'size': size})
    if timetype is TimeType.SIM:
        driver = 'time'
    else:
        raise Exception('FIXME: Only SIM timetype is supported currently.')
    items = list(keys.items())
    items.sort()

    # Temp sanity check while we transition from milliseconds to seconds
    # based time values.
    if __debug__:
        if not suppress_format_warning:
            for item in items:
                # (PyCharm seems to think item is a float, not a tuple)
                _ba.time_format_check(timeformat, item[0])

    if timeformat is TimeFormat.SECONDS:
        mult = 1000
    elif timeformat is TimeFormat.MILLISECONDS:
        mult = 1
    else:
        raise ValueError('invalid timeformat value: "' + str(timeformat) + '"')

    # We operate in either activities or sessions..
    try:
        globalsnode = _ba.getactivity().globalsnode
    except ActivityNotFoundError:
        globalsnode = _ba.getsession().sessionglobalsnode

    for i in range(size):
        curve = _ba.newnode('animcurve',
                            owner=node,
                            name=('Driving ' + str(node) + ' \'' + attr +
                                  '\' member ' + str(i)))
        globalsnode.connectattr(driver, curve, 'in')
        curve.times = [int(mult * time) for time, val in items]
        curve.values = [val[i] for time, val in items]
        curve.offset = _ba.time(timeformat=TimeFormat.MILLISECONDS) + int(
            mult * offset)
        curve.loop = loop
        curve.connectattr('out', combine, 'input' + str(i))

        # If we're not looping, set a timer to kill this
        # curve after its done its job.
        if not loop:
            # (PyCharm seems to think item is a float, not a tuple)
            _ba.timer(int(mult * items[-1][0]) + 1000,
                      curve.delete,
                      timeformat=TimeFormat.MILLISECONDS)
    combine.connectattr('output', node, attr)

    # If we're not looping, set a timer to kill the combine once
    # the job is done.
    # FIXME: Even if we are looping we should have a way to die
    #  once we get disconnected.
    if not loop:
        # (PyCharm seems to think item is a float, not a tuple)
        _ba.timer(int(mult * items[-1][0]) + 1000,
                  combine.delete,
                  timeformat=TimeFormat.MILLISECONDS)
コード例 #26
0
    def _setup_tournament_time_limit(self, duration: float) -> None:
        """
        Create a tournament game time-limit given the provided
        duration in seconds.
        This will be displayed at the top of the screen.
        If the time-limit expires, end_game() will be called.
        """
        from ba._nodeactor import NodeActor
        from ba._enums import TimeType
        if duration <= 0.0:
            return
        self._tournament_time_limit = int(duration)

        # We want this timer to match the server's time as close as possible,
        # so lets go with base-time. Theoretically we should do real-time but
        # then we have to mess with contexts and whatnot since its currently
        # not available in activity contexts. :-/
        self._tournament_time_limit_timer = _ba.Timer(
            1.0,
            WeakCall(self._tournament_time_limit_tick),
            repeat=True,
            timetype=TimeType.BASE)
        self._tournament_time_limit_title_text = NodeActor(
            _ba.newnode('text',
                        attrs={
                            'v_attach': 'bottom',
                            'h_attach': 'left',
                            'h_align': 'center',
                            'v_align': 'center',
                            'vr_depth': 300,
                            'maxwidth': 100,
                            'color': (1.0, 1.0, 1.0, 0.5),
                            'position': (60, 50),
                            'flatness': 1.0,
                            'scale': 0.5,
                            'text': Lstr(resource='tournamentText')
                        }))
        self._tournament_time_limit_text = NodeActor(
            _ba.newnode('text',
                        attrs={
                            'v_attach': 'bottom',
                            'h_attach': 'left',
                            'h_align': 'center',
                            'v_align': 'center',
                            'vr_depth': 300,
                            'maxwidth': 100,
                            'color': (1.0, 1.0, 1.0, 0.5),
                            'position': (60, 30),
                            'flatness': 1.0,
                            'scale': 0.9
                        }))
        self._tournament_time_limit_text_input = NodeActor(
            _ba.newnode('timedisplay',
                        attrs={
                            'timemin': 0,
                            'time2': self._tournament_time_limit * 1000
                        }))
        assert self._tournament_time_limit_text.node
        assert self._tournament_time_limit_text_input.node
        self._tournament_time_limit_text_input.node.connectattr(
            'output', self._tournament_time_limit_text.node, 'text')
コード例 #27
0
def cameraflash(duration: float = 999.0) -> None:
    """Create a strobing camera flash effect.

    Category: Gameplay Functions

    (as seen when a team wins a game)
    Duration is in seconds.
    """
    # pylint: disable=too-many-locals
    import random
    from ba._nodeactor import NodeActor
    x_spread = 10
    y_spread = 5
    positions = [[-x_spread, -y_spread], [0, -y_spread], [0, y_spread],
                 [x_spread, -y_spread], [x_spread, y_spread],
                 [-x_spread, y_spread]]
    times = [0, 2700, 1000, 1800, 500, 1400]

    # Store this on the current activity so we only have one at a time.
    # FIXME: Need a type safe way to do this.
    activity = _ba.getactivity()
    activity.camera_flash_data = []  # type: ignore
    for i in range(6):
        light = NodeActor(
            _ba.newnode('light',
                        attrs={
                            'position': (positions[i][0], 0, positions[i][1]),
                            'radius': 1.0,
                            'lights_volumes': False,
                            'height_attenuated': False,
                            'color': (0.2, 0.2, 0.8)
                        }))
        sval = 1.87
        iscale = 1.3
        tcombine = _ba.newnode('combine',
                               owner=light.node,
                               attrs={
                                   'size': 3,
                                   'input0': positions[i][0],
                                   'input1': 0,
                                   'input2': positions[i][1]
                               })
        assert light.node
        tcombine.connectattr('output', light.node, 'position')
        xval = positions[i][0]
        yval = positions[i][1]
        spd = 0.5 + random.random()
        spd2 = 0.5 + random.random()
        animate(tcombine,
                'input0', {
                    0.0: xval + 0,
                    0.069 * spd: xval + 10.0,
                    0.143 * spd: xval - 10.0,
                    0.201 * spd: xval + 0
                },
                loop=True)
        animate(tcombine,
                'input2', {
                    0.0: yval + 0,
                    0.15 * spd2: yval + 10.0,
                    0.287 * spd2: yval - 10.0,
                    0.398 * spd2: yval + 0
                },
                loop=True)
        animate(light.node,
                'intensity', {
                    0.0: 0,
                    0.02 * sval: 0,
                    0.05 * sval: 0.8 * iscale,
                    0.08 * sval: 0,
                    0.1 * sval: 0
                },
                loop=True,
                offset=times[i])
        _ba.timer((times[i] + random.randint(1, int(duration)) * 40 * sval),
                  light.node.delete,
                  timeformat=TimeFormat.MILLISECONDS)
        activity.camera_flash_data.append(light)  # type: ignore
コード例 #28
0
def animate(node: ba.Node,
            attr: str,
            keys: dict[float, float],
            loop: bool = False,
            offset: float = 0,
            timetype: ba.TimeType = TimeType.SIM,
            timeformat: ba.TimeFormat = TimeFormat.SECONDS,
            suppress_format_warning: bool = False) -> ba.Node:
    """Animate values on a target ba.Node.

    Category: Gameplay Functions

    Creates an 'animcurve' node with the provided values and time as an input,
    connect it to the provided attribute, and set it to die with the target.
    Key values are provided as time:value dictionary pairs.  Time values are
    relative to the current time. By default, times are specified in seconds,
    but timeformat can also be set to MILLISECONDS to recreate the old behavior
    (prior to ba 1.5) of taking milliseconds. Returns the animcurve node.
    """
    if timetype is TimeType.SIM:
        driver = 'time'
    else:
        raise Exception('FIXME; only SIM timetype is supported currently.')
    items = list(keys.items())
    items.sort()

    # Temp sanity check while we transition from milliseconds to seconds
    # based time values.
    if __debug__:
        if not suppress_format_warning:
            for item in items:
                _ba.time_format_check(timeformat, item[0])

    curve = _ba.newnode('animcurve',
                        owner=node,
                        name='Driving ' + str(node) + ' \'' + attr + '\'')

    if timeformat is TimeFormat.SECONDS:
        mult = 1000
    elif timeformat is TimeFormat.MILLISECONDS:
        mult = 1
    else:
        raise ValueError(f'invalid timeformat value: {timeformat}')

    curve.times = [int(mult * time) for time, val in items]
    curve.offset = _ba.time(timeformat=TimeFormat.MILLISECONDS) + int(
        mult * offset)
    curve.values = [val for time, val in items]
    curve.loop = loop

    # If we're not looping, set a timer to kill this curve
    # after its done its job.
    # FIXME: Even if we are looping we should have a way to die once we
    #  get disconnected.
    if not loop:
        _ba.timer(int(mult * items[-1][0]) + 1000,
                  curve.delete,
                  timeformat=TimeFormat.MILLISECONDS)

    # Do the connects last so all our attrs are in place when we push initial
    # values through.

    # We operate in either activities or sessions..
    try:
        globalsnode = _ba.getactivity().globalsnode
    except ActivityNotFoundError:
        globalsnode = _ba.getsession().sessionglobalsnode

    globalsnode.connectattr(driver, curve, 'in')
    curve.connectattr('out', node, attr)
    return curve
コード例 #29
0
    def __init__(self,
                 depsets: Sequence[ba.DependencySet],
                 team_names: Sequence[str] = None,
                 team_colors: Sequence[Sequence[float]] = None,
                 min_players: int = 1,
                 max_players: int = 8):
        """Instantiate a session.

        depsets should be a sequence of successfully resolved ba.DependencySet
        instances; one for each ba.Activity the session may potentially run.
        """
        # pylint: disable=too-many-statements
        # pylint: disable=too-many-locals
        # pylint: disable=cyclic-import
        from ba._lobby import Lobby
        from ba._stats import Stats
        from ba._gameactivity import GameActivity
        from ba._activity import Activity
        from ba._team import SessionTeam
        from ba._error import DependencyError
        from ba._dependency import Dependency, AssetPackage
        from efro.util import empty_weakref

        # First off, resolve all dependency-sets we were passed.
        # If things are missing, we'll try to gather them into a single
        # missing-deps exception if possible to give the caller a clean
        # path to download missing stuff and try again.
        missing_asset_packages: Set[str] = set()
        for depset in depsets:
            try:
                depset.resolve()
            except DependencyError as exc:
                # Gather/report missing assets only; barf on anything else.
                if all(issubclass(d.cls, AssetPackage) for d in exc.deps):
                    for dep in exc.deps:
                        assert isinstance(dep.config, str)
                        missing_asset_packages.add(dep.config)
                else:
                    missing_info = [(d.cls, d.config) for d in exc.deps]
                    raise RuntimeError(
                        f'Missing non-asset dependencies: {missing_info}'
                    ) from exc

        # Throw a combined exception if we found anything missing.
        if missing_asset_packages:
            raise DependencyError([
                Dependency(AssetPackage, set_id)
                for set_id in missing_asset_packages
            ])

        # Ok; looks like our dependencies check out.
        # Now give the engine a list of asset-set-ids to pass along to clients.
        required_asset_packages: Set[str] = set()
        for depset in depsets:
            required_asset_packages.update(depset.get_asset_package_ids())

        # print('Would set host-session asset-reqs to:',
        # required_asset_packages)

        # Init our C++ layer data.
        self._sessiondata = _ba.register_session(self)

        # Should remove this if possible.
        self.tournament_id: Optional[str] = None

        self.sessionteams = []
        self.sessionplayers = []
        self.min_players = min_players
        self.max_players = max_players

        self.customdata = {}
        self._in_set_activity = False
        self._next_team_id = 0
        self._activity_retained: Optional[ba.Activity] = None
        self._launch_end_session_activity_time: Optional[float] = None
        self._activity_end_timer: Optional[ba.Timer] = None
        self._activity_weak = empty_weakref(Activity)
        self._next_activity: Optional[ba.Activity] = None
        self._wants_to_end = False
        self._ending = False
        self._activity_should_end_immediately = False
        self._activity_should_end_immediately_results: (
            Optional[ba.GameResults]) = None
        self._activity_should_end_immediately_delay = 0.0

        # Create static teams if we're using them.
        if self.use_teams:
            assert team_names is not None
            assert team_colors is not None
            for i, color in enumerate(team_colors):
                team = SessionTeam(team_id=self._next_team_id,
                                   name=GameActivity.get_team_display_string(
                                       team_names[i]),
                                   color=color)
                self.sessionteams.append(team)
                self._next_team_id += 1
                try:
                    with _ba.Context(self):
                        self.on_team_join(team)
                except Exception:
                    print_exception(f'Error in on_team_join for {self}.')

        self.lobby = Lobby()
        self.stats = Stats()

        # Instantiate our session globals node which will apply its settings.
        self._sessionglobalsnode = _ba.newnode('sessionglobals')
コード例 #30
0
    def __init__(self, vpos: float, sessionplayer: _ba.SessionPlayer,
                 lobby: 'Lobby') -> None:
        self._deek_sound = _ba.getsound('deek')
        self._click_sound = _ba.getsound('click01')
        self._punchsound = _ba.getsound('punch01')
        self._swish_sound = _ba.getsound('punchSwish')
        self._errorsound = _ba.getsound('error')
        self._mask_texture = _ba.gettexture('characterIconMask')
        self._vpos = vpos
        self._lobby = weakref.ref(lobby)
        self._sessionplayer = sessionplayer
        self._inited = False
        self._dead = False
        self._text_node: Optional[ba.Node] = None
        self._profilename = ''
        self._profilenames: List[str] = []
        self._ready: bool = False
        self._character_names: List[str] = []
        self._last_change: Sequence[Union[float, int]] = (0, 0)
        self._profiles: Dict[str, Dict[str, Any]] = {}

        app = _ba.app

        # Load available player profiles either from the local config or
        # from the remote device.
        self.reload_profiles()

        # Note: this is just our local index out of available teams; *not*
        # the team-id!
        self._selected_team_index: int = self.lobby.next_add_team

        # Store a persistent random character index and colors; we'll use this
        # for the '_random' profile. Let's use their input_device id to seed
        # it. This will give a persistent character for them between games
        # and will distribute characters nicely if everyone is random.
        self._random_color, self._random_highlight = (
            get_player_profile_colors(None))

        # To calc our random character we pick a random one out of our
        # unlocked list and then locate that character's index in the full
        # list.
        char_index_offset = app.lobby_random_char_index_offset
        self._random_character_index = (
            (sessionplayer.inputdevice.id + char_index_offset) %
            len(self._character_names))

        # Attempt to set an initial profile based on what was used previously
        # for this input-device, etc.
        self._profileindex = self._select_initial_profile()
        self._profilename = self._profilenames[self._profileindex]

        self._text_node = _ba.newnode('text',
                                      delegate=self,
                                      attrs={
                                          'position': (-100, self._vpos),
                                          'maxwidth': 160,
                                          'shadow': 0.5,
                                          'vr_depth': -20,
                                          'h_align': 'left',
                                          'v_align': 'center',
                                          'v_attach': 'top'
                                      })
        animate(self._text_node, 'scale', {0: 0, 0.1: 1.0})
        self.icon = _ba.newnode('image',
                                owner=self._text_node,
                                attrs={
                                    'position': (-130, self._vpos + 20),
                                    'mask_texture': self._mask_texture,
                                    'vr_depth': -10,
                                    'attach': 'topCenter'
                                })

        animate_array(self.icon, 'scale', 2, {0: (0, 0), 0.1: (45, 45)})

        # Set our initial name to '<choosing player>' in case anyone asks.
        self._sessionplayer.setname(
            Lstr(resource='choosingPlayerText').evaluate(), real=False)

        # Init these to our rando but they should get switched to the
        # selected profile (if any) right after.
        self._character_index = self._random_character_index
        self._color = self._random_color
        self._highlight = self._random_highlight

        self.update_from_profile()
        self.update_position()
        self._inited = True

        self._set_ready(False)