Source code for bascenev1lib.tutorial
# Released under the MIT License. See LICENSE for details.
#
"""Wrangles the game tutorial sequence."""
# Not too concerned with keeping this old module pretty;
# don't expect to be revisiting it.
# pylint: disable=too-many-branches
# pylint: disable=too-many-statements
# pylint: disable=too-many-lines
# pylint: disable=missing-function-docstring, missing-class-docstring
# pylint: disable=too-many-locals
# pylint: disable=unused-argument
# pylint: disable=unused-variable
from __future__ import annotations
import math
import logging
from collections import deque
from typing import TYPE_CHECKING, override
import bascenev1 as bs
from bascenev1lib.actor.spaz import Spaz
if TYPE_CHECKING:
from typing import Any, Callable, Sequence
def _safesetattr(node: bs.Node | None, attr: str, value: Any) -> None:
if node:
setattr(node, attr, value)
[docs]
class ButtonPress:
def __init__(
self,
button: str,
delay: int = 0,
release: bool = True,
release_delay: int = 0,
):
self._button = button
self._delay = delay
self._release = release
self._release_delay = release_delay
[docs]
def run(self, a: TutorialActivity) -> None:
s = a.current_spaz
assert s is not None
img: bs.Node | None
release_call: Callable[[], None] | None
color: Sequence[float] | None
if self._button == 'punch':
call = s.on_punch_press
release_call = s.on_punch_release
img = a.punch_image
color = a.punch_image_color
elif self._button == 'jump':
call = s.on_jump_press
release_call = s.on_jump_release
img = a.jump_image
color = a.jump_image_color
elif self._button == 'bomb':
call = s.on_bomb_press
release_call = s.on_bomb_release
img = a.bomb_image
color = a.bomb_image_color
elif self._button == 'pickUp':
call = s.on_pickup_press
release_call = s.on_pickup_release
img = a.pickup_image
color = a.pickup_image_color
elif self._button == 'run':
call = bs.Call(s.on_run, 1.0)
release_call = bs.Call(s.on_run, 0.0)
img = None
color = None
else:
raise RuntimeError(f'invalid button: {self._button}')
brightness = 4.0
if color is not None:
c_bright = list(color)
c_bright[0] *= brightness
c_bright[1] *= brightness
c_bright[2] *= brightness
else:
c_bright = [1.0, 1.0, 1.0]
if self._delay == 0:
call()
if img is not None:
img.color = c_bright
img.vr_depth = -40
else:
bs.timer(self._delay / 1000.0, call)
if img is not None:
bs.timer(
self._delay / 1000.0,
bs.Call(_safesetattr, img, 'color', c_bright),
)
bs.timer(
self._delay / 1000.0,
bs.Call(_safesetattr, img, 'vr_depth', -30),
)
if self._release:
if self._delay == 0 and self._release_delay == 0:
release_call()
else:
bs.timer(
0.001 * (self._delay + self._release_delay), release_call
)
if img is not None:
bs.timer(
(self._delay + self._release_delay + 100) / 1000.0,
bs.Call(_safesetattr, img, 'color', color),
)
bs.timer(
(self._delay + self._release_delay + 100) / 1000.0,
bs.Call(_safesetattr, img, 'vr_depth', -20),
)
[docs]
class ButtonRelease:
def __init__(self, button: str, delay: int = 0):
self._button = button
self._delay = delay
[docs]
def run(self, a: TutorialActivity) -> None:
s = a.current_spaz
assert s is not None
call: Callable[[], None] | None
img: bs.Node | None
color: Sequence[float] | None
if self._button == 'punch':
call = s.on_punch_release
img = a.punch_image
color = a.punch_image_color
elif self._button == 'jump':
call = s.on_jump_release
img = a.jump_image
color = a.jump_image_color
elif self._button == 'bomb':
call = s.on_bomb_release
img = a.bomb_image
color = a.bomb_image_color
elif self._button == 'pickUp':
call = s.on_pickup_press
img = a.pickup_image
color = a.pickup_image_color
elif self._button == 'run':
call = bs.Call(s.on_run, 0.0)
img = None
color = None
else:
raise RuntimeError('invalid button: ' + self._button)
if self._delay == 0:
call()
else:
bs.timer(self._delay / 1000.0, call)
if img is not None:
bs.timer(
(self._delay + 100) / 1000.0,
bs.Call(_safesetattr, img, 'color', color),
)
bs.timer(
(self._delay + 100 / 1000.0),
bs.Call(_safesetattr, img, 'vr_depth', -20),
)
[docs]
class Player(bs.Player['Team']):
"""Our player type for this game."""
def __init__(self) -> None:
self.pressed = False
[docs]
class Team(bs.Team[Player]):
"""Our team type for this game."""
def __init__(self) -> None:
pass
[docs]
class TutorialActivity(bs.Activity[Player, Team]):
def __init__(self, settings: dict | None = None):
from bascenev1lib.maps import Rampage
if settings is None:
settings = {}
super().__init__(settings)
self.current_spaz: Spaz | None = None
self._benchmark_type = getattr(bs.getsession(), 'benchmark_type', None)
self.last_start_time: int | None = None
self.cycle_times: list[int] = []
self.allow_pausing = True
self.allow_kick_idle_players = False
self._issued_warning = False
self._map_type = Rampage
self._map_type.preload()
self._jump_button_tex = bs.gettexture('buttonJump')
self._pick_up_button_tex = bs.gettexture('buttonPickUp')
self._bomb_button_tex = bs.gettexture('buttonBomb')
self._punch_button_tex = bs.gettexture('buttonPunch')
self._r = 'tutorial'
self._have_skipped = False
self.stick_image_position_x = self.stick_image_position_y = 0.0
self.spawn_sound = bs.getsound('spawn')
self.map: bs.Map | None = None
self.text: bs.Node | None = None
self._skip_text: bs.Node | None = None
self._skip_count_text: bs.Node | None = None
self._scale: float | None = None
self._stick_base_position: tuple[float, float] = (0.0, 0.0)
self._stick_nub_position: tuple[float, float] = (0.0, 0.0)
self._stick_base_image_color: Sequence[float] = (1.0, 1.0, 1.0, 1.0)
self._stick_nub_image_color: Sequence[float] = (1.0, 1.0, 1.0, 1.0)
self._time: int = -1
self.punch_image_color = (1.0, 1.0, 1.0)
self.punch_image: bs.Node | None = None
self.bomb_image: bs.Node | None = None
self.jump_image: bs.Node | None = None
self.pickup_image: bs.Node | None = None
self._stick_base_image: bs.Node | None = None
self._stick_nub_image: bs.Node | None = None
self.bomb_image_color = (1.0, 1.0, 1.0)
self.pickup_image_color = (1.0, 1.0, 1.0)
self.control_ui_nodes: list[bs.Node] = []
self.spazzes: dict[int, Spaz] = {}
self.jump_image_color = (1.0, 1.0, 1.0)
self._entries: deque[Any] = deque()
self._read_entries_timer: bs.Timer | None = None
self._entry_timer: bs.Timer | None = None
[docs]
@override
def on_transition_in(self) -> None:
super().on_transition_in()
bs.setmusic(bs.MusicType.CHAR_SELECT, continuous=True)
self.map = self._map_type()
[docs]
@override
def on_begin(self) -> None:
super().on_begin()
bs.set_analytics_screen('Tutorial Start')
bs.increment_analytics_count('Tutorial start')
if bool(False):
# Buttons on top.
text_y = 140
buttons_y = 250
else:
# Buttons on bottom.
text_y = 260
buttons_y = 160
# Need different versions of this: taps/buttons/keys.
self.text = bs.newnode(
'text',
attrs={
'text': '',
'scale': 1.9,
'position': (0, text_y),
'maxwidth': 500,
'flatness': 0.0,
'shadow': 0.5,
'h_align': 'center',
'v_align': 'center',
'v_attach': 'center',
},
)
# Need different versions of this: taps/buttons/keys.
txt = (
bs.Lstr(resource=f'{self._r}.cpuBenchmarkText')
if self._benchmark_type == 'cpu'
else bs.Lstr(resource=f'{self._r}.toSkipPressAnythingText')
)
t = self._skip_text = bs.newnode(
'text',
attrs={
'text': txt,
'maxwidth': 900,
'scale': 1.1,
'vr_depth': 100,
'position': (0, 30),
'h_align': 'center',
'v_align': 'center',
'v_attach': 'bottom',
},
)
bs.animate(t, 'opacity', {1.0: 0.0, 2.0: 0.7})
self._skip_count_text = bs.newnode(
'text',
attrs={
'text': '',
'scale': 1.4,
'vr_depth': 90,
'position': (0, 70),
'h_align': 'center',
'v_align': 'center',
'v_attach': 'bottom',
},
)
ouya = False
self._scale = scale = 0.6
center_offs = 130.0 * scale
offs = 65.0 * scale
position = (0, buttons_y)
image_size = 90.0 * scale
image_size_2 = 220.0 * scale
nub_size = 110.0 * scale
p = (position[0] + center_offs, position[1] - offs)
def _sc(r: float, g: float, b: float) -> tuple[float, float, float]:
return 0.6 * r, 0.6 * g, 0.6 * b
self.jump_image_color = c = _sc(0.4, 1, 0.4)
self.jump_image = bs.newnode(
'image',
attrs={
'texture': self._jump_button_tex,
'absolute_scale': True,
'vr_depth': -20,
'position': p,
'scale': (image_size, image_size),
'color': c,
},
)
p = (position[0] + center_offs - offs, position[1])
self.punch_image_color = c = (
_sc(0.2, 0.6, 1) if ouya else _sc(1, 0.7, 0.3)
)
self.punch_image = bs.newnode(
'image',
attrs={
'texture': bs.gettexture('buttonPunch'),
'absolute_scale': True,
'vr_depth': -20,
'position': p,
'scale': (image_size, image_size),
'color': c,
},
)
p = (position[0] + center_offs + offs, position[1])
self.bomb_image_color = c = _sc(1, 0.3, 0.3)
self.bomb_image = bs.newnode(
'image',
attrs={
'texture': bs.gettexture('buttonBomb'),
'absolute_scale': True,
'vr_depth': -20,
'position': p,
'scale': (image_size, image_size),
'color': c,
},
)
p = (position[0] + center_offs, position[1] + offs)
self.pickup_image_color = c = (
_sc(1, 0.8, 0.3) if ouya else _sc(0.5, 0.5, 1)
)
self.pickup_image = bs.newnode(
'image',
attrs={
'texture': bs.gettexture('buttonPickUp'),
'absolute_scale': True,
'vr_depth': -20,
'position': p,
'scale': (image_size, image_size),
'color': c,
},
)
self._stick_base_position = p = (position[0] - center_offs, position[1])
self._stick_base_image_color = c2 = (0.25, 0.25, 0.25, 1.0)
self._stick_base_image = bs.newnode(
'image',
attrs={
'texture': bs.gettexture('nub'),
'absolute_scale': True,
'vr_depth': -40,
'position': p,
'scale': (image_size_2, image_size_2),
'color': c2,
},
)
self._stick_nub_position = p = (position[0] - center_offs, position[1])
self._stick_nub_image_color = c3 = (0.4, 0.4, 0.4, 1.0)
self._stick_nub_image = bs.newnode(
'image',
attrs={
'texture': bs.gettexture('nub'),
'absolute_scale': True,
'position': p,
'scale': (nub_size, nub_size),
'color': c3,
},
)
self.control_ui_nodes = [
self.jump_image,
self.punch_image,
self.bomb_image,
self.pickup_image,
self._stick_base_image,
self._stick_nub_image,
]
for n in self.control_ui_nodes:
n.opacity = 0.0
self._read_entries()
[docs]
def set_stick_image_position(self, x: float, y: float) -> None:
# Clamp this to a circle.
len_squared = x * x + y * y
if len_squared > 1.0:
length = math.sqrt(len_squared)
mult = 1.0 / length
x *= mult
y *= mult
self.stick_image_position_x = x
self.stick_image_position_y = y
offs = 50.0
assert self._scale is not None
p = [
self._stick_nub_position[0] + x * offs * self._scale,
self._stick_nub_position[1] + y * offs * self._scale,
]
c = list(self._stick_nub_image_color)
if abs(x) > 0.1 or abs(y) > 0.1:
c[0] *= 2.0
c[1] *= 4.0
c[2] *= 2.0
assert self._stick_nub_image is not None
self._stick_nub_image.position = p
self._stick_nub_image.color = c
c = list(self._stick_base_image_color)
if abs(x) > 0.1 or abs(y) > 0.1:
c[0] *= 1.5
c[1] *= 1.5
c[2] *= 1.5
assert self._stick_base_image is not None
self._stick_base_image.color = c
def _read_entries(self) -> None:
try:
class Reset:
def __init__(self) -> None:
pass
def run(self, a: TutorialActivity) -> None:
# if we're looping, print out how long each cycle took
# print out how long each cycle took..
if a.last_start_time is not None:
tval = int(bs.apptime() * 1000.0) - a.last_start_time
assert isinstance(tval, int)
diff = tval
a.cycle_times.append(diff)
bs.broadcastmessage(
'cycle time: '
+ str(diff)
+ ' (average: '
+ str(sum(a.cycle_times) / len(a.cycle_times))
+ ')'
)
tval = int(bs.apptime() * 1000.0)
assert isinstance(tval, int)
a.last_start_time = tval
assert a.text
a.text.text = ''
for spaz in list(a.spazzes.values()):
spaz.handlemessage(bs.DieMessage(immediate=True))
a.spazzes = {}
a.current_spaz = None
for n in a.control_ui_nodes:
n.opacity = 0.0
a.set_stick_image_position(0, 0)
# Can be used for debugging.
class SetSpeed:
def __init__(self, speed: int):
self._speed = speed
def run(self, a: TutorialActivity) -> None:
print('setting to', self._speed)
bs.set_debug_speed_exponent(self._speed)
class RemoveGloves:
def __init__(self) -> None:
pass
def run(self, a: TutorialActivity) -> None:
# pylint: disable=protected-access
assert a.current_spaz is not None
# noinspection PyProtectedMember
a.current_spaz._gloves_wear_off()
class KillSpaz:
def __init__(self, num: int, explode: bool = False):
self._num = num
self._explode = explode
def run(self, a: TutorialActivity) -> None:
if self._explode:
a.spazzes[self._num].shatter()
del a.spazzes[self._num]
class SpawnSpaz:
def __init__(
self,
num: int,
position: Sequence[float],
*,
color: Sequence[float] = (1.0, 1.0, 1.0),
make_current: bool = False,
relative_to: int | None = None,
name: str | bs.Lstr = '',
flash: bool = True,
angle: float = 0.0,
):
self._num = num
self._position = position
self._make_current = make_current
self._color = color
self._relative_to = relative_to
self._name = name
self._flash = flash
self._angle = angle
def run(self, a: TutorialActivity) -> None:
# if they gave a 'relative to' spaz, position is relative
# to them
pos: Sequence[float]
if self._relative_to is not None:
snode = a.spazzes[self._relative_to].node
assert snode
their_pos = snode.position
pos = (
their_pos[0] + self._position[0],
their_pos[1] + self._position[1],
their_pos[2] + self._position[2],
)
else:
pos = self._position
# if there's already a spaz at this spot, insta-kill it
if self._num in a.spazzes:
a.spazzes[self._num].handlemessage(
bs.DieMessage(immediate=True)
)
s = a.spazzes[self._num] = Spaz(
color=self._color,
start_invincible=self._flash,
demo_mode=True,
)
# FIXME: Should extend spaz to support Lstr names.
assert s.node
if isinstance(self._name, bs.Lstr):
s.node.name = self._name.evaluate()
else:
s.node.name = self._name
s.node.name_color = self._color
s.handlemessage(bs.StandMessage(pos, self._angle))
if self._make_current:
a.current_spaz = s
if self._flash:
a.spawn_sound.play(position=pos)
class Powerup:
def __init__(
self,
num: int,
position: Sequence[float],
*,
color: Sequence[float] = (1.0, 1.0, 1.0),
make_current: bool = False,
relative_to: int | None = None,
):
self._position = position
self._relative_to = relative_to
def run(self, a: TutorialActivity) -> None:
# If they gave a 'relative to' spaz, position is relative
# to them.
pos: Sequence[float]
if self._relative_to is not None:
snode = a.spazzes[self._relative_to].node
assert snode
their_pos = snode.position
pos = (
their_pos[0] + self._position[0],
their_pos[1] + self._position[1],
their_pos[2] + self._position[2],
)
else:
pos = self._position
from bascenev1lib.actor import powerupbox
powerupbox.PowerupBox(
position=pos, poweruptype='punch'
).autoretain()
class Delay:
def __init__(self, time: int) -> None:
self._time = time
def run(self, a: TutorialActivity) -> int:
return self._time
class AnalyticsScreen:
def __init__(self, screen: str) -> None:
self._screen = screen
def run(self, a: TutorialActivity) -> None:
bs.set_analytics_screen(self._screen)
class DelayOld:
def __init__(self, time: int) -> None:
self._time = time
def run(self, a: TutorialActivity) -> int:
return int(0.9 * self._time)
class DelayOld2:
def __init__(self, time: int) -> None:
self._time = time
def run(self, a: TutorialActivity) -> int:
return int(0.8 * self._time)
class End:
def __init__(self) -> None:
pass
def run(self, a: TutorialActivity) -> None:
bs.increment_analytics_count('Tutorial finish')
a.end()
class Move:
def __init__(self, x: float, y: float):
self._x = float(x)
self._y = float(y)
def run(self, a: TutorialActivity) -> None:
s = a.current_spaz
assert s
# FIXME: Game should take floats for this.
x_clamped = self._x
y_clamped = self._y
s.on_move_left_right(x_clamped)
s.on_move_up_down(y_clamped)
a.set_stick_image_position(self._x, self._y)
class MoveLR:
def __init__(self, x: float):
self._x = float(x)
def run(self, a: TutorialActivity) -> None:
s = a.current_spaz
assert s
# FIXME: Game should take floats for this.
x_clamped = self._x
s.on_move_left_right(x_clamped)
a.set_stick_image_position(
self._x, a.stick_image_position_y
)
class MoveUD:
def __init__(self, y: float):
self._y = float(y)
def run(self, a: TutorialActivity) -> None:
s = a.current_spaz
assert s
# FIXME: Game should take floats for this.
y_clamped = self._y
s.on_move_up_down(y_clamped)
a.set_stick_image_position(
a.stick_image_position_x, self._y
)
class Bomb(ButtonPress):
def __init__(
self,
delay: int = 0,
release: bool = True,
release_delay: int = 500,
):
ButtonPress.__init__(
self,
'bomb',
delay=delay,
release=release,
release_delay=release_delay,
)
class Jump(ButtonPress):
def __init__(
self,
delay: int = 0,
release: bool = True,
release_delay: int = 500,
):
ButtonPress.__init__(
self,
'jump',
delay=delay,
release=release,
release_delay=release_delay,
)
class Punch(ButtonPress):
def __init__(
self,
delay: int = 0,
release: bool = True,
release_delay: int = 500,
):
ButtonPress.__init__(
self,
'punch',
delay=delay,
release=release,
release_delay=release_delay,
)
class PickUp(ButtonPress):
def __init__(
self,
delay: int = 0,
release: bool = True,
release_delay: int = 500,
):
ButtonPress.__init__(
self,
'pickUp',
delay=delay,
release=release,
release_delay=release_delay,
)
class Run(ButtonPress):
def __init__(
self,
delay: int = 0,
release: bool = True,
release_delay: int = 500,
):
ButtonPress.__init__(
self,
'run',
delay=delay,
release=release,
release_delay=release_delay,
)
class BombRelease(ButtonRelease):
def __init__(self, delay: int = 0):
super().__init__('bomb', delay=delay)
class JumpRelease(ButtonRelease):
def __init__(self, delay: int = 0):
super().__init__('jump', delay=delay)
class PunchRelease(ButtonRelease):
def __init__(self, delay: int = 0):
super().__init__('punch', delay=delay)
class PickUpRelease(ButtonRelease):
def __init__(self, delay: int = 0):
super().__init__('pickUp', delay=delay)
class RunRelease(ButtonRelease):
def __init__(self, delay: int = 0):
super().__init__('run', delay=delay)
class ShowControls:
def __init__(self) -> None:
pass
def run(self, a: TutorialActivity) -> None:
for n in a.control_ui_nodes:
bs.animate(n, 'opacity', {0.0: 0.0, 1.0: 1.0})
class Text:
def __init__(self, text: str | bs.Lstr):
self.text = text
def run(self, a: TutorialActivity) -> None:
assert a.text
a.text.text = self.text
class PrintPos:
def __init__(self, spaz_num: int | None = None):
self._spaz_num = spaz_num
def run(self, a: TutorialActivity) -> None:
if self._spaz_num is None:
s = a.current_spaz
else:
s = a.spazzes[self._spaz_num]
assert s and s.node
t = list(s.node.position)
print('RestorePos(' + str((t[0], t[1] - 1.0, t[2])) + '),')
class RestorePos:
def __init__(self, pos: Sequence[float]) -> None:
self._pos = pos
def run(self, a: TutorialActivity) -> None:
s = a.current_spaz
assert s
s.handlemessage(bs.StandMessage(self._pos, 0))
class Celebrate:
def __init__(
self,
celebrate_type: str = 'both',
spaz_num: int | None = None,
duration: int = 1000,
):
self._spaz_num = spaz_num
self._celebrate_type = celebrate_type
self._duration = duration
def run(self, a: TutorialActivity) -> None:
if self._spaz_num is None:
s = a.current_spaz
else:
s = a.spazzes[self._spaz_num]
assert s and s.node
if self._celebrate_type == 'right':
s.node.handlemessage('celebrate_r', self._duration)
elif self._celebrate_type == 'left':
s.node.handlemessage('celebrate_l', self._duration)
elif self._celebrate_type == 'both':
s.node.handlemessage('celebrate', self._duration)
else:
raise RuntimeError(
'invalid celebrate type ' + self._celebrate_type
)
self._entries = deque(
[
Reset(),
SpawnSpaz(0, (0, 5.5, -3.0), make_current=True),
DelayOld(1000),
AnalyticsScreen('Tutorial Section 1'),
Text(
bs.Lstr(resource=f'{self._r}.phrase01Text')
), # hi there
Celebrate('left'),
DelayOld(2000),
Text(
bs.Lstr(
resource=f'{self._r}.phrase02Text',
subs=[
('${APP_NAME}', bs.Lstr(resource='titleText'))
],
)
), # welcome to <appname>
DelayOld(80),
Run(release=False),
Jump(release=False),
MoveLR(1),
MoveUD(0),
DelayOld(70),
RunRelease(),
JumpRelease(),
DelayOld(60),
MoveUD(1),
DelayOld(30),
MoveLR(0),
DelayOld(90),
MoveLR(-1),
DelayOld(20),
MoveUD(0),
DelayOld(70),
MoveUD(-1),
DelayOld(20),
MoveLR(0),
DelayOld(80),
MoveUD(0),
DelayOld(1500),
Text(
bs.Lstr(resource=f'{self._r}.phrase03Text')
), # here's a few tips
DelayOld(1000),
ShowControls(),
DelayOld(1000),
Jump(),
DelayOld(1000),
Jump(),
DelayOld(1000),
AnalyticsScreen('Tutorial Section 2'),
Text(
bs.Lstr(
resource=f'{self._r}.phrase04Text',
subs=[
('${APP_NAME}', bs.Lstr(resource='titleText'))
],
)
), # many things are based on physics
DelayOld(20),
MoveUD(0),
DelayOld(60),
MoveLR(0),
DelayOld(10),
MoveLR(0),
MoveUD(0),
DelayOld(10),
MoveLR(0),
MoveUD(0),
DelayOld(20),
MoveUD(-0.0575579),
DelayOld(10),
MoveUD(-0.207831),
DelayOld(30),
MoveUD(-0.309793),
DelayOld(10),
MoveUD(-0.474502),
DelayOld(10),
MoveLR(0.00390637),
MoveUD(-0.647053),
DelayOld(20),
MoveLR(-0.0745262),
MoveUD(-0.819605),
DelayOld(10),
MoveLR(-0.168645),
MoveUD(-0.937254),
DelayOld(30),
MoveLR(-0.294137),
MoveUD(-1),
DelayOld(10),
MoveLR(-0.411786),
DelayOld(10),
MoveLR(-0.639241),
DelayOld(30),
MoveLR(-0.75689),
DelayOld(10),
MoveLR(-0.905911),
DelayOld(20),
MoveLR(-1),
DelayOld(50),
MoveUD(-0.960784),
DelayOld(20),
MoveUD(-0.819605),
MoveUD(-0.61568),
DelayOld(20),
MoveUD(-0.427442),
DelayOld(20),
MoveUD(-0.231361),
DelayOld(10),
MoveUD(-0.00390637),
DelayOld(30),
MoveUD(0.333354),
MoveUD(0.584338),
DelayOld(20),
MoveUD(0.764733),
DelayOld(30),
MoveLR(-0.803949),
MoveUD(0.913755),
DelayOld(10),
MoveLR(-0.647084),
MoveUD(0.992187),
DelayOld(20),
MoveLR(-0.435316),
MoveUD(1),
DelayOld(20),
MoveLR(-0.168645),
MoveUD(0.976501),
MoveLR(0.0744957),
MoveUD(0.905911),
DelayOld(20),
MoveLR(0.270577),
MoveUD(0.843165),
DelayOld(20),
MoveLR(0.435286),
MoveUD(0.780419),
DelayOld(10),
MoveLR(0.66274),
MoveUD(0.647084),
DelayOld(30),
MoveLR(0.803919),
MoveUD(0.458846),
MoveLR(0.929411),
MoveUD(0.223548),
DelayOld(20),
MoveLR(0.95294),
MoveUD(0.137272),
DelayOld(20),
MoveLR(1),
MoveUD(-0.0509659),
DelayOld(20),
MoveUD(-0.247047),
DelayOld(20),
MoveUD(-0.443129),
DelayOld(20),
MoveUD(-0.694113),
MoveUD(-0.921567),
DelayOld(30),
MoveLR(0.858821),
MoveUD(-1),
DelayOld(10),
MoveLR(0.68627),
DelayOld(10),
MoveLR(0.364696),
DelayOld(20),
MoveLR(0.0509659),
DelayOld(20),
MoveLR(-0.223548),
DelayOld(10),
MoveLR(-0.600024),
MoveUD(-0.913724),
DelayOld(30),
MoveLR(-0.858852),
MoveUD(-0.717643),
MoveLR(-1),
MoveUD(-0.474502),
DelayOld(20),
MoveUD(-0.396069),
DelayOld(20),
MoveUD(-0.286264),
DelayOld(20),
MoveUD(-0.137242),
DelayOld(20),
MoveUD(0.0353099),
DelayOld(10),
MoveUD(0.32551),
DelayOld(20),
MoveUD(0.592181),
DelayOld(10),
MoveUD(0.851009),
DelayOld(10),
MoveUD(1),
DelayOld(30),
MoveLR(-0.764733),
DelayOld(20),
MoveLR(-0.403943),
MoveLR(-0.145116),
DelayOld(30),
MoveLR(0.0901822),
MoveLR(0.32548),
DelayOld(30),
MoveLR(0.560778),
MoveUD(0.929441),
DelayOld(20),
MoveLR(0.709799),
MoveUD(0.73336),
MoveLR(0.803919),
MoveUD(0.545122),
DelayOld(20),
MoveLR(0.882351),
MoveUD(0.356883),
DelayOld(10),
MoveLR(0.968627),
MoveUD(0.113742),
DelayOld(20),
MoveLR(0.992157),
MoveUD(-0.0823389),
DelayOld(30),
MoveUD(-0.309793),
DelayOld(10),
MoveUD(-0.545091),
DelayOld(20),
MoveLR(0.882351),
MoveUD(-0.874508),
DelayOld(20),
MoveLR(0.756859),
MoveUD(-1),
DelayOld(10),
MoveLR(0.576464),
DelayOld(20),
MoveLR(0.254891),
DelayOld(10),
MoveLR(-0.0274667),
DelayOld(10),
MoveLR(-0.356883),
DelayOld(30),
MoveLR(-0.592181),
MoveLR(-0.827479),
MoveUD(-0.921567),
DelayOld(20),
MoveLR(-1),
MoveUD(-0.749016),
DelayOld(20),
MoveUD(-0.61568),
DelayOld(10),
MoveUD(-0.403912),
DelayOld(20),
MoveUD(-0.207831),
DelayOld(10),
MoveUD(0.121586),
DelayOld(30),
MoveUD(0.34904),
DelayOld(10),
MoveUD(0.560808),
DelayOld(10),
MoveUD(0.827479),
DelayOld(30),
MoveUD(1),
DelayOld(20),
MoveLR(-0.976501),
MoveLR(-0.670614),
DelayOld(20),
MoveLR(-0.239235),
DelayOld(20),
MoveLR(0.160772),
DelayOld(20),
MoveLR(0.443129),
DelayOld(10),
MoveLR(0.68627),
MoveUD(0.976501),
DelayOld(30),
MoveLR(0.929411),
MoveUD(0.73336),
MoveLR(1),
MoveUD(0.482376),
DelayOld(20),
MoveUD(0.34904),
DelayOld(10),
MoveUD(0.160802),
DelayOld(30),
MoveUD(-0.0744957),
DelayOld(10),
MoveUD(-0.333323),
DelayOld(20),
MoveUD(-0.647053),
DelayOld(20),
MoveUD(-0.937254),
DelayOld(10),
MoveLR(0.858821),
MoveUD(-1),
DelayOld(10),
MoveLR(0.576464),
DelayOld(30),
MoveLR(0.184301),
DelayOld(10),
MoveLR(-0.121586),
DelayOld(10),
MoveLR(-0.474532),
DelayOld(30),
MoveLR(-0.670614),
MoveLR(-0.851009),
DelayOld(30),
MoveLR(-1),
MoveUD(-0.968627),
DelayOld(20),
MoveUD(-0.843135),
DelayOld(10),
MoveUD(-0.631367),
DelayOld(20),
MoveUD(-0.403912),
MoveUD(-0.176458),
DelayOld(20),
MoveUD(0.0902127),
DelayOld(20),
MoveUD(0.380413),
DelayOld(10),
MoveUD(0.717673),
DelayOld(30),
MoveUD(1),
DelayOld(10),
MoveLR(-0.741203),
DelayOld(20),
MoveLR(-0.458846),
DelayOld(10),
MoveLR(-0.145116),
DelayOld(10),
MoveLR(0.0980255),
DelayOld(20),
MoveLR(0.294107),
DelayOld(30),
MoveLR(0.466659),
MoveLR(0.717643),
MoveUD(0.796106),
DelayOld(20),
MoveLR(0.921567),
MoveUD(0.443159),
DelayOld(20),
MoveLR(1),
MoveUD(0.145116),
DelayOld(10),
MoveUD(-0.0274361),
DelayOld(30),
MoveUD(-0.223518),
MoveUD(-0.427442),
DelayOld(20),
MoveUD(-0.874508),
DelayOld(20),
MoveUD(-1),
DelayOld(10),
MoveLR(0.929411),
DelayOld(20),
MoveLR(0.68627),
DelayOld(20),
MoveLR(0.364696),
DelayOld(20),
MoveLR(0.0431227),
DelayOld(10),
MoveLR(-0.333354),
DelayOld(20),
MoveLR(-0.639241),
DelayOld(20),
MoveLR(-0.968657),
MoveUD(-0.968627),
DelayOld(20),
MoveLR(-1),
MoveUD(-0.890194),
MoveUD(-0.866665),
DelayOld(20),
MoveUD(-0.749016),
DelayOld(20),
MoveUD(-0.529405),
DelayOld(20),
MoveUD(-0.30195),
DelayOld(10),
MoveUD(-0.00390637),
DelayOld(10),
MoveUD(0.262764),
DelayOld(30),
MoveLR(-0.600024),
MoveUD(0.458846),
DelayOld(10),
MoveLR(-0.294137),
MoveUD(0.482376),
DelayOld(20),
MoveLR(-0.200018),
MoveUD(0.505905),
DelayOld(10),
MoveLR(-0.145116),
MoveUD(0.545122),
DelayOld(20),
MoveLR(-0.0353099),
MoveUD(0.584338),
DelayOld(20),
MoveLR(0.137242),
MoveUD(0.592181),
DelayOld(20),
MoveLR(0.30195),
DelayOld(10),
MoveLR(0.490188),
DelayOld(10),
MoveLR(0.599994),
MoveUD(0.529435),
DelayOld(30),
MoveLR(0.66274),
MoveUD(0.3961),
DelayOld(20),
MoveLR(0.670583),
MoveUD(0.231391),
MoveLR(0.68627),
MoveUD(0.0745262),
Move(0, -0.01),
DelayOld(100),
Move(0, 0),
DelayOld(1000),
Text(
bs.Lstr(resource=f'{self._r}.phrase05Text')
), # for example when you punch..
DelayOld(510),
Move(0, -0.01),
DelayOld(100),
Move(0, 0),
DelayOld(500),
SpawnSpaz(
0,
(-0.09249162673950195, 4.337906360626221, -2.3),
make_current=True,
flash=False,
),
SpawnSpaz(
1,
(-3.1, 4.3, -2.0),
make_current=False,
color=(1, 1, 0.4),
name=bs.Lstr(resource=f'{self._r}.randomName1Text'),
),
Move(-1.0, 0),
DelayOld(1050),
Move(0, -0.01),
DelayOld(100),
Move(0, 0),
DelayOld(1000),
Text(
bs.Lstr(resource=f'{self._r}.phrase06Text')
), # your damage is based
DelayOld(1200),
Move(-0.05, 0),
DelayOld(200),
Punch(),
DelayOld(800),
Punch(),
DelayOld(800),
Punch(),
DelayOld(800),
Move(0, -0.01),
DelayOld(100),
Move(0, 0),
Text(
bs.Lstr(
resource=f'{self._r}.phrase07Text',
subs=[
(
'${NAME}',
bs.Lstr(
resource=f'{self._r}.randomName1Text'
),
)
],
)
), # see that didn't hurt fred
DelayOld(2000),
Celebrate('right', spaz_num=1),
DelayOld(1400),
Text(
bs.Lstr(resource=f'{self._r}.phrase08Text')
), # lets jump and spin to get more speed
DelayOld(30),
MoveLR(0),
DelayOld(40),
MoveLR(0),
DelayOld(40),
MoveLR(0),
DelayOld(130),
MoveLR(0),
DelayOld(100),
MoveLR(0),
DelayOld(10),
MoveLR(0.0480667),
DelayOld(40),
MoveLR(0.056093),
MoveLR(0.0681173),
DelayOld(30),
MoveLR(0.0801416),
DelayOld(10),
MoveLR(0.184301),
DelayOld(10),
MoveLR(0.207831),
DelayOld(20),
MoveLR(0.231361),
DelayOld(30),
MoveLR(0.239204),
DelayOld(30),
MoveLR(0.254891),
DelayOld(40),
MoveLR(0.270577),
DelayOld(10),
MoveLR(0.30195),
DelayOld(20),
MoveLR(0.341166),
DelayOld(30),
MoveLR(0.388226),
MoveLR(0.435286),
DelayOld(30),
MoveLR(0.490188),
DelayOld(10),
MoveLR(0.560778),
DelayOld(20),
MoveLR(0.599994),
DelayOld(10),
MoveLR(0.647053),
DelayOld(10),
MoveLR(0.68627),
DelayOld(30),
MoveLR(0.733329),
DelayOld(20),
MoveLR(0.764702),
DelayOld(10),
MoveLR(0.827448),
DelayOld(20),
MoveLR(0.874508),
DelayOld(20),
MoveLR(0.929411),
DelayOld(10),
MoveLR(1),
DelayOld(830),
MoveUD(0.0274667),
DelayOld(10),
MoveLR(0.95294),
MoveUD(0.113742),
DelayOld(30),
MoveLR(0.780389),
MoveUD(0.184332),
DelayOld(10),
MoveLR(0.27842),
MoveUD(0.0745262),
DelayOld(20),
MoveLR(0),
MoveUD(0),
DelayOld(390),
MoveLR(0),
MoveLR(0),
DelayOld(20),
MoveLR(0),
DelayOld(20),
MoveLR(0),
DelayOld(10),
MoveLR(-0.0537431),
DelayOld(20),
MoveLR(-0.215705),
DelayOld(30),
MoveLR(-0.388256),
MoveLR(-0.529435),
DelayOld(30),
MoveLR(-0.694143),
DelayOld(20),
MoveLR(-0.851009),
MoveUD(0.0588397),
DelayOld(10),
MoveLR(-1),
MoveUD(0.0745262),
Run(release=False),
DelayOld(200),
MoveUD(0.0509964),
DelayOld(30),
MoveUD(0.0117801),
DelayOld(20),
MoveUD(-0.0901822),
MoveUD(-0.372539),
DelayOld(30),
MoveLR(-0.898068),
MoveUD(-0.890194),
Jump(release=False),
DelayOld(20),
MoveLR(-0.647084),
MoveUD(-1),
MoveLR(-0.427473),
DelayOld(20),
MoveLR(-0.00393689),
DelayOld(10),
MoveLR(0.537248),
DelayOld(30),
MoveLR(1),
DelayOld(50),
RunRelease(),
JumpRelease(),
DelayOld(50),
MoveUD(-0.921567),
MoveUD(-0.749016),
DelayOld(30),
MoveUD(-0.552934),
DelayOld(10),
MoveUD(-0.247047),
DelayOld(20),
MoveUD(0.200018),
DelayOld(20),
MoveUD(0.670614),
MoveUD(1),
DelayOld(70),
MoveLR(0.97647),
DelayOld(20),
MoveLR(0.764702),
DelayOld(20),
MoveLR(0.364696),
DelayOld(20),
MoveLR(0.00390637),
MoveLR(-0.309824),
DelayOld(20),
MoveLR(-0.576495),
DelayOld(30),
MoveLR(-0.898068),
DelayOld(10),
MoveLR(-1),
MoveUD(0.905911),
DelayOld(20),
MoveUD(0.498062),
DelayOld(20),
MoveUD(0.0274667),
MoveUD(-0.403912),
DelayOld(20),
MoveUD(-1),
Run(release=False),
Jump(release=False),
DelayOld(10),
Punch(release=False),
DelayOld(70),
JumpRelease(),
DelayOld(110),
MoveLR(-0.976501),
RunRelease(),
PunchRelease(),
DelayOld(10),
MoveLR(-0.952971),
DelayOld(20),
MoveLR(-0.905911),
MoveLR(-0.827479),
DelayOld(20),
MoveLR(-0.75689),
DelayOld(30),
MoveLR(-0.73336),
MoveLR(-0.694143),
DelayOld(20),
MoveLR(-0.670614),
DelayOld(30),
MoveLR(-0.66277),
DelayOld(10),
MoveUD(-0.960784),
DelayOld(20),
MoveLR(-0.623554),
MoveUD(-0.874508),
DelayOld(10),
MoveLR(-0.545122),
MoveUD(-0.694113),
DelayOld(20),
MoveLR(-0.505905),
MoveUD(-0.474502),
DelayOld(20),
MoveLR(-0.458846),
MoveUD(-0.356853),
MoveLR(-0.364727),
MoveUD(-0.27842),
DelayOld(20),
MoveLR(0.00390637),
Move(0, 0),
DelayOld(1000),
Text(
bs.Lstr(resource=f'{self._r}.phrase09Text')
), # ah that's better
DelayOld(1900),
AnalyticsScreen('Tutorial Section 3'),
Text(
bs.Lstr(resource=f'{self._r}.phrase10Text')
), # running also helps
DelayOld(100),
SpawnSpaz(
0, (-3.2, 4.3, -4.4), make_current=True, flash=False
),
SpawnSpaz(
1,
(3.3, 4.2, -5.8),
make_current=False,
color=(0.9, 0.5, 1.0),
name=bs.Lstr(resource=f'{self._r}.randomName2Text'),
),
DelayOld(1800),
Text(
bs.Lstr(resource=f'{self._r}.phrase11Text')
), # hold ANY button to run
DelayOld(300),
MoveUD(0),
DelayOld(20),
MoveUD(-0.0520646),
DelayOld(20),
MoveLR(0),
MoveUD(-0.223518),
Run(release=False),
Jump(release=False),
DelayOld(10),
MoveLR(0.0980255),
MoveUD(-0.309793),
DelayOld(30),
MoveLR(0.160772),
MoveUD(-0.427442),
DelayOld(20),
MoveLR(0.231361),
MoveUD(-0.545091),
DelayOld(10),
MoveLR(0.317637),
MoveUD(-0.678426),
DelayOld(20),
MoveLR(0.396069),
MoveUD(-0.819605),
MoveLR(0.482345),
MoveUD(-0.913724),
DelayOld(20),
MoveLR(0.560778),
MoveUD(-1),
DelayOld(20),
MoveLR(0.607837),
DelayOld(10),
MoveLR(0.623524),
DelayOld(30),
MoveLR(0.647053),
DelayOld(20),
MoveLR(0.670583),
MoveLR(0.694113),
DelayOld(30),
MoveLR(0.733329),
DelayOld(20),
MoveLR(0.764702),
MoveLR(0.788232),
DelayOld(20),
MoveLR(0.827448),
DelayOld(10),
MoveLR(0.858821),
DelayOld(20),
MoveLR(0.921567),
DelayOld(30),
MoveLR(0.97647),
MoveLR(1),
DelayOld(130),
MoveUD(-0.960784),
DelayOld(20),
MoveUD(-0.921567),
DelayOld(30),
MoveUD(-0.866665),
MoveUD(-0.819605),
DelayOld(30),
MoveUD(-0.772546),
MoveUD(-0.725486),
DelayOld(30),
MoveUD(-0.631367),
DelayOld(10),
MoveUD(-0.552934),
DelayOld(20),
MoveUD(-0.474502),
DelayOld(10),
MoveUD(-0.403912),
DelayOld(30),
MoveUD(-0.356853),
DelayOld(30),
MoveUD(-0.34901),
DelayOld(20),
MoveUD(-0.333323),
DelayOld(20),
MoveUD(-0.32548),
DelayOld(10),
MoveUD(-0.30195),
DelayOld(20),
MoveUD(-0.27842),
DelayOld(30),
MoveUD(-0.254891),
MoveUD(-0.231361),
DelayOld(30),
MoveUD(-0.207831),
DelayOld(20),
MoveUD(-0.199988),
MoveUD(-0.176458),
DelayOld(30),
MoveUD(-0.137242),
MoveUD(-0.0823389),
DelayOld(20),
MoveUD(-0.0274361),
DelayOld(20),
MoveUD(0.00393689),
DelayOld(40),
MoveUD(0.0353099),
DelayOld(20),
MoveUD(0.113742),
DelayOld(10),
MoveUD(0.137272),
DelayOld(20),
MoveUD(0.160802),
MoveUD(0.184332),
DelayOld(20),
MoveUD(0.207862),
DelayOld(30),
MoveUD(0.247078),
MoveUD(0.262764),
DelayOld(20),
MoveUD(0.270608),
DelayOld(30),
MoveUD(0.294137),
MoveUD(0.32551),
DelayOld(30),
MoveUD(0.37257),
Celebrate('left', 1),
DelayOld(20),
MoveUD(0.498062),
MoveUD(0.560808),
DelayOld(30),
MoveUD(0.654927),
MoveUD(0.694143),
DelayOld(30),
MoveUD(0.741203),
DelayOld(20),
MoveUD(0.780419),
MoveUD(0.819636),
DelayOld(20),
MoveUD(0.843165),
DelayOld(20),
MoveUD(0.882382),
DelayOld(10),
MoveUD(0.913755),
DelayOld(30),
MoveUD(0.968657),
MoveUD(1),
DelayOld(560),
Punch(release=False),
DelayOld(210),
MoveUD(0.968657),
DelayOld(30),
MoveUD(0.75689),
PunchRelease(),
DelayOld(20),
MoveLR(0.95294),
MoveUD(0.435316),
RunRelease(),
JumpRelease(),
MoveLR(0.811762),
MoveUD(0.270608),
DelayOld(20),
MoveLR(0.670583),
MoveUD(0.160802),
DelayOld(20),
MoveLR(0.466659),
MoveUD(0.0588397),
DelayOld(10),
MoveLR(0.317637),
MoveUD(-0.00390637),
DelayOld(20),
MoveLR(0.0801416),
DelayOld(10),
MoveLR(0),
DelayOld(20),
MoveLR(0),
DelayOld(30),
MoveLR(0),
DelayOld(30),
MoveLR(0),
DelayOld(20),
MoveLR(0),
DelayOld(100),
MoveLR(0),
DelayOld(30),
MoveUD(0),
DelayOld(30),
MoveUD(0),
DelayOld(50),
MoveUD(0),
MoveUD(0),
DelayOld(30),
MoveLR(0),
MoveUD(-0.0520646),
MoveLR(0),
MoveUD(-0.0640889),
DelayOld(20),
MoveLR(0),
MoveUD(-0.0881375),
DelayOld(30),
MoveLR(-0.0498978),
MoveUD(-0.199988),
MoveLR(-0.121586),
MoveUD(-0.207831),
DelayOld(20),
MoveLR(-0.145116),
MoveUD(-0.223518),
DelayOld(30),
MoveLR(-0.152959),
MoveUD(-0.231361),
MoveLR(-0.192175),
MoveUD(-0.262734),
DelayOld(30),
MoveLR(-0.200018),
MoveUD(-0.27842),
DelayOld(20),
MoveLR(-0.239235),
MoveUD(-0.30195),
MoveUD(-0.309793),
DelayOld(40),
MoveUD(-0.333323),
DelayOld(10),
MoveUD(-0.34901),
DelayOld(30),
MoveUD(-0.372539),
MoveUD(-0.396069),
DelayOld(20),
MoveUD(-0.443129),
DelayOld(20),
MoveUD(-0.458815),
DelayOld(10),
MoveUD(-0.474502),
DelayOld(50),
MoveUD(-0.482345),
DelayOld(30),
MoveLR(-0.215705),
DelayOld(30),
MoveLR(-0.200018),
DelayOld(10),
MoveLR(-0.192175),
DelayOld(10),
MoveLR(-0.176489),
DelayOld(30),
MoveLR(-0.152959),
DelayOld(20),
MoveLR(-0.145116),
MoveLR(-0.121586),
MoveUD(-0.458815),
DelayOld(30),
MoveLR(-0.098056),
MoveUD(-0.419599),
DelayOld(10),
MoveLR(-0.0745262),
MoveUD(-0.333323),
DelayOld(10),
MoveLR(0.00390637),
MoveUD(0),
DelayOld(990),
MoveLR(0),
DelayOld(660),
MoveUD(0),
AnalyticsScreen('Tutorial Section 4'),
Text(
bs.Lstr(resource=f'{self._r}.phrase12Text')
), # for extra-awesome punches,...
DelayOld(200),
SpawnSpaz(
0,
(
2.368781805038452,
4.337533950805664,
-4.360159873962402,
),
make_current=True,
flash=False,
),
SpawnSpaz(
1,
(-3.2, 4.3, -4.5),
make_current=False,
color=(1.0, 0.7, 0.3),
# name=R.randomName3Text),
name=bs.Lstr(resource=f'{self._r}.randomName3Text'),
),
DelayOld(100),
Powerup(1, (2.5, 0.0, 0), relative_to=0),
Move(1, 0),
DelayOld(1700),
Move(0, -0.1),
DelayOld(100),
Move(0, 0),
DelayOld(500),
DelayOld(320),
MoveLR(0),
DelayOld(20),
MoveLR(0),
DelayOld(10),
MoveLR(0),
DelayOld(20),
MoveLR(-0.333354),
MoveLR(-0.592181),
DelayOld(20),
MoveLR(-0.788263),
DelayOld(20),
MoveLR(-1),
MoveUD(0.0353099),
MoveUD(0.0588397),
DelayOld(10),
Run(release=False),
DelayOld(780),
MoveUD(0.0274667),
MoveUD(0.00393689),
DelayOld(10),
MoveUD(-0.00390637),
DelayOld(440),
MoveUD(0.0353099),
DelayOld(20),
MoveUD(0.0588397),
DelayOld(10),
MoveUD(0.0902127),
DelayOld(260),
MoveUD(0.0353099),
DelayOld(30),
MoveUD(0.00393689),
DelayOld(10),
MoveUD(-0.00390637),
MoveUD(-0.0274361),
Celebrate('left', 1),
DelayOld(10),
MoveUD(-0.0823389),
DelayOld(30),
MoveUD(-0.176458),
MoveUD(-0.286264),
DelayOld(20),
MoveUD(-0.498032),
Jump(release=False),
MoveUD(-0.764702),
DelayOld(30),
MoveLR(-0.858852),
MoveUD(-1),
MoveLR(-0.780419),
DelayOld(20),
MoveLR(-0.717673),
DelayOld(10),
MoveLR(-0.552965),
DelayOld(10),
MoveLR(-0.341197),
DelayOld(10),
MoveLR(-0.0274667),
DelayOld(10),
MoveLR(0.27842),
DelayOld(20),
MoveLR(0.811762),
MoveLR(1),
RunRelease(),
JumpRelease(),
DelayOld(260),
MoveLR(0.95294),
DelayOld(30),
MoveLR(0.756859),
DelayOld(10),
MoveLR(0.317637),
MoveLR(-0.00393689),
DelayOld(10),
MoveLR(-0.341197),
DelayOld(10),
MoveLR(-0.647084),
MoveUD(-0.921567),
DelayOld(10),
MoveLR(-1),
MoveUD(-0.599994),
MoveUD(-0.474502),
DelayOld(10),
MoveUD(-0.309793),
DelayOld(10),
MoveUD(-0.160772),
MoveUD(-0.0352794),
Delay(10),
MoveUD(0.176489),
Delay(10),
MoveUD(0.607868),
Run(release=False),
Jump(release=False),
DelayOld(20),
MoveUD(1),
DelayOld(30),
MoveLR(-0.921598),
DelayOld(10),
Punch(release=False),
MoveLR(-0.639241),
DelayOld(10),
MoveLR(-0.223548),
DelayOld(10),
MoveLR(0.254891),
DelayOld(10),
MoveLR(0.741172),
MoveLR(1),
DelayOld(40),
JumpRelease(),
DelayOld(40),
MoveUD(0.976501),
DelayOld(10),
MoveUD(0.73336),
DelayOld(10),
MoveUD(0.309824),
DelayOld(20),
MoveUD(-0.184301),
DelayOld(20),
MoveUD(-0.811762),
MoveUD(-1),
KillSpaz(1, explode=True),
DelayOld(10),
RunRelease(),
PunchRelease(),
DelayOld(110),
MoveLR(0.97647),
MoveLR(0.898038),
DelayOld(20),
MoveLR(0.788232),
DelayOld(20),
MoveLR(0.670583),
DelayOld(10),
MoveLR(0.505875),
DelayOld(10),
MoveLR(0.32548),
DelayOld(20),
MoveLR(0.137242),
DelayOld(10),
MoveLR(-0.00393689),
DelayOld(10),
MoveLR(-0.215705),
MoveLR(-0.356883),
DelayOld(20),
MoveLR(-0.451003),
DelayOld(10),
MoveLR(-0.552965),
DelayOld(20),
MoveLR(-0.670614),
MoveLR(-0.780419),
DelayOld(10),
MoveLR(-0.898068),
DelayOld(20),
MoveLR(-1),
DelayOld(370),
MoveLR(-0.976501),
DelayOld(10),
MoveLR(-0.952971),
DelayOld(10),
MoveLR(-0.929441),
MoveLR(-0.898068),
DelayOld(30),
MoveLR(-0.874538),
DelayOld(10),
MoveLR(-0.851009),
DelayOld(10),
MoveLR(-0.835322),
MoveUD(-0.968627),
DelayOld(10),
MoveLR(-0.827479),
MoveUD(-0.960784),
DelayOld(20),
MoveUD(-0.945097),
DelayOld(70),
MoveUD(-0.937254),
DelayOld(20),
MoveUD(-0.913724),
DelayOld(20),
MoveUD(-0.890194),
MoveLR(-0.780419),
MoveUD(-0.827448),
DelayOld(20),
MoveLR(0.317637),
MoveUD(0.3961),
MoveLR(0.0195929),
MoveUD(0.056093),
DelayOld(20),
MoveUD(0),
DelayOld(750),
MoveLR(0),
Text(
bs.Lstr(
resource=f'{self._r}.phrase13Text',
subs=[
(
'${NAME}',
bs.Lstr(
resource=f'{self._r}.randomName3Text'
),
)
],
)
), # whoops sorry bill
RemoveGloves(),
DelayOld(2000),
AnalyticsScreen('Tutorial Section 5'),
Text(
bs.Lstr(
resource=f'{self._r}.phrase14Text',
subs=[
(
'${NAME}',
bs.Lstr(
resource=f'{self._r}.randomName4Text'
),
)
],
)
), # you can pick up and throw things such as chuck here
SpawnSpaz(
0,
(-4.0, 4.3, -2.5),
make_current=True,
flash=False,
angle=90,
),
SpawnSpaz(
1,
(5, 0, -1.0),
relative_to=0,
make_current=False,
color=(0.4, 1.0, 0.7),
name=bs.Lstr(resource=f'{self._r}.randomName4Text'),
),
DelayOld(1000),
Celebrate('left', 1, duration=1000),
Move(1, 0.2),
DelayOld(2000),
PickUp(),
DelayOld(200),
Move(0.5, 1.0),
DelayOld(1200),
PickUp(),
Move(0, 0),
DelayOld(1000),
Celebrate('left'),
DelayOld(1500),
Move(0, -1.0),
DelayOld(800),
Move(0, 0),
DelayOld(800),
SpawnSpaz(
0,
(1.5, 4.3, -4.0),
make_current=True,
flash=False,
angle=0,
),
AnalyticsScreen('Tutorial Section 6'),
Text(
bs.Lstr(resource=f'{self._r}.phrase15Text')
), # lastly there's bombs
DelayOld(1900),
Text(
bs.Lstr(resource=f'{self._r}.phrase16Text')
), # throwing bombs takes practice
DelayOld(2000),
Bomb(),
Move(-0.1, -0.1),
DelayOld(100),
Move(0, 0),
DelayOld(500),
DelayOld(1000),
Bomb(),
DelayOld(2000),
Text(
bs.Lstr(resource=f'{self._r}.phrase17Text')
), # not a very good throw
DelayOld(3000),
Text(
bs.Lstr(resource=f'{self._r}.phrase18Text')
), # moving helps you get distance
DelayOld(1000),
Bomb(),
DelayOld(500),
Move(-0.3, 0),
DelayOld(100),
Move(-0.6, 0),
DelayOld(100),
Move(-1, 0),
DelayOld(800),
Bomb(),
DelayOld(400),
Move(0, -0.1),
DelayOld(100),
Move(0, 0),
DelayOld(2500),
Text(
bs.Lstr(resource=f'{self._r}.phrase19Text')
), # jumping helps you get height
DelayOld(2000),
Bomb(),
DelayOld(500),
Move(1, 0),
DelayOld(300),
Jump(release_delay=250),
DelayOld(500),
Jump(release_delay=250),
DelayOld(550),
Jump(release_delay=250),
DelayOld(160),
Punch(),
DelayOld(500),
Move(0, -0.1),
DelayOld(100),
Move(0, 0),
DelayOld(2000),
Text(
bs.Lstr(resource=f'{self._r}.phrase20Text')
), # whiplash your bombs
DelayOld(1000),
Bomb(release=False),
DelayOld2(80),
RunRelease(),
BombRelease(),
DelayOld2(620),
MoveLR(0),
DelayOld2(10),
MoveLR(0),
DelayOld2(40),
MoveLR(0),
DelayOld2(10),
MoveLR(-0.0537431),
MoveUD(0),
DelayOld2(20),
MoveLR(-0.262764),
DelayOld2(20),
MoveLR(-0.498062),
DelayOld2(10),
MoveLR(-0.639241),
DelayOld2(20),
MoveLR(-0.73336),
DelayOld2(10),
MoveLR(-0.843165),
MoveUD(-0.0352794),
DelayOld2(30),
MoveLR(-1),
DelayOld2(10),
MoveUD(-0.0588092),
DelayOld2(10),
MoveUD(-0.160772),
DelayOld2(20),
MoveUD(-0.286264),
DelayOld2(20),
MoveUD(-0.427442),
DelayOld2(10),
MoveUD(-0.623524),
DelayOld2(20),
MoveUD(-0.843135),
DelayOld2(10),
MoveUD(-1),
DelayOld2(40),
MoveLR(-0.890225),
DelayOld2(10),
MoveLR(-0.670614),
DelayOld2(20),
MoveLR(-0.435316),
DelayOld2(20),
MoveLR(-0.184332),
DelayOld2(10),
MoveLR(0.00390637),
DelayOld2(20),
MoveLR(0.223518),
DelayOld2(10),
MoveLR(0.388226),
DelayOld2(20),
MoveLR(0.560778),
DelayOld2(20),
MoveLR(0.717643),
DelayOld2(10),
MoveLR(0.890194),
DelayOld2(20),
MoveLR(1),
DelayOld2(30),
MoveUD(-0.968627),
DelayOld2(20),
MoveUD(-0.898038),
DelayOld2(10),
MoveUD(-0.741172),
DelayOld2(20),
MoveUD(-0.498032),
DelayOld2(20),
MoveUD(-0.247047),
DelayOld2(10),
MoveUD(0.00393689),
DelayOld2(20),
MoveUD(0.239235),
DelayOld2(20),
MoveUD(0.458846),
DelayOld2(10),
MoveUD(0.70983),
DelayOld2(30),
MoveUD(1),
DelayOld2(10),
MoveLR(0.827448),
DelayOld2(10),
MoveLR(0.678426),
DelayOld2(20),
MoveLR(0.396069),
DelayOld2(10),
MoveLR(0.0980255),
DelayOld2(20),
MoveLR(-0.160802),
DelayOld2(20),
MoveLR(-0.388256),
DelayOld2(10),
MoveLR(-0.545122),
DelayOld2(30),
MoveLR(-0.73336),
DelayOld2(10),
MoveLR(-0.945128),
DelayOld2(10),
MoveLR(-1),
DelayOld2(50),
MoveUD(0.960814),
DelayOld2(20),
MoveUD(0.890225),
DelayOld2(10),
MoveUD(0.749046),
DelayOld2(20),
MoveUD(0.623554),
DelayOld2(20),
MoveUD(0.498062),
DelayOld2(10),
MoveUD(0.34904),
DelayOld2(20),
MoveUD(0.239235),
DelayOld2(20),
MoveUD(0.137272),
DelayOld2(10),
MoveUD(0.0117801),
DelayOld2(20),
MoveUD(-0.0117496),
DelayOld2(10),
MoveUD(-0.0274361),
DelayOld2(90),
MoveUD(-0.0352794),
Run(release=False),
Jump(release=False),
Delay(80),
Punch(release=False),
DelayOld2(60),
MoveLR(-0.968657),
DelayOld2(20),
MoveLR(-0.835322),
DelayOld2(10),
MoveLR(-0.70983),
JumpRelease(),
DelayOld2(30),
MoveLR(-0.592181),
MoveUD(-0.0588092),
DelayOld2(10),
MoveLR(-0.490219),
MoveUD(-0.0744957),
DelayOld2(10),
MoveLR(-0.41963),
DelayOld2(20),
MoveLR(0),
MoveUD(0),
DelayOld2(20),
MoveUD(0),
PunchRelease(),
RunRelease(),
DelayOld(500),
Move(0, -0.1),
DelayOld(100),
Move(0, 0),
DelayOld(2000),
AnalyticsScreen('Tutorial Section 7'),
Text(
bs.Lstr(resource=f'{self._r}.phrase21Text')
), # timing your bombs can be tricky
Move(-1, 0),
DelayOld(1000),
Move(0, -0.1),
DelayOld(100),
Move(0, 0),
SpawnSpaz(
0,
(-0.7, 4.3, -3.9),
make_current=True,
flash=False,
angle=-30,
),
SpawnSpaz(
1,
(6.5, 0, -0.75),
relative_to=0,
make_current=False,
color=(0.3, 0.8, 1.0),
name=bs.Lstr(resource=f'{self._r}.randomName5Text'),
),
DelayOld2(1000),
Move(-1, 0),
DelayOld2(1800),
Bomb(),
Move(0, 0),
DelayOld2(300),
Move(1, 0),
DelayOld2(600),
Jump(),
DelayOld2(150),
Punch(),
DelayOld2(800),
Move(-1, 0),
DelayOld2(1000),
Move(0, 0),
DelayOld2(1500),
Text(bs.Lstr(resource=f'{self._r}.phrase22Text')), # dang
Delay(1500),
Text(''),
Delay(200),
Text(
bs.Lstr(resource=f'{self._r}.phrase23Text')
), # try cooking off
Delay(1500),
Bomb(),
Delay(800),
Move(1, 0.12),
Delay(1100),
Jump(),
Delay(100),
Punch(),
Delay(100),
Move(0, -0.1),
Delay(100),
Move(0, 0),
Delay(2000),
Text(
bs.Lstr(resource=f'{self._r}.phrase24Text')
), # hooray nicely cooked
Celebrate(),
DelayOld(2000),
KillSpaz(1),
Text(''),
Move(0.5, -0.5),
DelayOld(1000),
Move(0, -0.1),
DelayOld(100),
Move(0, 0),
DelayOld(1000),
AnalyticsScreen('Tutorial Section 8'),
Text(
bs.Lstr(resource=f'{self._r}.phrase25Text')
), # well that's just about it
DelayOld(2000),
Text(
bs.Lstr(resource=f'{self._r}.phrase26Text')
), # go get em tiger
DelayOld(2000),
Text(
bs.Lstr(resource=f'{self._r}.phrase27Text')
), # remember you training
DelayOld(3000),
Text(
bs.Lstr(resource=f'{self._r}.phrase28Text')
), # well maybe
DelayOld(1600),
Text(
bs.Lstr(resource=f'{self._r}.phrase29Text')
), # good luck
Celebrate('right', duration=10000),
DelayOld(1000),
AnalyticsScreen('Tutorial Complete'),
End(),
]
)
except Exception:
logging.exception('Error running tutorial.')
# If we read some, exec them.
if self._entries:
self._run_next_entry()
# Otherwise try again in a few seconds.
else:
self._read_entries_timer = bs.Timer(
3.0, bs.WeakCall(self._read_entries)
)
def _run_next_entry(self) -> None:
while self._entries:
entry = self._entries.popleft()
try:
result = entry.run(self)
except Exception:
result = None
logging.exception('Error in tutorial _run_next_entry.')
# If the entry returns an int value, set a timer;
# otherwise just keep going.
if result is not None:
self._entry_timer = bs.Timer(
result / 1000.0, bs.WeakCall(self._run_next_entry)
)
return
# Done with these entries.. start over soon.
self._read_entries_timer = bs.Timer(
1.0, bs.WeakCall(self._read_entries)
)
def _update_skip_votes(self) -> None:
count = sum(1 for player in self.players if player.pressed)
assert self._skip_count_text
self._skip_count_text.text = (
bs.Lstr(
resource=f'{self._r}.skipVoteCountText',
subs=[
('${COUNT}', str(count)),
('${TOTAL}', str(len(self.players))),
],
)
if count > 0
else ''
)
if (
count >= len(self.players)
and self.players
and not self._have_skipped
):
bs.increment_analytics_count('Tutorial skip')
bs.set_analytics_screen('Tutorial Skip')
self._have_skipped = True
bs.getsound('swish').play()
# self._skip_count_text.text = self._r.skippingText
self._skip_count_text.text = bs.Lstr(
resource=f'{self._r}.skippingText'
)
assert self._skip_text
self._skip_text.text = ''
self.end()
def _player_pressed_button(self, player: Player) -> None:
# Special case: if there's only one player, we give them a
# warning on their first press (some players were thinking the
# on-screen guide meant they were supposed to press something).
if len(self.players) == 1 and not self._issued_warning:
self._issued_warning = True
assert self._skip_text
self._skip_text.text = bs.Lstr(
resource=f'{self._r}.skipConfirmText'
)
self._skip_text.color = (1, 1, 1)
self._skip_text.scale = 1.3
incr = 50
t = incr
for _i in range(6):
bs.timer(
t / 1000.0,
bs.Call(setattr, self._skip_text, 'color', (1, 0.5, 0.1)),
)
t += incr
bs.timer(
t / 1000.0,
bs.Call(setattr, self._skip_text, 'color', (1, 1, 0)),
)
t += incr
bs.timer(6.0, bs.WeakCall(self._revert_confirm))
return
player.pressed = True
# test...
if not all(self.players):
logging.error(
'Nonexistent player in _player_pressed_button:'
' %s: we are %s',
[str(p) for p in self.players],
player,
)
self._update_skip_votes()
def _revert_confirm(self) -> None:
assert self._skip_text
self._skip_text.text = bs.Lstr(
resource=f'{self._r}.toSkipPressAnythingText'
)
self._skip_text.color = (1, 1, 1)
self._issued_warning = False
[docs]
@override
def on_player_join(self, player: Player) -> None:
super().on_player_join(player)
# We just wanna know if this player presses anything.
player.assigninput(
(
bs.InputType.JUMP_PRESS,
bs.InputType.PUNCH_PRESS,
bs.InputType.BOMB_PRESS,
bs.InputType.PICK_UP_PRESS,
),
bs.Call(self._player_pressed_button, player),
)
[docs]
@override
def on_player_leave(self, player: Player) -> None:
if not all(self.players):
logging.error(
'Nonexistent player in on_player_leave: %s: we are %s',
[str(p) for p in self.players],
player,
)
super().on_player_leave(player)
# our leaving may influence the vote total needed/etc
self._update_skip_votes()