# VERTICAL SLICE - NOT FOR PRODUCTION # Date: 2026-09-12(code-review 修复批——自 main.gd 拆出,规范 1.1/1.3 文案拼装独立成装配器) # 战斗流程叙事文案装配器:组合根在模式流转点的玩家可见文案(棋盘日志横幅 / 节点行 / 终局文案 / # 相位指示)在此统一拼装——main.gd 头注"本类不拼呈现字符串"由本类兑现。静态无状态。 # 数值口径注记:胜利文案携带的 HP/道具数 = 关卡载荷快照值(每关口径)。 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]商店房:金币 %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 表行名;缺省遗迹守卫) static func boss_name(runner, db) -> String: if runner != null: var node: Dictionary = runner.current_node() if String(node.get("type", "")) == "boss": var row: Dictionary = db.resolve_enemy_rowset(int(runner.level_row.level_index), int(node.get("node_depth", -1))) return String(row.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 "金币袋%s\n金币 +%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("relic_id", "")) != "": name = relic_card_text({"relic_id": String(result.get("relic_id", "")), "display_name": String(result.get("relic_name", "")), "archetype": String(result.get("relic_archetype", ""))}).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 呈现补强——第三行=hook 效果摘要,用户反馈「不知道件是 # 干嘛的」缺口承载;摘要=静态映射表 provisional 文案归代拍板白名单「件文案微调」) static func relic_card_text(row: Dictionary) -> String: if row.is_empty() or String(row.get("relic_id", "")) == "": return "特性\n???" var arch_names := {"magic": "魔法", "pierce": "穿刺", "normal": "普攻", "dig": "开格", "flag": "标旗", "charge": "蓄力", "devour": "吞噬"} var arch := String(arch_names.get(String(row.get("archetype", "")), "流派")) var summaries := { "hero_attack_rider": "普攻附加必中伤害", "clamp_modifier": "克制面强化", "first_dig_spell": "首格追加奥术矢", "kill_overflow": "击杀半额弹射", "dig_count_segments": "开格积穿刺矢", "roll_discount": "打轻甲更易命中", "round_dig_bonus": "开格越多打得越疼", "hit_trade": "加伤换命中", "armor_roll_rewrite": "攻击恒75%折扣", "heavy_debuff": "打重甲附加破防", "dig_yield_mult": "开格产出放大", "deep_dig": "开格2AP·产出双份", "flag_resource": "标中雷额外得资源", "nest_aura": "控巢攻击光环", "flag_all_hit": "全中标旗伤加倍", "flag_strike": "标中旗必中打敌", "ap_carry": "剩AP转下轮攻击", "charge_shield": "蓄力转减伤", "charge_spike": "蓄满下轮爆发", "terrain_refund": "吃地形伤返资源", "terrain_dig_double": "挖伤害格双产出", "flag_neutralize": "标旗中和伤害格", "variant_counter": "吃地形伤反打全体", } var summary := String(summaries.get(String(row.get("hook", "")), "")) if summary != "": return "%s · %s\n%s" % [String(row.get("display_name", row.get("relic_id", ""))), arch, summary] return "特性 · %s\n%s" % [arch, String(row.get("display_name", row.get("relic_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_bonus_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 = [] 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 = 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))