Author SHA1 Message Date
TaikoZhongandClaude Fable 5.1 dd036cb85e result: clear state and clear-zone art follow the engine's norma
gauge_state came from a cell count (68 of 87 for oni) while the game clears on soul >= norma (6000/7000/7000/8000); in the band just below the norma the result lit the gold cell, the clear caption and a crown for a failed run. Hard also used the oni layout although the HUD gives it normal's zone and norma. Derive the state from the soul value, pick the art tier like the HUD, and light the gold cell and caption only when cleared.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-17 12:18:31 +08:00
4 changed files with 324 additions and 1104 deletions

No files matched your search

Binary file not shown.
File diff suppressed because it is too large. Load diff
+53 -91
View File
@@ -23,10 +23,10 @@
"gauge_cell_fade_in": false
},
"chara_3d": {
"scale": 650.0,
"rot_x": 181.25,
"rot_y": 27.5,
"rot_z": 0
"scale": 650.0,
"rot_x": 181.25,
"rot_y": 27.5,
"rot_z": 0
}
},
"free_play": {
@@ -36,8 +36,7 @@
"text": {
"ja": "フリープレイ",
"en": "FREE PLAY",
"zh": "自由游戏",
"ko": "프리 플레이"
"zh": "自由游戏"
}
},
"hit_taiko_to_start": {
@@ -48,8 +47,7 @@
"text": {
"ja": "太鼓をたたいてスタート!",
"en": "HIT THE TAIKO!",
"zh": "敲打太鼓开始!",
"ko": "북을 쳐서 시작!"
"zh": "敲打太鼓开始!"
}
},
"indicator_text": {
@@ -58,8 +56,7 @@
"text": {
"ja": "フチでえらんで 面で決定",
"en": "Navigate Decide",
"zh": "敲打鼓边选择 敲打鼓面",
"ko": "테두리로 선택 면으로 결정"
"zh": "敲打鼓边选择 敲打鼓面"
}
},
"song_num": {
@@ -69,8 +66,7 @@
"text": {
"ja": "{0}曲目",
"en": "Song {0}",
"zh": "{0}曲目",
"ko": "{0}곡째"
"zh": "{0}曲目"
}
},
"song_num_result": {
@@ -81,40 +77,35 @@
"text": {
"en": "Game",
"ja": "演奏ゲーム",
"zh": "节奏游戏",
"ko": "연주 게임"
"zh": "节奏游戏"
}
},
"entry_practice": {
"text": {
"en": "Practice",
"ja": "特訓モード",
"zh": "练习游戏",
"ko": "특훈 모드"
"zh": "练习游戏"
}
},
"entry_settings": {
"text": {
"en": "Settings",
"ja": "ゲーム設定",
"zh": "游戏设定",
"ko": "게임 설정"
"zh": "游戏設定"
}
},
"entry_ai_battle": {
"text": {
"en": "AI Battle",
"ja": "AIバトル",
"zh": "AI斗争",
"ko": "AI 배틀"
"zh": "AI斗争"
}
},
"entry_costume": {
"text": {
"en": "Costume",
"ja": "きせかえ",
"zh": "装扮",
"ko": "옷 갈아입기"
"zh": "装扮"
}
},
"entry_costume_menu_1p": {
@@ -132,8 +123,7 @@
"text": {
"en": "Choose a Costume",
"ja": "きせかえをえらべるよ",
"zh": "选择装扮",
"ko": "옷을 골라 보자!"
"zh": "选择装扮"
}
},
"entry_cancel": {
@@ -143,8 +133,7 @@
"text": {
"en": "Cancel",
"ja": "つかわない",
"zh": "取消",
"ko": "사용 안 함"
"zh": "取消"
}
},
"entry_question": {
@@ -154,96 +143,84 @@
"text": {
"en": "Play as this user?",
"ja": "どちらでつかいますか?",
"zh": "你打算用它来做什么?",
"ko": "어느 쪽으로 플레이할까요?"
"zh": "你打算用它来做什么?"
}
},
"modifier_name_auto": {
"text": {
"en": "Auto",
"ja": "オート",
"zh": "自动播放",
"ko": "오토"
"zh": "自动播放"
}
},
"modifier_name_speed": {
"text": {
"en": "Speed",
"ja": "はやさ",
"zh": "速度",
"ko": "속도"
"zh": "速度"
}
},
"modifier_name_display": {
"text": {
"en": "Display",
"ja": "ドロン",
"zh": "隐身",
"ko": "도론"
"zh": "隐身"
}
},
"modifier_name_inverse": {
"text": {
"en": "Inverse",
"ja": "あべこべ",
"zh": "颠倒",
"ko": "아베코베"
"zh": "顛倒"
}
},
"modifier_name_random": {
"text": {
"en": "Random",
"ja": "ランダム",
"zh": "随机",
"ko": "랜덤"
"zh": "随机"
}
},
"modifier_name_skip": {
"text": {
"en": "Skip Song",
"ja": "演奏スキップ",
"zh": "跳略演奏",
"ko": "연주 스킵"
"zh": "跳略演奏"
}
},
"modifier_name_neiro": {
"text": {
"en": "Sound",
"ja": "音色",
"zh": "音色",
"ko": "음색"
"zh": "音色"
}
},
"modifier_text_true": {
"text": {
"en": "On",
"ja": "する",
"zh": "是",
"ko": "켬"
"zh": "是"
}
},
"modifier_text_false": {
"text": {
"en": "Off",
"ja": "しない",
"zh": "否",
"ko": "끔"
"zh": "否"
}
},
"modifier_text_kimagure": {
"text": {
"en": "Kimagure",
"ja": "きまぐれ",
"zh": "随意",
"ko": "기마구레"
"zh": "隨意"
}
},
"modifier_text_detarame": {
"text": {
"en": "Detarame",
"ja": "でたらめ",
"zh": "即兴",
"ko": "데타라메"
"zh": "即興"
}
},
"nameplate_text_padding": {
@@ -518,121 +495,106 @@
"practice_menu_end_game": {
"text": {
"ja": "ゲーム終了",
"zh": "结束游戏",
"en": "End Game",
"ko": "게임 종료"
"zh": "結束遊戲",
"en": "End Game"
}
},
"practice_menu_another_song": {
"text": {
"ja": "ほかの曲を演奏",
"zh": "选其他歌曲",
"en": "Another Song",
"ko": "다른 곡 연주"
"zh": "選其他歌曲",
"en": "Another Song"
}
},
"practice_menu_restart": {
"text": {
"ja": "最初から演奏",
"zh": "从头演奏",
"en": "From the Top",
"ko": "처음부터 연주"
"zh": "從頭演奏",
"en": "From the Top"
}
},
"practice_menu_auto_play": {
"text": {
"ja": "オート演奏設定",
"zh": "自动演奏设定",
"en": "Auto Play",
"ko": "오토 연주 설정"
"zh": "自動演奏設定",
"en": "Auto Play"
}
},
"practice_menu_jump_to_mark": {
"text": {
"ja": "ジャンプポイントへ",
"zh": "跳至跳转点",
"en": "To Jump Point",
"ko": "점프 지점으로"
"zh": "跳至跳轉點",
"en": "To Jump Point"
}
},
"practice_menu_set_mark": {
"text": {
"ja": "ジャンプポイント設定",
"zh": "设定跳转点",
"en": "Set Jump Point",
"ko": "점프 지점 설정"
"zh": "設定跳轉點",
"en": "Set Jump Point"
}
},
"practice_menu_close": {
"text": {
"ja": "メニューをとじる",
"zh": "关闭选单",
"en": "Close Menu",
"ko": "메뉴 닫기"
"zh": "關閉選單",
"en": "Close Menu"
}
},
"practice_menu_confirm_end": {
"text": {
"ja": "特訓モードを終了しますカッ?",
"zh": "要结束特训模式吗锵?",
"en": "End practice mode?",
"ko": "특훈 모드를 종료할까?"
"zh": "要結束特訓模式嗎鏘?",
"en": "End practice mode?"
}
},
"practice_menu_confirm_another": {
"text": {
"ja": "ほかの曲を演奏しますカッ?",
"zh": "要选其他歌曲吗锵?",
"en": "Play another song?",
"ko": "다른 곡을 연주할까?"
"zh": "要選其他歌曲嗎鏘?",
"en": "Play another song?"
}
},
"practice_menu_confirm_restart": {
"text": {
"ja": "最初から演奏しますカッ?",
"zh": "要从头演奏吗锵?",
"en": "Restart from the top?",
"ko": "처음부터 연주할까?"
"zh": "要從頭演奏嗎鏘?",
"en": "Restart from the top?"
}
},
"practice_menu_confirm_auto": {
"text": {
"ja": "オート演奏を設定するドン!",
"zh": "设定自动演奏咚!",
"en": "Set Auto Play!",
"ko": "오토 연주를 설정한다 동!"
"zh": "設定自動演奏咚!",
"en": "Set Auto Play!"
}
},
"practice_menu_yes": {
"text": {
"ja": "はい",
"zh": "是",
"en": "Yes",
"ko": "예"
"en": "Yes"
}
},
"practice_menu_no": {
"text": {
"ja": "いいえ",
"zh": "否",
"en": "No",
"ko": "아니오"
"en": "No"
}
},
"practice_menu_auto_on": {
"text": {
"ja": "ON",
"zh": "ON",
"en": "ON",
"ko": "ON"
"en": "ON"
}
},
"practice_menu_auto_off": {
"text": {
"ja": "OFF",
"zh": "OFF",
"en": "OFF",
"ko": "OFF"
"en": "OFF"
}
},
"practice_menu_panel": {
+22 -11
View File
@@ -22,19 +22,25 @@ function ResultPlayer.new(player_num, is_2p, has_2p,
self.mod_random = mod_random
self.mod_speed = mod_speed
local clear_start = { 52, 60, 69 }
local diff_clamped = math.min(difficulty, 2)
-- Clear follows the engine's gauge (Gauge::get_is_clear: soul >= norma), not a cell
-- count: the norma is easy 6000 / normal 7000 / hard 7000 / oni+ura 8000 of 10000 soul,
-- and gauge_length arrives as soul / 10000 * 87. Hard shares normal's norma and so, like
-- the in-game HUD, normal's clear-zone art (zone at cell 60), not the oni layout.
local ART = { [0] = 0, [1] = 1, [2] = 1, [3] = 2 }
local NORMA = { [0] = 6000, [1] = 7000, [2] = 7000, [3] = 8000 }
local d = math.min(math.max(difficulty, 0), 3)
self.art_index = ART[d]
if gauge_length == 87 then
self.gauge_state = 2
elseif gauge_length >= clear_start[diff_clamped + 1] - 1 then
elseif gauge_length / 87.0 * 10000.0 >= NORMA[d] - 0.5 then
self.gauge_state = 1
else
self.gauge_state = 0
end
if difficulty >= 2 then
if self.art_index >= 2 then
self.diff_str = "_hard"
elseif difficulty >= 1 then
elseif self.art_index >= 1 then
self.diff_str = "_normal"
else
self.diff_str = "_easy"
@@ -250,8 +256,8 @@ function ResultPlayer:draw_gauge()
local scale = 10.0 / 11.0
local base_fade = self.gauge_fade_in.attribute
local gl_int = math.floor(self.gauge_length)
local clear_pts = { 52, 60, 69 }
local clear_pt = clear_pts[math.min(self.difficulty, 2) + 1]
local clear_pts = { 52, 60, 69 } -- easy / normal+hard / oni norma cells (of 87)
local clear_pt = clear_pts[self.art_index + 1]
local bar_w = self.gauge_bar_width
if self.t_gauge_unfilled then
@@ -271,11 +277,15 @@ function ResultPlayer:draw_gauge()
local x2 = math.min(gl_int * bar_w, (clear_pt - 1) * bar_w) - bar_w
tex.draw_texture(self.t_gauge_bar, { scale = scale, x2 = x2, fade = base_fade, index = p_idx })
end
if gl_int >= clear_pt - 1 and self.t_gauge_bar_clear_transition then
-- The transition piece is the first gold cell (the rounded cap of the clear zone).
-- Light it only when the run is cleared, like the HUD gauge (Gauge::get_is_clear):
-- the norma falls mid-cell (oni 8000 = cell 69.6), so a cell-count test lit it for
-- a failed run at 7816..7999 soul.
if gl_int >= clear_pt - 1 and self.gauge_state >= 1 and self.t_gauge_bar_clear_transition then
tex.draw_texture(self.t_gauge_bar_clear_transition,
{ scale = scale, x = (clear_pt - 1) * bar_w, fade = base_fade, index = p_idx })
end
if gl_int > clear_pt then
if gl_int > clear_pt and self.gauge_state >= 1 then
local extra = (gl_int - clear_pt) * bar_w
if self.t_gauge_bar_clear_top then
tex.draw_texture(self.t_gauge_bar_clear_top,
@@ -296,8 +306,9 @@ function ResultPlayer:draw_gauge()
tex.draw_texture(self.t_gauge_footer, { scale = scale, fade = base_fade, index = p_idx })
end
local clear_idx = math.min(self.difficulty, 2) + p_idx * 3
if gl_int >= clear_pt - 1 then
local clear_idx = self.art_index + p_idx * 3
-- A failed run never lights the clear caption, even when the fill touches the zone.
if gl_int >= clear_pt - 1 and self.gauge_state >= 1 then
if self.t_gauge_clear then
tex.draw_texture(self.t_gauge_clear,
{ scale = scale, fade = base_fade, index = clear_idx })