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