Files
gplan-2/game/presentation/battle_narrator.gd
T
DKChan 32fdf957fd refactor(代码术语): 卡① flag→seal 全族换代+game/ 禁用词清扫(迭代 31 卡①)
标识符面:flag_*→seal_ 族(含 Godot 信号 flag_judged→seal_judged 与连接/回调同名、
FACTS 两键 flags_count→seals_count/dug_under_flag→dug_under_seal 全链、
.tres 键名与 schema .gd 同批同步);显式豁免零改动=size_flags_*(Godot 布局属性)+_fatal_flag。
中文面:金币→GP(金币袋→GP 袋)/插旗→封印/雷格→危险格/惩罚格→危险格/旗→封印·封印印记族,
game/ 内 *.gd/.tres/.md/.json 四类禁用词与 flag 双双零命中(V1/V4)。
守卫字面量不动(被检查的禁字不是使用):smoke_all V21 零「雷/旗」检查面、
ui_font_asset_test 字形集攻/雷/旗/封 保持原断言逻辑。
代拍板:板面单字槽 glyph「旗」→「封」(禁字纪律+单字位宽,消费面四处同名随动)。
素材换代(2026-10-18 用户裁案甲):assets/art/board/flag_seal.png→seal_mark.png(素材不入 git,
盘上改名+导入重刷),代码两处 res:// 字符串+COL_FLAG/CURSOR_FLAG 常量同步,
闸件四处随批(run_all_gates.sh/render_gate.sh/golden_baseline_refresh.sh/path_check.py)。
anchor.json 键名手改,重锚与改前改后 diff 随本卡收口(同裁案甲)。

基线读数(改前,主区 ed2dfaf6):冒烟 1151 检查 0 失败/单元 34 套全绿/探针 --click
存量红仅 screen_flow_probe(user:// 断点污染在册已知项)。
改后同批读数(本 worktree):冒烟 1151 检查 0 失败/探针 --click 同一存量红、其余全绿。
2026-10-19 02:52:38 +08:00

186 lines
9.6 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## VERTICAL SLICE - NOT FOR PRODUCTION
## 战斗流程叙事文案装配器:模式流转点的玩家可见文案(棋盘日志横幅/节点行/终局文案/相位指示)统一在此拼装,静态无状态。
## 文案内数值 = 载荷快照值(每关口径),非实时现值——日志断言与排障按此口径读。
extends RefCounted
# 节点进入横幅(棋盘日志——battle/boss 用;节点编号 = 行进器编号空间读面;行色 = UiKit.HEX_* 语义档单点)
static func node_banner(runner, node: Dictionary, t: String) -> String:
return "[color=#%s]── 节点 %d/%d · %s(编号 %d)──[/color]" % [UiKit.HEX_LOG_GAIN,
runner.node_index + 1, runner.nodes().size(),
"Boss 房" if t == "boss" else "战斗", runner.node_id_of(node)]
static func reward_line() -> String:
return "[color=#%s]关卡奖励点(节点内容待定——直通)[/color]" % UiKit.HEX_ROUTE_NOTE
static func rest_line(healed: int, hp: int, max_hp: int) -> String:
return "[color=#%s]恢复点:回复 %d HP(→ %d/%d)[/color]" % [UiKit.HEX_LOG_HEAL, healed, hp, max_hp]
# 商店房行(GP+商店房实施批——到达即开店;购买/离开归玩家意图)
static func shop_line(gp: int) -> String:
return "[color=#%s]商店房:GP %d——购买即时生效,离开后选下一跳[/color]" % [UiKit.HEX_LOG_GAIN, gp]
# 选路口行(零自动推进停点——战斗胜利/兑付确认后出现)
static func route_line(candidates: Array, runner) -> String:
var names: Array[String] = []
for cand in candidates:
names.append("节点 %d" % runner.node_id_of(cand))
return "[color=#%s]── 岔路口:可选下一跳 %s ──[/color]" % [UiKit.HEX_LOG_GAIN, " / ".join(names)]
# 关卡开场行(种子/加成口径注记——面板加成是否生效由调用方传入)
static func level_open_line(level_display: String, seed_val: int, atk: int, max_hp: int, boosted: bool) -> String:
return "[color=#%s]%s · 种子 %d · 攻击 %d / 生命 %d%s[/color]" % [UiKit.HEX_SEED_NOTE,
level_display, seed_val, atk, max_hp, "(含面板加成)" if boosted else ""]
# 调试开场行
static func debug_open_line(seed_val: int, level: int, level_mode: bool) -> String:
return "[color=#%s]种子 %d · 遭遇 %d 层%s[/color]" % [UiKit.HEX_SEED_NOTE,
seed_val, level, "(关卡模式 · HP 延续)" if level_mode else ""]
# 调试战斗终局文案(level_end = 关内两层模式的关卡末)
static func battle_end_text(result: String, level_end: bool) -> String:
if level_end:
return "关卡通关!"
if result == "victory":
return "战斗胜利!敌人全灭。"
return "战斗失败……探险者倒下了。"
# 相位指示文本(B 区轮次·阶段行)
static func phase_text(round_index: int, battle_result: String, waiting: bool, boss_active: bool, boss_name: String) -> String:
var boss_tag := " · Boss:%s" % boss_name if boss_active else ""
if battle_result != "":
# 打磨包 2026-09-29——判定文案两处措辞统一:相位行随结算判定行同词(战斗胜利/战斗失败,
# 与 battle_end_text 同源措辞),「战斗结束」退役
return "第 %d 轮 · %s%s" % [round_index, "战斗胜利" if battle_result == "victory" else "战斗失败", boss_tag]
if waiting:
return "第 %d 轮 · 结算中%s" % [round_index, boss_tag]
return "第 %d 轮 · 挖掘中%s" % [round_index, boss_tag]
# Boss 名解析(runner Boss 节点 → enemy 表行名;缺省遗迹守卫)。
# 行集多行后取**首行** display_name(2026-10-07 行集多行批——行序即契约的呈现面后果,
# Boss 行集混编护卫时 Boss 名显示可能为护卫名,知情代价登记 敌人系统 v0.15 / 出口报告;
# 零内容期每坐标恰一行,行为零变化)
static func boss_name(runner, db) -> String:
if runner != null:
var node: Dictionary[Variant, Variant] = runner.current_node()
if String(node.get("type", "")) == "boss":
var rowset: Array[Variant] = db.resolve_enemy_rowset(int(runner.level_row.level_index), int(node.get("node_depth", -1)))
if not rowset.is_empty():
return String(rowset[0].get("display_name", ""))
return "遗迹守卫"
# ---- 提升三选一文案族(迭代 10 奖励重构批——big_reward_text 随三件套退役)----
# 三选一停点头部行(回血反馈 + 选择引导;钥石增量行不上本面——单一展示面归战斗总结结算框,
# level-clear.md v2 禁入清单口径)
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))])
else:
lines.append("生命已满——通关回血 0(合法态)")
lines.append("选择一件提升带走(另两件丢弃不可反悔):")
return "\n".join(lines)
# 三选一选项卡文案(件名 + 档位 + 效果读数;值 = 冒险表行值,组合根喂入;TP0 令牌池——档位词
# 对齐 v2.2 追认 b 口径:小/大前缀(小GP/大GP 族)、恢复/AP 不带档位词;道具件掷定前置时
# treasure_line = 具体道具「名 · 效果摘要」行——池种退役后恒空走通用分支,机械惰性保留)
static func bonus_card_text(id: String, tier: String, value: int, treasure_line: String = "") -> String:
var ts := ""
if tier == "common":
ts = " · 小"
elif tier == "rare":
ts = " · 大"
match id:
"attack":
return "攻击力提升%s\n攻击 +%d" % [ts, value]
"max_hp":
return "生命上限提升%s\n生命上限 +%d" % [ts, value]
"armor":
return "护甲等级提升%s\n护甲 +%d" % [ts, value]
"gp":
return "GP 袋%s\nGP +%d" % [ts, value]
"heal":
return "恢复药剂\n生命 +%d(当即回复)" % value
"treasure_item":
if treasure_line != "":
return "秘宝\n%s" % treasure_line
return "秘宝\n随机获得一件"
"ap":
return "行动点提升\n每战首轮 AP +%d" % value
return id
# 三选一兑付反馈行(选中件效果 + 丢弃提示;tier 缺省普通档——旧载荷防御口径)
static func bonus_result_text(result: Dictionary) -> String:
var name := ""
if String(result.get("picked", "")) == "relic" and String(result.get("feat_id", "")) != "":
name = feat_card_text({"feat_id": String(result.get("feat_id", "")), "display_name": String(result.get("feat_name", ""))}).replace("\n", " · ")
else:
name = bonus_card_text(String(result.get("picked", "")), String(result.get("tier", "")), int(result.get("value", 0))).replace("\n", " · ")
return "已选择:%s\n其余选项已丢弃(不可反悔)" % name
# 特性卡片文案(迭代 12 T1;2026-10-04 呈现补强——第二行=效果摘要,用户反馈「不知道件是
# 干嘛的」缺口承载。内容数据驱动扩展批 T3 文案表值化:摘要=数据行 desc 字段,零硬编码映射。
# 2026-10-12 奇物呈现整改批:「件名 · 流派」族名字样摘除(内部分类禁上玩家可见面——新 29 行
# 无 archetype,原 fallback 实显兜底词「流派」=僵尸文案),卡片=件名+效果摘要两行)
static func feat_card_text(row: Dictionary) -> String:
if row.is_empty() or String(row.get("feat_id", "")) == "":
return "特性\n???"
var summary := String(row.get("desc", ""))
if summary != "":
return "%s\n%s" % [String(row.get("display_name", row.get("feat_id", ""))), summary]
return String(row.get("display_name", row.get("feat_id", "")))
# Boss 胜利战斗总结结算行(settlement-summary v1.1——钥石增量行;组合根拼 GP 行后追加)
static func bonus_keystone_line(payload: Dictionary) -> String:
return "钥石 +%d · 累计 %d" % [int(payload.get("keystone_gained", 0)), int(payload.get("keystone_total", 0))]
# 奖励节点三选一头部行(迭代 10 追加批——与通关三选一共享同池;node-reward.md v2)
static func node_curio_header() -> String:
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" % [
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))]
# 钥石入账明细文案(元进度实现批;迭代 10 改口径——钥石全源 = Boss 逐杀掉落,「大奖励」行退役;
# name_lookup = item_id → 显示名投影,由组合根自 meta 表喂入)
static func keystone_settle_text(detail: Dictionary, name_lookup: Dictionary = {}) -> String:
var parts: Array[Variant] = []
for d in detail.get("boss_drops", []):
parts.append("第 %d 关 Boss %s +%d" % [int(d.get("level", 0)),
"首杀" if bool(d.get("first", false)) else "重复杀", int(d.get("amount", 0))])
var line := "钥石入账:" + (" · ".join(parts) if not parts.is_empty() else "本局无掉落")
var unlocks: Array[Variant] = detail.get("new_unlocks", [])
if not unlocks.is_empty():
var names: Array[String] = []
for iid in unlocks:
names.append(String(name_lookup.get(String(iid), String(iid))))
line += "\n工坊新解锁:" + "、".join(names)
return line + "\n本局入账合计 +%d 钥石" % int(detail.get("total", 0))