From b605022001773f83a73aac78a05bcf63a975011c Mon Sep 17 00:00:00 2001 From: DKChan <524912269@qq.com> Date: Thu, 15 Oct 2026 01:05:17 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E8=BF=AD=E4=BB=A327=20T5):=20=E6=B3=A2?= =?UTF-8?q?=E4=BA=94a=20=E7=B1=BB=E5=9E=8B=E9=97=B8=E6=8B=89=E8=B5=B7+?= =?UTF-8?q?=E5=AD=98=E9=87=8F=E6=94=B6=E5=8F=A3=E2=80=94=E2=80=94=E5=AE=A1?= =?UTF-8?q?=E8=AE=A1=E5=99=A8=200=20=E5=AD=98=E9=87=8F+warning=20=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E5=9C=A8=E4=BD=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit P1-6 类型闸(须 T1-T4 全落后——本波按执行顺序锁殿后): - tools/audit_type_hints.py 新机器闸(V7): R1 无类型容器注解/R2 有值返回缺返回类型/ R3 参数缺类型三规则+行级 # type-hint: ignore 显式白名单通道;产品码 core/data/presentation - 存量收口: 368 处显式 Array/Dictionary 非参数化注解→机械参数化(Array[Variant]/ Dictionary[Variant, Variant] 零行为面);13 处参数真类型(SaveManagerCore/ConfigDb/ BattleOrchestrator/MinesweeperRules/LevelRunner/SettingsStore 等) - 白名单豁免四处: board_generator generate/_attempt/legality/_finalize 的 db 参—— 测量面 _BoardRowStub 冒烟夹具鸭子型注入(typed 参拒 stub) - project.godot 拉起 gdscript/warnings/untyped_declaration=1(V7 源码断言在 smoke _smoke_type_gate) - static_scan 新 S9 类型闸规则;顺修历史 cd 错位(9-24 布局迁移后 /.. 落 game/ 致守卫 恒跳过静默空转——修复后 S1-S4/S9 首次实跑全绿)+S1/S2 增 ADR-0007-exempt 标记豁免通道 (main _seed_value 引擎熵源=文档化豁免面) 验收: 冒烟 1081/0(+V7×2)+run_all 27 套+static_scan 全绿+probe click+render golden+对拍三变体全同 --- game/core/action_economy.gd | 2 +- game/core/adventure_runner.gd | 86 ++++++++--------- game/core/battle_orchestrator.gd | 80 ++++++++-------- game/core/board_data.gd | 2 +- game/core/board_generator.gd | 22 ++--- game/core/config_db.gd | 2 +- game/core/config_db_cross_validator.gd | 4 +- game/core/damage_controller.gd | 42 ++++---- game/core/effect_engine.gd | 26 ++--- game/core/feats_engine.gd | 2 +- game/core/level_runner.gd | 40 ++++---- game/core/meta_progression.gd | 72 +++++++------- game/core/minesweeper_rules.gd | 24 ++--- game/core/reward_economy.gd | 28 +++--- game/core/run_ledger.gd | 8 +- game/core/save_manager.gd | 32 +++---- game/core/settlement/combat_segment.gd | 2 +- game/core/settlement/grant_segment.gd | 14 +-- game/core/status_container.gd | 4 +- game/core/treasure_items.gd | 20 ++-- game/core/unit_container.gd | 14 +-- game/data/schemas/enemy_table.gd | 4 +- game/data/schemas/feat_table.gd | 4 +- game/data/schemas/forge_table.gd | 2 +- game/data/schemas/level_table.gd | 8 +- game/data/schemas/meta_tree_table.gd | 8 +- game/data/schemas/settlement_table.gd | 2 +- game/data/schemas/shop_table.gd | 2 +- game/data/schemas/treasure_item_table.gd | 16 ++-- game/presentation/battle_narrator.gd | 8 +- .../presentation/curio_interact_controller.gd | 14 +-- game/presentation/debug_session.gd | 2 +- game/presentation/hud_presenter.gd | 12 +-- game/presentation/main.gd | 42 ++++---- game/presentation/modal_stack.gd | 2 +- game/presentation/playtest_logger.gd | 6 +- game/presentation/row_pacer.gd | 4 +- game/presentation/run_scope.gd | 2 +- game/presentation/scenes/adventure_screen.gd | 2 +- game/presentation/scenes/menu_screen.gd | 2 +- .../treasure_interact_controller.gd | 16 ++-- game/presentation/views/battle_log_view.gd | 4 +- game/presentation/views/board_view.gd | 10 +- game/presentation/views/camp_overlay_view.gd | 18 ++-- game/presentation/views/career_overlay.gd | 2 +- game/presentation/views/curio_pick_overlay.gd | 8 +- game/presentation/views/debug_overlay.gd | 2 +- .../views/hero_info_panel_view.gd | 6 +- game/presentation/views/level_map_screen.gd | 14 +-- game/presentation/views/node_overlay.gd | 2 +- game/presentation/views/settings_overlay.gd | 4 +- game/presentation/views/shop_overlay_view.gd | 4 +- game/presentation/views/status_panel.gd | 4 +- game/presentation/views/status_slot_row.gd | 2 +- game/project.godot | 4 + game/smoke/smoke_all.gd | 12 +++ game/tests/static_scan.sh | 12 ++- tools/audit_type_hints.py | 95 +++++++++++++++++++ 58 files changed, 503 insertions(+), 384 deletions(-) create mode 100644 tools/audit_type_hints.py diff --git a/game/core/action_economy.gd b/game/core/action_economy.gd index 28bea1dd..a9ca8e0f 100644 --- a/game/core/action_economy.gd +++ b/game/core/action_economy.gd @@ -30,7 +30,7 @@ var _rap := 0 var rap: int: get: return _rap var _last_round_end: Dictionary[String, String] = {} # {path_tag, continuation_hint}——排雷玩法 组包 round_end 的合法数据源 -var last_round_end: Dictionary: +var last_round_end: Dictionary[Variant, Variant]: get: return _last_round_end var _settlement_consumed := false # AC-14 门:notify_settlement_done 消费后才受理 round_start var _next_round_mode := "" # 调试观测量(同板续轮 / 换新图) diff --git a/game/core/adventure_runner.gd b/game/core/adventure_runner.gd index 1d084e7b..67babfb9 100644 --- a/game/core/adventure_runner.gd +++ b/game/core/adventure_runner.gd @@ -75,7 +75,7 @@ var _base_override := {"atk": 0, "max_hp": 0, "armor_class": 0} var _first_script := false # 首次脚本模式(关卡节点图生成 v0.4——true = 全关零抽签取池内模板 0;测量夹具恒 false) # 永久强化 flat 注入(元进度实现批——冒险开始参数面 params.hero_override 存入,共享给 LevelRunner;全零 = 白板) var _last_treasure_cleared := 0 # additive(迭代 7 T4——最近一次关卡末清空计数,summary 取数面) var _treasure_unlocked := {} # aux 解锁门控集(迭代 7 T5——params.treasure_unlocked 接收面) -var _attunement: Array = [] # 同调装配集(迭代 11 第①批 T5——params.attunement 接收面,start_level 外下传 LevelRunner) +var _attunement: Array[Variant] = [] # 同调装配集(迭代 11 第①批 T5——params.attunement 接收面,start_level 外下传 LevelRunner) var _attune_refresh_x := 0 # 刷新件余量(迭代 11 第①批 T5b P1 甲——每碰三选一重置=X 表值;优先消耗、不结转) var _attune_refresh_y := 0 # Y 冒险周期池(开局参数面注入,用完即无——营地升级行表值) var _curio_paid_refreshes := 0 # 本次停点已付费刷新次数(2026-10-06 用户令对齐商店 MC-1——GP 刷新价曲线输入,随停点创建清零=与商店进店清零同构) @@ -89,12 +89,12 @@ var _save_manager = null # 断点承载注入(迭代 8 卡① var _resume_bp := {} # 续玩快照暂存(resume_adventure → _start_current_level 单跳传递) # ---- 三选一停点态(迭代 10 奖励重构批)---- var _curio_taken := {} # 件获取次数计数(kind 维度派生账本——断点附随件与摘要读数承载;池库存正主=_curio_deck) -var _curio_deck: Array = [] # 令牌池(TP0 v2.2——持久多重集,元素 {"kind","tier"};洗牌抽候选/选中踢池未选回池/池耗即枯竭;商店货架同库共账;迭代 12 T1 起规则改写型特性带=relic 令牌随表追加) +var _curio_deck: Array[Variant] = [] # 令牌池(TP0 v2.2——持久多重集,元素 {"kind","tier"};洗牌抽候选/选中踢池未选回池/池耗即枯竭;商店货架同库共账;迭代 12 T1 起规则改写型特性带=relic 令牌随表追加) var _feats: Array[String] = [] # 特性持有集(迭代 12 T1——局内随机渠道获得、冒险级跨关携带、收束清零;🔴 层级纪律承载面:不入同调位不入局外树) -var _tree_rules: Array = [] # 树 rule 键集(迭代 12 T10——Fork 激活侧 effects 透传;下传 enc.tree_rules 消费面) +var _tree_rules: Array[Variant] = [] # 树 rule 键集(迭代 12 T10——Fork 激活侧 effects 透传;下传 enc.tree_rules 消费面) var _curio_pending := {} # 停点态:{"candidates": Array, "payload": Dictionary, "chosen"/"incoming": 替换在途}——空 = 无停点 var _curio_result := {} # 已兑付待推进态(curio_resolved 载荷副本——中断续玩不重兑付) -var _keystone_drops: Array = [] # Boss 逐杀掉落明细(additive 摘要键 keystone_drops——结算明细/复盘归因数据源) +var _keystone_drops: Array[Variant] = [] # Boss 逐杀掉落明细(additive 摘要键 keystone_drops——结算明细/复盘归因数据源) var _node_curio_pending := {} # 奖励节点三选一停点态(迭代 10 追加批——共享池;断点不载:续玩重建到选路停点重走,恢复的计数器保证同候选重抽) var _feat_ticket_pity := 0 # 门票保底计数(T5——展示次数:节点开面/货架抽签/刷新重展各计一次;触发清零) var _feat_ticket_sold := 0 # 门票限购计数(T5——选中/买下即 +1;拿到即开不进持有集) @@ -141,12 +141,12 @@ func start_adventure(seed_val: int, params: Dictionary = {}) -> bool: # 刷新练习开局注入(迭代 22 秘宝批 T3——refresh_stock_grant 装配即得一次性加仓: # 装配集 kind 分发扫描;只入存量账 _refresh_stock、临时账不动=V8 契约;resume 不重注入〔存量随断点已在〕) for iid_v2 in _attunement: - var rp_row: Dictionary = db.treasure_item_row(String(iid_v2)) + var rp_row: Dictionary[Variant, Variant] = db.treasure_item_row(String(iid_v2)) if (rp_row.get("effect_kinds", []) as Array).has("refresh_stock_grant"): _refresh_stock += int(rp_row.get("magnitude", 0)) # 特性开局注入(迭代 12 T7——MC 成型画像载体:params.relics_seed 非空=开局持有集〔七流派 # 各 2 件档一成型——测量面专用;真实局规则改写型特性只走局内随机渠道,红线②层级纪律不破〕) - var feat_seed: Array = params.get("relics_seed", []) if params.get("relics_seed", []) is Array else [] + var feat_seed: Array[Variant] = params.get("relics_seed", []) if params.get("relics_seed", []) is Array else [] _tree_rules = params.get("tree_rules", []) if params.get("tree_rules", []) is Array else [] # 迭代 12 T10——A/B 面 rule 键集 if not feat_seed.is_empty(): _feats = [] @@ -179,7 +179,7 @@ func resume_adventure(bp: Dictionary, params: Dictionary = {}) -> bool: master_seed = int(bp.get("master_seed", 0)) rng.restore_adventure(master_seed, int(bp.get("rng_counter", 0))) ledger = {"hero_hp": int(bp.get("hero_hp", 0)), "gp": int(bp.get("gp", 0))} - var bp_bonus: Dictionary = bp.get("bonus", {}) as Dictionary + var bp_bonus: Dictionary[Variant, Variant] = bp.get("bonus", {}) as Dictionary run_ledger.bonus = {"atk": maxi(0, int(bp_bonus.get("atk", 0))), "max_hp": maxi(0, int(bp_bonus.get("max_hp", 0))), "armor_class": maxi(0, int(bp_bonus.get("armor_class", 0))), @@ -191,7 +191,7 @@ func resume_adventure(bp: Dictionary, params: Dictionary = {}) -> bool: level_index = int(bp.get("level_index", 1)) _pending_next_level = false _reset_curio_state() - var bp_taken: Dictionary = bp.get("bonus_taken", {}) as Dictionary + var bp_taken: Dictionary[Variant, Variant] = bp.get("bonus_taken", {}) as Dictionary for id in _curio_taken.keys(): _curio_taken[id] = maxi(0, int(bp_taken.get(id, 0))) for id in bp_taken.keys(): @@ -200,9 +200,9 @@ func resume_adventure(bp: Dictionary, params: Dictionary = {}) -> bool: # 迭代 12 T1——relic 令牌 feat_id 随还原) var bp_deck = bp.get("bonus_deck", null) if bp_deck is Array: - var restored: Array = [] + var restored: Array[Variant] = [] for t in (bp_deck as Array): - var tok: Dictionary = t as Dictionary + var tok: Dictionary[Variant, Variant] = t as Dictionary var rtok := {"kind": String(tok.get("kind", "")), "tier": String(tok.get("tier", ""))} if String(rtok["kind"]) == "relic": rtok["feat_id"] = String(tok.get("feat_id", "")) @@ -231,7 +231,7 @@ func resume_adventure(bp: Dictionary, params: Dictionary = {}) -> bool: _seed_attune_charges() # 迭代 11 第①批 T6——冒险开始充能账本按装备主动件重置(每冒险充能语义) _tree_rules = params.get("tree_rules", []) if params.get("tree_rules", []) is Array else [] # 迭代 12 T10——A/B 面 rule 键集(尾裁微批b 2026-10-08 卡⓪受理闭环:续玩参数面同 start_adventure :135 同式,消费面 set_tree_rules 下传现成零改) # 三选一停点续玩分支(迭代 10——选择态/已兑付态不重跑关卡;奖励选择态不重抽、已兑付不重付) - var bp_pending: Dictionary = bp.get("bonus_pending", {}) as Dictionary + var bp_pending: Dictionary[Variant, Variant] = bp.get("bonus_pending", {}) as Dictionary _feat_ticket_pity = int(bp.get("feat_ticket_pity", 0)) # T5 扩列——旧档缺键缺省 0(存档兼容) _feat_ticket_sold = int(bp.get("feat_ticket_sold", 0)) # 迭代 20 T6/T7——adventure 域计数器回填(缺键 = 旧版断点防御空集;_reset_curio_state 已清零在先) @@ -241,11 +241,11 @@ func resume_adventure(bp: Dictionary, params: Dictionary = {}) -> bool: _refresh_stock = maxi(0, int(bp.get("refresh_stock", 0))) # 迭代 20 T4/T7——旧档缺键缺省 0 run_ledger.vision_counter = {"used": maxi(0, int(bp.get("vision_counter_used", 0)))} # 迭代 22 秘宝批 T3——真视计数随断点回填(缺键=旧断点缺省 0) _battery_pending = maxi(0, int(bp.get("battery_pending", 0))) - var bp_result: Dictionary = bp.get("bonus_result", {}) as Dictionary + var bp_result: Dictionary[Variant, Variant] = bp.get("bonus_result", {}) as Dictionary if not bp_pending.is_empty(): _pending_next_level = true # 候选令牌化迁移(TP0——旧版断点候选为 kind 字符串,按普通档令牌防御归一;单版本周期口径) - var migrated: Array = [] + var migrated: Array[Variant] = [] for c in (bp_pending.get("candidates", []) as Array): if c is Dictionary: # T4——feat_id 随迁移(通关渠道候选=纯专长令牌,身份键 relic|tier|feat_id 三段全要) @@ -256,7 +256,7 @@ func resume_adventure(bp: Dictionary, params: Dictionary = {}) -> bool: else: migrated.append({"kind": String(c), "tier": db.adventure.TIER_COMMON}) _curio_pending = {"candidates": migrated} - var payload: Dictionary = (bp_pending.get("payload", {}) as Dictionary).duplicate(true) + var payload: Dictionary[Variant, Variant] = (bp_pending.get("payload", {}) as Dictionary).duplicate(true) state = "running" curio_choice_presented.emit(payload) return true @@ -299,7 +299,7 @@ func _reset_curio_state() -> void: # 回退映射 rarest→rare/其余→excellent,T3 行换代后回退分支自然休眠);relic 入普通档随新档位框架 # 解禁〔旧「永不入普通档」系 TP0 三档时代约束,四级框架未沿用——奇物.md 池结构节无此限制〕) func _build_curio_deck() -> Array: - var deck: Array = [] + var deck: Array[Variant] = [] for kind in db.adventure.CURIO_KINDS: for tier in db.adventure.TIERS: for i in int(db.adventure.curio_quota(String(kind), String(tier))): @@ -335,7 +335,7 @@ func effective_max_hp() -> int: # 断点承载注入(迭代 8 卡①——组合根冒险开始时调用;模拟/冒烟不注入 = 断点面整体静默) -func set_save_manager(sm) -> void: +func set_save_manager(sm: SaveManagerCore) -> void: _save_manager = sm @@ -382,7 +382,7 @@ func skip_empty_curio() -> bool: # 候选令牌匹配(身份键恒等——属性加成型特性 kind+tier、规则改写型特性 feat_id;迭代 12 T1 规则改写型特性带) func _find_token(candidates: Array, kind: String, tier: String, feat_id: String = "") -> Dictionary: for t in candidates: - var tok: Dictionary = t as Dictionary + var tok: Dictionary[Variant, Variant] = t as Dictionary if String(tok.get("kind", "")) != kind or String(tok.get("tier", "")) != tier: continue if kind == "relic" and String(tok.get("feat_id", "")) != feat_id: @@ -399,7 +399,7 @@ func pick_curio(kind: String, tier: String = "", feat_id: String = "") -> bool: if state != "running" or _curio_pending.is_empty(): push_error("[冒险结构] pick_curio 拒绝——无待选停点(state=%s)" % state) return false - var candidates: Array = _curio_pending.get("candidates", []) as Array + var candidates: Array[Variant] = _curio_pending.get("candidates", []) as Array if _find_token(candidates, kind, tier, feat_id).is_empty(): push_error("[冒险结构] pick_curio 拒绝——令牌不在候选: %s|%s|%s" % [kind, tier, feat_id]) return false @@ -418,7 +418,7 @@ func pick_curio(kind: String, tier: String = "", feat_id: String = "") -> bool: func _feat_payload_keys(feat_id: String) -> Dictionary: if feat_id == "": return {} - var row: Dictionary = db.feat_row(feat_id) + var row: Dictionary[Variant, Variant] = db.feat_row(feat_id) return {"feat_id": feat_id, "feat_name": String(row.get("display_name", "")), "feat_archetype": String(row.get("archetype", ""))} @@ -484,7 +484,7 @@ func _apply_curio_pick(kind: String, tier: String, value: int, feat_id: String = # 从牌库摘除一枚令牌(TP0——选中踢池单点;迭代 12 T1 relic 按 feat_id 精确摘;找不到 = false) func _consume_deck_token(kind: String, tier: String, feat_id: String = "") -> bool: for i in _curio_deck.size(): - var tok: Dictionary = _curio_deck[i] as Dictionary + var tok: Dictionary[Variant, Variant] = _curio_deck[i] as Dictionary if String(tok.get("kind", "")) != kind or String(tok.get("tier", "")) != tier: continue if kind == "relic" and String(tok.get("feat_id", "")) != feat_id: @@ -556,7 +556,7 @@ func _finish_curio_pick(picked: String, value: int, hp_before: int, tier: String # 候选呈现(节点到达时调用——恰一 reward_pick scope 洗牌 + 道具件掷定 scope;本停点断点不载, # 续玩重建到选路停点后重走重抽,恢复的全局计数器保证同候选逐位复现)。 func node_curio_candidates() -> Dictionary: - var cands: Array = _draw_curio_candidates("reward_node") + var cands: Array[Variant] = _draw_curio_candidates("reward_node") return {"candidates": cands} @@ -573,7 +573,7 @@ func apply_node_curio(kind: String, tier: String = "", feat_id: String = "") -> if _node_curio_pending.is_empty(): push_error("[冒险结构] apply_node_curio 拒绝——无待选停点") return false - var candidates: Array = _node_curio_pending.get("candidates", []) as Array + var candidates: Array[Variant] = _node_curio_pending.get("candidates", []) as Array if _find_token(candidates, kind, tier, feat_id).is_empty(): push_error("[冒险结构] apply_node_curio 拒绝——令牌不在候选: %s|%s|%s" % [kind, tier, feat_id]) return false @@ -589,7 +589,7 @@ func apply_node_curio(kind: String, tier: String = "", feat_id: String = "") -> func _seed_attune_charges() -> void: run_ledger.attune_charges = {} for iid in _attunement: - var arow: Dictionary = db.treasure_item_row(String(iid)) + var arow: Dictionary[Variant, Variant] = db.treasure_item_row(String(iid)) if String(arow.get("form", "")) == "active_charged": run_ledger.attune_charges[String(iid)] = maxi(0, int(arow.get("charges_per_adventure", 0))) @@ -603,7 +603,7 @@ func _attune_refresh_x_total() -> int: # 的行 magnitude 合计;刷新件类加件零 id 分支) var total := 0 for iid_v in _attunement: - var arow: Dictionary = db.treasure_item_row(String(iid_v)) + var arow: Dictionary[Variant, Variant] = db.treasure_item_row(String(iid_v)) if (arow.get("effect_kinds", []) as Array).has("bonus_refresh"): total += int(arow.get("magnitude", 0)) return total @@ -632,7 +632,7 @@ func choose_battery_target(item_id: String) -> bool: if not _attunement.has(item_id): push_error("[冒险结构] 电池分配拒绝——件不在装配集: %s" % item_id) return false - var arow: Dictionary = db.treasure_item_row(item_id) + var arow: Dictionary[Variant, Variant] = db.treasure_item_row(item_id) if String(arow.get("form", "")) != "active_charged": push_error("[冒险结构] 电池分配拒绝——非主动充能件: %s" % item_id) return false @@ -693,16 +693,16 @@ func _refresh_curio_common(paid: bool) -> bool: else: _refresh_stock -= 1 if not _curio_pending.is_empty(): - var cands: Array = _draw_curio_candidates("level_clear") + var cands: Array[Variant] = _draw_curio_candidates("level_clear") _curio_pending["candidates"] = cands.duplicate(true) - var payload: Dictionary = (_curio_pending.get("payload", {}) as Dictionary).duplicate(true) + var payload: Dictionary[Variant, Variant] = (_curio_pending.get("payload", {}) as Dictionary).duplicate(true) payload["candidates"] = cands payload["refreshed"] = true # 重发载荷带标——呈现层直落选择面,不再重开胜利总结面(2026-10-06) curio_choice_presented.emit(payload) # 同信号重 present(零自动推进契约不破——停点仍在途) _snapshot_breakpoint() return true if not _node_curio_pending.is_empty(): - var cands2: Array = _draw_curio_candidates("reward_node") + var cands2: Array[Variant] = _draw_curio_candidates("reward_node") _node_curio_pending["candidates"] = cands2.duplicate(true) return true return false @@ -743,14 +743,14 @@ func _draw_curio_candidates(channel: String = "reward_node") -> Array: var k := String((tok as Dictionary).get("kind", "")) return k != "relic" and k != "feat_ticket") pool = pool.filter(func(tok): - var t: Dictionary = tok as Dictionary + var t: Dictionary[Variant, Variant] = tok as Dictionary return _curio_cap_ok(String(t.get("kind", "")), String(t.get("feat_id", "")))) var rg: RandomNumberGenerator = rng.open_stream("reward_pick") if channel == "level_clear": # 迭代 20 T5——Boss 渠道按档配比加权抽取(无放回加权抽:逐席按 boss_feat_weight 掷点, # 身份去重照旧;rarity 语义约束兑付面——奇物.md v3.1,配比数值归调参批) - var out_w: Array = [] - var seen_w: Array = [] + var out_w: Array[Variant] = [] + var seen_w: Array[Variant] = [] var want_w := int(db.adventure.curio_candidate_count) while out_w.size() < want_w and not pool.is_empty(): var total_w := 0 @@ -765,7 +765,7 @@ func _draw_curio_candidates(channel: String = "reward_node") -> Array: if roll_w <= 0: pick_i = i_w break - var tok_p: Dictionary = pool[pick_i] + var tok_p: Dictionary[Variant, Variant] = pool[pick_i] var ident_p := _token_identity(tok_p) if seen_w.has(ident_p): pool.remove_at(pick_i) # 同身份已出——剔牌重掷(无放回) @@ -788,13 +788,13 @@ func _draw_curio_candidates(channel: String = "reward_node") -> Array: var tmp2 = sub[i] sub[i] = sub[j2] sub[j2] = tmp2 - var out: Array = [] - var seen: Array = [] + var out: Array[Variant] = [] + var seen: Array[Variant] = [] # 收集器:全池序取 n_full 格(普通档打底)→ 子池序取 1 格(曝光槽)→ 子池空回退全池续取 var want := int(db.adventure.curio_candidate_count) var n_full := maxi(1, want - 1) if want >= 2 else want while out.size() < want: - var src: Array = pool + var src: Array[Variant] = pool if out.size() >= n_full and not sub.is_empty(): src = sub # 曝光槽位——优先子池 var placed := false @@ -825,7 +825,7 @@ func _draw_curio_candidates(channel: String = "reward_node") -> Array: func _curio_cap_ok(kind: String, feat_id: String = "") -> bool: var cap := 99 if kind == "relic": - var row: Dictionary = db.feat_row(feat_id) + var row: Dictionary[Variant, Variant] = db.feat_row(feat_id) cap = int(row.get("cap", 1)) if not row.is_empty() else 1 return _curio_taken.get("relic:" + feat_id, 0) < cap if not db.adventure.CURIO_KINDS.has(kind): @@ -857,7 +857,7 @@ func _feat_ticket_consider(rg: RandomNumberGenerator) -> bool: func _redeem_feat_ticket() -> bool: _feat_ticket_sold += 1 _feat_ticket_pity = 0 - var cands: Array = _draw_curio_candidates("level_clear") # 专长池(与通关渠道同白名单) + var cands: Array[Variant] = _draw_curio_candidates("level_clear") # 专长池(与通关渠道同白名单) if cands.is_empty(): push_error("[冒险结构] 门票兑付失败——专长池空集") _feat_ticket_sold -= 1 @@ -865,7 +865,7 @@ func _redeem_feat_ticket() -> bool: if not _node_curio_pending.is_empty(): _node_curio_pending["candidates"] = cands.duplicate(true) return true - var payload: Dictionary = {"context": "feat_ticket", "candidates": cands.duplicate(true)} + var payload: Dictionary[Variant, Variant] = {"context": "feat_ticket", "candidates": cands.duplicate(true)} _curio_pending = {"candidates": cands.duplicate(true), "payload": payload.duplicate(true)} curio_choice_presented.emit(payload) _snapshot_breakpoint() @@ -879,7 +879,7 @@ func _boss_keystone_drop(li: int) -> void: var first := true if _save_manager != null: first = not _save_manager.boss_first_kill_marked(li) - var row: Dictionary = db.meta_boss_keystone_row(li) + var row: Dictionary[Variant, Variant] = db.meta_boss_keystone_row(li) var amount: int = int(row.get("first_kill", 0)) if first else int(row.get("repeat_kill", 0)) _keystone_drops.append({"level": li, "first": first, "amount": amount}) keystone += amount @@ -922,7 +922,7 @@ func _start_current_level() -> bool: _runner.set_hero_override(int(_override[0]), int(_override[1])) # 断点续玩分支(迭代 8 卡①):模板随快照强制(已抽模板零重抽零 RNG)、resume_state 带 # 停点节点与已到序——重建到停点后经 present_resumed_choice 呈现(信号顺序契约同入口)。 - var resume_state: Dictionary = {} + var resume_state: Dictionary[Variant, Variant] = {} var template_pick: int = 0 if _first_script else -1 # 首次脚本 = 每关固定模板 0 零抽签(v0.4 拍板点①);否则逐关抽签 if not _resume_bp.is_empty(): resume_state = {"node_id": int(_resume_bp.get("node_id", -1)), @@ -993,7 +993,7 @@ func _on_level_finished(payload: Dictionary) -> void: _pending_next_level = true _reset_refresh_x() # 迭代 11 第①批 T5b——碰三选一重置 X 余量(不结转) _curio_paid_refreshes = 0 # 付费刷计数随停点清零(对齐商店进店清零——2026-10-06) - var pick_payload: Dictionary = { + var pick_payload: Dictionary[Variant, Variant] = { "level_index": level_index, "candidates": _draw_curio_candidates("level_clear"), # T4 渠道白名单——关底 Boss 击败=通关渠道只出专长 "heal_gained": heal, @@ -1067,12 +1067,12 @@ func _forward_choices_presented(candidates: Array) -> void: func _snapshot_breakpoint() -> void: if _save_manager == null or state != "running": return - var treasure_snapshot: Array = [] + var treasure_snapshot: Array[Variant] = [] if _runner != null and _runner.treasure_inventory != null: treasure_snapshot = _runner.treasure_inventory.snapshot_inventory() # 三选一停点续玩分支 _runner 为 null(关卡已收束、下一关未开)——节点域字段按缺省落档, # 该两态的续玩只消费 bonus_* 附随件(选择停点/已兑付停点快照,迭代 10) - var bp: Dictionary = { + var bp: Dictionary[Variant, Variant] = { "level_index": level_index, "node_id": _runner.current_node_id() if _runner != null else -1, "hero_hp": int(ledger.get("hero_hp", 0)), diff --git a/game/core/battle_orchestrator.gd b/game/core/battle_orchestrator.gd index 27e76139..9ec68990 100644 --- a/game/core/battle_orchestrator.gd +++ b/game/core/battle_orchestrator.gd @@ -83,7 +83,7 @@ var _board_seq := 0 # 板序号(本战斗第几张板 var _board_from_midround_refresh := false # 装板 provenance:轮内刷新换来的图 = true(清图奖励守卫——刷新图不发清图奖励) var _flag_consumed := {} # 兑付资格以雷格实例为键(板级;换图重置) # 同调装配运行态(迭代 11 第①批 T5——begin_battle 经 enc.attunement 注入;off=空集全部效果零扰动) -var attunement: Array = [] # 装备中同调件 item_id 集 +var attunement: Array[Variant] = [] # 装备中同调件 item_id 集 var _ledger: RunLedger = null # 共享四本账(迭代 27 T3——begin_battle 经 enc["ledger"] 持引用;缺省新账=调试面) var _attune_safe_seen := 0 # 积点制件 battle 域安全开格计数(迭代 22 T3 通用化——装配集 ap_charge_safe 行扫描;反哺批⑤ 2026-10-12 用户裁:满 charge_n 自动兑付当轮 AP+1,无玩家发起入口) # ---- 效果引擎扩签运行态(迭代 20 专长实装批 T1——新效果类型施加面;空订阅零扰动)---- @@ -158,7 +158,7 @@ func _flush_pending_reveals(ri: int) -> void: var board: BoardData = minesweeper.current_board() while not _fx_pending_reveals.is_empty(): _fx_pending_reveals.pop_back() - var cands: Array = [] + var cands: Array[Variant] = [] for c in board.all_cells(): if not minesweeper.is_revealed(c) and not board.is_mine(c): cands.append(c) @@ -201,7 +201,7 @@ func _fx_fact(fact: String): hits_n += 1 return hits_n "hp_ratio": - var hu: Dictionary = _enemy_system.hero_unit() + var hu: Dictionary[Variant, Variant] = _enemy_system.hero_unit() return int(round(100.0 * float(int(hu.get("hp", 0))) / float(maxi(1, int(hu.get("max_hp", 1)))))) # 百分比 int(0-100——when lte 算子 int 截断比较口径) return null @@ -228,7 +228,7 @@ func _attune_row(item_id: String) -> Dictionary: func attune_param_sum(kind: String) -> int: var total := 0 for iid_v in attunement: - var arow: Dictionary = db.treasure_item_row(String(iid_v)) + var arow: Dictionary[Variant, Variant] = db.treasure_item_row(String(iid_v)) if (arow.get("effect_kinds", []) as Array).has(kind): total += int(arow.get("magnitude", 0)) return total @@ -238,7 +238,7 @@ func attune_param_sum(kind: String) -> int: # 首行语义=装配集内至多一件积点制〔新目录恰试炼镐〕) func _attune_charge_safe_row() -> Dictionary: for iid_v in attunement: - var arow: Dictionary = db.treasure_item_row(String(iid_v)) + var arow: Dictionary[Variant, Variant] = db.treasure_item_row(String(iid_v)) if (arow.get("effect_kinds", []) as Array).has("ap_charge_safe"): return arow return {} @@ -250,7 +250,7 @@ func _attune_charge_safe_row() -> Dictionary: func _reseed_attune_charges(reset: String) -> void: for iid_v in attunement: var iid := String(iid_v) - var arow: Dictionary = db.treasure_item_row(iid) + var arow: Dictionary[Variant, Variant] = db.treasure_item_row(iid) if String(arow.get("form", "")) != "active_charged": continue if String(arow.get("charge_reset", "adventure")) != reset: @@ -342,12 +342,12 @@ func begin_battle(enc: Dictionary) -> bool: # 开流条件、消费序、消费次数逐位一致——零内容期随机流零变化;区间宽度为零的行不掷 = 「定值不骰」 # 既有口径;atk 区间逐行同构 = 行值语义完整,代拍板随批留痕:属「每行一骰」预批准语义内的 # 行字段区间骰逐行化,非新增随机源形态)。 - var erowset_pre: Array = db.resolve_enemy_rowset(int(enc.level_index), int(enc.node_depth)) + var erowset_pre: Array[Variant] = db.resolve_enemy_rowset(int(enc.level_index), int(enc.node_depth)) if erowset_pre.is_empty(): _fatal("敌人表 行集解析为空(%d,%d)" % [int(enc.level_index), int(enc.node_depth)]) return false - var count_overrides: Array = [] - var atk_overrides: Array = [] + var count_overrides: Array[Variant] = [] + var atk_overrides: Array[Variant] = [] var needs_count := false var needs_atk := false for r in erowset_pre: @@ -397,7 +397,7 @@ func begin_battle(enc: Dictionary) -> bool: _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") - var feat_ids: Array = enc.get("feats", []) as Array + var feat_ids: Array[Variant] = enc.get("feats", []) as Array # 迭代 20 T3 换代(V14 旧实现摘除)——流派档位质变随重设计退役(奇物.md v1「不设流派标签、 # 不设档位机制」):FeatsEngine 求值与 rider_tier 注入拆除,散件单体效果=唯一形态 _damage.rider_tier_bonus = 0 @@ -406,7 +406,7 @@ func begin_battle(enc: Dictionary) -> bool: # 树 rule 键消费(迭代 12 T10 → 内容数据驱动扩展批 T3 换代:Fork 激活侧效果=效果引擎合成 # 订阅(rebuild 注入——round_start 首轮 AP+1 / damage_taken 减伤+1),旧直调拆除。其余 rule 键 # =stat 面投影不入战斗(T8 表内容 provisional 口径披露)) - var tree_rules: Array = enc.get("tree_rules", []) as Array + var tree_rules: Array[Variant] = enc.get("tree_rules", []) as Array # 效果引擎订阅重建(内容数据驱动扩展批 T1——每战从持有集/装备集重建,中途获得下一战生效; # 树规则合成订阅同面装载。装载序=feats 表序→treasure 表序→树规则,写死保种子复放。 # 迭代 20 T6——adventure 域计数器从共享引用回灌〔跨战持久〕) @@ -466,7 +466,7 @@ func _install_new_board(during_round := false) -> bool: # boss/elite 图命中全局型 → 写 board_variants(_place_variants 见非空即跳 region=替换制); # 全局池空 → 板级字段留空(_place_variants 原路走 region=兜底回落合法态)。 _resolve_global_variant(board) - var res: Dictionary = minesweeper.install_board(board, during_round) + var res: Dictionary[Variant, Variant] = minesweeper.install_board(board, during_round) if not res.accepted: _fatal("排雷玩法 install 拒绝(不变式违例): %s" % str(res)) return false @@ -507,7 +507,7 @@ func _install_new_board(during_round := false) -> bool: func _resolve_global_variant(board: BoardData) -> void: board.board_variants.clear() _reward_economy.board_skew = 0 - var row: Dictionary = db.global_variant_row(int(encounter.level_index), String(board.board_type), + var row: Dictionary[Variant, Variant] = db.global_variant_row(int(encounter.level_index), String(board.board_type), String(encounter.get("node_type", ""))) if row.is_empty(): return @@ -529,7 +529,7 @@ func _resolve_global_variant(board: BoardData) -> void: func _place_variants(board: BoardData) -> void: if not board.board_variants.is_empty(): return - var pool: Array = db.variant_pool(int(encounter.level_index), String(board.board_type)) + var pool: Array[Variant] = db.variant_pool(int(encounter.level_index), String(board.board_type)) if pool.is_empty(): return var r0 := {} @@ -542,7 +542,7 @@ func _place_variants(board: BoardData) -> void: if candidates.is_empty(): return var stream: RandomNumberGenerator = rng.open_stream("variant_place") # 一板恰一 scope(换图/刷新图各取号一次) - var vrow: Dictionary = pool[stream.randi_range(0, pool.size() - 1)] + var vrow: Dictionary[Variant, Variant] = pool[stream.randi_range(0, pool.size() - 1)] var total: int = int(round(float(candidates.size()) * float(db.variation.percent_of_cells) / 100.0)) \ + (int(encounter.level_index) - 1) * int(db.variation.per_level_bonus) total = mini(total, candidates.size()) @@ -560,7 +560,7 @@ func _place_variants(board: BoardData) -> void: break var seed_c: Vector2i = free[stream.randi_range(0, free.size() - 1)] placed[seed_c] = true - var pack: Array = [seed_c] + var pack: Array[Variant] = [seed_c] var pack_set := {seed_c: true} while pack.size() < pack_size: var fringe: Array[Vector2i] = [] @@ -602,7 +602,7 @@ func _on_cell_revealed(evt: Dictionary) -> void: # 先兑再扣,轮次不因本格结束;授予段已无积点段(本段唯一计数点)。fx_reveal 旁路不计数= # 沿现役缓冲路径口径〔天眼开格不计积点〕。grant_ap 相位防御在 ACTIVE 挖格链内恒过) if String(evt.get("kind", "")) == "safe": - var cs_row: Dictionary = _attune_charge_safe_row() + var cs_row: Dictionary[Variant, Variant] = _attune_charge_safe_row() if not cs_row.is_empty(): _attune_safe_seen += 1 var cn_b: int = int(cs_row.get("charge_n", 999)) @@ -664,7 +664,7 @@ func _feat_yield_adjust(snap: Dictionary, positive: bool, pos_m: int, neg_m: int return snap if not positive and neg_m <= 0: return snap - var out: Dictionary = snap.duplicate() + var out: Dictionary[Variant, Variant] = snap.duplicate() var power := int(out.get("power", int(_reward_economy.polarity_row(String(out.get("cell_type", "")), "positive" if positive else "negative").power))) var pct := pos_m if positive else neg_m if positive and combo_cap > 0: @@ -676,10 +676,10 @@ func _feat_yield_adjust(snap: Dictionary, positive: bool, pos_m: int, neg_m: int # 伤害型变异过滤(terrain_kind fact 取数面——variation 表 effect=flat_damage 判据; # 🔴 R4 甲永不对冲边界=仅限伤害型,信息型(hide_number 族)永不出现在本集) func _harmful_variants(vids: Array) -> Array: - var out: Array = [] + var out: Array[Variant] = [] for vid_v in vids: var vid := String(vid_v) - var vrow: Dictionary = db.variation.row_by_id(vid) + var vrow: Dictionary[Variant, Variant] = db.variation.row_by_id(vid) if String(vrow.get("effect", "")) == "flat_damage": out.append(vid) return out @@ -688,7 +688,7 @@ func _harmful_variants(vids: Array) -> Array: # 邻接伤害型变异收集(terrain_kind fact 取数面——flag_hit 发射点用:标中旗相邻八格的伤害型变异集; # 🔴 R4 甲永不对冲边界=仅限伤害型,信息型〔hide_number 族〕不入集) func _harmful_adjacent(cell: Vector2i) -> Array: - var out: Array = [] + var out: Array[Variant] = [] var board: BoardData = minesweeper.current_board() if board == null: return out @@ -699,7 +699,7 @@ func _harmful_adjacent(cell: Vector2i) -> Array: # 效果命中过滤(按效果类型取命中子集——发射点分桶施加面) func _hits_of(hits: Array, kind: String) -> Array: - var out: Array = [] + var out: Array[Variant] = [] for h in hits: if String((h as Dictionary).get("kind", "")) == kind: out.append(h) @@ -725,7 +725,7 @@ func use_treasure(handle: int, target: Vector2i) -> Dictionary: var board: MinesweeperRules = minesweeper # 恒等包装退役(2026-10-02 修复批——use_treasure 直引公有成员) # 板维度读数(迭代 22 T3 修——Rules 无 width 代理〔潜伏缺陷:旧目录期零直呼覆盖掩盖〕,读 BoardData 本体) var board_b: BoardData = board.current_board() - var v: Dictionary = treasure_inventory.validate_use(handle, target, + var v: Dictionary[Variant, Variant] = treasure_inventory.validate_use(handle, target, Vector2(board_b.width, board_b.height), func(c: Vector2i): return not board.is_revealed(c)) if not bool(v.ok): @@ -736,10 +736,10 @@ func use_treasure(handle: int, target: Vector2i) -> Dictionary: for h in tu_hits: if String(h.get("kind", "")) == "counter_spend": if not _fx.counter_spend(String(h.get("counter_id", "")), int(h.get("value", 1))): - var vrow: Dictionary = db.treasure_item_row(String(v.item_id)) + var vrow: Dictionary[Variant, Variant] = db.treasure_item_row(String(v.item_id)) var cn_info: int = int(vrow.get("charge_n", 0)) # 迭代 22 T3——读受理行自身(旧 dig_charge 硬编码随行退役) return {"ok": false, "reason": "%s:安全格积点未达(%d/%d)" % [String(vrow.get("display_name", "")), _attune_safe_seen % cn_info if cn_info > 0 else 0, cn_info]} - var kinds: Array = v.kinds + var kinds: Array[Variant] = v.kinds if kinds.has("mine_reveal") or kinds.has("reveal_tiles"): # 真视写入(迭代 22 T3——reveal_tiles 新真视 kind 与 mine_reveal 同叠加层通道: # 危险格显示=信息呈现零回写,棋盘事实不可变契约 3) @@ -765,10 +765,10 @@ func use_treasure(handle: int, target: Vector2i) -> Dictionary: # 爆破工兵包兑付(迭代 13 T8/D6——中立移除雷格:未揭示/无旗/是雷过滤归排雷玩法; # P1 甲邻接数字冻结零重算、清图分母零涉(雷格不入分母);空集拒绝零消耗—— # consume 之前 return(拒绝面注记扩「③内零消耗分支」),拒绝 reason 可呈现) - var blasted: Array = board.remove_mines_in(v.resolved) + var blasted: Array[Variant] = board.remove_mines_in(v.resolved) if blasted.is_empty(): return {"ok": false, "reason": "爆破工兵包:3×3 区域内无可移除雷格(须未揭示且未插旗)"} - var rec: Dictionary = treasure_inventory.consume(handle) + var rec: Dictionary[Variant, Variant] = treasure_inventory.consume(handle) # 同调主动件充能扣减(迭代 11 第①批 T6——共享账本直改:adventure/level/settlement 同一字典引用) if not rec.is_empty() and String(rec.get("source_class", "")) == "attunement": _ledger.attune_charge_consume(String(rec.get("item_id", ""))) @@ -788,7 +788,7 @@ func set_treasure_unlocked(ids: Dictionary) -> void: func _grant_routed(snap: Dictionary, polarity: String, source_class: String, cell: Vector2i, ri: int, highlight: String = "", label_suffix: String = "", halve_pct: int = 0) -> bool: # 迭代 11 第①批 T7——aux 授予分支随掉落/停点渠道退役拆除(reward 词表第十型 treasure_item 保留但零投放: # 无 aux 行入池、CURIO_KINDS 词表已除名);剩余类型走极性字典路由。 - var row: Dictionary = _reward_economy.polarity_row(String(snap.get("cell_type", "")), polarity) + var row: Dictionary[Variant, Variant] = _reward_economy.polarity_row(String(snap.get("cell_type", "")), polarity) if row.is_empty(): _fatal("极性字典缺行: %s %s" % [String(snap.get("cell_type", "")), polarity]) return false @@ -831,7 +831,7 @@ func _apply_percent_row(row: Dictionary, source_class: String, cell: Vector2i, r var eff := String(row.effect_id) if eff == "gp_gain" or eff == "gp_loss": return _apply_gp_row(row, source_class, cell, ri, highlight, label_suffix) - var srow: Dictionary = db.status_effect_row(eff) + var srow: Dictionary[Variant, Variant] = db.status_effect_row(eff) # 旗溢价贯穿(迭代14② T4——仅标中旗正向行携带 premium_pct;其余授予路径缺省 100 零扰动): # 应用幅度与呈现行值同乘,容器经 magnitude_override 拿同一快照(抗表重载口径保持)。 var prem_pct: int = int(row.get("premium_pct", 100)) @@ -980,7 +980,7 @@ func _roll_round_actions() -> void: # 技能名提示〔轻量 toast 非演出——D8 呈现面〕。门=确定性谓词,机制面零随机,摇号随机 # 消费仍在 behavior_roll 既有流——同种子逐位复现不破坏) for sid in _enemy_system.check_skill_unlocks(u): - var srow_u: Dictionary = db.skill_row(sid) + var srow_u: Dictionary[Variant, Variant] = db.skill_row(sid) _emit_row("highlight", "enemy_action", Vector2i(-1, -1), round_index, "%s 解锁了 %s" % [String(u.display_name), String(srow_u.get("display_name", sid))], 0, {"skill_id": sid}, "skill_unlock") @@ -998,9 +998,9 @@ func _roll_round_actions() -> void: for h in eht_hits: if String(h.get("kind", "")) == "target_attack_bonus": u["fx_atk_bonus"] = int(u.get("fx_atk_bonus", 0)) + int(h.get("value", 0)) - var entries: Array = _enemy_system.rollable_skills(u) + var entries: Array[Variant] = _enemy_system.rollable_skills(u) if _enemy_system.basic_streak(u) >= pity_m: - var specials: Array = _enemy_system.special_skills(u) + var specials: Array[Variant] = _enemy_system.special_skills(u) if not specials.is_empty(): entries = specials # 保底命中——普攻恒可摇 → 非保底路径池恒非空,此为唯一替代集合 var stream: RandomNumberGenerator = rng.open_stream("behavior_roll") @@ -1026,7 +1026,7 @@ func _defender_ac(unit: Dictionary) -> int: func _pool_effective(effect_id: String) -> int: var side := "hero" - var row: Dictionary = db.status_effect_row(effect_id) + var row: Dictionary[Variant, Variant] = db.status_effect_row(effect_id) if not row.is_empty(): side = String(row.target_side) for eff in _status_effects.query_active(side): @@ -1136,7 +1136,7 @@ func _battle_over(result: String) -> void: if result == "victory": for h in _fx_emit("battle_won", {"round": round_index}): if String(h.get("kind", "")) == "heal": - var hu_w: Dictionary = _enemy_system.hero_unit() + var hu_w: Dictionary[Variant, Variant] = _enemy_system.hero_unit() hu_w["hp"] = mini(int(hu_w.get("hp", 0)) + int(h.get("value", 0)), maxi(1, int(hu_w.get("max_hp", 1)))) # bonus_delta 战末汇入(迭代 20 T1——血税双向账:本战累积并入冒险加算区共享引用后清零)+ # adventure 域计数器导出回写(T6——共享引用覆写,attune_charges 先例) @@ -1165,7 +1165,7 @@ func _battle_over(result: String) -> void: # + level_gp_bonus(level_index) + escrow。掷定行缺位 = 数据面缺行(fail 报错按 0 兑付——不哑掉)。 func _roll_gp_drop() -> int: var tier := "boss" if String(encounter.get("node_type", "")) == "boss" else "normal" - var drops: Dictionary = db.shop_gp_drop_row(tier) + var drops: Dictionary[Variant, Variant] = db.shop_gp_drop_row(tier) var roll := 0 if drops.is_empty(): push_error("[战斗结算] gp_drops 缺行 tier=%s——掉落掷定按 0 兑付" % tier) @@ -1190,18 +1190,18 @@ func _roll_gp_drop() -> int: # 八型批 2026-09-20:ambush_layers 拆除——伏击退役,警觉并入 effects 投影)hero_effects[](增益状态 query_active 投影) func presentation_snapshot() -> Dictionary: var enemies: Array[Dictionary] = [] - var all: Array = _enemy_system.all_enemies() + var all: Array[Variant] = _enemy_system.all_enemies() # 敌方增益投影(side-wide 状态——蓄力/硬化/削弱/暴走 percent 行 + 警觉 ac 行,八型批 2026-09-20) var enemy_effects: Array[Dictionary] = [] for eff in _status_effects.query_active("enemy"): - var erow: Dictionary = db.status_effect_row(eff.effect_id) + var erow: Dictionary[Variant, Variant] = db.status_effect_row(eff.effect_id) if not erow.is_empty() and String(erow.unit) in ["percent", "ac"]: enemy_effects.append({"effect_id": eff.effect_id, "display_name": String(eff.display_name), "layers": int(eff.layers)}) for i in all.size(): - var u: Dictionary = all[i] + var u: Dictionary[Variant, Variant] = all[i] var alive: bool = bool(u.get("alive", false)) var pending: String = String(u.get("pending_skill", "")) if alive else "" - var pending_row: Dictionary = db.skill_row(pending) + var pending_row: Dictionary[Variant, Variant] = db.skill_row(pending) enemies.append({ "order": i + 1, "display_name": String(u.display_name), @@ -1218,7 +1218,7 @@ func presentation_snapshot() -> Dictionary: "alive": alive, }) var hero := {} - var h: Dictionary = _enemy_system.hero_unit() + var h: Dictionary[Variant, Variant] = _enemy_system.hero_unit() if not h.is_empty(): hero = {"hp": int(h.hp), "max_hp": int(h.max_hp), "atk": int(h.atk), "alive": bool(h.get("alive", false)), "armor_class": _defender_ac(h)} return { diff --git a/game/core/board_data.gd b/game/core/board_data.gd index 9402b9ae..cf9bb95f 100644 --- a/game/core/board_data.gd +++ b/game/core/board_data.gd @@ -106,7 +106,7 @@ func compute_r0() -> Array: var r0 := {} for c in b3: r0[c] = true - var queue: Array = b3.duplicate() + var queue: Array[Variant] = b3.duplicate() while not queue.is_empty(): var c: Vector2i = queue.pop_front() if is_safe(c) and digit_of(c) == 0: # 变异死分支退役(2026-09-18 用户拍板删——与 排雷玩法 展开实现口径归一;变异定稿批按正式语义重接) diff --git a/game/core/board_generator.gd b/game/core/board_generator.gd index ee8a7c11..4fb150aa 100644 --- a/game/core/board_generator.gd +++ b/game/core/board_generator.gd @@ -15,8 +15,8 @@ const METRIC_VERSION := 1 # rng_stream = 一次 open_stream("board_gen") 实例(一张图的全部取数;重试共用——attempt 边界非 scope 事件)。 # db 参数不标具体类型原因:冒烟工具注入 FakeDB 桩(experiment_density.gd 等)——typed 形参会拒收桩表(2026-10-02 注) -func generate(L: int, D: int, rng_stream: RandomNumberGenerator, db) -> BoardData: - var row: Dictionary = db.resolve_board_gen(L, D) +func generate(L: int, D: int, rng_stream: RandomNumberGenerator, db) -> BoardData: # type-hint: ignore(测量面 _BoardRowStub 冒烟夹具鸭子型注入——typed 参拒 stub) + var row: Dictionary[Variant, Variant] = db.resolve_board_gen(L, D) if row.is_empty(): _fatal("BoardGen 行缺失 (%d,%d)" % [L, D]) return null @@ -54,7 +54,7 @@ func generate(L: int, D: int, rng_stream: RandomNumberGenerator, db) -> BoardDat if int(params.r0_cap) > 0 and b.compute_r0().size() > int(params.r0_cap): r0_cap_rejected += 1 if legality(b, params, db): - var m: Dictionary = measure_inferability(b) # 求解器每合法板恰一次(O3 收口——此前重复调用两次) + var m: Dictionary[Variant, Variant] = measure_inferability(b) # 求解器每合法板恰一次(O3 收口——此前重复调用两次) var inf: float = m["inferability"] if best_legal == null or inf > best_legal_score: best_legal = b @@ -80,7 +80,7 @@ func generate(L: int, D: int, rng_stream: RandomNumberGenerator, db) -> BoardDat # ==================== 五阶段:单次尝试 ==================== -func _attempt(params: Dictionary, rng_stream: RandomNumberGenerator, db) -> BoardData: +func _attempt(params: Dictionary, rng_stream: RandomNumberGenerator, db) -> BoardData: # type-hint: ignore(同 generate——测量面 stub 注入) var b := BoardData.new() b.width = params.w b.height = params.h @@ -158,7 +158,7 @@ func measure_inferability(b: BoardData) -> Dictionary: while changed: changed = false # 算子① 连锁:0 格的 8 邻安全格全入 R - var frontier: Array = r.keys() + var frontier: Array[Variant] = r.keys() for x in frontier: if safe_set.has(x) and b.digit_of(x) == 0: for n in b.neighbors8(x): @@ -183,8 +183,8 @@ func measure_inferability(b: BoardData) -> Dictionary: var derived := [] for i in constraints.size(): for j in range(i + 1, constraints.size()): - var a: Dictionary = constraints[i] - var bb: Dictionary = constraints[j] + var a: Dictionary[Variant, Variant] = constraints[i] + var bb: Dictionary[Variant, Variant] = constraints[j] var sub := _subset_of(a.unknown, bb.unknown) if sub == 1: derived.append(_diff_constraint(a, bb)) @@ -264,7 +264,7 @@ func is_clearable(b: BoardData) -> bool: for c in b.safe_cells(): safe_set[c] = true var seen := {} - var queue: Array = b.spawn_cells().duplicate() + var queue: Array[Variant] = b.spawn_cells().duplicate() for c in queue: seen[c] = true while not queue.is_empty(): @@ -281,7 +281,7 @@ func is_clearable(b: BoardData) -> bool: return true -func legality(b: BoardData, params: Dictionary, db) -> bool: +func legality(b: BoardData, params: Dictionary, db) -> bool: # type-hint: ignore(同 generate——测量面 stub 注入) var reasons := _legality_faults(b, params) return reasons.is_empty() @@ -331,7 +331,7 @@ func _neighbor_mine_count(b: BoardData, c: Vector2i) -> int: # ==================== 产出上报(生成期指标集) ==================== -func _finalize(b: BoardData, params: Dictionary, attempts_used: int, accept_mode: String, _db, r0_cap_rejected: int = 0) -> void: +func _finalize(b: BoardData, params: Dictionary, attempts_used: int, accept_mode: String, _db, r0_cap_rejected: int = 0) -> void: # type-hint: ignore(同 generate——测量面 stub 注入) var m := measure_inferability(b) # 产物断言(棋盘生成 出生区安全与反退化布雷):实际雷数 ≤ 目标雷数 N——先布后删只减不增,失败即致命 var n_target: int = int(round(float(params.density) * float(params.w * params.h - BoardData.SPAWN_AREA_CELLS))) @@ -364,7 +364,7 @@ func _badness(b: BoardData, params: Dictionary) -> Array: for c in b.safe_cells(): safe_set[c] = true var seen := {} - var queue: Array = b.spawn_cells().duplicate() + var queue: Array[Variant] = b.spawn_cells().duplicate() for c in queue: seen[c] = true while not queue.is_empty(): diff --git a/game/core/config_db.gd b/game/core/config_db.gd index e3a21806..36156356 100644 --- a/game/core/config_db.gd +++ b/game/core/config_db.gd @@ -209,7 +209,7 @@ func variant_pool(level_index: int, board_type: String, node_type: String = "") continue if int(row.get("unlock_level", 999)) > level_index: continue - var tags: Array = row.get("board_types", []) + var tags: Array[Variant] = row.get("board_types", []) if tags.is_empty(): pool.append(row) continue diff --git a/game/core/config_db_cross_validator.gd b/game/core/config_db_cross_validator.gd index 63de9684..252c3c58 100644 --- a/game/core/config_db_cross_validator.gd +++ b/game/core/config_db_cross_validator.gd @@ -77,7 +77,7 @@ func cross_validate(loaded: Dictionary, push_diag: Callable) -> void: var li: int = int(lrow.level_index) # 模板池感知(关卡节点图生成 v0.4——池内全模板跨表校验,不只 row.nodes) var node_lists: Array[Array] = [] - var tpls: Array = lrow.get("templates", []) + var tpls: Array[Variant] = lrow.get("templates", []) if tpls.is_empty(): node_lists.append(lrow.get("nodes", [])) else: @@ -137,7 +137,7 @@ func validate_skill_pool_links(skill_table: SkillTable, enemy_table: EnemyTable, continue var want_side: String = "enemy" if st == "buff" else "hero" var eff: String = String(row.get("buff_ref", "")) - var srow: Dictionary = status_table.row_by_id(eff) if status_table != null else {} + var srow: Dictionary[Variant, Variant] = status_table.row_by_id(eff) if status_table != null else {} if not srow.is_empty() and String(srow.get("target_side", "")) != want_side: errs.append({"category": "cast_side", "message": "SkillTable %s(%s)引用 %s 的 target_side=%s ≠ %s(施放类 side 守卫)" % [String(row.get("skill_id", "")), st, eff, String(srow.get("target_side", "")), want_side]}) for row in enemy_table.rows: diff --git a/game/core/damage_controller.gd b/game/core/damage_controller.gd index 77a375c3..2a670816 100644 --- a/game/core/damage_controller.gd +++ b/game/core/damage_controller.gd @@ -113,7 +113,7 @@ func _fx_fact(fact: String): "dig_streak": return dig_streak "hp_ratio": - var hu: Dictionary = units.hero_unit() + var hu: Dictionary[Variant, Variant] = units.hero_unit() return int(round(100.0 * float(int(hu.get("hp", 0))) / float(maxi(1, int(hu.get("max_hp", 1)))))) # 百分比 int(when lte int 截断口径) return null @@ -292,9 +292,9 @@ func _hero_attack_ctx(t: Dictionary) -> Dictionary: var armor: String = String(target.get("armor_type", "light")) var atk_type: String = String(t.attack_type) var matchup := (atk_type + "_vs_" + armor) if atk_type != "" else "" - var ac_hits: Array = (t.context.get("fx_attack_hits", []) as Array) - var ac_payload: Dictionary = (t.context.get("fx_attack_payload", {}) as Dictionary) - var roll_read: Dictionary = _armor_roll() + var ac_hits: Array[Variant] = (t.context.get("fx_attack_hits", []) as Array) + var ac_payload: Dictionary[Variant, Variant] = (t.context.get("fx_attack_payload", {}) as Dictionary) + var roll_read: Dictionary[Variant, Variant] = _armor_roll() var threshold: int = defender_ac(target) + int(db.settlement.attack_bonus) # armor_roll 发射点(内容数据驱动扩展批 T1——敌护甲掷骰判定;免掷恒折扣订阅面) var ar_hits := _fx_hits("armor_roll", {"target_ac": threshold, "roll": int(roll_read.roll)}) @@ -349,8 +349,8 @@ func _hero_attack_ctx(t: Dictionary) -> Dictionary: # 基值装配(原 :329-355 逐字):基值+单位标记加伤+矿工重锤增伤面+加法族(attack_bonus/ # value_counter 层值)+计数器施加(attack_commit 用后清零行——命中序=声明序)+spike 倍乘。 func _hero_attack_base_value(t: Dictionary, ctx: Dictionary) -> float: - var ac_hits: Array = ctx["ac_hits"] - var ac_payload: Dictionary = ctx["ac_payload"] + var ac_hits: Array[Variant] = ctx["ac_hits"] + var ac_payload: Dictionary[Variant, Variant] = ctx["ac_payload"] var base_value := float(t.base_value) # 单位标记加伤(迭代 20 T1——处刑台 target_attack_bonus 挂单位字典 fx_atk_bonus 键; # 阈值沿发射点已写入,本处通用消费——对该敌伤害加算) @@ -385,11 +385,11 @@ func _hero_attack_base_value(t: Dictionary, ctx: Dictionary) -> float: # 击杀标记/kill 发射+贯穿弹射。返回 {landed, payload, hero_rider}(交付步消费)。 func _hero_attack_redeem(t: Dictionary, ctx: Dictionary, base_value: float) -> Dictionary: var target = ctx["target"] - var ac_hits: Array = ctx["ac_hits"] - var roll_read: Dictionary = ctx["roll_read"] + var ac_hits: Array[Variant] = ctx["ac_hits"] + var roll_read: Dictionary[Variant, Variant] = ctx["roll_read"] var atk_type: String = ctx["atk_type"] var rolled: int = DamageMath.rolled_component(base_value, int(ctx["discount"]), bool(ctx["crit"]), int(db.settlement.crit_multiplier), int(ctx["cdelta"])) - var payload: Dictionary = {"target": String(target.display_name)} + var payload: Dictionary[Variant, Variant] = {"target": String(target.display_name)} payload[atk_type] = rolled # 分量键 = 实际攻击类型(归因字段,单分量模型) payload["roll"] = int(roll_read.roll) payload["ac"] = int(ctx["threshold"]) @@ -440,7 +440,7 @@ func _hero_attack_redeem(t: Dictionary, ctx: Dictionary, base_value: float) -> D # 交付(原 :403-414 逐字):即时执行族(碎甲震击护甲裂解)/标签装配/明细行发射/兑现登记。 func _hero_attack_deliver(t: Dictionary, ctx: Dictionary, redemption: Dictionary) -> void: - var payload: Dictionary = redemption["payload"] + var payload: Dictionary[Variant, Variant] = redemption["payload"] # 即时执行族:碎甲震击打重甲附加「护甲裂解」(复用 off_guard——零新状态行;apply_effect 域闭/ # 重复授予由状态容器自行守卫) for h in (ctx["ac_hits"] as Array): @@ -459,8 +459,8 @@ func _hero_attack_deliver(t: Dictionary, ctx: Dictionary, redemption: Dictionary # 前态恒中性 delta 0,写法同 hero_attack 的 _counter_delta 消费位)→ hero_defense×enemy_output # 池乘区 → 下限 → +rider(后置必中附加)→ 交付 → commit_action(作废单不 commit——死亡即不动手) func _resolve_enemy_skill(t: Dictionary) -> void: - var hero: Dictionary = units.hero_unit() - var roll_read: Dictionary = _armor_roll() + var hero: Dictionary[Variant, Variant] = units.hero_unit() + var roll_read: Dictionary[Variant, Variant] = _armor_roll() var threshold: int = defender_ac(hero) + int(db.settlement.attack_bonus) var crit: bool = bool(roll_read.crit) var discount: int = _roll_discount(int(roll_read.roll), threshold) @@ -489,7 +489,7 @@ func _resolve_enemy_skill(t: Dictionary) -> void: if shield > 0: total = maxi(int(db.settlement.damage_floor), total - shield) units.apply_damage(hero, total) - var payload: Dictionary = {"target": "hero", "skill_id": String(t.context.get("skill_id", "")), "baseline": int(t.context.get("baseline", 0)), + var payload: Dictionary[Variant, Variant] = {"target": "hero", "skill_id": String(t.context.get("skill_id", "")), "baseline": int(t.context.get("baseline", 0)), "roll": int(roll_read.roll), "ac": threshold, "tier": discount, "attack_type": String(t.attack_type)} if crit: payload["crit"] = true @@ -529,7 +529,7 @@ func _resolve_board_typed(t: Dictionary) -> void: # 棋盘群伤:无类型恒定表值,结账时刻全体存活敌逐个交付(单行汇总——行内 count = 兑现时存活数) func _resolve_board_blast(t: Dictionary) -> void: - var alive: Array = units.query_alive("enemy") + var alive: Array[Variant] = units.query_alive("enemy") if alive.is_empty(): _ineffective_row(t, "无存活敌人") _outcomes[int(t.ticket_id)] = "zero" @@ -546,7 +546,7 @@ func _resolve_board_blast(t: Dictionary) -> void: func _resolve_board_flat_hero(t: Dictionary) -> void: var dealt: int = maxi(int(db.settlement.damage_floor), int(t.base_value)) # 伤害下限全来源(2026-09-20) units.apply_damage(units.hero_unit(), dealt) - var extra: Dictionary = {"target": "hero"} + var extra: Dictionary[Variant, Variant] = {"target": "hero"} for k in t.context.keys(): extra[k] = t.context[k] emit_row_cb.call("damage", String(t.source_class), t.origin_cell, int(t.round_index), "%s → 主角" % String(t.display), dealt, extra, String(t.highlight)) @@ -578,13 +578,13 @@ func defender_ac(unit: Dictionary) -> int: if total < 0: total = int(db.hero.armor_class) for eff in effects.query_active("hero"): - var row: Dictionary = db.status_effect_row(eff.effect_id) + var row: Dictionary[Variant, Variant] = db.status_effect_row(eff.effect_id) if not row.is_empty() and String(row.unit) == "ac": total += int(eff.effective_value) return total var total_e: int = int(unit.get("armor_class", 0)) for eff_e in effects.query_active("enemy"): - var erow_ac: Dictionary = db.status_effect_row(eff_e.effect_id) + var erow_ac: Dictionary[Variant, Variant] = db.status_effect_row(eff_e.effect_id) if not erow_ac.is_empty() and String(erow_ac.unit) == "ac": total_e += int(eff_e.effective_value) return total_e @@ -595,14 +595,14 @@ func defender_ac(unit: Dictionary) -> int: func _counter_delta(attack_type: String, armor: String) -> int: if attack_type == "": return 0 - var tbl: Dictionary = db.settlement.armor_effectiveness - var row: Dictionary = tbl.get(attack_type, {}) + var tbl: Dictionary[Variant, Variant] = db.settlement.armor_effectiveness + var row: Dictionary[Variant, Variant] = tbl.get(attack_type, {}) return int(row.get(armor, 0)) # 折扣档改善(迭代 12 T4 猎弱本能——折扣表向全额方向移 value 档;查不到当前档 = 不动防御口径) func _discount_shift_up(discount: int, shift: int) -> int: - var tbl: Array = db.settlement.roll_tier_discounts + var tbl: Array[Variant] = db.settlement.roll_tier_discounts var idx := tbl.find(discount) if idx < 0: return discount @@ -613,7 +613,7 @@ func _discount_shift_up(discount: int, shift: int) -> int: func clamped_pool(pool: String) -> int: var total := 0 for eff in effects.query_active("hero") + effects.query_active("enemy"): - var row: Dictionary = db.status_effect_row(eff.effect_id) + var row: Dictionary[Variant, Variant] = db.status_effect_row(eff.effect_id) if row.is_empty(): continue if String(db.settlement.effect_pool_map.get(eff.effect_id, "")) == pool and String(row.unit) == "percent": diff --git a/game/core/effect_engine.gd b/game/core/effect_engine.gd index 5c718fd9..af4f99da 100644 --- a/game/core/effect_engine.gd +++ b/game/core/effect_engine.gd @@ -91,8 +91,8 @@ const DO_KEYS := ["kind", "value", "value_from", "delta", "delta_from", "mult", const ACTIVE_KINDS := ["reveal_tiles", "preview", "grant_ap", "remove_mines", "refresh_pick", "safe_dig"] # ---- 订阅集与计数器账本 ---- -var _triggers: Array = [] # [{source, id, on, when, do}](装载序=声明序,重建时定死) -var _counters: Dictionary = {} # counter_id → int(battle 域账本;重建清零;重置点由订阅行声明) +var _triggers: Array[Variant] = [] # [{source, id, on, when, do}](装载序=声明序,重建时定死) +var _counters: Dictionary[Variant, Variant] = {} # counter_id → int(battle 域账本;重建清零;重置点由订阅行声明) var _reset_on_round_start: Array[String] = [] # round_start 重置点计数器名单(装载时收集) var _adventure_counter_ids: Array[String] = [] # domain=adventure 计数器名单(T6——重建时收集、不清零; # battle 末 export → 冒险账本、begin_battle seed 回灌) @@ -171,7 +171,7 @@ func rebuild(feat_ids: Array, item_ids: Array, tree_rules: Array = []) -> void: func _push_trigger(source: String, id: String, tr: Dictionary, row: Dictionary) -> void: var on := String(tr.get("on", "")) assert(EVENTS.has(on), "[效果引擎] 订阅事件出域: %s(%s %s)" % [on, source, id]) - var do_d: Dictionary = (tr.get("do", {}) as Dictionary).duplicate(true) + var do_d: Dictionary[Variant, Variant] = (tr.get("do", {}) as Dictionary).duplicate(true) # *_from 解析(rebuild 时把行字段引用定影为值——value_from:"magnitude" → value=行 magnitude) for pair in [["value_from", "value"], ["delta_from", "delta"], ["mult_from", "mult"], ["cap_from", "cap"]]: if do_d.has(pair[0]): @@ -179,9 +179,9 @@ func _push_trigger(source: String, id: String, tr: Dictionary, row: Dictionary) do_d.erase(pair[0]) # when value_from 同面解析(行字段引用条件——dawn_order effective_rounds 先例:字面量出行=数值 # 单一事实源破面,行引用随 rebuild 定影) - var when_d: Array = (tr.get("when", []) as Array).duplicate(true) + var when_d: Array[Variant] = (tr.get("when", []) as Array).duplicate(true) for c_v in when_d: - var c: Dictionary = c_v as Dictionary + var c: Dictionary[Variant, Variant] = c_v as Dictionary if c.has("value_from"): c["value"] = int(row.get(String(c["value_from"]), 0)) c.erase("value_from") @@ -206,7 +206,7 @@ func has_effect_on(event: String, kind: String, mode: String = "") -> bool: for tr in _triggers: if String(tr.on) != event: continue - var d: Dictionary = tr.do + var d: Dictionary[Variant, Variant] = tr.do if String(d.get("kind", "")) == kind and (mode == "" or String(d.get("mode", "")) == mode): # 有 when 时不能预判命中(发射点缺省行为保守按「可能命中」计——命中由 dispatch 定夺) return true @@ -221,7 +221,7 @@ func dispatch(event: String, payload: Dictionary, facts: Callable = Callable()) if event == "round_start": for cid in _reset_on_round_start: _counters[cid] = 0 - var hits: Array = [] + var hits: Array[Variant] = [] for tr in _triggers: if String(tr.on) != event: continue @@ -350,8 +350,8 @@ func _counter_delta_of(matchup: String) -> int: var parts := matchup.split("_vs_") if parts.size() != 2: return 0 - var tbl: Dictionary = db.settlement.armor_effectiveness - var row: Dictionary = tbl.get(String(parts[0]), {}) + var tbl: Dictionary[Variant, Variant] = db.settlement.armor_effectiveness + var row: Dictionary[Variant, Variant] = tbl.get(String(parts[0]), {}) return int(row.get(String(parts[1]), 0)) @@ -362,7 +362,7 @@ func _counter_delta_of(matchup: String) -> int: static func validate_trigger_row(rid: String, row: Dictionary, triggers: Array, errs: Array) -> bool: var mine_covered := false for tr_v in triggers: - var tr: Dictionary = tr_v as Dictionary + var tr: Dictionary[Variant, Variant] = tr_v as Dictionary for k in tr.keys(): if not ["on", "when", "do"].has(String(k)): errs.append({"category": "zero_flat", "message": "%s trigger 键出白名单: %s" % [rid, String(k)]}) @@ -372,7 +372,7 @@ static func validate_trigger_row(rid: String, row: Dictionary, triggers: Array, elif MINE_EVENTS.has(on): mine_covered = true for c_v in (tr.get("when", []) as Array): - var c: Dictionary = c_v as Dictionary + var c: Dictionary[Variant, Variant] = c_v as Dictionary for k in c.keys(): if not WHEN_KEYS.has(String(k)): errs.append({"category": "zero_flat", "message": "%s when 键出白名单: %s" % [rid, String(k)]}) @@ -381,7 +381,7 @@ static func validate_trigger_row(rid: String, row: Dictionary, triggers: Array, errs.append({"category": "enum", "message": "%s fact 出域(事实词汇封闭 9 项): %s" % [rid, fact]}) if not OPS.has(String(c.get("op", "eq"))): errs.append({"category": "enum", "message": "%s when 算子出域: %s" % [rid, String(c.get("op", ""))]}) - var do_d: Dictionary = tr.get("do", {}) as Dictionary + var do_d: Dictionary[Variant, Variant] = tr.get("do", {}) as Dictionary for k in do_d.keys(): if not DO_KEYS.has(String(k)): errs.append({"category": "zero_flat", "message": "%s do 键出白名单: %s(机制轴禁无条件属性加项)" % [rid, String(k)]}) @@ -396,7 +396,7 @@ static func validate_trigger_row(rid: String, row: Dictionary, triggers: Array, if fld != "" and not row.has(fld): errs.append({"category": "dangling", "message": "%s do.%s 悬空(行无字段 %s)" % [rid, vf_k, fld]}) for c_v in (tr.get("when", []) as Array): - var c_w: Dictionary = c_v as Dictionary + var c_w: Dictionary[Variant, Variant] = c_v as Dictionary if c_w.has("value_from"): var wfld := String(c_w.get("value_from", "")) if wfld != "" and not row.has(wfld): diff --git a/game/core/feats_engine.gd b/game/core/feats_engine.gd index 2e324a72..7141f26d 100644 --- a/game/core/feats_engine.gd +++ b/game/core/feats_engine.gd @@ -13,7 +13,7 @@ extends RefCounted static func tiers_for(feat_ids: Array, table: FeatTable) -> Dictionary: var counts := {} for rid in feat_ids: - var row: Dictionary = table.row_by_id(String(rid)) if table != null else {} + 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", "")) diff --git a/game/core/level_runner.gd b/game/core/level_runner.gd index 54541dc6..d88c34a6 100644 --- a/game/core/level_runner.gd +++ b/game/core/level_runner.gd @@ -47,7 +47,7 @@ var node_index := -1 # ---- 账本载体(冒险结构实现批——漂移②清偿)---- # 冒险模式 = AdventureRunner 共享字典(Dictionary 引用共享——hero_hp/gp 属性读写直接落冒险账本); # 单关模式 = 本实例私有账本(start_level 重建,既有单关语义逐位保留)。 -var _ledger: Dictionary = {"hero_hp": 0, "gp": 0} +var _ledger: Dictionary[Variant, Variant] = {"hero_hp": 0, "gp": 0} var _rest_handler: Callable # 治疗封顶回报执行体(默认内置 rest_heal_amount;冒险模式 = AdventureRunner.apply_rest——接口③) var _external := false # 冒险模式标记(钱包收束时机与播种语义分叉点) # hero_hp / gp 属性化(既有读写点零改动——值类型经属性落 _ledger) @@ -63,7 +63,7 @@ var _shop_purchased := {} # product_id → 已购档数(现价 var _settlement: BattleOrchestrator = null # 当前战斗编排器(关卡结束置 null) var treasure_inventory: TreasureItems = null # 秘宝 关卡域库存(迭代 7 T3——TreasureItems;跨战斗延续, var _treasure_unlocked := {} # aux 解锁门控集(迭代 7 T5——拍板③甲;空集=零投放) -var _attunement: Array = [] # 同调装配集(迭代 11 第①批 T5——adventure params.attunement 下传,begin_battle enc 携带进战斗结算;空集=off 零扰动) +var _attunement: Array[Variant] = [] # 同调装配集(迭代 11 第①批 T5——adventure params.attunement 下传,begin_battle enc 携带进战斗结算;空集=off 零扰动) # 关卡末清空+快照计数入 level_finished 载荷) # 两根类型轴(迭代 23 T4——adventure 参数面快照经 set_hero_axes 下传;空串=未注入→锻造配置默认值回退。 # 消费面:effective_ac 甲型基础 AC 层 + begin_battle enc 携带进 unit_container.spawn 英雄快照) @@ -71,11 +71,11 @@ var _hero_attack_type := "" var _hero_armor_type := "" # ---- 商店共享池模式态(迭代 11 第②批 T3——缺省 legacy=固定三件加定价 off 对照承载,零 RNG 零池消费;TP0 令牌池换代)---- var _shop_pool_mode := false # pool 模式旗(set_shop_pool 注入时置位——AdventureRunner 按 params.shop_mode 下传) -var _pool_deck: Array = [] # 共享池令牌库共享引用(AdventureRunner._curio_deck——库存=剩余令牌数单点) +var _pool_deck: Array[Variant] = [] # 共享池令牌库共享引用(AdventureRunner._curio_deck——库存=剩余令牌数单点) var _pool_consume := Callable() # 池消费 handler(AdventureRunner._consume_pool_purchase——_apply_curio_pick 同口写加算区+账本+摘牌) var _pool_ticket := Callable() # 门票展示判定 handler(T5——AdventureRunner._feat_ticket_consider;节点与商店共享计数) var _pool_cap_check := Callable() # cap 检查回调(迭代 20 T2——AdventureRunner._curio_cap_ok;案②提前退役) -var _shop_shelf: Array = [] # 当前货架(进店抽 3/买后全重抽——令牌字典 {"kind","tier"} 序,同 kind 去重每 kind 至多一卡) +var _shop_shelf: Array[Variant] = [] # 当前货架(进店抽 3/买后全重抽——令牌字典 {"kind","tier"} 序,同 kind 去重每 kind 至多一卡) var _shop_visit_buys := 0 # 本次进店已购次数(A 链定价输入——离开清零) var _shop_paid_refreshes := 0 # 本次进店已付费刷新次数(MC-1——GP 刷新价曲线输入,离开清零) @@ -132,7 +132,7 @@ func effective_ac() -> int: # 与 hero.tres 两轴删除前 armor_class=10 行为连续) func _armor_base_ac() -> int: var at: String = _hero_armor_type if _hero_armor_type != "" else db.forge_default_armor_type() - var row: Dictionary = db.forge_armor_route_by_type(at) + var row: Dictionary[Variant, Variant] = db.forge_armor_route_by_type(at) if row.is_empty(): return int(db.hero.armor_class) # 表缺失防御兜底(现役基准) return int(row.get("base_ac", 0)) @@ -274,7 +274,7 @@ func _restore_to_stop(node_id: int, visited_ids: Array) -> bool: _phase = Phase.AWAITING_CHOICE _candidates = [entry] return true - var stop: Dictionary = {} + var stop: Dictionary[Variant, Variant] = {} for node in nodes(): if _node_id(node) == node_id: stop = node @@ -330,7 +330,7 @@ var active_template_name := "" var active_template_index := -1 # -1 = 非池形态(旧单图)/未解析 func _resolve_template_row(row: Dictionary, template_pick: int) -> Dictionary: - var templates: Array = row.get("templates", []) + var templates: Array[Variant] = row.get("templates", []) active_template_name = "" active_template_index = -1 if templates.is_empty(): @@ -341,7 +341,7 @@ func _resolve_template_row(row: Dictionary, template_pick: int) -> Dictionary: else: var stream: RandomNumberGenerator = rng.open_stream("node_map") idx = stream.randi_range(0, templates.size() - 1) - var tpl: Dictionary = templates[idx] + var tpl: Dictionary[Variant, Variant] = templates[idx] active_template_name = String(tpl.get("display_name", "")) active_template_index = idx var out := row.duplicate(true) @@ -385,7 +385,7 @@ func _entry_node() -> Dictionary: func _out_ids(node: Dictionary) -> Array: if node.has("next"): return node.get("next", []) - var nodes: Array = nodes() + var nodes: Array[Variant] = nodes() var i := nodes.find(node) if i >= 0 and i < nodes.size() - 1: return [i + 1] @@ -627,7 +627,7 @@ func set_attunement(ids: Array) -> void: # 特性持有集注入(迭代 12 T1——AdventureRunner._start_current_level 唯一调用方;begin_battle # enc.feats 携带进战斗求值;**共享引用不复制**——关内奖励节点兑付的 relic 须当关后续战斗生效 # (attune_charges 共享账本同款);空集=零扰动) -var _feats: Array = [] +var _feats: Array[Variant] = [] func set_feats(ids: Array) -> void: @@ -640,7 +640,7 @@ func feats_held() -> Array: # 树 rule 键集注入(迭代 12 T10——begin_battle enc.tree_rules 携带;Fork 激活侧 effects) -var _tree_rules: Array = [] +var _tree_rules: Array[Variant] = [] func set_tree_rules(rules: Array) -> void: @@ -682,7 +682,7 @@ func set_voucher_grants(rap: int, time_sand: int, insurance: int) -> void: # 已含断点内 attune 记录。原 set_attune_charges/set_vision_counter/set_adventure_counters # 三散 setter 随单类下传承接) func sync_attune_inventory() -> void: - var charges: Dictionary = run_ledger.attune_charges + var charges: Dictionary[Variant, Variant] = run_ledger.attune_charges if treasure_inventory == null: treasure_inventory = TreasureItems.new(db) for iid in charges: @@ -771,7 +771,7 @@ func purchased_tier(product_id: String) -> int: # 购买(未知商品/属性键出域/余额不足拒绝——返回 false 零副作用): # 出账 → 加算区 += flat_value → 购血同步当前生命(封顶新上限)→ 已购档数 +1 func buy_product(product_id: String) -> bool: - var prow: Dictionary = db.shop_product_row(product_id) + var prow: Dictionary[Variant, Variant] = db.shop_product_row(product_id) if prow.is_empty(): push_error("[关卡节点地图] 商店房未知商品: %s" % product_id) return false @@ -826,13 +826,13 @@ func _shop_open_pool() -> void: # 合法态(池空硬闸——买光即枯竭既定后果)。 func _shop_draw_shelf() -> void: _shop_shelf = [] - var sellable: Array = [] + var sellable: Array[Variant] = [] for row in db.shop_pool_rows(): var shelf_kind := String(row.get("kind_id", "")) if _pool_cap_check.is_valid() and not bool(_pool_cap_check.call(shelf_kind, "")): continue # 迭代 20 T2 案②——cap 满提前退役(货架候选视图剔除;牌库本体不动) for t in _pool_deck: - var tok: Dictionary = t as Dictionary + var tok: Dictionary[Variant, Variant] = t as Dictionary if String(tok.get("kind", "")) == shelf_kind: sellable.append({"kind": shelf_kind, "tier": String(tok.get("tier", ""))}) if sellable.is_empty(): @@ -843,7 +843,7 @@ func _shop_draw_shelf() -> void: var tmp = sellable[i] sellable[i] = sellable[j] sellable[j] = tmp - var seen: Array = [] + var seen: Array[Variant] = [] for tok in sellable: var k := String((tok as Dictionary).get("kind", "")) if seen.has(k): @@ -874,7 +874,7 @@ func pool_stock(kind: String) -> int: # A/b 表 knob provisional;核心现算,呈现层禁复算——契约 5)。relic 卡(保底占位——shop 词表外) # 基价 provisional 15 固定锚(台账披露待追认——shop.tres 词表扩列归数值批)。 func pool_price(kind: String) -> int: - var row: Dictionary = db.shop_pool_row(kind) + var row: Dictionary[Variant, Variant] = db.shop_pool_row(kind) if row.is_empty(): if kind == "relic": return _spend_discounted(int(ceil(15.0 * pow(db.shop_price_b(), float(maxi(0, int(level_row.level_index) - 1))) * pow(db.shop_price_a(), float(_shop_visit_buys))))) @@ -892,11 +892,11 @@ func shop_shelf() -> Array: for tok in _shop_shelf: var kind := String((tok as Dictionary).get("kind", "")) var tier := String((tok as Dictionary).get("tier", "")) - var row: Dictionary = db.shop_pool_row(kind) + var row: Dictionary[Variant, Variant] = db.shop_pool_row(kind) var display := String(row.get("display_name", kind)) var value := int(db.adventure.curio_value(kind, tier)) if row.is_empty() and kind == "relic": - var rrow: Dictionary = db.feat_row(String(tok.get("feat_id", ""))) + var rrow: Dictionary[Variant, Variant] = db.feat_row(String(tok.get("feat_id", ""))) display = String(rrow.get("display_name", "特性")) value = 0 out.append({ @@ -924,7 +924,7 @@ func shop_refresh_free_left() -> int: func _attune_shop_refresh_bonus() -> int: var total := 0 for iid_v in _attunement: - var arow: Dictionary = db.treasure_item_row(String(iid_v)) + var arow: Dictionary[Variant, Variant] = db.treasure_item_row(String(iid_v)) if (arow.get("effect_kinds", []) as Array).has("shop_refresh_bonus"): total += int(arow.get("magnitude", 0)) return total diff --git a/game/core/meta_progression.gd b/game/core/meta_progression.gd index 9f955482..729139cc 100644 --- a/game/core/meta_progression.gd +++ b/game/core/meta_progression.gd @@ -69,7 +69,7 @@ func _treasure_unlock_progress(key: String) -> int: # 钥石购买解锁(营地秘宝匣唯一购买写口):仅购买件(行带 unlock_keystone_cost)可购; # 已解锁/余额不足/条件件误购 = 拒绝且零副作用(spend_keystone 内嵌余额闸,失败不扣币)。 func buy_treasure_unlock(item_id: String) -> bool: - var arow: Dictionary = db.treasure_item_row(item_id) + var arow: Dictionary[Variant, Variant] = db.treasure_item_row(item_id) var price := int(arow.get("unlock_keystone_cost", 0)) if arow.is_empty() or price < 1 or (arow.get("unlock_requires", []) as Array).size() > 0: return false # 非购买件(条件件/无价行)拒绝 @@ -89,12 +89,12 @@ func refresh_treasure_unlocks() -> Array: var iid := String(row.get("item_id", "")) if save.item_unlocked(iid): continue - var ureqs: Array = row.get("unlock_requires", []) as Array + var ureqs: Array[Variant] = row.get("unlock_requires", []) as Array if ureqs.is_empty(): continue # 购买件不入条件刷新面 var met := true for req_v in ureqs: - var req: Dictionary = req_v as Dictionary + var req: Dictionary[Variant, Variant] = req_v as Dictionary if _treasure_unlock_progress(String(req.get("key", ""))) < int(req.get("threshold", 999)): met = false break @@ -113,10 +113,10 @@ func treasure_shop_status() -> Array: for row in db.treasure_attunement_rows(): var iid := String(row.get("item_id", "")) var unlocked := save.item_unlocked(iid) - var ureqs: Array = row.get("unlock_requires", []) as Array - var req_view: Array = [] + var ureqs: Array[Variant] = row.get("unlock_requires", []) as Array + var req_view: Array[Variant] = [] for req_v in ureqs: - var req: Dictionary = req_v as Dictionary + var req: Dictionary[Variant, Variant] = req_v as Dictionary req_view.append({ "key": String(req.get("key", "")), "threshold": int(req.get("threshold", 0)), @@ -143,7 +143,7 @@ func treasure_shop_status() -> Array: # 路线解锁态派生(不落档——初始持有∨直购∨练满前一档;派生单点只此一处) func forge_route_unlocked(route_id: String) -> bool: - var row: Dictionary = db.forge_route(route_id) + var row: Dictionary[Variant, Variant] = db.forge_route(route_id) if row.is_empty(): return false if bool(row.get("initial_owned", false)): @@ -152,7 +152,7 @@ func forge_route_unlocked(route_id: String) -> bool: return true var prereq := String(row.get("prereq_route", "")) if prereq != "": - var prow: Dictionary = db.forge_route(prereq) + var prow: Dictionary[Variant, Variant] = db.forge_route(prereq) if not prow.is_empty() and save.forge_route_level(prereq) >= int(prow.get("cap", 0)): return true return false @@ -161,7 +161,7 @@ func forge_route_unlocked(route_id: String) -> bool: # 路线购买(几何价=基础价×150%^已购级数——锚表 20/30/45/67/101;等级+1 即时写盘)。 # 未解锁/已满级/余额不足/未知路线 = 拒绝且零副作用(spend_keystone 内嵌余额闸,失败不扣币不写盘)。 func forge_buy_level(route_id: String) -> bool: - var row: Dictionary = db.forge_route(route_id) + var row: Dictionary[Variant, Variant] = db.forge_route(route_id) if row.is_empty() or not forge_route_unlocked(route_id): return false var lvl := save.forge_route_level(route_id) @@ -177,7 +177,7 @@ func forge_buy_level(route_id: String) -> bool: # 一次性解锁价直购(并联双通道②)。已解锁(含练满前一档派生)= 拒绝零收费——两通道互不影响: # 未练满也可直购;直购后练满不重复收(练满通道本就是派生、零收费)。初始持有行无解锁面。 func forge_buy_unlock(route_id: String) -> bool: - var row: Dictionary = db.forge_route(route_id) + var row: Dictionary[Variant, Variant] = db.forge_route(route_id) if row.is_empty() or bool(row.get("initial_owned", false)): return false if forge_route_unlocked(route_id): @@ -192,7 +192,7 @@ func forge_buy_unlock(route_id: String) -> bool: # 当前武器/甲选中切换(免费——只受理已解锁路线;写存档域+即时写盘。冒险开始锁死归参数面快照, # 契约 8;冒险中无切换入口=UI 面无路径可达本口) func forge_select_weapon(route_id: String) -> bool: - var row: Dictionary = db.forge_route(route_id) + var row: Dictionary[Variant, Variant] = db.forge_route(route_id) if row.is_empty() or String(row.get("kind", "")) != "weapon" or not forge_route_unlocked(route_id): return false save.set_forge_weapon(route_id) @@ -200,7 +200,7 @@ func forge_select_weapon(route_id: String) -> bool: func forge_select_armor(route_id: String) -> bool: - var row: Dictionary = db.forge_route(route_id) + var row: Dictionary[Variant, Variant] = db.forge_route(route_id) if row.is_empty() or String(row.get("kind", "")) != "armor" or not forge_route_unlocked(route_id): return false save.set_forge_armor(route_id) @@ -211,7 +211,7 @@ func forge_select_armor(route_id: String) -> bool: # 选中态异常(未解锁/表缺行)防御回退 0——注入面只认合法态,禁把脏选中带进冒险。 func forge_weapon_atk_bonus() -> int: var rid := save.forge_weapon() - var row: Dictionary = db.forge_route(rid) + var row: Dictionary[Variant, Variant] = db.forge_route(rid) if row.is_empty() or String(row.get("kind", "")) != "weapon" or not forge_route_unlocked(rid): return 0 return save.forge_route_level(rid) * int(row.get("per_level_gain", 0)) @@ -219,7 +219,7 @@ func forge_weapon_atk_bonus() -> int: func forge_armor_ac_bonus() -> int: var rid := save.forge_armor() - var row: Dictionary = db.forge_route(rid) + var row: Dictionary[Variant, Variant] = db.forge_route(rid) if row.is_empty() or String(row.get("kind", "")) != "armor" or not forge_route_unlocked(rid): return 0 return save.forge_route_level(rid) * int(row.get("per_level_gain", 0)) @@ -229,7 +229,7 @@ func forge_armor_ac_bonus() -> int: # 默认值与柜台解锁状态解耦:树钥匙未点亮时本函数照常返回默认值(配置常驻可读)。 func hero_attack_type() -> String: var rid := save.forge_weapon() - var row: Dictionary = db.forge_route(rid) + var row: Dictionary[Variant, Variant] = db.forge_route(rid) if row.is_empty() or String(row.get("kind", "")) != "weapon" or not forge_route_unlocked(rid): return db.forge_default_attack_type() return String(row.get("type_axis", "")) @@ -237,7 +237,7 @@ func hero_attack_type() -> String: func hero_armor_type() -> String: var rid := save.forge_armor() - var row: Dictionary = db.forge_route(rid) + var row: Dictionary[Variant, Variant] = db.forge_route(rid) if row.is_empty() or String(row.get("kind", "")) != "armor" or not forge_route_unlocked(rid): return db.forge_default_armor_type() return String(row.get("type_axis", "")) @@ -289,7 +289,7 @@ func forge_shop_status() -> Array: func settle_adventure(summary: Dictionary) -> Dictionary: # 首次冒险布尔位置位(关卡节点图生成 v0.4——完成/失败/放弃三结局同经本收束挂点,写入只在收束) save.mark_first_adventure_done() - var drops: Array = (summary.get("keystone_drops", []) as Array).duplicate(true) + var drops: Array[Variant] = (summary.get("keystone_drops", []) as Array).duplicate(true) save.apply_adventure_summary(summary, drops) save.add_vision_used(int(summary.get("vision_used_delta", 0))) # 迭代 22 T3——真视使用计数收束聚合入档(信标网络行为条件) var new_unlocks := refresh_treasure_unlocks() # 迭代 22 T2——条件解锁刷新换代(旧工坊面退役) @@ -333,7 +333,7 @@ func tree_spent_total() -> int: # 节点可购校验(行门槛 + 显式前置 + 币 + cap;free 节点=钥匙不经购买,彩蛋=觉醒不经购买) func tree_can_buy(node_id: String) -> bool: - var row: Dictionary = db.tree_node(node_id) + var row: Dictionary[Variant, Variant] = db.tree_node(node_id) if row.is_empty(): return false # 彩蛋不经购买(觉醒唯一入口=_check_easter_egg 特化判定——V8 反例:手动购亮=红) @@ -354,7 +354,7 @@ func tree_can_buy(node_id: String) -> bool: return false # 等级阈值前置(R1 机制位——requires_level {node_id: n}:前置节点等级 ≥ n 才可购; # 2026-10-13 用户推翻裁撤案随批重立,现役表零配置=空转零行为影响) - var req_lv: Dictionary = row.get("requires_level", {}) as Dictionary + var req_lv: Dictionary[Variant, Variant] = row.get("requires_level", {}) as Dictionary for req_id in req_lv.keys(): if save.tree_level(String(req_id)) < int(req_lv[req_id]): return false @@ -368,7 +368,7 @@ func tree_can_buy(node_id: String) -> bool: func tree_buy(node_id: String) -> bool: if node_id == "ks_refresh": return buy_attune_refresh() - var row: Dictionary = db.tree_node(node_id) + var row: Dictionary[Variant, Variant] = db.tree_node(node_id) if row.is_empty() or not tree_can_buy(node_id): return false var price := int(row.get("price", 0)) @@ -393,7 +393,7 @@ func tree_toggle_fork(group: String, side: String) -> bool: # Fork 节点购买入口(一节点双态——购=解锁开关并置默认 A 态;重复购买由 cap 经 can_buy 拦截) func tree_buy_fork(node_id: String) -> bool: - var row: Dictionary = db.tree_node(node_id) + var row: Dictionary[Variant, Variant] = db.tree_node(node_id) if row.is_empty() or String(row.get("kind", "")) != "fork": return tree_buy(node_id) if not tree_can_buy(node_id): @@ -418,15 +418,15 @@ func tree_stat_flat(key: String) -> int: func tree_rule_keys() -> Array: - var out: Array = [] - var sides: Dictionary = save.fork_sides() + var out: Array[Variant] = [] + var sides: Dictionary[Variant, Variant] = save.fork_sides() for row in db.tree_rows(): if String(row.get("kind", "")) != "fork": continue var active := String(sides.get(String(row.get("fork_group", "")), "")) if active == "": continue # 未解锁组零规则输出 - var fx: Dictionary = row.get("effects_" + active, {}) as Dictionary + var fx: Dictionary[Variant, Variant] = row.get("effects_" + active, {}) as Dictionary for k in fx.keys(): if not out.has(String(k)): out.append(String(k)) @@ -435,7 +435,7 @@ func tree_rule_keys() -> Array: # 营地树面投影(CampOverlay _rebuild_tree 数据源——行板只读;账面归本类、拼句归视图同款分工) func tree_camp_rows() -> Array: - var out: Array = [] + var out: Array[Variant] = [] for row in db.tree_rows(): if String(row.get("kind", "")) == "fork": continue # Fork 行由 fork_groups 段承载 @@ -457,19 +457,19 @@ func tree_camp_rows() -> Array: # 行板门槛文案(R1 机制位呈现——requires_level 未达标项拼「需 {前置名} Lv{n}」;达标/未配置=空串。 # 只呈现未达标项:达标后锁标消失,不做全量前置罗列) func tree_gate_text(node_id: String) -> String: - var row: Dictionary = db.tree_node(node_id) - var rl: Dictionary = row.get("requires_level", {}) as Dictionary + var row: Dictionary[Variant, Variant] = db.tree_node(node_id) + var rl: Dictionary[Variant, Variant] = row.get("requires_level", {}) as Dictionary var parts: Array[String] = [] for req_id in rl.keys(): if save.tree_level(String(req_id)) < int(rl[req_id]): - var req_row: Dictionary = db.tree_node(String(req_id)) + var req_row: Dictionary[Variant, Variant] = db.tree_node(String(req_id)) parts.append("需 %s Lv%d" % [String(req_row.get("display_name", req_id)), int(rl[req_id])]) return "、".join(parts) func tree_fork_groups() -> Array: - var out: Array = [] - var sides: Dictionary = save.fork_sides() + var out: Array[Variant] = [] + var sides: Dictionary[Variant, Variant] = save.fork_sides() for row in db.tree_rows(): if String(row.get("kind", "")) != "fork": continue @@ -496,7 +496,7 @@ func _check_easter_egg() -> void: if save.tree_level("easter_ley") > 0: return for nid in ["sp_hp", "sp_hit"]: - var row: Dictionary = db.tree_node(nid) + var row: Dictionary[Variant, Variant] = db.tree_node(nid) if row.is_empty() or save.tree_level(nid) < int(row.get("cap", 1)): return for i in range(1, 4): @@ -517,9 +517,9 @@ func _check_easter_egg() -> void: # 格 1=普通格/格 2=优秀格/格 3=稀有格递增向下兼容;槽位来源=树同调位节点点亮数,树接口接缝现役)。 # 装配关系写存档持久。 func toggle_attunement(item_id: String) -> bool: - var equipped: Array = save.attunement_equipped() + var equipped: Array[Variant] = save.attunement_equipped() if not equipped.has(item_id): - var arow: Dictionary = db.treasure_item_row(item_id) + var arow: Dictionary[Variant, Variant] = db.treasure_item_row(item_id) if arow.is_empty() or String(arow.get("form", "")) == "": return false # 非同调行(过渡/退役行)禁装配 if not save.item_unlocked(item_id): @@ -538,7 +538,7 @@ func toggle_attunement(item_id: String) -> bool: # 营地装配面投影(账面归本类、拼句归 CampOverlay——_camp_data 同款分工) func attune_camp_status() -> Dictionary: - var equipped: Array = save.attunement_equipped() + var equipped: Array[Variant] = save.attunement_equipped() var items: Array[Dictionary] = [] for row in db.treasure_attunement_rows(): var iid := String(row.get("item_id", "")) @@ -552,7 +552,7 @@ func attune_camp_status() -> Dictionary: "unlocked": save.item_unlocked(iid), }) var lvl := save.attune_refresh_level() - var next_row: Dictionary = db.meta_attune_refresh_row(lvl + 1) + var next_row: Dictionary[Variant, Variant] = db.meta_attune_refresh_row(lvl + 1) return { "slots": Attunement.slots(db, save), "equipped": equipped, @@ -573,7 +573,7 @@ func attune_refresh_charges() -> int: # Y 升级购买(直购价 = 下一级行表值 keystone_cost;已满级/余额不足 false 零副作用——buy_forge 同款) func buy_attune_refresh() -> bool: - var next_row: Dictionary = db.meta_attune_refresh_row(save.attune_refresh_level() + 1) + var next_row: Dictionary[Variant, Variant] = db.meta_attune_refresh_row(save.attune_refresh_level() + 1) if next_row.is_empty(): return false if not save.spend_keystone(int(next_row.get("keystone_cost", 0))): diff --git a/game/core/minesweeper_rules.gd b/game/core/minesweeper_rules.gd index 475ae154..c527c41a 100644 --- a/game/core/minesweeper_rules.gd +++ b/game/core/minesweeper_rules.gd @@ -84,7 +84,7 @@ func clear_map_cleared() -> bool: # 移除集=未揭示、无旗、是雷——旗判定基线零回溯;雷格不入清图分母/连锁/C_dig 模拟,判定逻辑零改动) ---- func remove_mines_in(cells: Array) -> Array: - var out: Array = [] + var out: Array[Variant] = [] for c in cells: var cell: Vector2i = c if not _board.in_bounds(cell) or is_revealed(cell) or has_flag(cell) or not _board.is_mine(cell): @@ -188,11 +188,11 @@ func _install_invariant_faults(b: BoardData) -> Array: # 初始展开规范序:触发格 BFS、8 邻行主序(≡ 棋盘生成 F1 R₀ 算子;禁外扩环、禁只开 3×3) func _initial_expansion_order(b: BoardData) -> Array: - var order: Array = b.spawn_cells().duplicate() + var order: Array[Variant] = b.spawn_cells().duplicate() var seen := {} for c in order: seen[c] = true - var queue: Array = order.duplicate() + var queue: Array[Variant] = order.duplicate() while not queue.is_empty(): var c: Vector2i = queue.pop_front() if b.is_safe(c) and b.digit_of(c) == 0: @@ -240,7 +240,7 @@ func _classify(intent: Dictionary) -> int: var c: Vector2i = intent.get("cell", Vector2i(-9, -9)) if _board_face_dig(c) == "" and not _board.is_mine(c): # 模拟 Chain(O, c) 后清图判定——挖旗格同口径:本格旗视为已隐式退役(其余旗照挡) - var sim_flags: Dictionary = _flags.duplicate() + var sim_flags: Dictionary[Variant, Variant] = _flags.duplicate() sim_flags.erase(c) var chain := _chain_closure(c, sim_flags) # 与真实执行同口径——空旗集捷径会在旗挡连锁时误判 C_dig(排雷玩法:真模拟) var remaining := 0 @@ -279,7 +279,7 @@ func _do_dig(c: Vector2i) -> Dictionary: # 计算(旗格为 0 时连锁照常外扩);request_dig 被拒则旗原地保留(事务性),受理后该旗轮末不判 # (发射面基线 = 在场旗——标中/标空兑付让位于挖掘本体结局,四情况②④)。 var was_flagged := has_flag(c) - var eff_flags: Dictionary = _flags + var eff_flags: Dictionary[Variant, Variant] = _flags if was_flagged: eff_flags = _flags.duplicate() eff_flags.erase(c) @@ -297,7 +297,7 @@ func _do_dig(c: Vector2i) -> Dictionary: # 事务内执行:首格 + 连锁新格揭示(BFS 规范序,零特判);事件 = O′ ∖ O if was_flagged: _flags.erase(c) - var reveal_order: Array = _chain_reveal_order(c, eff_flags) + var reveal_order: Array[Variant] = _chain_reveal_order(c, eff_flags) for rc in reveal_order: if _revealed.has(rc): continue # 闭包可穿过已开区——已开格零事件(cell_revealed × |O′ ∖ O|) @@ -361,7 +361,7 @@ func fx_reveal(c: Vector2i) -> bool: # 连锁闭包:μX.[O∪{c} ⊆ X ∧ ∀x∈X:(x∈S ∧ num(x)=0 ⇒ N₈(x)∩S∖G ⊆ X)]——连锁不越旗 func _chain_closure(start: Vector2i, flag_set: Dictionary) -> Dictionary: var chain := {start: true} - var queue: Array = [start] + var queue: Array[Variant] = [start] while not queue.is_empty(): var c: Vector2i = queue.pop_front() if _board.is_safe(c) and not flag_set.has(c) and _board.digit_of(c) == 0: @@ -374,9 +374,9 @@ func _chain_closure(start: Vector2i, flag_set: Dictionary) -> Dictionary: # 揭示规范序:首格 + 触发格 BFS、8 邻行主序 func _chain_reveal_order(start: Vector2i, flag_set: Dictionary) -> Array: - var order: Array = [start] + var order: Array[Variant] = [start] var seen := {start: true} - var queue: Array = [start] + var queue: Array[Variant] = [start] while not queue.is_empty(): var c: Vector2i = queue.pop_front() if _board.is_safe(c) and not flag_set.has(c) and _board.digit_of(c) == 0: @@ -444,7 +444,7 @@ func compute_move_path(target: Vector2i) -> Array: return [] # BFS(8 邻行主序)——路径 = 合法性载体 + HUD框架预览数据源 var prev := {_hero: _hero} - var queue: Array = [_hero] + var queue: Array[Variant] = [_hero] while not queue.is_empty(): var c: Vector2i = queue.pop_front() if c == target: @@ -478,7 +478,7 @@ func _do_stop() -> Dictionary: func _emit_flags_batch() -> void: # 判定基线 = 轮次结束事务开始时旗位;行主序枚举(确定性——排序键单点 BoardData.row_major_key, # 2026-10-02 修复批);被拔的不判;诚实重复上报(去重归 战斗结算) - var cells: Array = _flags.keys() + var cells: Array[Variant] = _flags.keys() cells.sort_custom(func(a, b): return BoardData.row_major_key(a) < BoardData.row_major_key(b)) for c in cells: flag_judged.emit({"cell": c, "hit": _board.is_mine(c)}) @@ -486,7 +486,7 @@ func _emit_flags_batch() -> void: func _emit_round_end_flags_and_payload() -> void: _emit_flags_batch() - var payload: Dictionary = action_economy.last_round_end # 逐字段取自 行动经济(读取权 = 接口契约) + var payload: Dictionary[Variant, Variant] = action_economy.last_round_end # 逐字段取自 行动经济(读取权 = 接口契约) round_end_board.emit({"path_tag": payload.get("path_tag", ""), "continuation_hint": payload.get("continuation_hint", "")}) diff --git a/game/core/reward_economy.gd b/game/core/reward_economy.gd index 60dc29db..29def278 100644 --- a/game/core/reward_economy.gd +++ b/game/core/reward_economy.gd @@ -72,8 +72,8 @@ func roll_board(eligible: Array) -> bool: for h in _fx.dispatch("reward_roll", {"polarity": "positive", "cell_count": list.size()}): if String(h.get("kind", "")) == "bias_rewards": luck_bias += int(h.get("value", 0)) - var cell_rows: Array = _biased_cell_rows(luck_bias) # cell 池正向行 weight>0(偏运硬币装配时稀有正向 kinds 权重 +N——池结构零触碰) - var clear_rows: Array = _pool_rows("clear_default") + var cell_rows: Array[Variant] = _biased_cell_rows(luck_bias) # cell 池正向行 weight>0(偏运硬币装配时稀有正向 kinds 权重 +N——池结构零触碰) + var clear_rows: Array[Variant] = _pool_rows("clear_default") # ① 身份批:每非封锁格恰一类型身份(雷安同池——同一行集同一权重分布) var draft := {} for e in list: @@ -82,10 +82,10 @@ func roll_board(eligible: Array) -> bool: var clear_rid := weighted_pick(clear_rows, stream.randi_range(0, _pool_weight(clear_rows) - 1)) # ③ 区间行 power roll(身份定稿后逐格恰一追加取号,定值行零取号) for cell in draft.keys(): - var row: Dictionary = _row_by_id(draft[cell]) + var row: Dictionary[Variant, Variant] = _row_by_id(draft[cell]) if row.has("power_min"): _power_roll[cell] = stream.randi_range(int(row.power_min), int(row.power_max)) - var clear_row: Dictionary = _row_by_id(clear_rid) + var clear_row: Dictionary[Variant, Variant] = _row_by_id(clear_rid) if clear_row.has("power_min"): _clear_power = stream.randi_range(int(clear_row.power_min), int(clear_row.power_max)) _mapping = draft @@ -114,7 +114,7 @@ func query_identity(cell: Vector2i): if rid == "": _diag("query_identity 清单外坐标(E19——未分配非错误态;封锁格不入清单)") return null - var row: Dictionary = _row_by_id(rid) + var row: Dictionary[Variant, Variant] = _row_by_id(rid) return {"reward_id": rid, "cell_type": String(row.cell_type), "tier": int(row.tier)} @@ -155,7 +155,7 @@ func grant_clear(): if _clear_granted: _diag("grant_clear 同批次重调(E18 幂等兜底)") return _clear_snapshot - var row: Dictionary = _row_by_id(_clear_identity) + var row: Dictionary[Variant, Variant] = _row_by_id(_clear_identity) _clear_snapshot = _snapshot(row, _clear_power) _clear_granted = true return _clear_snapshot @@ -206,13 +206,13 @@ const SKEW_UP_KINDS := ["attack", "blast", "pierce", "magic"] # 基座换源(首发批 2026-10-04):先经图级偏斜投影再叠件级加法——合成序锚=图级先、件级后 #(环境事实先定型、玩家构筑叠其上;两投影均为池内权重面,雷安同池与取号次数零变化) func _biased_cell_rows(bias: int) -> Array: - var base: Array = _skewed_cell_rows() + var base: Array[Variant] = _skewed_cell_rows() if bias <= 0: return base - var out: Array = [] + var out: Array[Variant] = [] for row in base: if LUCK_BIAS_KINDS.has(String(row.get("cell_type", ""))): - var biased: Dictionary = row.duplicate() + var biased: Dictionary[Variant, Variant] = row.duplicate() biased["weight"] = int(row.weight) + bias out.append(biased) else: @@ -226,9 +226,9 @@ func _biased_cell_rows(bias: int) -> Array: func _skewed_cell_rows() -> Array: if board_skew <= 0: return _rollable_rows() - var base: Array = _rollable_rows() - var down: Array = [] - var up: Array = [] + var base: Array[Variant] = _rollable_rows() + var down: Array[Variant] = [] + var up: Array[Variant] = [] for row in base: var k := String(row.get("cell_type", "")) if SKEW_DOWN_KINDS.has(k): @@ -244,7 +244,7 @@ func _skewed_cell_rows() -> Array: var gains := _largest_remainder_split(up, drops_total(drops), _pool_weight(up), drops_total(drops)) var di := 0 var ui := 0 - var out: Array = [] + var out: Array[Variant] = [] for row in base: var k := String(row.get("cell_type", "")) var w := int(row.weight) @@ -255,7 +255,7 @@ func _skewed_cell_rows() -> Array: w += gains[ui] ui += 1 assert(w > 0, "图级偏斜投影产生非正权重(池结构变更须走另批重审)") - var skewed: Dictionary = row.duplicate() + var skewed: Dictionary[Variant, Variant] = row.duplicate() skewed["weight"] = w out.append(skewed) assert(_pool_weight(out) == _pool_weight(base), "图级偏斜守恒破(偏斜后合计须等于原池)") diff --git a/game/core/run_ledger.gd b/game/core/run_ledger.gd index 12480a22..8a0a3789 100644 --- a/game/core/run_ledger.gd +++ b/game/core/run_ledger.gd @@ -6,11 +6,11 @@ extends RefCounted # 下传并装配 encounter;战斗侧(BattleOrchestrator)经 encounter["ledger"] 拿本类引用, # 只走下方 read/append 原语——战末 export/开局 seed 时点语义不变(确定性设计原样保留, # 只把「函数签名上不可见的共享可变引用」换成签名可见的显式类)。 -var attune_charges: Dictionary = {} # 同调主动件充能账(迭代 11 第①批 T6——use_treasure 扣减;断点随局回档) -var vision_counter: Dictionary = {} # 真视类使用计数(迭代 22 秘宝批 T3——冒险开始置 {"used":0}; +var attune_charges: Dictionary[Variant, Variant] = {} # 同调主动件充能账(迭代 11 第①批 T6——use_treasure 扣减;断点随局回档) +var vision_counter: Dictionary[Variant, Variant] = {} # 真视类使用计数(迭代 22 秘宝批 T3——冒险开始置 {"used":0}; # 缺省空 = 调试/单场战斗不计——原 enc 缺键守卫语义) -var adventure_counters: Dictionary = {} # adventure 域效果计数器(迭代 20 T6——rebuild 回灌/战末导出回写目标) -var bonus: Dictionary = {} # 统一冒险加算区(冒险开始置四轴零账——商店购买/三选一属性件/战末 bonus_delta 汇入) +var adventure_counters: Dictionary[Variant, Variant] = {} # adventure 域效果计数器(迭代 20 T6——rebuild 回灌/战末导出回写目标) +var bonus: Dictionary[Variant, Variant] = {} # 统一冒险加算区(冒险开始置四轴零账——商店购买/三选一属性件/战末 bonus_delta 汇入) # ---- 战斗侧原语(裁点②甲:战斗侧只拿 read/append 两族;宿主侧冒险/关卡域直读字段域) ---- diff --git a/game/core/save_manager.gd b/game/core/save_manager.gd index a26635a2..d2dbcb3e 100644 --- a/game/core/save_manager.gd +++ b/game/core/save_manager.gd @@ -118,7 +118,7 @@ func _migrate_v3_v4(data: Dictionary) -> Dictionary: # 补齐;迭代 21 T5 换代:原「三体系迁移」映射体随 migrate_tree 一并删除——开发阶段破坏性废弃 # 〔Q3 用户裁〕,换代前结构由 _normalize 的 tree_struct 标记检测废弃重置,本函数只剩缺键补齐)。 func _migrate_v4_v5(data: Dictionary) -> Dictionary: - var meta: Dictionary = data.get("meta", {}) as Dictionary + var meta: Dictionary[Variant, Variant] = data.get("meta", {}) as Dictionary if not meta.has("tree_nodes"): meta["tree_nodes"] = {} if not meta.has("fork_sides"): @@ -330,7 +330,7 @@ func tree_level(node_id: String) -> int: func set_tree_level(node_id: String, level: int) -> void: - var tn: Dictionary = _data["meta"].get("tree_nodes", {}) as Dictionary + var tn: Dictionary[Variant, Variant] = _data["meta"].get("tree_nodes", {}) as Dictionary tn[node_id] = maxi(0, level) _data["meta"]["tree_nodes"] = tn @@ -344,7 +344,7 @@ func fork_side(group: String) -> String: func set_fork_side(group: String, side: String) -> void: - var fs: Dictionary = _data["meta"].get("fork_sides", {}) as Dictionary + var fs: Dictionary[Variant, Variant] = _data["meta"].get("fork_sides", {}) as Dictionary fs[group] = side _data["meta"]["fork_sides"] = fs @@ -363,13 +363,13 @@ func forge_routes_snapshot() -> Dictionary: func forge_route_level(route_id: String) -> int: - var lv: Dictionary = _forge_domain().get("levels", {}) as Dictionary + var lv: Dictionary[Variant, Variant] = _forge_domain().get("levels", {}) as Dictionary return int(lv.get(route_id, 0)) func set_forge_route_level(route_id: String, level: int) -> void: - var dom: Dictionary = _forge_domain() - var lv: Dictionary = dom.get("levels", {}) as Dictionary + var dom: Dictionary[Variant, Variant] = _forge_domain() + var lv: Dictionary[Variant, Variant] = dom.get("levels", {}) as Dictionary lv[route_id] = maxi(0, level) dom["levels"] = lv _data["meta"]["forge_routes"] = dom @@ -380,8 +380,8 @@ func forge_unlocked_purchases() -> Array: func add_forge_unlock(route_id: String) -> void: - var dom: Dictionary = _forge_domain() - var un: Array = dom.get("unlocked", []) as Array + var dom: Dictionary[Variant, Variant] = _forge_domain() + var un: Array[Variant] = dom.get("unlocked", []) as Array if not un.has(route_id): un.append(route_id) dom["unlocked"] = un @@ -393,7 +393,7 @@ func forge_weapon() -> String: func set_forge_weapon(route_id: String) -> void: - var dom: Dictionary = _forge_domain() + var dom: Dictionary[Variant, Variant] = _forge_domain() dom["weapon"] = route_id _data["meta"]["forge_routes"] = dom @@ -403,7 +403,7 @@ func forge_armor() -> String: func set_forge_armor(route_id: String) -> void: - var dom: Dictionary = _forge_domain() + var dom: Dictionary[Variant, Variant] = _forge_domain() dom["armor"] = route_id _data["meta"]["forge_routes"] = dom @@ -415,7 +415,7 @@ func attunement_equipped() -> Array: func set_attunement_equipped(item_ids: Array) -> void: - var out: Array = [] + var out: Array[Variant] = [] for v in item_ids: out.append(String(v)) _data["meta"]["attunement_equipped"] = out @@ -434,7 +434,7 @@ func set_attune_refresh_level(n: int) -> void: # clear_count(level_index) 逐关访问器零改动复用) func attunement_counters() -> Dictionary: var total := 0 - var counts: Dictionary = _data["meta"]["level_clear_counts"] + 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()} @@ -462,9 +462,9 @@ func apply_adventure_summary(summary: Dictionary, boss_drops: Array = []) -> voi _data["meta"]["level_clear_counts"][key] = int(_data["meta"]["level_clear_counts"].get(key, 0)) + 1 _data["meta"]["boss_first_kill"][key] = true _data["meta"]["boss_kill_count"] = boss_kill_count() + cleared # 迭代 11 T2——Boss 恒唯一终点:levels_cleared 即本次冒险 Boss 击杀数(含重复) - var entry: Dictionary = summary.duplicate(true) + var entry: Dictionary[Variant, Variant] = summary.duplicate(true) entry["boss_drops"] = boss_drops.duplicate(true) - var arr: Array = _data["meta"]["recent_summaries"] + var arr: Array[Variant] = _data["meta"]["recent_summaries"] arr.push_front(entry) while arr.size() > SUMMARY_CAP: arr.pop_back() @@ -509,10 +509,10 @@ func _corrupt_fallback(reason: String) -> bool: # tree_nodes/fork_sides 清空、钥石余额等他域零触碰(V16 断言面)。 func _normalize(data: Dictionary) -> Dictionary[String, Variant]: var merged := _default_data() - var def_meta: Dictionary = merged["meta"] # 补齐源引用(先于 data 覆盖取出——data 自带 meta 时 merged["meta"] 被换走,此引用仍指默认值) + var def_meta: Dictionary[Variant, Variant] = merged["meta"] # 补齐源引用(先于 data 覆盖取出——data 自带 meta 时 merged["meta"] 被换走,此引用仍指默认值) for key in data: merged[key] = data[key] - var meta: Dictionary = merged.get("meta", {}) + var meta: Dictionary[Variant, Variant] = merged.get("meta", {}) if int(meta.get("tree_struct", 0)) < TREE_STRUCT_VERSION: meta["tree_nodes"] = {} meta["fork_sides"] = {} diff --git a/game/core/settlement/combat_segment.gd b/game/core/settlement/combat_segment.gd index 6e9da4a8..ff80ef2d 100644 --- a/game/core/settlement/combat_segment.gd +++ b/game/core/settlement/combat_segment.gd @@ -61,7 +61,7 @@ func run(ri: int) -> void: if int(oc._enemy_system.hero_unit().hp) <= 0: break # hero hp=0 立即短路(死尸挨打行是归因噪音——用户拍板) var skill_id := oc._enemy_system.pending_action(u) - var srow: Dictionary = oc.db.skill_row(skill_id) + var srow: Dictionary[Variant, Variant] = oc.db.skill_row(skill_id) if srow.is_empty(): oc._fatal("敌实例待发动作悬空: %s" % skill_id) continue diff --git a/game/core/settlement/grant_segment.gd b/game/core/settlement/grant_segment.gd index bb1911b9..c5cfda5a 100644 --- a/game/core/settlement/grant_segment.gd +++ b/game/core/settlement/grant_segment.gd @@ -73,7 +73,7 @@ func run(ri: int) -> void: if String(h.get("kind", "")) == "grant_material": _apply_material_hit(h, evt.cell, ri) # 奥术矢(魔能共鸣——每战首次开安全格追加魔法矢;任意真族单次生效) - var spell_hits: Array = oc._hits_of(dr_hits, "spawn_spell") + var spell_hits: Array[Variant] = oc._hits_of(dr_hits, "spawn_spell") if not spell_hits.is_empty() and not oc._feat_first_dig_used: oc._feat_first_dig_used = true var spell_t := oc._damage.open_ticket(DamageController.KIND_BOARD_TYPED, "feat_first_dig_spell", @@ -104,7 +104,7 @@ func run(ri: int) -> void: else: mine_reduce += int(h.get("value", 0)) # 背水——定值减免 "heal": - var hu_heal: Dictionary = oc._enemy_system.hero_unit() + var hu_heal: Dictionary[Variant, Variant] = oc._enemy_system.hero_unit() hu_heal["hp"] = mini(int(hu_heal.get("hp", 0)) + int(h.get("value", 0)), maxi(1, int(hu_heal.get("max_hp", 1)))) "bonus_delta": var axis := String(h.get("axis", "")) @@ -133,9 +133,9 @@ func run(ri: int) -> void: # 雷格恒无变异——布置段排除,双层叠罚不存在;插旗格直挖照吃变异伤害——地形后果同 mine_hit 口径, # 非奖励不作废;吞噬流资源兑付〔荆棘编织/地脉补偿〕中伤害形态行同样经案C闸暂存,非伤害形态照常当场) for evt in oc._facts_reveals: - var vids: Array = evt.get("variant_ids", []) + var vids: Array[Variant] = evt.get("variant_ids", []) for vid in vids: - var vrow: Dictionary = oc.db.variation.row_by_id(String(vid)) + var vrow: Dictionary[Variant, Variant] = oc.db.variation.row_by_id(String(vid)) if String(vrow.get("effect", "")) != "flat_damage": continue # 触发词表按 effect 路由:flat_damage=on_open 兑现;hide_number/on_open 外组合 = 数据行新词表未接线(显式忽略面) # 地形变异伤害单据(伤害管线单据化批 2026-09-24)——无类型必中,下限保底在控制器管线内 @@ -177,7 +177,7 @@ func run(ri: int) -> void: fs_misses += 1 var fs_fx := oc._fx_emit("flag_settle", {"flags_hit_all": fs_hits > 0 and fs_misses == 0, "flags_count": fs_hits, "round": ri}) - var banner_hits: Array = [] + var banner_hits: Array[Variant] = [] for h in fs_fx: if String(h.get("kind", "")) == "open_ticket" and String(h.get("channel", "")) == "attack": banner_hits.append(h) @@ -269,12 +269,12 @@ func _judge_flag(evt: Dictionary, ri: int) -> void: if oc._fx_bonus_delta.has(axis_f): oc._fx_bonus_delta[axis_f] += int(h.get("delta", 0)) "remove_mines": - var removed: Array = oc.minesweeper.remove_mines_in([cell]) + var removed: Array[Variant] = oc.minesweeper.remove_mines_in([cell]) if not removed.is_empty(): oc._emit_row("mine_neutralized", "fx_seal_smith", cell, ri, "封印匠·危险格中立移除", 0, {}, "") "preview_area": var board_p: BoardData = oc.minesweeper.current_board() - var types: Array = [] + var types: Array[Variant] = [] for n in board_p.neighbors8(cell): var n_snap = oc._reward_economy.query_full_snapshot(n) if n_snap is Dictionary and not types.has(String((n_snap as Dictionary).get("cell_type", ""))): diff --git a/game/core/status_container.gd b/game/core/status_container.gd index 5e9159e9..685a50c4 100644 --- a/game/core/status_container.gd +++ b/game/core/status_container.gd @@ -33,7 +33,7 @@ func apply_effect(effect_id: String, source: Dictionary, duration_override: int if not _open: push_warning("[增益状态] 域闭授予(EC1)") return false - var row: Dictionary = db.status_effect_row(effect_id) + var row: Dictionary[Variant, Variant] = db.status_effect_row(effect_id) if row.is_empty(): push_warning("[增益状态] 未知 effect_id(EC2): %s" % effect_id) return false @@ -88,7 +88,7 @@ func _new_instance(effect_id: String, layers: int, dur: int, source: Dictionary, func query_active(side: String) -> Array: var out: Array[Dictionary] = [] for inst in _instances: - var row: Dictionary = db.status_effect_row(inst.effect_id) + var row: Dictionary[Variant, Variant] = db.status_effect_row(inst.effect_id) if row.is_empty(): continue if String(row.target_side) != side: diff --git a/game/core/treasure_items.gd b/game/core/treasure_items.gd index 9542f69a..f01bff30 100644 --- a/game/core/treasure_items.gd +++ b/game/core/treasure_items.gd @@ -17,7 +17,7 @@ class_name TreasureItems extends RefCounted -var _items: Array = [] # [{handle, item_id, used}]——受理序;Used 保留只读 +var _items: Array[Variant] = [] # [{handle, item_id, used}]——受理序;Used 保留只读 var _handle_seq := 0 # 受理序句柄(单调递增——零随机) var _reveal_cells := {} # 真视集:Vector2i → true(惩罚格标记——插旗仍走既有 flag 路径) var _preview_cells := {} # 预读集:Vector2i → true(奖励身份徽章呈现挂点) @@ -52,9 +52,9 @@ func add_item(item_id: String, source_class: String, cell: Vector2i, round_index # 枚举序=受理序;Used 保留只读;逐件含 usable 板级零信息预判(调用方喂板面回调) func query_inventory(board_has_preview_info: Callable) -> Array: - var out: Array = [] + var out: Array[Variant] = [] for rec in _items: - var row: Dictionary = rec.duplicate() + var row: Dictionary[Variant, Variant] = rec.duplicate() row["usable"] = _usable_now(String(rec.item_id), board_has_preview_info) out.append(row) return out @@ -92,10 +92,10 @@ func discard_item(handle: int) -> bool: # 真视恒可用(零雷窗口=信息非空);AP 类恒可用(组合判据取 OR——任一效果有信息即可用); # 纯预读行 = 板上还有未开格才可用。(积点件不入使用面——反哺批⑤自动兑付无玩家入口) func _usable_now(item_id: String, board_has_preview_info: Callable) -> bool: - var row: Dictionary = _item_row(item_id) + var row: Dictionary[Variant, Variant] = _item_row(item_id) if row.is_empty(): return false - var kinds: Array = row.get("effect_kinds", []) + var kinds: Array[Variant] = row.get("effect_kinds", []) if kinds.has("mine_reveal") or kinds.has("ap_grant"): return true return bool(board_has_preview_info.call()) if board_has_preview_info.is_valid() else true @@ -127,11 +127,11 @@ func validate_use(handle: int, target: Vector2i, board_size: Vector2i, cell_unre return _reject("句柄不存在") if bool(rec.used): return _reject("道具已使用(Used 不可逆)") - var row: Dictionary = _item_row(String(rec.item_id)) + var row: Dictionary[Variant, Variant] = _item_row(String(rec.item_id)) if row.is_empty() or bool(row.get("retired", false)) or bool(row.get("deferred", false)): return _reject("道具未激活") - var kinds: Array = row.get("effect_kinds", []) - var resolved: Array = [] + var kinds: Array[Variant] = row.get("effect_kinds", []) + var resolved: Array[Variant] = [] if kinds.has("mine_reveal") or kinds.has("reward_preview") or kinds.has("reveal_tiles") or kinds.has("safe_dig"): # 空间效果(迭代 22 秘宝批 T3 扩面——真视 reveal_tiles/天眼复合 safe_dig 入空间解析; # 真视窄域护栏 REVEAL_KINDS 同守): @@ -206,7 +206,7 @@ func consume(handle: int) -> Dictionary: # 随图消亡——停点处无战斗在飞,天然空集)。 func snapshot_inventory() -> Array: - var out: Array = [] + var out: Array[Variant] = [] for rec in _items: var cell: Vector2i = rec.get("origin_cell", Vector2i(-1, -1)) out.append({ @@ -224,7 +224,7 @@ func restore_inventory(items: Array) -> void: _handle_seq = 0 for row in items: _handle_seq += 1 - var cell_arr: Array = row.get("origin_cell", [-1, -1]) + var cell_arr: Array[Variant] = row.get("origin_cell", [-1, -1]) _items.append({ "handle": _handle_seq, "item_id": String(row.get("item_id", "")), diff --git a/game/core/unit_container.gd b/game/core/unit_container.gd index b4258fa9..97a1daec 100644 --- a/game/core/unit_container.gd +++ b/game/core/unit_container.gd @@ -51,7 +51,7 @@ func spawn(level_index: int, node_depth: int, hero_hp: int, unit_count_overrides push_warning("[敌人系统] Alive 相位重复 spawn(相位守卫)") return false # 行集数组口径(2026-10-07 行集多行批——resolver 返回该坐标全部行、文件序) - var rowset: Array = db.resolve_enemy_rowset(level_index, node_depth) + var rowset: Array[Variant] = db.resolve_enemy_rowset(level_index, node_depth) if rowset.is_empty(): push_error("[敌人系统] 遭遇行解析失败/越域 (%d,%d)" % [level_index, node_depth]) assert(false, "遭遇行悬空(R1 前置守卫)") @@ -65,7 +65,7 @@ func spawn(level_index: int, node_depth: int, hero_hp: int, unit_count_overrides # (恢复点/治疗包的满血守卫与治疗上限随之失效——切片登记接口语义澄清) _hero = {"hp": hero_hp, "max_hp": eff_max_hp, "atk": eff_atk, "is_hero": true, "alive": true, "display_name": "探险者", "enemy_id": "hero", "armor_type": hero_armor_type if hero_armor_type != "" else db.forge_default_armor_type(), "attack_type": hero_attack_type if hero_attack_type != "" else db.forge_default_attack_type()} # 护甲/攻击类型轴快照(迭代 23 T2——hero.tres 两轴删除后改读注入值,空串回退锻造工坊配置默认值 normal/light=行为连续;甲效矩阵消费面) _enemies.clear() # 难度曲线与Boss DifficultyTable 修饰(2026-09-12 结构位兑现):施加于行值、行值快照之前——MVP 空表 = 恒 1.0 无行为(AC-10);混编行集内全部敌种同乘((L,D) 精确匹配一次取值——F9 同乘不改变异,敌人系统 v0.15) - var mod: Dictionary = db.resolve_difficulty_modifiers(level_index, node_depth) + var mod: Dictionary[Variant, Variant] = db.resolve_difficulty_modifiers(level_index, node_depth) var hp_mult: float = float(mod.get("hp_mult", 1.0)) var atk_mult: float = float(mod.get("atk_mult", 1.0)) # 逐行数量定值(敌人系统 零随机纪律——骰定在 战斗结算 遭遇骰层按行完成,本层收确定值; @@ -73,7 +73,7 @@ func spawn(level_index: int, node_depth: int, hero_hp: int, unit_count_overrides var per_row_counts: Array[int] = [] var total_instances := 0 for row_i in range(rowset.size()): - var row_i_dict: Dictionary = rowset[row_i] + var row_i_dict: Dictionary[Variant, Variant] = rowset[row_i] var ovr: int = int(unit_count_overrides[row_i]) if row_i < unit_count_overrides.size() else -1 var count: int = ovr if ovr >= 1 else int(row_i_dict.unit_count) if count < EnemyTable.UNIT_COUNT_MIN or count > EnemyTable.UNIT_COUNT_MAX: @@ -87,7 +87,7 @@ func spawn(level_index: int, node_depth: int, hero_hp: int, unit_count_overrides return false var handle := 0 for row_i in range(rowset.size()): - var row: Dictionary = rowset[row_i] + var row: Dictionary[Variant, Variant] = rowset[row_i] var eovr: int = int(enemy_atk_overrides[row_i]) if row_i < enemy_atk_overrides.size() else -1 for i in range(per_row_counts[row_i]): var eff_row_atk: int = eovr if eovr >= 0 else int(row.atk) @@ -174,7 +174,7 @@ func special_skills(u: Dictionary) -> Array: for e in u.pool: if _entry_locked(e): continue - var srow: Dictionary = db.skill_row(String(e.skill_id)) + var srow: Dictionary[Variant, Variant] = db.skill_row(String(e.skill_id)) if not srow.is_empty() and String(srow.type) != "basic": out.append({"skill_id": String(e.skill_id), "weight": int(e.weight)}) return out @@ -184,7 +184,7 @@ func special_skills(u: Dictionary) -> Array: # 一次过门、单向闩〔实例标记,不解锁回退〕。门=确定性谓词零随机;返回本拍新解锁的 skill_id 列表 # 供编排层出归因行〔每技恰一次——闩已置位不再返回〕) func check_skill_unlocks(u: Dictionary) -> Array: - var out: Array = [] + var out: Array[Variant] = [] if u.is_empty() or _despawned or u.get("is_hero", false) or not bool(u.get("alive", false)): return out for e in u.pool: @@ -233,7 +233,7 @@ func commit_action(u: Dictionary) -> void: if sid == "": _diag("commit_action 无待发动作(兑现序违例)") return - var srow: Dictionary = db.skill_row(sid) + var srow: Dictionary[Variant, Variant] = db.skill_row(sid) var cd: int = int(srow.get("cooldown_beats", 0)) if not srow.is_empty() else 0 for e in u.pool: if String(e.skill_id) == sid: diff --git a/game/data/schemas/enemy_table.gd b/game/data/schemas/enemy_table.gd index 899cf046..8229043b 100644 --- a/game/data/schemas/enemy_table.gd +++ b/game/data/schemas/enemy_table.gd @@ -88,7 +88,7 @@ func validate() -> Array: # 池模板(敌人技能池批——单表结构校验;跨表构成纪律在 ConfigDB): # 非空池 + 条目 {skill_id 非空, weight > 0} + 池内 skill_id 不重复。 # 普攻冷却恒 0(SkillTable schema 锁死)→ 可摇集合恒非空,"空池不可能"由此结构保证。 - var pool: Array = row.get("pool", []) + var pool: Array[Variant] = row.get("pool", []) if pool.is_empty(): errs.append({"category": "domain", "message": "%s pool 空池(禁——技能池行必带池模板)" % eid}) var pool_ids := {} @@ -96,7 +96,7 @@ func validate() -> Array: if not (entry_v is Dictionary): errs.append({"category": "domain", "message": "%s pool 条目非字典: %s" % [eid, str(entry_v)]}) continue - var entry: Dictionary = entry_v + var entry: Dictionary[Variant, Variant] = entry_v var sid: String = String(entry.get("skill_id", "")) if sid == "": errs.append({"category": "domain", "message": "%s pool 条目 skill_id 空" % eid}) diff --git a/game/data/schemas/feat_table.gd b/game/data/schemas/feat_table.gd index 1e91f85a..51c028a8 100644 --- a/game/data/schemas/feat_table.gd +++ b/game/data/schemas/feat_table.gd @@ -24,7 +24,7 @@ class_name FeatTable extends Resource -@export var rows: Array = [] # Array[Dictionary] 键 feat_id +@export var rows: Array[Variant] = [] # Array[Dictionary] 键 feat_id ## 稀有度词表(四级——与令牌池 TIERS 同名词族;令牌档 = rarity 直映射) const RARITIES := ["common", "excellent", "rare", "legendary"] @@ -37,7 +37,7 @@ const TRIGGER_KEYS := ["on", "when", "do"] func validate() -> Array: - var errs: Array = [] + var errs: Array[Variant] = [] if rows.is_empty(): return errs # 空表合法态(T1 基建先行——逐件填行) var seen := {} diff --git a/game/data/schemas/forge_table.gd b/game/data/schemas/forge_table.gd index 4106ee8a..04624214 100644 --- a/game/data/schemas/forge_table.gd +++ b/game/data/schemas/forge_table.gd @@ -62,7 +62,7 @@ func validate() -> Array: continue # 类型轴:武器=attack_type 域、甲=armor_type 域;每型恰一路线(三武器三甲各一) var axis := String(row.get("type_axis", "")) - var domain: Array = ATTACK_TYPES if kind == "weapon" else ARMOR_TYPES + var domain: Array[Variant] = ATTACK_TYPES if kind == "weapon" else ARMOR_TYPES if not domain.has(axis): errs.append({"category": "enum", "message": "%s type_axis 出域 %s: %s" % [rid, domain, axis]}) var type_key := "%s|%s" % [kind, axis] diff --git a/game/data/schemas/level_table.gd b/game/data/schemas/level_table.gd index e33554a1..d4a6f3dd 100644 --- a/game/data/schemas/level_table.gd +++ b/game/data/schemas/level_table.gd @@ -49,7 +49,7 @@ func validate() -> Array: # 模板池(关卡节点图生成 v0.4——M-3 迭代 5 T5):row.templates 非空 = 池形态,池内全模板加载期校验 # (违规致命——不只抽中的那张);空 = 旧单图形态(row.nodes 直读,向后兼容零迁移)。 var node_lists: Array[Array] = [] # [ [上下文标签, nodes 数组], ... ] - var templates: Array = row.get("templates", []) + var templates: Array[Variant] = row.get("templates", []) if templates.is_empty(): node_lists.append(["", row.get("nodes", [])]) else: @@ -115,7 +115,7 @@ func _validate_graph(L: int, nodes: Array, is_graph: bool, tag: String = "") -> var nexts: Array[Array] = [] # 每节点出边 id 数组 var id_to_idx := {} for i in nodes.size(): - var node: Dictionary = nodes[i] + var node: Dictionary[Variant, Variant] = nodes[i] var nid: int = int(node.get("node_depth", i)) if is_graph else i if is_graph and not node.has("node_depth"): errs.append({"category": "structural", "message": "level %d%s 图形态节点[%d] 缺 node_depth(全节点须编号化)" % [L, tag, i]}) @@ -190,7 +190,7 @@ func _validate_graph(L: int, nodes: Array, is_graph: bool, tag: String = "") -> errs.append({"category": "structural", "message": "level %d 节点 %d 沿连线不可达 Boss" % [L, ids[i]]}) # ⑧ U2 目检新规则(迭代 5 返工批 2026-09-29):BFS 同深度层节点数上限 5(分支宽度天花板) var layer_of := {} - var bfs: Array = [entries[0]] + var bfs: Array[Variant] = [entries[0]] layer_of[entries[0]] = 0 while not bfs.is_empty(): var cur: int = bfs.pop_front() @@ -214,7 +214,7 @@ func _reachable(start: int, adj: Array, n: int) -> Dictionary: var seen := {} if start < 0 or start >= n: return seen - var queue: Array = [start] + var queue: Array[Variant] = [start] seen[start] = true while not queue.is_empty(): var cur: int = queue.pop_front() diff --git a/game/data/schemas/meta_tree_table.gd b/game/data/schemas/meta_tree_table.gd index fb6b4e7e..77d3b5d9 100644 --- a/game/data/schemas/meta_tree_table.gd +++ b/game/data/schemas/meta_tree_table.gd @@ -27,7 +27,7 @@ class_name MetaTreeTable extends Resource -@export var rows: Array = [] # Array[Dictionary] 键 node_id +@export var rows: Array[Variant] = [] # Array[Dictionary] 键 node_id const VEINS := ["spine", "forge", "keystone", "attune"] const KINDS := ["stat", "rule", "unlock", "socket", "fork", "easter"] @@ -47,7 +47,7 @@ const FORK_GROUPS := ["fg_posture", "fg_sense", "fg_economy"] func validate() -> Array: - var errs: Array = [] + var errs: Array[Variant] = [] if rows.is_empty(): return errs # 空表合法态(建表先行——内容随换代批落) var seen := {} @@ -81,7 +81,7 @@ func validate() -> Array: var price := int(row.get("price", 0)) if price < 0 or price > 9999: errs.append({"category": "domain", "message": "%s price 越域 [0,9999]: %d" % [nid, price]}) - var fx: Dictionary = row.get("effects", {}) as Dictionary + var fx: Dictionary[Variant, Variant] = row.get("effects", {}) as Dictionary for ek in fx.keys(): if not (STAT_KEYS.has(String(ek)) or RULE_KEYS.has(String(ek))): errs.append({"category": "zero_flat", "message": "%s effects 键出域(stat 线/rule 词表封闭): %s" % [nid, String(ek)]}) @@ -100,7 +100,7 @@ func validate() -> Array: if not fx.is_empty(): errs.append({"category": "cross_field", "message": "%s fork 行主 effects 须空(效果只放 a/b 双键)" % nid}) for skey in ["effects_a", "effects_b"]: - var sd: Dictionary = row.get(skey, {}) as Dictionary + var sd: Dictionary[Variant, Variant] = row.get(skey, {}) as Dictionary if sd.is_empty(): errs.append({"category": "domain", "message": "%s %s 缺失或空(双态节点须双侧效果)" % [nid, skey]}) for k in sd.keys(): diff --git a/game/data/schemas/settlement_table.gd b/game/data/schemas/settlement_table.gd index 6c8d60c3..2769e883 100644 --- a/game/data/schemas/settlement_table.gd +++ b/game/data/schemas/settlement_table.gd @@ -83,7 +83,7 @@ func validate() -> Array: if d < 1 or d > 99 or (i > 0 and d >= int(roll_tier_discounts[i - 1])): errs.append({"category": "cross_field", "message": "roll_tier_discounts[%d] 越域/非降序(档位不设零档): %d" % [i, d]}) for atk_type in ATTACK_TYPE_KEYS: - var row: Dictionary = armor_effectiveness.get(atk_type, {}) + var row: Dictionary[Variant, Variant] = armor_effectiveness.get(atk_type, {}) for armor in ARMOR_KEYS: var pct: int = int(row.get(armor, -99)) if pct < -3 or pct > 3: diff --git a/game/data/schemas/shop_table.gd b/game/data/schemas/shop_table.gd index fd257f72..cb05058e 100644 --- a/game/data/schemas/shop_table.gd +++ b/game/data/schemas/shop_table.gd @@ -57,7 +57,7 @@ func validate() -> Array: errs.append({"category": "cross_field", "message": "gp_drops %s 违反 roll_min(%d) ≤ mean(%d) ≤ roll_max(%d)——均值恒等期望锚" % [tier, rmin, mean, rmax]}) # level_gp_bonus / price_level_bonus:level_index 连续从 1 递增 + 加值域 for family in ["level_gp_bonus", "price_level_bonus"]: - var arr: Array = get(family) + var arr: Array[Variant] = get(family) var seen := {} for row in arr: var li: int = int(row.get("level_index", 0)) diff --git a/game/data/schemas/treasure_item_table.gd b/game/data/schemas/treasure_item_table.gd index f6db6863..76bc12cc 100644 --- a/game/data/schemas/treasure_item_table.gd +++ b/game/data/schemas/treasure_item_table.gd @@ -32,7 +32,7 @@ class_name TreasureItemTable extends Resource -@export var rows: Array = [] # Array[Dictionary] 键 item_id +@export var rows: Array[Variant] = [] # Array[Dictionary] 键 item_id # 计数键封闭枚举(原 AttunementGrowthTable.COUNTER_KEYS——迭代 21 T5 谓词表退役后就地内联; # 迭代 22 T1 换代:clear_count(旧工坊按关累计渠道)与 C6 通关口径(通关=打通整局冒险)不符, @@ -85,7 +85,7 @@ const KEY_WHITELIST := ["item_id", "display_name", "dimension_tags", "effect_kin func validate() -> Array: - var errs: Array = [] + var errs: Array[Variant] = [] if rows.is_empty(): # 空表合法态(2026-09-17 用户拍板——切片期无道具内容;F2 覆盖检查仅对非空清单执行) return errs @@ -104,13 +104,13 @@ func validate() -> Array: var retired: bool = bool(row.get("retired", false)) var deferred: bool = bool(row.get("deferred", false)) # dimension_tags:五维词表 + 非空(退役/失义行仍须登记维度——语义面保留) - var tags: Array = row.get("dimension_tags", []) + var tags: Array[Variant] = row.get("dimension_tags", []) if tags.is_empty(): errs.append({"category": "domain", "message": "%s dimension_tags 空" % iid}) for tag in tags: if not DIMENSIONS.has(String(tag)): errs.append({"category": "enum", "message": "%s dimension_tag 出域 %s: %s" % [iid, str(DIMENSIONS), str(tag)]}) - var kinds: Array = row.get("effect_kinds", []) + var kinds: Array[Variant] = row.get("effect_kinds", []) var target: String = String(row.get("target", "none")) if not TARGETS.has(target): errs.append({"category": "enum", "message": "%s target 出域 %s: %s" % [iid, str(TARGETS), target]}) @@ -217,7 +217,7 @@ func validate() -> Array: if not kind_seen.has("ap_charge_safe") and charge_n != 0: errs.append({"category": "cross_field", "message": "%s 非 ap_charge_safe 行 charge_n 应 0,实得 %d" % [iid, charge_n]}) # unlock_requires 谓词行:空 = 已解锁(首批全解锁合法态,底稿 §2.2);非空键 ∈ 计数键封闭枚举 - var ureqs: Array = row.get("unlock_requires", []) + var ureqs: Array[Variant] = row.get("unlock_requires", []) var ukeys := {} for req in ureqs: var ukey: String = String(req.get("key", "")) @@ -234,7 +234,7 @@ func validate() -> Array: # 同调行词表(form 非空)——装配覆盖层/效果接线/同调位上限的唯一取数口径(迭代 11 第①批 T1) func attunement_rows() -> Array: - var out: Array = [] + var out: Array[Variant] = [] for row in rows: if String(row.get("form", "")) != "" and not bool(row.get("retired", false)) and not bool(row.get("deferred", false)): out.append(row) @@ -244,7 +244,7 @@ func attunement_rows() -> Array: # 过渡行词表(form 空 = 切片期过渡形态活行)——掉落投放/停点池/过渡覆盖面的取数口径; # 同调行永不入过渡渠道。T7 退役面切换后本口径随渠道消亡。 func transitional_rows() -> Array: - var out: Array = [] + var out: Array[Variant] = [] for row in rows: if String(row.get("form", "")) == "" and not bool(row.get("retired", false)) and not bool(row.get("deferred", false)): out.append(row) @@ -253,7 +253,7 @@ func transitional_rows() -> Array: # 活行词表(非 retired 非 deferred)——投放校验/奖励节点池/F2 覆盖面的唯一取数口径 func active_rows() -> Array: - var out: Array = [] + var out: Array[Variant] = [] for row in rows: if not bool(row.get("retired", false)) and not bool(row.get("deferred", false)): out.append(row) diff --git a/game/presentation/battle_narrator.gd b/game/presentation/battle_narrator.gd index ef71cafa..b43ea54b 100644 --- a/game/presentation/battle_narrator.gd +++ b/game/presentation/battle_narrator.gd @@ -73,9 +73,9 @@ static func phase_text(round_index: int, battle_result: String, waiting: bool, b # 零内容期每坐标恰一行,行为零变化) static func boss_name(runner, db) -> String: if runner != null: - var node: Dictionary = runner.current_node() + var node: Dictionary[Variant, Variant] = runner.current_node() if String(node.get("type", "")) == "boss": - var rowset: Array = db.resolve_enemy_rowset(int(runner.level_row.level_index), int(node.get("node_depth", -1))) + 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 "遗迹守卫" @@ -173,12 +173,12 @@ static func adventure_summary_text(summary: Dictionary) -> String: # 钥石入账明细文案(元进度实现批;迭代 10 改口径——钥石全源 = Boss 逐杀掉落,「大奖励」行退役; # name_lookup = item_id → 显示名投影,由组合根自 meta 表喂入) static func keystone_settle_text(detail: Dictionary, name_lookup: Dictionary = {}) -> String: - var parts: Array = [] + 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 = detail.get("new_unlocks", []) + var unlocks: Array[Variant] = detail.get("new_unlocks", []) if not unlocks.is_empty(): var names: Array[String] = [] for iid in unlocks: diff --git a/game/presentation/curio_interact_controller.gd b/game/presentation/curio_interact_controller.gd index 58796e24..7200ad54 100644 --- a/game/presentation/curio_interact_controller.gd +++ b/game/presentation/curio_interact_controller.gd @@ -123,8 +123,8 @@ func on_presented(payload: Dictionary) -> void: func present_node_curio() -> void: var adventure = _adventure_get.call() _curio_context = "reward_node" - var node_draw: Dictionary = adventure.node_curio_candidates() - var node_cands: Array = node_draw.get("candidates", []) as Array + var node_draw: Dictionary[Variant, Variant] = adventure.node_curio_candidates() + var node_cands: Array[Variant] = node_draw.get("candidates", []) as Array if node_cands.is_empty(): # 空池降级(2026-10-06 用户令)——不开面、不挂节点停点(present_node_curio 未调即未挂), # 兑付确认直落回图选路(auto-sim「node_entered 回调内立即 continue_after_node」先例) @@ -141,7 +141,7 @@ func present_node_curio() -> void: # 三选一面开出(战斗总结结算确认 / 续玩停点恢复) func show_pick() -> void: - var payload: Dictionary = _pending_curio + var payload: Dictionary[Variant, Variant] = _pending_curio _open_modal.call(curio_overlay) curio_overlay.open_pick(Narrator.curio_pick_header(payload), _curio_card_texts(payload.get("candidates", []) as Array)) var adventure = _adventure_get.call() @@ -151,7 +151,7 @@ func show_pick() -> void: # 刷新呈现投喂单点(免费余量 X/Y + GP 付费价 + 冒险钱包可付——读数与商店 set_refresh_display 同构) -func _feed_refresh_display(adventure) -> void: +func _feed_refresh_display(adventure: AdventureRunner) -> void: var runner = _runner_get.call() var gp := int(runner.gp) if runner != null else 0 var cost: int = adventure.curio_refresh_gp_cost() @@ -161,7 +161,7 @@ func _feed_refresh_display(adventure) -> void: func _curio_card_texts(candidates: Array) -> Array: # TP0 令牌池——候选 = {"kind","tier"} 令牌字典;卡片 id = "kind|tier|feat_id" 复合键(同 kind # 大小两档/同档多 relic 可同屏,kind 单键不构成唯一身份),确认回程拆键(on_pick_confirmed) - var out: Array = [] + var out: Array[Variant] = [] for tok in candidates: var kind := String((tok as Dictionary).get("kind", "")) var tier := String((tok as Dictionary).get("tier", "")) @@ -203,7 +203,7 @@ func on_refresh() -> void: if not refreshed: return if _curio_context == "reward_node": - var st: Dictionary = adventure.node_curio_pending_state() + var st: Dictionary[Variant, Variant] = adventure.node_curio_pending_state() curio_overlay.open_pick(Narrator.node_curio_header(), _curio_card_texts(st.get("candidates", []) as Array)) _feed_refresh_display(adventure) @@ -224,7 +224,7 @@ func on_pick_confirmed(item_id: String) -> void: # T5——节点渠道门票拿到即开:候选已被替换为专长面,本地读在途态重绘(零 emit——context 保持 reward_node, # 后续兑付/确认仍走节点路径回图选路) if _curio_context == "reward_node" and kind == "feat_ticket": - var st2: Dictionary = adventure.node_curio_pending_state() + var st2: Dictionary[Variant, Variant] = adventure.node_curio_pending_state() curio_overlay.open_pick(Narrator.node_curio_header(), _curio_card_texts(st2.get("candidates", []) as Array)) diff --git a/game/presentation/debug_session.gd b/game/presentation/debug_session.gd index 4979cb51..04feb149 100644 --- a/game/presentation/debug_session.gd +++ b/game/presentation/debug_session.gd @@ -77,7 +77,7 @@ func reset() -> void: func _start_battle(level: int, carry_hp: int) -> bool: if not _level_mode or _level_idx == 0: rng.new_adventure(_seed_value.call()) - var ov: Dictionary = _hero_override.call() # E4:生效值经 encounter 传入——零表写 + var ov: Dictionary[Variant, Variant] = _hero_override.call() # E4:生效值经 encounter 传入——零表写 settlement = BattleOrchestrator.new(db, rng) battle_created.emit(settlement, level, carry_hp) var hp: int = carry_hp if carry_hp > 0 else int(ov.max_hp) diff --git a/game/presentation/hud_presenter.gd b/game/presentation/hud_presenter.gd index 103bc550..85a75f9f 100644 --- a/game/presentation/hud_presenter.gd +++ b/game/presentation/hud_presenter.gd @@ -23,7 +23,7 @@ static func refresh(stage: BattleStageView, info: HeroInfoPanelView, ap_bar: ApB # 共账本〔level_runner bonus{atk,max_hp,armor_class}〕;空加算=整节隐藏〔视图内空态纪律〕。 # 呈现只读投影零核心触碰;行文格式=代拍板白名单项) if runner != null and level_active: - var b: Dictionary = runner.run_ledger.bonus # 迭代 27 T3——加算区住 RunLedger(P1-4 收口) + var b: Dictionary[Variant, Variant] = runner.run_ledger.bonus # 迭代 27 T3——加算区住 RunLedger(P1-4 收口) var lines: Array[String] = [] if int(b.get("atk", 0)) != 0: lines.append("攻击 %+d" % int(b.get("atk", 0))) @@ -44,11 +44,11 @@ static func refresh(stage: BattleStageView, info: HeroInfoPanelView, ap_bar: ApB info.set_board_variants([]) # 战斗域角标出战斗即隐(首发批 2026-10-04——空态纪律) _refresh_out_of_battle(stage, info, ap_bar, runner, level_active, db) return - var s: Dictionary = settlement.presentation_snapshot() + var s: Dictionary[Variant, Variant] = settlement.presentation_snapshot() # 地形变异常驻角标投影(全局作用域变异首发批 2026-10-04——板级变异快照 → 文案行; # 空态隐藏=特性段先例;战斗全程常驻防中局遗忘) info.set_board_variants(board_variant_lines(db, s.get("board_variants", []))) - var hero: Dictionary = s.hero + var hero: Dictionary[Variant, Variant] = s.hero if hero.is_empty() or not bool(hero.get("alive", false)): _refresh_out_of_battle(stage, info, ap_bar, runner, level_active, db) return @@ -60,7 +60,7 @@ static func refresh(stage: BattleStageView, info: HeroInfoPanelView, ap_bar: ApB # ---- B 区主角站桩(HP/状态唯一展示面)+ C 区数据框(攻击/AC) ---- static func _refresh_hero(stage: BattleStageView, info: HeroInfoPanelView, s: Dictionary) -> void: - var hero: Dictionary = s.hero + var hero: Dictionary[Variant, Variant] = s.hero var hp: int = int(hero.hp) var max_hp: int = int(hero.max_hp) var atk: int = int(hero.atk) @@ -102,7 +102,7 @@ static func feat_holdings_lines(db, held: Array) -> Array[String]: return rlines var counts := {} for rid in held: - var rrow: Dictionary = db.feat_row(String(rid)) + var rrow: Dictionary[Variant, Variant] = db.feat_row(String(rid)) var fname := String(rrow.get("display_name", String(rid))) counts[fname] = int(counts.get(fname, 0)) + 1 for fname in counts.keys(): @@ -118,7 +118,7 @@ static func board_variant_lines(db, ids: Array) -> Array[String]: if db == null or db.variation == null: return lines for vid in ids: - var row: Dictionary = db.variation.row_by_id(String(vid)) + var row: Dictionary[Variant, Variant] = db.variation.row_by_id(String(vid)) if not row.is_empty(): lines.append(String(row.get("display_name", vid))) lines.append("回复与护甲资源减少") diff --git a/game/presentation/main.gd b/game/presentation/main.gd index a393cf8e..bf56a4b4 100644 --- a/game/presentation/main.gd +++ b/game/presentation/main.gd @@ -104,7 +104,7 @@ var _bonus = null # 三选一与替换子交互(curio_ var _camp_pres = null # 营地/商店投影(camp_shop_presenter.gd——本类只接线、探针缝保缝转发) # 排播队列只读委托面(探针/测试读 main._row_queue——RowPacer 迁移后保留的同名可读面,共享同一 Array 引用) -var _row_queue: Array: +var _row_queue: Array[Variant]: get: return _row_pacer.pending_rows() @@ -159,7 +159,7 @@ func _on_pump_ended() -> void: func _replay_locked_click() -> void: if run_scope.locked_click.is_empty() or not _row_queue.is_empty(): return - var click: Dictionary = run_scope.locked_click + var click: Dictionary[Variant, Variant] = run_scope.locked_click run_scope.locked_click = {} _on_board_action(click["cell"], click["kind"]) @@ -259,7 +259,7 @@ func _build_ui() -> void: # 模态覆盖层(登记序 = 树序 = z 遮挡序:图例 < 明细回看 < 节点 < 关卡画面 < 三选一 < 终局 # < 商店 < 营地 < 设置 < 状态面板(只读) < 生涯页(只读·迭代 18 卡④) < 确认弹窗 < 调试浮窗; # 关卡画面住冒险屏壳(树序在模态之下)——登记序仍夹在节点层与三选一层之间,遮挡语义零改动) - var overlays: Dictionary = UiAssembler.build_overlays(self, db) + var overlays: Dictionary[Variant, Variant] = UiAssembler.build_overlays(self, db) legend_overlay = overlays.legend_overlay node_overlay = overlays.node_overlay _end_overlay = overlays.end_overlay @@ -332,7 +332,7 @@ func _build_ui() -> void: _menu_screen.settingsbtn.pressed.connect(_open_settings) # 迭代 8 卡④——最小设置面入口 _menu_screen.quitbtn.pressed.connect(_quit_game) # 二轮修改批 D1——退出游戏提级一级菜单 - var inter: Dictionary = UiAssembler.build_inter_overlay(self) + var inter: Dictionary[Variant, Variant] = UiAssembler.build_inter_overlay(self) _inter_overlay = inter.overlay _inter_label = inter.label inter.next_btn.pressed.connect(_on_inter_next_pressed) @@ -420,7 +420,7 @@ func _start_adventure_from_menu() -> void: # 互斥确认弹窗化(二轮修改批 A——用户目检裁决「弹窗提示」):有断点时开新冒险弹实底确认框, # 「开新冒险」提交(弃旧断点+写盘)/「取消」回菜单 if save_manager.has_breakpoint(): - var bp: Dictionary = save_manager.breakpoint_snapshot() + var bp: Dictionary[Variant, Variant] = save_manager.breakpoint_snapshot() confirm_dialog.open("已有未完冒险(第 %d 关)——开新冒险将覆盖旧断点" % maxi(1, int(bp.get("level_index", 1))), "开新冒险", "取消") return @@ -454,7 +454,7 @@ func _refresh_resume_button() -> void: var has: bool = save_manager.has_breakpoint() _resume_btn.visible = has if has: - var bp: Dictionary = save_manager.breakpoint_snapshot() + var bp: Dictionary[Variant, Variant] = save_manager.breakpoint_snapshot() _resume_btn.text = "▶ 继续上局(第 %d 关 · 自最近停点恢复)" % maxi(1, int(bp.get("level_index", 1))) @@ -462,7 +462,7 @@ func _refresh_resume_button() -> void: func _resume_from_menu() -> void: if not save_manager.has_breakpoint(): return - var bp: Dictionary = save_manager.breakpoint_snapshot() + var bp: Dictionary[Variant, Variant] = save_manager.breakpoint_snapshot() _close_modal(debug_overlay) # 开冒险路径显式关浮窗(迭代 18 卡⑦ T2 防护——续玩同口径) _start_overlay.visible = false _begin_session(true) @@ -506,7 +506,7 @@ func _wire_adventure() -> void: # 注:runner 侧 resume_adventure 受理面现收 attunement/attune_refresh_y/tree_rules 三键 # (2026-10-08 尾裁微批b 卡⓪受理闭环——tree_rules 受理补齐,传参/受理两侧对齐,披露句退役)。 static func build_adventure_params(save_manager_ref, meta_ref, extra: Dictionary = {}) -> Dictionary: - var params: Dictionary = { + var params: Dictionary[Variant, Variant] = { "hero_override": meta_ref.hero_override() if meta_ref != null else {}, "attack_type": meta_ref.hero_attack_type() if meta_ref != null else "", "armor_type": meta_ref.hero_armor_type() if meta_ref != null else "", @@ -548,7 +548,7 @@ func _on_adventure_level_started(level_index: int, display_name: String) -> void # 迭代 19 卡⓪②——开场行改传当前生效值(runner.effective_atk/effective_max_hp 与战斗面板同源: # 表值+永久强化 flat+加算区 flat 后乘调试浮窗 pct,本回调时点 runner 已带全部加成);「含面板加成」 # 后缀判断同步改认局外加成——永久强化 atk/max_hp flat 或调试浮窗 pct 任一存在才带,白板局不带 - var ov: Dictionary = meta.hero_override() + var ov: Dictionary[Variant, Variant] = meta.hero_override() log_view.append_bb(Narrator.level_open_line(display_name, int(run_scope.adventure.master_seed), run_scope.runner.effective_atk(), run_scope.runner.effective_max_hp(), int(ov.get("atk", 0)) > 0 or int(ov.get("max_hp", 0)) > 0 @@ -664,14 +664,14 @@ func _start_debug_levels() -> void: _fatal_screen("战斗启动失败——见输出日志") -func _on_debug_battle_created(settlement_ref, _level: int, _carry: int) -> void: +func _on_debug_battle_created(settlement_ref: BattleOrchestrator, _level: int, _carry: int) -> void: # begin_battle 前接手 + 接线(ADR-0001:signal.connect 唯一归组合根)。settlement 必须先于 begin_battle # 就位——begin 期间发出的 board_changed 要用新板重建棋盘并触发一次性提示(缺陷①同款时序义务) run_scope.settlement = settlement_ref _wire_battle(settlement_ref) -func _on_debug_battle_ready(settlement_ref, _level: int, _carry: int) -> void: +func _on_debug_battle_ready(settlement_ref: BattleOrchestrator, _level: int, _carry: int) -> void: run_scope.settlement = settlement_ref board_view.rebuild(run_scope.settlement.minesweeper.current_board(), run_scope.settlement.minesweeper) _check_board_hints() @@ -717,7 +717,7 @@ func _variant_mask_lookup(c: Vector2i) -> bool: if b == null: return false for vid in b.variant_ids(c): - var vrow: Dictionary = db.variation.row_by_id(String(vid)) + var vrow: Dictionary[Variant, Variant] = db.variation.row_by_id(String(vid)) if String(vrow.get("effect", "")) == "hide_number": return true return false @@ -732,7 +732,7 @@ func _variant_mark_lookup(c: Vector2i) -> String: if b == null: return "" for vid in b.variant_ids(c): - var vrow: Dictionary = db.variation.row_by_id(String(vid)) + var vrow: Dictionary[Variant, Variant] = db.variation.row_by_id(String(vid)) if String(vrow.get("effect", "")) == "flat_damage": return "flat_damage" return "" @@ -749,7 +749,7 @@ func _on_battle_finished(payload: Dictionary) -> void: var result: String = String(payload.get("result", "")) _sync_settlement_visuals() # → 全关(battle_over 反馈面) HudPresenter.battle_end_feedback(stage, ap_bar, true) # 灰化/idle 回正/RAP 清零/状态全清——HUD 单通道(T3 收口批) - var decision: Dictionary = _debug.on_battle_finished(payload) + var decision: Dictionary[Variant, Variant] = _debug.on_battle_finished(payload) match String(decision.outcome): "next_layer": _inter_label.text = "第 %d 层通过!\nHP %d/%d 带入下一层" % [ @@ -784,7 +784,7 @@ func _on_battle_finished(payload: Dictionary) -> void: # 此刻必在档,语义=续玩恢复面的合计即存档合计;与 _bonus_summary_text 同款守卫式) func _gp_summary_text(payload: Dictionary) -> String: if run_scope.runner == null: - var bp: Dictionary = save_manager.breakpoint_snapshot() + var bp: Dictionary[Variant, Variant] = save_manager.breakpoint_snapshot() return "战利品 GP +%d · 合计 %d" % [int(payload.get("gp_dropped", 0)), int(bp.get("gp", 0))] return "战利品 GP +%d · 合计 %d" % [int(payload.get("gp_dropped", 0)), int(run_scope.runner.gp)] @@ -793,7 +793,7 @@ func _gp_summary_text(payload: Dictionary) -> String: func _bonus_summary_text() -> String: if run_scope.runner == null: return "" - var b: Dictionary = run_scope.runner.run_ledger.bonus # 迭代 27 T3——加算区住 RunLedger + var b: Dictionary[Variant, Variant] = run_scope.runner.run_ledger.bonus # 迭代 27 T3——加算区住 RunLedger var parts: Array[String] = [] if int(b.get("atk", 0)) != 0: parts.append("攻 %+d" % int(b.get("atk", 0))) @@ -806,7 +806,7 @@ func _bonus_summary_text() -> String: # ==================== core↔view 接线(战斗信号统一走这里)==================== -func _wire_battle(settlement_ref) -> void: +func _wire_battle(settlement_ref: BattleOrchestrator) -> void: settlement_ref.step_row.connect(_on_step_row) settlement_ref.battle_finished.connect(_on_battle_finished) settlement_ref.round_started.connect(_on_round_started) @@ -858,7 +858,7 @@ func _on_round_started(ri: int) -> void: # 开战横幅——板级地形变异预告(全局作用域变异首发批 2026-10-04):首轮快照 board_variants # 非空才发射(Boss 图恒带一型=替换制;普通图空快照零发射);轮横幅下方叠行,view 内判减动效。 if ri == 1 and run_scope.settlement != null: - var bvs: Array = run_scope.settlement.presentation_snapshot().get("board_variants", []) + var bvs: Array[Variant] = run_scope.settlement.presentation_snapshot().get("board_variants", []) if not bvs.is_empty(): var vlines: Array[String] = HudPresenter.board_variant_lines(db, bvs) if not vlines.is_empty(): @@ -1145,7 +1145,7 @@ func _on_board_action(cell: Vector2i, kind: String) -> void: if treasure_sel != 0 and kind == "dig": _treasure_ui.attempt_use(treasure_sel, cell) return - var outcomes: Array = run_scope.settlement.minesweeper.settle_batch([{"action": kind, "cell": cell}]) + var outcomes: Array[Variant] = run_scope.settlement.minesweeper.settle_batch([{"action": kind, "cell": cell}]) var accepted: bool = not outcomes.is_empty() and bool(outcomes[0].accepted) logger.log_action(kind, cell, accepted) if run_scope.runner != null and run_scope.runner.treasure_inventory != null: @@ -1172,7 +1172,7 @@ func _on_stop_pressed() -> void: if _input_locked(): return # stop 经批次提交(排雷玩法 D1×6①——不直调 行动经济) - var outcomes: Array = run_scope.settlement.minesweeper.settle_batch([{"action": "stop"}]) + var outcomes: Array[Variant] = run_scope.settlement.minesweeper.settle_batch([{"action": "stop"}]) logger.log_action("stop", Vector2i(-1, -1), not outcomes.is_empty() and bool(outcomes[0].accepted)) if not outcomes.is_empty() and String(outcomes[0].get("outcome", "")) == "voided": # voided ≠ 拒绝——独立文案(pattern 1:防静默改判读感欺骗) @@ -1376,5 +1376,5 @@ func _seed_value() -> int: if _seed_edit.text.is_valid_int(): return int(_seed_edit.text) if save_manager.is_first_adventure_done(): - return randi() + return randi() # ADR-0007-exempt(非首局空框引擎熵——组合根建种子不进玩法 RNG 消费,static_scan S1/S2 标记豁免) return 42 diff --git a/game/presentation/modal_stack.gd b/game/presentation/modal_stack.gd index ad13a8ce..3b51f27e 100644 --- a/game/presentation/modal_stack.gd +++ b/game/presentation/modal_stack.gd @@ -55,7 +55,7 @@ func has_open() -> bool: func has_open_excluding_tags(exclude_tags: Array) -> bool: for m in _list: if is_instance_valid(m) and m.visible: - var mt: Array = _tags.get(m, []) + var mt: Array[Variant] = _tags.get(m, []) var excluded := false for t in exclude_tags: if mt.has(t): diff --git a/game/presentation/playtest_logger.gd b/game/presentation/playtest_logger.gd index 9e312642..13d5e862 100644 --- a/game/presentation/playtest_logger.gd +++ b/game/presentation/playtest_logger.gd @@ -50,7 +50,7 @@ func log_path() -> String: return _path -func attach_battle(settlement) -> void: +func attach_battle(settlement: BattleOrchestrator) -> void: settlement.step_row.connect(_on_step_row) settlement.settlement_snapshot.connect(_on_settlement_snapshot) settlement.battle_finished.connect(_on_battle_finished_log) @@ -94,10 +94,10 @@ func log_action(kind: String, cell: Vector2i, accepted: bool, extra: Dictionary func _on_settlement_snapshot(snap: Dictionary) -> void: _append(snap) # ux_round_exit{path_tag}:对齐上游封闭枚举 {stopped, ap_exhausted, cleared} - var facts: Dictionary = snap.get("round_facts", {}) + var facts: Dictionary[Variant, Variant] = snap.get("round_facts", {}) log_ux("ux_round_exit", {"path_tag": String(facts.get("path", ""))}) # ux_round_input_ratio:原始输入动作数∶AP 消耗(本轮 AP 配额 − 快照时剩余 = 消耗) - var economy: Dictionary = snap.get("economy", {}) + var economy: Dictionary[Variant, Variant] = snap.get("economy", {}) var ap_left: int = int(economy.get("ap", 0)) var consumed: int = clampi(ap_per_round - ap_left, 0, ap_per_round) log_ux("ux_round_input_ratio", {"inputs": _ux_inputs, "ap_consumed": consumed, diff --git a/game/presentation/row_pacer.gd b/game/presentation/row_pacer.gd index ceec0e39..81e59bd4 100644 --- a/game/presentation/row_pacer.gd +++ b/game/presentation/row_pacer.gd @@ -11,7 +11,7 @@ extends Node const PACE_SEGMENT_GAP := 0.3 # 段间阶梯延迟(代拍板:白名单 0.2–0.4s 内取值,追认清单呈报) const PACE_ROW_GAP := 0.22 # 段内间隔(三轮 #3 上调 0.06→0.22——逐条演出可感知,配合数字浮现/状态脉冲) -var _rows: Array = [] # 待排播行(step_row 载荷原样入队;__round_line 标记=轮首分隔行) +var _rows: Array[Variant] = [] # 待排播行(step_row 载荷原样入队;__round_line 标记=轮首分隔行) var _pace_wait := 0.0 # 距下一行排播的剩余秒(_process 消耗) var _prev_segment := "" # 上一排播行的段型(段间/段内延迟判据) var _was_pumping := false # 排播活跃边沿(T17——末行消费帧触发结算视觉回挖掘态刷新) @@ -75,7 +75,7 @@ func _process(delta: float) -> void: _pace_wait -= delta if _pace_wait > 0.0: return - var entry: Dictionary = _rows.pop_front() + var entry: Dictionary[Variant, Variant] = _rows.pop_front() _consume_row(entry) var seg := String(entry.get("type", "")) _pace_wait = 0.0 if UiKit.motion_scale < 1.0 else (PACE_SEGMENT_GAP if seg != _prev_segment else PACE_ROW_GAP) diff --git a/game/presentation/run_scope.gd b/game/presentation/run_scope.gd index d2a122dd..bf379d05 100644 --- a/game/presentation/run_scope.gd +++ b/game/presentation/run_scope.gd @@ -17,6 +17,6 @@ var boss_active := false # Boss 遭遇变体(阶段指示附 Boss var was_settling := false # 结算相位上升沿检测(结算轮光带触发——演出链批) var hints_shown := {} # 一次性非模态提示去重(每 run 一次:chord/no_adjacent) var settlement_stop_timer := ArmedTimer.new() # 卡⑥ 结算框挂起计时(T3/D3 P4 乙——窗口 0=挂起到显式回落) -var pending_candidates: Array = [] # 结算框挂起期的选路候选暂存(choices_presented 载荷) +var pending_candidates: Array[Variant] = [] # 结算框挂起期的选路候选暂存(choices_presented 载荷) var locked_click := {} # 锁期点击缓存(T17 v3.1——最后一次;演播完边沿重放) var last_gain_amount := 0 # 最近一笔钱包入账(卡⑥ GP 增量取数面——wallet_changed gain 载荷) diff --git a/game/presentation/scenes/adventure_screen.gd b/game/presentation/scenes/adventure_screen.gd index f31e1776..e5d6bf75 100644 --- a/game/presentation/scenes/adventure_screen.gd +++ b/game/presentation/scenes/adventure_screen.gd @@ -25,7 +25,7 @@ func _ready() -> void: mouse_filter = Control.MOUSE_FILTER_IGNORE # 屏壳=纯容器(ui_input 实证:全屏 STOP 壳会吃掉下层模态鼠标输入) battle_root = VBoxContainer.new() add_child(battle_root) - var battle_ui: Dictionary = UiAssembler.build_battle_ui(battle_root, get_node("/root/ConfigDB")) + var battle_ui: Dictionary[Variant, Variant] = UiAssembler.build_battle_ui(battle_root, get_node("/root/ConfigDB")) stage = battle_ui.stage log_view = battle_ui.log_view info_panel = battle_ui.info_panel diff --git a/game/presentation/scenes/menu_screen.gd b/game/presentation/scenes/menu_screen.gd index ee8eb9d4..ea57bb81 100644 --- a/game/presentation/scenes/menu_screen.gd +++ b/game/presentation/scenes/menu_screen.gd @@ -20,7 +20,7 @@ const UiAssembler := preload("res://presentation/ui_assembler.gd") # 构建归 func _ready() -> void: mouse_filter = Control.MOUSE_FILTER_IGNORE # 屏壳=纯容器(ui_input 实证:全屏 STOP 壳会吃掉下层模态鼠标输入) - var start: Dictionary = UiAssembler.build_start_overlay(self, get_node("/root/ConfigDB")) + var start: Dictionary[Variant, Variant] = UiAssembler.build_start_overlay(self, get_node("/root/ConfigDB")) start_overlay = start.overlay box = start.box lvbtn = start.lvbtn diff --git a/game/presentation/treasure_interact_controller.gd b/game/presentation/treasure_interact_controller.gd index f5137d1b..ecd77a41 100644 --- a/game/presentation/treasure_interact_controller.gd +++ b/game/presentation/treasure_interact_controller.gd @@ -66,14 +66,14 @@ func on_slot_clicked(slot_handle: int) -> void: if slot_handle < 0: toast.show_toast("自动兑付件:满足条件自动生效,无需手动使用", "info") return - var rec: Dictionary = {} + var rec: Dictionary[Variant, Variant] = {} for r in inventory_projection(): if int(r.get("handle", 0)) == slot_handle: rec = r break if rec.is_empty() or bool(rec.get("used", false)): return - var arow: Dictionary = db.treasure_item_row(String(rec.get("item_id", ""))) + var arow: Dictionary[Variant, Variant] = db.treasure_item_row(String(rec.get("item_id", ""))) if String(arow.get("target", "none")) == "none": attempt_use(slot_handle) # AP 类:显式无目标即用(不再伪造哨兵坐标) return @@ -92,7 +92,7 @@ func attempt_use(handle: int, cell: Variant = null) -> void: if settlement == null: return var target: Vector2i = cell if cell != null else NO_TARGET - var v: Dictionary = settlement.use_treasure(handle, target) + var v: Dictionary[Variant, Variant] = settlement.use_treasure(handle, target) if not bool(v.ok): toast.show_toast(String(v.get("reason", "无法使用")), "warn") return @@ -100,7 +100,7 @@ func attempt_use(handle: int, cell: Variant = null) -> void: refresh_slots() refresh_overlays() board_view.refresh() # 迭代 13 T8/D6——移除格即时落图(无移除态的件 diff 零变化幂等) - var arow: Dictionary = db.treasure_item_row(String(v.get("item_id", ""))) + var arow: Dictionary[Variant, Variant] = db.treasure_item_row(String(v.get("item_id", ""))) toast.show_toast("已使用 %s" % String(arow.get("display_name", "")), "info") @@ -115,11 +115,11 @@ func inventory_projection() -> Array: if ms != null: cb = func(_c: Vector2i) -> bool: return ms.has_unrevealed() # 只读查询面(排雷玩法 has_unrevealed——手搓全板双循环收口) - var out: Array = [] - var cs: Dictionary = settlement.attune_convert_state() if settlement != null else {} + var out: Array[Variant] = [] + var cs: Dictionary[Variant, Variant] = settlement.attune_convert_state() if settlement != null else {} var auto_seen := false # 自动兑付件是否已随骑库存出现(恒否——charges=0 不入骑库存;守合成行重复) for rec in runner.treasure_inventory.query_inventory(cb): - var arow: Dictionary = db.treasure_item_row(String(rec.get("item_id", ""))) + var arow: Dictionary[Variant, Variant] = db.treasure_item_row(String(rec.get("item_id", ""))) var dn := String(arow.get("display_name", "")) # 积点进度浮窗行(反哺批⑤——自动兑付件 C 区悬停 tooltip 补 N/M 进度;件 id 锁定=装配行唯一) var note := "" @@ -161,5 +161,5 @@ func refresh_overlays() -> void: var runner = _runner_get.call() if runner == null or runner.treasure_inventory == null or board_view == null: return - var ov: Dictionary = runner.treasure_inventory.query_overlays() + var ov: Dictionary[Variant, Variant] = runner.treasure_inventory.query_overlays() board_view.set_treasure_overlays(ov.get("mine_reveal", []), ov.get("reward_preview", [])) diff --git a/game/presentation/views/battle_log_view.gd b/game/presentation/views/battle_log_view.gd index b3ebdd88..a99c05ea 100644 --- a/game/presentation/views/battle_log_view.gd +++ b/game/presentation/views/battle_log_view.gd @@ -130,8 +130,8 @@ func _flush_pending_agg() -> void: if _pending_agg.is_empty(): return for key in _pending_agg: - var item: Dictionary = _pending_agg[key] - var row: Dictionary = item["row"] + var item: Dictionary[Variant, Variant] = _pending_agg[key] + var row: Dictionary[Variant, Variant] = item["row"] var count: int = item["count"] var bb := _format_row(row, count) _round_rows.append(bb) diff --git a/game/presentation/views/board_view.gd b/game/presentation/views/board_view.gd index 7e8bd7ba..3316c866 100644 --- a/game/presentation/views/board_view.gd +++ b/game/presentation/views/board_view.gd @@ -275,7 +275,7 @@ func _refresh_slab() -> void: # 战斗启动/换板:按板尺寸重建格单元(minesweeper 引用随之更新) -func rebuild(board: BoardData, minesweeper) -> void: +func rebuild(board: BoardData, minesweeper: MinesweeperRules) -> void: _board = board _minesweeper = minesweeper for child in _world.get_children(): @@ -630,7 +630,7 @@ func _show_path_preview(c: Vector2i) -> void: # D3 合法性指示:悬停已开格 → BFS 路径蓝细线点迹(compute_move_path 只读面) if _minesweeper == null: return - var path: Array = _minesweeper.compute_move_path(c) + var path: Array[Variant] = _minesweeper.compute_move_path(c) for pc in path: if _cells.has(pc): _cells[pc].dot_visible = true @@ -648,7 +648,7 @@ func _update_badge(c: Vector2i) -> String: return "" if _r0.has(c): return "" # R₀ 初始展开格无奖励身份(地图战斗资源 资格域 = S∖R₀) - var rec: Dictionary = _minesweeper.reveal_record(c) # E2 收口:已开记录经查询面(禁直读 排雷玩法 状态字典) + var rec: Dictionary[Variant, Variant] = _minesweeper.reveal_record(c) # E2 收口:已开记录经查询面(禁直读 排雷玩法 状态字典) if rec.get("kind", "") != "safe": return "" var ident = _identity_cb.call(c) @@ -704,7 +704,7 @@ func _paint_cell(c: Vector2i, hero: Vector2i) -> void: flag = false u.tex_flag = null if revealed: - var rec: Dictionary = _minesweeper.reveal_record(c) # E2 收口:已开记录经查询面 + var rec: Dictionary[Variant, Variant] = _minesweeper.reveal_record(c) # E2 收口:已开记录经查询面 if rec.get("kind", "") == "mine": bg = COL_FLOOR glyph = "雷" @@ -910,7 +910,7 @@ func _load_board_tiles(board_type: String = "dungeon") -> void: # 方向稿 _v3_darken_floors 同式移植;现行瓦片 L≈0.036 低于带下沿故实为提亮;素材重产归资产批, # 本归一为程序代役(逐批装载时对 freshly-loaded 原图测光,幂等)。归一后逐像素明度顺路采集, # 驱动数字墨族按板自适应(_digit_ink_mode——均值分位同源同像素,禁二次解码) - var lumas: Array = [] + var lumas: Array[Variant] = [] for i in range(floors.size()): floors[i] = _normalize_floor_luma(floors[i], 0.10, lumas) var ink_mode := _digit_ink_mode(lumas) diff --git a/game/presentation/views/camp_overlay_view.gd b/game/presentation/views/camp_overlay_view.gd index a892bd16..420bc166 100644 --- a/game/presentation/views/camp_overlay_view.gd +++ b/game/presentation/views/camp_overlay_view.gd @@ -238,8 +238,8 @@ func _rebuild_tree(data: Dictionary) -> void: c.queue_free() _tree_buy_btns.clear() _tree_fork_btns.clear() - var tree: Array = data.get("tree", []) as Array - var forks: Array = data.get("fork_groups", []) as Array + var tree: Array[Variant] = data.get("tree", []) as Array + var forks: Array[Variant] = data.get("fork_groups", []) as Array var vein_names := {"spine": "·脊柱淬炼·", "forge": "·锻造脉·", "keystone": "·钥石脉·", "attune": "·同调脉·"} var last_vein := "" for row in tree: @@ -407,7 +407,7 @@ func refresh_camp(data: Dictionary) -> void: for row in data.get("workshop", []): ws_by_id[String(row.get("item_id", ""))] = row for card in _workshop_rows: - var row: Dictionary = ws_by_id.get(String(card.item_id), {}) + var row: Dictionary[Variant, Variant] = ws_by_id.get(String(card.item_id), {}) if row.is_empty(): continue var unlocked := bool(row.get("unlocked", false)) @@ -422,7 +422,7 @@ func refresh_camp(data: Dictionary) -> void: elif mode == "condition": var parts: Array[String] = [] for req_v in (row.get("reqs", []) as Array): - var req: Dictionary = req_v as Dictionary + var req: Dictionary[Variant, Variant] = req_v as Dictionary var lbl := String(UiKit.TREASURE_REQ_LABELS.get(String(req.get("key", "")), "条件")) parts.append("%s %d/%d" % [lbl, int(req.get("progress", 0)), int(req.get("threshold", 0))]) prog_text = "、".join(parts) @@ -440,10 +440,10 @@ func refresh_camp(data: Dictionary) -> void: if card.get("buy_btn") != null: card.buy_btn.disabled = balance < int(card.get("price", 0)) # 同调装配段状态就地刷(迭代 11 第①批 T4——Slots 点亮/满位禁选/Y 升级行余额联动) - var att: Dictionary = data.get("attune", {}) + var att: Dictionary[Variant, Variant] = data.get("attune", {}) if _attune_header_l != null: var slots_n := int(att.get("slots", 0)) - var eq: Array = att.get("equipped", []) + var eq: Array[Variant] = att.get("equipped", []) _attune_header_l.text = "── 同调装配 Slots %d/%d ──" % [eq.size(), slots_n] var eq_set := {} for v in eq: @@ -454,7 +454,7 @@ func refresh_camp(data: Dictionary) -> void: # 满位禁选+未解锁禁装(迭代 22 T4——已装件恒可卸下;unlocked 快照随 rebuild 刷新) card2.btn.disabled = (not on and eq.size() >= slots_n) or (not on and not bool(card2.get("unlocked", false))) if _refresh_l != null: - var rf: Dictionary = att.get("refresh", {}) + var rf: Dictionary[Variant, Variant] = att.get("refresh", {}) _refresh_l.text = "三选一刷新 +%d 次/冒险 · Lv%d" % [int(rf.get("charges", 0)), int(rf.get("level", 0))] _refresh_btn.text = "已满级" if bool(rf.get("maxed", false)) else "升级 %d 钥石" % int(rf.get("price", 0)) _refresh_btn.disabled = bool(rf.get("maxed", false)) or balance < int(rf.get("price", 0)) @@ -508,7 +508,7 @@ func _rebuild_workshop(data: Dictionary) -> void: # 同调装配段重建(迭代 11 第①批 T4——秘宝匣柜台尾部段〔迭代 21 T6 随柜台迁入〕;Slots 点亮+ # 九件行板+Y 升级行,程序占位面) func _rebuild_attune(data: Dictionary) -> void: - var att: Dictionary = data.get("attune", {}) + var att: Dictionary[Variant, Variant] = data.get("attune", {}) _attune_header_l = UiKit.label("",UiKit.FS_HINT, true, UiKit.INK) _workshop_holder.add_child(_attune_header_l) _attune_nodes.append(_attune_header_l) @@ -548,7 +548,7 @@ func _rebuild_attune(data: Dictionary) -> void: func _rebuild_recent(data: Dictionary) -> void: var lines: Array[String] = [] - var recent: Array = data.get("recent", []) + var recent: Array[Variant] = data.get("recent", []) if recent.is_empty(): lines.append(" 最近冒险:(暂无记录——完成或结束一次冒险后入账)") for s in recent: diff --git a/game/presentation/views/career_overlay.gd b/game/presentation/views/career_overlay.gd index a405a122..64a7a3de 100644 --- a/game/presentation/views/career_overlay.gd +++ b/game/presentation/views/career_overlay.gd @@ -28,7 +28,7 @@ var _keystone_badge: UiKit.KeystoneBadge # 钥石余额(badge 工厂件— func _init() -> void: # 全屏页壳(T6/T7 对齐批先例——暖亮底+石纹弱质感+暗角+56px 通栏顶条+内容列) - var page: Dictionary = UiKit.page_shell(self, true) + var page: Dictionary[Variant, Variant] = UiKit.page_shell(self, true) var top: HBoxContainer = page.top_row top.add_child(UiKit.label("生 涯",UiKit.FS_TITLE, true, UiKit.WARM)) var top_sp := Control.new() diff --git a/game/presentation/views/curio_pick_overlay.gd b/game/presentation/views/curio_pick_overlay.gd index a47113ea..e25a3180 100644 --- a/game/presentation/views/curio_pick_overlay.gd +++ b/game/presentation/views/curio_pick_overlay.gd @@ -24,8 +24,8 @@ var _refresh_allowance := 0 # 组合根投喂免费余量(X/Y var _refresh_gp_cost := 0 # 组合根投喂 GP 刷新价(免费位用尽后的付费档读数) var _selected := "" # 点选暂存(确认前可改选) var _header := "" # 停点头部文案(选择态保持显示) -var _card_meta: Array = [] # open_pick 缓存 [{id, text}]——高亮还原读面 -var _bcards: Array = [] # M7 bcard 引用列(select 高亮=set_selected) +var _card_meta: Array[Variant] = [] # open_pick 缓存 [{id, text}]——高亮还原读面 +var _bcards: Array[Variant] = [] # M7 bcard 引用列(select 高亮=set_selected) func _init() -> void: @@ -33,7 +33,7 @@ func _init() -> void: # 根底暗档)——56px 通栏顶条(标题+刷新图标钮/角标)+ 居中入账信息行(GP 汇总行承载)+ # bcard 三竖卡(D1 232×352)+ 底部确认主钮与免费选择注记。差异白名单=免费选择/✓章/无价签。 # 状态机不变:pick→result。M7 同构裁决遗产保留。 - var page: Dictionary = UiKit.page_shell(self, false) + var page: Dictionary[Variant, Variant] = UiKit.page_shell(self, false) var top: HBoxContainer = page.top_row top.add_child(UiKit.label("战利品三选一",UiKit.FS_TITLE, true, UiKit.WARM)) var top_sp := Control.new() @@ -137,7 +137,7 @@ func _select_card(id: String) -> void: _selected = id # 选中态=木描边+✓章(demo ⑥;set_selected 逐卡置真值防改选残留) for i in _bcards.size(): - var meta: Dictionary = _card_meta[i] if i < _card_meta.size() else {} + var meta: Dictionary[Variant, Variant] = _card_meta[i] if i < _card_meta.size() else {} (_bcards[i] as UiKit.BCard).set_selected(String(meta.get("id", "")) == id) _confirm_btn.visible = true diff --git a/game/presentation/views/debug_overlay.gd b/game/presentation/views/debug_overlay.gd index 4bedb82d..f4b90ef6 100644 --- a/game/presentation/views/debug_overlay.gd +++ b/game/presentation/views/debug_overlay.gd @@ -158,7 +158,7 @@ func effective_max_hp() -> int: # ---- 内部构建(标题/关闭固定于浮窗骨架,可扩展列表区居中滚动)---- func _build() -> void: - var shell: Dictionary = UiKit.modal_shell(self, UiKit.BG_OVERLAY, Vector2(640, 0), 8) + var shell: Dictionary[Variant, Variant] = UiKit.modal_shell(self, UiKit.BG_OVERLAY, Vector2(640, 0), 8) var box: VBoxContainer = shell.box var title := UiKit.label("调试",UiKit.FS_TITLE, true, UiKit.WARM) diff --git a/game/presentation/views/hero_info_panel_view.gd b/game/presentation/views/hero_info_panel_view.gd index 3b972820..5923cf4c 100644 --- a/game/presentation/views/hero_info_panel_view.gd +++ b/game/presentation/views/hero_info_panel_view.gd @@ -16,9 +16,9 @@ var _atk_label: Label var _armor_label: Label signal treasure_slot_clicked(handle: int) # 迭代 7 T4——槽点击(组合根消费:AP 类即用/空间类进选中态) var item_slots: HBoxContainer # 秘宝槽(迭代 7 T4 接线——库存条常读+两步交互第一步) -var _slot_panels: Array = [] # 槽 PanelContainer 引用(选中高亮/置灰重刷) +var _slot_panels: Array[Variant] = [] # 槽 PanelContainer 引用(选中高亮/置灰重刷) var _selected_handle := 0 # 选中态(0=无;组合根回写维持单源) -var _slot_handles: Array = [] # 槽位→handle 映射(set_treasure_inventory 刷新) +var _slot_handles: Array[Variant] = [] # 槽位→handle 映射(set_treasure_inventory 刷新) var _curio_section: VBoxContainer # 特性(预留——空态隐藏,接线归大奖励/锻造呈现批) var _curio_list: VBoxContainer var _feat_section: VBoxContainer # 特性持有段(迭代 12 呈现补齐——空态隐藏) @@ -180,7 +180,7 @@ func set_treasure_inventory(items: Array, selected_handle: int) -> void: slot.add_theme_stylebox_override("panel", UiKit.panel_flat(UiKit.PANEL_DATA, UiKit.PANEL_BORDER, 0, 1, Vector4(4, 4, 4, 4))) continue - var rec: Dictionary = items[i] + var rec: Dictionary[Variant, Variant] = items[i] _slot_handles.append(int(rec.get("handle", 0))) var used := bool(rec.get("used", false)) var usable := bool(rec.get("usable", true)) diff --git a/game/presentation/views/level_map_screen.gd b/game/presentation/views/level_map_screen.gd index b987404f..6003909a 100644 --- a/game/presentation/views/level_map_screen.gd +++ b/game/presentation/views/level_map_screen.gd @@ -282,7 +282,7 @@ func _process(delta: float) -> void: # 打开选路口(cands = 候选节点引用数组;runner 只读用于整图渲染与状态栏刷新) -func open_choices(cands: Array, runner) -> void: +func open_choices(cands: Array, runner: LevelRunner) -> void: var was_visible := visible # 进入转场判据(就地刷新/模态栈恢复不重放淡入) _runner = runner _hover_id = -1 @@ -400,7 +400,7 @@ func _on_canvas_input(ev: InputEvent) -> void: func _candidate_at(local_pos: Vector2) -> int: if _runner == null: return -1 - var nodes: Array = _runner.nodes() + var nodes: Array[Variant] = _runner.nodes() var pos := _layout(nodes, _canvas) for nid in _cand_ids: var node := _node_by_id(nid, nodes) @@ -427,7 +427,7 @@ func _submit_choice(nid: int) -> void: func press_candidate(index: int) -> void: if index < 0 or index >= _cand_ids.size() or _runner == null: return - var nodes: Array = _runner.nodes() + var nodes: Array[Variant] = _runner.nodes() var node := _node_by_id(int(_cand_ids[index]), nodes) if node.is_empty(): return @@ -447,12 +447,12 @@ func _id_of(node: Dictionary) -> int: func _draw_graph(canvas: Control) -> void: if _runner == null or not visible: return - var nodes: Array = _runner.nodes() + var nodes: Array[Variant] = _runner.nodes() var pos := _layout(nodes, canvas) var current_id: int = _runner.current_node_id() # 当前节点 id(单链=下标 / 图=编号)——visited_ids() 已按编号空间解析 - var visited: Array = _runner.visited_ids() - var cand_ids: Array = _runner.next_candidate_ids() + var visited: Array[Variant] = _runner.visited_ids() + var cand_ids: Array[Variant] = _runner.next_candidate_ids() # 连线(通道)先行画(方块覆盖其上);悬停预承诺 = 所属通道再增亮(加粗) # T17 修复:选路停点(候选非空)非候选通道降透明——金岔路从结构网分离(干线共线重叠缺陷的 # 视觉主因:不同源通道共用同一垂直干线,金边与石色边焊接成「整列可走」假象) @@ -549,7 +549,7 @@ func _assign_layers(nodes: Array, layer: Dictionary, slot: Dictionary) -> void: queue.append(entry) layer[entry] = 0 while not queue.is_empty(): - var cur: Dictionary = queue.pop_front() + var cur: Dictionary[Variant, Variant] = queue.pop_front() var l: int = int(layer[cur]) layer_counts[l] = int(layer_counts.get(l, 0)) + 1 slot[cur] = int(layer_counts[l]) - 1 diff --git a/game/presentation/views/node_overlay.gd b/game/presentation/views/node_overlay.gd index 46bfd38a..59554438 100644 --- a/game/presentation/views/node_overlay.gd +++ b/game/presentation/views/node_overlay.gd @@ -13,7 +13,7 @@ var _btn: Button func _init() -> void: # M5(UI 落码批):rest 兑付面板 plate9 石板化+暖金头带字(demo ④休息弹层;暗字上亮板 §7.2) - var shell: Dictionary = UiKit.modal_shell(self, Color(0, 0, 0, 0.6), Vector2.ZERO, 14, true, + var shell: Dictionary[Variant, Variant] = UiKit.modal_shell(self, Color(0, 0, 0, 0.6), Vector2.ZERO, 14, true, UiKit.panel_skinned(Vector4(26, 18, 26, 18))) var box: VBoxContainer = shell.box _label = UiKit.label("",UiKit.FS_SUB, true, UiKit.INK_TITLE) diff --git a/game/presentation/views/settings_overlay.gd b/game/presentation/views/settings_overlay.gd index f44918a4..7673bcd9 100644 --- a/game/presentation/views/settings_overlay.gd +++ b/game/presentation/views/settings_overlay.gd @@ -26,7 +26,7 @@ func _init(store) -> void: # 探针隔离换注(冒烟/探针禁碰真实 user://settings.cfg——换注后按新 store 重挂控件态) -func retarget(store) -> void: +func retarget(store: SettingsStore) -> void: _store = store _sync() @@ -37,7 +37,7 @@ func _build() -> void: # 减动效与结算演出动画=同一设置(语义命名批合并),一行承载。 # T8(UI 设计对齐批 2026-10-06):两列对齐(label 列定宽 140——demo .setrow label/控制两列)+ # 后果说明内联随行(demo .why 同排)+返回钮紧凑化(demo .btn9 紧凑——原 216 宽砖)。 - var shell: Dictionary = UiKit.modal_shell(self, UiKit.BG_BRIGHT, Vector2(560, 0), 8, true, + var shell: Dictionary[Variant, Variant] = UiKit.modal_shell(self, UiKit.BG_BRIGHT, Vector2(560, 0), 8, true, UiKit.panel_skinned(Vector4(20, 16, 20, 16))) var box: VBoxContainer = shell.box diff --git a/game/presentation/views/shop_overlay_view.gd b/game/presentation/views/shop_overlay_view.gd index f612aeb4..1b4b4c41 100644 --- a/game/presentation/views/shop_overlay_view.gd +++ b/game/presentation/views/shop_overlay_view.gd @@ -33,7 +33,7 @@ func _init() -> void: # 角标+GP 框)+ 爬价规则行 + bcard 三竖卡居中 + 底部离开钮与池源注记。 # M6 遗产:库存余量退役(关卡节点地图 v1.5-a 用户裁);刷新钮呈现位照摆(机制=MC-1 投喂)。 # legacy 面(open_shop)行卡同壳呈现(shop=off 对照承载原样)。高内聚边界不变:纯视图 setter 面。 - var page: Dictionary = UiKit.page_shell(self, true) + var page: Dictionary[Variant, Variant] = UiKit.page_shell(self, true) var top: HBoxContainer = page.top_row top.add_child(UiKit.label("商 店",UiKit.FS_TITLE, true, UiKit.WARM)) var top_sp := Control.new() @@ -110,7 +110,7 @@ func refresh_state(products: Array, gp: int) -> void: for p in products: by_id[String(p.product_id)] = p for card in _card_rows: - var p: Dictionary = by_id.get(String(card.product_id), {}) + var p: Dictionary[Variant, Variant] = by_id.get(String(card.product_id), {}) if p.is_empty(): continue card.price.text = "现价 %d 金币" % int(p.price) diff --git a/game/presentation/views/status_panel.gd b/game/presentation/views/status_panel.gd index abec822c..a00652b8 100644 --- a/game/presentation/views/status_panel.gd +++ b/game/presentation/views/status_panel.gd @@ -21,7 +21,7 @@ var _tree_label: Label func _init() -> void: - var shell: Dictionary = UiKit.modal_shell(self, Color(UiKit.DIM, 0.55), Vector2(560, 0), 10, true, + var shell: Dictionary[Variant, Variant] = UiKit.modal_shell(self, Color(UiKit.DIM, 0.55), Vector2(560, 0), 10, true, UiKit.panel_skinned(Vector4(20, 16, 20, 16))) # 石板皮(缺位回退数据板——同设置面先例) var box: VBoxContainer = shell.box @@ -103,7 +103,7 @@ func _rebuild_workshop(data: Dictionary) -> void: else: var parts: Array[String] = [] for req_v in (row.get("reqs", []) as Array): - var req: Dictionary = req_v as Dictionary + var req: Dictionary[Variant, Variant] = req_v as Dictionary var lbl := String(UiKit.TREASURE_REQ_LABELS.get(String(req.get("key", "")), "条件")) parts.append("%s %d/%d" % [lbl, int(req.get("progress", 0)), int(req.get("threshold", 0))]) prog_text = "、".join(parts) diff --git a/game/presentation/views/status_slot_row.gd b/game/presentation/views/status_slot_row.gd index 3cd3c09d..b773b070 100644 --- a/game/presentation/views/status_slot_row.gd +++ b/game/presentation/views/status_slot_row.gd @@ -165,7 +165,7 @@ func _on_slot_hover(slot: Control) -> void: # tooltip:来源 + 语义 + 剩余(≥13px;悬停只读反馈通道——无点击无写) if not slot.has_meta("eff"): return - var eff: Dictionary = slot.get_meta("eff") + var eff: Dictionary[Variant, Variant] = slot.get_meta("eff") var eid := String(eff.effect_id) var src: String = String(SOURCE_NAMES.get(String(eff.get("source_class", "")), String(eff.get("source_class", "")))) var semantic: String = String(EFFECT_SEMANTIC.get(eid, String(eff.get("display_name", "")))) diff --git a/game/project.godot b/game/project.godot index 1eb9b8ec..ce7ab42d 100644 --- a/game/project.godot +++ b/game/project.godot @@ -14,6 +14,10 @@ config/name="DungeonSweeperVerticalSlice" run/main_scene="res://presentation/main.tscn" config/features=PackedStringArray("4.7") +[debug] + +gdscript/warnings/untyped_declaration=1 + [autoload] ConfigDB="*res://core/config_db.gd" diff --git a/game/smoke/smoke_all.gd b/game/smoke/smoke_all.gd index 6339a089..ccb0e191 100644 --- a/game/smoke/smoke_all.gd +++ b/game/smoke/smoke_all.gd @@ -82,6 +82,7 @@ func _split_tail() -> void: await process_frame await process_frame var autoload_ok: bool = get_root().get_node_or_null("/root/ConfigDB") != null and get_root().get_node_or_null("/root/SaveManager") != null + _smoke_type_gate() if autoload_ok: await _smoke_scene_split() # 协程函数必须 await(无 await=发射后不管,首个 await 后即被 quit 截断) await _smoke_run_lifecycle() @@ -91,6 +92,17 @@ func _split_tail() -> void: quit(1 if _failures > 0 else 0) +# ---- 类型闸断言(迭代 27 T5 V7——project.godot warning 配置在位〔源码断言〕+审计器脚本在位; +# 审计器归零判定走 static_scan S9 闸机,此处兜配置面)---- +func _smoke_type_gate() -> void: + var pj: String = FileAccess.get_file_as_string("res://project.godot") + check(pj.contains("gdscript/warnings/untyped_declaration=1"), + "V7 类型闸 warning 配置在位(untyped_declaration=1 显形)〔迭代27 T5〕") + var scan_src: String = FileAccess.get_file_as_string("res://tests/static_scan.sh") + check(scan_src.contains("audit_type_hints.py"), + "V7 类型审计器闸机在位(static_scan S9 → tools/audit_type_hints.py)〔迭代27 T5〕") + + # ---- 三屏场景化断言(迭代 27 T1 V2——独立实例化+关键节点在位+组合根树序/z 序保序)---- func _smoke_scene_split() -> void: print("--- 迭代27 T1 三屏场景化(V2:屏壳独立实例化+组合根装配保序)---") diff --git a/game/tests/static_scan.sh b/game/tests/static_scan.sh index f1c7c785..4b649534 100644 --- a/game/tests/static_scan.sh +++ b/game/tests/static_scan.sh @@ -4,7 +4,7 @@ # 正式工程(core/ 存在)时执行;否则优雅跳过(工程在 game/core——2026-09-24 转正结构批后仓库根布局)。 # 纪律:新违规案例必须先补本脚本规则再修码(design/dev/code-conventions.md 缺陷-规范对照表)。 set -u -cd "$(dirname "$0")/.." +cd "$(dirname "$0")/../.." # 迭代 27 T5 修正——2026-09-24 布局迁移后 /.. 落 game/ 致 game/core 守卫恒跳过(静默空转遗产),S 闸规则路径均为仓根相对 if [ ! -d game/core ]; then echo "[static_scan] game/core 不存在(工程未创建)——跳过,退出 0" @@ -13,11 +13,19 @@ fi fail=0 +# S9 — 类型闸(迭代 27 T5——code-conventions §4 机器闸:产品码无类型容器/有值返回缺类型/参数缺类型) +if python tools/audit_type_hints.py; then + echo "[static_scan] S9 ok" +else + echo "[static_scan] S9 FAIL: 类型闸存量未归零(R1/R2/R3 见上清单)" + fail=1 +fi + # S1/S2 — 玩法代码禁 randomize()/自建 RandomNumberGenerator/全局随机函数(绕开 RngService 的口子) # 2026-09-24 首次实跑收敛(转正结构批):违规签名=裸 randomize/rand* 调用(实例方法 .randi() 排除) # + RngService 之外的 RandomNumberGenerator.new();接收服务注入流的类型标注不属违规(core 五文件 15 处实证) hits=$(grep -rnE '\brandomize[[:space:]]*\(|(^|[^.[:alnum:]_])randi[[:space:]]*\(|(^|[^.[:alnum:]_])randf[[:space:]]*\(|(^|[^.[:alnum:]_])randf_range[[:space:]]*\(' \ - game/core game/presentation --include='*.gd' 2>/dev/null | grep -vE '^[^:]+:[0-9]+:[[:space:]]*#' | wc -l) + game/core game/presentation --include='*.gd' 2>/dev/null | grep -vE '^[^:]+:[0-9]+:[[:space:]]*#' | grep -v 'ADR-0007-exempt' | wc -l) hits2=$(grep -rn 'RandomNumberGenerator\.new()' game/core game/presentation --include='*.gd' 2>/dev/null | grep -vc rng_service) if [ "$hits" -gt 0 ] || [ "$hits2" -gt 0 ]; then echo "[static_scan] S1/S2 FAIL: 全局随机/RNG 自建命中 $hits+$hits2 处"; grep -rnE '\brandomize[[:space:]]*\(|(^|[^.[:alnum:]_])randi[[:space:]]*\(|(^|[^.[:alnum:]_])randf[[:space:]]*\(|(^|[^.[:alnum:]_])randf_range[[:space:]]*\(' game/core game/presentation --include='*.gd' 2>/dev/null; grep -rn 'RandomNumberGenerator\.new()' game/core game/presentation --include='*.gd' 2>/dev/null | grep -v rng_service; fail=1; else echo "[static_scan] S1/S2 ok"; fi diff --git a/tools/audit_type_hints.py b/tools/audit_type_hints.py new file mode 100644 index 00000000..fe012c24 --- /dev/null +++ b/tools/audit_type_hints.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +"""audit_type_hints.py — 迭代27 T5 类型闸机器审计(code-conventions §4 语言纪律的机器闸)。 + +产品码 = game/core + game/data + game/presentation(smoke/tests 豁免——测量面容器直构是惯例)。 +三条规则: + R1 无类型容器声明:显式 Dictionary/Array 非参数化类型注解(审计 §2.2「73 处」同口径—— + `:= {}` 推断形态属类型推断合法面不算债)。 + R2 有值返回缺返回类型:func 无 `->` 且函数体内存在 `return <非空值>`(纯副作用 -> void + 不属本闸危害域,归静态写法面)。 + R3 参数缺类型:func 声明参数无 `: Type`(_init 豁免——构造注入面形态自由)。 +白名单:行内 `# type-hint: ignore` 豁免该行(V7 显式白名单通道)。 +退出码:0=归零,1=有存量(逐条列 file:line)。用法:python tools/audit_type_hints.py +""" +import re +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent +SCAN_DIRS = ["game/core", "game/data", "game/presentation"] + +R1A_UNPARAM = re.compile(r"\bvar\s+\w+\s*:\s*(Dictionary|Array)\b(?!\[)") +R1B_LITERAL = re.compile(r"^\s*var\s+\w+\s*(:?=)\s*[\{\[]") +R1B_TYPED = re.compile(r"^\s*var\s+\w+\s*:\s*[\w\[\]]+") +FUNC_DECL = re.compile(r"^(\s*)func\s+(_?\w+)\s*\((.*)\)\s*(->\s*.+)?\s*(#.*)?$") +PARAM_SPLIT = re.compile(r",(?![^(\[]*[)\]])") +RETURN_VALUE = re.compile(r"^\s*return\s+\S") + + +def strip_strings(line): + line = re.sub(r'"[^"]*"', '""', line) + line = re.sub(r"'[^']*'", "''", line) + if line.lstrip().startswith("#"): + return "" + return line.split("#")[0] + + +def audit_file(path): + hits = [] + lines = path.read_text(encoding="utf-8").split("\n") + for idx, raw in enumerate(lines): + if "# type-hint: ignore" in raw: + continue + code = strip_strings(raw) + # R1a 显式非参数化容器类型 + if R1A_UNPARAM.search(code): + hits.append((idx + 1, "R1", raw.strip()[:90])) + continue + fm = FUNC_DECL.match(code) + if not fm: + continue + indent, fname, params, ret = fm.group(1), fm.group(2), fm.group(3), fm.group(4) + # R3 参数缺类型(_init 豁免) + if fname != "_init" and params.strip(): + for prm in PARAM_SPLIT.split(params): + prm = prm.strip() + if prm and ":" not in prm: + hits.append((idx + 1, "R3", "%s(参数 %s)" % (raw.strip()[:80], prm.split("=")[0].strip()))) + # R2 有值返回缺返回类型(函数体 = 缩进更深的后续连续块) + if not ret and fname != "_init": + body = [] + j = idx + 1 + while j < len(lines): + l = lines[j] + if l.strip() == "": + j += 1 + continue + if len(l) - len(l.lstrip("\t")) > len(indent): + body.append(strip_strings(l)) + j += 1 + continue + break + if any(RETURN_VALUE.match(b) for b in body): + hits.append((idx + 1, "R2", raw.strip()[:90])) + return hits + + +def main(): + if hasattr(sys.stdout, "reconfigure"): + sys.stdout.reconfigure(encoding="utf-8", errors="replace") + total = 0 + for d in SCAN_DIRS: + for p in sorted((ROOT / d).rglob("*.gd")): + for line, rule, text in audit_file(p): + print("[%s] %s:%d: %s" % (rule, p.relative_to(ROOT).as_posix(), line, text)) + total += 1 + if total: + print("[audit_type_hints] FAIL——存量 %d 处(R1 无类型容器/R2 有值返回缺类型/R3 参数缺类型)" % total) + return 1 + print("[audit_type_hints] PASS——产品码类型闸归零(R1/R2/R3)") + return 0 + + +if __name__ == "__main__": + sys.exit(main())