Author SHA1 Message Date
TaikoZhongandClaude Opus 5.5 2e694d4952 song select: refresh a folder's song count text when the count changes
The folder texts are cached on first draw, but the engine fills in a
folder's song count with a background scan after the boxes are first
drawn, so the count text stayed at 0 for the rest of the session (e.g.
the folder under the cursor at startup kept showing 0 songs). The cache
now remembers the count it was built with and rebuilds that one text
when box.tja_count changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 14:12:42 +08:00
Yonokid 6330aebefd add firework effect to big notes thanks qbaraki 2026-10-04 17:54:02 -04:00
Yonokid 8f0c4f69bc fix big hit animation 2026-10-04 16:02:26 -04:00
Yonokid f7f7d5cb4e add audio device switching 2026-10-02 11:01:09 -04:00
Yonokid 45ed83d19c move dan boxes to lua 2026-10-02 10:46:58 -04:00
Yonokid 565c6e4eae fix background animations 2026-10-01 16:47:45 -04:00
Yonokid d31a03ab58 remove load_animations() 2026-10-01 14:52:38 -04:00
Yonokid 4011ce9f60 move song select code to lua 2026-10-01 14:07:54 -04:00
Yonokid 5d6967ec47 move score history to lua 2026-09-30 22:59:42 -04:00
Yonokid 5d9fd60406 change scos 2026-09-30 15:51:13 -04:00
Yonokid 48a33b09d5 Update checksums.sha256 2026-09-30 15:29:39 -04:00
Yonokid 7a58faf27c welcome back attract sequence 2026-09-30 14:35:34 -04:00
Yonokid 6dea3a00b5 port normal transition 2026-09-30 14:25:42 -04:00
Yonokid 2081847b4f Create ura_switch.lua 2026-09-30 13:31:37 -04:00
Yonokid c911cc29de move fail and fc animations to lua 2026-09-30 13:01:07 -04:00
Yonokid 6b12833bea Create clear_animation.lua 2026-09-30 12:54:04 -04:00
Yonokid 1a762ee396 Create result_transition.lua 2026-09-30 12:42:40 -04:00
Yonokid 613a750706 add green and 2011 OPs 2026-09-30 11:50:26 -04:00
Yonokid 080bd0b5d4 Update checksums.sha256 2026-09-29 15:34:18 -04:00
Yonokid 5490fdd94f Merge pull request 'Add loading_countdown skin config key' (#9) from GanYiZhong/PyTaikoGreen:sc-loading-countdown into main
Reviewed-on: Yonokid/PyTaikoGreen#9
2026-09-29 20:34:22 +02:00
52 changed files with 3678 additions and 957 deletions

No files matched your search

File diff suppressed because it is too large. Load diff
Binary file not shown.
+114 -90
View File
@@ -1,90 +1,114 @@
{
"judge_bad": {
"x": 370,
"y": -40
},
"judge_ok": {
"x": 370,
"y": -40
},
"judge_good": {
"x": 370,
"y": -40
},
"hit_effect_good_big": {
"x": 342
},
"outer_good_big": {
"x": 304,
"y": -41,
"crop": [
[0, 0, 224, 224],
[224, 0, 224, 224],
[448, 0, 224, 224],
[672, 0, 224, 224]]
},
"hit_effect_good": {
"x": 342
},
"hit_effect_ok": {
"x": 342
},
"outer_good": {
"x": 304,
"y": -41,
"crop": [
[0, 0, 224, 224],
[224, 0, 224, 224],
[448, 0, 224, 224],
[672, 0, 224, 224]]
},
"outer_ok": {
"x": 304,
"y": -41,
"crop": [
[0, 0, 224, 224],
[224, 0, 224, 224],
[448, 0, 224, 224],
[672, 0, 224, 224]]
},
"hit_effect_ok_big": {
"x": 342
},
"outer_ok_big": {
"x": 304,
"y": -41,
"crop": [
[0, 0, 224, 224],
[224, 0, 224, 224],
[448, 0, 224, 224],
[672, 0, 224, 224]]
},
"explosion": [
{
"crop": [
[0, 0, 192, 192],
[192, 0, 192, 192],
[384, 0, 192, 192],
[576, 0, 192, 192],
[0, 192, 192, 192],
[192, 192, 192, 192],
[384, 192, 192, 192],
[576, 192, 192, 192],
[0, 384, 192, 192],
[192, 384, 192, 192],
[384, 384, 192, 192],
[576, 384, 192, 192],
[0, 576, 192, 192],
[192, 576, 192, 192],
[384, 576, 192, 192],
[576, 576, 192, 192],
[0, 768, 192, 192],
[192, 768, 192, 192],
[384, 768, 192, 192],
[576, 768, 192, 192],
[0, 960, 192, 192],
[192, 960, 192, 192],
[384, 960, 192, 192]]
}
]
}
{
"judge_bad": {
"x": 370,
"y": -40
},
"judge_ok": {
"x": 370,
"y": -40
},
"judge_good": {
"x": 370,
"y": -40
},
"hit_effect_good_big": {
"x": 342
},
"outer_good_big": {
"x": 304,
"y": -41,
"crop": [
[0, 0, 224, 224],
[224, 0, 224, 224],
[448, 0, 224, 224],
[672, 0, 224, 224]]
},
"hit_effect_good": {
"x": 342
},
"hit_effect_ok": {
"x": 342
},
"outer_good": {
"x": 304,
"y": -41,
"crop": [
[0, 0, 224, 224],
[224, 0, 224, 224],
[448, 0, 224, 224],
[672, 0, 224, 224]]
},
"outer_ok": {
"x": 304,
"y": -41,
"crop": [
[0, 0, 224, 224],
[224, 0, 224, 224],
[448, 0, 224, 224],
[672, 0, 224, 224]]
},
"hit_effect_ok_big": {
"x": 342
},
"outer_ok_big": {
"x": 304,
"y": -41,
"crop": [
[0, 0, 224, 224],
[224, 0, 224, 224],
[448, 0, 224, 224],
[672, 0, 224, 224]]
},
"explosion": [
{
"crop": [
[0, 0, 192, 192],
[192, 0, 192, 192],
[384, 0, 192, 192],
[576, 0, 192, 192],
[0, 192, 192, 192],
[192, 192, 192, 192],
[384, 192, 192, 192],
[576, 192, 192, 192],
[0, 384, 192, 192],
[192, 384, 192, 192],
[384, 384, 192, 192],
[576, 384, 192, 192],
[0, 576, 192, 192],
[192, 576, 192, 192],
[384, 576, 192, 192],
[576, 576, 192, 192],
[0, 768, 192, 192],
[192, 768, 192, 192],
[384, 768, 192, 192],
[576, 768, 192, 192],
[0, 960, 192, 192],
[192, 960, 192, 192],
[384, 960, 192, 192]]
}
],
"outer_effect_ok": {
"x": 304,
"y": -41
},
"outer_effect_good": {
"x": 304,
"y": -41
},
"firework": {
"crop": [
[0, 0, 256, 256],
[256, 0, 256, 256],
[512, 0, 256, 256],
[768, 0, 256, 256],
[0, 256, 256, 256],
[256, 256, 256, 256],
[512, 256, 256, 256],
[768, 256, 256, 256],
[0, 512, 256, 256],
[256, 512, 256, 256],
[512, 512, 256, 256],
[768, 512, 256, 256],
[0, 768, 256, 256]]
}
}
+24
View File
@@ -1565,6 +1565,30 @@
}
}
},
"device": {
"path": "audio/device",
"name": {
"en": "Audio Device",
"ja": "オーディオデバイス",
"zh": "音频设备",
"ko": "오디오 장치"
},
"description": {
"en": "Output device to use for the selected backend. Default uses the system default device.",
"ja": "選択したバックエンドで使用する出力デバイス。デフォルトはシステムの既定デバイスを使用します。",
"zh": "为所选后端使用的输出设备。默认使用系统默认设备。",
"ko": "선택한 백엔드에서 사용할 출력 장치. 기본값은 시스템 기본 장치를 사용합니다."
},
"type": "audiodevice",
"values": {
"": {
"en": "Default",
"ja": "デフォルト",
"zh": "默认",
"ko": "기본값"
}
}
},
"sample_rate": {
"path": "audio/sample_rate",
"name": {
+2 -3
View File
@@ -13,15 +13,14 @@
"dan_select_rank_plate": false,
"entry_credit_arcade": false,
"dan_confirm_yes_left": false,
"songselect_2p_join": false,
"costume_preset_slots": false,
"dan_title_horizontal": false,
"dan_chip_name_black": false,
"dan_exam_grid": false,
"dan_select_total_notes": true,
"combo_color_tiers": false,
"gauge_cell_fade_in": false,
"dan_exam_value_center": false
"dan_exam_value_center": false,
"disable_chara_3d": false
},
"chara_3d": {
"scale": 650.0,
-31
View File
@@ -12,11 +12,6 @@
"y": 129
},
"background": {},
"background_static": {},
"bar_genre": {},
"bar_genre_back": {},
"bar_genre_overlay": {},
"box_chara": {},
"folder_texture_left": {
"y": 95
},
@@ -41,32 +36,6 @@
"x": 483,
"y": 189
},
"folder_text": {
"x": 650,
"y": 125,
"frame_order": [
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
18
]
},
"folder_graphic": {
"x": 470,
"y": 180,
@@ -1,10 +1,4 @@
{
"difficulty_back": {},
"difficulty_crown": {},
"difficulty_star": {},
"difficulty_bar": {},
"difficulty_select_bar": {},
"difficulty_select_bar2": {},
"1p_outline": {
"x": 461,
"y": 132
@@ -11,22 +11,10 @@
"x": 20,
"y": 64
},
"frame_score": {},
"level_number": {},
"score_rank": {},
"crown_badge": {},
"song_count_back": {
"x": 470,
"y": 125
},
"song_count_num": {
"x": 475,
"y": 125
},
"song_count_songs": {
"x": 600,
"y": 125
},
"s_crown_dfc": {
"x": 473,
"y": 175
-2
View File
@@ -98,8 +98,6 @@ function Background.new(player_num, bpm, scene_preset)
self.scene_preset = scene_preset
self.is_2p = (player_num == 3) -- PlayerNum::TWO_PLAYER
tex.load_animations("background")
local entry = scene_preset and COLLABS[scene_preset]
if entry then
local ctor, path, max_dancers = entry[1], entry[2], entry[3]
+3 -3
View File
@@ -38,7 +38,7 @@ function A3BGNormal.new()
local self = setmetatable({}, A3BGNormal)
self.name = "bg_0"
tex.load_folder(COLLAB_PATH, "bg_normal/" .. self.name)
self.flicker = tex.get_animation(9)
self.flicker = tex.get_animation(9, "background")
self._id_bg = tex.get_texture(COLLAB_PATH .. "/" .. self.name .. "/" .. "background") or 0
self._id_overlay = tex.get_texture(COLLAB_PATH .. "/" .. self.name .. "/" .. "overlay") or 0
self._id_curtain = tex.get_texture(COLLAB_PATH .. "/" .. self.name .. "/" .. "curtain") or 0
@@ -70,8 +70,8 @@ function A3BGFever.new()
self.name = "bg_fever_0"
tex.load_folder(COLLAB_PATH, "bg_fever/" .. self.name)
self.transitioned = false
self.horizontal_move = tex.get_animation(16)
self.bg_texture_move = tex.get_animation(17)
self.horizontal_move = tex.get_animation(16, "background")
self.bg_texture_move = tex.get_animation(17, "background")
local petal_info = tex.get_texture_info(self.name, "petals")
self.petal_width = petal_info and petal_info.width or 0
self.vertical_move = anim.move(1300, { total_distance = self.petal_width, loop = true })
+1 -1
View File
@@ -13,7 +13,7 @@ function DanDonBG.new(player_num)
local self = setmetatable({}, DanDonBG)
local base = DonBGBase.new(1, player_num, COLLAB_PATH)
for k, v in pairs(base) do self[k] = v end
self.overlay_move = tex.get_animation(2)
self.overlay_move = tex.get_animation(2, "background")
local bg_overlay_info = tex.get_texture_info(self.name, "bg_overlay")
self.bg_overlay_width = bg_overlay_info and bg_overlay_info.width or 0
local bg_overlay_2_info = tex.get_texture_info(self.name, "bg_overlay_2")
+2 -2
View File
@@ -18,8 +18,8 @@ function FunassyiBGFever.new()
self.name = "bg_fever_0"
tex.load_folder(COLLAB_PATH, "bg_fever/" .. self.name)
self.transitioned = false
self.horizontal_move = tex.get_animation(16)
self.bg_texture_move = tex.get_animation(17)
self.horizontal_move = tex.get_animation(16, "background")
self.bg_texture_move = tex.get_animation(17, "background")
local overlay_info = tex.get_texture_info(self.name, "overlay")
self.overlay_width = overlay_info and overlay_info.width or 0
self._id_bg = tex.get_texture(COLLAB_PATH .. "/" .. self.name .. "/" .. "background") or 0
+1 -1
View File
@@ -12,7 +12,7 @@ function TutorialDonBG.new(index, player_num, path)
end
self.move = anim.move(6000, { total_distance = -self.bg_texture.width, loop = true })
self.move:start()
self.overlay_move = tex.get_animation(2)
self.overlay_move = tex.get_animation(2, "background")
self.overlay_texture = tex.get_texture_info(self.name, "overlay")
self.footer_texture = tex.get_texture_info(self.name, "footer")
self._id_overlay = tex.get_texture(COLLAB_PATH .. "/" .. self.name .. "/" .. "overlay") or 0
+16 -16
View File
@@ -46,8 +46,8 @@ function BGFever0.new(index, path)
self.overlay_move_up = anim.move(133, {total_distance = 360, ease_out = "quadratic", delay = 300})
self.overlay_move_down = anim.move(133, {total_distance = 160, ease_in = "quadratic", delay = 433})
self.wave_spin = tex.get_animation(28)
self.bg_move = tex.get_animation(16)
self.wave_spin = tex.get_animation(28, "background")
self.bg_move = tex.get_animation(16, "background")
self.circle = {
center_x = 100 * self.screen_scale,
@@ -162,10 +162,10 @@ function BGFever1.new(index, path)
local base = BGFeverBase.new(index, path)
for k, v in pairs(base) do self[k] = v end
self.fadein = tex.get_animation(19)
self.bg_texture_change = tex.get_animation(20)
self.ship_rotation = tex.get_animation(21)
self.move_in = tex.get_animation(22)
self.fadein = tex.get_animation(19, "background")
self.bg_texture_change = tex.get_animation(20, "background")
self.ship_rotation = tex.get_animation(21, "background")
self.move_in = tex.get_animation(22, "background")
local ship_info = tex.get_texture_info(self.name, "ship")
self.ship_origin = {
@@ -226,13 +226,13 @@ function BGFever2.new(index, path)
self.screen_scale = tex.get_screen_scale()
self.fadein = tex.get_animation(19)
self.move_in = tex.get_animation(24)
self.footer_move_up = tex.get_animation(26)
self.bird_tc = tex.get_animation(20)
self.overlay_tc = tex.get_animation(25)
self.circle_rotate = tex.get_animation(27)
self.fish_spin = tex.get_animation(28)
self.fadein = tex.get_animation(19, "background")
self.move_in = tex.get_animation(24, "background")
self.footer_move_up = tex.get_animation(26, "background")
self.bird_tc = tex.get_animation(20, "background")
self.overlay_tc = tex.get_animation(25, "background")
self.circle_rotate = tex.get_animation(27, "background")
self.fish_spin = tex.get_animation(28, "background")
self.circle = {
center_x = 500 * self.screen_scale,
@@ -366,9 +366,9 @@ function BGFever3.new(index, path)
local base = BGFeverBase.new(index, path)
for k, v in pairs(base) do self[k] = v end
self.vertical_move = tex.get_animation(15)
self.horizontal_move = tex.get_animation(16)
self.bg_texture_move = tex.get_animation(17)
self.vertical_move = tex.get_animation(15, "background")
self.horizontal_move = tex.get_animation(16, "background")
self.bg_texture_move = tex.get_animation(17, "background")
self._id_bg = tex.get_texture(self.path .. "/" .. self.name .. "/" .. "background") or 0
self._id_overlay_1 = tex.get_texture(self.path .. "/" .. self.name .. "/" .. "overlay_1") or 0
+6 -6
View File
@@ -28,7 +28,7 @@ function BGNormal01.new(index, path)
local self = setmetatable({}, BGNormal01)
local base = BGNormalBase.new(index, path)
for k, v in pairs(base) do self[k] = v end
self.flicker = tex.get_animation(9)
self.flicker = tex.get_animation(9, "background")
self._id_overlay = tex.get_texture(self.path .. "/" .. self.name .. "/" .. "overlay") or 0
return self
end
@@ -50,7 +50,7 @@ function BGNormal2.new(index, path)
local self = setmetatable({}, BGNormal2)
local base = BGNormalBase.new(index, path)
for k, v in pairs(base) do self[k] = v end
self.flicker = tex.get_animation(10)
self.flicker = tex.get_animation(10, "background")
self._id_center = tex.get_texture(self.path .. "/" .. self.name .. "/" .. "center") or 0
self._id_overlay = tex.get_texture(self.path .. "/" .. self.name .. "/" .. "overlay") or 0
self._id_lamps = tex.get_texture(self.path .. "/" .. self.name .. "/" .. "lamps") or 0
@@ -114,9 +114,9 @@ function BGNormal3.new(index, path)
local self = setmetatable({}, BGNormal3)
local base = BGNormalBase.new(index, path)
for k, v in pairs(base) do self[k] = v end
self.flicker = tex.get_animation(11)
self.turtle_move = tex.get_animation(12)
self.turtle_change = tex.get_animation(13)
self.flicker = tex.get_animation(11, "background")
self.turtle_move = tex.get_animation(12, "background")
self.turtle_change = tex.get_animation(13, "background")
self.screen_scale = tex.get_screen_scale()
self.screen_width = tex.get_screen_width()
self.screen_height = tex.get_screen_height()
@@ -171,7 +171,7 @@ function BGNormal4.new(index, path)
local self = setmetatable({}, BGNormal4)
local base = BGNormalBase.new(index, path)
for k, v in pairs(base) do self[k] = v end
self.flicker = tex.get_animation(14)
self.flicker = tex.get_animation(14, "background")
self._id_paper_lamp = tex.get_texture(self.path .. "/" .. self.name .. "/" .. "paper_lamp") or 0
self._id_light_overlay= tex.get_texture(self.path .. "/" .. self.name .. "/" .. "light_overlay") or 0
self._id_overlay = tex.get_texture(self.path .. "/" .. self.name .. "/" .. "overlay") or 0
+1 -1
View File
@@ -10,7 +10,7 @@ function DonBG1.new(index, player_num, path)
for k, v in pairs(base) do
self[k] = v
end
self.overlay_move = tex.get_animation(2)
self.overlay_move = tex.get_animation(2, "background")
self.overlay_texture = tex.get_texture_info(self.name, "overlay")
self.footer_texture = tex.get_texture_info(self.name, "footer")
self._id_overlay = tex.get_texture(self.path .. "/" .. self.name .. "/" .. "overlay") or 0
+1 -1
View File
@@ -9,7 +9,7 @@ function DonBG2.new(index, player_num, path)
for k, v in pairs(base) do
self[k] = v
end
self.overlay_move = tex.get_animation(3)
self.overlay_move = tex.get_animation(3, "background")
self.overlay_texture = tex.get_texture_info(self.name, "overlay")
self._id_overlay = tex.get_texture(self.path .. "/" .. self.name .. "/" .. "overlay") or 0
return self
+3 -3
View File
@@ -8,10 +8,10 @@ function DonBG3.new(index, player_num, path)
for k, v in pairs(base) do
self[k] = v
end
self.bounce = tex.get_animation(4)
self.bounce = tex.get_animation(4, "background")
self.bounce:start()
self.overlay_move = tex.get_animation(6)
self.overlay_move_2 = tex.get_animation(7)
self.overlay_move = tex.get_animation(6, "background")
self.overlay_move_2 = tex.get_animation(7, "background")
self.overlay_texture = tex.get_texture_info(self.name, "overlay")
self._id_overlay = tex.get_texture(self.path .. "/" .. self.name .. "/" .. "overlay") or 0
return self
+1 -1
View File
@@ -8,7 +8,7 @@ function DonBG4.new(index, player_num, path)
for k, v in pairs(base) do
self[k] = v
end
self.overlay_move = tex.get_animation(2)
self.overlay_move = tex.get_animation(2, "background")
self.overlay_texture = tex.get_texture_info(self.name, "overlay")
self._id_overlay = tex.get_texture(self.path .. "/" .. self.name .. "/" .. "overlay") or 0
return self
+2 -2
View File
@@ -8,9 +8,9 @@ function DonBG5.new(index, player_num, path)
for k, v in pairs(base) do
self[k] = v
end
self.bounce = tex.get_animation(4)
self.bounce = tex.get_animation(4, "background")
self.bounce:start()
self.adjust = tex.get_animation(8)
self.adjust = tex.get_animation(8, "background")
self.overlay_texture = tex.get_texture_info(self.name, "overlay")
self._id_overlay = tex.get_texture(self.path .. "/" .. self.name .. "/" .. "overlay") or 0
return self
+1 -1
View File
@@ -8,7 +8,7 @@ function DonBG6.new(index, player_num, path)
for k, v in pairs(base) do
self[k] = v
end
self.overlay_move = tex.get_animation(2)
self.overlay_move = tex.get_animation(2, "background")
self.overlay_1_texture = tex.get_texture_info(self.name, "overlay_1")
self.overlay_2_texture = tex.get_texture_info(self.name, "overlay_2")
self._id_overlay_1 = tex.get_texture(self.path .. "/" .. self.name .. "/" .. "overlay_1") or 0
+1 -1
View File
@@ -16,7 +16,7 @@ function DonBGBase.new(index, player_num, path)
self.move:start()
self.is_clear = false
self.clear_fade = tex.get_animation(1)
self.clear_fade = tex.get_animation(1, "background")
self._id_bg = tex.get_texture(self.path .. "/" .. self.name .. "/" .. "background") or 0
+487
View File
@@ -0,0 +1,487 @@
-- DanSelect scene rendering. Mirrors src/scenes/dan_select.cpp + DanBox
-- (src/objects/song_select/file_navigator/box_dan.cpp).
-- The DanBox draw methods (draw_chip / draw_open / draw_exam_box / draw_exam_grid) are
-- ported here; the C++ side only prepares the data (load_text) and hands the box over.
DanSelect = {}
DanSelect.__index = DanSelect
-- ---------------------------------------------------------------------------
-- Texture / config helpers (the dan_select screen has its own box/yellow_box subsets).
-- ---------------------------------------------------------------------------
local _tex = {}
local function T(subset, name)
local key = subset .. "/" .. name
local id = _tex[key]
if id == nil then
id = tex.get_texture("dan_select/" .. subset .. "/" .. name)
_tex[key] = id
end
return id
end
local function cfg(name)
return tex.get_skin_config(name)
end
local function opt(name)
return tex.get_option(name)
end
-- Texture draw-offset at index 0 (TextureObject::x[0] / y[0] / x2[0]).
local function tex_x0(subset, name)
local info = tex.get_texture_info(subset, name)
if info and info.x and info.x[1] then return info.x[1] end
return 0
end
local function tex_y0(subset, name)
local info = tex.get_texture_info(subset, name)
if info and info.y and info.y[1] then return info.y[1] end
return 0
end
local function tex_x2(subset, name)
local info = tex.get_texture_info(subset, name)
if info and info.x2 and info.x2[1] then return info.x2[1] end
return 0
end
-- exam.type -> icon texture name (exam_icon_id in box_dan.cpp). "renda" resolves to
-- exam_roll, falling back to exam_drumroll in older skins. Returns the resolved name.
local EXAM_ICON = {
gauge = "exam_gauge", combo = "exam_combo",
hit = "exam_hit", judgebad = "exam_judgebad",
judgegood = "exam_judgegood", judgeperfect = "exam_judgeperfect",
score = "exam_score", renda = "exam_roll",
}
local function exam_icon_name(type)
local name = EXAM_ICON[type]
if not name then return nil end
if T("yellow_box", name) then return name end
if T("yellow_box", "exam_drumroll") then return "exam_drumroll" end
return nil
end
local yb_box = { path = nil, state = "closed" }
function DanSelect.new()
local self = setmetatable({}, DanSelect)
yb_box.path, yb_box.state = nil, "closed"
return self
end
-- ---------------------------------------------------------------------------
-- Yellow box (the 9-slice board the course details sit on). The dan box is always in
-- diff-select once open, so it uses the diff-select animation set (13..18); the engine no
-- longer drives them, so the skin advances them here (see DanSelect:update / track).
-- ---------------------------------------------------------------------------
local YB_DIFF_ANIMS = { 13, 14, 15, 16, 17, 18 }
local function draw_dan_yellow_box(is_diff, fade)
local right_x = tex.get_animation(is_diff and 14 or 10).attribute
local left_x = tex.get_animation(is_diff and 13 or 9).attribute
local center_width = tex.get_animation(is_diff and 15 or 11).attribute
local top_y = tex.get_animation(16).attribute
local center_height = tex.get_animation(17).attribute
local bottom_y = (cfg("yellow_box_bottom") or { y = 0 }).y
local info = tex.get_texture_info("yellow_box", "yellow_box_bottom_right")
local eh = (info and info.height) or 0
tex.draw_texture(T("yellow_box", "yellow_box_bottom_right"), { x = right_x, y = bottom_y, fade = fade })
tex.draw_texture(T("yellow_box", "yellow_box_bottom_left"), { x = left_x, y = bottom_y, fade = fade })
tex.draw_texture(T("yellow_box", "yellow_box_top_right"), { x = right_x, y = top_y, fade = fade })
tex.draw_texture(T("yellow_box", "yellow_box_top_left"), { x = left_x, y = top_y, fade = fade })
tex.draw_texture(T("yellow_box", "yellow_box_bottom"), { x = left_x + eh, y = bottom_y, x2 = center_width, fade = fade })
tex.draw_texture(T("yellow_box", "yellow_box_right"), { x = right_x, y = top_y + eh, y2 = center_height, fade = fade })
tex.draw_texture(T("yellow_box", "yellow_box_left"), { x = left_x, y = top_y + eh, y2 = center_height, fade = fade })
tex.draw_texture(T("yellow_box", "yellow_box_top"), { x = left_x + eh, y = top_y, x2 = center_width, fade = fade })
tex.draw_texture(T("yellow_box", "yellow_box_center"), { x = left_x + eh, y = top_y + eh, x2 = center_width, y2 = center_height, fade = fade })
end
-- Track the open box and start the diff-select expansion on the closed -> diff_select
-- transition (the dan box never passes through the "open" draw_state).
local function track_dan_yellow_box(box)
local state = box:draw_state()
local path = box:path()
if state == "closed" then
if yb_box.path == path then
yb_box.path, yb_box.state = nil, "closed"
end
return
end
if yb_box.path ~= path then
yb_box.path, yb_box.state = path, state
for _, id in ipairs(YB_DIFF_ANIMS) do tex.get_animation(id):reset() end
for _, id in ipairs(YB_DIFF_ANIMS) do tex.get_animation(id):start() end
end
end
local function update_dan_yellow_box(ms)
for _, id in ipairs(YB_DIFF_ANIMS) do tex.get_animation(id):update(ms) end
end
function DanSelect:update(ms)
update_dan_yellow_box(ms)
end
-- Draw a texture at an absolute position, ignoring its own x[0]/y[0] anchor (draw_abs).
local function draw_abs(subset, name, X, Y, fade, frame)
local id = T(subset, name)
if not id then return end
tex.draw_texture(id, {
frame = frame or 0,
x = X - tex_x0(subset, name),
y = Y - tex_y0(subset, name),
fade = fade,
})
end
function DanSelect:draw_box(box)
track_dan_yellow_box(box)
local state = box:draw_state()
if state == "closed" then
self:draw_dan_closed(box)
else
self:draw_dan_open(box)
end
end
-- ---------------------------------------------------------------------------
-- draw_chip (closed): folder board + vertical chip name.
-- ---------------------------------------------------------------------------
function DanSelect:draw_dan_closed(box)
local bx = box:box_x()
local fade = box.fade.attribute
tex.draw_texture(T("box", "folder"), { frame = box.dan_color, x = bx, fade = fade })
local name = box:chip_name()
if name then
local nb = cfg("dan_chip_name") or cfg("song_box_name") or { x = 0, y = 0, height = 0 }
name:draw({
x = bx + nb.x - name.width / 2,
y = nb.y,
y2 = math.min(name.height, nb.height or name.height) - name.height,
fade = fade,
})
end
end
-- ---------------------------------------------------------------------------
-- draw_open: chip + song ribbon + total notes + rank + exam box.
-- ---------------------------------------------------------------------------
function DanSelect:draw_dan_open(box)
self:draw_dan_closed(box)
local f = box.open_fade.attribute
-- The yellow box board the course details sit on (opaque; its growth is driven by
-- the diff-select animations, matching the song-select wheel).
draw_dan_yellow_box(true, 1.0)
local off = cfg("dan_yellow_box_offset") or { x = 0, y = 0 }
local offset_x = off.x or 0
local offset_y = off.y or 0
local n = box:song_count()
for i = 0, n - 1 do
local x = i * offset_x
local y = i * offset_y
tex.draw_texture(T("yellow_box", "genre_banner"), { frame = box:song_genre(i), x = x, y = y, fade = f })
local song_label = T("yellow_box", "song_label")
if song_label then
tex.draw_texture(song_label, { frame = math.min(i, 2), x = x, y = y, fade = f })
end
tex.draw_texture(T("yellow_box", "difficulty"), { frame = box:song_difficulty(i), x = x, y = y, fade = f })
tex.draw_texture(T("yellow_box", "difficulty_x"), { x = x, y = y, fade = f })
tex.draw_texture(T("yellow_box", "difficulty_star"), { x = x, y = y, fade = f })
-- Level counter
local lvl = tostring(box:song_level(i))
local margin = (cfg("dan_level_counter_margin") or { x = 0 }).x
local total_w = #lvl * margin
for j = 1, #lvl do
local digit = string.byte(lvl, j) - 48
tex.draw_texture(T("yellow_box", "difficulty_num"), {
frame = digit, x = x - total_w / 2 + (j - 1) * margin, y = y, fade = f })
end
-- Song title + subtitle
local title_text = box:song_title_text(i)
local sub_text = box:song_subtitle_text(i)
local td = cfg("dan_title") or { x = 0, y = 0, height = 0 }
local sd = cfg("dan_subtitle") or { x = 0, y = 0, height = 0 }
if title_text then
title_text:draw({
x = td.x + x, y = td.y + y,
y2 = math.min(title_text.height, td.height or title_text.height) - title_text.height,
fade = f })
end
if sub_text then
sub_text:draw({
x = sd.x + x,
y = sd.y + y - math.min(sub_text.height, sd.height or sub_text.height),
y2 = math.min(sub_text.height, sd.height or sub_text.height) - sub_text.height,
fade = f })
end
end
-- Total notes
tex.draw_texture(T("yellow_box", "total_notes_bg"), { fade = f })
local tn_slot = cfg("dan_select_total_notes")
if not tn_slot or tn_slot.x >= 1.0 then
tex.draw_texture(T("yellow_box", "total_notes"), { fade = f })
local tn = tostring(box.total_notes)
local tn_margin = (cfg("total_notes_counter_margin") or { x = 0 }).x
for i = 1, #tn do
local digit = string.byte(tn, i) - 48
tex.draw_texture(T("yellow_box", "total_notes_counter"), { frame = digit, x = (i - 1) * tn_margin, fade = f })
end
end
-- Rank plate / frame
if box.dan_rank >= 0 and opt("dan_select_rank_plate") then
tex.draw_texture(T("yellow_box", "rank_plate"), { frame = box.dan_rank, fade = f })
else
tex.draw_texture(T("yellow_box", "frame"), { frame = box.dan_color, fade = f })
local hori = box:hori_name()
if hori then
local hn = cfg("dan_hori_name") or { x = 0, y = 0, width = 0 }
hori:draw({
x = hn.x - hori.width / 2,
y = hn.y,
x2 = math.min(hori.width, hn.width or hori.width) - hori.width,
fade = f })
end
end
self:draw_exam_box(box, f)
end
-- ---------------------------------------------------------------------------
-- Exam board (the default "condition rows" layout).
-- ---------------------------------------------------------------------------
function DanSelect:draw_exam_box(box, f)
if opt("dan_exam_grid") then
self:draw_exam_grid(box, f)
return
end
tex.draw_texture(T("yellow_box", "exam_box_bottom_right"), { fade = f })
tex.draw_texture(T("yellow_box", "exam_box_bottom_left"), { fade = f })
tex.draw_texture(T("yellow_box", "exam_box_top_right"), { fade = f })
tex.draw_texture(T("yellow_box", "exam_box_top_left"), { fade = f })
tex.draw_texture(T("yellow_box", "exam_box_bottom"), { fade = f })
tex.draw_texture(T("yellow_box", "exam_box_right"), { fade = f })
tex.draw_texture(T("yellow_box", "exam_box_left"), { fade = f })
tex.draw_texture(T("yellow_box", "exam_box_top"), { fade = f })
tex.draw_texture(T("yellow_box", "exam_box_center"), { fade = f })
tex.draw_texture(T("yellow_box", "exam_header"), { fade = f })
local offset_y = (cfg("dan_exam_info") or { y = 0 }).y
local margin = (cfg("exam_counter_margin") or { x = 0 }).x
-- Four or more conditions do not fit the board: drop the soul-gauge row.
local exam_count = box:exam_count()
local shown = {}
for i = 0, exam_count - 1 do
if not (exam_count >= 4 and box:exam_type(i) == "gauge") then
shown[#shown + 1] = i
end
end
if #shown > 3 then
while #shown > 3 do shown[#shown] = nil end
end
for idx = 1, #shown do
local i = shown[idx]
local y = (idx - 1) * offset_y
local etype = box:exam_type(i)
local range = box:exam_range(i)
tex.draw_texture(T("yellow_box", "judge_box"), { y = y, fade = f })
local icon_name = exam_icon_name(etype)
if icon_name then tex.draw_texture(T("yellow_box", icon_name), { y = y, fade = f }) end
if opt("dan_exam_value_center") then
-- Centre the whole value (digits, %, suffix) on dan_exam_value_center.x.
local vc = cfg("dan_exam_value_center") or { x = 0, width = 0 }
local digits = tostring(box:exam_red(i))
local pct = (etype == "gauge") and T("yellow_box", "exam_percent") or nil
local suf = (range == "more") and T("yellow_box", "exam_more")
or (range == "less") and T("yellow_box", "exam_less") or nil
local digits_w = (#digits - 1) * margin + tex_x2("yellow_box", "judge_num")
local pct_w = pct and tex_x2("yellow_box", "exam_percent") or 0
local suf_w = suf and tex_x2("yellow_box", (range == "more") and "exam_more" or "exam_less") or 0
local gap = vc.width or 0
local parts = 1 + (pct and 1 or 0) + (suf and 1 or 0)
local total = digits_w + pct_w + suf_w + gap * (parts - 1)
local cx = (vc.x or 0) - total / 2
for j = 1, #digits do
local digit = string.byte(digits, j) - 48
tex.draw_texture(T("yellow_box", "judge_num"),
{ frame = digit, x = cx + (j - 1) * margin - tex_x0("yellow_box", "judge_num"), y = y, fade = f })
end
cx = cx + digits_w + gap
if pct then
tex.draw_texture(pct, { x = cx - tex_x0("yellow_box", "exam_percent"), y = y, fade = f })
cx = cx + pct_w + gap
end
if suf then
local suf_name = (range == "more") and "exam_more" or "exam_less"
tex.draw_texture(suf, { x = cx - tex_x0("yellow_box", suf_name), y = y, fade = f })
end
else
local x_offset = 0
if etype == "gauge" then
tex.draw_texture(T("yellow_box", "exam_percent"), { y = y, fade = f })
x_offset = (cfg("exam_gauge_offset") or { x = 0 }).x
end
local counter = tostring(box:exam_red(i))
for j = 1, #counter do
local digit = string.byte(counter, j) - 48
local x = x_offset - (#counter - j + 1) * margin
tex.draw_texture(T("yellow_box", "judge_num"), { frame = digit, x = x, y = y, fade = f })
end
if range == "more" then
tex.draw_texture(T("yellow_box", "exam_more"), { x = -x_offset * 1.7, y = y, fade = f })
elseif range == "less" then
tex.draw_texture(T("yellow_box", "exam_less"), { x = -x_offset * 1.7, y = y, fade = f })
end
end
end
end
-- ---------------------------------------------------------------------------
-- Exam board grid mode (dan_exam_grid option): a two-column gauge/score layout.
-- ---------------------------------------------------------------------------
local function caption(self, str, font_size, outline)
self._captions = self._captions or {}
local key = str .. "\0" .. tostring(font_size) .. "\0" .. tostring(outline)
local cap = self._captions[key]
if cap == nil then
cap = text.create_raw_text(str, font_size, { 255, 255, 255, 255 }, { 0, 0, 0, 255 }, false, outline)
self._captions[key] = cap
end
return cap
end
local function draw_value_text(self, cap_str, bt, right_x, top_y, f)
if not bt then return false end
local ol = (bt.outline and bt.outline >= 0) and bt.outline or (3.0 / tex.get_screen_scale())
local size = (bt.font_size and bt.font_size > 0) and bt.font_size or 28
local cap = caption(self, cap_str, size, ol)
if not cap then return false end
local pad = math.floor(ol * tex.get_screen_scale()) + 2
cap:draw({ x = right_x - cap.width + pad, y = top_y - pad, fade = f })
return true
end
-- draw_value (grid mode): centred digits + optional percent/suffix, absolute anchors.
local function draw_value(self, red, etype, range, centre_x, top_y, f)
local digits = tostring(red)
local margin = (cfg("exam_counter_margin") or { x = 0 }).x
local pct_w = 0
if etype == "gauge" and T("yellow_box", "exam_percent") then
pct_w = tex_x2("yellow_box", "exam_percent")
end
local suffix = (range == "less") and "exam_less" or (range == "more") and "exam_more" or nil
local suf_w = 0
if suffix and T("yellow_box", suffix) then
suf_w = tex_x2("yellow_box", suffix)
end
local total = #digits * margin + pct_w + suf_w
local cx = centre_x - total * 0.5
for j = 1, #digits do
local digit = string.byte(digits, j) - 48
draw_abs("yellow_box", "judge_num", cx, top_y, f, digit)
cx = cx + margin
end
if pct_w > 0 then
draw_abs("yellow_box", "exam_percent", cx, top_y + 8, f)
cx = cx + pct_w
end
if suf_w > 0 then
draw_abs("yellow_box", suffix, cx, top_y + 6, f)
end
end
function DanSelect:draw_exam_grid(box, f)
local exam_frame = T("yellow_box", "exam_frame")
if exam_frame then tex.draw_texture(exam_frame, { fade = f }) end
tex.draw_texture(T("yellow_box", "exam_header"), { fade = f })
local gb = cfg("dan_exam_gauge_box")
local sb = cfg("dan_exam_score_box")
local gx = (gb and gb.x) or 436.0
local gy = (gb and gb.y) or 615.0
local sx = (sb and sb.x) or 769.0
local sy = (sb and sb.y) or 615.0
local pitch = (sb and sb.height and sb.height > 0) and sb.height or 132.0
local sg = cfg("dan_exam_seg")
local seg_x = (sg and sg.x) or 14.0
local seg_y = (sg and sg.y) or 48.0
local seg_dx = (sg and sg.width and sg.width > 0) and sg.width or 327.0
local vp = cfg("dan_exam_value")
local val_x = (vp and vp.x) or 185.0
local val_y = (vp and vp.y) or 40.0
local pp = cfg("dan_exam_label_pill")
local pill_x = (pp and pp.x) or 26.0
local pill_y = (pp and pp.y) or 2.0
local seg_bt = cfg("dan_select_exam_border_text")
local gauge_bt = cfg("dan_select_gauge_border_text")
local right_row = 0
local gauge_used = false
local exam_count = box:exam_count()
for i = 0, exam_count - 1 do
local etype = box:exam_type(i)
local range = box:exam_range(i)
local left = (etype == "gauge" and not gauge_used)
if left then gauge_used = true end
-- The cabinet has three right-side slots; skip (don't stop) further right rows.
if left or right_row < 3 then
local BX = left and gx or sx
local BY = left and gy or (sy + pitch * right_row)
draw_abs("yellow_box", left and "exam_gauge_box" or "judge_box",
BX - (left and 0 or 3), BY - (left and 0 or 2), f)
local icon_name = exam_icon_name(etype)
if icon_name then draw_abs("yellow_box", icon_name, BX + pill_x, BY + pill_y, f) end
if left then
if not draw_value_text(self, box:exam_caption(i), gauge_bt,
BX + ((gauge_bt and gauge_bt.x) or 0),
BY + ((gauge_bt and gauge_bt.y) or 0), f) then
draw_value(self, box:exam_red(i), etype, range, BX + 181.0, BY + 45.0, f)
end
else
local gothrough = box:exam_gothrough(i)
local segs = gothrough and 1 or math.min(box:song_count(), 3)
for k = 0, segs - 1 do
local frame = gothrough and 0 or (k + 1)
draw_abs("yellow_box", "exam_seg_label", BX + seg_x + seg_dx * k, BY + seg_y, f, frame)
local red = gothrough and box:exam_red(i) or box:exam_song_red(i, k)
if not draw_value_text(self, box:exam_song_caption(i, k), seg_bt,
BX + ((seg_bt and seg_bt.x) or 0) + seg_dx * k,
BY + ((seg_bt and seg_bt.y) or 0), f) then
draw_value(self, red, etype, range, BX + val_x + seg_dx * k, BY + val_y, f)
end
end
end
if not left then right_row = right_row + 1 end
end
end
end
-1
View File
@@ -3,7 +3,6 @@ Entry.__index = Entry
function Entry.new()
local self = setmetatable({}, Entry)
tex.load_animations("entry")
self.bg_flicker = tex.get_animation(1, "entry")
self.side_select_fade = tex.get_animation(0, "entry")
+114
View File
@@ -0,0 +1,114 @@
ClearAnimation = {}
ClearAnimation.__index = ClearAnimation
function ClearAnimation.new(is_2p)
local self = setmetatable({}, ClearAnimation)
self.is_2p = is_2p
self.idx = is_2p and 1 or 0
self.draw_clear_full = false
self.name = "in"
self.frame = 0
self.bachio_fade_in = tex.get_animation(46, "game")
self.bachio_texture_change = tex.get_animation(47, "game")
self.bachio_out = tex.get_animation(55, "game")
self.bachio_move_out = tex.get_animation(66, "game")
self.clear_highlight_fade_in = tex.get_animation(56, "game")
self.bachio_fade_in:start()
self.bachio_texture_change:start()
self.bachio_out:start()
self.bachio_move_out:start()
self.clear_highlight_fade_in:start()
self.clear_separate_fade_in = {}
self.clear_separate_stretch = {}
for i = 0, 4 do
local fade = anim.fade(100, { initial_opacity = 0.0, final_opacity = 1.0, delay = i * 50 })
fade:start()
self.clear_separate_fade_in[i + 1] = fade
local stretch = anim.text_stretch(200, { delay = i * 50 })
stretch:start()
self.clear_separate_stretch[i + 1] = stretch
end
audio.play_sound("clear", "sound")
self.t_clear = tex.get_texture("game/ending_anim/clear")
self.t_clear_separated = tex.get_texture("game/ending_anim/clear_separated")
self.t_clear_highlight = tex.get_texture("game/ending_anim/clear_highlight")
self.t_bachio_l_in = tex.get_texture("game/ending_anim/bachio_l_in")
self.t_bachio_l_out = tex.get_texture("game/ending_anim/bachio_l_out")
self.t_bachio_r_in = tex.get_texture("game/ending_anim/bachio_r_in")
self.t_bachio_r_out = tex.get_texture("game/ending_anim/bachio_r_out")
local spacing = tex.get_skin_config("clear_animation_x_spacing")
self.clear_anim_x_spacing = (spacing and spacing.x) or 0
return self
end
function ClearAnimation:update(current_ms)
self.bachio_fade_in:update(current_ms)
self.bachio_texture_change:update(current_ms)
self.bachio_out:update(current_ms)
self.bachio_move_out:update(current_ms)
self.clear_highlight_fade_in:update(current_ms)
if self.clear_highlight_fade_in.is_finished or self.clear_highlight_fade_in.attribute >= 1.0 then
self.draw_clear_full = true
end
for i = 1, 5 do
self.clear_separate_fade_in[i]:update(current_ms)
self.clear_separate_stretch[i]:update(current_ms)
end
if self.bachio_texture_change.is_finished then
self.name = "out"
self.frame = math.floor(self.bachio_out.attribute)
else
self.frame = math.floor(self.bachio_texture_change.attribute)
end
end
function ClearAnimation:draw()
if self.draw_clear_full then
tex.draw_texture(self.t_clear, { index = self.idx })
else
for i = 5, 1, -1 do
local j = i - 1
tex.draw_texture(self.t_clear_separated, {
frame = j,
x = j * self.clear_anim_x_spacing,
y = -self.clear_separate_stretch[i].attribute,
y2 = self.clear_separate_stretch[i].attribute,
fade = self.clear_separate_fade_in[i].attribute,
index = self.idx,
})
end
end
tex.draw_texture(self.t_clear_highlight, {
fade = self.clear_highlight_fade_in.attribute,
index = self.idx,
})
local ltex = (self.name == "in") and self.t_bachio_l_in or self.t_bachio_l_out
local rtex = (self.name == "in") and self.t_bachio_r_in or self.t_bachio_r_out
tex.draw_texture(ltex, {
frame = self.frame,
x = -self.bachio_move_out.attribute,
fade = self.bachio_fade_in.attribute,
index = self.idx,
})
tex.draw_texture(rtex, {
frame = self.frame,
x = self.bachio_move_out.attribute,
fade = self.bachio_fade_in.attribute,
index = self.idx,
})
end
+97
View File
@@ -0,0 +1,97 @@
FailAnimation = {}
FailAnimation.__index = FailAnimation
function FailAnimation.new(is_2p)
local self = setmetatable({}, FailAnimation)
self.is_2p = is_2p
self.idx = is_2p and 1 or 0
self.name = "in"
self.frame = 0
self.bachio_fade_in = tex.get_animation(46, "game")
self.bachio_texture_change = tex.get_animation(47, "game")
self.bachio_fall = tex.get_animation(48, "game")
self.bachio_move_out = tex.get_animation(49, "game")
self.bachio_boom_fade_in = tex.get_animation(50, "game")
self.bachio_boom_scale = tex.get_animation(51, "game")
self.bachio_up = tex.get_animation(52, "game")
self.bachio_down = tex.get_animation(53, "game")
self.text_fade_in = tex.get_animation(54, "game")
self.text_fade_in:start()
self.bachio_fade_in:start()
self.bachio_texture_change:start()
self.bachio_fall:start()
self.bachio_move_out:start()
self.bachio_boom_fade_in:start()
self.bachio_boom_scale:start()
self.bachio_up:start()
self.bachio_down:start()
audio.play_sound("fail", "sound")
self.t_fail = tex.get_texture("game/ending_anim/fail")
self.t_bachio_boom = tex.get_texture("game/ending_anim/bachio_boom")
self.t_bachio_l_in = tex.get_texture("game/ending_anim/bachio_l_in")
self.t_bachio_l_fall = tex.get_texture("game/ending_anim/bachio_l_fall")
self.t_bachio_r_in = tex.get_texture("game/ending_anim/bachio_r_in")
self.t_bachio_r_fall = tex.get_texture("game/ending_anim/bachio_r_fall")
local offset2p = tex.get_skin_config("offset_2p")
self.offset_2p_y = (offset2p and offset2p.y) or 0
return self
end
function FailAnimation:update(current_ms)
self.bachio_fade_in:update(current_ms)
self.bachio_texture_change:update(current_ms)
self.bachio_fall:update(current_ms)
self.bachio_move_out:update(current_ms)
self.bachio_boom_fade_in:update(current_ms)
self.bachio_boom_scale:update(current_ms)
self.bachio_up:update(current_ms)
self.bachio_down:update(current_ms)
self.text_fade_in:update(current_ms)
if self.bachio_texture_change.is_finished then
self.name = "fall"
self.frame = math.floor(self.bachio_fall.attribute)
else
self.frame = math.floor(self.bachio_texture_change.attribute)
end
end
function FailAnimation:draw()
tex.draw_texture(self.t_fail, {
fade = self.text_fade_in.attribute,
index = self.idx,
})
local ltex = (self.name == "in") and self.t_bachio_l_in or self.t_bachio_l_fall
local rtex = (self.name == "in") and self.t_bachio_r_in or self.t_bachio_r_fall
tex.draw_texture(ltex, {
frame = self.frame,
x = -self.bachio_move_out.attribute - (self.bachio_up.attribute / 2),
y = self.bachio_down.attribute - self.bachio_up.attribute,
fade = self.bachio_fade_in.attribute,
index = self.idx,
})
tex.draw_texture(rtex, {
frame = self.frame,
x = self.bachio_move_out.attribute + (self.bachio_up.attribute / 2),
y = self.bachio_down.attribute - self.bachio_up.attribute,
fade = self.bachio_fade_in.attribute,
index = self.idx,
})
tex.draw_texture(self.t_bachio_boom, {
scale = self.bachio_boom_scale.attribute,
center = true,
y = self.idx * self.offset_2p_y,
fade = self.bachio_boom_fade_in.attribute,
index = self.idx,
})
end
+194
View File
@@ -0,0 +1,194 @@
FCAnimation = {}
FCAnimation.__index = FCAnimation
function FCAnimation.new(is_2p, donderful)
local self = setmetatable({}, FCAnimation)
self.is_2p = is_2p
self.idx = is_2p and 1 or 0
self.draw_clear_full = false
self.name = "in"
self.frame = 0
local has_dfc_tex = donderful
and tex.get_texture("game/ending_donderful/full_combo") ~= nil
and tex.get_texture("game/ending_donderful/full_combo_highlight") ~= nil
and tex.get_texture("game/ending_donderful/full_combo_overlay") ~= nil
local subset = has_dfc_tex and "ending_donderful/" or "ending_anim/"
self.combo_tex = tex.get_texture("game/" .. subset .. "full_combo")
self.combo_highlight_tex = tex.get_texture("game/" .. subset .. "full_combo_highlight")
self.combo_overlay_tex = tex.get_texture("game/" .. subset .. "full_combo_overlay")
self.has_panel = has_dfc_tex and tex.get_texture("game/ending_donderful/background") ~= nil
self.panel_tex = self.has_panel and tex.get_texture("game/ending_donderful/background") or nil
self.panel_fade_in = anim.fade(250, { initial_opacity = 0.0, final_opacity = 1.0, delay = 0 })
local has_dfc_sound = donderful and audio.has_sound("donderful_combo")
self.combo_sound = has_dfc_sound and "donderful_combo" or "full_combo"
self.combo_voice = (donderful and audio.has_sound("donderful_combo_voice")) and "donderful_combo_voice" or "full_combo_voice"
self.bachio_fade_in = tex.get_animation(46, "game")
self.bachio_texture_change = tex.get_animation(47, "game")
self.bachio_out = tex.get_animation(55, "game")
self.bachio_move_out = tex.get_animation(49, "game")
self.bachio_fade_in:start()
self.bachio_texture_change:start()
self.bachio_out:start()
self.bachio_move_out:start()
self.clear_separate_fade_in = {}
self.clear_separate_stretch = {}
for i = 0, 4 do
local fade = anim.fade(100, { initial_opacity = 1.0, final_opacity = 0.0, delay = i * 50 })
fade:start()
self.clear_separate_fade_in[i + 1] = fade
local stretch = anim.text_stretch(200, { delay = i * 50 })
stretch:start()
self.clear_separate_stretch[i + 1] = stretch
end
self.clear_highlight_fade_in = tex.get_animation(56, "game")
self.fc_highlight_up = tex.get_animation(57, "game")
self.fc_highlight_fade_out = tex.get_animation(58, "game")
self.bachio_move_out_2 = tex.get_animation(59, "game")
self.bachio_move_up = tex.get_animation(60, "game")
self.fan_fade_in = tex.get_animation(61, "game")
self.fan_texture_change = tex.get_animation(62, "game")
self.clear_highlight_fade_in:start()
self.fc_highlight_up:start()
audio.play_sound(self.combo_sound, "sound")
self.t_fan_l = tex.get_texture("game/ending_anim/fan_l")
self.t_fan_r = tex.get_texture("game/ending_anim/fan_r")
self.t_clear_separated = tex.get_texture("game/ending_anim/clear_separated")
self.t_clear_highlight = tex.get_texture("game/ending_anim/clear_highlight")
self.t_bachio_l_in = tex.get_texture("game/ending_anim/bachio_l_in")
self.t_bachio_l_out = tex.get_texture("game/ending_anim/bachio_l_out")
self.t_bachio_r_in = tex.get_texture("game/ending_anim/bachio_r_in")
self.t_bachio_r_out = tex.get_texture("game/ending_anim/bachio_r_out")
local spacing = tex.get_skin_config("clear_animation_x_spacing")
self.clear_anim_x_spacing = (spacing and spacing.x) or 0
return self
end
function FCAnimation:update(current_ms)
self.bachio_fade_in:update(current_ms)
self.bachio_texture_change:update(current_ms)
self.bachio_out:update(current_ms)
self.bachio_move_out:update(current_ms)
self.clear_highlight_fade_in:update(current_ms)
self.fc_highlight_up:update(current_ms)
self.fc_highlight_fade_out:update(current_ms)
self.bachio_move_out_2:update(current_ms)
self.bachio_move_up:update(current_ms)
self.fan_fade_in:update(current_ms)
self.fan_texture_change:update(current_ms)
if self.fc_highlight_up.is_finished and not self.fc_highlight_fade_out.is_started then
self.fc_highlight_fade_out:start()
self.bachio_move_out_2:start()
self.bachio_move_up:start()
self.fan_fade_in:start()
self.fan_texture_change:start()
audio.play_sound(self.combo_voice, "voice")
end
if self.clear_highlight_fade_in.is_finished then
if not self.draw_clear_full and self.has_panel then
self.panel_fade_in:start()
end
self.draw_clear_full = true
end
self.panel_fade_in:update(current_ms)
for i = 1, 5 do
self.clear_separate_fade_in[i]:update(current_ms)
self.clear_separate_stretch[i]:update(current_ms)
end
if self.bachio_texture_change.is_finished then
self.name = "out"
self.frame = math.floor(self.bachio_out.attribute)
else
self.frame = math.floor(self.bachio_texture_change.attribute)
end
end
function FCAnimation:draw()
if self.draw_clear_full then
if self.has_panel then
tex.draw_texture(self.panel_tex, {
fade = self.panel_fade_in.attribute,
index = self.idx,
})
end
tex.draw_texture(self.t_fan_l, {
frame = math.floor(self.fan_texture_change.attribute),
fade = self.fan_fade_in.attribute,
index = self.idx,
})
tex.draw_texture(self.t_fan_r, {
frame = math.floor(self.fan_texture_change.attribute),
fade = self.fan_fade_in.attribute,
index = self.idx,
})
tex.draw_texture(self.combo_overlay_tex, {
y = -self.fc_highlight_up.attribute,
fade = 0.5,
index = self.idx,
})
tex.draw_texture(self.combo_tex, {
y = -self.fc_highlight_up.attribute,
index = self.idx,
})
tex.draw_texture(self.combo_highlight_tex, {
y = -self.fc_highlight_up.attribute,
fade = self.fc_highlight_fade_out.attribute,
index = self.idx,
})
else
for i = 5, 1, -1 do
local j = i - 1
tex.draw_texture(self.t_clear_separated, {
frame = j,
x = j * self.clear_anim_x_spacing,
y = -self.clear_separate_stretch[i].attribute,
y2 = self.clear_separate_stretch[i].attribute,
fade = self.clear_separate_fade_in[i].attribute,
index = self.idx,
})
end
end
tex.draw_texture(self.t_clear_highlight, {
fade = self.clear_highlight_fade_in.attribute,
index = self.idx,
})
local ltex = (self.name == "in") and self.t_bachio_l_in or self.t_bachio_l_out
local rtex = (self.name == "in") and self.t_bachio_r_in or self.t_bachio_r_out
tex.draw_texture(ltex, {
frame = self.frame,
x = (-self.bachio_move_out.attribute - self.bachio_move_out_2.attribute) * 1.15,
y = -self.bachio_move_up.attribute,
fade = self.bachio_fade_in.attribute,
index = self.idx,
})
tex.draw_texture(rtex, {
frame = self.frame,
x = (self.bachio_move_out.attribute + self.bachio_move_out_2.attribute) * 1.15,
y = -self.bachio_move_up.attribute,
fade = self.bachio_fade_in.attribute,
index = self.idx,
})
end
+72
View File
@@ -0,0 +1,72 @@
ResultTransition = {}
ResultTransition.__index = ResultTransition
function ResultTransition.new(player_num)
local self = setmetatable({}, ResultTransition)
self.player_num = player_num
self.move = tex.get_animation(5, "global") -- result shutter move in
local screen_w = tex.get_screen_width()
if player_num == 3 then -- PlayerNum.TWO_PLAYER
self.t_shutter_1p = tex.get_texture("global/result_transition/1p_shutter")
self.t_shutter_2p = tex.get_texture("global/result_transition/2p_shutter")
self.t_footer_1p = tex.get_texture("global/result_transition/1p_shutter_footer")
self.t_footer_2p = tex.get_texture("global/result_transition/2p_shutter_footer")
self.has_footer = self.t_footer_1p ~= nil and self.t_footer_2p ~= nil
local f1 = tex.get_texture_info("result_transition", "1p_shutter_footer")
local f2 = tex.get_texture_info("result_transition", "2p_shutter_footer")
self.tex_height = (f1 and f1.height) or 0
self.tex_height_2p = (f2 and f2.height) or 0
local s = tex.get_texture_info("result_transition", "1p_shutter")
self.shutter_width = (s and s.width) or (screen_w / 5)
else
local ps = (player_num == 2) and "2p" or "1p" -- PlayerNum.P2, else 1p
self.t_shutter = tex.get_texture("global/result_transition/" .. ps .. "_shutter")
self.t_footer = tex.get_texture("global/result_transition/" .. ps .. "_shutter_footer")
self.has_footer = self.t_footer ~= nil
local f = tex.get_texture_info("result_transition", ps .. "_shutter_footer")
self.tex_height = (f and f.height) or 0
local s = tex.get_texture_info("result_transition", ps .. "_shutter")
self.shutter_width = (s and s.width) or (screen_w / 5)
end
return self
end
function ResultTransition:start()
self.move:start()
end
function ResultTransition:update(current_ms)
self.move:update(current_ms)
end
function ResultTransition:is_finished()
return false -- the engine's move animation and result_transition voice decide
end
function ResultTransition:draw()
if not self.has_footer then return end
local attr = self.move.attribute
local screen_h = tex.get_screen_height()
local screen_w = tex.get_screen_width()
local x = 0
while x < screen_w do
if self.player_num == 3 then
tex.draw_texture(self.t_shutter_1p, { x = x, y = -screen_h + attr })
tex.draw_texture(self.t_shutter_2p, { x = x, y = screen_h - attr })
tex.draw_texture(self.t_footer_1p, { x = x, y = -(self.tex_height * 3) + attr })
tex.draw_texture(self.t_footer_2p, { x = x, y = screen_h + (self.tex_height_2p * 2) - attr })
else
tex.draw_texture(self.t_shutter, { x = x, y = -screen_h + attr })
tex.draw_texture(self.t_shutter, { x = x, y = screen_h - attr })
tex.draw_texture(self.t_footer, { x = x, y = -(self.tex_height * 3) + attr })
tex.draw_texture(self.t_footer, { x = x, y = screen_h + (self.tex_height * 2) - attr })
end
x = x + self.shutter_width
end
end
+34
View File
@@ -0,0 +1,34 @@
SongTransition = {}
SongTransition.__index = SongTransition
function SongTransition.new(title, subtitle, is_second)
local self = setmetatable({}, SongTransition)
self.is_second = is_second and true or false
self.t_rainbow_bg_bottom = tex.get_texture("global/rainbow_transition/rainbow_bg_bottom")
self.t_rainbow_bg_top = tex.get_texture("global/rainbow_transition/rainbow_bg_top")
self.t_rainbow_bg = tex.get_texture("global/rainbow_transition/rainbow_bg")
self.t_chara_left = tex.get_texture("global/rainbow_transition/chara_left")
self.t_chara_right = tex.get_texture("global/rainbow_transition/chara_right")
self.t_chara_center = tex.get_texture("global/rainbow_transition/chara_center")
return self
end
function SongTransition:draw_bg(total_offset, rainbow_up, mini_up, chara_down)
tex.draw_texture(self.t_rainbow_bg_bottom, { y = -rainbow_up - total_offset })
tex.draw_texture(self.t_rainbow_bg_top, { y = -rainbow_up - total_offset })
tex.draw_texture(self.t_rainbow_bg, { y = -rainbow_up - total_offset })
local offset = chara_down
local chara_offset = 0
if self.is_second then
offset = chara_down - mini_up / 3
local cfg = tex.get_skin_config("transition_chara_offset")
chara_offset = (cfg and cfg.y) or 0
end
tex.draw_texture(self.t_chara_left, { x = -mini_up / 2 - chara_offset, y = -mini_up + offset - total_offset })
tex.draw_texture(self.t_chara_right, { x = mini_up / 2 + chara_offset, y = -mini_up + offset - total_offset })
tex.draw_texture(self.t_chara_center, { y = -rainbow_up + offset - total_offset })
end
-1
View File
@@ -3,7 +3,6 @@ BottomCharacters.__index = BottomCharacters
function BottomCharacters.new()
local self = setmetatable({}, BottomCharacters)
tex.load_animations("result")
self.move_up = tex.get_animation(7, "result")
self.move_down = tex.get_animation(8, "result")
-1
View File
@@ -3,7 +3,6 @@ HighScoreIndicator.__index = HighScoreIndicator
function HighScoreIndicator.new(score_diff, is_2p)
local self = setmetatable({}, HighScoreIndicator)
tex.load_animations("result")
self.move_anim = tex.get_animation(18, "result")
self.fade_anim = tex.get_animation(19, "result")
-1
View File
@@ -3,7 +3,6 @@ ResultCrown.__index = ResultCrown
function ResultCrown.new(crown_type, is_2p)
local self = setmetatable({}, ResultCrown)
tex.load_animations("result")
self.resize = tex.get_animation(2, "result")
self.resize_fix = tex.get_animation(3, "result")
-1
View File
@@ -3,7 +3,6 @@ ResultCrownMessage.__index = ResultCrownMessage
function ResultCrownMessage.new(frame, is_2p)
local self = setmetatable({}, ResultCrownMessage)
tex.load_animations("result")
self.scale_in = tex.get_animation(21, "result")
self.scale_fix = tex.get_animation(22, "result")
-1
View File
@@ -38,7 +38,6 @@ ResultFadeIn.__index = ResultFadeIn
function ResultFadeIn.new(player_num)
local self = setmetatable({}, ResultFadeIn)
self.player_num = player_num
tex.load_animations("result")
self.fade_in = tex.get_animation(15, "result")
self.fade_in:start()
return self
-1
View File
@@ -6,7 +6,6 @@ function ResultPlayer.new(player_num, is_2p, has_2p,
crown_type, score_diff, score_method,
mod_display, mod_inverse, mod_random, mod_speed)
local self = setmetatable({}, ResultPlayer)
tex.load_animations("result")
self.player_num = player_num
self.is_2p = is_2p
@@ -0,0 +1,68 @@
-- BackBox rendering ("back" brown board + vertical label), split out of song_select.lua.
-- Mirrors src/objects/song_select/file_navigator/box_back.cpp.
local base = require("song_select/file_navigator/box_base")
local T = base.T
local draw_shadows = base.draw_shadows
local draw_yellow_box = base.draw_yellow_box
local draw_base_closed = base.draw_base_closed
local WHITE = { 255, 255, 255, 255 }
local BLACK = { 0, 0, 0, 255 }
local OUTLINE_COLOR = { 77, 39, 0, 255 }
-- back_text / back_text_highlight (vertical), cached on self.
local function back_texts(self)
if self._back_texts then return self._back_texts end
local cfg = tex.get_skin_config("box_back_text") or { font_size = 40 }
local label = tex.skin_text("box_back_text")
if label == "" then label = "Back" end
local back_text = text.create_raw_text(label, cfg.font_size or 40, WHITE, OUTLINE_COLOR, true, 5.0, 2.0, 1.0)
local back_text_highlight = text.create_raw_text(label, cfg.font_size or 40, WHITE, BLACK, true, 5.0, 2.0, 1.0)
self._back_texts = { back_text = back_text, back_text_highlight = back_text_highlight }
return self._back_texts
end
function SongSelect:draw_back_closed(box)
draw_base_closed(box)
local bx = box:box_x()
local by = box:box_y()
local fade = box.fade.attribute
tex.draw_texture(T("box", "back_icon"), { x = bx, y = by, fade = fade })
local texts = back_texts(self)
local cfg = tex.get_skin_config("box_back_text") or { x = 47, y = 173 }
texts.back_text:draw({
x = bx + cfg.x - texts.back_text.width / 2.0,
y = by + cfg.y,
fade = fade
})
end
function SongSelect:draw_back_open(box)
local bx = box:box_x()
local by = box:box_y()
local mfade = math.min(box.fade.attribute, box.open_fade.attribute)
draw_shadows(bx, by, mfade, 1)
local right_out = tex.get_animation(10)
local right_out_2 = tex.get_animation(14)
local x = bx
draw_yellow_box(false, mfade, by)
x = x + (right_out.attribute * 0.85 - right_out.start_position * 0.85)
+ right_out_2.attribute - right_out_2.start_position
tex.draw_texture(T("box", "back_icon_highlight"), { x = x, y = by, fade = mfade })
local texts = back_texts(self)
local cfg = tex.get_skin_config("box_back_text") or { x = 47, y = 173 }
texts.back_text_highlight:draw({
x = x + cfg.x - texts.back_text_highlight.width / 2.0,
y = by + cfg.y,
fade = mfade
})
tex.draw_texture(T("box", "back_graphic"), { x = x, y = by, fade = mfade })
end
@@ -0,0 +1,232 @@
-- Shared BaseBox + YellowBox draw code and text helpers, split out of song_select.lua.
-- Mirrors src/objects/song_select/file_navigator/box_base.cpp and box_yellow.cpp.
-- All positions are in the skin's own pixels (1280x720 for PyTaikoGreen); box positions
-- and engine animations are already in screen pixels.
local M = {}
-- TextureIndex (box/folder_texture colour frame) — must match enums.h.
local TI_NONE, TI_VOCALOID, TI_DEFAULT, TI_RECOMMENDED, TI_FAVORITE, TI_RECENT = 0, 1, 2, 3, 4, 5
-- GenreIndex (raw enum; folder_graphic / diff_overlay frame) — must match enums.h.
local GI_DIFFICULTY = 14
M.TI_NONE, M.TI_DEFAULT = TI_NONE, TI_DEFAULT
M.GI_DIFFICULTY = GI_DIFFICULTY
-- Lazy texture cache: tex.get_texture returns nil on a miss, and the C++ board code
-- dereferences these unconditionally, so cache the resolved id (or nil) once.
local _tex = {}
local function T(subset, name)
local key = subset .. "/" .. name
local id = _tex[key]
if id == nil then
id = tex.get_texture("song_select/" .. subset .. "/" .. name)
_tex[key] = id
end
return id
end
M.T = T
-- Lazy skin_config geometry.
local _song_box_bg_width, _yellow_box_bottom_y, _edge_height
local function song_box_bg_width()
if _song_box_bg_width == nil then
local c = tex.get_skin_config("song_box_bg")
_song_box_bg_width = (c and c.width) or 0
end
return _song_box_bg_width
end
local function yellow_box_bottom_y()
if _yellow_box_bottom_y == nil then
local c = tex.get_skin_config("yellow_box_bottom")
_yellow_box_bottom_y = (c and c.y) or 0
end
return _yellow_box_bottom_y
end
local function edge_height()
if _edge_height == nil then
local info = tex.get_texture_info("yellow_box", "yellow_box_bottom_right")
_edge_height = (info and info.height) or 0
end
return _edge_height
end
M.song_box_bg_width = song_box_bg_width
-- Count UTF-8 codepoints (== Lua string length for ASCII; handles multibyte genres).
function M.utf8_char_count(s)
local count = 0
local i = 1
while i <= #s do
local c = s:byte(i)
if not c then break end
if (c & 0xC0) ~= 0x80 then count = count + 1 end
i = i + 1
end
return count
end
-- The five yellow_box shadow strips. index 0 = closed (1p), index 1 = open (2p/highlight).
function M.draw_shadows(bx, by, fade, index)
tex.draw_texture(T("yellow_box", "shadow_bottom_left"), { x = bx, y = by, fade = fade, index = index })
tex.draw_texture(T("yellow_box", "shadow_bottom"), { x = bx, y = by, fade = fade, index = index })
tex.draw_texture(T("yellow_box", "shadow_bottom_right"), { x = bx, y = by, fade = fade, index = index })
tex.draw_texture(T("yellow_box", "shadow_right"), { x = bx, y = by, fade = fade, index = index })
tex.draw_texture(T("yellow_box", "shadow_top_right"), { x = bx, y = by, fade = fade, index = index })
end
-- The 9-slice yellow frame (YellowBox::draw). is_diff picks the diff-select animation set.
function M.draw_yellow_box(is_diff, fade, y_offset)
y_offset = y_offset or 0
local right_x = tex.get_animation(is_diff and 14 or 10).attribute
local left_x = tex.get_animation(is_diff and 13 or 9).attribute
local center_width = tex.get_animation(is_diff and 15 or 11).attribute
local top_y = tex.get_animation(16).attribute
local center_height = tex.get_animation(17).attribute
local bottom_y = yellow_box_bottom_y()
local eh = edge_height()
tex.draw_texture(T("yellow_box", "yellow_box_bottom_right"), { x = right_x, y = bottom_y + y_offset, fade = fade })
tex.draw_texture(T("yellow_box", "yellow_box_bottom_left"), { x = left_x, y = bottom_y + y_offset, fade = fade })
tex.draw_texture(T("yellow_box", "yellow_box_top_right"), { x = right_x, y = top_y + y_offset, fade = fade })
tex.draw_texture(T("yellow_box", "yellow_box_top_left"), { x = left_x, y = top_y + y_offset, fade = fade })
tex.draw_texture(T("yellow_box", "yellow_box_bottom"), { x = left_x + eh, y = bottom_y + y_offset, x2 = center_width, fade = fade })
tex.draw_texture(T("yellow_box", "yellow_box_right"), { x = right_x, y = top_y + eh + y_offset, y2 = center_height, fade = fade })
tex.draw_texture(T("yellow_box", "yellow_box_left"), { x = left_x, y = top_y + eh + y_offset, y2 = center_height, fade = fade })
tex.draw_texture(T("yellow_box", "yellow_box_top"), { x = left_x + eh, y = top_y + y_offset, x2 = center_width, fade = fade })
tex.draw_texture(T("yellow_box", "yellow_box_center"), { x = left_x + eh, y = top_y + eh + y_offset, x2 = center_width, y2 = center_height, fade = fade })
end
-- BaseBox::draw_closed — the generic folder_texture board (colour frame = texture_index).
-- BackBox and DanBox reuse this; SongBox/FolderBox call it before their own closed content.
function M.draw_base_closed(box)
local bx = box:box_x()
local by = box:box_y()
local fade = box.fade.attribute
local tex_index = box:texture_index()
local use_shader = box:has_recolor()
M.draw_shadows(bx, by, fade, 0)
if use_shader then box:begin_recolor() end
tex.draw_texture(T("box", "folder_texture_left"), { frame = tex_index, x = bx, y = by, fade = fade })
tex.draw_texture(T("box", "folder_texture"), { frame = tex_index, x = bx, y = by, x2 = song_box_bg_width(), fade = fade })
tex.draw_texture(T("box", "folder_texture_right"), { frame = tex_index, x = bx, y = by, fade = fade })
if use_shader then box:end_recolor() end
if tex_index == TI_DEFAULT then
tex.draw_texture(T("box", "genre_overlay"), { x = bx, y = by, fade = fade })
end
if box:genre_index() == GI_DIFFICULTY then
tex.draw_texture(T("box", "diff_overlay"), { x = bx, y = by, fade = fade })
end
end
-- The vertical song/folder title (BaseBox::load_text name). text_color is always white for
-- song/folder/back; the outline is the genre's fore_color. Cached per box on `self`.
function M.vertical_name(self, box)
self._name_cache = self._name_cache or {}
local name = box.text_name or ""
local fc = box:fore_color() or { r = 0, g = 0, b = 0, a = 255 }
local key = box:path() .. "\0" .. tostring(fc.r or 0) .. "." .. tostring(fc.g or 0) .. "." .. tostring(fc.b or 0)
local t = self._name_cache[key]
if t == nil then
local cfg = tex.get_skin_config("song_box_name")
local font_size = (cfg and cfg.font_size) or 40
if M.utf8_char_count(name) >= 30 then
font_size = font_size - 10 * tex.get_screen_scale()
end
local outline = { fc.r or 0, fc.g or 0, fc.b or 0, fc.a or 255 }
t = text.create_raw_text(name, font_size, { 255, 255, 255, 255 }, outline, true, 5.0, 2.0, 1.0)
self._name_cache[key] = t
end
return t
end
-- Highest-crown helper shared by SongBox/FolderBox closed states.
-- Returns frame (min(diff, URA)) and the crown value, or nil.
function M.highest_crown(map)
local highest_key = -1
for k in pairs(map) do
if type(k) == "number" and k > highest_key then highest_key = k end
end
if highest_key < 0 then return nil end
local frame = math.min(4, highest_key) -- Difficulty::URA == 4
return map[highest_key], frame
end
-- Draw a single crown icon (crown_dfc/crown_fc/crown_clear) at frame, position px, y.
-- crowns: {0=none,1=clear,2=fc,3=dfc}
function M.draw_crown(crown, frame, px, y, fade)
if crown == 3 then
tex.draw_texture(T("yellow_box", "crown_dfc"), { frame = frame, x = px, y = y, fade = fade })
elseif crown == 2 then
tex.draw_texture(T("yellow_box", "crown_fc"), { frame = frame, x = px, y = y, fade = fade })
elseif crown and crown >= 1 then
tex.draw_texture(T("yellow_box", "crown_clear"), { frame = frame, x = px, y = y, fade = fade })
end
end
-- Localized song count ("全{0}曲" / "{0} Songs").
function M.format_song_count(count)
local template = tex.skin_text("song_count")
if template == "" then template = "全{0}曲" end
local out, n = template:gsub("{0}", tostring(count))
if n == 0 then out = template .. tostring(count) end
return out
end
-- ---------------------------------------------------------------------------
-- Yellow-box animation driving. The engine no longer starts/advances the shared
-- yellow-box animations (9..18); the skin does it, keyed off the box draw_state.
-- ---------------------------------------------------------------------------
local YB_OPEN_ANIMS = { 9, 10, 11 }
local YB_DIFF_ANIMS = { 13, 14, 15, 16, 17, 18 }
local YB_RESET_ANIMS = { 9, 10, 11, 14, 16, 17 }
local function yb_anim(id) return tex.get_animation(id) end
local function yb_open() -- expand_box reset + create_anim (start 9/10/11)
for _, id in ipairs(YB_RESET_ANIMS) do yb_anim(id):reset() end
for _, id in ipairs(YB_OPEN_ANIMS) do yb_anim(id):start() end
end
local function yb_diff() -- create_anim_2 (start 13..18)
for _, id in ipairs(YB_DIFF_ANIMS) do yb_anim(id):start() end
end
local function yb_back() -- exit_box -> create_anim (reset 14/16/17 + start 9/10/11)
yb_anim(14):reset(); yb_anim(16):reset(); yb_anim(17):reset()
for _, id in ipairs(YB_OPEN_ANIMS) do yb_anim(id):start() end
end
-- Per-open-box state (only one box is open at a time).
local yb_box = { path = nil, state = "closed" }
function M.reset_yellow_box()
yb_box.path, yb_box.state = nil, "closed"
end
-- Call once per box from draw_box; detects open/enter/exit transitions.
function M.track_yellow_box(box)
local state = box:draw_state()
local path = box:path()
if state == "closed" then
if yb_box.path == path then
yb_box.path, yb_box.state = nil, "closed"
end
return
end
if yb_box.path ~= path then
yb_box.path, yb_box.state = path, state
yb_open()
elseif yb_box.state ~= state then
yb_box.state = state
if state == "diff_select" then yb_diff() else yb_back() end
end
end
-- Call once per frame from SongSelect:update; advances the shared yellow-box animations.
function M.update_yellow_box(current_ms)
for _, id in ipairs(YB_OPEN_ANIMS) do yb_anim(id):update(current_ms) end
for _, id in ipairs(YB_DIFF_ANIMS) do yb_anim(id):update(current_ms) end
end
return M
@@ -0,0 +1,232 @@
-- FolderBox rendering (genre folder closed/open), split out of song_select.lua.
-- Mirrors src/objects/song_select/file_navigator/box_folder.cpp.
local base = require("song_select/file_navigator/box_base")
local T = base.T
local draw_shadows = base.draw_shadows
local draw_base_closed = base.draw_base_closed
local vertical_name = base.vertical_name
local WHITE = { 255, 255, 255, 255 }
local BLACK = { 0, 0, 0, 255 }
local function is_2p_screen()
return (tex.current_screen and tex.current_screen() or "") == "SONG_SELECT_2P"
end
local function make_count_text(count)
local count_cfg = tex.get_skin_config("song_tja_count") or { font_size = 35 }
return text.create_raw_text(base.format_song_count(count), count_cfg.font_size or 35, WHITE, BLACK, false, 5.0, 2.0, 1.0)
end
-- Folder text objects (hori_name, tja_count_text, explanation) from FolderBox::load_text,
-- cached per box on self. The song count is filled in by a background scan after the box is
-- first drawn, so its text is rebuilt whenever the count changes (it stayed at 0 otherwise).
local function folder_texts(self, box)
self._folder_texts = self._folder_texts or {}
local key = box:path()
local t = self._folder_texts[key]
if t == nil then
local name = box.text_name or ""
local hori_cfg = tex.get_skin_config("song_hori_name") or { font_size = 40 }
local hori_name = text.create_raw_text(name, hori_cfg.font_size or 40, WHITE, BLACK, false, 5.0, 2.0, 1.0)
local count = box.tja_count or 0
local tja_count_text = make_count_text(count)
local explanation = box:explanation() or {}
local exp_cfg = tex.get_skin_config("box_folder_explanation") or { font_size = 34 }
local exp_box = tex.get_skin_config("box_folder_explanation_box") or { width = 160 }
local lang = tex.language()
local is_cjk = text.language_is_cjk(lang)
local exp_texts = {}
for i = 1, 3 do
local s = explanation[i]
if s and s ~= "" then
local content = s
if not is_cjk then
content = text.word_wrap(s, exp_cfg.font_size or 34, 2.0, (exp_box.width or 160) - 20.0)
end
exp_texts[i] = text.create_raw_text(content, exp_cfg.font_size or 34, WHITE, BLACK, is_cjk, 5.0, 2.0, 1.0)
end
end
t = { hori_name = hori_name, tja_count_text = tja_count_text, count = count, explanation = exp_texts }
self._folder_texts[key] = t
else
local count = box.tja_count or 0
if count ~= t.count then
t.tja_count_text = make_count_text(count)
t.count = count
end
end
return t
end
-- FolderBox::draw_closed crown badge (best crown, no fade).
local function draw_folder_crown(box, bx, by)
local function draw_one(info, px)
if not info then return end
base.draw_crown(info.crown, info.frame, px, by, 1.0)
end
local crown_1p = box:highest_crown()
local crown_2p
if is_2p_screen() then crown_2p = box:highest_crown_p2() end
if crown_2p then
local info = tex.get_texture_info("yellow_box", "crown_dfc")
local half = (info and info.width or 0) * 0.35
draw_one(crown_1p, bx - half)
draw_one(crown_2p, bx + half)
elseif crown_1p then
draw_one(crown_1p, bx)
end
end
function SongSelect:draw_folder_closed(box)
draw_base_closed(box)
local bx = box:box_x()
local by = box:box_y()
local fade = box.fade.attribute
local tex_index = box:texture_index()
local use_shader = box:has_recolor()
if use_shader then box:begin_recolor() end
tex.draw_texture(T("box", "folder_clip"), { frame = tex_index, x = bx - (1.0 * tex.get_screen_scale()), y = by, fade = fade })
if use_shader then box:end_recolor() end
local name = vertical_name(self, box)
local name_cfg = tex.get_skin_config("song_box_name") or { x = 47, y = 129, height = 417 }
local name_h = math.min(name.height, name_cfg.height)
name:draw({
x = bx + name_cfg.x - math.floor(name.width / 2),
y = name_cfg.y + by,
y2 = name_h - name.height,
fade = fade
})
draw_folder_crown(box, bx, by)
end
local function draw_open_bg(self, box, fade)
local bx = box:box_x()
local by = box:box_y()
local shadow_fade = math.min(fade, box.open_fade.attribute)
draw_shadows(bx, by, shadow_fade, 1)
local tex_index = box:texture_index()
local use_shader = box:has_recolor()
local open_anim = box.open_anim.attribute
if open_anim >= (100.0 * tex.get_screen_scale()) then
if use_shader then box:begin_recolor() end
tex.draw_texture(T("box", "folder_top_edge"), { frame = tex_index, mirror = "horizontal", y = by - open_anim, fade = fade })
tex.draw_texture(T("box", "folder_top"), { frame = tex_index, y = by - open_anim, fade = fade })
local top_cfg = tex.get_skin_config("song_folder_top") or { x = 268 }
tex.draw_texture(T("box", "folder_top_edge"), { frame = tex_index, x = top_cfg.x, y = by - open_anim, fade = fade })
if use_shader then box:end_recolor() end
end
if use_shader then box:begin_recolor() end
tex.draw_texture(T("box", "folder_texture_left"), { frame = tex_index, x = bx - open_anim, y = by, fade = fade })
tex.draw_texture(T("box", "folder_texture"), { frame = tex_index, x = bx - open_anim, y = by, x2 = (open_anim * 2.0) + base.song_box_bg_width(), fade = fade })
tex.draw_texture(T("box", "folder_texture_right"), { frame = tex_index, x = bx + open_anim, y = by, fade = fade })
if use_shader then box:end_recolor() end
end
local function draw_open_fg(self, box, fade)
local texts = folder_texts(self, box)
local open_anim = box.open_anim.attribute
if open_anim >= (100.0 * tex.get_screen_scale()) then
local hori_cfg = tex.get_skin_config("song_hori_name") or { x = 642, y = 204, width = 300 }
local dest_width = math.min(hori_cfg.width, texts.hori_name.width)
texts.hori_name:draw({
x = hori_cfg.x - dest_width / 2.0,
y = hori_cfg.y - open_anim,
x2 = dest_width - texts.hori_name.width,
fade = fade
})
end
if box:texture_index() == base.TI_DEFAULT then
tex.draw_texture(T("box", "genre_overlay_large"), { fade = fade })
end
if box:genre_index() == base.GI_DIFFICULTY then
tex.draw_texture(T("box", "diff_overlay_large"), { fade = fade })
end
if box:genre_index() ~= base.GI_DIFFICULTY then
tex.draw_texture(T("yellow_box", "song_count_back"), { fade = math.min(fade, 0.5) })
local count_cfg = tex.get_skin_config("song_tja_count") or { x = 560, y = 130, width = 184 }
local dest_width = math.min(count_cfg.width, texts.tja_count_text.width)
texts.tja_count_text:draw({
x = count_cfg.x - dest_width / 2.0,
y = count_cfg.y,
x2 = dest_width - texts.tja_count_text.width,
fade = fade
})
end
if box:has_box_texture() then
local box_cfg = tex.get_skin_config("box_texture") or { x = 48, y = 330 }
local max_size = tex.get_skin_config("box_folder_max_size") or { width = 344, height = 424 }
box:draw_box_texture({
cx = box:box_x() + box_cfg.x,
cy = box_cfg.y + box:box_y(),
max_w = max_size.width,
max_h = max_size.height,
fade = fade
})
elseif box:texture_index() ~= base.TI_DEFAULT then
tex.draw_texture(T("box", "folder_graphic"), { frame = box:genre_index(), fade = fade })
local exp_cfg = tex.get_skin_config("box_folder_explanation") or { x = 776, y = 131, width = 43, height = 73 }
local exp_box = tex.get_skin_config("box_folder_explanation_box") or { x = 650, y = 125, width = 160, height = 400 }
local lang = tex.language()
local is_cjk = text.language_is_cjk(lang)
if is_cjk then
for i = 1, 3 do
local t = texts.explanation[i]
if t then
local idx = i - 1
local center_x = exp_cfg.x - exp_cfg.width * idx
t:draw({ x = center_x - t.width / 2.0, y = exp_cfg.y + exp_cfg.height * idx, fade = fade })
end
end
else
local total_h = 0.0
for i = 1, 3 do
if texts.explanation[i] then total_h = total_h + texts.explanation[i].height end
end
local scale = total_h > exp_box.height and (exp_box.height / total_h) or 1.0
local y = exp_box.y
for i = 1, 3 do
local t = texts.explanation[i]
if t then
local dest_h = t.height * scale
t:draw({
x = exp_box.x + (exp_box.width - t.width) / 2.0,
y = y,
y2 = dest_h - t.height,
fade = fade
})
y = y + dest_h
end
end
end
end
end
function SongSelect:draw_folder_open(box)
if box.entered then
draw_open_bg(self, box, 0.0)
draw_open_fg(self, box, box:enter_fade().attribute)
else
draw_open_bg(self, box, math.min(1.0, box.fade.attribute))
draw_open_fg(self, box, math.min(box.open_fade.attribute, box.fade.attribute))
end
end
@@ -0,0 +1,366 @@
-- SongBox rendering (song board closed/open/diff-select), split out of song_select.lua.
-- Mirrors src/objects/song_select/file_navigator/box_song.cpp.
local base = require("song_select/file_navigator/box_base")
local T = base.T
local utf8_char_count = base.utf8_char_count
local draw_shadows = base.draw_shadows
local draw_yellow_box = base.draw_yellow_box
local draw_base_closed = base.draw_base_closed
local vertical_name = base.vertical_name
-- Difficulty enum (must match enums.h).
local DIFF_EASY, DIFF_NORMAL, DIFF_HARD, DIFF_ONI, DIFF_URA = 0, 1, 2, 3, 4
local BLACK = { 0, 0, 0, 255 }
local WHITE = { 255, 255, 255, 255 }
local BLANK = { 0, 0, 0, 0 }
local function is_2p_screen()
return (tex.current_screen and tex.current_screen() or "") == "SONG_SELECT_2P"
end
-- Build a set of vertical OutlinedText labels sized to fit one shared box (mirrors
-- build_vertical_label_set in box_song.cpp).
local function build_vertical_labels(keys, box_cfg, v_advance, font_bonus, fills, outlines, thickness)
local labels = {}
local max_chars = 1
for i, key in ipairs(keys) do
labels[i] = tex.skin_text(key)
if labels[i] == "" then labels[i] = "?" end
max_chars = math.max(max_chars, utf8_char_count(labels[i]))
end
local width_cap = box_cfg.width - 4.0
local height_cap = (box_cfg.height - 4.0) / (1.0 + (max_chars - 1) * v_advance)
local font_size = math.floor(math.min(width_cap, height_cap) + font_bonus)
local out = {}
for i = 1, #keys do
out[i] = text.create_raw_text(labels[i], font_size, fills[i], outlines[i], true, thickness, 2.0, v_advance)
end
return out
end
-- diff_tower_label (5 diffs) — static cache in C++, cached on self here.
local function diff_tower_labels(self)
if self._diff_tower_labels then return self._diff_tower_labels end
local cfg = tex.get_skin_config("diff_tower_label_box") or { x = 475, y = 158, width = 53, height = 152 }
local keys = { "diff_tower_easy", "diff_tower_normal", "diff_tower_hard", "diff_tower_oni", "diff_tower_ura" }
local fills = { BLACK, BLACK, BLACK, BLACK, WHITE }
local outlines = { BLANK, BLANK, BLANK, BLANK, { 0, 73, 90, 255 } }
self._diff_tower_labels = build_vertical_labels(keys, cfg, 0.9, 5.0, fills, outlines, 3.0)
return self._diff_tower_labels
end
-- difficulty_bar_label (4 diffs).
local function difficulty_bar_labels(self)
if self._difficulty_bar_labels then return self._difficulty_bar_labels end
local cfg = tex.get_skin_config("difficulty_bar_label_box") or { x = 481, y = 256, width = 26, height = 96 }
local keys = { "diff_tower_easy", "diff_tower_normal", "diff_tower_hard", "diff_tower_oni" }
local colors = { BLACK, BLACK, BLACK, BLACK }
self._difficulty_bar_labels = build_vertical_labels(keys, cfg, 0.9, 7.0, colors, colors, 0.0)
return self._difficulty_bar_labels
end
-- The three song text objects (name, name_black, subtitle) from BaseBox::load_text +
-- SongBox::load_text, cached per box on self.
local function song_texts(self, box)
self._song_texts = self._song_texts or {}
local name = box.text_name or ""
local fc = box:fore_color() or { r = 0, g = 0, b = 0, a = 255 }
local key = box:path() .. "\0" .. tostring(fc.r or 0) .. "." .. tostring(fc.g or 0) .. "." .. tostring(fc.b or 0)
local t = self._song_texts[key]
if t == nil then
local sub = box.text_subtitle or ""
local fore = { fc.r or 0, fc.g or 0, fc.b or 0, fc.a or 255 }
local name_cfg = tex.get_skin_config("song_box_name") or { font_size = 40 }
local base_name_font = name_cfg.font_size or 40
local name_font = base_name_font
local name_outline = 5.0
if utf8_char_count(name) >= 30 then
name_font = base_name_font - 10 * tex.get_screen_scale()
name_outline = 5.0 * (name_font / base_name_font)
end
local name_text = text.create_raw_text(name, name_font, WHITE, fore, true, 5.0, 2.0, 1.0)
local name_black = text.create_raw_text(name, name_font, WHITE, BLACK, true, name_outline, 2.0, 1.0)
local sub_cfg = tex.get_skin_config("yb_subtitle") or { font_size = 30 }
local base_sub_font = sub_cfg.font_size or 30
local sub_font = base_sub_font
local sub_outline = 5.0
if utf8_char_count(sub) >= 30 then
sub_font = base_sub_font - 10 * tex.get_screen_scale()
sub_outline = 5.0 * (sub_font / base_sub_font)
end
local subtitle = text.create_raw_text(sub, sub_font, WHITE, BLACK, true, sub_outline, 2.0, 1.0)
t = { name = name_text, name_black = name_black, subtitle = subtitle }
self._song_texts[key] = t
end
return t
end
-- draw_diff_outline (small crown outline; doubled in 2P).
local function draw_diff_outline(x, y, fade)
if is_2p_screen() then
local half = tex.get_texture_info("yellow_box", "s_crown_dfc")
half = (half and half.width or 0) * 0.35
tex.draw_texture(T("yellow_box", "s_crown_outline"), { x = x - half, y = y, fade = fade })
tex.draw_texture(T("yellow_box", "s_crown_outline"), { x = x + half, y = y, fade = fade })
else
tex.draw_texture(T("yellow_box", "s_crown_outline"), { x = x, y = y, fade = fade })
end
end
-- draw_diff_crown (small crown per diff; uses p1/p2 scores in 2P).
local function draw_diff_crown(box, diff, x, y, fade)
local function draw_one(info, px)
if not info.has_score then return end
if info.crown == 3 then
tex.draw_texture(T("yellow_box", "s_crown_dfc"), { x = px, y = y, fade = fade })
elseif info.crown == 2 then
tex.draw_texture(T("yellow_box", "s_crown_fc"), { x = px, y = y, fade = fade })
elseif info.crown >= 1 then
tex.draw_texture(T("yellow_box", "s_crown_clear"), { x = px, y = y, fade = fade })
end
end
if is_2p_screen() then
local half = tex.get_texture_info("yellow_box", "s_crown_dfc")
half = (half and half.width or 0) * 0.35
draw_one(box:course_info(diff), x - half)
draw_one(box:course_info_p2(diff), x + half)
else
draw_one(box:course_info(diff), x)
end
end
-- draw_box_crown (the closed-state best crown; highest-difficulty score wins).
local function draw_box_crown(box, x, y, fade)
local function highest(info_fn)
local best_key = -1
local best_crown = 0
for d = 0, 4 do
local info = info_fn(d)
if info.has_course and info.has_score then
if d > best_key then best_key = d; best_crown = info.crown end
end
end
if best_key < 0 then return nil, nil end
return best_crown, math.min(DIFF_URA, best_key)
end
local crown_1p, frame_1p = highest(function(d) return box:course_info(d) end)
local crown_2p, frame_2p
if is_2p_screen() then
crown_2p, frame_2p = highest(function(d) return box:course_info_p2(d) end)
end
if crown_2p then
local info = tex.get_texture_info("yellow_box", "crown_dfc")
local half = (info and info.width or 0) * 0.35
if crown_1p then base.draw_crown(crown_1p, frame_1p, x - half, y, fade) end
base.draw_crown(crown_2p, frame_2p, x + half, y, fade)
elseif crown_1p then
base.draw_crown(crown_1p, frame_1p, x, y, fade)
end
end
-- draw_text (the open-state name/subtitle, tracking the yellow box's expansion).
local function draw_text(box, texts)
local right_out = tex.get_animation(10)
local right_out_2 = tex.get_animation(14)
local top_y_out = tex.get_animation(16)
local x = box:box_x()
+ (right_out.attribute * 0.90 - right_out.start_position * 0.90)
+ right_out_2.attribute - right_out_2.start_position
local sub_cfg = tex.get_skin_config("yb_subtitle") or { x = -15, y = 545, height = 410 }
local h = math.min(texts.subtitle.height, sub_cfg.height)
texts.subtitle:draw({
x = x + sub_cfg.x,
y = sub_cfg.y - h + top_y_out.attribute - top_y_out.start_position,
y2 = h - texts.subtitle.height,
fade = box.open_fade.attribute
})
local name_cfg = tex.get_skin_config("song_box_name") or { x = 47, y = 129, height = 417 }
local name_h = math.min(texts.name.height, name_cfg.height) - texts.name.height
local name_x = x + name_cfg.x - math.floor(texts.name.width / 2)
local name_y = name_cfg.y + top_y_out.attribute - top_y_out.start_position
texts.name_black:draw({ x = name_x, y = name_y, y2 = name_h, fade = box.open_fade.attribute })
texts.name:draw({ x = name_x, y = name_y, y2 = name_h, fade = 1 - box.open_fade.attribute })
end
function SongSelect:draw_song_closed(box)
draw_base_closed(box)
local bx = box:box_x()
local by = box:box_y()
local fade = box.fade.attribute
local name = vertical_name(self, box)
local name_cfg = tex.get_skin_config("song_box_name") or { x = 47, y = 129, height = 417 }
local name_x = bx + name_cfg.x - math.floor(name.width / 2)
local name_y = name_cfg.y + by
local name_h = math.min(name.height, name_cfg.height)
name:draw({ x = name_x, y = name_y, y2 = name_h - name.height, fade = fade })
if box:has_preimage() then
tex.draw_texture(T("yellow_box", "preimage_bg"), { x = bx, y = by, fade = fade })
box:draw_preimage(bx, by, fade)
elseif box:ex_data_flag() == 3 then -- limited_time
tex.draw_texture(T("yellow_box", "ex_data_limited_time_balloon"), { x = bx, y = by, fade = fade })
elseif box.is_new then
tex.draw_texture(T("yellow_box", "ex_data_new_song_balloon"), { x = bx, y = by, fade = fade })
end
draw_box_crown(box, bx, by, fade)
end
function SongSelect:draw_song_open(box)
local bx = box:box_x()
local by = box:box_y()
local open_fade = box.open_fade.attribute
draw_shadows(bx, by, open_fade, 1)
draw_yellow_box(false, 1.0, 0.0)
local offset = (tex.get_skin_config("yb_diff_offset") or { x = 60 }).x
local texts = song_texts(self, box)
-- diff outlines + crowns (diff < URA)
for d = 0, 3 do
local info = box:course_info(d)
if info.has_course then
draw_diff_outline(d * offset, 0.0, math.min(open_fade, 0.25))
draw_diff_crown(box, d, d * offset, 0.0, open_fade)
end
end
-- ex_data / new icon
local exd = box:ex_data_flag()
if exd == 1 then
tex.draw_texture(T("yellow_box", "ex_data_new_audio"), { fade = open_fade })
elseif exd == 2 then
tex.draw_texture(T("yellow_box", "ex_data_old_audio"), { fade = open_fade })
elseif exd == 3 then
tex.draw_texture(T("yellow_box", "ex_data_limited_time"), { fade = open_fade })
elseif box.is_new then
tex.draw_texture(T("yellow_box", "ex_data_new_song"), { fade = open_fade })
end
if tex.display_bpm() then
local bpm = box:bpm_text()
local cfg = tex.get_skin_config("song_box_bpm") or { x = 628, y = 120 }
if bpm then bpm:draw({ x = cfg.x, y = cfg.y, fade = open_fade }) end
end
if box.is_favorite then
tex.draw_texture(T("yellow_box", "favorite_" .. tex.player_num() .. "p"), { fade = open_fade })
end
-- difficulty bars (4)
for i = 0, 3 do
tex.draw_texture(T("yellow_box", "difficulty_bar"), { frame = i, x = i * offset, fade = open_fade })
if not box:course_info(i).has_course then
tex.draw_texture(T("yellow_box", "difficulty_bar_shadow"), { frame = i, x = i * offset, fade = math.min(open_fade, 0.25) })
end
end
-- difficulty bar labels
local labels = difficulty_bar_labels(self)
local box_cfg = tex.get_skin_config("difficulty_bar_label_box") or { x = 481, y = 256, width = 26, height = 96 }
for i = 0, 3 do
local label = labels[i + 1]
label:draw({
x = box_cfg.x + i * offset + box_cfg.width / 2 - label.width / 2,
y = box_cfg.y,
y2 = math.min(label.height, box_cfg.height) - label.height,
fade = open_fade
})
end
-- stars + branch indicator (diff < URA)
local offset_y = (tex.get_skin_config("yb_diff_offset") or { y = -17 }).y
for d = 0, 3 do
local info = box:course_info(d)
if info.has_course then
for j = 0, info.level - 1 do
tex.draw_texture(T("yellow_box", "star"), { x = d * offset, y = j * offset_y, fade = open_fade })
end
if info.is_branching and (math.floor(tex.get_current_ms() / 1000) % 2 == 0) then
tex.draw_texture(T("yellow_box", "branch_indicator"), { x = d * offset, fade = open_fade })
end
end
end
draw_text(box, texts)
end
function SongSelect:draw_song_diff_select(box)
draw_yellow_box(true, 1.0, 0.0)
local diff_fade = box.diff_fade_in.attribute
tex.draw_texture(T("diff_select", "back"), { fade = diff_fade })
tex.draw_texture(T("diff_select", "option"), { fade = diff_fade })
tex.draw_texture(T("diff_select", "neiro"), { fade = diff_fade })
local offset_x = (tex.get_skin_config("yb_diff_offset_diff_select") or { x = 115 }).x
local offset_y = (tex.get_skin_config("yb_diff_offset_diff_select") or { y = -120 }).y
local crown_offset = (tex.get_skin_config("yb_diff_offset_crown") or { x = 8 }).x
-- diff crowns (diff < URA)
for d = 0, 3 do
local info = box:course_info(d)
if info.has_course then
local cx = d * offset_x + crown_offset
draw_diff_outline(cx, offset_y, math.min(diff_fade, 0.25))
draw_diff_crown(box, d, cx, offset_y, diff_fade)
end
end
-- diff towers + labels
local tower_labels = diff_tower_labels(self)
local box_cfg = tex.get_skin_config("diff_tower_label_box") or { x = 475, y = 158, width = 53, height = 152 }
for i = 0, 3 do
local label_index = i
if i == DIFF_ONI and box.is_ura then
tex.draw_texture(T("diff_select", "diff_tower"), { frame = 4, x = i * offset_x, fade = diff_fade })
tex.draw_texture(T("diff_select", "ura_oni_plate"), { fade = diff_fade })
label_index = 4
else
tex.draw_texture(T("diff_select", "diff_tower"), { frame = i, x = i * offset_x, fade = diff_fade })
end
if not box:course_info(i).has_course then
tex.draw_texture(T("diff_select", "diff_tower_shadow"), { frame = i, x = i * offset_x, fade = math.min(diff_fade, 0.25) })
end
local label = tower_labels[label_index + 1]
label:draw({
x = box_cfg.x + i * offset_x + (box_cfg.width - label.width) / 2,
y = box_cfg.y,
fade = diff_fade
})
end
-- ura stars + branch indicator
local star_offset_y = (tex.get_skin_config("yb_diff_offset_crown") or { y = -20 }).y
for d = 0, 4 do
local info = box:course_info(d)
if info.has_course then
if (d == DIFF_URA and not box.is_ura) or (d == DIFF_ONI and box.is_ura) then
-- skip
else
for j = 0, info.level - 1 do
tex.draw_texture(T("yellow_box", "star_ura"), { x = math.min(d, DIFF_ONI) * offset_x, y = j * star_offset_y, fade = diff_fade })
end
if info.is_branching and (math.floor(tex.get_current_ms() / 1000) % 2 == 0) then
local bt = (d == DIFF_URA) and T("yellow_box", "branch_indicator_ura") or T("yellow_box", "branch_indicator_diff")
tex.draw_texture(bt, { x = math.min(d, DIFF_ONI) * offset_x, fade = diff_fade })
end
end
end
end
draw_text(box, song_texts(self, box))
end
@@ -0,0 +1,184 @@
-- Inline-list background (GenreBG) drawing, split out of song_select.lua.
-- Mirrors src/objects/song_select/file_navigator/genre_bg.cpp.
local function T(name)
return tex.get_texture("song_select/box/" .. name)
end
local _bg_edge_width, _bg_folder_edge_width
local function bg_edge_width()
if _bg_edge_width == nil then
local info = tex.get_texture_info("box", "folder_background_edge")
_bg_edge_width = (info and info.width) or 0
end
return _bg_edge_width
end
local function bg_folder_edge_width()
if _bg_folder_edge_width == nil then
local info = tex.get_texture_info("box", "folder_background_folder_edge")
_bg_folder_edge_width = (info and info.width) or 0
end
return _bg_folder_edge_width
end
-- draw_anim (folder still entering; box's bounds drive the growing span).
local function draw_anim(gb, box)
if not box then return end
local s = gb.scale.attribute
local offset = 5 * s
local edge_width = bg_edge_width() * s
local center = (box.left_bound + box.right_bound) / 2.0
local half_width = (box.right_bound - box.left_bound) / 2.0
local base_start = center - half_width * s
local base_end = center + half_width * s
local exiting = (gb.move_left ~= nil and gb.move_right ~= nil)
local start_position = exiting and (base_start + gb.move_left.attribute) or base_start
local end_position = exiting and (base_end + gb.move_right.attribute) or (base_end + gb.move.attribute)
local frame = gb:texture_frame()
local fade = gb.fade.attribute
local sy = -(gb.stretch.attribute * s)
local recolor = gb:has_recolor()
if recolor then gb:begin_recolor() end
tex.draw_texture(T("folder_background_edge"), { frame = frame, scale = s, center = true, mirror = "horizontal", x = start_position - offset, y = sy, y2 = gb.stretch.attribute * s, fade = fade })
tex.draw_texture(T("folder_background"), { frame = frame, scale = s, center = true, x = start_position + edge_width - offset, y = sy, x2 = end_position - start_position - edge_width - offset, y2 = gb.stretch.attribute * s, fade = fade })
tex.draw_texture(T("folder_background_edge"), { frame = frame, scale = s, center = true, x = end_position - edge_width + offset, y = sy, y2 = gb.stretch.attribute * s, fade = fade })
local edge_width_top = bg_folder_edge_width() * s
local title_cfg = tex.get_skin_config("genre_bg_title") or { y = 0, width = 300 }
local name = gb:name()
local dest_width = math.min(title_cfg.width, name.width) * s
tex.draw_texture(T("folder_background_folder_edge"), { frame = frame, scale = s, center = true, mirror = "horizontal", x = center - dest_width / 2 - edge_width_top, y = sy, y2 = gb.stretch.attribute * s, fade = fade })
tex.draw_texture(T("folder_background_folder"), { frame = frame, scale = s, center = true, x = center - dest_width / 2, y = sy, x2 = dest_width, y2 = gb.stretch.attribute * s, fade = fade })
tex.draw_texture(T("folder_background_folder_edge"), { frame = frame, scale = s, center = true, x = center + dest_width / 2, y = sy, y2 = gb.stretch.attribute * s, fade = fade })
if recolor then gb:end_recolor() end
name:draw({ center = true, x = center - dest_width / 2, y = title_cfg.y - gb.stretch.attribute * s, x2 = dest_width - name.width, y2 = gb.stretch.attribute * s, fade = fade })
end
-- draw_exit_anim (folder closing; move_left/move_right drive the shrink).
local function draw_exit_anim(gb)
local s = gb.scale.attribute
local offset = 5 * s
local edge_width = bg_edge_width() * s
local anim_start = gb.move_left.attribute
local anim_end = gb.move_right.attribute
local frame = gb:texture_frame()
local fade = gb.fade.attribute
local recolor = gb:has_recolor()
if recolor then gb:begin_recolor() end
tex.draw_texture(T("folder_background_edge"), { frame = frame, scale = s, mirror = "horizontal", x = anim_start - offset, fade = fade })
tex.draw_texture(T("folder_background"), { frame = frame, scale = s, x = anim_start + edge_width - offset, x2 = anim_end - anim_start - edge_width - offset, fade = fade })
tex.draw_texture(T("folder_background_edge"), { frame = frame, scale = s, x = anim_end - edge_width + offset, fade = fade })
local edge_width_top = bg_folder_edge_width()
local title_cfg = tex.get_skin_config("genre_bg_title") or { width = 300 }
local name = gb:name()
local dest_width = math.min(title_cfg.width, name.width)
local center = (tex.get_skin_config("genre_bg_center_x") or { x = 638.5 }).x
tex.draw_texture(T("folder_background_folder_edge"), { frame = frame, mirror = "horizontal", x = center - dest_width / 2 - edge_width_top, fade = fade })
tex.draw_texture(T("folder_background_folder"), { frame = frame, x = center - dest_width / 2, x2 = dest_width, fade = fade })
tex.draw_texture(T("folder_background_folder_edge"), { frame = frame, x = center + dest_width / 2, fade = fade })
if recolor then gb:end_recolor() end
name:draw({ x = center - dest_width / 2, y = title_cfg.y, x2 = dest_width - name.width, fade = fade })
end
function SongSelect:draw_genre_bg(gb, start_position, end_position, folder)
if not gb:is_finished() then
draw_anim(gb, folder)
return
elseif not gb:is_complete() then
draw_exit_anim(gb)
return
end
-- Steady state: fill the span between the first and last song's bounds.
local screen_width = tex.get_screen_width()
local frame = gb:texture_frame()
local fade = gb.fade.attribute
local name = gb:name()
local title_cfg = tex.get_skin_config("genre_bg_title") or { y = 0, width = 300 }
local center_cfg = tex.get_skin_config("genre_bg_center_x") or { x = 638.5 }
local boundary = tex.get_skin_config("genre_bg_select_boundary") or { x = 442, width = 835 }
local edge_width_top = bg_folder_edge_width()
local dest_width = math.min(title_cfg.width, name.width)
local center = center_cfg.x
local bg_start_pos = start_position
local bg_end_pos = end_position
if boundary.x < start_position and start_position < center then
bg_start_pos = boundary.x
end
if center < end_position and end_position < boundary.width then
bg_end_pos = boundary.width
end
local recolor = gb:has_recolor()
if recolor then gb:begin_recolor() end
if bg_start_pos > bg_end_pos then
-- wraps around: two segments
local offset = 5
local edge_width = bg_edge_width()
local draw_end_l = math.min(bg_end_pos, screen_width)
if 0 < draw_end_l then
if bg_end_pos < screen_width then
tex.draw_texture(T("folder_background"), { frame = frame, x = 0, x2 = draw_end_l - edge_width + offset, fade = fade })
tex.draw_texture(T("folder_background_edge"), { frame = frame, x = draw_end_l - edge_width + offset, fade = fade })
else
tex.draw_texture(T("folder_background"), { frame = frame, x = 0, x2 = draw_end_l, fade = fade })
end
end
local draw_start_r = math.max(bg_start_pos, 0)
if draw_start_r < screen_width then
if bg_start_pos > 0 then
tex.draw_texture(T("folder_background"), { frame = frame, x = draw_start_r + edge_width - offset, x2 = screen_width, fade = fade })
tex.draw_texture(T("folder_background_edge"), { frame = frame, mirror = "horizontal", x = draw_start_r - offset, fade = fade })
else
tex.draw_texture(T("folder_background"), { frame = frame, x = draw_start_r, x2 = screen_width, fade = fade })
end
end
else
local offset = 5
local edge_width = bg_edge_width()
local draw_start = math.max(bg_start_pos, 0)
local draw_end = math.min(bg_end_pos, screen_width)
if draw_start < draw_end then
local fill_start = draw_start
local fill_end = draw_end
if bg_start_pos > 0 then
tex.draw_texture(T("folder_background_edge"), { frame = frame, mirror = "horizontal", x = draw_start - offset, fade = fade })
fill_start = draw_start + edge_width - offset
end
if bg_end_pos < screen_width then
tex.draw_texture(T("folder_background_edge"), { frame = frame, x = draw_end - edge_width + offset, fade = fade })
fill_end = draw_end - edge_width + offset
end
if fill_start < fill_end then
tex.draw_texture(T("folder_background"), { frame = frame, x = fill_start, x2 = fill_end - fill_start, fade = fade })
end
end
end
local center_not_covered = (start_position <= end_position)
and (center < start_position or end_position < center)
or (end_position < center and center < start_position)
if center_not_covered then
if recolor then gb:end_recolor() end
return
end
tex.draw_texture(T("folder_background_folder_edge"), { frame = frame, mirror = "horizontal", x = center - dest_width / 2 - edge_width_top, fade = fade })
tex.draw_texture(T("folder_background_folder"), { frame = frame, x = center - dest_width / 2, x2 = dest_width, fade = fade })
tex.draw_texture(T("folder_background_folder_edge"), { frame = frame, x = center + dest_width / 2, fade = fade })
if recolor then gb:end_recolor() end
name:draw({ x = center - dest_width / 2, y = title_cfg.y, x2 = dest_width - name.width, fade = fade })
end
+123
View File
@@ -0,0 +1,123 @@
ScoreHistory = {}
ScoreHistory.__index = ScoreHistory
local DIFF_URA = 4
function ScoreHistory.new(available, is_shinuchi, current_ms)
local self = setmetatable({}, ScoreHistory)
self.available = available
self.is_shinuchi = is_shinuchi
self.last_ms = current_ms
self.curr_index = #available > 0 and ((1 % #available) + 1) or 0
local offset = tex.get_skin_config("score_info_bg_offset")
local margin = tex.get_skin_config("score_info_counter_margin")
self.offset_y = offset and offset.y or 0
self.margin_w = margin and margin.width or 0
self.margin_x = margin and margin.x or 0
local title_cfg = tex.get_skin_config("leaderboard_title_long")
self.title_pos = title_cfg or {}
self.title = nil
local label = tex.skin_text("leaderboard_title")
if label ~= "" and title_cfg then
local font_size = title_cfg.height
while font_size > 8 do
local t = text.create_raw_text(label, font_size, { 0, 0, 0, 255 }, { 0, 0, 0, 0 }, false, 0, 2.0)
if t.width <= title_cfg.width then
self.title = t
break
end
font_size = font_size - 1
end
if not self.title then
self.title = text.create_raw_text(label, 8, { 0, 0, 0, 255 }, { 0, 0, 0, 0 }, false, 0, 2.0)
end
end
self.t_background_2 = tex.get_texture("song_select/leaderboard/background_2")
self.t_shinuchi = tex.get_texture("song_select/leaderboard/shinuchi")
self.t_shinuchi_ura = tex.get_texture("song_select/leaderboard/shinuchi_ura")
self.t_pts = tex.get_texture("song_select/leaderboard/pts")
self.t_normal = tex.get_texture("song_select/leaderboard/normal")
self.t_difficulty = tex.get_texture("song_select/leaderboard/difficulty")
self.t_judge_good = tex.get_texture("song_select/leaderboard/judge_good")
self.t_judge_ok = tex.get_texture("song_select/leaderboard/judge_ok")
self.t_judge_bad = tex.get_texture("song_select/leaderboard/judge_bad")
self.t_judge_drumroll = tex.get_texture("song_select/leaderboard/judge_drumroll")
self.t_counter = tex.get_texture("song_select/leaderboard/counter")
self.t_judge_num = tex.get_texture("song_select/leaderboard/judge_num")
return self
end
function ScoreHistory:update(current_ms)
if #self.available == 0 then return end
if current_ms >= self.last_ms + 1000.0 then
self.last_ms = current_ms
self.curr_index = (self.curr_index % #self.available) + 1
end
end
function ScoreHistory:draw()
if #self.available == 0 then return end
local entry = self.available[self.curr_index]
local diff = entry.diff
if self.t_background_2 then tex.draw_texture(self.t_background_2) end
if self.title then self.title:draw({ x = self.title_pos.x, y = self.title_pos.y }) end
if self.is_shinuchi then
if diff == DIFF_URA then
if self.t_shinuchi_ura then tex.draw_texture(self.t_shinuchi_ura, { index = 1 }) end
else
if self.t_shinuchi then tex.draw_texture(self.t_shinuchi, { index = 1 }) end
end
if self.t_pts then tex.draw_texture(self.t_pts, { color = { 255, 255, 255, 255 }, index = 1 }) end
else
if self.t_normal then tex.draw_texture(self.t_normal, { index = 1 }) end
if self.t_pts then tex.draw_texture(self.t_pts, { color = { 0, 0, 0, 255 }, index = 1 }) end
end
if self.t_difficulty then tex.draw_texture(self.t_difficulty, { frame = diff, index = 1 }) end
for i = 0, 3 do
if self.t_normal then
tex.draw_texture(self.t_normal, { y = self.offset_y + i * self.offset_y, index = 1 })
end
end
if self.t_judge_good then tex.draw_texture(self.t_judge_good) end
if self.t_judge_ok then tex.draw_texture(self.t_judge_ok) end
if self.t_judge_bad then tex.draw_texture(self.t_judge_bad) end
if self.t_judge_drumroll then tex.draw_texture(self.t_judge_drumroll) end
local values = { entry.score, entry.good, entry.ok, entry.bad, entry.drumroll }
local score_color = self.is_shinuchi and { 255, 255, 255, 255 } or { 0, 0, 0, 255 }
for j = 1, 5 do
local counter = tostring(values[j])
local len = #counter
if j == 1 then
for i = 1, len do
local digit = tonumber(string.sub(counter, i, i)) or 0
local x = -((len * self.margin_w) / 2.0) + ((i - 1) * self.margin_w)
if self.t_counter then
tex.draw_texture(self.t_counter, { color = score_color, frame = digit, x = x, index = 1 })
end
end
else
for i = 1, len do
local digit = tonumber(string.sub(counter, i, i)) or 0
local x = -(len - i + 1) * self.margin_x
local y = (j - 1) * self.offset_y
if self.t_judge_num then
tex.draw_texture(self.t_judge_num, { frame = digit, x = x, y = y })
end
end
end
end
end
+42 -1
View File
@@ -8,10 +8,23 @@ local STATE_DAN_SELECTED = 4
SongSelect = {}
SongSelect.__index = SongSelect
-- The wheel box drawing (BaseBox/YellowBox/SongBox/FolderBox/BackBox) lives in
-- file_navigator/, mirroring src/objects/song_select/file_navigator/. Each module adds
-- its draw_* methods onto SongSelect as a side effect of being required.
require("song_select/file_navigator/box_base")
require("song_select/file_navigator/box_song")
require("song_select/file_navigator/box_folder")
require("song_select/file_navigator/box_back")
require("song_select/file_navigator/genre_bg")
local base = require("song_select/file_navigator/box_base")
function SongSelect.new()
local self = setmetatable({}, SongSelect)
tex.load_animations("song_select")
base.reset_yellow_box()
tex.load_folder("song_select", "global")
tex.load_folder("song_select", "box")
tex.load_folder("song_select", "yellow_box")
tex.load_folder("song_select", "diff_select")
-- Animations (IDs from song_select/animation.json)
self.text_fade_in = tex.get_animation(4) -- fade in/out the top-left label text
@@ -36,6 +49,7 @@ function SongSelect:update(current_ms)
self.text_fade_in:update(current_ms)
self.blue_arrow_fade:update(current_ms)
self.blue_arrow_move:update(current_ms)
base.update_yellow_box(current_ms)
end
-- Call whenever the screen state changes so the label cross-fades correctly.
@@ -69,3 +83,30 @@ function SongSelect:draw_overlays(state)
tex.draw_texture(self._id_song_select, { fade = label_fade })
end
end
function SongSelect:draw_box(box)
base.track_yellow_box(box)
local kind = box:lua_kind()
local state = box:draw_state()
if kind == "song" then
if state == "diff_select" then
self:draw_song_diff_select(box)
elseif state == "open" then
self:draw_song_open(box)
else
self:draw_song_closed(box)
end
elseif kind == "folder" then
if state == "open" then
self:draw_folder_open(box)
else
self:draw_folder_closed(box)
end
elseif kind == "back" then
if state == "open" then
self:draw_back_open(box)
else
self:draw_back_closed(box)
end
end
end
+30
View File
@@ -0,0 +1,30 @@
UraSwitchAnimation = {}
UraSwitchAnimation.__index = UraSwitchAnimation
function UraSwitchAnimation.new()
local self = setmetatable({}, UraSwitchAnimation)
self.texture_change = tex.get_animation(7, "song_select")
self.fade_out = tex.get_animation(8, "song_select")
self.t_ura_switch = tex.get_texture("song_select/diff_select/ura_switch")
return self
end
function UraSwitchAnimation:start(is_backwards)
self.texture_change = tex.get_animation(is_backwards and 6 or 7, "song_select")
self.texture_change:start()
self.fade_out:start()
end
function UraSwitchAnimation:update(current_ms)
self.texture_change:update(current_ms)
self.fade_out:update(current_ms)
end
function UraSwitchAnimation:draw()
tex.draw_texture(self.t_ura_switch, {
frame = math.floor(self.texture_change.attribute),
fade = self.fade_out.attribute,
})
end
+54
View File
@@ -0,0 +1,54 @@
AttractCamera = {}
AttractCamera.__index = AttractCamera
function AttractCamera.new()
local self = setmetatable({}, AttractCamera)
self.start_ms = tex.get_current_ms()
self.finished = false
camera.open()
audio.play_sound("camera", "attract_mode")
self.live_icon_texture_change = tex.get_animation(36, "title")
self.live_icon_texture_change:start()
local ok, timer = pcall(tex.get_animation, 37, "title")
self.scene_timer = ok and timer or nil
if self.scene_timer then self.scene_timer:start() end
self.t_background = tex.get_texture("title/camera/background")
self.t_live_icon = tex.get_texture("title/camera/live_icon")
self.t_live_text = tex.get_texture("title/camera/live_text")
self.viewport = tex.get_skin_config("attract_camera_viewport")
return self
end
function AttractCamera:update(current_ms)
camera.update()
self.live_icon_texture_change:update(current_ms)
if self.scene_timer then
self.scene_timer:update(current_ms)
self.finished = self.scene_timer.is_finished
else
self.finished = (current_ms - self.start_ms >= 30000)
end
end
function AttractCamera:draw()
tex.draw_texture(self.t_background)
if self.viewport then
camera.draw(self.viewport.x, self.viewport.y, self.viewport.width, self.viewport.height)
end
tex.draw_texture(self.t_live_icon, { frame = math.floor(self.live_icon_texture_change.attribute) })
tex.draw_texture(self.t_live_text)
end
function AttractCamera:is_finished()
return self.finished
end
function AttractCamera:close()
camera.close()
end
+37
View File
@@ -0,0 +1,37 @@
require("title/attract_camera")
require("title/camera_cloud")
require("title/bana_advert")
AttractScene = {}
AttractScene.__index = AttractScene
function AttractScene.new()
local self = setmetatable({}, AttractScene)
self.attract_camera = AttractCamera.new()
self.camera_cloud = CameraCloud.new()
self.bana_advert_1 = BanaAdvertisement.new()
self.bana_advert_2 = BanaAdvertisement.new()
return self
end
function AttractScene:update(current_ms)
self.attract_camera:update(current_ms)
self.camera_cloud:update(current_ms)
self.bana_advert_1:update(current_ms)
self.bana_advert_2:update(current_ms)
end
function AttractScene:draw()
self.attract_camera:draw()
self.camera_cloud:draw()
self.bana_advert_1:draw(33, 136)
self.bana_advert_2:draw(1023, 136)
end
function AttractScene:is_finished()
return self.attract_camera:is_finished()
end
function AttractScene:close()
self.attract_camera:close()
end
-1
View File
@@ -1,4 +1,3 @@
-- Ported 1:1 from src/objects/title/bana_advert.cpp (pure visual, no input).
BanaAdvertisement = {}
BanaAdvertisement.__index = BanaAdvertisement
+42
View File
@@ -0,0 +1,42 @@
CameraCloud = {}
CameraCloud.__index = CameraCloud
function CameraCloud.new()
local self = setmetatable({}, CameraCloud)
self.fade_in = tex.get_animation(32, "title")
self.fade_in:start()
self.text_fade = tex.get_animation(33, "title")
self.text_fade:start()
self.move_up = tex.get_animation(34, "title")
self.move_up:start()
self.breathing = tex.get_animation(35, "title")
self.breathing:start()
self.t_cloud = tex.get_texture("title/camera/camera_cloud")
self.t_cloud_text_1 = tex.get_texture("title/camera/camera_cloud_text_1")
self.t_cloud_text_2 = tex.get_texture("title/camera/camera_cloud_text_2")
return self
end
function CameraCloud:update(current_ms)
self.fade_in:update(current_ms)
self.text_fade:update(current_ms)
self.move_up:update(current_ms)
self.breathing:update(current_ms)
end
function CameraCloud:draw()
tex.draw_texture(self.t_cloud, {
scale = self.breathing.attribute, center = true, y = self.move_up.attribute, fade = self.fade_in.attribute,
})
tex.draw_texture(self.t_cloud_text_1, {
scale = self.breathing.attribute, center = true, y = self.move_up.attribute,
fade = math.min(self.fade_in.attribute, 1 - self.text_fade.attribute),
})
tex.draw_texture(self.t_cloud_text_2, {
scale = self.breathing.attribute, center = true, y = self.move_up.attribute,
fade = math.min(self.fade_in.attribute, self.text_fade.attribute),
})
end
+218
View File
@@ -0,0 +1,218 @@
local WarningX = {}
WarningX.__index = WarningX
function WarningX.new()
local self = setmetatable({}, WarningX)
self.resize = tex.get_animation(0, "title")
self.resize:start()
self.fade_in = tex.get_animation(1, "title")
self.fade_in:start()
self.fade_in_2 = tex.get_animation(2, "title")
self.fade_in_2:start()
self.sound_played = false
self.t_x_lightred = tex.get_texture("title/warning/x_lightred")
self.t_x_red = tex.get_texture("title/warning/x_red")
return self
end
function WarningX:update(current_ms)
self.resize:update(current_ms)
self.fade_in:update(current_ms)
self.fade_in_2:update(current_ms)
if self.resize.attribute > 1 and not self.sound_played then
audio.play_sound("error", "attract_mode")
self.sound_played = true
end
end
function WarningX:draw_bg()
tex.draw_texture(self.t_x_lightred, { fade = self.fade_in_2.attribute })
end
function WarningX:draw_fg()
tex.draw_texture(self.t_x_red, { scale = self.resize.attribute, center = true, fade = self.fade_in.attribute })
end
function WarningX:is_finished()
return self.resize.is_finished and self.fade_in.is_finished and self.fade_in_2.is_finished
end
local WarningBachiHit = {}
WarningBachiHit.__index = WarningBachiHit
function WarningBachiHit.new()
local self = setmetatable({}, WarningBachiHit)
self.resize = tex.get_animation(3, "title")
self.fade_in = tex.get_animation(4, "title")
self.sound_played = false
self.t_bachi_hit = tex.get_texture("title/warning/bachi_hit")
self.t_bachi = tex.get_texture("title/warning/bachi")
return self
end
function WarningBachiHit:update(current_ms)
if not self.sound_played then
audio.play_sound("bachi_hit", "attract_mode")
self.sound_played = true
self.fade_in:start()
self.resize:start()
end
self.resize:update(current_ms)
self.fade_in:update(current_ms)
end
function WarningBachiHit:draw()
tex.draw_texture(self.t_bachi_hit, { scale = self.resize.attribute, center = true, fade = self.fade_in.attribute })
if self.resize.attribute > 0 and self.sound_played then
tex.draw_texture(self.t_bachi)
end
end
function WarningBachiHit:is_finished()
return self.fade_in.is_finished and self.sound_played and self.resize.is_finished
end
local WarningCharacters = {}
WarningCharacters.__index = WarningCharacters
function WarningCharacters.new()
local self = setmetatable({}, WarningCharacters)
self.shadow_fade = tex.get_animation(5, "title")
self.chara_0_frame = tex.get_animation(7, "title")
self.chara_1_frame = tex.get_animation(6, "title")
self.chara_0_frame:start()
self.chara_1_frame:start()
self.saved_frame = 0
self.t_chara_0_shadow = tex.get_texture("title/warning/chara_0_shadow")
self.t_chara_0 = tex.get_texture("title/warning/chara_0")
self.t_chara_1_shadow = tex.get_texture("title/warning/chara_1_shadow")
self.t_chara_1 = tex.get_texture("title/warning/chara_1")
return self
end
function WarningCharacters:update(current_ms)
self.shadow_fade:update(current_ms)
self.chara_1_frame:update(current_ms)
self.chara_0_frame:update(current_ms)
if self.chara_1_frame.attribute ~= self.saved_frame then
self.saved_frame = math.floor(self.chara_1_frame.attribute)
if not self.shadow_fade.is_started then
self.shadow_fade:start()
else
self.shadow_fade:restart()
end
end
end
function WarningCharacters:draw(fade, fade_2, y_pos)
tex.draw_texture(self.t_chara_0_shadow, { y = y_pos, fade = fade_2 })
tex.draw_texture(self.t_chara_0, { frame = math.floor(self.chara_0_frame.attribute), y = y_pos, fade = fade })
tex.draw_texture(self.t_chara_1_shadow, { y = y_pos, fade = fade_2 })
if -1 < self.chara_1_frame.attribute - 1 and self.chara_1_frame.attribute - 1 < 7 then
tex.draw_texture(self.t_chara_1, {
frame = math.floor(self.chara_1_frame.attribute) - 1, y = y_pos, fade = self.shadow_fade.attribute,
})
end
tex.draw_texture(self.t_chara_1, { frame = math.floor(self.chara_1_frame.attribute), y = y_pos, fade = fade })
end
function WarningCharacters:is_finished()
return self.chara_1_frame.is_finished
end
local Board = {}
Board.__index = Board
function Board.new()
local self = setmetatable({}, Board)
self.move_down = tex.get_animation(10, "title")
self.move_down:start()
self.move_up = tex.get_animation(11, "title")
self.move_up:start()
self.move_center = tex.get_animation(12, "title")
self.move_center:start()
self.y_pos = 0
self.t_warning_box = tex.get_texture("title/warning/warning_box")
return self
end
function Board:update(current_ms)
self.move_down:update(current_ms)
self.move_up:update(current_ms)
self.move_center:update(current_ms)
if self.move_up.is_finished then
self.y_pos = self.move_center.attribute
elseif self.move_down.is_finished then
self.y_pos = self.move_up.attribute
else
self.y_pos = self.move_down.attribute
end
end
function Board:draw()
tex.draw_texture(self.t_warning_box, { y = self.y_pos })
end
WarningScreen = {}
WarningScreen.__index = WarningScreen
function WarningScreen.new(current_ms)
local self = setmetatable({}, WarningScreen)
self.start_ms = current_ms
self.fade_in = tex.get_animation(8, "title")
self.fade_out = tex.get_animation(9, "title")
self.fade_in:start()
self.board = Board.new()
self.warning_x = WarningX.new()
self.warning_bachi_hit = WarningBachiHit.new()
self.characters = WarningCharacters.new()
self.t_warning_background = tex.get_texture("title/warning/background")
self.t_movie_background = tex.get_texture("title/movie/background")
return self
end
function WarningScreen:update(current_ms)
self.board:update(current_ms)
self.fade_in:update(current_ms)
self.fade_out:update(current_ms)
local delay = 533.33
local elapsed_time = current_ms - self.start_ms
self.warning_x:update(current_ms)
self.characters:update(current_ms)
if self.characters:is_finished() then
self.warning_bachi_hit:update(current_ms)
else
self.fade_out:start()
if delay <= elapsed_time and not audio.is_sound_playing("bachi_swipe") then
audio.play_sound("warning_voiceover", "attract_mode")
audio.play_sound("bachi_swipe", "attract_mode")
end
end
end
function WarningScreen:is_finished()
return self.fade_out.is_finished
end
function WarningScreen:draw()
tex.draw_texture(self.t_warning_background)
self.board:draw()
self.warning_x:draw_bg()
self.characters:draw(self.fade_in.attribute, math.min(self.fade_in.attribute, 0.75), self.board.y_pos)
self.warning_x:draw_fg()
self.warning_bachi_hit:draw()
tex.draw_texture(self.t_movie_background, { fade = self.fade_out.attribute })
end
Binary file not shown.
Binary file not shown.
Binary file not shown.
+50 -31
View File
@@ -7183,7 +7183,7 @@ ec22711f72132b837a6522269a014cc713c7c935e998875fb22360a189a2967b *./Graphics/gam
06fe1e3fa447d26ec0048e6ae6b5ec8b48612e0ca8ae52f0358189c21e4f1af8 *./Graphics/game/ai_battle/section_text.png
d559d1c557b1eb6c5060833d7ffc575a430e2870542b6e5f886e3004440b8ef0 *./Graphics/game/ai_battle/section_win.png
e3e9b341cefc71c014ed1e3516a24810e514329ebe6f1525d70967f09eccf946 *./Graphics/game/ai_battle/texture.json
83421b95cdb48489c75189c1dc9bc94c40cc4b9ab3a1d885e859fe674d7ac439 *./Graphics/game/animation.json
b85dea08ad9363e76bf0caa2df33ae87ec706b36565bb2732e9cb6d86cb82e7f *./Graphics/game/animation.json
73bf74fada6ff0e5dc6637b185f5c5d32afd60b8944f43d540d00c80000a944c *./Graphics/game/balloon/bubble.png
2b14b07611a63758d2e6082ea56e7eb16472db7372afb52aa54eeb28f93c7690 *./Graphics/game/balloon/counter.png
ec3282ce743cdf67b5687dfd500e79107fff450878549f223a68522a33a38c76 *./Graphics/game/balloon/explosion.png
@@ -7379,6 +7379,7 @@ db99e39129f2e44f565fca9c218aff2f824ae9b7044d5315bc538654b07943e7 *./Graphics/gam
67815c99eed1c22b5befe318ca399a4a2cd449d0ff149b536db29e208b279591 *./Graphics/game/gogo_time/fire.png
c314c3c7413611465757586b739fc6c10745807cd9eabec2658545c2146b8b34 *./Graphics/game/gogo_time/texture.json
bbd5a5061a53d18f6a8644b5b20ca12dc6fb8bd7d8ec0ed0fe3d3bb13045d761 *./Graphics/game/hit_effect/explosion.png
6ea89c726cc649ea9d73f1d5967df3e7ba5aea185b11ba1875b0963c181eec80 *./Graphics/game/hit_effect/firework.png
f6bedd2f88bf307fc74dd5bd07a81138cfc0a1a2c4f518156450532b973cd0df *./Graphics/game/hit_effect/hit_effect_good_big.png
f629e3fc5b85a3e0efbd715e757e2424f09242ddf287eafe8ae4ab9ea276b14d *./Graphics/game/hit_effect/hit_effect_good.png
c43c31d9863b46ecd90b8afbc50af9a21e00ee60286d338e8fef012413c60efe *./Graphics/game/hit_effect/hit_effect_ok_big.png
@@ -7392,7 +7393,7 @@ de425fbaa86f180075798976c0c13e1409499058b29d5107b7a0c27ad3180d90 *./Graphics/gam
ff6b64321a92f4fc79d6e315d0913a4f9e9684f673d09d954f6acd7cb0ccb56a *./Graphics/game/hit_effect/outer_good.png
e74d70db802bcf58f47e1605a0429031e79812fc8a909382c19fb82ff682e29a *./Graphics/game/hit_effect/outer_ok_big.png
430407a54fb50bb51873401731f4fe29ed5b680a0a80d3bf7aa4cdda1e87e40c *./Graphics/game/hit_effect/outer_ok.png
f5a0e9e5260288b4810e2f5c9e87bc8ad4aa22e6cf36cd41a8135d04619bc143 *./Graphics/game/hit_effect/texture.json
d92f635e690cef7ff7dd17eca98a0675af50f3340e3ec88c0c15ca5bb54b2235 *./Graphics/game/hit_effect/texture.json
1f5298d88bdc570c3acc0f069fc7c0426144f1ef3fbb352afd96962e77f6ddf0 *./Graphics/game/judge_counter/bg.png
c96c90add5c561470fe26a52ab6b439e09ec3cd61bb390fdc19e0bce8c787122 *./Graphics/game/judge_counter/counter.png
51de40cea517e72dbc2cbdd4949c04bc9bc086198374b94589843a5ffd476b26 *./Graphics/game/judge_counter/drumrolls.png
@@ -7713,8 +7714,8 @@ b47abc211bb29186bfb7303f8495f3a48b6e65b60dd10d92a7ae9625b372734c *./Graphics/set
04622b407745197ace032eeaf81b66a7fef7daeee0e46693b5c55f7d22ad1425 *./Graphics/settings/option/button_off.png
67474d69d95b8f3bec5e42c8b93e6d6ff5569abb13c6a32235c265c135c6e9d7 *./Graphics/settings/option/button_on.png
82c2a2804f77b2e12c4dd0a46f7ac669e60b1ad92225432729bcc0a321387e79 *./Graphics/settings/option/texture.json
7d7a166df0fbd966d4e2f2ff9b1eb427ac6ce4f23b0b8383aab78db11f771cac *./Graphics/settings_template.json
4627a2fbde5152765248ec18947c79e0e2afa63891e68acbad36141856454bf5 *./Graphics/skin_config.json
4cc4e7e785417812939bfcf710d4d883c0b264fb11cfce6fb0a71f95623ac9f0 *./Graphics/settings_template.json
de3c661eddf8d1e992f23c3a7def29e759b600a1076bf897bea82a5a3efaf8f2 *./Graphics/skin_config.json
179393f86dc603a103e240879b952c363f2f06d48fba7b320f6215b645bea110 *./Graphics/song_select/ai_battle/1p.png
c7bae3649859b1033d9b1a56a04c1c5c3f39b172b8660374a94d40f257de7790 *./Graphics/song_select/ai_battle/2p.png
f26377ce7459cb2a76d56d051be50c0b41f2ee13a1a5b87200fb53de60a7b264 *./Graphics/song_select/ai_battle/background_overlay_2.png
@@ -7847,7 +7848,7 @@ ea2a69c8d0adec73c74aa38d3b413cbd2b7f7719eb348adf50965cedfbc121c3 *./Graphics/son
03e4e191fdd811fc582bc4f0bd9aeb750e87c3250953b8ca7d7231ec5ce7c3bf *./Graphics/song_select/box/folder_top_edge/5.png
5b41ddf36b11bbea7cc6020242509509492969a21c23e51825258a25b1333c0b *./Graphics/song_select/box/genre_overlay_large.png
80c269126421c31972cd9bdec7018dfb5a4ef5217ea63d8d62702093f5d8faac *./Graphics/song_select/box/genre_overlay.png
187cef20577d6f879e9164ee4c473803035a0ec5b9df9f1fdfc438204da34dfe *./Graphics/song_select/box/texture.json
b4aef881789c1e3ca0b59ec707268fd64cd0d7b3420a392a99d90894eb24304b *./Graphics/song_select/box/texture.json
db40896e4c182e849aa2e8ab32d3908bc065d07c04aeb40ed74ee90b718505f1 *./Graphics/song_select/dan_transition/background.png
a2af149c9018d929f95322661cace991a284b33a24451db1ae915e80a0aaf8a2 *./Graphics/song_select/dan_transition/texture.json
97cd5cd60ab1685243fb8c5ce4507eaa02dd49f83a2238a7bdf87f4711a1fd9d *./Graphics/song_select/diff_select/1p2p_outline.png
@@ -7885,7 +7886,7 @@ b3db527d5615f79e6ce392b41da8f39d7b253826fe3cb4d92868e9711bd205a6 *./Graphics/son
8448bf0f9e0e97165fe62689fae021da5aeb444a1ab29b839c5702495c85be02 *./Graphics/song_select/diff_select/neiro_ja.png
aaef148da3ec49db52d6608b44fa6f1a339f20068f2eb0fba8448aaa6cf29f60 *./Graphics/song_select/diff_select/option_en.png
3951843f5e2a686b63e42376e23b08a2d34a6a943e5bc0dac3491fb267faa9de *./Graphics/song_select/diff_select/option_ja.png
fa364540fc4c2b04fc033ce894b71eb897054791b52802e43f4f0876f0e18e14 *./Graphics/song_select/diff_select/texture.json
57ceaa0012c4e23d27feee077e91d18aa5b89bf69c5b02d1c15dad656bc97161 *./Graphics/song_select/diff_select/texture.json
de18b6cda60689780194bb7cf3352fc3e580a7569d6634b35f3bb6de3ec78f74 *./Graphics/song_select/diff_select/ura_oni_plate.png
b5fac3ff36f4e782c6a332fdf6c68fa573c15ed5ee311903ce3734c835168cdf *./Graphics/song_select/diff_select/ura_switch/0.png
da76351a327504b87c3b41489fa91d8fc4cfd573b72ca27d59e8b3eeb6e5ca36 *./Graphics/song_select/diff_select/ura_switch/1.png
@@ -8090,7 +8091,7 @@ b105727879d67808c29432ed76804be6671474c0475ff3efa0b49e28af5eb3a5 *./Graphics/son
2defaf1f501f723418a820c053ee7e835443630589f89e41b95f1a23ff1aba3e *./Graphics/song_select/yellow_box/song_count_back.png
2fae896b776d2fc4bb803da39a782b3d3baac6036ab40502f2bb0ce568183037 *./Graphics/song_select/yellow_box/star.png
85ba22108eb709e13b3478bd716a81746bdd5a51068fc2ce005b796860661aeb *./Graphics/song_select/yellow_box/star_ura.png
5b59a818436479699a1c6506df33477506fdaa57c7ef373d4c74428f01246b12 *./Graphics/song_select/yellow_box/texture.json
7e1de82961268ad64aa78df4e89e8577cfc69ddab92f7eaf525f8ff1a5d30e53 *./Graphics/song_select/yellow_box/texture.json
0bcfb5d2ecf7b6d90401b3800231e8ff813c5a8706082b1703a9551962128c29 *./Graphics/song_select/yellow_box/yellow_box_bottom_left.png
49fd2d19d41ab1634ece2f95260acd5bc28ed39fe50e5841d93609321c365c2c *./Graphics/song_select/yellow_box/yellow_box_bottom.png
6f4d392bfa2fd02557a6d98d2f7e4633ce62222b1589b0018351e15fa830ee68 *./Graphics/song_select/yellow_box/yellow_box_bottom_right.png
@@ -9397,13 +9398,13 @@ f59099edce8feeaac2f45064d02730cd8450d83722389c3becc0ca91bea27041 *./Models/paint
4ef33adf5d02ff2d9890794b55239a52357250b533ed8a69e8d3f09275080693 *./Models/paint/paint_055000.png
0a82b9087600457ee8aa507ebcb3048c70effa95a83d63cac4eccc30c75efc6b *./Models/paint/paint_056000.png
57ab9d3072d32051eef5483db883362518c7cf402799c63d0bc792a52429c1a8 *./Models/paint/paint_057000.png
e3d8a05b9a6ddf886caf99adbf24296c1d712b9ab68c209cdc10ab7b69ff91b7 *./Scripts/background/background.lua
9c1e1e38b5f4936c6a6c46f76931f5cda86029cbccc84619144b4980dea02814 *./Scripts/background/bg_collabs/a3.lua
e1b70b28b4233377c9e077d83964fe50423d4afac8b90961242fd4924a7650ba *./Scripts/background/background.lua
b97f4ede34f9e042912df23a38c3231c2d15cc615f53071fb0e5ccc21e60fb1d *./Scripts/background/bg_collabs/a3.lua
7a7c675f7b906396dc7f743bad887a0f4fa617f37faa2887a762fecb017375ab *./Scripts/background/bg_collabs/animal.lua
dab3a2f285242d8e77296697650fc6a0822fd1f2a43f5db29653cc7a569accd6 *./Scripts/background/bg_collabs/buttoburst.lua
799740087cc746f90f4a889a72503806f3d8dc3277445b3fdcdbde1b16a2c3b9 *./Scripts/background/bg_collabs/dan.lua
4aa90019064f93e5073569b4bebb25fcff611aa60f983fbe518b054041fdd8a1 *./Scripts/background/bg_collabs/dan.lua
b6036a69641a0e2feaa7d530e2a20664accfe407aa471d70bf6b62a8706f6b6d *./Scripts/background/bg_collabs/dq10.lua
8efe92bc22d236714560d41cf672141c4b3597b1afbebae1ceb89b9c5a0db79e *./Scripts/background/bg_collabs/funassyi.lua
4bcaea3a618e9e2961e94819d7997fe05451b3e1b56d43e3b9b7461e7c9b34b2 *./Scripts/background/bg_collabs/funassyi.lua
480a18fdfa78f0e34fc479a232fa684993bdad883d61e9b4a97ccd104c587cf5 *./Scripts/background/bg_collabs/gb.lua
b3722520f22d01bc605c85fa129d1c20ea58f0459a82a46dcbe47455d4e87cbf *./Scripts/background/bg_collabs/gmt.lua
32c145cec643cab6b13aa5bbb701b897894c5a44b84c670de38af7a8eba7daa3 *./Scripts/background/bg_collabs/gumi.lua
@@ -9428,15 +9429,15 @@ e63a9e706e8644968b689e9685fcc772415ebd00133e39f5887f6f76817630ed *./Scripts/back
79845f0df65c8d27b1c44a102cabf0c12d8d66c90f9666dbf0e1640d6563b26d *./Scripts/background/bg_collabs/toho.lua
9f6e0f0d2e16738d224fc4b15c666bef970a084865da93a89f1f9ab37700beab *./Scripts/background/bg_collabs/tonkatsudj.lua
c2b495c1a24d5ad132a8fa53f6634b570fae5f08410fa30a39aa8e9875f2e6d4 *./Scripts/background/bg_collabs/touken.lua
d5e200b0506bec44b6e8e0ed1319076a8a653fcea35617fb1368bdf835ad3f42 *./Scripts/background/bg_collabs/tutorial.lua
afd2a748135b6a9666dfbb3595d055747919e4ed78c64ef5e0013b090c0975eb *./Scripts/background/bg_collabs/tutorial.lua
33fc6edae8530cf54a5ce5d41032e8836ebe216bb21f68e6655140dd3731193a *./Scripts/background/bg_collabs/ymck.lua
00328555c6ef8ee293497af40ae00b1a6236cc80c53199e12b297fb1d0ac791a *./Scripts/background/bg_collabs/yokai_hatsukoi.lua
8b4b45900cd72e1a216dc48fb0a5551e2a82a44611c306747478c07d27a6e74c *./Scripts/background/bg_collabs/yokai.lua
3c9360694b8d579ee8ace8088113948866340d11b8c8ac8df521d1d7a3d504ee *./Scripts/background/bg_collabs/yokai_matsuri.lua
c146e82e7315f2962d33c0457c87aa65ca97e5ac9c0569684bb2f69efb8dcb24 *./Scripts/background/bg_collabs/yokai_tokoroten.lua
13876795d050284dc656b016a786419108d6cca44e3df67e7ec39910ff389f85 *./Scripts/background/bg_collabs/yokai_yougota.lua
10acae2c2553289faccd6454137a98ce6e1627d4a2b8edd64c6959ae4fa21dcd *./Scripts/background/bg_objects/bg_fever.lua
b56662a706ccd159b2e7d66383114c41ed0d233612729eff6297e546cda8191b *./Scripts/background/bg_objects/bg_normal.lua
0e29a01e037fe9e3ce501a971088fcd8094dbe646d4999cf0f7140b98b79b056 *./Scripts/background/bg_objects/bg_fever.lua
c54aa6cc8f39569990571f760060a56038d38bfc14b7559ff59d8ac13a0f4a40 *./Scripts/background/bg_objects/bg_normal.lua
29d4674757cd0d6e94dc05cc31b5409471a9d123272b537807a21eade1be4bf3 *./Scripts/background/bg_objects/chibi/0.lua
d75c5ff40da942078abf7287c5bc39c2cf048fc01be5727d1d6a334572a42c91 *./Scripts/background/bg_objects/chibi/13.lua
3ef303e55bb9ef39cec84dee91ec19186c6f77468b1f382a2dc789f7b84284b1 *./Scripts/background/bg_objects/chibi/2.lua
@@ -9447,39 +9448,56 @@ ed026aa86fa6a89190bf5d40c2e2262cec192c8a2bd7b11c11d3013bf9ed24ff *./Scripts/back
aa5fd5410a8c09337164d5fe462943199ef069558a9696ea34ce6b7c5b0397bb *./Scripts/background/bg_objects/chibi/base.lua
b5e6feee78ae5550fb95769f73b4f5daf2d438795fa1c5799195a5b01489ea97 *./Scripts/background/bg_objects/chibi.lua
e17386ec247b369ccf6012a82b0204b5942f09022a3d25c4e9294f2fde014d20 *./Scripts/background/bg_objects/dancer.lua
c26b717e4eab21cd6c9ef8130f455c386cd4292dd9ad64dc6f5ce3bcbd2472a0 *./Scripts/background/bg_objects/donbg/1.lua
545799c5826be896d4f557efe38d5ec824664422b17256004e06e44b71e8d8fd *./Scripts/background/bg_objects/donbg/2.lua
5c40f347670f86e18973905baa25b88a7313ceb0ae826abe33f732fe0c14c343 *./Scripts/background/bg_objects/donbg/3.lua
ff39cbf53938208c5b938292eca7c617424ce42078cd5d5dd5118c05d3ef784e *./Scripts/background/bg_objects/donbg/4.lua
9bb9af0d12c5fee21fde44c3ad1dec7873d0ad0c73e22db8154b0e1e7bc4809d *./Scripts/background/bg_objects/donbg/5.lua
2ed31ade85ee6fae6f4d14d8b3861b780f5dbc8640fed872254782742006f6e5 *./Scripts/background/bg_objects/donbg/6.lua
e1f1fb711a02282ead83f22f80abe669e40a3cd3da3204311ea5183bde932843 *./Scripts/background/bg_objects/donbg/base.lua
13c128386df378b19c68b06b66618ad2672d6755be2545ea56e46bbfb228db85 *./Scripts/background/bg_objects/donbg/1.lua
174623720acc4cfea845936bfe65431545a4d4c19911dacb0b7b5b7a71fd3305 *./Scripts/background/bg_objects/donbg/2.lua
3ad8d298f3abf8b7767d73e28ade6df71984d9edaa894539c7d708e3ee01ff16 *./Scripts/background/bg_objects/donbg/3.lua
84cdb3d84bb825f2eb51533013aef62677547c745994e5bd17f3dfdb0ad08c6e *./Scripts/background/bg_objects/donbg/4.lua
d192fb5918ab1f36fdf1c894e658cabcf0da9182713bba364321cd2c7e2b7b55 *./Scripts/background/bg_objects/donbg/5.lua
5d311aa24fd2e265ff497893c35d7e73be2d959a62a728952c3c822870680c36 *./Scripts/background/bg_objects/donbg/6.lua
2ef847c7cad3b82bbd2bc5ba0b86bfaff63a46fa760f1c52719808eed16cd3bb *./Scripts/background/bg_objects/donbg/base.lua
63bd3b3d984c5c970fbe1a3b45aadb0eee6e1409a790889a3585b7e8fda44f4a *./Scripts/background/bg_objects/donbg.lua
bd01b4301d3c9326f4c6326ec127f08106d9061f816f14d7b410a9e0ef76cf91 *./Scripts/background/bg_objects/fever.lua
78f73786c14818861403bb9a0ecc158ecded5f03fc47bc859ed88b4ede5b01eb *./Scripts/background/bg_objects/footer.lua
3a4ee9f50497a8140a5371da69158f86909dd291df2cd80229c9eb4d658cd597 *./Scripts/background/bg_objects/renda.lua
43c5a3e745d0677f350be1150942f1f01b296bd84cf209cda92d1d0b828c8afb *./Scripts/dan_select/dan_select.lua
72b69fdfa1ea00202b7ef8f1b65220a73c3ea5e74116054acec6e61ce0ba7256 *./Scripts/entry/box.lua
a6c89e3b8c5654487c345dcc95a226ee3f7a2951329a88227c4e50374ff93ed2 *./Scripts/entry/costume_menu.lua
ba92fb9f70cbc1bcb12d9382ce2edc3e06e1c1e526a5069098b401b233e7fabe *./Scripts/entry/entry.lua
a126611332f4ea9b1638aec79dbe8a284ce1b89410d988216a7fe4c0f58b5a87 *./Scripts/entry/entry.lua
869ba2c149387ff5f44c505cfe261453abe4112017c7ca586fd4dc4233752213 *./Scripts/entry/player.lua
460bd5339860be5abbf9b1f1db2f36a4a4be77de73c4f933a2fb1be5386250bb *./Scripts/game/clear_animation.lua
a2004e6df3bc3105fc2401662c38ed94cf5f45b05a20175f227d3e19f80b653f *./Scripts/game/combo_announce.lua
5cb0047469951863e3e2fee62ffde3afc62e91974add5764c7783deb2d35d721 *./Scripts/game/fail_animation.lua
2caf735860515a66f3c1305ebe40df2f22fe2ff9837d4f385ad0f480040b34cf *./Scripts/game/fc_animation.lua
66ad0c99972d48e4b613951bd3532cb59b12f164c7562ff52a01d8b26d882f10 *./Scripts/game_over/game_over.lua
5c643bc0c522ca69e9611696c78117c6bf8c96b2e3767bb56ecc459e8e7d4e09 *./Scripts/global/allnet_indicator.lua
381cfe3e402d076f4cbbc89b7763a034bc5086e486058e1339adc6199f029cd3 *./Scripts/global/coin_overlay.lua
38b2e25f67245044589d3dec14abadc6b115b6be87490c2fbc075a8cedafd00f *./Scripts/global/entry_overlay.lua
1e5760785aff98db26e649b5493cd0ee8ddf8a46b2c59afcbf5da39d49bfd703 *./Scripts/global/indicator.lua
977e8a64929eb9557c409e8230dfbaeb3d032b9b64a8d25bbb3cd5655c0d2994 *./Scripts/global/nameplate.lua
0fd986d17090c7e1d3c4d7a949a606d8780f52ff6c5c90746f1fccc2f7462635 *./Scripts/global/result_transition.lua
d0d202a05d7e94f900b49b1cd50b56c5d55426439cb746689b87d645660076bc *./Scripts/global/timer.lua
12afbce120fa933331efd27d5eb2b2c87367ef5b3d761930ec7ab154e12343ba *./Scripts/result/bottom_characters.lua
9c44dbc951eaf076824422733562f77c97b9c4cd01788663094f344ff297fa49 *./Scripts/global/transition.lua
cba4e6940f37bb2fdd124acb9242e3147397660471a62d547d0a30ad06598a0c *./Scripts/result/bottom_characters.lua
7fe28b74da62b99d430ae9045f10abd0804652a1940f4a0de24d34b96a228b84 *./Scripts/result/dan_result_draw.lua
6ebddd307fddb708bf592aec3383fa8944c39d14af640eb51ff6ffaac10e2bd6 *./Scripts/result/high_score_indicator.lua
8f176d535096b666910500b390c69073c22e8745a49e040cad89cdccf3f5d3ba *./Scripts/result/high_score_indicator.lua
f91f76d652457ad3987d00729f3af415b0270d0cd765234bfd7531c145befa59 *./Scripts/result/result_background.lua
c8a4a8b57ad3f4c2b9018ce493f63ecdfae2f318af7fcef37aaa513c94a10c96 *./Scripts/result/result_crown.lua
28de4bd17f383b117ce3cf0e570a7fbb4c0f0a8c9843c13c908c27653e6bf784 *./Scripts/result/result_crown_message.lua
0ae5263aa53b9454d843c49c495fc956e11afd2f1a5cdaa34bf1c082f82f23fd *./Scripts/result/result_fade_in.lua
5f80977b72d37ad7cc5f03cf29e3684fe8f8aaa2cb24417ee8d26f77cbaf99b2 *./Scripts/result/result_player.lua
4451d2d3d0b11d4296bbd410f3147072bebcfb0f06a51a59362b901896898cd6 *./Scripts/song_select/song_select.lua
6b1cd4fa907aaa87fd734c980dc11210069619dd9baaa81e487265f5ed53ebb2 *./Scripts/title/bana_advert.lua
f57648d9d50f9524c9d70601f4fe0a870d7397fafdf1dd9c6873644d3d4b6d0a *./Scripts/result/result_crown.lua
078c8226626462c9409f40deb680208a79c67fc6f9c00ce2168fa54c4c622498 *./Scripts/result/result_crown_message.lua
b230f98362a7ffb936b4cf877bb6f43da506fbe519db780a260ef0a669d5af31 *./Scripts/result/result_fade_in.lua
45645c3ab0703b1a988c4b29657d83a0dc28c0b2148b755f120084f30f145187 *./Scripts/result/result_player.lua
572b381758ea394312bab5f87d23dd1e2d839fedcc64452d4ba5991774d84540 *./Scripts/song_select/file_navigator/box_back.lua
8de3941867d2ba1a45b2191eaafb82eb43f28aef6253d00954367df95e01f1f7 *./Scripts/song_select/file_navigator/box_base.lua
935157086cee5116056ab0765293e4742377a2ddd18c84d3c7168a99f79c9a1c *./Scripts/song_select/file_navigator/box_folder.lua
74967f50f67b75f8ed6fb491d324cf4e599503012e714f2a129892d092d3cdf0 *./Scripts/song_select/file_navigator/box_song.lua
2888c36c94622669f5d29c3b15b852ea0e4c95562f983b2c9e8114f626e8bc2d *./Scripts/song_select/file_navigator/genre_bg.lua
211a2d44aa1fd75ba51ddd21bacf65384cc08601c68ead7d431091c210950ba4 *./Scripts/song_select/score_history.lua
602f624bca93141410ddfaa87cd6112cedca639d17432abecb558fc67fda9178 *./Scripts/song_select/song_select.lua
a51f83e335365b7b0ef6cb09bf600af31ae5483f49fedc8e8426e9a18ef644a7 *./Scripts/song_select/ura_switch.lua
dcf22728819b568733320c61091ad6a646a925dd2207769b760b56edc0d21092 *./Scripts/title/attract_camera.lua
0ed0bce45846756c096ac787a8f590071b8c93416aee1f8cf0774a38f8006b52 *./Scripts/title/attract_scene.lua
b0e1e379d68422efda99d6641585827ce92f91b07c1a4af0d6fed4db41512742 *./Scripts/title/bana_advert.lua
78280c263d0ce372ec5e2e5c872ae978b894bf174896f4e8833b4e09f1cc85cb *./Scripts/title/camera_cloud.lua
a4b7febf5d36106c41d957bfdb1e4f0f565782fb57c28e699b29960508578bd9 *./Scripts/title/warning_screen.lua
990d75d13855e5bea9bce66f709c67e7f352cedbee6e2956e0cf7bc85ea563d1 *./.skin-repo
9bfea0be2634c99786f3372e854b296ccb68693d1121bf70a8aa05b22406948e *./Sounds/dan_result/bgm.ogg
537962311370ff5553118cb7e1ebbf098723e110100e6a531f03dc159e235616 *./Sounds/dan_result/num_up.ogg
@@ -9792,12 +9810,13 @@ c7f9bf9959ec984ae34e4063acbb4cf3bb1622986cfff33eab9cb1cccd5e84b2 *./Videos/attra
fb84815f727a5f39cc192a863455815319da8187b4d8b3af5c6e87270f7cf330 *./Videos/attract_videos/attract_cm_154.mp4
2783d99ed2484dda532eec15ee4178bb2f4b821a73983d011ec9a6ca00bc12e4 *./Videos/attract_videos/attract_cm_155.mp4
84dbaa9da9a4455023598976f0d040c3a5f37b378f11e80072c46917a2848e21 *./Videos/attract_videos/attract_cm_156.mp4
87627559e70b07b65e681c60b2fa79d0351fef7684294828fa64a4a5fe33f820 *./Videos/attract_videos/attract_cm_157.mp4
4e01deb35ecf00435482cc5b423adc702e12e5663b559983c4dc83317d09a118 *./Videos/attract_videos/attract_cm_158.mp4
e9bd01e74725875474d0de1ffb2819923d8cd28e9b64fce68586e4f14ebc6d95 *./Videos/attract_videos/attract_cm_159.mp4
bd0f8656e58c524265d98a0e08955984825416e72d42655e3934df11bf8c177d *./Videos/attract_videos/attract_cm_160.mp4
a90f7bed9ec0a39627f9fd046ad465de9cd800138d0f373f592a60a1734ac1fe *./Videos/attract_videos/attract_cm_161.mp4
1b5d8ff0af9b0d34657b04d1963d52066c5e6399034cbf7635df53dc7ce218a6 *./Videos/op_videos/2011_OP.mp4
34871603e997026022f96a8ff71f3611638f34c32269cba53c57aacc39983608 *./Videos/op_videos/Blue_OP.mp4
6ea1d57749ad98e93ca35b67370bf615353615d1961b5bfb4ad2fe89f1897910 *./Videos/op_videos/Green_OP.mp4
c210215e5869c76c5ae620c61a1bad739a4dde30736b4850c9ca7c21ca08a29d *./Videos/op_videos/KATSU-DON_OP.mp4
d25f7bd967c1f45286fb20a579119fe3e8dd7125d1bd18ba5731066fe2d96c17 *./Videos/op_videos/Kimidori_OP.mp4
a9bcf14f216cfbab3cf2adf11f3bce3ffc885d11855b337e1ba225f3cc12fa33 *./Videos/op_videos/Momoiro_OP.mp4