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