bascenev1lib.activity.multiteamvictory

Functionality related to the final screen in multi-teams sessions.

  1# Released under the MIT License. See LICENSE for details.
  2#
  3"""Functionality related to the final screen in multi-teams sessions."""
  4
  5from __future__ import annotations
  6
  7from typing import override, TYPE_CHECKING
  8
  9import bascenev1 as bs
 10
 11from bascenev1lib.activity.multiteamscore import MultiTeamScoreScreenActivity
 12
 13if TYPE_CHECKING:
 14    from typing import Any
 15
 16
 17class TeamSeriesVictoryScoreScreenActivity(MultiTeamScoreScreenActivity):
 18    """Final score screen for a team series."""
 19
 20    # Dont' play music by default; (we do manually after a delay).
 21    default_music = None
 22
 23    def __init__(self, settings: dict):
 24        super().__init__(settings=settings)
 25        self._min_view_time = 15.0
 26        self._is_ffa = isinstance(self.session, bs.FreeForAllSession)
 27        self._allow_server_transition = True
 28        self._tips_text = None
 29        self._default_show_tips = False
 30        self._ffa_top_player_info: list[Any] | None = None
 31        self._ffa_top_player_rec: bs.PlayerRecord | None = None
 32
 33    @override
 34    def on_begin(self) -> None:
 35        # pylint: disable=too-many-branches
 36        # pylint: disable=too-many-locals
 37        # pylint: disable=too-many-statements
 38        from bascenev1lib.actor.text import Text
 39        from bascenev1lib.actor.image import Image
 40
 41        bs.set_analytics_screen(
 42            'FreeForAll Series Victory Screen'
 43            if self._is_ffa
 44            else 'Teams Series Victory Screen'
 45        )
 46        assert bs.app.classic is not None
 47        if bs.app.ui_v1.uiscale is bs.UIScale.LARGE:
 48            sval = bs.Lstr(resource='pressAnyKeyButtonPlayAgainText')
 49        else:
 50            sval = bs.Lstr(resource='pressAnyButtonPlayAgainText')
 51        self._show_up_next = False
 52        self._custom_continue_message = sval
 53        super().on_begin()
 54        winning_sessionteam = self.settings_raw['winner']
 55
 56        # Pause a moment before playing victory music.
 57        bs.timer(0.6, bs.WeakCall(self._play_victory_music))
 58        bs.timer(
 59            4.4, bs.WeakCall(self._show_winner, self.settings_raw['winner'])
 60        )
 61        bs.timer(4.6, self._score_display_sound.play)
 62
 63        # Score / Name / Player-record.
 64        player_entries: list[tuple[int, str, bs.PlayerRecord]] = []
 65
 66        # Note: for ffa, exclude players who haven't entered the game yet.
 67        if self._is_ffa:
 68            for _pkey, prec in self.stats.get_records().items():
 69                if prec.player.in_game:
 70                    player_entries.append(
 71                        (
 72                            prec.player.sessionteam.customdata['score'],
 73                            prec.getname(full=True),
 74                            prec,
 75                        )
 76                    )
 77            player_entries.sort(reverse=True, key=lambda x: x[0])
 78            if len(player_entries) > 0:
 79                # Store some info for the top ffa player so we can
 80                # show winner info even if they leave.
 81                self._ffa_top_player_info = list(player_entries[0])
 82                self._ffa_top_player_info[1] = self._ffa_top_player_info[
 83                    2
 84                ].getname()
 85                self._ffa_top_player_info[2] = self._ffa_top_player_info[
 86                    2
 87                ].get_icon()
 88        else:
 89            for _pkey, prec in self.stats.get_records().items():
 90                player_entries.append((prec.score, prec.name_full, prec))
 91            player_entries.sort(reverse=True, key=lambda x: x[0])
 92
 93        ts_height = 300.0
 94        ts_h_offs = -390.0
 95        tval = 6.4
 96        t_incr = 0.12
 97
 98        always_use_first_to = bs.app.lang.get_resource(
 99            'bestOfUseFirstToInstead'
100        )
101
102        session = self.session
103        if self._is_ffa:
104            assert isinstance(session, bs.FreeForAllSession)
105            txt = bs.Lstr(
106                value='${A}:',
107                subs=[
108                    (
109                        '${A}',
110                        bs.Lstr(
111                            resource='firstToFinalText',
112                            subs=[
113                                (
114                                    '${COUNT}',
115                                    str(session.get_ffa_series_length()),
116                                )
117                            ],
118                        ),
119                    )
120                ],
121            )
122        else:
123            assert isinstance(session, bs.MultiTeamSession)
124
125            # Some languages may prefer to always show 'first to X' instead of
126            # 'best of X'.
127            # FIXME: This will affect all clients connected to us even if
128            #  they're not using this language. Should try to come up
129            #  with a wording that works everywhere.
130            if always_use_first_to:
131                txt = bs.Lstr(
132                    value='${A}:',
133                    subs=[
134                        (
135                            '${A}',
136                            bs.Lstr(
137                                resource='firstToFinalText',
138                                subs=[
139                                    (
140                                        '${COUNT}',
141                                        str(
142                                            session.get_series_length() / 2 + 1
143                                        ),
144                                    )
145                                ],
146                            ),
147                        )
148                    ],
149                )
150            else:
151                txt = bs.Lstr(
152                    value='${A}:',
153                    subs=[
154                        (
155                            '${A}',
156                            bs.Lstr(
157                                resource='bestOfFinalText',
158                                subs=[
159                                    (
160                                        '${COUNT}',
161                                        str(session.get_series_length()),
162                                    )
163                                ],
164                            ),
165                        )
166                    ],
167                )
168
169        Text(
170            txt,
171            v_align=Text.VAlign.CENTER,
172            maxwidth=300,
173            color=(0.5, 0.5, 0.5, 1.0),
174            position=(0, 220),
175            scale=1.2,
176            transition=Text.Transition.IN_TOP_SLOW,
177            h_align=Text.HAlign.CENTER,
178            transition_delay=t_incr * 4,
179        ).autoretain()
180
181        win_score = (session.get_series_length() - 1) // 2 + 1
182        lose_score = 0
183        for team in self.teams:
184            if team.sessionteam.customdata['score'] != win_score:
185                lose_score = team.sessionteam.customdata['score']
186
187        if not self._is_ffa:
188            Text(
189                bs.Lstr(
190                    resource='gamesToText',
191                    subs=[
192                        ('${WINCOUNT}', str(win_score)),
193                        ('${LOSECOUNT}', str(lose_score)),
194                    ],
195                ),
196                color=(0.5, 0.5, 0.5, 1.0),
197                maxwidth=160,
198                v_align=Text.VAlign.CENTER,
199                position=(0, -215),
200                scale=1.8,
201                transition=Text.Transition.IN_LEFT,
202                h_align=Text.HAlign.CENTER,
203                transition_delay=4.8 + t_incr * 4,
204            ).autoretain()
205
206        if self._is_ffa:
207            v_extra = 120
208        else:
209            v_extra = 0
210
211        mvp: bs.PlayerRecord | None = None
212        mvp_name: str | None = None
213
214        # Show game MVP.
215        if not self._is_ffa:
216            mvp, mvp_name = None, None
217            for entry in player_entries:
218                if entry[2].team == winning_sessionteam:
219                    mvp = entry[2]
220                    mvp_name = entry[1]
221                    break
222            if mvp is not None:
223                Text(
224                    bs.Lstr(resource='mostValuablePlayerText'),
225                    color=(0.5, 0.5, 0.5, 1.0),
226                    v_align=Text.VAlign.CENTER,
227                    maxwidth=300,
228                    position=(180, ts_height / 2 + 15),
229                    transition=Text.Transition.IN_LEFT,
230                    h_align=Text.HAlign.LEFT,
231                    transition_delay=tval,
232                ).autoretain()
233                tval += 4 * t_incr
234
235                Image(
236                    mvp.get_icon(),
237                    position=(230, ts_height / 2 - 55 + 14 - 5),
238                    scale=(70, 70),
239                    transition=Image.Transition.IN_LEFT,
240                    transition_delay=tval,
241                ).autoretain()
242                assert mvp_name is not None
243                Text(
244                    bs.Lstr(value=mvp_name),
245                    position=(280, ts_height / 2 - 55 + 15 - 5),
246                    h_align=Text.HAlign.LEFT,
247                    v_align=Text.VAlign.CENTER,
248                    maxwidth=170,
249                    scale=1.3,
250                    color=bs.safecolor(mvp.team.color + (1,)),
251                    transition=Text.Transition.IN_LEFT,
252                    transition_delay=tval,
253                ).autoretain()
254                tval += 4 * t_incr
255
256        # Most violent.
257        most_kills = 0
258        for entry in player_entries:
259            if entry[2].kill_count >= most_kills:
260                mvp = entry[2]
261                mvp_name = entry[1]
262                most_kills = entry[2].kill_count
263        if mvp is not None:
264            Text(
265                bs.Lstr(resource='mostViolentPlayerText'),
266                color=(0.5, 0.5, 0.5, 1.0),
267                v_align=Text.VAlign.CENTER,
268                maxwidth=300,
269                position=(180, ts_height / 2 - 150 + v_extra + 15),
270                transition=Text.Transition.IN_LEFT,
271                h_align=Text.HAlign.LEFT,
272                transition_delay=tval,
273            ).autoretain()
274            Text(
275                bs.Lstr(
276                    value='(${A})',
277                    subs=[
278                        (
279                            '${A}',
280                            bs.Lstr(
281                                resource='killsTallyText',
282                                subs=[('${COUNT}', str(most_kills))],
283                            ),
284                        )
285                    ],
286                ),
287                position=(260, ts_height / 2 - 150 - 15 + v_extra),
288                color=(0.3, 0.3, 0.3, 1.0),
289                scale=0.6,
290                h_align=Text.HAlign.LEFT,
291                transition=Text.Transition.IN_LEFT,
292                transition_delay=tval,
293            ).autoretain()
294            tval += 4 * t_incr
295
296            Image(
297                mvp.get_icon(),
298                position=(233, ts_height / 2 - 150 - 30 - 46 + 25 + v_extra),
299                scale=(50, 50),
300                transition=Image.Transition.IN_LEFT,
301                transition_delay=tval,
302            ).autoretain()
303            assert mvp_name is not None
304            Text(
305                bs.Lstr(value=mvp_name),
306                position=(270, ts_height / 2 - 150 - 30 - 36 + v_extra + 15),
307                h_align=Text.HAlign.LEFT,
308                v_align=Text.VAlign.CENTER,
309                maxwidth=180,
310                color=bs.safecolor(mvp.team.color + (1,)),
311                transition=Text.Transition.IN_LEFT,
312                transition_delay=tval,
313            ).autoretain()
314            tval += 4 * t_incr
315
316        # Most killed.
317        most_killed = 0
318        mkp, mkp_name = None, None
319        for entry in player_entries:
320            if entry[2].killed_count >= most_killed:
321                mkp = entry[2]
322                mkp_name = entry[1]
323                most_killed = entry[2].killed_count
324        if mkp is not None:
325            Text(
326                bs.Lstr(resource='mostViolatedPlayerText'),
327                color=(0.5, 0.5, 0.5, 1.0),
328                v_align=Text.VAlign.CENTER,
329                maxwidth=300,
330                position=(180, ts_height / 2 - 300 + v_extra + 15),
331                transition=Text.Transition.IN_LEFT,
332                h_align=Text.HAlign.LEFT,
333                transition_delay=tval,
334            ).autoretain()
335            Text(
336                bs.Lstr(
337                    value='(${A})',
338                    subs=[
339                        (
340                            '${A}',
341                            bs.Lstr(
342                                resource='deathsTallyText',
343                                subs=[('${COUNT}', str(most_killed))],
344                            ),
345                        )
346                    ],
347                ),
348                position=(260, ts_height / 2 - 300 - 15 + v_extra),
349                h_align=Text.HAlign.LEFT,
350                scale=0.6,
351                color=(0.3, 0.3, 0.3, 1.0),
352                transition=Text.Transition.IN_LEFT,
353                transition_delay=tval,
354            ).autoretain()
355            tval += 4 * t_incr
356            Image(
357                mkp.get_icon(),
358                position=(233, ts_height / 2 - 300 - 30 - 46 + 25 + v_extra),
359                scale=(50, 50),
360                transition=Image.Transition.IN_LEFT,
361                transition_delay=tval,
362            ).autoretain()
363            assert mkp_name is not None
364            Text(
365                bs.Lstr(value=mkp_name),
366                position=(270, ts_height / 2 - 300 - 30 - 36 + v_extra + 15),
367                h_align=Text.HAlign.LEFT,
368                v_align=Text.VAlign.CENTER,
369                color=bs.safecolor(mkp.team.color + (1,)),
370                maxwidth=180,
371                transition=Text.Transition.IN_LEFT,
372                transition_delay=tval,
373            ).autoretain()
374            tval += 4 * t_incr
375
376        # Now show individual scores.
377        tdelay = tval
378        Text(
379            bs.Lstr(resource='finalScoresText'),
380            color=(0.5, 0.5, 0.5, 1.0),
381            position=(ts_h_offs, ts_height / 2),
382            transition=Text.Transition.IN_RIGHT,
383            transition_delay=tdelay,
384        ).autoretain()
385        tdelay += 4 * t_incr
386
387        v_offs = 0.0
388        tdelay += len(player_entries) * 8 * t_incr
389        for _score, name, prec in player_entries:
390            if not prec.player.in_game:
391                continue
392            tdelay -= 4 * t_incr
393            v_offs -= 40
394            Text(
395                (
396                    str(prec.team.customdata['score'])
397                    if self._is_ffa
398                    else str(prec.score)
399                ),
400                color=(0.5, 0.5, 0.5, 1.0),
401                position=(ts_h_offs + 230, ts_height / 2 + v_offs),
402                h_align=Text.HAlign.RIGHT,
403                transition=Text.Transition.IN_RIGHT,
404                transition_delay=tdelay,
405            ).autoretain()
406            tdelay -= 4 * t_incr
407
408            Image(
409                prec.get_icon(),
410                position=(ts_h_offs - 72, ts_height / 2 + v_offs + 15),
411                scale=(30, 30),
412                transition=Image.Transition.IN_LEFT,
413                transition_delay=tdelay,
414            ).autoretain()
415            Text(
416                bs.Lstr(value=name),
417                position=(ts_h_offs - 50, ts_height / 2 + v_offs + 15),
418                h_align=Text.HAlign.LEFT,
419                v_align=Text.VAlign.CENTER,
420                maxwidth=180,
421                color=bs.safecolor(prec.team.color + (1,)),
422                transition=Text.Transition.IN_RIGHT,
423                transition_delay=tdelay,
424            ).autoretain()
425
426        bs.timer(15.0, bs.WeakCall(self._show_tips))
427
428    def _show_tips(self) -> None:
429        from bascenev1lib.actor.tipstext import TipsText
430
431        self._tips_text = TipsText(offs_y=70)
432
433    def _play_victory_music(self) -> None:
434        # Make sure we don't stomp on the next activity's music choice.
435        if not self.is_transitioning_out():
436            bs.setmusic(bs.MusicType.VICTORY)
437
438    def _show_winner(self, team: bs.SessionTeam) -> None:
439        from bascenev1lib.actor.image import Image
440        from bascenev1lib.actor.zoomtext import ZoomText
441
442        if not self._is_ffa:
443            offs_v = 0.0
444            ZoomText(
445                team.name,
446                position=(0, 97),
447                color=team.color,
448                scale=1.15,
449                jitter=1.0,
450                maxwidth=250,
451            ).autoretain()
452        else:
453            offs_v = -80
454            assert isinstance(self.session, bs.MultiTeamSession)
455            series_length = self.session.get_ffa_series_length()
456            icon: dict | None
457            # Pull live player info if they're still around.
458            if len(team.players) == 1:
459                icon = team.players[0].get_icon()
460                player_name = team.players[0].getname(full=True, icon=False)
461            # Otherwise use the special info we stored when we came in.
462            elif (
463                self._ffa_top_player_info is not None
464                and self._ffa_top_player_info[0] >= series_length
465            ):
466                icon = self._ffa_top_player_info[2]
467                player_name = self._ffa_top_player_info[1]
468            else:
469                icon = None
470                player_name = 'Player Not Found'
471
472            if icon is not None:
473                i = Image(
474                    icon,
475                    position=(0, 143),
476                    scale=(100, 100),
477                ).autoretain()
478                assert i.node
479                bs.animate(i.node, 'opacity', {0.0: 0.0, 0.25: 1.0})
480
481            ZoomText(
482                bs.Lstr(value=player_name),
483                position=(0, 97 + offs_v + (0 if icon is not None else 60)),
484                color=team.color,
485                scale=1.15,
486                jitter=1.0,
487                maxwidth=250,
488            ).autoretain()
489
490        s_extra = 1.0 if self._is_ffa else 1.0
491
492        # Some languages say "FOO WINS" differently for teams vs players.
493        if isinstance(self.session, bs.FreeForAllSession):
494            wins_resource = 'seriesWinLine1PlayerText'
495        else:
496            wins_resource = 'seriesWinLine1TeamText'
497        wins_text = bs.Lstr(resource=wins_resource)
498
499        # Temp - if these come up as the english default, fall-back to the
500        # unified old form which is more likely to be translated.
501        ZoomText(
502            wins_text,
503            position=(0, -10 + offs_v),
504            color=team.color,
505            scale=0.65 * s_extra,
506            jitter=1.0,
507            maxwidth=250,
508        ).autoretain()
509        ZoomText(
510            bs.Lstr(resource='seriesWinLine2Text'),
511            position=(0, -110 + offs_v),
512            scale=1.0 * s_extra,
513            color=team.color,
514            jitter=1.0,
515            maxwidth=250,
516        ).autoretain()
class TeamSeriesVictoryScoreScreenActivity(bascenev1._activity.Activity[bascenev1._player.EmptyPlayer, bascenev1._team.EmptyTeam]):
 18class TeamSeriesVictoryScoreScreenActivity(MultiTeamScoreScreenActivity):
 19    """Final score screen for a team series."""
 20
 21    # Dont' play music by default; (we do manually after a delay).
 22    default_music = None
 23
 24    def __init__(self, settings: dict):
 25        super().__init__(settings=settings)
 26        self._min_view_time = 15.0
 27        self._is_ffa = isinstance(self.session, bs.FreeForAllSession)
 28        self._allow_server_transition = True
 29        self._tips_text = None
 30        self._default_show_tips = False
 31        self._ffa_top_player_info: list[Any] | None = None
 32        self._ffa_top_player_rec: bs.PlayerRecord | None = None
 33
 34    @override
 35    def on_begin(self) -> None:
 36        # pylint: disable=too-many-branches
 37        # pylint: disable=too-many-locals
 38        # pylint: disable=too-many-statements
 39        from bascenev1lib.actor.text import Text
 40        from bascenev1lib.actor.image import Image
 41
 42        bs.set_analytics_screen(
 43            'FreeForAll Series Victory Screen'
 44            if self._is_ffa
 45            else 'Teams Series Victory Screen'
 46        )
 47        assert bs.app.classic is not None
 48        if bs.app.ui_v1.uiscale is bs.UIScale.LARGE:
 49            sval = bs.Lstr(resource='pressAnyKeyButtonPlayAgainText')
 50        else:
 51            sval = bs.Lstr(resource='pressAnyButtonPlayAgainText')
 52        self._show_up_next = False
 53        self._custom_continue_message = sval
 54        super().on_begin()
 55        winning_sessionteam = self.settings_raw['winner']
 56
 57        # Pause a moment before playing victory music.
 58        bs.timer(0.6, bs.WeakCall(self._play_victory_music))
 59        bs.timer(
 60            4.4, bs.WeakCall(self._show_winner, self.settings_raw['winner'])
 61        )
 62        bs.timer(4.6, self._score_display_sound.play)
 63
 64        # Score / Name / Player-record.
 65        player_entries: list[tuple[int, str, bs.PlayerRecord]] = []
 66
 67        # Note: for ffa, exclude players who haven't entered the game yet.
 68        if self._is_ffa:
 69            for _pkey, prec in self.stats.get_records().items():
 70                if prec.player.in_game:
 71                    player_entries.append(
 72                        (
 73                            prec.player.sessionteam.customdata['score'],
 74                            prec.getname(full=True),
 75                            prec,
 76                        )
 77                    )
 78            player_entries.sort(reverse=True, key=lambda x: x[0])
 79            if len(player_entries) > 0:
 80                # Store some info for the top ffa player so we can
 81                # show winner info even if they leave.
 82                self._ffa_top_player_info = list(player_entries[0])
 83                self._ffa_top_player_info[1] = self._ffa_top_player_info[
 84                    2
 85                ].getname()
 86                self._ffa_top_player_info[2] = self._ffa_top_player_info[
 87                    2
 88                ].get_icon()
 89        else:
 90            for _pkey, prec in self.stats.get_records().items():
 91                player_entries.append((prec.score, prec.name_full, prec))
 92            player_entries.sort(reverse=True, key=lambda x: x[0])
 93
 94        ts_height = 300.0
 95        ts_h_offs = -390.0
 96        tval = 6.4
 97        t_incr = 0.12
 98
 99        always_use_first_to = bs.app.lang.get_resource(
100            'bestOfUseFirstToInstead'
101        )
102
103        session = self.session
104        if self._is_ffa:
105            assert isinstance(session, bs.FreeForAllSession)
106            txt = bs.Lstr(
107                value='${A}:',
108                subs=[
109                    (
110                        '${A}',
111                        bs.Lstr(
112                            resource='firstToFinalText',
113                            subs=[
114                                (
115                                    '${COUNT}',
116                                    str(session.get_ffa_series_length()),
117                                )
118                            ],
119                        ),
120                    )
121                ],
122            )
123        else:
124            assert isinstance(session, bs.MultiTeamSession)
125
126            # Some languages may prefer to always show 'first to X' instead of
127            # 'best of X'.
128            # FIXME: This will affect all clients connected to us even if
129            #  they're not using this language. Should try to come up
130            #  with a wording that works everywhere.
131            if always_use_first_to:
132                txt = bs.Lstr(
133                    value='${A}:',
134                    subs=[
135                        (
136                            '${A}',
137                            bs.Lstr(
138                                resource='firstToFinalText',
139                                subs=[
140                                    (
141                                        '${COUNT}',
142                                        str(
143                                            session.get_series_length() / 2 + 1
144                                        ),
145                                    )
146                                ],
147                            ),
148                        )
149                    ],
150                )
151            else:
152                txt = bs.Lstr(
153                    value='${A}:',
154                    subs=[
155                        (
156                            '${A}',
157                            bs.Lstr(
158                                resource='bestOfFinalText',
159                                subs=[
160                                    (
161                                        '${COUNT}',
162                                        str(session.get_series_length()),
163                                    )
164                                ],
165                            ),
166                        )
167                    ],
168                )
169
170        Text(
171            txt,
172            v_align=Text.VAlign.CENTER,
173            maxwidth=300,
174            color=(0.5, 0.5, 0.5, 1.0),
175            position=(0, 220),
176            scale=1.2,
177            transition=Text.Transition.IN_TOP_SLOW,
178            h_align=Text.HAlign.CENTER,
179            transition_delay=t_incr * 4,
180        ).autoretain()
181
182        win_score = (session.get_series_length() - 1) // 2 + 1
183        lose_score = 0
184        for team in self.teams:
185            if team.sessionteam.customdata['score'] != win_score:
186                lose_score = team.sessionteam.customdata['score']
187
188        if not self._is_ffa:
189            Text(
190                bs.Lstr(
191                    resource='gamesToText',
192                    subs=[
193                        ('${WINCOUNT}', str(win_score)),
194                        ('${LOSECOUNT}', str(lose_score)),
195                    ],
196                ),
197                color=(0.5, 0.5, 0.5, 1.0),
198                maxwidth=160,
199                v_align=Text.VAlign.CENTER,
200                position=(0, -215),
201                scale=1.8,
202                transition=Text.Transition.IN_LEFT,
203                h_align=Text.HAlign.CENTER,
204                transition_delay=4.8 + t_incr * 4,
205            ).autoretain()
206
207        if self._is_ffa:
208            v_extra = 120
209        else:
210            v_extra = 0
211
212        mvp: bs.PlayerRecord | None = None
213        mvp_name: str | None = None
214
215        # Show game MVP.
216        if not self._is_ffa:
217            mvp, mvp_name = None, None
218            for entry in player_entries:
219                if entry[2].team == winning_sessionteam:
220                    mvp = entry[2]
221                    mvp_name = entry[1]
222                    break
223            if mvp is not None:
224                Text(
225                    bs.Lstr(resource='mostValuablePlayerText'),
226                    color=(0.5, 0.5, 0.5, 1.0),
227                    v_align=Text.VAlign.CENTER,
228                    maxwidth=300,
229                    position=(180, ts_height / 2 + 15),
230                    transition=Text.Transition.IN_LEFT,
231                    h_align=Text.HAlign.LEFT,
232                    transition_delay=tval,
233                ).autoretain()
234                tval += 4 * t_incr
235
236                Image(
237                    mvp.get_icon(),
238                    position=(230, ts_height / 2 - 55 + 14 - 5),
239                    scale=(70, 70),
240                    transition=Image.Transition.IN_LEFT,
241                    transition_delay=tval,
242                ).autoretain()
243                assert mvp_name is not None
244                Text(
245                    bs.Lstr(value=mvp_name),
246                    position=(280, ts_height / 2 - 55 + 15 - 5),
247                    h_align=Text.HAlign.LEFT,
248                    v_align=Text.VAlign.CENTER,
249                    maxwidth=170,
250                    scale=1.3,
251                    color=bs.safecolor(mvp.team.color + (1,)),
252                    transition=Text.Transition.IN_LEFT,
253                    transition_delay=tval,
254                ).autoretain()
255                tval += 4 * t_incr
256
257        # Most violent.
258        most_kills = 0
259        for entry in player_entries:
260            if entry[2].kill_count >= most_kills:
261                mvp = entry[2]
262                mvp_name = entry[1]
263                most_kills = entry[2].kill_count
264        if mvp is not None:
265            Text(
266                bs.Lstr(resource='mostViolentPlayerText'),
267                color=(0.5, 0.5, 0.5, 1.0),
268                v_align=Text.VAlign.CENTER,
269                maxwidth=300,
270                position=(180, ts_height / 2 - 150 + v_extra + 15),
271                transition=Text.Transition.IN_LEFT,
272                h_align=Text.HAlign.LEFT,
273                transition_delay=tval,
274            ).autoretain()
275            Text(
276                bs.Lstr(
277                    value='(${A})',
278                    subs=[
279                        (
280                            '${A}',
281                            bs.Lstr(
282                                resource='killsTallyText',
283                                subs=[('${COUNT}', str(most_kills))],
284                            ),
285                        )
286                    ],
287                ),
288                position=(260, ts_height / 2 - 150 - 15 + v_extra),
289                color=(0.3, 0.3, 0.3, 1.0),
290                scale=0.6,
291                h_align=Text.HAlign.LEFT,
292                transition=Text.Transition.IN_LEFT,
293                transition_delay=tval,
294            ).autoretain()
295            tval += 4 * t_incr
296
297            Image(
298                mvp.get_icon(),
299                position=(233, ts_height / 2 - 150 - 30 - 46 + 25 + v_extra),
300                scale=(50, 50),
301                transition=Image.Transition.IN_LEFT,
302                transition_delay=tval,
303            ).autoretain()
304            assert mvp_name is not None
305            Text(
306                bs.Lstr(value=mvp_name),
307                position=(270, ts_height / 2 - 150 - 30 - 36 + v_extra + 15),
308                h_align=Text.HAlign.LEFT,
309                v_align=Text.VAlign.CENTER,
310                maxwidth=180,
311                color=bs.safecolor(mvp.team.color + (1,)),
312                transition=Text.Transition.IN_LEFT,
313                transition_delay=tval,
314            ).autoretain()
315            tval += 4 * t_incr
316
317        # Most killed.
318        most_killed = 0
319        mkp, mkp_name = None, None
320        for entry in player_entries:
321            if entry[2].killed_count >= most_killed:
322                mkp = entry[2]
323                mkp_name = entry[1]
324                most_killed = entry[2].killed_count
325        if mkp is not None:
326            Text(
327                bs.Lstr(resource='mostViolatedPlayerText'),
328                color=(0.5, 0.5, 0.5, 1.0),
329                v_align=Text.VAlign.CENTER,
330                maxwidth=300,
331                position=(180, ts_height / 2 - 300 + v_extra + 15),
332                transition=Text.Transition.IN_LEFT,
333                h_align=Text.HAlign.LEFT,
334                transition_delay=tval,
335            ).autoretain()
336            Text(
337                bs.Lstr(
338                    value='(${A})',
339                    subs=[
340                        (
341                            '${A}',
342                            bs.Lstr(
343                                resource='deathsTallyText',
344                                subs=[('${COUNT}', str(most_killed))],
345                            ),
346                        )
347                    ],
348                ),
349                position=(260, ts_height / 2 - 300 - 15 + v_extra),
350                h_align=Text.HAlign.LEFT,
351                scale=0.6,
352                color=(0.3, 0.3, 0.3, 1.0),
353                transition=Text.Transition.IN_LEFT,
354                transition_delay=tval,
355            ).autoretain()
356            tval += 4 * t_incr
357            Image(
358                mkp.get_icon(),
359                position=(233, ts_height / 2 - 300 - 30 - 46 + 25 + v_extra),
360                scale=(50, 50),
361                transition=Image.Transition.IN_LEFT,
362                transition_delay=tval,
363            ).autoretain()
364            assert mkp_name is not None
365            Text(
366                bs.Lstr(value=mkp_name),
367                position=(270, ts_height / 2 - 300 - 30 - 36 + v_extra + 15),
368                h_align=Text.HAlign.LEFT,
369                v_align=Text.VAlign.CENTER,
370                color=bs.safecolor(mkp.team.color + (1,)),
371                maxwidth=180,
372                transition=Text.Transition.IN_LEFT,
373                transition_delay=tval,
374            ).autoretain()
375            tval += 4 * t_incr
376
377        # Now show individual scores.
378        tdelay = tval
379        Text(
380            bs.Lstr(resource='finalScoresText'),
381            color=(0.5, 0.5, 0.5, 1.0),
382            position=(ts_h_offs, ts_height / 2),
383            transition=Text.Transition.IN_RIGHT,
384            transition_delay=tdelay,
385        ).autoretain()
386        tdelay += 4 * t_incr
387
388        v_offs = 0.0
389        tdelay += len(player_entries) * 8 * t_incr
390        for _score, name, prec in player_entries:
391            if not prec.player.in_game:
392                continue
393            tdelay -= 4 * t_incr
394            v_offs -= 40
395            Text(
396                (
397                    str(prec.team.customdata['score'])
398                    if self._is_ffa
399                    else str(prec.score)
400                ),
401                color=(0.5, 0.5, 0.5, 1.0),
402                position=(ts_h_offs + 230, ts_height / 2 + v_offs),
403                h_align=Text.HAlign.RIGHT,
404                transition=Text.Transition.IN_RIGHT,
405                transition_delay=tdelay,
406            ).autoretain()
407            tdelay -= 4 * t_incr
408
409            Image(
410                prec.get_icon(),
411                position=(ts_h_offs - 72, ts_height / 2 + v_offs + 15),
412                scale=(30, 30),
413                transition=Image.Transition.IN_LEFT,
414                transition_delay=tdelay,
415            ).autoretain()
416            Text(
417                bs.Lstr(value=name),
418                position=(ts_h_offs - 50, ts_height / 2 + v_offs + 15),
419                h_align=Text.HAlign.LEFT,
420                v_align=Text.VAlign.CENTER,
421                maxwidth=180,
422                color=bs.safecolor(prec.team.color + (1,)),
423                transition=Text.Transition.IN_RIGHT,
424                transition_delay=tdelay,
425            ).autoretain()
426
427        bs.timer(15.0, bs.WeakCall(self._show_tips))
428
429    def _show_tips(self) -> None:
430        from bascenev1lib.actor.tipstext import TipsText
431
432        self._tips_text = TipsText(offs_y=70)
433
434    def _play_victory_music(self) -> None:
435        # Make sure we don't stomp on the next activity's music choice.
436        if not self.is_transitioning_out():
437            bs.setmusic(bs.MusicType.VICTORY)
438
439    def _show_winner(self, team: bs.SessionTeam) -> None:
440        from bascenev1lib.actor.image import Image
441        from bascenev1lib.actor.zoomtext import ZoomText
442
443        if not self._is_ffa:
444            offs_v = 0.0
445            ZoomText(
446                team.name,
447                position=(0, 97),
448                color=team.color,
449                scale=1.15,
450                jitter=1.0,
451                maxwidth=250,
452            ).autoretain()
453        else:
454            offs_v = -80
455            assert isinstance(self.session, bs.MultiTeamSession)
456            series_length = self.session.get_ffa_series_length()
457            icon: dict | None
458            # Pull live player info if they're still around.
459            if len(team.players) == 1:
460                icon = team.players[0].get_icon()
461                player_name = team.players[0].getname(full=True, icon=False)
462            # Otherwise use the special info we stored when we came in.
463            elif (
464                self._ffa_top_player_info is not None
465                and self._ffa_top_player_info[0] >= series_length
466            ):
467                icon = self._ffa_top_player_info[2]
468                player_name = self._ffa_top_player_info[1]
469            else:
470                icon = None
471                player_name = 'Player Not Found'
472
473            if icon is not None:
474                i = Image(
475                    icon,
476                    position=(0, 143),
477                    scale=(100, 100),
478                ).autoretain()
479                assert i.node
480                bs.animate(i.node, 'opacity', {0.0: 0.0, 0.25: 1.0})
481
482            ZoomText(
483                bs.Lstr(value=player_name),
484                position=(0, 97 + offs_v + (0 if icon is not None else 60)),
485                color=team.color,
486                scale=1.15,
487                jitter=1.0,
488                maxwidth=250,
489            ).autoretain()
490
491        s_extra = 1.0 if self._is_ffa else 1.0
492
493        # Some languages say "FOO WINS" differently for teams vs players.
494        if isinstance(self.session, bs.FreeForAllSession):
495            wins_resource = 'seriesWinLine1PlayerText'
496        else:
497            wins_resource = 'seriesWinLine1TeamText'
498        wins_text = bs.Lstr(resource=wins_resource)
499
500        # Temp - if these come up as the english default, fall-back to the
501        # unified old form which is more likely to be translated.
502        ZoomText(
503            wins_text,
504            position=(0, -10 + offs_v),
505            color=team.color,
506            scale=0.65 * s_extra,
507            jitter=1.0,
508            maxwidth=250,
509        ).autoretain()
510        ZoomText(
511            bs.Lstr(resource='seriesWinLine2Text'),
512            position=(0, -110 + offs_v),
513            scale=1.0 * s_extra,
514            color=team.color,
515            jitter=1.0,
516            maxwidth=250,
517        ).autoretain()

Final score screen for a team series.

TeamSeriesVictoryScoreScreenActivity(settings: dict)
24    def __init__(self, settings: dict):
25        super().__init__(settings=settings)
26        self._min_view_time = 15.0
27        self._is_ffa = isinstance(self.session, bs.FreeForAllSession)
28        self._allow_server_transition = True
29        self._tips_text = None
30        self._default_show_tips = False
31        self._ffa_top_player_info: list[Any] | None = None
32        self._ffa_top_player_rec: bs.PlayerRecord | None = None

Creates an Activity in the current bascenev1.Session.

The activity will not be actually run until bascenev1.Session.setactivity is called. 'settings' should be a dict of key/value pairs specific to the activity.

Activities should preload as much of their media/etc as possible in their constructor, but none of it should actually be used until they are transitioned in.

default_music = None
@override
def on_begin(self) -> None:
 34    @override
 35    def on_begin(self) -> None:
 36        # pylint: disable=too-many-branches
 37        # pylint: disable=too-many-locals
 38        # pylint: disable=too-many-statements
 39        from bascenev1lib.actor.text import Text
 40        from bascenev1lib.actor.image import Image
 41
 42        bs.set_analytics_screen(
 43            'FreeForAll Series Victory Screen'
 44            if self._is_ffa
 45            else 'Teams Series Victory Screen'
 46        )
 47        assert bs.app.classic is not None
 48        if bs.app.ui_v1.uiscale is bs.UIScale.LARGE:
 49            sval = bs.Lstr(resource='pressAnyKeyButtonPlayAgainText')
 50        else:
 51            sval = bs.Lstr(resource='pressAnyButtonPlayAgainText')
 52        self._show_up_next = False
 53        self._custom_continue_message = sval
 54        super().on_begin()
 55        winning_sessionteam = self.settings_raw['winner']
 56
 57        # Pause a moment before playing victory music.
 58        bs.timer(0.6, bs.WeakCall(self._play_victory_music))
 59        bs.timer(
 60            4.4, bs.WeakCall(self._show_winner, self.settings_raw['winner'])
 61        )
 62        bs.timer(4.6, self._score_display_sound.play)
 63
 64        # Score / Name / Player-record.
 65        player_entries: list[tuple[int, str, bs.PlayerRecord]] = []
 66
 67        # Note: for ffa, exclude players who haven't entered the game yet.
 68        if self._is_ffa:
 69            for _pkey, prec in self.stats.get_records().items():
 70                if prec.player.in_game:
 71                    player_entries.append(
 72                        (
 73                            prec.player.sessionteam.customdata['score'],
 74                            prec.getname(full=True),
 75                            prec,
 76                        )
 77                    )
 78            player_entries.sort(reverse=True, key=lambda x: x[0])
 79            if len(player_entries) > 0:
 80                # Store some info for the top ffa player so we can
 81                # show winner info even if they leave.
 82                self._ffa_top_player_info = list(player_entries[0])
 83                self._ffa_top_player_info[1] = self._ffa_top_player_info[
 84                    2
 85                ].getname()
 86                self._ffa_top_player_info[2] = self._ffa_top_player_info[
 87                    2
 88                ].get_icon()
 89        else:
 90            for _pkey, prec in self.stats.get_records().items():
 91                player_entries.append((prec.score, prec.name_full, prec))
 92            player_entries.sort(reverse=True, key=lambda x: x[0])
 93
 94        ts_height = 300.0
 95        ts_h_offs = -390.0
 96        tval = 6.4
 97        t_incr = 0.12
 98
 99        always_use_first_to = bs.app.lang.get_resource(
100            'bestOfUseFirstToInstead'
101        )
102
103        session = self.session
104        if self._is_ffa:
105            assert isinstance(session, bs.FreeForAllSession)
106            txt = bs.Lstr(
107                value='${A}:',
108                subs=[
109                    (
110                        '${A}',
111                        bs.Lstr(
112                            resource='firstToFinalText',
113                            subs=[
114                                (
115                                    '${COUNT}',
116                                    str(session.get_ffa_series_length()),
117                                )
118                            ],
119                        ),
120                    )
121                ],
122            )
123        else:
124            assert isinstance(session, bs.MultiTeamSession)
125
126            # Some languages may prefer to always show 'first to X' instead of
127            # 'best of X'.
128            # FIXME: This will affect all clients connected to us even if
129            #  they're not using this language. Should try to come up
130            #  with a wording that works everywhere.
131            if always_use_first_to:
132                txt = bs.Lstr(
133                    value='${A}:',
134                    subs=[
135                        (
136                            '${A}',
137                            bs.Lstr(
138                                resource='firstToFinalText',
139                                subs=[
140                                    (
141                                        '${COUNT}',
142                                        str(
143                                            session.get_series_length() / 2 + 1
144                                        ),
145                                    )
146                                ],
147                            ),
148                        )
149                    ],
150                )
151            else:
152                txt = bs.Lstr(
153                    value='${A}:',
154                    subs=[
155                        (
156                            '${A}',
157                            bs.Lstr(
158                                resource='bestOfFinalText',
159                                subs=[
160                                    (
161                                        '${COUNT}',
162                                        str(session.get_series_length()),
163                                    )
164                                ],
165                            ),
166                        )
167                    ],
168                )
169
170        Text(
171            txt,
172            v_align=Text.VAlign.CENTER,
173            maxwidth=300,
174            color=(0.5, 0.5, 0.5, 1.0),
175            position=(0, 220),
176            scale=1.2,
177            transition=Text.Transition.IN_TOP_SLOW,
178            h_align=Text.HAlign.CENTER,
179            transition_delay=t_incr * 4,
180        ).autoretain()
181
182        win_score = (session.get_series_length() - 1) // 2 + 1
183        lose_score = 0
184        for team in self.teams:
185            if team.sessionteam.customdata['score'] != win_score:
186                lose_score = team.sessionteam.customdata['score']
187
188        if not self._is_ffa:
189            Text(
190                bs.Lstr(
191                    resource='gamesToText',
192                    subs=[
193                        ('${WINCOUNT}', str(win_score)),
194                        ('${LOSECOUNT}', str(lose_score)),
195                    ],
196                ),
197                color=(0.5, 0.5, 0.5, 1.0),
198                maxwidth=160,
199                v_align=Text.VAlign.CENTER,
200                position=(0, -215),
201                scale=1.8,
202                transition=Text.Transition.IN_LEFT,
203                h_align=Text.HAlign.CENTER,
204                transition_delay=4.8 + t_incr * 4,
205            ).autoretain()
206
207        if self._is_ffa:
208            v_extra = 120
209        else:
210            v_extra = 0
211
212        mvp: bs.PlayerRecord | None = None
213        mvp_name: str | None = None
214
215        # Show game MVP.
216        if not self._is_ffa:
217            mvp, mvp_name = None, None
218            for entry in player_entries:
219                if entry[2].team == winning_sessionteam:
220                    mvp = entry[2]
221                    mvp_name = entry[1]
222                    break
223            if mvp is not None:
224                Text(
225                    bs.Lstr(resource='mostValuablePlayerText'),
226                    color=(0.5, 0.5, 0.5, 1.0),
227                    v_align=Text.VAlign.CENTER,
228                    maxwidth=300,
229                    position=(180, ts_height / 2 + 15),
230                    transition=Text.Transition.IN_LEFT,
231                    h_align=Text.HAlign.LEFT,
232                    transition_delay=tval,
233                ).autoretain()
234                tval += 4 * t_incr
235
236                Image(
237                    mvp.get_icon(),
238                    position=(230, ts_height / 2 - 55 + 14 - 5),
239                    scale=(70, 70),
240                    transition=Image.Transition.IN_LEFT,
241                    transition_delay=tval,
242                ).autoretain()
243                assert mvp_name is not None
244                Text(
245                    bs.Lstr(value=mvp_name),
246                    position=(280, ts_height / 2 - 55 + 15 - 5),
247                    h_align=Text.HAlign.LEFT,
248                    v_align=Text.VAlign.CENTER,
249                    maxwidth=170,
250                    scale=1.3,
251                    color=bs.safecolor(mvp.team.color + (1,)),
252                    transition=Text.Transition.IN_LEFT,
253                    transition_delay=tval,
254                ).autoretain()
255                tval += 4 * t_incr
256
257        # Most violent.
258        most_kills = 0
259        for entry in player_entries:
260            if entry[2].kill_count >= most_kills:
261                mvp = entry[2]
262                mvp_name = entry[1]
263                most_kills = entry[2].kill_count
264        if mvp is not None:
265            Text(
266                bs.Lstr(resource='mostViolentPlayerText'),
267                color=(0.5, 0.5, 0.5, 1.0),
268                v_align=Text.VAlign.CENTER,
269                maxwidth=300,
270                position=(180, ts_height / 2 - 150 + v_extra + 15),
271                transition=Text.Transition.IN_LEFT,
272                h_align=Text.HAlign.LEFT,
273                transition_delay=tval,
274            ).autoretain()
275            Text(
276                bs.Lstr(
277                    value='(${A})',
278                    subs=[
279                        (
280                            '${A}',
281                            bs.Lstr(
282                                resource='killsTallyText',
283                                subs=[('${COUNT}', str(most_kills))],
284                            ),
285                        )
286                    ],
287                ),
288                position=(260, ts_height / 2 - 150 - 15 + v_extra),
289                color=(0.3, 0.3, 0.3, 1.0),
290                scale=0.6,
291                h_align=Text.HAlign.LEFT,
292                transition=Text.Transition.IN_LEFT,
293                transition_delay=tval,
294            ).autoretain()
295            tval += 4 * t_incr
296
297            Image(
298                mvp.get_icon(),
299                position=(233, ts_height / 2 - 150 - 30 - 46 + 25 + v_extra),
300                scale=(50, 50),
301                transition=Image.Transition.IN_LEFT,
302                transition_delay=tval,
303            ).autoretain()
304            assert mvp_name is not None
305            Text(
306                bs.Lstr(value=mvp_name),
307                position=(270, ts_height / 2 - 150 - 30 - 36 + v_extra + 15),
308                h_align=Text.HAlign.LEFT,
309                v_align=Text.VAlign.CENTER,
310                maxwidth=180,
311                color=bs.safecolor(mvp.team.color + (1,)),
312                transition=Text.Transition.IN_LEFT,
313                transition_delay=tval,
314            ).autoretain()
315            tval += 4 * t_incr
316
317        # Most killed.
318        most_killed = 0
319        mkp, mkp_name = None, None
320        for entry in player_entries:
321            if entry[2].killed_count >= most_killed:
322                mkp = entry[2]
323                mkp_name = entry[1]
324                most_killed = entry[2].killed_count
325        if mkp is not None:
326            Text(
327                bs.Lstr(resource='mostViolatedPlayerText'),
328                color=(0.5, 0.5, 0.5, 1.0),
329                v_align=Text.VAlign.CENTER,
330                maxwidth=300,
331                position=(180, ts_height / 2 - 300 + v_extra + 15),
332                transition=Text.Transition.IN_LEFT,
333                h_align=Text.HAlign.LEFT,
334                transition_delay=tval,
335            ).autoretain()
336            Text(
337                bs.Lstr(
338                    value='(${A})',
339                    subs=[
340                        (
341                            '${A}',
342                            bs.Lstr(
343                                resource='deathsTallyText',
344                                subs=[('${COUNT}', str(most_killed))],
345                            ),
346                        )
347                    ],
348                ),
349                position=(260, ts_height / 2 - 300 - 15 + v_extra),
350                h_align=Text.HAlign.LEFT,
351                scale=0.6,
352                color=(0.3, 0.3, 0.3, 1.0),
353                transition=Text.Transition.IN_LEFT,
354                transition_delay=tval,
355            ).autoretain()
356            tval += 4 * t_incr
357            Image(
358                mkp.get_icon(),
359                position=(233, ts_height / 2 - 300 - 30 - 46 + 25 + v_extra),
360                scale=(50, 50),
361                transition=Image.Transition.IN_LEFT,
362                transition_delay=tval,
363            ).autoretain()
364            assert mkp_name is not None
365            Text(
366                bs.Lstr(value=mkp_name),
367                position=(270, ts_height / 2 - 300 - 30 - 36 + v_extra + 15),
368                h_align=Text.HAlign.LEFT,
369                v_align=Text.VAlign.CENTER,
370                color=bs.safecolor(mkp.team.color + (1,)),
371                maxwidth=180,
372                transition=Text.Transition.IN_LEFT,
373                transition_delay=tval,
374            ).autoretain()
375            tval += 4 * t_incr
376
377        # Now show individual scores.
378        tdelay = tval
379        Text(
380            bs.Lstr(resource='finalScoresText'),
381            color=(0.5, 0.5, 0.5, 1.0),
382            position=(ts_h_offs, ts_height / 2),
383            transition=Text.Transition.IN_RIGHT,
384            transition_delay=tdelay,
385        ).autoretain()
386        tdelay += 4 * t_incr
387
388        v_offs = 0.0
389        tdelay += len(player_entries) * 8 * t_incr
390        for _score, name, prec in player_entries:
391            if not prec.player.in_game:
392                continue
393            tdelay -= 4 * t_incr
394            v_offs -= 40
395            Text(
396                (
397                    str(prec.team.customdata['score'])
398                    if self._is_ffa
399                    else str(prec.score)
400                ),
401                color=(0.5, 0.5, 0.5, 1.0),
402                position=(ts_h_offs + 230, ts_height / 2 + v_offs),
403                h_align=Text.HAlign.RIGHT,
404                transition=Text.Transition.IN_RIGHT,
405                transition_delay=tdelay,
406            ).autoretain()
407            tdelay -= 4 * t_incr
408
409            Image(
410                prec.get_icon(),
411                position=(ts_h_offs - 72, ts_height / 2 + v_offs + 15),
412                scale=(30, 30),
413                transition=Image.Transition.IN_LEFT,
414                transition_delay=tdelay,
415            ).autoretain()
416            Text(
417                bs.Lstr(value=name),
418                position=(ts_h_offs - 50, ts_height / 2 + v_offs + 15),
419                h_align=Text.HAlign.LEFT,
420                v_align=Text.VAlign.CENTER,
421                maxwidth=180,
422                color=bs.safecolor(prec.team.color + (1,)),
423                transition=Text.Transition.IN_RIGHT,
424                transition_delay=tdelay,
425            ).autoretain()
426
427        bs.timer(15.0, bs.WeakCall(self._show_tips))

Called once the previous Activity has finished transitioning out.

At this point the activity's initial players and teams are filled in and it should begin its actual game logic.

Inherited Members
bascenev1lib.activity.multiteamscore.MultiTeamScoreScreenActivity
show_player_scores
bascenev1._activitytypes.ScoreScreenActivity
transition_time
inherits_tint
inherits_vr_camera_offset
use_fixed_vr_overlay
on_player_join
on_transition_in
bascenev1._activity.Activity
settings_raw
teams
players
announce_player_deaths
is_joining_activity
allow_pausing
allow_kick_idle_players
slow_motion
inherits_slow_motion
inherits_music
inherits_vr_overlay_center
allow_mid_activity_joins
can_show_ad_on_death
paused_text
preloads
lobby
context
globalsnode
stats
on_expire
customdata
expired
playertype
teamtype
retain_actor
add_actor_weak_ref
session
on_player_leave
on_team_join
on_team_leave
on_transition_out
handlemessage
has_transitioned_in
has_begun
has_ended
is_transitioning_out
transition_out
end
create_player
create_team
bascenev1._dependency.DependencyComponent
dep_is_present
get_dynamic_deps