bascenev1lib.activity.freeforallvictory

Functionality related to the final screen in free-for-all games.

  1# Released under the MIT License. See LICENSE for details.
  2#
  3"""Functionality related to the final screen in free-for-all games."""
  4
  5from __future__ import annotations
  6
  7from typing import TYPE_CHECKING
  8
  9from bascenev1lib.activity.multiteamscore import MultiTeamScoreScreenActivity
 10import bascenev1 as bs
 11
 12if TYPE_CHECKING:
 13    from typing import Any
 14
 15
 16class FreeForAllVictoryScoreScreenActivity(MultiTeamScoreScreenActivity):
 17    """Score screen shown at after free-for-all rounds."""
 18
 19    def __init__(self, settings: dict):
 20        super().__init__(settings=settings)
 21
 22        # Keep prev activity alive while we fade in.
 23        self.transition_time = 0.5
 24        self._cymbal_sound = bs.getsound('cymbal')
 25
 26    def on_begin(self) -> None:
 27        # pylint: disable=too-many-locals
 28        # pylint: disable=too-many-statements
 29        from bascenev1lib.actor.text import Text
 30        from bascenev1lib.actor.image import Image
 31
 32        bs.set_analytics_screen('FreeForAll Score Screen')
 33        super().on_begin()
 34
 35        y_base = 100.0
 36        ts_h_offs = -305.0
 37        tdelay = 1.0
 38        scale = 1.2
 39        spacing = 37.0
 40
 41        # We include name and previous score in the sort to reduce the amount
 42        # of random jumping around the list we do in cases of ties.
 43        player_order_prev = list(self.players)
 44        player_order_prev.sort(
 45            reverse=True,
 46            key=lambda p: (
 47                p.team.sessionteam.customdata['previous_score'],
 48                p.getname(full=True),
 49            ),
 50        )
 51        player_order = list(self.players)
 52        player_order.sort(
 53            reverse=True,
 54            key=lambda p: (
 55                p.team.sessionteam.customdata['score'],
 56                p.team.sessionteam.customdata['score'],
 57                p.getname(full=True),
 58            ),
 59        )
 60
 61        v_offs = -74.0 + spacing * len(player_order_prev) * 0.5
 62        delay1 = 1.3 + 0.1
 63        delay2 = 2.9 + 0.1
 64        delay3 = 2.9 + 0.1
 65        order_change = player_order != player_order_prev
 66
 67        if order_change:
 68            delay3 += 1.5
 69
 70        bs.timer(0.3, self._score_display_sound.play)
 71        results = self.settings_raw['results']
 72        assert isinstance(results, bs.GameResults)
 73        self.show_player_scores(
 74            delay=0.001, results=results, scale=1.2, x_offset=-110.0
 75        )
 76
 77        sound_times: set[float] = set()
 78
 79        def _scoretxt(
 80            text: str,
 81            x_offs: float,
 82            y_offs: float,
 83            highlight: bool,
 84            delay: float,
 85            extrascale: float,
 86            flash: bool = False,
 87        ) -> Text:
 88            return Text(
 89                text,
 90                position=(
 91                    ts_h_offs + x_offs * scale,
 92                    y_base + (y_offs + v_offs + 2.0) * scale,
 93                ),
 94                scale=scale * extrascale,
 95                color=(
 96                    (1.0, 0.7, 0.3, 1.0) if highlight else (0.7, 0.7, 0.7, 0.7)
 97                ),
 98                h_align=Text.HAlign.RIGHT,
 99                transition=Text.Transition.IN_LEFT,
100                transition_delay=tdelay + delay,
101                flash=flash,
102            ).autoretain()
103
104        v_offs -= spacing
105        slide_amt = 0.0
106        transtime = 0.250
107        transtime2 = 0.250
108
109        session = self.session
110        assert isinstance(session, bs.FreeForAllSession)
111        title = Text(
112            bs.Lstr(
113                resource='firstToSeriesText',
114                subs=[('${COUNT}', str(session.get_ffa_series_length()))],
115            ),
116            scale=1.05 * scale,
117            position=(
118                ts_h_offs - 0.0 * scale,
119                y_base + (v_offs + 50.0) * scale,
120            ),
121            h_align=Text.HAlign.CENTER,
122            color=(0.5, 0.5, 0.5, 0.5),
123            transition=Text.Transition.IN_LEFT,
124            transition_delay=tdelay,
125        ).autoretain()
126
127        v_offs -= 25
128        v_offs_start = v_offs
129
130        bs.timer(
131            tdelay + delay3,
132            bs.WeakCall(
133                self._safe_animate,
134                title.position_combine,
135                'input0',
136                {
137                    0.0: ts_h_offs - 0.0 * scale,
138                    transtime2: ts_h_offs - (0.0 + slide_amt) * scale,
139                },
140            ),
141        )
142
143        for i, player in enumerate(player_order_prev):
144            v_offs_2 = v_offs_start - spacing * (player_order.index(player))
145            bs.timer(tdelay + 0.3, self._score_display_sound_small.play)
146            if order_change:
147                bs.timer(tdelay + delay2 + 0.1, self._cymbal_sound.play)
148            img = Image(
149                player.get_icon(),
150                position=(
151                    ts_h_offs - 72.0 * scale,
152                    y_base + (v_offs + 15.0) * scale,
153                ),
154                scale=(30.0 * scale, 30.0 * scale),
155                transition=Image.Transition.IN_LEFT,
156                transition_delay=tdelay,
157            ).autoretain()
158            bs.timer(
159                tdelay + delay2,
160                bs.WeakCall(
161                    self._safe_animate,
162                    img.position_combine,
163                    'input1',
164                    {
165                        0: y_base + (v_offs + 15.0) * scale,
166                        transtime: y_base + (v_offs_2 + 15.0) * scale,
167                    },
168                ),
169            )
170            bs.timer(
171                tdelay + delay3,
172                bs.WeakCall(
173                    self._safe_animate,
174                    img.position_combine,
175                    'input0',
176                    {
177                        0: ts_h_offs - 72.0 * scale,
178                        transtime2: ts_h_offs - (72.0 + slide_amt) * scale,
179                    },
180                ),
181            )
182            txt = Text(
183                bs.Lstr(value=player.getname(full=True)),
184                maxwidth=130.0,
185                scale=0.75 * scale,
186                position=(
187                    ts_h_offs - 50.0 * scale,
188                    y_base + (v_offs + 15.0) * scale,
189                ),
190                h_align=Text.HAlign.LEFT,
191                v_align=Text.VAlign.CENTER,
192                color=bs.safecolor(player.team.color + (1,)),
193                transition=Text.Transition.IN_LEFT,
194                transition_delay=tdelay,
195            ).autoretain()
196            bs.timer(
197                tdelay + delay2,
198                bs.WeakCall(
199                    self._safe_animate,
200                    txt.position_combine,
201                    'input1',
202                    {
203                        0: y_base + (v_offs + 15.0) * scale,
204                        transtime: y_base + (v_offs_2 + 15.0) * scale,
205                    },
206                ),
207            )
208            bs.timer(
209                tdelay + delay3,
210                bs.WeakCall(
211                    self._safe_animate,
212                    txt.position_combine,
213                    'input0',
214                    {
215                        0: ts_h_offs - 50.0 * scale,
216                        transtime2: ts_h_offs - (50.0 + slide_amt) * scale,
217                    },
218                ),
219            )
220
221            txt_num = Text(
222                '#' + str(i + 1),
223                scale=0.55 * scale,
224                position=(
225                    ts_h_offs - 95.0 * scale,
226                    y_base + (v_offs + 8.0) * scale,
227                ),
228                h_align=Text.HAlign.RIGHT,
229                color=(0.6, 0.6, 0.6, 0.6),
230                transition=Text.Transition.IN_LEFT,
231                transition_delay=tdelay,
232            ).autoretain()
233            bs.timer(
234                tdelay + delay3,
235                bs.WeakCall(
236                    self._safe_animate,
237                    txt_num.position_combine,
238                    'input0',
239                    {
240                        0: ts_h_offs - 95.0 * scale,
241                        transtime2: ts_h_offs - (95.0 + slide_amt) * scale,
242                    },
243                ),
244            )
245
246            s_txt = _scoretxt(
247                str(player.team.sessionteam.customdata['previous_score']),
248                80,
249                0,
250                False,
251                0,
252                1.0,
253            )
254            bs.timer(
255                tdelay + delay2,
256                bs.WeakCall(
257                    self._safe_animate,
258                    s_txt.position_combine,
259                    'input1',
260                    {
261                        0: y_base + (v_offs + 2.0) * scale,
262                        transtime: y_base + (v_offs_2 + 2.0) * scale,
263                    },
264                ),
265            )
266            bs.timer(
267                tdelay + delay3,
268                bs.WeakCall(
269                    self._safe_animate,
270                    s_txt.position_combine,
271                    'input0',
272                    {
273                        0: ts_h_offs + 80.0 * scale,
274                        transtime2: ts_h_offs + (80.0 - slide_amt) * scale,
275                    },
276                ),
277            )
278
279            score_change = (
280                player.team.sessionteam.customdata['score']
281                - player.team.sessionteam.customdata['previous_score']
282            )
283            if score_change > 0:
284                xval = 113
285                yval = 3.0
286                s_txt_2 = _scoretxt(
287                    '+' + str(score_change),
288                    xval,
289                    yval,
290                    True,
291                    0,
292                    0.7,
293                    flash=True,
294                )
295                bs.timer(
296                    tdelay + delay2,
297                    bs.WeakCall(
298                        self._safe_animate,
299                        s_txt_2.position_combine,
300                        'input1',
301                        {
302                            0: y_base + (v_offs + yval + 2.0) * scale,
303                            transtime: y_base + (v_offs_2 + yval + 2.0) * scale,
304                        },
305                    ),
306                )
307                bs.timer(
308                    tdelay + delay3,
309                    bs.WeakCall(
310                        self._safe_animate,
311                        s_txt_2.position_combine,
312                        'input0',
313                        {
314                            0: ts_h_offs + xval * scale,
315                            transtime2: ts_h_offs + (xval - slide_amt) * scale,
316                        },
317                    ),
318                )
319
320                def _safesetattr(
321                    node: bs.Node | None, attr: str, value: Any
322                ) -> None:
323                    if node:
324                        setattr(node, attr, value)
325
326                bs.timer(
327                    tdelay + delay1,
328                    bs.Call(_safesetattr, s_txt.node, 'color', (1, 1, 1, 1)),
329                )
330                for j in range(score_change):
331                    bs.timer(
332                        (tdelay + delay1 + 0.15 * j),
333                        bs.Call(
334                            _safesetattr,
335                            s_txt.node,
336                            'text',
337                            str(
338                                player.team.sessionteam.customdata[
339                                    'previous_score'
340                                ]
341                                + j
342                                + 1
343                            ),
344                        ),
345                    )
346                    tfin = tdelay + delay1 + 0.15 * j
347                    if tfin not in sound_times:
348                        sound_times.add(tfin)
349                        bs.timer(tfin, self._score_display_sound_small.play)
350            v_offs -= spacing
351
352    def _safe_animate(
353        self, node: bs.Node | None, attr: str, keys: dict[float, float]
354    ) -> None:
355        """Run an animation on a node if the node still exists."""
356        if node:
357            bs.animate(node, attr, keys)
class FreeForAllVictoryScoreScreenActivity(bascenev1._activity.Activity[bascenev1._player.EmptyPlayer, bascenev1._team.EmptyTeam]):
 17class FreeForAllVictoryScoreScreenActivity(MultiTeamScoreScreenActivity):
 18    """Score screen shown at after free-for-all rounds."""
 19
 20    def __init__(self, settings: dict):
 21        super().__init__(settings=settings)
 22
 23        # Keep prev activity alive while we fade in.
 24        self.transition_time = 0.5
 25        self._cymbal_sound = bs.getsound('cymbal')
 26
 27    def on_begin(self) -> None:
 28        # pylint: disable=too-many-locals
 29        # pylint: disable=too-many-statements
 30        from bascenev1lib.actor.text import Text
 31        from bascenev1lib.actor.image import Image
 32
 33        bs.set_analytics_screen('FreeForAll Score Screen')
 34        super().on_begin()
 35
 36        y_base = 100.0
 37        ts_h_offs = -305.0
 38        tdelay = 1.0
 39        scale = 1.2
 40        spacing = 37.0
 41
 42        # We include name and previous score in the sort to reduce the amount
 43        # of random jumping around the list we do in cases of ties.
 44        player_order_prev = list(self.players)
 45        player_order_prev.sort(
 46            reverse=True,
 47            key=lambda p: (
 48                p.team.sessionteam.customdata['previous_score'],
 49                p.getname(full=True),
 50            ),
 51        )
 52        player_order = list(self.players)
 53        player_order.sort(
 54            reverse=True,
 55            key=lambda p: (
 56                p.team.sessionteam.customdata['score'],
 57                p.team.sessionteam.customdata['score'],
 58                p.getname(full=True),
 59            ),
 60        )
 61
 62        v_offs = -74.0 + spacing * len(player_order_prev) * 0.5
 63        delay1 = 1.3 + 0.1
 64        delay2 = 2.9 + 0.1
 65        delay3 = 2.9 + 0.1
 66        order_change = player_order != player_order_prev
 67
 68        if order_change:
 69            delay3 += 1.5
 70
 71        bs.timer(0.3, self._score_display_sound.play)
 72        results = self.settings_raw['results']
 73        assert isinstance(results, bs.GameResults)
 74        self.show_player_scores(
 75            delay=0.001, results=results, scale=1.2, x_offset=-110.0
 76        )
 77
 78        sound_times: set[float] = set()
 79
 80        def _scoretxt(
 81            text: str,
 82            x_offs: float,
 83            y_offs: float,
 84            highlight: bool,
 85            delay: float,
 86            extrascale: float,
 87            flash: bool = False,
 88        ) -> Text:
 89            return Text(
 90                text,
 91                position=(
 92                    ts_h_offs + x_offs * scale,
 93                    y_base + (y_offs + v_offs + 2.0) * scale,
 94                ),
 95                scale=scale * extrascale,
 96                color=(
 97                    (1.0, 0.7, 0.3, 1.0) if highlight else (0.7, 0.7, 0.7, 0.7)
 98                ),
 99                h_align=Text.HAlign.RIGHT,
100                transition=Text.Transition.IN_LEFT,
101                transition_delay=tdelay + delay,
102                flash=flash,
103            ).autoretain()
104
105        v_offs -= spacing
106        slide_amt = 0.0
107        transtime = 0.250
108        transtime2 = 0.250
109
110        session = self.session
111        assert isinstance(session, bs.FreeForAllSession)
112        title = Text(
113            bs.Lstr(
114                resource='firstToSeriesText',
115                subs=[('${COUNT}', str(session.get_ffa_series_length()))],
116            ),
117            scale=1.05 * scale,
118            position=(
119                ts_h_offs - 0.0 * scale,
120                y_base + (v_offs + 50.0) * scale,
121            ),
122            h_align=Text.HAlign.CENTER,
123            color=(0.5, 0.5, 0.5, 0.5),
124            transition=Text.Transition.IN_LEFT,
125            transition_delay=tdelay,
126        ).autoretain()
127
128        v_offs -= 25
129        v_offs_start = v_offs
130
131        bs.timer(
132            tdelay + delay3,
133            bs.WeakCall(
134                self._safe_animate,
135                title.position_combine,
136                'input0',
137                {
138                    0.0: ts_h_offs - 0.0 * scale,
139                    transtime2: ts_h_offs - (0.0 + slide_amt) * scale,
140                },
141            ),
142        )
143
144        for i, player in enumerate(player_order_prev):
145            v_offs_2 = v_offs_start - spacing * (player_order.index(player))
146            bs.timer(tdelay + 0.3, self._score_display_sound_small.play)
147            if order_change:
148                bs.timer(tdelay + delay2 + 0.1, self._cymbal_sound.play)
149            img = Image(
150                player.get_icon(),
151                position=(
152                    ts_h_offs - 72.0 * scale,
153                    y_base + (v_offs + 15.0) * scale,
154                ),
155                scale=(30.0 * scale, 30.0 * scale),
156                transition=Image.Transition.IN_LEFT,
157                transition_delay=tdelay,
158            ).autoretain()
159            bs.timer(
160                tdelay + delay2,
161                bs.WeakCall(
162                    self._safe_animate,
163                    img.position_combine,
164                    'input1',
165                    {
166                        0: y_base + (v_offs + 15.0) * scale,
167                        transtime: y_base + (v_offs_2 + 15.0) * scale,
168                    },
169                ),
170            )
171            bs.timer(
172                tdelay + delay3,
173                bs.WeakCall(
174                    self._safe_animate,
175                    img.position_combine,
176                    'input0',
177                    {
178                        0: ts_h_offs - 72.0 * scale,
179                        transtime2: ts_h_offs - (72.0 + slide_amt) * scale,
180                    },
181                ),
182            )
183            txt = Text(
184                bs.Lstr(value=player.getname(full=True)),
185                maxwidth=130.0,
186                scale=0.75 * scale,
187                position=(
188                    ts_h_offs - 50.0 * scale,
189                    y_base + (v_offs + 15.0) * scale,
190                ),
191                h_align=Text.HAlign.LEFT,
192                v_align=Text.VAlign.CENTER,
193                color=bs.safecolor(player.team.color + (1,)),
194                transition=Text.Transition.IN_LEFT,
195                transition_delay=tdelay,
196            ).autoretain()
197            bs.timer(
198                tdelay + delay2,
199                bs.WeakCall(
200                    self._safe_animate,
201                    txt.position_combine,
202                    'input1',
203                    {
204                        0: y_base + (v_offs + 15.0) * scale,
205                        transtime: y_base + (v_offs_2 + 15.0) * scale,
206                    },
207                ),
208            )
209            bs.timer(
210                tdelay + delay3,
211                bs.WeakCall(
212                    self._safe_animate,
213                    txt.position_combine,
214                    'input0',
215                    {
216                        0: ts_h_offs - 50.0 * scale,
217                        transtime2: ts_h_offs - (50.0 + slide_amt) * scale,
218                    },
219                ),
220            )
221
222            txt_num = Text(
223                '#' + str(i + 1),
224                scale=0.55 * scale,
225                position=(
226                    ts_h_offs - 95.0 * scale,
227                    y_base + (v_offs + 8.0) * scale,
228                ),
229                h_align=Text.HAlign.RIGHT,
230                color=(0.6, 0.6, 0.6, 0.6),
231                transition=Text.Transition.IN_LEFT,
232                transition_delay=tdelay,
233            ).autoretain()
234            bs.timer(
235                tdelay + delay3,
236                bs.WeakCall(
237                    self._safe_animate,
238                    txt_num.position_combine,
239                    'input0',
240                    {
241                        0: ts_h_offs - 95.0 * scale,
242                        transtime2: ts_h_offs - (95.0 + slide_amt) * scale,
243                    },
244                ),
245            )
246
247            s_txt = _scoretxt(
248                str(player.team.sessionteam.customdata['previous_score']),
249                80,
250                0,
251                False,
252                0,
253                1.0,
254            )
255            bs.timer(
256                tdelay + delay2,
257                bs.WeakCall(
258                    self._safe_animate,
259                    s_txt.position_combine,
260                    'input1',
261                    {
262                        0: y_base + (v_offs + 2.0) * scale,
263                        transtime: y_base + (v_offs_2 + 2.0) * scale,
264                    },
265                ),
266            )
267            bs.timer(
268                tdelay + delay3,
269                bs.WeakCall(
270                    self._safe_animate,
271                    s_txt.position_combine,
272                    'input0',
273                    {
274                        0: ts_h_offs + 80.0 * scale,
275                        transtime2: ts_h_offs + (80.0 - slide_amt) * scale,
276                    },
277                ),
278            )
279
280            score_change = (
281                player.team.sessionteam.customdata['score']
282                - player.team.sessionteam.customdata['previous_score']
283            )
284            if score_change > 0:
285                xval = 113
286                yval = 3.0
287                s_txt_2 = _scoretxt(
288                    '+' + str(score_change),
289                    xval,
290                    yval,
291                    True,
292                    0,
293                    0.7,
294                    flash=True,
295                )
296                bs.timer(
297                    tdelay + delay2,
298                    bs.WeakCall(
299                        self._safe_animate,
300                        s_txt_2.position_combine,
301                        'input1',
302                        {
303                            0: y_base + (v_offs + yval + 2.0) * scale,
304                            transtime: y_base + (v_offs_2 + yval + 2.0) * scale,
305                        },
306                    ),
307                )
308                bs.timer(
309                    tdelay + delay3,
310                    bs.WeakCall(
311                        self._safe_animate,
312                        s_txt_2.position_combine,
313                        'input0',
314                        {
315                            0: ts_h_offs + xval * scale,
316                            transtime2: ts_h_offs + (xval - slide_amt) * scale,
317                        },
318                    ),
319                )
320
321                def _safesetattr(
322                    node: bs.Node | None, attr: str, value: Any
323                ) -> None:
324                    if node:
325                        setattr(node, attr, value)
326
327                bs.timer(
328                    tdelay + delay1,
329                    bs.Call(_safesetattr, s_txt.node, 'color', (1, 1, 1, 1)),
330                )
331                for j in range(score_change):
332                    bs.timer(
333                        (tdelay + delay1 + 0.15 * j),
334                        bs.Call(
335                            _safesetattr,
336                            s_txt.node,
337                            'text',
338                            str(
339                                player.team.sessionteam.customdata[
340                                    'previous_score'
341                                ]
342                                + j
343                                + 1
344                            ),
345                        ),
346                    )
347                    tfin = tdelay + delay1 + 0.15 * j
348                    if tfin not in sound_times:
349                        sound_times.add(tfin)
350                        bs.timer(tfin, self._score_display_sound_small.play)
351            v_offs -= spacing
352
353    def _safe_animate(
354        self, node: bs.Node | None, attr: str, keys: dict[float, float]
355    ) -> None:
356        """Run an animation on a node if the node still exists."""
357        if node:
358            bs.animate(node, attr, keys)

Score screen shown at after free-for-all rounds.

FreeForAllVictoryScoreScreenActivity(settings: dict)
20    def __init__(self, settings: dict):
21        super().__init__(settings=settings)
22
23        # Keep prev activity alive while we fade in.
24        self.transition_time = 0.5
25        self._cymbal_sound = bs.getsound('cymbal')

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.

transition_time = 0.5

If the activity fades or transitions in, it should set the length of time here so that previous activities will be kept alive for that long (avoiding 'holes' in the screen) This value is given in real-time seconds.

def on_begin(self) -> None:
 27    def on_begin(self) -> None:
 28        # pylint: disable=too-many-locals
 29        # pylint: disable=too-many-statements
 30        from bascenev1lib.actor.text import Text
 31        from bascenev1lib.actor.image import Image
 32
 33        bs.set_analytics_screen('FreeForAll Score Screen')
 34        super().on_begin()
 35
 36        y_base = 100.0
 37        ts_h_offs = -305.0
 38        tdelay = 1.0
 39        scale = 1.2
 40        spacing = 37.0
 41
 42        # We include name and previous score in the sort to reduce the amount
 43        # of random jumping around the list we do in cases of ties.
 44        player_order_prev = list(self.players)
 45        player_order_prev.sort(
 46            reverse=True,
 47            key=lambda p: (
 48                p.team.sessionteam.customdata['previous_score'],
 49                p.getname(full=True),
 50            ),
 51        )
 52        player_order = list(self.players)
 53        player_order.sort(
 54            reverse=True,
 55            key=lambda p: (
 56                p.team.sessionteam.customdata['score'],
 57                p.team.sessionteam.customdata['score'],
 58                p.getname(full=True),
 59            ),
 60        )
 61
 62        v_offs = -74.0 + spacing * len(player_order_prev) * 0.5
 63        delay1 = 1.3 + 0.1
 64        delay2 = 2.9 + 0.1
 65        delay3 = 2.9 + 0.1
 66        order_change = player_order != player_order_prev
 67
 68        if order_change:
 69            delay3 += 1.5
 70
 71        bs.timer(0.3, self._score_display_sound.play)
 72        results = self.settings_raw['results']
 73        assert isinstance(results, bs.GameResults)
 74        self.show_player_scores(
 75            delay=0.001, results=results, scale=1.2, x_offset=-110.0
 76        )
 77
 78        sound_times: set[float] = set()
 79
 80        def _scoretxt(
 81            text: str,
 82            x_offs: float,
 83            y_offs: float,
 84            highlight: bool,
 85            delay: float,
 86            extrascale: float,
 87            flash: bool = False,
 88        ) -> Text:
 89            return Text(
 90                text,
 91                position=(
 92                    ts_h_offs + x_offs * scale,
 93                    y_base + (y_offs + v_offs + 2.0) * scale,
 94                ),
 95                scale=scale * extrascale,
 96                color=(
 97                    (1.0, 0.7, 0.3, 1.0) if highlight else (0.7, 0.7, 0.7, 0.7)
 98                ),
 99                h_align=Text.HAlign.RIGHT,
100                transition=Text.Transition.IN_LEFT,
101                transition_delay=tdelay + delay,
102                flash=flash,
103            ).autoretain()
104
105        v_offs -= spacing
106        slide_amt = 0.0
107        transtime = 0.250
108        transtime2 = 0.250
109
110        session = self.session
111        assert isinstance(session, bs.FreeForAllSession)
112        title = Text(
113            bs.Lstr(
114                resource='firstToSeriesText',
115                subs=[('${COUNT}', str(session.get_ffa_series_length()))],
116            ),
117            scale=1.05 * scale,
118            position=(
119                ts_h_offs - 0.0 * scale,
120                y_base + (v_offs + 50.0) * scale,
121            ),
122            h_align=Text.HAlign.CENTER,
123            color=(0.5, 0.5, 0.5, 0.5),
124            transition=Text.Transition.IN_LEFT,
125            transition_delay=tdelay,
126        ).autoretain()
127
128        v_offs -= 25
129        v_offs_start = v_offs
130
131        bs.timer(
132            tdelay + delay3,
133            bs.WeakCall(
134                self._safe_animate,
135                title.position_combine,
136                'input0',
137                {
138                    0.0: ts_h_offs - 0.0 * scale,
139                    transtime2: ts_h_offs - (0.0 + slide_amt) * scale,
140                },
141            ),
142        )
143
144        for i, player in enumerate(player_order_prev):
145            v_offs_2 = v_offs_start - spacing * (player_order.index(player))
146            bs.timer(tdelay + 0.3, self._score_display_sound_small.play)
147            if order_change:
148                bs.timer(tdelay + delay2 + 0.1, self._cymbal_sound.play)
149            img = Image(
150                player.get_icon(),
151                position=(
152                    ts_h_offs - 72.0 * scale,
153                    y_base + (v_offs + 15.0) * scale,
154                ),
155                scale=(30.0 * scale, 30.0 * scale),
156                transition=Image.Transition.IN_LEFT,
157                transition_delay=tdelay,
158            ).autoretain()
159            bs.timer(
160                tdelay + delay2,
161                bs.WeakCall(
162                    self._safe_animate,
163                    img.position_combine,
164                    'input1',
165                    {
166                        0: y_base + (v_offs + 15.0) * scale,
167                        transtime: y_base + (v_offs_2 + 15.0) * scale,
168                    },
169                ),
170            )
171            bs.timer(
172                tdelay + delay3,
173                bs.WeakCall(
174                    self._safe_animate,
175                    img.position_combine,
176                    'input0',
177                    {
178                        0: ts_h_offs - 72.0 * scale,
179                        transtime2: ts_h_offs - (72.0 + slide_amt) * scale,
180                    },
181                ),
182            )
183            txt = Text(
184                bs.Lstr(value=player.getname(full=True)),
185                maxwidth=130.0,
186                scale=0.75 * scale,
187                position=(
188                    ts_h_offs - 50.0 * scale,
189                    y_base + (v_offs + 15.0) * scale,
190                ),
191                h_align=Text.HAlign.LEFT,
192                v_align=Text.VAlign.CENTER,
193                color=bs.safecolor(player.team.color + (1,)),
194                transition=Text.Transition.IN_LEFT,
195                transition_delay=tdelay,
196            ).autoretain()
197            bs.timer(
198                tdelay + delay2,
199                bs.WeakCall(
200                    self._safe_animate,
201                    txt.position_combine,
202                    'input1',
203                    {
204                        0: y_base + (v_offs + 15.0) * scale,
205                        transtime: y_base + (v_offs_2 + 15.0) * scale,
206                    },
207                ),
208            )
209            bs.timer(
210                tdelay + delay3,
211                bs.WeakCall(
212                    self._safe_animate,
213                    txt.position_combine,
214                    'input0',
215                    {
216                        0: ts_h_offs - 50.0 * scale,
217                        transtime2: ts_h_offs - (50.0 + slide_amt) * scale,
218                    },
219                ),
220            )
221
222            txt_num = Text(
223                '#' + str(i + 1),
224                scale=0.55 * scale,
225                position=(
226                    ts_h_offs - 95.0 * scale,
227                    y_base + (v_offs + 8.0) * scale,
228                ),
229                h_align=Text.HAlign.RIGHT,
230                color=(0.6, 0.6, 0.6, 0.6),
231                transition=Text.Transition.IN_LEFT,
232                transition_delay=tdelay,
233            ).autoretain()
234            bs.timer(
235                tdelay + delay3,
236                bs.WeakCall(
237                    self._safe_animate,
238                    txt_num.position_combine,
239                    'input0',
240                    {
241                        0: ts_h_offs - 95.0 * scale,
242                        transtime2: ts_h_offs - (95.0 + slide_amt) * scale,
243                    },
244                ),
245            )
246
247            s_txt = _scoretxt(
248                str(player.team.sessionteam.customdata['previous_score']),
249                80,
250                0,
251                False,
252                0,
253                1.0,
254            )
255            bs.timer(
256                tdelay + delay2,
257                bs.WeakCall(
258                    self._safe_animate,
259                    s_txt.position_combine,
260                    'input1',
261                    {
262                        0: y_base + (v_offs + 2.0) * scale,
263                        transtime: y_base + (v_offs_2 + 2.0) * scale,
264                    },
265                ),
266            )
267            bs.timer(
268                tdelay + delay3,
269                bs.WeakCall(
270                    self._safe_animate,
271                    s_txt.position_combine,
272                    'input0',
273                    {
274                        0: ts_h_offs + 80.0 * scale,
275                        transtime2: ts_h_offs + (80.0 - slide_amt) * scale,
276                    },
277                ),
278            )
279
280            score_change = (
281                player.team.sessionteam.customdata['score']
282                - player.team.sessionteam.customdata['previous_score']
283            )
284            if score_change > 0:
285                xval = 113
286                yval = 3.0
287                s_txt_2 = _scoretxt(
288                    '+' + str(score_change),
289                    xval,
290                    yval,
291                    True,
292                    0,
293                    0.7,
294                    flash=True,
295                )
296                bs.timer(
297                    tdelay + delay2,
298                    bs.WeakCall(
299                        self._safe_animate,
300                        s_txt_2.position_combine,
301                        'input1',
302                        {
303                            0: y_base + (v_offs + yval + 2.0) * scale,
304                            transtime: y_base + (v_offs_2 + yval + 2.0) * scale,
305                        },
306                    ),
307                )
308                bs.timer(
309                    tdelay + delay3,
310                    bs.WeakCall(
311                        self._safe_animate,
312                        s_txt_2.position_combine,
313                        'input0',
314                        {
315                            0: ts_h_offs + xval * scale,
316                            transtime2: ts_h_offs + (xval - slide_amt) * scale,
317                        },
318                    ),
319                )
320
321                def _safesetattr(
322                    node: bs.Node | None, attr: str, value: Any
323                ) -> None:
324                    if node:
325                        setattr(node, attr, value)
326
327                bs.timer(
328                    tdelay + delay1,
329                    bs.Call(_safesetattr, s_txt.node, 'color', (1, 1, 1, 1)),
330                )
331                for j in range(score_change):
332                    bs.timer(
333                        (tdelay + delay1 + 0.15 * j),
334                        bs.Call(
335                            _safesetattr,
336                            s_txt.node,
337                            'text',
338                            str(
339                                player.team.sessionteam.customdata[
340                                    'previous_score'
341                                ]
342                                + j
343                                + 1
344                            ),
345                        ),
346                    )
347                    tfin = tdelay + delay1 + 0.15 * j
348                    if tfin not in sound_times:
349                        sound_times.add(tfin)
350                        bs.timer(tfin, self._score_display_sound_small.play)
351            v_offs -= spacing

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
inherits_tint
inherits_vr_camera_offset
use_fixed_vr_overlay
default_music
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