iter(36): 卡C+卡A 实现——开场行后缀认加算区(六项布尔+三路径断言:白板反向/续玩重建正向,卡片②奖励节点前提缺陷改快照喂入形态代拍2)、营地六处灰字STONE_DEEP+取色/源扫描断言、迷雾测点三关归一后基准+派生校验(T0对拍L2/L3修值L2条款)、文案禁字清扫钉值表13单元+player_copy_sweep新宿主24断言入清单、变异验证全套留痕(对照表证据随实现报告)
This commit is contained in:
1 parent
2931b1f250
commit
9468c70ee2
19 files changed
+489
-31
No files matched your search
@@ -794,7 +794,7 @@ func _grant_routed(snap: Dictionary, polarity: String, source_class: String, cel
|
||||
if halve_pct > 0 and polarity == "negative" and source_class == "mine_hit":
|
||||
row = row.duplicate()
|
||||
var half: int = int(ceil(float(int(row.power)) * float(100 - halve_pct) / 100.0))
|
||||
_emit_row("effect", source_class, cell, ri, "惊巢反制——挖雷惩罚减半(%d→%d)" % [int(row.power), half], 0, {"effect_id": "nest_counter"}, highlight)
|
||||
_emit_row("effect", source_class, cell, ri, "惊巢反制——危险格挖掘惩罚减半(%d→%d)" % [int(row.power), half], 0, {"effect_id": "nest_counter"}, highlight)
|
||||
row.power = half
|
||||
return _dispatch_row(row, source_class, cell, ri, highlight, label_suffix)
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ segment_order = PackedStringArray("grant", "buff_debuff", "combat", "decay", "ve
|
||||
source_label_map = {
|
||||
"safe_cell": "安全格",
|
||||
"seal_hit": "封印命中",
|
||||
"mine_hit": "踩雷",
|
||||
"mine_hit": "危险格",
|
||||
"clear_reward": "清图奖励",
|
||||
"enemy_action": "敌方行动",
|
||||
"hero_attack": "主角攻击",
|
||||
|
||||
@@ -139,7 +139,7 @@ static func bonus_result_text(result: Dictionary) -> String:
|
||||
# 表无 archetype,fallback 兜底词「流派」会实显僵尸文案),卡片=件名+效果摘要两行)
|
||||
static func feat_card_text(row: Dictionary) -> String:
|
||||
if row.is_empty() or String(row.get("feat_id", "")) == "":
|
||||
return "特性\n???"
|
||||
return "专长\n???"
|
||||
var summary := String(row.get("desc", ""))
|
||||
if summary != "":
|
||||
return "%s\n%s" % [String(row.get("display_name", row.get("feat_id", ""))), summary]
|
||||
@@ -151,9 +151,9 @@ static func bonus_keystone_line(payload: Dictionary) -> String:
|
||||
return "钥石 +%d · 累计 %d" % [int(payload.get("keystone_gained", 0)), int(payload.get("keystone_total", 0))]
|
||||
|
||||
|
||||
# 奖励节点三选一头部行(与通关三选一共享同池;node-reward.md v2)
|
||||
# 奖励节点三选一头部行(与通关三选一共享同库;渠道白名单分流——node-reward.md v2)
|
||||
static func node_curio_header() -> String:
|
||||
return "关卡奖励点\n从提升池抽三选一(与通关三选一同池同权重同上限):\n选择一件带走:"
|
||||
return "关卡奖励点\n从冒险牌库抽三选一(属性值提升与补给;与通关专长三选一同库分流):\n选择一件带走:"
|
||||
|
||||
|
||||
# 冒险结算屏文案(摘要五件事:打穿没有 / 通关几关 / 止步第几关 / 钥石合计 / 主种子)
|
||||
|
||||
@@ -604,12 +604,16 @@ func _on_adventure_level_started(level_index: int, display_name: String) -> void
|
||||
run_scope.runner = run_scope.adventure.runner()
|
||||
# 开场行传当前生效值(runner.effective_atk/effective_max_hp 与战斗面板同源:
|
||||
# 表值+永久强化 flat+加算区 flat 后乘调试浮窗 pct,本回调时点 runner 已带全部加成);「含面板加成」
|
||||
# 后缀判断认局外加成——永久强化 atk/max_hp flat 或调试浮窗 pct 任一存在才带,白板局不带
|
||||
# 后缀判断认任一加成源——永久强化 atk/max_hp flat 或调试浮窗 pct 或局内加算区 atk/max_hp
|
||||
#(run_ledger.bonus——属性值提升入账后显示值已含加算区 flat,注记随动;armor_class/ap 不入条件:
|
||||
# 开场行只显示 atk/max_hp 两值,注记指向不存在的显示轴反成失真),白板局不带
|
||||
var ov: Dictionary[Variant, Variant] = meta.hero_override()
|
||||
var boosted: bool = int(ov.get("atk", 0)) > 0 or int(ov.get("max_hp", 0)) > 0 \
|
||||
or debug_overlay.atk_pct() > 0 or debug_overlay.hp_pct() > 0 \
|
||||
or int(run_scope.runner.run_ledger.bonus.get("atk", 0)) > 0 \
|
||||
or int(run_scope.runner.run_ledger.bonus.get("max_hp", 0)) > 0
|
||||
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
|
||||
or debug_overlay.atk_pct() > 0 or debug_overlay.hp_pct() > 0))
|
||||
run_scope.runner.effective_atk(), run_scope.runner.effective_max_hp(), boosted))
|
||||
|
||||
|
||||
func _on_runner_node(node: Dictionary, info: Dictionary) -> void:
|
||||
|
||||
@@ -267,7 +267,8 @@ func _draw() -> void:
|
||||
elif fog_state == 2:
|
||||
# 记忆态换装(demo 口径「地板纹理 + 灰罩 α.66」:
|
||||
# 地板纹理原样直画 + FOG_MEMORY_PLATE α.66 灰罩压顶;素材零改动、格边长零改动。
|
||||
# 记忆墨 FOG_MEMORY_INK 对合成底实测 ≈6.7:1 ≥4.5:1(§7.2 硬指标;floor avg #453020 复算在册))
|
||||
# 记忆墨 FOG_MEMORY_INK 对合成底归一后(真实渲染面)三关 5.71–5.93:1 ≥4.5:1(§7.2 硬指标;
|
||||
# 测点行值与素材随动,见 ui_contrast_probe 迷雾段))
|
||||
if tex_base != null:
|
||||
draw_texture_rect(tex_base, Rect2(Vector2.ZERO, Vector2(edge, edge)), false)
|
||||
draw_rect(Rect2(Vector2.ZERO, Vector2(edge, edge)), Color(UiKit.FOG_MEMORY_PLATE, 0.66), true)
|
||||
|
||||
@@ -727,7 +727,7 @@ func _paint_cell(c: Vector2i, hero: Vector2i) -> void:
|
||||
var rec: Dictionary[Variant, Variant] = _minesweeper.reveal_record(c) # 已开记录经查询面
|
||||
if rec.get("kind", "") == "mine":
|
||||
bg = COL_FLOOR
|
||||
glyph = "雷"
|
||||
glyph = "危"
|
||||
digit_col = UiKit.RED_MINE
|
||||
else:
|
||||
bg = COL_FLOOR
|
||||
@@ -773,13 +773,13 @@ func _paint_cell(c: Vector2i, hero: Vector2i) -> void:
|
||||
u.bg_sb = sb
|
||||
u.glyph = glyph
|
||||
u.glyph_color = digit_col
|
||||
# 色牌后备(数字直画退牌;雷/「?」维持牌载保读性)+ v3.4:双峰纹理板
|
||||
# 色牌后备(数字直画退牌;「危」/「?」维持牌载保读性)+ v3.4:双峰纹理板
|
||||
#(_digit_plate——underdark 系亮白筋+近黑斑并存的直画不可救纹理)数字落牌,牌上深墨 12:1
|
||||
var plate_digit := _digit_plate and glyph != "" and glyph != "雷" and glyph != "?" and glyph != "封" and glyph != "人"
|
||||
var plate_digit := _digit_plate and glyph != "" and glyph != "危" and glyph != "?" and glyph != "封" and glyph != "人"
|
||||
if plate_digit:
|
||||
digit_col = COL_DIGIT # 牌上深墨族(素牌 12:1——双峰纹理免疫)
|
||||
u.glyph_color = digit_col
|
||||
u.plate = revealed and (glyph == "雷" or glyph == "?" or plate_digit)
|
||||
u.plate = revealed and (glyph == "危" or glyph == "?" or plate_digit)
|
||||
u.outline_sides.assign([]) # typed Array[int](assign 保持引用语义)
|
||||
u.facade_south = false
|
||||
u.proj_band = 0.0
|
||||
|
||||
@@ -164,7 +164,7 @@ func _init() -> void:
|
||||
atlas_panel.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
atlas_panel.size_flags_vertical = Control.SIZE_FILL # 四栏等高:任一栏长高时四栏同步拉伸
|
||||
mid.add_child(atlas_panel)
|
||||
var lock_l := UiKit.label(" —— 柜台结构位预留:内容排 Alpha(地图战斗资源差异化批)",UiKit.FS_HINT, false, UiKit.STONE)
|
||||
var lock_l := UiKit.label(" —— 柜台结构位预留:内容排 Alpha(地图战斗资源差异化批)",UiKit.FS_HINT, false, UiKit.STONE_DEEP)
|
||||
lock_l.size_flags_vertical = Control.SIZE_SHRINK_CENTER # 锁文在等高栏内垂直居中
|
||||
lock_l.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
lock_l.custom_minimum_size = Vector2(280, 0)
|
||||
@@ -414,7 +414,7 @@ func refresh_camp(data: Dictionary) -> void:
|
||||
card.progress_l.text = prog_text
|
||||
# 状态色用亮板双色墨制(深墨族替代暖白族——亮板上暖白不可读)
|
||||
var st_text := "未解锁"
|
||||
var st_col: Color = UiKit.STONE
|
||||
var st_col: Color = UiKit.STONE_DEEP
|
||||
if unlocked:
|
||||
st_text = "已解锁"
|
||||
st_col = UiKit.GOLD_INK # GOLD_DEEP 对行板 3.00:1 越带,换深金墨 5.84:1
|
||||
@@ -473,7 +473,7 @@ func _rebuild_workshop(data: Dictionary) -> void:
|
||||
progress_l.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
||||
line.add_child(progress_l)
|
||||
# 状态色用亮板双色墨制(深墨族替代暖白族)——色随刷新态条件赋值,非机械两行式
|
||||
var status_l := UiKit.label("",UiKit.FS_HINT, true, UiKit.STONE)
|
||||
var status_l := UiKit.label("",UiKit.FS_HINT, true, UiKit.STONE_DEEP)
|
||||
status_l.clip_text = true # 同上——「已解锁 · 能力封存中」不撑柜(余宽吸收者)
|
||||
status_l.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
||||
status_l.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
@@ -506,11 +506,11 @@ func _rebuild_attune(data: Dictionary) -> void:
|
||||
name_l.clip_text = true
|
||||
name_l.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
||||
line.add_child(name_l)
|
||||
var form_l := UiKit.label("被动" if String(row.get("form", "")) == "passive" else "主动·充能",UiKit.FS_CAPTION, false, UiKit.STONE)
|
||||
var form_l := UiKit.label("被动" if String(row.get("form", "")) == "passive" else "主动·充能",UiKit.FS_CAPTION, false, UiKit.STONE_DEEP)
|
||||
form_l.custom_minimum_size = Vector2(58, 0)
|
||||
line.add_child(form_l)
|
||||
# 档位标签(槽位档位显示:件档=普通/优秀/稀有,装入须件档≤格档契约 2 的呈现半边)
|
||||
var tier_l := UiKit.label(String(UiKit.TREASURE_TIER_LABELS.get(String(row.get("tier", "")), "—")),UiKit.FS_CAPTION, false, UiKit.STONE)
|
||||
var tier_l := UiKit.label(String(UiKit.TREASURE_TIER_LABELS.get(String(row.get("tier", "")), "—")),UiKit.FS_CAPTION, false, UiKit.STONE_DEEP)
|
||||
tier_l.custom_minimum_size = Vector2(36, 0)
|
||||
line.add_child(tier_l)
|
||||
var btn := UiKit.button("装配", 0.0) # 行板紧凑钮豁免
|
||||
@@ -522,7 +522,7 @@ func _rebuild_attune(data: Dictionary) -> void:
|
||||
_attune_btns.append({"item_id": iid, "btn": btn, "unlocked": bool(row.get("unlocked", false)), "equipped": bool(row.get("equipped", false))})
|
||||
var refresh_line := HBoxContainer.new()
|
||||
refresh_line.add_theme_constant_override("separation", 6)
|
||||
_refresh_l = UiKit.label("",UiKit.FS_CAPTION, false, UiKit.STONE)
|
||||
_refresh_l = UiKit.label("",UiKit.FS_CAPTION, false, UiKit.STONE_DEEP)
|
||||
refresh_line.add_child(_refresh_l)
|
||||
_refresh_btn = UiKit.button("升级", 0.0) # 行板紧凑钮豁免
|
||||
_refresh_btn.pressed.connect(_emit_buy_requested.bind("attune_refresh"))
|
||||
|
||||
@@ -70,7 +70,7 @@ func _init() -> void:
|
||||
_feat_section.add_theme_constant_override("separation", 4)
|
||||
_feat_section.visible = false
|
||||
col.add_child(_feat_section)
|
||||
_feat_section.add_child(UiKit.label("特性",UiKit.FS_BODY, true))
|
||||
_feat_section.add_child(UiKit.label("专长",UiKit.FS_BODY, true))
|
||||
_feat_list = VBoxContainer.new()
|
||||
_feat_list.add_theme_constant_override("separation", 2)
|
||||
_feat_section.add_child(_feat_list)
|
||||
@@ -90,7 +90,7 @@ func _init() -> void:
|
||||
_curio_section.add_theme_constant_override("separation", 4)
|
||||
_curio_section.visible = false
|
||||
col.add_child(_curio_section)
|
||||
_curio_section.add_child(UiKit.label("特性",UiKit.FS_BODY, true))
|
||||
_curio_section.add_child(UiKit.label("属性值提升",UiKit.FS_BODY, true))
|
||||
_curio_list = VBoxContainer.new()
|
||||
_curio_list.add_theme_constant_override("separation", 2)
|
||||
_curio_section.add_child(_curio_list)
|
||||
|
||||
@@ -8,7 +8,7 @@ signal closed
|
||||
const ENTRIES := [
|
||||
["#C9AF83", "完整覆尘地砖", "未开格——左键挖开(1AP,须相邻)· 右键封印(1AP)"],
|
||||
["#B39A78", "碎裂裸土", "已开 0 格——可通行,左键移动(免费)"],
|
||||
["#000000", "封印", "封印印记(呈现名·不能拔)——轮末自动判定标中;插错可左键直接挖开(安全格零奖励、危险格吃挖雷惩罚)"],
|
||||
["#000000", "封印", "封印印记(呈现名·不能拔)——轮末自动判定标中;标空可左键直接挖开(安全格零奖励、危险格受挖掘惩罚)"],
|
||||
["#2e86de", "小人", "探险者——8 向相邻未开格常驻微光 = 可挖域"],
|
||||
["#F0A828", "金徽章", "已开格奖励身份 icon(封印之下、数值之上,不进数字区)"],
|
||||
["#F0A828", "九徽章解码", "攻=攻击 · 守=护甲 · 爆=爆裂 · 医=回复 · 弱=削弱 · 穿=穿刺 · 魔=魔法 · 行=行动点 · 金=GP"],
|
||||
|
||||
@@ -168,7 +168,7 @@ func _init() -> void:
|
||||
_bonus_row.add_child(bonus_box)
|
||||
var bonus_inner := VBoxContainer.new()
|
||||
bonus_box.add_child(bonus_inner)
|
||||
var bonus_cap := UiKit.label("特性",UiKit.FS_CAPTION, false, UiKit.INK_HINT) # 深墨(亮板可读)
|
||||
var bonus_cap := UiKit.label("属性值提升",UiKit.FS_CAPTION, false, UiKit.INK_HINT) # 深墨(亮板可读)
|
||||
bonus_inner.add_child(bonus_cap)
|
||||
_bonus_value = UiKit.label("",UiKit.FS_BODY, true, UiKit.INK_TITLE) # 淡墨→深墨(同上)
|
||||
bonus_inner.add_child(_bonus_value)
|
||||
|
||||
@@ -37,7 +37,7 @@ const EFFECT_SEMANTIC := {
|
||||
"empower": "敌方输出 ×2(enemy_output 池加算——蓄力技施放;持续 1 个有效结算)",
|
||||
"fortify": "敌方防御 −50(enemy_defense 池——硬化技施放,主角攻击 ×0.5;持续 1 个有效结算)",
|
||||
}
|
||||
const SOURCE_NAMES := {"safe_cell": "安全格", "seal_hit": "封印命中", "mine_hit": "踩雷", "clear_reward": "清图奖励"}
|
||||
const SOURCE_NAMES := {"safe_cell": "安全格", "seal_hit": "封印命中", "mine_hit": "危险格", "clear_reward": "清图奖励"}
|
||||
# 未知图标键的占位(HUD框架:占位也要可见,禁静默空图标——debug 断言先行,release 回退槽内 ? 文本)
|
||||
const ICON_PLACEHOLDER := "?"
|
||||
|
||||
|
||||
@@ -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:
|
||||
@@ -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()
|
||||
@@ -553,6 +628,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": [], "template_pick": -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 +704,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 +735,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()
|
||||
@@ -660,6 +817,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() # 关闭被栈还原的选档层(动线序中间态——卡④ 换代点)
|
||||
|
||||
@@ -99,6 +99,7 @@ func _suites() -> Array:
|
||||
{"path": "res://tests/unit/presentation/battle_narrator_test.gd", "expect": 0}, # 战斗叙述(数值带入/胜负互异/收束态恒定)
|
||||
{"path": "res://tests/unit/presentation/type_glow_test.gd", "expect": 0}, # 未开格类型微光数据面(九型归组封闭/清单外显式失败/冻结色相只准动明度/α170·带宽cell÷8锚定/合成空间阶梯单调+模型CR≥1.20/probe API 形状)
|
||||
{"path": "res://tests/unit/presentation/theme_projection_test.gd", "expect": 0}, # 呈现投影与主题宿主(微光数据面/战斗日志行协议/全局主题与按钮五态/纹理过滤白名单/对比度墨色锚/元素细节源码锚/诊断钮退役源码契约/状态面板与生涯页只读投影——批 B 自 smoke_all 迁入;试玩记录仪整套按裁决七转办集成宿主)
|
||||
{"path": "res://tests/unit/presentation/player_copy_sweep_test.gd", "expect": 0}, # 玩家面文案清扫宿主(迭代36 卡A——钉值表换代断言①–⑧⑩+源 token 扫描两处;直建断言族,禁字「雷」族与旧术语清零钉值)
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -662,6 +662,30 @@ func _smoke_tree_rules() -> void:
|
||||
ctx_fz2.level_bucket = lvl_shared # 挂同一 level 桶引用(新形态跨战持久:无 export/seed,同字典即持久)
|
||||
Kit.check("锋:限次跨战持久(level 桶共享引用——替代旧副本搬运链)〔无状态化 V4〕",
|
||||
(fz2.dispatch(ctx_fz2, "mine_hit", {}, Callable()) as Array).is_empty())
|
||||
# 锋·惊巢反制效果行换代断言(迭代36 u13/wa12——_grant_routed 减半分支效果行=战斗日志
|
||||
# step_row 真实生产面;display_name 玩家可见,禁字「挖雷」由此行清零钉住)
|
||||
var orch_nc = load("res://core/battle_orchestrator.gd").new(_db, _fresh_rng(777003))
|
||||
orch_nc.begin_battle({"level_index": 1, "node_depth": 0, "hero_hp": 40, "hero_atk": 8, "hero_max_hp": 40,
|
||||
"hero_armor_class": 10, "hero_ap_bonus": 0, "attunement": [], "attune_charges": {},
|
||||
"feats": [], "tree_rules": [], "battle_index": 1,
|
||||
"bonus_ref": {"atk": 0, "max_hp": 0, "armor_class": 0}, "adventure_counters": {},
|
||||
"voucher_rap": 0, "voucher_time_sand": 0, "node_type": "battle"}) # 真实战斗上下文(状态容器开域——dispatch 链受理)
|
||||
var nc_rows: Array = [] # step_row 采集盒(lambda 捕引用容器——捕获变量赋值不出闭包)
|
||||
orch_nc.step_row.connect(func(r): nc_rows.append(r))
|
||||
# heal 负向行表值 power=6(极性字典行值——负向 power 走表值不走快照)→ halve 50% ceil=3
|
||||
orch_nc._grant_routed({"cell_type": "heal", "power": 10}, "negative",
|
||||
"mine_hit", Vector2i.ZERO, 0, "", "", 50)
|
||||
# 受理判据=惊巢反制效果行落 step_row(halve 分支先于 dispatch 发行——flat 行 _dispatch_row
|
||||
# 恒返 false,返回值不承载受理语义;行发行即分支受理)
|
||||
var nc_hit := ""
|
||||
for r in nc_rows:
|
||||
if String((r as Dictionary).get("display_name", "")).contains("惊巢反制"):
|
||||
nc_hit = String((r as Dictionary).get("display_name", ""))
|
||||
break
|
||||
Kit.check("锋:惊巢反制 mine_hit 减半分支受理(wa12 前提——halve_pct=50 效果行落 step_row)〔wa12〕",
|
||||
nc_hit != "")
|
||||
Kit.check("惊巢反制效果行==「惊巢反制——危险格挖掘惩罚减半(6→3)」零「挖雷」(u13 换代)〔wa12〕",
|
||||
nc_hit == "惊巢反制——危险格挖掘惩罚减半(6→3)" and not nc_hit.contains("挖雷"))
|
||||
# 守侧五场景(契约 12):编排 rig——begin_battle 携 sense_guard,授予段 _grant_seg._judge_seal 直注
|
||||
var orch = load("res://core/battle_orchestrator.gd").new(_db, _fresh_rng(777001))
|
||||
Kit.check("守侧 rig:战斗编排启动(tree_rules=sense_guard 携带)",
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
# tests/unit/presentation/player_copy_sweep_test.gd — 玩家面文案清扫直建断言宿主(迭代36 卡A)
|
||||
# 断言面 = 玩家面字符串字面量换代钉值(u1–u13 钉值表——分类词/渠道旧话/「雷」族禁字清零):
|
||||
# ① hero 面板两段头(专长/属性值提升——同屏分类撞车消除)
|
||||
# ② 奖励节点三选一头部(冒险牌库/渠道分流三机制事实,零「同权重/同上限/提升池」旧话)
|
||||
# ③ 专长卡兜底(feat_card_text 空行兜底「专长\n???」)
|
||||
# ④ 图例封印行(标空/受挖掘惩罚,零「插错/挖雷」)
|
||||
# ⑤ 状态槽来源名(mine_hit→危险格——tooltip 消费半)
|
||||
# ⑥ 结算来源名(settlement 表值经 ConfigDB 真装载+battle_log 行消费半)
|
||||
# ⑦ 已开危险格字形(glyph「危」+plate+RED_MINE——BoardView rebuild 真实刷新路径)
|
||||
# ⑧ 源 token 扫描一:board_view.gd 零 "雷" 字面量(哨兵族统一钉住)
|
||||
# ⑩ 源 token 扫描二:battle_orchestrator.gd 玩家面字符串字面量零「挖雷」(注释剥离=行首 # 判)
|
||||
# 扫域口径:玩家面字符串字面量(契约 2 保留面——注释/内部常量名与注记/测试消息串不入域)。
|
||||
# 协议照单元侧惯例:extends SceneTree+Kit.check 主通道;--script 模式无 autoload,
|
||||
# ConfigDB 显式实例化构造注入(统一流 master 种子 42 沿源)。
|
||||
# 运行:godot --headless --path game --script res://tests/unit/presentation/player_copy_sweep_test.gd
|
||||
extends SceneTree
|
||||
|
||||
const Kit := preload("res://tests/unit/test_kit.gd")
|
||||
const ConfigDBScript := preload("res://core/config_db.gd")
|
||||
const AutoPlayer := preload("res://sim/auto_player.gd") # 自动玩家策略单一实现(夹具开域驱动)
|
||||
|
||||
var _db
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
_db = ConfigDBScript.new()
|
||||
var load_ok: bool = _db.load_tables("res://data/tables", true)
|
||||
if not load_ok:
|
||||
push_error("[player_copy_sweep_test] 表加载返回失败——诊断如下(红由具体断言落)")
|
||||
for d in _db.diagnostics():
|
||||
if d.severity == "error" or d.severity == "fatal":
|
||||
push_error(" [%s] %s: %s" % [d.severity, d.category, d.message])
|
||||
_sweep_hero_panel()
|
||||
_sweep_narrator()
|
||||
_sweep_legend()
|
||||
_sweep_source_names()
|
||||
_sweep_board_glyph()
|
||||
_sweep_token_scans()
|
||||
quit(Kit.finish("player_copy_sweep_test"))
|
||||
|
||||
|
||||
# ① C 区两段头(u1/u2——直建 hero 面板,adventure_feats_test ⑦c 族同构)
|
||||
func _sweep_hero_panel() -> void:
|
||||
var panel = load("res://presentation/views/hero_info_panel_view.gd").new()
|
||||
var feat_head := _first_label(panel._feat_section)
|
||||
var curio_head := _first_label(panel._curio_section)
|
||||
Kit.check("C 区专长段头==「专长」(u1 换代——段头文案钉值)〔wa1〕",
|
||||
feat_head != null and feat_head.text == "专长")
|
||||
Kit.check("C 区属性值提升段头==「属性值提升」(u2 换代——同屏两段同标撞车消除)〔wa1〕",
|
||||
curio_head != null and curio_head.text == "属性值提升")
|
||||
|
||||
|
||||
# ②③ 奖励节点头部+专长卡兜底(u3/u11——静态直调)
|
||||
func _sweep_narrator() -> void:
|
||||
var narrator = load("res://presentation/battle_narrator.gd")
|
||||
var head := String(narrator.node_curio_header())
|
||||
Kit.check("奖励点头部含「冒险牌库」(u3 机制事实①同库)〔wa4〕", head.contains("冒险牌库"))
|
||||
Kit.check("奖励点头部含「属性值提升与补给」(u3 机制事实②渠道分流)〔wa4〕", head.contains("属性值提升与补给"))
|
||||
Kit.check("奖励点头部含「同库分流」(u3 机制事实②措辞)〔wa4〕", head.contains("同库分流"))
|
||||
Kit.check("奖励点头部零「同权重」旧话(u3 清零)〔wa4〕", not head.contains("同权重"))
|
||||
Kit.check("奖励点头部零「同上限」旧话(u3 清零)〔wa4〕", not head.contains("同上限"))
|
||||
Kit.check("奖励点头部零「提升池」旧话(u3 清零)〔wa4〕", not head.contains("提升池"))
|
||||
Kit.check("专长卡兜底==「专长\n???」(u11 换代——数据缺行真实兜底路径)〔wa3〕",
|
||||
String(narrator.feat_card_text({})) == "专长\n???")
|
||||
|
||||
|
||||
# ④ 图例封印行(u4——直建 LegendOverlay 读 ENTRIES 常量)
|
||||
func _sweep_legend() -> void:
|
||||
load("res://presentation/views/legend_overlay.gd").new() # 直建(_init(rap_cost) 现签名)
|
||||
var seal_row: Array = []
|
||||
for e in load("res://presentation/views/legend_overlay.gd").ENTRIES:
|
||||
if String(e[1]) == "封印":
|
||||
seal_row = e
|
||||
break
|
||||
Kit.check("图例封印行存在(wa5 夹具前提)〔wa5〕", not seal_row.is_empty())
|
||||
Kit.check("图例封印行含「标空」(u4——与「标中」成对)〔wa5〕", String(seal_row[2] if seal_row.size() > 2 else "").contains("标空"))
|
||||
Kit.check("图例封印行含「受挖掘惩罚」(u4 雷字清零换代)〔wa5〕", String(seal_row[2] if seal_row.size() > 2 else "").contains("受挖掘惩罚"))
|
||||
Kit.check("图例封印行零「插错」(u4 旗族遗留词干清零)〔wa5〕", not String(seal_row[2] if seal_row.size() > 2 else "").contains("插错"))
|
||||
Kit.check("图例封印行零「挖雷」(u4 雷字清零)〔wa5〕", not String(seal_row[2] if seal_row.size() > 2 else "").contains("挖雷"))
|
||||
|
||||
|
||||
# ⑤⑥ 来源名双源(u5/u6——状态槽消费半+表值 ConfigDB 真装载+battle_log 行消费半)
|
||||
func _sweep_source_names() -> void:
|
||||
var row_view = load("res://presentation/views/status_slot_row.gd").new()
|
||||
row_view.set_effects([{"effect_id": "alert", "source_class": "mine_hit",
|
||||
"display_name": "危险格受击", "remaining_settlements": 2, "round_index": 1}])
|
||||
var slot: PanelContainer = null
|
||||
for child in row_view.get_children():
|
||||
if child is PanelContainer and child.has_meta("eff"):
|
||||
slot = child
|
||||
break
|
||||
Kit.check("状态槽 mine_hit 效果行在座(wa6 夹具前提)〔wa6〕", slot != null)
|
||||
if slot != null:
|
||||
row_view._on_slot_hover(slot)
|
||||
Kit.check("状态槽来源注记==「危险格」(u5 换代——meta tooltip 消费半)〔wa6〕",
|
||||
row_view._tip_label.text.contains("来源:危险格"))
|
||||
row_view._hide_tip()
|
||||
Kit.check("结算表 source_label_map[mine_hit]==「危险格」(u6 换代——ConfigDB 真装载)〔wa7〕",
|
||||
String(_db.settlement.source_label_map.get("mine_hit", "")) == "危险格")
|
||||
var log_view = load("res://presentation/views/battle_log_view.gd").new(_db)
|
||||
var row_txt := String(log_view._format_row({"type": "effect", "source_class": "mine_hit",
|
||||
"display_name": "挖掘反噬", "value": -5, "pool_value": -5, "round_index": 1}))
|
||||
Kit.check("战斗日志 mine_hit 来源行含「(危险格)」(u6 消费半——_format_row 生产路径)〔wa7〕",
|
||||
row_txt.contains("(危险格)"))
|
||||
|
||||
|
||||
# ⑦⑧ 已开危险格字形族(u7/u8/u9——BoardView rebuild 真实刷新路径+源 token 扫描)
|
||||
func _sweep_board_glyph() -> void:
|
||||
var board_generator = load("res://core/board_generator.gd").new()
|
||||
var b = board_generator.generate(1, 0, _mk_stream("board_gen", 4242), _db)
|
||||
var ae = load("res://core/action_economy.gd").new(_db)
|
||||
var ms = load("res://core/minesweeper_rules.gd").new(_db, ae)
|
||||
ms.install_board(b)
|
||||
ae.notify_round_start()
|
||||
# 邻接雷定位(挖掘面规则=须邻接英雄——novice 驱动开域至危险格入挖掘射程;上限 60 步防冻态)
|
||||
var mine_cell := Vector2i(-1, -1)
|
||||
var glow_steps := 0
|
||||
while glow_steps < 60:
|
||||
for c in b.neighbors8(ms.hero_pos()):
|
||||
if b.is_mine(c) and not ms.is_revealed(c):
|
||||
mine_cell = c
|
||||
break
|
||||
if mine_cell.x >= 0:
|
||||
break
|
||||
var act: Dictionary = AutoPlayer.novice_action(ms)
|
||||
if String(act.get("kind", "stop")) == "stop":
|
||||
break
|
||||
var op := String(act.get("kind", ""))
|
||||
if op == "seal":
|
||||
op = "seal_place"
|
||||
ms.settle_batch([{"action": op, "cell": act.get("cell")}])
|
||||
glow_steps += 1
|
||||
Kit.check("棋盘夹具含挖掘射程内未开危险格(wa8 前提——实测 %d 步)〔wa8〕" % glow_steps, mine_cell.x >= 0)
|
||||
if mine_cell.x >= 0:
|
||||
ms.settle_batch([{"action": "dig", "cell": mine_cell}]) # 挖开危险格(真实规则路径——受击开格)
|
||||
var view = load("res://presentation/views/board_view.gd").new()
|
||||
view.rebuild(b, ms) # theme_projection_test.gd:200 先例式直建(缺素材程序化回退态——fresh checkout 冒烟同纪律)
|
||||
var u: Node2D = view._cells[mine_cell]
|
||||
Kit.check("已开危险格 glyph==「危」(u7 换代)〔wa8〕", u.glyph == "危")
|
||||
Kit.check("已开危险格 plate==true(u9 牌载哨兵包含项随动)〔wa8〕", u.plate == true)
|
||||
Kit.check("已开危险格 glyph_color==RED_MINE(u7 墨色不随字形换代漂移)〔wa8〕",
|
||||
u.glyph_color == UiKit.RED_MINE)
|
||||
|
||||
|
||||
# ⑧⑩ 源 token 扫描(注释剥离=行首 # 判——契约 2 保留面不入域)
|
||||
func _sweep_token_scans() -> void:
|
||||
var board_src := FileAccess.get_file_as_string("res://presentation/views/board_view.gd")
|
||||
Kit.check("board_view.gd 源零 \"雷\" 字面量(u7/u8/u9 哨兵族统一钉住)〔wa8〕",
|
||||
board_src.count("\"雷\"") == 0)
|
||||
var orch_src := FileAccess.get_file_as_string("res://core/battle_orchestrator.gd")
|
||||
var orch_hits := 0
|
||||
for line in orch_src.split("\n"):
|
||||
if line.strip_edges().begins_with("#"):
|
||||
continue
|
||||
if line.contains("挖雷"):
|
||||
orch_hits += 1
|
||||
Kit.check("battle_orchestrator.gd 玩家面字符串字面量零「挖雷」(u13——注释保留面不入域)〔wa12〕",
|
||||
orch_hits == 0)
|
||||
|
||||
|
||||
func _first_label(node: Node) -> Label:
|
||||
for child in node.get_children():
|
||||
if child is Label:
|
||||
return child
|
||||
return null
|
||||
|
||||
|
||||
func _mk_stream(stream_name: String, scope: int) -> RandomNumberGenerator:
|
||||
# stream_name = 调用点自述标签(不参与派生),保留参数零改调用方(theme_projection_test 同构)
|
||||
var rg := RandomNumberGenerator.new()
|
||||
var svc = load("res://core/rng_service.gd").new()
|
||||
svc.set_master_seed(42)
|
||||
rg.seed = svc.derive_at(scope)
|
||||
return rg
|
||||
@@ -1,7 +1,8 @@
|
||||
# tests/unit/presentation/theme_projection_test.gd — 呈现投影与主题单元测试宿主
|
||||
# 断言面 = 十套(批 B 断言迁移自 smoke_all.gd,G11 施工组;十套按源编排序迁入):
|
||||
# 断言面 = 十一套(批 B 断言迁移自 smoke_all.gd,G11 施工组;十套按源编排序迁入+开场行后缀尾接协议扩段):
|
||||
# 未开格类型微光(作用集恒等/九型归组封闭/零玩法写入——board_view 微光数据面)
|
||||
# 战斗日志视图行格式化(行协议 9 类呈现层同步闸——真人试玩卡断 bug 回归)
|
||||
# 开场行后缀尾接协议(level_open_line 第五参布尔直控「(含面板加成)」尾接——签名零变)
|
||||
# UI 全局主题(main_theme.tres 挂载/类型覆盖/滑轨两段对比/退役实现零残留/中文字体防御)
|
||||
# UI 按钮五态矩阵(四工厂五态样式盒逐实例 override)
|
||||
# UI 全局纹理过滤(工程 Nearest+平滑渐变件 LINEAR 白名单+光柱 alpha 结构)
|
||||
@@ -42,6 +43,7 @@ func _init() -> void:
|
||||
# 套执行序沿源编排序(_run_core_smoke 序 + _smoke_meta 薄壳尾序)
|
||||
_smoke_type_glow()
|
||||
_smoke_battle_log_view()
|
||||
_smoke_level_open_line()
|
||||
_smoke_global_theme()
|
||||
_smoke_button_five_states()
|
||||
_smoke_texture_filter()
|
||||
@@ -420,6 +422,19 @@ func _smoke_battle_log_view() -> void:
|
||||
view.parsed_text().length() > before)
|
||||
|
||||
|
||||
# ---- 开场行后缀尾接协议(迭代36 wc1 静态半——level_open_line 第五参布尔直控尾接,签名零变;
|
||||
# 该函数此前零既有断言,尾接行为由此钉住)----
|
||||
func _smoke_level_open_line() -> void:
|
||||
var NarratorScript := load("res://presentation/battle_narrator.gd")
|
||||
Kit.check("开场行 boosted=true 尾接「(含面板加成)」〔wc1〕",
|
||||
String(NarratorScript.level_open_line("第一层", 42, 5, 30, true)).contains("(含面板加成)"))
|
||||
Kit.check("开场行 boosted=false 零后缀(白板局不带)〔wc1〕",
|
||||
not String(NarratorScript.level_open_line("第一层", 42, 5, 30, false)).contains("(含面板加成)"))
|
||||
Kit.check("开场行种子/两值带入不受后缀位影响(数值带入回归)〔wc1〕",
|
||||
String(NarratorScript.level_open_line("第一层", 42, 5, 30, true)).contains("种子 42")
|
||||
and String(NarratorScript.level_open_line("第一层", 42, 5, 30, true)).contains("攻击 5 / 生命 30"))
|
||||
|
||||
|
||||
# ---- UI 全局主题冒烟(需求卡片 §7 验收清单 V1-V6)----
|
||||
# V1 挂载覆盖 / V2 滑轨两段 / V3 预盖件运行时证词 / V4 旧形态零残留 /
|
||||
# V5 回归(本冒烟全绿即回归网)/ V6 中文字体防御(挂主题后 default_font 顶掉 custom_font 也不丢字形)
|
||||
|
||||
@@ -17,6 +17,7 @@ func _init() -> void:
|
||||
if f != null:
|
||||
Kit.check("中文字形在集(攻/雷/旗/封)", f.has_char("攻".unicode_at(0)) and f.has_char("雷".unicode_at(0)) \
|
||||
and f.has_char("旗".unicode_at(0)) and f.has_char("封".unicode_at(0)))
|
||||
Kit.check("中文字形在集(危——已开危险格字形换代前置,迭代36 u12/wa9)", f.has_char("危".unicode_at(0)))
|
||||
Kit.check("ASCII/数字字形在集(A/0/%)", f.has_char("A".unicode_at(0)) and f.has_char("0".unicode_at(0)) and f.has_char("%".unicode_at(0)))
|
||||
var sz := 18
|
||||
var w_cn: float = f.get_string_size("战斗胜利", HORIZONTAL_ALIGNMENT_LEFT, -1, sz).x
|
||||
|
||||
Reference in new issue
Block a user