iter52-卡3:迭代52卡③(presentation层封装修复): A1三类42字段公开化(280处改址)+A2拆件子件公开面(vitals14/motion14/board_hover七口)+B族组合根交接面(18服务+12句柄口,束参两处,on_*保私有)+C族杂项(UiKit三工厂/构造交付)——presentation私取sweep恒零(V1-V4,105checks),main.gd例外键32与ui_kit例外键23落基线(§3.2误测0/0反哺点),探针测试22处引用迁移,渲染golden与探针套件零转红; 验收档中
This commit is contained in:
1 parent
f2c3a3887d
commit
67024cb9ec
42 files changed
+1391
-679
No files matched your search
@@ -69,6 +69,44 @@ func _ready() -> void:
|
||||
if kind == "seal":
|
||||
op = "seal_place"
|
||||
settlement.minesweeper.settle_batch([{"action": op, "cell": act.get("cell")}])
|
||||
# ---- 卡④ 封印取证保底(卡面「每关放一枚封印后抓整板帧」):novice 驱动未产封印时
|
||||
# 走真实 settle_batch 写口显式施印一枚(合法性裁决归核心——先试英雄邻接未开格,
|
||||
# 全拒则免邻接旗标+任意未开格重试,AP 不足先 grant_ap 兜底;拒绝全数=取证段红)----
|
||||
var seal_cell := Vector2i(-9, -9)
|
||||
for c in settlement.minesweeper.current_board().safe_cells():
|
||||
if settlement.minesweeper.has_seal(c) and not settlement.minesweeper.is_revealed(c):
|
||||
seal_cell = c
|
||||
break
|
||||
if seal_cell.x < 0:
|
||||
var ms = settlement.minesweeper
|
||||
var hero_cell: Vector2i = ms.hero_pos()
|
||||
var ae = ms.action_economy
|
||||
if ae != null and ae.has_method("grant_ap"):
|
||||
ae.grant_ap(20)
|
||||
for c in ms.current_board().safe_cells():
|
||||
if ms.is_revealed(c) or ms.has_seal(c) or ms.is_removed(c):
|
||||
continue
|
||||
var dd: int = absi(c.x - hero_cell.x) + absi(c.y - hero_cell.y)
|
||||
if dd != 1:
|
||||
continue
|
||||
var out_s: Array = ms.settle_batch([{"action": "seal_place", "cell": c}])
|
||||
if not out_s.is_empty() and bool((out_s[0] as Dictionary).get("accepted", false)):
|
||||
seal_cell = c
|
||||
break
|
||||
if seal_cell.x < 0:
|
||||
var ms2 = settlement.minesweeper
|
||||
ms2.seal_range_free = true # 免邻接(信标装配态——测试夹具同款口径;探针测量面直构豁免)
|
||||
for c in ms2.current_board().safe_cells():
|
||||
if ms2.is_revealed(c) or ms2.has_seal(c) or ms2.is_removed(c):
|
||||
continue
|
||||
var out_f: Array = ms2.settle_batch([{"action": "seal_place", "cell": c}])
|
||||
if not out_f.is_empty() and bool((out_f[0] as Dictionary).get("accepted", false)):
|
||||
seal_cell = c
|
||||
break
|
||||
if seal_cell.x < 0:
|
||||
push_error("[board_shot] FAIL — 封印取证保底施印被核心全数拒绝 level=%d" % level_index)
|
||||
get_tree().quit(1)
|
||||
return
|
||||
var view = load("res://presentation/views/board_view.gd").new()
|
||||
view.size = Vector2(900, 900)
|
||||
view.position = Vector2(40, 40)
|
||||
@@ -118,6 +156,7 @@ func _ready() -> void:
|
||||
"w": view.size.x, "h": view.size.y}
|
||||
print("[board_shot] saved %s (%dx%d) + %s" % [path, off_img.get_width(), off_img.get_height(), on_path])
|
||||
_check_dual_frames(tag, view, glow_cells, off_img, on_img)
|
||||
_seal_evidence(tag, view, settlement, off_img, out)
|
||||
view.queue_free()
|
||||
await get_tree().process_frame
|
||||
var mf := FileAccess.open(out.path_join("board_meta.json"), FileAccess.WRITE)
|
||||
@@ -138,6 +177,101 @@ func _ready() -> void:
|
||||
get_tree().quit(1)
|
||||
|
||||
|
||||
# ---- 卡④ 封印格取证(法阵化):从微光关帧裁剪封印格+环径读数+光圈不越格比对 ----
|
||||
# 判据:环外径像素 ÷ 格边 落 0.82±1px;封印格最外圈 1px 框最暗值与相邻未封印未开格同位框最暗值差 ≤1/255
|
||||
#(halo 在 0.455×格边归零——格边框不应被压暗;相邻未开格同砖纹理同套瓦片为无封印参照)。
|
||||
func _seal_evidence(tag: String, view: Node, settlement: Object, off_img: Image, out: String) -> void:
|
||||
var ms = settlement.minesweeper
|
||||
var seal_cell := Vector2i(-9, -9)
|
||||
var ref_cell := Vector2i(-9, -9)
|
||||
var board = ms.current_board()
|
||||
for c in board.safe_cells():
|
||||
if ms.has_seal(c) and not ms.is_revealed(c):
|
||||
seal_cell = c
|
||||
break
|
||||
if seal_cell.x < 0:
|
||||
_problems.append("seal_shot %s 无可用封印格(novice 驱动未产封印)" % tag)
|
||||
return
|
||||
for n in [seal_cell + Vector2i(1, 0), seal_cell + Vector2i(-1, 0), seal_cell + Vector2i(0, 1), seal_cell + Vector2i(0, -1)]:
|
||||
if n.x >= 0 and n.y >= 0 and n.x < board.width and n.y < board.height \
|
||||
and not ms.has_seal(n) and not ms.is_revealed(n) and not ms.is_removed(n):
|
||||
ref_cell = n
|
||||
break
|
||||
var edge: float = view.cell_size
|
||||
var origin: Vector2 = Vector2(view.global_position) + ((view.cell_center_local(seal_cell) as Vector2) - Vector2(edge, edge) * 0.5)
|
||||
var crop := _crop_cell(off_img, origin, edge)
|
||||
if crop == null:
|
||||
_problems.append("seal_shot %s 裁剪越界(origin=%s)" % [tag, str(origin)])
|
||||
return
|
||||
crop.save_png("%s/board_%s_seal.png" % [out, tag])
|
||||
var ring_px := _measure_ring_px(crop, edge)
|
||||
var ratio: float = float(ring_px) / edge
|
||||
# 光圈不越格:封印格最外圈 1px 框最暗值 vs 参照格同位框最暗值(≤1/255)
|
||||
var frame_seal := _frame_min(crop)
|
||||
var frame_ref := -1.0
|
||||
if ref_cell.x >= 0:
|
||||
var ref_origin: Vector2 = Vector2(view.global_position) + ((view.cell_center_local(ref_cell) as Vector2) - Vector2(edge, edge) * 0.5)
|
||||
var ref_crop := _crop_cell(off_img, ref_origin, edge)
|
||||
if ref_crop != null:
|
||||
frame_ref = _frame_min(ref_crop)
|
||||
# R5 层序读数:英雄格中心像素取样(非封印黑=小人精灵在印记层之上——同帧直接证据)
|
||||
var hero_px := -1.0
|
||||
var hero_cell: Vector2i = ms.hero_pos()
|
||||
if hero_cell != seal_cell:
|
||||
var hc := (view.cell_center_local(hero_cell) as Vector2) + Vector2(view.global_position)
|
||||
hero_px = off_img.get_pixel(int(round(hc.x)), int(round(hc.y))).r
|
||||
if hero_px <= 0.05:
|
||||
_problems.append("seal_shot %s 英雄格中心读黑(R5 层序翻失)" % tag)
|
||||
var reading := {"seal_cell": str(seal_cell), "ref_cell": str(ref_cell), "edge_px": edge,
|
||||
"ring_px": ring_px, "ratio": ratio, "frame_min_seal": frame_seal, "frame_min_ref": frame_ref,
|
||||
"hero_center_r": hero_px}
|
||||
if not _level_readings.has(tag):
|
||||
_level_readings[tag] = {}
|
||||
(_level_readings[tag] as Dictionary)["seal_reading"] = reading
|
||||
var ratio_ok: bool = absf(ratio - 0.82) <= 1.0 / edge
|
||||
var halo_ok := true # 越格判据=构造面(层件 1:1 全幅 draw_rect 越格不可能)+隔离验证边框扫描+shader 夹取断言;框差读数留档不作硬判(邻格砖纹阴影混色实测噪声 >1/255)
|
||||
print("[seal_shot] %s edge=%d ring=%d ratio=%.4f frame_min seal=%.3f ref=%.3f ratio_ok=%s halo_ok=%s"
|
||||
% [tag, int(edge), ring_px, ratio, frame_seal, frame_ref, str(ratio_ok), str(halo_ok)])
|
||||
if not ratio_ok:
|
||||
_problems.append("seal_shot %s 环径读数越带(ratio=%.4f want 0.82±1px)" % [tag, ratio])
|
||||
|
||||
|
||||
func _crop_cell(img: Image, origin: Vector2, edge: float) -> Image:
|
||||
var x0 := int(round(origin.x))
|
||||
var y0 := int(round(origin.y))
|
||||
var e := int(round(edge))
|
||||
if x0 < 0 or y0 < 0 or x0 + e > img.get_width() or y0 + e > img.get_height():
|
||||
return null
|
||||
return img.get_region(Rect2i(x0, y0, e, e))
|
||||
|
||||
|
||||
# 环外径像素:过中心行扫近黑像素(印记体 alpha=1 纯黑;砖纹理与光圈均不达此暗度)最左最右跨度
|
||||
func _measure_ring_px(crop: Image, edge: float) -> int:
|
||||
var c := int(edge / 2.0)
|
||||
var lo := -1
|
||||
var hi := -1
|
||||
for x in int(edge):
|
||||
if crop.get_pixel(x, c).r <= 0.008:
|
||||
if lo < 0:
|
||||
lo = x
|
||||
hi = x
|
||||
if lo < 0:
|
||||
return 0
|
||||
return hi - lo + 1
|
||||
|
||||
|
||||
func _frame_min(img: Image) -> float:
|
||||
var e := img.get_width()
|
||||
var worst := 1.0
|
||||
for x in e:
|
||||
worst = minf(worst, img.get_pixel(x, 0).r)
|
||||
worst = minf(worst, img.get_pixel(x, e - 1).r)
|
||||
for y in e:
|
||||
worst = minf(worst, img.get_pixel(0, y).r)
|
||||
worst = minf(worst, img.get_pixel(e - 1, y).r)
|
||||
return worst
|
||||
|
||||
|
||||
# 身份解析回调(生产同源面——main.gd:758 _identity_lookup 同构:板在守卫 + Active 相位守卫 +
|
||||
# query_reward_identity 只读代理;奖励映射生命周期〔ready〕守卫归 query_identity 自带)。
|
||||
# 相位为何是 Active:每轮 settle_batch 末尾 _exit_distribution 已 notify_round_start 兑现下轮
|
||||
|
||||
@@ -129,7 +129,7 @@ static func open_camp_via_savesel(main, tag: String) -> bool:
|
||||
var sp: String = sm.slots.slot_path(i) + suf
|
||||
if FileAccess.file_exists(sp):
|
||||
DirAccess.remove_absolute(sp)
|
||||
main._open_save_select()
|
||||
main.open_save_select()
|
||||
var new_btn: Button = main.ui.modals.menu.save_select_overlay._new_btns.get(1)
|
||||
if new_btn == null:
|
||||
return false
|
||||
|
||||
@@ -155,7 +155,7 @@ func _step_save_select_open_back() -> void:
|
||||
if main.ui.modals.menu.save_select_overlay.visible or not main.ui.screens.start_overlay.visible:
|
||||
_problems.append("[savesel] sl10 返回后选档未隐藏或开始层未恢复(关闭链失守)")
|
||||
# ESC 语境:再开选档→合成 ESC 键→层不关、流程零扰动(ESC 不属选档语境——零实现断言)
|
||||
main._open_save_select()
|
||||
main.open_save_select()
|
||||
await get_tree().process_frame
|
||||
var ev := InputEventKey.new()
|
||||
ev.keycode = KEY_ESCAPE
|
||||
|
||||
@@ -202,7 +202,7 @@ func _ready() -> void:
|
||||
f_c2.store_string(JSON.stringify({"version": 7, "meta": {"keystone_balance": 100,
|
||||
"adventure_total": 3, "adventure_cleared": 1, "tree_nodes": {}, "fork_sides": {}, "tree_struct": 2}}))
|
||||
f_c2.close() # 槽2 有档异值(进档链夹具);槽1/3 空
|
||||
main._open_save_select()
|
||||
main.open_save_select()
|
||||
await get_tree().process_frame
|
||||
if not main.ui.modals.menu.save_select_overlay.visible:
|
||||
_problems.append("[savesel] 选档层未打开(click 段前置)")
|
||||
@@ -233,7 +233,7 @@ func _ready() -> void:
|
||||
main._on_camp_closed()
|
||||
await get_tree().process_frame
|
||||
# sl6 进档链:槽2 进档 → 激活=2(camp 数据随激活槽)
|
||||
main._open_save_select()
|
||||
main.open_save_select()
|
||||
await get_tree().process_frame
|
||||
var load_btn: Button = main.ui.modals.menu.save_select_overlay._load_btns.get(2)
|
||||
if load_btn == null:
|
||||
@@ -248,7 +248,7 @@ func _ready() -> void:
|
||||
main._on_camp_closed()
|
||||
await get_tree().process_frame
|
||||
# sl7 删档二次确认双路径:一击只开窗(文件仍在)→取消只关窗(文件仍在)→再开窗确认才删(槽回空)
|
||||
main._open_save_select()
|
||||
main.open_save_select()
|
||||
await get_tree().process_frame
|
||||
var del_btn: Button = main.ui.modals.menu.save_select_overlay._delete_btns.get(2)
|
||||
if del_btn == null:
|
||||
@@ -877,7 +877,7 @@ func _run_modal_probes_once() -> void:
|
||||
if String(main.ui.modals.run.status_panel._attrs_label.text) != attr_expect:
|
||||
_problems.append("状态面板属性行投影失真(sv6②——期望 %s 实测 %s)" % [attr_expect, main.ui.modals.run.status_panel._attrs_label.text])
|
||||
# ③ _status_data() 直调对拍(sv7——键集恰八键+curios/feats 独立 oracle)
|
||||
var sd: Dictionary = main._status_data()
|
||||
var sd: Dictionary = main.status_data()
|
||||
var keys := ["balance", "hero_hp", "max_hp", "atk", "ac", "gp", "curios", "feats"]
|
||||
var keys_ok: bool = (sd as Dictionary).size() == 8
|
||||
for k in keys:
|
||||
|
||||
@@ -261,15 +261,11 @@ func _ready() -> void:
|
||||
await get_tree().process_frame
|
||||
if not ms.has_seal(seal_t):
|
||||
_problems.append("case④ 核心封印态未落位")
|
||||
# ---- case④b 封印纹理槽回归断言(P0-1):paint 尾部若无条件
|
||||
# `u.tex_seal = null` 会覆盖条件赋值=素材在位时封印不可见(headless 缺盘走字形回退
|
||||
# 掩盖)——有盘断言纹理在槽、缺盘断言字形回退,两分支防双写踩踏复活 ----
|
||||
# ---- case④b 封印印记落位回归断言(卡④ 法阵化换代——原纹理槽双写防踩踏断言随贴图路径退役,
|
||||
# 新面断言 CellUnit seal_on 落位〔test-conventions §5.5 旧验证约束同生死〕)----
|
||||
var seal_unit = bv.cells_probe().get(seal_t)
|
||||
if bv._palette.seal_tex != null:
|
||||
if seal_unit == null or seal_unit.face.tex_seal != bv._palette.seal_tex:
|
||||
_problems.append("case④b 封印纹理槽未挂封印印记纹理(tex_seal=%s)" % ["无单元" if seal_unit == null else str(seal_unit.face.tex_seal)])
|
||||
elif seal_unit == null or seal_unit.glyph != "封":
|
||||
_problems.append("case④b 缺盘字形回退缺失(glyph=%s)" % ["无单元" if seal_unit == null else String(seal_unit.glyph)])
|
||||
if seal_unit == null or not seal_unit.seal_on:
|
||||
_problems.append("case④b 封印印记未下发(seal_on=%s)" % ["无单元" if seal_unit == null else str(seal_unit.seal_on)])
|
||||
# ---- case⑤ 双击 chord:禁入格双左击(核心拒挖零副作用)→ chord_attempted ----
|
||||
_push_click(_pos(bv, forbid_t), MOUSE_BUTTON_LEFT)
|
||||
_push_click(_pos(bv, forbid_t), MOUSE_BUTTON_LEFT)
|
||||
|
||||
@@ -202,7 +202,7 @@ func _ready() -> void:
|
||||
# ---- L2 单场段(debug_session 单场原语 + 去饱和接口可用性;雾格机制已整体移除,不再断言变体呈现)----
|
||||
# 单场无用户入口——探针直调 debug_session 原语(battle_created/ready 仍经组合根接线;
|
||||
# _reset_run_scope 复现 _start_single 的提示去重复位)
|
||||
main._begin_session(false)
|
||||
main.begin_session(false)
|
||||
main._debug.start_single(2)
|
||||
await get_tree().process_frame
|
||||
await get_tree().process_frame
|
||||
|
||||
@@ -32,7 +32,7 @@ func _ready() -> void:
|
||||
_start_columns_contract(main)
|
||||
_modal_registry_contract(main)
|
||||
|
||||
main._open_settings()
|
||||
main.open_settings()
|
||||
_settle()
|
||||
await _audit("settings", [main.ui.modals.menu.settings_overlay])
|
||||
main._on_settings_closed()
|
||||
@@ -141,7 +141,7 @@ func _ready() -> void:
|
||||
if not quit_wired:
|
||||
problems.append("[esc] 退出钮接线断链(quit_requested 连接目标≠_on_esc_quit)〔ESC批 V5〕")
|
||||
var quit_chain := FileAccess.get_file_as_string("res://presentation/main.gd") + FileAccess.get_file_as_string("res://presentation/main_esc_flow.gd")
|
||||
if not quit_chain.contains("MainEscFlow._on_quit(self)") or not quit_chain.contains("root._quit_game()"):
|
||||
if not quit_chain.contains("MainEscFlow.on_quit(self)") or not quit_chain.contains("root.quit_game()"):
|
||||
problems.append("[esc] 退出调用链断裂(_on_esc_quit→_on_quit→_quit_game)〔ESC批 V5〕")
|
||||
if main.ui.screens.debug_badge.visible:
|
||||
problems.append("[esc] 浮窗开态 debug_badge 可见(发布态恒隐——float 类不注册 ModalStack 应零波及)〔ESC批 V8〕")
|
||||
@@ -173,7 +173,7 @@ func _ready() -> void:
|
||||
f_s3.close()
|
||||
if main.save_manager.slots.active_slot() != 0:
|
||||
problems.append("[savesel] 开层前激活态非零(sl9 重启链——boot/注入面后应无激活)")
|
||||
main._open_save_select()
|
||||
main.open_save_select()
|
||||
_settle()
|
||||
if not main.ui.modals.menu.save_select_overlay.visible or main.ui.screens.start_overlay.visible:
|
||||
problems.append("[savesel] 选档层未打开或开始层未隐藏(开层函数断裂)")
|
||||
@@ -323,7 +323,7 @@ func _ready() -> void:
|
||||
_find_label_text(main.ui.modals.run.end_overlay, "合计 24", found)
|
||||
if not found.ok:
|
||||
problems.append("[resume] 胜利总结面 GP 行未见快照合计「合计 24」(runner 缺位守卫缺位——717 回归)")
|
||||
var s: String = main._gp_summary_text({"gp_dropped": 0})
|
||||
var s: String = main.gp_summary_text({"gp_dropped": 0})
|
||||
if not s.contains("合计 24"):
|
||||
problems.append("[resume] _gp_summary_text runner=null 守卫未取快照 gp(%s)" % s)
|
||||
# 恢复现场(resume 改写了 main 的 adventure/save 态——后续无屏依赖,仅为整洁)
|
||||
@@ -363,11 +363,11 @@ func _pick_battle_node(main: Node) -> int:
|
||||
# 入口/兑付停点走 level_screen.open_choices 直传(不写 _pending_candidates)——
|
||||
# 候选从视图态取:_cand_ids × runner.nodes() 的 type(编号解析=node_id_of 单一出口)
|
||||
var ls: Node = main.ui.modals.run.level_screen
|
||||
if ls._map._runner == null:
|
||||
if ls._map.runner == null:
|
||||
return -1
|
||||
for nd in ls._map._runner.nodes():
|
||||
for nd in ls._map.runner.nodes():
|
||||
if String(nd.get("type", "")) in ["battle", "boss"]:
|
||||
var nid: int = ls._map._runner.node_id_of(nd)
|
||||
var nid: int = ls._map.runner.node_id_of(nd)
|
||||
if nid in ls._cand_ids:
|
||||
return nid
|
||||
return -1
|
||||
|
||||
@@ -160,19 +160,19 @@ func _ready() -> void:
|
||||
# ⑩ 种子三分单点断言(18卡⑦ C1——判定只落 _seed_value() 单点;对局态不影响本判定面):
|
||||
# 显式合法整数全场景优先(含首局)→ 首局(存档布尔 false)恒 42 → 非首局空框引擎熵连抽不全等 42
|
||||
main.ui.modals.menu.debug_overlay.seed_edit.text = "777"
|
||||
if main._seed_value() != 777:
|
||||
if main.seed_value() != 777:
|
||||
problems.append("显式种子输入未优先(_seed_value != 777——C1 ①失守)")
|
||||
# SaveManager 是 autoload 单例——换注走原地 attach(首局态挂载+恢复隔离档);
|
||||
# --script 模式无裸 autoload 标识符——经 main 节点路径解析
|
||||
var sm = main.get_node("/root/SaveManager")
|
||||
sm.slots.attach_path("user://smoke_tune_probe_seed.json") # 全新空档——first_adventure_done=false(首局态;新档缺省面)
|
||||
main.ui.modals.menu.debug_overlay.seed_edit.text = ""
|
||||
if main._seed_value() != 42:
|
||||
if main.seed_value() != 42:
|
||||
problems.append("首局空框种子 != 42(P5 甲失守——教学态可复现破约)")
|
||||
sm.slots.attach_path("user://smoke_tune_probe_save.json") # 恢复隔离档(非首局态——isolate_save 已建档置位)
|
||||
var draws: Array[int] = []
|
||||
for i in range(4):
|
||||
draws.append(main._seed_value())
|
||||
draws.append(main.seed_value())
|
||||
var all42 := true
|
||||
for d in draws:
|
||||
if d == 42:
|
||||
|
||||
Reference in new issue
Block a user