feat(通关口径换代·提前实施卡批 T1): battle_narrator 六处(a1 关卡完成!/a2-a3 关卡完成回血/a4 渠道句/a5 完成关数行+:159 注释)+career_overlay 标签两处(a6 冒险成功局数/a7 各关完成次数,注释:11/:12/:36 顺改)+save_select a8(冒险成功 X 局,注释:2/:18 顺改)+ui_kit a9(解锁标签冒险成功,:211 注释顺改)+camp a10(完成 X 关)+hud a11/a12(关卡完成率/关卡完成剩余 HP 均)——A 表 12 条逐字落地;wr1 断言随动(battle_narrator_test:39/:41)+V1-V3 新增四断言(_test_level_clear_wording,直调 curio_pick_header/node_curio_header/adventure_summary_text,17 checks 孤立直跑绿);撤销证明 a1-a5 逐条闭合(回退 hunk→对应断言红→复原复绿)

This commit is contained in:
DKChan committed 2026-10-26 06:57:11 +08:00
1 parent 0b501743cc
commit 54395b8b43
7 files changed
+41 -22

No files matched your search

+7 -7
View File
@@ -47,7 +47,7 @@ static func debug_open_line(seed_val: int, level: int, level_mode: bool) -> Stri
# 调试战斗终局文案(level_end = 关内两层模式的关卡末)
static func battle_end_text(result: String, level_end: bool) -> String:
if level_end:
return "关卡通关!"
return "关卡完成!"
if result == "victory":
return "战斗胜利!敌人全灭。"
return "战斗失败……探险者倒下了。"
@@ -87,9 +87,9 @@ static func curio_pick_header(payload: Dictionary) -> String:
var lines: Array[String] = ["第 %d 层通过!" % int(payload.get("level_index", 0))]
var heal := int(payload.get("heal_gained", 0))
if heal > 0:
lines.append("通关回血 +%d(生命 %d → %d)" % [heal, int(payload.get("hp_before", 0)), int(payload.get("hp_after", 0))])
lines.append("关卡完成回血 +%d(生命 %d → %d)" % [heal, int(payload.get("hp_before", 0)), int(payload.get("hp_after", 0))])
else:
lines.append("生命已满——通关回血 0(合法态)")
lines.append("生命已满——关卡完成回血 0(合法态)")
lines.append("选择一件提升带走(另两件丢弃不可反悔):")
return "\n".join(lines)
@@ -151,19 +151,19 @@ static func bonus_keystone_line(payload: Dictionary) -> String:
return "钥石 +%d · 累计 %d" % [int(payload.get("keystone_gained", 0)), int(payload.get("keystone_total", 0))]
# 奖励节点三选一头部行(与通关三选一共享同库;渠道白名单分流——node-reward.md v2)
# 奖励节点三选一头部行(与关卡完成三选一共享同库;渠道白名单分流——node-reward.md v2)
static func node_curio_header() -> String:
return "关卡奖励点\n从冒险牌库抽三选一(属性值提升与补给;与通关专长三选一同库分流):\n选择一件带走:"
return "关卡奖励点\n从冒险牌库抽三选一(属性值提升与补给;与关卡完成专长三选一同库分流):\n选择一件带走:"
# 冒险结算屏文案(摘要五件事:打穿没有 / 通关几关 / 止步第几关 / 钥石合计 / 主种子)
# 冒险结算屏文案(摘要五件事:打穿没有 / 完成几关 / 止步第几关 / 钥石合计 / 主种子)
static func adventure_summary_text(summary: Dictionary) -> String:
var verdict := "冒险失败 · 未打穿"
if bool(summary.get("cleared", false)):
verdict = "冒险完成 · 已打穿"
elif String(summary.get("result", "")) == "abandoned":
verdict = "冒险放弃 · 未打穿"
return "%s\n通关 %d 关 · 止步第 %d 层 · 钥石合计 %d · 主种子 %d" % [
return "%s\n完成 %d 关 · 止步第 %d 层 · 钥石合计 %d · 主种子 %d" % [
verdict, int(summary.get("levels_cleared", 0)), int(summary.get("stopped_at", 0)),
int(summary.get("keystone_total", 0)), int(summary.get("master_seed", 0))]
+2 -2
View File
@@ -188,10 +188,10 @@ static func outcome_text(outcome: Dictionary) -> String:
static func tune_sim_report(n: int, atk_pct: int, hp_pct: int, wins: int, dmg_all: Array, hp_left_win: Array, hp_boss: Array, rounds_all: Array, death_nodes: Dictionary, base_hp: int) -> String:
var lines: Array[String] = []
lines.append("[b]模拟 N=%d(atk+%d%% / hp+%d%%)[/b]" % [n, atk_pct, hp_pct])
lines.append("通关率 [color=#%s]%.0f%%[/color](%d/%d)" % [UiKit.HEX_LOG_WIN, 100.0 * float(wins) / float(maxi(1, n)), wins, n])
lines.append("关卡完成率 [color=#%s]%.0f%%[/color](%d/%d)" % [UiKit.HEX_LOG_WIN, 100.0 * float(wins) / float(maxi(1, n)), wins, n])
lines.append("整关承伤:均 %.1f · p90 %.0f(基础 HP %d)" % [_mean(dmg_all), _p90(dmg_all), base_hp])
if not hp_left_win.is_empty():
lines.append("通关剩余 HP 均 %.1f · 进 Boss HP 均 %.1f" % [_mean(hp_left_win), _mean(hp_boss)])
lines.append("关卡完成剩余 HP 均 %.1f · 进 Boss HP 均 %.1f" % [_mean(hp_left_win), _mean(hp_boss)])
lines.append("平均轮数 %.1f" % _mean(rounds_all))
if death_nodes.is_empty():
lines.append("败北节点:—")
+1 -1
View File
@@ -542,7 +542,7 @@ func _rebuild_recent(data: Dictionary) -> void:
verdict = "完成"
elif String(s.get("result", "")) == "abandoned":
verdict = "放弃"
lines.append("最近冒险:%s · 通关 %d 关 · 钥石 +%d · 种子 %d" % [verdict,
lines.append("最近冒险:%s · 完成 %d 关 · 钥石 +%d · 种子 %d" % [verdict,
int(s.get("levels_cleared", 0)), int(s.get("keystone_total", 0)), int(s.get("master_seed", 0))])
_recent_list.text = " | ".join(lines)
+5 -5
View File
@@ -8,8 +8,8 @@ extends Control
signal closed
var _total_label: Label # 冒险总局数(开局即计含放弃与失败)
var _cleared_label: Label # 通关局数(三关全通才计 1)
var _clears_label: Label # 各关通关次数(一行三关——display_name ×次数)
var _cleared_label: Label # 冒险成功局数(三关全通才计 1)
var _clears_label: Label # 各关完成次数(一行三关——display_name ×次数)
var _boss_first_label: Label # Boss 首杀(已首杀关数/总关数)
var _boss_kills_label: Label # Boss 总击杀(含重复)
var _keystone_badge: UiKit.KeystoneBadge # 钥石余额(badge 工厂件——状态面板同形制)
@@ -33,14 +33,14 @@ func _init() -> void:
UiKit.FS_HINT, false, UiKit.WARM_SOFT)
box.add_child(hint)
# 组一·冒险战绩(终身计数域——存档 v6:总局数开局即计、通关局数三关全通才计)
# 组一·冒险战绩(终身计数域——存档 v6:总局数开局即计、冒险成功局数三关全通才计)
# 对比度口径:页底=暖亮档深棕(BG_BRIGHT),文字全用亮墨族 token(WARM 9.0:1 / WARM_SOFT 8.0:1 /
# CANDLE 7.2:1 / INK_L_GOLD 9.1:1 实测对 BG_BRIGHT 全过 §7.2 ≥4.5:1——深墨族〔INK_LABEL〕
# 压深棕底仅 ~1.3:1 属违例,禁用)
_section_head(box, "冒险战绩")
_total_label = _row(box, "冒险总局数")
_cleared_label = _row(box, "通关局数")
_clears_label = _row(box, "各关通关次数")
_cleared_label = _row(box, "冒险成功局数")
_clears_label = _row(box, "各关完成次数")
_boss_first_label = _row(box, "Boss 首杀")
_boss_kills_label = _row(box, "Boss 总击杀")
@@ -1,5 +1,5 @@
## VERTICAL SLICE - NOT FOR PRODUCTION
## 选档界面·全屏页:3 存档位(新档/进档/删档)+ 槽摘要三项(钥石余额/通关局数/冒险总局数)。
## 选档界面·全屏页:3 存档位(新档/进档/删档)+ 槽摘要三项(钥石余额/冒险成功局数/冒险总局数)。
## 结构树层级:选档 → 进档 → 营地(本面零存档内功能入口——禁营地/冒险/生涯跳转与购买/查看入口);
## 删档必经二次确认(确认弹窗与分派归组合根消费方编排——本面只发意图信号)。
## 槽数与槽态(空/有档)唯一来自组合根喂入数据(open_save_select 每开重建——组合根经卡① 窥视 API
@@ -15,7 +15,7 @@ signal load_slot_requested(slot: int)
signal delete_slot_requested(slot: int)
var _rows_box: VBoxContainer
var _summary_labels: Dictionary[int, Label] = {} # 槽号→摘要读数 label(通关/冒险两值——探针取值缝)
var _summary_labels: Dictionary[int, Label] = {} # 槽号→摘要读数 label(冒险成功/冒险两值——探针取值缝)
var _occupied_flags: Dictionary[int, bool] = {} # 槽号→有档态(操作集二分断言缝;判定源=喂入数据)
var _new_btns: Dictionary[int, Button] = {} # 槽号→新档钮(空/坏槽——操作集断言与点击链驱动缝)
var _load_btns: Dictionary[int, Button] = {} # 槽号→进档钮(有档槽)
@@ -84,7 +84,7 @@ func _build_slot_row(sd: Dictionary) -> void:
sp.size_flags_horizontal = Control.SIZE_EXPAND_FILL
if occupied:
row.add_child(UiKit.keystone_badge("钥石 %d" % int(sd.get("keystone", 0))))
var summary_l := UiKit.label("通关 %d 局 · 冒险 %d 局" % [int(sd.get("cleared", 0)), int(sd.get("total", 0))],
var summary_l := UiKit.label("冒险成功 %d 局 · 冒险 %d 局" % [int(sd.get("cleared", 0)), int(sd.get("total", 0))],
UiKit.FS_BODY, false, UiKit.CANDLE)
_summary_labels[slot] = summary_l # 摘要读数缝(禁词断言枚举 Label 全集时含此件)
row.add_child(summary_l)
+2 -2
View File
@@ -208,7 +208,7 @@ const HEX_LOG_DMG_BODY := "e0a050" # 伤害行正文橙
const HEX_LOG_VOIDED := "b08060" # 未生效行暗橙(⚠ 划线)
const HEX_LOG_CAST := "b09a72" # 施放行暖石(✦ 具名技能)
const HEX_LOG_DECAY := "8a9ab0" # 状态衰减蓝灰
const HEX_LOG_WIN := "80d080" # 胜利判定绿(模拟报告通关率同档)
const HEX_LOG_WIN := "80d080" # 胜利判定绿(模拟报告关卡完成率同档)
const HEX_ROUTE_NOTE := "d0c090" # 奖励点预告暗金(narrator 行)
const HEX_SEED_NOTE := "c8a860" # 种子/开场行暗金(narrator 行)
@@ -400,7 +400,7 @@ const FS_H1 := 34 # 主标题/演算暴击
# ── 秘宝解锁条件计数键 → 呈现标签(秘宝匣柜台条件进度文案单点;键域=
# TreasureItemTable.UNLOCK_COUNTER_KEYS 封闭枚举,键出域走「条件」兜底不炸)──
const TREASURE_REQ_LABELS := {"adventure_cleared": "通关", "boss_kill_count": "Boss击杀", "vision_used": "真视使用"}
const TREASURE_REQ_LABELS := {"adventure_cleared": "冒险成功", "boss_kill_count": "Boss击杀", "vision_used": "真视使用"}
# 秘宝档位 → 呈现名(契约 2 三档;legendary 传说空集合法不在柜)
const TREASURE_TIER_LABELS := {"common": "普通", "excellent": "优秀", "rare": "稀有", "legendary": "传说"}
@@ -22,6 +22,7 @@ func _init() -> void:
_test_end_text_differs_by_result()
_test_phase_text_nonempty()
_test_boss_name_uses_boss_rowset()
_test_level_clear_wording()
quit(Kit.finish("battle_narrator_test"))
@@ -36,9 +37,9 @@ func _test_numeric_lines_carry_values() -> void:
func _test_end_text_differs_by_result() -> void:
# 关卡收束(level_end=true)恒为「关卡通关!」——单场胜负文案只在非收束态出现
# 关卡收束(level_end=true)恒为「关卡完成!」——单场胜负文案只在非收束态出现
var level: String = NarratorScript.battle_end_text("lose", true)
Kit.check("关卡收束态恒为「关卡通关!」(实际「%s」)" % level, level == "关卡通关!")
Kit.check("关卡收束态恒为「关卡完成!」(实际「%s」)" % level, level == "关卡完成!")
var win: String = NarratorScript.battle_end_text("victory", false)
var lose: String = NarratorScript.battle_end_text("lose", false)
Kit.check("单场胜负文案互异(胜「%s」/负「%s」)" % [win, lose], win != lose)
@@ -80,3 +81,21 @@ func _test_boss_name_uses_boss_rowset() -> void:
var got: String = NarratorScript.boss_name(runner, db)
Kit.check("Boss 名取自 Boss 行集首行(期望「%s」实际「%s」)〔fv24〕" % [boss_expect, got], got == boss_expect)
Kit.check("Boss 名与同深度战斗行集名不同(期望「%s」≠「%s」——行类路由可判别)〔fv24〕" % [boss_expect, battle_name], boss_expect != battle_name)
# 关卡完成停点文案族措辞钉子(通关口径换代批):回血行/满血合法态/渠道句/摘要关数行——
# 「通关」退役后玩家可见面由本组断言钉在新措辞上(撤销证明:回退 battle_narrator 对应行→断言红→复原复绿)
func _test_level_clear_wording() -> void:
var healed: String = NarratorScript.curio_pick_header({"level_index": 1, "heal_gained": 5, "hp_before": 10, "hp_after": 15})
Kit.check("关卡完成回血行带入回血量与生命区间(实际「%s」)〔通关换代 V1〕" % healed,
healed.contains("关卡完成回血 +5(生命 10 → 15)"))
var full_hp: String = NarratorScript.curio_pick_header({"level_index": 1, "heal_gained": 0, "hp_before": 12, "hp_after": 12})
Kit.check("满血零回血合法态文案(实际「%s」)〔通关换代 V1〕" % full_hp,
full_hp.contains("生命已满——关卡完成回血 0(合法态)"))
var node_header: String = NarratorScript.node_curio_header()
Kit.check("奖励节点头部行渠道句点明关卡完成三选一(实际「%s」)〔通关换代 V2〕" % node_header,
node_header.contains("与关卡完成专长三选一同库分流"))
var summary: String = NarratorScript.adventure_summary_text(
{"cleared": false, "result": "failed", "levels_cleared": 1, "stopped_at": 2, "keystone_total": 4, "master_seed": 42})
Kit.check("冒险摘要完成关数行(实际「%s」)〔通关换代 V3〕" % summary,
summary.contains("完成 1 关 · 止步第 2 层") and not summary.contains("通关"))