merge(master)+禁令残留清扫:并回 master 新进提交(gdstyle 闸件批/S9 卡/iter36 收尾),冲突 test-conventions §8.4 保重编号版+吸收 master 新条款「判据绑可观测提交面」(措辞去变异化);随批清扫——待办清单 gdstyle/S9 两条目「变异验证义务」改撤销证明口径;S9 活卡全量改写(契约 3/T2/D2/D4/来源行/对照表列头/审计面/两轮 grilling 记录——旧「闸件豁免」定界标注经用户全项目禁令推翻,验证义务统一撤销证明,禁令条款原文保留)
This commit is contained in:
commit
e89f486aa9
94 files changed
+4608
-3451
No files matched your search
@@ -54,7 +54,7 @@ func _ready() -> void:
|
||||
var rng = load("res://core/rng_service.gd").new()
|
||||
rng.set_master_seed(420000 + level_index)
|
||||
var runner = load("res://core/level_runner.gd").new(db, rng)
|
||||
if not runner.start_level(level_index, 420000 + level_index, {}, 0):
|
||||
if not runner.start_level(level_index, 420000 + level_index):
|
||||
push_error("[board_shot] 开关失败 level=%d" % level_index)
|
||||
get_tree().quit(1)
|
||||
return
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
# 每屏双帧 + frame_post_draw 同步后抓 get_viewport().get_texture(),PNG 落输出目录;
|
||||
# 最小自检 = 七件存在且非零字节(像素方差断言归 tools/shot_checker.py——B 档骨架分工)。
|
||||
# 商店房不纳屏(真实路径口径):真实路径必经 L1 打穿→L2 商店节点
|
||||
# 选路(商店层在 L2 Boss 前必经位)——探针现行驱动只进首战节点,低代价确定性
|
||||
# 选路(商店按摆放约束恒在终点前一位,每局必经恰一店)——探针现行驱动只进首战节点,低代价确定性
|
||||
# 到达不成立;合成投喂(ui_struct_probe 同款)破坏本探针真实路径口径 → 不纳屏。
|
||||
extends Node
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ func _ready() -> void:
|
||||
var rng = load("res://core/rng_service.gd").new()
|
||||
rng.set_master_seed(421001)
|
||||
var runner = load("res://core/level_runner.gd").new(db, rng)
|
||||
if not runner.start_level(1, 421001, {}, 0):
|
||||
if not runner.start_level(1, 421001):
|
||||
push_error("[type_glow_shot] 开局失败")
|
||||
get_tree().quit(1)
|
||||
return
|
||||
|
||||
@@ -26,6 +26,9 @@ var _tested_review := false
|
||||
var _tested_end_review := false # 终局面板回看入口(z 序修复回归)
|
||||
var _tested_refresh_chain := false
|
||||
var _tested_tooltip := false
|
||||
var _tested_copy := false # 迭代36 卡A——C 区两段头真实入口断言(每 run 一次)
|
||||
var _tested_curio_head := false # 迭代36 卡A——三选一头部旧话清零断言(奖励点停点捕获)
|
||||
var _tested_map_cap := false # 迭代36 卡A——地图屏加成帽换代断言(每 run 一次)
|
||||
var _tested_status := false # 状态面板角标开合/守卫断言(每 run 一次)
|
||||
var _tested_map_status := false # 地图屏角标一次性 case(选路态零扰动)
|
||||
var _inter_confirms := 0 # 大奖励屏确认计数(冒险推进停点——首见确认推进 L2,再见 = L2 通关收口)
|
||||
@@ -121,6 +124,54 @@ func _ready() -> void:
|
||||
iso_fk.load_from_disk()
|
||||
if String(iso_fk.fork_side("fg_posture")) != "b":
|
||||
_problems.append("Fork 切态未持久(reload 后激活态 != b)")
|
||||
# ---- 营地六处灰字取色断言(迭代36 wc4——真实入口开营地后按文本锚取 label,
|
||||
# font_color 逐处 == STONE_DEEP;六源位 :167 图鉴锁文/:417+:476 行状态字/:509 同调形态/:513 档位标签/:525 刷新行)----
|
||||
var camp_labels: Array = []
|
||||
_collect_labels_by_text(main.camp_overlay, camp_labels)
|
||||
var atlas_locks := _labels_with_text(camp_labels, "柜台结构位预留")
|
||||
if atlas_locks.is_empty():
|
||||
_problems.append("营地灰字:图鉴柜台锁文 label 未找到(wc4 定位锚失守)")
|
||||
for l in atlas_locks:
|
||||
if l.get_theme_color("font_color") != UiKit.STONE_DEEP:
|
||||
_problems.append("营地灰字:图鉴锁文未用 STONE_DEEP(wc4 :167)")
|
||||
# 「未解锁」锚收窄到秘宝匣行容器子树(树行/锻造行也有「未解锁」态字样——非 wc4 目标位)
|
||||
var locked_status: Array = []
|
||||
if main.camp_overlay.get("_workshop_holder") != null:
|
||||
var ws_labels: Array = []
|
||||
_collect_labels_by_text(main.camp_overlay._workshop_holder, ws_labels)
|
||||
locked_status = _labels_with_text(ws_labels, "未解锁")
|
||||
if locked_status.is_empty():
|
||||
_problems.append("营地灰字:秘宝匣行「未解锁」态 label 未找到(wc4 夹具前提失守——新档应有锁定秘宝行)")
|
||||
for l in locked_status:
|
||||
if l.get_theme_color("font_color") != UiKit.STONE_DEEP:
|
||||
_problems.append("营地灰字:秘宝行「未解锁」态未用 STONE_DEEP(wc4 :417/:476)")
|
||||
var form_found := false
|
||||
for l in camp_labels:
|
||||
if l.text == "被动" or l.text == "主动·充能":
|
||||
form_found = true
|
||||
if l.get_theme_color("font_color") != UiKit.STONE_DEEP:
|
||||
_problems.append("营地灰字:同调形态标签未用 STONE_DEEP(wc4 :509)")
|
||||
if not form_found:
|
||||
_problems.append("营地灰字:同调行形态标签未找到(wc4 定位锚失守——同调段应有行)")
|
||||
var tier_vals := {}
|
||||
for v in UiKit.TREASURE_TIER_LABELS.values():
|
||||
tier_vals[String(v)] = true
|
||||
var tier_found := false
|
||||
for l in camp_labels:
|
||||
if tier_vals.has(l.text):
|
||||
tier_found = true
|
||||
if l.get_theme_color("font_color") != UiKit.STONE_DEEP:
|
||||
_problems.append("营地灰字:同调档位标签未用 STONE_DEEP(wc4 :513)")
|
||||
if not tier_found:
|
||||
_problems.append("营地灰字:同调档位标签未找到(wc4 定位锚失守)")
|
||||
var refresh_inked := false
|
||||
for l in camp_labels:
|
||||
if String(l.text).contains("三选一刷新"):
|
||||
refresh_inked = true
|
||||
if l.get_theme_color("font_color") != UiKit.STONE_DEEP:
|
||||
_problems.append("营地灰字:刷新行未用 STONE_DEEP(wc4 :525)")
|
||||
if not refresh_inked:
|
||||
_problems.append("营地灰字:刷新行 label 未找到(wc4 定位锚失守)")
|
||||
main._on_camp_closed()
|
||||
await get_tree().process_frame
|
||||
main._on_save_select_closed() # 关闭被栈还原的选档层(动线序中间态——卡④ 换代点)
|
||||
@@ -322,6 +373,12 @@ func _ready() -> void:
|
||||
var t1 := Time.get_ticks_msec()
|
||||
main._start_adventure_from_menu()
|
||||
await get_tree().process_frame
|
||||
await get_tree().process_frame
|
||||
# 既有口径回归(迭代36 wc3——调试滑条 400/200=局外加成 pct>0,开场行应带「(含面板加成)」;
|
||||
# 本局为本探针首个冒险,日志无先前行,全文本判即可)
|
||||
var wc3_txt: String = main.log_view._log.get_parsed_text()
|
||||
if not wc3_txt.contains("种子 42") or not wc3_txt.contains("(含面板加成)"):
|
||||
_problems.append("开场行回归失守:局外加成(pct>0)局的开场行应带「(含面板加成)」(wc3)")
|
||||
# 关卡开始 = 入口选路停点——press_candidate 图上命中路径选入口候选进首战
|
||||
#(首格可点 AC 口径随之含入口选路一步:菜单点击 → 停点呈现 → pick → 棋盘安装)
|
||||
if main.level_screen.visible:
|
||||
@@ -358,6 +415,12 @@ func _ready() -> void:
|
||||
# 未适配则走到即卡死)。两态分派:result 态(兑付反馈——点继续推进)/
|
||||
# 待选态(点首件+确认)。真实信号路径全线。〔无替换子态——装配不可变契约。〕
|
||||
# 三选一停点输入锁定断言(停点态断言面:待选/兑付两态共用此层)。
|
||||
if not _tested_curio_head and main.run_scope.adventure != null and not main.run_scope.adventure._curio_pending.is_empty() and String(main.curio_overlay._header).contains("关卡奖励点"):
|
||||
_tested_curio_head = true
|
||||
var ch := String(main.curio_overlay._header)
|
||||
if not ch.contains("冒险牌库") or not ch.contains("属性值提升与补给") or ch.contains("同权重") or ch.contains("同上限") or ch.contains("提升池"):
|
||||
_problems.append("三选一头部旧话残留或新话缺失(wa4——%s)" % ch.replace("
|
||||
", "|"))
|
||||
if not main._input_locked():
|
||||
_problems.append("三选一停点打开时输入未锁定")
|
||||
if main.curio_overlay._continue_btn.visible:
|
||||
@@ -370,7 +433,7 @@ func _ready() -> void:
|
||||
_seals_placed.clear()
|
||||
continue
|
||||
if main.shop_overlay.visible:
|
||||
# 商店房覆盖层(L2 商店为 Boss 前必经位):零购离开(真实 leave 信号路径)放行选路。
|
||||
# 商店房覆盖层(摆放约束:每终点前一位恰一店,真实路径恒必经一店):零购离开(真实 leave 信号路径)放行选路。
|
||||
# 分派链序:商店层=选路页上的模态覆盖(停点呈现为
|
||||
# level_screen+shop 双 visible)——必须先于 level_screen 分派,否则下层候选点击
|
||||
# 空转烧尽预算(实证:lv2 商店停点双 visible 循环 12000 帧 done=false)。
|
||||
@@ -395,6 +458,18 @@ func _ready() -> void:
|
||||
rbtn2.pressed.emit()
|
||||
if main.status_panel.visible or not main.level_screen.visible:
|
||||
_problems.append("状态面板关闭后地图屏态失守(恢复语义破)")
|
||||
# 地图屏加成帽换代断言(迭代36 卡A wa2——「特性」→「属性值提升」;帽 Label 常在树;
|
||||
# 独立旗标——_tested_map_status 在同拍前置块已置位,禁复用其门控)
|
||||
if not _tested_map_cap:
|
||||
_tested_map_cap = true
|
||||
var cap_found := false
|
||||
var ls_labels: Array = []
|
||||
_collect_labels_by_text(main.level_screen, ls_labels)
|
||||
for l in ls_labels:
|
||||
if l.text == "属性值提升":
|
||||
cap_found = true
|
||||
if not cap_found:
|
||||
_problems.append("地图屏加成帽未换代为「属性值提升」(wa2)")
|
||||
main.level_screen.press_candidate(_route_clicks % 2)
|
||||
_route_clicks += 1
|
||||
_seals_placed.clear()
|
||||
@@ -537,6 +612,12 @@ func _ready() -> void:
|
||||
# ① 战败结局清断点(T3 口径——三结局清断点之一):停点快照随局写入,战败结算须清
|
||||
iso_chk.load_from_disk()
|
||||
if iso_chk.has_breakpoint():
|
||||
var bpf: Dictionary = iso_chk.breakpoint_snapshot()
|
||||
print("[click_probe][diag] 断点未清指纹 keys=%s lv=%s node=%s bonus=%s deck=%s pending=%s" % [
|
||||
str((bpf as Dictionary).keys()), str((bpf as Dictionary).get("level_index")),
|
||||
str((bpf as Dictionary).get("node_id")), str((bpf as Dictionary).get("bonus")),
|
||||
str(((bpf as Dictionary).get("bonus_deck", []) as Array).size()),
|
||||
str((bpf as Dictionary).get("bonus_pending"))])
|
||||
_problems.append("断点段:战败结算后断点未清(T3 三结局清断点失守)")
|
||||
if not main._end_overlay.is_open():
|
||||
_problems.append("断点段:战败后未出冒险终局面")
|
||||
@@ -553,6 +634,67 @@ func _ready() -> void:
|
||||
# ②③ 中断→续玩全链+互斥确认两分支:随三入口化撤出(卡② §7.2 申报——断点确认探针段移交
|
||||
# 卡④大门 T5 接收,两头登记禁静默删除;续玩钮已退役,主菜单确认链已摘 mn6)。
|
||||
# ① 战斗收口断点写盘断言保留(上方——冒险流直调中间态 mn10)。
|
||||
|
||||
# ---- 开场行三路径段(迭代36 wc1/wc2——真实入口驱动;隔离新档)----
|
||||
# 驱动设计(0/0 白板局生存性不成立的实证处置——L2 记录进实现报告):
|
||||
# ① 菜单开新局·白板反向=独立短局:0/0 滑条开局断言 L1 开场行零后缀(不开驱动——
|
||||
# 断言完成后该局被 ② 的开局自然替换,白板局生存性不参与判定);
|
||||
# ② 局内加算区正向=生存局:400/200 滑条开局保可通关(:363 先例),三选一专选 atk/max_hp
|
||||
# 卡(无则任选推进——不触后缀、循环重试);至大奖励屏先 reset_sliders() 再确认进下一层
|
||||
# ——L2 开场行时点 pct=0 且新档 ov=0,后缀只能归因局内加算区(变异:回退条件 hunk→红);
|
||||
# ③ 断点续玩正向:L2 入口选路停点(bonus 已入账、bonus_pending/bonus_result 皆空——选路停点形)
|
||||
# 经 _resume_from_menu 唯一消费口重建,重建开场行带后缀。
|
||||
# 判定面=开局清日志后的新局日志(_start_adventure 内 log_view.clear());「(含面板加成)」
|
||||
# 唯一来源=开场行,计数即判定;「· 种子 」=开场行独有形态锚。
|
||||
for p_ol in ["user://smoke_click_probe_openline.json", "user://smoke_click_probe_openline.json.tmp"]:
|
||||
if FileAccess.file_exists(p_ol):
|
||||
DirAccess.remove_absolute(p_ol)
|
||||
ProbeUiUtil.isolate_save(main, "smoke_click_probe_openline", true, false, true)
|
||||
main.debug_overlay.reset_sliders() # ① 白板反向前提(_start_adventure 只关窗不清值——残值跨段存活防坑)
|
||||
main.debug_overlay.seed_edit.text = "42"
|
||||
main._start_adventure_from_menu()
|
||||
var ol_l1_wait := 0
|
||||
while ol_l1_wait < 24 and String(main.log_view._log.get_parsed_text()).count("· 种子 ") < 1:
|
||||
ol_l1_wait += 1
|
||||
await get_tree().process_frame
|
||||
if ol_l1_wait >= 24:
|
||||
_problems.append("开场行路径①:L1 开场行未达日志(开局清日志后 24 帧内未现种子行)")
|
||||
elif String(main.log_view._log.get_parsed_text()).count("(含面板加成)") > 0:
|
||||
_problems.append("开场行路径①失守:白板局 L1 开场行带「(含面板加成)」(wc1 反向)")
|
||||
print("[click_probe] 开场行①判定 wait=%d 帧断言完成" % ol_l1_wait)
|
||||
# ②③ 断点续玩正向(既有快照喂入形态——代拍板 2 预授权;生产「继续上局」钮已随三入口化
|
||||
# 撤出,_resume_from_menu=唯一消费口;断点形态=选路停点形钉值:bonus 已入账、
|
||||
# bonus_pending/bonus_result 皆空——set_breakpoint 注入口同 system_test V3i 先例,
|
||||
# 卡内警示的 bonus_pending 形夹具是本形态反例,禁复用)。续玩重建开场行时点 pct=0
|
||||
# +新档 ov=0+bonus.atk=5 已回填(resume_adventure :151-154)——后缀只能归因局内加算区。
|
||||
# 【卡片前提缺陷记录(随实现报告/停机呈报)】路径②原设计「奖励节点三选一拿属性卡→下一关
|
||||
# 开场行」在现行 build 不可驱动:L1 全模板零商店节点、reward 节点=零兑付直通占位
|
||||
# (level_runner _enter_node 注记)——新开局在任一开场行时点均无局内 bonus 来源。
|
||||
# 条件加算区两项的机制面由本续玩路径全链承载(回填→level_started→条件求值,与 fresh 同式),
|
||||
# 变异(回退条件 hunk)见红;fresh 正向拦截能力未证,如实申报。
|
||||
var bp_ol: Dictionary = {
|
||||
"master_seed": 42, "rng_counter": 0, "hero_hp": 30, "gp": 10, "keystone": 0,
|
||||
"level_index": 1, "bonus": {"atk": 5, "max_hp": 0, "armor_class": 0, "ap": 0},
|
||||
"shop_purchased": {}, "relics": [], "keystone_drops": [], "bonus_taken": {},
|
||||
"node_id": -1, "visited_ids": [], "graph_gen_v2": 1,
|
||||
}
|
||||
main.save_manager.set_breakpoint(bp_ol)
|
||||
# 判定面=续玩开局清日志后的新局日志(_resume_from_menu 内 log_view.clear()——选路停点形
|
||||
# resume 同步重建开场行,返回时已在日志)
|
||||
main._resume_from_menu()
|
||||
var ol_r_wait := 0
|
||||
while ol_r_wait < 24 and String(main.log_view._log.get_parsed_text()).count("· 种子 ") < 1:
|
||||
ol_r_wait += 1
|
||||
await get_tree().process_frame
|
||||
if main.run_scope.runner == null or int(main.run_scope.runner.run_ledger.bonus.get("atk", 0)) != 5:
|
||||
_problems.append("开场行路径②③前提失守:续玩重建后加算区 atk!=5(断点回填链断)")
|
||||
if ol_r_wait >= 24:
|
||||
_problems.append("开场行路径②③失守:续玩重建未发开场行(选路停点形应重发——形态/驱动复核)")
|
||||
elif String(main.log_view._log.get_parsed_text()).count("(含面板加成)") < 1:
|
||||
_problems.append("开场行路径②③失守:bonus 已入账(pct=0/ov=0)的续玩重建开场行未带「(含面板加成)」(wc1 正向/wc2)")
|
||||
else:
|
||||
print("[click_probe] 开场行②③判定:续玩重建开场行带后缀(bonus.atk=5、pct=0)— wait=%d 帧" % ol_r_wait)
|
||||
|
||||
print("[click_probe] 循环退出(done=%s budget=%s)问题=%d" % [
|
||||
str(_done), str(action_budget), _problems.size()])
|
||||
for p in _problems:
|
||||
@@ -568,6 +710,23 @@ func _ready() -> void:
|
||||
# ---- 模态/tooltip/刷新链一次性覆盖(真实信号路径驱动) ----
|
||||
|
||||
func _run_modal_probes_once() -> void:
|
||||
# ⓪ 玩家面文案真实入口断言(迭代36 卡A wa1——C 区两段头随战斗常驻)
|
||||
if not _tested_copy and main.info_panel != null and main.info_panel.visible:
|
||||
_tested_copy = true
|
||||
var feat_head: Label = null
|
||||
var curio_head: Label = null
|
||||
for child in main.info_panel._feat_section.get_children():
|
||||
if child is Label:
|
||||
feat_head = child
|
||||
break
|
||||
for child in main.info_panel._curio_section.get_children():
|
||||
if child is Label:
|
||||
curio_head = child
|
||||
break
|
||||
if feat_head == null or feat_head.text != "专长":
|
||||
_problems.append("C 区专长段头未换代(wa1——真实入口)")
|
||||
if curio_head == null or curio_head.text != "属性值提升":
|
||||
_problems.append("C 区属性值提升段头未换代(wa1——真实入口)")
|
||||
# ① 图例模态(pattern 6):打开 → 遮挡物隐藏 + 输入锁定;关闭 → 恢复
|
||||
if not _tested_legend:
|
||||
_tested_legend = true
|
||||
@@ -582,6 +741,10 @@ func _run_modal_probes_once() -> void:
|
||||
for e in main.legend_overlay.ENTRIES:
|
||||
if String(e[1]) == "封印":
|
||||
legend_has_seal = true
|
||||
# 图例封印行换代断言(迭代36 卡A wa5——真实 AP 条图例钮开层路径)
|
||||
if not String(e[2]).contains("标空") or not String(e[2]).contains("受挖掘惩罚") or String(e[2]).contains("插错") or String(e[2]).contains("挖雷"):
|
||||
_problems.append("图例封印行旧词残留(wa5——%s)" % String(e[2]).replace("
|
||||
", "|"))
|
||||
if not legend_has_seal:
|
||||
_problems.append("图例缺『封印』格态条目(格态图例基线)")
|
||||
main.legend_overlay.close()
|
||||
@@ -605,6 +768,46 @@ func _run_modal_probes_once() -> void:
|
||||
var bal_txt: String = main.status_panel._balance_badge.value_label.text
|
||||
if not bal_txt.begins_with("钥石 "):
|
||||
_problems.append("状态面板余额徽投影缺失(%s)" % bal_txt)
|
||||
# ---- 本局信息面板换代断言(迭代36 卡③——真实入口三断言)----
|
||||
# ① 面板子树零旧段文案(跨关概览退役零残留)
|
||||
var sv_labels: Array = []
|
||||
_collect_labels_by_text(main.status_panel, sv_labels)
|
||||
for l in sv_labels:
|
||||
if String(l.text).contains("道具工坊") or String(l.text).contains("天赋树"):
|
||||
_problems.append("状态面板子树残留旧段文案「%s」(sv6①——零旧段失守)" % String(l.text))
|
||||
# ② 属性行全值 = 契约4 格式 % runner 五现值(main 侧投影值层真链路)
|
||||
var rnr = main.run_scope.runner
|
||||
if rnr == null:
|
||||
_problems.append("状态面板开层时 runner 空指针(契约5——状态机缺陷,禁软回退)")
|
||||
else:
|
||||
var attr_expect := "生命 %d/%d · 攻击 %d · 护甲 AC %d · GP %d" % [
|
||||
int(rnr.hero_hp), rnr.effective_max_hp(), rnr.effective_atk(),
|
||||
rnr.effective_ac(), int(rnr.gp)]
|
||||
if String(main.status_panel._attrs_label.text) != attr_expect:
|
||||
_problems.append("状态面板属性行投影失真(sv6②——期望 %s 实测 %s)" % [attr_expect, main.status_panel._attrs_label.text])
|
||||
# ③ _status_data() 直调对拍(sv7——键集恰八键+curios/feats 独立 oracle)
|
||||
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:
|
||||
keys_ok = keys_ok and (sd as Dictionary).has(k)
|
||||
if not keys_ok:
|
||||
_problems.append("_status_data() 键集≠八键(sv7——实测 %s)" % str((sd as Dictionary).keys()))
|
||||
var oracle: Array = []
|
||||
for rec in rnr.treasure_inventory.query_inventory(Callable()):
|
||||
var rr: Dictionary = rec
|
||||
oracle.append({"name": String(main.db.treasure_item_row(String(rr.get("item_id", ""))).get("display_name", "")),
|
||||
"used": bool(rr.get("used", false))})
|
||||
var sd_curios: Array = (sd as Dictionary).get("curios", [])
|
||||
var curios_ok: bool = (sd_curios as Array).size() == (oracle as Array).size()
|
||||
if curios_ok:
|
||||
for i in (oracle as Array).size():
|
||||
curios_ok = curios_ok and String((sd_curios[i] as Dictionary).get("name", "")) == String((oracle[i] as Dictionary).get("name", "")) and bool((sd_curios[i] as Dictionary).get("used", false)) == bool((oracle[i] as Dictionary).get("used", false))
|
||||
if not curios_ok:
|
||||
_problems.append("_status_data().curios 对拍独立 oracle 失守(sv7——实测 %s vs %s)" % [str(sd_curios), str(oracle)])
|
||||
var feats_expect: Array = load("res://presentation/hud_presenter.gd").feat_holdings_lines(main.db, rnr.feats_held())
|
||||
if str((sd as Dictionary).get("feats", [])) != str(feats_expect):
|
||||
_problems.append("_status_data().feats 对拍 feat_holdings_lines 失守(sv7)")
|
||||
var resume_btn: Button = ProbeUiUtil.find_button(main.status_panel, "继续", "prefix")
|
||||
if resume_btn == null:
|
||||
_problems.append("状态面板关闭钮缺失(「继续」)")
|
||||
@@ -660,6 +863,23 @@ func _run_modal_probes_once() -> void:
|
||||
_problems.append("HUD 刷新余量与快照推导失符(HUD='%s' 应含 %d)" % [main.ap_bar.refresh_remaining_text(), remain_expect])
|
||||
|
||||
|
||||
# ---- 营地灰字取色断言辅助(迭代36 wc4——文本锚定位,全 Label 递归收集)----
|
||||
|
||||
func _collect_labels_by_text(root: Node, out: Array) -> void:
|
||||
if root is Label:
|
||||
out.append(root)
|
||||
for child in root.get_children():
|
||||
_collect_labels_by_text(child, out)
|
||||
|
||||
|
||||
func _labels_with_text(labels: Array, needle: String) -> Array:
|
||||
var found: Array = []
|
||||
for l in labels:
|
||||
if String(l.text).contains(needle):
|
||||
found.append(l)
|
||||
return found
|
||||
|
||||
|
||||
func _play_one_action() -> void:
|
||||
var settlement = main.run_scope.settlement
|
||||
if settlement == null:
|
||||
|
||||
@@ -58,10 +58,22 @@ func _pairs() -> Array:
|
||||
["设置·选项标签/设置板", UiKit.INK_LABEL, UiKit.PANEL_DATA, 1.0], # 设置行标签/状态字(标签深墨)
|
||||
["三选一·停点头部墨/暗页底", UiKit.WARM_SOFT, UiKit.BG_ROOT, 1.0], # 三选一停点头部/回血反馈行(page_shell 根底档)
|
||||
["三选一·卡名墨/货架板", UiKit.INK_TITLE, UiKit.PANEL_DATA, 1.0], # bcard 选项卡名(与商店货架同构卡)
|
||||
["迷雾·记忆墨/灰罩合成底", UiKit.FOG_MEMORY_INK, # fog_state2 记忆板 α.66 压 floor avg #453020
|
||||
Color(UiKit.FOG_MEMORY_PLATE.r * 0.66 + 69.0 / 255.0 * 0.34, # 合成底(素材侧值无常量形态——取 art-bible §389 在册复算值,素材更新随动;
|
||||
UiKit.FOG_MEMORY_PLATE.g * 0.66 + 48.0 / 255.0 * 0.34, # 三关逐关 avg 缺在册且素材不入 git 不可实查——最坏情况判据扩对归移交)
|
||||
UiKit.FOG_MEMORY_PLATE.b * 0.66 + 32.0 / 255.0 * 0.34), 1.0],
|
||||
# ---- 迷雾测点三关扩对(迭代36 wc5——甲·归一后基准:行值=三关归一后(真实渲染面)
|
||||
# 地板均值合成底(写卡会话 PIL 复算+两轮 grilling 独立复算吻合;T0 探针派生首跑对拍
|
||||
# L2/L3 修值(PIL↔Godot 导入解码系统差约 5/255,卡内 L2 条款预授权以派生为准);
|
||||
# 素材更新随动——一致性由 _ready 派生校验断言钉住,行值回原图基准或素材换代未随动均红)----
|
||||
["迷雾·记忆墨/合成底L1地牢", UiKit.FOG_MEMORY_INK,
|
||||
Color(UiKit.FOG_MEMORY_PLATE.r * 0.66 + 111.0 / 255.0 * 0.34, # L1 归一后地板均值 #6F4D33(合成底 #4C4038 5.71:1·最差关)
|
||||
UiKit.FOG_MEMORY_PLATE.g * 0.66 + 77.0 / 255.0 * 0.34,
|
||||
UiKit.FOG_MEMORY_PLATE.b * 0.66 + 51.0 / 255.0 * 0.34), 1.0],
|
||||
["迷雾·记忆墨/合成底L2幽暗", UiKit.FOG_MEMORY_INK,
|
||||
Color(UiKit.FOG_MEMORY_PLATE.r * 0.66 + 74.0 / 255.0 * 0.34, # L2 归一后地板均值 #4A4F60(合成底 #3F4147 5.83:1)
|
||||
UiKit.FOG_MEMORY_PLATE.g * 0.66 + 79.0 / 255.0 * 0.34,
|
||||
UiKit.FOG_MEMORY_PLATE.b * 0.66 + 96.0 / 255.0 * 0.34), 1.0],
|
||||
["迷雾·记忆墨/合成底L3深幽", UiKit.FOG_MEMORY_INK,
|
||||
Color(UiKit.FOG_MEMORY_PLATE.r * 0.66 + 71.0 / 255.0 * 0.34, # L3 归一后地板均值 #474B63(合成底 #3E4048 5.93:1)
|
||||
UiKit.FOG_MEMORY_PLATE.g * 0.66 + 75.0 / 255.0 * 0.34,
|
||||
UiKit.FOG_MEMORY_PLATE.b * 0.66 + 99.0 / 255.0 * 0.34), 1.0],
|
||||
]
|
||||
|
||||
|
||||
@@ -94,8 +106,9 @@ func _ready() -> void:
|
||||
if r < MIN_RATIO:
|
||||
problems.append("[contrast] %s = %.2f:1 < %.1f(测点对须垫牌或换墨)" % [p[0], r, MIN_RATIO])
|
||||
problems.append_array(_scan_static())
|
||||
problems.append_array(_fog_derived_check())
|
||||
if problems.is_empty():
|
||||
print("[ui_contrast_probe] PASS — 对比度 %d 对测点全绿(≥%.1f:1 无区域例外)+ 裸色白名单/preload 红线静态段绿" % [pairs.size(), MIN_RATIO])
|
||||
print("[ui_contrast_probe] PASS — 对比度 %d 对测点全绿(≥%.1f:1 无区域例外)+ 裸色白名单/preload 红线静态段绿 + 迷雾三关派生校验绿" % [pairs.size(), MIN_RATIO])
|
||||
get_tree().quit(0)
|
||||
else:
|
||||
for msg in problems:
|
||||
@@ -104,6 +117,52 @@ func _ready() -> void:
|
||||
get_tree().quit(1)
|
||||
|
||||
|
||||
# ---- 迷雾测点行值一致性派生校验(迭代36 wc5——行值 vs 运行时按生产归一函数实算的
|
||||
# 三关归一后地板均值,容差 ±2/255=8-bit 量化级;行值回原图基准或素材换代未随动均红)----
|
||||
func _fog_derived_check() -> Array:
|
||||
var problems: Array = []
|
||||
var rows := [
|
||||
["L1地牢", "dungeon", Color8(111, 77, 51)],
|
||||
["L2幽暗", "underdark", Color8(74, 79, 96)],
|
||||
["L3深幽", "deep_underdark", Color8(71, 75, 99)],
|
||||
]
|
||||
for row in rows:
|
||||
var derived := _norm_floor_avg(row[1])
|
||||
var expect: Color = row[2]
|
||||
if absi(derived.r8 - expect.r8) > 2 or absi(derived.g8 - expect.g8) > 2 or absi(derived.b8 - expect.b8) > 2:
|
||||
problems.append("[contrast] 迷雾测点行值失真:%s 行值 (%d,%d,%d) vs 运行时归一派生 (%d,%d,%d)(±2/255——素材更新须随动改行值)"
|
||||
% [row[0], expect.r8, expect.g8, expect.b8, derived.r8, derived.g8, derived.b8])
|
||||
return problems
|
||||
|
||||
|
||||
# 迷雾测点派生助手(迭代36 wc5 契约 3——全仓唯一「按板型算归一后均值」判据面):纹理路径经
|
||||
# BoardView.TILE_FLOOR_FMT 常量(禁复制字面量)、归一经 BoardView._normalize_floor_luma
|
||||
# 生产静态函数直呼(真渲染同源——禁探针内复制 k 公式);逐像素归一后均值再五变体平均。
|
||||
static func _norm_floor_avg(board_type: String) -> Color:
|
||||
var sum_r := 0.0
|
||||
var sum_g := 0.0
|
||||
var sum_b := 0.0
|
||||
var px := 0
|
||||
for i in range(1, BoardView.TILE_FLOOR_VARIANTS + 1):
|
||||
var tex := UiKit.load_tex(BoardView.TILE_FLOOR_FMT % [board_type, i])
|
||||
if tex == null:
|
||||
continue
|
||||
var img: Image = BoardView._normalize_floor_luma(tex, 0.10, null).get_image()
|
||||
if img.is_compressed():
|
||||
img.decompress()
|
||||
img.convert(Image.FORMAT_RGBA8)
|
||||
for y in range(img.get_height()):
|
||||
for x in range(img.get_width()):
|
||||
var c := img.get_pixel(x, y)
|
||||
sum_r += c.r
|
||||
sum_g += c.g
|
||||
sum_b += c.b
|
||||
px += 1
|
||||
if px == 0:
|
||||
return Color(-1, -1, -1) # 素材缺位=派生校验必红(禁跳过——契约 3 齐盘条款)
|
||||
return Color(sum_r / px, sum_g / px, sum_b / px)
|
||||
|
||||
|
||||
# ---- 机检段②③(需求卡片 §7):呈现层裸 Color8( 计数 vs 白名单 + 素材 preload 计数 = 0 ----
|
||||
func _scan_static() -> Array:
|
||||
var problems: Array = []
|
||||
|
||||
@@ -43,6 +43,11 @@ func _ready() -> void:
|
||||
else:
|
||||
_settle()
|
||||
await _audit("camp", [main.camp_overlay])
|
||||
# 营地灰字源 token 扫描(迭代36 wc4 静态半——源内零裸 UiKit.STONE;判别式=
|
||||
# STONE 出现次数 == STONE_DEEP 出现次数,即每处 STONE 都带 _DEEP 后缀)
|
||||
var camp_src := FileAccess.get_file_as_string("res://presentation/views/camp_overlay_view.gd")
|
||||
if camp_src.count("UiKit.STONE") != camp_src.count("UiKit.STONE_DEEP"):
|
||||
problems.append("[camp] camp_overlay_view.gd 存在裸 UiKit.STONE(非 _DEEP 后缀——wc4 源扫描)")
|
||||
main._on_camp_closed()
|
||||
_settle()
|
||||
main._on_save_select_closed() # 关闭被栈还原的选档层(动线序中间态——卡④ 换代点)
|
||||
@@ -252,11 +257,12 @@ func _ready() -> void:
|
||||
get_tree().quit(1)
|
||||
|
||||
|
||||
# ⑦resume 夹具:最小合法断点快照——bonus_pending 非空=三选一停点分支(合计 gp=24 供守卫兜底断言)
|
||||
# ⑦resume 夹具:最小合法断点快照(含生成图代差标记键)——bonus_pending 非空=三选一停点分支(合计 gp=24 供守卫兜底断言)
|
||||
func _resume_bp_fixture() -> Dictionary:
|
||||
return {
|
||||
"master_seed": 42, "rng_counter": 0, "hero_hp": 20, "gp": 24, "keystone": 0,
|
||||
"level_index": 1, "bonus": {}, "shop_purchased": {}, "relics": [],
|
||||
"graph_gen_v2": 1, # 生成图换代代差标记键(缺键=换代前旧断点,resume_adventure 显式拒绝)
|
||||
"keystone_drops": [], "bonus_taken": {},
|
||||
"bonus_pending": {"candidates": [{"kind": "heal", "tier": "common"}], "payload": {"gp_dropped": 0}},
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user