diff --git a/game/README.md b/game/README.md index 0ec0e54b..e205732f 100644 --- a/game/README.md +++ b/game/README.md @@ -33,6 +33,8 @@ Godot_v4.7.2-stable_win64_console.exe --headless --path . res://smoke/ui_probe.t Godot_v4.7.2-stable_win64_console.exe --headless --path . res://smoke/ui_tune_probe.tscn ``` +其余七场景探针(九探针全 roster 登记——ui_click_probe 行为链 / ui_input_probe 输入合成 / ui_layout_probe 布局 / shot_probe·fx_probe·fx_shot_probe·board_shot_probe 画面与特效采样,同上式运行)+ 板图校准工具 `smoke/calibrate_boards.gd`(board_calibration.csv 重跑入口,--script 运行): + 全冒险期望扫描(零加成三关连探批量——通关率/止步分布/败退节点;调参期难度读数一律走全冒险口径): ``` @@ -79,7 +81,7 @@ Godot_v4.7.2-stable_win64_console.exe --headless --path . --script res://smoke/s - `B1BattleOrchestrator`:begin_battle 门序列增 `b4.notify_battle_start()`(编排唯一性不变)。 - 表/护栏:RewardTable `POOL_TAGS` + `active_inject`;RewardConfigTable + `active_p0/active_decay`、k_dmg 1→0(F5 休眠);ConfigDB 跨表护栏更新(clear_no_value 含即时类 / no_damage_row 扫描全池活行 / 新增 inject_pool 护栏:非可选类拒绝 + 空池致命)。 -**实测(N=200 板/深度,工具 reward_expectation.gd)**:注入 1.00 件/板(j=0,多件板 0)· 占比 小刀 59–65% / 精工 25–29% / 治疗包 7–17%(名义 60/30/10)· 格池零可选类泄漏 ✓。**实际获取期望**(level_monte_carlo「可选类/战」列,N=15):基线 0.88 → +200% 攻击 0.68 件/战(低于分配保证 1.0 的机理 = 注入格均匀随机,落在残余格/速杀未挖区时该件不获取;战斗末无兜底补发——**是否加"战斗末 j==0 补发 1 件"待用户拍板**)。每关合计 ≈ 4.5–6.5 件(原 24.8)✓ 拍板目标达成。 +**实测(N=200 板/深度,工具 reward_expectation.gd)**:注入 1.00 件/板(j=0,多件板 0)· 占比 小刀 59–65% / 精工 25–29% / 治疗包 7–17%(名义 60/30/10)· 格池零可选类泄漏 ✓。**实际获取期望**(level_monte_carlo「可选类/战」列,N=15——工具已退役,数据归档):基线 0.88 → +200% 攻击 0.68 件/战(低于分配保证 1.0 的机理 = 注入格均匀随机,落在残余格/速杀未挖区时该件不获取;战斗末无兜底补发——**是否加"战斗末 j==0 补发 1 件"待用户拍板**)。每关合计 ≈ 4.5–6.5 件(原 24.8)✓ 拍板目标达成。 **⚠️ 供给侧副作用(本轮扫描发现,待拍板)**:可选类退出格子池后,其 25% 概率质量被动流向即时类——攻防 buff 每格频率从 44% 升至 59%,基线通关率 55%→80%(N=15)、整关承伤 127→80:**难度不降反升方向反了**。即时类是"每格必有货"锁定设计下无法清空的(除非引入空白格 = 违反锁定事实)。候选杠杆:① 接受更快的 buff 节奏,敌 HP 曲线上调对冲(C3 主杠杆)② 下调 attack_up/defense_up 在即时池内的相对权重(压 buff 频率)③ buff 幅度下调(触碰 B3 表值)。 diff --git a/game/core/board_data.gd b/game/core/board_data.gd index 7548cb3f..a02ffacf 100644 --- a/game/core/board_data.gd +++ b/game/core/board_data.gd @@ -34,10 +34,6 @@ static func row_major_key(c: Vector2i) -> int: return c.y * 1000 + c.x -func index_of(c: Vector2i) -> int: - return c.y * width + c.x - - func is_mine(c: Vector2i) -> bool: return mines.has(c) @@ -54,10 +50,6 @@ func variant_ids(c: Vector2i) -> Array: return variants.get(c, []) -func has_variant(c: Vector2i, vid: String) -> bool: - return variant_ids(c).has(vid) - - func neighbors8(c: Vector2i) -> Array: var out: Array[Vector2i] = [] for dy in range(-1, 2): diff --git a/game/core/meta_progression.gd b/game/core/meta_progression.gd index 4bf60bb4..96bc2968 100644 --- a/game/core/meta_progression.gd +++ b/game/core/meta_progression.gd @@ -81,6 +81,11 @@ func _workshop_progress(channel: String) -> int: return 0 # 未知渠道恒 0 = 永不解锁(fail-safe 非致命——渠道词表校验兜底,不静默误解锁) +# 道具解锁读数访问器(组合根/呈现层解锁门控查询面——禁再穿透 meta.save 直读 save_manager) +func is_item_unlocked(item_id: String) -> bool: + return save.item_unlocked(item_id) + + # 工坊六行只读投影(呈现层数据源):{item_id, display_name, threshold, unlocked, progress} func workshop_status() -> Array: var out: Array[Dictionary] = [] diff --git a/game/core/minesweeper_rules.gd b/game/core/minesweeper_rules.gd index 6bb50563..548bf0ba 100644 --- a/game/core/minesweeper_rules.gd +++ b/game/core/minesweeper_rules.gd @@ -13,8 +13,6 @@ signal cell_revealed(evt: Dictionary) signal flag_judged(evt: Dictionary) signal round_end_board(payload: Dictionary) # 双发射源之棋盘路径(排雷玩法 外发;stopped 由 行动经济 直发) -const SUB_REASONS := ["not_adjacent", "target_revealed", "target_concealed", "flag_present", "no_path", "out_of_catalog"] # flag_absent 随 flag_pull 退役(2026-09-19) - var db: ConfigDb var action_economy: ActionEconomy # 棋盘状态私有(E2 封装收口 2026-09-13):对外只暴露 current_board()/reveal_record() 等查询面, @@ -75,6 +73,15 @@ func clear_map_cleared() -> bool: return safe_remaining() == 0 +# 未开格存在查询(aux 道具 usable 预判消费——全板扫描只读面) +func has_unrevealed() -> bool: + for y in _board.height: + for x in _board.width: + if not _revealed.has(Vector2i(x, y)): + return true + return false + + # ---- install(编排边界相位 = 行动经济 Idle/Waiting;Active 拒绝——防轮内换板)---- # during_round = 轮内刷新编排路径(2026-09-12 方案 A,战斗结算 唯一调用方):Active 相位合法—— @@ -107,11 +114,11 @@ func install_board(b: BoardData, during_round := false) -> Dictionary: return _outcome(true, "executed", "", "") -# during_round = 轮内刷新编排路径(2026-09-12 方案 A):Active 相位合法——换图前退役旧板 func flag_count() -> int: return _flags.size() +# during_round = 轮内刷新编排路径(2026-09-12 方案 A):Active 相位合法——换图前退役旧板 func retire_board(during_round := false) -> Dictionary: if _board == null: return _outcome(true, "executed", "", "") # 幂等受理 diff --git a/game/core/reward_economy.gd b/game/core/reward_economy.gd index fae67b5c..37052bca 100644 --- a/game/core/reward_economy.gd +++ b/game/core/reward_economy.gd @@ -172,24 +172,6 @@ func polarity_row(cell_type: String, polarity: String) -> Dictionary: return {} -# ---- F6 当量供给(行动经济 条款①对照实物输入——全活行投影 + tier_min_buff)---- - -func equivalence_supply() -> Dictionary: - var live: Array[Dictionary] = [] - var tier_min_buff := 99 - for row in db.reward.rows: - if int(row.weight) <= 0: - continue - live.append(row.duplicate()) # E6:表行值拷贝出口——消费方禁改配置源 - # tier_min_buff = 我侧增益(hero_offense/hero_defense 池的 percent 正向行——attack_up/defense_up)最低档 - if String(row.get("pool_tag", "")) == "cell" and String(row.get("polarity", "")) == "positive" \ - and String(row.get("effect_id", "")) != "": - var eff_row: Dictionary = db.status_effect_row(String(row.effect_id)) - if not eff_row.is_empty() and String(eff_row.get("target_side", "")) == "hero": - tier_min_buff = mini(tier_min_buff, int(row.tier)) - return {"live_rows": live, "tier_min_buff": tier_min_buff} - - # ---- F1 选行算子纯函数 ---- func weighted_pick(rows: Array, r: int) -> String: diff --git a/game/core/save_manager.gd b/game/core/save_manager.gd index 22126da8..e9c91a3f 100644 --- a/game/core/save_manager.gd +++ b/game/core/save_manager.gd @@ -39,7 +39,7 @@ const FORGE_ATTRS := ["max_hp", "atk", "armor_class"] # 属性锻造三档合 var _path: String # 存档文件路径(默认 user://save.json;冒烟注入隔离路径) var _read_only := false # 高版本拒读置位——write_to_disk 一律拒绝(契约 4 禁写) var _data: Dictionary[String, Variant] = {} # 内存数据模型(_init 即内置默认;load 后为档案内容) -var _migrations: Dictionary[int, Callable] = {} # from_version → Callable(data)->data(升一版;生产链现为空——骨架先行) +var _migrations: Dictionary[int, Callable] = {} # from_version → Callable(data)->data(升一版;v1→v3 生产迁移链现行——_init 自注册) func _init(path: String = "user://save.json") -> void: @@ -155,7 +155,7 @@ func write_to_disk() -> bool: # 迁移注册口:from_version → from_version+1 的升级函数(读档时链式调用)。fn 契约: -# Dictionary → Dictionary,须写回 version = from_version + 1。生产迁移当前为空(v1 首版), +# Dictionary → Dictionary,须写回 version = from_version + 1。生产迁移链现行(v1→v3,_init 自注册), # 冒烟经本口验证链式升级。 func register_migration(from_version: int, fn: Callable) -> void: _migrations[from_version] = fn @@ -265,10 +265,10 @@ func _corrupt_fallback(reason: String) -> bool: # 迁移后/低版本档字段补齐(改版不弃档:缺新域按默认补齐,既有值保留) func _normalize(data: Dictionary) -> Dictionary[String, Variant]: var merged := _default_data() + var def_meta: Dictionary = merged["meta"] # 补齐源引用(先于 data 覆盖取出——data 自带 meta 时 merged["meta"] 被换走,此引用仍指默认值) for key in data: merged[key] = data[key] var meta: Dictionary = merged.get("meta", {}) - var def_meta: Dictionary = _default_data()["meta"] for key in def_meta: if not meta.has(key): meta[key] = def_meta[key] diff --git a/game/core/status_container.gd b/game/core/status_container.gd index 718d54a7..30397dc0 100644 --- a/game/core/status_container.gd +++ b/game/core/status_container.gd @@ -106,14 +106,6 @@ func query_active(side: String) -> Array: return out -# 池层数读值(战斗结算·战斗段伏击池 L 的消费面) -func pool_layers(effect_id: String) -> int: - for inst in _instances: - if inst.effect_id == effect_id: - return inst.layers - return 0 - - # 轮末衰减:先兑付后衰减;每实例恰 −1;归 0 移除;返回移除数 k(恰一次执行由 战斗结算 编排信任承载) func decay_round() -> int: if not _open: diff --git a/game/data/schemas/reward_table.gd b/game/data/schemas/reward_table.gd index d97c6712..f8ff8f76 100644 --- a/game/data/schemas/reward_table.gd +++ b/game/data/schemas/reward_table.gd @@ -44,7 +44,8 @@ func validate() -> Array: errs.append({"category": "enum", "message": "%s polarity 出域: %s" % [rid, pol]}) var atk_type: String = String(row.get("attack_type", "")) if atk_type != "" and not ["normal", "pierce", "magic"].has(atk_type): - errs.append({"category": "enum", "message": "%s attack_type 出域 {normal, pierce, magic}: %s" % [rid, atk_type]}) # aux 投放行专属校验(迭代 7 T1):极性恒正/加权入池/effect_id=item_id 非空(悬空在 config_db 跨表层 + errs.append({"category": "enum", "message": "%s attack_type 出域 {normal, pierce, magic}: %s" % [rid, atk_type]}) + # aux 投放行专属校验(迭代 7 T1):极性恒正/加权入池/effect_id=item_id 非空(悬空在 config_db 跨表层 # 对 aux_items 词表校验——reward_dangling_aux)/无 power 无区间(数值语义不复用,道具效果读 AuxItemTable) if ctype == AUX_ITEM_TYPE: # 负向行 = 雷格极性字典占位(雷安同池摇号下雷格可摇中 aux 身份——负路由零兑付兜底, @@ -118,14 +119,6 @@ func validate() -> Array: return errs -func rows_by_pool(pool_tag: String) -> Array: - var out: Array[Dictionary] = [] - for row in rows: - if row.get("pool_tag", "") == pool_tag: - out.append(row) - return out - - func row_by_id(rid: String) -> Dictionary: for row in rows: if row.get("reward_id", "") == rid: diff --git a/game/presentation/aux_interact_controller.gd b/game/presentation/aux_interact_controller.gd index 40580426..c9374082 100644 --- a/game/presentation/aux_interact_controller.gd +++ b/game/presentation/aux_interact_controller.gd @@ -41,7 +41,7 @@ func unlocked_set() -> Dictionary: return out for row in db.aux_active_rows(): var iid := String(row.get("item_id", "")) - if meta.save.item_unlocked(iid): + if meta.is_item_unlocked(iid): out[iid] = true return out @@ -108,11 +108,7 @@ func inventory_projection() -> Array: var cb := Callable() if ms != null: cb = func(_c: Vector2i) -> bool: - for y in ms.current_board().height: - for x in ms.current_board().width: - if not ms.is_revealed(Vector2i(x, y)): - return true - return false + return ms.has_unrevealed() # 只读查询面(排雷玩法 has_unrevealed——手搓全板双循环收口) var out: Array = [] for rec in runner.aux_inventory.query_inventory(cb): var arow: Dictionary = db.aux_item_row(String(rec.get("item_id", ""))) diff --git a/game/presentation/debug_session.gd b/game/presentation/debug_session.gd index d1c34fbf..4979cb51 100644 --- a/game/presentation/debug_session.gd +++ b/game/presentation/debug_session.gd @@ -33,10 +33,6 @@ func _init(db_ref, rng_ref, hero_override: Callable, seed_value: Callable) -> vo _seed_value = seed_value -func is_level_mode() -> bool: - return _level_mode - - # 单场遭遇原语(2026-09-13 用户裁决:单场用户入口已删——本方法仅 ui_probe 单场段使用; # 无延续、standalone 终局语义保留供探针战斗收尾) func start_single(level: int) -> bool: diff --git a/game/presentation/main.gd b/game/presentation/main.gd index 87af7d65..959865d6 100644 --- a/game/presentation/main.gd +++ b/game/presentation/main.gd @@ -870,9 +870,6 @@ func _on_node_continue() -> void: func _on_choices_presented(candidates: Array) -> void: # 零自动推进的停点呈现:整幅节点图 + 图上点击选路(候选按钮退役 2026-09-28——战斗胜利/兑付确认统一到达此处) - if OS.get_environment("T17_LOCK_DEBUG") != "": - print("[choices_dbg] handler entered cands=%d runner_null=%s node=%s" % [ - candidates.size(), str(runner == null), str(runner.node_index) if runner != null else "-"]) if runner == null: return log_view.append_bb(Narrator.route_line(candidates, runner)) @@ -882,8 +879,6 @@ func _on_choices_presented(candidates: Array) -> void: # 的当前节点非 battle 型——三态天然分流)。Boss 胜/失败走 level_finished 另路不经此处。 if _adventure_mode and runner.current_battle() == null \ and String(runner.current_node().get("type", "")) == "battle" and not candidates.is_empty(): - if OS.get_environment("T17_LOCK_DEBUG") != "": - print("[choices_dbg] -> battle stop overlay (node %s)" % str(runner.node_index)) _settlement_stop_pending = true _pending_candidates = candidates.duplicate() _end_overlay.open_victory(Narrator.battle_end_text("victory", false), "下一节点", @@ -892,9 +887,6 @@ func _on_choices_presented(candidates: Array) -> void: _open_modal(_end_overlay) _refresh_presentations() return - if OS.get_environment("T17_LOCK_DEBUG") != "": - print("[choices_dbg] -> level_screen (node %s type %s cands %d)" % [ - str(runner.node_index), String(runner.current_node().get("type", "")), candidates.size()]) _enter_level_screen() level_screen.open_choices(candidates, runner) _refresh_presentations() @@ -1109,7 +1101,7 @@ func _on_refresh_confirmed() -> void: board_zone.toast.show_toast("RAP−%d · 旧板旗作废——新图已就位,AP 保留继续挖" % int(db.action_economy.rap_refresh_cost), "gold") else: # 资源面/上限拒绝(EC13 合法拒绝可见;行动经济 无载荷——客户端按上下文推断) - var rap: int = int(settlement.action_economy.rap) if settlement != null else 0 + var rap: int = int(settlement.presentation_snapshot().get("rap", 0)) if settlement != null else 0 var cost: int = int(db.action_economy.rap_refresh_cost) var result := "rejected_rap" if rap < cost else "rejected_cap" logger.log_ux("ux_refresh_attempt", {"result": result, "round_refresh_index": _refresh_used_round + 1}) diff --git a/game/presentation/views/ap_bar_view.gd b/game/presentation/views/ap_bar_view.gd index c4afab2c..5ec1450c 100644 --- a/game/presentation/views/ap_bar_view.gd +++ b/game/presentation/views/ap_bar_view.gd @@ -171,10 +171,6 @@ func _render_rap_dots(rap: int) -> void: d.color = COL_GOLD if i < shown else COL_DOT_OFF -func set_legend_enabled(on: bool) -> void: - _btn_legend.disabled = not on - - # 探针/装配复用:刷新按钮可用性(预判 = RAP 足额 + 上限未到) func is_refresh_ready() -> bool: return not _btn_refresh.disabled diff --git a/game/presentation/views/battle_log_view.gd b/game/presentation/views/battle_log_view.gd index 009cb04e..37ed2808 100644 --- a/game/presentation/views/battle_log_view.gd +++ b/game/presentation/views/battle_log_view.gd @@ -20,7 +20,6 @@ const BAND_H_DIG := 24 const BAND_H_SETTLE := 84 const AGG_TYPES := ["reward_ineffective"] const COL_GOLD := UiKit.HEX_GOLD -const COL_INK := UiKit.HEX_INK var _db var _log: RichTextLabel diff --git a/game/presentation/views/board_cell_unit.gd b/game/presentation/views/board_cell_unit.gd index 2134a0b9..5bc58028 100644 --- a/game/presentation/views/board_cell_unit.gd +++ b/game/presentation/views/board_cell_unit.gd @@ -67,7 +67,6 @@ var bevel := 0.0: # 顶面倒角强度(0=关——立体原 const FACADE_HIGHLIGHT_PX := 2.0 # 口沿高光线厚(像素) const FACADE_NEAR_FRAC := 0.16 # 立面近段高(格边比——研究件 10/64) const FACADE_FAR_FRAC := 0.25 # 立面远段高(格边比——研究件 16/64) -const FACADE_HIGHLIGHT_COL := Color8(196, 172, 132) # 口沿高光(研究件取色暖米白) const FACADE_NEAR_COL := Color(0, 0, 0, 0.60) # 立面近段黑罩(墙顶 132→52 实测 α≈0.61) const FACADE_FAR_COL := Color(0, 0, 0, 0.82) # 立面远段黑罩(墙顶 132→24 实测 α≈0.82) const PROJECTION_NEAR_COL := Color(0, 0, 0, 0.28) # 投影贴墙段(地面 66→46 实测 α≈0.28) @@ -251,7 +250,7 @@ func _draw_south_facade() -> void: var y_far := edge - far_h var y_near := y_far - near_h var y_hl := y_near - FACADE_HIGHLIGHT_PX - draw_rect(Rect2(0.0, y_hl, edge, FACADE_HIGHLIGHT_PX), FACADE_HIGHLIGHT_COL, true) + draw_rect(Rect2(0.0, y_hl, edge, FACADE_HIGHLIGHT_PX), UiKit.STAND_STONE_RIM, true) # 口沿高光(UiKit.STAND_STONE_RIM 并档——同值单点,清扫批 2026-10-02) draw_rect(Rect2(0.0, y_near, edge, near_h), FACADE_NEAR_COL, true) draw_rect(Rect2(0.0, y_far, edge, far_h), FACADE_FAR_COL, true) diff --git a/game/presentation/views/board_view.gd b/game/presentation/views/board_view.gd index 3ddaee05..2001c672 100644 --- a/game/presentation/views/board_view.gd +++ b/game/presentation/views/board_view.gd @@ -35,7 +35,6 @@ const COL_DIGIT := UiKit.INK # 数字——墨族 #33261B(对 const COL_HERO := UiKit.HERO_BLUE # 小人边框——小人蓝 #2E6E8E const COL_FLAG := UiKit.SEAL_BLACK # 封印黑 #000000(旗朱→封印紫→封印黑,2026-09-28 用户拍板) const COL_GOLD := UiKit.GOLD # 奖励金 #F0A828 -const COL_PATH := UiKit.HERO_BLUE # 路径预览点迹——小人蓝细线 const COL_CONTACT := UiKit.CONTACT_GLOW # 接触带微光基色(蓝族浅一档·近白蓝——§3.2 v2.8;旧绿相退役 2026-09-26) const COL_HL_TARGET := UiKit.GOLD # 悬停目标格边框(金) const REWARD_BADGE := { # 已开格格面资源身份 icon(cell_type 九型键;极性视觉归批 2) @@ -269,8 +268,6 @@ func rebuild(board: BoardData, minesweeper) -> void: _hover = Vector2i(-9, -9) for c in _board.compute_r0(): _r0[c] = true - if _board == null: - return # 瓦片套随板图型切换(T6 换色批——board_gen 词表 v1.1 dungeon/underdark/deep_underdark; # 同图型重复 rebuild 零重载,_tiles_board_type 守卫) var bt: String = _board.board_type if _board.board_type != "" else "dungeon" diff --git a/game/presentation/views/hero_info_panel_view.gd b/game/presentation/views/hero_info_panel_view.gd index 0ae0f173..937eb64f 100644 --- a/game/presentation/views/hero_info_panel_view.gd +++ b/game/presentation/views/hero_info_panel_view.gd @@ -99,12 +99,6 @@ func set_level_bonuses(lines: Array) -> void: _bonus_section.visible = not lines.is_empty() -func set_idle() -> void: - _atk_label.text = "攻击 —" - _armor_label.text = "护甲 AC —" - set_level_bonuses([]) - - func attack_text() -> String: return _atk_label.text diff --git a/game/presentation/views/legend_overlay.gd b/game/presentation/views/legend_overlay.gd index f0eb03b3..c6cec4be 100644 --- a/game/presentation/views/legend_overlay.gd +++ b/game/presentation/views/legend_overlay.gd @@ -3,7 +3,7 @@ # 状态槽空态说明成文载入〔增益状态 D1×①〕)。 # Date: 2026-09-12 # pattern 6 模态 overlay 层级纪律:打开时由组合根隐藏上方遮挡层 + 锁棋盘输入; -# 本类自持开关(toggle),探针断言"打开后自身 visible"——遮挡物断言归场景探针。 +# 本类自持开关(open/close),探针断言"打开后自身 visible"——遮挡物断言归场景探针。 class_name LegendOverlay extends Control @@ -72,13 +72,6 @@ func is_open() -> bool: return visible -func toggle() -> void: - if visible: - close() - else: - open() - - func open() -> void: visible = true diff --git a/game/presentation/views/shop_overlay_view.gd b/game/presentation/views/shop_overlay_view.gd index 8f778a86..de80dd17 100644 --- a/game/presentation/views/shop_overlay_view.gd +++ b/game/presentation/views/shop_overlay_view.gd @@ -1,7 +1,7 @@ # VERTICAL SLICE - NOT FOR PRODUCTION # Date: 2026-09-21(GP+商店房实施批——商店房覆盖层呈现) # 商店覆盖层(模态):标题「商店」/ 金币余额 / 三商品卡全陈列(显示名·档位文案·现价·已购档数)/ -# 购买按钮(余额不足禁用置灰)/ 离开按钮。模态挂接方式仿既有覆盖层(route_overlay 现成参照—— +# 购买按钮(余额不足禁用置灰)/ 离开按钮。模态挂接方式仿既有覆盖层(legend_overlay 现成参照—— # dim + CenterContainer + box,组合根 _register_modal 收口层级)。 # 高内聚边界:纯视图 setter 面——商品快照与余额经组合根喂入(runner.shop_products() 只读投影, # 现价/已购档数由 runner 现算),购买/离开意图经信号外发——零核心写调用、零 runner 持有。 diff --git a/game/presentation/views/standee_body.gd b/game/presentation/views/standee_body.gd index 47bfc9b2..8c58a569 100644 --- a/game/presentation/views/standee_body.gd +++ b/game/presentation/views/standee_body.gd @@ -124,11 +124,6 @@ func _set_hp(hp: int) -> void: _hp_tween.tween_property(self, "hp_value", target, UiKit.motion(0.15)) -func set_idle() -> void: - _set_hp(0) - set_effects([]) - - # 主角/敌方共用——状态效果唯一展示面(壳层伴随件转发;空态 = 图标行隐藏不占位) func set_effects(effects: Array) -> void: if slot_row == null: diff --git a/game/presentation/views/tune_panel.gd b/game/presentation/views/tune_panel.gd index 01b9edfe..599f5264 100644 --- a/game/presentation/views/tune_panel.gd +++ b/game/presentation/views/tune_panel.gd @@ -1,15 +1,14 @@ # VERTICAL SLICE - NOT FOR PRODUCTION # Date: 2026-09-11(2026-09-12 code-review 修复批重构) # 测试用角色数值调整板(模拟天赋树属性提升——仅测试,正式侧归元进度)。 -# 纯视图边界(规范 1.3):本类只承载滑杆 UI 与生效值预览;加成应用 / 重置落表 / 批量模拟编排 -# 全部经信号外发由组合根承接(apply_requested / reset_requested / simulate_requested / closed), +# 纯视图边界(规范 1.3):本类只承载滑杆 UI 与生效值预览;加成应用 / 批量模拟编排 +# 全部经信号外发由组合根承接(apply_requested / simulate_requested / closed); # 模拟进度与报告经 setter 注入——视图零核心写、零 smoke 脚本依赖、不拼报告文案(装配归 presenter)。 # 探针接缝(ADR-0004"合成输入走真实翻译出口"口径):press_apply_for_probe 等三键 = 按钮真实信号路径。 class_name TunePanel extends Control signal apply_requested(atk_pct: int, hp_pct: int) # 应用并关闭——加成落表归组合根单一应用路径 -signal reset_requested() # 重置为 0——视图自清滑杆,落表归组合根 signal simulate_requested(count: int) # 批量模拟编排归组合根(表基线复位 + 单路径施加) signal closed @@ -106,7 +105,6 @@ func _apply_btn_pressed() -> void: func _reset_btn_pressed() -> void: reset_sliders() - reset_requested.emit() func effective_atk() -> int: diff --git a/game/presentation/views/ui_kit.gd b/game/presentation/views/ui_kit.gd index 14b81b8a..73eb135f 100644 --- a/game/presentation/views/ui_kit.gd +++ b/game/presentation/views/ui_kit.gd @@ -21,10 +21,7 @@ const INK_TITLE := Color8(30, 26, 22) # 亮底标题墨 const INK_ROUTE := Color8(24, 20, 16) # 节点图最深墨 const INK_ON_GOLD := Color8(60, 44, 20) # 金底上的深墨 -const PANEL_HERO := Color8(232, 220, 196) # 面板底·主角侧(英雄卡/角色信息框) -const PANEL_ENEMY := Color8(226, 212, 190) # 面板底·敌方卡 const PANEL_DATA := Color8(226, 210, 182) # 面板底·数据框(AP 条/状态槽) -const PANEL_STAGE := Color8(216, 200, 172) # 面板底·战斗画面区 const PANEL_BORDER := Color8(120, 106, 88) # 面板细线框;兼熄灭点阵(DOT_OFF 别名) const GOLD := Color8(240, 168, 40) # 奖励金 @@ -50,12 +47,10 @@ const DUSK := Color8(90, 108, 140) # 暮蓝(全作唯一暮蓝档 const WARM := Color8(240, 226, 200) # 暖白(暗底正文) const WARM_SOFT := Color8(200, 188, 166) # 暖白次级(说明文) const WARM_FAINT := Color8(178, 166, 146) # 暖白弱化(帮助文) -const WARM_ROUTE_SUB := Color8(210, 196, 170) # 节点图编号暖白 const STONE := Color8(120, 110, 98) # 灰石(通道/箭头/失效) const STONE_FAINT := Color8(140, 124, 100) # 灰石弱化(占位提示文) const DARK_UPCOMING := Color8(64, 58, 52) # 未达暗字(底/文同值) -const DARK_TEXT_FAINT := Color8(160, 150, 138)# 未达节点文字 const BRICK := Color8(201, 175, 131) # 棋盘·未开掩尘地砖 #C9AF83 const FLOOR := Color8(179, 154, 120) # 棋盘·已开裸土 #B39A78 @@ -78,7 +73,7 @@ const STAGE_BG := Color8(62, 52, 42) # B 区舞台暗底(方向稿 re const BEAM_LIGHT := Color8(255, 236, 200) # B 区顶光柱暖光(用时乘 alpha 渐隐——battle_stage_view 光柱层) const STAND_STONE := Color8(150, 130, 100) # 站桩石板地台·板身(standee_body 方向稿取色) const STAND_STONE_EDGE := Color8(90, 76, 58) # 站桩石板地台·描边 -const STAND_STONE_RIM := Color8(196, 172, 132) # 站桩石板地台·顶缘受光线(与 board_cell_unit.FACADE_HIGHLIGHT_COL 异源同值——并档归清扫卡) +const STAND_STONE_RIM := Color8(196, 172, 132) # 站桩石板地台·顶缘受光线(board_cell_unit 口沿高光已并档至此——清扫批 2026-10-02) const CAMP_HEADER_BAND := Color8(122, 82, 50) # 营地柜台头带深棕(4-camp.png 方向稿取色) const CAMP_ROW_PLATE := Color8(240, 230, 206) # 营地行板暖米(同上) const CAMP_BADGE_PANEL := Color8(40, 32, 24) # 营地钥石徽暗底(同上) diff --git a/game/smoke/level_autoplay.gd b/game/smoke/level_autoplay.gd index aca66b9d..57b7699b 100644 --- a/game/smoke/level_autoplay.gd +++ b/game/smoke/level_autoplay.gd @@ -15,7 +15,6 @@ # 驱动循环按 route 策略显式 pick_next_node;route = "min"/"max"(候选节点编号最小/最大—— # 确定性路径轴,零随机流自开;默认 "min" 既有调用方签名兼容)。 # 2026-09-21 冒险结构实现批:run_adventure 三关连探驱动(AdventureRunner——主种子恰一次、 -# 2026-09-21 冒险结构实现批:run_adventure 三关连探驱动(AdventureRunner——主种子恰一次、 # 通关兑付停点确认、adventure_finished 置 result/summary;外层每关重取 runner 实例经 _drive_level # 复用单关驱动)。run_level 驱动段抽成 _drive_level——签名与返回结构零变化,行为逐位不变。 # 2026-10-01 迭代 10 奖励重构批:大奖励立即确认改三选一停点驱动(候选取首件/替换放弃新件)。 diff --git a/game/smoke/smoke_all.gd b/game/smoke/smoke_all.gd index 46181d99..2cb95873 100644 --- a/game/smoke/smoke_all.gd +++ b/game/smoke/smoke_all.gd @@ -750,7 +750,7 @@ func _smoke_level() -> void: var expect: int = mini(int(floor(float(eff_max) * 0.3)), eff_max - rest.hp_before_rest) check(rest.rest_healed == expect, "恢复点回复 = floor(生效max_hp×30%%) 与缺口的较小值(%d/%d,生效 max %d)" % [rest.rest_healed, expect, eff_max]) else: - check(false, "恢复点未到达(seed 4242 hp+100 在恢复点前败北?%s)" % rest.result) + check(false, "恢复点未到达(seed 42 hp+100 在恢复点前败北?%s)" % rest.result) # ③ 裸装败北路径(atk1/hp10——Roguelike 基线应难以通关) var lose = autoplay.run_level(_db, 42, 1, -80, -80) check(lose.result == "defeat", "关卡·裸装(atk≈1/hp≈10)败北路径可达(实测:%s)" % lose.result) diff --git a/game/smoke/ui_click_probe.gd b/game/smoke/ui_click_probe.gd index 44a7a535..beeef8eb 100644 --- a/game/smoke/ui_click_probe.gd +++ b/game/smoke/ui_click_probe.gd @@ -542,12 +542,3 @@ func _dump_state() -> void: str(diag.battle_result), str(diag.settling)]) print(" board_installed=%s facts_path='%s' refresh_used=%s flags=%s" % [ str(diag.minesweeper_board_installed), str(diag.facts_path), str(diag.refresh_used_round), str(_flags_placed)]) - # T17 硬锁 v3 归因面:收尾链全态 - print(" [t17dump] q=%d end_overlay=%s stop_pending=%s ref_same=%s level_screen=%s node_overlay=%s node=%s" % [ - main._row_queue.size(), str(main._end_overlay.is_open()), str(main._settlement_stop_pending), - str(main.settlement == main.runner.current_battle()), str(main.level_screen.visible), - str(main.node_overlay.visible), str(main.runner.node_index)]) - print(" [t17dump2] type=%s bonus_overlay=%s inter_overlay=%s shop=%s cands=%s" % [ - String(main.runner.current_node().get("type", "")), str(main.bonus_overlay.visible), - str(main._inter_overlay.visible), str(main.shop_overlay.visible), - str(main.runner.next_candidate_ids())]) diff --git a/game/tests/README.md b/game/tests/README.md index d639d919..d807138e 100644 --- a/game/tests/README.md +++ b/game/tests/README.md @@ -4,7 +4,7 @@ **Test Framework**: 双载体(ADR-0004)——`--headless --script` 逻辑冒烟 + headless 场景探针;gdUnit4 **deferred**(D2 OQ1 关闭形态,正式 CI 期再议) **CI**: `.github/workflows/tests.yml`(首次 push 到 GitHub 后自动生效;仓库 2026-09-12 尚未初始化 git) **Setup date**: 2026-09-12 -**参考实现**: `game/smoke/`(冒烟 96/96 + 三 UI 探针——正式工程的活样板) +**参考实现**: `game/smoke/`(冒烟 628+ 断言 + 九场景探针——正式工程的活样板) ## Directory Layout @@ -26,7 +26,7 @@ godot --headless --script tests/run_all.gd # 统一入口(CI 调用 godot --headless --script tests/unit/example_test.gd # 场景探针(双载体之二)——以探针场景为运行目标,走完整项目引导(autoload 在位) -godot --headless --path . res://tests/probes/.tscn +godot --headless --path . res://smoke/.tscn # CI 旁必须配外部 wall-clock 超时(ADR-0004:引擎无死锁探测) timeout 120s godot --headless --script tests/run_all.gd diff --git a/game/tests/smoke/critical-paths.md b/game/tests/smoke/critical-paths.md index d2ef2fa9..9fccca72 100644 --- a/game/tests/smoke/critical-paths.md +++ b/game/tests/smoke/critical-paths.md @@ -17,17 +17,16 @@ 5. 移动:已开格瞬移、**已引爆雷格可通行可读**(工程债呈现半边) 6. 停手:RAP 1:1 转存、下轮 AP 硬重置同图继续 7. 轮末结算:明细逐行呈现、来源标注可读(P2——"这一行是我哪步扫雷带来的"可答) -8. 道具使用窗口:授予段后弹出、至多选一件、满血治疗包 usable_now 禁选态正确 -9. 清图:清图奖励兑付 + 完美排雷高光(零误标时双份)+ 换新图;未刷新的停手轮不换图 -10. 轮内刷新(方案 A):Active 当场换图、AP 保留、RAP 扣减、每轮 ≤2 次、跨图事实结算完整 +8. 清图:清图奖励兑付 + 完美排雷高光(零误标时双份)+ 换新图;未刷新的停手轮不换图 +9. 轮内刷新(方案 A):Active 当场换图、AP 保留、RAP 扣减、每轮 ≤2 次、跨图事实结算完整 ## Battle & Level -11. 战斗段:主角先手攻击行(hero_attack 来源)、敌方行为环读怪(姿态↔下一拍)、死亡即不动手 -12. 胜负判定与 battle_over 清理全序;库存跨战斗延续、关卡末"剩余道具 ×N 清空"反馈 -13. 关卡推进:节点链兑付(奖励点/恢复点)、Boss 房(Dmax 行)、层间 HP 携带 +10. 战斗段:主角先手攻击行(hero_attack 来源)、敌方行为环读怪(姿态↔下一拍)、死亡即不动手 +11. 胜负判定与 battle_over 清理全序;aux 魔法道具关内跨战斗延续(三选一 BonusPickOverlay 入包、C 区槽位两步交互施用)、关卡末"剩余道具 ×N 清空"反馈(aux_cleared) +12. 关卡推进:节点链兑付(奖励点/恢复点)、Boss 房(Dmax 行)、层间 HP 携带 ## Data & Perf -14. 数值调整板:**打开可见**(覆盖层层级回归——缺陷⑤)、滑条即时生效、批量模拟可跑 -15. 无明显卡顿(60fps 目标);连续 5 分钟无内存异常增长;窗口/结算屏文案中文显示正常(CJK 字体核对) +13. 数值调整板:**打开可见**(覆盖层层级回归——缺陷⑤)、滑条即时生效、批量模拟可跑 +14. 无明显卡顿(60fps 目标);连续 5 分钟无内存异常增长;窗口/结算屏文案中文显示正常(CJK 字体核对) diff --git a/game/tests/unit/README.md b/game/tests/unit/README.md index 810dbd6e..d7d46e47 100644 --- a/game/tests/unit/README.md +++ b/game/tests/unit/README.md @@ -3,4 +3,4 @@ 每系统一个子目录(`reward_economy/`、`board_generator/`…)。协议与命名见 `tests/README.md`。 **活样板**:`example_test.gd`(本目录)——第一个真实测试请以它为模板。 -**参考实现**:`game/smoke/smoke_all.gd`(96 条断言的组织方式)。 +**参考实现**:`game/smoke/smoke_all.gd`(628+ 条断言的组织方式)。