接手前任中断的 T3 半成品(脏文件三件:battle_orchestrator/effects engine/feats.tres),方向核实无误后续做收口; 承接修正四处随本提交留痕:①perfect_defense 的 when 用了 fact 封闭 9 项外的 flags_hit_all(装载校验必红)—— 改 flags_count gte 1 保守判定,全中闸=flag_settle 发射点施加面承载(载荷字段仅供观测);②has 算子语义与行 不匹配(数组包含对 String 恒假)——落非空在场判定(value 缺省)+数组包含(value 给定),查询面落法=代拍板 白名单;③前任漏写 _harmful_adjacent 助手(flag_hit 发射点调用必炸);④counter_add set 键语义 bool() 吞 0 (清零行 set:0 被当 false 走加零通道)——改 has(set) 置值模式,四处施加点同修。 本提交主干(专长族全量迁移——任务书 §六 T3): - damage_controller:feat_flags 旗面整体退役,五事件施加面接线——attack_commit 命中随单据 context 携带 (发射点=开单入口,任务书 §3.1 定稿位;主角攻击单恒段内首单,开单现场=结账现场),rider/改写/加值/ 段数/状态五族按迁移前算序施加(重锤两拍/专注/spike 恒全额/猎弱移档/魔网湛蓝猎手克制直调/整备每层 +1/厚积满层倍乘/奥术+猎巢+执拗 rider 合计后档位暴击);armor_roll 免掷恒折扣、clamp_check 双面 (hero+board_typed)delta 窗口、kill 溢出弹射(landed×value%)、damage_taken shield 加法族合计。 _feat_clamp_bonus/13 处旗消费点拆除。 - battle_orchestrator:_combat_segment 换代(dig_streak/charge_layers 注伤害控制器运行态;连珠矢段数 施加位紧随主角攻击单开立=迁移前开单位,flush 兑现序逐位一致);round_start 发射位前移至行动经济轮 切换前(树规则 grant_ap queue 当轮入队当轮兑现——先手压制首轮语义锚);深挖快照滚动拆除(计数器 reset_at 声明承载);熔火/连击旧计数变量退役。 - action_economy:feat_ap_carry/feat_dig_extra/deep_paid_count 旧注入旗拆除;dig_paid 施加面落地 (counter_spend 对价:AP 富余付全价积计数、不足 fallback 只付基础 1——2026-10-04 降级口径逐位保留)。 - feat_table schema:HOOKS 23 挂点枚举/MINE_HOOKS/EFFECT_KEYS/hook+effects 双形态校验全删——triggers 订阅形态唯一合法行形(行键白名单收口 feat_id/display_name/rarity/archetype/triggers/desc/note)。 - feats_engine:hooks_for 挂点投影退役(订阅装载序=feats 表序,由效果引擎 rebuild 承载)。 - battle_narrator:专长卡片文案表值化(summaries 硬编码映射拆除,读行 desc——27 件措辞逐字迁移)。 - smoke 断言面随迁(任务书 v1.1 补列项):构造表 5 行换代 triggers 形态;挂点投影断言换代订阅装载面; ④-⑧ 求值断言(_feat_flags_for 直呼+旗读数)全量改写为 rebuild+dispatch 命中面(27 件逐事件覆盖); 深挖钻头付费开掘回归闸改 counter_spend 计数器面;特性卡 desc 直读断言新增。 映射表对拍注记(行为等价优先,.tres 尾注同步留痕):猎巢人/矿工的执拗映射表标 modify_ticket/attack_bonus, 现行为必中附加通道(hero_rider 后置于乘区与下限)——落 add_ticket_rider(per 取数载荷);整备军备现行为 双效果(轮末转蓄力+每层首段攻 +1)——两行订阅;深挖钻头 per-round 计数器对(paid/given)复刻旧 v2 跨轮不对称语义(given 不随轮重置)。 验证:冒烟 800 项 0 失败(三闸过)· 单元 17 套全绿 · S1 魔法流对拍预检=MC formed/off 两态与 T0 pre 锚 (83000 种子段)同种子逐位一致(2×5 局,三档只测不判)——14 件迁移行实战斗承载零分岔。 设计权威:design/research/研究-内容数据驱动扩展架构-2026-10-06.md(案乙)。 任务书:design/research/任务书-内容数据驱动扩展批-2026-10-06.md §六 T3、§五映射表、§七契约 2/4。
186 lines
9.6 KiB
GDScript
186 lines
9.6 KiB
GDScript
# 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("feat_id", "")) != "":
|
||
name = feat_card_text({"feat_id": String(result.get("feat_id", "")), "display_name": String(result.get("feat_name", "")), "archetype": String(result.get("feat_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 呈现补强——第三行=效果摘要,用户反馈「不知道件是
|
||
# 干嘛的」缺口承载。内容数据驱动扩展批 T3 文案表值化:摘要=数据行 desc 字段,零硬编码映射——
|
||
# 新件写行即有名有姓,措辞与旧 summaries 表逐字一致)
|
||
static func feat_card_text(row: Dictionary) -> String:
|
||
if row.is_empty() or String(row.get("feat_id", "")) == "":
|
||
return "特性\n???"
|
||
var arch_names := {"magic": "魔法", "pierce": "穿刺", "normal": "普攻", "dig": "开格",
|
||
"flag": "标旗", "charge": "蓄力", "devour": "吞噬"}
|
||
var arch := String(arch_names.get(String(row.get("archetype", "")), "流派"))
|
||
var summary := String(row.get("desc", ""))
|
||
if summary != "":
|
||
return "%s · %s\n%s" % [String(row.get("display_name", row.get("feat_id", ""))), arch, summary]
|
||
return "特性 · %s\n%s" % [arch, 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 = []
|
||
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))
|