557 lines
21 KiB
Lua
557 lines
21 KiB
Lua
RetryGame = {}
|
|
RetryGame.__index = RetryGame
|
|
|
|
local PATH = "retry_game/"
|
|
local FRAME = 1000 / 60
|
|
|
|
local SHUTTER_MS = 500 -- shutter halves leave (ease-in)
|
|
local SHUTTER_TRAVEL = 380
|
|
local MOON_IN_MS = 283
|
|
local MOON_SETTLE_MS = 84
|
|
local RULE_AT = 700
|
|
local RULE_MS = 5600 -- the two rule voice lines (2.3 s + 3.3 s)
|
|
local READY_MS = 2000
|
|
local GO_MS = 700
|
|
local COUNT_MS = 5000
|
|
local ZERO_MS = 1500 -- the "0" after time-up
|
|
local WHITE_RISE_AT, WHITE_RISE_MS = 1000, 633
|
|
local WHITE_HOLD_MS, WHITE_FALL_MS = 784, 466
|
|
local SWITCH_AT = 2400 -- scene changes under the white
|
|
local SUCCESS_MS = 1660
|
|
local FAILURE_MS = 2250
|
|
local END_HOLD_MS = 2000
|
|
local LAMP_OFF_MS = 130
|
|
local MARK_SPIN_MS = 2000
|
|
|
|
local MOON_CENTER_X, MOON_CENTER_Y = 640, 180
|
|
local BACK_SCALE = { 0.4, 0.5, 0.6, 0.7, 0.75, 0.8 }
|
|
local BIG_W = { 112, 144, 176, 240, 296, 360 }
|
|
-- light beams when the moon lights up: rest centre x, y, angle (right side mirrors), and where each sweeps in from
|
|
local BEAMS = {
|
|
{ 400, 400, 20, -130, 357, 5 }, { 260, 340, 35, -227, 305, 20 }, { 150, 250, 40, -256, 298, 30 },
|
|
}
|
|
-- fireworks over the lit moon: centre x, y and delay after the beams start (ms)
|
|
local FIREWORKS = { { 370, 100, 1167 }, { 940, 100, 1333 }, { 640, 130, 1483 } }
|
|
local ENCOURAGE = { "voice_renda" }
|
|
-- the leaf that blows across after a failure: frame, x, y (from (592, 508)), turn in degrees
|
|
local LEAF = {
|
|
{ 0, 737, -139, 0 }, { 10, 520, -33, 7.8 }, { 17, 360, 22, 9.3 }, { 30, 56, 80, 20.6 }, { 37, -77, 4, 94.3 },
|
|
{ 45, 37, -134, 177.2 }, { 47, 60, -141, 194 }, { 52, 180, -81, 265.7 }, { 61, 20, 41, 351.2 },
|
|
{ 69, -165, 86, 352.2 }, { 77, -356, 111, 357.5 }, { 89, -647, 100, 360 },
|
|
}
|
|
local TILES_X = { 0, 256, 512, 768, 1024 }
|
|
|
|
local function clamp(v, lo, hi) return math.max(lo, math.min(hi, v)) end
|
|
local function lerp(a, b, p) return a + (b - a) * p end
|
|
local function ease_out(p) return 1 - (1 - p) * (1 - p) end
|
|
local function ease_in(p) return p * p end
|
|
local function seg(t, from, len) return clamp((t - from) / len, 0, 1) end
|
|
|
|
local function snd(name, kind)
|
|
if audio.has_sound(name) then audio.play_sound(name, kind or "sound") end
|
|
end
|
|
|
|
local function T(folder, name) return tex.get_texture(PATH .. folder .. "/" .. name) end
|
|
local function draw(t, params) if t then tex.draw_texture(t, params) end end
|
|
|
|
function RetryGame.new(player_num, norma)
|
|
local self = setmetatable({}, RetryGame)
|
|
self.player = player_num or 0
|
|
self.norma = math.max(1, norma or 30)
|
|
self.hits = 0
|
|
self.lit = false
|
|
self.lit_at = nil
|
|
self.hand_pose = 0
|
|
self.hand_toggle = 0
|
|
self.hand_hit_at = -1e9
|
|
self.drum_hit_at = -1e9
|
|
self.waves = {}
|
|
self.t0 = nil
|
|
self.t = 0
|
|
self.finished = false
|
|
self.success = false
|
|
self.timeup = false
|
|
self.result_at = nil
|
|
|
|
self.sky = T("background", "sky")
|
|
self.sky_win = T("background", "sky_win")
|
|
self.stars = T("background", "stars")
|
|
self.moon = T("moon", "moon")
|
|
self.mark = T("moon", "mark")
|
|
self.face_win = T("moon", "face_win")
|
|
self.face_lose = T("moon", "face_lose")
|
|
self.wave = T("moon", "wave")
|
|
self.pole = T("stage", "pole")
|
|
self.rope_a = T("stage", "rope_a")
|
|
self.rope_b = T("stage", "rope_b")
|
|
self.yagura = T("stage", "yagura")
|
|
self.stall_octopus = T("stage", "stall_octopus")
|
|
self.stall_squid = T("stage", "stall_squid")
|
|
self.face_octopus = T("stage", "face_octopus")
|
|
self.face_squid = T("stage", "face_squid")
|
|
self.drum_body = T("drum", "body")
|
|
self.drum_hit = T("drum", "hit")
|
|
self.drum_ring = T("drum", "ring")
|
|
self.back, self.front, self.big, self.glow = {}, {}, {}, {}
|
|
for i = 0, 5 do
|
|
self.back[i] = T("lamps", "back_" .. i)
|
|
self.front[i] = T("lamps", "front_" .. i)
|
|
self.big[i] = T("lamps", "big_" .. i)
|
|
self.glow[i] = T("lamps", "glow_" .. i)
|
|
end
|
|
self.fist_line = T("hands", "fist_outline")
|
|
self.stick_line = T("hands", "stick_outline")
|
|
self.fist_fill = T("hands", "fist_fill")
|
|
self.stick_fill = T("hands", "stick_fill")
|
|
self.rule = T("text", "rule_" .. tex.language())
|
|
self.ready = T("text", "ready_" .. tex.language())
|
|
self.go = T("text", "go_" .. tex.language())
|
|
self.burst = T("text", "burst_" .. tex.language())
|
|
self.success_text = T("text", "success_" .. tex.language())
|
|
self.failure_text = T("text", "failure_" .. tex.language())
|
|
self.more_text = T("text", "more_" .. tex.language())
|
|
self.digits = T("text", "digits")
|
|
self.rays = T("effects", "rays")
|
|
self.white = T("effects", "white")
|
|
self.beam = T("effects", "beam")
|
|
self.firework = T("effects", "firework")
|
|
self.leaf = T("effects", "leaf")
|
|
local color = self.player == 2 and "blue" or "red"
|
|
self.tile = T("shutter", color .. "_tile")
|
|
self.cap = T("shutter", color .. "_cap")
|
|
|
|
self.fired = {}
|
|
self.t_rule = RULE_AT
|
|
self.t_ready = self.t_rule + RULE_MS
|
|
self.t_go = self.t_ready + READY_MS
|
|
self.t_end = self.t_go + COUNT_MS
|
|
return self
|
|
end
|
|
|
|
function RetryGame:counting()
|
|
return self.t >= self.t_go and self.t < self.t_end
|
|
end
|
|
|
|
function RetryGame:lamps_lit()
|
|
if self.result_at and not self.success then
|
|
local e = self.t - self.result_at
|
|
return math.max(0, self.lamps_at_end - math.floor(e / LAMP_OFF_MS))
|
|
end
|
|
if self.lit then return 6 end
|
|
return clamp(math.floor(self.hits * 6 / self.norma), 0, 5)
|
|
end
|
|
|
|
function RetryGame:hit()
|
|
if not self:counting() then return end
|
|
local lamps_before = self:lamps_lit()
|
|
self.hits = self.hits + 1
|
|
self.drum_hit_at = self.t
|
|
self.hand_hit_at = self.t
|
|
self.hand_pose = self.hand_toggle == 0 and 1 or 3
|
|
self.hand_toggle = 1 - self.hand_toggle
|
|
if not self.lit then
|
|
self.waves[#self.waves + 1] = self.t
|
|
if #self.waves > 8 then table.remove(self.waves, 1) end
|
|
if self.hits >= self.norma then
|
|
self.lit = true
|
|
self.lit_at = self.t
|
|
self.waves = {}
|
|
snd("moon")
|
|
snd("voice_more", "voice")
|
|
end
|
|
if self:lamps_lit() > lamps_before then snd("lamp_up") end
|
|
end
|
|
end
|
|
|
|
-- fire `fn` once, the first time `cond` holds
|
|
function RetryGame:once(key, cond, fn)
|
|
if cond and not self.fired[key] then
|
|
self.fired[key] = true
|
|
fn()
|
|
end
|
|
end
|
|
|
|
function RetryGame:sounds()
|
|
local t = self.t
|
|
self:once("appear", true, function() snd("appear") end)
|
|
self:once("about", t >= self.t_rule, function() snd("voice_about_1", "voice") end)
|
|
self:once("about2", self.fired.about and t >= self.t_rule + 100 and not audio.is_sound_playing("voice_about_1"),
|
|
function() snd("voice_about_2", "voice") end)
|
|
self:once("yoi", t >= self.t_ready, function() snd("voice_yoi", "voice") end)
|
|
self:once("go", t >= self.t_go, function()
|
|
snd("voice_don", "voice")
|
|
snd("bgm", "music")
|
|
end)
|
|
for k = 1, 4 do
|
|
self:once("cnt" .. k, t >= self.t_go + k * 1000 - FRAME, function() snd("timer_blip") end)
|
|
end
|
|
-- encouragement at equal spacing while the goal is out of reach
|
|
for i, name in ipairs(ENCOURAGE) do
|
|
local at = self.t_go + i * COUNT_MS / (#ENCOURAGE + 1)
|
|
self:once("encourage" .. i, t >= at and t < at + 500 and not self.lit, function() snd(name, "voice") end)
|
|
end
|
|
if self.lit_at and not self.timeup then
|
|
for i, f in ipairs(FIREWORKS) do
|
|
self:once("firework" .. i, t - self.lit_at >= f[3], function() snd("fireworks") end)
|
|
end
|
|
end
|
|
self:once("timeout", self.timeup, function()
|
|
if audio.is_sound_playing("bgm") then audio.stop_sound("bgm") end
|
|
snd("whistle")
|
|
end)
|
|
if self.timeup and self.success then
|
|
self:once("flash", t >= self.t_end + ZERO_MS + 650, function() snd("se_flash") end)
|
|
self:once("success", self.result_at ~= nil, function()
|
|
snd("clear")
|
|
snd("voice_clear", "voice")
|
|
end)
|
|
elseif self.timeup then
|
|
self:once("miss", true, function()
|
|
snd("fail")
|
|
snd("voice_fail", "voice")
|
|
end)
|
|
local lamps = self:lamps_lit()
|
|
if lamps < (self.last_lamps or lamps) then snd("lamp_down") end
|
|
self.last_lamps = lamps
|
|
self:once("wind", t >= self.result_at + FAILURE_MS, function() snd("wind") end)
|
|
end
|
|
end
|
|
|
|
function RetryGame:update(current_ms)
|
|
if not self.t0 then self.t0 = current_ms end
|
|
self.t = current_ms - self.t0
|
|
|
|
if not self.timeup and self.t >= self.t_end then
|
|
self.timeup = true
|
|
self.success = self.lit
|
|
self.lamps_at_end = self:lamps_lit()
|
|
if not self.success then self.result_at = self.t_end end
|
|
end
|
|
if self.timeup and self.success and not self.result_at and self.t >= self.t_end + ZERO_MS + SWITCH_AT then
|
|
self.result_at = self.t
|
|
end
|
|
if self.result_at then
|
|
local len = self.success and SUCCESS_MS or FAILURE_MS
|
|
if self.t >= self.result_at + len + END_HOLD_MS then self.finished = true end
|
|
end
|
|
self:sounds()
|
|
end
|
|
|
|
function RetryGame:is_finished() return self.finished end
|
|
function RetryGame:is_success() return self.success end
|
|
|
|
-- hands: a pose rests on wait0/wait1 and strikes on swing0/swing1 for a few frames
|
|
function RetryGame:hand_frame()
|
|
local since = self.t - self.hand_hit_at
|
|
if since < 100 then return self.hand_pose end
|
|
if self.hand_pose == 1 then return 2 end
|
|
return 0
|
|
end
|
|
|
|
function RetryGame:draw_lamp_set(set, size_of, count)
|
|
for i = 0, 5 do
|
|
local t = set[i]
|
|
if t then
|
|
local on = i < count and 1 or 0
|
|
local s = size_of(i)
|
|
for side = 0, 1 do
|
|
local params = { index = side, frame = on, scale = s }
|
|
if side == 0 then params.mirror = "horizontal" end
|
|
draw(t, params)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function RetryGame:draw_lamp_glows(count)
|
|
local pulse = 0.6 + 0.4 * math.sin(self.t / 130)
|
|
local sets = {
|
|
{ set = self.back, name = "back_", w = function(i) return 96 * BACK_SCALE[i + 1] end },
|
|
{ set = self.front, name = "front_", w = function() return 96 end },
|
|
{ set = self.big, name = "big_", w = function(i) return BIG_W[i + 1] end },
|
|
}
|
|
for _, spec in ipairs(sets) do
|
|
for i = 0, count - 1 do
|
|
local g = self.glow[i]
|
|
local info = tex.get_texture_info("retry_game/lamps", spec.name .. i)
|
|
if g and info then
|
|
local w = spec.w(i)
|
|
local px = w * 1.45
|
|
for side = 1, 2 do
|
|
draw(g, { x = info.x[side] + (w - px) / 2, y = info.y[side] + (w * 1.25 - px) / 2,
|
|
x2 = px - 256, y2 = px - 256, fade = 0.55 * pulse, blend = "additive" })
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function RetryGame:draw_shutter()
|
|
local p = ease_in(seg(self.t, 0, SHUTTER_MS))
|
|
if p >= 1 then return end
|
|
local dy = SHUTTER_TRAVEL * p
|
|
for half = 0, 1 do
|
|
local y = half == 0 and (-360 - dy) or (360 + dy)
|
|
for _, x in ipairs(TILES_X) do
|
|
draw(self.tile, { x = x, y = y + 20, fade = 0.4 })
|
|
end
|
|
for _, x in ipairs(TILES_X) do
|
|
draw(self.tile, { x = x, y = y })
|
|
draw(self.cap, { x = x, y = y + 288 })
|
|
end
|
|
end
|
|
end
|
|
|
|
function RetryGame:draw_moon()
|
|
local t = self.t
|
|
local y = 0
|
|
if t < MOON_IN_MS then
|
|
y = lerp(-560, 20, ease_out(t / MOON_IN_MS))
|
|
elseif t < MOON_IN_MS + MOON_SETTLE_MS then
|
|
y = lerp(20, 0, ease_in((t - MOON_IN_MS) / MOON_SETTLE_MS))
|
|
end
|
|
local shine = self.lit or (self.success and self.timeup)
|
|
if shine then
|
|
local px = 256 * 2.5
|
|
local pulse = 0.6 + 0.2 * math.sin(t / 160)
|
|
draw(self.glow[2], { x = MOON_CENTER_X - px / 2, y = MOON_CENTER_Y + y - px / 2, x2 = px - 256, y2 = px - 256, fade = pulse, blend = "additive" })
|
|
end
|
|
draw(self.moon, { y = y })
|
|
if shine then
|
|
draw(self.face_win, { y = y })
|
|
elseif self.timeup then
|
|
draw(self.face_lose, { y = y })
|
|
else
|
|
local angle = (t % MARK_SPIN_MS) / MARK_SPIN_MS * 360
|
|
draw(self.mark, { x = 320, y = 320 + y, rotation = angle, origin = { 320, 320 }, fade = 0.4 })
|
|
end
|
|
for _, at in ipairs(self.waves) do
|
|
local p = seg(t, at, 333)
|
|
if p < 1 then
|
|
local s = p < 0.5 and lerp(1.6, 1.04, p / 0.5) or lerp(1.04, 0.95, (p - 0.5) / 0.5)
|
|
local a = p < 0.35 and 0.3 or (p < 0.7 and lerp(0.3, 0.9, (p - 0.35) / 0.35) or lerp(0.9, 0, (p - 0.7) / 0.3))
|
|
draw(self.wave, { center = true, scale = s, y = y, fade = a, blend = "additive" })
|
|
end
|
|
end
|
|
end
|
|
|
|
function RetryGame:draw_hands()
|
|
if self.timeup then return end
|
|
local frame = self:hand_frame()
|
|
draw(self.fist_line, { frame = frame })
|
|
draw(self.stick_line, { frame = frame })
|
|
draw(self.fist_fill, { frame = frame })
|
|
draw(self.stick_fill, { frame = frame })
|
|
end
|
|
|
|
function RetryGame:draw_drum()
|
|
draw(self.drum_body)
|
|
local since = self.t - self.drum_hit_at
|
|
if since >= 0 and since < 133 then
|
|
local dip = since < 67 and lerp(0, 5, ease_out(since / 67)) or lerp(5, 0, ease_in((since - 67) / 66))
|
|
draw(self.drum_ring, { y = dip, fade = 0.4, blend = "additive" })
|
|
draw(self.drum_hit, { y = dip })
|
|
end
|
|
end
|
|
|
|
function RetryGame:draw_stalls()
|
|
draw(self.yagura, { index = 0 })
|
|
draw(self.yagura, { index = 1, mirror = "horizontal" })
|
|
if self.lit or (self.timeup and self.success) then
|
|
local pulse = 0.5 + 0.2 * math.sin(self.t / 140)
|
|
for _, x in ipairs({ 467, 537, 607, 677, 747 }) do
|
|
draw(self.glow[5], { x = x, y = 406, x2 = 64 - 256, y2 = 64 - 256, fade = pulse, blend = "additive" })
|
|
end
|
|
end
|
|
draw(self.stall_octopus)
|
|
draw(self.stall_squid)
|
|
local face = 0
|
|
if self.timeup and self.success then face = 1 elseif self.timeup then face = 2 end
|
|
draw(self.face_octopus, { frame = face })
|
|
draw(self.face_squid, { frame = face })
|
|
end
|
|
|
|
function RetryGame:draw_signal()
|
|
local t = self.t
|
|
if t >= self.t_rule and t < self.t_ready + 1 then
|
|
local e = t - self.t_rule
|
|
local s = e < 250 and lerp(0.8, 1.1, ease_out(e / 250)) or (e < 500 and lerp(1.1, 1, (e - 250) / 250) or 1)
|
|
draw(self.rule, { center = true, scale = s, fade = clamp(e / 250, 0, 1) })
|
|
elseif t >= self.t_ready and t < self.t_go then
|
|
local e = t - self.t_ready
|
|
local s = e < 317 and lerp(2, 0.95, ease_in(e / 317)) or (e < 467 and lerp(0.955, 1, (e - 317) / 150) or 1)
|
|
draw(self.ready, { center = true, scale = s, fade = e < 317 and lerp(0.2, 1, e / 317) or 1 })
|
|
elseif t >= self.t_go and t < self.t_go + GO_MS then
|
|
local e = t - self.t_go
|
|
local s, a = 1, 1
|
|
if e < 167 then s = lerp(1, 1.2, ease_out(e / 167))
|
|
elseif e < 334 then s = lerp(1.2, 1, ease_in((e - 167) / 167))
|
|
elseif e > 451 then a = lerp(0.8, 0, ease_out(clamp((e - 451) / 133, 0, 1))) end
|
|
draw(self.go, { center = true, scale = s, fade = a })
|
|
end
|
|
end
|
|
|
|
function RetryGame:draw_count()
|
|
local t = self.t
|
|
if self:counting() then
|
|
local e = t - self.t_go
|
|
local left = math.ceil((COUNT_MS - e) / 1000)
|
|
local p = (e % 1000) / 1000
|
|
local s = 0.7 + 0.3 * math.sin(math.pi * clamp(p / 0.95, 0, 1))
|
|
draw(self.digits, { frame = clamp(left, 1, 5), center = true, scale = s })
|
|
local pulse = 0.975 + 0.025 * math.sin(e / 467 * math.pi * 2)
|
|
local bs, ba = pulse, 1
|
|
if self.lit_at then
|
|
local q = t - self.lit_at
|
|
if q < 100 then bs, ba = lerp(0.6, 1, q / 100), q / 100
|
|
elseif q < 200 then bs = lerp(1, 1.2, (q - 100) / 100)
|
|
elseif q < 300 then bs = lerp(1.2, 1, (q - 200) / 100) end
|
|
end
|
|
draw(self.burst, { center = true, scale = bs, fade = ba })
|
|
elseif self.timeup and t < self.t_end + ZERO_MS then
|
|
local e = t - self.t_end
|
|
local s = e < 100 and lerp(1.3, 1.14, e / 100) or (e < 320 and lerp(1.14, 1, ease_out((e - 100) / 220)) or 1)
|
|
local a = e > 1283 and lerp(0.93, 0, (e - 1283) / 217) or 1
|
|
draw(self.digits, { frame = 0, center = true, scale = s, fade = a })
|
|
end
|
|
end
|
|
|
|
function RetryGame:draw_result()
|
|
if not self.result_at then return end
|
|
local e = self.t - self.result_at
|
|
if self.success then
|
|
local y, s = 0, 1
|
|
if e < 217 then y = lerp(-90, -40, e / 217); s = lerp(0.4, 0.7, e / 217)
|
|
elseif e < 317 then y = lerp(-40, 10, (e - 217) / 100); s = lerp(0.7, 1.1, (e - 217) / 100)
|
|
elseif e < 400 then y = lerp(10, 0, (e - 317) / 83); s = lerp(1.1, 1, (e - 317) / 83)
|
|
elseif e < 1300 then y = 0
|
|
else y = lerp(0, -90, ease_out(clamp((e - 1300) / 267, 0, 1))) end
|
|
draw(self.success_text, { center = true, scale = s, y = y, fade = clamp(e / 217, 0, 1) })
|
|
if e > 1283 then
|
|
local p = ease_out(clamp((e - 1283) / 183, 0, 1))
|
|
draw(self.more_text, { center = true, scale = lerp(1, 1.02, p), fade = p })
|
|
end
|
|
else
|
|
local y = 0
|
|
if e < 317 then y = lerp(-450, 0, ease_in(e / 317))
|
|
elseif e < 550 then y = -119.5 * ease_out(seg(e, 317, 233))
|
|
elseif e < 767 then y = lerp(-119.5, 10, ease_in(seg(e, 550, 217)))
|
|
elseif e < 950 then y = lerp(10, -20, ease_out(seg(e, 767, 183)))
|
|
elseif e < 1050 then y = lerp(-20, 0, ease_in(seg(e, 950, 100)))
|
|
end
|
|
draw(self.failure_text, { y = y, fade = clamp(e / 317, 0, 1) })
|
|
self:draw_leaf(e - FAILURE_MS)
|
|
end
|
|
end
|
|
|
|
-- side rays flash over the success scene
|
|
function RetryGame:draw_rays()
|
|
if not (self.timeup and self.success and self.result_at) then return end
|
|
local e = self.t - self.result_at
|
|
local a = clamp(e / 333, 0, 1) * 0.5
|
|
local frame = math.floor(e / (5 * FRAME)) % 3
|
|
draw(self.rays, { index = 0, frame = frame, fade = a, blend = "additive" })
|
|
draw(self.rays, { index = 1, frame = frame, fade = a, blend = "additive", mirror = "horizontal" })
|
|
end
|
|
|
|
-- beams and fireworks play while the moon is lit and the clock still runs
|
|
function RetryGame:draw_lit_effects()
|
|
if not self.lit_at or self.timeup then return end
|
|
local e = self.t - self.lit_at
|
|
for _, b in ipairs(BEAMS) do
|
|
local p = ease_out(seg(e, 233, 560))
|
|
local sy = lerp(0.1, 1, ease_out(seg(e, 233, 300)))
|
|
if e >= 233 then
|
|
for side = -1, 1, 2 do
|
|
local x = side == -1 and lerp(b[1] + b[4], b[1], p) or lerp(1280 - b[1] - b[4], 1280 - b[1], p)
|
|
local y = lerp(b[2] + b[5], b[2], p)
|
|
local ang = lerp(b[6], b[3], p) * (side == -1 and 1 or -1)
|
|
draw(self.beam, { x = x, y = y, y2 = 840 * sy - 840, origin = { 60, 420 * sy }, rotation = ang, fade = 0.7 })
|
|
end
|
|
end
|
|
end
|
|
for _, f in ipairs(FIREWORKS) do
|
|
local fe = e - f[3]
|
|
if fe >= 0 and fe < 1000 then
|
|
local s = fe < 450 and lerp(0.001, 1, ease_out(fe / 450)) or lerp(1, 1.1, (fe - 450) / 550)
|
|
local a = fe < 500 and 1 or lerp(1, 0, (fe - 500) / 483)
|
|
draw(self.firework, { x = f[1] - 200, y = f[2] - 200, center = true, scale = s, fade = clamp(a, 0, 1), blend = "additive" })
|
|
end
|
|
end
|
|
end
|
|
|
|
function RetryGame:draw_leaf(e)
|
|
local f = e / FRAME
|
|
local last = LEAF[#LEAF]
|
|
if f < 0 or f > last[1] then return end
|
|
for i = 2, #LEAF do
|
|
local a, b = LEAF[i - 1], LEAF[i]
|
|
if f <= b[1] then
|
|
local p = (f - a[1]) / (b[1] - a[1])
|
|
draw(self.leaf, { x = 592 + lerp(a[2], b[2], p), y = 508 + lerp(a[3], b[3], p), origin = { 48, 32 }, rotation = lerp(a[4], b[4], p) })
|
|
return
|
|
end
|
|
end
|
|
end
|
|
|
|
function RetryGame:draw_white()
|
|
if not self.timeup or not self.success then return end
|
|
local e = self.t - (self.t_end + ZERO_MS)
|
|
local a = 0
|
|
if e >= WHITE_RISE_AT then
|
|
if e < WHITE_RISE_AT + WHITE_RISE_MS then a = (e - WHITE_RISE_AT) / WHITE_RISE_MS
|
|
elseif e < WHITE_RISE_AT + WHITE_RISE_MS + WHITE_HOLD_MS then a = 1
|
|
else a = 1 - seg(e, WHITE_RISE_AT + WHITE_RISE_MS + WHITE_HOLD_MS, WHITE_FALL_MS) end
|
|
end
|
|
if a > 0 then draw(self.white, { fade = a }) end
|
|
end
|
|
|
|
local CHARA_X, CHARA_Y, CHARA_SCALE = 640, 330, 1.0
|
|
|
|
function RetryGame:chara_pos() return { CHARA_X, CHARA_Y, CHARA_SCALE } end
|
|
|
|
function RetryGame:phase()
|
|
if not self.timeup then return self:counting() and "count" or "wait" end
|
|
return self.success and "success" or "failure"
|
|
end
|
|
|
|
function RetryGame:draw_back()
|
|
-- the success scene (daylight sky, happy faces) replaces the night one under the white
|
|
local e_end = self.t - (self.t_end + ZERO_MS)
|
|
local switched = self.timeup and self.success and e_end >= SWITCH_AT
|
|
draw(switched and self.sky_win or self.sky)
|
|
draw(self.stars, { fade = 0.4, blend = "additive" })
|
|
self:draw_moon()
|
|
self:draw_lit_effects() -- behind the stage, over the moon
|
|
draw(self.pole, { index = 0 })
|
|
draw(self.pole, { index = 1, mirror = "horizontal" })
|
|
draw(self.rope_a, { index = 0 })
|
|
draw(self.rope_a, { index = 1, mirror = "horizontal" })
|
|
draw(self.rope_b, { index = 0 })
|
|
draw(self.rope_b, { index = 1, mirror = "horizontal" })
|
|
|
|
local count = self:lamps_lit()
|
|
self:draw_lamp_set(self.back, function(i) return BACK_SCALE[i + 1] end, count)
|
|
self:draw_lamp_set(self.front, function() return 1 end, count)
|
|
end
|
|
|
|
function RetryGame:draw_front()
|
|
local count = self:lamps_lit()
|
|
self:draw_hands()
|
|
self:draw_drum()
|
|
self:draw_stalls()
|
|
self:draw_lamp_set(self.big, function() return 1 end, count)
|
|
self:draw_lamp_glows(count)
|
|
self:draw_rays()
|
|
self:draw_signal()
|
|
self:draw_count()
|
|
self:draw_white()
|
|
self:draw_result()
|
|
self:draw_shutter()
|
|
end
|
|
|
|
function RetryGame:draw()
|
|
self:draw_back()
|
|
self:draw_front()
|
|
end
|