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

Inherited Members
bascenev1._team.Team
players
id
name
color
manual_init
customdata
on_expire
sessionteam
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=self._r + '.cpuBenchmarkText')
 279            if self._benchmark_type == 'cpu'
 280            else bs.Lstr(resource=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                    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                    color: Sequence[float] = (1.0, 1.0, 1.0),
 582                    make_current: bool = False,
 583                    relative_to: int | None = None,
 584                ):
 585                    self._position = position
 586                    self._relative_to = relative_to
 587
 588                def run(self, a: TutorialActivity) -> None:
 589                    # If they gave a 'relative to' spaz, position is relative
 590                    # to them.
 591                    pos: Sequence[float]
 592                    if self._relative_to is not None:
 593                        snode = a.spazzes[self._relative_to].node
 594                        assert snode
 595                        their_pos = snode.position
 596                        pos = (
 597                            their_pos[0] + self._position[0],
 598                            their_pos[1] + self._position[1],
 599                            their_pos[2] + self._position[2],
 600                        )
 601                    else:
 602                        pos = self._position
 603                    from bascenev1lib.actor import powerupbox
 604
 605                    powerupbox.PowerupBox(
 606                        position=pos, poweruptype='punch'
 607                    ).autoretain()
 608
 609            class Delay:
 610                def __init__(self, time: int) -> None:
 611                    self._time = time
 612
 613                def run(self, a: TutorialActivity) -> int:
 614                    return self._time
 615
 616            class AnalyticsScreen:
 617                def __init__(self, screen: str) -> None:
 618                    self._screen = screen
 619
 620                def run(self, a: TutorialActivity) -> None:
 621                    bs.set_analytics_screen(self._screen)
 622
 623            class DelayOld:
 624                def __init__(self, time: int) -> None:
 625                    self._time = time
 626
 627                def run(self, a: TutorialActivity) -> int:
 628                    return int(0.9 * self._time)
 629
 630            class DelayOld2:
 631                def __init__(self, time: int) -> None:
 632                    self._time = time
 633
 634                def run(self, a: TutorialActivity) -> int:
 635                    return int(0.8 * self._time)
 636
 637            class End:
 638                def __init__(self) -> None:
 639                    pass
 640
 641                def run(self, a: TutorialActivity) -> None:
 642                    bs.increment_analytics_count('Tutorial finish')
 643                    a.end()
 644
 645            class Move:
 646                def __init__(self, x: float, y: float):
 647                    self._x = float(x)
 648                    self._y = float(y)
 649
 650                def run(self, a: TutorialActivity) -> None:
 651                    s = a.current_spaz
 652                    assert s
 653                    # FIXME: Game should take floats for this.
 654                    x_clamped = self._x
 655                    y_clamped = self._y
 656                    s.on_move_left_right(x_clamped)
 657                    s.on_move_up_down(y_clamped)
 658                    a.set_stick_image_position(self._x, self._y)
 659
 660            class MoveLR:
 661                def __init__(self, x: float):
 662                    self._x = float(x)
 663
 664                def run(self, a: TutorialActivity) -> None:
 665                    s = a.current_spaz
 666                    assert s
 667                    # FIXME: Game should take floats for this.
 668                    x_clamped = self._x
 669                    s.on_move_left_right(x_clamped)
 670                    a.set_stick_image_position(
 671                        self._x, a.stick_image_position_y
 672                    )
 673
 674            class MoveUD:
 675                def __init__(self, y: float):
 676                    self._y = float(y)
 677
 678                def run(self, a: TutorialActivity) -> None:
 679                    s = a.current_spaz
 680                    assert s
 681                    # FIXME: Game should take floats for this.
 682                    y_clamped = self._y
 683                    s.on_move_up_down(y_clamped)
 684                    a.set_stick_image_position(
 685                        a.stick_image_position_x, self._y
 686                    )
 687
 688            class Bomb(ButtonPress):
 689                def __init__(
 690                    self,
 691                    delay: int = 0,
 692                    release: bool = True,
 693                    release_delay: int = 500,
 694                ):
 695                    ButtonPress.__init__(
 696                        self,
 697                        'bomb',
 698                        delay=delay,
 699                        release=release,
 700                        release_delay=release_delay,
 701                    )
 702
 703            class Jump(ButtonPress):
 704                def __init__(
 705                    self,
 706                    delay: int = 0,
 707                    release: bool = True,
 708                    release_delay: int = 500,
 709                ):
 710                    ButtonPress.__init__(
 711                        self,
 712                        'jump',
 713                        delay=delay,
 714                        release=release,
 715                        release_delay=release_delay,
 716                    )
 717
 718            class Punch(ButtonPress):
 719                def __init__(
 720                    self,
 721                    delay: int = 0,
 722                    release: bool = True,
 723                    release_delay: int = 500,
 724                ):
 725                    ButtonPress.__init__(
 726                        self,
 727                        'punch',
 728                        delay=delay,
 729                        release=release,
 730                        release_delay=release_delay,
 731                    )
 732
 733            class PickUp(ButtonPress):
 734                def __init__(
 735                    self,
 736                    delay: int = 0,
 737                    release: bool = True,
 738                    release_delay: int = 500,
 739                ):
 740                    ButtonPress.__init__(
 741                        self,
 742                        'pickUp',
 743                        delay=delay,
 744                        release=release,
 745                        release_delay=release_delay,
 746                    )
 747
 748            class Run(ButtonPress):
 749                def __init__(
 750                    self,
 751                    delay: int = 0,
 752                    release: bool = True,
 753                    release_delay: int = 500,
 754                ):
 755                    ButtonPress.__init__(
 756                        self,
 757                        'run',
 758                        delay=delay,
 759                        release=release,
 760                        release_delay=release_delay,
 761                    )
 762
 763            class BombRelease(ButtonRelease):
 764                def __init__(self, delay: int = 0):
 765                    super().__init__('bomb', delay=delay)
 766
 767            class JumpRelease(ButtonRelease):
 768                def __init__(self, delay: int = 0):
 769                    super().__init__('jump', delay=delay)
 770
 771            class PunchRelease(ButtonRelease):
 772                def __init__(self, delay: int = 0):
 773                    super().__init__('punch', delay=delay)
 774
 775            class PickUpRelease(ButtonRelease):
 776                def __init__(self, delay: int = 0):
 777                    super().__init__('pickUp', delay=delay)
 778
 779            class RunRelease(ButtonRelease):
 780                def __init__(self, delay: int = 0):
 781                    super().__init__('run', delay=delay)
 782
 783            class ShowControls:
 784                def __init__(self) -> None:
 785                    pass
 786
 787                def run(self, a: TutorialActivity) -> None:
 788                    for n in a.control_ui_nodes:
 789                        bs.animate(n, 'opacity', {0.0: 0.0, 1.0: 1.0})
 790
 791            class Text:
 792                def __init__(self, text: str | bs.Lstr):
 793                    self.text = text
 794
 795                def run(self, a: TutorialActivity) -> None:
 796                    assert a.text
 797                    a.text.text = self.text
 798
 799            class PrintPos:
 800                def __init__(self, spaz_num: int | None = None):
 801                    self._spaz_num = spaz_num
 802
 803                def run(self, a: TutorialActivity) -> None:
 804                    if self._spaz_num is None:
 805                        s = a.current_spaz
 806                    else:
 807                        s = a.spazzes[self._spaz_num]
 808                    assert s and s.node
 809                    t = list(s.node.position)
 810                    print('RestorePos(' + str((t[0], t[1] - 1.0, t[2])) + '),')
 811
 812            class RestorePos:
 813                def __init__(self, pos: Sequence[float]) -> None:
 814                    self._pos = pos
 815
 816                def run(self, a: TutorialActivity) -> None:
 817                    s = a.current_spaz
 818                    assert s
 819                    s.handlemessage(bs.StandMessage(self._pos, 0))
 820
 821            class Celebrate:
 822                def __init__(
 823                    self,
 824                    celebrate_type: str = 'both',
 825                    spaz_num: int | None = None,
 826                    duration: int = 1000,
 827                ):
 828                    self._spaz_num = spaz_num
 829                    self._celebrate_type = celebrate_type
 830                    self._duration = duration
 831
 832                def run(self, a: TutorialActivity) -> None:
 833                    if self._spaz_num is None:
 834                        s = a.current_spaz
 835                    else:
 836                        s = a.spazzes[self._spaz_num]
 837                    assert s and s.node
 838                    if self._celebrate_type == 'right':
 839                        s.node.handlemessage('celebrate_r', self._duration)
 840                    elif self._celebrate_type == 'left':
 841                        s.node.handlemessage('celebrate_l', self._duration)
 842                    elif self._celebrate_type == 'both':
 843                        s.node.handlemessage('celebrate', self._duration)
 844                    else:
 845                        raise RuntimeError(
 846                            'invalid celebrate type ' + self._celebrate_type
 847                        )
 848
 849            self._entries = deque(
 850                [
 851                    Reset(),
 852                    SpawnSpaz(0, (0, 5.5, -3.0), make_current=True),
 853                    DelayOld(1000),
 854                    AnalyticsScreen('Tutorial Section 1'),
 855                    Text(
 856                        bs.Lstr(resource=self._r + '.phrase01Text')
 857                    ),  # hi there
 858                    Celebrate('left'),
 859                    DelayOld(2000),
 860                    Text(
 861                        bs.Lstr(
 862                            resource=self._r + '.phrase02Text',
 863                            subs=[
 864                                ('${APP_NAME}', bs.Lstr(resource='titleText'))
 865                            ],
 866                        )
 867                    ),  # welcome to <appname>
 868                    DelayOld(80),
 869                    Run(release=False),
 870                    Jump(release=False),
 871                    MoveLR(1),
 872                    MoveUD(0),
 873                    DelayOld(70),
 874                    RunRelease(),
 875                    JumpRelease(),
 876                    DelayOld(60),
 877                    MoveUD(1),
 878                    DelayOld(30),
 879                    MoveLR(0),
 880                    DelayOld(90),
 881                    MoveLR(-1),
 882                    DelayOld(20),
 883                    MoveUD(0),
 884                    DelayOld(70),
 885                    MoveUD(-1),
 886                    DelayOld(20),
 887                    MoveLR(0),
 888                    DelayOld(80),
 889                    MoveUD(0),
 890                    DelayOld(1500),
 891                    Text(
 892                        bs.Lstr(resource=self._r + '.phrase03Text')
 893                    ),  # here's a few tips
 894                    DelayOld(1000),
 895                    ShowControls(),
 896                    DelayOld(1000),
 897                    Jump(),
 898                    DelayOld(1000),
 899                    Jump(),
 900                    DelayOld(1000),
 901                    AnalyticsScreen('Tutorial Section 2'),
 902                    Text(
 903                        bs.Lstr(
 904                            resource=self._r + '.phrase04Text',
 905                            subs=[
 906                                ('${APP_NAME}', bs.Lstr(resource='titleText'))
 907                            ],
 908                        )
 909                    ),  # many things are based on physics
 910                    DelayOld(20),
 911                    MoveUD(0),
 912                    DelayOld(60),
 913                    MoveLR(0),
 914                    DelayOld(10),
 915                    MoveLR(0),
 916                    MoveUD(0),
 917                    DelayOld(10),
 918                    MoveLR(0),
 919                    MoveUD(0),
 920                    DelayOld(20),
 921                    MoveUD(-0.0575579),
 922                    DelayOld(10),
 923                    MoveUD(-0.207831),
 924                    DelayOld(30),
 925                    MoveUD(-0.309793),
 926                    DelayOld(10),
 927                    MoveUD(-0.474502),
 928                    DelayOld(10),
 929                    MoveLR(0.00390637),
 930                    MoveUD(-0.647053),
 931                    DelayOld(20),
 932                    MoveLR(-0.0745262),
 933                    MoveUD(-0.819605),
 934                    DelayOld(10),
 935                    MoveLR(-0.168645),
 936                    MoveUD(-0.937254),
 937                    DelayOld(30),
 938                    MoveLR(-0.294137),
 939                    MoveUD(-1),
 940                    DelayOld(10),
 941                    MoveLR(-0.411786),
 942                    DelayOld(10),
 943                    MoveLR(-0.639241),
 944                    DelayOld(30),
 945                    MoveLR(-0.75689),
 946                    DelayOld(10),
 947                    MoveLR(-0.905911),
 948                    DelayOld(20),
 949                    MoveLR(-1),
 950                    DelayOld(50),
 951                    MoveUD(-0.960784),
 952                    DelayOld(20),
 953                    MoveUD(-0.819605),
 954                    MoveUD(-0.61568),
 955                    DelayOld(20),
 956                    MoveUD(-0.427442),
 957                    DelayOld(20),
 958                    MoveUD(-0.231361),
 959                    DelayOld(10),
 960                    MoveUD(-0.00390637),
 961                    DelayOld(30),
 962                    MoveUD(0.333354),
 963                    MoveUD(0.584338),
 964                    DelayOld(20),
 965                    MoveUD(0.764733),
 966                    DelayOld(30),
 967                    MoveLR(-0.803949),
 968                    MoveUD(0.913755),
 969                    DelayOld(10),
 970                    MoveLR(-0.647084),
 971                    MoveUD(0.992187),
 972                    DelayOld(20),
 973                    MoveLR(-0.435316),
 974                    MoveUD(1),
 975                    DelayOld(20),
 976                    MoveLR(-0.168645),
 977                    MoveUD(0.976501),
 978                    MoveLR(0.0744957),
 979                    MoveUD(0.905911),
 980                    DelayOld(20),
 981                    MoveLR(0.270577),
 982                    MoveUD(0.843165),
 983                    DelayOld(20),
 984                    MoveLR(0.435286),
 985                    MoveUD(0.780419),
 986                    DelayOld(10),
 987                    MoveLR(0.66274),
 988                    MoveUD(0.647084),
 989                    DelayOld(30),
 990                    MoveLR(0.803919),
 991                    MoveUD(0.458846),
 992                    MoveLR(0.929411),
 993                    MoveUD(0.223548),
 994                    DelayOld(20),
 995                    MoveLR(0.95294),
 996                    MoveUD(0.137272),
 997                    DelayOld(20),
 998                    MoveLR(1),
 999                    MoveUD(-0.0509659),
1000                    DelayOld(20),
1001                    MoveUD(-0.247047),
1002                    DelayOld(20),
1003                    MoveUD(-0.443129),
1004                    DelayOld(20),
1005                    MoveUD(-0.694113),
1006                    MoveUD(-0.921567),
1007                    DelayOld(30),
1008                    MoveLR(0.858821),
1009                    MoveUD(-1),
1010                    DelayOld(10),
1011                    MoveLR(0.68627),
1012                    DelayOld(10),
1013                    MoveLR(0.364696),
1014                    DelayOld(20),
1015                    MoveLR(0.0509659),
1016                    DelayOld(20),
1017                    MoveLR(-0.223548),
1018                    DelayOld(10),
1019                    MoveLR(-0.600024),
1020                    MoveUD(-0.913724),
1021                    DelayOld(30),
1022                    MoveLR(-0.858852),
1023                    MoveUD(-0.717643),
1024                    MoveLR(-1),
1025                    MoveUD(-0.474502),
1026                    DelayOld(20),
1027                    MoveUD(-0.396069),
1028                    DelayOld(20),
1029                    MoveUD(-0.286264),
1030                    DelayOld(20),
1031                    MoveUD(-0.137242),
1032                    DelayOld(20),
1033                    MoveUD(0.0353099),
1034                    DelayOld(10),
1035                    MoveUD(0.32551),
1036                    DelayOld(20),
1037                    MoveUD(0.592181),
1038                    DelayOld(10),
1039                    MoveUD(0.851009),
1040                    DelayOld(10),
1041                    MoveUD(1),
1042                    DelayOld(30),
1043                    MoveLR(-0.764733),
1044                    DelayOld(20),
1045                    MoveLR(-0.403943),
1046                    MoveLR(-0.145116),
1047                    DelayOld(30),
1048                    MoveLR(0.0901822),
1049                    MoveLR(0.32548),
1050                    DelayOld(30),
1051                    MoveLR(0.560778),
1052                    MoveUD(0.929441),
1053                    DelayOld(20),
1054                    MoveLR(0.709799),
1055                    MoveUD(0.73336),
1056                    MoveLR(0.803919),
1057                    MoveUD(0.545122),
1058                    DelayOld(20),
1059                    MoveLR(0.882351),
1060                    MoveUD(0.356883),
1061                    DelayOld(10),
1062                    MoveLR(0.968627),
1063                    MoveUD(0.113742),
1064                    DelayOld(20),
1065                    MoveLR(0.992157),
1066                    MoveUD(-0.0823389),
1067                    DelayOld(30),
1068                    MoveUD(-0.309793),
1069                    DelayOld(10),
1070                    MoveUD(-0.545091),
1071                    DelayOld(20),
1072                    MoveLR(0.882351),
1073                    MoveUD(-0.874508),
1074                    DelayOld(20),
1075                    MoveLR(0.756859),
1076                    MoveUD(-1),
1077                    DelayOld(10),
1078                    MoveLR(0.576464),
1079                    DelayOld(20),
1080                    MoveLR(0.254891),
1081                    DelayOld(10),
1082                    MoveLR(-0.0274667),
1083                    DelayOld(10),
1084                    MoveLR(-0.356883),
1085                    DelayOld(30),
1086                    MoveLR(-0.592181),
1087                    MoveLR(-0.827479),
1088                    MoveUD(-0.921567),
1089                    DelayOld(20),
1090                    MoveLR(-1),
1091                    MoveUD(-0.749016),
1092                    DelayOld(20),
1093                    MoveUD(-0.61568),
1094                    DelayOld(10),
1095                    MoveUD(-0.403912),
1096                    DelayOld(20),
1097                    MoveUD(-0.207831),
1098                    DelayOld(10),
1099                    MoveUD(0.121586),
1100                    DelayOld(30),
1101                    MoveUD(0.34904),
1102                    DelayOld(10),
1103                    MoveUD(0.560808),
1104                    DelayOld(10),
1105                    MoveUD(0.827479),
1106                    DelayOld(30),
1107                    MoveUD(1),
1108                    DelayOld(20),
1109                    MoveLR(-0.976501),
1110                    MoveLR(-0.670614),
1111                    DelayOld(20),
1112                    MoveLR(-0.239235),
1113                    DelayOld(20),
1114                    MoveLR(0.160772),
1115                    DelayOld(20),
1116                    MoveLR(0.443129),
1117                    DelayOld(10),
1118                    MoveLR(0.68627),
1119                    MoveUD(0.976501),
1120                    DelayOld(30),
1121                    MoveLR(0.929411),
1122                    MoveUD(0.73336),
1123                    MoveLR(1),
1124                    MoveUD(0.482376),
1125                    DelayOld(20),
1126                    MoveUD(0.34904),
1127                    DelayOld(10),
1128                    MoveUD(0.160802),
1129                    DelayOld(30),
1130                    MoveUD(-0.0744957),
1131                    DelayOld(10),
1132                    MoveUD(-0.333323),
1133                    DelayOld(20),
1134                    MoveUD(-0.647053),
1135                    DelayOld(20),
1136                    MoveUD(-0.937254),
1137                    DelayOld(10),
1138                    MoveLR(0.858821),
1139                    MoveUD(-1),
1140                    DelayOld(10),
1141                    MoveLR(0.576464),
1142                    DelayOld(30),
1143                    MoveLR(0.184301),
1144                    DelayOld(10),
1145                    MoveLR(-0.121586),
1146                    DelayOld(10),
1147                    MoveLR(-0.474532),
1148                    DelayOld(30),
1149                    MoveLR(-0.670614),
1150                    MoveLR(-0.851009),
1151                    DelayOld(30),
1152                    MoveLR(-1),
1153                    MoveUD(-0.968627),
1154                    DelayOld(20),
1155                    MoveUD(-0.843135),
1156                    DelayOld(10),
1157                    MoveUD(-0.631367),
1158                    DelayOld(20),
1159                    MoveUD(-0.403912),
1160                    MoveUD(-0.176458),
1161                    DelayOld(20),
1162                    MoveUD(0.0902127),
1163                    DelayOld(20),
1164                    MoveUD(0.380413),
1165                    DelayOld(10),
1166                    MoveUD(0.717673),
1167                    DelayOld(30),
1168                    MoveUD(1),
1169                    DelayOld(10),
1170                    MoveLR(-0.741203),
1171                    DelayOld(20),
1172                    MoveLR(-0.458846),
1173                    DelayOld(10),
1174                    MoveLR(-0.145116),
1175                    DelayOld(10),
1176                    MoveLR(0.0980255),
1177                    DelayOld(20),
1178                    MoveLR(0.294107),
1179                    DelayOld(30),
1180                    MoveLR(0.466659),
1181                    MoveLR(0.717643),
1182                    MoveUD(0.796106),
1183                    DelayOld(20),
1184                    MoveLR(0.921567),
1185                    MoveUD(0.443159),
1186                    DelayOld(20),
1187                    MoveLR(1),
1188                    MoveUD(0.145116),
1189                    DelayOld(10),
1190                    MoveUD(-0.0274361),
1191                    DelayOld(30),
1192                    MoveUD(-0.223518),
1193                    MoveUD(-0.427442),
1194                    DelayOld(20),
1195                    MoveUD(-0.874508),
1196                    DelayOld(20),
1197                    MoveUD(-1),
1198                    DelayOld(10),
1199                    MoveLR(0.929411),
1200                    DelayOld(20),
1201                    MoveLR(0.68627),
1202                    DelayOld(20),
1203                    MoveLR(0.364696),
1204                    DelayOld(20),
1205                    MoveLR(0.0431227),
1206                    DelayOld(10),
1207                    MoveLR(-0.333354),
1208                    DelayOld(20),
1209                    MoveLR(-0.639241),
1210                    DelayOld(20),
1211                    MoveLR(-0.968657),
1212                    MoveUD(-0.968627),
1213                    DelayOld(20),
1214                    MoveLR(-1),
1215                    MoveUD(-0.890194),
1216                    MoveUD(-0.866665),
1217                    DelayOld(20),
1218                    MoveUD(-0.749016),
1219                    DelayOld(20),
1220                    MoveUD(-0.529405),
1221                    DelayOld(20),
1222                    MoveUD(-0.30195),
1223                    DelayOld(10),
1224                    MoveUD(-0.00390637),
1225                    DelayOld(10),
1226                    MoveUD(0.262764),
1227                    DelayOld(30),
1228                    MoveLR(-0.600024),
1229                    MoveUD(0.458846),
1230                    DelayOld(10),
1231                    MoveLR(-0.294137),
1232                    MoveUD(0.482376),
1233                    DelayOld(20),
1234                    MoveLR(-0.200018),
1235                    MoveUD(0.505905),
1236                    DelayOld(10),
1237                    MoveLR(-0.145116),
1238                    MoveUD(0.545122),
1239                    DelayOld(20),
1240                    MoveLR(-0.0353099),
1241                    MoveUD(0.584338),
1242                    DelayOld(20),
1243                    MoveLR(0.137242),
1244                    MoveUD(0.592181),
1245                    DelayOld(20),
1246                    MoveLR(0.30195),
1247                    DelayOld(10),
1248                    MoveLR(0.490188),
1249                    DelayOld(10),
1250                    MoveLR(0.599994),
1251                    MoveUD(0.529435),
1252                    DelayOld(30),
1253                    MoveLR(0.66274),
1254                    MoveUD(0.3961),
1255                    DelayOld(20),
1256                    MoveLR(0.670583),
1257                    MoveUD(0.231391),
1258                    MoveLR(0.68627),
1259                    MoveUD(0.0745262),
1260                    Move(0, -0.01),
1261                    DelayOld(100),
1262                    Move(0, 0),
1263                    DelayOld(1000),
1264                    Text(
1265                        bs.Lstr(resource=self._r + '.phrase05Text')
1266                    ),  # for example when you punch..
1267                    DelayOld(510),
1268                    Move(0, -0.01),
1269                    DelayOld(100),
1270                    Move(0, 0),
1271                    DelayOld(500),
1272                    SpawnSpaz(
1273                        0,
1274                        (-0.09249162673950195, 4.337906360626221, -2.3),
1275                        make_current=True,
1276                        flash=False,
1277                    ),
1278                    SpawnSpaz(
1279                        1,
1280                        (-3.1, 4.3, -2.0),
1281                        make_current=False,
1282                        color=(1, 1, 0.4),
1283                        name=bs.Lstr(resource=self._r + '.randomName1Text'),
1284                    ),
1285                    Move(-1.0, 0),
1286                    DelayOld(1050),
1287                    Move(0, -0.01),
1288                    DelayOld(100),
1289                    Move(0, 0),
1290                    DelayOld(1000),
1291                    Text(
1292                        bs.Lstr(resource=self._r + '.phrase06Text')
1293                    ),  # your damage is based
1294                    DelayOld(1200),
1295                    Move(-0.05, 0),
1296                    DelayOld(200),
1297                    Punch(),
1298                    DelayOld(800),
1299                    Punch(),
1300                    DelayOld(800),
1301                    Punch(),
1302                    DelayOld(800),
1303                    Move(0, -0.01),
1304                    DelayOld(100),
1305                    Move(0, 0),
1306                    Text(
1307                        bs.Lstr(
1308                            resource=self._r + '.phrase07Text',
1309                            subs=[
1310                                (
1311                                    '${NAME}',
1312                                    bs.Lstr(
1313                                        resource=self._r + '.randomName1Text'
1314                                    ),
1315                                )
1316                            ],
1317                        )
1318                    ),  # see that didn't hurt fred
1319                    DelayOld(2000),
1320                    Celebrate('right', spaz_num=1),
1321                    DelayOld(1400),
1322                    Text(
1323                        bs.Lstr(resource=self._r + '.phrase08Text')
1324                    ),  # lets jump and spin to get more speed
1325                    DelayOld(30),
1326                    MoveLR(0),
1327                    DelayOld(40),
1328                    MoveLR(0),
1329                    DelayOld(40),
1330                    MoveLR(0),
1331                    DelayOld(130),
1332                    MoveLR(0),
1333                    DelayOld(100),
1334                    MoveLR(0),
1335                    DelayOld(10),
1336                    MoveLR(0.0480667),
1337                    DelayOld(40),
1338                    MoveLR(0.056093),
1339                    MoveLR(0.0681173),
1340                    DelayOld(30),
1341                    MoveLR(0.0801416),
1342                    DelayOld(10),
1343                    MoveLR(0.184301),
1344                    DelayOld(10),
1345                    MoveLR(0.207831),
1346                    DelayOld(20),
1347                    MoveLR(0.231361),
1348                    DelayOld(30),
1349                    MoveLR(0.239204),
1350                    DelayOld(30),
1351                    MoveLR(0.254891),
1352                    DelayOld(40),
1353                    MoveLR(0.270577),
1354                    DelayOld(10),
1355                    MoveLR(0.30195),
1356                    DelayOld(20),
1357                    MoveLR(0.341166),
1358                    DelayOld(30),
1359                    MoveLR(0.388226),
1360                    MoveLR(0.435286),
1361                    DelayOld(30),
1362                    MoveLR(0.490188),
1363                    DelayOld(10),
1364                    MoveLR(0.560778),
1365                    DelayOld(20),
1366                    MoveLR(0.599994),
1367                    DelayOld(10),
1368                    MoveLR(0.647053),
1369                    DelayOld(10),
1370                    MoveLR(0.68627),
1371                    DelayOld(30),
1372                    MoveLR(0.733329),
1373                    DelayOld(20),
1374                    MoveLR(0.764702),
1375                    DelayOld(10),
1376                    MoveLR(0.827448),
1377                    DelayOld(20),
1378                    MoveLR(0.874508),
1379                    DelayOld(20),
1380                    MoveLR(0.929411),
1381                    DelayOld(10),
1382                    MoveLR(1),
1383                    DelayOld(830),
1384                    MoveUD(0.0274667),
1385                    DelayOld(10),
1386                    MoveLR(0.95294),
1387                    MoveUD(0.113742),
1388                    DelayOld(30),
1389                    MoveLR(0.780389),
1390                    MoveUD(0.184332),
1391                    DelayOld(10),
1392                    MoveLR(0.27842),
1393                    MoveUD(0.0745262),
1394                    DelayOld(20),
1395                    MoveLR(0),
1396                    MoveUD(0),
1397                    DelayOld(390),
1398                    MoveLR(0),
1399                    MoveLR(0),
1400                    DelayOld(20),
1401                    MoveLR(0),
1402                    DelayOld(20),
1403                    MoveLR(0),
1404                    DelayOld(10),
1405                    MoveLR(-0.0537431),
1406                    DelayOld(20),
1407                    MoveLR(-0.215705),
1408                    DelayOld(30),
1409                    MoveLR(-0.388256),
1410                    MoveLR(-0.529435),
1411                    DelayOld(30),
1412                    MoveLR(-0.694143),
1413                    DelayOld(20),
1414                    MoveLR(-0.851009),
1415                    MoveUD(0.0588397),
1416                    DelayOld(10),
1417                    MoveLR(-1),
1418                    MoveUD(0.0745262),
1419                    Run(release=False),
1420                    DelayOld(200),
1421                    MoveUD(0.0509964),
1422                    DelayOld(30),
1423                    MoveUD(0.0117801),
1424                    DelayOld(20),
1425                    MoveUD(-0.0901822),
1426                    MoveUD(-0.372539),
1427                    DelayOld(30),
1428                    MoveLR(-0.898068),
1429                    MoveUD(-0.890194),
1430                    Jump(release=False),
1431                    DelayOld(20),
1432                    MoveLR(-0.647084),
1433                    MoveUD(-1),
1434                    MoveLR(-0.427473),
1435                    DelayOld(20),
1436                    MoveLR(-0.00393689),
1437                    DelayOld(10),
1438                    MoveLR(0.537248),
1439                    DelayOld(30),
1440                    MoveLR(1),
1441                    DelayOld(50),
1442                    RunRelease(),
1443                    JumpRelease(),
1444                    DelayOld(50),
1445                    MoveUD(-0.921567),
1446                    MoveUD(-0.749016),
1447                    DelayOld(30),
1448                    MoveUD(-0.552934),
1449                    DelayOld(10),
1450                    MoveUD(-0.247047),
1451                    DelayOld(20),
1452                    MoveUD(0.200018),
1453                    DelayOld(20),
1454                    MoveUD(0.670614),
1455                    MoveUD(1),
1456                    DelayOld(70),
1457                    MoveLR(0.97647),
1458                    DelayOld(20),
1459                    MoveLR(0.764702),
1460                    DelayOld(20),
1461                    MoveLR(0.364696),
1462                    DelayOld(20),
1463                    MoveLR(0.00390637),
1464                    MoveLR(-0.309824),
1465                    DelayOld(20),
1466                    MoveLR(-0.576495),
1467                    DelayOld(30),
1468                    MoveLR(-0.898068),
1469                    DelayOld(10),
1470                    MoveLR(-1),
1471                    MoveUD(0.905911),
1472                    DelayOld(20),
1473                    MoveUD(0.498062),
1474                    DelayOld(20),
1475                    MoveUD(0.0274667),
1476                    MoveUD(-0.403912),
1477                    DelayOld(20),
1478                    MoveUD(-1),
1479                    Run(release=False),
1480                    Jump(release=False),
1481                    DelayOld(10),
1482                    Punch(release=False),
1483                    DelayOld(70),
1484                    JumpRelease(),
1485                    DelayOld(110),
1486                    MoveLR(-0.976501),
1487                    RunRelease(),
1488                    PunchRelease(),
1489                    DelayOld(10),
1490                    MoveLR(-0.952971),
1491                    DelayOld(20),
1492                    MoveLR(-0.905911),
1493                    MoveLR(-0.827479),
1494                    DelayOld(20),
1495                    MoveLR(-0.75689),
1496                    DelayOld(30),
1497                    MoveLR(-0.73336),
1498                    MoveLR(-0.694143),
1499                    DelayOld(20),
1500                    MoveLR(-0.670614),
1501                    DelayOld(30),
1502                    MoveLR(-0.66277),
1503                    DelayOld(10),
1504                    MoveUD(-0.960784),
1505                    DelayOld(20),
1506                    MoveLR(-0.623554),
1507                    MoveUD(-0.874508),
1508                    DelayOld(10),
1509                    MoveLR(-0.545122),
1510                    MoveUD(-0.694113),
1511                    DelayOld(20),
1512                    MoveLR(-0.505905),
1513                    MoveUD(-0.474502),
1514                    DelayOld(20),
1515                    MoveLR(-0.458846),
1516                    MoveUD(-0.356853),
1517                    MoveLR(-0.364727),
1518                    MoveUD(-0.27842),
1519                    DelayOld(20),
1520                    MoveLR(0.00390637),
1521                    Move(0, 0),
1522                    DelayOld(1000),
1523                    Text(
1524                        bs.Lstr(resource=self._r + '.phrase09Text')
1525                    ),  # ah that's better
1526                    DelayOld(1900),
1527                    AnalyticsScreen('Tutorial Section 3'),
1528                    Text(
1529                        bs.Lstr(resource=self._r + '.phrase10Text')
1530                    ),  # running also helps
1531                    DelayOld(100),
1532                    SpawnSpaz(
1533                        0, (-3.2, 4.3, -4.4), make_current=True, flash=False
1534                    ),
1535                    SpawnSpaz(
1536                        1,
1537                        (3.3, 4.2, -5.8),
1538                        make_current=False,
1539                        color=(0.9, 0.5, 1.0),
1540                        name=bs.Lstr(resource=self._r + '.randomName2Text'),
1541                    ),
1542                    DelayOld(1800),
1543                    Text(
1544                        bs.Lstr(resource=self._r + '.phrase11Text')
1545                    ),  # hold ANY button to run
1546                    DelayOld(300),
1547                    MoveUD(0),
1548                    DelayOld(20),
1549                    MoveUD(-0.0520646),
1550                    DelayOld(</