feat(迭代27 T5): 波五a 类型闸拉起+存量收口——审计器 0 存量+warning 配置在位

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+对拍三变体全同
This commit is contained in:
DKChan committed 2026-10-15 01:05:17 +08:00
1 parent 8b9e55673f
commit b605022001
58 files changed
+503 -384

No files matched your search

+4 -4
View File
@@ -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:
@@ -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))
+1 -1
View File
@@ -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)
+6 -6
View File
@@ -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("回复与护甲资源减少")
+21 -21
View File
@@ -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
+1 -1
View File
@@ -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):
+3 -3
View File
@@ -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,
+2 -2
View File
@@ -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)
+1 -1
View File
@@ -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 载荷)
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
@@ -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", []))
+2 -2
View File
@@ -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)
+5 -5
View File
@@ -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)
+9 -9
View File
@@ -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:
+1 -1
View File
@@ -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()
@@ -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
+1 -1
View File
@@ -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)
@@ -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))
+7 -7
View File
@@ -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
+1 -1
View File
@@ -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)
+2 -2
View File
@@ -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
+2 -2
View File
@@ -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)
+2 -2
View File
@@ -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)
+1 -1
View File
@@ -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", ""))))