423 lines
18 KiB
Lua
423 lines
18 KiB
Lua
-- YataiDON-HSS child override of PyTaikoGreen/Scripts/background/background.lua:
|
|
-- byte-copy of the parent plus the ArcadeGauge overlay (3 hooks, marked below).
|
|
local DonBG = require("background/bg_objects/donbg")
|
|
local BGNormal = require("background/bg_objects/bg_normal")
|
|
local BGFever = require("background/bg_objects/bg_fever")
|
|
local Fever = require("background/bg_objects/fever")
|
|
local Footer = require("background/bg_objects/footer")
|
|
local Dancer = require("background/bg_objects/dancer")
|
|
local Ending = require("background/bg_objects/ending")
|
|
local RankBadge = require("background/bg_objects/rank_badge")
|
|
local RendaController = require("background/bg_objects/renda")
|
|
local ChibiController = require("background/bg_objects/chibi")
|
|
local ArcadeGauge = require("background/bg_objects/arcade_gauge") -- child-only
|
|
local EnsoSkip = require("background/bg_objects/enso_skip") -- child-only
|
|
|
|
local PracticeBackground = require("background/bg_collabs/practice")
|
|
local TutorialBackground = require("background/bg_collabs/tutorial")
|
|
local A3Background = require("background/bg_collabs/a3")
|
|
local AnimalBackground = require("background/bg_collabs/animal")
|
|
local ButtoBackground = require("background/bg_collabs/buttoburst")
|
|
local DanBackground = require("background/bg_collabs/dan")
|
|
local FunassyiBackground = require("background/bg_collabs/funassyi")
|
|
local ImasBackground = require("background/bg_collabs/imas")
|
|
local ImasSidemBackground = require("background/bg_collabs/imas_sidem")
|
|
local OshiriBackground = require("background/bg_collabs/oshiri")
|
|
-- the collabs PyTaikoGreen added after this file was forked (2026-09-07/09): Green's modules
|
|
local DQ10Background = require("background/bg_collabs/dq10")
|
|
local GbBackground = require("background/bg_collabs/gb")
|
|
local GmtBackground = require("background/bg_collabs/gmt")
|
|
local GumiBackground = require("background/bg_collabs/gumi")
|
|
local I7id7Background = require("background/bg_collabs/i7id7")
|
|
local I7natsuBackground = require("background/bg_collabs/i7natsu")
|
|
local I7revBackground = require("background/bg_collabs/i7rev")
|
|
local I7triBackground = require("background/bg_collabs/i7tri")
|
|
local IaBackground = require("background/bg_collabs/ia")
|
|
local KobayashiBackground = require("background/bg_collabs/kobayashi")
|
|
local KumamonBackground = require("background/bg_collabs/kumamon")
|
|
local LoveliveBackground = require("background/bg_collabs/lovelive")
|
|
local MarioBackground = require("background/bg_collabs/mario")
|
|
local Mh3GBackground = require("background/bg_collabs/mh3G")
|
|
local Mh3rdBackground = require("background/bg_collabs/mh3rd")
|
|
local MikuBackground = require("background/bg_collabs/miku")
|
|
local MomocloBackground = require("background/bg_collabs/momoclo")
|
|
local PzdBackground = require("background/bg_collabs/pzd")
|
|
local TohoBackground = require("background/bg_collabs/toho")
|
|
local TonkatsudjBackground = require("background/bg_collabs/tonkatsudj")
|
|
local ToukenBackground = require("background/bg_collabs/touken")
|
|
local YmckBackground = require("background/bg_collabs/ymck")
|
|
local YokaiBackground = require("background/bg_collabs/yokai")
|
|
local YokaiHatsukoiBackground = require("background/bg_collabs/yokai_hatsukoi")
|
|
local YokaiMatsuriBackground = require("background/bg_collabs/yokai_matsuri")
|
|
local YokaiTokorotenBackground = require("background/bg_collabs/yokai_tokoroten")
|
|
local YokaiYougotaBackground = require("background/bg_collabs/yokai_yougota")
|
|
|
|
local COLLABS = {
|
|
PRACTICE = { PracticeBackground.new, nil, 1 },
|
|
TUTORIAL = { TutorialBackground.new, nil, 1 },
|
|
A3 = { A3Background.new, "background/collab/a3", 4 },
|
|
ANIMAL = { AnimalBackground.new, "background/collab/animal", 5 },
|
|
BUTTOBURST = { ButtoBackground.new, "background/collab/buttoburst", 4 },
|
|
DAN = { DanBackground.new, "background/collab/dan", 1 },
|
|
FUNASSYI = { FunassyiBackground.new, "background/collab/funassyi", 5 },
|
|
IMAS = { ImasBackground.new, "background/collab/imas", 5 },
|
|
IMAS_CG = { ImasBackground.new, "background/collab/imas_cg", 3 },
|
|
IMAS_ML = { ImasBackground.new, "background/collab/imas_ml", 3 },
|
|
IMAS_SIDEM = { ImasSidemBackground.new, "background/collab/imas_sidem", 3 },
|
|
OSHIRI = { OshiriBackground.new, "background/collab/oshiri", 5 },
|
|
DQ10 = { DQ10Background.new, "background/collab/dq10", 5 },
|
|
GB = { GbBackground.new, "background/collab/gb", 5 },
|
|
GMT = { GmtBackground.new, "background/collab/gmt", 5 },
|
|
GUMI = { GumiBackground.new, "background/collab/gumi", 5 },
|
|
I7ID7 = { I7id7Background.new, "background/collab/i7id7", 5 },
|
|
I7NATSU = { I7natsuBackground.new, "background/collab/i7natsu", 5 },
|
|
I7REV = { I7revBackground.new, "background/collab/i7rev", 5 },
|
|
I7TRI = { I7triBackground.new, "background/collab/i7tri", 5 },
|
|
IA = { IaBackground.new, "background/collab/ia", 5 },
|
|
KOBAYASHI = { KobayashiBackground.new, "background/collab/kobayashi", 5 },
|
|
KUMAMON = { KumamonBackground.new, "background/collab/kumamon", 5 },
|
|
LOVELIVE = { LoveliveBackground.new, "background/collab/lovelive", 5 },
|
|
MARIO = { MarioBackground.new, "background/collab/mario", 5 },
|
|
MH3G = { Mh3GBackground.new, "background/collab/mh3G", 5 },
|
|
MH3RD = { Mh3rdBackground.new, "background/collab/mh3rd", 5 },
|
|
MIKU = { MikuBackground.new, "background/collab/miku", 5 },
|
|
MOMOCLO = { MomocloBackground.new, "background/collab/momoclo", 5 },
|
|
PZD = { PzdBackground.new, "background/collab/pzd", 5 },
|
|
TOHO = { TohoBackground.new, "background/collab/toho", 5 },
|
|
TONKATSUDJ = { TonkatsudjBackground.new, "background/collab/tonkatsudj", 1 },
|
|
TOUKEN = { ToukenBackground.new, "background/collab/touken", 5 },
|
|
YMCK = { YmckBackground.new, "background/collab/ymck", 5 },
|
|
YOKAI = { YokaiBackground.new, "background/collab/yokai", 5 },
|
|
YOKAI_HATSUKOI = { YokaiHatsukoiBackground.new, "background/collab/yokai_hatsukoi", 5 },
|
|
YOKAI_MATSURI = { YokaiMatsuriBackground.new, "background/collab/yokai_matsuri", 5 },
|
|
YOKAI_TOKOROTEN = { YokaiTokorotenBackground.new, "background/collab/yokai_tokoroten", 5 },
|
|
YOKAI_YOUGOTA = { YokaiYougotaBackground.new, "background/collab/yokai_yougota", 5 },
|
|
}
|
|
|
|
Background = {}
|
|
Background.__index = Background
|
|
|
|
function Background.new(player_num, bpm, scene_preset)
|
|
local self = setmetatable({}, Background)
|
|
|
|
self.player_num = player_num
|
|
self.bpm = bpm
|
|
self.scene_preset = scene_preset
|
|
self.is_2p = (player_num == 3) -- PlayerNum::TWO_PLAYER
|
|
|
|
-- child-only: arcade soul-gauge overlay, drawn in draw_fore (in front of the HUD)
|
|
self.arcade_gauge = ArcadeGauge.new()
|
|
|
|
-- child-only: 演奏スキップ countdown card / dim / banner.
|
|
self.enso_skip = EnsoSkip.new()
|
|
_G.g_active_enso_skip = self.enso_skip
|
|
|
|
local entry = (not self.is_2p) and scene_preset and COLLABS[scene_preset] or nil
|
|
if entry then
|
|
local ctor, path, max_dancers = entry[1], entry[2], entry[3]
|
|
if path then
|
|
self.collab = ctor(player_num, bpm, path, max_dancers)
|
|
else
|
|
self.collab = ctor(player_num, bpm)
|
|
end
|
|
else
|
|
-- Standard (non-collab) case
|
|
local max_dancers = 5
|
|
self.max_dancers = max_dancers
|
|
|
|
if self.is_2p then
|
|
local donbg_index = math.random(1, 6)
|
|
self.donbg_1 = DonBG.create(1, "background", donbg_index)
|
|
self.donbg_2 = DonBG.create(2, "background", donbg_index)
|
|
local skin_cfg = tex.get_skin_config("game_2p_offset")
|
|
self.p2_y_offset = skin_cfg and skin_cfg.y or 0
|
|
self.is_clear_1 = false
|
|
self.is_clear_2 = false
|
|
else
|
|
self.donbg = DonBG.create(player_num, "background")
|
|
self.bg_normal = BGNormal.create(math.random(0, 4), "background")
|
|
self.bg_fever = BGFever.create(math.random(0, 3), "background")
|
|
self.fever = Fever.create(math.random(0, Fever.variant_count("background") - 1), bpm, "background")
|
|
self.dancer = Dancer.create(math.random(0, 20), bpm, "background", max_dancers)
|
|
self.footer = Footer.new("background", math.random(0, 2))
|
|
end
|
|
|
|
local renda_index = math.random(0, 2)
|
|
self.renda = RendaController.new("background", renda_index)
|
|
if self.is_2p then
|
|
self.renda_2 = RendaController.new("background", renda_index, self.p2_y_offset)
|
|
end
|
|
self.chibi = ChibiController.new(bpm, "background")
|
|
self.is_clear = false
|
|
self.is_rainbow = false
|
|
self.clear_progress = nil
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
-- a miss streak darkens the donbg band (each player's own band in 2P)
|
|
function Background:handle_miss_streak(player_num, on)
|
|
if self.collab then return end
|
|
local d = self.is_2p and (player_num == 2 and self.donbg_2 or self.donbg_1) or self.donbg
|
|
if d then d:set_dark(on) end
|
|
end
|
|
|
|
function Background:update(current_ms, bpm)
|
|
self.bpm = bpm
|
|
self.enso_skip:update(current_ms)
|
|
if self.collab then
|
|
self.collab:update(current_ms, bpm)
|
|
else
|
|
if self.is_2p then
|
|
self.donbg_1:update(current_ms)
|
|
self.donbg_2:update(current_ms)
|
|
else
|
|
self.donbg:update(current_ms)
|
|
self.bg_normal:update(current_ms)
|
|
self.bg_fever:update(current_ms)
|
|
self.fever:update(current_ms, bpm)
|
|
self.dancer:update(current_ms, bpm)
|
|
end
|
|
self.renda:update(current_ms)
|
|
if self.renda_2 then self.renda_2:update(current_ms) end
|
|
self.chibi:update(current_ms, bpm)
|
|
end
|
|
end
|
|
|
|
function Background:handle_good(player_num)
|
|
if self.collab then
|
|
self.collab:handle_good(player_num)
|
|
else
|
|
self.chibi:add_chibi(player_num, false)
|
|
end
|
|
end
|
|
|
|
function Background:handle_ok(player_num)
|
|
if self.collab then
|
|
self.collab:handle_ok(player_num)
|
|
else
|
|
self.chibi:add_chibi(player_num, false)
|
|
end
|
|
end
|
|
|
|
function Background:handle_bad(player_num)
|
|
if self.collab then
|
|
self.collab:handle_bad(player_num)
|
|
else
|
|
self.chibi:add_chibi(player_num, true)
|
|
end
|
|
end
|
|
|
|
function Background:handle_drumroll(player_num)
|
|
if self.collab then
|
|
self.collab:handle_drumroll(player_num)
|
|
else
|
|
if player_num == 2 and self.renda_2 then
|
|
self.renda_2:add_renda()
|
|
else
|
|
self.renda:add_renda()
|
|
end
|
|
end
|
|
end
|
|
|
|
function Background:handle_balloon(player_num)
|
|
if self.collab then
|
|
self.collab:handle_balloon(player_num)
|
|
end
|
|
end
|
|
|
|
function Background:handle_dan(player_num, state)
|
|
if self.collab and self.collab.handle_dan then
|
|
self.collab:handle_dan(player_num, state)
|
|
end
|
|
end
|
|
|
|
function Background:handle_gauge(player_num, progress, is_clear, is_rainbow, clear_progress, flash)
|
|
-- child-only
|
|
self.arcade_gauge:handle_gauge(player_num, progress, is_clear, is_rainbow, clear_progress, flash)
|
|
if self.collab then
|
|
self.collab:handle_gauge(player_num, progress, is_clear, is_rainbow, clear_progress)
|
|
elseif self.is_2p then
|
|
-- Update each player's donbg clear state independently
|
|
if player_num == 1 then
|
|
self.donbg_1:set_clear(is_clear)
|
|
self.is_clear_1 = is_clear
|
|
elseif player_num == 2 then
|
|
self.donbg_2:set_clear(is_clear)
|
|
self.is_clear_2 = is_clear
|
|
end
|
|
self.is_clear = self.is_clear_1 or self.is_clear_2
|
|
self.is_rainbow = is_rainbow
|
|
else
|
|
self.donbg:set_clear(is_clear)
|
|
if is_clear and not self.is_clear then self.bg_fever:start() end
|
|
if not is_clear and self.is_clear then self.bg_fever:stop() end
|
|
if is_rainbow and not self.is_rainbow then self.fever:start() end
|
|
if not is_rainbow and self.is_rainbow then self.fever:stop() end
|
|
self.is_clear = is_clear
|
|
self.is_rainbow = is_rainbow
|
|
|
|
self:handle_dancer_count(progress, is_clear, clear_progress)
|
|
end
|
|
end
|
|
|
|
local GAUGE_MAX = 87.0
|
|
local CLEAR_STARTS = { 52, 60, 69 } -- easy / normal / hard+oni
|
|
local CLEAR_DEFAULT = 69 / GAUGE_MAX -- 0.79310
|
|
|
|
function Background:handle_dancer_count(progress, is_clear, clear_progress)
|
|
if not self.dancer or not self.dancer.set_dancer_count then return end
|
|
|
|
if clear_progress and clear_progress > 0 then
|
|
self.clear_progress = clear_progress
|
|
elseif is_clear and not self.clear_progress then
|
|
-- The engine flips is_clear at gauge_length > clear_start - 1, so the
|
|
-- progress at the flip is just under clear_start/87; pick the first
|
|
-- threshold at or above it.
|
|
local p = progress or 0
|
|
for _, cs in ipairs(CLEAR_STARTS) do
|
|
if p < cs / GAUGE_MAX + 0.0005 then
|
|
self.clear_progress = cs / GAUGE_MAX
|
|
break
|
|
end
|
|
end
|
|
self.clear_progress = self.clear_progress or CLEAR_DEFAULT
|
|
end
|
|
|
|
local cp = self.clear_progress or CLEAR_DEFAULT
|
|
local r = (progress or 0) / cp
|
|
local n = math.floor(2 * r + 3)
|
|
if n < 3 then n = 3 elseif n > 5 then n = 5 end
|
|
-- is_clear flips one gauge unit early; honour it so the fifth dancer and the
|
|
-- クリア state arrive on the same frame.
|
|
if is_clear then n = 5 end
|
|
self.dancer:set_dancer_count(n)
|
|
end
|
|
|
|
function Background:draw_back()
|
|
if self.collab then
|
|
self.collab:draw_back()
|
|
else
|
|
if not self.is_2p then
|
|
if self.bg_fever:is_visible() then
|
|
if not self.bg_fever.transitioned then
|
|
self.bg_normal:draw()
|
|
end
|
|
self.bg_fever:draw()
|
|
else
|
|
self.bg_normal:draw()
|
|
end
|
|
end
|
|
|
|
if self.is_2p then
|
|
self.donbg_1:draw(0)
|
|
self.donbg_2:draw(self.p2_y_offset)
|
|
else
|
|
self.donbg:draw()
|
|
end
|
|
|
|
self.renda:draw()
|
|
if self.renda_2 then self.renda_2:draw() end
|
|
if not self.is_2p then
|
|
self.dancer:draw()
|
|
-- footer (dai band) paints after the crowd so it rises from behind it.
|
|
self.footer:draw()
|
|
if self.fever:is_visible() then self.fever:draw() end
|
|
end
|
|
self.chibi:draw()
|
|
end
|
|
end
|
|
|
|
-- child-only. The engine only calls this when the skin declares it
|
|
-- (Background::wants_skip), and it then suppresses its own skip_counter text.
|
|
function Background:handle_skip(player_num, state)
|
|
self.enso_skip:handle(state)
|
|
end
|
|
|
|
-- Ending banner (engine hook): the cabinet clip for the result kind
|
|
-- ("clear" / "full_combo" / "donderful" / "fail") replaces the engine's drawing
|
|
-- when its exported data exists; otherwise the engine keeps drawing its own.
|
|
function Background:handle_ending(player_num, kind)
|
|
self.endings = self.endings or {}
|
|
if self.collab and self.collab.handle_ending then
|
|
self.collab:handle_ending(player_num, kind)
|
|
end
|
|
if Ending.available(kind) then
|
|
self.endings[player_num] = Ending.new(kind, player_num, "game")
|
|
end
|
|
end
|
|
|
|
function Background:draw_ending(player_num)
|
|
local e = self.endings and self.endings[player_num]
|
|
if e then e:draw(tex.get_current_ms()) end
|
|
end
|
|
|
|
-- Running score (engine hook): the 粋 / 雅 / 極 plate at the player panel's top-left
|
|
-- appears as each rank line is crossed (information/score `scorerank_Np`).
|
|
function Background:handle_score(player_num, score)
|
|
if self.collab and self.collab.handle_score then
|
|
self.collab:handle_score(player_num, score)
|
|
end
|
|
if not RankBadge.available() then return end
|
|
self.rank_badges = self.rank_badges or {}
|
|
local b = self.rank_badges[player_num]
|
|
if not b then
|
|
b = RankBadge.new(player_num, "game")
|
|
self.rank_badges[player_num] = b
|
|
end
|
|
b:set_score(score, tex.get_current_ms())
|
|
end
|
|
|
|
-- child-only: repaint the arcade gauge for the lane whose engine gauge just drew.
|
|
function Background:draw_gauge(player_num)
|
|
self.arcade_gauge:draw_one(player_num)
|
|
if self.collab and self.collab.draw_gauge then
|
|
self.collab:draw_gauge(player_num)
|
|
end
|
|
end
|
|
|
|
function Background:draw_fore()
|
|
if self.collab then
|
|
self.collab:draw_fore()
|
|
end
|
|
if self.rank_badges then
|
|
local now = tex.get_current_ms()
|
|
for _, b in pairs(self.rank_badges) do b:draw(now) end
|
|
end
|
|
end
|
|
|
|
function Background:destroy()
|
|
if _G.g_active_enso_skip == self.enso_skip then
|
|
_G.g_active_enso_skip = nil
|
|
end
|
|
if self.endings then
|
|
for _, e in pairs(self.endings) do e:destroy() end
|
|
self.endings = nil
|
|
end
|
|
if self.rank_badges then
|
|
for _, b in pairs(self.rank_badges) do b:destroy() end
|
|
self.rank_badges = nil
|
|
end
|
|
if self.collab then
|
|
self.collab:destroy()
|
|
else
|
|
if self.is_2p then
|
|
self.donbg_1:destroy()
|
|
self.donbg_2:destroy()
|
|
else
|
|
self.donbg:destroy()
|
|
self.bg_normal:destroy()
|
|
self.bg_fever:destroy()
|
|
self.fever:destroy()
|
|
self.dancer:destroy()
|
|
self.footer:destroy()
|
|
end
|
|
self.renda:destroy()
|
|
-- renda_2 shares the same texture folder; unload_folder is dedup-keyed
|
|
-- so the second destroy is a safe no-op.
|
|
if self.renda_2 then self.renda_2:destroy() end
|
|
self.chibi:destroy()
|
|
end
|
|
end
|