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