bascenev1lib.tutorial

Wrangles the game tutorial sequence.

   1# Released under the MIT License. See LICENSE for details.
   2#
   3"""Wrangles the game tutorial sequence."""
   4
   5# Not too concerned with keeping this old module pretty;
   6# don't expect to be revisiting it.
   7# pylint: disable=too-many-branches
   8# pylint: disable=too-many-statements
   9# pylint: disable=too-many-lines
  10# pylint: disable=missing-function-docstring, missing-class-docstring
  11# pylint: disable=too-many-locals
  12# pylint: disable=unused-argument
  13# pylint: disable=unused-variable
  14
  15from __future__ import annotations
  16
  17import math
  18import logging
  19from collections import deque
  20from typing import TYPE_CHECKING, override
  21
  22import bascenev1 as bs
  23
  24from bascenev1lib.actor.spaz import Spaz
  25
  26if TYPE_CHECKING:
  27    from typing import Any, Callable, Sequence
  28
  29
  30def _safesetattr(node: bs.Node | None, attr: str, value: Any) -> None:
  31    if node:
  32        setattr(node, attr, value)
  33
  34
  35class ButtonPress:
  36    def __init__(
  37        self,
  38        button: str,
  39        delay: int = 0,
  40        release: bool = True,
  41        release_delay: int = 0,
  42    ):
  43        self._button = button
  44        self._delay = delay
  45        self._release = release
  46        self._release_delay = release_delay
  47
  48    def run(self, a: TutorialActivity) -> None:
  49        s = a.current_spaz
  50        assert s is not None
  51        img: bs.Node | None
  52        release_call: Callable[[], None] | None
  53        color: Sequence[float] | None
  54        if self._button == 'punch':
  55            call = s.on_punch_press
  56            release_call = s.on_punch_release
  57            img = a.punch_image
  58            color = a.punch_image_color
  59        elif self._button == 'jump':
  60            call = s.on_jump_press
  61            release_call = s.on_jump_release
  62            img = a.jump_image
  63            color = a.jump_image_color
  64        elif self._button == 'bomb':
  65            call = s.on_bomb_press
  66            release_call = s.on_bomb_release
  67            img = a.bomb_image
  68            color = a.bomb_image_color
  69        elif self._button == 'pickUp':
  70            call = s.on_pickup_press
  71            release_call = s.on_pickup_release
  72            img = a.pickup_image
  73            color = a.pickup_image_color
  74        elif self._button == 'run':
  75            call = bs.Call(s.on_run, 1.0)
  76            release_call = bs.Call(s.on_run, 0.0)
  77            img = None
  78            color = None
  79        else:
  80            raise RuntimeError(f'invalid button: {self._button}')
  81
  82        brightness = 4.0
  83        if color is not None:
  84            c_bright = list(color)
  85            c_bright[0] *= brightness
  86            c_bright[1] *= brightness
  87            c_bright[2] *= brightness
  88        else:
  89            c_bright = [1.0, 1.0, 1.0]
  90
  91        if self._delay == 0:
  92            call()
  93            if img is not None:
  94                img.color = c_bright
  95                img.vr_depth = -40
  96        else:
  97            bs.timer(self._delay / 1000.0, call)
  98            if img is not None:
  99                bs.timer(
 100                    self._delay / 1000.0,
 101                    bs.Call(_safesetattr, img, 'color', c_bright),
 102                )
 103                bs.timer(
 104                    self._delay / 1000.0,
 105                    bs.Call(_safesetattr, img, 'vr_depth', -30),
 106                )
 107        if self._release:
 108            if self._delay == 0 and self._release_delay == 0:
 109                release_call()
 110            else:
 111                bs.timer(
 112                    0.001 * (self._delay + self._release_delay), release_call
 113                )
 114            if img is not None:
 115                bs.timer(
 116                    (self._delay + self._release_delay + 100) / 1000.0,
 117                    bs.Call(_safesetattr, img, 'color', color),
 118                )
 119                bs.timer(
 120                    (self._delay + self._release_delay + 100) / 1000.0,
 121                    bs.Call(_safesetattr, img, 'vr_depth', -20),
 122                )
 123
 124
 125class ButtonRelease:
 126    def __init__(self, button: str, delay: int = 0):
 127        self._button = button
 128        self._delay = delay
 129
 130    def run(self, a: TutorialActivity) -> None:
 131        s = a.current_spaz
 132        assert s is not None
 133        call: Callable[[], None] | None
 134        img: bs.Node | None
 135        color: Sequence[float] | None
 136        if self._button == 'punch':
 137            call = s.on_punch_release
 138            img = a.punch_image
 139            color = a.punch_image_color
 140        elif self._button == 'jump':
 141            call = s.on_jump_release
 142            img = a.jump_image
 143            color = a.jump_image_color
 144        elif self._button == 'bomb':
 145            call = s.on_bomb_release
 146            img = a.bomb_image
 147            color = a.bomb_image_color
 148        elif self._button == 'pickUp':
 149            call = s.on_pickup_press
 150            img = a.pickup_image
 151            color = a.pickup_image_color
 152        elif self._button == 'run':
 153            call = bs.Call(s.on_run, 0.0)
 154            img = None
 155            color = None
 156        else:
 157            raise RuntimeError('invalid button: ' + self._button)
 158        if self._delay == 0:
 159            call()
 160        else:
 161            bs.timer(self._delay / 1000.0, call)
 162        if img is not None:
 163            bs.timer(
 164                (self._delay + 100) / 1000.0,
 165                bs.Call(_safesetattr, img, 'color', color),
 166            )
 167            bs.timer(
 168                (self._delay + 100 / 1000.0),
 169                bs.Call(_safesetattr, img, 'vr_depth', -20),
 170            )
 171
 172
 173class Player(bs.Player['Team']):
 174    """Our player type for this game."""
 175
 176    def __init__(self) -> None:
 177        self.pressed = False
 178
 179
 180class Team(bs.Team[Player]):
 181    """Our team type for this game."""
 182
 183    def __init__(self) -> None:
 184        pass
 185
 186
 187class TutorialActivity(bs.Activity[Player, Team]):
 188    def __init__(self, settings: dict | None = None):
 189        from bascenev1lib.maps import Rampage
 190
 191        if settings is None:
 192            settings = {}
 193        super().__init__(settings)
 194        self.current_spaz: Spaz | None = None
 195        self._benchmark_type = getattr(bs.getsession(), 'benchmark_type', None)
 196        self.last_start_time: int | None = None
 197        self.cycle_times: list[int] = []
 198        self.allow_pausing = True
 199        self.allow_kick_idle_players = False
 200        self._issued_warning = False
 201        self._map_type = Rampage
 202        self._map_type.preload()
 203        self._jump_button_tex = bs.gettexture('buttonJump')
 204        self._pick_up_button_tex = bs.gettexture('buttonPickUp')
 205        self._bomb_button_tex = bs.gettexture('buttonBomb')
 206        self._punch_button_tex = bs.gettexture('buttonPunch')
 207        self._r = 'tutorial'
 208        self._have_skipped = False
 209        self.stick_image_position_x = self.stick_image_position_y = 0.0
 210        self.spawn_sound = bs.getsound('spawn')
 211        self.map: bs.Map | None = None
 212        self.text: bs.Node | None = None
 213        self._skip_text: bs.Node | None = None
 214        self._skip_count_text: bs.Node | None = None
 215        self._scale: float | None = None
 216        self._stick_base_position: tuple[float, float] = (0.0, 0.0)
 217        self._stick_nub_position: tuple[float, float] = (0.0, 0.0)
 218        self._stick_base_image_color: Sequence[float] = (1.0, 1.0, 1.0, 1.0)
 219        self._stick_nub_image_color: Sequence[float] = (1.0, 1.0, 1.0, 1.0)
 220        self._time: int = -1
 221        self.punch_image_color = (1.0, 1.0, 1.0)
 222        self.punch_image: bs.Node | None = None
 223        self.bomb_image: bs.Node | None = None
 224        self.jump_image: bs.Node | None = None
 225        self.pickup_image: bs.Node | None = None
 226        self._stick_base_image: bs.Node | None = None
 227        self._stick_nub_image: bs.Node | None = None
 228        self.bomb_image_color = (1.0, 1.0, 1.0)
 229        self.pickup_image_color = (1.0, 1.0, 1.0)
 230        self.control_ui_nodes: list[bs.Node] = []
 231        self.spazzes: dict[int, Spaz] = {}
 232        self.jump_image_color = (1.0, 1.0, 1.0)
 233        self._entries: deque[Any] = deque()
 234        self._read_entries_timer: bs.Timer | None = None
 235        self._entry_timer: bs.Timer | None = None
 236
 237    @override
 238    def on_transition_in(self) -> None:
 239        super().on_transition_in()
 240        bs.setmusic(bs.MusicType.CHAR_SELECT, continuous=True)
 241        self.map = self._map_type()
 242
 243    @override
 244    def on_begin(self) -> None:
 245        super().on_begin()
 246
 247        bs.set_analytics_screen('Tutorial Start')
 248        bs.increment_analytics_count('Tutorial start')
 249
 250        if bool(False):
 251            # Buttons on top.
 252            text_y = 140
 253            buttons_y = 250
 254        else:
 255            # Buttons on bottom.
 256            text_y = 260
 257            buttons_y = 160
 258
 259        # Need different versions of this: taps/buttons/keys.
 260        self.text = bs.newnode(
 261            'text',
 262            attrs={
 263                'text': '',
 264                'scale': 1.9,
 265                'position': (0, text_y),
 266                'maxwidth': 500,
 267                'flatness': 0.0,
 268                'shadow': 0.5,
 269                'h_align': 'center',
 270                'v_align': 'center',
 271                'v_attach': 'center',
 272            },
 273        )
 274
 275        # Need different versions of this: taps/buttons/keys.
 276        txt = (
 277            bs.Lstr(resource=f'{self._r}.cpuBenchmarkText')
 278            if self._benchmark_type == 'cpu'
 279            else bs.Lstr(resource=f'{self._r}.toSkipPressAnythingText')
 280        )
 281        t = self._skip_text = bs.newnode(
 282            'text',
 283            attrs={
 284                'text': txt,
 285                'maxwidth': 900,
 286                'scale': 1.1,
 287                'vr_depth': 100,
 288                'position': (0, 30),
 289                'h_align': 'center',
 290                'v_align': 'center',
 291                'v_attach': 'bottom',
 292            },
 293        )
 294        bs.animate(t, 'opacity', {1.0: 0.0, 2.0: 0.7})
 295        self._skip_count_text = bs.newnode(
 296            'text',
 297            attrs={
 298                'text': '',
 299                'scale': 1.4,
 300                'vr_depth': 90,
 301                'position': (0, 70),
 302                'h_align': 'center',
 303                'v_align': 'center',
 304                'v_attach': 'bottom',
 305            },
 306        )
 307
 308        ouya = False
 309
 310        self._scale = scale = 0.6
 311        center_offs = 130.0 * scale
 312        offs = 65.0 * scale
 313        position = (0, buttons_y)
 314        image_size = 90.0 * scale
 315        image_size_2 = 220.0 * scale
 316        nub_size = 110.0 * scale
 317        p = (position[0] + center_offs, position[1] - offs)
 318
 319        def _sc(r: float, g: float, b: float) -> tuple[float, float, float]:
 320            return 0.6 * r, 0.6 * g, 0.6 * b
 321
 322        self.jump_image_color = c = _sc(0.4, 1, 0.4)
 323        self.jump_image = bs.newnode(
 324            'image',
 325            attrs={
 326                'texture': self._jump_button_tex,
 327                'absolute_scale': True,
 328                'vr_depth': -20,
 329                'position': p,
 330                'scale': (image_size, image_size),
 331                'color': c,
 332            },
 333        )
 334        p = (position[0] + center_offs - offs, position[1])
 335        self.punch_image_color = c = (
 336            _sc(0.2, 0.6, 1) if ouya else _sc(1, 0.7, 0.3)
 337        )
 338        self.punch_image = bs.newnode(
 339            'image',
 340            attrs={
 341                'texture': bs.gettexture('buttonPunch'),
 342                'absolute_scale': True,
 343                'vr_depth': -20,
 344                'position': p,
 345                'scale': (image_size, image_size),
 346                'color': c,
 347            },
 348        )
 349        p = (position[0] + center_offs + offs, position[1])
 350        self.bomb_image_color = c = _sc(1, 0.3, 0.3)
 351        self.bomb_image = bs.newnode(
 352            'image',
 353            attrs={
 354                'texture': bs.gettexture('buttonBomb'),
 355                'absolute_scale': True,
 356                'vr_depth': -20,
 357                'position': p,
 358                'scale': (image_size, image_size),
 359                'color': c,
 360            },
 361        )
 362        p = (position[0] + center_offs, position[1] + offs)
 363        self.pickup_image_color = c = (
 364            _sc(1, 0.8, 0.3) if ouya else _sc(0.5, 0.5, 1)
 365        )
 366        self.pickup_image = bs.newnode(
 367            'image',
 368            attrs={
 369                'texture': bs.gettexture('buttonPickUp'),
 370                'absolute_scale': True,
 371                'vr_depth': -20,
 372                'position': p,
 373                'scale': (image_size, image_size),
 374                'color': c,
 375            },
 376        )
 377
 378        self._stick_base_position = p = (position[0] - center_offs, position[1])
 379        self._stick_base_image_color = c2 = (0.25, 0.25, 0.25, 1.0)
 380        self._stick_base_image = bs.newnode(
 381            'image',
 382            attrs={
 383                'texture': bs.gettexture('nub'),
 384                'absolute_scale': True,
 385                'vr_depth': -40,
 386                'position': p,
 387                'scale': (image_size_2, image_size_2),
 388                'color': c2,
 389            },
 390        )
 391        self._stick_nub_position = p = (position[0] - center_offs, position[1])
 392        self._stick_nub_image_color = c3 = (0.4, 0.4, 0.4, 1.0)
 393        self._stick_nub_image = bs.newnode(
 394            'image',
 395            attrs={
 396                'texture': bs.gettexture('nub'),
 397                'absolute_scale': True,
 398                'position': p,
 399                'scale': (nub_size, nub_size),
 400                'color': c3,
 401            },
 402        )
 403        self.control_ui_nodes = [
 404            self.jump_image,
 405            self.punch_image,
 406            self.bomb_image,
 407            self.pickup_image,
 408            self._stick_base_image,
 409            self._stick_nub_image,
 410        ]
 411        for n in self.control_ui_nodes:
 412            n.opacity = 0.0
 413        self._read_entries()
 414
 415    def set_stick_image_position(self, x: float, y: float) -> None:
 416        # Clamp this to a circle.
 417        len_squared = x * x + y * y
 418        if len_squared > 1.0:
 419            length = math.sqrt(len_squared)
 420            mult = 1.0 / length
 421            x *= mult
 422            y *= mult
 423
 424        self.stick_image_position_x = x
 425        self.stick_image_position_y = y
 426        offs = 50.0
 427        assert self._scale is not None
 428        p = [
 429            self._stick_nub_position[0] + x * offs * self._scale,
 430            self._stick_nub_position[1] + y * offs * self._scale,
 431        ]
 432        c = list(self._stick_nub_image_color)
 433        if abs(x) > 0.1 or abs(y) > 0.1:
 434            c[0] *= 2.0
 435            c[1] *= 4.0
 436            c[2] *= 2.0
 437        assert self._stick_nub_image is not None
 438        self._stick_nub_image.position = p
 439        self._stick_nub_image.color = c
 440        c = list(self._stick_base_image_color)
 441        if abs(x) > 0.1 or abs(y) > 0.1:
 442            c[0] *= 1.5
 443            c[1] *= 1.5
 444            c[2] *= 1.5
 445        assert self._stick_base_image is not None
 446        self._stick_base_image.color = c
 447
 448    def _read_entries(self) -> None:
 449        try:
 450
 451            class Reset:
 452                def __init__(self) -> None:
 453                    pass
 454
 455                def run(self, a: TutorialActivity) -> None:
 456                    # if we're looping, print out how long each cycle took
 457                    # print out how long each cycle took..
 458                    if a.last_start_time is not None:
 459                        tval = int(bs.apptime() * 1000.0) - a.last_start_time
 460                        assert isinstance(tval, int)
 461                        diff = tval
 462                        a.cycle_times.append(diff)
 463                        bs.broadcastmessage(
 464                            'cycle time: '
 465                            + str(diff)
 466                            + ' (average: '
 467                            + str(sum(a.cycle_times) / len(a.cycle_times))
 468                            + ')'
 469                        )
 470                    tval = int(bs.apptime() * 1000.0)
 471                    assert isinstance(tval, int)
 472                    a.last_start_time = tval
 473
 474                    assert a.text
 475                    a.text.text = ''
 476                    for spaz in list(a.spazzes.values()):
 477                        spaz.handlemessage(bs.DieMessage(immediate=True))
 478                    a.spazzes = {}
 479                    a.current_spaz = None
 480                    for n in a.control_ui_nodes:
 481                        n.opacity = 0.0
 482                    a.set_stick_image_position(0, 0)
 483
 484            # Can be used for debugging.
 485            class SetSpeed:
 486                def __init__(self, speed: int):
 487                    self._speed = speed
 488
 489                def run(self, a: TutorialActivity) -> None:
 490                    print('setting to', self._speed)
 491                    bs.set_debug_speed_exponent(self._speed)
 492
 493            class RemoveGloves:
 494                def __init__(self) -> None:
 495                    pass
 496
 497                def run(self, a: TutorialActivity) -> None:
 498                    # pylint: disable=protected-access
 499                    assert a.current_spaz is not None
 500                    # noinspection PyProtectedMember
 501                    a.current_spaz._gloves_wear_off()
 502
 503            class KillSpaz:
 504                def __init__(self, num: int, explode: bool = False):
 505                    self._num = num
 506                    self._explode = explode
 507
 508                def run(self, a: TutorialActivity) -> None:
 509                    if self._explode:
 510                        a.spazzes[self._num].shatter()
 511                    del a.spazzes[self._num]
 512
 513            class SpawnSpaz:
 514                def __init__(
 515                    self,
 516                    num: int,
 517                    position: Sequence[float],
 518                    *,
 519                    color: Sequence[float] = (1.0, 1.0, 1.0),
 520                    make_current: bool = False,
 521                    relative_to: int | None = None,
 522                    name: str | bs.Lstr = '',
 523                    flash: bool = True,
 524                    angle: float = 0.0,
 525                ):
 526                    self._num = num
 527                    self._position = position
 528                    self._make_current = make_current
 529                    self._color = color
 530                    self._relative_to = relative_to
 531                    self._name = name
 532                    self._flash = flash
 533                    self._angle = angle
 534
 535                def run(self, a: TutorialActivity) -> None:
 536                    # if they gave a 'relative to' spaz, position is relative
 537                    # to them
 538                    pos: Sequence[float]
 539                    if self._relative_to is not None:
 540                        snode = a.spazzes[self._relative_to].node
 541                        assert snode
 542                        their_pos = snode.position
 543                        pos = (
 544                            their_pos[0] + self._position[0],
 545                            their_pos[1] + self._position[1],
 546                            their_pos[2] + self._position[2],
 547                        )
 548                    else:
 549                        pos = self._position
 550
 551                    # if there's already a spaz at this spot, insta-kill it
 552                    if self._num in a.spazzes:
 553                        a.spazzes[self._num].handlemessage(
 554                            bs.DieMessage(immediate=True)
 555                        )
 556
 557                    s = a.spazzes[self._num] = Spaz(
 558                        color=self._color,
 559                        start_invincible=self._flash,
 560                        demo_mode=True,
 561                    )
 562
 563                    # FIXME: Should extend spaz to support Lstr names.
 564                    assert s.node
 565                    if isinstance(self._name, bs.Lstr):
 566                        s.node.name = self._name.evaluate()
 567                    else:
 568                        s.node.name = self._name
 569                    s.node.name_color = self._color
 570                    s.handlemessage(bs.StandMessage(pos, self._angle))
 571                    if self._make_current:
 572                        a.current_spaz = s
 573                    if self._flash:
 574                        a.spawn_sound.play(position=pos)
 575
 576            class Powerup:
 577                def __init__(
 578                    self,
 579                    num: int,
 580                    position: Sequence[float],
 581                    *,
 582                    color: Sequence[float] = (1.0, 1.0, 1.0),
 583                    make_current: bool = False,
 584                    relative_to: int | None = None,
 585                ):
 586                    self._position = position
 587                    self._relative_to = relative_to
 588
 589                def run(self, a: TutorialActivity) -> None:
 590                    # If they gave a 'relative to' spaz, position is relative
 591                    # to them.
 592                    pos: Sequence[float]
 593                    if self._relative_to is not None:
 594                        snode = a.spazzes[self._relative_to].node
 595                        assert snode
 596                        their_pos = snode.position
 597                        pos = (
 598                            their_pos[0] + self._position[0],
 599                            their_pos[1] + self._position[1],
 600                            their_pos[2] + self._position[2],
 601                        )
 602                    else:
 603                        pos = self._position
 604                    from bascenev1lib.actor import powerupbox
 605
 606                    powerupbox.PowerupBox(
 607                        position=pos, poweruptype='punch'
 608                    ).autoretain()
 609
 610            class Delay:
 611                def __init__(self, time: int) -> None:
 612                    self._time = time
 613
 614                def run(self, a: TutorialActivity) -> int:
 615                    return self._time
 616
 617            class AnalyticsScreen:
 618                def __init__(self, screen: str) -> None:
 619                    self._screen = screen
 620
 621                def run(self, a: TutorialActivity) -> None:
 622                    bs.set_analytics_screen(self._screen)
 623
 624            class DelayOld:
 625                def __init__(self, time: int) -> None:
 626                    self._time = time
 627
 628                def run(self, a: TutorialActivity) -> int:
 629                    return int(0.9 * self._time)
 630
 631            class DelayOld2:
 632                def __init__(self, time: int) -> None:
 633                    self._time = time
 634
 635                def run(self, a: TutorialActivity) -> int:
 636                    return int(0.8 * self._time)
 637
 638            class End:
 639                def __init__(self) -> None:
 640                    pass
 641
 642                def run(self, a: TutorialActivity) -> None:
 643                    bs.increment_analytics_count('Tutorial finish')
 644                    a.end()
 645
 646            class Move:
 647                def __init__(self, x: float, y: float):
 648                    self._x = float(x)
 649                    self._y = float(y)
 650
 651                def run(self, a: TutorialActivity) -> None:
 652                    s = a.current_spaz
 653                    assert s
 654                    # FIXME: Game should take floats for this.
 655                    x_clamped = self._x
 656                    y_clamped = self._y
 657                    s.on_move_left_right(x_clamped)
 658                    s.on_move_up_down(y_clamped)
 659                    a.set_stick_image_position(self._x, self._y)
 660
 661            class MoveLR:
 662                def __init__(self, x: float):
 663                    self._x = float(x)
 664
 665                def run(self, a: TutorialActivity) -> None:
 666                    s = a.current_spaz
 667                    assert s
 668                    # FIXME: Game should take floats for this.
 669                    x_clamped = self._x
 670                    s.on_move_left_right(x_clamped)
 671                    a.set_stick_image_position(
 672                        self._x, a.stick_image_position_y
 673                    )
 674
 675            class MoveUD:
 676                def __init__(self, y: float):
 677                    self._y = float(y)
 678
 679                def run(self, a: TutorialActivity) -> None:
 680                    s = a.current_spaz
 681                    assert s
 682                    # FIXME: Game should take floats for this.
 683                    y_clamped = self._y
 684                    s.on_move_up_down(y_clamped)
 685                    a.set_stick_image_position(
 686                        a.stick_image_position_x, self._y
 687                    )
 688
 689            class Bomb(ButtonPress):
 690                def __init__(
 691                    self,
 692                    delay: int = 0,
 693                    release: bool = True,
 694                    release_delay: int = 500,
 695                ):
 696                    ButtonPress.__init__(
 697                        self,
 698                        'bomb',
 699                        delay=delay,
 700                        release=release,
 701                        release_delay=release_delay,
 702                    )
 703
 704            class Jump(ButtonPress):
 705                def __init__(
 706                    self,
 707                    delay: int = 0,
 708                    release: bool = True,
 709                    release_delay: int = 500,
 710                ):
 711                    ButtonPress.__init__(
 712                        self,
 713                        'jump',
 714                        delay=delay,
 715                        release=release,
 716                        release_delay=release_delay,
 717                    )
 718
 719            class Punch(ButtonPress):
 720                def __init__(
 721                    self,
 722                    delay: int = 0,
 723                    release: bool = True,
 724                    release_delay: int = 500,
 725                ):
 726                    ButtonPress.__init__(
 727                        self,
 728                        'punch',
 729                        delay=delay,
 730                        release=release,
 731                        release_delay=release_delay,
 732                    )
 733
 734            class PickUp(ButtonPress):
 735                def __init__(
 736                    self,
 737                    delay: int = 0,
 738                    release: bool = True,
 739                    release_delay: int = 500,
 740                ):
 741                    ButtonPress.__init__(
 742                        self,
 743                        'pickUp',
 744                        delay=delay,
 745                        release=release,
 746                        release_delay=release_delay,
 747                    )
 748
 749            class Run(ButtonPress):
 750                def __init__(
 751                    self,
 752                    delay: int = 0,
 753                    release: bool = True,
 754                    release_delay: int = 500,
 755                ):
 756                    ButtonPress.__init__(
 757                        self,
 758                        'run',
 759                        delay=delay,
 760                        release=release,
 761                        release_delay=release_delay,
 762                    )
 763
 764            class BombRelease(ButtonRelease):
 765                def __init__(self, delay: int = 0):
 766                    super().__init__('bomb', delay=delay)
 767
 768            class JumpRelease(ButtonRelease):
 769                def __init__(self, delay: int = 0):
 770                    super().__init__('jump', delay=delay)
 771
 772            class PunchRelease(ButtonRelease):
 773                def __init__(self, delay: int = 0):
 774                    super().__init__('punch', delay=delay)
 775
 776            class PickUpRelease(ButtonRelease):
 777                def __init__(self, delay: int = 0):
 778                    super().__init__('pickUp', delay=delay)
 779
 780            class RunRelease(ButtonRelease):
 781                def __init__(self, delay: int = 0):
 782                    super().__init__('run', delay=delay)
 783
 784            class ShowControls:
 785                def __init__(self) -> None:
 786                    pass
 787
 788                def run(self, a: TutorialActivity) -> None:
 789                    for n in a.control_ui_nodes:
 790                        bs.animate(n, 'opacity', {0.0: 0.0, 1.0: 1.0})
 791
 792            class Text:
 793                def __init__(self, text: str | bs.Lstr):
 794                    self.text = text
 795
 796                def run(self, a: TutorialActivity) -> None:
 797                    assert a.text
 798                    a.text.text = self.text
 799
 800            class PrintPos:
 801                def __init__(self, spaz_num: int | None = None):
 802                    self._spaz_num = spaz_num
 803
 804                def run(self, a: TutorialActivity) -> None:
 805                    if self._spaz_num is None:
 806                        s = a.current_spaz
 807                    else:
 808                        s = a.spazzes[self._spaz_num]
 809                    assert s and s.node
 810                    t = list(s.node.position)
 811                    print('RestorePos(' + str((t[0], t[1] - 1.0, t[2])) + '),')
 812
 813            class RestorePos:
 814                def __init__(self, pos: Sequence[float]) -> None:
 815                    self._pos = pos
 816
 817                def run(self, a: TutorialActivity) -> None:
 818                    s = a.current_spaz
 819                    assert s
 820                    s.handlemessage(bs.StandMessage(self._pos, 0))
 821
 822            class Celebrate:
 823                def __init__(
 824                    self,
 825                    celebrate_type: str = 'both',
 826                    spaz_num: int | None = None,
 827                    duration: int = 1000,
 828                ):
 829                    self._spaz_num = spaz_num
 830                    self._celebrate_type = celebrate_type
 831                    self._duration = duration
 832
 833                def run(self, a: TutorialActivity) -> None:
 834                    if self._spaz_num is None:
 835                        s = a.current_spaz
 836                    else:
 837                        s = a.spazzes[self._spaz_num]
 838                    assert s and s.node
 839                    if self._celebrate_type == 'right':
 840                        s.node.handlemessage('celebrate_r', self._duration)
 841                    elif self._celebrate_type == 'left':
 842                        s.node.handlemessage('celebrate_l', self._duration)
 843                    elif self._celebrate_type == 'both':
 844                        s.node.handlemessage('celebrate', self._duration)
 845                    else:
 846                        raise RuntimeError(
 847                            'invalid celebrate type ' + self._celebrate_type
 848                        )
 849
 850            self._entries = deque(
 851                [
 852                    Reset(),
 853                    SpawnSpaz(0, (0, 5.5, -3.0), make_current=True),
 854                    DelayOld(1000),
 855                    AnalyticsScreen('Tutorial Section 1'),
 856                    Text(
 857                        bs.Lstr(resource=f'{self._r}.phrase01Text')
 858                    ),  # hi there
 859                    Celebrate('left'),
 860                    DelayOld(2000),
 861                    Text(
 862                        bs.Lstr(
 863                            resource=f'{self._r}.phrase02Text',
 864                            subs=[
 865                                ('${APP_NAME}', bs.Lstr(resource='titleText'))
 866                            ],
 867                        )
 868                    ),  # welcome to <appname>
 869                    DelayOld(80),
 870                    Run(release=False),
 871                    Jump(release=False),
 872                    MoveLR(1),
 873                    MoveUD(0),
 874                    DelayOld(70),
 875                    RunRelease(),
 876                    JumpRelease(),
 877                    DelayOld(60),
 878                    MoveUD(1),
 879                    DelayOld(30),
 880                    MoveLR(0),
 881                    DelayOld(90),
 882                    MoveLR(-1),
 883                    DelayOld(20),
 884                    MoveUD(0),
 885                    DelayOld(70),
 886                    MoveUD(-1),
 887                    DelayOld(20),
 888                    MoveLR(0),
 889                    DelayOld(80),
 890                    MoveUD(0),
 891                    DelayOld(1500),
 892                    Text(
 893                        bs.Lstr(resource=f'{self._r}.phrase03Text')
 894                    ),  # here's a few tips
 895                    DelayOld(1000),
 896                    ShowControls(),
 897                    DelayOld(1000),
 898                    Jump(),
 899                    DelayOld(1000),
 900                    Jump(),
 901                    DelayOld(1000),
 902                    AnalyticsScreen('Tutorial Section 2'),
 903                    Text(
 904                        bs.Lstr(
 905                            resource=f'{self._r}.phrase04Text',
 906                            subs=[
 907                                ('${APP_NAME}', bs.Lstr(resource='titleText'))
 908                            ],
 909                        )
 910                    ),  # many things are based on physics
 911                    DelayOld(20),
 912                    MoveUD(0),
 913                    DelayOld(60),
 914                    MoveLR(0),
 915                    DelayOld(10),
 916                    MoveLR(0),
 917                    MoveUD(0),
 918                    DelayOld(10),
 919                    MoveLR(0),
 920                    MoveUD(0),
 921                    DelayOld(20),
 922                    MoveUD(-0.0575579),
 923                    DelayOld(10),
 924                    MoveUD(-0.207831),
 925                    DelayOld(30),
 926                    MoveUD(-0.309793),
 927                    DelayOld(10),
 928                    MoveUD(-0.474502),
 929                    DelayOld(10),
 930                    MoveLR(0.00390637),
 931                    MoveUD(-0.647053),
 932                    DelayOld(20),
 933                    MoveLR(-0.0745262),
 934                    MoveUD(-0.819605),
 935                    DelayOld(10),
 936                    MoveLR(-0.168645),
 937                    MoveUD(-0.937254),
 938                    DelayOld(30),
 939                    MoveLR(-0.294137),
 940                    MoveUD(-1),
 941                    DelayOld(10),
 942                    MoveLR(-0.411786),
 943                    DelayOld(10),
 944                    MoveLR(-0.639241),
 945                    DelayOld(30),
 946                    MoveLR(-0.75689),
 947                    DelayOld(10),
 948                    MoveLR(-0.905911),
 949                    DelayOld(20),
 950                    MoveLR(-1),
 951                    DelayOld(50),
 952                    MoveUD(-0.960784),
 953                    DelayOld(20),
 954                    MoveUD(-0.819605),
 955                    MoveUD(-0.61568),
 956                    DelayOld(20),
 957                    MoveUD(-0.427442),
 958                    DelayOld(20),
 959                    MoveUD(-0.231361),
 960                    DelayOld(10),
 961                    MoveUD(-0.00390637),
 962                    DelayOld(30),
 963                    MoveUD(0.333354),
 964                    MoveUD(0.584338),
 965                    DelayOld(20),
 966                    MoveUD(0.764733),
 967                    DelayOld(30),
 968                    MoveLR(-0.803949),
 969                    MoveUD(0.913755),
 970                    DelayOld(10),
 971                    MoveLR(-0.647084),
 972                    MoveUD(0.992187),
 973                    DelayOld(20),
 974                    MoveLR(-0.435316),
 975                    MoveUD(1),
 976                    DelayOld(20),
 977                    MoveLR(-0.168645),
 978                    MoveUD(0.976501),
 979                    MoveLR(0.0744957),
 980                    MoveUD(0.905911),
 981                    DelayOld(20),
 982                    MoveLR(0.270577),
 983                    MoveUD(0.843165),
 984                    DelayOld(20),
 985                    MoveLR(0.435286),
 986                    MoveUD(0.780419),
 987                    DelayOld(10),
 988                    MoveLR(0.66274),
 989                    MoveUD(0.647084),
 990                    DelayOld(30),
 991                    MoveLR(0.803919),
 992                    MoveUD(0.458846),
 993                    MoveLR(0.929411),
 994                    MoveUD(0.223548),
 995                    DelayOld(20),
 996                    MoveLR(0.95294),
 997                    MoveUD(0.137272),
 998                    DelayOld(20),
 999                    MoveLR(1),
1000                    MoveUD(-0.0509659),
1001                    DelayOld(20),
1002                    MoveUD(-0.247047),
1003                    DelayOld(20),
1004                    MoveUD(-0.443129),
1005                    DelayOld(20),
1006                    MoveUD(-0.694113),
1007                    MoveUD(-0.921567),
1008                    DelayOld(30),
1009                    MoveLR(0.858821),
1010                    MoveUD(-1),
1011                    DelayOld(10),
1012                    MoveLR(0.68627),
1013                    DelayOld(10),
1014                    MoveLR(0.364696),
1015                    DelayOld(20),
1016                    MoveLR(0.0509659),
1017                    DelayOld(20),
1018                    MoveLR(-0.223548),
1019                    DelayOld(10),
1020                    MoveLR(-0.600024),
1021                    MoveUD(-0.913724),
1022                    DelayOld(30),
1023                    MoveLR(-0.858852),
1024                    MoveUD(-0.717643),
1025                    MoveLR(-1),
1026                    MoveUD(-0.474502),
1027                    DelayOld(20),
1028                    MoveUD(-0.396069),
1029                    DelayOld(20),
1030                    MoveUD(-0.286264),
1031                    DelayOld(20),
1032                    MoveUD(-0.137242),
1033                    DelayOld(20),
1034                    MoveUD(0.0353099),
1035                    DelayOld(10),
1036                    MoveUD(0.32551),
1037                    DelayOld(20),
1038                    MoveUD(0.592181),
1039                    DelayOld(10),
1040                    MoveUD(0.851009),
1041                    DelayOld(10),
1042                    MoveUD(1),
1043                    DelayOld(30),
1044                    MoveLR(-0.764733),
1045                    DelayOld(20),
1046                    MoveLR(-0.403943),
1047                    MoveLR(-0.145116),
1048                    DelayOld(30),
1049                    MoveLR(0.0901822),
1050                    MoveLR(0.32548),
1051                    DelayOld(30),
1052                    MoveLR(0.560778),
1053                    MoveUD(0.929441),
1054                    DelayOld(20),
1055                    MoveLR(0.709799),
1056                    MoveUD(0.73336),
1057                    MoveLR(0.803919),
1058                    MoveUD(0.545122),
1059                    DelayOld(20),
1060                    MoveLR(0.882351),
1061                    MoveUD(0.356883),
1062                    DelayOld(10),
1063                    MoveLR(0.968627),
1064                    MoveUD(0.113742),
1065                    DelayOld(20),
1066                    MoveLR(0.992157),
1067                    MoveUD(-0.0823389),
1068                    DelayOld(30),
1069                    MoveUD(-0.309793),
1070                    DelayOld(10),
1071                    MoveUD(-0.545091),
1072                    DelayOld(20),
1073                    MoveLR(0.882351),
1074                    MoveUD(-0.874508),
1075                    DelayOld(20),
1076                    MoveLR(0.756859),
1077                    MoveUD(-1),
1078                    DelayOld(10),
1079                    MoveLR(0.576464),
1080                    DelayOld(20),
1081                    MoveLR(0.254891),
1082                    DelayOld(10),
1083                    MoveLR(-0.0274667),
1084                    DelayOld(10),
1085                    MoveLR(-0.356883),
1086                    DelayOld(30),
1087                    MoveLR(-0.592181),
1088                    MoveLR(-0.827479),
1089                    MoveUD(-0.921567),
1090                    DelayOld(20),
1091                    MoveLR(-1),
1092                    MoveUD(-0.749016),
1093                    DelayOld(20),
1094                    MoveUD(-0.61568),
1095                    DelayOld(10),
1096                    MoveUD(-0.403912),
1097                    DelayOld(20),
1098                    MoveUD(-0.207831),
1099                    DelayOld(10),
1100                    MoveUD(0.121586),
1101                    DelayOld(30),
1102                    MoveUD(0.34904),
1103                    DelayOld(10),
1104                    MoveUD(0.560808),
1105                    DelayOld(10),
1106                    MoveUD(0.827479),
1107                    DelayOld(30),
1108                    MoveUD(1),
1109                    DelayOld(20),
1110                    MoveLR(-0.976501),
1111                    MoveLR(-0.670614),
1112                    DelayOld(20),
1113                    MoveLR(-0.239235),
1114                    DelayOld(20),
1115                    MoveLR(0.160772),
1116                    DelayOld(20),
1117                    MoveLR(0.443129),
1118                    DelayOld(10),
1119                    MoveLR(0.68627),
1120                    MoveUD(0.976501),
1121                    DelayOld(30),
1122                    MoveLR(0.929411),
1123                    MoveUD(0.73336),
1124                    MoveLR(1),
1125                    MoveUD(0.482376),
1126                    DelayOld(20),
1127                    MoveUD(0.34904),
1128                    DelayOld(10),
1129                    MoveUD(0.160802),
1130                    DelayOld(30),
1131                    MoveUD(-0.0744957),
1132                    DelayOld(10),
1133                    MoveUD(-0.333323),
1134                    DelayOld(20),
1135                    MoveUD(-0.647053),
1136                    DelayOld(20),
1137                    MoveUD(-0.937254),
1138                    DelayOld(10),
1139                    MoveLR(0.858821),
1140                    MoveUD(-1),
1141                    DelayOld(10),
1142                    MoveLR(0.576464),
1143                    DelayOld(30),
1144                    MoveLR(0.184301),
1145                    DelayOld(10),
1146                    MoveLR(-0.121586),
1147                    DelayOld(10),
1148                    MoveLR(-0.474532),
1149                    DelayOld(30),
1150                    MoveLR(-0.670614),
1151                    MoveLR(-0.851009),
1152                    DelayOld(30),
1153                    MoveLR(-1),
1154                    MoveUD(-0.968627),
1155                    DelayOld(20),
1156                    MoveUD(-0.843135),
1157                    DelayOld(10),
1158                    MoveUD(-0.631367),
1159                    DelayOld(20),
1160                    MoveUD(-0.403912),
1161                    MoveUD(-0.176458),
1162                    DelayOld(20),
1163                    MoveUD(0.0902127),
1164                    DelayOld(20),
1165                    MoveUD(0.380413),
1166                    DelayOld(10),
1167                    MoveUD(0.717673),
1168                    DelayOld(30),
1169                    MoveUD(1),
1170                    DelayOld(10),
1171                    MoveLR(-0.741203),
1172                    DelayOld(20),
1173                    MoveLR(-0.458846),
1174                    DelayOld(10),
1175                    MoveLR(-0.145116),
1176                    DelayOld(10),
1177                    MoveLR(0.0980255),
1178                    DelayOld(20),
1179                    MoveLR(0.294107),
1180                    DelayOld(30),
1181                    MoveLR(0.466659),
1182                    MoveLR(0.717643),
1183                    MoveUD(0.796106),
1184                    DelayOld(20),
1185                    MoveLR(0.921567),
1186                    MoveUD(0.443159),
1187                    DelayOld(20),
1188                    MoveLR(1),
1189                    MoveUD(0.145116),
1190                    DelayOld(10),
1191                    MoveUD(-0.0274361),
1192                    DelayOld(30),
1193                    MoveUD(-0.223518),
1194                    MoveUD(-0.427442),
1195                    DelayOld(20),
1196                    MoveUD(-0.874508),
1197                    DelayOld(20),
1198                    MoveUD(-1),
1199                    DelayOld(10),
1200                    MoveLR(0.929411),
1201                    DelayOld(20),
1202                    MoveLR(0.68627),
1203                    DelayOld(20),
1204                    MoveLR(0.364696),
1205                    DelayOld(20),
1206                    MoveLR(0.0431227),
1207                    DelayOld(10),
1208                    MoveLR(-0.333354),
1209                    DelayOld(20),
1210                    MoveLR(-0.639241),
1211                    DelayOld(20),
1212                    MoveLR(-0.968657),
1213                    MoveUD(-0.968627),
1214                    DelayOld(20),
1215                    MoveLR(-1),
1216                    MoveUD(-0.890194),
1217                    MoveUD(-0.866665),
1218                    DelayOld(20),
1219                    MoveUD(-0.749016),
1220                    DelayOld(20),
1221                    MoveUD(-0.529405),
1222                    DelayOld(20),
1223                    MoveUD(-0.30195),
1224                    DelayOld(10),
1225                    MoveUD(-0.00390637),
1226                    DelayOld(10),
1227                    MoveUD(0.262764),
1228                    DelayOld(30),
1229                    MoveLR(-0.600024),
1230                    MoveUD(0.458846),
1231                    DelayOld(10),
1232                    MoveLR(-0.294137),
1233                    MoveUD(0.482376),
1234                    DelayOld(20),
1235                    MoveLR(-0.200018),
1236                    MoveUD(0.505905),
1237                    DelayOld(10),
1238                    MoveLR(-0.145116),
1239                    MoveUD(0.545122),
1240                    DelayOld(20),
1241                    MoveLR(-0.0353099),
1242                    MoveUD(0.584338),
1243                    DelayOld(20),
1244                    MoveLR(0.137242),
1245                    MoveUD(0.592181),
1246                    DelayOld(20),
1247                    MoveLR(0.30195),
1248                    DelayOld(10),
1249                    MoveLR(0.490188),
1250                    DelayOld(10),
1251                    MoveLR(0.599994),
1252                    MoveUD(0.529435),
1253                    DelayOld(30),
1254                    MoveLR(0.66274),
1255                    MoveUD(0.3961),
1256                    DelayOld(20),
1257                    MoveLR(0.670583),
1258                    MoveUD(0.231391),
1259                    MoveLR(0.68627),
1260                    MoveUD(0.0745262),
1261                    Move(0, -0.01),
1262                    DelayOld(100),
1263                    Move(0, 0),
1264                    DelayOld(1000),
1265                    Text(
1266                        bs.Lstr(resource=f'{self._r}.phrase05Text')
1267                    ),  # for example when you punch..
1268                    DelayOld(510),
1269                    Move(0, -0.01),
1270                    DelayOld(100),
1271                    Move(0, 0),
1272                    DelayOld(500),
1273                    SpawnSpaz(
1274                        0,
1275                        (-0.09249162673950195, 4.337906360626221, -2.3),
1276                        make_current=True,
1277                        flash=False,
1278                    ),
1279                    SpawnSpaz(
1280                        1,
1281                        (-3.1, 4.3, -2.0),
1282                        make_current=False,
1283                        color=(1, 1, 0.4),
1284                        name=bs.Lstr(resource=f'{self._r}.randomName1Text'),
1285                    ),
1286                    Move(-1.0, 0),
1287                    DelayOld(1050),
1288                    Move(0, -0.01),
1289                    DelayOld(100),
1290                    Move(0, 0),
1291                    DelayOld(1000),
1292                    Text(
1293                        bs.Lstr(resource=f'{self._r}.phrase06Text')
1294                    ),  # your damage is based
1295                    DelayOld(1200),
1296                    Move(-0.05, 0),
1297                    DelayOld(200),
1298                    Punch(),
1299                    DelayOld(800),
1300                    Punch(),
1301                    DelayOld(800),
1302                    Punch(),
1303                    DelayOld(800),
1304                    Move(0, -0.01),
1305                    DelayOld(100),
1306                    Move(0, 0),
1307                    Text(
1308                        bs.Lstr(
1309                            resource=f'{self._r}.phrase07Text',
1310                            subs=[
1311                                (
1312                                    '${NAME}',
1313                                    bs.Lstr(
1314                                        resource=f'{self._r}.randomName1Text'
1315                                    ),
1316                                )
1317                            ],
1318                        )
1319                    ),  # see that didn't hurt fred
1320                    DelayOld(2000),
1321                    Celebrate('right', spaz_num=1),
1322                    DelayOld(1400),
1323                    Text(
1324                        bs.Lstr(resource=f'{self._r}.phrase08Text')
1325                    ),  # lets jump and spin to get more speed
1326                    DelayOld(30),
1327                    MoveLR(0),
1328                    DelayOld(40),
1329                    MoveLR(0),
1330                    DelayOld(40),
1331                    MoveLR(0),
1332                    DelayOld(130),
1333                    MoveLR(0),
1334                    DelayOld(100),
1335                    MoveLR(0),
1336                    DelayOld(10),
1337                    MoveLR(0.0480667),
1338                    DelayOld(40),
1339                    MoveLR(0.056093),
1340                    MoveLR(0.0681173),
1341                    DelayOld(30),
1342                    MoveLR(0.0801416),
1343                    DelayOld(10),
1344                    MoveLR(0.184301),
1345                    DelayOld(10),
1346                    MoveLR(0.207831),
1347                    DelayOld(20),
1348                    MoveLR(0.231361),
1349                    DelayOld(30),
1350                    MoveLR(0.239204),
1351                    DelayOld(30),
1352                    MoveLR(0.254891),
1353                    DelayOld(40),
1354                    MoveLR(0.270577),
1355                    DelayOld(10),
1356                    MoveLR(0.30195),
1357                    DelayOld(20),
1358                    MoveLR(0.341166),
1359                    DelayOld(30),
1360                    MoveLR(0.388226),
1361                    MoveLR(0.435286),
1362                    DelayOld(30),
1363                    MoveLR(0.490188),
1364                    DelayOld(10),
1365                    MoveLR(0.560778),
1366                    DelayOld(20),
1367                    MoveLR(0.599994),
1368                    DelayOld(10),
1369                    MoveLR(0.647053),
1370                    DelayOld(10),
1371                    MoveLR(0.68627),
1372                    DelayOld(30),
1373                    MoveLR(0.733329),
1374                    DelayOld(20),
1375                    MoveLR(0.764702),
1376                    DelayOld(10),
1377                    MoveLR(0.827448),
1378                    DelayOld(20),
1379                    MoveLR(0.874508),
1380                    DelayOld(20),
1381                    MoveLR(0.929411),
1382                    DelayOld(10),
1383                    MoveLR(1),
1384                    DelayOld(830),
1385                    MoveUD(0.0274667),
1386                    DelayOld(10),
1387                    MoveLR(0.95294),
1388                    MoveUD(0.113742),
1389                    DelayOld(30),
1390                    MoveLR(0.780389),
1391                    MoveUD(0.184332),
1392                    DelayOld(10),
1393                    MoveLR(0.27842),
1394                    MoveUD(0.0745262),
1395                    DelayOld(20),
1396                    MoveLR(0),
1397                    MoveUD(0),
1398                    DelayOld(390),
1399                    MoveLR(0),
1400                    MoveLR(0),
1401                    DelayOld(20),
1402                    MoveLR(0),
1403                    DelayOld(20),
1404                    MoveLR(0),
1405                    DelayOld(10),
1406                    MoveLR(-0.0537431),
1407                    DelayOld(20),
1408                    MoveLR(-0.215705),
1409                    DelayOld(30),
1410                    MoveLR(-0.388256),
1411                    MoveLR(-0.529435),
1412                    DelayOld(30),
1413                    MoveLR(-0.694143),
1414                    DelayOld(20),
1415                    MoveLR(-0.851009),
1416                    MoveUD(0.0588397),
1417                    DelayOld(10),
1418                    MoveLR(-1),
1419                    MoveUD(0.0745262),
1420                    Run(release=False),
1421                    DelayOld(200),
1422                    MoveUD(0.0509964),
1423                    DelayOld(30),
1424                    MoveUD(0.0117801),
1425                    DelayOld(20),
1426                    MoveUD(-0.0901822),
1427                    MoveUD(-0.372539),
1428                    DelayOld(30),
1429                    MoveLR(-0.898068),
1430                    MoveUD(-0.890194),
1431                    Jump(release=False),
1432                    DelayOld(20),
1433                    MoveLR(-0.647084),
1434                    MoveUD(-1),
1435                    MoveLR(-0.427473),
1436                    DelayOld(20),
1437                    MoveLR(-0.00393689),
1438                    DelayOld(10),
1439                    MoveLR(0.537248),
1440                    DelayOld(30),
1441                    MoveLR(1),
1442                    DelayOld(50),
1443                    RunRelease(),
1444                    JumpRelease(),
1445                    DelayOld(50),
1446                    MoveUD(-0.921567),
1447                    MoveUD(-0.749016),
1448                    DelayOld(30),
1449                    MoveUD(-0.552934),
1450                    DelayOld(10),
1451                    MoveUD(-0.247047),
1452                    DelayOld(20),
1453                    MoveUD(0.200018),
1454                    DelayOld(20),
1455                    MoveUD(0.670614),
1456                    MoveUD(1),
1457                    DelayOld(70),
1458                    MoveLR(0.97647),
1459                    DelayOld(20),
1460                    MoveLR(0.764702),
1461                    DelayOld(20),
1462                    MoveLR(0.364696),
1463                    DelayOld(20),
1464                    MoveLR(0.00390637),
1465                    MoveLR(-0.309824),
1466                    DelayOld(20),
1467                    MoveLR(-0.576495),
1468                    DelayOld(30),
1469                    MoveLR(-0.898068),
1470                    DelayOld(10),
1471                    MoveLR(-1),
1472                    MoveUD(0.905911),
1473                    DelayOld(20),
1474                    MoveUD(0.498062),
1475                    DelayOld(20),
1476                    MoveUD(0.0274667),
1477                    MoveUD(-0.403912),
1478                    DelayOld(20),
1479                    MoveUD(-1),
1480                    Run(release=False),
1481                    Jump(release=False),
1482                    DelayOld(10),
1483                    Punch(release=False),
1484                    DelayOld(70),
1485                    JumpRelease(),
1486                    DelayOld(110),
1487                    MoveLR(-0.976501),
1488                    RunRelease(),
1489                    PunchRelease(),
1490                    DelayOld(10),
1491                    MoveLR(-0.952971),
1492                    DelayOld(20),
1493                    MoveLR(-0.905911),
1494                    MoveLR(-0.827479),
1495                    DelayOld(20),
1496                    MoveLR(-0.75689),
1497                    DelayOld(30),
1498                    MoveLR(-0.73336),
1499                    MoveLR(-0.694143),
1500                    DelayOld(20),
1501                    MoveLR(-0.670614),
1502                    DelayOld(30),
1503                    MoveLR(-0.66277),
1504                    DelayOld(10),
1505                    MoveUD(-0.960784),
1506                    DelayOld(20),
1507                    MoveLR(-0.623554),
1508                    MoveUD(-0.874508),
1509                    DelayOld(10),
1510                    MoveLR(-0.545122),
1511                    MoveUD(-0.694113),
1512                    DelayOld(20),
1513                    MoveLR(-0.505905),
1514                    MoveUD(-0.474502),
1515                    DelayOld(20),
1516                    MoveLR(-0.458846),
1517                    MoveUD(-0.356853),
1518                    MoveLR(-0.364727),
1519                    MoveUD(-0.27842),
1520                    DelayOld(20),
1521                    MoveLR(0.00390637),
1522                    Move(0, 0),
1523                    DelayOld(1000),
1524                    Text(
1525                        bs.Lstr(resource=f'{self._r}.phrase09Text')
1526                    ),  # ah that's better
1527                    DelayOld(1900),
1528                    AnalyticsScreen('Tutorial Section 3'),
1529                    Text(
1530                        bs.Lstr(resource=f'{self._r}.phrase10Text')
1531                    ),  # running also helps
1532                    DelayOld(100),
1533                    SpawnSpaz(
1534                        0, (-3.2, 4.3, -4.4), make_current=True, flash=False
1535                    ),
1536                    SpawnSpaz(
1537                        1,
1538                        (3.3, 4.2, -5.8),
1539                        make_current=False,
1540                        color=(0.9, 0.5, 1.0),
1541                        name=bs.Lstr(resource=f'{self._r}.randomName2Text'),
1542                    ),
1543                    DelayOld(1800),
1544                    Text(
1545                        bs.Lstr(resource=f'{self._r}.phrase11Text')
1546                    ),  # hold ANY button to run
1547                    DelayOld(300),
1548                    MoveUD(0),
1549                    DelayOld(20),
1550                    MoveUD(-0.0520646),
1551                    DelayOld(20),
1552                    MoveLR(0),
1553                    MoveUD(-0.223518),
1554                    Run(release=False),
1555                    Jump(release=False),
1556                    DelayOld(10),
1557                    MoveLR(0.0980255),
1558                    MoveUD(-0.309793),
1559                    DelayOld(30),
1560                    MoveLR(0.160772),
1561                    MoveUD(-0.427442),
1562                    DelayOld(20),
1563                    MoveLR(0.231361),
1564                    MoveUD(-0.545091),
1565                    DelayOld(10),
1566                    MoveLR(0.317637),
1567                    MoveUD(-0.678426),
1568                    DelayOld(20),
1569                    MoveLR(0.396069),
1570                    MoveUD(-0.819605),
1571                    MoveLR(0.482345),
1572                    MoveUD(-0.913724),
1573                    DelayOld(20),
1574                    MoveLR(0.560778),
1575                    MoveUD(-1),
1576                    DelayOld(20),
1577                    MoveLR(0.607837),
1578                    DelayOld(10),
1579                    MoveLR(0.623524),
1580                    DelayOld(30),
1581                    MoveLR(0.647053),
1582                    DelayOld(20),
1583                    MoveLR(0.670583),
1584                    MoveLR(0.694113),
1585                    DelayOld(30),
1586                    MoveLR(0.733329),
1587                    DelayOld(20),
1588                    MoveLR(0.764702),
1589                    MoveLR(0.788232),
1590                    DelayOld(20),
1591                    MoveLR(0.827448),
1592                    DelayOld(10),
1593                    MoveLR(0.858821),
1594                    DelayOld(20),
1595                    MoveLR(0.921567),
1596                    DelayOld(30),
1597                    MoveLR(0.97647),
1598                    MoveLR(1),
1599                    DelayOld(130),
1600                    MoveUD(-0.960784),
1601                    DelayOld(20),
1602                    MoveUD(-0.921567),
1603                    DelayOld(30),
1604                    MoveUD(-0.866665),
1605                    MoveUD(-0.819605),
1606                    DelayOld(30),
1607                    MoveUD(-0.772546),
1608                    MoveUD(-0.725486),
1609                    DelayOld(30),
1610                    MoveUD(-0.631367),
1611                    DelayOld(10),
1612                    MoveUD(-0.552934),
1613                    DelayOld(20),
1614                    MoveUD(-0.474502),
1615                    DelayOld(10),
1616                    MoveUD(-0.403912),
1617                    DelayOld(30),
1618                    MoveUD(-0.356853),
1619                    DelayOld(30),
1620                    MoveUD(-0.34901),
1621                    DelayOld(20),
1622                    MoveUD(-0.333323),
1623                    DelayOld(20),
1624                    MoveUD(-0.32548),
1625                    DelayOld(10),
1626                    MoveUD(-0.30195),
1627                    DelayOld(20),
1628                    MoveUD(-0.27842),
1629                    DelayOld(30),
1630                    MoveUD(-0.254891),
1631                    MoveUD(-0.231361),
1632                    DelayOld(30),
1633                    MoveUD(-0.207831),
1634                    DelayOld(20),
1635                    MoveUD(-0.199988),
1636                    MoveUD(-0.176458),
1637                    DelayOld(30),
1638                    MoveUD(-0.137242),
1639                    MoveUD(-0.0823389),
1640                    DelayOld(20),
1641                    MoveUD(-0.0274361),
1642                    DelayOld(20),
1643                    MoveUD(0.00393689),
1644                    DelayOld(40),
1645                    MoveUD(0.0353099),
1646                    DelayOld(20),
1647                    MoveUD(0.113742),
1648                    DelayOld(10),
1649                    MoveUD(0.137272),
1650                    DelayOld(20),
1651                    MoveUD(0.160802),
1652                    MoveUD(0.184332),
1653                    DelayOld(20),
1654                    MoveUD(0.207862),
1655                    DelayOld(30),
1656                    MoveUD(0.247078),
1657                    MoveUD(0.262764),
1658                    DelayOld(20),
1659                    MoveUD(0.270608),
1660                    DelayOld(30),
1661                    MoveUD(0.294137),
1662                    MoveUD(0.32551),
1663                    DelayOld(30),
1664                    MoveUD(0.37257),
1665                    Celebrate('left', 1),
1666                    DelayOld(20),
1667                    MoveUD(0.498062),
1668                    MoveUD(0.560808),
1669                    DelayOld(30),
1670                    MoveUD(0.654927),
1671                    MoveUD(0.694143),
1672                    DelayOld(30),
1673                    MoveUD(0.741203),
1674                    DelayOld(20),
1675                    MoveUD(0.780419),
1676                    MoveUD(0.819636),
1677                    DelayOld(20),
1678                    MoveUD(0.843165),
1679                    DelayOld(20),
1680                    MoveUD(0.882382),
1681                    DelayOld(10),
1682                    MoveUD(0.913755),
1683                    DelayOld(30),
1684                    MoveUD(0.968657),
1685                    MoveUD(1),
1686                    DelayOld(560),
1687                    Punch(release=False),
1688                    DelayOld(210),
1689                    MoveUD(0.968657),
1690                    DelayOld(30),
1691                    MoveUD(0.75689),
1692                    PunchRelease(),
1693                    DelayOld(20),
1694                    MoveLR(0.95294),
1695                    MoveUD(0.435316),
1696                    RunRelease(),
1697                    JumpRelease(),
1698                    MoveLR(0.811762),
1699                    MoveUD(0.270608),
1700                    DelayOld(20),
1701                    MoveLR(0.670583),
1702                    MoveUD(0.160802),
1703                    DelayOld(20),
1704                    MoveLR(0.466659),
1705                    MoveUD(0.0588397),
1706                    DelayOld(10),
1707                    MoveLR(0.317637),
1708                    MoveUD(-0.00390637),
1709                    DelayOld(20),
1710                    MoveLR(0.0801416),
1711                    DelayOld(10),
1712                    MoveLR(0),
1713                    DelayOld(20),
1714                    MoveLR(0),
1715                    DelayOld(30),
1716                    MoveLR(0),
1717                    DelayOld(30),
1718                    MoveLR(0),
1719                    DelayOld(20),
1720                    MoveLR(0),
1721                    DelayOld(100),
1722                    MoveLR(0),
1723                    DelayOld(30),
1724                    MoveUD(0),
1725                    DelayOld(30),
1726                    MoveUD(0),
1727                    DelayOld(50),
1728                    MoveUD(0),
1729                    MoveUD(0),
1730                    DelayOld(30),
1731                    MoveLR(0),
1732                    MoveUD(-0.0520646),
1733                    MoveLR(0),
1734                    MoveUD(-0.0640889),
1735                    DelayOld(20),
1736                    MoveLR(0),
1737                    MoveUD(-0.0881375),
1738                    DelayOld(30),
1739                    MoveLR(-0.0498978),
1740                    MoveUD(-0.199988),
1741                    MoveLR(-0.121586),
1742                    MoveUD(-0.207831),
1743                    DelayOld(20),
1744                    MoveLR(-0.145116),
1745                    MoveUD(-0.223518),
1746                    DelayOld(30),
1747                    MoveLR(-0.152959),
1748                    MoveUD(-0.231361),
1749                    MoveLR(-0.192175),
1750                    MoveUD(-0.262734),
1751                    DelayOld(30),
1752                    MoveLR(-0.200018),
1753                    MoveUD(-0.27842),
1754                    DelayOld(20),
1755                    MoveLR(-0.239235),
1756                    MoveUD(-0.30195),
1757                    MoveUD(-0.309793),
1758                    DelayOld(40),
1759                    MoveUD(-0.333323),
1760                    DelayOld(10),
1761                    MoveUD(-0.34901),
1762                    DelayOld(30),
1763                    MoveUD(-0.372539),
1764                    MoveUD(-0.396069),
1765                    DelayOld(20),
1766                    MoveUD(-0.443129),
1767                    DelayOld(20),
1768                    MoveUD(-0.458815),
1769                    DelayOld(10),
1770                    MoveUD(-0.474502),
1771                    DelayOld(50),
1772                    MoveUD(-0.482345),
1773                    DelayOld(30),
1774                    MoveLR(-0.215705),
1775                    DelayOld(30),
1776                    MoveLR(-0.200018),
1777                    DelayOld(10),
1778                    MoveLR(-0.192175),
1779                    DelayOld(10),
1780                    MoveLR(-0.176489),
1781                    DelayOld(30),
1782                    MoveLR(-0.152959),
1783                    DelayOld(20),
1784                    MoveLR(-0.145116),
1785                    MoveLR(-0.121586),
1786                    MoveUD(-0.458815),
1787                    DelayOld(30),
1788                    MoveLR(-0.098056),
1789                    MoveUD(-0.419599),
1790                    DelayOld(10),
1791                    MoveLR(-0.0745262),
1792                    MoveUD(-0.333323),
1793                    DelayOld(10),
1794                    MoveLR(0.00390637),
1795                    MoveUD(0),
1796                    DelayOld(990),
1797                    MoveLR(0),
1798                    DelayOld(660),
1799                    MoveUD(0),
1800                    AnalyticsScreen('Tutorial Section 4'),
1801                    Text(
1802                        bs.Lstr(resource=f'{self._r}.phrase12Text')
1803                    ),  # for extra-awesome punches,...
1804                    DelayOld(200),
1805                    SpawnSpaz(
1806                        0,
1807                        (
1808                            2.368781805038452,
1809                            4.337533950805664,
1810                            -4.360159873962402,
1811                        ),
1812                        make_current=True,
1813                        flash=False,
1814                    ),
1815                    SpawnSpaz(
1816                        1,
1817                        (-3.2, 4.3, -4.5),
1818                        make_current=False,
1819                        color=(1.0, 0.7, 0.3),
1820                        # name=R.randomName3Text),
1821                        name=bs.Lstr(resource=f'{self._r}.randomName3Text'),
1822                    ),
1823                    DelayOld(100),
1824                    Powerup(1, (2.5, 0.0, 0), relative_to=0),
1825                    Move(1, 0),
1826                    DelayOld(1700),
1827                    Move(0, -0.1),
1828                    DelayOld(100),
1829                    Move(0, 0),
1830                    DelayOld(500),
1831                    DelayOld(320),
1832                    MoveLR(0),
1833                    DelayOld(20),
1834                    MoveLR(0),
1835                    DelayOld(10),
1836                    MoveLR(0),
1837                    DelayOld(20),
1838                    MoveLR(-0.333354),
1839                    MoveLR(-0.592181),
1840                    DelayOld(20),
1841                    MoveLR(-0.788263),
1842                    DelayOld(20),
1843                    MoveLR(-1),
1844                    MoveUD(0.0353099),
1845                    MoveUD(0.0588397),
1846                    DelayOld(10),
1847                    Run(release=False),
1848                    DelayOld(780),
1849                    MoveUD(0.0274667),
1850                    MoveUD(0.00393689),
1851                    DelayOld(10),
1852                    MoveUD(-0.00390637),
1853                    DelayOld(440),
1854                    MoveUD(0.0353099),
1855                    DelayOld(20),
1856                    MoveUD(0.0588397),
1857                    DelayOld(10),
1858                    MoveUD(0.0902127),
1859                    DelayOld(260),
1860                    MoveUD(0.0353099),
1861                    DelayOld(30),
1862                    MoveUD(0.00393689),
1863                    DelayOld(10),
1864                    MoveUD(-0.00390637),
1865                    MoveUD(-0.0274361),
1866                    Celebrate('left', 1),
1867                    DelayOld(10),
1868                    MoveUD(-0.0823389),
1869                    DelayOld(30),
1870                    MoveUD(-0.176458),
1871                    MoveUD(-0.286264),
1872                    DelayOld(20),
1873                    MoveUD(-0.498032),
1874                    Jump(release=False),
1875                    MoveUD(-0.764702),
1876                    DelayOld(30),
1877                    MoveLR(-0.858852),
1878                    MoveUD(-1),
1879                    MoveLR(-0.780419),
1880                    DelayOld(20),
1881                    MoveLR(-0.717673),
1882                    DelayOld(10),
1883                    MoveLR(-0.552965),
1884                    DelayOld(10),
1885                    MoveLR(-0.341197),
1886                    DelayOld(10),
1887                    MoveLR(-0.0274667),
1888                    DelayOld(10),
1889                    MoveLR(0.27842),
1890                    DelayOld(20),
1891                    MoveLR(0.811762),
1892                    MoveLR(1),
1893                    RunRelease(),
1894                    JumpRelease(),
1895                    DelayOld(260),
1896                    MoveLR(0.95294),
1897                    DelayOld(30),
1898                    MoveLR(0.756859),
1899                    DelayOld(10),
1900                    MoveLR(0.317637),
1901                    MoveLR(-0.00393689),
1902                    DelayOld(10),
1903                    MoveLR(-0.341197),
1904                    DelayOld(10),
1905                    MoveLR(-0.647084),
1906                    MoveUD(-0.921567),
1907                    DelayOld(10),
1908                    MoveLR(-1),
1909                    MoveUD(-0.599994),
1910                    MoveUD(-0.474502),
1911                    DelayOld(10),
1912                    MoveUD(-0.309793),
1913                    DelayOld(10),
1914                    MoveUD(-0.160772),
1915                    MoveUD(-0.0352794),
1916                    Delay(10),
1917                    MoveUD(0.176489),
1918                    Delay(10),
1919                    MoveUD(0.607868),
1920                    Run(release=False),
1921                    Jump(release=False),
1922                    DelayOld(20),
1923                    MoveUD(1),
1924                    DelayOld(30),
1925                    MoveLR(-0.921598),
1926                    DelayOld(10),
1927                    Punch(release=False),
1928                    MoveLR(-0.639241),
1929                    DelayOld(10),
1930                    MoveLR(-0.223548),
1931                    DelayOld(10),
1932                    MoveLR(0.254891),
1933                    DelayOld(10),
1934                    MoveLR(0.741172),
1935                    MoveLR(1),
1936                    DelayOld(40),
1937                    JumpRelease(),
1938                    DelayOld(40),
1939                    MoveUD(0.976501),
1940                    DelayOld(10),
1941                    MoveUD(0.73336),
1942                    DelayOld(10),
1943                    MoveUD(0.309824),
1944                    DelayOld(20),
1945                    MoveUD(-0.184301),
1946                    DelayOld(20),
1947                    MoveUD(-0.811762),
1948                    MoveUD(-1),
1949                    KillSpaz(1, explode=True),
1950                    DelayOld(10),
1951                    RunRelease(),
1952                    PunchRelease(),
1953                    DelayOld(110),
1954                    MoveLR(0.97647),
1955                    MoveLR(0.898038),
1956                    DelayOld(20),
1957                    MoveLR(0.788232),
1958                    DelayOld(20),
1959                    MoveLR(0.670583),
1960                    DelayOld(10),
1961                    MoveLR(0.505875),
1962                    DelayOld(10),
1963                    MoveLR(0.32548),
1964                    DelayOld(20),
1965                    MoveLR(0.137242),
1966                    DelayOld(10),
1967                    MoveLR(-0.00393689),
1968                    DelayOld(10),
1969                    MoveLR(-0.215705),
1970                    MoveLR(-0.356883),
1971                    DelayOld(20),
1972                    MoveLR(-0.451003),
1973                    DelayOld(10),
1974                    MoveLR(-0.552965),
1975                    DelayOld(20),
1976                    MoveLR(-0.670614),
1977                    MoveLR(-0.780419),
1978                    DelayOld(10),
1979                    MoveLR(-0.898068),
1980                    DelayOld(20),
1981                    MoveLR(-1),
1982                    DelayOld(370),
1983                    MoveLR(-0.976501),
1984                    DelayOld(10),
1985                    MoveLR(-0.952971),
1986                    DelayOld(10),
1987                    MoveLR(-0.929441),
1988                    MoveLR(-0.898068),
1989                    DelayOld(30),
1990                    MoveLR(-0.874538),
1991                    DelayOld(10),
1992                    MoveLR(-0.851009),
1993                    DelayOld(10),
1994                    MoveLR(-0.835322),
1995                    MoveUD(-0.968627),
1996                    DelayOld(10),
1997                    MoveLR(-0.827479),
1998                    MoveUD(-0.960784),
1999                    DelayOld(20),
2000                    MoveUD(-0.945097),
2001                    DelayOld(70),
2002                    MoveUD(-0.937254),
2003                    DelayOld(20),
2004                    MoveUD(-0.913724),
2005                    DelayOld(20),
2006                    MoveUD(-0.890194),
2007                    MoveLR(-0.780419),
2008                    MoveUD(-0.827448),
2009                    DelayOld(20),
2010                    MoveLR(0.317637),
2011                    MoveUD(0.3961),
2012                    MoveLR(0.0195929),
2013                    MoveUD(0.056093),
2014                    DelayOld(20),
2015                    MoveUD(0),
2016                    DelayOld(750),
2017                    MoveLR(0),
2018                    Text(
2019                        bs.Lstr(
2020                            resource=f'{self._r}.phrase13Text',
2021                            subs=[
2022                                (
2023                                    '${NAME}',
2024                                    bs.Lstr(
2025                                        resource=f'{self._r}.randomName3Text'
2026                                    ),
2027                                )
2028                            ],
2029                        )
2030                    ),  # whoops sorry bill
2031                    RemoveGloves(),
2032                    DelayOld(2000),
2033                    AnalyticsScreen('Tutorial Section 5'),
2034                    Text(
2035                        bs.Lstr(
2036                            resource=f'{self._r}.phrase14Text',
2037                            subs=[
2038                                (
2039                                    '${NAME}',
2040                                    bs.Lstr(
2041                                        resource=f'{self._r}.randomName4Text'
2042                                    ),
2043                                )
2044                            ],
2045                        )
2046                    ),  # you can pick up and throw things such as chuck here
2047                    SpawnSpaz(
2048                        0,
2049                        (-4.0, 4.3, -2.5),
2050                        make_current=True,
2051                        flash=False,
2052                        angle=90,
2053                    ),
2054                    SpawnSpaz(
2055                        1,
2056                        (5, 0, -1.0),
2057                        relative_to=0,
2058                        make_current=False,
2059                        color=(0.4, 1.0, 0.7),
2060                        name=bs.Lstr(resource=f'{self._r}.randomName4Text'),
2061                    ),
2062                    DelayOld(1000),
2063                    Celebrate('left', 1, duration=1000),
2064                    Move(1, 0.2),
2065                    DelayOld(2000),
2066                    PickUp(),
2067                    DelayOld(200),
2068                    Move(0.5, 1.0),
2069                    DelayOld(1200),
2070                    PickUp(),
2071                    Move(0, 0),
2072                    DelayOld(1000),
2073                    Celebrate('left'),
2074                    DelayOld(1500),
2075                    Move(0, -1.0),
2076                    DelayOld(800),
2077                    Move(0, 0),
2078                    DelayOld(800),
2079                    SpawnSpaz(
2080                        0,
2081                        (1.5, 4.3, -4.0),
2082                        make_current=True,
2083                        flash=False,
2084                        angle=0,
2085                    ),
2086                    AnalyticsScreen('Tutorial Section 6'),
2087                    Text(
2088                        bs.Lstr(resource=f'{self._r}.phrase15Text')
2089                    ),  # lastly there's bombs
2090                    DelayOld(1900),
2091                    Text(
2092                        bs.Lstr(resource=f'{self._r}.phrase16Text')
2093                    ),  # throwing bombs takes practice
2094                    DelayOld(2000),
2095                    Bomb(),
2096                    Move(-0.1, -0.1),
2097                    DelayOld(100),
2098                    Move(0, 0),
2099                    DelayOld(500),
2100                    DelayOld(1000),
2101                    Bomb(),
2102                    DelayOld(2000),
2103                    Text(
2104                        bs.Lstr(resource=f'{self._r}.phrase17Text')
2105                    ),  # not a very good throw
2106                    DelayOld(3000),
2107                    Text(
2108                        bs.Lstr(resource=f'{self._r}.phrase18Text')
2109                    ),  # moving helps you get distance
2110                    DelayOld(1000),
2111                    Bomb(),
2112                    DelayOld(500),
2113                    Move(-0.3, 0),
2114                    DelayOld(100),
2115                    Move(-0.6, 0),
2116                    DelayOld(100),
2117                    Move(-1, 0),
2118                    DelayOld(800),
2119                    Bomb(),
2120                    DelayOld(400),
2121                    Move(0, -0.1),
2122                    DelayOld(100),
2123                    Move(0, 0),
2124                    DelayOld(2500),
2125                    Text(
2126                        bs.Lstr(resource=f'{self._r}.phrase19Text')
2127                    ),  # jumping helps you get height
2128                    DelayOld(2000),
2129                    Bomb(),
2130                    DelayOld(500),
2131                    Move(1, 0),
2132                    DelayOld(300),
2133                    Jump(release_delay=250),
2134                    DelayOld(500),
2135                    Jump(release_delay=250),
2136                    DelayOld(550),
2137                    Jump(release_delay=250),
2138                    DelayOld(160),
2139                    Punch(),
2140                    DelayOld(500),
2141                    Move(0, -0.1),
2142                    DelayOld(100),
2143                    Move(0, 0),
2144                    DelayOld(2000),
2145                    Text(
2146                        bs.Lstr(resource=f'{self._r}.phrase20Text')
2147                    ),  # whiplash your bombs
2148                    DelayOld(1000),
2149                    Bomb(release=False),
2150                    DelayOld2(80),
2151                    RunRelease(),
2152                    BombRelease(),
2153                    DelayOld2(620),
2154                    MoveLR(0),
2155                    DelayOld2(10),
2156                    MoveLR(0),
2157                    DelayOld2(40),
2158                    MoveLR(0),
2159                    DelayOld2(10),
2160                    MoveLR(-0.0537431),
2161                    MoveUD(0),
2162                    DelayOld2(20),
2163                    MoveLR(-0.262764),
2164                    DelayOld2(20),
2165                    MoveLR(-0.498062),
2166                    DelayOld2(10),
2167                    MoveLR(-0.639241),
2168                    DelayOld2(20),
2169                    MoveLR(-0.73336),
2170                    DelayOld2(10),
2171                    MoveLR(-0.843165),
2172                    MoveUD(-0.0352794),
2173                    DelayOld2(30),
2174                    MoveLR(-1),
2175                    DelayOld2(10),
2176                    MoveUD(-0.0588092),
2177                    DelayOld2(10),
2178                    MoveUD(-0.160772),
2179                    DelayOld2(20),
2180                    MoveUD(-0.286264),
2181                    DelayOld2(20),
2182                    MoveUD(-0.427442),
2183                    DelayOld2(10),
2184                    MoveUD(-0.623524),
2185                    DelayOld2(20),
2186                    MoveUD(-0.843135),
2187                    DelayOld2(10),
2188                    MoveUD(-1),
2189                    DelayOld2(40),
2190                    MoveLR(-0.890225),
2191                    DelayOld2(10),
2192                    MoveLR(-0.670614),
2193                    DelayOld2(20),
2194                    MoveLR(-0.435316),
2195                    DelayOld2(20),
2196                    MoveLR(-0.184332),
2197                    DelayOld2(10),
2198                    MoveLR(0.00390637),
2199                    DelayOld2(20),
2200                    MoveLR(0.223518),
2201                    DelayOld2(10),
2202                    MoveLR(0.388226),
2203                    DelayOld2(20),
2204                    MoveLR(0.560778),
2205                    DelayOld2(20),
2206                    MoveLR(0.717643),
2207                    DelayOld2(10),
2208                    MoveLR(0.890194),
2209                    DelayOld2(20),
2210                    MoveLR(1),
2211                    DelayOld2(30),
2212                    MoveUD(-0.968627),
2213                    DelayOld2(20),
2214                    MoveUD(-0.898038),
2215                    DelayOld2(10),
2216                    MoveUD(-0.741172),
2217                    DelayOld2(20),
2218                    MoveUD(-0.498032),
2219                    DelayOld2(20),
2220                    MoveUD(-0.247047),
2221                    DelayOld2(10),
2222                    MoveUD(0.00393689),
2223                    DelayOld2(20),
2224                    MoveUD(0.239235),
2225                    DelayOld2(20),
2226                    MoveUD(0.458846),
2227                    DelayOld2(10),
2228                    MoveUD(0.70983),
2229                    DelayOld2(30),
2230                    MoveUD(1),
2231                    DelayOld2(10),
2232                    MoveLR(0.827448),
2233                    DelayOld2(10),
2234                    MoveLR(0.678426),
2235                    DelayOld2(20),
2236                    MoveLR(0.396069),
2237                    DelayOld2(10),
2238                    MoveLR(0.0980255),
2239                    DelayOld2(20),
2240                    MoveLR(-0.160802),
2241                    DelayOld2(20),
2242                    MoveLR(-0.388256),
2243                    DelayOld2(10),
2244                    MoveLR(-0.545122),
2245                    DelayOld2(30),
2246                    MoveLR(-0.73336),
2247                    DelayOld2(10),
2248                    MoveLR(-0.945128),
2249                    DelayOld2(10),
2250                    MoveLR(-1),
2251                    DelayOld2(50),
2252                    MoveUD(0.960814),
2253                    DelayOld2(20),
2254                    MoveUD(0.890225),
2255                    DelayOld2(10),
2256                    MoveUD(0.749046),
2257                    DelayOld2(20),
2258                    MoveUD(0.623554),
2259                    DelayOld2(20),
2260                    MoveUD(0.498062),
2261                    DelayOld2(10),
2262                    MoveUD(0.34904),
2263                    DelayOld2(20),
2264                    MoveUD(0.239235),
2265                    DelayOld2(20),
2266                    MoveUD(0.137272),
2267                    DelayOld2(10),
2268                    MoveUD(0.0117801),
2269                    DelayOld2(20),
2270                    MoveUD(-0.0117496),
2271                    DelayOld2(10),
2272                    MoveUD(-0.0274361),
2273                    DelayOld2(90),
2274                    MoveUD(-0.0352794),
2275                    Run(release=False),
2276                    Jump(release=False),
2277                    Delay(80),
2278                    Punch(release=False),
2279                    DelayOld2(60),
2280                    MoveLR(-0.968657),
2281                    DelayOld2(20),
2282                    MoveLR(-0.835322),
2283                    DelayOld2(10),
2284                    MoveLR(-0.70983),
2285                    JumpRelease(),
2286                    DelayOld2(30),
2287                    MoveLR(-0.592181),
2288                    MoveUD(-0.0588092),
2289                    DelayOld2(10),
2290                    MoveLR(-0.490219),
2291                    MoveUD(-0.0744957),
2292                    DelayOld2(10),
2293                    MoveLR(-0.41963),
2294                    DelayOld2(20),
2295                    MoveLR(0),
2296                    MoveUD(0),
2297                    DelayOld2(20),
2298                    MoveUD(0),
2299                    PunchRelease(),
2300                    RunRelease(),
2301                    DelayOld(500),
2302                    Move(0, -0.1),
2303                    DelayOld(100),
2304                    Move(0, 0),
2305                    DelayOld(2000),
2306                    AnalyticsScreen('Tutorial Section 7'),
2307                    Text(
2308                        bs.Lstr(resource=f'{self._r}.phrase21Text')
2309                    ),  # timing your bombs can be tricky
2310                    Move(-1, 0),
2311                    DelayOld(1000),
2312                    Move(0, -0.1),
2313                    DelayOld(100),
2314                    Move(0, 0),
2315                    SpawnSpaz(
2316                        0,
2317                        (-0.7, 4.3, -3.9),
2318                        make_current=True,
2319                        flash=False,
2320                        angle=-30,
2321                    ),
2322                    SpawnSpaz(
2323                        1,
2324                        (6.5, 0, -0.75),
2325                        relative_to=0,
2326                        make_current=False,
2327                        color=(0.3, 0.8, 1.0),
2328                        name=bs.Lstr(resource=f'{self._r}.randomName5Text'),
2329                    ),
2330                    DelayOld2(1000),
2331                    Move(-1, 0),
2332                    DelayOld2(1800),
2333                    Bomb(),
2334                    Move(0, 0),
2335                    DelayOld2(300),
2336                    Move(1, 0),
2337                    DelayOld2(600),
2338                    Jump(),
2339                    DelayOld2(150),
2340                    Punch(),
2341                    DelayOld2(800),
2342                    Move(-1, 0),
2343                    DelayOld2(1000),
2344                    Move(0, 0),
2345                    DelayOld2(1500),
2346                    Text(bs.Lstr(resource=f'{self._r}.phrase22Text')),  # dang
2347                    Delay(1500),
2348                    Text(''),
2349                    Delay(200),
2350                    Text(
2351                        bs.Lstr(resource=f'{self._r}.phrase23Text')
2352                    ),  # try cooking off
2353                    Delay(1500),
2354                    Bomb(),
2355                    Delay(800),
2356                    Move(1, 0.12),
2357                    Delay(1100),
2358                    Jump(),
2359                    Delay(100),
2360                    Punch(),
2361                    Delay(100),
2362                    Move(0, -0.1),
2363                    Delay(100),
2364                    Move(0, 0),
2365                    Delay(2000),
2366                    Text(
2367                        bs.Lstr(resource=f'{self._r}.phrase24Text')
2368                    ),  # hooray nicely cooked
2369                    Celebrate(),
2370                    DelayOld(2000),
2371                    KillSpaz(1),
2372                    Text(''),
2373                    Move(0.5, -0.5),
2374                    DelayOld(1000),
2375                    Move(0, -0.1),
2376                    DelayOld(100),
2377                    Move(0, 0),
2378                    DelayOld(1000),
2379                    AnalyticsScreen('Tutorial Section 8'),
2380                    Text(
2381                        bs.Lstr(resource=f'{self._r}.phrase25Text')
2382                    ),  # well that's just about it
2383                    DelayOld(2000),
2384                    Text(
2385                        bs.Lstr(resource=f'{self._r}.phrase26Text')
2386                    ),  # go get em tiger
2387                    DelayOld(2000),
2388                    Text(
2389                        bs.Lstr(resource=f'{self._r}.phrase27Text')
2390                    ),  # remember you training
2391                    DelayOld(3000),
2392                    Text(
2393                        bs.Lstr(resource=f'{self._r}.phrase28Text')
2394                    ),  # well maybe
2395                    DelayOld(1600),
2396                    Text(
2397                        bs.Lstr(resource=f'{self._r}.phrase29Text')
2398                    ),  # good luck
2399                    Celebrate('right', duration=10000),
2400                    DelayOld(1000),
2401                    AnalyticsScreen('Tutorial Complete'),
2402                    End(),
2403                ]
2404            )
2405
2406        except Exception:
2407            logging.exception('Error running tutorial.')
2408
2409        # If we read some, exec them.
2410        if self._entries:
2411            self._run_next_entry()
2412        # Otherwise try again in a few seconds.
2413        else:
2414            self._read_entries_timer = bs.Timer(
2415                3.0, bs.WeakCall(self._read_entries)
2416            )
2417
2418    def _run_next_entry(self) -> None:
2419        while self._entries:
2420            entry = self._entries.popleft()
2421            try:
2422                result = entry.run(self)
2423            except Exception:
2424                result = None
2425                logging.exception('Error in tutorial _run_next_entry.')
2426
2427            # If the entry returns an int value, set a timer;
2428            # otherwise just keep going.
2429            if result is not None:
2430                self._entry_timer = bs.Timer(
2431                    result / 1000.0, bs.WeakCall(self._run_next_entry)
2432                )
2433                return
2434
2435        # Done with these entries.. start over soon.
2436        self._read_entries_timer = bs.Timer(
2437            1.0, bs.WeakCall(self._read_entries)
2438        )
2439
2440    def _update_skip_votes(self) -> None:
2441        count = sum(1 for player in self.players if player.pressed)
2442        assert self._skip_count_text
2443        self._skip_count_text.text = (
2444            bs.Lstr(
2445                resource=f'{self._r}.skipVoteCountText',
2446                subs=[
2447                    ('${COUNT}', str(count)),
2448                    ('${TOTAL}', str(len(self.players))),
2449                ],
2450            )
2451            if count > 0
2452            else ''
2453        )
2454        if (
2455            count >= len(self.players)
2456            and self.players
2457            and not self._have_skipped
2458        ):
2459            bs.increment_analytics_count('Tutorial skip')
2460            bs.set_analytics_screen('Tutorial Skip')
2461            self._have_skipped = True
2462            bs.getsound('swish').play()
2463            # self._skip_count_text.text = self._r.skippingText
2464            self._skip_count_text.text = bs.Lstr(
2465                resource=f'{self._r}.skippingText'
2466            )
2467            assert self._skip_text
2468            self._skip_text.text = ''
2469            self.end()
2470
2471    def _player_pressed_button(self, player: Player) -> None:
2472        # Special case: if there's only one player, we give them a
2473        # warning on their first press (some players were thinking the
2474        # on-screen guide meant they were supposed to press something).
2475        if len(self.players) == 1 and not self._issued_warning:
2476            self._issued_warning = True
2477            assert self._skip_text
2478            self._skip_text.text = bs.Lstr(
2479                resource=f'{self._r}.skipConfirmText'
2480            )
2481            self._skip_text.color = (1, 1, 1)
2482            self._skip_text.scale = 1.3
2483            incr = 50
2484            t = incr
2485            for _i in range(6):
2486                bs.timer(
2487                    t / 1000.0,
2488                    bs.Call(setattr, self._skip_text, 'color', (1, 0.5, 0.1)),
2489                )
2490                t += incr
2491                bs.timer(
2492                    t / 1000.0,
2493                    bs.Call(setattr, self._skip_text, 'color', (1, 1, 0)),
2494                )
2495                t += incr
2496            bs.timer(6.0, bs.WeakCall(self._revert_confirm))
2497            return
2498
2499        player.pressed = True
2500
2501        # test...
2502        if not all(self.players):
2503            logging.error(
2504                'Nonexistent player in _player_pressed_button:'
2505                ' %s: we are %s',
2506                [str(p) for p in self.players],
2507                player,
2508            )
2509
2510        self._update_skip_votes()
2511
2512    def _revert_confirm(self) -> None:
2513        assert self._skip_text
2514        self._skip_text.text = bs.Lstr(
2515            resource=f'{self._r}.toSkipPressAnythingText'
2516        )
2517        self._skip_text.color = (1, 1, 1)
2518        self._issued_warning = False
2519
2520    @override
2521    def on_player_join(self, player: Player) -> None:
2522        super().on_player_join(player)
2523
2524        # We just wanna know if this player presses anything.
2525        player.assigninput(
2526            (
2527                bs.InputType.JUMP_PRESS,
2528                bs.InputType.PUNCH_PRESS,
2529                bs.InputType.BOMB_PRESS,
2530                bs.InputType.PICK_UP_PRESS,
2531            ),
2532            bs.Call(self._player_pressed_button, player),
2533        )
2534
2535    @override
2536    def on_player_leave(self, player: Player) -> None:
2537        if not all(self.players):
2538            logging.error(
2539                'Nonexistent player in on_player_leave: %s: we are %s',
2540                [str(p) for p in self.players],
2541                player,
2542            )
2543        super().on_player_leave(player)
2544        # our leaving may influence the vote total needed/etc
2545        self._update_skip_votes()
class ButtonPress:
 36class ButtonPress:
 37    def __init__(
 38        self,
 39        button: str,
 40        delay: int = 0,
 41        release: bool = True,
 42        release_delay: int = 0,
 43    ):
 44        self._button = button
 45        self._delay = delay
 46        self._release = release
 47        self._release_delay = release_delay
 48
 49    def run(self, a: TutorialActivity) -> None:
 50        s = a.current_spaz
 51        assert s is not None
 52        img: bs.Node | None
 53        release_call: Callable[[], None] | None
 54        color: Sequence[float] | None
 55        if self._button == 'punch':
 56            call = s.on_punch_press
 57            release_call = s.on_punch_release
 58            img = a.punch_image
 59            color = a.punch_image_color
 60        elif self._button == 'jump':
 61            call = s.on_jump_press
 62            release_call = s.on_jump_release
 63            img = a.jump_image
 64            color = a.jump_image_color
 65        elif self._button == 'bomb':
 66            call = s.on_bomb_press
 67            release_call = s.on_bomb_release
 68            img = a.bomb_image
 69            color = a.bomb_image_color
 70        elif self._button == 'pickUp':
 71            call = s.on_pickup_press
 72            release_call = s.on_pickup_release
 73            img = a.pickup_image
 74            color = a.pickup_image_color
 75        elif self._button == 'run':
 76            call = bs.Call(s.on_run, 1.0)
 77            release_call = bs.Call(s.on_run, 0.0)
 78            img = None
 79            color = None
 80        else:
 81            raise RuntimeError(f'invalid button: {self._button}')
 82
 83        brightness = 4.0
 84        if color is not None:
 85            c_bright = list(color)
 86            c_bright[0] *= brightness
 87            c_bright[1] *= brightness
 88            c_bright[2] *= brightness
 89        else:
 90            c_bright = [1.0, 1.0, 1.0]
 91
 92        if self._delay == 0:
 93            call()
 94            if img is not None:
 95                img.color = c_bright
 96                img.vr_depth = -40
 97        else:
 98            bs.timer(self._delay / 1000.0, call)
 99            if img is not None:
100                bs.timer(
101                    self._delay / 1000.0,
102                    bs.Call(_safesetattr, img, 'color', c_bright),
103                )
104                bs.timer(
105                    self._delay / 1000.0,
106                    bs.Call(_safesetattr, img, 'vr_depth', -30),
107                )
108        if self._release:
109            if self._delay == 0 and self._release_delay == 0:
110                release_call()
111            else:
112                bs.timer(
113                    0.001 * (self._delay + self._release_delay), release_call
114                )
115            if img is not None:
116                bs.timer(
117                    (self._delay + self._release_delay + 100) / 1000.0,
118                    bs.Call(_safesetattr, img, 'color', color),
119                )
120                bs.timer(
121                    (self._delay + self._release_delay + 100) / 1000.0,
122                    bs.Call(_safesetattr, img, 'vr_depth', -20),
123                )
ButtonPress( button: str, delay: int = 0, release: bool = True, release_delay: int = 0)
37    def __init__(
38        self,
39        button: str,
40        delay: int = 0,
41        release: bool = True,
42        release_delay: int = 0,
43    ):
44        self._button = button
45        self._delay = delay
46        self._release = release
47        self._release_delay = release_delay
def run(self, a: TutorialActivity) -> None:
 49    def run(self, a: TutorialActivity) -> None:
 50        s = a.current_spaz
 51        assert s is not None
 52        img: bs.Node | None
 53        release_call: Callable[[], None] | None
 54        color: Sequence[float] | None
 55        if self._button == 'punch':
 56            call = s.on_punch_press
 57            release_call = s.on_punch_release
 58            img = a.punch_image
 59            color = a.punch_image_color
 60        elif self._button == 'jump':
 61            call = s.on_jump_press
 62            release_call = s.on_jump_release
 63            img = a.jump_image
 64            color = a.jump_image_color
 65        elif self._button == 'bomb':
 66            call = s.on_bomb_press
 67            release_call = s.on_bomb_release
 68            img = a.bomb_image
 69            color = a.bomb_image_color
 70        elif self._button == 'pickUp':
 71            call = s.on_pickup_press
 72            release_call = s.on_pickup_release
 73            img = a.pickup_image
 74            color = a.pickup_image_color
 75        elif self._button == 'run':
 76            call = bs.Call(s.on_run, 1.0)
 77            release_call = bs.Call(s.on_run, 0.0)
 78            img = None
 79            color = None
 80        else:
 81            raise RuntimeError(f'invalid button: {self._button}')
 82
 83        brightness = 4.0
 84        if color is not None:
 85            c_bright = list(color)
 86            c_bright[0] *= brightness
 87            c_bright[1] *= brightness
 88            c_bright[2] *= brightness
 89        else:
 90            c_bright = [1.0, 1.0, 1.0]
 91
 92        if self._delay == 0:
 93            call()
 94            if img is not None:
 95                img.color = c_bright
 96                img.vr_depth = -40
 97        else:
 98            bs.timer(self._delay / 1000.0, call)
 99            if img is not None:
100                bs.timer(
101                    self._delay / 1000.0,
102                    bs.Call(_safesetattr, img, 'color', c_bright),
103                )
104                bs.timer(
105                    self._delay / 1000.0,
106                    bs.Call(_safesetattr, img, 'vr_depth', -30),
107                )
108        if self._release:
109            if self._delay == 0 and self._release_delay == 0:
110                release_call()
111            else:
112                bs.timer(
113                    0.001 * (self._delay + self._release_delay), release_call
114                )
115            if img is not None:
116                bs.timer(
117                    (self._delay + self._release_delay + 100) / 1000.0,
118                    bs.Call(_safesetattr, img, 'color', color),
119                )
120                bs.timer(
121                    (self._delay + self._release_delay + 100) / 1000.0,
122                    bs.Call(_safesetattr, img, 'vr_depth', -20),
123                )
class ButtonRelease:
126class ButtonRelease:
127    def __init__(self, button: str, delay: int = 0):
128        self._button = button
129        self._delay = delay
130
131    def run(self, a: TutorialActivity) -> None:
132        s = a.current_spaz
133        assert s is not None
134        call: Callable[[], None] | None
135        img: bs.Node | None
136        color: Sequence[float] | None
137        if self._button == 'punch':
138            call = s.on_punch_release
139            img = a.punch_image
140            color = a.punch_image_color
141        elif self._button == 'jump':
142            call = s.on_jump_release
143            img = a.jump_image
144            color = a.jump_image_color
145        elif self._button == 'bomb':
146            call = s.on_bomb_release
147            img = a.bomb_image
148            color = a.bomb_image_color
149        elif self._button == 'pickUp':
150            call = s.on_pickup_press
151            img = a.pickup_image
152            color = a.pickup_image_color
153        elif self._button == 'run':
154            call = bs.Call(s.on_run, 0.0)
155            img = None
156            color = None
157        else:
158            raise RuntimeError('invalid button: ' + self._button)
159        if self._delay == 0:
160            call()
161        else:
162            bs.timer(self._delay / 1000.0, call)
163        if img is not None:
164            bs.timer(
165                (self._delay + 100) / 1000.0,
166                bs.Call(_safesetattr, img, 'color', color),
167            )
168            bs.timer(
169                (self._delay + 100 / 1000.0),
170                bs.Call(_safesetattr, img, 'vr_depth', -20),
171            )
ButtonRelease(button: str, delay: int = 0)
127    def __init__(self, button: str, delay: int = 0):
128        self._button = button
129        self._delay = delay
def run(self, a: TutorialActivity) -> None:
131    def run(self, a: TutorialActivity) -> None:
132        s = a.current_spaz
133        assert s is not None
134        call: Callable[[], None] | None
135        img: bs.Node | None
136        color: Sequence[float] | None
137        if self._button == 'punch':
138            call = s.on_punch_release
139            img = a.punch_image
140            color = a.punch_image_color
141        elif self._button == 'jump':
142            call = s.on_jump_release
143            img = a.jump_image
144            color = a.jump_image_color
145        elif self._button == 'bomb':
146            call = s.on_bomb_release
147            img = a.bomb_image
148            color = a.bomb_image_color
149        elif self._button == 'pickUp':
150            call = s.on_pickup_press
151            img = a.pickup_image
152            color = a.pickup_image_color
153        elif self._button == 'run':
154            call = bs.Call(s.on_run, 0.0)
155            img = None
156            color = None
157        else:
158            raise RuntimeError('invalid button: ' + self._button)
159        if self._delay == 0:
160            call()
161        else:
162            bs.timer(self._delay / 1000.0, call)
163        if img is not None:
164            bs.timer(
165                (self._delay + 100) / 1000.0,
166                bs.Call(_safesetattr, img, 'color', color),
167            )
168            bs.timer(
169                (self._delay + 100 / 1000.0),
170                bs.Call(_safesetattr, img, 'vr_depth', -20),
171            )
class Player(bascenev1._player.Player[ForwardRef('Team')]):
174class Player(bs.Player['Team']):
175    """Our player type for this game."""
176
177    def __init__(self) -> None:
178        self.pressed = False

Our player type for this game.

pressed
class Team(bascenev1._team.Team[bascenev1lib.tutorial.Player]):
181class Team(bs.Team[Player]):
182    """Our team type for this game."""
183
184    def __init__(self) -> None:
185        pass

Our team type for this game.

class TutorialActivity(bascenev1._activity.Activity[bascenev1lib.tutorial.Player, bascenev1lib.tutorial.Team]):
 188class TutorialActivity(bs.Activity[Player, Team]):
 189    def __init__(self, settings: dict | None = None):
 190        from bascenev1lib.maps import Rampage
 191
 192        if settings is None:
 193            settings = {}
 194        super().__init__(settings)
 195        self.current_spaz: Spaz | None = None
 196        self._benchmark_type = getattr(bs.getsession(), 'benchmark_type', None)
 197        self.last_start_time: int | None = None
 198        self.cycle_times: list[int] = []
 199        self.allow_pausing = True
 200        self.allow_kick_idle_players = False
 201        self._issued_warning = False
 202        self._map_type = Rampage
 203        self._map_type.preload()
 204        self._jump_button_tex = bs.gettexture('buttonJump')
 205        self._pick_up_button_tex = bs.gettexture('buttonPickUp')
 206        self._bomb_button_tex = bs.gettexture('buttonBomb')
 207        self._punch_button_tex = bs.gettexture('buttonPunch')
 208        self._r = 'tutorial'
 209        self._have_skipped = False
 210        self.stick_image_position_x = self.stick_image_position_y = 0.0
 211        self.spawn_sound = bs.getsound('spawn')
 212        self.map: bs.Map | None = None
 213        self.text: bs.Node | None = None
 214        self._skip_text: bs.Node | None = None
 215        self._skip_count_text: bs.Node | None = None
 216        self._scale: float | None = None
 217        self._stick_base_position: tuple[float, float] = (0.0, 0.0)
 218        self._stick_nub_position: tuple[float, float] = (0.0, 0.0)
 219        self._stick_base_image_color: Sequence[float] = (1.0, 1.0, 1.0, 1.0)
 220        self._stick_nub_image_color: Sequence[float] = (1.0, 1.0, 1.0, 1.0)
 221        self._time: int = -1
 222        self.punch_image_color = (1.0, 1.0, 1.0)
 223        self.punch_image: bs.Node | None = None
 224        self.bomb_image: bs.Node | None = None
 225        self.jump_image: bs.Node | None = None
 226        self.pickup_image: bs.Node | None = None
 227        self._stick_base_image: bs.Node | None = None
 228        self._stick_nub_image: bs.Node | None = None
 229        self.bomb_image_color = (1.0, 1.0, 1.0)
 230        self.pickup_image_color = (1.0, 1.0, 1.0)
 231        self.control_ui_nodes: list[bs.Node] = []
 232        self.spazzes: dict[int, Spaz] = {}
 233        self.jump_image_color = (1.0, 1.0, 1.0)
 234        self._entries: deque[Any] = deque()
 235        self._read_entries_timer: bs.Timer | None = None
 236        self._entry_timer: bs.Timer | None = None
 237
 238    @override
 239    def on_transition_in(self) -> None:
 240        super().on_transition_in()
 241        bs.setmusic(bs.MusicType.CHAR_SELECT, continuous=True)
 242        self.map = self._map_type()
 243
 244    @override
 245    def on_begin(self) -> None:
 246        super().on_begin()
 247
 248        bs.set_analytics_screen('Tutorial Start')
 249        bs.increment_analytics_count('Tutorial start')
 250
 251        if bool(False):
 252            # Buttons on top.
 253            text_y = 140
 254            buttons_y = 250
 255        else:
 256            # Buttons on bottom.
 257            text_y = 260
 258            buttons_y = 160
 259
 260        # Need different versions of this: taps/buttons/keys.
 261        self.text = bs.newnode(
 262            'text',
 263            attrs={
 264                'text': '',
 265                'scale': 1.9,
 266                'position': (0, text_y),
 267                'maxwidth': 500,
 268                'flatness': 0.0,
 269                'shadow': 0.5,
 270                'h_align': 'center',
 271                'v_align': 'center',
 272                'v_attach': 'center',
 273            },
 274        )
 275
 276        # Need different versions of this: taps/buttons/keys.
 277        txt = (
 278            bs.Lstr(resource=f'{self._r}.cpuBenchmarkText')
 279            if self._benchmark_type == 'cpu'
 280            else bs.Lstr(resource=f'{self._r}.toSkipPressAnythingText')
 281        )
 282        t = self._skip_text = bs.newnode(
 283            'text',
 284            attrs={
 285                'text': txt,
 286                'maxwidth': 900,
 287                'scale': 1.1,
 288                'vr_depth': 100,
 289                'position': (0, 30),
 290                'h_align': 'center',
 291                'v_align': 'center',
 292                'v_attach': 'bottom',
 293            },
 294        )
 295        bs.animate(t, 'opacity', {1.0: 0.0, 2.0: 0.7})
 296        self._skip_count_text = bs.newnode(
 297            'text',
 298            attrs={
 299                'text': '',
 300                'scale': 1.4,
 301                'vr_depth': 90,
 302                'position': (0, 70),
 303                'h_align': 'center',
 304                'v_align': 'center',
 305                'v_attach': 'bottom',
 306            },
 307        )
 308
 309        ouya = False
 310
 311        self._scale = scale = 0.6
 312        center_offs = 130.0 * scale
 313        offs = 65.0 * scale
 314        position = (0, buttons_y)
 315        image_size = 90.0 * scale
 316        image_size_2 = 220.0 * scale
 317        nub_size = 110.0 * scale
 318        p = (position[0] + center_offs, position[1] - offs)
 319
 320        def _sc(r: float, g: float, b: float) -> tuple[float, float, float]:
 321            return 0.6 * r, 0.6 * g, 0.6 * b
 322
 323        self.jump_image_color = c = _sc(0.4, 1, 0.4)
 324        self.jump_image = bs.newnode(
 325            'image',
 326            attrs={
 327                'texture': self._jump_button_tex,
 328                'absolute_scale': True,
 329                'vr_depth': -20,
 330                'position': p,
 331                'scale': (image_size, image_size),
 332                'color': c,
 333            },
 334        )
 335        p = (position[0] + center_offs - offs, position[1])
 336        self.punch_image_color = c = (
 337            _sc(0.2, 0.6, 1) if ouya else _sc(1, 0.7, 0.3)
 338        )
 339        self.punch_image = bs.newnode(
 340            'image',
 341            attrs={
 342                'texture': bs.gettexture('buttonPunch'),
 343                'absolute_scale': True,
 344                'vr_depth': -20,
 345                'position': p,
 346                'scale': (image_size, image_size),
 347                'color': c,
 348            },
 349        )
 350        p = (position[0] + center_offs + offs, position[1])
 351        self.bomb_image_color = c = _sc(1, 0.3, 0.3)
 352        self.bomb_image = bs.newnode(
 353            'image',
 354            attrs={
 355                'texture': bs.gettexture('buttonBomb'),
 356                'absolute_scale': True,
 357                'vr_depth': -20,
 358                'position': p,
 359                'scale': (image_size, image_size),
 360                'color': c,
 361            },
 362        )
 363        p = (position[0] + center_offs, position[1] + offs)
 364        self.pickup_image_color = c = (
 365            _sc(1, 0.8, 0.3) if ouya else _sc(0.5, 0.5, 1)
 366        )
 367        self.pickup_image = bs.newnode(
 368            'image',
 369            attrs={
 370                'texture': bs.gettexture('buttonPickUp'),
 371                'absolute_scale': True,
 372                'vr_depth': -20,
 373                'position': p,
 374                'scale': (image_size, image_size),
 375                'color': c,
 376            },
 377        )
 378
 379        self._stick_base_position = p = (position[0] - center_offs, position[1])
 380        self._stick_base_image_color = c2 = (0.25, 0.25, 0.25, 1.0)
 381        self._stick_base_image = bs.newnode(
 382            'image',
 383            attrs={
 384                'texture': bs.gettexture('nub'),
 385                'absolute_scale': True,
 386                'vr_depth': -40,
 387                'position': p,
 388                'scale': (image_size_2, image_size_2),
 389                'color': c2,
 390            },
 391        )
 392        self._stick_nub_position = p = (position[0] - center_offs, position[1])
 393        self._stick_nub_image_color = c3 = (0.4, 0.4, 0.4, 1.0)
 394        self._stick_nub_image = bs.newnode(
 395            'image',
 396            attrs={
 397                'texture': bs.gettexture('nub'),
 398                'absolute_scale': True,
 399                'position': p,
 400                'scale': (nub_size, nub_size),
 401                'color': c3,
 402            },
 403        )
 404        self.control_ui_nodes = [
 405            self.jump_image,
 406            self.punch_image,
 407            self.bomb_image,
 408            self.pickup_image,
 409            self._stick_base_image,
 410            self._stick_nub_image,
 411        ]
 412        for n in self.control_ui_nodes:
 413            n.opacity = 0.0
 414        self._read_entries()
 415
 416    def set_stick_image_position(self, x: float, y: float) -> None:
 417        # Clamp this to a circle.
 418        len_squared = x * x + y * y
 419        if len_squared > 1.0:
 420            length = math.sqrt(len_squared)
 421            mult = 1.0 / length
 422            x *= mult
 423            y *= mult
 424
 425        self.stick_image_position_x = x
 426        self.stick_image_position_y = y
 427        offs = 50.0
 428        assert self._scale is not None
 429        p = [
 430            self._stick_nub_position[0] + x * offs * self._scale,
 431            self._stick_nub_position[1] + y * offs * self._scale,
 432        ]
 433        c = list(self._stick_nub_image_color)
 434        if abs(x) > 0.1 or abs(y) > 0.1:
 435            c[0] *= 2.0
 436            c[1] *= 4.0
 437            c[2] *= 2.0
 438        assert self._stick_nub_image is not None
 439        self._stick_nub_image.position = p
 440        self._stick_nub_image.color = c
 441        c = list(self._stick_base_image_color)
 442        if abs(x) > 0.1 or abs(y) > 0.1:
 443            c[0] *= 1.5
 444            c[1] *= 1.5
 445            c[2] *= 1.5
 446        assert self._stick_base_image is not None
 447        self._stick_base_image.color = c
 448
 449    def _read_entries(self) -> None:
 450        try:
 451
 452            class Reset:
 453                def __init__(self) -> None:
 454                    pass
 455
 456                def run(self, a: TutorialActivity) -> None:
 457                    # if we're looping, print out how long each cycle took
 458                    # print out how long each cycle took..
 459                    if a.last_start_time is not None:
 460                        tval = int(bs.apptime() * 1000.0) - a.last_start_time
 461                        assert isinstance(tval, int)
 462                        diff = tval
 463                        a.cycle_times.append(diff)
 464                        bs.broadcastmessage(
 465                            'cycle time: '
 466                            + str(diff)
 467                            + ' (average: '
 468                            + str(sum(a.cycle_times) / len(a.cycle_times))
 469                            + ')'
 470                        )
 471                    tval = int(bs.apptime() * 1000.0)
 472                    assert isinstance(tval, int)
 473                    a.last_start_time = tval
 474
 475                    assert a.text
 476                    a.text.text = ''
 477                    for spaz in list(a.spazzes.values()):
 478                        spaz.handlemessage(bs.DieMessage(immediate=True))
 479                    a.spazzes = {}
 480                    a.current_spaz = None
 481                    for n in a.control_ui_nodes:
 482                        n.opacity = 0.0
 483                    a.set_stick_image_position(0, 0)
 484
 485            # Can be used for debugging.
 486            class SetSpeed:
 487                def __init__(self, speed: int):
 488                    self._speed = speed
 489
 490                def run(self, a: TutorialActivity) -> None:
 491                    print('setting to', self._speed)
 492                    bs.set_debug_speed_exponent(self._speed)
 493
 494            class RemoveGloves:
 495                def __init__(self) -> None:
 496                    pass
 497
 498                def run(self, a: TutorialActivity) -> None:
 499                    # pylint: disable=protected-access
 500                    assert a.current_spaz is not None
 501                    # noinspection PyProtectedMember
 502                    a.current_spaz._gloves_wear_off()
 503
 504            class KillSpaz:
 505                def __init__(self, num: int, explode: bool = False):
 506                    self._num = num
 507                    self._explode = explode
 508
 509                def run(self, a: TutorialActivity) -> None:
 510                    if self._explode:
 511                        a.spazzes[self._num].shatter()
 512                    del a.spazzes[self._num]
 513
 514            class SpawnSpaz:
 515                def __init__(
 516                    self,
 517                    num: int,
 518                    position: Sequence[float],
 519                    *,
 520                    color: Sequence[float] = (1.0, 1.0, 1.0),
 521                    make_current: bool = False,
 522                    relative_to: int | None = None,
 523                    name: str | bs.Lstr = '',
 524                    flash: bool = True,
 525                    angle: float = 0.0,
 526                ):
 527                    self._num = num
 528                    self._position = position
 529                    self._make_current = make_current
 530                    self._color = color
 531                    self._relative_to = relative_to
 532                    self._name = name
 533                    self._flash = flash
 534                    self._angle = angle
 535
 536                def run(self, a: TutorialActivity) -> None:
 537                    # if they gave a 'relative to' spaz, position is relative
 538                    # to them
 539                    pos: Sequence[float]
 540                    if self._relative_to is not None:
 541                        snode = a.spazzes[self._relative_to].node
 542                        assert snode
 543                        their_pos = snode.position
 544                        pos = (
 545                            their_pos[0] + self._position[0],
 546                            their_pos[1] + self._position[1],
 547                            their_pos[2] + self._position[2],
 548                        )
 549                    else:
 550                        pos = self._position
 551
 552                    # if there's already a spaz at this spot, insta-kill it
 553                    if self._num in a.spazzes:
 554                        a.spazzes[self._num].handlemessage(
 555                            bs.DieMessage(immediate=True)
 556                        )
 557
 558                    s = a.spazzes[self._num] = Spaz(
 559                        color=self._color,
 560                        start_invincible=self._flash,
 561                        demo_mode=True,
 562                    )
 563
 564                    # FIXME: Should extend spaz to support Lstr names.
 565                    assert s.node
 566                    if isinstance(self._name, bs.Lstr):
 567                        s.node.name = self._name.evaluate()
 568                    else:
 569                        s.node.name = self._name
 570                    s.node.name_color = self._color
 571                    s.handlemessage(bs.StandMessage(pos, self._angle))
 572                    if self._make_current:
 573                        a.current_spaz = s
 574                    if self._flash:
 575                        a.spawn_sound.play(position=pos)
 576
 577            class Powerup:
 578                def __init__(
 579                    self,
 580                    num: int,
 581                    position: Sequence[float],
 582                    *,
 583                    color: Sequence[float] = (1.0, 1.0, 1.0),
 584                    make_current: bool = False,
 585                    relative_to: int | None = None,
 586                ):
 587                    self._position = position
 588                    self._relative_to = relative_to
 589
 590                def run(self, a: TutorialActivity) -> None:
 591                    # If they gave a 'relative to' spaz, position is relative
 592                    # to them.
 593                    pos: Sequence[float]
 594                    if self._relative_to is not None:
 595                        snode = a.spazzes[self._relative_to].node
 596                        assert snode
 597                        their_pos = snode.position
 598                        pos = (
 599                            their_pos[0] + self._position[0],
 600                            their_pos[1] + self._position[1],
 601                            their_pos[2] + self._position[2],
 602                        )
 603                    else:
 604                        pos = self._position
 605                    from bascenev1lib.actor import powerupbox
 606
 607                    powerupbox.PowerupBox(
 608                        position=pos, poweruptype='punch'
 609                    ).autoretain()
 610
 611            class Delay:
 612                def __init__(self, time: int) -> None:
 613                    self._time = time
 614
 615                def run(self, a: TutorialActivity) -> int:
 616                    return self._time
 617
 618            class AnalyticsScreen:
 619                def __init__(self, screen: str) -> None:
 620                    self._screen = screen
 621
 622                def run(self, a: TutorialActivity) -> None:
 623                    bs.set_analytics_screen(self._screen)
 624
 625            class DelayOld:
 626                def __init__(self, time: int) -> None:
 627                    self._time = time
 628
 629                def run(self, a: TutorialActivity) -> int:
 630                    return int(0.9 * self._time)
 631
 632            class DelayOld2:
 633                def __init__(self, time: int) -> None:
 634                    self._time = time
 635
 636                def run(self, a: TutorialActivity) -> int:
 637                    return int(0.8 * self._time)
 638
 639            class End:
 640                def __init__(self) -> None:
 641                    pass
 642
 643                def run(self, a: TutorialActivity) -> None:
 644                    bs.increment_analytics_count('Tutorial finish')
 645                    a.end()
 646
 647            class Move:
 648                def __init__(self, x: float, y: float):
 649                    self._x = float(x)
 650                    self._y = float(y)
 651
 652                def run(self, a: TutorialActivity) -> None:
 653                    s = a.current_spaz
 654                    assert s
 655                    # FIXME: Game should take floats for this.
 656                    x_clamped = self._x
 657                    y_clamped = self._y
 658                    s.on_move_left_right(x_clamped)
 659                    s.on_move_up_down(y_clamped)
 660                    a.set_stick_image_position(self._x, self._y)
 661
 662            class MoveLR:
 663                def __init__(self, x: float):
 664                    self._x = float(x)
 665
 666                def run(self, a: TutorialActivity) -> None:
 667                    s = a.current_spaz
 668                    assert s
 669                    # FIXME: Game should take floats for this.
 670                    x_clamped = self._x
 671                    s.on_move_left_right(x_clamped)
 672                    a.set_stick_image_position(
 673                        self._x, a.stick_image_position_y
 674                    )
 675
 676            class MoveUD:
 677                def __init__(self, y: float):
 678                    self._y = float(y)
 679
 680                def run(self, a: TutorialActivity) -> None:
 681                    s = a.current_spaz
 682                    assert s
 683                    # FIXME: Game should take floats for this.
 684                    y_clamped = self._y
 685                    s.on_move_up_down(y_clamped)
 686                    a.set_stick_image_position(
 687                        a.stick_image_position_x, self._y
 688                    )
 689
 690            class Bomb(ButtonPress):
 691                def __init__(
 692                    self,
 693                    delay: int = 0,
 694                    release: bool = True,
 695                    release_delay: int = 500,
 696                ):
 697                    ButtonPress.__init__(
 698                        self,
 699                        'bomb',
 700                        delay=delay,
 701                        release=release,
 702                        release_delay=release_delay,
 703                    )
 704
 705            class Jump(ButtonPress):
 706                def __init__(
 707                    self,
 708                    delay: int = 0,
 709                    release: bool = True,
 710                    release_delay: int = 500,
 711                ):
 712                    ButtonPress.__init__(
 713                        self,
 714                        'jump',
 715                        delay=delay,
 716                        release=release,
 717                        release_delay=release_delay,
 718                    )
 719
 720            class Punch(ButtonPress):
 721                def __init__(
 722                    self,
 723                    delay: int = 0,
 724                    release: bool = True,
 725                    release_delay: int = 500,
 726                ):
 727                    ButtonPress.__init__(
 728                        self,
 729                        'punch',
 730                        delay=delay,
 731                        release=release,
 732                        release_delay=release_delay,
 733                    )
 734
 735            class PickUp(ButtonPress):
 736                def __init__(
 737                    self,
 738                    delay: int = 0,
 739                    release: bool = True,
 740                    release_delay: int = 500,
 741                ):
 742                    ButtonPress.__init__(
 743                        self,
 744                        'pickUp',
 745                        delay=delay,
 746                        release=release,
 747                        release_delay=release_delay,
 748                    )
 749
 750            class Run(ButtonPress):
 751                def __init__(
 752                    self,
 753                    delay: int = 0,
 754                    release: bool = True,
 755                    release_delay: int = 500,
 756                ):
 757                    ButtonPress.__init__(
 758                        self,
 759                        'run',
 760                        delay=delay,
 761                        release=release,
 762                        release_delay=release_delay,
 763                    )
 764
 765            class BombRelease(ButtonRelease):
 766                def __init__(self, delay: int = 0):
 767                    super().__init__('bomb', delay=delay)
 768
 769            class JumpRelease(ButtonRelease):
 770                def __init__(self, delay: int = 0):
 771                    super().__init__('jump', delay=delay)
 772
 773            class PunchRelease(ButtonRelease):
 774                def __init__(self, delay: int = 0):
 775                    super().__init__('punch', delay=delay)
 776
 777            class PickUpRelease(ButtonRelease):
 778                def __init__(self, delay: int = 0):
 779                    super().__init__('pickUp', delay=delay)
 780
 781            class RunRelease(ButtonRelease):
 782                def __init__(self, delay: int = 0):
 783                    super().__init__('run', delay=delay)
 784
 785            class ShowControls:
 786                def __init__(self) -> None:
 787                    pass
 788
 789                def run(self, a: TutorialActivity) -> None:
 790                    for n in a.control_ui_nodes:
 791                        bs.animate(n, 'opacity', {0.0: 0.0, 1.0: 1.0})
 792
 793            class Text:
 794                def __init__(self, text: str | bs.Lstr):
 795                    self.text = text
 796
 797                def run(self, a: TutorialActivity) -> None:
 798                    assert a.text
 799                    a.text.text = self.text
 800
 801            class PrintPos:
 802                def __init__(self, spaz_num: int | None = None):
 803                    self._spaz_num = spaz_num
 804
 805                def run(self, a: TutorialActivity) -> None:
 806                    if self._spaz_num is None:
 807                        s = a.current_spaz
 808                    else:
 809                        s = a.spazzes[self._spaz_num]
 810                    assert s and s.node
 811                    t = list(s.node.position)
 812                    print('RestorePos(' + str((t[0], t[1] - 1.0, t[2])) + '),')
 813
 814            class RestorePos:
 815                def __init__(self, pos: Sequence[float]) -> None:
 816                    self._pos = pos
 817
 818                def run(self, a: TutorialActivity) -> None:
 819                    s = a.current_spaz
 820                    assert s
 821                    s.handlemessage(bs.StandMessage(self._pos, 0))
 822
 823            class Celebrate:
 824                def __init__(
 825                    self,
 826                    celebrate_type: str = 'both',
 827                    spaz_num: int | None = None,
 828                    duration: int = 1000,
 829                ):
 830                    self._spaz_num = spaz_num
 831                    self._celebrate_type = celebrate_type
 832                    self._duration = duration
 833
 834                def run(self, a: TutorialActivity) -> None:
 835                    if self._spaz_num is None:
 836                        s = a.current_spaz
 837                    else:
 838                        s = a.spazzes[self._spaz_num]
 839                    assert s and s.node
 840                    if self._celebrate_type == 'right':
 841                        s.node.handlemessage('celebrate_r', self._duration)
 842                    elif self._celebrate_type == 'left':
 843                        s.node.handlemessage('celebrate_l', self._duration)
 844                    elif self._celebrate_type == 'both':
 845                        s.node.handlemessage('celebrate', self._duration)
 846                    else:
 847                        raise RuntimeError(
 848                            'invalid celebrate type ' + self._celebrate_type
 849                        )
 850
 851            self._entries = deque(
 852                [
 853                    Reset(),
 854                    SpawnSpaz(0, (0, 5.5, -3.0), make_current=True),
 855                    DelayOld(1000),
 856                    AnalyticsScreen('Tutorial Section 1'),
 857                    Text(
 858                        bs.Lstr(resource=f'{self._r}.phrase01Text')
 859                    ),  # hi there
 860                    Celebrate('left'),
 861                    DelayOld(2000),
 862                    Text(
 863                        bs.Lstr(
 864                            resource=f'{self._r}.phrase02Text',
 865                            subs=[
 866                                ('${APP_NAME}', bs.Lstr(resource='titleText'))
 867                            ],
 868                        )
 869                    ),  # welcome to <appname>
 870                    DelayOld(80),
 871                    Run(release=False),
 872                    Jump(release=False),
 873                    MoveLR(1),
 874                    MoveUD(0),
 875                    DelayOld(70),
 876                    RunRelease(),
 877                    JumpRelease(),
 878                    DelayOld(60),
 879                    MoveUD(1),
 880                    DelayOld(30),
 881                    MoveLR(0),
 882                    DelayOld(90),
 883                    MoveLR(-1),
 884                    DelayOld(20),
 885                    MoveUD(0),
 886                    DelayOld(70),
 887                    MoveUD(-1),
 888                    DelayOld(20),
 889                    MoveLR(0),
 890                    DelayOld(80),
 891                    MoveUD(0),
 892                    DelayOld(1500),
 893                    Text(
 894                        bs.Lstr(resource=f'{self._r}.phrase03Text')
 895                    ),  # here's a few tips
 896                    DelayOld(1000),
 897                    ShowControls(),
 898                    DelayOld(1000),
 899                    Jump(),
 900                    DelayOld(1000),
 901                    Jump(),
 902                    DelayOld(1000),
 903                    AnalyticsScreen('Tutorial Section 2'),
 904                    Text(
 905                        bs.Lstr(
 906                            resource=f'{self._r}.phrase04Text',
 907                            subs=[
 908                                ('${APP_NAME}', bs.Lstr(resource='titleText'))
 909                            ],
 910                        )
 911                    ),  # many things are based on physics
 912                    DelayOld(20),
 913                    MoveUD(0),
 914                    DelayOld(60),
 915                    MoveLR(0),
 916                    DelayOld(10),
 917                    MoveLR(0),
 918                    MoveUD(0),
 919                    DelayOld(10),
 920                    MoveLR(0),
 921                    MoveUD(0),
 922                    DelayOld(20),
 923                    MoveUD(-0.0575579),
 924                    DelayOld(10),
 925                    MoveUD(-0.207831),
 926                    DelayOld(30),
 927                    MoveUD(-0.309793),
 928                    DelayOld(10),
 929                    MoveUD(-0.474502),
 930                    DelayOld(10),
 931                    MoveLR(0.00390637),
 932                    MoveUD(-0.647053),
 933                    DelayOld(20),
 934                    MoveLR(-0.0745262),
 935                    MoveUD(-0.819605),
 936                    DelayOld(10),
 937                    MoveLR(-0.168645),
 938                    MoveUD(-0.937254),
 939                    DelayOld(30),
 940                    MoveLR(-0.294137),
 941                    MoveUD(-1),
 942                    DelayOld(10),
 943                    MoveLR(-0.411786),
 944                    DelayOld(10),
 945                    MoveLR(-0.639241),
 946                    DelayOld(30),
 947                    MoveLR(-0.75689),
 948                    DelayOld(10),
 949                    MoveLR(-0.905911),
 950                    DelayOld(20),
 951                    MoveLR(-1),
 952                    DelayOld(50),
 953                    MoveUD(-0.960784),
 954                    DelayOld(20),
 955                    MoveUD(-0.819605),
 956                    MoveUD(-0.61568),
 957                    DelayOld(20),
 958                    MoveUD(-0.427442),
 959                    DelayOld(20),
 960                    MoveUD(-0.231361),
 961                    DelayOld(10),
 962                    MoveUD(-0.00390637),
 963                    DelayOld(30),
 964                    MoveUD(0.333354),
 965                    MoveUD(0.584338),
 966                    DelayOld(20),
 967                    MoveUD(0.764733),
 968                    DelayOld(30),
 969                    MoveLR(-0.803949),
 970                    MoveUD(0.913755),
 971                    DelayOld(10),
 972                    MoveLR(-0.647084),
 973                    MoveUD(0.992187),
 974                    DelayOld(20),
 975                    MoveLR(-0.435316),
 976                    MoveUD(1),
 977                    DelayOld(20),
 978                    MoveLR(-0.168645),
 979                    MoveUD(0.976501),
 980                    MoveLR(0.0744957),
 981                    MoveUD(0.905911),
 982                    DelayOld(20),
 983                    MoveLR(0.270577),
 984                    MoveUD(0.843165),
 985                    DelayOld(20),
 986                    MoveLR(0.435286),
 987                    MoveUD(0.780419),
 988                    DelayOld(10),
 989                    MoveLR(0.66274),
 990                    MoveUD(0.647084),
 991                    DelayOld(30),
 992                    MoveLR(0.803919),
 993                    MoveUD(0.458846),
 994                    MoveLR(0.929411),
 995                    MoveUD(0.223548),
 996                    DelayOld(20),
 997                    MoveLR(0.95294),
 998                    MoveUD(0.137272),
 999                    DelayOld(20),
1000                    MoveLR(1),
1001                    MoveUD(-0.0509659),
1002                    DelayOld(20),
1003                    MoveUD(-0.247047),
1004                    DelayOld(20),
1005                    MoveUD(-0.443129),
1006                    DelayOld(20),
1007                    MoveUD(-0.694113),
1008                    MoveUD(-0.921567),
1009                    DelayOld(30),
1010                    MoveLR(0.858821),
1011                    MoveUD(-1),
1012                    DelayOld(10),
1013                    MoveLR(0.68627),
1014                    DelayOld(10),
1015                    MoveLR(0.364696),
1016                    DelayOld(20),
1017                    MoveLR(0.0509659),
1018                    DelayOld(20),
1019                    MoveLR(-0.223548),
1020                    DelayOld(10),
1021                    MoveLR(-0.600024),
1022                    MoveUD(-0.913724),
1023                    DelayOld(30),
1024                    MoveLR(-0.858852),
1025                    MoveUD(-0.717643),
1026                    MoveLR(-1),
1027                    MoveUD(-0.474502),
1028                    DelayOld(20),
1029                    MoveUD(-0.396069),
1030                    DelayOld(20),
1031                    MoveUD(-0.286264),
1032                    DelayOld(20),
1033                    MoveUD(-0.137242),
1034                    DelayOld(20),
1035                    MoveUD(0.0353099),
1036                    DelayOld(10),
1037                    MoveUD(0.32551),
1038                    DelayOld(20),
1039                    MoveUD(0.592181),
1040                    DelayOld(10),
1041                    MoveUD(0.851009),
1042                    DelayOld(10),
1043                    MoveUD(1),
1044                    DelayOld(30),
1045                    MoveLR(-0.764733),
1046                    DelayOld(20),
1047                    MoveLR(-0.403943),
1048                    MoveLR(-0.145116),
1049                    DelayOld(30),
1050                    MoveLR(0.0901822),
1051                    MoveLR(0.32548),
1052                    DelayOld(30),
1053                    MoveLR(0.560778),
1054                    MoveUD(0.929441),
1055                    DelayOld(20),
1056                    MoveLR(0.709799),
1057                    MoveUD(0.73336),
1058                    MoveLR(0.803919),
1059                    MoveUD(0.545122),
1060                    DelayOld(20),
1061                    MoveLR(0.882351),
1062                    MoveUD(0.356883),
1063                    DelayOld(10),
1064                    MoveLR(0.968627),
1065                    MoveUD(0.113742),
1066                    DelayOld(20),
1067                    MoveLR(0.992157),
1068                    MoveUD(-0.0823389),
1069                    DelayOld(30),
1070                    MoveUD(-0.309793),
1071                    DelayOld(10),
1072                    MoveUD(-0.545091),
1073                    DelayOld(20),
1074                    MoveLR(0.882351),
1075                    MoveUD(-0.874508),
1076                    DelayOld(20),
1077                    MoveLR(0.756859),
1078                    MoveUD(-1),
1079                    DelayOld(10),
1080                    MoveLR(0.576464),
1081                    DelayOld(20),
1082                    MoveLR(0.254891),
1083                    DelayOld(10),
1084                    MoveLR(-0.0274667),
1085                    DelayOld(10),
1086                    MoveLR(-0.356883),
1087                    DelayOld(30),
1088                    MoveLR(-0.592181),
1089                    MoveLR(-0.827479),
1090                    MoveUD(-0.921567),
1091                    DelayOld(20),
1092                    MoveLR(-1),
1093                    MoveUD(-0.749016),
1094                    DelayOld(20),
1095                    MoveUD(-0.61568),
1096                    DelayOld(10),
1097                    MoveUD(-0.403912),
1098                    DelayOld(20),
1099                    MoveUD(-0.207831),
1100                    DelayOld(10),
1101                    MoveUD(0.121586),
1102                    DelayOld(30),
1103                    MoveUD(0.34904),
1104                    DelayOld(10),
1105                    MoveUD(0.560808),
1106                    DelayOld(10),
1107                    MoveUD(0.827479),
1108                    DelayOld(30),
1109                    MoveUD(1),
1110                    DelayOld(20),
1111                    MoveLR(-0.976501),
1112                    MoveLR(-0.670614),
1113                    DelayOld(20),
1114                    MoveLR(-0.239235),
1115                    DelayOld(20),
1116                    MoveLR(0.160772),
1117                    DelayOld(20),
1118                    MoveLR(0.443129),
1119                    DelayOld(10),
1120                    MoveLR(0.68627),
1121                    MoveUD(0.976501),
1122                    DelayOld(30),
1123                    MoveLR(0.929411),
1124                    MoveUD(0.73336),
1125                    MoveLR(1),
1126                    MoveUD(0.482376),
1127                    DelayOld(20),
1128                    MoveUD(0.34904),
1129                    DelayOld(10),
1130                    MoveUD(0.160802),
1131                    DelayOld(30),
1132                    MoveUD(-0.0744957),
1133                    DelayOld(10),
1134                    MoveUD(-0.333323),
1135                    DelayOld(20),
1136                    MoveUD(-0.647053),
1137                    DelayOld(20),
1138                    MoveUD(-0.937254),
1139                    DelayOld(10),
1140                    MoveLR(0.858821),
1141                    MoveUD(-1),
1142                    DelayOld(10),
1143                    MoveLR(0.576464),
1144                    DelayOld(30),
1145                    MoveLR(0.184301),
1146                    DelayOld(10),
1147                    MoveLR(-0.121586),
1148                    DelayOld(10),
1149                    MoveLR(-0.474532),
1150                    DelayOld(30),
1151                    MoveLR(-0.670614),
1152                    MoveLR(-0.851009),
1153                    DelayOld(30),
1154                    MoveLR(-1),
1155                    MoveUD(-0.968627),
1156                    DelayOld(20),
1157                    MoveUD(-0.843135),
1158                    DelayOld(10),
1159                    MoveUD(-0.631367),
1160                    DelayOld(20),
1161                    MoveUD(-0.403912),
1162                    MoveUD(-0.176458),
1163                    DelayOld(20),
1164                    MoveUD(0.0902127),
1165                    DelayOld(20),
1166                    MoveUD(0.380413),
1167                    DelayOld(10),
1168                    MoveUD(0.717673),
1169                    DelayOld(30),
1170                    MoveUD(1),
1171                    DelayOld(10),
1172                    MoveLR(-0.741203),
1173                    DelayOld(20),
1174                    MoveLR(-0.458846),
1175                    DelayOld(10),
1176                    MoveLR(-0.145116),
1177                    DelayOld(10),
1178                    MoveLR(0.0980255),
1179                    DelayOld(20),
1180                    MoveLR(0.294107),
1181                    DelayOld(30),
1182                    MoveLR(0.466659),
1183                    MoveLR(0.717643),
1184                    MoveUD(0.796106),
1185                    DelayOld(20),
1186                    MoveLR(0.921567),
1187                    MoveUD(0.443159),
1188                    DelayOld(20),
1189                    MoveLR(1),
1190                    MoveUD(0.145116),
1191                    DelayOld(10),
1192                    MoveUD(-0.0274361),
1193                    DelayOld(30),
1194                    MoveUD(-0.223518),
1195                    MoveUD(-0.427442),
1196                    DelayOld(20),
1197                    MoveUD(-0.874508),
1198                    DelayOld(20),
1199                    MoveUD(-1),
1200                    DelayOld(10),
1201                    MoveLR(0.929411),
1202                    DelayOld(20),
1203                    MoveLR(0.68627),
1204                    DelayOld(20),
1205                    MoveLR(0.364696),
1206                    DelayOld(20),
1207                    MoveLR(0.0431227),
1208                    DelayOld(10),
1209                    MoveLR(-0.333354),
1210                    DelayOld(20),
1211                    MoveLR(-0.639241),
1212                    DelayOld(20),
1213                    MoveLR(-0.968657),
1214                    MoveUD(-0.968627),
1215                    DelayOld(20),
1216                    MoveLR(-1),
1217                    MoveUD(-0.890194),
1218                    MoveUD(-0.866665),
1219                    DelayOld(20),
1220                    MoveUD(-0.749016),
1221                    DelayOld(20),
1222                    MoveUD(-0.529405),
1223                    DelayOld(20),
1224                    MoveUD(-0.30195),
1225                    DelayOld(10),
1226                    MoveUD(-0.00390637),
1227                    DelayOld(10),
1228                    MoveUD(0.262764),
1229                    DelayOld(30),
1230                    MoveLR(-0.600024),
1231                    MoveUD(0.458846),
1232                    DelayOld(10),
1233                    MoveLR(-0.294137),
1234                    MoveUD(0.482376),
1235                    DelayOld(20),
1236                    MoveLR(-0.200018),
1237                    MoveUD(0.505905),
1238                    DelayOld(10),
1239                    MoveLR(-0.145116),
1240                    MoveUD(0.545122),
1241                    DelayOld(20),
1242                    MoveLR(-0.0353099),
1243                    MoveUD(0.584338),
1244                    DelayOld(20),
1245                    MoveLR(0.137242),
1246                    MoveUD(0.592181),
1247                    DelayOld(20),
1248                    MoveLR(0.30195),
1249                    DelayOld(10),
1250                    MoveLR(0.490188),
1251                    DelayOld(10),
1252                    MoveLR(0.599994),
1253                    MoveUD(0.529435),
1254                    DelayOld(30),
1255                    MoveLR(0.66274),
1256                    MoveUD(0.3961),
1257                    DelayOld(20),
1258                    MoveLR(0.670583),
1259                    MoveUD(0.231391),
1260                    MoveLR(0.68627),
1261                    MoveUD(0.0745262),
1262                    Move(0, -0.01),
1263                    DelayOld(100),
1264                    Move(0, 0),
1265                    DelayOld(1000),
1266                    Text(
1267                        bs.Lstr(resource=f'{self._r}.phrase05Text')
1268                    ),  # for example when you punch..
1269                    DelayOld(510),
1270                    Move(0, -0.01),
1271                    DelayOld(100),
1272                    Move(0, 0),
1273                    DelayOld(500),
1274                    SpawnSpaz(
1275                        0,
1276                        (-0.09249162673950195, 4.337906360626221, -2.3),
1277                        make_current=True,
1278                        flash=False,
1279                    ),
1280                    SpawnSpaz(
1281                        1,
1282                        (-3.1, 4.3, -2.0),
1283                        make_current=False,
1284                        color=(1, 1, 0.4),
1285                        name=bs.Lstr(resource=f'{self._r}.randomName1Text'),
1286                    ),
1287                    Move(-1.0, 0),
1288                    DelayOld(1050),
1289                    Move(0, -0.01),
1290                    DelayOld(100),
1291                    Move(0, 0),
1292                    DelayOld(1000),
1293                    Text(
1294                        bs.Lstr(resource=f'{self._r}.phrase06Text')
1295                    ),  # your damage is based
1296                    DelayOld(1200),
1297                    Move(-0.05, 0),
1298                    DelayOld(200),
1299                    Punch(),
1300                    DelayOld(800),
1301                    Punch(),
1302                    DelayOld(800),
1303                    Punch(),
1304                    DelayOld(800),
1305                    Move(0, -0.01),
1306                    DelayOld(100),
1307                    Move(0, 0),
1308                    Text(
1309                        bs.Lstr(
1310                            resource=f'{self._r}.phrase07Text',
1311                            subs=[
1312                                (
1313                                    '${NAME}',
1314                                    bs.Lstr(
1315                                        resource=f'{self._r}.randomName1Text'
1316                                    ),
1317                                )
1318                            ],
1319                        )
1320                    ),  # see that didn't hurt fred
1321                    DelayOld(2000),
1322                    Celebrate('right', spaz_num=1),
1323                    DelayOld(1400),
1324                    Text(
1325                        bs.Lstr(resource=f'{self._r}.phrase08Text')
1326                    ),  # lets jump and spin to get more speed
1327                    DelayOld(30),
1328                    MoveLR(0),
1329                    DelayOld(40),
1330                    MoveLR(0),
1331                    DelayOld(40),
1332                    MoveLR(0),
1333                    DelayOld(130),
1334                    MoveLR(0),
1335                    DelayOld(100),
1336                    MoveLR(0),
1337                    DelayOld(10),
1338                    MoveLR(0.0480667),
1339                    DelayOld(40),
1340                    MoveLR(0.056093),
1341                    MoveLR(0.0681173),
1342                    DelayOld(30),
1343                    MoveLR(0.0801416),
1344                    DelayOld(10),
1345                    MoveLR(0.184301),
1346                    DelayOld(10),
1347                    MoveLR(0.207831),
1348                    DelayOld(20),
1349                    MoveLR(0.231361),
1350                    DelayOld(30),
1351                    MoveLR(0.239204),
1352                    DelayOld(30),
1353                    MoveLR(0.254891),
1354                    DelayOld(40),
1355                    MoveLR(0.270577),
1356                    DelayOld(10),
1357                    MoveLR(0.30195),
1358                    DelayOld(20),
1359                    MoveLR(0.341166),
1360                    DelayOld(30),
1361                    MoveLR(0.388226),
1362                    MoveLR(0.435286),
1363                    DelayOld(30),
1364                    MoveLR(0.490188),
1365                    DelayOld(10),
1366                    MoveLR(0.560778),
1367                    DelayOld(20),
1368                    MoveLR(0.599994),
1369                    DelayOld(10),
1370                    MoveLR(0.647053),
1371                    DelayOld(10),
1372                    MoveLR(0.68627),
1373                    DelayOld(30),
1374                    MoveLR(0.733329),
1375                    DelayOld(20),
1376                    MoveLR(0.764702),
1377                    DelayOld(10),
1378                    MoveLR(0.827448),
1379                    DelayOld(20),
1380                    MoveLR(0.874508),
1381                    DelayOld(20),
1382                    MoveLR(0.929411),
1383                    DelayOld(10),
1384                    MoveLR(1),
1385                    DelayOld(830),
1386                    MoveUD(0.0274667),
1387                    DelayOld(10),
1388                    MoveLR(0.95294),
1389                    MoveUD(0.113742),
1390                    DelayOld(30),
1391                    MoveLR(0.780389),
1392                    MoveUD(0.184332),
1393                    DelayOld(10),
1394                    MoveLR(0.27842),
1395                    MoveUD(0.0745262),
1396                    DelayOld(20),
1397                    MoveLR(0),
1398                    MoveUD(0),
1399                    DelayOld(390),
1400                    MoveLR(0),
1401                    MoveLR(0),
1402                    DelayOld(20),
1403                    MoveLR(0),
1404                    DelayOld(20),
1405                    MoveLR(0),
1406                    DelayOld(10),
1407                    MoveLR(-0.0537431),
1408                    DelayOld(20),
1409                    MoveLR(-0.215705),
1410                    DelayOld(30),
1411                    MoveLR(-0.388256),
1412                    MoveLR(-0.529435),
1413                    DelayOld(30),
1414                    MoveLR(-0.694143),
1415                    DelayOld(20),
1416                    MoveLR(-0.851009),
1417                    MoveUD(0.0588397),
1418                    DelayOld(10),
1419                    MoveLR(-1),
1420                    MoveUD(0.0745262),
1421                    Run(release=False),
1422                    DelayOld(200),
1423                    MoveUD(0.0509964),
1424                    DelayOld(30),
1425                    MoveUD(0.0117801),
1426                    DelayOld(20),
1427                    MoveUD(-0.0901822),
1428                    MoveUD(-0.372539),
1429                    DelayOld(30),
1430                    MoveLR(-0.898068),
1431                    MoveUD(-0.890194),
1432                    Jump(release=False),
1433                    DelayOld(20),
1434                    MoveLR(-0.647084),
1435                    MoveUD(-1),
1436                    MoveLR(-0.427473),
1437                    DelayOld(20),
1438                    MoveLR(-0.00393689),
1439                    DelayOld(10),
1440                    MoveLR(0.537248),
1441                    DelayOld(30),
1442                    MoveLR(1),
1443                    DelayOld(50),
1444                    RunRelease(),
1445                    JumpRelease(),
1446                    DelayOld(50),
1447                    MoveUD(-0.921567),
1448                    MoveUD(-0.749016),
1449                    DelayOld(30),
1450                    MoveUD(-0.552934),
1451                    DelayOld(10),
1452                    MoveUD(-0.247047),
1453                    DelayOld(20),
1454                    MoveUD(0.200018),
1455                    DelayOld(20),
1456                    MoveUD(0.670614),
1457                    MoveUD(1),
1458                    DelayOld(70),
1459                    MoveLR(0.97647),
1460                    DelayOld(20),
1461                    MoveLR(0.764702),
1462                    DelayOld(20),
1463                    MoveLR(0.364696),
1464                    DelayOld(20),
1465                    MoveLR(0.00390637),
1466                    MoveLR(-0.309824),
1467                    DelayOld(20),
1468                    MoveLR(-0.576495),
1469                    DelayOld(30),
1470                    MoveLR(-0.898068),
1471                    DelayOld(10),
1472                    MoveLR(-1),
1473                    MoveUD(0.905911),
1474                    DelayOld(20),
1475                    MoveUD(0.498062),
1476                    DelayOld(20),
1477                    MoveUD(0.0274667),
1478                    MoveUD(-0.403912),
1479                    DelayOld(20),
1480                    MoveUD(-1),
1481                    Run(release=False),
1482                    Jump(release=False),
1483                    DelayOld(10),
1484                    Punch(release=False),
1485                    DelayOld(70),
1486                    JumpRelease(),
1487                    DelayOld(110),
1488                    MoveLR(-0.976501),
1489                    RunRelease(),
1490                    PunchRelease(),
1491                    DelayOld(10),
1492                    MoveLR(-0.952971),
1493                    DelayOld(20),
1494                    MoveLR(-0.905911),
1495                    MoveLR(-0.827479),
1496                    DelayOld(20),
1497                    MoveLR(-0.75689),
1498                    DelayOld(30),
1499                    MoveLR(-0.73336),
1500                    MoveLR(-0.694143),
1501                    DelayOld(20),
1502                    MoveLR(-0.670614),
1503                    DelayOld(30),
1504                    MoveLR(-0.66277),
1505                    DelayOld(10),
1506                    MoveUD(-0.960784),
1507                    DelayOld(20),
1508                    MoveLR(-0.623554),
1509                    MoveUD(-0.874508),
1510                    DelayOld(10),
1511                    MoveLR(-0.545122),
1512                    MoveUD(-0.694113),
1513                    DelayOld(20),
1514                    MoveLR(-0.505905),
1515                    MoveUD(-0.474502),
1516                    DelayOld(20),
1517                    MoveLR(-0.458846),
1518                    MoveUD(-0.356853),
1519                    MoveLR(-0.364727),
1520                    MoveUD(-0.27842),
1521                    DelayOld(20),
1522                    MoveLR(0.00390637),
1523                    Move(0, 0),
1524                    DelayOld(1000),
1525                    Text(
1526                        bs.Lstr(resource=f'{self._r}.phrase09Text')
1527                    ),  # ah that's better
1528                    DelayOld(1900),
1529                    AnalyticsScreen('Tutorial Section 3'),
1530                    Text(
1531                        bs.Lstr(resource=f'{self._r}.phrase10Text')
1532                    ),  # running also helps
1533                    DelayOld(100),
1534                    SpawnSpaz(
1535                        0, (-3.2, 4.3, -4.4), make_current=True, flash=False
1536                    ),
1537                    SpawnSpaz(
1538                        1,
1539                        (3.3, 4.2, -5.8),
1540                        make_current=False,
1541                        color=(0.9, 0.5, 1.0),
1542                        name=bs.Lstr(resource=f'{self._r}.randomName2Text'),
1543                    ),
1544                    DelayOld(1800),
1545                    Text(
1546                        bs.Lstr(resource=f'{self._r}.phrase11Text')
1547                    ),  # hold ANY button to run
1548                    DelayOld(300),
1549                    MoveUD(0),
1550                    DelayOld(20),
1551                    MoveUD(-0.0520646),
1552                    DelayOld(20),
1553                    MoveLR(0),
1554                    MoveUD(-0.223518),
1555                    Run(release=False),
1556                    Jump(release=False),
1557                    DelayOld(10),
1558                    MoveLR(0.0980255),
1559                    MoveUD(-0.309793),
1560                    DelayOld(30),
1561                    MoveLR(0.160772),
1562                    MoveUD(-0.427442),
1563                    DelayOld(20),
1564                    MoveLR(0.231361),
1565                    MoveUD(-0.545091),
1566                    DelayOld(10),
1567                    MoveLR(0.317637),
1568                    MoveUD(-0.678426),
1569                    DelayOld(20),
1570                    MoveLR(0.396069),
1571                    MoveUD(-0.819605),
1572                    MoveLR(0.482345),
1573                    MoveUD(-0.913724),
1574                    DelayOld(20),
1575                    MoveLR(0.560778),
1576                    MoveUD(-1),
1577                    DelayOld(20),
1578                    MoveLR(0.607837),
1579                    DelayOld(10),
1580                    MoveLR(0.623524),
1581                    DelayOld(30),
1582                    MoveLR(0.647053),
1583                    DelayOld(20),
1584                    MoveLR(0.670583),
1585                    MoveLR(0.694113),
1586                    DelayOld(30),
1587                    MoveLR(0.733329),
1588                    DelayOld(20),
1589                    MoveLR(0.764702),
1590                    MoveLR(0.788232),
1591                    DelayOld(20),
1592                    MoveLR(0.827448),
1593                    DelayOld(10),
1594                    MoveLR(0.858821),
1595                    DelayOld(20),
1596                    MoveLR(0.921567),
1597                    DelayOld(30),
1598                    MoveLR(0.97647),
1599                    MoveLR(1),
1600                    DelayOld(130),
1601                    MoveUD(-0.960784),
1602                    DelayOld(20),
1603                    MoveUD(-0.921567),
1604                    DelayOld(30),
1605                    MoveUD(-0.866665),
1606                    MoveUD(-0.819605),
1607                    DelayOld(30),
1608                    MoveUD(-0.772546),
1609                    MoveUD(-0.725486),
1610                    DelayOld(30),
1611                    MoveUD(-0.631367),
1612                    DelayOld(10),
1613                    MoveUD(-0.552934),
1614                    DelayOld(20),
1615                    MoveUD(-0.474502),
1616                    DelayOld(10),
1617                    MoveUD(-0.403912),
1618                    DelayOld(30),
1619                    MoveUD(-0.356853),
1620                    DelayOld(30),
1621                    MoveUD(-0.34901),
1622                    DelayOld(20),
1623                    MoveUD(-0.333323),
1624                    DelayOld(20),
1625                    MoveUD(-0.32548),
1626                    DelayOld(10),
1627                    MoveUD(-0.30195),
1628                    DelayOld(20),
1629                    MoveUD(-0.27842),
1630                    DelayOld(30),
1631                    MoveUD(-0.254891),
1632                    MoveUD(-0.231361),
1633                    DelayOld(30),
1634                    MoveUD(-0.207831),
1635                    DelayOld(20),
1636                    MoveUD(-0.199988),
1637                    MoveUD(-0.176458),
1638                    DelayOld(30),
1639                    MoveUD(-0.137242),
1640                    MoveUD(-0.0823389),
1641                    DelayOld(20),
1642                    MoveUD(-0.0274361),
1643                    DelayOld(20),
1644                    MoveUD(0.00393689),
1645                    DelayOld(40),
1646                    MoveUD(0.0353099),
1647                    DelayOld(20),
1648                    MoveUD(0.113742),
1649                    DelayOld(10),
1650                    MoveUD(0.137272),
1651                    DelayOld(20),
1652                    MoveUD(0.160802),
1653                    MoveUD(0.184332),
1654                    DelayOld(20),
1655                    MoveUD(0.207862),
1656                    DelayOld(30),
1657                    MoveUD(0.247078),
1658                    MoveUD(0.262764),
1659                    DelayOld(20),
1660                    MoveUD(0.270608),
1661                    DelayOld(30),
1662                    MoveUD(0.294137),
1663                    MoveUD(0.32551),
1664                    DelayOld(30),
1665                    MoveUD(0.37257),
1666                    Celebrate('left', 1),
1667                    DelayOld(20),
1668                    MoveUD(0.498062),
1669                    MoveUD(0.560808),
1670                    DelayOld(30),
1671                    MoveUD(0.654927),
1672                    MoveUD(0.694143),
1673                    DelayOld(30),
1674                    MoveUD(0.741203),
1675                    DelayOld(20),
1676                    MoveUD(0.780419),
1677                    MoveUD(0.819636),
1678                    DelayOld(20),
1679                    MoveUD(0.843165),
1680                    DelayOld(20),
1681                    MoveUD(0.882382),
1682                    DelayOld(10),
1683                    MoveUD(0.913755),
1684                    DelayOld(30),
1685                    MoveUD(0.968657),
1686                    MoveUD(1),
1687                    DelayOld(560),
1688                    Punch(release=False),
1689                    DelayOld(210),
1690                    MoveUD(0.968657),
1691                    DelayOld(30),
1692                    MoveUD(0.75689),
1693                    PunchRelease(),
1694                    DelayOld(20),
1695                    MoveLR(0.95294),
1696                    MoveUD(0.435316),
1697                    RunRelease(),
1698                    JumpRelease(),
1699                    MoveLR(0.811762),
1700                    MoveUD(0.270608),
1701                    DelayOld(20),
1702                    MoveLR(0.670583),
1703                    MoveUD(0.160802),
1704                    DelayOld(20),
1705                    MoveLR(0.466659),
1706                    MoveUD(0.0588397),
1707                    DelayOld(10),
1708                    MoveLR(0.317637),
1709                    MoveUD(-0.00390637),
1710                    DelayOld(20),
1711                    MoveLR(0.0801416),
1712                    DelayOld(10),
1713                    MoveLR(0),
1714                    DelayOld(20),
1715                    MoveLR(0),
1716                    DelayOld(30),
1717                    MoveLR(0),
1718                    DelayOld(30),
1719                    MoveLR(0),
1720                    DelayOld(20),
1721                    MoveLR(0),
1722                    DelayOld(100),
1723                    MoveLR(0),
1724                    DelayOld(30),
1725                    MoveUD(0),
1726                    DelayOld(30),
1727                    MoveUD(0),
1728                    DelayOld(50),
1729                    MoveUD(0),
1730                    MoveUD(0),
1731                    DelayOld(30),
1732                    MoveLR(0),
1733                    MoveUD(-0.0520646),
1734                    MoveLR(0),
1735                    MoveUD(-0.0640889),
1736                    DelayOld(20),
1737                    MoveLR(0),
1738                    MoveUD(-0.0881375),
1739                    DelayOld(30),
1740                    MoveLR(-0.0498978),
1741                    MoveUD(-0.199988),
1742                    MoveLR(-0.121586),
1743                    MoveUD(-0.207831),
1744                    DelayOld(20),
1745                    MoveLR(-0.145116),
1746                    MoveUD(-0.223518),
1747                    DelayOld(30),
1748                    MoveLR(-0.152959),
1749                    MoveUD(-0.231361),
1750                    MoveLR(-0.192175),
1751                    MoveUD(-0.262734),
1752                    DelayOld(30),
1753                    MoveLR(-0.200018),
1754                    MoveUD(-0.27842),
1755                    DelayOld(20),
1756                    MoveLR(-0.239235),
1757                    MoveUD(-0.30195),
1758                    MoveUD(-0.309793),
1759                    DelayOld(40),
1760                    MoveUD(-0.333323),
1761                    DelayOld(10),
1762                    MoveUD(-0.34901),
1763                    DelayOld(30),
1764                    MoveUD(-0.372539),
1765                    MoveUD(-0.396069),
1766                    DelayOld(20),
1767                    MoveUD(-0.443129),
1768                    DelayOld(20),
1769                    MoveUD(-0.458815),
1770                    DelayOld(10),
1771                    MoveUD(-0.474502),
1772                    DelayOld(50),
1773                    MoveUD(-0.482345),
1774                    DelayOld(30),
1775                    MoveLR(-0.215705),
1776                    DelayOld(30),
1777                    MoveLR(-0.200018),
1778                    DelayOld(10),
1779                    MoveLR(-0.192175),
1780                    DelayOld(10),
1781                    MoveLR(-0.176489),
1782                    DelayOld(30),
1783                    MoveLR(-0.152959),
1784                    DelayOld(20),
1785                    MoveLR(-0.145116),
1786                    MoveLR(-0.121586),
1787                    MoveUD(-0.458815),
1788                    DelayOld(30),
1789                    MoveLR(-0.098056),
1790                    MoveUD(-0.419599),
1791                    DelayOld(10),
1792                    MoveLR(-0.0745262),
1793                    MoveUD(-0.333323),
1794                    DelayOld(10),
1795                    MoveLR(0.00390637),
1796                    MoveUD(0),
1797                    DelayOld(990),
1798                    MoveLR(0),
1799                    DelayOld(660),
1800                    MoveUD(0),
1801                    AnalyticsScreen('Tutorial Section 4'),
1802                    Text(
1803                        bs.Lstr(resource=f'{self._r}.phrase12Text')
1804                    ),  # for extra-awesome punches,...
1805                    DelayOld(200),
1806                    SpawnSpaz(
1807                        0,
1808                        (
1809                            2.368781805038452,
1810                            4.337533950805664,
1811                            -4.360159873962402,
1812                        ),
1813                        make_current=True,
1814                        flash=False,
1815                    ),
1816                    SpawnSpaz(
1817                        1,
1818                        (-3.2, 4.3, -4.5),
1819                        make_current=False,
1820                        color=(1.0, 0.7, 0.3),
1821                        # name=R.randomName3Text),
1822                        name=bs.Lstr(resource=f'{self._r}.randomName3Text'),
1823                    ),
1824                    DelayOld(100),
1825                    Powerup(1, (2.5, 0.0, 0), relative_to=0),
1826                    Move(1, 0),
1827                    DelayOld(1700),
1828                    Move(0, -0.1),
1829                    DelayOld(100),
1830                    Move(0, 0),
1831                    DelayOld(500),
1832                    DelayOld(320),
1833                    MoveLR(0),
1834                    DelayOld(20),
1835                    MoveLR(0),
1836                    DelayOld(10),
1837                    MoveLR(0),
1838                    DelayOld(20),
1839                    MoveLR(-0.333354),
1840                    MoveLR(-0.592181),
1841                    DelayOld(20),
1842                    MoveLR(-0.788263),
1843                    DelayOld(20),
1844                    MoveLR(-1),
1845                    MoveUD(0.0353099),
1846                    MoveUD(0.0588397),
1847                    DelayOld(10),
1848                    Run(release=False),
1849                    DelayOld(780),
1850                    MoveUD(0.0274667),
1851                    MoveUD(0.00393689),
1852                    DelayOld(10),
1853                    MoveUD(-0.00390637),
1854                    DelayOld(440),
1855                    MoveUD(0.0353099),
1856                    DelayOld(20),
1857                    MoveUD(0.0588397),
1858                    DelayOld(10),
1859                    MoveUD(0.0902127),
1860                    DelayOld(260),
1861                    MoveUD(0.0353099),
1862                    DelayOld(30),
1863                    MoveUD(0.00393689),
1864                    DelayOld(10),
1865                    MoveUD(-0.00390637),
1866                    MoveUD(-0.0274361),
1867                    Celebrate('left', 1),
1868                    DelayOld(10),
1869                    MoveUD(-0.0823389),
1870                    DelayOld(30),
1871                    MoveUD(-0.176458),
1872                    MoveUD(-0.286264),
1873                    DelayOld(20),
1874                    MoveUD(-0.498032),
1875                    Jump(release=False),
1876                    MoveUD(-0.764702),
1877                    DelayOld(30),
1878                    MoveLR(-0.858852),
1879                    MoveUD(-1),
1880                    MoveLR(-0.780419),
1881                    DelayOld(20),
1882                    MoveLR(-0.717673),
1883                    DelayOld(10),
1884                    MoveLR(-0.552965),
1885                    DelayOld(10),
1886                    MoveLR(-0.341197),
1887                    DelayOld(10),
1888                    MoveLR(-0.0274667),
1889                    DelayOld(10),
1890                    MoveLR(0.27842),
1891                    DelayOld(20),
1892                    MoveLR(0.811762),
1893                    MoveLR(1),
1894                    RunRelease(),
1895                    JumpRelease(),
1896                    DelayOld(260),
1897                    MoveLR(0.95294),
1898                    DelayOld(30),
1899                    MoveLR(0.756859),
1900                    DelayOld(10),
1901                    MoveLR(0.317637),
1902                    MoveLR(-0.00393689),
1903                    DelayOld(10),
1904                    MoveLR(-0.341197),
1905                    DelayOld(10),
1906                    MoveLR(-0.647084),
1907                    MoveUD(-0.921567),
1908                    DelayOld(10),
1909                    MoveLR(-1),
1910                    MoveUD(-0.599994),
1911                    MoveUD(-0.474502),
1912                    DelayOld(10),
1913                    MoveUD(-0.309793),
1914                    DelayOld(10),
1915                    MoveUD(-0.160772),
1916                    MoveUD(-0.0352794),
1917                    Delay(10),
1918                    MoveUD(0.176489),
1919                    Delay(10),
1920                    MoveUD(0.607868),
1921                    Run(release=False),
1922                    Jump(release=False),
1923                    DelayOld(20),
1924                    MoveUD(1),
1925                    DelayOld(30),
1926                    MoveLR(-0.921598),
1927                    DelayOld(10),
1928                    Punch(release=False),
1929                    MoveLR(-0.639241),
1930                    DelayOld(10),
1931                    MoveLR(-0.223548),
1932                    DelayOld(10),
1933                    MoveLR(0.254891),
1934                    DelayOld(10),
1935                    MoveLR(0.741172),
1936                    MoveLR(1),
1937                    DelayOld(40),
1938                    JumpRelease(),
1939                    DelayOld(40),
1940                    MoveUD(0.976501),
1941                    DelayOld(10),
1942                    MoveUD(0.73336),
1943                    DelayOld(10),
1944                    MoveUD(0.309824),
1945                    DelayOld(20),
1946                    MoveUD(-0.184301),
1947                    DelayOld(20),
1948                    MoveUD(-0.811762),
1949                    MoveUD(-1),
1950                    KillSpaz(1, explode=True),
1951                    DelayOld(10),
1952                    RunRelease(),
1953                    PunchRelease(),
1954                    DelayOld(110),
1955                    MoveLR(0.97647),
1956                    MoveLR(0.898038),
1957                    DelayOld(20),
1958                    MoveLR(0.788232),
1959                    DelayOld(20),
1960                    MoveLR(0.670583),
1961                    DelayOld(10),
1962                    MoveLR(0.505875),
1963                    DelayOld(10),
1964                    MoveLR(0.32548),
1965                    DelayOld(20),
1966                    MoveLR(0.137242),
1967                    DelayOld(10),
1968                    MoveLR(-0.00393689),
1969                    DelayOld(10),
1970                    MoveLR(-0.215705),
1971                    MoveLR(-0.356883),
1972                    DelayOld(20),
1973                    MoveLR(-0.451003),
1974                    DelayOld(10),
1975                    MoveLR(-0.552965),
1976                    DelayOld(20),
1977                    MoveLR(-0.670614),
1978                    MoveLR(-0.780419),
1979                    DelayOld(10),
1980                    MoveLR(-0.898068),
1981                    DelayOld(20),
1982                    MoveLR(-1),
1983                    DelayOld(370),
1984                    MoveLR(-0.976501),
1985                    DelayOld(10),
1986                    MoveLR(-0.952971),
1987                    DelayOld(10),
1988                    MoveLR(-0.929441),
1989                    MoveLR(-0.898068),
1990                    DelayOld(30),
1991                    MoveLR(-0.874538),
1992                    DelayOld(10),
1993                    MoveLR(-0.851009),
1994                    DelayOld(10),
1995                    MoveLR(-0.835322),
1996                    MoveUD(-0.968627),
1997                    DelayOld(10),
1998                    MoveLR(-0.827479),
1999                    MoveUD(-0.960784),
2000                    DelayOld(20),
2001                    MoveUD(-0.945097),
2002                    DelayOld(70),
2003                    MoveUD(-0.937254),
2004                    DelayOld(20),
2005                    MoveUD(-0.913724),
2006                    DelayOld(20),
2007                    MoveUD(-0.890194),
2008                    MoveLR(-0.780419),
2009                    MoveUD(-0.827448),
2010                    DelayOld(20),
2011                    MoveLR(0.317637),
2012                    MoveUD(0.3961),
2013                    MoveLR(0.0195929),
2014                    MoveUD(0.056093),
2015                    DelayOld(20),
2016                    MoveUD(0),
2017                    DelayOld(750),
2018                    MoveLR(0),
2019                    Text(
2020                        bs.Lstr(
2021                            resource=f'{self._r}.phrase13Text',
2022                            subs=[
2023                                (
2024                                    '${NAME}',
2025                                    bs.Lstr(
2026                                        resource=f'{self._r}.randomName3Text'
2027                                    ),
2028                                )
2029                            ],
2030                        )
2031                    ),  # whoops sorry bill
2032                    RemoveGloves(),
2033                    DelayOld(2000),
2034                    AnalyticsScreen('Tutorial Section 5'),
2035                    Text(
2036                        bs.Lstr(
2037                            resource=f'{self._r}.phrase14Text',
2038                            subs=[
2039                                (
2040                                    '${NAME}',
2041                                    bs.Lstr(
2042                                        resource=f'{self._r}.randomName4Text'
2043                                    ),
2044                                )
2045                            ],
2046                        )
2047                    ),  # you can pick up and throw things such as chuck here
2048                    SpawnSpaz(
2049                        0,
2050                        (-4.0, 4.3, -2.5),
2051                        make_current=True,
2052                        flash=False,
2053                        angle=90,
2054                    ),
2055                    SpawnSpaz(
2056                        1,
2057                        (5, 0, -1.0),
2058                        relative_to=0,
2059                        make_current=False,
2060                        color=(0.4, 1.0, 0.7),
2061                        name=bs.Lstr(resource=f'{self._r}.randomName4Text'),
2062                    ),
2063                    DelayOld(1000),
2064                    Celebrate('left', 1, duration=1000),
2065                    Move(1, 0.2),
2066                    DelayOld(2000),
2067                    PickUp(),
2068                    DelayOld(200),
2069                    Move(0.5, 1.0),
2070                    DelayOld(1200),
2071                    PickUp(),
2072                    Move(0, 0),
2073                    DelayOld(1000),
2074                    Celebrate('left'),
2075                    DelayOld(1500),
2076                    Move(0, -1.0),
2077                    DelayOld(800),
2078                    Move(0, 0),
2079                    DelayOld(800),
2080                    SpawnSpaz(
2081                        0,
2082                        (1.5, 4.3, -4.0),
2083                        make_current=True,
2084                        flash=False,
2085                        angle=0,
2086                    ),
2087                    AnalyticsScreen('Tutorial Section 6'),
2088                    Text(
2089                        bs.Lstr(resource=f'{self._r}.phrase15Text')
2090                    ),  # lastly there's bombs
2091                    DelayOld(1900),
2092                    Text(
2093                        bs.Lstr(resource=f'{self._r}.phrase16Text')
2094                    ),  # throwing bombs takes practice
2095                    DelayOld(2000),
2096                    Bomb(),
2097                    Move(-0.1, -0.1),
2098                    DelayOld(100),
2099                    Move(0, 0),
2100                    DelayOld(500),
2101                    DelayOld(1000),
2102                    Bomb(),
2103                    DelayOld(2000),
2104                    Text(
2105                        bs.Lstr(resource=f'{self._r}.phrase17Text')
2106                    ),  # not a very good throw
2107                    DelayOld(3000),
2108                    Text(
2109                        bs.Lstr(resource=f'{self._r}.phrase18Text')
2110                    ),  # moving helps you get distance
2111                    DelayOld(1000),
2112                    Bomb(),
2113                    DelayOld(500),
2114                    Move(-0.3, 0),
2115                    DelayOld(100),
2116                    Move(-0.6, 0),
2117                    DelayOld(100),
2118                    Move(-1, 0),
2119                    DelayOld(800),
2120                    Bomb(),
2121                    DelayOld(400),
2122                    Move(0, -0.1),
2123                    DelayOld(100),
2124                    Move(0, 0),
2125                    DelayOld(2500),
2126                    Text(
2127                        bs.Lstr(resource=f'{self._r}.phrase19Text')
2128                    ),  # jumping helps you get height
2129                    DelayOld(2000),
2130                    Bomb(),
2131                    DelayOld(500),
2132                    Move(1, 0),
2133                    DelayOld(300),
2134                    Jump(release_delay=250),
2135                    DelayOld(500),
2136                    Jump(release_delay=250),
2137                    DelayOld(550),
2138                    Jump(release_delay=250),
2139                    DelayOld(160),
2140                    Punch(),
2141                    DelayOld(500),
2142                    Move(0, -0.1),
2143                    DelayOld(100),
2144                    Move(0, 0),
2145                    DelayOld(2000),
2146                    Text(
2147                        bs.Lstr(resource=f'{self._r}.phrase20Text')
2148                    ),  # whiplash your bombs
2149                    DelayOld(1000),
2150                    Bomb(release=False),
2151                    DelayOld2(80),
2152                    RunRelease(),
2153                    BombRelease(),
2154                    DelayOld2(620),
2155                    MoveLR(0),
2156                    DelayOld2(10),
2157                    MoveLR(0),
2158                    DelayOld2(40),
2159                    MoveLR(0),
2160                    DelayOld2(10),
2161                    MoveLR(-0.0537431),
2162                    MoveUD(0),
2163                    DelayOld2(20),
2164                    MoveLR(-0.262764),
2165                    DelayOld2(20),
2166                    MoveLR(-0.498062),
2167                    DelayOld2(10),
2168                    MoveLR(-0.639241),
2169                    DelayOld2(20),
2170                    MoveLR(-0.73336),
2171                    DelayOld2(10),
2172                    MoveLR(-0.843165),
2173                    MoveUD(-0.0352794),
2174                    DelayOld2(30),
2175                    MoveLR(-1),
2176                    DelayOld2(10),
2177                    MoveUD(-0.0588092),
2178                    DelayOld2(10),
2179                    MoveUD(-0.160772),
2180                    DelayOld2(20),
2181                    MoveUD(-0.286264),
2182                    DelayOld2(20),
2183                    MoveUD(-0.427442),
2184                    DelayOld2(10),
2185                    MoveUD(-0.623524),
2186                    DelayOld2(20),
2187                    MoveUD(-0.843135),
2188                    DelayOld2(10),
2189                    MoveUD(-1),
2190                    DelayOld2(40),
2191                    MoveLR(-0.890225),
2192                    DelayOld2(10),
2193                    MoveLR(-0.670614),
2194                    DelayOld2(20),
2195                    MoveLR(-0.435316),
2196                    DelayOld2(20),
2197                    MoveLR(-0.184332),
2198                    DelayOld2(10),
2199                    MoveLR(0.00390637),
2200                    DelayOld2(20),
2201                    MoveLR(0.223518),
2202                    DelayOld2(10),
2203                    MoveLR(0.388226),
2204                    DelayOld2(20),
2205                    MoveLR(0.560778),
2206                    DelayOld2(20),
2207                    MoveLR(0.717643),
2208                    DelayOld2(10),
2209                    MoveLR(0.890194),
2210                    DelayOld2(20),
2211                    MoveLR(1),
2212                    DelayOld2(30),
2213                    MoveUD(-0.968627),
2214                    DelayOld2(20),
2215                    MoveUD(-0.898038),
2216                    DelayOld2(10),
2217                    MoveUD(-0.741172),
2218                    DelayOld2(20),
2219                    MoveUD(-0.498032),
2220                    DelayOld2(20),
2221                    MoveUD(-0.247047),
2222                    DelayOld2(10),
2223                    MoveUD(0.00393689),
2224                    DelayOld2(20),
2225                    MoveUD(0.239235),
2226                    DelayOld2(20),
2227                    MoveUD(0.458846),
2228                    DelayOld2(10),
2229                    MoveUD(0.70983),
2230                    DelayOld2(30),
2231                    MoveUD(1),
2232                    DelayOld2(10),
2233                    MoveLR(0.827448),
2234                    DelayOld2(10),
2235                    MoveLR(0.678426),
2236                    DelayOld2(20),
2237                    MoveLR(0.396069),
2238                    DelayOld2(10),
2239                    MoveLR(0.0980255),
2240                    DelayOld2(20),
2241                    MoveLR(-0.160802),
2242                    DelayOld2(20),
2243                    MoveLR(-0.388256),
2244                    DelayOld2(10),
2245                    MoveLR(-0.545122),
2246                    DelayOld2(30),
2247                    MoveLR(-0.73336),
2248                    DelayOld2(10),
2249                    MoveLR(-0.945128),
2250                    DelayOld2(10),
2251                    MoveLR(-1),
2252                    DelayOld2(50),
2253                    MoveUD(0.960814),
2254                    DelayOld2(20),
2255                    MoveUD(0.890225),
2256                    DelayOld2(10),
2257                    MoveUD(0.749046),
2258                    DelayOld2(20),
2259                    MoveUD(0.623554),
2260                    DelayOld2(20),
2261                    MoveUD(0.498062),
2262                    DelayOld2(10),
2263                    MoveUD(0.34904),
2264                    DelayOld2(20),
2265                    MoveUD(0.239235),
2266                    DelayOld2(20),
2267                    MoveUD(0.137272),
2268                    DelayOld2(10),
2269                    MoveUD(0.0117801),
2270                    DelayOld2(20),
2271                    MoveUD(-0.0117496),
2272                    DelayOld2(10),
2273                    MoveUD(-0.0274361),
2274                    DelayOld2(90),
2275                    MoveUD(-0.0352794),
2276                    Run(release=False),
2277                    Jump(release=False),
2278                    Delay(80),
2279                    Punch(release=False),
2280                    DelayOld2(60),
2281                    MoveLR(-0.968657),
2282                    DelayOld2(20),
2283                    MoveLR(-0.835322),
2284                    DelayOld2(10),
2285                    MoveLR(-0.70983),
2286                    JumpRelease(),
2287                    DelayOld2(30),
2288                    MoveLR(-0.592181),
2289                    MoveUD(-0.0588092),
2290                    DelayOld2(10),
2291                    MoveLR(-0.490219),
2292                    MoveUD(-0.0744957),
2293                    DelayOld2(10),
2294                    MoveLR(-0.41963),
2295                    DelayOld2(20),
2296                    MoveLR(0),
2297                    MoveUD(0),
2298                    DelayOld2(20),
2299                    MoveUD(0),
2300                    PunchRelease(),
2301                    RunRelease(),
2302                    DelayOld(500),
2303                    Move(0, -0.1),
2304                    DelayOld(100),
2305                    Move(0, 0),
2306                    DelayOld(2000),
2307                    AnalyticsScreen('Tutorial Section 7'),
2308                    Text(
2309                        bs.Lstr(resource=f'{self._r}.phrase21Text')
2310                    ),  # timing your bombs can be tricky
2311                    Move(-1, 0),
2312                    DelayOld(1000),
2313                    Move(0, -0.1),
2314                    DelayOld(100),
2315                    Move(0, 0),
2316                    SpawnSpaz(
2317                        0,
2318                        (-0.7, 4.3, -3.9),
2319                        make_current=True,
2320                        flash=False,
2321                        angle=-30,
2322                    ),
2323                    SpawnSpaz(
2324                        1,
2325                        (6.5, 0, -0.75),
2326                        relative_to=0,
2327                        make_current=False,
2328                        color=(0.3, 0.8, 1.0),
2329                        name=bs.Lstr(resource=f'{self._r}.randomName5Text'),
2330                    ),
2331                    DelayOld2(1000),
2332                    Move(-1, 0),
2333                    DelayOld2(1800),
2334                    Bomb(),
2335                    Move(0, 0),
2336                    DelayOld2(300),
2337                    Move(1, 0),
2338                    DelayOld2(600),
2339                    Jump(),
2340                    DelayOld2(150),
2341                    Punch(),
2342                    DelayOld2(800),
2343                    Move(-1, 0),
2344                    DelayOld2(1000),
2345                    Move(0, 0),
2346                    DelayOld2(1500),
2347                    Text(bs.Lstr(resource=f'{self._r}.phrase22Text')),  # dang
2348                    Delay(1500),
2349                    Text(''),
2350                    Delay(200),
2351                    Text(
2352                        bs.Lstr(resource=f'{self._r}.phrase23Text')
2353                    ),  # try cooking off
2354                    Delay(1500),
2355                    Bomb(),
2356                    Delay(800),
2357                    Move(1, 0.12),
2358                    Delay(1100),
2359                    Jump(),
2360                    Delay(100),
2361                    Punch(),
2362                    Delay(100),
2363                    Move(0, -0.1),
2364                    Delay(100),
2365                    Move(0, 0),
2366                    Delay(2000),
2367                    Text(
2368                        bs.Lstr(resource=f'{self._r}.phrase24Text')
2369                    ),  # hooray nicely cooked
2370                    Celebrate(),
2371                    DelayOld(2000),
2372                    KillSpaz(1),
2373                    Text(''),
2374                    Move(0.5, -0.5),
2375                    DelayOld(1000),
2376                    Move(0, -0.1),
2377                    DelayOld(100),
2378                    Move(0, 0),
2379                    DelayOld(1000),
2380                    AnalyticsScreen('Tutorial Section 8'),
2381                    Text(
2382                        bs.Lstr(resource=f'{self._r}.phrase25Text')
2383                    ),  # well that's just about it
2384                    DelayOld(2000),
2385                    Text(
2386                        bs.Lstr(resource=f'{self._r}.phrase26Text')
2387                    ),  # go get em tiger
2388                    DelayOld(2000),
2389                    Text(
2390                        bs.Lstr(resource=f'{self._r}.phrase27Text')
2391                    ),  # remember you training
2392                    DelayOld(3000),
2393                    Text(
2394                        bs.Lstr(resource=f'{self._r}.phrase28Text')
2395                    ),  # well maybe
2396                    DelayOld(1600),
2397                    Text(
2398                        bs.Lstr(resource=f'{self._r}.phrase29Text')
2399                    ),  # good luck
2400                    Celebrate('right', duration=10000),
2401                    DelayOld(1000),
2402                    AnalyticsScreen('Tutorial Complete'),
2403                    End(),
2404                ]
2405            )
2406
2407        except Exception:
2408            logging.exception('Error running tutorial.')
2409
2410        # If we read some, exec them.
2411        if self._entries:
2412            self._run_next_entry()
2413        # Otherwise try again in a few seconds.
2414        else:
2415            self._read_entries_timer = bs.Timer(
2416                3.0, bs.WeakCall(self._read_entries)
2417            )
2418
2419    def _run_next_entry(self) -> None:
2420        while self._entries:
2421            entry = self._entries.popleft()
2422            try:
2423                result = entry.run(self)
2424            except Exception:
2425                result = None
2426                logging.exception('Error in tutorial _run_next_entry.')
2427
2428            # If the entry returns an int value, set a timer;
2429            # otherwise just keep going.
2430            if result is not None:
2431                self._entry_timer = bs.Timer(
2432                    result / 1000.0, bs.WeakCall(self._run_next_entry)
2433                )
2434                return
2435
2436        # Done with these entries.. start over soon.
2437        self._read_entries_timer = bs.Timer(
2438            1.0, bs.WeakCall(self._read_entries)
2439        )
2440
2441    def _update_skip_votes(self) -> None:
2442        count = sum(1 for player in self.players if player.pressed)
2443        assert self._skip_count_text
2444        self._skip_count_text.text = (
2445            bs.Lstr(
2446                resource=f'{self._r}.skipVoteCountText',
2447                subs=[
2448                    ('${COUNT}', str(count)),
2449                    ('${TOTAL}', str(len(self.players))),
2450                ],
2451            )
2452            if count > 0
2453            else ''
2454        )
2455        if (
2456            count >= len(self.players)
2457            and self.players
2458            and not self._have_skipped
2459        ):
2460            bs.increment_analytics_count('Tutorial skip')
2461            bs.set_analytics_screen('Tutorial Skip')
2462            self._have_skipped = True
2463            bs.getsound('swish').play()
2464            # self._skip_count_text.text = self._r.skippingText
2465            self._skip_count_text.text = bs.Lstr(
2466                resource=f'{self._r}.skippingText'
2467            )
2468            assert self._skip_text
2469            self._skip_text.text = ''
2470            self.end()
2471
2472    def _player_pressed_button(self, player: Player) -> None:
2473        # Special case: if there's only one player, we give them a
2474        # warning on their first press (some players were thinking the
2475        # on-screen guide meant they were supposed to press something).
2476        if len(self.players) == 1 and not self._issued_warning:
2477            self._issued_warning = True
2478            assert self._skip_text
2479            self._skip_text.text = bs.Lstr(
2480                resource=f'{self._r}.skipConfirmText'
2481            )
2482            self._skip_text.color = (1, 1, 1)
2483            self._skip_text.scale = 1.3
2484            incr = 50
2485            t = incr
2486            for _i in range(6):
2487                bs.timer(
2488                    t / 1000.0,
2489                    bs.Call(setattr, self._skip_text, 'color', (1, 0.5, 0.1)),
2490                )
2491                t += incr
2492                bs.timer(
2493                    t / 1000.0,
2494                    bs.Call(setattr, self._skip_text, 'color', (1, 1, 0)),
2495                )
2496                t += incr
2497            bs.timer(6.0, bs.WeakCall(self._revert_confirm))
2498            return
2499
2500        player.pressed = True
2501
2502        # test...
2503        if not all(self.players):
2504            logging.error(
2505                'Nonexistent player in _player_pressed_button:'
2506                ' %s: we are %s',
2507                [str(p) for p in self.players],
2508                player,
2509            )
2510
2511        self._update_skip_votes()
2512
2513    def _revert_confirm(self) -> None:
2514        assert self._skip_text
2515        self._skip_text.text = bs.Lstr(
2516            resource=f'{self._r}.toSkipPressAnythingText'
2517        )
2518        self._skip_text.color = (1, 1, 1)
2519        self._issued_warning = False
2520
2521    @override
2522    def on_player_join(self, player: Player) -> None:
2523        super().on_player_join(player)
2524
2525        # We just wanna know if this player presses anything.
2526        player.assigninput(
2527            (
2528                bs.InputType.JUMP_PRESS,
2529                bs.InputType.PUNCH_PRESS,
2530                bs.InputType.BOMB_PRESS,
2531                bs.InputType.PICK_UP_PRESS,
2532            ),
2533            bs.Call(self._player_pressed_button, player),
2534        )
2535
2536    @override
2537    def on_player_leave(self, player: Player) -> None:
2538        if not all(self.players):
2539            logging.error(
2540                'Nonexistent player in on_player_leave: %s: we are %s',
2541                [str(p) for p in self.players],
2542                player,
2543            )
2544        super().on_player_leave(player)
2545        # our leaving may influence the vote total needed/etc
2546        self._update_skip_votes()

Units of execution wrangled by a bascenev1.Session.

Category: Gameplay Classes

Examples of Activities include games, score-screens, cutscenes, etc. A bascenev1.Session has one 'current' Activity at any time, though their existence can overlap during transitions.

TutorialActivity(settings: dict | None = None)
189    def __init__(self, settings: dict | None = None):
190        from bascenev1lib.maps import Rampage
191
192        if settings is None:
193            settings = {}
194        super().__init__(settings)
195        self.current_spaz: Spaz | None = None
196        self._benchmark_type = getattr(bs.getsession(), 'benchmark_type', None)
197        self.last_start_time: int | None = None
198        self.cycle_times: list[int] = []
199        self.allow_pausing = True
200        self.allow_kick_idle_players = False
201        self._issued_warning = False
202        self._map_type = Rampage
203        self._map_type.preload()
204        self._jump_button_tex = bs.gettexture('buttonJump')
205        self._pick_up_button_tex = bs.gettexture('buttonPickUp')
206        self._bomb_button_tex = bs.gettexture('buttonBomb')
207        self._punch_button_tex = bs.gettexture('buttonPunch')
208        self._r = 'tutorial'
209        self._have_skipped = False
210        self.stick_image_position_x = self.stick_image_position_y = 0.0
211        self.spawn_sound = bs.getsound('spawn')
212        self.map: bs.Map | None = None
213        self.text: bs.Node | None = None
214        self._skip_text: bs.Node | None = None
215        self._skip_count_text: bs.Node | None = None
216        self._scale: float | None = None
217        self._stick_base_position: tuple[float, float] = (0.0, 0.0)
218        self._stick_nub_position: tuple[float, float] = (0.0, 0.0)
219        self._stick_base_image_color: Sequence[float] = (1.0, 1.0, 1.0, 1.0)
220        self._stick_nub_image_color: Sequence[float] = (1.0, 1.0, 1.0, 1.0)
221        self._time: int = -1
222        self.punch_image_color = (1.0, 1.0, 1.0)
223        self.punch_image: bs.Node | None = None
224        self.bomb_image: bs.Node | None = None
225        self.jump_image: bs.Node | None = None
226        self.pickup_image: bs.Node | None = None
227        self._stick_base_image: bs.Node | None = None
228        self._stick_nub_image: bs.Node | None = None
229        self.bomb_image_color = (1.0, 1.0, 1.0)
230        self.pickup_image_color = (1.0, 1.0, 1.0)
231        self.control_ui_nodes: list[bs.Node] = []
232        self.spazzes: dict[int, Spaz] = {}
233        self.jump_image_color = (1.0, 1.0, 1.0)
234        self._entries: deque[Any] = deque()
235        self._read_entries_timer: bs.Timer | None = None
236        self._entry_timer: bs.Timer | 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.

current_spaz: bascenev1lib.actor.spaz.Spaz | None
last_start_time: int | None
cycle_times: list[int]
allow_pausing = False

Whether game-time should still progress when in menus/etc.

allow_kick_idle_players = True

Whether idle players can potentially be kicked (should not happen in menus/etc).

spawn_sound
map: bascenev1.Map | None
text: _bascenev1.Node | None
punch_image_color
punch_image: _bascenev1.Node | None
bomb_image: _bascenev1.Node | None
jump_image: _bascenev1.Node | None
pickup_image: _bascenev1.Node | None
bomb_image_color
pickup_image_color
control_ui_nodes: list[_bascenev1.Node]
spazzes: dict[int, bascenev1lib.actor.spaz.Spaz]
jump_image_color
@override
def on_transition_in(self) -> None:
238    @override
239    def on_transition_in(self) -> None:
240        super().on_transition_in()
241        bs.setmusic(bs.MusicType.CHAR_SELECT, continuous=True)
242        self.map = self._map_type()

Called when the Activity is first becoming visible.

Upon this call, the Activity should fade in backgrounds, start playing music, etc. It does not yet have access to players or teams, however. They remain owned by the previous Activity up until bascenev1.Activity.on_begin() is called.

@override
def on_begin(self) -> None:
244    @override
245    def on_begin(self) -> None:
246        super().on_begin()
247
248        bs.set_analytics_screen('Tutorial Start')
249        bs.increment_analytics_count('Tutorial start')
250
251        if bool(False):
252            # Buttons on top.
253            text_y = 140
254            buttons_y = 250
255        else:
256            # Buttons on bottom.
257            text_y = 260
258            buttons_y = 160
259
260        # Need different versions of this: taps/buttons/keys.
261        self.text = bs.newnode(
262            'text',
263            attrs={
264                'text': '',
265                'scale': 1.9,
266                'position': (0, text_y),
267                'maxwidth': 500,
268                'flatness': 0.0,
269                'shadow': 0.5,
270                'h_align': 'center',
271                'v_align': 'center',
272                'v_attach': 'center',
273            },
274        )
275
276        # Need different versions of this: taps/buttons/keys.
277        txt = (
278            bs.Lstr(resource=f'{self._r}.cpuBenchmarkText')
279            if self._benchmark_type == 'cpu'
280            else bs.Lstr(resource=f'{self._r}.toSkipPressAnythingText')
281        )
282        t = self._skip_text = bs.newnode(
283            'text',
284            attrs={
285                'text': txt,
286                'maxwidth': 900,
287                'scale': 1.1,
288                'vr_depth': 100,
289                'position': (0, 30),
290                'h_align': 'center',
291                'v_align': 'center',
292                'v_attach': 'bottom',
293            },
294        )
295        bs.animate(t, 'opacity', {1.0: 0.0, 2.0: 0.7})
296        self._skip_count_text = bs.newnode(
297            'text',
298            attrs={
299                'text': '',
300                'scale': 1.4,
301                'vr_depth': 90,
302                'position': (0, 70),
303                'h_align': 'center',
304                'v_align': 'center',
305                'v_attach': 'bottom',
306            },
307        )
308
309        ouya = False
310
311        self._scale = scale = 0.6
312        center_offs = 130.0 * scale
313        offs = 65.0 * scale
314        position = (0, buttons_y)
315        image_size = 90.0 * scale
316        image_size_2 = 220.0 * scale
317        nub_size = 110.0 * scale
318        p = (position[0] + center_offs, position[1] - offs)
319
320        def _sc(r: float, g: float, b: float) -> tuple[float, float, float]:
321            return 0.6 * r, 0.6 * g, 0.6 * b
322
323        self.jump_image_color = c = _sc(0.4, 1, 0.4)
324        self.jump_image = bs.newnode(
325            'image',
326            attrs={
327                'texture': self._jump_button_tex,
328                'absolute_scale': True,
329                'vr_depth': -20,
330                'position': p,
331                'scale': (image_size, image_size),
332                'color': c,
333            },
334        )
335        p = (position[0] + center_offs - offs, position[1])
336        self.punch_image_color = c = (
337            _sc(0.2, 0.6, 1) if ouya else _sc(1, 0.7, 0.3)
338        )
339        self.punch_image = bs.newnode(
340            'image',
341            attrs={
342                'texture': bs.gettexture('buttonPunch'),
343                'absolute_scale': True,
344                'vr_depth': -20,
345                'position': p,
346                'scale': (image_size, image_size),
347                'color': c,
348            },
349        )
350        p = (position[0] + center_offs + offs, position[1])
351        self.bomb_image_color = c = _sc(1, 0.3, 0.3)
352        self.bomb_image = bs.newnode(
353            'image',
354            attrs={
355                'texture': bs.gettexture('buttonBomb'),
356                'absolute_scale': True,
357                'vr_depth': -20,
358                'position': p,
359                'scale': (image_size, image_size),
360                'color': c,
361            },
362        )
363        p = (position[0] + center_offs, position[1] + offs)
364        self.pickup_image_color = c = (
365            _sc(1, 0.8, 0.3) if ouya else _sc(0.5, 0.5, 1)
366        )
367        self.pickup_image = bs.newnode(
368            'image',
369            attrs={
370                'texture': bs.gettexture('buttonPickUp'),
371                'absolute_scale': True,
372                'vr_depth': -20,
373                'position': p,
374                'scale': (image_size, image_size),
375                'color': c,
376            },
377        )
378
379        self._stick_base_position = p = (position[0] - center_offs, position[1])
380        self._stick_base_image_color = c2 = (0.25, 0.25, 0.25, 1.0)
381        self._stick_base_image = bs.newnode(
382            'image',
383            attrs={
384                'texture': bs.gettexture('nub'),
385                'absolute_scale': True,
386                'vr_depth': -40,
387                'position': p,
388                'scale': (image_size_2, image_size_2),
389                'color': c2,
390            },
391        )
392        self._stick_nub_position = p = (position[0] - center_offs, position[1])
393        self._stick_nub_image_color = c3 = (0.4, 0.4, 0.4, 1.0)
394        self._stick_nub_image = bs.newnode(
395            'image',
396            attrs={
397                'texture': bs.gettexture('nub'),
398                'absolute_scale': True,
399                'position': p,
400                'scale': (nub_size, nub_size),
401                'color': c3,
402            },
403        )
404        self.control_ui_nodes = [
405            self.jump_image,
406            self.punch_image,
407            self.bomb_image,
408            self.pickup_image,
409            self._stick_base_image,
410            self._stick_nub_image,
411        ]
412        for n in self.control_ui_nodes:
413            n.opacity = 0.0
414        self._read_entries()

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.

def set_stick_image_position(self, x: float, y: float) -> None:
416    def set_stick_image_position(self, x: float, y: float) -> None:
417        # Clamp this to a circle.
418        len_squared = x * x + y * y
419        if len_squared > 1.0:
420            length = math.sqrt(len_squared)
421            mult = 1.0 / length
422            x *= mult
423            y *= mult
424
425        self.stick_image_position_x = x
426        self.stick_image_position_y = y
427        offs = 50.0
428        assert self._scale is not None
429        p = [
430            self._stick_nub_position[0] + x * offs * self._scale,
431            self._stick_nub_position[1] + y * offs * self._scale,
432        ]
433        c = list(self._stick_nub_image_color)
434        if abs(x) > 0.1 or abs(y) > 0.1:
435            c[0] *= 2.0
436            c[1] *= 4.0
437            c[2] *= 2.0
438        assert self._stick_nub_image is not None
439        self._stick_nub_image.position = p
440        self._stick_nub_image.color = c
441        c = list(self._stick_base_image_color)
442        if abs(x) > 0.1 or abs(y) > 0.1:
443            c[0] *= 1.5
444            c[1] *= 1.5
445            c[2] *= 1.5
446        assert self._stick_base_image is not None
447        self._stick_base_image.color = c
@override
def on_player_join(self, player: Player) -> None:
2521    @override
2522    def on_player_join(self, player: Player) -> None:
2523        super().on_player_join(player)
2524
2525        # We just wanna know if this player presses anything.
2526        player.assigninput(
2527            (
2528                bs.InputType.JUMP_PRESS,
2529                bs.InputType.PUNCH_PRESS,
2530                bs.InputType.BOMB_PRESS,
2531                bs.InputType.PICK_UP_PRESS,
2532            ),
2533            bs.Call(self._player_pressed_button, player),
2534        )

Called when a new bascenev1.Player has joined the Activity.

(including the initial set of Players)

@override
def on_player_leave(self, player: Player) -> None:
2536    @override
2537    def on_player_leave(self, player: Player) -> None:
2538        if not all(self.players):
2539            logging.error(
2540                'Nonexistent player in on_player_leave: %s: we are %s',
2541                [str(p) for p in self.players],
2542                player,
2543            )
2544        super().on_player_leave(player)
2545        # our leaving may influence the vote total needed/etc
2546        self._update_skip_votes()

Called when a bascenev1.Player is leaving the Activity.