feat(迭代27 T6+T7): 波五b 卫生件包+文档同步闸——对拍全同

T6 卫生件(P2 组收口):
- 死代码 13 函数删除(审计 16 处复核=ui_kit:486 实读现役不删,追认单#14)+FeatsEngine
  整文件退役(class_name 注销)
- 字符串 Callable×10 位→方法引用形态(编译期方法存在性校验;语法实证 METHOD_REF=true)
- 状态机 enum 化: adventure State(外部 String 契约 getter 词表逐位一致+外部纯读实证)/
  standee HeroState(register/set String 形参零变化+probe 面恒等)
- ConfigDB _fatal 退出归根(核心层去 quit/crash→_fatal_flag,决策归组合根 fail-fast 通道)
  +resolver 诊断/数据通道全面 Callable 化(ConfigDb↔Resolver 类型环断根)

T7 文档同步闸:
- 实现报告 design/qa/iter27-实现报告-2026-10-14.md(五波+闸表+追认单十五项+反哺点两条+目检五件)
- 06 迭代 27 条销号+GDD 反哺点两条新条;07 ㉖ 目检节+v1.0 版本行;roadmap ✅ 换代
- 审计报告本体按退役规则 git rm(吸收即死;修正计数由卡片承载);卡片来源行改指路

闸: 对拍三变体全同(第五跑)+冒烟 1081/0+run_all 27 套+probe click+render golden+static_scan 全绿
This commit is contained in:
DKChan committed 2026-10-15 01:41:37 +08:00
1 parent b605022001
commit eea345882b
24 files changed
+200 -309

No files matched your search

+13 -9
View File
@@ -54,7 +54,11 @@ signal wallet_changed(event: String, gp: int, extra: Dictionary)
var db: ConfigDb
var rng: RngServiceCore
var state := "idle" # idle / running / settling(冒险结构.md 三态)
enum State { IDLE, RUNNING, SETTLING } # 会话状态机(迭代 27 T6——裸字符串 enum 化:内部 enum,外部 String 契约 getter 词表逐位一致)
var _state: State = State.IDLE
var state: String: # 外部契约面(只读——外部零写点实证;"idle"/"running"/"settling" 零变化)
get:
return State.keys()[_state].to_lower()
var ledger := {"hero_hp": 0, "gp": 0} # 冒险账本(生命 + GP 钱包——经 start_level_external 注入 runner 共享)
var run_ledger := RunLedger.new() # 共享四本账(迭代 27 T3——P1-4 收口:attune_charges/vision_counter/adventure_counters/bonus 单类持账;裁点②甲)
var purchased := {} # product_id → 已购档数(跨关延续、冒险收束清零——递增定价输入)
@@ -153,7 +157,7 @@ func start_adventure(seed_val: int, params: Dictionary = {}) -> bool:
for r in feat_seed:
_feats.append(String(r))
ledger["hero_hp"] = effective_max_hp() # 生命账本置满(生效上限口径——T17 修复:含调试浮窗 pct,与战斗内同源)
state = "running"
_state = State.RUNNING
return _start_current_level()
@@ -257,13 +261,13 @@ func resume_adventure(bp: Dictionary, params: Dictionary = {}) -> bool:
migrated.append({"kind": String(c), "tier": db.adventure.TIER_COMMON})
_curio_pending = {"candidates": migrated}
var payload: Dictionary[Variant, Variant] = (bp_pending.get("payload", {}) as Dictionary).duplicate(true)
state = "running"
_state = State.RUNNING
curio_choice_presented.emit(payload)
return true
if not bp_result.is_empty():
_pending_next_level = true
_curio_result = bp_result.duplicate(true)
state = "running"
_state = State.RUNNING
curio_resolved.emit(_curio_result.duplicate(true))
return true
_resume_bp = bp.duplicate(true)
@@ -271,7 +275,7 @@ func resume_adventure(bp: Dictionary, params: Dictionary = {}) -> bool:
var bp_charges = bp.get("attunement_charges", null)
if bp_charges is Dictionary:
run_ledger.attune_charges = (bp_charges as Dictionary).duplicate(true)
state = "running"
_state = State.RUNNING
return _start_current_level()
@@ -936,7 +940,7 @@ func _start_current_level() -> bool:
_runner.set_run_ledger(run_ledger) # 迭代 27 T3——共享四账单类下传(原四 setter/参数散传收口;run_ledger=四账类,ledger=关卡账沿用旧名)
_runner.sync_attune_inventory() # 迭代 11 第①批 T6 原语义——能力件骑库存面注入(随账下传同拍)
var ok: bool = _runner.start_level_external(level_index, ledger, purchased,
Callable(self, "apply_rest"), _base_override, true, template_pick, resume_state)
apply_rest, _base_override, true, template_pick, resume_state)
if ok:
_runner.set_attunement(_attunement) # 迭代 11 第①批 T5——同调装配下传(enc 携带进战斗结算)
_runner.set_feats(_feats) # 迭代 12 T1——特性持有集下传(begin_battle enc.feats 求值面)
@@ -945,7 +949,7 @@ func _start_current_level() -> bool:
if _shop_mode == "pool":
# 商店共享池注入(迭代 11 第②批 T3——库存账本引用+消费 handler 双下传;legacy 模式零注入零扰动。
# 迭代 20 T2——cap 检查回调四参注入)
_runner.set_shop_pool(_curio_deck, Callable(self, "_consume_pool_purchase"), Callable(self, "_feat_ticket_consider"), Callable(self, "_curio_cap_ok"))
_runner.set_shop_pool(_curio_deck, _consume_pool_purchase, _feat_ticket_consider, _curio_cap_ok)
# 迭代 20 T4——凭证件三账注入(节点级/回合级一次性:注入即清零,未用即过期=寿命档语义)
_runner.set_voucher_grants(_pending_rap, _time_sand_queue, _pending_insurance)
_pending_rap = 0
@@ -1043,9 +1047,9 @@ func _settle(result: String) -> void:
if _save_manager != null:
_save_manager.clear_breakpoint() # 三结局清断点(迭代 8 卡①/T3——退出冒险=放弃流程+清除断点)
_save_manager.write_to_disk()
state = "settling"
_state = State.SETTLING
adventure_finished.emit(summary)
state = "idle"
_state = State.IDLE
# ---- 转发族(签名与 LevelRunner 同款——呈现层对接透明)----
+3 -7
View File
@@ -108,7 +108,7 @@ var _fx: EffectEngine
func _fx_emit(event: String, payload: Dictionary) -> Array:
if _fx == null:
return []
return _fx.dispatch(event, payload, Callable(self, "_fx_fact"))
return _fx.dispatch(event, payload, _fx_fact)
# round_start 发射+施加(内容数据驱动扩展批 T3——grant_ap mode:queue=当轮入队,行动经济轮切换
@@ -219,10 +219,6 @@ func attune_has(item_id: String) -> bool:
# 装备件表值读取(charge_n 等——TreasureItemTable 行;未装配不读表。magnitude 读数面随 T4
# 秘宝订阅迁移退役——行值经 value_from 在 rebuild 定影,零运行期按件取值)
func _attune_row(item_id: String) -> Dictionary:
return db.treasure_item_row(item_id) if _attune_has(item_id) else {}
# 装配集参数合计(迭代 22 秘宝批 T3——常时参数面 kind 的装配集 Σmagnitude 读数口;
# kind 分发查询先例=_attune_refresh_x_total bonus_refresh。零装配=0)
func attune_param_sum(kind: String) -> int:
@@ -395,8 +391,8 @@ func begin_battle(enc: Dictionary) -> bool:
_fx_bonus_delta = {"atk": 0, "max_hp": 0, "armor_class": 0, "ap": 0}
var ledger_ref = enc.get("ledger", null)
_ledger = ledger_ref if ledger_ref is RunLedger else RunLedger.new() # 缺省新账=调试/单场零扰动面
_damage.fx_reveal_request = Callable(self, "_fx_enqueue_reveal")
_damage.fx_ac_request = Callable(self, "_apply_ac_bonus")
_damage.fx_reveal_request = _fx_enqueue_reveal
_damage.fx_ac_request = _apply_ac_bonus
var feat_ids: Array[Variant] = enc.get("feats", []) as Array
# 迭代 20 T3 换代(V14 旧实现摘除)——流派档位质变随重设计退役(奇物.md v1「不设流派标签、
# 不设档位机制」):FeatsEngine 求值与 rider_tier 注入拆除,散件单体效果=唯一形态
-4
View File
@@ -58,10 +58,6 @@ func board_variant_ids() -> Array[String]:
# 中和格变异(迭代 12 T6 净化工兵——伤害型地形格变普通格;🔴 R4 甲仅限伤害型的过滤在调用方)
func clear_variants(c: Vector2i) -> void:
variants.erase(c)
func neighbors8(c: Vector2i) -> Array:
var out: Array[Vector2i] = []
for dy in range(-1, 2):
+15 -20
View File
@@ -60,7 +60,8 @@ var _reload_gate := false # 数据配置 EC8——战斗结
func _init() -> void:
_resolver = ConfigDbResolver.new(self) # 协作类持门面引用(诊断/致命通道回呼)
_resolver = ConfigDbResolver.new(Callable(self, "push_diag"), Callable(self, "reset_warn_dedup"),
func() -> Variant: return board_gen.rows, func() -> Variant: return enemy.rows, func() -> Variant: return difficulty) # 协作类持门面引用(诊断/致命通道回呼)
func push_diag(severity: String, category: String, message: String) -> void:
@@ -71,20 +72,25 @@ func push_diag(severity: String, category: String, message: String) -> void:
push_error("[ConfigDB/%s] %s" % [category, message])
# 运行期数据致命(2026-09-18 用户拍板:越域记 Fatal 级日志后立即退出——数据配置通道 fail-fast 纪律补齐到查询期)
# 运行期数据致命(2026-09-18 用户拍板 fail-fast;迭代 27 T6——退出执行归根:核心层不再自携
# quit/crash,越域记 Fatal 级日志后置 _fatal_flag,进程退出决策归组合根(main._ready 表加载
# 失败已有同通道 fatal 屏+fail-fast 边界)——查询期消费方按返回值安全面走既有默认值回退)
func _fatal(category: String, message: String) -> void:
push_diag("fatal", category, message)
push_error("[ConfigDB/%s] FATAL: %s —— 数据配置致命,进程退出" % [category, message])
var main_loop := Engine.get_main_loop()
if main_loop is SceneTree:
main_loop.quit(1) # 帧末退出,退出码 1——调用方须立即 return,勿再消费返回值
else:
OS.crash("ConfigDB/%s fatal: %s" % [category, message])
push_error("[ConfigDB/%s] FATAL: %s —— 数据配置致命(退出决策归组合根)" % [category, message])
_fatal_flag = true
var _fatal_flag := false # 运行期致命旗(迭代 27 T6——退出归根承载;组合根/闸读面)
func is_fatal() -> bool:
return _fatal_flag
func reset_diagnostics() -> void:
_diagnostics.clear()
_resolver.reset_warn_dedup() # R1 告警去重注册表(协作类持钥——清空时点与拆分前一致)
_resolver._reset_warn.call() # R1 告警去重注册表(协作类持钥——清空时点与拆分前一致;通道 Callable 化随 T6)
func diagnostics() -> Array:
@@ -250,10 +256,6 @@ func level_count() -> int:
# ---- GP+商店房实施批(2026-09-21):ShopTable 消费面(行集解析单点——runner/战斗编排禁自扫表)----
# ── 秘宝访问器(迭代 7 T1——消费面一律走访问器,禁直读行集)──
# 活行集(非 retired 非 deferred)——投放校验/奖励节点池/呈现可用性的唯一取数口径
func treasure_active_rows() -> Array:
return treasure_items.active_rows() if treasure_items != null else []
func treasure_item_row(item_id: String) -> Dictionary:
return treasure_items.row_by_id(item_id) if treasure_items != null else {}
@@ -270,13 +272,6 @@ func treasure_attunement_rows() -> Array:
# 活行词表(掉落投放 effect_id 白名单——过渡渠道口径)
func treasure_live_ids() -> Dictionary:
var out := {}
for row in treasure_transitional_rows():
out[String(row.get("item_id", ""))] = true
return out
# ── 同调轴访问器已删除(迭代 21 T5——attunement_growth 谓词表随槽位收编退役:槽位数量=树同调位
# 节点点亮数,判定源=Attunement.slots;boss_kill_count/clear_count 计数域保留——非槽位硬消费在册)──
+20 -10
View File
@@ -11,12 +11,21 @@
class_name ConfigDbResolver
extends RefCounted
var _db: ConfigDb # 门面引用(表读取 + 诊断/致命通道——_init 注入)
var _diag: Callable # 诊断/致命通道(迭代 27 T6——门面类型反持解耦,validator 先例:签名 (level, category, message))
var _reset_warn: Callable # 去重注册表清空通道(reset_diagnostics 时点由门面驱动)
var _board_gen_rows_get: Callable # () -> Array——board_gen 行集活读(数据面经 Callable 注入,ConfigDb 类型环断根)
var _enemy_rows_get: Callable # () -> Array——enemy 行集活读
var _difficulty_get: Callable # () -> DifficultyTable(可空——表缺省合法态)
var _warn_dedup: Dictionary[String, bool] = {} # R1 四元组去重注册表(自 ConfigDb 迁入)
func _init(db_ref: ConfigDb) -> void:
_db = db_ref
func _init(diag_cb: Callable, reset_warn_cb: Callable, board_gen_rows_cb: Callable,
enemy_rows_cb: Callable, difficulty_cb: Callable) -> void:
_diag = diag_cb
_reset_warn = reset_warn_cb
_board_gen_rows_get = board_gen_rows_cb
_enemy_rows_get = enemy_rows_cb
_difficulty_get = difficulty_cb
# 去重注册表清空(门面 load_tables / reset_diagnostics 调用——与拆分前清空时点逐位一致)
@@ -27,21 +36,22 @@ func reset_warn_dedup() -> void:
# ---- R1 难度行解析(2026-09-18 用户拍板修订:ceil 向上取整保证偏难;域越界与内容缺口 Fatal 退出)----
func resolve_board_gen(L: int, D: int) -> Dictionary:
return _resolve_progress(_db.board_gen.rows, L, D, "board_gen")
return _resolve_progress(_board_gen_rows_get.call(), L, D, "board_gen")
func resolve_enemy_rowset(L: int, D: int) -> Array[Dictionary]:
# 行集数组形态(2026-10-07 敌人表行集多行批——原单行 Dictionary 退役):ceil 命中 (l*,d*) 后
# 返回该坐标全部行、按表文件行序(行序即契约——同坐标多行文件内相邻由 EnemyTable.validate 兜底)。
# 空集表达 = 空数组(生产表下 ceil 命中坐标恒有行,空集仅表空时可达——validate 已拦)。
return _resolve_progress(_db.enemy.rows, L, D, "enemy", true)
return _resolve_progress(_enemy_rows_get.call(), L, D, "enemy", true)
# 难度曲线与Boss DifficultyTable 消费面(敌人系统.spawn 调用):(L,D) 精确匹配修饰乘数,越域值 clamp(GDD Edge Case——组合事故非配置事故)
func resolve_difficulty_modifiers(L: int, D: int) -> Dictionary:
if _db.difficulty == null:
var dt = _difficulty_get.call()
if dt == null:
return {}
for row in _db.difficulty.rows:
for row in dt.rows:
if int(row.get("level_index", 0)) == L and int(row.get("node_depth", -1)) == D:
return {
"hp_mult": clampf(float(row.get("hp_mult", 1.0)), 0.1, 10.0),
@@ -52,7 +62,7 @@ func resolve_difficulty_modifiers(L: int, D: int) -> Dictionary:
func _resolve_progress(rows: Array, L: int, D: int, table_id: String, return_all := false) -> Variant:
if L < 1 or D < 0:
_db._fatal("request_oob", "%s 请求越域 (%d,%d)——进度坐标低于合法域(关卡序号≥1、节点深度≥0)" % [table_id, L, D])
_diag.call("fatal", "request_oob", "%s 请求越域 (%d,%d)——进度坐标低于合法域(关卡序号≥1、节点深度≥0)" % [table_id, L, D])
return {}
var level_keys: Array[int] = []
for row in rows:
@@ -60,7 +70,7 @@ func _resolve_progress(rows: Array, L: int, D: int, table_id: String, return_all
if not level_keys.has(lv):
level_keys.append(lv)
if level_keys.is_empty():
_db._fatal("structural", "%s 行集为空——锚行校验应已拦截" % table_id)
_diag.call("fatal", "structural", "%s 行集为空——锚行校验应已拦截" % table_id)
return {}
level_keys.sort()
var l_star := _ceil_key(level_keys, L)
@@ -96,7 +106,7 @@ func _resolve_progress(rows: Array, L: int, D: int, table_id: String, return_all
if not _warn_dedup.has(dedup_key):
_warn_dedup[dedup_key] = true
var verb := "clamp 解析(超出最大行取最大值)" if missing.contains("clamp") else "ceil 解析(向上取整保证偏难)"
_db.push_diag("warning", "resolve_" + missing, "%s 请求 (%d,%d) %s → (%d,%d) [%s]——2026-09-18 拍板" % [table_id, L, D, verb, l_star, d_star, missing])
_diag.call("warning", "resolve_" + missing, "%s 请求 (%d,%d) %s → (%d,%d) [%s]——2026-09-18 拍板" % [table_id, L, D, verb, l_star, d_star, missing])
if return_all:
var all_out: Array[Dictionary] = []
for row in rows:
+1 -5
View File
@@ -95,14 +95,10 @@ func set_fx(fx: EffectEngine) -> void:
func _fx_hits(event: String, payload: Dictionary) -> Array:
if _fx == null:
return []
return _fx.dispatch(event, payload, Callable(self, "_fx_fact"))
return _fx.dispatch(event, payload, _fx_fact)
# 事件发射薄面(免施加观测位——enemy_hp_threshold 等现无订阅者的事件保留零扰动面)
func _fx_dispatch(event: String, payload: Dictionary) -> void:
_fx_hits(event, payload)
# 补充事实面(fact 取值第二优先源——payload 未携带的运行态读数:charge_layers 编排器每轮注入。
# 迭代 20 T1——hp_ratio 补入〔背水 when 面,敌技 damage_taken 载荷不带血比例时回落〕;
# fx_reveal_request=揭示效果回调〔斩首学者 kill 订阅面——编排器注入,入队授予段末兑付〕)
+1 -18
View File
@@ -413,21 +413,4 @@ static func validate_trigger_row(rid: String, row: Dictionary, triggers: Array,
## 按序改写族(收集→单点按声明序施加):modify_ticket 及一切改写窗口效果。
## 按序消耗族(按订阅序消耗共享资源,耗尽即止;可选 fallback):counter_spend。
## 即时执行族(每条命中各自执行一次,执行序=声明序):open_ticket、grant_ap(mode:transfer)、
## rewrite_board、apply_status——发射点逐条执行。
static func combo_of(kind: String) -> String:
match kind:
"add_ticket_rider", "attack_bonus", "grant_material", "counter_add", "shield", "heal", "ac_bonus", "damage_reduce", "bonus_delta":
return "additive"
"grant_ap":
return "additive" # mode:transfer 由发射点即时执行族承载(转移非加法)
"yield_mult", "damage_mult":
return "multiplicative"
"spawn_spell", "overflow", "spike":
return "any_true"
"modify_ticket":
return "ordered_write"
"counter_spend":
return "ordered_spend"
"open_ticket", "apply_status", "blast_scaled":
return "immediate"
return "immediate" # 默认即时执行(每条命中各自执行一次)
## rewrite_board、apply_status——发射点逐条执行。
-39
View File
@@ -1,39 +0,0 @@
# VERTICAL SLICE - NOT FOR PRODUCTION
# Date: 2026-10-04(双层 BD 实施批 T1——标签档位激活引擎)
# 流派成型判据机制化(整合定稿 设计工具 1——以撒套装/小骨铭文先例):
# 同流派标签 relic 计数 ≥2 → 档一(流派核心规则);≥4 → 档二(完全体);<2 = 散件(单体保底——
# 每件效果独立生效,档位只做质变不做启动,反例 B-2)。
# 纯函数零 await 零 RNG 零副作用(Attunement 求值服务同款纪律);核心层零特判延伸——引擎只读
# feat_table 行集与持有集,接线点消费 tiers/hooks 投影。
class_name FeatsEngine
extends RefCounted
## 档位求值:feat_ids 持有集 × 行集 → {archetype: tier}(tier ∈ 0/1/2;无行流派不出现)
static func tiers_for(feat_ids: Array, table: FeatTable) -> Dictionary:
var counts := {}
for rid in feat_ids:
var row: Dictionary[Variant, Variant] = table.row_by_id(String(rid)) if table != null else {}
if row.is_empty():
continue
var arch := String(row.get("archetype", ""))
counts[arch] = int(counts.get(arch, 0)) + 1
var out := {}
for arch in counts.keys():
var n := int(counts[arch])
if n >= 4:
out[arch] = 2
elif n >= 2:
out[arch] = 1
else:
out[arch] = 0
return out
## 挂点投影随 T3 退役(内容数据驱动扩展批——hook 词族消亡:订阅投影=效果引擎 rebuild 装载序
## (feats 表序),投影稳定性由 EffectEngine._triggers 声明序承载)
## 成型判读(读数面口径):流派 tier ≥1 = 成型(档一即判——需求卡片完成定义「标签+档位激活引擎可判成型」)
static func is_formed(tiers: Dictionary, archetype: String) -> bool:
return int(tiers.get(archetype, 0)) >= 1
-1
View File
@@ -1 +0,0 @@
uid://6d12lkx5w2x8
+1 -1
View File
@@ -160,7 +160,7 @@ func start_level(level_index: int, seed_val: int, base_override_in: Dictionary =
# 三键 {atk, max_hp, armor_class}、缺键/-1 = 0、负值 clamp 0。游戏直入口仍恒白板——
# 注入面只服务数值验证工具,非玩家可达路径)
_external = false
_rest_handler = Callable(self, "rest_heal_amount")
_rest_handler = rest_heal_amount
_ledger = {"hero_hp": 0, "gp": 0}
var row := _find_level_row(level_index)
if row.is_empty():
-4
View File
@@ -38,10 +38,6 @@ func _init(db_ref: ConfigDb, action_economy_ref: ActionEconomy) -> void:
# ---- 板态 ----
func has_board() -> bool:
return _board != null
# 板产物只读出口(E2 收口——战斗结算 快照 / 组合根 rebuild / 探针经此取板,禁再直读字段)
func current_board() -> BoardData:
return _board
-8
View File
@@ -432,14 +432,6 @@ func set_attune_refresh_level(n: int) -> void:
# 同调位计数源组装(Attunement 服务取数口——clear_count = 逐关通关次数合计〔含重复关,底稿 §2.1 口径〕;
# clear_count(level_index) 逐关访问器零改动复用)
func attunement_counters() -> Dictionary:
var total := 0
var counts: Dictionary[Variant, Variant] = _data["meta"]["level_clear_counts"]
for k in counts.keys():
total += int(counts[k])
return {"clear_count": total, "boss_kill_count": boss_kill_count()}
func recent_summaries() -> Array:
return _data["meta"]["recent_summaries"].duplicate(true) # 值拷贝出户(本类契约「禁碰内部结构」——消费方直改不得渗入存档内存态)
-16
View File
@@ -65,14 +65,6 @@ func inventory_count() -> int:
# 持有数(迭代 10 奖励重构批——同调位上限判定读数:只计未消耗件;Used 不可逆即不再持有)
func holding_count() -> int:
var n := 0
for rec in _items:
if not bool(rec.get("used", false)):
n += 1
return n
# 同调位守卫谓词——迭代 11 第①批 T7 退役(读取源 transitional_sync_slots 过渡键已随表退役;
# 容量闸正主 = 营地装配面 Attunement.slots 满位禁选。函数体保留空壳防四散引用误删遗漏——
# T8 冒烟「过渡键读取点归零」断言覆盖)
@@ -80,14 +72,6 @@ func holding_count() -> int:
# 持有件记录(迭代 10 追加批——奖励节点替换子交互数据源:只列未消耗件,handle 定位丢弃目标)
# 丢弃(同调位替换交互节点渠道——腾位收新;与 Used 消费分轨的一次性移除,快照不再含)
func discard_item(handle: int) -> bool:
for i in _items.size():
if int(_items[i].get("handle", -1)) == handle and not bool(_items[i].get("used", false)):
_items.remove_at(i)
return true
return false
# 板级零信息可用性预判(图标置灰/入口禁用判据——GDD「整张图都没东西可看」级):
# 真视恒可用(零雷窗口=信息非空);AP 类恒可用(组合判据取 OR——任一效果有信息即可用);
# 纯预读行 = 板上还有未开格才可用。(积点件不入使用面——反哺批⑤自动兑付无玩家入口)
@@ -52,10 +52,6 @@ func _init(ctx: CurioFlowContext) -> void:
# 停点在途判据(组合根 _on_battle_end_continue 分派面——战斗总结确认后开三选一)
func has_pending() -> bool:
return not _pending_curio.is_empty()
# 续玩停点恢复入口(true=已接手呈现,调用方中止后续分派)。空候选降级(2026-10-06 用户令):
# 候选 0 件不开选择面(空面无卡无钮=死面)——消化停点后按上下文直落后续推进(战斗总结面已
# 给过玩家确认拍,三选一环节整体跳过)
+15 -7
View File
@@ -42,6 +42,14 @@
# autoload 占净名),存档读盘挂 autoload _ready(时序与旧 main._ready 读盘等价)。
extends Control
# autoload 节点路径单点(迭代 27 T6 P2-2 降格实现:审计处方「编译期全局名」与本仓 --script
# 模式编译器不解析裸 autoload 标识符冲突〔run_all.gd 头注+pacing 实证〕——字符串拼写风险
# 收敛到四常量,类型面由读视图属性承载)
const AUTOLOAD_PATH_CFG := "/root/ConfigDB"
const AUTOLOAD_PATH_RNG := "/root/RngService"
const AUTOLOAD_PATH_SAVE := "/root/SaveManager"
const AUTOLOAD_PATH_META := "/root/MetaProgression"
const UiAssembler := preload("res://presentation/ui_assembler.gd")
const Narrator := preload("res://presentation/battle_narrator.gd")
const ModalStack := preload("res://presentation/modal_stack.gd")
@@ -62,10 +70,10 @@ var run_scope = null # 会话状态承载(run_scope.gd——迭
# 节点路径双模式通用;组合根调用点/探针挂载面 main.save_manager 语义零迁移)。
var save_manager: SaveManagerCore:
get:
return get_node("/root/SaveManager")
return get_node(AUTOLOAD_PATH_SAVE)
var meta: MetaProgressionCore:
get:
return get_node("/root/MetaProgression")
return get_node(AUTOLOAD_PATH_META)
var logger = null # 试玩日志器(JSONL 持久化 + 卡死诊断转储——呈现层组件)
var _debug_entry := false # 调试会话门控(迭代 18 卡⑦——DS_DEBUG=1:启动即开浮窗+两屏角标点亮;_ready 赋值)
var _seed_edit: LineEdit
@@ -175,7 +183,7 @@ func _begin_session(adventure_session: bool) -> void:
func _ready() -> void:
db = get_node("/root/ConfigDB")
db = get_node(AUTOLOAD_PATH_CFG)
if not db.is_ready:
var ok: bool = db.load_tables("res://data/tables", false)
if not ok:
@@ -187,8 +195,8 @@ func _ready() -> void:
settings_store = SettingsStore.new()
settings_store.load_settings()
settings_store.apply()
_debug = DebugSessionScript.new(db, get_node("/root/RngService"),
Callable(self, "_debug_hero_override"), Callable(self, "_seed_value"))
_debug = DebugSessionScript.new(db, get_node(AUTOLOAD_PATH_RNG),
_debug_hero_override, _seed_value)
_debug.battle_created.connect(_on_debug_battle_created)
_debug.battle_ready.connect(_on_debug_battle_ready)
_build_ui()
@@ -482,7 +490,7 @@ func _resume_from_menu() -> void:
# 冒险编排构建+接线(开新局/续玩共用——信号连接只在组合根,ADR-0001;断点注入同口)
func _wire_adventure() -> void:
run_scope.adventure = AdventureRunner.new(db, get_node("/root/RngService"))
run_scope.adventure = AdventureRunner.new(db, get_node(AUTOLOAD_PATH_RNG))
run_scope.adventure.step_row.connect(_on_step_row)
run_scope.adventure.round_started.connect(_on_round_started)
run_scope.adventure.board_changed.connect(_on_board_changed)
@@ -1343,7 +1351,7 @@ func _debug_run_data() -> Dictionary:
func _current_master_seed() -> int:
if run_scope.adventure != null:
return int(run_scope.adventure.master_seed)
return int(get_node("/root/RngService").master_seed)
return int(get_node(AUTOLOAD_PATH_RNG).master_seed)
func _close_debug_overlay() -> void:
@@ -167,10 +167,6 @@ func press_refresh() -> void:
_refresh_btn.pressed.emit()
func selected_id() -> String:
return _selected
# 兑付反馈态(curio_resolved 到达——结果行追加在头部之后;唯一出口 = 继续)
func show_result(text: String) -> void:
_label.text = _header + "\n" + text
+1 -6
View File
@@ -13,9 +13,4 @@ extends Node2D
var world_font: Font = null # 字体注入槽位(null = ThemeDB.fallback_font——BattleStageView.set_world_font 注入)
# C6 headless 可验判据(ADR-0003 Decision 4):注入字体非空 + 中文字形 has_char + 采样文本宽度 > 0。
# 实际光栅观感归批末目检清单。
func font_probe() -> Dictionary:
var f: Font = world_font if world_font != null else ThemeDB.fallback_font
var has_char: bool = f.has_char("人".unicode_at(0))
var width: float = f.get_string_size("兑现演出区", HORIZONTAL_ALIGNMENT_LEFT, -1, 14).x
return {"font": f, "has_char": has_char, "width": width}
# 实际光栅观感归批末目检清单。
+10 -6
View File
@@ -37,7 +37,10 @@ var _portrait_flip := false
# 主角三态切换(迭代 6 T3):state 名 → 纹理(battle_stage_view 组合点注册);缺态=保持现纹理
# (缺位回退链延伸——三态素材缺任一不炸不空)。敌站桩不注册不消费。
var _hero_tex := {}
var _hero_state := "idle"
enum HeroState { IDLE, RESET, ATTACK, HIT } # 主角三态+复位哨兵(迭代 27 T6——裸字符串 enum 化;外部 String 契约=register/set 形参零变化)
const _HS_NAME := {HeroState.IDLE: "idle", HeroState.RESET: "reset", HeroState.ATTACK: "attack", HeroState.HIT: "hit"}
const _HS_VALUE := {"idle": HeroState.IDLE, "reset": HeroState.RESET, "attack": HeroState.ATTACK, "hit": HeroState.HIT}
var _hero_state: int = HeroState.IDLE
var _state_revert_tw: Tween
var _order := 1
var _intent_glyph := ""
@@ -330,7 +333,7 @@ func _revive() -> void:
modulate = Color(1, 1, 1)
rotation_degrees = 0.0
if _kind == "hero":
_hero_state = "reset" # 强制回 idle 态(绕过同态早退)
_hero_state = HeroState.RESET # 强制回 idle 态(绕过同态早退)
set_hero_state("idle")
_refresh_portrait_glyph()
if slot_row != null:
@@ -368,11 +371,12 @@ func register_hero_state(state: String, tex: Texture2D) -> void:
# 状态切换:现纹理换注册态(attack/hit);未注册态名或缺位 = 保持现纹理不炸。
# 非 idle 态自动短驻回归 idle(0.35s——演出一拍即回,结算收尾另有显式 idle 兜底)。
func set_hero_state(state: String) -> void:
if _kind != "hero" or state == _hero_state:
var target: int = _HS_VALUE.get(state, -1)
if _kind != "hero" or target == _hero_state:
return
if not _hero_tex.has(state) and state != "idle":
return
_hero_state = state
_hero_state = target
if _portrait_sprite != null and _hero_tex.has(state):
_portrait_sprite.texture = _hero_tex[state]
if _state_revert_tw != null and _state_revert_tw.is_valid():
@@ -384,14 +388,14 @@ func set_hero_state(state: String) -> void:
func _settle_hero_idle() -> void:
_hero_state = "idle"
_hero_state = HeroState.IDLE
if _portrait_sprite != null and _hero_tex.has("idle"):
_portrait_sprite.texture = _hero_tex["idle"]
# 三态探针出口(fx_probe——当前态名+注册态集合)
func hero_state_probe() -> Dictionary:
return {"state": _hero_state, "registered": _hero_tex.keys()}
return {"state": _HS_NAME.get(_hero_state, "idle"), "registered": _hero_tex.keys()}
func _portrait_color() -> Color: