合并 master 整合(棋盘瓦片与立体表现重做批): 冲突三件解决+封印呈现对齐迭代56+卡片第六轮复核
- art-bible 冲突:保留 C 地形八条,采纳 master v3.48 封印规则并适配——封印居已揭示地面格心、身份字形垫色牌叠于法阵上、印记与色牌固定低于小人、不参与浮空层;承载面因新地面 #695038 对封印黑仅 2.35–2.98:1,改本关受光棱色封印石(6.45–9.94:1,零新增色);版本 v3.51;§1.2/§1.4/§3.2/§4.1/§6.5/§8.2 同步。 - README 冲突:保留 master 迭代56「结算期零逐行文字与 FX 排播链保持」+旧地形行(父注保留),追加本批 6 条新能力;能力名改「旧瓦片退役与普通数字零底牌」「棋盘顶面锚点与迷雾几何(微光、变异岩顶偏移,封印格心)」。 - roadmap 冲突:保留 master 迭代55/56当前位原文,仅把待编池「二三关棋盘观感」替换为「棋盘瓦片与立体表现重做(已写卡,待编排)」。 - 需求卡片适配:封印格色牌为唯一例外、plate 保留(CellUnit 12→13)、board_terrain_layer 增 sealed_at(≤13 成员)、测试改 _test_no_ordinary_plate_path+_test_seal_substrate、§12 行8/9/11/14/15 与 §16 第六轮 6 条记录;术语表/checklist/battle.md/待办/08 同步 v3.51。 - 区内凭据:pipeline_gate PASS(条目45/总表56/交叉核3/卡片5);capability_audit 红项0(能力行123/套件87);run_all_gates --docs 内容无新增红(回执待本提交后重跑)。
No files matched your search
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 46 KiB |
@@ -1,4 +1,4 @@
|
||||
## 同调配装件:装备中的秘宝件对象集唯一所有者(每件内含充能单位与重置周期——冒险域拆分姊妹批自共享充能账字典轨迁移)。
|
||||
## 同调配装件:装备中的秘宝件对象集唯一所有者(每件内含充能单位与重置周期)。
|
||||
## 同调充能单源化:物化行(treasure_inventory 内 source_class="attunement" 行)为使用面唯一事实源——
|
||||
## 剩余次数由行的未用数派生(唯一读法,无影子账);字典轨与三处事后差额对账退役,改「写时种行/补行」。
|
||||
## 种行/补行写时同步:冒险开始全量种行(不筛周期——battle/level 周期件开局即有行,C 区投影行数不变);
|
||||
|
||||
@@ -49,8 +49,8 @@ var _enemy_system: UnitContainer
|
||||
var _status_effects: StatusContainer
|
||||
var _reward_economy: RewardEconomy
|
||||
var _damage: DamageController # 伤害控制器(单据唯一兑付管线)
|
||||
var _fx_host: BattleFxHost # 效果引擎宿主件(fx 族持有与兑付——拆件定稿轴件一)
|
||||
var _reward_grant: RewardGrant # 奖励授予件(授予管线与秘宝使用——拆件定稿轴件二)
|
||||
var _fx_host: BattleFxHost # 效果引擎宿主件(fx 族持有与兑付)
|
||||
var _reward_grant: RewardGrant # 奖励授予件(授予管线与秘宝使用)
|
||||
|
||||
var round_index := 0
|
||||
var battle_result := "" # "" / "victory" / "defeat" / "fatal_error"
|
||||
|
||||
@@ -414,12 +414,15 @@ func grant_clear_reward(ri: int, skip_on_refresh: bool, perfect: bool) -> void:
|
||||
if not (snap is Dictionary):
|
||||
return
|
||||
var first_ok := _grant_clear_identity(snap, ri, "(清图奖励)")
|
||||
# 完美封印追加兑付倍率读表(数值单一事实源——p_perfect 经 ConfigDB settlement_core 行,
|
||||
# 载体域 {100,200}:200=×2 追加第二份、100=×1 单份不发。追加份数结构上限 1 份不变)
|
||||
var perfect_mult := int(db.combat.settlement.core.p_perfect) / 100
|
||||
var second_ok := false
|
||||
if perfect and first_ok:
|
||||
if perfect and first_ok and perfect_mult >= 2:
|
||||
second_ok = _grant_clear_identity(snap, ri, "(完美第二份)")
|
||||
if perfect and first_ok:
|
||||
emit_cb.call({"type": "highlight", "source_class": "clear_reward", "origin_cell": Vector2i(-1, -1), "highlight": "perfect_sweep"},
|
||||
ri, "完美封印!清图奖励 ×2", (2 if second_ok else 1), {})
|
||||
ri, "完美封印!清图奖励 ×%d" % perfect_mult, (1 + (1 if second_ok else 0)), {})
|
||||
|
||||
|
||||
# 清图奖励 = 整图独立对象(不属于任何一格):兑付形态按其 cell_type 的正向字典形态走,
|
||||
|
||||
@@ -6,7 +6,7 @@ extends Resource
|
||||
@export var canvas := Vector2(1280, 800) # 终稿画布(工程档同幅面)
|
||||
@export var hud_band_h := 48 # 顶栏禁区高度(命中不侵)
|
||||
@export var gate_gap := Vector2i(520, 760) # 门洞缺口 x 范围(透出背景层小路,禁填死)
|
||||
@export var wall_band := Rect2(0, 240, 1280, 70) # 围墙带包络(门柱顶 240—桩底 310;围墙贴图须落在其内)
|
||||
@export var wall_band := Rect2(0, 220, 1280, 200) # 围墙带包络(显示矩形 (0,220,1280,200);围墙贴图须落在其内)
|
||||
@export var layers: Array[CampSceneLayerSpec] = []
|
||||
|
||||
|
||||
|
||||
@@ -13,64 +13,64 @@ hotspot_key = &""
|
||||
[sub_resource type="Resource" id="2"]
|
||||
script = ExtResource("2")
|
||||
key = &"wall"
|
||||
texture_rect = Rect2(0, 261, 1280, 49)
|
||||
texture_rect = Rect2(0, 220, 1280, 200)
|
||||
hotspot = Rect2(0, 0, 0, 0)
|
||||
hotspot_key = &""
|
||||
|
||||
[sub_resource type="Resource" id="3"]
|
||||
script = ExtResource("2")
|
||||
key = &"board"
|
||||
texture_rect = Rect2(845, 268, 130, 121)
|
||||
hotspot = Rect2(845, 268, 130, 140)
|
||||
texture_rect = Rect2(845, 241, 130, 121)
|
||||
hotspot = Rect2(845, 241, 130, 140)
|
||||
hotspot_key = &"board"
|
||||
|
||||
[sub_resource type="Resource" id="4"]
|
||||
script = ExtResource("2")
|
||||
key = &"gate"
|
||||
texture_rect = Rect2(520, 250, 240, 211)
|
||||
hotspot = Rect2(520, 250, 240, 220)
|
||||
texture_rect = Rect2(520, 282, 240, 138)
|
||||
hotspot = Rect2(520, 282, 240, 147)
|
||||
hotspot_key = &"gate"
|
||||
|
||||
[sub_resource type="Resource" id="5"]
|
||||
script = ExtResource("2")
|
||||
key = &"dummy"
|
||||
texture_rect = Rect2(340, 520, 120, 180)
|
||||
hotspot = Rect2(340, 520, 120, 180)
|
||||
texture_rect = Rect2(340, 474, 120, 180)
|
||||
hotspot = Rect2(340, 474, 120, 180)
|
||||
hotspot_key = &"dummy"
|
||||
|
||||
[sub_resource type="Resource" id="6"]
|
||||
script = ExtResource("2")
|
||||
key = &"forge"
|
||||
texture_rect = Rect2(40, 467, 240, 193)
|
||||
hotspot = Rect2(40, 420, 240, 240)
|
||||
texture_rect = Rect2(40, 421, 240, 193)
|
||||
hotspot = Rect2(40, 374, 240, 240)
|
||||
hotspot_key = &"forge"
|
||||
|
||||
[sub_resource type="Resource" id="7"]
|
||||
script = ExtResource("2")
|
||||
key = &"tent"
|
||||
texture_rect = Rect2(982, 520, 251, 170)
|
||||
texture_rect = Rect2(982, 335, 251, 170)
|
||||
hotspot = Rect2(0, 0, 0, 0)
|
||||
hotspot_key = &""
|
||||
|
||||
[sub_resource type="Resource" id="8"]
|
||||
script = ExtResource("2")
|
||||
key = &"chest"
|
||||
texture_rect = Rect2(1097, 700, 106, 90)
|
||||
hotspot = Rect2(1085, 700, 130, 90)
|
||||
texture_rect = Rect2(1097, 654, 106, 90)
|
||||
hotspot = Rect2(1085, 654, 130, 90)
|
||||
hotspot_key = &"chest"
|
||||
|
||||
[sub_resource type="Resource" id="9"]
|
||||
script = ExtResource("2")
|
||||
key = &"notebook"
|
||||
texture_rect = Rect2(818, 704, 100, 66)
|
||||
hotspot = Rect2(818, 690, 100, 80)
|
||||
texture_rect = Rect2(870, 515, 100, 66)
|
||||
hotspot = Rect2(870, 501, 100, 80)
|
||||
hotspot_key = &"notebook"
|
||||
|
||||
[sub_resource type="Resource" id="10"]
|
||||
script = ExtResource("2")
|
||||
key = &"campfire"
|
||||
texture_rect = Rect2(560, 571, 160, 149)
|
||||
hotspot = Rect2(560, 640, 160, 80)
|
||||
texture_rect = Rect2(560, 525, 160, 149)
|
||||
hotspot = Rect2(560, 594, 160, 80)
|
||||
hotspot_key = &"campfire"
|
||||
|
||||
[resource]
|
||||
@@ -78,5 +78,5 @@ script = ExtResource("1")
|
||||
canvas = Vector2(1280, 800)
|
||||
hud_band_h = 48
|
||||
gate_gap = Vector2i(520, 760)
|
||||
wall_band = Rect2(0, 240, 1280, 70)
|
||||
wall_band = Rect2(0, 220, 1280, 200)
|
||||
layers = [SubResource("1"), SubResource("2"), SubResource("3"), SubResource("4"), SubResource("5"), SubResource("6"), SubResource("7"), SubResource("8"), SubResource("9"), SubResource("10")]
|
||||
@@ -133,6 +133,7 @@ func _build_layer_node(spec: CampSceneLayerSpec) -> Control:
|
||||
if tex != null:
|
||||
var tr := TextureRect.new()
|
||||
tr.texture = tex
|
||||
tr.expand_mode = TextureRect.EXPAND_IGNORE_SIZE # 允许节点小于贴图原尺寸(门扇 240×211→矩形 138 高)
|
||||
tr.stretch_mode = TextureRect.STRETCH_SCALE
|
||||
node = tr
|
||||
else:
|
||||
|
||||
@@ -23,10 +23,77 @@ var _level_readings := {} # 关 → 双帧比对读数(实现报告读数表
|
||||
var _cur_settlement = null # 当关 settlement(身份回调读取面——仿生产 run_scope.settlement 现值;
|
||||
# 不用 Callable.bind 传参:bind 的 Callable 动态派发时对类型化参数做 Object 硬校验,
|
||||
# 报「Cannot convert argument 2 from Object to Vector2i」——直接方法引用+成员现值无此坑)
|
||||
var _cur_mark_counts := {} # 当关 cell_mark_overlay 绘制域逐类型格计数(注入后读数——V14 采集态核对)
|
||||
|
||||
|
||||
func _probe_settlement_getter():
|
||||
return _cur_settlement
|
||||
|
||||
|
||||
# 绘制域缺型注入(确定性直构——每关固定选格,同种子同态可复现):
|
||||
# 真视框=首个未开未封印危险格;预读徽章=身份查询命中的未开格(循环挑到命中,全空记零);
|
||||
# 移除退色=remove_mines_in 真实写口移除一个未揭示危险格;点迹/高亮=英雄格 face 直设定格。
|
||||
func _mark_domain_inject(view: Node, settlement: Object) -> void:
|
||||
var ms = settlement.minesweeper
|
||||
var board = ms.current_board()
|
||||
var reveal_cell := Vector2i(-9, -9)
|
||||
var preview_cell := Vector2i(-9, -9)
|
||||
var remove_cell := Vector2i(-9, -9)
|
||||
for y in board.height:
|
||||
for x in board.width:
|
||||
var c := Vector2i(x, y)
|
||||
if ms.is_revealed(c) or ms.has_seal(c) or ms.is_removed(c):
|
||||
continue
|
||||
if board.is_mine(c):
|
||||
if reveal_cell.x < 0:
|
||||
reveal_cell = c
|
||||
if remove_cell.x < 0:
|
||||
remove_cell = c
|
||||
elif preview_cell.x < 0 and _probe_identity_lookup(c) != null:
|
||||
preview_cell = c
|
||||
if remove_cell.x >= 0:
|
||||
ms.remove_mines_in([remove_cell])
|
||||
view.set_treasure_overlays([reveal_cell] if reveal_cell.x >= 0 else [],
|
||||
[preview_cell] if preview_cell.x >= 0 else [])
|
||||
view.refresh()
|
||||
var hero: Vector2i = ms.hero_pos()
|
||||
var cells: Dictionary = view.cells_probe()
|
||||
if cells.has(hero):
|
||||
(cells[hero] as Node2D).face.dot_visible = true
|
||||
var nb: Vector2i = hero + Vector2i(1, 0)
|
||||
if not cells.has(nb):
|
||||
nb = hero + Vector2i(0, 1)
|
||||
if cells.has(nb):
|
||||
(cells[nb] as Node2D).face.hl_visible = true
|
||||
_cur_mark_counts = {"injected": {"reveal": str(reveal_cell), "preview": str(preview_cell),
|
||||
"remove": str(remove_cell), "dot": str(hero)}}
|
||||
|
||||
|
||||
# 逐类型格计数读数(off 帧呈现态——face 现值直读,CellUnit 在册探针读口同面)
|
||||
func _mark_domain_counts(view: Node, tag: String) -> Dictionary:
|
||||
var counts := {"mutation_mark": 0, "treasure_mark": 0, "badge_glyph": 0,
|
||||
"dot_visible": 0, "hl_visible": 0, "removed_dim": 0}
|
||||
var cells: Dictionary = view.cells_probe()
|
||||
for c in cells:
|
||||
var f = (cells[c] as Node2D).face
|
||||
if String(f.mutation_mark) != "":
|
||||
counts["mutation_mark"] = int(counts["mutation_mark"]) + 1
|
||||
if String(f.treasure_mark) != "":
|
||||
counts["treasure_mark"] = int(counts["treasure_mark"]) + 1
|
||||
if String(f.badge_glyph) != "":
|
||||
counts["badge_glyph"] = int(counts["badge_glyph"]) + 1
|
||||
if bool(f.dot_visible):
|
||||
counts["dot_visible"] = int(counts["dot_visible"]) + 1
|
||||
if bool(f.hl_visible):
|
||||
counts["hl_visible"] = int(counts["hl_visible"]) + 1
|
||||
if bool(f.removed_dim):
|
||||
counts["removed_dim"] = int(counts["removed_dim"]) + 1
|
||||
print("[board_shot] mark_domain %s: %s" % [tag, counts])
|
||||
return counts
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
# oracle: design/engineering/test-conventions.md:84 (棋盘截图取证——渲染闸采集组)
|
||||
# 判据出处: design/engineering/test-conventions.md:84 (棋盘截图取证——渲染闸采集组)
|
||||
# 运行前提=窗口化真渲染面(shot_probe 同款护栏——headless 下 frame_post_draw 永不触发+视口纹理恒空)
|
||||
if DisplayServer.get_name() == "headless":
|
||||
print("[board_shot] FAIL — 本探针必须窗口化运行(headless 无渲染面,截图必空);现行事实源=tools/render_gate.sh 取证段")
|
||||
@@ -120,7 +187,17 @@ func _ready() -> void:
|
||||
# 双帧比对空转、PASS 假绿)。现值经 _cur_settlement 成员读入
|
||||
# (仿生产 run_scope.settlement;bind 传参有 Callable 类型校验坑,见成员注)。
|
||||
view.set_identity_resolver(_probe_identity_lookup)
|
||||
# 变异角标 resolver(生产同源——main_ui_build:262 set_variant_mark_resolver 接线同款):
|
||||
# 裸视图不接 = mutation_mark 恒空 = L3 伤害型变异角标缺帧。rebuild 前接(paint 链读回调)。
|
||||
var resolvers = load("res://presentation/board_resolvers.gd").new(db, null, _probe_settlement_getter)
|
||||
view.set_variant_mark_resolver(resolvers.variant_mark_lookup)
|
||||
view.rebuild(settlement.minesweeper.current_board(), settlement.minesweeper)
|
||||
# ---- cell_mark_overlay 绘制域采集态注入(卡修轮 2 V14——确定性直构,缺型补进采集态)----
|
||||
# 五类型注入全走真链/生产同源:真视框与预读徽章=set_treasure_overlays(生产呈现链);
|
||||
# 移除退色=remove_mines_in(核心真实写口——移除集=未揭示无封印是雷,判定基线零回溯);
|
||||
# 点迹/高亮=u.face 直设(CellUnit 探针读口同面的写侧,交互瞬态的定格化)。
|
||||
_mark_domain_inject(view, settlement)
|
||||
var mark_counts := _mark_domain_counts(view, tag)
|
||||
# 进挖掘相位(生产态 = hud_presenter.set_dig_glow_enabled(not settling) 接线——裸视图默认
|
||||
# _dig_glow=false,接触带与类型微光同源门控都不渲染;golden 基线即捕获于
|
||||
# 该裸态。双帧取证要求生产默认态,故显式开——两帧均含静态接触带(motion_scale=0.5 停格),
|
||||
@@ -182,7 +259,7 @@ func _ready() -> void:
|
||||
# 判据:环外径像素 ÷ 格边 落 0.82±1px;封印格最外圈 1px 框最暗值与相邻未封印未开格同位框最暗值差 ≤1/255
|
||||
#(halo 在 0.455×格边归零——格边框不应被压暗;相邻未开格同砖纹理同套瓦片为无封印参照)。
|
||||
func _seal_evidence(tag: String, view: Node, settlement: Object, off_img: Image, out: String) -> void:
|
||||
# oracle: design/engineering/test-conventions.md:84 (封印印记像素取证组)
|
||||
# 判据出处: design/engineering/test-conventions.md:84 (封印印记像素取证组)
|
||||
var ms = settlement.minesweeper
|
||||
var seal_cell := Vector2i(-9, -9)
|
||||
var ref_cell := Vector2i(-9, -9)
|
||||
@@ -290,7 +367,7 @@ func _probe_identity_lookup(c: Vector2i):
|
||||
# 双帧逐像素比对:所有变化像素必须落在生效格的内缘带内
|
||||
# (de < cell÷8——带外含砖心零侵入=禁整格染色/禁越圈);每个生效格须有实绘像素(不空转)。
|
||||
func _check_dual_frames(tag: String, view, glow_cells: Dictionary, off_img: Image, on_img: Image) -> void:
|
||||
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
var board = view.board()
|
||||
if board == null:
|
||||
_problems.append("%s 双帧比对取板失败(view.board() 为 null)" % tag)
|
||||
@@ -342,7 +419,8 @@ func _check_dual_frames(tag: String, view, glow_cells: Dictionary, off_img: Imag
|
||||
if first_outside == "":
|
||||
first_outside = "%s 非生效格" % str(c2)
|
||||
_level_readings[tag] = {"glow_cells": glow_cells.size(), "changed_total": changed_total,
|
||||
"changed_outside": changed_outside, "band_px": band_px}
|
||||
"changed_outside": changed_outside, "band_px": band_px,
|
||||
"mark_domain": _cur_mark_counts}
|
||||
print("[board_shot] %s 双帧读数 glow=%d changed=%d outside=%d band=%.0fpx"
|
||||
% [tag, glow_cells.size(), changed_total, changed_outside, band_px])
|
||||
if changed_outside != 0:
|
||||
|
||||
@@ -8,7 +8,7 @@ extends Node
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/战斗表现.md:148 (结算演出——挖掘期零打扰组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/战斗表现.md:148 (结算演出——挖掘期零打扰组)
|
||||
await get_tree().process_frame
|
||||
var problems: Array = []
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ var _problems: Array = []
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
# oracle: design/engineering/test-conventions.md:84 (特效截图取证——渲染闸采集组)
|
||||
# 判据出处: design/engineering/test-conventions.md:84 (特效截图取证——渲染闸采集组)
|
||||
var args := OS.get_cmdline_user_args()
|
||||
out_dir = args[0] if args.size() >= 1 and args[0] != "" else "user://qa-fx"
|
||||
DirAccess.make_dir_recursive_absolute(out_dir)
|
||||
@@ -108,7 +108,7 @@ func _ready() -> void:
|
||||
|
||||
|
||||
func _snap(tag: String) -> void:
|
||||
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
await RenderingServer.frame_post_draw
|
||||
var img: Image = get_viewport().get_texture().get_image()
|
||||
if img == null or img.is_empty():
|
||||
@@ -118,7 +118,7 @@ func _snap(tag: String) -> void:
|
||||
|
||||
|
||||
func _fail(msg: String) -> void:
|
||||
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
_problems.append(msg)
|
||||
push_error("[fx_shot_probe] FAIL — " + msg)
|
||||
get_tree().quit(1)
|
||||
@@ -19,7 +19,7 @@ var _est_reads: Dictionary = {} # 逐档 13/14 板推定读数留痕(label
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/HUD框架.md:264 (战斗屏三区几何与棋盘格边长——分辨率档位组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/HUD框架.md:264 (战斗屏三区几何与棋盘格边长——分辨率档位组)
|
||||
await get_tree().process_frame
|
||||
var main: Node = load("res://presentation/main.tscn").instantiate()
|
||||
add_child(main)
|
||||
@@ -59,7 +59,7 @@ func _ready() -> void:
|
||||
|
||||
# 一份不变量断言,逐档代入——代码量与档数无关
|
||||
func _check_one(label: String, main: Node) -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/HUD框架.md:264 (单档几何断言组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/HUD框架.md:264 (单档几何断言组)
|
||||
var vr: Rect2 = get_viewport().get_visible_rect()
|
||||
if vr.size != Vector2(VIEWPORTS[0]["size"]) and vr.size.y < 1.0:
|
||||
_problems.append("[%s] 逻辑视口退化 %s" % [label, vr.size])
|
||||
|
||||
@@ -11,7 +11,7 @@ extends Node
|
||||
const SEED_L3 := 910212 # 深图固定种子(与 level_map_viewport_probe 同源——图确定性承载)
|
||||
|
||||
func _ready() -> void:
|
||||
# oracle: design/engineering/test-conventions.md:84 (关卡画面截图取证——载入守卫组)
|
||||
# 判据出处: design/engineering/test-conventions.md:84 (关卡画面截图取证——载入守卫组)
|
||||
var args := OS.get_cmdline_user_args()
|
||||
var out_dir := args[0] if args.size() > 0 else "user://"
|
||||
await get_tree().process_frame
|
||||
@@ -45,7 +45,7 @@ func _settle() -> void:
|
||||
|
||||
|
||||
func _make_runner(entry: bool, node_id: int):
|
||||
# oracle: design/engineering/test-conventions.md:84 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:84 (判定上报通道——验证函数组)
|
||||
var rng = load("res://core/rng_service.gd").new()
|
||||
rng.new_adventure(SEED_L3)
|
||||
var runner = load("res://core/level_runner.gd").new(get_node("/root/ConfigDB"), rng)
|
||||
@@ -85,7 +85,7 @@ func _deepest_with_outs(screen, runner) -> int:
|
||||
|
||||
|
||||
func _shot(out_dir: String, fname: String) -> void:
|
||||
# oracle: design/engineering/test-conventions.md:84 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:84 (判定上报通道——验证函数组)
|
||||
DirAccess.make_dir_recursive_absolute(out_dir) # 落盘目录自建(save_png 对缺目录静默失败——刷新链不预建)
|
||||
var img := get_viewport().get_texture().get_image()
|
||||
var err := img.save_png(out_dir.path_join(fname))
|
||||
|
||||
@@ -22,14 +22,14 @@ var _runner # 当前停点的单关 runner(open_choices 驱动随记——
|
||||
|
||||
|
||||
func _check(desc: String, ok: bool) -> void:
|
||||
# oracle: design/ux/level-map.md:3 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/ux/level-map.md:3 (判定上报通道——验证函数组)
|
||||
_checks += 1
|
||||
if not ok:
|
||||
_problems.append(desc)
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
# oracle: design/ux/level-map.md:3 (关卡画面视口跟随——卷轴视口组)
|
||||
# 判据出处: design/ux/level-map.md:3 (关卡画面视口跟随——卷轴视口组)
|
||||
await get_tree().process_frame
|
||||
await get_tree().process_frame
|
||||
var host := Control.new()
|
||||
@@ -62,7 +62,7 @@ func _ready() -> void:
|
||||
|
||||
## R1 浅图整幅居中无裁切+画布入视口件承载(画布=视口首子件——setup 注入序)
|
||||
func _probe_shallow_no_crop(screen, vp) -> void:
|
||||
# oracle: design/ux/level-map.md:3 (浅层不裁切组)
|
||||
# 判据出处: design/ux/level-map.md:3 (浅层不裁切组)
|
||||
_runner = _make_runner_entry(1, SEED_L1)
|
||||
screen.open_choices(_cand_nodes(_runner), _runner)
|
||||
await get_tree().process_frame
|
||||
@@ -81,7 +81,7 @@ func _probe_shallow_no_crop(screen, vp) -> void:
|
||||
|
||||
## R5 前置+深图入口停点:锚点=入口组包围盒中心夹紧(spec :101——多入口并列取包围盒居中)
|
||||
func _probe_deep_entry_clamp(screen, vp) -> void:
|
||||
# oracle: design/ux/level-map.md:3 (深层入口钳制组)
|
||||
# 判据出处: design/ux/level-map.md:3 (深层入口钳制组)
|
||||
_runner = _make_runner_entry(3, SEED_L3)
|
||||
screen.open_choices(_cand_nodes(_runner), _runner)
|
||||
await get_tree().process_frame
|
||||
@@ -175,7 +175,7 @@ func _probe_clamp_bounds_and_columns(screen, vp) -> void:
|
||||
|
||||
## R6 拖拽不触发选路提交;阈值另一侧完整点击仍提交(提交语义零改动)
|
||||
func _probe_drag_no_submit(screen, vp) -> void:
|
||||
# oracle: design/ux/level-map.md:3 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/ux/level-map.md:3 (判定上报通道——验证函数组)
|
||||
# 重建到中段可居中停点(候选=停点出边——候选节点居屏中可拖可达)
|
||||
var stops := _centerable_stops(screen, vp)
|
||||
if stops.size() < 3:
|
||||
@@ -214,7 +214,7 @@ func _probe_drag_no_submit(screen, vp) -> void:
|
||||
|
||||
## R2 停点切换后当前节点居中(视口中线投影 x=640±2——视口满屏宽即屏中线)
|
||||
func _probe_follow_anchor(screen, vp) -> void:
|
||||
# oracle: design/ux/level-map.md:3 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/ux/level-map.md:3 (判定上报通道——验证函数组)
|
||||
var stops := _centerable_stops(screen, vp)
|
||||
if stops.size() < 3:
|
||||
_problems.append("R2 可居中停点不足(=%d)" % stops.size())
|
||||
@@ -230,7 +230,7 @@ func _probe_follow_anchor(screen, vp) -> void:
|
||||
|
||||
## R7 平滑滚动有 tween 中间帧;减动效开=位置即达(画布坐标域判定)
|
||||
func _probe_smooth_scroll_reduced_motion(screen, vp) -> void:
|
||||
# oracle: design/ux/level-map.md:3 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/ux/level-map.md:3 (判定上报通道——验证函数组)
|
||||
var saved_scale: float = UiKit.motion_scale
|
||||
UiKit.motion_scale = 1.0
|
||||
var stops := _centerable_stops(screen, vp)
|
||||
@@ -270,7 +270,7 @@ func _probe_smooth_scroll_reduced_motion(screen, vp) -> void:
|
||||
|
||||
## R8 指引件可见性与几何(深图超窗——像素级判据②③)
|
||||
func _probe_guidance_chrome(screen, vp) -> void:
|
||||
# oracle: design/ux/level-map.md:3 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/ux/level-map.md:3 (判定上报通道——验证函数组)
|
||||
var band_l: Control = vp.get_node_or_null(NodePath("FadeL"))
|
||||
var band_r: Control = vp.get_node_or_null(NodePath("FadeR"))
|
||||
var chip: Control = vp.get_node_or_null(NodePath("PanHint"))
|
||||
@@ -302,7 +302,7 @@ func _probe_shallow_guidance_hidden(screen, vp) -> void:
|
||||
|
||||
## 入口停点 runner(defer_entry——选路态挂起、候选=入口节点)
|
||||
func _make_runner_entry(level_index: int, seed_val: int):
|
||||
# oracle: design/ux/level-map.md:3 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/ux/level-map.md:3 (判定上报通道——验证函数组)
|
||||
var rng = load("res://core/rng_service.gd").new()
|
||||
rng.new_adventure(seed_val)
|
||||
var runner = load("res://core/level_runner.gd").new(get_node("/root/ConfigDB"), rng)
|
||||
@@ -313,7 +313,7 @@ func _make_runner_entry(level_index: int, seed_val: int):
|
||||
|
||||
## 中段停点 runner(断点续玩重建——图由种子+参数档重生成同图,零 RNG 消耗)
|
||||
func _make_runner_at_stop(level_index: int, seed_val: int, node_id: int):
|
||||
# oracle: design/ux/level-map.md:3 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/ux/level-map.md:3 (判定上报通道——验证函数组)
|
||||
var rng = load("res://core/rng_service.gd").new()
|
||||
rng.new_adventure(seed_val)
|
||||
var runner = load("res://core/level_runner.gd").new(get_node("/root/ConfigDB"), rng)
|
||||
@@ -388,7 +388,7 @@ func _extreme_node(pos: Dictionary, nodes: Array, first: bool) -> Dictionary:
|
||||
|
||||
|
||||
func _finish() -> void:
|
||||
# oracle: design/ux/level-map.md:3 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/ux/level-map.md:3 (判定上报通道——验证函数组)
|
||||
if _checks == 0:
|
||||
_problems.append("零断言执行(断言数下限闸——三硬闸③)")
|
||||
if _problems.is_empty():
|
||||
|
||||
@@ -14,6 +14,11 @@
|
||||
# 锚(具名节点用其名、带脚本节点用脚本文件名;@ 临时名段整体不入键),控件本体记 类#钮面文本,
|
||||
# 同键控件按接线遍历序追加 #序位。键在确定构建序下可复现;UI 构建序变更会平移同键序位——
|
||||
# 表现为键漂移而非静默丢失,属测量面可接受代价。
|
||||
# 开发面边界(2026-11-07 用户裁——调试面不入玩家可达对账宇宙,登记层过滤、不产豁免条目):
|
||||
# DebugOverlay 浮窗(class_name 声明,main_ui_build 装配注「玩家画面零常驻调试件」)+
|
||||
# ui.screens.debug_badge 调试角标+ui.screens.inter_overlay 调试两层模式专用层
|
||||
# (main.gd _on_battle_finished「仅调试模式接线」——正常冒险层间推进归 AdventureRunner)。
|
||||
# 依据=test-conventions「调试注入两层」调试面发布态恒隐条款;过滤为纯减法(玩家可达面零影响)。
|
||||
# 用法:探针 _ready 里 `_recorder = ProbeCoverageRecorder.new()` → `attach(main, "<探针名>")`;
|
||||
# 收尾 `dump_js()`。探针共用面不入 S 系扫描面(code-conventions §4.13 分档)。
|
||||
extends RefCounted
|
||||
@@ -29,6 +34,8 @@ var _node_keys: Dictionary = {} # 接线时分配的节点键(instanc
|
||||
var _key_seen: Dictionary = {} # 基键出现次数(同键控件按遍历序追加序位)
|
||||
var _wired: Dictionary = {} # 已连 pressed 的按钮实例 id → true(防重连)
|
||||
var _skipped_temp := 0 # 临时名命中笔数(不入 key,只计数)
|
||||
var _dev_roots: Array[Node] = [] # 开发面根(DebugOverlay 浮窗/调试角标/inter 调试专用层)——整棵子树不入账
|
||||
var _dev_skipped := 0 # 开发面接线跳过笔数(观测面——dump meta 携带)
|
||||
|
||||
|
||||
# ---- 装载与接线 ----
|
||||
@@ -37,10 +44,32 @@ func attach(main: Node, probe_name: String) -> void:
|
||||
_main = main
|
||||
_probe_name = probe_name
|
||||
ProbeUiUtil.coverage_hook = self # 共用件真输入口统一过旗标(push_click/push_click_at/push_motion)
|
||||
_resolve_dev_roots()
|
||||
_wire_buttons()
|
||||
_wire_camp_scene()
|
||||
|
||||
|
||||
# 开发面根解析(三处成员引用/脚本身份单点,见头注边界声明)——解析仅 attach 时一次,
|
||||
# 解析不到的根跳过(无独立计数;attach 后重建的开发面根会漏滤)——漏滤方向由 seen 集
|
||||
# 「开发面键 0」核对兜住(开发面键再现=过滤失守),玩家可达面零误滤风险(早退只命中三根)。
|
||||
func _resolve_dev_roots() -> void:
|
||||
var overlay := _find_by_script(_main, "debug_overlay")
|
||||
if overlay != null:
|
||||
_dev_roots.append(overlay)
|
||||
var ui: Object = _main.get("ui")
|
||||
if ui != null:
|
||||
var screens: Object = ui.get("screens")
|
||||
if screens != null:
|
||||
for prop in ["inter_overlay", "debug_badge"]:
|
||||
var n: Node = screens.get(prop)
|
||||
if n != null:
|
||||
_dev_roots.append(n)
|
||||
|
||||
|
||||
func _is_dev_root(n: Node) -> bool:
|
||||
return _dev_roots.has(n)
|
||||
|
||||
|
||||
# 懒重扫:每次真输入 push 前接线一遍新入树的按钮(开层重建的钮行在下次点击前接上);
|
||||
# 已释放按钮的连接随节点释放失效,登记表按 instance_id 清扫。
|
||||
func mark_real_input() -> void:
|
||||
@@ -63,6 +92,9 @@ func _wire_buttons() -> void:
|
||||
|
||||
|
||||
func _wire_into(node: Node) -> void:
|
||||
if _is_dev_root(node): # 开发面整棵子树不入账(接线与命中双断——pressed 沿都不连)
|
||||
_dev_skipped += 1
|
||||
return
|
||||
if node is BaseButton:
|
||||
var id := node.get_instance_id()
|
||||
if not _wired.has(id):
|
||||
@@ -184,6 +216,9 @@ func enumerate_seen() -> Array[String]:
|
||||
|
||||
|
||||
func _seen_into(node: Node, keys: Array[String]) -> void:
|
||||
if _is_dev_root(node): # seen 枚举与登记同源同滤——开发面不入「存在但零覆盖」缺口面
|
||||
_dev_skipped += 1
|
||||
return
|
||||
if node is BaseButton:
|
||||
var id := node.get_instance_id()
|
||||
var key := String(_node_keys.get(id, ""))
|
||||
@@ -199,7 +234,7 @@ func _seen_into(node: Node, keys: Array[String]) -> void:
|
||||
# ---- 落盘(JSONP 式 .js——file:// 页面 <script src> 直载)----
|
||||
|
||||
func dump_js() -> void:
|
||||
# oracle: design/engineering/test-conventions.md:30 (覆盖登记落盘守卫——判定上报通道组)
|
||||
# 判据出处: design/engineering/test-conventions.md:30 (覆盖登记落盘守卫——判定上报通道组)
|
||||
var dir := DirAccess.open("res://")
|
||||
if dir == null:
|
||||
push_error("[coverage] res:// 打不开,登记未落盘")
|
||||
@@ -228,6 +263,8 @@ func dump_js() -> void:
|
||||
"hits_total": total,
|
||||
"seen_total": seen.size(),
|
||||
"skipped_temp_names": _skipped_temp,
|
||||
"dev_face_roots": _dev_roots.size(),
|
||||
"dev_face_skipped": _dev_skipped,
|
||||
},
|
||||
"hits": _hits,
|
||||
"seen": seen,
|
||||
|
||||
@@ -198,16 +198,30 @@ static func window_panel(view: Control) -> PanelContainer:
|
||||
# 判据:钮 rect 整体落在窗板内(上缘留 8px、下缘让开底栏 64px——返回营地钮带高约 56px)。
|
||||
# 保险丝 60 次滚叶防异常内容死循环;窗板缺失或钮句柄失效即返回(调用方前置断言兜底)。
|
||||
static func scroll_button_into_view(main, view: Control, btn: Control) -> void:
|
||||
var panel := window_panel(view)
|
||||
if panel == null or btn == null:
|
||||
if btn == null:
|
||||
return
|
||||
# 可视区基准=按钮最近的 ScrollContainer 祖先(滚动裁剪的真实可视域);
|
||||
# 无滚动祖先时回退窗壳面板(旧基准——按钮不在滚动容器内的窗体形态)。
|
||||
# 修因(迭代 55):秘宝匣 _ws_scroll 336px 高于窗壳面板内部——以面板为基准时
|
||||
# 裁剪区外按钮被误判「在视」,滚轮零滚动、真点落空(实证:购买 25/装配/升级 零命中)。
|
||||
var panel: Control = null
|
||||
var anc: Node = btn.get_parent()
|
||||
while anc != null and anc is Control:
|
||||
if anc is ScrollContainer:
|
||||
panel = anc
|
||||
break
|
||||
anc = anc.get_parent()
|
||||
if panel == null:
|
||||
panel = window_panel(view)
|
||||
if panel == null:
|
||||
return
|
||||
var guard := 60
|
||||
while guard > 0:
|
||||
var r := btn.get_global_rect()
|
||||
var pr := panel.get_global_rect()
|
||||
if r.position.y >= pr.position.y + 8.0 and r.end.y <= pr.end.y - 64.0:
|
||||
if r.position.y >= pr.position.y + 8.0 and r.end.y <= pr.end.y - 8.0:
|
||||
return
|
||||
var down := r.end.y > pr.end.y - 64.0
|
||||
var down := r.end.y > pr.end.y - 8.0
|
||||
push_wheel(main, pr.get_center(), 1 if down else -1)
|
||||
guard -= 1
|
||||
await main.get_tree().process_frame
|
||||
|
||||
@@ -76,14 +76,14 @@ func _seals_text(f: Dictionary) -> String:
|
||||
|
||||
# ---- 流程状态位(与可见性是两回事,逐步单独判)----
|
||||
func _expect_adventure_seal(step: String, expect: bool) -> void:
|
||||
# oracle: design/engineering/architecture/architecture.md:168 (状态位判据组)
|
||||
# 判据出处: design/engineering/architecture/architecture.md「呈现层装配与会话生命周期」 (状态位判据组)
|
||||
var got: bool = main.run_scope != null and main.run_scope.is_adventure
|
||||
if got != expect:
|
||||
_problems.append("%s:流程状态位 is_adventure 应为 %s,实测 %s" % [step, str(expect), str(got)])
|
||||
|
||||
|
||||
func _expect_exclusive(step: String, expect: String) -> void:
|
||||
# oracle: design/engineering/architecture/architecture.md:168 (屏级互斥组)
|
||||
# 判据出处: design/engineering/architecture/architecture.md「呈现层装配与会话生命周期」 (屏级互斥组)
|
||||
var f: Dictionary = _screen_seals()
|
||||
var pairs := [["菜单", "营地"], ["菜单", "冒险"], ["营地", "冒险"], ["终局", "营地"], ["终局", "菜单"]]
|
||||
for pair in pairs:
|
||||
@@ -110,7 +110,7 @@ func _frames(n: int = 2) -> void:
|
||||
|
||||
# ---- ① boot:菜单独占 ----
|
||||
func _step_boot_menu() -> void:
|
||||
# oracle: design/engineering/test-conventions.md:49 (boot 组)
|
||||
# 判据出处: design/engineering/test-conventions.md:49 (boot 组)
|
||||
_expect_exclusive("①boot", "菜单")
|
||||
_expect_adventure_seal("①boot", false)
|
||||
if main.ui.modals.menu.debug_overlay != null and main.ui.modals.menu.debug_overlay.visible:
|
||||
@@ -119,7 +119,7 @@ func _step_boot_menu() -> void:
|
||||
|
||||
# ---- ② 菜单 → 营地(真实按钮信号路径)----
|
||||
func _step_menu_to_camp() -> void:
|
||||
# oracle: design/ux/camp.md:10 (菜单→营地组)
|
||||
# 判据出处: design/ux/camp.md:10 (菜单→营地组)
|
||||
# 营地经进档开层(卡② mn9 新路径——campbtn 已撤;槽模板注入隔离命名空间)
|
||||
if not await ProbeUiUtil.open_camp_via_savesel(main, "smoke_screen_flow_camp"):
|
||||
_problems.append("②菜单→营地:选档层新档钮缺失(新路径驱动中断)")
|
||||
@@ -130,7 +130,7 @@ func _step_menu_to_camp() -> void:
|
||||
|
||||
# ---- ③ 营地 → 菜单(回退路径:篝火二次确认动线——卡④ 营地唯一回菜单出口;选档层随关层收闭同收)----
|
||||
func _step_camp_back_to_menu() -> void:
|
||||
# oracle: design/ux/camp.md:22 (营地→菜单组)
|
||||
# 判据出处: design/ux/camp.md:22 (营地→菜单组)
|
||||
if main.ui.modals.camp.scene == null:
|
||||
_problems.append("③营地→菜单:营地场景根句柄缺失〔句柄子容器批 V1〕")
|
||||
return
|
||||
@@ -147,7 +147,7 @@ func _step_camp_back_to_menu() -> void:
|
||||
# ---- 选档界面开合(卡③ sl10):函数直调开层(「进入存档」钮归卡② mn3)→屏级互斥采样→
|
||||
# 返回钮真实信号路径关闭→ESC 语境不响应(卡⑤ 契约 2 范围外——零实现即断言)。----
|
||||
func _step_save_select_open_back() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/存档序列化.md:61 (选档开合组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/存档序列化.md:61 (选档开合组)
|
||||
ProbeUiUtil.isolate_save(main, "smoke_screen_flow_savesel", false, false, true)
|
||||
main.save_manager.slots.attach_slot_template("user://smoke_screen_flow_savesel_slot_%d.json")
|
||||
var esb: Button = ProbeUiUtil.find_button(main.ui.screens.start_overlay, "进入存档", "prefix")
|
||||
@@ -194,7 +194,7 @@ func seals_check_single(f: Dictionary) -> bool:
|
||||
|
||||
|
||||
func _step_menu_to_adventure() -> void:
|
||||
# oracle: design/ux/level-map.md:11 (菜单→冒险组)
|
||||
# 判据出处: design/ux/level-map.md:11 (菜单→冒险组)
|
||||
# 开始冒险钮已随三入口化撤出——函数直调中间态(卡② mn10;卡④ 大门两分支换代)
|
||||
main._start_adventure_from_menu()
|
||||
await _frames(3)
|
||||
@@ -208,7 +208,7 @@ func _step_menu_to_adventure() -> void:
|
||||
|
||||
# ---- ⑤ 冒险内屏切换:关卡地图屏 ⇄ 战斗屏(屏内两态,屏级仍独占)----
|
||||
func _step_level_to_battle_and_back() -> void:
|
||||
# oracle: design/ux/battle.md:57 (冒险内切换组)
|
||||
# 判据出处: design/ux/battle.md:57 (冒险内切换组)
|
||||
if not main.ui.modals.run.level_screen.visible:
|
||||
_problems.append("⑤冒险内切换:前置不在关卡地图屏")
|
||||
return
|
||||
@@ -228,7 +228,7 @@ func _step_level_to_battle_and_back() -> void:
|
||||
|
||||
# ---- ⑥ 放弃 → 终局(放弃路径=回退路径的非正常出口)----
|
||||
func _step_abandon_to_end() -> void:
|
||||
# oracle: design/ux/level-map.md:42 (放弃→终局组)
|
||||
# 判据出处: design/ux/level-map.md:42 (放弃→终局组)
|
||||
if main.run_scope == null or main.run_scope.adventure == null:
|
||||
_problems.append("⑥放弃→终局:前置无冒险实例")
|
||||
return
|
||||
@@ -241,7 +241,7 @@ func _step_abandon_to_end() -> void:
|
||||
|
||||
# ---- ⑦ 终局 → 菜单(终局出口返回开始界面)----
|
||||
func _step_end_back_to_menu() -> void:
|
||||
# oracle: design/ux/settlement-summary.md:24 (终局→菜单组)
|
||||
# 判据出处: design/ux/settlement-summary.md:24 (终局→菜单组)
|
||||
if main.ui.modals.run.end_overlay == null or not main.ui.modals.run.end_overlay.visible:
|
||||
_problems.append("⑦终局→菜单:前置不在终局")
|
||||
return
|
||||
@@ -254,7 +254,7 @@ func _step_end_back_to_menu() -> void:
|
||||
|
||||
# ---- ③' 营地 Esc 浮窗+HUD(卡⑤——真实键盘合成路径 push_input;四态路由+三出口+语境分支+余额单口)----
|
||||
func _step_camp_esc_and_hud() -> void:
|
||||
# oracle: design/ux/camp.md:45 (Esc 段组)
|
||||
# 判据出处: design/ux/camp.md:45 (Esc 段组)
|
||||
ProbeUiUtil.isolate_save(main, "smoke_screen_flow_esc", false, false, true)
|
||||
if not await ProbeUiUtil.open_camp_via_savesel(main, "smoke_screen_flow_esc"):
|
||||
_problems.append("[esc] 营地入口缺失(选档层新档钮驱动中断)〔ESC批 V1〕")
|
||||
@@ -320,7 +320,7 @@ func _step_camp_esc_and_hud() -> void:
|
||||
# 判的是呈现形态换壳(近全屏面板→居中定尺寸窗壳 720×640):窗几何/场景根留底/遮罩 0.6/关闭钮/
|
||||
# 互斥单开/ESC 优先序/窗内滚动/生涯同壳——行为面(开合路由)零改,只钉窗壳可观测提交面。
|
||||
func _step_camp_window_shell() -> void:
|
||||
# oracle: design/ux/camp.md:45 (窗壳 V1–V8 组)
|
||||
# 判据出处: design/ux/camp.md:45 (窗壳 V1–V8 组)
|
||||
ProbeUiUtil.isolate_save(main, "smoke_screen_flow_winshell", false, false, true)
|
||||
if not await ProbeUiUtil.open_camp_via_savesel(main, "smoke_screen_flow_winshell"):
|
||||
_problems.append("[winshell] 营地入口缺失(选档层新档钮驱动中断)〔窗口化批 V1〕")
|
||||
@@ -478,7 +478,7 @@ func _step_camp_window_shell() -> void:
|
||||
|
||||
# 清窗经真实钮(逐窗巡检 visible→钮 pressed——栈 close 恢复账随层清空)
|
||||
func _close_all_windows(views: Dictionary) -> void:
|
||||
# oracle: design/ux/camp.md:45 (清窗组)
|
||||
# 判据出处: design/ux/camp.md:45 (清窗组)
|
||||
for k: StringName in [&"forge", &"chest", &"dummy", &"board"]:
|
||||
var w: Control = views[k]
|
||||
if not w.visible:
|
||||
|
||||
@@ -46,7 +46,7 @@ func _ready() -> void:
|
||||
await get_tree().process_frame
|
||||
# ① 菜单(启动态)——无帮助文腿(玩法速览牌不在画面,checker 无对应断言段);
|
||||
# 无副标题件,菜单文字锚只剩标题
|
||||
# oracle: design/engineering/code-conventions.md:54 模态覆盖层层级规则——boot 态顶层=start_overlay、菜单文字在屏(菜单组)
|
||||
# 判据出处: design/engineering/code-conventions.md:54 模态覆盖层层级规则——boot 态顶层=start_overlay、菜单文字在屏(菜单组)
|
||||
# 自检一:boot 态顶层可见全屏层=start_overlay(内容层树遍历——屏壳是容器不当候选:
|
||||
# camp 壳 boot 态 vis=true 但成员恒隐,裸树遍历自顶向下会先命中透明壳)
|
||||
var top: Node = null
|
||||
@@ -83,7 +83,7 @@ func _ready() -> void:
|
||||
_meta[String(pair[0])] = ProbeUiUtil.label_rect(main.ui.screens.start_overlay, String(pair[1]))
|
||||
await _snap("menu")
|
||||
# ①b 设置面(settingsbtn 真实信号路径,开合同款真实钮)
|
||||
# oracle: design/gdd/systems/表现与支撑/设置与本地化.md:95 最小设置面已兑现:音量/减动效/全屏/退出,载体=独立设置存储+设置界面视图(设置组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/设置与本地化.md:95 最小设置面已兑现:音量/减动效/全屏/退出,载体=独立设置存储+设置界面视图(设置组)
|
||||
var setbtn: Button = ProbeUiUtil.find_button(main.ui.screens.start_overlay, "设置", "prefix")
|
||||
if setbtn == null:
|
||||
_problems.append("设置入口缺失(开始层 settingsbtn)")
|
||||
@@ -103,7 +103,7 @@ func _ready() -> void:
|
||||
ProbeUiUtil.push_click(main, set_close)
|
||||
await get_tree().process_frame
|
||||
# ①c 生涯页(函数直调中间态——卡② mn10:careerbtn 已撤,卡④ 笔记本改挂前无 UI 路径)
|
||||
# oracle: design/ux/camp.md:45 生涯页与四子界面同壳同形态(生涯组)
|
||||
# 判据出处: design/ux/camp.md:45 生涯页与四子界面同壳同形态(生涯组)
|
||||
if true:
|
||||
main._open_career()
|
||||
for i in 3:
|
||||
@@ -123,7 +123,7 @@ func _ready() -> void:
|
||||
if main.ui.modals.menu.career_overlay.visible or not main.ui.screens.start_overlay.visible:
|
||||
_problems.append("生涯页关闭后页未隐藏或开始层未恢复")
|
||||
# ② 营地场景图(进档开层新路径——卡② mn9;截图=可点击场景图形态——卡④ 契约 9 V1 验收载体)
|
||||
# oracle: design/ux/camp.md:16 营地=可点击场景图(七物件命中区表——场景图形态组)
|
||||
# 判据出处: design/ux/camp.md:16 营地=可点击场景图(七物件命中区表——场景图形态组)
|
||||
if not await ProbeUiUtil.open_camp_via_savesel(main, "smoke_shot_probe_camp"):
|
||||
_problems.append("营地入口缺失(选档层新档钮——新路径驱动中断)〔句柄子容器批 V1〕")
|
||||
else:
|
||||
@@ -132,16 +132,16 @@ func _ready() -> void:
|
||||
ProbeUiUtil.push_click_at(main, main.ui.modals.camp.scene.get_hotspot_rect(&"campfire").get_center()) # 收闭改接篝火动线(营地根态无关闭钮——卡④ T5)
|
||||
await get_tree().process_frame
|
||||
ProbeUiUtil.push_click(main, main.ui.modals.menu.confirm_dialog._confirm_btn) # 二次确认离场(关层收闭——选档层随收)
|
||||
# oracle: design/ux/camp.md:22 篝火=离开营地回主菜单、必须二次确认(篝心动线组)
|
||||
# 判据出处: design/ux/camp.md:22 篝火=离开营地回主菜单、必须二次确认(篝心动线组)
|
||||
await get_tree().process_frame
|
||||
# ③ 关卡画面(开始冒险→停点出图——入口停点语义;函数直调中间态 mn10)
|
||||
# oracle: design/ux/level-map.md:11 入口停点候选数=入口个数(关卡画面出图组)
|
||||
# 判据出处: design/ux/level-map.md:11 入口停点候选数=入口个数(关卡画面出图组)
|
||||
main._start_adventure_from_menu()
|
||||
for i in 4:
|
||||
await get_tree().process_frame
|
||||
await _snap("level")
|
||||
# GP 常驻徽章断言(读数=runner.gp 现值,刷新=写路径回执后重查〔open_choices〕)
|
||||
# oracle: design/ux/level-map.md:77 GP 常驻徽章=GP 图标+数字(冒险钱包读数;写路径回执后重查)
|
||||
# 判据出处: design/ux/level-map.md:77 GP 常驻徽章=GP 图标+数字(冒险钱包读数;写路径回执后重查)
|
||||
var gp_label: Label = ProbeUiUtil.find_label(main.ui.modals.run.level_screen, "◈ ")
|
||||
if gp_label == null or not gp_label.is_visible_in_tree():
|
||||
_problems.append("GP 徽章不可见(关卡画面顶栏)")
|
||||
@@ -150,7 +150,7 @@ func _ready() -> void:
|
||||
if gp_label.text != expect:
|
||||
_problems.append("GP 徽章读数不符: %s != %s" % [gp_label.text, expect])
|
||||
# ④ 战斗(节点一——candidate_rect 命中矩形真点选路提交,ui_click_probe 同款)
|
||||
# oracle: design/ux/battle.md:122 B 区构建:站桩/明细带/轮次指示(战斗出图与几何锚组)
|
||||
# 判据出处: design/ux/battle.md:122 B 区构建:站桩/明细带/轮次指示(战斗出图与几何锚组)
|
||||
if main.ui.modals.run.level_screen != null and main.ui.modals.run.level_screen.visible:
|
||||
ProbeUiUtil.push_click_at(main, main.ui.modals.run.level_screen.candidate_rect(0).get_center())
|
||||
for i in 10:
|
||||
@@ -162,14 +162,14 @@ func _ready() -> void:
|
||||
_meta["stage_view"] = _node_rect(main.ui.battle.stage)
|
||||
_meta["board_zone"] = _node_rect(main.ui.battle.board_zone)
|
||||
# draw calls 真机测量(基线 270→目标 <200;窗口化渲染帧读数,
|
||||
# oracle: design/gdd/systems/表现与支撑/HUD框架.md:264 同屏 draw calls < 200(战斗屏性能预算组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/HUD框架.md:264 同屏 draw calls < 200(战斗屏性能预算组)
|
||||
# 取值时机=战斗截图同帧后一帧——呈现终态满负荷)
|
||||
await get_tree().process_frame
|
||||
var dc := int(Performance.get_monitor(Performance.RENDER_TOTAL_DRAW_CALLS_IN_FRAME))
|
||||
_meta["battle_draw_calls"] = dc
|
||||
print("[shot_probe] battle draw_calls=%d(基线 270/目标 <200)" % dc)
|
||||
# ⑤ 状态面板(战斗屏状态角标真实信号路径;层在战斗屏之上开合)
|
||||
# oracle: design/gdd/systems/表现与支撑/HUD框架.md:277 模态打开遮挡或输入锁定、关闭逐层恢复(状态面板组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/HUD框架.md:277 模态打开遮挡或输入锁定、关闭逐层恢复(状态面板组)
|
||||
var pb: Button = main.ui.battle.board_zone.status_badge
|
||||
if pb == null or not pb.visible or pb.disabled:
|
||||
_problems.append("状态面板入口角标缺失/置灰(战斗屏 board_zone)")
|
||||
@@ -211,7 +211,7 @@ func _ready() -> void:
|
||||
|
||||
|
||||
func _snap(key: String) -> void:
|
||||
# oracle: design/engineering/test-conventions.md:84 golden 测试项扩项——截图采集清单扩项、实跑截图冻结(截图采集组)
|
||||
# 判据出处: design/engineering/test-conventions.md:84 golden 测试项扩项——截图采集清单扩项、实跑截图冻结(截图采集组)
|
||||
await RenderingServer.frame_post_draw
|
||||
await RenderingServer.frame_post_draw
|
||||
var img: Image = get_viewport().get_texture().get_image()
|
||||
|
||||
@@ -13,7 +13,7 @@ var _problems: Array = []
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
# oracle: design/ux/battle.md:122 (明细带挖掘期收起——T17 视觉扩展时序组)
|
||||
# 判据出处: design/ux/battle.md:122 (明细带挖掘期收起——T17 视觉扩展时序组)
|
||||
await get_tree().process_frame
|
||||
main = load("res://presentation/main.tscn").instantiate()
|
||||
add_child(main)
|
||||
|
||||
@@ -30,7 +30,7 @@ var _hero_pos := Vector2i.ZERO
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
# oracle: design/engineering/test-conventions.md:84 (微光截图取证——渲染闸采集组)
|
||||
# 判据出处: design/engineering/test-conventions.md:84 (微光截图取证——渲染闸采集组)
|
||||
if DisplayServer.get_name() == "headless":
|
||||
print("[type_glow_shot] FAIL — 本探针必须窗口化运行(headless 无渲染面,截图必空)")
|
||||
get_tree().quit(1)
|
||||
|
||||
@@ -42,6 +42,9 @@ var _locktest_cell := Vector2i(-9, -9)
|
||||
var _locktest_frames := 0
|
||||
var _stoptest_state := 0 # 停手钮定型化覆盖段状态机(0=待命 1=已点击待核效 2=完结)——每 run 一次
|
||||
var _stoptest_frames := 0
|
||||
var _tested_node_continue := false # 迭代 55 B5 扩面——节点继续钮真输入一次性段
|
||||
var _tested_curio_refresh := false # 迭代 55 B5 扩面——三选一刷新钮真输入一次性段
|
||||
var _tested_shop_buy := false # 迭代 55 B5 扩面——商店购买+刷新钮真输入一次性段
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
@@ -61,7 +64,7 @@ func _ready() -> void:
|
||||
if main.ui.modals.menu.debug_overlay.visible:
|
||||
_problems.append("调试浮窗 boot 自动开(菜单优先失守)")
|
||||
# ---- 营地购买合成点击路径(选档进档开层→购买→关闭全走真实按钮信号路径;
|
||||
# oracle: design/gdd/systems/跨局循环/天赋树.md:198 (营地购买段——单币购买只扣钥石组)
|
||||
# 判据出处: design/gdd/systems/跨局循环/天赋树.md:198 (营地购买段——单币购买只扣钥石组)
|
||||
# 隔离存档换注 main.meta 保确定性(禁碰真实档 user://save.json);陈旧档段首清理)----
|
||||
for p0 in ["user://smoke_click_probe_reset.json", "user://smoke_click_probe_reset.json.tmp"]:
|
||||
if FileAccess.file_exists(p0):
|
||||
@@ -139,6 +142,47 @@ func _ready() -> void:
|
||||
iso_fk.load_from_disk()
|
||||
if String(iso_fk.meta_state.tree_state().fork_sides.get("fg_posture", "")) != "b":
|
||||
_problems.append("Fork 切态未持久(reload 后激活态 != b)〔句柄子容器批 V1〕")
|
||||
# ---- 树面高档钮扩面段(迭代 55 B5——购买0/20/40/60 档经调试注钥后真点+先手压制 A 侧回切)----
|
||||
# 判据出处: design/gdd/systems/跨局循环/天赋树.md:198 (营地购买段——购买面真输入扩面组)
|
||||
iso_c.meta_state._add_keystone(9999)
|
||||
main.ui.modals.camp.tree.refresh_camp(main._camp_data())
|
||||
await get_tree().process_frame
|
||||
for target_id in ["forge_workshop", "att_socket2", "att_socket3"]:
|
||||
var hi_btn: Button = null
|
||||
for card in main.ui.modals.camp.tree._tree_buy_btns:
|
||||
if String(card.node_id) == target_id:
|
||||
hi_btn = card.btn
|
||||
if hi_btn == null:
|
||||
_problems.append("树面 %s 购买钮缺失(高档扩面驱动中断)〔QA余 V13〕" % target_id)
|
||||
elif hi_btn.disabled:
|
||||
_problems.append("树面 %s 购买钮置灰(注钥后仍不可达——前置链失守)〔QA余 V13〕" % target_id)
|
||||
else:
|
||||
var price_hi: int = int(main.db.crossrun.meta_tree.row_by_id(target_id).get("price", 0))
|
||||
var bal_hi: int = iso_c.meta_state.keystone_balance()
|
||||
await ProbeUiUtil.scroll_button_into_view(main, main.ui.modals.camp.tree, hi_btn)
|
||||
ProbeUiUtil.push_click(main, hi_btn) # 真点高档购买(forge_workshop=购买 0 钥石档)
|
||||
await get_tree().process_frame
|
||||
main.ui.modals.camp.tree.refresh_camp(main._camp_data())
|
||||
await get_tree().process_frame
|
||||
if int(iso_c.meta_state.keystone_balance()) != bal_hi - price_hi:
|
||||
_problems.append("树面 %s 购买未出账(%d→%d 应 −%d)〔QA余 V13〕" % [target_id, bal_hi, iso_c.meta_state.keystone_balance(), price_hi])
|
||||
if int(iso_c.meta_state.tree_state().nodes.get(target_id, 0)) != 1:
|
||||
_problems.append("树面 %s 购买落点失守(节点未点亮)〔QA余 V13〕" % target_id)
|
||||
# 先手压制 A 侧回切(先手压制=fg_posture A 侧名——购买默认 A 后既有段切至 B,A 侧钮零真输入命中)
|
||||
var side_a: Button = null
|
||||
for fk in main.ui.modals.camp.tree._tree_fork_btns:
|
||||
if String(fk.group) == "fg_posture" and String(fk.side) == "a":
|
||||
side_a = fk.btn
|
||||
if side_a == null:
|
||||
_problems.append("Fork A 侧切换钮缺失(先手压制回切驱动中断)〔QA余 V13〕")
|
||||
else:
|
||||
await ProbeUiUtil.scroll_button_into_view(main, main.ui.modals.camp.tree, side_a)
|
||||
ProbeUiUtil.push_click(main, side_a) # 真点先手压制(B→A 回切)
|
||||
await get_tree().process_frame
|
||||
var iso_fk2 = load("res://core/save_manager.gd").new("user://smoke_click_probe_camp_slot_1.json")
|
||||
iso_fk2.load_from_disk()
|
||||
if String(iso_fk2.meta_state.tree_state().fork_sides.get("fg_posture", "")) != "a":
|
||||
_problems.append("先手压制回切未持久(reload 后激活态 != a)〔QA余 V13〕")
|
||||
# ---- 营地灰字取色断言(迭代36 wc4——四子界面拆分后按视图分扫;卡④ T5 迁移):
|
||||
# 图鉴锁文→codex 视图(锚随形态订正去『柜台』);秘宝行未解锁/同调形态/档位/刷新行→treasure 视图
|
||||
# _workshop_holder 子树。font_color 逐处 == STONE_DEEP;树链段已闭树界面。
|
||||
@@ -193,6 +237,116 @@ func _ready() -> void:
|
||||
else:
|
||||
ProbeUiUtil.push_click(main, treasure_back) # 真点返回(closed 同源钮)
|
||||
await get_tree().process_frame
|
||||
# ---- 秘宝架扩面段(迭代 55 B5——购买10/购买25/装配/升级30 真点;钥匙门先经树购 ks_workshop 点亮)----
|
||||
# 判据出处: design/gdd/systems/跨局循环/秘宝.md:246 (秘宝匣柜台——购买/装配/升级真输入扩面组)
|
||||
ProbeUiUtil.push_click_at(main, main.ui.modals.camp.scene.get_hotspot_rect(&"dummy").get_center()) # 木桩→树界面
|
||||
await get_tree().process_frame
|
||||
var ks_btn: Button = null
|
||||
for card in main.ui.modals.camp.tree._tree_buy_btns:
|
||||
if String(card.node_id) == "ks_workshop":
|
||||
ks_btn = card.btn
|
||||
if ks_btn == null:
|
||||
_problems.append("树面 ks_workshop 购买钮缺失(秘宝架扩面前置中断)〔QA余 V13〕")
|
||||
else:
|
||||
await ProbeUiUtil.scroll_button_into_view(main, main.ui.modals.camp.tree, ks_btn)
|
||||
ProbeUiUtil.push_click(main, ks_btn) # 真点(free 行——钥匙门点亮)
|
||||
await get_tree().process_frame
|
||||
var ws_tree_back: Button = ProbeUiUtil.find_button(main.ui.modals.camp.tree, "返回营地")
|
||||
if ws_tree_back != null:
|
||||
ProbeUiUtil.push_click(main, ws_tree_back) # 真点返回营地
|
||||
await get_tree().process_frame
|
||||
ProbeUiUtil.push_click_at(main, main.ui.modals.camp.scene.get_hotspot_rect(&"chest").get_center()) # 宝箱→秘宝匣(已点亮态)
|
||||
await get_tree().process_frame
|
||||
var tview = main.ui.modals.camp.treasure
|
||||
iso_c.meta_state._add_keystone(9999)
|
||||
tview.refresh_camp(main._camp_data())
|
||||
await get_tree().process_frame
|
||||
var buy10: Button = null
|
||||
var buy25: Button = null
|
||||
for c in tview.find_children("*", "Button", true, false):
|
||||
var cb := c as Button
|
||||
if cb.text.begins_with("购买 10") and not cb.disabled and buy10 == null:
|
||||
buy10 = cb
|
||||
if cb.text.begins_with("购买 25") and not cb.disabled and buy25 == null:
|
||||
buy25 = cb
|
||||
if buy10 == null:
|
||||
_problems.append("秘宝架「购买 10」钮缺失或置灰(真输入可达性失守)〔QA余 V13〕")
|
||||
else:
|
||||
var bal10: int = iso_c.meta_state.keystone_balance()
|
||||
await ProbeUiUtil.scroll_button_into_view(main, tview, buy10)
|
||||
ProbeUiUtil.push_click(main, buy10) # 真点购买 10
|
||||
await get_tree().process_frame
|
||||
if int(iso_c.meta_state.keystone_balance()) != bal10 - 10:
|
||||
_problems.append("秘宝购买 10 未出账(%d→%d 应 −10)〔QA余 V13〕" % [bal10, iso_c.meta_state.keystone_balance()])
|
||||
if buy25 == null:
|
||||
_problems.append("秘宝架「购买 25」钮缺失或置灰(真输入可达性失守)〔QA余 V13〕")
|
||||
else:
|
||||
var bal25: int = iso_c.meta_state.keystone_balance()
|
||||
await ProbeUiUtil.scroll_button_into_view(main, tview, buy25)
|
||||
ProbeUiUtil.push_click(main, buy25) # 真点购买 25
|
||||
await get_tree().process_frame
|
||||
if int(iso_c.meta_state.keystone_balance()) != bal25 - 25:
|
||||
_problems.append("秘宝购买 25 未出账(%d→%d 应 −25)〔QA余 V13〕" % [bal25, iso_c.meta_state.keystone_balance()])
|
||||
# 装配/升级段前重开视图(_refresh_attune_segment 的 unlocked 快照仅随 rebuild 刷新——
|
||||
# 购后不重开则新解锁件的装配钮恒禁用;重开=全量重建,快照与解锁集对齐)
|
||||
var ws_back_mid: Button = ProbeUiUtil.find_button(main.ui.modals.camp.treasure, "返回营地")
|
||||
if ws_back_mid != null:
|
||||
ProbeUiUtil.push_click(main, ws_back_mid)
|
||||
await get_tree().process_frame
|
||||
ProbeUiUtil.push_click_at(main, main.ui.modals.camp.scene.get_hotspot_rect(&"chest").get_center())
|
||||
await get_tree().process_frame
|
||||
tview = main.ui.modals.camp.treasure
|
||||
var attune_entry: Dictionary = {}
|
||||
for a in tview._attune_btns:
|
||||
if not (a["btn"] as Button).disabled:
|
||||
attune_entry = a
|
||||
break
|
||||
if attune_entry.is_empty():
|
||||
_problems.append("秘宝架「装配」钮缺失或全置灰(解锁后应可装)〔QA余 V13〕")
|
||||
else:
|
||||
await ProbeUiUtil.scroll_button_into_view(main, tview, attune_entry["btn"])
|
||||
ProbeUiUtil.push_click(main, attune_entry["btn"]) # 真点装配(toggle_attunement 写口)
|
||||
await get_tree().process_frame
|
||||
if not (iso_c.meta_state.attune_state().equipped as Array).has(String(attune_entry["item_id"])):
|
||||
_problems.append("秘宝装配落点失守(%s 未入装配集)〔QA余 V13〕" % String(attune_entry["item_id"]))
|
||||
if tview._refresh_btn == null or tview._refresh_btn.disabled:
|
||||
_problems.append("秘宝架「升级」钮缺失或置灰(Y 刷新升级行可达性失守)〔QA余 V13〕")
|
||||
else:
|
||||
var bal_y: int = iso_c.meta_state.keystone_balance()
|
||||
var ylvl0: int = int(iso_c.meta_state.attune_state().refresh_level)
|
||||
await ProbeUiUtil.scroll_button_into_view(main, tview, tview._refresh_btn)
|
||||
ProbeUiUtil.push_click(main, tview._refresh_btn) # 真点升级(attune_refresh 同账写口)
|
||||
await get_tree().process_frame
|
||||
if int(iso_c.meta_state.attune_state().refresh_level) != ylvl0 + 1:
|
||||
_problems.append("秘宝升级落点失守(refresh_level %d→%d)〔QA余 V13〕" % [ylvl0, int(iso_c.meta_state.attune_state().refresh_level)])
|
||||
if int(iso_c.meta_state.keystone_balance()) >= bal_y:
|
||||
_problems.append("秘宝升级未出账(%d→%d)〔QA余 V13〕" % [bal_y, iso_c.meta_state.keystone_balance()])
|
||||
var ws_back2: Button = ProbeUiUtil.find_button(main.ui.modals.camp.treasure, "返回营地")
|
||||
if ws_back2 != null:
|
||||
ProbeUiUtil.push_click(main, ws_back2) # 真点返回(关闭后交还布告板段)
|
||||
await get_tree().process_frame
|
||||
# 树面购买 30 档(ks_refresh——前置 ks_workshop 已随秘宝段点亮;同账路由购 L2 扣 60)
|
||||
ProbeUiUtil.push_click_at(main, main.ui.modals.camp.scene.get_hotspot_rect(&"dummy").get_center())
|
||||
await get_tree().process_frame
|
||||
var ks30_btn: Button = null
|
||||
for card in main.ui.modals.camp.tree._tree_buy_btns:
|
||||
if String(card.node_id) == "ks_refresh":
|
||||
ks30_btn = card.btn
|
||||
if ks30_btn == null:
|
||||
_problems.append("树面 ks_refresh 购买钮缺失(30 档扩面驱动中断)〔QA余 V13〕")
|
||||
elif ks30_btn.disabled:
|
||||
_problems.append("树面 ks_refresh 购买钮置灰(ks_workshop 点亮后仍不可达)〔QA余 V13〕")
|
||||
else:
|
||||
var bal30: int = iso_c.meta_state.keystone_balance()
|
||||
await ProbeUiUtil.scroll_button_into_view(main, main.ui.modals.camp.tree, ks30_btn)
|
||||
ProbeUiUtil.push_click(main, ks30_btn) # 真点购买 30(同账路由——L2 档扣 60)
|
||||
await get_tree().process_frame
|
||||
if int(iso_c.meta_state.keystone_balance()) != bal30 - 60:
|
||||
_problems.append("树面 ks_refresh 购买未出账(%d→%d 应 −60)〔QA余 V13〕" % [bal30, iso_c.meta_state.keystone_balance()])
|
||||
var tree_back3: Button = ProbeUiUtil.find_button(main.ui.modals.camp.tree, "返回营地")
|
||||
if tree_back3 != null:
|
||||
ProbeUiUtil.push_click(main, tree_back3)
|
||||
await get_tree().process_frame
|
||||
ProbeUiUtil.push_click_at(main, main.ui.modals.camp.scene.get_hotspot_rect(&"board").get_center()) # 布告板→图鉴占位(锁文宿主)
|
||||
await get_tree().process_frame
|
||||
var codex_labels: Array = []
|
||||
@@ -209,12 +363,44 @@ func _ready() -> void:
|
||||
else:
|
||||
ProbeUiUtil.push_click(main, codex_back) # 真点返回(closed 同源钮)
|
||||
await get_tree().process_frame
|
||||
# ---- 营地 Esc 浮窗设置动线(迭代 55 B5——Esc 真键唤起浮窗→设置钮真点开设置层)----
|
||||
# 判据出处: design/ux/camp.md:45 (Esc 段组——三出口真输入扩面)
|
||||
var esc_ev := InputEventKey.new()
|
||||
esc_ev.keycode = KEY_ESCAPE
|
||||
esc_ev.physical_keycode = KEY_ESCAPE
|
||||
esc_ev.pressed = true
|
||||
main.get_viewport().push_input(esc_ev, true) # 真键(营地根态——screen_flow 同款合成路径)
|
||||
await get_tree().process_frame
|
||||
if not main.ui.modals.camp.esc_overlay.visible:
|
||||
_problems.append("营地 Esc 真键未唤起浮窗(扩面驱动中断)〔QA余 V13〕")
|
||||
else:
|
||||
var esc_settings: Button = main.ui.modals.camp.esc_overlay._settings_btn
|
||||
if esc_settings == null:
|
||||
_problems.append("Esc 浮窗「设置」钮缺失〔QA余 V13〕")
|
||||
else:
|
||||
ProbeUiUtil.push_click(main, esc_settings) # 真点设置(settings_requested 三出口之一)
|
||||
await get_tree().process_frame
|
||||
if not main.ui.modals.menu.settings_overlay.visible:
|
||||
_problems.append("Esc 浮窗「设置」真点击未开设置层(落点判定失守)〔QA余 V13〕")
|
||||
var esc_set_close: Button = ProbeUiUtil.find_button(main.ui.modals.menu.settings_overlay, "返回", "prefix")
|
||||
if esc_set_close != null:
|
||||
ProbeUiUtil.push_click(main, esc_set_close) # 真点返回收设置层
|
||||
await get_tree().process_frame
|
||||
if main.ui.modals.camp.esc_overlay.visible:
|
||||
var esc_ev2 := InputEventKey.new()
|
||||
esc_ev2.keycode = KEY_ESCAPE
|
||||
esc_ev2.physical_keycode = KEY_ESCAPE
|
||||
esc_ev2.pressed = true
|
||||
main.get_viewport().push_input(esc_ev2, true) # 再按 Esc 关浮窗(同键单义切换)
|
||||
await get_tree().process_frame
|
||||
if main.ui.modals.camp.esc_overlay.visible:
|
||||
_problems.append("再按 Esc 未关浮窗(扩面收口失守)〔QA余 V13〕")
|
||||
main._on_camp_closed() # 关层收闭(营地与选档层同收+开始层恢复——卡④ T3)
|
||||
await get_tree().process_frame
|
||||
if main.ui.modals.camp.scene.visible or not main.ui.screens.start_overlay.visible:
|
||||
_problems.append("营地关闭后未恢复开始层〔句柄子容器批 V1〕")
|
||||
# ---- 选档界面行为走查(迭代 33 卡③):sl5 空槽新档链/sl6 有档槽进档链/sl7 删档二次确认双路径
|
||||
# oracle: design/gdd/systems/表现与支撑/存档序列化.md:61 (选档走查段——激活态建立组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/存档序列化.md:61 (选档走查段——激活态建立组)
|
||||
# /sl8 消费方分派单口/sl12 动线序不回闪。开层=函数直调(「进入存档」钮归卡② mn3——中间态在案);
|
||||
# 隔离=attach_slot_template 注入命名空间,夹具按槽直写(卡① 注入口)。----
|
||||
var csel_tpl := "user://smoke_click_probe_savesel_slot_%d.json"
|
||||
@@ -318,7 +504,7 @@ func _ready() -> void:
|
||||
_problems.append("[savesel] 关闭后层未隐藏或开始层未恢复(模态栈单口失守)")
|
||||
|
||||
# ---- 设置面段(隔离 store 换注禁碰真实 user://settings.cfg;真实按钮信号路径)----
|
||||
# oracle: design/gdd/systems/表现与支撑/设置与本地化.md:4 (设置面段——最小设置面组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/设置与本地化.md:4 (设置面段——最小设置面组)
|
||||
for p0 in ["user://smoke_click_probe_settings.cfg"]:
|
||||
if FileAccess.file_exists(p0):
|
||||
DirAccess.remove_absolute(p0)
|
||||
@@ -341,7 +527,7 @@ func _ready() -> void:
|
||||
if motion_btn == null:
|
||||
_problems.append("设置面减动效开关缺失")
|
||||
else:
|
||||
motion_btn.button_pressed = false # off=快速(减动效开)
|
||||
ProbeUiUtil.push_click(main, motion_btn) # 真点拨杆(迭代 55 B5 扩面——原 button_pressed 直改绕过真输入命中面;off=快速)
|
||||
await get_tree().process_frame
|
||||
if UiKit.motion_scale != 0.5:
|
||||
_problems.append("减动效开未生效(motion_scale != 0.5)")
|
||||
@@ -349,10 +535,24 @@ func _ready() -> void:
|
||||
chk.load_settings()
|
||||
if not chk.reduced_motion:
|
||||
_problems.append("减动效开未持久化(设置独立存储失守——卡④ AC⑤)")
|
||||
motion_btn.button_pressed = true # on=完整回常态
|
||||
ProbeUiUtil.push_click(main, motion_btn) # 真点回完整(on=完整)
|
||||
await get_tree().process_frame
|
||||
if UiKit.motion_scale != 1.0:
|
||||
_problems.append("减动效关未恢复(motion_scale != 1.0)")
|
||||
# 全屏拨杆真输入段(迭代 55 B5 扩面——settings 两枚 switch_toggle 其二;store 态断言为准)
|
||||
var fs_btn: Button = main.ui.modals.menu.settings_overlay._fs_btn
|
||||
if fs_btn == null:
|
||||
_problems.append("设置面全屏拨杆缺失〔QA余 V13〕")
|
||||
else:
|
||||
var fs0: bool = iso_s.fullscreen
|
||||
ProbeUiUtil.push_click(main, fs_btn) # 真点全屏拨杆
|
||||
await get_tree().process_frame
|
||||
if bool(iso_s.fullscreen) == fs0:
|
||||
_problems.append("全屏拨杆真点击未翻转 store 态(%s→%s)〔QA余 V13〕" % [str(fs0), str(iso_s.fullscreen)])
|
||||
ProbeUiUtil.push_click(main, fs_btn) # 真点复原
|
||||
await get_tree().process_frame
|
||||
if bool(iso_s.fullscreen) != fs0:
|
||||
_problems.append("全屏拨杆复原失守〔QA余 V13〕")
|
||||
# 音量实时生效(卡④ AC①):滑杆 30% → master bus db 与 store 双读
|
||||
var vol: HSlider = main.ui.modals.menu.settings_overlay._volume
|
||||
vol.value = 30
|
||||
@@ -385,7 +585,7 @@ func _ready() -> void:
|
||||
if ProbeUiUtil.find_button(main.ui.modals.menu.settings_overlay, "退出游戏", "prefix") != null:
|
||||
_problems.append("设置面仍含退出游戏按钮(二轮 D1——只在一级菜单出现)")
|
||||
# ---- 生涯页段(入口点击链+关闭回返,真实按钮信号路径;本页只读零写,
|
||||
# oracle: design/ux/camp.md:45 (生涯页段——同壳同形态组)
|
||||
# 判据出处: design/ux/camp.md:45 (生涯页段——同壳同形态组)
|
||||
# 值断言归冒烟投影段与 ui_struct 结构族,此处只验开合路径与零跳转)----
|
||||
# 生涯页驱动=函数直调中间态(卡② mn10——careerbtn 已撤,卡④ 笔记本改挂前无 UI 路径)
|
||||
if true:
|
||||
@@ -412,7 +612,7 @@ func _ready() -> void:
|
||||
_problems.append("生涯页关闭后未恢复开始层")
|
||||
# 换回全新零档(营地购买态不泄入后续冒险段——确定性;档案残留下次段首清理兜底)
|
||||
ProbeUiUtil.isolate_save(main, "smoke_click_probe_reset", true, false, true) # 零档换注(继续上局钮已随三入口化撤出——卡② mn8)
|
||||
main.ui.modals.menu.debug_overlay.set_sliders(400, 200) # 高加成保 L1 可完成——L2 岔路选路段只能经冒险推进到达(单关直入口退役)
|
||||
main.ui.modals.menu.debug_overlay.set_sliders(2000, 2000) # 高加成保 L1 可完成——L2 岔路选路段只能经冒险推进到达(单关直入口退役)
|
||||
main.ui.modals.menu.debug_overlay.seed_edit.text = "42" # 种子钉死(种子框默认为空=非首局随机,行为走查确定性禁依赖默认分支)
|
||||
var t1 := Time.get_ticks_msec()
|
||||
main._start_adventure_from_menu()
|
||||
@@ -451,7 +651,17 @@ func _ready() -> void:
|
||||
action_budget -= 1
|
||||
await get_tree().process_frame
|
||||
if main.ui.modals.run.node_overlay.visible:
|
||||
ProbeUiUtil.push_click(main, main.ui.modals.run.node_overlay._btn)
|
||||
# 节点继续一次性落点断言(迭代 55 B5 扩面——休息停点真输入继续,落点=overlay 关闭回到战斗屏)
|
||||
if not _tested_node_continue:
|
||||
_tested_node_continue = true
|
||||
if not main.ui.flow.locks_input():
|
||||
_problems.append("节点信息浮窗打开时输入未锁定〔QA余 V13〕")
|
||||
ProbeUiUtil.push_click(main, main.ui.modals.run.node_overlay._btn) # 真点「继续」
|
||||
await get_tree().process_frame
|
||||
if main.ui.modals.run.node_overlay.visible:
|
||||
_problems.append("节点「继续」真点击后浮窗未关(落点判定失守)〔QA余 V13〕")
|
||||
else:
|
||||
ProbeUiUtil.push_click(main, main.ui.modals.run.node_overlay._btn)
|
||||
_seals_placed.clear()
|
||||
continue
|
||||
if main.ui.modals.run.curio_overlay.visible:
|
||||
@@ -475,6 +685,17 @@ func _ready() -> void:
|
||||
_problems.append("奖励节点战报文案未换代(卡⑤ wr2——旧话残留或新话缺失)")
|
||||
if not main.ui.flow.locks_input():
|
||||
_problems.append("三选一停点打开时输入未锁定")
|
||||
# 三选一刷新钮一次性真输入段(迭代 55 B5 扩面——重抽意图经 refresh_requested 信号走组合根
|
||||
# on_refresh;落点=停点仍开且候选重投喂,零崩零关)
|
||||
if not _tested_curio_refresh and main.ui.modals.run.curio_overlay._refresh_btn.visible \
|
||||
and not main.ui.modals.run.curio_overlay._refresh_btn.disabled:
|
||||
_tested_curio_refresh = true
|
||||
var cards_before: int = (main.ui.modals.run.curio_overlay._bcards as Array).size()
|
||||
ProbeUiUtil.push_click(main, main.ui.modals.run.curio_overlay._refresh_btn) # 真点刷新图标钮
|
||||
await get_tree().process_frame
|
||||
if not main.ui.modals.run.curio_overlay.visible \
|
||||
or (main.ui.modals.run.curio_overlay._bcards as Array).size() != cards_before:
|
||||
_problems.append("三选一刷新钮真点击后停点未保持重投喂形态(落点判定失守)〔QA余 V13〕")
|
||||
if main.ui.modals.run.curio_overlay._continue_btn.visible:
|
||||
ProbeUiUtil.push_click(main, main.ui.modals.run.curio_overlay._continue_btn)
|
||||
elif main.run_scope.adventure != null and not main.run_scope.adventure.stop()._node_pending.is_empty():
|
||||
@@ -498,6 +719,39 @@ func _ready() -> void:
|
||||
if shop_leave == null:
|
||||
_problems.append("商店「离开商店」钮缺失(真输入化驱动中断)")
|
||||
else:
|
||||
# 商店购买+刷新一次性真输入段(迭代 55 B5 扩面——购买写口经 buy_requested 信号,
|
||||
# 刷新经 refresh_requested→on_shop_refresh MC-1;测试通道注 GP 保可达,行为序列零依赖)
|
||||
if not _tested_shop_buy:
|
||||
_tested_shop_buy = true
|
||||
var rnr_shop = main.run_scope.runner
|
||||
if rnr_shop != null and int(rnr_shop.gp) < 500:
|
||||
rnr_shop.gp = 500 # 测试通道:注 GP 使购买与付费刷新可达(探针行为序列不依赖本注记)
|
||||
await get_tree().process_frame
|
||||
var buy_btn: Button = null
|
||||
for c in main.ui.modals.menu.shop_overlay.find_children("*", "Button", true, false):
|
||||
var cb := c as Button
|
||||
if cb.text.begins_with("购买") and not cb.disabled:
|
||||
buy_btn = cb
|
||||
break
|
||||
if buy_btn == null:
|
||||
_problems.append("商店「购买」钮缺失或全置灰(真输入可达性失守)〔QA余 V13〕")
|
||||
else:
|
||||
var gp_before: int = int(rnr_shop.gp)
|
||||
ProbeUiUtil.push_click(main, buy_btn) # 真点购买
|
||||
await get_tree().process_frame
|
||||
if int(rnr_shop.gp) >= gp_before:
|
||||
_problems.append("商店购买真点击未出账(GP %d→%d)〔QA余 V13〕" % [gp_before, int(rnr_shop.gp)])
|
||||
var shop_refresh: Button = main.ui.modals.menu.shop_overlay._refresh_btn
|
||||
if shop_refresh != null and not shop_refresh.disabled:
|
||||
var gp_r: int = int(rnr_shop.gp)
|
||||
ProbeUiUtil.push_click(main, shop_refresh) # 真点刷新(重抽货架)
|
||||
await get_tree().process_frame
|
||||
if not main.ui.modals.menu.shop_overlay.visible:
|
||||
_problems.append("商店刷新真点击后覆盖层异常关闭(落点判定失守)〔QA余 V13〕")
|
||||
if int(rnr_shop.gp) > gp_r:
|
||||
_problems.append("商店刷新未出账反涨(GP %d→%d)" % [gp_r, int(rnr_shop.gp)])
|
||||
else:
|
||||
_problems.append("商店刷新钮缺失或置灰(MC-1 投喂可达性失守)〔QA余 V13〕")
|
||||
ProbeUiUtil.push_click(main, shop_leave) # 真点离店(leave_pressed 同源钮)放行选路
|
||||
_seals_placed.clear()
|
||||
continue
|
||||
@@ -534,13 +788,21 @@ func _ready() -> void:
|
||||
_problems.append("地图屏加成帽未换代为「属性值提升」(wa2)")
|
||||
# 选路定向(卡⑤ d4——wa4 转正的必达前提):候选含奖励节点优先选(真实
|
||||
# press_candidate 命中路径零变化,只改索引策略);无奖励候选回退交替(岔路使用率观测面保留)
|
||||
# 迭代 55 B5 扩面——节点继续段未测时休息候选最优先(node_overlay 仅休息停点开启)
|
||||
var pick := _route_clicks % 2
|
||||
if main.run_scope.runner != null:
|
||||
for nd in main.run_scope.runner.nodes():
|
||||
var nd_id: int = main.run_scope.runner.node_id_of(nd)
|
||||
if String(nd.get("type", "")) == "reward" and main.ui.modals.run.level_screen._cand_ids.has(nd_id):
|
||||
pick = main.ui.modals.run.level_screen._cand_ids.find(nd_id)
|
||||
break
|
||||
if not _tested_node_continue:
|
||||
for nd in main.run_scope.runner.nodes():
|
||||
var nd_id_rest: int = main.run_scope.runner.node_id_of(nd)
|
||||
if String(nd.get("type", "")) == "rest" and main.ui.modals.run.level_screen._cand_ids.has(nd_id_rest):
|
||||
pick = main.ui.modals.run.level_screen._cand_ids.find(nd_id_rest)
|
||||
break
|
||||
if pick == _route_clicks % 2:
|
||||
for nd in main.run_scope.runner.nodes():
|
||||
var nd_id: int = main.run_scope.runner.node_id_of(nd)
|
||||
if String(nd.get("type", "")) == "reward" and main.ui.modals.run.level_screen._cand_ids.has(nd_id):
|
||||
pick = main.ui.modals.run.level_screen._cand_ids.find(nd_id)
|
||||
break
|
||||
ProbeUiUtil.push_click_at(main, main.ui.modals.run.level_screen.candidate_rect(pick).get_center())
|
||||
_route_clicks += 1
|
||||
_seals_placed.clear()
|
||||
@@ -640,7 +902,7 @@ func _ready() -> void:
|
||||
_run_modal_probes_once()
|
||||
_play_one_action()
|
||||
# ---- 预算耗尽收口(T17 硬锁批适配):选件变强后 probe 可能活过预算不再战败——断点段前提
|
||||
# oracle: design/ux/level-map.md:11 (主循环选路段——入口候选组)
|
||||
# 判据出处: design/ux/level-map.md:11 (主循环选路段——入口候选组)
|
||||
# (冒险终局面在案)须主动满足。收口驱动:任何停点层逐个推进(复用主循环分支语义)至
|
||||
# level_screen 选路停点→真实放弃二击→终局面。战斗中则继续 autoplay 打完。
|
||||
if not _done and not main.ui.modals.run.end_overlay.is_open():
|
||||
@@ -689,7 +951,7 @@ func _ready() -> void:
|
||||
_play_one_action()
|
||||
# 排播期/Waiting 等待态=自然流逝(演播秒级、锁随队列空解除)
|
||||
# T17 适配:战败或放弃二途皆达终局面(放弃=预算耗尽收口路径——见上),断点段前提不变。
|
||||
# oracle: design/gdd/systems/表现与支撑/存档序列化.md:180 (断点段——断点域与续玩组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/存档序列化.md:180 (断点段——断点域与续玩组)
|
||||
await get_tree().process_frame
|
||||
var bp_file := "user://smoke_click_probe_reset.json"
|
||||
var iso_chk = load("res://core/save_manager.gd").new(bp_file)
|
||||
@@ -720,8 +982,56 @@ func _ready() -> void:
|
||||
# 卡④大门 T5 接收,两头登记禁静默删除;续玩钮已退役,主菜单确认链已摘 mn6)。
|
||||
# ① 战斗收口断点写盘断言保留(上方——冒险流直调中间态 mn10)。
|
||||
|
||||
# ---- 迭代 55 B5 扩面收尾段(地图放弃动线——主循环流不定达放弃,独立新局确定性驱动)----
|
||||
# 判据出处: design/ux/level-map.md:11 (主循环选路段——放弃二击真输入组)
|
||||
ProbeUiUtil.isolate_save(main, "smoke_click_probe_expansion", true, false, true)
|
||||
main.ui.modals.menu.debug_overlay.set_sliders(2000, 2000) # 高加成上限(与主循环同口径——层速推进保放弃停点快达)
|
||||
main.ui.modals.menu.debug_overlay.seed_edit.text = "44"
|
||||
main._start_adventure_from_menu()
|
||||
await get_tree().process_frame
|
||||
await get_tree().process_frame
|
||||
var ab_guard := 4000
|
||||
var ab_done := false
|
||||
while ab_guard > 0 and not ab_done:
|
||||
ab_guard -= 1
|
||||
await get_tree().process_frame
|
||||
if main.ui.modals.run.end_overlay.is_open():
|
||||
break # 败退先至——放弃动线未达(问题行呈报)
|
||||
if main.ui.modals.run.curio_overlay.visible:
|
||||
if main.ui.modals.run.curio_overlay._continue_btn.visible:
|
||||
ProbeUiUtil.push_click(main, main.ui.modals.run.curio_overlay._continue_btn)
|
||||
elif main.run_scope.adventure != null and not main.run_scope.adventure.stop()._node_pending.is_empty():
|
||||
ProbeUiUtil.push_click_at(main, (main.ui.modals.run.curio_overlay._bcards[0] as Control).get_global_rect().get_center())
|
||||
await get_tree().process_frame
|
||||
ProbeUiUtil.push_click(main, main.ui.modals.run.curio_overlay._confirm_btn)
|
||||
else:
|
||||
ProbeUiUtil.push_click(main, main.ui.modals.run.curio_overlay._continue_btn)
|
||||
continue
|
||||
if main.ui.modals.run.node_overlay.visible:
|
||||
ProbeUiUtil.push_click(main, main.ui.modals.run.node_overlay._btn)
|
||||
continue
|
||||
if main.ui.modals.run.level_screen.visible:
|
||||
var ab_btn: Button = ProbeUiUtil.find_button(main.ui.modals.run.level_screen, "放弃", "prefix")
|
||||
if ab_btn != null:
|
||||
ProbeUiUtil.push_click(main, ab_btn) # 真点「放弃」一击(3s 确认窗)
|
||||
await get_tree().process_frame
|
||||
ProbeUiUtil.push_click(main, ab_btn) # 真点二击确认(窗内)
|
||||
for i in 10:
|
||||
await get_tree().process_frame
|
||||
if main.ui.modals.run.end_overlay.is_open():
|
||||
print("[click_probe] 放弃动线落点=终局面(地图放弃真输入)")
|
||||
else:
|
||||
_problems.append("放弃二击后未达终局面(落点判定失守)〔QA余 V13〕")
|
||||
ab_done = true
|
||||
if not ab_done:
|
||||
_problems.append("扩面收尾段放弃动线未达(guard 耗尽或败退先至)〔QA余 V13〕")
|
||||
# 终局面返回开始菜单(大门段菜单前提归位——句柄沿断点段同源 _btn,文本查找零命中实证)
|
||||
if main.ui.modals.run.end_overlay.is_open() and main.ui.modals.run.end_overlay._btn != null:
|
||||
ProbeUiUtil.push_click(main, main.ui.modals.run.end_overlay._btn) # 真点「返回」(_btn=继续/返回共用句柄——断点段同款)
|
||||
await get_tree().process_frame
|
||||
|
||||
# ---- 大门两分支断言段(卡④ T5 接收——卡② 移交落点;V3 断言面)----
|
||||
# oracle: design/ux/camp.md:16 (大门段——点大门先出确认组)
|
||||
# 判据出处: design/ux/camp.md:16 (大门段——点大门先出确认组)
|
||||
# 驱动=营地场景根点选信号(真实路由)→组合根 _on_camp_object_pressed→确认弹窗。
|
||||
# 无断点=两出口(是/否);有断点=三出口(set_breakpoint 注入选路停点形——同下方开场行③先例)。
|
||||
# 判据绑可观测提交面:点大门不直接进冒险(is_adventure 翻转只在 confirmed 后);cancelled 恒=
|
||||
@@ -746,7 +1056,7 @@ func _ready() -> void:
|
||||
await get_tree().process_frame
|
||||
# R4 位次真点(营地交互修复批——位次与语义绑定钉):真点前消费方前置读(gate_new 在册)→
|
||||
# 按类型定位按钮行 child(0) 全局矩形中心真点——位次若错接,此点命中「否」,下方主证(翻转)即红。
|
||||
# oracle: design/gdd/systems/表现与支撑/HUD框架.md:90 确认弹窗位次定值:推进在左、维持居右(R4 位次真点)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/HUD框架.md:90 确认弹窗位次定值:推进在左、维持居右(R4 位次真点)
|
||||
if String(main._confirm_consumer) != "gate_new":
|
||||
_problems.append("R4 前置:确认消费方非 gate_new(分派面漂移)〔营地交互批 V3〕")
|
||||
var gate_row := _first_hbox(main.ui.modals.menu.confirm_dialog)
|
||||
@@ -761,7 +1071,7 @@ func _ready() -> void:
|
||||
if String(main._confirm_consumer) != "":
|
||||
_problems.append("R4 主证:确认后消费方未清零〔营地交互批 V3〕")
|
||||
# 开局终态两钉(营地交互修复批——开始层隐藏收一处后的落点断言):
|
||||
# oracle: design/gdd/systems/冒险循环/关卡节点地图.md:78 关卡开始 → 呈现节点图选路停点(开局终态两钉)
|
||||
# 判据出处: design/gdd/systems/冒险循环/关卡节点地图.md:78 关卡开始 → 呈现节点图选路停点(开局终态两钉)
|
||||
# R1=确认后开始层不可见(被测改动特有提交面——修复前该句恒红=「开局被开始层盖住」缺陷本体);
|
||||
# R2=关卡画面(选路停点)在位(开局终态呈现面,与既有 is_adventure 断言同批缺一即红)
|
||||
if main.ui.screens.start_overlay.visible:
|
||||
@@ -865,10 +1175,10 @@ func _ready() -> void:
|
||||
|
||||
|
||||
# ---- 模态/tooltip/刷新链一次性覆盖(真实信号路径驱动) ----
|
||||
# oracle: design/gdd/systems/表现与支撑/HUD框架.md:277 (模态/tooltip 段——遮挡与输入锁组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/HUD框架.md:277 (模态/tooltip 段——遮挡与输入锁组)
|
||||
|
||||
func _run_modal_probes_once() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/HUD框架.md:277 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/HUD框架.md:277 (判定上报通道——验证函数组)
|
||||
# ⓪ 玩家面文案真实入口断言(迭代36 卡A wa1——C 区两段头随战斗常驻)
|
||||
if not _tested_copy and main.ui.battle.info_panel != null and main.ui.battle.info_panel.visible:
|
||||
_tested_copy = true
|
||||
@@ -906,7 +1216,15 @@ func _run_modal_probes_once() -> void:
|
||||
", "|"))
|
||||
if not legend_has_seal:
|
||||
_problems.append("图例缺『封印』格态条目(格态图例基线)")
|
||||
main.ui.modals.run.legend_overlay.close()
|
||||
# 图例关闭走真输入关闭钮(迭代 55 B5 扩面——原 .close() 函数直调绕过关闭钮命中面;
|
||||
# 本函数为主循环内同步调用(协程化会与主循环双分派竞态)——push_click 的 release
|
||||
# 同帧触发关闭处理器,断言同帧读数,零 await)
|
||||
var legend_close_btn: Button = ProbeUiUtil.find_button(main.ui.modals.run.legend_overlay, "关闭")
|
||||
if legend_close_btn == null:
|
||||
_problems.append("图例「关闭」钮缺失(真输入关闭路径驱动中断)〔QA余 V13〕")
|
||||
main.ui.modals.run.legend_overlay.close()
|
||||
else:
|
||||
ProbeUiUtil.push_click(main, legend_close_btn) # 真点「关闭」收图例
|
||||
if main.ui.modals.run.legend_overlay.is_open() or main.ui.flow.locks_input():
|
||||
_problems.append("图例关闭后未恢复输入/可见性")
|
||||
# ②/②b 明细回看模态无探针(M9/P-C7——冒险中回看归「生涯」,06 登记)
|
||||
@@ -944,7 +1262,7 @@ func _run_modal_probes_once() -> void:
|
||||
rnr.effective_ac(), int(rnr.gp)]
|
||||
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)
|
||||
# ③ _status_data() 直调对拍(sv7——键集恰八键+curios/feats 独立预期表)
|
||||
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
|
||||
@@ -952,18 +1270,18 @@ func _run_modal_probes_once() -> void:
|
||||
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 = []
|
||||
var expected: Array = []
|
||||
for rec in rnr.treasure_inventory.query_inventory(Callable()):
|
||||
var rr: Dictionary = rec
|
||||
oracle.append({"name": String(main.db.crossrun.treasure_items.row_by_id(String(rr.get("item_id", ""))).get("display_name", "")),
|
||||
expected.append({"name": String(main.db.crossrun.treasure_items.row_by_id(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()
|
||||
var curios_ok: bool = (sd_curios as Array).size() == (expected 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))
|
||||
for i in (expected as Array).size():
|
||||
curios_ok = curios_ok and String((sd_curios[i] as Dictionary).get("name", "")) == String((expected[i] as Dictionary).get("name", "")) and bool((sd_curios[i] as Dictionary).get("used", false)) == bool((expected[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)])
|
||||
_problems.append("_status_data().curios 对拍独立预期表失守(sv7——实测 %s vs %s)" % [str(sd_curios), str(expected)])
|
||||
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)")
|
||||
@@ -1001,6 +1319,13 @@ func _run_modal_probes_once() -> void:
|
||||
_problems.append("状态槽 tooltip 未随移出隐藏")
|
||||
break
|
||||
# ④ 弃图刷新确认链(pattern 7 异位:确认 chips 与常规确认不同位 + 显式代价文案)
|
||||
# 迭代 55 B5 扩面——确定性前置:RAP 不足时经 grant_reserve_ap 注足(测试通道——探针行为序列
|
||||
# 不依赖本注记;零注记时本链只在停手转存积累 RAP≥8 的偶发轮触发=覆盖不稳定实证)
|
||||
if not _tested_refresh_chain and main.run_scope.settlement != null \
|
||||
and main.run_scope.settlement.action_economy.phase == "Active" \
|
||||
and not main.run_scope.settlement.can_refresh() \
|
||||
and int(main.run_scope.settlement.action_economy.rap) < 8:
|
||||
main.run_scope.settlement.action_economy.grant_reserve_ap(8 - int(main.run_scope.settlement.action_economy.rap))
|
||||
if not _tested_refresh_chain and main.ui.battle.board_zone.ap_bar.is_refresh_ready():
|
||||
_tested_refresh_chain = true
|
||||
var rap_before: int = int(main.run_scope.settlement.action_economy.rap)
|
||||
@@ -1045,7 +1370,7 @@ func _labels_with_text(labels: Array, needle: String) -> Array:
|
||||
|
||||
|
||||
func _play_one_action() -> void:
|
||||
# oracle: design/gdd/systems/排雷循环/排雷玩法.md:236 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/gdd/systems/排雷循环/排雷玩法.md:236 (判定上报通道——验证函数组)
|
||||
var settlement = main.run_scope.settlement
|
||||
if settlement == null:
|
||||
return # 结算竞态防抖:battle_finished 与本轮动作调度同帧到达时 settlement 可能已被置空(基线既有噪声——加固)
|
||||
@@ -1086,7 +1411,7 @@ func _play_one_action() -> void:
|
||||
|
||||
|
||||
func _random_unknown_adjacent(minesweeper) -> Vector2i:
|
||||
# oracle: design/gdd/systems/排雷循环/排雷玩法.md:236 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/gdd/systems/排雷循环/排雷玩法.md:236 (判定上报通道——验证函数组)
|
||||
var hero: Vector2i = minesweeper.hero_pos()
|
||||
var cands: Array = []
|
||||
for dy in range(-1, 2):
|
||||
@@ -1102,13 +1427,13 @@ func _random_unknown_adjacent(minesweeper) -> Vector2i:
|
||||
|
||||
|
||||
func _click_stop() -> void:
|
||||
# oracle: design/gdd/systems/排雷循环/行动经济.md:262 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/gdd/systems/排雷循环/行动经济.md:262 (判定上报通道——验证函数组)
|
||||
# 停手走真实点击路径(AP 条「停手」钮 → 组合根 settle_batch——排雷玩法 D1×6① 回归载体)
|
||||
ProbeUiUtil.push_click(main, main.ui.battle.board_zone.ap_bar._btn_stop)
|
||||
|
||||
|
||||
# ---- T1 D1:锁期/重放断言(每 run 一次,脚本化场景——演播期豁免的断言化承载) ----
|
||||
# oracle: design/gdd/systems/排雷循环/排雷玩法.md:236 (锁期/重放段——拒绝与锁组)
|
||||
# 判据出处: design/gdd/systems/排雷循环/排雷玩法.md:236 (锁期/重放段——拒绝与锁组)
|
||||
# 锁期断言(排播队列活跃=锁面):注入封印击 → 直落被冻结不受理(封印未置)+ `_locked_click`
|
||||
# 缓存命中且为本注入(「最后一次」语义——注入帧内无并发点击)。
|
||||
# 重放断言(演播完边沿 pump_ended→_replay_locked_click 重入唯一动作通道):缓存清空 +
|
||||
@@ -1116,7 +1441,7 @@ func _click_stop() -> void:
|
||||
# 前提消失守卫:等重放期间出现停点层/战斗收场(注入时点恰逢终结演播)→ 静默弃测不计
|
||||
# problem(封印动作不杀伤,正常局不触发;断言实现形态取舍在代拍白名单内)。
|
||||
func _locktest_tick() -> void:
|
||||
# oracle: design/gdd/systems/排雷循环/排雷玩法.md:236 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/gdd/systems/排雷循环/排雷玩法.md:236 (判定上报通道——验证函数组)
|
||||
if _locktest_state == 2:
|
||||
return
|
||||
var settlement = main.run_scope.settlement
|
||||
@@ -1173,7 +1498,7 @@ func _locktest_tick() -> void:
|
||||
|
||||
|
||||
func _locktest_pick_cell(minesweeper) -> Vector2i:
|
||||
# oracle: design/gdd/systems/排雷循环/排雷玩法.md:236 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/gdd/systems/排雷循环/排雷玩法.md:236 (判定上报通道——验证函数组)
|
||||
var hero: Vector2i = minesweeper.hero_pos()
|
||||
for dy in range(-1, 2):
|
||||
for dx in range(-1, 2):
|
||||
@@ -1191,7 +1516,7 @@ func _locktest_pick_cell(minesweeper) -> Vector2i:
|
||||
# 断言两钉:①覆盖登记件停手键在场(零命中=驱动空转假绿,即红);②相位离开 Active(停手
|
||||
# 收轮)。前提消失守卫:点击后战局收场(终结演播竞态)→ 静默弃测不计 problem(正常局不触发)。
|
||||
func _stoptest_tick() -> void:
|
||||
# oracle: design/gdd/systems/排雷循环/排雷玩法.md:167 (停手=玩家主动收轮——判定上报通道)
|
||||
# 判据出处: design/gdd/systems/排雷循环/排雷玩法.md:167 (停手=玩家主动收轮——判定上报通道)
|
||||
if _stoptest_state == 2:
|
||||
return
|
||||
var settlement = main.run_scope.settlement
|
||||
@@ -1228,7 +1553,7 @@ func _stoptest_tick() -> void:
|
||||
|
||||
|
||||
func _stoptest_assert_hit() -> void:
|
||||
# oracle: design/gdd/systems/排雷循环/排雷玩法.md:167 (停手钮真输入命中登记——判定上报通道)
|
||||
# 判据出处: design/gdd/systems/排雷循环/排雷玩法.md:167 (停手钮真输入命中登记——判定上报通道)
|
||||
var stop_btn: Button = main.ui.battle.board_zone.ap_bar._btn_stop
|
||||
var stop_key := String(_recorder._node_keys.get(stop_btn.get_instance_id(), ""))
|
||||
if stop_key == "" or not _recorder._hits.has(stop_key):
|
||||
@@ -1236,7 +1561,7 @@ func _stoptest_assert_hit() -> void:
|
||||
|
||||
|
||||
func _first_hbox(node: Node) -> HBoxContainer:
|
||||
# oracle: design/gdd/systems/表现与支撑/HUD框架.md:90 (位次真点定位——R4 判据面辅助)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/HUD框架.md:90 (位次真点定位——R4 判据面辅助)
|
||||
if node is HBoxContainer:
|
||||
return node
|
||||
for ch in node.get_children():
|
||||
@@ -1247,7 +1572,7 @@ func _first_hbox(node: Node) -> HBoxContainer:
|
||||
|
||||
|
||||
func _report_stall(st: Dictionary) -> void:
|
||||
# oracle: design/engineering/test-conventions.md:30 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:30 (判定上报通道——验证函数组)
|
||||
var why := ""
|
||||
if st.phase == "Waiting":
|
||||
why = "结算停滞:Waiting 相位无推进(结算应同步走完——窗口段已删除)"
|
||||
@@ -1263,7 +1588,7 @@ func _report_stall(st: Dictionary) -> void:
|
||||
|
||||
|
||||
func _dump_state() -> void:
|
||||
# oracle: design/engineering/test-conventions.md:30 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:30 (判定上报通道——验证函数组)
|
||||
var diag: Dictionary = main.run_scope.settlement.diagnostic_snapshot() # 诊断观测面(私有字段零直读)
|
||||
print(" node_index=%s round=%s phase=%s ap=%s rap=%s" % [
|
||||
str(main.run_scope.runner.node_index), str(main.run_scope.settlement.round_index),
|
||||
|
||||
@@ -88,7 +88,7 @@ func _ratio(a: Color, b: Color) -> float:
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/HUD框架.md:281 (UI 文字对比度机检 ≥4.5:1 无区域例外——对比度组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/HUD框架.md:281 (UI 文字对比度机检 ≥4.5:1 无区域例外——对比度组)
|
||||
var problems: Array = []
|
||||
var pairs := _pairs()
|
||||
for p in pairs:
|
||||
|
||||
@@ -242,7 +242,7 @@ func _ready() -> void:
|
||||
_pre_fail("目标格选取失败 seal=%s dig=%s forbid=%s reveal=%s" % [seal_t, dig_t, forbid_t, reveal_t])
|
||||
return
|
||||
# ---- case① 悬停可挖域:motion → 悬停态到达(金框 + 十字光标)----
|
||||
# oracle: design/gdd/systems/表现与支撑/HUD框架.md:269 悬停光标三语义随目标切换、已开格路径预览、可挖域常驻微光(case①-③ 输入面)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/HUD框架.md:269 悬停光标三语义随目标切换、已开格路径预览、可挖域常驻微光(case①-③ 输入面)
|
||||
_push_motion(_pos(bv, seal_t))
|
||||
await get_tree().process_frame
|
||||
if bv.hover_probe() != seal_t:
|
||||
@@ -271,7 +271,7 @@ func _ready() -> void:
|
||||
if bv.mouse_default_cursor_shape != Input.CURSOR_ARROW:
|
||||
_problems.append("case③ 移动光标缺失(%d)" % bv.mouse_default_cursor_shape)
|
||||
# ---- case④ 右键封印:seal_place 到达组合根 + 核心封印态落位 ----
|
||||
# oracle: design/gdd/systems/排雷循环/排雷玩法.md:231 封印受理与印记随挖消失(case④/④b)
|
||||
# 判据出处: design/gdd/systems/排雷循环/排雷玩法.md:231 封印受理与印记随挖消失(case④/④b)
|
||||
_push_click(_pos(bv, seal_t), MOUSE_BUTTON_RIGHT)
|
||||
await get_tree().process_frame
|
||||
if not _has_action(seal_t, "seal_place"):
|
||||
@@ -285,14 +285,14 @@ func _ready() -> void:
|
||||
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 ----
|
||||
# oracle: design/gdd/systems/排雷循环/排雷玩法.md:236 拒绝三源可辨、任何拒绝零副作用(case⑤ chord)
|
||||
# 判据出处: design/gdd/systems/排雷循环/排雷玩法.md:236 拒绝三源可辨、任何拒绝零副作用(case⑤ chord)
|
||||
_push_click(_pos(bv, forbid_t), MOUSE_BUTTON_LEFT)
|
||||
_push_click(_pos(bv, forbid_t), MOUSE_BUTTON_LEFT)
|
||||
await get_tree().process_frame
|
||||
if _chords.is_empty() or _chords[_chords.size() - 1] != forbid_t:
|
||||
_problems.append("case⑤ chord_attempted 未达(chords=%s)" % [str(_chords)])
|
||||
# ---- case⑥ 左键挖:dig 到达组合根 + 核心揭格落位 ----
|
||||
# oracle: design/gdd/systems/排雷循环/排雷玩法.md:231 挖掘受理与揭格落位(case⑥)
|
||||
# 判据出处: design/gdd/systems/排雷循环/排雷玩法.md:231 挖掘受理与揭格落位(case⑥)
|
||||
_push_click(_pos(bv, dig_t), MOUSE_BUTTON_LEFT)
|
||||
await get_tree().process_frame
|
||||
if not _has_action(dig_t, "dig"):
|
||||
@@ -301,7 +301,7 @@ func _ready() -> void:
|
||||
if not ms.is_revealed(dig_t):
|
||||
_problems.append("case⑥ 核心揭格未落位(目标=%s)" % dig_t)
|
||||
# ---- case⑦ 英雄精灵世界落位(回归闸):精灵在 SubViewport 世界内,
|
||||
# oracle: design/ux/battle.md:154 主角站桩立绘带落影、贴脚与缩放档(case⑦-⑩ 呈现落位)
|
||||
# 判据出处: design/ux/battle.md:154 主角站桩立绘带落影、贴脚与缩放档(case⑦-⑩ 呈现落位)
|
||||
# 落位 = 格×步距 + 脚底锚(横=格中线/纵=格底往上 25% 线=HERO_GROUND_FRAC 自底边口径——「脚踩
|
||||
# 格子 1/4 处·从下往上数」;禁带壳层 _svp_container.position 居中偏移——上批误用壳层
|
||||
# cell_origin 实测右偏 ~7 格+偏离格心,实机截图用户报缺陷)。素材缺位树(hero==null,字形
|
||||
@@ -368,7 +368,7 @@ func _ready() -> void:
|
||||
if bv._world.hero.position.distance_to(expect10b) > 0.5:
|
||||
_problems.append("case⑩ 还原后英雄未复位(pos=%s expect=%s)" % [str(bv._world.hero.position), str(expect10b)])
|
||||
# ---- case⑪ 贴图缺位回退(字形「人」回归闸):texture 置 null + refresh → 英雄格数字 0 时字形
|
||||
# oracle: design/gdd/systems/表现与支撑/HUD框架.md:264 棋盘格子与数字完整可见——贴图缺位回退字形保可读(case⑪)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/HUD框架.md:264 棋盘格子与数字完整可见——贴图缺位回退字形保可读(case⑪)
|
||||
# 必须回退「人」(数字格仍显数字——回退判据与抑制判据同源);验毕还原纹理重绘。 ----
|
||||
if bv._world.hero != null:
|
||||
var hp11: Vector2i = ms.hero_pos()
|
||||
@@ -385,7 +385,7 @@ func _ready() -> void:
|
||||
bv._hero_sprite.tex_idle = tex_saved
|
||||
bv.refresh()
|
||||
# ---- case⑭ 变异角标(flat_damage 未开格视觉标记;resolver 注入面+落位+复位) ----
|
||||
# oracle: design/gdd/systems/排雷循环/地形变异.md:154 呈现按 effect/trigger/placement 数据路由、零特判(case⑭ 角标)
|
||||
# 判据出处: design/gdd/systems/排雷循环/地形变异.md:154 呈现按 effect/trigger/placement 数据路由、零特判(case⑭ 角标)
|
||||
var mark_t: Vector2i = _pick_forbidden(bv, ms) # 任一未开格(禁入=未开非相邻)
|
||||
if mark_t.x >= 0:
|
||||
bv.set_variant_mark_resolver(func(_c): return "flat_damage")
|
||||
@@ -399,7 +399,7 @@ func _ready() -> void:
|
||||
else:
|
||||
_problems.append("case⑭ 无未开格可验(板面异常)")
|
||||
# ---- case⑮ 视野迷雾(三态呈态/半径圈/移动跟随/开关两态零扰动/提灯插值位;
|
||||
# oracle: design/gdd/systems/表现与支撑/HUD框架.md:268 视野迷雾:灯光区随移动跟随、开关两态结算逐位一致(case⑮)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/HUD框架.md:268 视野迷雾:灯光区随移动跟随、开关两态结算逐位一致(case⑮)
|
||||
# 纯呈现零逻辑——迷雾开关切换前后 minesweeper 状态摘要逐位一致) ----
|
||||
var fogp: Dictionary = bv.fog_params_probe()
|
||||
if int(bv.FOG_RADIUS) != 8 or int(fogp["lantern"]) != 0 or not fogp["enabled"]:
|
||||
@@ -495,7 +495,7 @@ func _ready() -> void:
|
||||
# attune_param_sum 只读出口→set_fog_glow_bonus〕→视野 +4 生效与卸装复原。
|
||||
# settlement.attunement 手动注入=模拟 begin_battle enc 注入后的运行态〔注入面本体归
|
||||
# 冒烟+单测覆盖〕;不重启冒险——case⑯ 起既有板面继续用)
|
||||
# oracle: design/gdd/systems/跨局循环/元进度.md:156 解锁可见:装配强化下一局生效(case⑮ 装配链)
|
||||
# 判据出处: design/gdd/systems/跨局循环/元进度.md:156 解锁可见:装配强化下一局生效(case⑮ 装配链)
|
||||
main.save_manager.meta_state.set_attunement_equipped(["wide_scout_band"])
|
||||
var p_lantern: Dictionary = main.build_adventure_params(main.save_manager, main.meta)
|
||||
if not (p_lantern.get("attunement", []) as Array).has("wide_scout_band"):
|
||||
@@ -514,7 +514,7 @@ func _ready() -> void:
|
||||
_problems.append("case⑮ 卸装后 dist==R+1 格未复原遮蔽")
|
||||
main.save_manager.meta_state.set_attunement_equipped([])
|
||||
# ---- case⑯ 状态面板输入锁语义(R3——开层锁棋盘、关层恢复无残留;真实 push_input
|
||||
# oracle: design/gdd/systems/表现与支撑/HUD框架.md:277 模态打开输入锁定、关闭逐层恢复(case⑯ R3)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/HUD框架.md:277 模态打开输入锁定、关闭逐层恢复(case⑯ R3)
|
||||
# 路径:入口角标本体 press+release 真点击 → 状态面板开;锁期挖掘点击零穿透;「继续」关层后
|
||||
# 同格挖掘重新到达——锁语义与既有模态同款,无状态残留。置于全 case 之尾:取格若需前沿移位
|
||||
#(真实点击 move 扩大可挖域),其板面扰动不再波及任何既有断言——域恰 2 枚时 seal_t 带封印+
|
||||
@@ -570,7 +570,7 @@ func _ready() -> void:
|
||||
if not _has_action(status_dig, "dig") or not ms.is_revealed(status_dig):
|
||||
_problems.append("case⑯ 关闭后挖掘未到达(恢复无残留验证失守)")
|
||||
# ---- case⑰ 未开格类型微光(活板面走查:作用集恒等/开格灭/移动复亮
|
||||
# oracle: design/gdd/systems/表现与支撑/HUD框架.md:267 类型微光出圈即灭入圈即亮、已开与封锁格恒无微光(case⑰)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/HUD框架.md:267 类型微光出圈即灭入圈即亮、已开与封锁格恒无微光(case⑰)
|
||||
# 与出圈灭/相位门控/开关两态零扰动。与冒烟段数据面断言分工:此处走真实点击路径) ----
|
||||
if not bool(bv.type_glow_probe().get("enabled", false)):
|
||||
_problems.append("case⑰ 微光默认态非开(生产默认开——Q4 丙案)")
|
||||
@@ -759,7 +759,7 @@ func _push_release(pos: Vector2, btn: int) -> void:
|
||||
|
||||
|
||||
func _pre_fail(why: String) -> void:
|
||||
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
push_error("[ui_input_probe] FAIL — " + why)
|
||||
print("[ui_input_probe] FAIL — " + why)
|
||||
get_tree().quit(1)
|
||||
@@ -16,7 +16,7 @@ func _ready() -> void:
|
||||
await get_tree().process_frame
|
||||
var problems: Array = []
|
||||
# ---- 放弃入口(台账 119):默认隐藏(非冒险模式无冒险可放弃——开始层/调试两层)----
|
||||
# oracle: design/ux/level-map.md:59 放弃冒险入口=边缘动作、任意选路停点承载——非冒险模式无入口(默认隐藏)
|
||||
# 判据出处: design/ux/level-map.md:59 放弃冒险入口=边缘动作、任意选路停点承载——非冒险模式无入口(默认隐藏)
|
||||
if main.ui.modals.run.level_screen.abandon_btn == null or main.ui.modals.run.level_screen.abandon_btn.visible:
|
||||
print("[ui_probe] FAIL — 放弃按钮默认应隐藏(非冒险模式关)")
|
||||
get_tree().quit(1)
|
||||
@@ -24,7 +24,7 @@ func _ready() -> void:
|
||||
# ---- 营地覆盖层(主菜单入口/开合/余额文案/购买禁用态;
|
||||
# 隔离存档换注 main.meta 保确定性(禁碰真实档 user://save.json——SaveManager 路径注入纪律;
|
||||
# 营地先行段只换 meta 不换 save_manager——冒险段换注零档在前)----
|
||||
# oracle: design/ux/camp.md:45 五功能窗统一窗壳、五窗互斥单开、各窗自带关闭钮(营地开合组)
|
||||
# 判据出处: design/ux/camp.md:45 五功能窗统一窗壳、五窗互斥单开、各窗自带关闭钮(营地开合组)
|
||||
ProbeUiUtil.isolate_save(main, "smoke_ui_probe_camp", false, false, false)
|
||||
if not await ProbeUiUtil.open_camp_via_savesel(main, "smoke_ui_probe_camp"):
|
||||
problems.append("营地入口缺失(选档层新档钮——mn9 新路径驱动中断)〔句柄子容器批 V1〕")
|
||||
@@ -36,12 +36,12 @@ func _ready() -> void:
|
||||
problems.append("营地层打开时开始层未隐藏(规范 2.3 遮挡纪律)〔句柄子容器批 V1〕")
|
||||
else:
|
||||
# 天赋树子界面(场景点选真实路由——木桩命中区进树界面;卡④ T5 深员改指)
|
||||
# oracle: design/ux/camp.md:20 木桩命中区→天赋树界面入口(场景点选路由)
|
||||
# 判据出处: design/ux/camp.md:20 木桩命中区→天赋树界面入口(场景点选路由)
|
||||
ProbeUiUtil.push_click_at(main, main.ui.modals.camp.scene.get_hotspot_rect(&"dummy").get_center())
|
||||
await get_tree().process_frame
|
||||
if not main.ui.modals.camp.tree.visible:
|
||||
problems.append("天赋树子界面未打开(场景点选路由中断)〔营地图批 V2〕")
|
||||
# oracle: design/ux/camp.md:41 钥石余额常驻画面顶部 HUD 栏(余额文案组)
|
||||
# 判据出处: design/ux/camp.md:41 钥石余额常驻画面顶部 HUD 栏(余额文案组)
|
||||
if not String(main.ui.modals.camp.tree._balance_label.text).begins_with("钥石"):
|
||||
problems.append("营地余额文案缺失(%s)〔句柄子容器批 V1〕" % main.ui.modals.camp.tree._balance_label.text)
|
||||
if main.ui.modals.camp.tree._tree_buy_btns.is_empty():
|
||||
@@ -57,7 +57,7 @@ func _ready() -> void:
|
||||
ProbeUiUtil.push_click(main, ProbeUiUtil.find_button(main.ui.modals.camp.tree, "返回营地")) # 真点底栏返回钮→_emit_closed(camp_sub_view_base.gd:66-84)
|
||||
await get_tree().process_frame
|
||||
# 秘宝匣子界面(场景点选真实路由——宝箱命中区)
|
||||
# oracle: design/ux/camp.md:18 宝箱命中区→秘宝匣界面入口
|
||||
# 判据出处: design/ux/camp.md:18 宝箱命中区→秘宝匣界面入口
|
||||
ProbeUiUtil.push_click_at(main, main.ui.modals.camp.scene.get_hotspot_rect(&"chest").get_center())
|
||||
await get_tree().process_frame
|
||||
if main.ui.modals.camp.treasure._workshop_rows.size() != 12:
|
||||
@@ -76,7 +76,7 @@ func _ready() -> void:
|
||||
await get_tree().process_frame
|
||||
# ---- 入口选路停点(关卡开始即出节点图——level_started 后、首战前弹选路层;
|
||||
# candidate_rect 命中矩形真点 pick 入口单候选进首战)----
|
||||
# oracle: design/ux/level-map.md:11 入口停点候选数=入口个数(入口单候选断言)
|
||||
# 判据出处: design/ux/level-map.md:11 入口停点候选数=入口个数(入口单候选断言)
|
||||
# ---- 独立性断言(独立屏激活期战斗屏宿主整体隐藏,零透出)----
|
||||
if not main.ui.modals.run.level_screen.visible:
|
||||
problems.append("关卡开始未弹入口选路停点(level_screen 应可见)")
|
||||
@@ -92,7 +92,7 @@ func _ready() -> void:
|
||||
if not main.ui.screens.battle_root.visible or main.ui.modals.run.level_screen.visible:
|
||||
problems.append("选路提交战斗节点后未切回战斗屏(屏级互斥恢复失守)")
|
||||
# ---- 放弃入口·冒险模式可见 + 战斗中按下被拒(真实按钮路径——入口挂选路停点,战斗中拒绝为防御分支)----
|
||||
# oracle: design/ux/level-map.md:42 放弃冒险(任意选路停点)→ 冒险落幕——战斗域单事务在飞时拒绝
|
||||
# 判据出处: design/ux/level-map.md:42 放弃冒险(任意选路停点)→ 冒险落幕——战斗域单事务在飞时拒绝
|
||||
if main.ui.modals.run.level_screen.abandon_btn == null or not main.ui.modals.run.level_screen.abandon_btn.visible:
|
||||
problems.append("冒险模式放弃按钮未开放(选路停点入口在案)")
|
||||
else:
|
||||
@@ -101,7 +101,7 @@ func _ready() -> void:
|
||||
if main.run_scope.adventure == null or String(main.run_scope.adventure.state) != "running":
|
||||
problems.append("首战中放弃未被拒(战斗域单事务在飞——state 应保持 running)")
|
||||
# ---- 既有断言(回归基线;A 区无进度条——节点链进度由关卡画面独立屏承载)----
|
||||
# oracle: design/ux/battle.md:122 B 区构建组:主角与敌人站桩、明细带挖掘期收起、轮次指示、棋盘与 C 区/秘宝槽、首战进行中
|
||||
# 判据出处: design/ux/battle.md:122 B 区构建组:主角与敌人站桩、明细带挖掘期收起、轮次指示、棋盘与 C 区/秘宝槽、首战进行中
|
||||
if main.ui.battle.board_zone.board_view.cells_probe().is_empty():
|
||||
problems.append("棋盘格未生成(cells=0)")
|
||||
if main.ui.battle.board_zone.board_view.board() == null:
|
||||
@@ -119,7 +119,7 @@ func _ready() -> void:
|
||||
problems.append("B 区敌方站桩未构建(%d 个)" % main.ui.battle.stage.enemy_box.get_child_count())
|
||||
if main.ui.battle.board_zone.ap_bar._dots.get_child_count() != int(main.db.run.action_economy.ap_per_round):
|
||||
problems.append("D 区 AP 点阵数量错误(%d)" % main.ui.battle.board_zone.ap_bar._dots.get_child_count())
|
||||
# oracle: design/gdd/systems/排雷循环/行动经济.md:258 开轮硬重置:行动点回到表值(AP 点阵数=ap_per_round)
|
||||
# 判据出处: design/gdd/systems/排雷循环/行动经济.md:258 开轮硬重置:行动点回到表值(AP 点阵数=ap_per_round)
|
||||
if main.ui.battle.board_zone.ap_bar.refresh_remaining_text() == "":
|
||||
problems.append("D 区弃图刷新按钮文案缺失")
|
||||
if main.ui.battle.board_zone.toast == null:
|
||||
@@ -136,7 +136,7 @@ func _ready() -> void:
|
||||
# E 区无道具面板——四区构建由其余断言覆盖
|
||||
|
||||
# ---- 敌方站桩投影(顺位徽标/意图三档动作动画占位——敌人系统×4,全站桩)----
|
||||
# oracle: design/gdd/systems/表现与支撑/HUD框架.md:275 敌人呈现:意图占位三档可见性——普通攻击零表示、类型级提示(站桩投影组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/HUD框架.md:275 敌人呈现:意图占位三档可见性——普通攻击零表示、类型级提示(站桩投影组)
|
||||
# B 区站桩子件伴随件化(壳层 tooltip 载体)——断言口径见台账 171:
|
||||
# 伴随件化(壳层 tooltip 载体),穿透明(子件 _name_label/_armor_icon/_intent_badge)改本体
|
||||
# 探针出口 stage.enemy_body_at(i).name_line_probe()/intent_probe()——呈现口径逐字保持
|
||||
@@ -163,7 +163,7 @@ func _ready() -> void:
|
||||
elif not bool(main.ui.battle.stage.hero_standee.portrait_probe().get("has_tex", false)):
|
||||
problems.append("主角立绘纹理未落位(显示档 hero=144——显示档批)")
|
||||
# ---- BUG-0001 回归(tooltip 入树后 set_effects 槽位不错位——结算软锁修复沉淀)----
|
||||
# oracle: design/gdd/systems/表现与支撑/HUD框架.md:274 状态槽图标+层数+剩余次数、悬停见来源(tooltip 回归组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/HUD框架.md:274 状态槽图标+层数+剩余次数、悬停见来源(tooltip 回归组)
|
||||
var slots_row = main.ui.battle.stage.hero_standee.slot_row # BUG-0001 回归随组件迁入站桩(tooltip/槽位错位同源)
|
||||
var probe_eff: Array = [{"effect_id": "attack_up", "display_name": "增伤", "layers": 1,
|
||||
"remaining_settlements": 1, "source_class": "safe_cell", "round_index": 1, "effective_value": 50}]
|
||||
@@ -189,7 +189,7 @@ func _ready() -> void:
|
||||
# ---- 图标键回归(HUD框架 图标键契约:状态表九行全映射〔定稿 PNG 纹理〕、
|
||||
# 格面资源 cell_type 九型全映射〔含 gp 键〕;查不到的键 fail-fast + ? 占位
|
||||
# ——此处钉死映射不残缺,未知键断言由 _icon_tex/_badge_glyph 承担)----
|
||||
# oracle: design/gdd/systems/表现与支撑/HUD框架.md:274 极性按键级表呈现——状态图标键映射不残缺(图标键回归组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/HUD框架.md:274 极性按键级表呈现——状态图标键映射不残缺(图标键回归组)
|
||||
for eid in ["attack_up", "ac_up", "alert", "weaken", "enrage", "off_guard", "sapped", "empower", "fortify"]:
|
||||
if not StatusSlotRow.EFFECT_ICON.has(eid) or StatusSlotRow.EFFECT_ICON[eid] == null:
|
||||
problems.append("状态图标九键纹理映射缺失(%s)" % eid)
|
||||
@@ -214,7 +214,7 @@ func _ready() -> void:
|
||||
# ---- L2 单场段(debug_session 单场原语 + 去饱和接口可用性;雾格机制已整体移除,不再断言变体呈现)----
|
||||
# 单场无用户入口——探针直调 debug_session 原语(battle_created/ready 仍经组合根接线;
|
||||
# _reset_run_scope 复现 _start_single 的提示去重复位)
|
||||
# oracle: design/gdd/systems/排雷循环/地形变异.md:156 布置域:变异格全在初始展开区外(L2 布置域组)
|
||||
# 判据出处: design/gdd/systems/排雷循环/地形变异.md:156 布置域:变异格全在初始展开区外(L2 布置域组)
|
||||
main.begin_session(false)
|
||||
main._debug.start_single(2)
|
||||
await get_tree().process_frame
|
||||
@@ -237,7 +237,7 @@ func _ready() -> void:
|
||||
# ---- GP 终局行:summary gp_final 键取数+胜利面板 GP 行呈现(C5:GP 行只上冒险终局面板;
|
||||
# 断言面 = summary 键,无影子账清零转存)----
|
||||
# (置于战斗态断言之后——合成收束会灰化站桩并清 settlement 引用,具污染性)
|
||||
# oracle: design/ux/settlement-summary.md:20 战利品行=GP 增量+总量(冒险终局面板 GP 行组)
|
||||
# 判据出处: design/ux/settlement-summary.md:20 战利品行=GP 增量+总量(冒险终局面板 GP 行组)
|
||||
var meta_saved = main.meta.save
|
||||
for p2 in ["user://smoke_ui_probe_settle_scratch.json", "user://smoke_ui_probe_settle_scratch.json.tmp"]:
|
||||
if FileAccess.file_exists(p2):
|
||||
|
||||
@@ -89,7 +89,7 @@ func _ready() -> void:
|
||||
# ---- 生涯页:入口=帐篷旁笔记本(卡④ T3 改挂——场景点选信号真实路由→组合根 _open_career)→
|
||||
# 结构断言族→只读巡检(契约 3)+零动效契约+零跳转契约(提纲 P4 甲)→组合根真实路径关闭。
|
||||
#(登记序/树序契约统一在全模态 _modal_registry_contract——boot 段跑)----
|
||||
# oracle: design/ux/camp.md:19 帐篷旁笔记本→生涯记录;生涯页返回钮口径由结构校验兜底(生涯段)
|
||||
# 判据出处: design/ux/camp.md:19 帐篷旁笔记本→生涯记录;生涯页返回钮口径由结构校验兜底(生涯段)
|
||||
# 营地语境:开始层全程隐藏(笔记本入口在营地场景根——关生涯回营地不回开始层,卡④ T3 语境分支)
|
||||
main._camp_pres.open_camp() # 重开营地场景根(笔记本入口语境)
|
||||
_settle()
|
||||
@@ -106,7 +106,7 @@ func _ready() -> void:
|
||||
if main.ui.modals.menu.career_overlay.visible or main.ui.screens.start_overlay.visible:
|
||||
problems.append("[career] 关闭后页未隐藏或开始层回弹(营地语境分支失守)〔营地图批 V5〕")
|
||||
# ---- Esc 浮窗结构段(卡⑤——三钮在位且仅三+零动效契约+登记序在册+debug_badge 不波及)----
|
||||
# oracle: design/ux/camp.md:45 ESC 优先序:有窗先关最上层窗、无窗才进浮窗;各窗自带关闭钮(Esc 段)
|
||||
# 判据出处: design/ux/camp.md:45 ESC 优先序:有窗先关最上层窗、无窗才进浮窗;各窗自带关闭钮(Esc 段)
|
||||
main._camp_pres.open_camp()
|
||||
_settle()
|
||||
var gpos_gate: Vector2 = main.ui.modals.camp.scene.get_hotspot_rect(&"gate").get_center()
|
||||
@@ -213,7 +213,7 @@ func _ready() -> void:
|
||||
_settle()
|
||||
|
||||
# ---- 冒险簇(真流推进:开冒险→地图选路态→选战斗节点→战斗 HUD)----
|
||||
# oracle: design/ux/level-map.md:3 图上点击选路·候选按钮退役(冒险簇选路真点组)
|
||||
# 判据出处: design/ux/level-map.md:3 图上点击选路·候选按钮退役(冒险簇选路真点组)
|
||||
ProbeUiUtil.isolate_save(main, "smoke_ui_struct_probe_adv", true, false, true)
|
||||
main.ui.modals.menu.debug_overlay.seed_edit.text = "42" # 种子钉死(种子框默认为空=非首局随机,结构采样确定性禁依赖默认分支)
|
||||
main._start_adventure_from_menu()
|
||||
@@ -243,7 +243,7 @@ func _ready() -> void:
|
||||
# ---- 图例层(旧模态、首过结构族):ap_bar legend_toggled
|
||||
# 信号真路径开(board_zone 信号→组合根 _on_legend_toggled→_open_modal)→结构断言族→
|
||||
# closed 信号真路径关(close()→组合根 _on_modal_closed)。
|
||||
# oracle: design/gdd/systems/表现与支撑/HUD框架.md:88 图例面板=模态族:统一登记序栈、入栈出栈、关闭逐层恢复(图例组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/HUD框架.md:88 图例面板=模态族:统一登记序栈、入栈出栈、关闭逐层恢复(图例组)
|
||||
main.ui.battle.board_zone.ap_bar._btn_legend.pressed.emit() # 前提注入(保留 emit):emit 选路路径下 battle_root 未及显示且输入锁在(实证在案),真点不可达——图例开启的真输入承载随「route 信号路径屏幕切换时序」发现一并呈报待裁
|
||||
_settle()
|
||||
if not main.ui.modals.run.legend_overlay.visible:
|
||||
@@ -406,7 +406,7 @@ const _PAUSE_READONLY_SIGNALS: Array[String] = [
|
||||
|
||||
|
||||
func _status_readonly_contract(main: Node) -> void:
|
||||
# oracle: design/engineering/code-conventions.md:21 视图零核心写调用、依赖单向(status 只读巡检组)
|
||||
# 判据出处: design/engineering/code-conventions.md:21 视图零核心写调用、依赖单向(status 只读巡检组)
|
||||
var overlay: Control = main.ui.modals.run.status_panel
|
||||
var stack: Array = [overlay]
|
||||
var conn_count := 0
|
||||
@@ -453,7 +453,7 @@ func _status_zero_motion_contract(main: Node) -> void:
|
||||
# 清单外零连接。白名单={视图自身 _emit_closed〔返回钮〕, 组合根 _on_career_closed};页内任何
|
||||
# 购买/导航/写入方向的新连接一律红,须先入册审查。
|
||||
func _career_readonly_contract(main: Node) -> void:
|
||||
# oracle: design/engineering/code-conventions.md:21 视图零核心写调用、依赖单向(career 只读巡检组)
|
||||
# 判据出处: design/engineering/code-conventions.md:21 视图零核心写调用、依赖单向(career 只读巡检组)
|
||||
var overlay: Control = main.ui.modals.menu.career_overlay
|
||||
var stack: Array = [overlay]
|
||||
var conn_count := 0
|
||||
@@ -500,7 +500,7 @@ func _career_zero_motion_contract(main: Node) -> void:
|
||||
# _career_registry_contract 的断言(career=状态面板后/确认弹窗前、树序同步)由本全量合同覆盖)
|
||||
# ⑭选档界面契约(卡③ sl1/sl2/sl3/sl4/sl9——呈现面全部来自喂入数据,视图零自判)
|
||||
func _save_select_contract(main: Node, tpl: String) -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/存档序列化.md:61 三存档位独立文件、激活态由选档界面建立(选档契约组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/存档序列化.md:61 三存档位独立文件、激活态由选档界面建立(选档契约组)
|
||||
var sso: Node = main.ui.modals.menu.save_select_overlay
|
||||
# sl1 三槽呈现:槽卡计数==3+零滚动容器(禁滚动长列表形态——契约 1)
|
||||
var rows: VBoxContainer = sso._rows_box
|
||||
@@ -580,7 +580,7 @@ func _collect_labels(node: Node, out: Array[String]) -> void:
|
||||
|
||||
|
||||
func _modal_registry_contract(main: Node) -> void:
|
||||
# oracle: design/engineering/code-conventions.md:54 模态覆盖层层级规则——新增覆盖层必须登记 z 序位置(登记序契约组)
|
||||
# 判据出处: design/engineering/code-conventions.md:54 模态覆盖层层级规则——新增覆盖层必须登记 z 序位置(登记序契约组)
|
||||
var list: Array = main.ui.flow.stack._list
|
||||
# ① 登记册序==合同模态序(逐位点名——合同是登记序唯一事实源)
|
||||
var contract_modals: Array = []
|
||||
@@ -648,7 +648,7 @@ func _modal_registry_contract(main: Node) -> void:
|
||||
|
||||
# ⑬career 零跳转契约(提纲 P4 甲——页内零「前往他处」入口:除「返回」外任何导航钮=红)
|
||||
func _career_nojump_contract(main: Node) -> void:
|
||||
# oracle: design/ux/camp.md:19 生涯记录入口与返回钮口径由结构校验兜底——页内零导航入口(零跳转契约组)
|
||||
# 判据出处: design/ux/camp.md:19 生涯记录入口与返回钮口径由结构校验兜底——页内零导航入口(零跳转契约组)
|
||||
for nav in ["营地", "商店", "设置", "冒险", "继续上局", "退出", "前往"]:
|
||||
if ProbeUiUtil.find_button(main.ui.modals.menu.career_overlay, nav, "contains") != null:
|
||||
problems.append("[career][nojump] 页内出现导航入口「%s」(零跳转违约——生涯纯只读)" % nav)
|
||||
@@ -658,7 +658,7 @@ func _career_nojump_contract(main: Node) -> void:
|
||||
# (名行基线离盒底 1px 同型高危写法返修回归闸;
|
||||
# 内容矩形=StandeeBody.content_rect(),最低绘制元素=底座下缘)
|
||||
func _standeefit_contract(main: Node) -> void:
|
||||
# oracle: design/ux/battle.md:154 主角站桩立绘(无框立绘+落影)——站桩内容矩形⊆舞台(站桩适配组)
|
||||
# 判据出处: design/ux/battle.md:154 主角站桩立绘(无框立绘+落影)——站桩内容矩形⊆舞台(站桩适配组)
|
||||
var st: Node = main.ui.battle.stage
|
||||
if st == null or st.hero_standee == null:
|
||||
problems.append("[standee] 舞台/主角站桩缺失(断言中断)")
|
||||
@@ -743,7 +743,7 @@ func _in_scroll(c: Control) -> bool:
|
||||
|
||||
|
||||
func _audit(screen: String, roots: Array) -> void:
|
||||
# oracle: design/engineering/architecture/architecture.md:168 组合根交接面与呈现装配——屏结构审计宿主面(结构审计组)
|
||||
# 判据出处: design/engineering/architecture/architecture.md「模块归属·呈现层」D1 行(组合根交接面) 组合根交接面与呈现装配——屏结构审计宿主面(结构审计组)
|
||||
for r in roots:
|
||||
if r != null and is_instance_valid(r):
|
||||
_force_layout(r)
|
||||
|
||||
@@ -43,7 +43,7 @@ func _ready() -> void:
|
||||
# ① 全局浮层角标开窗(调试入口=UI 最顶层浮层,任何 UI 不遮挡——
|
||||
# main 直接子层、LAYER_CONTRACT 末位 float 类;boot 菜单优先不开浮窗,角标真实点击路径开窗;
|
||||
# 悬浮于菜单之上——开始层不隐藏;树序新基准=登记册末位(确认弹窗之后=绘制序最上))
|
||||
# oracle: design/engineering/test-conventions.md:49 调试面=DebugOverlay/DS_DEBUG——发布态恒隐无 UI 开启口、开发期临时启用(①开窗层级组)
|
||||
# 判据出处: design/engineering/test-conventions.md:49 调试面=DebugOverlay/DS_DEBUG——发布态恒隐无 UI 开启口、开发期临时启用(①开窗层级组)
|
||||
main._show_debug_overlay() # 发布态角标恒隐(main.gd:138 _debug_entry 缺省 false)——角标信号路径已退役(:123 同款先例),开窗经组合根直调承载
|
||||
await get_tree().process_frame
|
||||
if not main.ui.modals.menu.debug_overlay.visible:
|
||||
@@ -57,7 +57,7 @@ func _ready() -> void:
|
||||
if str(boosted_atk) not in main.ui.modals.menu.debug_overlay.effect_text() or str(boosted_hp) not in main.ui.modals.menu.debug_overlay.effect_text():
|
||||
problems.append("生效预览未联动(%s)" % main.ui.modals.menu.debug_overlay.effect_text())
|
||||
# ③ 应用(真实信号路径:apply_requested → 组合根)——E4 后加成零表写,生效断言移至 ⑧ 战斗快照
|
||||
# oracle: design/engineering/test-conventions.md:49 调试面加成链路:滑条联动/应用/重置、hero 表运行期只读(②③④⑦组)
|
||||
# 判据出处: design/engineering/test-conventions.md:49 调试面加成链路:滑条联动/应用/重置、hero 表运行期只读(②③④⑦组)
|
||||
var apply_btn: Button = ProbeUiUtil.find_button(main.ui.modals.menu.debug_overlay, "应用并关闭")
|
||||
if apply_btn == null:
|
||||
problems.append("调试浮窗「应用并关闭」钮缺失")
|
||||
@@ -72,7 +72,7 @@ func _ready() -> void:
|
||||
ProbeUiUtil.push_click(main, reset_btn)
|
||||
# ⑤ 批量模拟全链路(N=10——走 level_autoplay 与 headless 扫描同口径;表基线由组合根复位;
|
||||
# 菜单态上下文未锁定=按钮可点,is_running 语义=模拟在跑〔18卡⑦ 与对局禁用态拆分〕)
|
||||
# oracle: design/engineering/test-conventions.md:49 调试面批量模拟链路:N=10 输出关卡完成率/剩余 HP/败北节点(⑤组)
|
||||
# 判据出处: design/engineering/test-conventions.md:49 调试面批量模拟链路:N=10 输出关卡完成率/剩余 HP/败北节点(⑤组)
|
||||
main.ui.modals.menu.debug_overlay.set_sliders(100, 0)
|
||||
main.ui.modals.menu.debug_overlay.set_sim_count(10)
|
||||
# 折叠区控件真输入通道(test-conventions §2——先滚轮滚入可视区再点):sim 钮在滚动列表 boot
|
||||
@@ -155,7 +155,7 @@ func _ready() -> void:
|
||||
# ⑨ 对局中可开(18卡⑦ P2 甲/T2 防护——S1+ 停机点回归闸):战斗屏角标真实信号路径开窗;
|
||||
# 棋盘输入不锁、状态角标不置灰(「默认开×遮挡复活」下必锁死的破约回归面);
|
||||
# 对局中批量模拟/两层调试两钮禁用;当局数据六项与只读访问器逐项一致;关闭钮真实路径收窗恢复
|
||||
# oracle: design/engineering/test-conventions.md:49 调试面对局态:不锁棋盘输入/两钮禁用/当局数据只读一致(⑨组)
|
||||
# 判据出处: design/engineering/test-conventions.md:49 调试面对局态:不锁棋盘输入/两钮禁用/当局数据只读一致(⑨组)
|
||||
var dbg_badge: Button = main.ui.screens.debug_badge
|
||||
if dbg_badge == null or dbg_badge.visible:
|
||||
problems.append("对局中调试浮层角标非恒隐(发布态语义失守——卡⑦ 删写死行批)")
|
||||
@@ -199,7 +199,7 @@ func _ready() -> void:
|
||||
_overlay_zero_motion_contract(main, problems)
|
||||
# ⑩ 种子三分单点断言(18卡⑦ C1——判定只落 _seed_value() 单点;对局态不影响本判定面):
|
||||
# 显式合法整数全场景优先(含首局)→ 首局(存档布尔 false)恒 42 → 非首局空框引擎熵连抽不全等 42
|
||||
# oracle: design/engineering/architecture/architecture.md:168 seed_value=组合根公开服务面(⑩种子三分判定单点组)
|
||||
# 判据出处: design/engineering/architecture/architecture.md「模块归属·呈现层」D1 行(组合根交接面) seed_value=组合根公开服务面(⑩种子三分判定单点组)
|
||||
main.ui.modals.menu.debug_overlay.seed_edit.text = "777"
|
||||
if main.seed_value() != 777:
|
||||
problems.append("显式种子输入未优先(_seed_value != 777——C1 ①失守)")
|
||||
@@ -240,7 +240,7 @@ func _ready() -> void:
|
||||
# 全连接巡检——白名单={视图自身八方法 + 注册面模态编排口 ui.flow.close(closed 外发消费——
|
||||
# 注册面批把该连接目标从组合根转发壳改为 flow 真方法,机检白名单随批登记同一连接)};
|
||||
# 当局数据区块=纯标签零连接在此被一并约束(新增任何连接=红须入册审查)。
|
||||
# oracle: design/engineering/code-conventions.md:21 视图零核心写调用、依赖单向(白名单外零连接巡检组)
|
||||
# 判据出处: design/engineering/code-conventions.md:21 视图零核心写调用、依赖单向(白名单外零连接巡检组)
|
||||
func _overlay_readonly_contract(main: Node, problems: Array) -> void:
|
||||
var overlay: Control = main.ui.modals.menu.debug_overlay
|
||||
var stack: Array = [overlay]
|
||||
|
||||
@@ -78,7 +78,7 @@ static func _deduce_single(minesweeper) -> Dictionary:
|
||||
# 〔轮末 weaken + 魔法 首次充能〕且雷上封印不阻连锁)· 卡死且刷新资源可用 → 轮内刷新
|
||||
# (经 settlement.player_request_refresh 核心编排)· 否则停手(残 AP 1:1 转存 RAP——下轮刷新资源)。
|
||||
# 推理规则集 = 棋盘生成 F1 measure_inferability 同族(single + subset——solver_rules=single_subset 冻结域);
|
||||
# 口径差异 = unknown 集不含 棋盘生成 生成期 safe_set oracle(真实玩家约束的未知邻格含危险格)——比生成期闸
|
||||
# 口径差异 = unknown 集不含 棋盘生成 生成期 safe_set 真值(真实玩家约束的未知邻格含危险格)——比生成期闸
|
||||
# 保守,卡死率略高属口径诚实。输入只经 排雷玩法 查询面(reveal_record/is_revealed/has_seal/current_board
|
||||
# 的邻居与可达面),禁读棋盘雷区真值(测推理上界,非全知作弊)。
|
||||
# 待拍板(未实现):非卡死主动刷新、囤刀倾泻 vs 即用即耗、完美板目标加权。
|
||||
@@ -148,8 +148,8 @@ static func _deduce(minesweeper) -> Dictionary:
|
||||
changed = true
|
||||
# 0 格连锁算子(架构整改批补齐——与棋盘生成 _apply_chain_rule 对位):safe 集中未揭示格
|
||||
# 挖开必触发 0 格连锁(连锁只经安全格扩散=闭包集全安全,真实玩家挖 c 免费获得的信息面),
|
||||
# 经 preview_reveal 真实闭包取新开格集并入 safe——双口径显式登记:unknown 不含 safe_set oracle
|
||||
# 〔口径差异=board_generator 版含 oracle,本版 no-peek——:80-83 注释在账〕
|
||||
# 经 preview_reveal 真实闭包取新开格集并入 safe——双口径显式登记:unknown 不含 safe_set 真值
|
||||
# 〔口径差异=board_generator 版含真值,本版 no-peek——:80-83 注释在账〕
|
||||
for c in (safe.keys() as Array).duplicate():
|
||||
if board.is_safe(c) and not minesweeper.is_revealed(c):
|
||||
for rc in minesweeper.preview_reveal(c):
|
||||
|
||||
@@ -114,10 +114,12 @@ tests/
|
||||
| 同参数同种子逐位复现、异种子不退化 | 棋盘生成 | `BoardGenerator.generate()` | `board_generator_test.gd:_test_same_seed_bit_identical`;`board_gen_reward_test.gd:_smoke_board_generator` |
|
||||
| 可推理率与清图可达(合法性先于质量分)、重试有界、降级诚实 | 棋盘生成 | `BoardGenerator.measure_inferability()`/`BoardGenerator.is_clearable()` | `board_generator_test.gd:_test_clearable_and_inferability_selfconsistent`;`board_generator_test.gd:_test_target_mine_count_single_source`;`board_gen_reward_test.gd:_smoke_board_generator` |
|
||||
| 装板与开局免费展开(封锁格无资源、计入清图) | 排雷玩法 | `MinesweeperRules.install_board()` | `minesweeper_rules_test.gd:_test_install_invariants`;`seal_polarity_test.gd:_smoke_resource_surface` |
|
||||
| 挖/封印合法性与可辨拒绝(拒绝零副作用) | 排雷玩法 | `MinesweeperRules.settle_batch()` | `minesweeper_rules_test.gd:_test_dig_face_rules`;`minesweeper_rules_test.gd:_test_seal_place`;`minesweeper_rules_test.gd:_test_reject_out_of_catalog` |
|
||||
| 挖/封印合法性与可辨拒绝(拒绝零副作用、挖封印格被拒) | 排雷玩法 | `MinesweeperRules.settle_batch()` | `minesweeper_rules_test.gd:_test_dig_face_rules`;`minesweeper_rules_test.gd:_test_seal_place`;`minesweeper_rules_test.gd:_test_reject_out_of_catalog`;`minesweeper_rules_test.gd:_test_dig_sealed_rejected`;`minesweeper_rules_test.gd:_test_classify_seal_clears`(计划——需求卡片「封印动作改版批」2026-11-07 锚定) |
|
||||
| 棋盘揭示预览只读口(零状态写入/与实挖揭示集同口径) | 排雷玩法 | `MinesweeperRules.preview_reveal()` | `minesweeper_rules_test.gd:_test_preview_reveal_readonly` |
|
||||
| 封印不可逆与挖开封印格放行(兑付改判四情况) | 排雷玩法 | `MinesweeperRules.settle_batch()` | `seal_polarity_test.gd:_smoke_seal_irreversible_quadrants`;`minesweeper_rules_test.gd:_test_seal_place` |
|
||||
| 棋盘封印印记呈现 | 排雷玩法 | `BoardCellUnit.edge` → `SealMarkLayer.setup()` | `seal_mark_params_test.gd:_test_param_values`;`seal_mark_params_test.gd:_test_fog_gate` |
|
||||
| 封印受理即揭示(身份可查、受理零事件) | 排雷玩法 | `MinesweeperRules.settle_batch()` | `minesweeper_rules_test.gd:_test_seal_reveal_identity`(计划——需求卡片「封印动作改版批」2026-11-07 锚定) |
|
||||
| 清图口径换代(挖开∪封印、封印可完成清图) | 排雷玩法 | `MinesweeperRules.settle_batch()` | `minesweeper_rules_test.gd:_test_clear_by_seal`(计划——需求卡片「封印动作改版批」2026-11-07 锚定) |
|
||||
| 棋盘封印印记呈现(已揭示格承载+已封印格零动作) | 排雷玩法 | `BoardCellUnit.edge` → `SealMarkLayer.setup()`;`ui_input_probe` 真输入动线 | `seal_mark_params_test.gd:_test_param_values`;`seal_mark_params_test.gd:_test_fog_gate`;`seal_mark_params_test.gd:_test_seal_on_revealed_visible`(计划——需求卡片「封印动作改版批」2026-11-07 锚定) |
|
||||
| 棋盘格面绘制显示(渲染闸棋盘三图 golden 逐像素——变异标记/奖励徽章/移除退色) | 排雷玩法 | `board_shot_probe.tscn` 窗口化真渲染(`BoardView.rebuild()` 固定种子单关直构)→`golden_compare.py` 逐像素 | `board_shot_probe.gd:_ready`(棋盘三图采集——渲染闸基线面)(计划——需求卡片「QA覆盖余量收口批」2026-11-07 锚定) |
|
||||
| AP 扣费、末点耗尽即收轮与战斗收束清零 | 行动经济 | `MinesweeperRules.settle_batch()`(→`ActionEconomy.try_spend()`/`ActionEconomy.notify_combat_end()`) | `seal_polarity_test.gd:_smoke_seal_ap_exhausted`;`round_flow_skill_pool_test.gd:_smoke_full_battle`;`round_flow_skill_pool_test.gd:_smoke_active_refresh` |
|
||||
| 停手转存储备与轮内刷新(上限/AP 不重置/刷新图零清图奖励) | 行动经济 | `BattleOrchestrator.player_request_refresh()`/`MinesweeperRules.settle_batch()` | `round_flow_skill_pool_test.gd:_smoke_active_refresh`;`seal_polarity_test.gd:_smoke_refresh_provenance_guard`;`refresh_feats_test.gd:test_no_feats_baseline` |
|
||||
| 换图系刷新链路与效果词表接线 | 行动经济×战斗结算×奇物 | `MinesweeperRules.settle_batch()`/`BattleOrchestrator.player_request_refresh()` | `effect_words_t1_test.gd:_init`;`forge_table_test.gd:_test_effect_kinds_closed`;`refresh_feats_test.gd:test_board_refresh_fact_only_on_success` |
|
||||
@@ -132,7 +134,8 @@ tests/
|
||||
| 能力 | 系统 | 真实入口 | 代表用例(文件:函数) |
|
||||
|---|---|---|---|
|
||||
| 拒绝反馈分面可辨与未知兜底文案 | HUD框架 | `HudPresenter.outcome_text()` | `hud_presenter_test.gd:_test_reject_text_by_reason`;`hud_presenter_test.gd:_test_unknown_reason_fallback`;`hud_presenter_test.gd:_test_accepted_text` |
|
||||
| 结算明细带逐行呈现与排播节奏 | HUD框架 | `BattleLogView.append_row()`/`RowPacer.pace_delay_for()` | `theme_projection_test.gd:_smoke_battle_log_view`;`player_copy_sweep_test.gd:_sweep_source_names`;`pacing_queue_test.gd:_init` |
|
||||
| 结算期零逐行文字与 FX 排播链保持 | HUD框架 | `RowPacer._consume_row()`/`BattleStageView.on_step_row_fx()`(真实停手→轮末结算→B 区场景树与 FX 调用面) | `battle_log_zero_text_test.gd:_smoke_settle_zero_rows`;`pacing_queue_test.gd:_assert_fx_chain`;`standee_fx_sweep_test.gd:_init` |
|
||||
| 日志 step 行零文案与结构化判读等价 | 表现与支撑 | `PlaytestLogger._on_step_row()`→`PlaytestLogger._append()`(真实结算→落盘 JSONL 读回) | `playtest_logger_step_fields_test.gd:_assert_no_text_and_full_payload`;`reward_ineffective_reason_test.gd:_assert_four_reasons` |
|
||||
| 模态覆盖层编排与输入锁 | HUD框架 | `ModalFlow.locks_input()`/`ModalFlow.open_after_playback()` | `modal_flow_test.gd:_init`;`reward_overlay_test.gd:_run` |
|
||||
| 模态句柄分域寻址面 | HUD框架 | 局内/菜单/营地三子容器句柄经注册面合同寻址(`UiRegistry.node()` 逐键取层)+模态编排口开合(`ModalFlow.open()`/`ModalFlow.close()`) | `ui_struct_probe.gd:_modal_registry_contract`(registry 合同序段+分域句柄段) |
|
||||
| 未开格类型微光(8 邻作用集、五组色相、零玩法写入) | HUD框架 | `BoardView.type_glow_probe()`/`UiKit.glow_group_for_cell_type()` | `theme_projection_test.gd:_smoke_type_glow`;`type_glow_test.gd:_test_grouping_closed`;`type_glow_test.gd:_test_frozen_hue_face` |
|
||||
@@ -142,10 +145,10 @@ tests/
|
||||
| 地形瓦片套与格面数字可读 | HUD框架 | `TilePalette.digit_ink_mode()`/`TilePalette.normalize_floor_luma()` | `terrain_tile_sets_test.gd:_test_set_completeness`;`terrain_tile_sets_test.gd:_test_hue_domains`;`terrain_tile_sets_test.gd:_test_digit_ink_modes`(旧行保留,删行归写卡段后续,实现批禁写) |
|
||||
| 棋盘合并岩体与连续地面(深度遮挡、投影、崖面) | 美术圣经 | `BoardTerrainLayer.setup()`/`BoardTerrainLayer.terrain_metrics()` | `board_terrain_test.gd:_test_merged_mass_geometry`;`board_terrain_test.gd:_test_projection_ratios`;`board_terrain_test.gd:_test_depth_order` |
|
||||
| 棋盘浮空数字与引导线(最顶层、顶部预留) | 美术圣经 | `BoardFloatLayer.set_entries()`/`BoardFloatLayer.label_metrics()`/`BoardLabelShadowLayer.setup()` | `board_float_label_test.gd:_test_label_geometry`;`board_float_label_test.gd:_test_topmost_layer`;`board_float_label_test.gd:_test_contrast` |
|
||||
| 棋盘顶面锚点与迷雾几何(封印、微光、变异的岩顶偏移) | 美术圣经 | `BoardView.refresh()`/`BoardView._visual_rect_for()`/`CellUnit.fog_state` | `board_terrain_test.gd:_test_top_face_anchor`;`board_terrain_test.gd:_test_fog_rect` |
|
||||
| 棋盘顶面锚点与迷雾几何(微光、变异岩顶偏移,封印格心) | 美术圣经 | `BoardView.refresh()`/`BoardView._visual_rect_for()`/`CellUnit.fog_state` | `board_terrain_test.gd:_test_top_face_anchor`;`board_terrain_test.gd:_test_fog_rect` |
|
||||
| D 区行动资源侧栏(AP 左栏、RAP 右栏、全高棋盘) | HUD框架 | `ApBarView.set_ap()`/`ApBarView.set_rap()`/`BoardZoneView._on_board_area_resized()` | `board_side_rail_test.gd:_test_side_rail_layout`;`board_side_rail_test.gd:_test_signals`;`stage_background_test.gd:_test_side_rail_width_from_table` |
|
||||
| 棋盘点击命中与格边公式(逻辑格矩形) | HUD框架 | `BoardView.set_cell_area()`/`BoardView.cell_center_local()` | `board_terrain_test.gd:_test_cell_formula_base`;`board_terrain_test.gd:_test_hit_test_logical_rect`;`layout_probe.gd:_ready` |
|
||||
| 旧瓦片退役与棋盘零底牌 | 美术圣经 | `BoardView.rebuild()`/`BoardView.refresh()`(资产装载与绘制路径) | `board_terrain_test.gd:_test_legacy_assets_absent`;`board_float_label_test.gd:_test_no_plate_path` |
|
||||
| 旧瓦片退役与普通数字零底牌 | 美术圣经 | `BoardView.rebuild()`/`BoardView.refresh()`(资产装载与绘制路径) | `board_terrain_test.gd:_test_legacy_assets_absent`;`board_float_label_test.gd:_test_no_ordinary_plate_path` |
|
||||
| 站桩敌人呈现与结算演出落点 | 战斗表现 | `BattleStageView.enemy_standee_center()`/`BattleEndOverlay.open_victory()` | `standee_fx_sweep_test.gd:_init`;`pacing_queue_test.gd:_init` |
|
||||
| 站桩呈现定值与素材绑定 | 战斗表现 | `UiKit.load_tex()`/`StandeeBody.name_line_probe()` | `standee_present_values_test.gd:_init` |
|
||||
| 站桩显示档与素材分辨率绑定 | 战斗表现 | `StandeeBody.portrait_probe()`/`StandeeMotion.PORTRAIT_DISPLAY_H` | `standee_present_values_test.gd:_check_display_tier` |
|
||||
@@ -178,7 +181,7 @@ tests/
|
||||
| 终局结算框关闭动线零引擎报错 | 战斗表现 | fx_probe/screen_flow_probe 真实败走关闭动线(`BattleEndOverlay.open_defeat()` → `BattleEndOverlay.close()`)孤立直跑,日志零报错行读数 | `fx_probe.gd:_ready`(计划测试——需求卡片「QA harness 缺陷与覆盖遗漏收口批」2026-11-06 锚定) |
|
||||
| 停手钮真输入命中与停手收轮落效 | 行动经济 | ui_click_probe 使能窗真输入点击(AP 条停手钮经 `ProbeUiUtil.push_click()`)+journey_player 全线跑批(sim) | `ui_click_probe.gd:_stoptest_tick`(计划测试——需求卡片「QA harness 缺陷与覆盖遗漏收口批」2026-11-06 锚定) |
|
||||
| 停点链与控件级真输入命中 | HUD框架 | 探针与测试宿主真输入驱动(节点继续/三选一选卡·确认/弃图刷新确认/奖励刷新/地图候选矩形中心/调试浮窗钮,一律经 `ProbeUiUtil.push_click()`/`push_click_at()` 进 `push_input`) | `ui_click_probe.gd:_ready`(停点链段——计划测试,需求卡片「QA harness 缺陷与覆盖遗漏收口批」2026-11-06 锚定) |
|
||||
| 控件动线扩面真输入可达(图例关闭/节点继续/三选一与商店/秘宝架/树高档与先手/弃图链/地图放弃/进入下一层/Esc 设置/减动效开关)+cell_mark 模态断言 | HUD框架×各屏 | 合成 InputEvent 经 `Viewport.push_input()` 真点(`ProbeUiUtil.push_click_at()`;扩面十二段,高档钮经调试注钥使能) | `ui_click_probe.gd:_ready`(动线扩面段);`ui_struct_probe.gd:_ready`(cell_mark 段)(计划——需求卡片「QA覆盖余量收口批」2026-11-07 锚定) |
|
||||
| 控件动线扩面真输入可达(图例关闭/节点继续/三选一与商店/秘宝架/树高档与先手/弃图链/地图放弃/Esc 设置/减动效开关) | HUD框架×各屏 | 合成 InputEvent 经 `Viewport.push_input()` 真点(`ProbeUiUtil.push_click_at()`;扩面十一段,高档钮经调试注钥使能) | `ui_click_probe.gd:_ready`(动线扩面段)(计划——需求卡片「QA覆盖余量收口批」2026-11-07 锚定) |
|
||||
|
||||
**旅程段七:数据配置与跨系统契约**
|
||||
|
||||
@@ -218,10 +221,10 @@ tests/
|
||||
| 战斗循环 | 战斗表现.md | 站桩敌人呈现与结算演出落点;站桩呈现定值与素材绑定;站桩显示档与素材分辨率绑定 | 已覆盖 | 死亡灰化与减动效演出档以探针+人工目检为终态(2026-11-02 用户裁除名,不再补自动断言) |
|
||||
| 战斗循环 | 敌人系统.md | 技能池数据面与回放独立;Boss 半血解锁技门控;敌人技能池跨表链接校验;精英行:池继承合并、加载期校验与暂不接线 | 部分覆盖 | 精英行「开战快照拿到合并后池」随接线批验收;见待办清单「战斗节点遭遇编排方式」条 |
|
||||
| 排雷循环 | 地形变异.md | 变异布置门控;表结构守卫:域校验/行序键/跨字段互锁/配额锚 | 已覆盖 | — |
|
||||
| 排雷循环 | 排雷玩法.md | 装板与开局免费展开(封锁格无资源、计入清图);挖/封印合法性与可辨拒绝(拒绝零副作用);封印不可逆与挖开封印格放行(兑付改判四情况) | 已覆盖 | — |
|
||||
| 排雷循环 | 排雷玩法.md | 装板与开局免费展开(封锁格无资源、计入清图);挖/封印合法性与可辨拒绝(拒绝零副作用、挖封印格被拒);封印受理即揭示(身份可查、受理零事件);清图口径换代(挖开∪封印、封印可完成清图) | 已覆盖 | — |
|
||||
| 排雷循环 | 棋盘生成.md | 棋盘按参数行出图、数字恒真;同参数同种子逐位复现、异种子不退化;可推理率与清图可达(合法性先于质量分)、重试有界、降级诚实 | 已覆盖 | — |
|
||||
| 排雷循环 | 行动经济.md | AP 扣费、末点耗尽即收轮与战斗收束清零;停手转存储备与轮内刷新(上限/AP 不重置/刷新图零清图奖励);下轮行动点修正排队与兑现 | 已覆盖 | — |
|
||||
| 表现与支撑 | HUD框架.md | 拒绝反馈分面可辨与未知兜底文案;结算明细带逐行呈现与排播节奏;模态覆盖层编排与输入锁;未开格类型微光(8 邻作用集、五组色相、零玩法写入);UI 全局主题与按钮五态(含对比度锚);中文字体随包与字形覆盖;棋盘合并岩体与连续地面(深度遮挡、投影、崖面);棋盘浮空数字与引导线(最顶层、顶部预留);D 区行动资源侧栏(AP 左栏、RAP 右栏、全高棋盘);棋盘点击命中与格边公式(逻辑格矩形);旧瓦片退役与棋盘零底牌;棋盘顶面锚点与迷雾几何(封印、微光、变异的岩顶偏移);战斗叙述与终局文案(数值带入/胜负互异);玩家面文案与危险格格面字形;战斗屏三区几何与棋盘格边长(分辨率两档×四档板型);B 区舞台背景三关变体与连续地面层(按 board_type 选图/地面层高度读布局表/四柱光柱退役);模态句柄分域寻址面 | 已覆盖 | — |
|
||||
| 表现与支撑 | HUD框架.md | 拒绝反馈分面可辨与未知兜底文案;结算期零逐行文字与 FX 排播链保持;模态覆盖层编排与输入锁;未开格类型微光(8 邻作用集、五组色相、零玩法写入);UI 全局主题与按钮五态(含对比度锚);中文字体随包与字形覆盖;地形瓦片套与格面数字可读(旧行保留,删行归写卡段后续,实现批禁写);棋盘合并岩体与连续地面(深度遮挡、投影、崖面);棋盘浮空数字与引导线(最顶层、顶部预留);D 区行动资源侧栏(AP 左栏、RAP 右栏、全高棋盘);棋盘点击命中与格边公式(逻辑格矩形);旧瓦片退役与普通数字零底牌;棋盘顶面锚点与迷雾几何(微光、变异岩顶偏移,封印格心);战斗叙述与终局文案(数值带入/胜负互异);玩家面文案与危险格格面字形;战斗屏三区几何与棋盘格边长(分辨率两档×四档板型);B 区舞台背景三关变体与连续地面层(按 board_type 选图/地面层高度读布局表/四柱光柱退役);模态句柄分域寻址面 | 已覆盖 | — |
|
||||
| 表现与支撑 | 存档序列化.md | 存档写盘⇄读回往返保真;三槽位隔离与激活失败回滚;槽位摘要窥视与坏槽三态;启动态无激活不写盘;旧档迁移链与断点续玩零跳号;存档⇄RNG 快照往返保真 | 已覆盖 | — |
|
||||
| 表现与支撑 | 教学引导.md | — | 未覆盖 | 教学形态与首遇时机待设计;见待办清单「教学形态未定」条 |
|
||||
| 表现与支撑 | 数据配置.md | 全表装载、就绪态与表清单目录对账;进度坐标查行:精确命中/向上取整/封顶/警告去重;运行期越域致命与进程非 0 退出;加载期违规表拒绝:跨表悬空/计数器前缀/真视窄域;表结构守卫:域校验/行序键/跨字段互锁/配额锚 | 已覆盖 | — |
|
||||
@@ -337,10 +340,26 @@ godot --headless --path . res://probe/<probe>.tscn
|
||||
|
||||
## Static Scan(提交前本地跑)
|
||||
|
||||
`tests/static_scan.sh` 实现静态扫描域 S1–S12 的可执行子集;S1–S10 的规则全集与动机见
|
||||
`design/engineering/architecture/architecture.md`「测试与质量域」节,**S11/S12 两段的规则说明自持于本文件与
|
||||
`tests/static_scan.sh` 头注**,这两段不另进架构文档。
|
||||
新违规案例必须**先补扫描规则再修码**。
|
||||
`tests/static_scan.sh` 实现静态扫描域 S1–S13;**本节是全部 S 系规则的规则全集与动机**(2026-11-07 架构收窄批迁驻——原指架构文档「测试与质量域」节,该文档已收窄为约束面)。适用域分档(产品码三目录 vs 非产品码三目录)与无机器闸的理由见 `design/engineering/code-conventions.md` §4 第 13 条。
|
||||
新违规案例必须**先补扫描规则再修码**(code-conventions 缺陷-规范对照表纪律)。
|
||||
|
||||
S1–S10 扫描面止于产品码三目录(`game/core`+`game/data`+`game/presentation`);S11/S12 扫描面覆盖 game/ 全域;S13 扫描面=产品码三目录。
|
||||
|
||||
| # | 规则 | 判级与要点 |
|
||||
|---|---|---|
|
||||
| S1 | 玩法代码禁 `randomize()`、自建 `RandomNumberGenerator`(D2 Rule 5) | 硬失败;扫描面 game/core + game/presentation。非产品码三目录(`game/tests`/`game/probe`/`game/sim`)不进本闸与 S2——它们自建且**显式定种**的随机源是合法测量口径(同种子对拍改前改后读数),禁的是不定种形态 |
|
||||
| S2 | 禁全局随机函数 `randi()` / `randf()` / `randf_range()`——全局 RNG 绕开 RngService | 硬失败,与 S1 同段;实例方法 `.randi()` 排除;`rng-seed-exempt` 豁免注记在案 |
|
||||
| S3 | core/ 禁 ConfigDB 外 data 表加载(`load(` / `preload(` / `ResourceLoader.load`;schema class_name 直引豁免)(D2 Rule 2) | **warning 级**(不参与 fail 判定)——class_name 直引误报需人工复核;路径=`game/core`;豁免 `config_db.gd` 自身——它就是数值单一事实源的定义所属文件。本行即状态事实源(2026-11-03 架构审查 P1 裁维持 warning 级不升硬,升硬前提=可靠的直引误报排除判据,该判据未立),禁在别处另列 |
|
||||
| S4 | core/ 禁 `extends Control/Node2D`、禁 `.tscn` preload(D2 Rule 7) | 硬失败 |
|
||||
| S5 | core/ 禁裸引用 autoload 全局标识符(`ConfigDB` / `RngService` 直接写=--script 模式 parse error)——构造注入 only | 硬失败;形态=标识符后紧跟 `.` 的成员访问,排除注释行与字符串字面量 |
|
||||
| S6 | core↔core 信号连接只许方法 Callable(禁 lambda 捕获 self);发射者禁持有订阅者反向引用 | 硬失败(2026-10-25 落闸:内联 lambda 禁令机器闸——同行与跨行两形态、豁免注记 `s6-signal-exempt`);「发射者禁反向引用」句静态判据不成熟,归人工评审承接、不造弱判据 |
|
||||
| S7 | B1 编排唯一性:B2/B3/B4 API 符号命中文件 ⊆ B1 文件集;A2/B3/B4 零相互直调(B1 AC-43) | 硬失败;**符号清单=从 B2/B3/B4 三族文件自动提取公开方法面**(非下划线 func 名,无手写清单);B1 域文件集清单单点=本脚本 `B1_SET`(脚本即清单);豁免注记 `s7-b1-exempt`;缺位自检在位 |
|
||||
| S8 | 结算路径 `await` 零命中(B1 结算/编排文件集) | 硬失败,排除注释与字符串;文件集单点=`B1_SET`;自检两件:①清单内文件缺位即红;②**目录域反向对账**——`game/core/settlement/` 实有 `.gd` 未登记进 `B1_SET` 即红;装板/授予/效果端口/掷格四件结算码住 `game/core` 根下,纯目录域覆盖不住,根下新增由写卡段申报承接 |
|
||||
| S9 | 类型闸(`tools/audit_type_hints.py`):产品码无类型容器/有值返回缺类型/参数缺类型(R1/R2/R3 细则=code-conventions §4 第 7/8/12 条) | 硬失败;扫描面缺目录或零个 `.gd` 判红(防空转自检),归绿时打印扫描面件数供核 |
|
||||
| S10 | 文件头闸(`tools/audit_file_headers.py`):头注 `##` 载体+日期/变更叙事黑名单(细则=code-conventions §1.4.1) | 硬失败;同款防空转自检 |
|
||||
| S11 | 代码注释禁用词闸 | 规则说明见下段 |
|
||||
| S12 | 效果引擎无状态化旧件零残留闸 | 规则说明见下段 |
|
||||
| S13 | 第三方质量规则闸(`tools/gdstyle_gate.py`,钉 gdstyle v0.4.0:真缺陷类规则——重复 load、循环内 await、循环内分配、不可达代码等) | 硬失败;基线键=文件\|规则\|函数名(类/文件级=文件\|规则),数字漂移不假红,同键指标恶化超基线登记值即红;存量以 `tools/gdstyle_baseline.txt` 欠账簿登记;扫描面=产品码三目录 |
|
||||
|
||||
S11 代码注释禁用词:扫描 game/ 全域 .gd/.tres/.md/.json/.sh 的注释面;禁词全部由 `design/gdd/术语表.md`「退役词对照」第一列解析供给(脚本内不内联任何词面——单一事实源);解析结果再按「巢穴」「旗」「flag_*」三条整行排除,另设同形异义豁免「去重闸」(封印重判去重的机制判别器,与某闸档退役词后二字同形——行内含「去重闸」整行跳过);三层豁免(闸门自指/显式留档 20 处〔契约原名引述与带日期历史读数注释,清单见 `tests/static_scan.sh` 头注〕/非注释面天然不取);两道自检(扫描面文件数非空+词表解析非空)。
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
## 时效判定纪律
|
||||
|
||||
基线冻结于 2026-09-30;关卡视口两 Shot(lmap_entry/lmap_boss)冻结于 2026-11-05——两 Shot 面口径(只对拍入口停点与 Boss 节点)见上文现持清单行。**判定先于动作**:每代换代面(视觉/素材/布局改动)
|
||||
基线冻结于 2026-09-30;关卡视口两 Shot(lmap_entry/lmap_boss)冻结于 2026-11-05;棋盘三图 2026-11-07 换代(采集态扩型——cell_mark_overlay 六绘制域入帧,需求卡片「QA覆盖余量收口批」卡修轮 2 V14 渲染通道裁定,新帧用户过目后重制)——两 Shot 面口径(只对拍入口停点与 Boss 节点)见上文现持清单行。**判定先于动作**:每代换代面(视觉/素材/布局改动)
|
||||
跑批时先跑一次渲染闸看差异落不落在裁剪区与容差内——
|
||||
|
||||
- **判有效**(changed 远低于 2% 阈值):基线**留在位**,不得顺手重制;
|
||||
|
||||
|
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 106 KiB |
|
Before Width: | Height: | Size: 115 KiB After Width: | Height: | Size: 124 KiB |
|
Before Width: | Height: | Size: 109 KiB After Width: | Height: | Size: 114 KiB |
@@ -169,6 +169,7 @@ func _suites() -> Array:
|
||||
# 样板件占位断言跑绿没有信息量,且占据「27 套件」计数。文件保留作模板(同类先例见下 fog_lantern 摘除行)。
|
||||
{"path": "res://tests/unit/data_config/config_db_test.gd", "expect": 0},
|
||||
{"path": "res://tests/unit/data_config/forge_table_test.gd", "expect": 0}, # 六路线 schema 正反(V1)+特效留空(V11)+几何价格梯(V2 半边)
|
||||
{"path": "res://tests/unit/schema_vocab_violation_test.gd", "expect": 0}, # schema 词表违约词拒绝路径集中面(十七词族七检查——迭代 55 卡 T1〔QA余 V1〕;validate 诊断面直调非负面套件)
|
||||
{"path": "res://tests/unit/data_config/level_gen_params_test.gd", "expect": 0}, # 参数档 schema 正反+访问器读回(节点图生成器核心批 卡① V2〔fv2〕)
|
||||
{"path": "res://tests/unit/data_config/config_db_fatal_oob_test.gd", "expect": 1},
|
||||
{"path": "res://tests/unit/data_config/treasure_target_violation_test.gd", "expect": 1}, # 真视窄域护栏
|
||||
|
||||
@@ -16,7 +16,7 @@ var _checks: int = 0
|
||||
|
||||
|
||||
func check(cond: bool, label: String) -> void:
|
||||
# oracle: design/engineering/architecture/architecture.md:10 (测试层级四级——冒烟存活检查组)
|
||||
# 判据出处: design/engineering/test-conventions.md「测试层三元组」冒烟测试行 (测试层三元组——冒烟存活检查组)
|
||||
_checks += 1
|
||||
if cond:
|
||||
print(" [PASS] " + label)
|
||||
@@ -35,7 +35,7 @@ func _ready() -> void:
|
||||
|
||||
# 开机前置面:autoload 注册与资源可加载(挂主屏之前先查,失败时主屏断言整段跳过)
|
||||
func _check_autoloads() -> void:
|
||||
# oracle: design/engineering/architecture/architecture.md:10 (测试层级四级——冒烟存活检查组)
|
||||
# 判据出处: design/engineering/test-conventions.md「测试层三元组」冒烟测试行 (测试层三元组——冒烟存活检查组)
|
||||
for autoload_name in ["ConfigDB", "RngService", "SaveManager", "MetaProgression"]:
|
||||
var node: Node = get_node_or_null(NodePath("/root/" + autoload_name))
|
||||
check(node != null, "autoload %s 在位" % autoload_name)
|
||||
@@ -64,7 +64,7 @@ func _boot_main_scene() -> Node:
|
||||
|
||||
# 主屏存活面:组合根装配句柄在位(main.X 一级句柄契约,探针挂载假设的启动侧证明)
|
||||
func _check_main_scene(main: Node) -> void:
|
||||
# oracle: design/engineering/architecture/architecture.md:10 (测试层级四级——冒烟存活检查组)
|
||||
# 判据出处: design/engineering/test-conventions.md「测试层三元组」冒烟测试行 (测试层三元组——冒烟存活检查组)
|
||||
check(main != null and main.is_inside_tree(), "主屏场景已实例化并在树")
|
||||
if main == null:
|
||||
return
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
# tests/static_scan.sh — 静态扫描域 S1–S10 全量在位
|
||||
# 规则全集与动机:design/engineering/architecture/architecture.md「测试与质量域」节
|
||||
# 规则全集与动机:game/tests/README.md「Static Scan」节(2026-11-07 架构收窄批迁驻)
|
||||
# 正式工程(core/ 存在)时执行;否则优雅跳过(工程在 game/core——仓库根布局)。
|
||||
# 纪律:新违规案例必须先补本脚本规则再修码(design/engineering/code-conventions.md 缺陷-规范对照表)。
|
||||
set -u
|
||||
@@ -313,17 +313,16 @@ else
|
||||
fi
|
||||
|
||||
# ── S8 — 结算路径 await 零命中(B1 结算/编排文件集)──
|
||||
# 规则(architecture.md「静态扫描域 S1–S8 承接清单」S8 行 / D2 AC-14 口径):结算本体是
|
||||
# 可回放纯逻辑步骤流(同种子同输入逐位复现),await 会打断确定性——「模块归属」B1 行已写明
|
||||
# 「禁 await——切片 88 冒烟实证」。本闸为防回归防线(2026-10-24 人工 grep 盘面干净)。
|
||||
# 扫描面:B1 域文件集(与 architecture.md「模块归属」B1 行同口径)=battle_orchestrator.gd
|
||||
# 规则(game/tests/README.md「Static Scan」节 S8 行 / D2 AC-14 口径):结算本体是
|
||||
# 可回放纯逻辑步骤流(同种子同输入逐位复现),await 会打断确定性。本闸为防回归防线。
|
||||
# 扫描面:B1 域文件集=battle_orchestrator.gd
|
||||
# + battle_fx_host.gd + reward_grant.gd + board_installer.gd 三拆件 + settlement/ 五段段类 + damage_controller.gd,清单单点=B1_SET。
|
||||
# 执行侧字面值豁免:脚本必须带字面值才能工作;文件集与架构文档冲突时以 B1 域文件集为准。
|
||||
# 判据:文件集内出现 await 关键字即红(词边界 \bawait\b,任何形态——await 信号/协程/
|
||||
# get_tree());排除注释行(行首 #/##)与字符串字面量(双引号包裹段内的 await 非代码),
|
||||
# 口径仿 S1/S2 的 grep 排除手法。
|
||||
# 自检:①清单内文件缺位(路径写错/被改名)即判红——防扫面为空静默绿;②settlement/ 目录域反向对账——新段类未登记即判红(见下)。
|
||||
# B1 域文件集=单点清单(S8 await 禁扫面与 S7 豁免名单共用;改成员先改 architecture.md「模块归属」B1 行再改此处,2026-11-03 架构审查 P1 裁并点)
|
||||
# B1 域文件集=单点清单(S8 await 禁扫面与 S7 豁免名单共用;2026-11-03 并点。清单单点=本脚本,
|
||||
# 脚本即清单——2026-11-07 架构收窄后架构文档不再承载文件集登记,改成员只改此处)
|
||||
B1_SET="game/core/battle_orchestrator.gd game/core/battle_fx_host.gd game/core/reward_grant.gd game/core/board_installer.gd game/core/settlement/aura_segment.gd game/core/settlement/combat_segment.gd game/core/settlement/decay_segment.gd game/core/settlement/grant_segment.gd game/core/settlement/verdict_segment.gd game/core/settlement/round_cycle.gd game/core/damage_controller.gd game/core/settlement/battle_refresh_flow.gd game/core/effect_port.gd game/core/reward_roller.gd"
|
||||
S8_FILES="$B1_SET"
|
||||
S8_MISS=0
|
||||
@@ -335,13 +334,13 @@ for f in $S8_FILES; do
|
||||
done
|
||||
# 自检二(2026-11-03 架构审查 P1 裁):目录域反向对账——settlement/ 实有 .gd 未登记进 B1_SET=该扫的没扫,判红。
|
||||
# 只反向对 settlement/ 一个目录域:装板/授予/效果端口/掷格四件结算码住 game/core 根下,纯目录域覆盖不住,
|
||||
# 根下新增仍由「模块归属」B1 行+写卡段申报面承接(禁把目录判据当成全域判据)。
|
||||
# 根下新增仍由写卡段申报面承接(禁把目录判据当成全域判据)。
|
||||
S8_UNREG=""
|
||||
for f in $(find game/core/settlement -name '*.gd' 2>/dev/null | sort); do
|
||||
case " $B1_SET " in *" $f "*) ;; *) S8_UNREG="$S8_UNREG $f" ;; esac
|
||||
done
|
||||
if [ -n "$S8_UNREG" ]; then
|
||||
echo "[static_scan] S8 FAIL: settlement/ 有结算段类未登记进 B1 域文件集——await 禁扫面对它静默失效:$S8_UNREG(先改 architecture.md「模块归属」B1 行,再补 B1_SET)"
|
||||
echo "[static_scan] S8 FAIL: settlement/ 有结算段类未登记进 B1 域文件集——await 禁扫面对它静默失效:$S8_UNREG(补 B1_SET 登记)"
|
||||
S8_MISS=1
|
||||
fi
|
||||
if [ "$S8_MISS" -eq 1 ]; then
|
||||
@@ -361,13 +360,13 @@ else
|
||||
fi
|
||||
|
||||
# ── S7 — B1 编排唯一性:B2/B3/B4 API 符号命中文件 ⊆ B1 域文件集 ──
|
||||
# 规则(architecture.md「静态扫描域 S1–S8 承接清单」S7 行 / B1 AC-43;依赖约束节同款):
|
||||
# 规则(game/tests/README.md「Static Scan」节 S7 行 / B1 AC-43;依赖约束节同款):
|
||||
# B2/B3/B4 的对外 API 只允许被 B1 域文件集调用——全调用面集中 B1、禁第二调用方。
|
||||
# 附带覆盖「A2/B3/B4 零相互直调」:B2/B3/B4 自身文件 ∉ B1 文件集,命中彼家符号即红
|
||||
# (同族自调用豁免只放自家符号,彼家符号照红)。
|
||||
# 符号清单权威源:architecture.md「模块归属」各表「对外暴露」列——B2(unit_container)14 符号、
|
||||
# B3(status_container)5 符号、B4(reward_economy)9 符号;脚本字面值与文档冲突时以文档为准
|
||||
# (执行侧字面值豁免,同 S8 手法)。B4 表内 is_mapped 静默布尔投影注明批量工具专用、不在暴露面清单。
|
||||
# 符号清单=从 B2/B3/B4 三族文件自动提取公开方法面(非下划线 func 名,含 static func)——
|
||||
# 清单即代码现状,无手写清单、无文档同步义务(2026-11-07 架构收窄批改版;原手写清单
|
||||
# 对文档核录的做法废弃——头注计数与脚本字面已实测漂移 1 符号,手写必漂)。
|
||||
# 扫描面:$DIR_CORE(含 settlement/,B1 文件集在其中)。
|
||||
# 判据:文件内出现 `.符号(` 调用形态(符号后紧跟括号,天然词边界;`.my_grant(` 不误伤 `grant`)
|
||||
# 且该文件 ∉ B1 文件集 → 红。排除注释行与字符串字面量(口径仿 S1/S2/S8 的 grep 排除手法)。
|
||||
@@ -379,7 +378,7 @@ fi
|
||||
# (文件被改名=豁免名单失效,宁可红不可静默放行)。
|
||||
if require_scan_face "S7" "$DIR_CORE"; then
|
||||
# B1 域文件集(迭代 46 卡A 增补 battle_refresh_flow——换图刷新流程件=C13 拆分边界的 B1 域拆件,
|
||||
# 承载旧资源退役/装板编排调用;architecture.md「模块归属」BattleRefreshFlow 行随反哺点落文)
|
||||
# 承载旧资源退役/装板编排调用;清单单点=本脚本)
|
||||
S7_B1_SET="$B1_SET" # 与 S8 同一单点清单(原第二份抄本,2026-11-03 并点——两份靠人工同步必漂)
|
||||
S7_MISS=0
|
||||
for f in $S7_B1_SET game/core/unit_container.gd game/core/status_container.gd game/core/reward_economy.gd; do
|
||||
@@ -406,13 +405,27 @@ else
|
||||
"
|
||||
done < <(find "$DIR_CORE" -name '*.gd' 2>/dev/null | sort)
|
||||
}
|
||||
# 符号清单(2026-10-25 对 architecture.md「模块归属」表核录;改符号先改文档再改此处)
|
||||
s7_scan B2 game/core/unit_container.gd \
|
||||
'spawn|query_alive|rollable_skills|special_skills|check_skill_unlocks|set_pending_action|pending_action|basic_streak|commit_action|tick_cooldowns|apply_damage|heal|default_target|despawn_all'
|
||||
s7_scan B3 game/core/status_container.gd \
|
||||
'notify_battle_start|apply_effect|query_active|decay_round|notify_battle_end'
|
||||
s7_scan B4 game/core/reward_economy.gd \
|
||||
'roll_board|query_identity|query_full_snapshot|grant|grant_clear|retire_mapping|polarity_row|phase'
|
||||
# 符号清单自动提取(2026-11-07 架构收窄批):公开方法面=非下划线 func 名(含 static func),
|
||||
# 清单=代码现状;is_mapped 为批量工具专用投影、不在暴露面(沿用原手写清单口径,提取后剔除)
|
||||
extract_public_funcs() {
|
||||
grep -oE '^[[:space:]]*(static )?func [a-z][a-z_0-9]*' "$1" 2>/dev/null \
|
||||
| sed 's/^[[:space:]]*\(static \)\?func //' | paste -sd'|' -
|
||||
}
|
||||
B2_SYMS=$(extract_public_funcs game/core/unit_container.gd)
|
||||
B3_SYMS=$(extract_public_funcs game/core/status_container.gd)
|
||||
B4_SYMS=$(extract_public_funcs game/core/reward_economy.gd | sed 's/|is_mapped//')
|
||||
# 提取非空自检(防空转:提取失败=空 pattern,grep 零命中恒绿)
|
||||
S7_EXTRACT_MISS=0
|
||||
for v in "$B2_SYMS" "$B3_SYMS" "$B4_SYMS"; do
|
||||
if [ -z "$v" ]; then
|
||||
echo "[static_scan] S7 FAIL: 公开方法面提取为空——三族文件缺位或无公开 func(提取机制静默空转)"
|
||||
S7_EXTRACT_MISS=1
|
||||
fi
|
||||
done
|
||||
[ "$S7_EXTRACT_MISS" -eq 1 ] && fail=1
|
||||
[ -n "$B2_SYMS" ] && s7_scan B2 game/core/unit_container.gd "$B2_SYMS"
|
||||
[ -n "$B3_SYMS" ] && s7_scan B3 game/core/status_container.gd "$B3_SYMS"
|
||||
[ -n "$B4_SYMS" ] && s7_scan B4 game/core/reward_economy.gd "$B4_SYMS"
|
||||
if [ -n "$S7_HITS" ]; then
|
||||
echo "[static_scan] S7 FAIL: B2/B3/B4 API 符号越出 B1 文件集命中 $(printf '%s' "$S7_HITS" | grep -c .) 处(编排唯一性,B1 AC-43)"
|
||||
printf '%s' "$S7_HITS"
|
||||
|
||||
@@ -34,7 +34,7 @@ func _initialize() -> void:
|
||||
# (自 game/smoke/smoke_all.gd 剪切迁入——断言 label 逐字原样;G12 两套调用不随迁,
|
||||
# 归 source_guard_test.gd,断言迁移批 B G1)
|
||||
func _split_tail() -> void:
|
||||
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
await process_frame
|
||||
await process_frame
|
||||
var autoload_ok: bool = get_root().get_node_or_null("/root/ConfigDB") != null and get_root().get_node_or_null("/root/SaveManager") != null
|
||||
@@ -54,7 +54,7 @@ func _split_tail() -> void:
|
||||
# ---- 三屏场景化断言(V2——独立实例化+关键节点在位+组合根树序/z 序保序)----
|
||||
# (自 game/smoke/smoke_all.gd 剪切迁入——label 与 cond 逐字原样,断言迁移批 B G1)
|
||||
func _smoke_scene_split() -> void:
|
||||
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
print("--- 迭代27 T1 三屏场景化(V2:屏壳独立实例化+组合根装配保序)---")
|
||||
# ① 三屏壳独立实例化(F6 等价——headless 直挂;视图代码构建在屏壳 _ready)
|
||||
var menu: Node = load("res://presentation/scenes/menu_screen.tscn").instantiate()
|
||||
@@ -151,7 +151,7 @@ func _count_visible_buttons(node: Node) -> int:
|
||||
|
||||
# ---- boot 链路时序后置条件(启动态六断言;只立行为可判断言,不造假时序)----
|
||||
func _smoke_boot_chain() -> void:
|
||||
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
print("--- 迭代28 T2 boot 链路时序后置条件(V2:六断言)---")
|
||||
var main: Node = load("res://presentation/main.tscn").instantiate()
|
||||
root.add_child(main)
|
||||
@@ -216,7 +216,7 @@ func _smoke_boot_chain() -> void:
|
||||
|
||||
|
||||
func _smoke_run_lifecycle() -> void:
|
||||
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
print("--- 迭代27 T1 会话物化(V3:两连局复位+脏状态不残留)---")
|
||||
var main: Node = load("res://presentation/main.tscn").instantiate()
|
||||
root.add_child(main)
|
||||
@@ -274,7 +274,7 @@ func _smoke_run_lifecycle() -> void:
|
||||
|
||||
# ---- d6/d7 批量模拟互斥守卫(wd6/wd7——白盒置态直调入口,以守卫早退的可见后果为断言面)----
|
||||
func _smoke_sim_guards() -> void:
|
||||
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
print("--- 卡① T2:批量模拟互斥两守卫(d6/d7)---")
|
||||
var main: Node = load("res://presentation/main.tscn").instantiate()
|
||||
root.add_child(main)
|
||||
@@ -348,7 +348,7 @@ func _face_snapshot(main: Node) -> Dictionary:
|
||||
# 阶段面断言:adv/run/set=alive|null|both(会话三面存活态);恒活面每阶段必查;
|
||||
# 现值回调与 run_scope 成员必须同源(回调链无悬挂铁证);extra=阶段附加判据。
|
||||
func _stage_faces(main: Node, tag: String, adv: String, run: String, set: String, extra: Dictionary = {}) -> void:
|
||||
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
var snap := _face_snapshot(main)
|
||||
var bad := ""
|
||||
for k in ["bonus_meta", "bonus_save", "camp_meta", "camp_save", "rp_log", "rp_stage", "rp_board_zone", "rp_fx", "rp_board"]:
|
||||
@@ -417,7 +417,7 @@ func _face_liveness(name: String, v, want: String) -> String:
|
||||
#(level_started→choices_presented 同步到达,无可观测中间态)——合并为 V3b 一处断言。
|
||||
# (自 game/smoke/smoke_all.gd 剪切迁入——label 与 cond 逐字原样,断言迁移批 B G1)
|
||||
func _smoke_lifecycle_matrix() -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/冒险结构.md:284 (生命周期矩阵——三关序列组)
|
||||
# 判据出处: design/gdd/systems/冒险循环/冒险结构.md:284 (生命周期矩阵——三关序列组)
|
||||
print("--- 迭代28 T3 生命周期存活性矩阵(V3:九阶段×五引用面)---")
|
||||
var main: Node = load("res://presentation/main.tscn").instantiate()
|
||||
root.add_child(main)
|
||||
@@ -608,7 +608,7 @@ func _smoke_lifecycle_matrix() -> void:
|
||||
|
||||
|
||||
func _smoke_camp_hud_band() -> void:
|
||||
# oracle: design/ux/camp.md:41 (营地顶栏栏体定值与落座——几何/色值/吃点击五钉组)
|
||||
# 判据出处: design/ux/camp.md:41 (营地顶栏栏体定值与落座——几何/色值/吃点击五钉组)
|
||||
print("--- 营地交互修复批 T3 顶栏栏体(R5:色值/铺满/不吃点击/首子件/徽章落座)---")
|
||||
var main: Node = load("res://presentation/main.tscn").instantiate()
|
||||
root.add_child(main)
|
||||
|
||||
@@ -8,13 +8,13 @@ const Kit := preload("res://tests/unit/test_kit.gd")
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/敌人系统.md:293 Boss 节点开战行集宿主(装载组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/敌人系统.md:293 Boss 节点开战行集宿主(装载组)
|
||||
_test_boss_node_spawns_boss_rowset()
|
||||
quit(Kit.finish("boss_gate_test"))
|
||||
|
||||
|
||||
func _test_boss_node_spawns_boss_rowset() -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/敌人系统.md:295 开战生成按遭遇坐标取行集(Boss 行集组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/敌人系统.md:295 开战生成按遭遇坐标取行集(Boss 行集组)
|
||||
var db = load("res://core/config_db.gd").new()
|
||||
var load_ok: bool = db.load_tables("res://data/tables", true) and db.diagnostics_count("error") == 0
|
||||
Kit.check("前置:表加载零 error", load_ok)
|
||||
|
||||
@@ -23,7 +23,7 @@ var _rows: Array = [] # 当前 run 的行协议收集(_on_row 绑定法—
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/战斗结算.md:360 伤害单据编排宿主(装载组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/战斗结算.md:360 伤害单据编排宿主(装载组)
|
||||
var db = ConfigDBScript.new()
|
||||
var load_ok: bool = db.load_tables("res://data/tables", true)
|
||||
Kit.check("前置:表加载零 error", load_ok and db.diagnostics_count("error") == 0)
|
||||
@@ -71,7 +71,7 @@ func _row_by_display_prefix(prefix: String) -> Dictionary:
|
||||
# ---- 五配方一次全流程 + 双跑逐位复现 ----
|
||||
|
||||
func _run_recipe_sequence(db) -> Array:
|
||||
# oracle: design/gdd/systems/战斗循环/战斗结算.md:362 (五段全序——配方序列组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/战斗结算.md:362 (五段全序——配方序列组)
|
||||
_rows = []
|
||||
var h = _harness(db)
|
||||
var units = h.units
|
||||
@@ -178,7 +178,7 @@ func _run_recipe_sequence(db) -> Array:
|
||||
|
||||
|
||||
func _test_five_recipes_and_replay(db) -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/战斗结算.md:363 (同种子回放逐行相同组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/战斗结算.md:363 (同种子回放逐行相同组)
|
||||
var run1: Array = _run_recipe_sequence(db)
|
||||
Kit.check("五配方序列产出 5 行伤害行", run1.size() == 5)
|
||||
var run2: Array = _run_recipe_sequence(db)
|
||||
@@ -188,7 +188,7 @@ func _test_five_recipes_and_replay(db) -> void:
|
||||
# ---- A 模式(开单即结——回切位) ----
|
||||
|
||||
func _test_policy_immediate_mode(db) -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/战斗结算.md:362 (结算策略即时面组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/战斗结算.md:362 (结算策略即时面组)
|
||||
_rows = []
|
||||
var h = _harness(db)
|
||||
if not h.spawn_ok:
|
||||
@@ -204,7 +204,7 @@ func _test_policy_immediate_mode(db) -> void:
|
||||
# ---- 来源敌死亡:队列中未结单据作废(死亡即不动手) ----
|
||||
|
||||
func _test_source_death_void(db) -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/敌人系统.md:133 (摇定后兑现前被杀本拍作废组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/敌人系统.md:133 (摇定后兑现前被杀本拍作废组)
|
||||
_rows = []
|
||||
var h = _harness(db)
|
||||
if not h.spawn_ok:
|
||||
@@ -238,7 +238,7 @@ func _unit_fixture() -> Dictionary:
|
||||
|
||||
|
||||
func _test_identical_content_dual_enemy(db: ConfigDb) -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/敌人系统.md:303 (双敌同内容顺位组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/敌人系统.md:303 (双敌同内容顺位组)
|
||||
var a := _unit_fixture()
|
||||
var b := _unit_fixture()
|
||||
Kit.check("前置:双敌字典内容全同且非同一实例", a == b and not is_same(a, b))
|
||||
@@ -268,7 +268,7 @@ func _test_identical_content_dual_enemy(db: ConfigDb) -> void:
|
||||
|
||||
# d12 产发端增键:目标身份随行/随载荷下发(同名敌按 id 精确解析的产发半)
|
||||
func _test_target_id_payload(db: ConfigDb) -> void:
|
||||
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
_rows = []
|
||||
var h = _harness(db)
|
||||
if not h.spawn_ok:
|
||||
@@ -291,7 +291,7 @@ func _test_target_id_payload(db: ConfigDb) -> void:
|
||||
|
||||
|
||||
func _test_requires_gate_and_zero(db) -> void:
|
||||
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
_rows = []
|
||||
var h = _harness(db)
|
||||
if not h.spawn_ok:
|
||||
@@ -318,7 +318,7 @@ func _test_requires_gate_and_zero(db) -> void:
|
||||
# ---- 主角倒下:flush 余单全部作废(_halted 终止) ----
|
||||
|
||||
func _test_hero_death_halt(db) -> void:
|
||||
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
_rows = []
|
||||
var h = _harness(db)
|
||||
if not h.spawn_ok:
|
||||
@@ -336,7 +336,7 @@ func _test_hero_death_halt(db) -> void:
|
||||
# ---- AC 合流:override 入参 / 表值兜底 / ac 型状态叠层(不入池) ----
|
||||
|
||||
func _test_ac_merge(db) -> void:
|
||||
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
_rows = []
|
||||
var h = _harness(db)
|
||||
if not h.spawn_ok:
|
||||
@@ -364,7 +364,7 @@ func _test_ac_merge(db) -> void:
|
||||
# ---- 池乘区收口:池内加算 → clamp;ac 型不入池 ----
|
||||
|
||||
func _test_pool_clamp(db) -> void:
|
||||
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
_rows = []
|
||||
var h = _harness(db)
|
||||
if not h.spawn_ok:
|
||||
@@ -422,7 +422,7 @@ func _test_pool_clamp(db) -> void:
|
||||
# ---- 个体池读数 / 个体 AC 合流(增益状态 v1.5——敌方状态个体化批)----
|
||||
|
||||
func _test_pool_consumption_per_unit(db) -> void:
|
||||
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
var h = _harness(db, 3) # depth 3 = 混编行集(熊地精 1 + 地精 1–2,最少 2 只——个体对照面需 ≥2 敌)
|
||||
if not h.spawn_ok:
|
||||
return
|
||||
@@ -442,7 +442,7 @@ func _test_pool_consumption_per_unit(db) -> void:
|
||||
|
||||
|
||||
func _test_defender_ac_per_unit(db) -> void:
|
||||
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
var h = _harness(db, 3)
|
||||
if not h.spawn_ok:
|
||||
return
|
||||
|
||||
@@ -12,7 +12,7 @@ var db
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/战斗结算.md:360 (伤害公式宿主——装载组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/战斗结算.md:360 (伤害公式宿主——装载组)
|
||||
db = ConfigDBScript.new()
|
||||
var load_ok: bool = db.load_tables("res://data/tables", true)
|
||||
Kit.check("前置:settlement 表加载零 error", load_ok and db.diagnostics_count("error") == 0)
|
||||
@@ -31,7 +31,7 @@ func _floor_val() -> int:
|
||||
# ---- F2 乘区:池内加算 → 池间乘算 → floor(单一取整规则)----
|
||||
|
||||
func _test_pool_damage() -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/战斗结算.md:360 (池伤害公式组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/战斗结算.md:360 (池伤害公式组)
|
||||
var d := _floor_val() + 9 # 表值推导的任意正探针量(非玩法行值)
|
||||
Kit.check("中性双池 (0,0) 原样透传", DamageMathScript.pool_damage(d, 0, 0) == d)
|
||||
Kit.check("负敌方池压低不增高", DamageMathScript.pool_damage(d, 0, int(db.combat.settlement.core.pool_clamp_lo)) <= d)
|
||||
@@ -44,7 +44,7 @@ func _test_pool_damage() -> void:
|
||||
# ---- 克制分量:空桶不吃修正恒 0;落地下限保底([1,+∞) 全来源)----
|
||||
|
||||
func _test_counter_component() -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/战斗结算.md:360 (计数器分量组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/战斗结算.md:360 (计数器分量组)
|
||||
var floor_v := _floor_val()
|
||||
Kit.check("空桶(点数≤0)恒 0 不吃修正", DamageMathScript.counter_component(0, floor_v, floor_v) == 0
|
||||
and DamageMathScript.counter_component(-1, 5, floor_v) == 0)
|
||||
@@ -56,7 +56,7 @@ func _test_counter_component() -> void:
|
||||
# ---- 掷点折扣查档(纯函数):满阈值恒全额;档位边界/末档语义与表数组一致 ----
|
||||
|
||||
func _test_roll_discount_percent() -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/战斗结算.md:360 (乘区折扣组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/战斗结算.md:360 (乘区折扣组)
|
||||
var threshold := 10 # 查档探针阈值(夹具值——档位参数本身全部来自表)
|
||||
var bounds: Array = db.combat.settlement.damage_roll.roll_tier_bounds
|
||||
var discounts: Array = db.combat.settlement.damage_roll.roll_tier_discounts
|
||||
@@ -81,7 +81,7 @@ func _test_roll_discount_percent() -> void:
|
||||
# ---- 吃掷点分量算术:折扣→(暴击)→floor→+克制修正(单一取整链)----
|
||||
|
||||
func _test_rolled_component() -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/战斗结算.md:360 (掷点分量组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/战斗结算.md:360 (掷点分量组)
|
||||
var base := float(_floor_val() + 7) # 表值推导探针基线
|
||||
var crit_mult: int = int(db.combat.settlement.damage_roll.crit_multiplier)
|
||||
Kit.check("满额折扣无暴击 = floor(基线) + 修正 0", DamageMathScript.rolled_component(base, 100, false, crit_mult, 0) == int(floor(base)))
|
||||
|
||||
@@ -19,7 +19,7 @@ var _db
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/engineering/architecture/architecture.md:144 (效果端口宿主——装载组)
|
||||
# 判据出处: design/engineering/architecture/architecture.md「模块归属·功能层」battle_orchestrator 行(域文件集含 EffectPort) (效果端口宿主——装载组)
|
||||
_db = ConfigDBScript.new()
|
||||
var load_ok: bool = _db.load_tables("res://data/tables", true)
|
||||
Kit.check("前置:表加载零 error", load_ok and _db.diagnostics_count("error") == 0)
|
||||
@@ -54,7 +54,7 @@ func _hero_attack_value(rows: Array) -> int:
|
||||
# ---- V1 端口单实例与真实发射路由 ----
|
||||
|
||||
func _smoke_effect_port_wiring() -> void:
|
||||
# oracle: design/engineering/architecture/architecture.md:144 (端口单实例与六 Callable 路由组)
|
||||
# 判据出处: design/engineering/architecture/architecture.md「模块归属·功能层」battle_orchestrator 行(域文件集含 EffectPort) (端口单实例与六 Callable 路由组)
|
||||
print("--- 效果端口:装配链单实例+真实发射路由 ---")
|
||||
# 充能跑(蓄力周转 charge_rotation 在册——attack_commit 订阅行 attack_bonus per 蓄力周转计数器)
|
||||
var orch_c = _orch(TEST_SEED)
|
||||
@@ -88,7 +88,7 @@ func _smoke_effect_port_wiring() -> void:
|
||||
# ---- V2 业务类零引擎直连(源码契约锚——theme_projection_test 源码锚惯例)----
|
||||
|
||||
func _test_business_classes_engine_free() -> void:
|
||||
# oracle: design/engineering/architecture/architecture.md:144 (业务类零引擎直连组)
|
||||
# 判据出处: design/engineering/architecture/architecture.md「模块归属·功能层」battle_orchestrator 行(域文件集含 EffectPort) (业务类零引擎直连组)
|
||||
print("--- 效果端口:业务类零引擎直连源码锚 ---")
|
||||
for path in ["res://core/damage_controller.gd", "res://core/action_economy.gd"]:
|
||||
var src := FileAccess.get_file_as_string(path)
|
||||
@@ -100,7 +100,7 @@ func _test_business_classes_engine_free() -> void:
|
||||
# ---- V3 伤害域事实面闭集(合成行两对照——damage_taken 真实单据流)----
|
||||
|
||||
func _test_biz_fact_face_closed() -> void:
|
||||
# oracle: design/engineering/architecture/architecture.md:144 (闭面四键 biz_fact 组)
|
||||
# 判据出处: design/engineering/architecture/architecture.md「模块归属·功能层」battle_orchestrator 行(域文件集含 EffectPort) (闭面四键 biz_fact 组)
|
||||
print("--- 效果端口:业务发射事实面闭集 ---")
|
||||
var orch = _orch(TEST_SEED)
|
||||
Kit.check("V3 begin_battle 受理", orch.begin_battle(_enc([])))
|
||||
|
||||
@@ -11,7 +11,7 @@ const Kit := preload("res://tests/unit/test_kit.gd")
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/数据配置.md:199 (扩展行必载 effect_id——加载致命组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/数据配置.md:199 (扩展行必载 effect_id——加载致命组)
|
||||
var db = load("res://core/config_db.gd").new()
|
||||
if not db.load_tables("res://data/tables", false):
|
||||
push_error("[extended_row_violation_test] 表加载失败")
|
||||
|
||||
@@ -154,7 +154,7 @@ func _seal_direct_with_expansion(orch) -> Vector2i:
|
||||
# 四条真实路径(敌方攻击/危险格/错误封印/地形)逐路驱动:每条至少一张主角承伤单据在案,
|
||||
# 且承伤计数器 == 主角承伤单据行数(每单据恰发射一次的全量判据——重复发射即计数器>行数)。
|
||||
func test_all_hero_damage_paths_emit_once() -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/奇物.md:465 (换图生存:全局固定减伤全伤害路径恰一次组)
|
||||
# 判据出处: design/gdd/systems/冒险循环/奇物.md:465 (换图生存:全局固定减伤全伤害路径恰一次组)
|
||||
print("--- V5 全局 damage_taken:四路径逐路恰一次 ---")
|
||||
# ① 敌方攻击路径(Boss 战零挖掘连停三轮——干净样本:零地形零资源单据;多轮保证伤害技在案)
|
||||
var orch1 = _orch(46001)
|
||||
@@ -224,7 +224,7 @@ func _plan_seal(orch, target: Vector2i) -> Dictionary:
|
||||
# 同一伤害不得出现两次消费(重复发射=计数器>行数):敌方路径与地形路径两样本逐位核对——
|
||||
# 地形单据带 hero_damage_consumed 标记,战斗段末 _resolve_board_flat_hero 跳过二次发射由此钉住。
|
||||
func test_no_duplicate_damage_taken_event() -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/奇物.md:465 (受击事件零重复组)
|
||||
# 判据出处: design/gdd/systems/冒险循环/奇物.md:465 (受击事件零重复组)
|
||||
print("--- V5 全局 damage_taken:无重复消费 ---")
|
||||
var orch1 = _orch(46011)
|
||||
var rows1 := _collect_hero_rows(orch1)
|
||||
@@ -266,7 +266,7 @@ func _auto_dig(orch) -> void:
|
||||
|
||||
# V2 矿脉储能:受理的付费挖格 RAP 恰 +1、AP 按表扣、板资源不变
|
||||
func test_reserve_on_accepted_dig() -> void:
|
||||
# oracle: design/gdd/systems/排雷循环/行动经济.md:256 (换图基座:挖格产 RAP 组)
|
||||
# 判据出处: design/gdd/systems/排雷循环/行动经济.md:256 (换图基座:挖格产 RAP 组)
|
||||
print("--- V2 矿脉储能:付费挖格产 RAP ---")
|
||||
var orch = _orch(46021)
|
||||
Kit.check("V2 前置:战斗受理", orch.begin_battle(_enc(["vein_reserve"])))
|
||||
@@ -304,7 +304,7 @@ func _drain_round_rap_accounting(orch) -> bool:
|
||||
|
||||
# V3 省力周转:折价门槛与扣费(can 与执行同源;base 8→扣 4)
|
||||
func test_refresh_discount_gate_and_charge() -> void:
|
||||
# oracle: design/gdd/systems/排雷循环/行动经济.md:256 (刷新折扣门控与充能组)
|
||||
# 判据出处: design/gdd/systems/排雷循环/行动经济.md:256 (刷新折扣门控与充能组)
|
||||
print("--- V3 省力周转:折价门槛与扣费 ---")
|
||||
var orch = _orch(46022)
|
||||
Kit.check("V3 前置:战斗受理", orch.begin_battle(_enc(["easy_swap"])))
|
||||
@@ -326,7 +326,7 @@ func test_refresh_discount_gate_and_charge() -> void:
|
||||
|
||||
# V3 双基座叠加:挖格产 RAP 与刷新折价同场生效
|
||||
func test_double_base_stack() -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/奇物.md:465 (双基座叠加组)
|
||||
# 判据出处: design/gdd/systems/冒险循环/奇物.md:465 (双基座叠加组)
|
||||
print("--- V3 双基座叠加 ---")
|
||||
var orch = _orch(46023)
|
||||
Kit.check("V3 前置:战斗受理", orch.begin_battle(_enc(["vein_reserve", "easy_swap"])))
|
||||
@@ -343,7 +343,7 @@ func test_double_base_stack() -> void:
|
||||
|
||||
# V12(refresh_feats 侧):无专长基线——挖格零产 RAP、刷新按表基线扣、AP 保留、上限照旧
|
||||
func test_no_feats_baseline() -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/奇物.md:465 (零专长基线组)
|
||||
# 判据出处: design/gdd/systems/冒险循环/奇物.md:465 (零专长基线组)
|
||||
print("--- V12 无专长基线(refresh_feats 侧) ---")
|
||||
var orch = _orch(46024)
|
||||
Kit.check("V12 前置:战斗受理", orch.begin_battle(_enc([])))
|
||||
@@ -379,7 +379,7 @@ func _br_count(orch) -> int:
|
||||
|
||||
# V4 成功事实只在新板就位后发射:成功恰一次;非 Active/每轮上限/RAP 不足三拒面全零事实零换图
|
||||
func test_board_refresh_fact_only_on_success() -> void:
|
||||
# oracle: design/gdd/systems/排雷循环/行动经济.md:256 (弃图刷新事实面仅成功态组)
|
||||
# 判据出处: design/gdd/systems/排雷循环/行动经济.md:256 (弃图刷新事实面仅成功态组)
|
||||
print("--- V4 成功刷新事实:拒绝零事实 ---")
|
||||
_install_br_probe()
|
||||
var orch = _orch(46031)
|
||||
@@ -414,7 +414,7 @@ func test_board_refresh_fact_only_on_success() -> void:
|
||||
|
||||
# V10 矿道红利:每次成功刷新 escrow 恰 +1 且 fx_grant_gp 行在案;拒绝零变化
|
||||
func test_mine_profit_gp() -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/奇物.md:465 (换图资源:刷新固定 GP 收益组)
|
||||
# 判据出处: design/gdd/systems/冒险循环/奇物.md:465 (换图资源:刷新固定 GP 收益组)
|
||||
print("--- V10 矿道红利:刷新固定 GP ---")
|
||||
var orch = _orch(46032)
|
||||
var gp_rows := {"n": 0}
|
||||
@@ -461,7 +461,7 @@ func _damage_rows_by_source(rows: Array, source: String) -> Array:
|
||||
# V6 石层缓冲:全局固定减伤(危险格 pierce 8→6 两档钉死:单刷减 2·同轮多刷不叠加·未刷新轮不减;
|
||||
# 地形路径减后值=max(下限, effect_value−2)——含下限保持;敌方路径照常单次发射)
|
||||
func test_stone_mitigation_all_damage() -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/奇物.md:465 (全局减伤全覆盖组)
|
||||
# 判据出处: design/gdd/systems/冒险循环/奇物.md:465 (全局减伤全覆盖组)
|
||||
print("--- V6 石层缓冲:全局固定减伤 ---")
|
||||
# S1 同轮双刷 → 危险格 pierce 8 减 2=6(叠加即 4——多刷不增钉死)
|
||||
var orch1 = _orch(46041)
|
||||
@@ -517,7 +517,7 @@ func test_stone_mitigation_all_damage() -> void:
|
||||
# V9 坑道小憩:轮末合计回复(白盒预置承载上限 3——计数器直改属数据面注入;
|
||||
# 自然累计 2 与满血未生效两形态分战场钉死)
|
||||
func test_rest_rotation_cap_and_clear() -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/奇物.md:465 (轮末恢复 cap 与清账组)
|
||||
# 判据出处: design/gdd/systems/冒险循环/奇物.md:465 (轮末恢复 cap 与清账组)
|
||||
print("--- V9 坑道小憩:轮末恢复 ---")
|
||||
# R1 白盒预置 3 层 + 刷新闸(when<3 拒第 4 层)→ 停手回复 min(3, 缺血)——护甲降档保缺血 ≥3
|
||||
var orch1 = _orch(46051)
|
||||
@@ -575,7 +575,7 @@ func test_rest_rotation_cap_and_clear() -> void:
|
||||
|
||||
# V8 蓄力周转:当轮攻击消费全部层数(上限 3 闸=白盒预置;战斗新建自然清空)
|
||||
func test_charge_layers_consumed_same_round() -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/奇物.md:465 (充能当轮消耗组)
|
||||
# 判据出处: design/gdd/systems/冒险循环/奇物.md:465 (充能当轮消耗组)
|
||||
print("--- V8 蓄力周转:攻击消费层 ---")
|
||||
var orch1 = _orch(46061)
|
||||
var rows1 := _collect_damage_rows(orch1)
|
||||
@@ -602,7 +602,7 @@ func test_charge_layers_consumed_same_round() -> void:
|
||||
|
||||
# V7 崩壁冲击:延迟群伤单(刷新时点零伤害行——战斗段末按存活目标结账)
|
||||
func test_blast_defers_to_combat_segment() -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/战斗结算.md:362 (刷新群伤让位战斗段组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/战斗结算.md:362 (刷新群伤让位战斗段组)
|
||||
print("--- V7 崩壁冲击:延迟群伤 ---")
|
||||
var orch = _orch(46071)
|
||||
var rows := _collect_damage_rows(orch)
|
||||
@@ -621,7 +621,7 @@ func test_blast_defers_to_combat_segment() -> void:
|
||||
|
||||
# 七件 validate/在册/稀有度 cap 对表/跨表计数器引用闭合/desc 禁词
|
||||
func test_seven_feats_validate_and_pool() -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/奇物.md:465 (换图系七件校验与池组)
|
||||
# 判据出处: design/gdd/systems/冒险循环/奇物.md:465 (换图系七件校验与池组)
|
||||
print("--- V11 七件数据与池 ---")
|
||||
var seven := {"vein_reserve": "excellent", "easy_swap": "excellent", "wall_blast": "excellent",
|
||||
"charge_rotation": "excellent", "stone_mitigation": "common", "tunnel_rest": "common", "mine_profit": "rare"}
|
||||
|
||||
@@ -19,7 +19,7 @@ var _db
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/战斗结算.md:360 (掷点与乘区宿主——装载组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/战斗结算.md:360 (掷点与乘区宿主——装载组)
|
||||
_db = ConfigDBScript.new()
|
||||
# 表加载失败不中止(承 G2/G3 同款:表损坏的红落在具体断言上——
|
||||
# 诊断在此展开,后续断言按坏表实况逐条验红;collect_only=true 下成员照常赋值可评)
|
||||
@@ -44,7 +44,7 @@ func _init() -> void:
|
||||
# 受理后不变量(encounter 受理后只读)。攻击单 base_value 贯通由 _smoke_damage_types 的
|
||||
# hero_atk_val=100 全链路行值承载(同夹具既有覆盖——派生读在攻击链上工作即行值随 override 走)----
|
||||
func _smoke_hero_atk_derived() -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/战斗结算.md:360 (英雄攻击派生组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/战斗结算.md:360 (英雄攻击派生组)
|
||||
var rng_a = load("res://core/rng_service.gd").new()
|
||||
rng_a.set_master_seed(4242)
|
||||
var s_a = load("res://core/battle_orchestrator.gd").new(_db, rng_a)
|
||||
@@ -67,7 +67,7 @@ func _smoke_hero_atk_derived() -> void:
|
||||
# → heavy 伤害 == light −1、ward == light +1(要求基线伤害 ≥ 2——地板 1 会吞 −1 位移)。
|
||||
# 承伤位移红线(卡片 §8 L3):全局位移只允许 ±1 量级——非此量级 = 矩阵消费面有未盘点的第二落点(停机)。
|
||||
func _smoke_enemy_attack_type() -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/敌人系统.md:295 (敌攻击类型行集组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/敌人系统.md:295 (敌攻击类型行集组)
|
||||
print("--- 敌方攻击类型轴(迭代 24 N3)---")
|
||||
# ① 表列:20 行全填 normal(N3 全填不做差异化——Q9)
|
||||
var at_ok := true
|
||||
@@ -126,7 +126,7 @@ func _smoke_enemy_attack_type() -> void:
|
||||
# ---- 护甲等级掷点·行为面(折扣档分布 / nat20 暴击 / 伤害下限 / 棋盘来源必中)----
|
||||
|
||||
func _smoke_armor_roll() -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/增益状态.md:254 (护甲掷点与叠层合并组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/增益状态.md:254 (护甲掷点与叠层合并组)
|
||||
print("--- 护甲等级掷点(查档纯函数 + 暴击 + 伤害下限 + AC12 分布 + 棋盘必中)---")
|
||||
# ① 查档纯函数:全掷点域逐位对照(AC10 / AC12)+ EV 锚(AC10 期望减免 21.25% —— registry 参考数)
|
||||
var expect_ac10 := {100: 11, 75: 3, 50: 4, 25: 2} # 掷 10-20 全额 / 7-9 / 3-6 / 1-2
|
||||
@@ -345,7 +345,7 @@ func _auto_play_round(settlement, out: Dictionary) -> void:
|
||||
# 基线构成:基线 100——hero 攻击类型=normal(锻造工坊配置默认值,
|
||||
# hero.tres 无两轴字段)对轻甲中性 0 修正(目标 = L1 地精轻甲;AC 0 恒全额非暴击种子)。
|
||||
func _smoke_pool_wiring() -> void:
|
||||
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
print("--- 主角乘区接线 + 硬化折半方向 ---")
|
||||
var base_row := _hero_attack_row_with({}, 100, 20260918)
|
||||
var up_row := _hero_attack_row_with({"effects": ["attack_up"]}, 100, 20260918)
|
||||
@@ -387,7 +387,7 @@ func _hero_attack_row_with(opts: Dictionary, hero_atk_val: int, seed_val: int) -
|
||||
# ---- 攻击类型 × 护甲克制 + 标错惩罚冒烟(护甲快照/克制矩阵/充能/反噬三选一)----
|
||||
|
||||
func _smoke_damage_types() -> void:
|
||||
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
print("--- 攻击类型 × 护甲克制 + 标错惩罚 ---")
|
||||
# ① 护甲类型快照传播(spawn 行值 → 单位字典)
|
||||
var enemy_system = load("res://core/unit_container.gd").new(_db)
|
||||
@@ -482,7 +482,7 @@ func _find_seal_target(minesweeper, want_mine: bool) -> Dictionary:
|
||||
# d_tick = max(damage_floor, pool_damage(floor(基线×档位%), 0, 0)),档位读数随行重建可归因)----
|
||||
|
||||
func _smoke_attack_chain_fresh_pool() -> void:
|
||||
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
print("--- 战斗结算·fresh 池攻击链回归(Bug#2·掷点口径)---")
|
||||
var rng = load("res://core/rng_service.gd").new()
|
||||
rng.set_master_seed(20260913)
|
||||
|
||||
@@ -22,7 +22,7 @@ var _db
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/战斗结算.md:360 (轮内流与技能池宿主——装载组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/战斗结算.md:360 (轮内流与技能池宿主——装载组)
|
||||
_db = ConfigDBScript.new()
|
||||
# 表加载失败不中止(承 G2/G3/G4/G5 同款:表损坏的红落在具体断言上——
|
||||
# 诊断在此展开,后续断言按坏表实况逐条验红;collect_only=true 下成员照常赋值可评)
|
||||
@@ -42,13 +42,14 @@ func _init() -> void:
|
||||
_smoke_counter_buckets()
|
||||
_smoke_extended_rows()
|
||||
_smoke_enemy_buff_targets_self()
|
||||
_check_defensive_stance_values()
|
||||
quit(Kit.finish("round_flow_skill_pool_test"))
|
||||
|
||||
|
||||
# ---- 完整战斗回放(自动玩家,确定性)——回放断言:同种子两遍步骤流逐行相同 ----
|
||||
|
||||
func _smoke_full_battle(L: int, D: int, label: String) -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/战斗结算.md:362 (全战斗回放——五段全序组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/战斗结算.md:362 (全战斗回放——五段全序组)
|
||||
print("--- 完整战斗回放:%s ---" % label)
|
||||
var run1 = _run_battle(L, D)
|
||||
var run2 = _run_battle(L, D)
|
||||
@@ -83,7 +84,7 @@ func _smoke_full_battle(L: int, D: int, label: String) -> void:
|
||||
|
||||
|
||||
func _run_battle(L: int, D: int, axes: Dictionary = {}) -> Dictionary:
|
||||
# oracle: design/gdd/systems/战斗循环/战斗结算.md:362 (战斗驱动组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/战斗结算.md:362 (战斗驱动组)
|
||||
# 每遍全新 RngService(同 master 种子)——回放断言的隔离前提(地图战斗资源 AC"两套全新 RngService+地图战斗资源"同款)
|
||||
var rng = load("res://core/rng_service.gd").new()
|
||||
rng.set_master_seed(42)
|
||||
@@ -148,7 +149,7 @@ func _auto_play_round(settlement, out: Dictionary) -> void:
|
||||
# ---- 行动经济·轮内刷新冒烟(方案 A:Active 相位当场换图继续挖)----
|
||||
|
||||
func _smoke_active_refresh() -> void:
|
||||
# oracle: design/gdd/systems/排雷循环/行动经济.md:262 (停手转存储备与轮内刷新组)
|
||||
# 判据出处: design/gdd/systems/排雷循环/行动经济.md:262 (停手转存储备与轮内刷新组)
|
||||
print("--- 行动经济·轮内刷新(方案 A)---")
|
||||
var rng = load("res://core/rng_service.gd").new()
|
||||
rng.set_master_seed(20260912)
|
||||
@@ -255,7 +256,7 @@ func _active_refresh_replay() -> Array:
|
||||
# ---- 遭遇随机化冒烟(难度曲线与Boss Core Rule 8:区间骰/encounter 流/可回放/空表定值路径)----
|
||||
|
||||
func _smoke_encounter_roll() -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/敌人系统.md:296 (遭遇区间骰——摇号确定性组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/敌人系统.md:296 (遭遇区间骰——摇号确定性组)
|
||||
print("--- 遭遇随机化(unit_count 区间骰)---")
|
||||
var counts: Array = []
|
||||
for i in range(20):
|
||||
@@ -390,7 +391,7 @@ func _encounter_replay(seed_val: int) -> Array:
|
||||
|
||||
# 五段全序断言(战斗结算 验收"五段全序"+ 行型封闭 10 类):逐轮行相位序 = 授予 → 增益/减益 → 战斗 → 衰减 → 判定。
|
||||
func _smoke_settlement_segments() -> void:
|
||||
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
print("--- 五段全序 + 行型封闭 ---")
|
||||
Kit.check("segment_order 五段冻结在位(表值 = 拍板序恒等)",
|
||||
Array(_db.combat.settlement.segment_order) == SettlementTable.FROZEN_SEGMENT_ORDER
|
||||
@@ -509,7 +510,7 @@ func _smoke_skill_pool() -> void:
|
||||
|
||||
# ① 数据面:技能表行集 + 普攻锁死 + 建议池行值 + 空池不可能(结构断言)
|
||||
func _smoke_skill_pool_data() -> void:
|
||||
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
Kit.check("技能表十六行(普攻/突刺/蓄力/重击/硬化 + 撕咬/毒爆/蛛网束缚 + 大地脉动 + 震地怒击/遏毒/岩崩 + 毒吐/盾击——前三批 + Boss 半血解锁技三行〔迭代14④ T5〕+ 普通敌错峰两行〔迭代 24 N2〕+ 触发条件批示范两行〔孤注守势/趁隙偷袭〕)", _db.combat.skill.rows.size() == 16)
|
||||
var basic: Dictionary = _db.combat.skill.row_by_id("attack")
|
||||
Kit.check("普攻行:倍率恒 1 · 冷却恒 0(域校验锁死——池恒非空的结构前提)",
|
||||
@@ -726,7 +727,7 @@ func _fresh_rng(seed_val: int):
|
||||
# 单敌轨迹不独立复放——轨迹取数与其它流交错,无法脱离全序重建;
|
||||
# 同种子全战斗逐位复现由 ② 全对拍承载,单点定位归事件日志。
|
||||
func _smoke_skill_pool_replay_independence() -> void:
|
||||
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
var seed_val := -1
|
||||
for s in range(1, 60):
|
||||
var seqs := _record_pool_trajectories(s, 1)
|
||||
@@ -780,7 +781,7 @@ func _record_pool_trajectories(seed_val: int, rounds: int) -> Dictionary:
|
||||
# 2026-10-28 重锚 [0.82, 1.02](照酋长先例 ±0.10 绝对幅度,中值取理论稳态 0.92;旧带 [1.00,1.20] 系触发器前
|
||||
# 唯一特殊技=突刺的池结构下登记,随池构成换代作废;撤销证明=带宽回退旧值本断言复红)→ 宽带登记。
|
||||
func _smoke_skill_pool_ev_anchor() -> void:
|
||||
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
var bands := {"goblin": [0.82, 1.02], "bugbear": [0.97, 1.03], "ogre_chief": [1.10, 1.30]} # 地精重锚 2026-10-28(触发条件批孤注守势入池稀释,无情境口径实测 0.9148;EV 锚注 enemy.tres)· 酋长重锚(震地怒击半血解锁入池,含闸长期速率上移:实测 1.1948;EV 锚注 enemy.tres)
|
||||
var species := ["goblin", "bugbear", "ogre_chief"]
|
||||
var rng = load("res://core/rng_service.gd").new()
|
||||
@@ -836,7 +837,7 @@ func _smoke_skill_pool_ev_anchor() -> void:
|
||||
|
||||
# ⑤ 死亡打断作废(摇定后、兑现前死亡 → 本拍技能作废、不动手、不进账)
|
||||
func _smoke_skill_pool_death_void() -> void:
|
||||
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
var void_seed := -1
|
||||
for s in range(1, 400):
|
||||
var rg = load("res://core/rng_service.gd").new()
|
||||
@@ -876,7 +877,7 @@ func _smoke_skill_pool_death_void() -> void:
|
||||
|
||||
# ⑥ 加载校验拒绝(单表域校验 + 跨表池构成纪律)
|
||||
func _smoke_skill_pool_validation_reject() -> void:
|
||||
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
var skill_table = load("res://data/schemas/skill_table.gd").new()
|
||||
var enemy_table = load("res://data/schemas/enemy_table.gd").new()
|
||||
enemy_table.rows.assign([{"enemy_id": "x", "display_name": "x", "pool": [{"skill_id": "ghost", "weight": 1}]}])
|
||||
@@ -935,7 +936,7 @@ func _has_err_category(errs: Array, category: String) -> bool:
|
||||
# 停手轮驱动 (2,9)——零挖掘零封印 → 零池,伤害行可逐拍精确复算;种子 hunt 至少各命一粒毒爆伤害行
|
||||
# 与蛛网束缚施放行;毒分量随开战掷定的攻击基线缩放(atk 区间 2–4 → rider 1–3)。
|
||||
func _smoke_debuff_rider() -> void:
|
||||
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
var venom_seed := -1
|
||||
var venom_check_ok := true
|
||||
var venom_rows_seen := 0
|
||||
@@ -1006,7 +1007,7 @@ func _smoke_debuff_rider() -> void:
|
||||
# ---- 天赋树六键行为冒烟(V10:开局姿态窗/危险直觉限次/财富之道聚散/守侧失误补偿;
|
||||
# 引擎直构 + 编排授予段直注(_grant_seg._judge_seal=结算授予段封印判定单口))----
|
||||
func _smoke_counter_buckets() -> void:
|
||||
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
print("--- 效果引擎无状态化四桶正向验收(卡② §9 V1/V6)---")
|
||||
# V1——adventure 桶跨战持久:同一宿主字典下两战各真实 request_dig 一次,
|
||||
# 断付费计数经 ctx 就地写入宿主字典并跨战累积(副本搬运态:读不到上一战值→第 2 战仍 1,此断言会红)。
|
||||
@@ -1069,7 +1070,7 @@ func _smoke_counter_buckets() -> void:
|
||||
# V6 形态先例同源:直入真实管线缝(_judge_seal 直调同款口径)。
|
||||
|
||||
func _smoke_extended_rows() -> void:
|
||||
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
print("--- 扩展行收敛:fx_reveal 兑付/落空 ---")
|
||||
var RigS = load("res://core/battle_orchestrator.gd") # 局部装配别名(_smoke_counter_buckets 同款——跨函数不共享局部量)
|
||||
var enc_fx = {"level_index": 1, "node_depth": 0, "hero_hp": 40, "hero_atk": 8, "hero_max_hp": 40,
|
||||
@@ -1115,7 +1116,7 @@ func _smoke_extended_rows() -> void:
|
||||
# 蓄力表值 2 拍衰减后剩 1 拍仍在池)→ 逐敌读敌方输出池。判据=施放者与被他敌的池读数差。
|
||||
|
||||
func _smoke_enemy_buff_targets_self() -> void:
|
||||
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
var emp_mag: int = int(_db.combat.status_effects.row_by_id("empower").magnitude)
|
||||
var sc_script = load("res://core/status_container.gd")
|
||||
var found_seed := -1
|
||||
@@ -1156,3 +1157,69 @@ func _smoke_enemy_buff_targets_self() -> void:
|
||||
break
|
||||
Kit.check("蓄力施放种子可达(恰一次 cast=charge,seed=%d)" % found_seed, found_seed > 0)
|
||||
Kit.check("敌方增益技自体落点:施放者 output 池 = 蓄力幅度(%d,实测 %d)、他敌恒 0〔个体化批 V5〕" % [emp_mag, carrier_pool], found_ok)
|
||||
|
||||
|
||||
# ---- G6 状态行值断言(迭代 55「QA覆盖余量收口批」T6/V9——defensive_stance 生产行:减伤/持续/叠层)----
|
||||
# 真实入口=战斗结算编排链(技能施放):begin_battle → 压低施法者 HP 触发 desperate_guard
|
||||
#(self_hp_pct lte 35 非 latched)→ stop 推进结算 → 施放行 duration 与施法者个体状态池读数。
|
||||
# 种子狩猎沿 _smoke_enemy_buff_targets_self 同款;叠层行为=容器级再授(roll_multiplier 先例形态)。
|
||||
|
||||
func _check_defensive_stance_values() -> void:
|
||||
# 判据出处: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
var row: Dictionary = _db.combat.status_effects.row_by_id("defensive_stance")
|
||||
Kit.check("defensive_stance 生产行值:magnitude=-25 percent/duration_settlements=2/accumulate max_stack=1〔QA余 V9〕",
|
||||
int(row.get("magnitude", 0)) == -25 and String(row.get("unit", "")) == "percent"
|
||||
and int(row.get("duration_settlements", 0)) == 2 and String(row.get("stack_policy", "")) == "accumulate"
|
||||
and int(row.get("max_stack", 0)) == 1)
|
||||
var sc_script = load("res://core/status_container.gd")
|
||||
var found_seed := -1
|
||||
var cast_dur := -1
|
||||
var eff_val := 999
|
||||
var keep_settlement = null
|
||||
var keep_key := ""
|
||||
for s in range(1, 401):
|
||||
var rg = load("res://core/rng_service.gd").new()
|
||||
rg.set_master_seed(s)
|
||||
var settlement = load("res://core/battle_orchestrator.gd").new(_db, rg)
|
||||
var rows: Array = []
|
||||
settlement.step_row.connect(func(r): rows.append(r))
|
||||
if not settlement.begin_battle({"level_index": 1, "node_depth": 3, "hero_hp": int(_db.combat.hero.max_hp)}):
|
||||
continue
|
||||
for turn in range(4):
|
||||
if settlement.battle_result != "":
|
||||
break
|
||||
for u in settlement._enemy_system.all_enemies():
|
||||
u.hp = mini(int(u.hp), maxi(1, int(u.max_hp) * 35 / 100)) # 测试通道:压至触发阈内(弱形式登记)
|
||||
settlement.minesweeper.settle_batch([{"action": "stop"}])
|
||||
if settlement.battle_result != "":
|
||||
break
|
||||
var cast_seen := false
|
||||
for r in rows:
|
||||
if String(r.get("type", "")) == "cast" and String(r.get("skill_id", "")) == "desperate_guard":
|
||||
cast_seen = true
|
||||
cast_dur = int(r.get("duration", -1))
|
||||
if cast_seen:
|
||||
for u in settlement._enemy_system.all_enemies():
|
||||
var okey: String = sc_script.owner_key_for(u)
|
||||
for eff in settlement._status_effects.query_active(okey):
|
||||
if String(eff.get("effect_id", "")) == "defensive_stance":
|
||||
found_seed = s
|
||||
eff_val = int(eff.get("effective_value", 999))
|
||||
keep_settlement = settlement
|
||||
keep_key = okey
|
||||
if found_seed > 0:
|
||||
break
|
||||
if found_seed > 0:
|
||||
break
|
||||
Kit.check("种子狩猎命中(真实链施放 desperate_guard——401 种子内)〔QA余 V9〕", found_seed > 0)
|
||||
Kit.check("减伤==生产行值:施法者个体池 effective_value==-25(L×m·max_stack1 单层)〔QA余 V9〕", eff_val == -25)
|
||||
Kit.check("持续==生产行值:施放行 duration==2(buff_duration 表值)〔QA余 V9〕", cast_dur == 2)
|
||||
# 叠层行为:max_stack=1 accumulate 下容器级再授(生产 dur=行值 2)→ effective 仍单层 -25 不叠加
|
||||
if keep_settlement != null:
|
||||
var src := {"source_class": "enemy_action", "origin_cell": Vector2i(-1, -1), "round_index": 1}
|
||||
Kit.check("叠层再授受理", bool(keep_settlement._status_effects.apply_effect("defensive_stance", keep_key, src)))
|
||||
var eff2 := 999
|
||||
for eff in keep_settlement._status_effects.query_active(keep_key):
|
||||
if String(eff.get("effect_id", "")) == "defensive_stance":
|
||||
eff2 = int(eff.get("effective_value", 999))
|
||||
Kit.check("叠层行为:max_stack=1 再授后 effective_value 仍==-25(不翻倍)〔QA余 V9〕", eff2 == -25)
|
||||
@@ -22,7 +22,7 @@ var _db
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/gdd/systems/排雷循环/排雷玩法.md:231 (封印与极性宿主——装载组)
|
||||
# 判据出处: design/gdd/systems/排雷循环/排雷玩法.md:231 (封印与极性宿主——装载组)
|
||||
_db = ConfigDBScript.new()
|
||||
# 表加载失败不中止(承 G2/G3/G4 同款:表损坏的红落在具体断言上——
|
||||
# 诊断在此展开,后续断言按坏表实况逐条验红;collect_only=true 下成员照常赋值可评)
|
||||
@@ -40,13 +40,15 @@ func _init() -> void:
|
||||
_smoke_seal_irreversible_quadrants()
|
||||
_smoke_negative_dictionary()
|
||||
_smoke_resource_surface()
|
||||
_check_enemy_alert_production_row()
|
||||
_check_ap_drain_production_row()
|
||||
quit(Kit.finish("seal_polarity_test"))
|
||||
|
||||
|
||||
# ---- 末点 AP 封印轮末结算(playtest 缺陷②回归:末点 AP 花在封印必须终结轮次进入下一轮)----
|
||||
|
||||
func _smoke_seal_ap_exhausted() -> void:
|
||||
# oracle: design/gdd/systems/排雷循环/行动经济.md:260 (末点 AP 耗尽即收轮组)
|
||||
# 判据出处: design/gdd/systems/排雷循环/行动经济.md:260 (末点 AP 耗尽即收轮组)
|
||||
print("--- 排雷玩法·封印动作耗尽末点 AP 回归 ---")
|
||||
var rng = load("res://core/rng_service.gd").new()
|
||||
rng.set_master_seed(20260911)
|
||||
@@ -324,8 +326,8 @@ func _rows_at(rows: Array, source_class: String, cell) -> Array:
|
||||
|
||||
|
||||
func _smoke_clear_reward_chain() -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/地图战斗资源.md:235 (清图奖励链——清图槽对象身份组)
|
||||
# oracle: design/gdd/systems/战斗循环/地图战斗资源.md:235 (清图奖励链——清图槽对象身份组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/地图战斗资源.md:235 (清图奖励链——清图槽对象身份组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/地图战斗资源.md:235 (清图奖励链——清图槽对象身份组)
|
||||
print("--- 清图奖励·双份兑付链(完美封印 / 零兑付 / 第二份被拒 / 高光记实)---")
|
||||
# ① 真实流完美封印:标中一面真雷封印 + 零误标 + 清全图 → grant_clear 身份(clear_blast)双份真实兑付
|
||||
var rows: Array = []
|
||||
@@ -354,8 +356,8 @@ func _smoke_clear_reward_chain() -> void:
|
||||
# 弱形式登记:正常战斗流下"授段时刻无存活敌"不可达——敌全灭当轮判定段即判胜并终止战斗,而清图批在判定段之前,
|
||||
# 恒有存活敌;故本组直调编排私有面构造状态,只断言"路径存在 + 行为符合契约"(构造手法见各行注释)。
|
||||
func _smoke_clear_reward_zero_paths() -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/地图战斗资源.md:240 (无存活敌群伤记「奖励未生效」组)
|
||||
# oracle: design/gdd/systems/战斗循环/地图战斗资源.md:240 (无存活敌群伤记「奖励未生效」组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/地图战斗资源.md:240 (无存活敌群伤记「奖励未生效」组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/地图战斗资源.md:240 (无存活敌群伤记「奖励未生效」组)
|
||||
# ②-a 首份零兑付判定(无存活敌)→ first_ok=false + 未生效行
|
||||
var rng = load("res://core/rng_service.gd").new()
|
||||
rng.set_master_seed(20260915)
|
||||
@@ -410,8 +412,8 @@ func _smoke_clear_reward_zero_paths() -> void:
|
||||
|
||||
# 轮内刷新 provenance 守卫:刷新换来的图同轮清图不发清图奖励(_board_from_midround_refresh 守卫)
|
||||
func _smoke_refresh_provenance_guard() -> void:
|
||||
# oracle: design/gdd/systems/排雷循环/排雷玩法.md:237 (换图重置:旧封印印记先判定后消失组)
|
||||
# oracle: design/gdd/systems/排雷循环/排雷玩法.md:237 (换图重置:旧封印印记先判定后消失组)
|
||||
# 判据出处: design/gdd/systems/排雷循环/排雷玩法.md:237 (换图重置:旧封印印记先判定后消失组)
|
||||
# 判据出处: design/gdd/systems/排雷循环/排雷玩法.md:237 (换图重置:旧封印印记先判定后消失组)
|
||||
print("--- 轮内刷新 provenance 守卫(刷新图清图不发清图奖励)---")
|
||||
var rows: Array = []
|
||||
var run := _run_full_clear(20260916, true, rows)
|
||||
@@ -438,8 +440,8 @@ func _smoke_refresh_provenance_guard() -> void:
|
||||
|
||||
|
||||
func _smoke_polarity_routing() -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/战斗结算.md:366 (极性路由:正向只兑正向、负向只兑负向组)
|
||||
# oracle: design/gdd/systems/战斗循环/战斗结算.md:366 (极性路由:正向只兑正向、负向只兑负向组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/战斗结算.md:366 (极性路由:正向只兑正向、负向只兑负向组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/战斗结算.md:366 (极性路由:正向只兑正向、负向只兑负向组)
|
||||
print("--- 极性路由(挖安全格正向 / 挖危险格负向 / 标中正向 / 标空负向)---")
|
||||
# 战斗随机值面随全局序漂移——构造种子按行为面扫描(谓词见 _polarity_probe_accepts)
|
||||
var probe := {}
|
||||
@@ -577,8 +579,8 @@ func _smoke_polarity_routing() -> void:
|
||||
# 场景 B(②):标危险格 → 直接挖开 → 收轮。
|
||||
# 场景 C(①③):标安全格 + 标危险格 → 不挖收轮 → 标空/标中判定照常。
|
||||
func _smoke_seal_irreversible_quadrants() -> void:
|
||||
# oracle: design/gdd/systems/排雷循环/排雷玩法.md:231 (封印不可逆四情况组)
|
||||
# oracle: design/gdd/systems/排雷循环/排雷玩法.md:231 (封印不可逆四情况组)
|
||||
# 判据出处: design/gdd/systems/排雷循环/排雷玩法.md:231 (封印不可逆四情况组)
|
||||
# 判据出处: design/gdd/systems/排雷循环/排雷玩法.md:231 (封印不可逆四情况组)
|
||||
print("--- 封印不可逆四情况(禁撤封印 + 挖封印格兑付改判)---")
|
||||
# 场景 A:安全格直挖(情况④)+ seal_pull/重复封印拒绝面
|
||||
var rng_a = load("res://core/rng_service.gd").new()
|
||||
@@ -687,8 +689,8 @@ func _smoke_seal_irreversible_quadrants() -> void:
|
||||
# 负向字典逐型直验(类型化兑付行协议):找真实板上各型非封锁格,直调 grant_routed 负向路由(att 归因束形态),
|
||||
# 断言每型的兑付形态(enrage/off_guard/sapped 效果行、heal 直伤行、blast 警觉行、action 行动点排队)。
|
||||
func _smoke_negative_dictionary() -> void:
|
||||
# oracle: design/gdd/systems/排雷循环/行动经济.md:256 (下轮行动点修正排队与兑现组)
|
||||
# oracle: design/gdd/systems/排雷循环/行动经济.md:256 (下轮行动点修正排队与兑现组)
|
||||
# 判据出处: design/gdd/systems/排雷循环/行动经济.md:256 (下轮行动点修正排队与兑现组)
|
||||
# 判据出处: design/gdd/systems/排雷循环/行动经济.md:256 (下轮行动点修正排队与兑现组)
|
||||
print("--- 负向字典逐型直验(sapped 在位 / heal 直伤 / blast 警觉 / action 行动点)---")
|
||||
# 数据面:sapped 行在位(状态表七行 + effect_pool_map 映射在位)
|
||||
var sapped: Dictionary = _db.combat.status_effects.row_by_id("sapped")
|
||||
@@ -782,8 +784,8 @@ func _smoke_negative_dictionary() -> void:
|
||||
# 资源面断言:chain/dig 事件恒携带 resource=true(危险格同权);initial 恒 false(封锁格);
|
||||
# 封锁格查询 = null、危险格未开恒可查。
|
||||
func _smoke_resource_surface() -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/地图战斗资源.md:235 (资源面只读查询组)
|
||||
# oracle: design/gdd/systems/战斗循环/地图战斗资源.md:235 (资源面只读查询组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/地图战斗资源.md:235 (资源面只读查询组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/地图战斗资源.md:235 (资源面只读查询组)
|
||||
print("--- 格面资源资格面(危险格携带 / 封锁格无资源)---")
|
||||
var run1 = _run_battle(1, 0)
|
||||
var run2 = _run_battle(2, 0)
|
||||
@@ -827,3 +829,44 @@ func _smoke_resource_surface() -> void:
|
||||
|
||||
|
||||
# 五段全序断言(战斗结算 验收"五段全序"+ 行型封闭 10 类):逐轮行相位序 = 授予 → 增益/减益 → 战斗 → 衰减 → 判定。
|
||||
|
||||
|
||||
# ---- G8 reward 行读值改断(迭代 55「QA覆盖余量收口批」T8/V12——生产行驱动兑付链,替代内联合成行主证)----
|
||||
# 现役断言(上方 action 型负向段)为内联合成行——行值变更零失败;本组读生产 reward 行对象走同一兑付链,
|
||||
# 断言真值依赖生产行值(行值变更即红)。合成行旧断言保留并存(零删除)。
|
||||
|
||||
func _check_enemy_alert_production_row() -> void:
|
||||
# 判据出处: design/engineering/test-conventions.md:133 (真实入口断言——生产行值驱动)
|
||||
var row: Dictionary = _db.run.reward.row_by_id("enemy_alert")
|
||||
Kit.check("enemy_alert 生产行值:blast 负向 effect=alert power0 weight0(不入池警觉行)〔QA余 V12〕",
|
||||
String(row.get("cell_type", "")) == "blast" and String(row.get("polarity", "")) == "negative"
|
||||
and String(row.get("effect_id", "")) == "alert" and int(row.get("power", -1)) == 0
|
||||
and int(row.get("weight", -1)) == 0)
|
||||
var rg = load("res://core/rng_service.gd").new()
|
||||
rg.set_master_seed(20261108)
|
||||
var settlement = load("res://core/battle_orchestrator.gd").new(_db, rg)
|
||||
Kit.check("警觉生产行驱动:战斗启动", bool(settlement.begin_battle({"level_index": 1, "node_depth": 3, "hero_hp": int(_db.combat.hero.max_hp)})))
|
||||
settlement.reward_grant().grant_routed(row, String(row.get("polarity", "negative")),
|
||||
{"source_class": "mine_hit", "cell": Vector2i(0, 0), "ri": 1})
|
||||
var alert_ok := false
|
||||
for eff_a in settlement._status_effects.query_active_side("enemy"):
|
||||
if String(eff_a.effect_id) == String(row.get("effect_id", "")):
|
||||
alert_ok = true
|
||||
Kit.check("警觉兑付随生产行:effect_id=alert 状态在敌侧池(行 effect_id 变更即红)〔QA余 V12〕", alert_ok)
|
||||
|
||||
|
||||
func _check_ap_drain_production_row() -> void:
|
||||
# 判据出处: design/engineering/test-conventions.md:133 (真实入口断言——生产行值驱动)
|
||||
var row: Dictionary = _db.run.reward.row_by_id("ap_drain")
|
||||
Kit.check("ap_drain 生产行值:action 负向 power=1 weight0〔QA余 V12〕",
|
||||
String(row.get("cell_type", "")) == "action" and String(row.get("polarity", "")) == "negative"
|
||||
and int(row.get("power", -1)) == 1 and int(row.get("weight", -1)) == 0)
|
||||
var rg = load("res://core/rng_service.gd").new()
|
||||
rg.set_master_seed(20261109)
|
||||
var settlement = load("res://core/battle_orchestrator.gd").new(_db, rg)
|
||||
Kit.check("迟滞生产行驱动:战斗启动", bool(settlement.begin_battle({"level_index": 1, "node_depth": 3, "hero_hp": int(_db.combat.hero.max_hp)})))
|
||||
var d0: int = settlement.action_economy.pending_ap_delta()
|
||||
settlement.reward_grant().grant_routed(row, String(row.get("polarity", "negative")),
|
||||
{"source_class": "mine_hit", "cell": Vector2i(0, 0), "ri": 1})
|
||||
Kit.check("迟滞兑付随生产行:排队增量==-row.power==-1(行 power 变更即红)〔QA余 V12〕",
|
||||
settlement.action_economy.pending_ap_delta() == d0 - int(row.get("power", 0)))
|
||||
@@ -419,7 +419,7 @@ var _db
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/战斗结算.md:362 (结算接线同种子逐项 golden 对拍组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/战斗结算.md:362 (结算接线同种子逐项 golden 对拍组)
|
||||
_db = ConfigDBScript.new()
|
||||
var load_ok: bool = _db.load_tables("res://data/tables", true)
|
||||
Kit.check("前置:表加载零 error", load_ok and _db.diagnostics_count("error") == 0)
|
||||
|
||||
@@ -13,7 +13,7 @@ const Kit := preload("res://tests/unit/test_kit.gd")
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/敌人系统.md:293 Boss 半血解锁技宿主(装载组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/敌人系统.md:293 Boss 半血解锁技宿主(装载组)
|
||||
var db = ConfigDBScript.new()
|
||||
var load_ok: bool = db.load_tables("res://data/tables", true)
|
||||
Kit.check("前置:表加载零 error", load_ok and db.diagnostics_count("error") == 0)
|
||||
@@ -26,7 +26,7 @@ func _init() -> void:
|
||||
|
||||
# 门谓词:卓尔 (2,9) hp 130(×mult 1.0)→ venom_curb unlock 50 → 阈值 round(130×0.5)=65
|
||||
func _boss(db, L: int, D: int, kind := "boss"):
|
||||
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
var ec = UnitContainerScript.new(db)
|
||||
var ok: bool = ec.spawn({"level_index": L, "node_depth": D, "hero_hp": int(db.combat.hero.max_hp), "row_kind": kind})
|
||||
Kit.check("前置:spawn (%d,%d) 受理" % [L, D], ok)
|
||||
@@ -45,7 +45,7 @@ func _has_sid(arr: Array, sid: String) -> bool:
|
||||
|
||||
|
||||
func _test_gate_predicate(db) -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/敌人系统.md:143 (门谓词:条件不满足不进可摇集合组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/敌人系统.md:143 (门谓词:条件不满足不进可摇集合组)
|
||||
var ec = _boss(db, 2, 9)
|
||||
var u: Dictionary = _boss_u(ec)
|
||||
var mhp: int = int(u.max_hp)
|
||||
@@ -74,7 +74,7 @@ func _test_gate_predicate(db) -> void:
|
||||
|
||||
# 闩不可逆:解锁后治疗抬回满血 → 复查零新解锁、可摇集合不回退
|
||||
func _test_latch_irreversible(db) -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/敌人系统.md:143 (闩型条件首次为真整场保持组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/敌人系统.md:143 (闩型条件首次为真整场保持组)
|
||||
var ec = _boss(db, 2, 9)
|
||||
var u: Dictionary = _boss_u(ec)
|
||||
ec.apply_damage(u, int(u.max_hp) / 2) # → 半血触发
|
||||
@@ -86,7 +86,7 @@ func _test_latch_irreversible(db) -> void:
|
||||
|
||||
# 普通行无字段逐位零扰动:地精 (1,0) 池快照零 unlock 键、过滤恒空转、可摇=全池
|
||||
func _test_plain_rows_zero_drift(db) -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/敌人系统.md:301 (普攻倍率 1 特例——零漂移组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/敌人系统.md:301 (普攻倍率 1 特例——零漂移组)
|
||||
var ec = load("res://core/unit_container.gd").new(db)
|
||||
Kit.check("零扰动前置:巨蜘蛛首遇带 spawn(池三行全无 trigger——快照键策略钉死载体)", ec.spawn({"level_index": 2, "node_depth": 1, "hero_hp": int(db.combat.hero.max_hp)}))
|
||||
var u: Dictionary = ec.all_enemies()[0]
|
||||
|
||||
@@ -9,7 +9,7 @@ const Kit := preload("res://tests/unit/test_kit.gd")
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/敌人系统.md:143 (触发条件机制 V1–V10 组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/敌人系统.md:143 (触发条件机制 V1–V10 组)
|
||||
var db = load("res://core/config_db.gd").new()
|
||||
if not db.load_tables("res://data/tables", false):
|
||||
print("[trigger_cond_test] FAIL — 数值表加载失败")
|
||||
|
||||
@@ -15,7 +15,7 @@ var _db
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/gdd/systems/排雷循环/地形变异.md:151 变异布置宿主(装载组)
|
||||
# 判据出处: design/gdd/systems/排雷循环/地形变异.md:151 变异布置宿主(装载组)
|
||||
_db = ConfigDBScript.new()
|
||||
# 表加载失败不中止(承 G2/G3/G4/G5 同款:表损坏的红落在具体断言上——
|
||||
# 诊断在此展开,后续断言按坏表实况逐条验红;collect_only=true 下成员照常赋值可评)
|
||||
@@ -28,8 +28,9 @@ func _init() -> void:
|
||||
# 套执行序沿源编排(基点 _run_core_smoke 调用序——本组套的相对序)
|
||||
_smoke_variant_placement(1)
|
||||
_smoke_variant_placement(2)
|
||||
_smoke_global_variant()
|
||||
_smoke_global_variant() # 审计 U1 修复恢复(T7 编辑回退分支误删调用——33 条在役断言复归执行)
|
||||
_test_pack_min_from_table()
|
||||
_test_l3_rockfall_minedust_rows()
|
||||
quit(Kit.finish("variant_placement_test"))
|
||||
|
||||
|
||||
@@ -54,7 +55,7 @@ class _CountVariantRng extends RngServiceCore:
|
||||
# 本段经 begin_battle 走完整初始化序后断言:门控一致性 / 布置域 / 数量公式 / 连通片上限 / 确定性。
|
||||
|
||||
func _smoke_variant_placement(L: int) -> void:
|
||||
# oracle: design/gdd/systems/排雷循环/地形变异.md:63 (战内布置段门控组)
|
||||
# 判据出处: design/gdd/systems/排雷循环/地形变异.md:63 (战内布置段门控组)
|
||||
print("--- 地形变异 布置段(L%d)---" % L)
|
||||
var rng = load("res://core/rng_service.gd").new()
|
||||
rng.set_master_seed(42)
|
||||
@@ -148,7 +149,7 @@ func _variant_dump(b) -> String:
|
||||
|
||||
|
||||
func _smoke_global_variant() -> void:
|
||||
# oracle: design/gdd/systems/排雷循环/地形变异.md:74 (全局替换制与回落组)
|
||||
# 判据出处: design/gdd/systems/排雷循环/地形变异.md:74 (全局替换制与回落组)
|
||||
print("--- 全局作用域变异(门控三维 + 全局池空兜底回落 + 资源面偏斜)---")
|
||||
# ① 跨字段互锁负样本(resource_face ⟺ scope=global 三条 + effect_value 新域 [0,50])
|
||||
var vt = load("res://data/schemas/variation_table.gd").new()
|
||||
@@ -343,7 +344,7 @@ func _errs_has_cross_field(errs: Array) -> bool:
|
||||
# 全称不可断)。可达判据=①静态(布置面零字面量下界)+②同种子改值产物互异(真实入口=begin_battle 布置段)。
|
||||
|
||||
func _test_pack_min_from_table() -> void:
|
||||
# oracle: design/gdd/systems/排雷循环/地形变异.md:63 (连通片格数 [pack_min,pack_max] 读表组)
|
||||
# 判据出处: design/gdd/systems/排雷循环/地形变异.md:63 (连通片格数 [pack_min,pack_max] 读表组)
|
||||
# ① 静态:布置面零字面量下界(randi_range(2, 退役——下界读 pack_min 表头单点)
|
||||
var src := FileAccess.get_file_as_string("res://core/board_installer.gd")
|
||||
Kit.check("布置面零字面量下界(randi_range(2, 退役、下界读表单点在位)〔架构整改 V10·静态〕",
|
||||
@@ -368,4 +369,82 @@ func _test_pack_min_from_table() -> void:
|
||||
found_diff = true
|
||||
break
|
||||
vt.pack_min = saved_min
|
||||
Kit.check("同种子 pack_min=3 vs 1 布置产物互异(下界进 randi_range——改值可观测)〔架构整改 V10〕", found_diff)
|
||||
Kit.check("同种子 pack_min=3 vs 1 布置产物互异(下界进 randi_range——改值可观测)〔架构整改 V10〕", found_diff)
|
||||
|
||||
# ---- G7 L3 变异行(迭代 55「QA覆盖余量收口批」T7/V11——rockfall/mine_dust 第三关布置与效果通道)----
|
||||
# 真实入口=变异布置段(begin_battle L3→deep_underdark 候选池→真实布置);rockfall 效果通道=
|
||||
# 授予段地形批(on_open 兑现 flat_damage);mine_dust=信息型遮蔽解析数据面(board.variant_ids×表行 effect)。
|
||||
|
||||
func _test_l3_rockfall_minedust_rows() -> void:
|
||||
# 判据出处: design/gdd/systems/排雷循环/地形变异.md:63 (L3 布置段门控与效果兑现组)
|
||||
var pool := {}
|
||||
for r in _db.combat.variation.variant_pool(3, "deep_underdark"):
|
||||
pool[String(r.variant_id)] = r
|
||||
Kit.check("L3 deep_underdark 候选池含 rockfall 行(flat_damage 3·on_open)〔QA余 V11〕",
|
||||
pool.has("rockfall") and String((pool["rockfall"] as Dictionary).get("effect", "")) == "flat_damage"
|
||||
and int((pool["rockfall"] as Dictionary).get("effect_value", -1)) == 3)
|
||||
Kit.check("L3 deep_underdark 候选池含 mine_dust 行(hide_number)〔QA余 V11〕",
|
||||
pool.has("mine_dust") and String((pool["mine_dust"] as Dictionary).get("effect", "")) == "hide_number")
|
||||
var rock_seed := -1
|
||||
var dust_seed := -1
|
||||
var rock_settlement = null
|
||||
var dust_settlement = null
|
||||
for s in range(1, 201):
|
||||
var rg = load("res://core/rng_service.gd").new()
|
||||
rg.set_master_seed(s)
|
||||
var st = load("res://core/battle_orchestrator.gd").new(_db, rg)
|
||||
if not st.begin_battle({"level_index": 3, "node_depth": 0, "hero_hp": int(_db.combat.hero.max_hp)}):
|
||||
continue
|
||||
var b = st.minesweeper.current_board()
|
||||
if b == null or String(b.board_type) != "deep_underdark":
|
||||
continue
|
||||
var ids := {}
|
||||
for c in b.variants.keys():
|
||||
for vid in (b.variants[c] as Array):
|
||||
ids[String(vid)] = true
|
||||
if ids.has("rockfall") and rock_seed < 0:
|
||||
rock_seed = s
|
||||
rock_settlement = st
|
||||
elif ids.has("mine_dust") and dust_seed < 0:
|
||||
dust_seed = s
|
||||
dust_settlement = st
|
||||
if rock_seed > 0 and dust_seed > 0:
|
||||
break
|
||||
Kit.check("rockfall 放置可达(L3 真实布置段 variants 命中——200 种子内)〔QA余 V11〕", rock_seed > 0)
|
||||
Kit.check("mine_dust 放置可达(同链)〔QA余 V11〕", dust_seed > 0)
|
||||
# rockfall 效果通道:挖开变异格 → 授予段地形批兑付 flat_damage 3(伤害行 display 落石·值 3)
|
||||
if rock_settlement != null:
|
||||
var rows: Array = []
|
||||
rock_settlement.step_row.connect(func(r): rows.append(r))
|
||||
var b2 = rock_settlement.minesweeper.current_board()
|
||||
var target := Vector2i(-1, -1)
|
||||
for c in b2.variants.keys():
|
||||
if not b2.is_mine(c):
|
||||
target = c
|
||||
break
|
||||
Kit.check("rockfall 变异格在板(可挖)", target != Vector2i(-1, -1))
|
||||
if target != Vector2i(-1, -1):
|
||||
rock_settlement.minesweeper.fx_reveal(target)
|
||||
rock_settlement.minesweeper.settle_batch([{"action": "stop"}])
|
||||
var hit_val := -1
|
||||
var hit_name := ""
|
||||
for r in rows:
|
||||
if String(r.get("type", "")) == "damage" and String(r.get("display_name", "")).contains("落石"):
|
||||
hit_val = int(r.get("value", -1))
|
||||
hit_name = String(r.get("display_name", ""))
|
||||
Kit.check("rockfall 效果通道命中:挖开即伤兑付行值==3(表行 effect_value 驱动)〔QA余 V11〕",
|
||||
hit_val == 3 and hit_name != "")
|
||||
# mine_dust 效果通道:变异格经遮蔽解析数据面命中 hide_number 行(信息型不进结算——呈现读取)
|
||||
if dust_settlement != null:
|
||||
var b3 = dust_settlement.minesweeper.current_board()
|
||||
var masked := false
|
||||
var unmasked_clean := true
|
||||
for c in b3.variants.keys():
|
||||
for vid in (b3.variants[c] as Array):
|
||||
if String(_db.combat.variation.row_by_id(String(vid)).get("effect", "")) == "hide_number":
|
||||
masked = true
|
||||
for c in b3.safe_cells():
|
||||
if (b3.variants.get(c, []) as Array).is_empty() and not (b3.variant_ids(c) as Array).is_empty():
|
||||
unmasked_clean = false
|
||||
Kit.check("mine_dust 效果通道:变异格 hide_number 行命中(遮蔽解析数据面真值)且无变异格零遮蔽〔QA余 V11〕",
|
||||
masked and unmasked_clean)
|
||||
@@ -21,7 +21,7 @@ func _init() -> void:
|
||||
# ---- 类型闸断言(V7——project.godot warning 配置在位〔源码断言〕+审计器脚本在位;
|
||||
# 审计器归零判定走 static_scan S9 闸机,此处兜配置面)----
|
||||
func _smoke_type_gate() -> void:
|
||||
# oracle: design/engineering/code-conventions.md:41 (封装边界——类型闸源码扫描组)
|
||||
# 判据出处: design/engineering/code-conventions.md:41 (封装边界——类型闸源码扫描组)
|
||||
var pj: String = FileAccess.get_file_as_string("res://project.godot")
|
||||
Kit.check("V7 类型闸 warning 配置在位(untyped_declaration=1 显形)〔迭代27 T5〕",
|
||||
pj.contains("gdscript/warnings/untyped_declaration=1"))
|
||||
@@ -32,7 +32,7 @@ func _smoke_type_gate() -> void:
|
||||
|
||||
# ---- 卫生件 grep 闸(V8——死代码/字符串 Callable/影子账残留零命中;源码扫描面)----
|
||||
func _smoke_hygiene_gates() -> void:
|
||||
# oracle: design/engineering/code-conventions.md:54 (模态覆盖层层级与登记纪律——卫生闸组)
|
||||
# 判据出处: design/engineering/code-conventions.md:54 (模态覆盖层层级与登记纪律——卫生闸组)
|
||||
var callables := 0
|
||||
for d in ["res://core", "res://presentation", "res://data"]:
|
||||
var dir := DirAccess.open(d)
|
||||
|
||||
@@ -18,7 +18,7 @@ const Kit := preload("res://tests/unit/test_kit.gd") # T6 收口:_check/汇
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
var db = ConfigDBScript.new()
|
||||
var load_ok: bool = db.load_tables("res://data/tables", true)
|
||||
Kit.check("前置:表加载零 error", load_ok and db.diagnostics_count("error") == 0)
|
||||
@@ -41,8 +41,8 @@ func _init() -> void:
|
||||
# ---- B1 兑付配方词表:五配方/三目标规则/双结账政策——常量非空且互异(封闭枚举负空间) ----
|
||||
|
||||
func _test_damage_controller_vocabularies() -> void:
|
||||
# oracle: design/engineering/architecture/architecture.md:89 (事件/事实/效果类型/算子四词表封闭——契约词表组)
|
||||
# oracle: design/engineering/architecture/architecture.md:89 (事件/事实/效果类型/算子四词表封闭——契约词表组)
|
||||
# 判据出处: design/engineering/architecture/architecture.md「模块归属·基础层」effect_engine 行 (事件/事实/效果类型/算子四词表封闭——契约词表组)
|
||||
# 判据出处: design/engineering/architecture/architecture.md「模块归属·基础层」effect_engine 行 (事件/事实/效果类型/算子四词表封闭——契约词表组)
|
||||
var kinds: Array = [
|
||||
DamageControllerScript.KIND_HERO_ATTACK, DamageControllerScript.KIND_ENEMY_SKILL,
|
||||
DamageControllerScript.KIND_BOARD_TYPED, DamageControllerScript.KIND_BOARD_BLAST,
|
||||
@@ -72,8 +72,8 @@ func _test_damage_controller_vocabularies() -> void:
|
||||
# ---- D2↔B1:掷点用途标签 ∈ RngService 封闭词表(EC12——跨系统字面量契约的成员性断言) ----
|
||||
|
||||
func _test_rng_tag_closure(db) -> void:
|
||||
# oracle: design/engineering/architecture/architecture.md:90 (流 scope 封闭词表组)
|
||||
# oracle: design/engineering/architecture/architecture.md:90 (流 scope 封闭词表组)
|
||||
# 判据出处: design/engineering/architecture/architecture.md「模块归属·基础层」RngService 行 (流 scope 封闭词表组)
|
||||
# 判据出处: design/engineering/architecture/architecture.md「模块归属·基础层」RngService 行 (流 scope 封闭词表组)
|
||||
var stream_label: String = String(db.combat.settlement.damage_roll.armor_roll_stream)
|
||||
Kit.check("settlement.armor_roll_stream ∈ RngService 封闭标签词表(EC12)",
|
||||
RngServiceScript.PURPOSE_TAGS.has(stream_label))
|
||||
@@ -87,8 +87,8 @@ func _test_rng_tag_closure(db) -> void:
|
||||
# ---- B1→D1 行协议:单据九栏缺省齐备 + 协议公共键形态 ----
|
||||
|
||||
func _test_ticket_nine_columns() -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/战斗结算.md:362 (单据九列协议组)
|
||||
# oracle: design/gdd/systems/战斗循环/战斗结算.md:362 (单据九列协议组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/战斗结算.md:362 (单据九列协议组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/战斗结算.md:362 (单据九列协议组)
|
||||
var ctrl = _bare_controller()
|
||||
var t: Dictionary = ctrl.open_ticket(DamageControllerScript.KIND_BOARD_FLAT_HERO, "mine_hit", "契约探针",
|
||||
{"channel": "棋盘兑付", "base_value": 1, "target_rule": DamageControllerScript.TARGET_HERO, "ri": 0})
|
||||
@@ -118,8 +118,8 @@ func _bare_controller():
|
||||
# ---- B4→B3:增益授予布尔受理口径(受理/拒绝二元面 + 投影一致性) ----
|
||||
|
||||
func _test_status_boolean_acceptance(db) -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/增益状态.md:250 (受理布尔面组)
|
||||
# oracle: design/gdd/systems/战斗循环/增益状态.md:250 (受理布尔面组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/增益状态.md:250 (受理布尔面组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/增益状态.md:250 (受理布尔面组)
|
||||
var effects = StatusContainerScript.new(db)
|
||||
# EC1 域闭:notify_battle_start 前授予 = false(受理面封闭)
|
||||
var any_row: Dictionary = {}
|
||||
@@ -146,8 +146,8 @@ func _test_status_boolean_acceptance(db) -> void:
|
||||
# 个体键直传字面量("e1"/"e2"/"e3"——拼写约定单点在 StatusContainer.owner_key_for,测试面模拟授予路径逐只授)
|
||||
|
||||
func _test_status_individual_isolation(db) -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/增益状态.md:251 (按个体归属组)
|
||||
# oracle: design/gdd/systems/战斗循环/增益状态.md:251 (按个体归属组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/增益状态.md:251 (按个体归属组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/增益状态.md:251 (按个体归属组)
|
||||
var effects = StatusContainerScript.new(db)
|
||||
effects.notify_battle_start()
|
||||
var src := {"source_class": "seal_hit", "origin_cell": Vector2i(-1, -1), "round_index": 0}
|
||||
@@ -169,8 +169,8 @@ func _test_status_individual_isolation(db) -> void:
|
||||
|
||||
|
||||
func _test_status_grant_all_per_unit(db) -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/增益状态.md:252 (授予即生效组)
|
||||
# oracle: design/gdd/systems/战斗循环/增益状态.md:252 (授予即生效组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/增益状态.md:252 (授予即生效组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/增益状态.md:252 (授予即生效组)
|
||||
var effects = StatusContainerScript.new(db)
|
||||
effects.notify_battle_start()
|
||||
var src := {"source_class": "mine_hit", "origin_cell": Vector2i(-1, -1), "round_index": 0}
|
||||
@@ -197,8 +197,8 @@ func _test_status_grant_all_per_unit(db) -> void:
|
||||
|
||||
|
||||
func _test_status_death_clears(db) -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/增益状态.md:250 (战斗结束清空组)
|
||||
# oracle: design/gdd/systems/战斗循环/增益状态.md:250 (战斗结束清空组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/增益状态.md:250 (战斗结束清空组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/增益状态.md:250 (战斗结束清空组)
|
||||
var effects = StatusContainerScript.new(db)
|
||||
effects.notify_battle_start()
|
||||
var src := {"source_class": "seal_hit", "origin_cell": Vector2i(-1, -1), "round_index": 0}
|
||||
@@ -212,8 +212,8 @@ func _test_status_death_clears(db) -> void:
|
||||
|
||||
|
||||
func _test_status_merge_per_unit(db) -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/增益状态.md:253 (只登记不去重组)
|
||||
# oracle: design/gdd/systems/战斗循环/增益状态.md:253 (只登记不去重组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/增益状态.md:253 (只登记不去重组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/增益状态.md:253 (只登记不去重组)
|
||||
var effects = StatusContainerScript.new(db)
|
||||
effects.notify_battle_start()
|
||||
var src := {"source_class": "enemy_action", "origin_cell": Vector2i(-1, -1), "round_index": 0}
|
||||
@@ -236,8 +236,8 @@ func _test_status_merge_per_unit(db) -> void:
|
||||
# ---- B4 结算链契约(断言补齐批——①增益状态结算链):衰减逐值/战斗结束全清/满层拒绝/独立并存/首授快照 ----
|
||||
|
||||
func _test_status_decay_and_clear(db) -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/增益状态.md:21 (先兑付后衰减组)
|
||||
# oracle: design/gdd/systems/战斗循环/增益状态.md:21 (先兑付后衰减组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/增益状态.md:21 (先兑付后衰减组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/增益状态.md:21 (先兑付后衰减组)
|
||||
var effects = StatusContainerScript.new(db)
|
||||
effects.notify_battle_start()
|
||||
var src := {"source_class": "seal_hit", "origin_cell": Vector2i(-1, -1), "round_index": 0}
|
||||
@@ -258,8 +258,8 @@ func _test_status_decay_and_clear(db) -> void:
|
||||
|
||||
|
||||
func _test_status_stack_full_reject(db) -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/增益状态.md:255 (满层拒绝原子且留痕组)
|
||||
# oracle: design/gdd/systems/战斗循环/增益状态.md:255 (满层拒绝原子且留痕组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/增益状态.md:255 (满层拒绝原子且留痕组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/增益状态.md:255 (满层拒绝原子且留痕组)
|
||||
var effects = StatusContainerScript.new(db)
|
||||
effects.notify_battle_start()
|
||||
var src := {"source_class": "seal_hit", "origin_cell": Vector2i(-1, -1), "round_index": 0}
|
||||
@@ -277,8 +277,8 @@ func _test_status_stack_full_reject(db) -> void:
|
||||
|
||||
|
||||
func _test_status_independent_branch(db) -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/增益状态.md:257 (合成不设上下限组)
|
||||
# oracle: design/gdd/systems/战斗循环/增益状态.md:257 (合成不设上下限组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/增益状态.md:257 (合成不设上下限组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/增益状态.md:257 (合成不设上下限组)
|
||||
var effects = StatusContainerScript.new(db)
|
||||
effects.notify_battle_start()
|
||||
var probe := {
|
||||
@@ -303,8 +303,8 @@ func _test_status_independent_branch(db) -> void:
|
||||
|
||||
|
||||
func _test_status_snapshot_first_grant(db) -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/增益状态.md:258 (快照抗重载组)
|
||||
# oracle: design/gdd/systems/战斗循环/增益状态.md:258 (快照抗重载组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/增益状态.md:258 (快照抗重载组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/增益状态.md:258 (快照抗重载组)
|
||||
var effects = StatusContainerScript.new(db)
|
||||
effects.notify_battle_start()
|
||||
var src_first := {"source_class": "seal_hit", "origin_cell": Vector2i(3, 4), "round_index": 1}
|
||||
|
||||
@@ -17,7 +17,7 @@ var _db
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/冒险结构.md:281 冒险结构与专长宿主(装载组)
|
||||
# 判据出处: design/gdd/systems/冒险循环/冒险结构.md:281 冒险结构与专长宿主(装载组)
|
||||
_db = ConfigDBScript.new()
|
||||
# 表加载失败不中止(承 G2/G3/G4/G5 同款:表损坏的红落在具体断言上——
|
||||
# 诊断在此展开,后续断言按坏表实况逐条验红;collect_only=true 下成员照常赋值可评)
|
||||
@@ -59,7 +59,7 @@ func _hits_kind(hits: Array, kind: String) -> Array:
|
||||
|
||||
# ② 冒险结构 API 闸(状态机受理面——重复开始拒绝 / 入口停点 / 放弃两态)
|
||||
func _smoke_adventure_api() -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/冒险结构.md:283 (冒险 API 组:开始受理/序列/回调)
|
||||
# 判据出处: design/gdd/systems/冒险循环/冒险结构.md:283 (冒险 API 组:开始受理/序列/回调)
|
||||
print("--- 冒险结构 API 闸(重复开始拒绝 · 入口停点 · 放弃两态受理)---")
|
||||
var adr = load("res://core/adventure_runner.gd").new(_db, _fresh_rng(42))
|
||||
adr.set_hero_override(900, 400) # 首战速胜档(放弃的非战斗受理要走到选路态)
|
||||
@@ -122,7 +122,7 @@ func _api_mark_node(_node: Dictionary, _info: Dictionary, seq: Array) -> void:
|
||||
# ③ 冒险验收断言(映射 冒险结构.md 验收标准——表覆盖/满配冒险成功/种子复现/大奖励/败北线/
|
||||
# 重复拒绝/GP 跨关延续/加算区同账/恢复数学/放弃)
|
||||
func _smoke_adventure() -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/冒险结构.md:284 (冒险生命周期组:推进/末关/倒下/重开/生命账)
|
||||
# 判据出处: design/gdd/systems/冒险循环/冒险结构.md:284 (冒险生命周期组:推进/末关/倒下/重开/生命账)
|
||||
print("--- 冒险结构验收(三关序列 · 大奖励 · 摘要五件事 · 账本封闭)---")
|
||||
var autoplay = load("res://sim/level_autoplay.gd")
|
||||
# ① 表与覆盖义务(序列关数 = 行集关数;节点源 = 参数档在固定种子下的生成图,
|
||||
@@ -463,7 +463,7 @@ func _smoke_adventure() -> void:
|
||||
# ---- 专长冒烟(feats 四级 · 案D 池接线 · V3 订阅面逐件三列)----
|
||||
|
||||
func _smoke_feats() -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/奇物.md:465 (奇物授予与凭证保底组)
|
||||
# 判据出处: design/gdd/systems/冒险循环/奇物.md:465 (奇物授予与凭证保底组)
|
||||
print("--- 专长重设计实装(feat_table 四级 · 专长带池接线 · 订阅面逐件)---")
|
||||
# ① 现行表牌库构成:37 件全量入池——102 数值令牌+37 relic 令牌(四级 rarity 直映射)
|
||||
Kit.check("feat_table 已加载(37 件=基础 27+迁入 2+完美保险 1+迭代 46 换图系七件——换代)", _db.crossrun.feats != null and (_db.crossrun.feats.rows as Array).size() == 37)
|
||||
@@ -577,6 +577,7 @@ func _smoke_feats() -> void:
|
||||
offered = true
|
||||
break
|
||||
Kit.check("A6 概率面存在(chance=20% 200 展示内必出现——零触发=概率面失守)", offered)
|
||||
_check_ticket_quota_exhausted(a_t) # A6-补 配额满分支(迭代 55 T3/V4——生产配额值驱动恒 false)
|
||||
a_r.stop()._pending = {"candidates": [{"kind": "relic", "tier": "rare", "feat_id": "x3"}, {"kind": "gp", "tier": "common"}], "payload": {}}
|
||||
Kit.check("relic 兑付:入持有集 + 牌库摘牌(选中踢池——池库存 %d→%d)" % [deck_relic, deck_relic - 1], a_r.stop().pick_curio("relic", "rare", "x3") and (a_r.feats_held() as Array).has("x3") and a_r.deck().pool_stock("relic") == deck_relic - 1)
|
||||
# 断点往返三条(基点 1420–1426:relics 快照/计数器键/余库逐枚还原)按主会话裁决②改判跨系统闭环宿主——
|
||||
@@ -915,3 +916,26 @@ func _fresh_rng(seed_val: int):
|
||||
func _level_active_nodes(level_index: int) -> Array:
|
||||
var gen = NodeMapGenerator.new(_fresh_rng(42))
|
||||
return (gen.generate(_db.run.level_gen_params.row_by_level_index(level_index), 42) as Dictionary).get("nodes", [])
|
||||
|
||||
|
||||
# A6-补 配额满分支(迭代 55「QA覆盖余量收口批」T3/V4):ticket_appeared 达生产配额后恒不再出现——
|
||||
# 生产 feat_ticket_quota==2 读表驱动;保底同置满使 false 只能来自配额分支(先于概率掷点)。
|
||||
# 真实入口链 = node_curio_candidates→draw_candidates("reward_node")→feat_ticket_consider。
|
||||
func _check_ticket_quota_exhausted(a_t) -> void:
|
||||
# 判据出处: design/engineering/test-conventions.md:133 (真实入口断言——配额满分支售满后恒 false)
|
||||
Kit.check("生产配额值在役:feat_ticket_quota==2〔QA余 V4〕", int(a_t.db.run.adventure.feat_ticket_quota) == 2)
|
||||
a_t.stop()._deck.ticket_appeared = int(a_t.db.run.adventure.feat_ticket_quota) # 配额满注入(A6 同款测试通道直填)
|
||||
a_t.stop()._deck.ticket_pity = int(a_t.db.run.adventure.feat_ticket_pity_step) # 保底同置满——false 唯一来源=配额分支
|
||||
var rg_q: RandomNumberGenerator = a_t.rng.open_stream("reward_pick")
|
||||
Kit.check("配额满后 consider 恒 false(保底已满仍拒——分支先于概率掷点)〔QA余 V4〕", not a_t.deck().feat_ticket_consider(rg_q))
|
||||
for i in 3:
|
||||
var cands: Array = (a_t.stop().node_curio_candidates().get("candidates", []) as Array)
|
||||
var has_ticket := false
|
||||
for cd in cands:
|
||||
if String((cd as Dictionary).get("kind", "")) == "feat_ticket":
|
||||
has_ticket = true
|
||||
Kit.check("配额满后第 %d 次停点候选零专长凭证(注入链零凭证)〔QA余 V4〕" % (i + 1), not has_ticket)
|
||||
Kit.check("配额满分支零记账(ticket_appeared 停在配额值——不随展示递增)〔QA余 V4〕",
|
||||
int(a_t.stop()._deck.ticket_appeared) == int(a_t.db.run.adventure.feat_ticket_quota))
|
||||
a_t.stop()._deck.ticket_appeared = 0 # 夹具还原(后续段零污染)
|
||||
a_t.stop()._deck.ticket_pity = 0
|
||||
@@ -16,7 +16,7 @@ func _init() -> void:
|
||||
|
||||
|
||||
func _smoke_zero_chain_rule() -> void:
|
||||
# oracle: design/gdd/systems/排雷循环/排雷玩法.md:230 (0 格连锁传播闭包——模拟玩家算子组)
|
||||
# 判据出处: design/gdd/systems/排雷循环/排雷玩法.md:230 (0 格连锁传播闭包——模拟玩家算子组)
|
||||
var db = load("res://core/config_db.gd").new()
|
||||
var load_ok: bool = db.load_tables("res://data/tables", true) and db.diagnostics_count("error") == 0
|
||||
Kit.check("前置:表加载零 error", load_ok)
|
||||
|
||||
@@ -55,7 +55,7 @@ func _init() -> void:
|
||||
|
||||
# ① 缺省两参(真编排器):同种子两跑整轮驱动逐位(确定性自证——动作序列快照经 cell_revealed 事件流)
|
||||
func _smoke_drive_round_contract() -> void:
|
||||
# oracle: design/engineering/architecture/architecture.md:144 (效果端口件:收事件交结算取回命中批——驱动循环契约组)
|
||||
# 判据出处: design/engineering/architecture/architecture.md「模块归属·功能层」battle_orchestrator 行(域文件集含 EffectPort) (效果端口件:收事件交结算取回命中批——驱动循环契约组)
|
||||
var db = load("res://core/config_db.gd").new()
|
||||
var load_ok: bool = db.load_tables("res://data/tables", true) and db.diagnostics_count("error") == 0
|
||||
Kit.check("前置:表加载零 error", load_ok)
|
||||
@@ -81,7 +81,7 @@ func _smoke_drive_round_contract() -> void:
|
||||
|
||||
# ② provider 形态:策略回调产出 dig/seal_place/refresh 各一——提交与日志回调四元组在案
|
||||
func _provider_shapes() -> void:
|
||||
# oracle: design/engineering/architecture/architecture.md:144 (端口应答束形状组)
|
||||
# 判据出处: design/engineering/architecture/architecture.md「模块归属·功能层」battle_orchestrator 行(域文件集含 EffectPort) (端口应答束形状组)
|
||||
var st := _StubSettlement.new()
|
||||
var script := [ {"kind": "dig", "cell": Vector2i(1, 1)}, {"kind": "seal", "cell": Vector2i(2, 2)}, {"kind": "refresh"}, {"kind": "stop"} ]
|
||||
var logs: Array = []
|
||||
@@ -102,7 +102,7 @@ func _provider_shapes() -> void:
|
||||
|
||||
# ③ max_rejects:策略恒返被拒动作——第 3 拒后停手出环(循环次数=3)
|
||||
func _max_rejects() -> void:
|
||||
# oracle: design/engineering/architecture/architecture.md:144 (拒绝通道组)
|
||||
# 判据出处: design/engineering/architecture/architecture.md「模块归属·功能层」battle_orchestrator 行(域文件集含 EffectPort) (拒绝通道组)
|
||||
var st := _StubSettlement.new()
|
||||
st.minesweeper.accept = false
|
||||
st.minesweeper.reject_cells = {Vector2i(9, 9): true}
|
||||
@@ -122,7 +122,7 @@ func _max_rejects() -> void:
|
||||
|
||||
# ④ 合步 next:move 后相位变更即断弃 dig(桩 on_submit 钩子把 phase 推离 Active)
|
||||
func _combined_step_next() -> void:
|
||||
# oracle: design/engineering/architecture/architecture.md:144 (合并步进组)
|
||||
# 判据出处: design/engineering/architecture/architecture.md「模块归属·功能层」battle_orchestrator 行(域文件集含 EffectPort) (合并步进组)
|
||||
var st := _StubSettlement.new()
|
||||
st.minesweeper.on_submit = func(action: String) -> void:
|
||||
if action == "move":
|
||||
|
||||
@@ -18,7 +18,7 @@ var _rng
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/gdd/systems/排雷循环/棋盘生成.md:219 棋盘生成宿主(装载组)
|
||||
# 判据出处: design/gdd/systems/排雷循环/棋盘生成.md:219 棋盘生成宿主(装载组)
|
||||
_db = ConfigDBScript.new()
|
||||
# 表加载失败不中止(承 G2 同款:表损坏的红落在具体断言上——
|
||||
# 诊断在此展开,后续断言按坏表实况逐条验红;collect_only=true 下成员照常赋值可评)
|
||||
@@ -52,7 +52,7 @@ func _mk_stream(stream_name: String, scope: int) -> RandomNumberGenerator:
|
||||
# 公式 N×cell ≤ 可用高−12 必须计入 (N−1)×2px 网格间距,否则网格实高溢出被 clip_contents 裁掉;
|
||||
# 现公式 = 计入间距 + 垂直预留 24px〔上 12/下 12——底部不贴 D 区边框〕)----
|
||||
func _smoke_layout_cell_formula() -> void:
|
||||
# oracle: design/gdd/systems/排雷循环/棋盘生成.md:75 (出生区边长表单点——格边长公式组)
|
||||
# 判据出处: design/gdd/systems/排雷循环/棋盘生成.md:75 (出生区边长表单点——格边长公式组)
|
||||
print("--- 棋盘布局格边长公式(底部预留回归·四档板型)---")
|
||||
var board_generator = load("res://core/board_generator.gd").new()
|
||||
var board_view = load("res://presentation/views/board_view.gd").new()
|
||||
@@ -82,7 +82,7 @@ func _smoke_layout_cell_formula() -> void:
|
||||
# ---- 地图战斗资源·身份面单元冒烟(格面资源:roll 恰一次 / 雷安同池 / 幂等 / 清图批尾)----
|
||||
|
||||
func _smoke_reward_identity_unit() -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/地图战斗资源.md:233 (格面资源身份——三分出口与资源身份组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/地图战斗资源.md:233 (格面资源身份——三分出口与资源身份组)
|
||||
print("--- 地图战斗资源·格面资源身份面 ---")
|
||||
var rng = load("res://core/rng_service.gd").new()
|
||||
rng.set_master_seed(20260911)
|
||||
@@ -144,7 +144,7 @@ class _BoardRowStub:
|
||||
|
||||
|
||||
func _smoke_board_generator() -> void:
|
||||
# oracle: design/gdd/systems/排雷循环/棋盘生成.md:221 (确定性复现+布雷纪律组)
|
||||
# 判据出处: design/gdd/systems/排雷循环/棋盘生成.md:221 (确定性复现+布雷纪律组)
|
||||
print("--- 棋盘生成 ---")
|
||||
var board_generator = load("res://core/board_generator.gd").new()
|
||||
for L in [1, 2]:
|
||||
@@ -239,7 +239,7 @@ func _board_dump(b) -> String:
|
||||
# ---- 地图战斗资源·伤害量值区间冒烟(roll 域内 + 中点锚 + 变异实际流动 + 定值行不受扰)----
|
||||
|
||||
func _smoke_power_interval() -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/地图战斗资源.md:233 (资源量值区间——伤害量值区间组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/地图战斗资源.md:233 (资源量值区间——伤害量值区间组)
|
||||
print("--- 地图战斗资源·伤害量值区间(上阶/下阶随机)---")
|
||||
# ① 表侧静态锚(配置推导——改表不断锚):凡带 power_min/power_max 的行 = 区间行,
|
||||
# 校验配对齐全(lo ≤ hi)+ power = floor 中点(行动经济 条款①期望当量锚);其余行必须零区间字段。
|
||||
|
||||
@@ -13,7 +13,7 @@ var _db: ConfigDb
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/gdd/systems/排雷循环/棋盘生成.md:219 棋盘生成(装载组)
|
||||
# 判据出处: design/gdd/systems/排雷循环/棋盘生成.md:219 棋盘生成(装载组)
|
||||
_db = load("res://core/config_db.gd").new() as ConfigDb
|
||||
if not _db.load_tables("res://data/tables", false):
|
||||
Kit.check("数值表加载", false)
|
||||
@@ -40,7 +40,7 @@ func _generate(L: int, D: int, seed0: int) -> BoardData:
|
||||
|
||||
|
||||
func _test_generate_shape_and_mines() -> void:
|
||||
# oracle: design/gdd/systems/排雷循环/棋盘生成.md:224 (布雷纪律组)
|
||||
# 判据出处: design/gdd/systems/排雷循环/棋盘生成.md:224 (布雷纪律组)
|
||||
var b := _generate(1, 0, 71000)
|
||||
if b == null:
|
||||
Kit.check("生成返回棋盘", false)
|
||||
@@ -61,7 +61,7 @@ func _test_generate_shape_and_mines() -> void:
|
||||
|
||||
# 目标雷数公式单点(d14):同参同值对拍原式 + 真实产图指标读数同源
|
||||
func _test_target_mine_count_single_source() -> void:
|
||||
# oracle: design/gdd/systems/排雷循环/棋盘生成.md:224 (雷数单源组)
|
||||
# 判据出处: design/gdd/systems/排雷循环/棋盘生成.md:224 (雷数单源组)
|
||||
var bad := 0
|
||||
var checked := 0
|
||||
for L in [1, 2, 3]:
|
||||
@@ -92,7 +92,7 @@ func _test_target_mine_count_single_source() -> void:
|
||||
|
||||
|
||||
func _test_digits_equal_neighbor_mine_count() -> void:
|
||||
# oracle: design/gdd/systems/排雷循环/棋盘生成.md:223 (数字恒真组)
|
||||
# 判据出处: design/gdd/systems/排雷循环/棋盘生成.md:223 (数字恒真组)
|
||||
var b := _generate(1, 1, 72000)
|
||||
if b == null:
|
||||
Kit.check("生成返回棋盘(数字段)", false)
|
||||
@@ -113,7 +113,7 @@ func _test_digits_equal_neighbor_mine_count() -> void:
|
||||
|
||||
|
||||
func _test_same_seed_bit_identical() -> void:
|
||||
# oracle: design/gdd/systems/排雷循环/棋盘生成.md:221 (同种子逐位复现组)
|
||||
# 判据出处: design/gdd/systems/排雷循环/棋盘生成.md:221 (同种子逐位复现组)
|
||||
var a := _generate(2, 0, 73000)
|
||||
var b := _generate(2, 0, 73000)
|
||||
if a == null or b == null:
|
||||
@@ -124,7 +124,7 @@ func _test_same_seed_bit_identical() -> void:
|
||||
|
||||
|
||||
func _test_clearable_and_inferability_selfconsistent() -> void:
|
||||
# oracle: design/gdd/systems/排雷循环/棋盘生成.md:226 (可清图与及格线自洽组)
|
||||
# 判据出处: design/gdd/systems/排雷循环/棋盘生成.md:226 (可清图与及格线自洽组)
|
||||
var b := _generate(1, 0, 74000)
|
||||
if b == null:
|
||||
Kit.check("生成返回棋盘(可推理段)", false)
|
||||
@@ -140,7 +140,7 @@ func _test_clearable_and_inferability_selfconsistent() -> void:
|
||||
# ---- V8 出生区边长表单点(架构整改——spawn_edge 表头驱动布雷域基/几何/护栏全导出)----
|
||||
|
||||
func _test_spawn_edge_from_table() -> void:
|
||||
# oracle: design/gdd/systems/排雷循环/棋盘生成.md:75 (出生区边长读表组)
|
||||
# 判据出处: design/gdd/systems/排雷循环/棋盘生成.md:75 (出生区边长读表组)
|
||||
# ① 纯函数对拍:edge=5 夹具 → target = round(d×(W×H−25))(25 格布雷域基可观测)
|
||||
var p5 := {"w": 12, "h": 12, "density": 0.15, "spawn_edge": 5}
|
||||
Kit.check("V8 纯函数:edge=5 布雷域基 25 格(target=round(d×(144−25)))〔架构整改 V8〕",
|
||||
|
||||
@@ -15,7 +15,7 @@ var _bp_path := "user://test_breakpoint_resume.json"
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/存档序列化.md:180 (断点域契约:七字段往返/续玩确定性/幂等/三结局清除组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/存档序列化.md:180 (断点域契约:七字段往返/续玩确定性/幂等/三结局清除组)
|
||||
var db = load("res://core/config_db.gd").new()
|
||||
if not db.load_tables("res://data/tables", false):
|
||||
print("[breakpoint_resume_test] FAIL — 数值表加载失败")
|
||||
|
||||
@@ -13,7 +13,7 @@ const StatusTableScript := preload("res://data/schemas/status_effect_table.gd")
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/数据配置.md:195 (跨表校验宿主——装载组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/数据配置.md:195 (跨表校验宿主——装载组)
|
||||
_test_all_links_valid()
|
||||
_test_dangling_skill_id()
|
||||
_test_cast_side_mismatch()
|
||||
@@ -47,7 +47,7 @@ func _cats(errs: Array) -> Array[String]:
|
||||
|
||||
# ---- 正向:技能池全对齿(普攻恰一行、buff 挂敌方侧)应零错,防校验误伤 ----
|
||||
func _test_all_links_valid() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/数据配置.md:195 (悬空引用类别:跨表链接全有效组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/数据配置.md:195 (悬空引用类别:跨表链接全有效组)
|
||||
var skills := _skill([
|
||||
{"skill_id": "attack", "type": "basic"},
|
||||
{"skill_id": "raise_morale", "type": "buff", "buff_ref": "st_strong"},
|
||||
@@ -63,7 +63,7 @@ func _test_all_links_valid() -> void:
|
||||
|
||||
# ---- 负向:技能池引用表里不存在的 skill_id 必须报 ----
|
||||
func _test_dangling_skill_id() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/数据配置.md:195 (悬空引用类别:skill_id 悬空组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/数据配置.md:195 (悬空引用类别:skill_id 悬空组)
|
||||
var skills := _skill([{"skill_id": "attack", "type": "basic"}])
|
||||
var enemies := _enemy([
|
||||
{"level_index": 1, "row_kind": "battle", "depth_band": 0, "enemy_id": "goblin", "pool": [
|
||||
@@ -76,7 +76,7 @@ func _test_dangling_skill_id() -> void:
|
||||
|
||||
# ---- 负向:buff 挂到主角侧状态(side 反向)必须报 ----
|
||||
func _test_cast_side_mismatch() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/数据配置.md:195 (side 反向校验组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/数据配置.md:195 (side 反向校验组)
|
||||
var skills := _skill([{"skill_id": "raise_morale", "type": "buff", "buff_ref": "st_hero_side"}])
|
||||
var enemies := _enemy([
|
||||
{"level_index": 1, "row_kind": "battle", "depth_band": 0, "enemy_id": "goblin", "pool": [
|
||||
|
||||
@@ -12,7 +12,7 @@ var _resolver: ConfigDbResolver
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/数据配置.md:202 (解析器宿主——装载组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/数据配置.md:202 (解析器宿主——装载组)
|
||||
_resolver = ResolverScript.new(
|
||||
func(level, category, message) -> void: # 诊断通道签名按调用实参收(首参为表 id 字符串)
|
||||
_diags.append("%s/%s/%s" % [str(level), str(category), str(message)]),
|
||||
@@ -55,7 +55,7 @@ func _difficulty_table():
|
||||
|
||||
|
||||
func _test_band_hit() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/数据配置.md:202 (精确命中零警告组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/数据配置.md:202 (精确命中零警告组)
|
||||
var hit: Array = _resolver.resolve_enemy_rowset(1, 4, "battle")
|
||||
Kit.check("带命中取偏难一侧:请求深度 4 命中带 5(逐字不回带 0)〔fv1〕",
|
||||
hit.size() == 1 and String((hit[0] as Dictionary).get("enemy_id", "")) == "giant_spider")
|
||||
@@ -65,7 +65,7 @@ func _test_band_hit() -> void:
|
||||
|
||||
|
||||
func _test_rowset_order_preserved() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/数据配置.md:202 (行集寻址与行序保序组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/数据配置.md:202 (行集寻址与行序保序组)
|
||||
var rows: Array = _resolver.resolve_enemy_rowset(1, 0, "battle")
|
||||
var ids: Array[String] = []
|
||||
for r in rows:
|
||||
@@ -75,7 +75,7 @@ func _test_rowset_order_preserved() -> void:
|
||||
|
||||
|
||||
func _test_top_band_clamp() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/数据配置.md:202 (超最大行走封顶取最大行组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/数据配置.md:202 (超最大行走封顶取最大行组)
|
||||
_diags.clear()
|
||||
var rows: Array = _resolver.resolve_enemy_rowset(1, 99, "battle")
|
||||
Kit.check("越顶带 clamp 取顶带行集(battle 顶带 5)〔fv3〕",
|
||||
@@ -91,7 +91,7 @@ func _test_top_band_clamp() -> void:
|
||||
|
||||
|
||||
func _test_row_kind_routing() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/数据配置.md:202 (行类别路由组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/数据配置.md:202 (行类别路由组)
|
||||
var battle_rows: Array = _resolver.resolve_enemy_rowset(1, 0, "battle")
|
||||
var boss_rows: Array = _resolver.resolve_enemy_rowset(1, 0, "boss")
|
||||
Kit.check("行类路由:同请求深度 battle 与 boss 命中不同键域〔fv4〕",
|
||||
@@ -100,7 +100,7 @@ func _test_row_kind_routing() -> void:
|
||||
|
||||
|
||||
func _test_request_oob() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/数据配置.md:203 (越域致命中止组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/数据配置.md:203 (越域致命中止组)
|
||||
_diags.clear()
|
||||
_resolver.resolve_enemy_rowset(0, 0, "battle")
|
||||
Kit.check("关卡序号 <1 → fatal request_oob〔fv5〕", _has_fatal_oob())
|
||||
@@ -120,7 +120,7 @@ func _has_fatal_oob() -> bool:
|
||||
|
||||
|
||||
func _test_difficulty_clamp() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/数据配置.md:202 (难度 clamp 组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/数据配置.md:202 (难度 clamp 组)
|
||||
var m: Dictionary = _resolver.resolve_difficulty_modifiers(1, 0)
|
||||
Kit.check("难度乘数越域被 clamp:hp 99 → 上限 10", absf(float(m.get("hp_mult", 0.0)) - 10.0) < 0.001)
|
||||
Kit.check("难度乘数越域被 clamp:atk 0.01 → 下限 0.1", absf(float(m.get("atk_mult", 0.0)) - 0.1) < 0.001)
|
||||
|
||||
@@ -8,7 +8,7 @@ const Kit := preload("res://tests/unit/test_kit.gd")
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/engineering/architecture/architecture.md:107 (CurioDeck:三选一/商店共享池、抽签、cap 库存——刷新付费组)
|
||||
# 判据出处: design/engineering/architecture/architecture.md「模块归属·核心层」CurioDeck 行 (CurioDeck:三选一/商店共享池、抽签、cap 库存——刷新付费组)
|
||||
var db = load("res://core/config_db.gd").new()
|
||||
if not db.load_tables("res://data/tables", false):
|
||||
print("[curio_refresh_paid_test] FAIL — 数值表加载失败")
|
||||
|
||||
@@ -11,7 +11,7 @@ const EffectEngineScript = preload("res://core/effect_engine.gd")
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/engineering/architecture/architecture.md:89 (效果词表封闭——扩签封闭性组)
|
||||
# 判据出处: design/engineering/architecture/architecture.md「模块归属·基础层」effect_engine 行 (效果词表封闭——扩签封闭性组)
|
||||
# ① 词表在册断言(扩签面:2 事件+8 事实+7 效果类型+1 算子)
|
||||
Kit.check_quiet(EffectEngineScript.EVENTS.has("dig_stop") and EffectEngineScript.EVENTS.has("battle_won"),
|
||||
"新事件在册:dig_stop/battle_won〔专长批 V1〕")
|
||||
|
||||
@@ -11,34 +11,34 @@ const Kit := preload("res://tests/unit/test_kit.gd")
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/engineering/code-conventions.md:41 (封装边界:跨对象往来只走公开方法与属性——P2 词表单点扫描组)
|
||||
# 判据出处: design/engineering/code-conventions.md:41 (封装边界:跨对象往来只走公开方法与属性——P2 词表单点扫描组)
|
||||
_sweep_generator()
|
||||
_sweep_production_face()
|
||||
quit(Kit.finish("encapsulation_p2_sweep_test"))
|
||||
|
||||
|
||||
func _src(path: String) -> String:
|
||||
# oracle: design/engineering/code-conventions.md:41 (源码采样组)
|
||||
# 判据出处: design/engineering/code-conventions.md:41 (源码采样组)
|
||||
var src := FileAccess.get_file_as_string(path)
|
||||
Kit.check("扫描面非空前置:%s" % path, src.length() > 0)
|
||||
return src
|
||||
|
||||
|
||||
func _zero(pattern: String, src: String, label: String) -> void:
|
||||
# oracle: design/engineering/code-conventions.md:41 (零命中扫描组)
|
||||
# 判据出处: design/engineering/code-conventions.md:41 (零命中扫描组)
|
||||
var re := RegEx.create_from_string(pattern)
|
||||
Kit.check("%s:%s 零命中〔架构P2〕" % [label, pattern], re.search(src) == null)
|
||||
|
||||
|
||||
func _anchor(pattern: String, src: String, label: String) -> void:
|
||||
# oracle: design/engineering/code-conventions.md:41 (正锚存在组)
|
||||
# 判据出处: design/engineering/code-conventions.md:41 (正锚存在组)
|
||||
var re := RegEx.create_from_string(pattern)
|
||||
Kit.check("%s:%s 正锚在案〔架构P2〕" % [label, pattern], re.search(src) != null)
|
||||
|
||||
|
||||
# ① V1 生成器词表定义删除与改指
|
||||
func _sweep_generator() -> void:
|
||||
# oracle: design/engineering/code-conventions.md:41 (生成器本地副本归零组)
|
||||
# 判据出处: design/engineering/code-conventions.md:41 (生成器本地副本归零组)
|
||||
var src := _src("res://core/node_map_generator.gd")
|
||||
if src.length() == 0:
|
||||
return
|
||||
@@ -48,7 +48,7 @@ func _sweep_generator() -> void:
|
||||
|
||||
# ② V2 生产三目录定义唯一性(递归扫 .gd,恰好 1 处且落 level_table.gd)
|
||||
func _sweep_production_face() -> void:
|
||||
# oracle: design/engineering/code-conventions.md:41 (schema 唯一定点组)
|
||||
# 判据出处: design/engineering/code-conventions.md:41 (schema 唯一定点组)
|
||||
var files: Array[String] = []
|
||||
for dir_path in ["res://core", "res://data", "res://presentation"]:
|
||||
_collect_gd(dir_path, files)
|
||||
|
||||
@@ -14,7 +14,7 @@ const Kit := preload("res://tests/unit/test_kit.gd")
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/engineering/code-conventions.md:41 (封装边界——五处结构性零命中扫描组)
|
||||
# 判据出处: design/engineering/code-conventions.md:41 (封装边界——五处结构性零命中扫描组)
|
||||
_sweep_settlement_segments()
|
||||
_sweep_orchestrator_methods()
|
||||
_sweep_breakpoint_pair()
|
||||
@@ -26,7 +26,7 @@ func _init() -> void:
|
||||
|
||||
# 源文件读取+非空前置(返 "" 即已判红——调用侧跳过后续正则断言)
|
||||
func _src(path: String) -> String:
|
||||
# oracle: design/engineering/code-conventions.md:41 (源码采样组)
|
||||
# 判据出处: design/engineering/code-conventions.md:41 (源码采样组)
|
||||
var src := FileAccess.get_file_as_string(path)
|
||||
Kit.check("扫描面非空前置:%s" % path, src.length() > 0)
|
||||
return src
|
||||
@@ -34,21 +34,21 @@ func _src(path: String) -> String:
|
||||
|
||||
# 正则零命中断言(-pattern 意图写入断言文案——可 grep 定位)
|
||||
func _zero(pattern: String, src: String, label: String, path: String) -> void:
|
||||
# oracle: design/engineering/code-conventions.md:41 (零命中扫描组)
|
||||
# 判据出处: design/engineering/code-conventions.md:41 (零命中扫描组)
|
||||
var re := RegEx.create_from_string(pattern)
|
||||
Kit.check("%s:%s 零命中〔封装5.1〕" % [label, pattern], re.search(src) == null)
|
||||
|
||||
|
||||
# 正锚在案断言(正则命中 ≥1——新公开面/新形态在用)
|
||||
func _anchor(pattern: String, src: String, label: String, path: String) -> void:
|
||||
# oracle: design/engineering/code-conventions.md:41 (正锚存在组)
|
||||
# 判据出处: design/engineering/code-conventions.md:41 (正锚存在组)
|
||||
var re := RegEx.create_from_string(pattern)
|
||||
Kit.check("%s:%s 正锚在案〔封装5.1〕" % [label, pattern], re.search(src) != null)
|
||||
|
||||
|
||||
# ① V2 settlement 七文件(束注入换代——对编排器引用归零)
|
||||
func _sweep_settlement_segments() -> void:
|
||||
# oracle: design/engineering/code-conventions.md:41 (结算段封装扫描组)
|
||||
# 判据出处: design/engineering/code-conventions.md:41 (结算段封装扫描组)
|
||||
var classes := {
|
||||
"res://core/settlement/grant_segment.gd": "GrantSegment",
|
||||
"res://core/settlement/combat_segment.gd": "CombatSegment",
|
||||
@@ -68,7 +68,7 @@ func _sweep_settlement_segments() -> void:
|
||||
|
||||
# ② V3 编排器搬家五方法零残留
|
||||
func _sweep_orchestrator_methods() -> void:
|
||||
# oracle: design/engineering/code-conventions.md:41 (编排器方法面扫描组)
|
||||
# 判据出处: design/engineering/code-conventions.md:41 (编排器方法面扫描组)
|
||||
var src := _src("res://core/battle_orchestrator.gd")
|
||||
if src.length() == 0:
|
||||
return
|
||||
@@ -78,7 +78,7 @@ func _sweep_orchestrator_methods() -> void:
|
||||
|
||||
# ③ V4 断点域(adventure_runner → BreakpointStore 公开面)
|
||||
func _sweep_breakpoint_pair() -> void:
|
||||
# oracle: design/engineering/code-conventions.md:41 (断点对封装扫描组)
|
||||
# 判据出处: design/engineering/code-conventions.md:41 (断点对封装扫描组)
|
||||
var src := _src("res://core/adventure_runner.gd")
|
||||
if src.length() == 0:
|
||||
return
|
||||
@@ -88,7 +88,7 @@ func _sweep_breakpoint_pair() -> void:
|
||||
|
||||
# ④ V5 存档双向公开面(save_manager↔slot_store)
|
||||
func _sweep_save_pair() -> void:
|
||||
# oracle: design/engineering/code-conventions.md:41 (存档对封装扫描组)
|
||||
# 判据出处: design/engineering/code-conventions.md:41 (存档对封装扫描组)
|
||||
var src_m := _src("res://core/save_manager.gd")
|
||||
if src_m.length() > 0:
|
||||
_zero("slots\\._", src_m, "save_manager 对槽位件私取归零", "res://core/save_manager.gd")
|
||||
@@ -102,7 +102,7 @@ func _sweep_save_pair() -> void:
|
||||
|
||||
# ⑤ V6 效果宿主累积面(grant → BattleFxHost 公开累积方法)
|
||||
func _sweep_fx_host_accumulators() -> void:
|
||||
# oracle: design/engineering/code-conventions.md:41 (特效宿主累积面扫描组)
|
||||
# 判据出处: design/engineering/code-conventions.md:41 (特效宿主累积面扫描组)
|
||||
var src := _src("res://core/settlement/grant_segment.gd")
|
||||
if src.length() == 0:
|
||||
return
|
||||
@@ -112,7 +112,7 @@ func _sweep_fx_host_accumulators() -> void:
|
||||
|
||||
# ⑥ V7 配置挂载面(config_db → 三 schema bind_resolver)
|
||||
func _sweep_config_resolver() -> void:
|
||||
# oracle: design/engineering/code-conventions.md:41 (配置挂载面扫描组)
|
||||
# 判据出处: design/engineering/code-conventions.md:41 (配置挂载面扫描组)
|
||||
var src := _src("res://core/config_db.gd")
|
||||
if src.length() == 0:
|
||||
return
|
||||
|
||||
@@ -16,7 +16,7 @@ func EffectEngineKindProbe(_db) -> bool:
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/奇物.md:465 (奇物凭证五件与留任二件组)
|
||||
# 判据出处: design/gdd/systems/冒险循环/奇物.md:465 (奇物凭证五件与留任二件组)
|
||||
var db = load("res://core/config_db.gd").new()
|
||||
if not db.load_tables("res://data/tables", false):
|
||||
print("[feat_vouchers_test] FAIL — 数值表加载失败")
|
||||
|
||||
@@ -14,7 +14,7 @@ const LevelRunnerScript := preload("res://core/level_runner.gd")
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/冒险结构.md:288 (生命一致性:三写入口封顶 [0,生效上限]——主角账组)
|
||||
# 判据出处: design/gdd/systems/冒险循环/冒险结构.md:288 (生命一致性:三写入口封顶 [0,生效上限]——主角账组)
|
||||
var db = load("res://core/config_db.gd").new()
|
||||
if not db.load_tables("res://data/tables", false):
|
||||
print("[hero_ledger_test] FAIL — 数值表加载失败")
|
||||
|
||||
@@ -10,7 +10,7 @@ const LevelTableScript := preload("res://data/schemas/level_table.gd")
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/关卡节点地图.md:176 (信息迷雾类型可见性宿主——装载组)
|
||||
# 判据出处: design/gdd/systems/冒险循环/关卡节点地图.md:176 (信息迷雾类型可见性宿主——装载组)
|
||||
var db = load("res://core/config_db.gd").new()
|
||||
var loaded: bool = db.load_tables("res://data/tables", false)
|
||||
Kit.check_quiet(loaded, "可见性用例:数值表加载成功")
|
||||
@@ -103,7 +103,7 @@ func _runner(db, nodes: Array, anchor_id: int, candidates: Array, n: int = 2):
|
||||
# ---- ⑥ N 进数据表(含越域校验)〔fv6〕----
|
||||
|
||||
func _test_field_in_table(db) -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/关卡节点地图.md:176 (迷雾参数读表——字段在表组)
|
||||
# 判据出处: design/gdd/systems/冒险循环/关卡节点地图.md:176 (迷雾参数读表——字段在表组)
|
||||
var n_in_table: int = int(db.run.level.info_visible_layers)
|
||||
Kit.check("N 落表:level.tres 读数 2(实际 %d)〔fv6〕" % n_in_table, n_in_table == 2)
|
||||
var t := LevelTableScript.new()
|
||||
@@ -127,7 +127,7 @@ func _has_n_domain_err(errs: Array) -> bool:
|
||||
# ---- ① 邻近 N 层见类型(层距边界)〔fv1〕----
|
||||
|
||||
func _test_layer_distance(db) -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/关卡节点地图.md:176 (层距 N 可见性组)
|
||||
# 判据出处: design/gdd/systems/冒险循环/关卡节点地图.md:176 (层距 N 可见性组)
|
||||
var nodes := _graph_nodes()
|
||||
var r = _runner(db, nodes, 1, [nodes[2]]) # 锚 = 节点 1(层 1),候选 = 节点 2
|
||||
var vis: Array = r.type_visible_ids()
|
||||
@@ -139,7 +139,7 @@ func _test_layer_distance(db) -> void:
|
||||
# ---- ② 下一跳恒可见(跨层跳边候选)〔fv4〕----
|
||||
|
||||
func _test_candidate_exempt(db) -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/关卡节点地图.md:176 (候选豁免组)
|
||||
# 判据出处: design/gdd/systems/冒险循环/关卡节点地图.md:176 (候选豁免组)
|
||||
var nodes := _graph_nodes()
|
||||
var r = _runner(db, nodes, 6, [nodes[3]]) # 锚 = 节点 6(层 5),候选 = 节点 3(层 2,层距 3)
|
||||
var vis: Array = r.type_visible_ids()
|
||||
@@ -150,7 +150,7 @@ func _test_candidate_exempt(db) -> void:
|
||||
# ---- ③ Boss/终点不入雾+⑤ 入口停点锚层 0〔fv3〕〔fv1〕----
|
||||
|
||||
func _test_boss_exempt(db) -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/关卡节点地图.md:176 (Boss 豁免组)
|
||||
# 判据出处: design/gdd/systems/冒险循环/关卡节点地图.md:176 (Boss 豁免组)
|
||||
var nodes := _graph_nodes()
|
||||
var r = _runner(db, nodes, -1, [nodes[0]]) # 入口停点(无当前节点,锚层 0)
|
||||
var vis: Array = r.type_visible_ids()
|
||||
@@ -163,7 +163,7 @@ func _test_boss_exempt(db) -> void:
|
||||
# ---- ④ 单圈回雾(visited 不豁免)〔fv7〕----
|
||||
|
||||
func _test_single_circle(db) -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/关卡节点地图.md:176 (单圈可见组)
|
||||
# 判据出处: design/gdd/systems/冒险循环/关卡节点地图.md:176 (单圈可见组)
|
||||
var nodes := _graph_nodes()
|
||||
var r = _runner(db, nodes, 6, [nodes[7], nodes[3]])
|
||||
r.visited.assign([nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], nodes[6]])
|
||||
@@ -176,7 +176,7 @@ func _test_single_circle(db) -> void:
|
||||
# ---- ⑤ 单链退化形态(出度 ≤1 图,编号 = id 句柄)〔fv1〕----
|
||||
|
||||
func _test_single_chain_entry(db) -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/关卡节点地图.md:176 (单链入口组)
|
||||
# 判据出处: design/gdd/systems/冒险循环/关卡节点地图.md:176 (单链入口组)
|
||||
var nodes := _chain_nodes()
|
||||
var r = _runner(db, nodes, -1, [nodes[0]])
|
||||
var vis: Array = r.type_visible_ids()
|
||||
@@ -189,7 +189,7 @@ func _test_single_chain_entry(db) -> void:
|
||||
# ---- ⑥ N 读表:可见窗随表值随动〔fv6〕----
|
||||
|
||||
func _test_n_from_table(db) -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/关卡节点地图.md:176 (N 读表组)
|
||||
# 判据出处: design/gdd/systems/冒险循环/关卡节点地图.md:176 (N 读表组)
|
||||
var nodes := _graph_nodes()
|
||||
var r = _runner(db, nodes, 1, [nodes[2]]) # 锚层 1
|
||||
db.run.level.info_visible_layers = 1
|
||||
@@ -207,7 +207,7 @@ func _test_n_from_table(db) -> void:
|
||||
# ---- ⑦ 续玩重建同值〔fv7〕----
|
||||
|
||||
func _test_resume_rebuild(db) -> void:
|
||||
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
var t := LevelTableScript.new()
|
||||
t.info_visible_layers = 2
|
||||
t.rows = [{"level_index": 1, "display_name": "夹具关", "rest_heal_pct": 30}]
|
||||
|
||||
@@ -232,7 +232,7 @@ func _board_row_exact(level_index: int, depth: int) -> bool:
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
_db = ConfigDBScript.new()
|
||||
# 表加载失败不中止(承 G2/G3/G4/G5 同款:表损坏的红落在具体断言上——
|
||||
# 诊断在此展开,后续断言按坏表实况逐条验红;collect_only=true 下成员照常赋值可评)
|
||||
@@ -262,7 +262,7 @@ func _init() -> void:
|
||||
# ---- 数值入表等价证明(d11/d16a——原字面量迁移:表值 == 迁移前字面量,消费端读表)----
|
||||
|
||||
func _smoke_table_migration() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/数据配置.md:195 (行集纪律——表迁移组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/数据配置.md:195 (行集纪律——表迁移组)
|
||||
print("--- 数值入表(shop 两 knob + hero 显示名 · 原值迁移)---")
|
||||
Kit.check("shop.relic_base_price 表值 == 迁移前字面量(15.0)〔wd11〕", _db.run.shop.relic_base_price == 15.0)
|
||||
Kit.check("hero.display_name 表值 == 迁移前字面量(探险者)〔wd16〕", _db.combat.hero.display_name == "探险者")
|
||||
@@ -277,7 +277,7 @@ func _smoke_table_migration() -> void:
|
||||
# 敌侧/板侧解析一律走 ConfigDB 真实消费链,禁直读行集自算)----
|
||||
|
||||
func _smoke_generated_coverage() -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/关卡节点地图.md:184 (每终点前恰一店——生成覆盖扫描组)
|
||||
# 判据出处: design/gdd/systems/冒险循环/关卡节点地图.md:184 (每终点前恰一店——生成覆盖扫描组)
|
||||
var enemy_ok := true
|
||||
var board_ok := true
|
||||
var boss_band_ok := true
|
||||
@@ -328,7 +328,7 @@ func _smoke_generated_coverage() -> void:
|
||||
# ---- 关卡节点图·生成图推进冒烟(图 = 该关参数档 × 种子)----
|
||||
|
||||
func _smoke_level() -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/关卡节点地图.md:178 (关卡图校验推进组)
|
||||
# 判据出处: design/gdd/systems/冒险循环/关卡节点地图.md:178 (关卡图校验推进组)
|
||||
print("--- 关卡节点图·生成图推进(参数档×种子产图·满配冒险成功/恢复公式/败北线/回放确定性)---")
|
||||
var autoplay = load("res://sim/level_autoplay.gd")
|
||||
var hero_base := {"atk": int(_db.combat.hero.atk), "hp": int(_db.combat.hero.max_hp), "ac": int(_db.combat.hero.armor_class)} # 配置基线快照——⑤ 回基线对比用(改表自动跟随,不再硬编码)
|
||||
@@ -442,7 +442,7 @@ func _drive_to_rest(level_index: int, seed_val: int, gap: int) -> Dictionary:
|
||||
# ---- 分支节点图冒烟:图合法性校验 + L2 幽暗地域生成图行进(图 = 参数档 × 种子)----
|
||||
|
||||
func _smoke_level_graph() -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/关卡节点地图.md:178 (图结构与行进纪律组)
|
||||
# 判据出处: design/gdd/systems/冒险循环/关卡节点地图.md:178 (图结构与行进纪律组)
|
||||
print("--- 关卡节点图·分支节点图(L2 幽暗地域·生成图)---")
|
||||
var autoplay = load("res://sim/level_autoplay.gd")
|
||||
# ① 关卡表行级校验零错误(关卡表只承载序号/显示名/恢复比例——图合法性由 validate_graph 在起关时承担)
|
||||
@@ -801,7 +801,7 @@ func _fresh_rng(seed_val: int):
|
||||
# ---- ① GP 冒险钱包·到账 ----
|
||||
|
||||
func _smoke_gp_wallet() -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/地图战斗资源.md:233 (GP 钱包入账组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/地图战斗资源.md:233 (GP 钱包入账组)
|
||||
print("--- GP 冒险钱包·到账(战斗完成入账恰一次 + Boss 加值)---")
|
||||
var autoplay = load("res://sim/level_autoplay.gd")
|
||||
# ① L1 首战(普通战档——掷定 [2,4] + 关级加值 0):runner 驱动,收集 battle_finished 与钱包事件
|
||||
@@ -867,7 +867,7 @@ func _escrow_sum_of(rows: Array) -> int:
|
||||
# ---- ② escrow 隔离(GP安全格兑付 / 破财直验 / 钱包 clamp)----
|
||||
|
||||
func _smoke_gp_escrow() -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/地图战斗资源.md:233 (GP escrow 组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/地图战斗资源.md:233 (GP escrow 组)
|
||||
print("--- GP escrow 隔离(GP安全格兑付入缓冲 · 破财不透支)---")
|
||||
# ① 种子扫描:找GP 袋(gp 型安全格)可挖掘局——挖开兑付 +3 入 escrow,战斗末随 gp_dropped 一次上抛
|
||||
var scan := {}
|
||||
@@ -981,7 +981,7 @@ func _find_gp_dig(minesweeper, settlement) -> Dictionary:
|
||||
# ---- ③ 商店房三收口 ----
|
||||
|
||||
func _smoke_shop_room() -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/关卡节点地图.md:184 (商店房呈现组)
|
||||
# 判据出处: design/gdd/systems/冒险循环/关卡节点地图.md:184 (商店房呈现组)
|
||||
print("--- 商店房三收口(现价/加算区/余额不足拒/离开回选路/收束清零)---")
|
||||
var autoplay = load("res://sim/level_autoplay.gd")
|
||||
# 行进到商店房(按图型别定位商店节点——候选取最小句柄行进,到达即停):
|
||||
@@ -1115,7 +1115,7 @@ func _shop_products_by_id(prods: Array) -> Dictionary:
|
||||
# ---- ④ 商店编排性质(生成图扫描面——三条性质 + 终点跨表解析)----
|
||||
|
||||
func _smoke_shop_graph() -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/关卡节点地图.md:184 (必经恰一店图纪律组)
|
||||
# 判据出处: design/gdd/systems/冒险循环/关卡节点地图.md:184 (必经恰一店图纪律组)
|
||||
print("--- 商店编排性质(每终点前一位恰一店 · 店数=终点数 · 全路径恰穿一店)---")
|
||||
var pred_bad := 0
|
||||
var term_total := 0
|
||||
@@ -1186,7 +1186,7 @@ func _smoke_shop_graph() -> void:
|
||||
|
||||
# ---- ⑥ 大岔口分区域兑付(V3——多终点图走哪条路决定打谁,任一 Boss 胜即收束恰一次)----
|
||||
func _smoke_multi_terminal_settlement() -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/关卡节点地图.md:185 (多终局收束组)
|
||||
# 判据出处: design/gdd/systems/冒险循环/关卡节点地图.md:185 (多终局收束组)
|
||||
print("--- 大岔口分区域兑付(L2 两轴各打各的终点)---")
|
||||
var autoplay = load("res://sim/level_autoplay.gd")
|
||||
var nodes: Array = _generated_graph(2, 42).get("nodes", [])
|
||||
@@ -1254,7 +1254,7 @@ func _paths_all_one_shop(nodes: Array) -> bool:
|
||||
# ---- ⑤ 商店共享池(三选一同构/乘法价/跨渠道共账/切边/断点扩键)----
|
||||
|
||||
func _smoke_shop_pool() -> void:
|
||||
# oracle: design/engineering/architecture/architecture.md:107 (商店共享池组)
|
||||
# 判据出处: design/engineering/architecture/architecture.md「模块归属·核心层」CurioDeck 行 (商店共享池组)
|
||||
print("--- 商店共享池(抽3买1全重抽 · 乘法定价 ceil · 池空硬闸 · 切边零入店)---")
|
||||
var autoplay = load("res://sim/level_autoplay.gd")
|
||||
var shelf_kinds: Array = []
|
||||
@@ -1417,7 +1417,7 @@ func _smoke_shop_pool() -> void:
|
||||
# ---- 商店房 node_entered 载荷(尾波 V1——_enter_node pool/legacy 双发射路径归一后的行为保持断言)----
|
||||
|
||||
func _test_shop_node_entered_payload() -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/关卡节点地图.md:184 (行进至此打开商店覆盖层——载荷组)
|
||||
# 判据出处: design/gdd/systems/冒险循环/关卡节点地图.md:184 (行进至此打开商店覆盖层——载荷组)
|
||||
print("--- 商店房 node_entered 载荷(pool 含 shelf:true+gp 现值/legacy 无 shelf 键)---")
|
||||
var autoplay = load("res://sim/level_autoplay.gd")
|
||||
# pool 态:载荷字典经信号闭包当场取样(字典为引用累积器——lambda 按值捕获标量的绕行惯例)
|
||||
@@ -1576,7 +1576,7 @@ func _has_err_category(errs: Array, category: String) -> bool:
|
||||
# ---- 货架位数读表(架构整改——shelf_size 表头单点:进店与重抽同源)----
|
||||
|
||||
func _test_shelf_size_from_table() -> void:
|
||||
# oracle: design/ux/shop.md:13 (货架位数读表组)
|
||||
# 判据出处: design/ux/shop.md:13 (货架位数读表组)
|
||||
var autoplay = load("res://sim/level_autoplay.gd")
|
||||
var st = _db.run.shop
|
||||
var saved: int = int(st.shelf_size)
|
||||
@@ -1598,7 +1598,7 @@ func _test_shelf_size_from_table() -> void:
|
||||
# ---- 散折价收回规则键(架构整改 R2——shop.spend_discount 表字段退役,折价读 economy_spend.discount_pct)----
|
||||
|
||||
func _test_spend_discount_from_rule_key() -> void:
|
||||
# oracle: design/gdd/systems/排雷循环/行动经济.md:256 (换图基座:挖格产 RAP 与刷新折扣组)
|
||||
# 判据出处: design/gdd/systems/排雷循环/行动经济.md:256 (换图基座:挖格产 RAP 与刷新折扣组)
|
||||
# 字段真删:shop.tres 加载零诊断 + spend_discount 属性不在位
|
||||
Kit.check("shop.tres 加载零诊断(spend_discount 字段真删——schema 与表行同卸)〔架构整改 V2〕",
|
||||
_db.diagnostics_count("error") == 0 and _db.run.shop.get("spend_discount") == null)
|
||||
|
||||
@@ -11,7 +11,7 @@ const LevelTableScript := preload("res://data/schemas/level_table.gd")
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/关卡节点地图.md:178 (关卡表消费侧校验宿主)
|
||||
# 判据出处: design/gdd/systems/冒险循环/关卡节点地图.md:178 (关卡表消费侧校验宿主)
|
||||
_test_positive()
|
||||
_test_negative()
|
||||
_test_scope_not_extended()
|
||||
@@ -38,7 +38,7 @@ func _has(errs: Array, cat: String, needle: String = "") -> bool:
|
||||
# ---- 正例(四检全过)----
|
||||
|
||||
func _test_positive() -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/关卡节点地图.md:178 (良构基底正向组)
|
||||
# 判据出处: design/gdd/systems/冒险循环/关卡节点地图.md:178 (良构基底正向组)
|
||||
# 多入口多终点:两入口共汇入一岔口,两终点各在互异深度
|
||||
var multi := [
|
||||
_n(0, "battle", 0, [2]), _n(1, "battle", 0, [2]),
|
||||
@@ -61,7 +61,7 @@ func _test_positive() -> void:
|
||||
# ---- 负例九类(各报对应类别与判据消息)----
|
||||
|
||||
func _test_negative() -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/关卡节点地图.md:178 (违规配置加载期致命——负向组)
|
||||
# 判据出处: design/gdd/systems/冒险循环/关卡节点地图.md:178 (违规配置加载期致命——负向组)
|
||||
var non_boss_end := [_n(0, "battle", 0, [1]), _n(1, "battle", 1, [])]
|
||||
Kit.check("负例:终点非 Boss 命中「终点恒为 Boss」〔v1〕",
|
||||
_has(_errs(non_boss_end), "structural", "终点 1 非 Boss"))
|
||||
@@ -95,7 +95,7 @@ func _test_negative() -> void:
|
||||
# ---- 「勿扩勿缩」反证:本检不含商店位与个数对参数档的核对 ----
|
||||
|
||||
func _test_scope_not_extended() -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/关卡节点地图.md:178 (校验范围不外扩组)
|
||||
# 判据出处: design/gdd/systems/冒险循环/关卡节点地图.md:178 (校验范围不外扩组)
|
||||
# 合法形状但全程无商店/三个终点同入口图——四检不该因此报错(商店位归冒烟断言、个数归产出侧)
|
||||
var no_shop := [_n(0, "battle", 0, [1, 2]), _n(1, "battle", 1, [3]), _n(2, "rest", 1, [3]), _n(3, "boss", 2, [])]
|
||||
Kit.check("范围反证:无商店图不在加载校验内报错(商店位由冒烟断言把关)〔v1〕", _errs(no_shop).is_empty())
|
||||
|
||||
@@ -21,7 +21,7 @@ var _db
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/gdd/systems/跨局循环/元进度.md:148 元进度与锻造宿主(装载组)
|
||||
# 判据出处: design/gdd/systems/跨局循环/元进度.md:148 元进度与锻造宿主(装载组)
|
||||
_db = ConfigDBScript.new()
|
||||
# 表加载失败不中止(承 G2/G3/G4/G5 同款:表损坏的红落在具体断言上——
|
||||
# 诊断在此展开,后续断言按坏表实况逐条验红;collect_only=true 下成员照常赋值可评)
|
||||
@@ -42,6 +42,10 @@ func _init() -> void:
|
||||
_test_blessing_no_forge_leak()
|
||||
_test_default_axes_pre_key()
|
||||
_test_armor_switch_dual_axes()
|
||||
_check_ks_workshop_row()
|
||||
_check_ks_refresh_row()
|
||||
_check_fork_rows()
|
||||
_check_fork_side_projection()
|
||||
quit(Kit.finish("meta_progression_forge_test"))
|
||||
|
||||
|
||||
@@ -56,7 +60,9 @@ const _META_SMOKE_PATHS := ["user://smoke_meta_buy.json", "user://smoke_meta_dry
|
||||
"user://smoke_meta_career_v5.json", "user://smoke_meta_treasure_unlock.json", "user://smoke_meta_treasure_blank.json",
|
||||
"user://smoke_meta_forge.json", "user://smoke_meta_forge2.json", "user://smoke_meta_forge3.json",
|
||||
"user://smoke_meta_forge4.json", "user://smoke_meta_forge5.json",
|
||||
"user://smoke_meta_reject.json", "user://smoke_meta_blessing.json", "user://smoke_meta_axesw.json"]
|
||||
"user://smoke_meta_reject.json", "user://smoke_meta_blessing.json", "user://smoke_meta_axesw.json",
|
||||
"user://smoke_meta_ks_workshop.json", "user://smoke_meta_ks_refresh.json",
|
||||
"user://smoke_meta_fork_rows.json", "user://smoke_meta_fork_side.json"]
|
||||
|
||||
|
||||
func _meta_smoke_cleanup() -> void:
|
||||
@@ -133,7 +139,7 @@ func _auto_play_round(settlement, out: Dictionary) -> void:
|
||||
# ---- 元进度 ②树购买面(购买受理/持久/余额不足拒绝/未知节点拒绝;
|
||||
# 行价=表值 flat 单价)----
|
||||
func _meta_pricing_and_buy() -> void:
|
||||
# oracle: design/gdd/systems/跨局循环/天赋树.md:198 (单币可判:购买只扣钥石、余额不足拒绝零变化组)
|
||||
# 判据出处: design/gdd/systems/跨局循环/天赋树.md:198 (单币可判:购买只扣钥石、余额不足拒绝零变化组)
|
||||
print("--- 元进度·树购买面(隔离档 smoke_meta_buy/dry)---")
|
||||
_meta_smoke_cleanup()
|
||||
var sv = _meta_new_save(_META_SMOKE_PATHS[0])
|
||||
@@ -160,7 +166,7 @@ func _meta_pricing_and_buy() -> void:
|
||||
|
||||
# ---- 元进度 ⑥Boss 两档 ⑦钥石不丢 ⑧工坊解锁 ⑩摘要副本与 cap(合成摘要驱动——settle 纯账面零跑局)----
|
||||
func _meta_settle_and_workshop() -> void:
|
||||
# oracle: design/gdd/systems/跨局循环/元进度.md:150 (钥石收束入账不丢组)
|
||||
# 判据出处: design/gdd/systems/跨局循环/元进度.md:150 (钥石收束入账不丢组)
|
||||
print("--- 元进度·收束入账与工坊(隔离档 smoke_meta_settle/workshop/cap)---")
|
||||
var sv = _meta_new_save(_META_SMOKE_PATHS[2])
|
||||
var mp = load("res://core/meta_progression.gd").new(_db, sv)
|
||||
@@ -353,7 +359,7 @@ func _meta_settle_and_workshop() -> void:
|
||||
|
||||
# ---- 元进度 ④注入可见 + 白板成立(验收 4/2/3——程序面;MC 逐位回归归 T7 全量验证)----
|
||||
func _meta_injection() -> void:
|
||||
# oracle: design/gdd/systems/跨局循环/天赋树.md:201 (注入可判:开局覆盖值分层可加组)
|
||||
# 判据出处: design/gdd/systems/跨局循环/天赋树.md:201 (注入可判:开局覆盖值分层可加组)
|
||||
print("--- 元进度·注入可见与白板(隔离档 smoke_meta_white/dry)---")
|
||||
var sv = _meta_new_save(_META_SMOKE_PATHS[1])
|
||||
var mp = load("res://core/meta_progression.gd").new(_db, sv)
|
||||
@@ -397,7 +403,7 @@ func _meta_injection() -> void:
|
||||
# ---- 锻造工坊·两根类型轴所有权移交(V5 前半:hero.tres 两轴删除+锻造配置默认值)----
|
||||
# 后半(build_adventure_params 注入默认值/选中覆盖)随 T4 注入接线同批断言;写口/解锁面归 T3。
|
||||
func _smoke_forge_axes() -> void:
|
||||
# oracle: design/gdd/systems/跨局循环/锻造工坊.md:220 (锻造两轴单选注入与树零攻防组)
|
||||
# 判据出处: design/gdd/systems/跨局循环/锻造工坊.md:220 (锻造两轴单选注入与树零攻防组)
|
||||
print("--- 锻造工坊·类型轴所有权移交(T2)---")
|
||||
# V5:hero.tres 静态表两轴已删(全表加载零违例——config_db_test 同款闸;此处钉字段不在位)
|
||||
Kit.check("hero.tres 无 attack_type/armor_type 字段(契约 2——静态表两轴删除)〔锻造工坊批 V5〕",
|
||||
@@ -560,7 +566,7 @@ func _smoke_forge_axes() -> void:
|
||||
# ---- 天赋树冒烟(v0.8 12 节点:schema/购买面/Fork 一节点双态/彩蛋特化)----
|
||||
|
||||
func _smoke_tree() -> void:
|
||||
# oracle: design/gdd/systems/跨局循环/天赋树.md:194 (天赋树购买与六键合成组)
|
||||
# 判据出处: design/gdd/systems/跨局循环/天赋树.md:194 (天赋树购买与六键合成组)
|
||||
print("--- 天赋树(12 节点 · 购买面 · Fork 双态 · 彩蛋觉醒)---")
|
||||
# 〔V1〕结构:12 行 + schema 零违例
|
||||
Kit.check("meta_tree 已加载(12 节点=脊柱 2〔生命/命中淬炼一线一节点 cap4〕+锻造 2〔钥匙+彩蛋——彩蛋含在锻造 2 内非第 13 行〕+钥石 2〔秘宝匣/刷新升级〕+同调 6〔socket 3+Fork 3 双态〕——v0.8 终态)〔天赋树批 V1〕",
|
||||
@@ -643,7 +649,7 @@ func _smoke_tree() -> void:
|
||||
# ---- 天赋树六键行为冒烟(V10:开局姿态窗/危险直觉限次/财富之道聚散/守侧失误补偿;
|
||||
# 引擎直构 + 编排授予段直注(_grant_seg._judge_seal=结算授予段封印判定单口))----
|
||||
func _smoke_tree_rules() -> void:
|
||||
# oracle: design/gdd/systems/跨局循环/天赋树.md:194 (规则键参数读表组)
|
||||
# 判据出处: design/gdd/systems/跨局循环/天赋树.md:194 (规则键参数读表组)
|
||||
print("--- 天赋树六键行为(V10:窗/限次/聚散/守侧)---")
|
||||
# 开局姿态组:每关首轮窗(battle_index 事实——引擎直构 + 自建 ctx)
|
||||
var fx = load("res://core/effect_engine.gd").new(null)
|
||||
@@ -784,7 +790,7 @@ func _smoke_tree_rules() -> void:
|
||||
# ---- 锻造契约补齐(断言补齐批——⑥锻造工坊):拒购零副作用/祝福件零泄漏/默认值解耦/甲型双轴 ----
|
||||
|
||||
func _test_forge_buy_reject() -> void:
|
||||
# oracle: design/gdd/systems/跨局循环/锻造工坊.md:222 (钥石不足购买被拒、余额与等级零变化组)
|
||||
# 判据出处: design/gdd/systems/跨局循环/锻造工坊.md:222 (钥石不足购买被拒、余额与等级零变化组)
|
||||
var sv = _meta_new_save("user://smoke_meta_reject.json")
|
||||
var mp = load("res://core/meta_progression.gd").new(_db, sv)
|
||||
sv.meta_state._add_keystone(10) # 低于首购价 20——余额不足面
|
||||
@@ -799,7 +805,7 @@ func _test_forge_buy_reject() -> void:
|
||||
|
||||
|
||||
func _test_blessing_no_forge_leak() -> void:
|
||||
# oracle: design/gdd/systems/跨局循环/锻造工坊.md:220 (树淬炼对攻击与 AC 零贡献组)
|
||||
# 判据出处: design/gdd/systems/跨局循环/锻造工坊.md:220 (树淬炼对攻击与 AC 零贡献组)
|
||||
var sv = _meta_new_save("user://smoke_meta_blessing.json")
|
||||
var mp = load("res://core/meta_progression.gd").new(_db, sv)
|
||||
var fs0: Dictionary = sv.meta_state.forge_state().duplicate(true)
|
||||
@@ -818,7 +824,7 @@ func _test_blessing_no_forge_leak() -> void:
|
||||
|
||||
|
||||
func _test_default_axes_pre_key() -> void:
|
||||
# oracle: design/gdd/systems/跨局循环/锻造工坊.md:217 (未点亮期间默认值照常注入组)
|
||||
# 判据出处: design/gdd/systems/跨局循环/锻造工坊.md:217 (未点亮期间默认值照常注入组)
|
||||
var sv = _meta_new_save("user://smoke_meta_axesw.json")
|
||||
var mp = load("res://core/meta_progression.gd").new(_db, sv)
|
||||
var ax: Dictionary = mp.hero_type_axes()
|
||||
@@ -827,7 +833,7 @@ func _test_default_axes_pre_key() -> void:
|
||||
|
||||
|
||||
func _test_armor_switch_dual_axes() -> void:
|
||||
# oracle: design/gdd/systems/跨局循环/锻造工坊.md:221 (换装不丢级不生效——双轴组)
|
||||
# 判据出处: design/gdd/systems/跨局循环/锻造工坊.md:221 (换装不丢级不生效——双轴组)
|
||||
var sv = _meta_new_save("user://smoke_meta_axesw.json")
|
||||
sv.meta_state._add_keystone(1000)
|
||||
var mp = load("res://core/meta_progression.gd").new(_db, sv)
|
||||
@@ -852,3 +858,115 @@ func _test_armor_switch_dual_axes() -> void:
|
||||
return
|
||||
Kit.check("甲型双轴:切回 light 复原(AC 10+0)且 heavy 等级保留(Lv1 不丢)〔断言补齐批 V16〕",
|
||||
int(adr2.runner().effective_ac()) == 10 and sv.meta_state.forge_state().levels.get("heavy", 0) == 1)
|
||||
|
||||
|
||||
# ---- G5 天赋树行与投影断言(迭代 55「QA覆盖余量收口批」T5——ks_workshop/ks_refresh/fork 两行/侧投影+spine 读行)----
|
||||
|
||||
func _check_ks_workshop_row() -> void:
|
||||
# 判据出处: design/gdd/systems/跨局循环/天赋树.md:30 (钥匙门行——前置/零价/柜台展开)
|
||||
var row: Dictionary = _db.crossrun.meta_tree.row_by_id("ks_workshop")
|
||||
Kit.check("ks_workshop 行值:keystone 脉络 unlock 行且前置=[sp_hp](脊柱生命第 1 级)〔QA余 V7〕",
|
||||
String(row.get("vein", "")) == "keystone" and String(row.get("kind", "")) == "unlock"
|
||||
and (row.get("requires", []) as Array) == ["sp_hp"])
|
||||
Kit.check("ks_workshop 零价行值:currency=free 且 price=0(初始零购零币可点亮族)〔QA余 V7〕",
|
||||
String(row.get("currency", "")) == "free" and int(row.get("price", -1)) == 0)
|
||||
var sv = _meta_new_save("user://smoke_meta_ks_workshop.json")
|
||||
var mp = load("res://core/meta_progression.gd").new(_db, sv)
|
||||
sv.meta_state._add_keystone(1000)
|
||||
Kit.check("前置未满足不可购(sp_hp 未点亮——钥匙门锁定)", not mp.tree_buy("ks_workshop"))
|
||||
Kit.check("脊柱生命 L1 购买受理(前置行)", mp.tree_buy("sp_hp"))
|
||||
Kit.check("购买前投影 level=0(锁定态行在投影)", _ks_row_level(mp, "ks_workshop") == 0)
|
||||
var bal_pre: int = sv.meta_state.keystone_balance()
|
||||
Kit.check("ks_workshop 购买受理(零价 free)", mp.tree_buy("ks_workshop"))
|
||||
Kit.check("购买后投影 level=1(钥匙门开——柜台展开判据)且零币(余额差分零)",
|
||||
_ks_row_level(mp, "ks_workshop") == 1 and int(sv.meta_state.keystone_balance()) == bal_pre)
|
||||
_meta_smoke_cleanup()
|
||||
|
||||
|
||||
# 树面投影行 level 读数(_check_ks_workshop_row/_check_ks_refresh_row 共用读数面)
|
||||
func _ks_row_level(mp, node_id: String) -> int:
|
||||
for r in (mp.tree_camp_status()["rows"] as Array):
|
||||
if String((r as Dictionary).get("node_id", "")) == node_id:
|
||||
return int((r as Dictionary).get("level", 0))
|
||||
return -1
|
||||
|
||||
|
||||
func _check_ks_refresh_row() -> void:
|
||||
# 判据出处: design/gdd/systems/跨局循环/天赋树.md:30 (刷新升级行——同账路由与零刷新分支)
|
||||
var row: Dictionary = _db.crossrun.meta_tree.row_by_id("ks_refresh")
|
||||
Kit.check("ks_refresh 行值:rule 行 keystone 币 price=30 cap=2 前置=[ks_workshop]〔QA余 V7〕",
|
||||
String(row.get("kind", "")) == "rule" and String(row.get("currency", "")) == "keystone"
|
||||
and int(row.get("price", -1)) == 30 and int(row.get("cap", -1)) == 2
|
||||
and (row.get("requires", []) as Array) == ["ks_workshop"])
|
||||
var sv = _meta_new_save("user://smoke_meta_ks_refresh.json")
|
||||
var mp = load("res://core/meta_progression.gd").new(_db, sv)
|
||||
sv.meta_state._add_keystone(1000)
|
||||
var bal0: int = sv.meta_state.keystone_balance()
|
||||
Kit.check("ks_refresh 购买受理(tree_buy 同账路由→buy_attune_refresh)", mp.tree_buy("ks_refresh"))
|
||||
Kit.check("同账写读:refresh_level=1(树上+秘宝匣装配段双入口同一账本)", int(sv.meta_state.attune_state().refresh_level) == 1)
|
||||
Kit.check("同账币值:扣 meta 行 L1 keystone_cost=30(非树行 price 面定夺)", int(sv.meta_state.keystone_balance()) == bal0 - 30)
|
||||
Kit.check("树节点账零写入(同账语义——nodes 无 ks_refresh)", int(sv.meta_state.tree_state().nodes.get("ks_refresh", 0)) == 0)
|
||||
Kit.check("第二次购买受理(L2 keystone_cost=60)", mp.tree_buy("ks_refresh") and int(sv.meta_state.attune_state().refresh_level) == 2)
|
||||
var bal2: int = sv.meta_state.keystone_balance()
|
||||
Kit.check("零刷新分支:L2 后再购被拒(attune_refresh_row(3) 空返——升级行族耗尽)〔QA余 V7〕", not mp.tree_buy("ks_refresh"))
|
||||
Kit.check("零刷新分支零消费零升账", int(sv.meta_state.attune_state().refresh_level) == 2 and int(sv.meta_state.keystone_balance()) == bal2)
|
||||
Kit.check("同账投影:树面 ks_refresh 行 level 读 Y 账本(=2)", _ks_row_level(mp, "ks_refresh") == 2)
|
||||
_meta_smoke_cleanup()
|
||||
|
||||
|
||||
func _check_fork_rows() -> void:
|
||||
# 判据出处: design/gdd/systems/跨局循环/天赋树.md:30 (fork 一节点双态行——行值经购买面驱动)
|
||||
for spec in [{"id": "fork_sense", "group": "fg_sense", "na": "锋", "nb": "守"},
|
||||
{"id": "fork_economy", "group": "fg_economy", "na": "聚", "nb": "散"}]:
|
||||
var row: Dictionary = _db.crossrun.meta_tree.row_by_id(String(spec["id"]))
|
||||
Kit.check("%s 行值:fork 行 price=25 前置=[att_socket1] 组=%s〔QA余 V7〕" % [spec["id"], spec["group"]],
|
||||
String(row.get("kind", "")) == "fork" and int(row.get("price", -1)) == 25
|
||||
and (row.get("requires", []) as Array) == ["att_socket1"]
|
||||
and String(row.get("fork_group", "")) == String(spec["group"]))
|
||||
Kit.check("%s 侧名行值:name_a=%s / name_b=%s〔QA余 V7〕" % [spec["id"], spec["na"], spec["nb"]],
|
||||
String(row.get("name_a", "")) == String(spec["na"]) and String(row.get("name_b", "")) == String(spec["nb"]))
|
||||
var sv = _meta_new_save("user://smoke_meta_fork_rows.json")
|
||||
var mp = load("res://core/meta_progression.gd").new(_db, sv)
|
||||
sv.meta_state._add_keystone(1000)
|
||||
Kit.check("同调位 1 购买受理(fork 前置行——根节点无前置)", mp.tree_buy("att_socket1"))
|
||||
var bal0: int = sv.meta_state.keystone_balance()
|
||||
Kit.check("fork_sense 购买受理(tree_buy_fork 真链——解锁+默认 A 态)",
|
||||
mp.tree_buy_fork("fork_sense") and int(sv.meta_state.tree_state().nodes.get("fork_sense", 0)) == 1
|
||||
and String(sv.meta_state.tree_state().fork_sides.get("fg_sense", "")) == "a")
|
||||
Kit.check("fork_economy 购买受理(同链)",
|
||||
mp.tree_buy_fork("fork_economy") and int(sv.meta_state.tree_state().nodes.get("fork_economy", 0)) == 1
|
||||
and String(sv.meta_state.tree_state().fork_sides.get("fg_economy", "")) == "a")
|
||||
Kit.check("两 fork 行扣币共 50(表行 price 驱动)", int(sv.meta_state.keystone_balance()) == bal0 - 50)
|
||||
_meta_smoke_cleanup()
|
||||
|
||||
|
||||
func _check_fork_side_projection() -> void:
|
||||
# 判据出处: design/gdd/systems/跨局循环/天赋树.md:30 (组侧投影与脉络行板——真实表行读出)
|
||||
var sv = _meta_new_save("user://smoke_meta_fork_side.json")
|
||||
var mp = load("res://core/meta_progression.gd").new(_db, sv)
|
||||
sv.meta_state._add_keystone(1000)
|
||||
Kit.check("投影夹具:同调位 1+fork_economy 购买受理", mp.tree_buy("att_socket1") and mp.tree_buy_fork("fork_economy"))
|
||||
var fg_entry: Dictionary = {}
|
||||
for g in (mp.tree_camp_status()["fork_groups"] as Array):
|
||||
if String((g as Dictionary).get("group", "")) == "fg_economy":
|
||||
fg_entry = g
|
||||
Kit.check("fg_economy 组投影在册:display_name=财富之道 owned=true active_side=a〔QA余 V7〕",
|
||||
String(fg_entry.get("display_name", "")) == "财富之道" and bool(fg_entry.get("owned", false))
|
||||
and String(fg_entry.get("active_side", "")) == "a")
|
||||
var sides: Array = fg_entry.get("sides", []) as Array
|
||||
Kit.check("侧名投影:A 侧=聚(economy_hoard)/B 侧=散(economy_spend)〔QA余 V7〕",
|
||||
sides.size() == 2 and String((sides[0] as Dictionary).get("display_name", "")) == "聚"
|
||||
and ((sides[0] as Dictionary).get("effects", {}) as Dictionary).has("economy_hoard")
|
||||
and String((sides[1] as Dictionary).get("display_name", "")) == "散"
|
||||
and ((sides[1] as Dictionary).get("effects", {}) as Dictionary).has("economy_spend"))
|
||||
Kit.check("A 态规则键=economy_hoard(聚)", (mp.tree_rules() as Dictionary).has("economy_hoard"))
|
||||
Kit.check("切 b 受理(零币换侧)", mp.tree_toggle_fork("fg_economy", "b"))
|
||||
Kit.check("B 态规则键=economy_spend(散)且 A 键退场〔QA余 V7〕",
|
||||
(mp.tree_rules() as Dictionary).has("economy_spend") and not (mp.tree_rules() as Dictionary).has("economy_hoard"))
|
||||
var spine_names := {}
|
||||
for r in (mp.tree_camp_status()["rows"] as Array):
|
||||
if String((r as Dictionary).get("vein", "")) == "spine":
|
||||
spine_names[String((r as Dictionary).get("node_id", ""))] = String((r as Dictionary).get("display_name", ""))
|
||||
Kit.check("spine 脉络投影读行:sp_hp=生命淬炼/sp_hit=命中淬炼(生产表行名非夹具喂值)〔QA余 V7〕",
|
||||
String(spine_names.get("sp_hp", "")) == "生命淬炼" and String(spine_names.get("sp_hit", "")) == "命中淬炼")
|
||||
_meta_smoke_cleanup()
|
||||
@@ -17,7 +17,7 @@ var _ae: ActionEconomy
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/gdd/systems/排雷循环/排雷玩法.md:224 (排雷规则宿主——装载组)
|
||||
# 判据出处: design/gdd/systems/排雷循环/排雷玩法.md:224 (排雷规则宿主——装载组)
|
||||
_db = load("res://core/config_db.gd").new() as ConfigDb
|
||||
if not _db.load_tables("res://data/tables", false):
|
||||
Kit.check("数值表加载", false)
|
||||
@@ -46,7 +46,7 @@ func _init() -> void:
|
||||
|
||||
|
||||
func _test_install_invariants() -> void:
|
||||
# oracle: design/gdd/systems/排雷循环/排雷玩法.md:228 (装板四不变式组)
|
||||
# 判据出处: design/gdd/systems/排雷循环/排雷玩法.md:228 (装板四不变式组)
|
||||
_rules.install_board(_board)
|
||||
Kit.check("装板后 current_board 即所装棋盘", _rules.current_board() == _board)
|
||||
var opened: int = _rules.revealed_cells().size()
|
||||
@@ -70,7 +70,7 @@ func _unrevealed_safe() -> Vector2i:
|
||||
|
||||
|
||||
func _test_dig_face_rules() -> void:
|
||||
# oracle: design/gdd/systems/排雷循环/排雷玩法.md:231 (挖/封印合法性与可辨拒绝组)
|
||||
# 判据出处: design/gdd/systems/排雷循环/排雷玩法.md:231 (挖/封印合法性与可辨拒绝组)
|
||||
var revealed_first = _rules.revealed_cells()[0] as Vector2i
|
||||
var out: Array = _rules.settle_batch([{"action": "dig", "cell": revealed_first}])
|
||||
var o: Dictionary = (out[0] as Dictionary) if not out.is_empty() else {}
|
||||
@@ -85,7 +85,7 @@ func _test_dig_face_rules() -> void:
|
||||
|
||||
|
||||
func _test_reveal_advances() -> void:
|
||||
# oracle: design/gdd/systems/排雷循环/排雷玩法.md:230 (揭示推进与连锁组)
|
||||
# 判据出处: design/gdd/systems/排雷循环/排雷玩法.md:230 (揭示推进与连锁组)
|
||||
var c := _unrevealed_safe()
|
||||
if c == Vector2i(-1, -1):
|
||||
Kit.check("存在未揭示安全格(揭示推进前置)", false)
|
||||
@@ -99,7 +99,7 @@ func _test_reveal_advances() -> void:
|
||||
|
||||
|
||||
func _test_seal_place() -> void:
|
||||
# oracle: design/gdd/systems/排雷循环/排雷玩法.md:231 (重复封印拒绝组)
|
||||
# 判据出处: design/gdd/systems/排雷循环/排雷玩法.md:231 (重复封印拒绝组)
|
||||
var c := _unrevealed_safe()
|
||||
if c == Vector2i(-1, -1):
|
||||
Kit.check("存在未揭示安全格(封印前置)", false)
|
||||
@@ -112,7 +112,7 @@ func _test_seal_place() -> void:
|
||||
|
||||
|
||||
func _test_reject_out_of_catalog() -> void:
|
||||
# oracle: design/gdd/systems/排雷循环/排雷玩法.md:236 (目录外拒绝组)
|
||||
# 判据出处: design/gdd/systems/排雷循环/排雷玩法.md:236 (目录外拒绝组)
|
||||
var out: Array = _rules.settle_batch([{"action": "not_a_real_action", "cell": Vector2i(0, 0)}])
|
||||
var o: Dictionary = (out[0] as Dictionary) if not out.is_empty() else {}
|
||||
Kit.check("目录外意图被拒(不静默吞掉)——实际载荷 %s" % str(o).substr(0, 80),
|
||||
@@ -122,7 +122,7 @@ func _test_reject_out_of_catalog() -> void:
|
||||
# ---- V14 棋盘揭示预览只读口(架构整改——preview_reveal 与实挖揭示集同口径、零状态写入零事件)----
|
||||
|
||||
func _test_preview_reveal_readonly() -> void:
|
||||
# oracle: design/engineering/architecture/architecture.md:103 (preview_reveal 只读预览口——零写入零事件组)
|
||||
# 判据出处: design/engineering/architecture/architecture.md「模块归属·核心层」minesweeper_rules 行 (preview_reveal 只读预览口——零写入零事件组)
|
||||
var events := 0
|
||||
_rules.cell_revealed.connect(func(_evt): events += 1) # 零发射哨兵(preview 全程不触发揭示事件)
|
||||
# 形态 A:封印在场——preview(未开格) 不越封印且零状态写入
|
||||
|
||||
@@ -16,7 +16,7 @@ var _gen
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
_db = ConfigDBScript.new()
|
||||
var load_ok: bool = _db.load_tables("res://data/tables", true)
|
||||
if not load_ok:
|
||||
@@ -78,7 +78,7 @@ func _max_depth(g: Dictionary) -> int:
|
||||
# ---- V1 产出合格:三档×种子扫描,自证零错误 ----
|
||||
|
||||
func _test_output_ok() -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/关卡节点图生成.md:129 (产出约束六条+结构铁律组)
|
||||
# 判据出处: design/gdd/systems/冒险循环/关卡节点图生成.md:129 (产出约束六条+结构铁律组)
|
||||
var all_ok := true
|
||||
var entry_exit_ok := true
|
||||
for L in [1, 2, 3]:
|
||||
@@ -96,7 +96,7 @@ func _test_output_ok() -> void:
|
||||
# ---- V3 可复现:纯函数+counter 无关 ----
|
||||
|
||||
func _test_reproducible() -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/关卡节点图生成.md:134 (同种子同参数同图组)
|
||||
# 判据出处: design/gdd/systems/冒险循环/关卡节点图生成.md:134 (同种子同参数同图组)
|
||||
var same := true
|
||||
for L in [1, 2, 3]:
|
||||
if _sig(_graph(L, 42)) != _sig(_graph(L, 42)):
|
||||
@@ -127,7 +127,7 @@ func _test_reproducible() -> void:
|
||||
# ---- V5 岔路取舍两条 ----
|
||||
|
||||
func _test_fork_rules() -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/关卡节点图生成.md:88 (岔路取舍组)
|
||||
# 判据出处: design/gdd/systems/冒险循环/关卡节点图生成.md:88 (岔路取舍组)
|
||||
var next_hop_ok := true
|
||||
var comp_ok := true
|
||||
for L in [1, 2, 3]:
|
||||
@@ -203,7 +203,7 @@ func _branch_types(g: Dictionary, head_id: int, sibling_outs: Array) -> Array:
|
||||
# ---- V6 必经恰一店+归属锁定+多终点深度互异 ----
|
||||
|
||||
func _test_shop_and_attribution() -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/关卡节点图生成.md:48 (必经恰一店与归属锁定组)
|
||||
# 判据出处: design/gdd/systems/冒险循环/关卡节点图生成.md:48 (必经恰一店与归属锁定组)
|
||||
var paths_ok := true
|
||||
var lock_ok := true
|
||||
var depth_ok := true
|
||||
@@ -256,7 +256,7 @@ func _all_paths(g: Dictionary) -> Array:
|
||||
# ---- V7 战斗保底 ----
|
||||
|
||||
func _test_battle_floor() -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/关卡节点图生成.md:129 (战斗保底组)
|
||||
# 判据出处: design/gdd/systems/冒险循环/关卡节点图生成.md:129 (战斗保底组)
|
||||
var ok := true
|
||||
for L in [1, 2, 3]:
|
||||
for s in range(42, 52):
|
||||
@@ -268,7 +268,7 @@ func _test_battle_floor() -> void:
|
||||
# ---- V9 含岔口与出口结构 ----
|
||||
|
||||
func _test_exit_structure() -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/关卡节点图生成.md:48 (出口结构与分支互斥组)
|
||||
# 判据出处: design/gdd/systems/冒险循环/关卡节点图生成.md:48 (出口结构与分支互斥组)
|
||||
var fork_all := true
|
||||
var l1_single := true
|
||||
var big_fork_ok := true
|
||||
@@ -315,7 +315,7 @@ func _terminals_reachable(g: Dictionary, id: int) -> int:
|
||||
# ---- V9 Boss 出场门控(难度曲线与Boss——每区域恰一 Boss 终点)----
|
||||
|
||||
func _test_boss_exit_per_region() -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/关卡节点图生成.md:48 (每区域 Boss 出口组)
|
||||
# 判据出处: design/gdd/systems/冒险循环/关卡节点图生成.md:48 (每区域 Boss 出口组)
|
||||
# 真实生成多区域图:全部终点 type=="boss"、boss 终点数==exit_count、深度=L−r(r∈[0,exit_count)
|
||||
# ——每区域恰一 boss、深度互异即分属不同区域)
|
||||
# 节点 depth 字段=入口起最长路径深(生成器校验轴);区域序号 r 的可观测形态=最深 boss(r=0)
|
||||
@@ -351,7 +351,7 @@ func _test_boss_exit_per_region() -> void:
|
||||
# ---- V10 RNG 单点(纯流面 counter 不变)----
|
||||
|
||||
func _test_rng_single_point() -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/关卡节点图生成.md:134 (RNG 单点流组)
|
||||
# 判据出处: design/gdd/systems/冒险循环/关卡节点图生成.md:134 (RNG 单点流组)
|
||||
var svc = RngScript.new()
|
||||
svc.new_adventure(4242)
|
||||
var before: int = svc.counter()
|
||||
@@ -366,7 +366,7 @@ func _test_rng_single_point() -> void:
|
||||
# ---- V1 违规夹具:各报对应错误类别 ----
|
||||
|
||||
func _test_negative_fixtures() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/数据配置.md:198 (无解 fail-fast 夹具组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/数据配置.md:198 (无解 fail-fast 夹具组)
|
||||
# 夹具构造器:手写 nodes/entries/terminals
|
||||
var dup := _fx([
|
||||
{"id": 0, "type": "battle", "depth": 0, "next": [1]},
|
||||
@@ -493,7 +493,7 @@ func _has_msg(errs: Array, needle: String) -> bool:
|
||||
# ---- V6 节点图三参数表值驱动(架构整改——fork_third_way_pct/fill_rest_first_pct/width_max 入 level_gen_params)----
|
||||
|
||||
func _test_gen_params_table_driven() -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/关卡节点图生成.md:129 (参数档读表驱动组)
|
||||
# 判据出处: design/gdd/systems/冒险循环/关卡节点图生成.md:129 (参数档读表驱动组)
|
||||
# ① 同种子真实表行产图逐位(表值驱动回归锚)
|
||||
var g1 := _graph(2, 42)
|
||||
var g2 := _graph(2, 42)
|
||||
|
||||
@@ -12,7 +12,7 @@ const Kit := preload("res://tests/unit/test_kit.gd")
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
if OS.get_cmdline_user_args().has("fatal-fixture"):
|
||||
_trigger_fatal()
|
||||
return
|
||||
@@ -39,7 +39,7 @@ func _reading(gen, db, L: int) -> Dictionary:
|
||||
# ---- V8 生长递增(三档×多种子逐档读数)----
|
||||
|
||||
func _test_growth(gen, db) -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/关卡节点图生成.md:130 (生长递增组)
|
||||
# 判据出处: design/gdd/systems/冒险循环/关卡节点图生成.md:130 (生长递增组)
|
||||
var steps_ok := true
|
||||
var count_ok := true
|
||||
for s in [42, 43, 44, 45, 46]:
|
||||
@@ -63,7 +63,7 @@ func _test_growth(gen, db) -> void:
|
||||
# ---- V4 参数无解判定(静态面:域/预算无解夹具必须被 check_params 拦下)----
|
||||
|
||||
func _test_fail_fast_static(gen, db) -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/数据配置.md:198 (参数无解静态拒绝组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/数据配置.md:198 (参数无解静态拒绝组)
|
||||
Kit.check("生产三档参数档可行性判定零错误〔fv4〕",
|
||||
NodeMapGenerator.check_params(db.run.level_gen_params.row_by_level_index(1)).is_empty()
|
||||
and NodeMapGenerator.check_params(db.run.level_gen_params.row_by_level_index(3)).is_empty())
|
||||
@@ -82,7 +82,7 @@ func _test_fail_fast_static(gen, db) -> void:
|
||||
# ---- V4 致命中止真实通道(子进程退出码非零;零回退分支=件核)----
|
||||
|
||||
func _test_fatal_subprocess() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/数据配置.md:199 (无解致命子进程组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/数据配置.md:199 (无解致命子进程组)
|
||||
var bin := _engine_bin()
|
||||
if bin == "":
|
||||
Kit.check("未找到引擎二进制,致命子进程通道未跑(如实标注)〔fv4〕", false)
|
||||
@@ -98,7 +98,7 @@ func _test_fatal_subprocess() -> void:
|
||||
|
||||
|
||||
func _trigger_fatal() -> void:
|
||||
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
var gen = GenScript.new(RngScript.new())
|
||||
# 域内合法但深度预算无解:层深 6 放三出口(check_params 拦下 → 致命中止)
|
||||
var unsolvable := {"layers": 6, "branch_density": 0.0, "battle_ratio": 0.5, "reward_ratio": 0.2,
|
||||
|
||||
@@ -13,7 +13,7 @@ func _init() -> void:
|
||||
|
||||
|
||||
func _smoke_required_injection() -> void:
|
||||
# oracle: design/engineering/code-conventions.md:40 (拆分交接:依赖构造期一次交付——日志器必填注入守卫组)
|
||||
# 判据出处: design/engineering/code-conventions.md:40 (拆分交接:依赖构造期一次交付——日志器必填注入守卫组)
|
||||
# 未注入(缺省 0):start_session 返回空串、零文件产出
|
||||
var lg0 = load("res://presentation/playtest_logger.gd").new()
|
||||
var p0: String = lg0.start_session(42)
|
||||
|
||||
@@ -9,7 +9,7 @@ const Kit := preload("res://tests/unit/test_kit.gd")
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/engineering/architecture/architecture.md:107 (池结构案 D:候选抽取与池纪律组)
|
||||
# 判据出处: design/engineering/architecture/architecture.md「模块归属·核心层」CurioDeck 行 (池结构案 D:候选抽取与池纪律组)
|
||||
var db = load("res://core/config_db.gd").new()
|
||||
if not db.load_tables("res://data/tables", false):
|
||||
print("[pool_case_d_test] FAIL — 数值表加载失败")
|
||||
@@ -66,7 +66,7 @@ func _tiers_of(cands: Array) -> Array:
|
||||
|
||||
|
||||
func check_v7(a, db) -> void:
|
||||
# oracle: design/engineering/architecture/architecture.md:107 (池案 D V7 组)
|
||||
# 判据出处: design/engineering/architecture/architecture.md「模块归属·核心层」CurioDeck 行 (池案 D V7 组)
|
||||
if not a.start_adventure(201):
|
||||
Kit.check_quiet(false, "V7 前置:冒险启动")
|
||||
return
|
||||
@@ -112,7 +112,7 @@ func check_v7(a, db) -> void:
|
||||
|
||||
|
||||
func check_v6(b, db) -> void:
|
||||
# oracle: design/engineering/architecture/architecture.md:107 (池案 D V6 组)
|
||||
# 判据出处: design/engineering/architecture/architecture.md「模块归属·核心层」CurioDeck 行 (池案 D V6 组)
|
||||
if not b.start_adventure(202):
|
||||
Kit.check_quiet(false, "V6 前置:冒险启动")
|
||||
return
|
||||
@@ -142,7 +142,7 @@ func check_v6(b, db) -> void:
|
||||
|
||||
|
||||
func check_v12(m, db) -> void:
|
||||
# oracle: design/engineering/architecture/architecture.md:107 (池案 D V12 组)
|
||||
# 判据出处: design/engineering/architecture/architecture.md「模块归属·核心层」CurioDeck 行 (池案 D V12 组)
|
||||
if not m.start_adventure(401):
|
||||
Kit.check_quiet(false, "V12 前置:冒险启动")
|
||||
return
|
||||
@@ -161,7 +161,7 @@ func check_v12(m, db) -> void:
|
||||
|
||||
|
||||
func check_v8(c, db) -> void:
|
||||
# oracle: design/engineering/architecture/architecture.md:107 (池案 D V8 组)
|
||||
# 判据出处: design/engineering/architecture/architecture.md「模块归属·核心层」CurioDeck 行 (池案 D V8 组)
|
||||
if not c.start_adventure(203):
|
||||
Kit.check_quiet(false, "V8 前置:冒险启动")
|
||||
return
|
||||
|
||||
@@ -20,7 +20,7 @@ const TAG_OK := "board_gen" # 在册合法标签(PURPOSE_TAGS 含;wr7 必
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/数据配置.md:199 (运行期违约通道——致命真的会死组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/数据配置.md:199 (运行期违约通道——致命真的会死组)
|
||||
var db = ConfigDBScript.new()
|
||||
if not db.load_tables("res://data/tables", true):
|
||||
push_error("[run_time_fatal_test] 表加载失败——夹具不可用(回归)")
|
||||
@@ -45,7 +45,7 @@ func _init() -> void:
|
||||
|
||||
# 缺省形态(契约 2):不接组装点的协作件违约只落自身 push_error+assert,不外溢封印
|
||||
func _default_form(db) -> bool:
|
||||
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
var rng = RngScript.new()
|
||||
rng.set_master_seed(3)
|
||||
var oc = OrchestratorScript.new(db, rng)
|
||||
@@ -70,7 +70,7 @@ func _default_form(db) -> bool:
|
||||
# (棋盘生成半边:装板域拆件后生成器随 BoardInstaller 件内自建——组装点=引用束 fatal_cb 接线,
|
||||
# 经新组装点构建后触发生成器违约,封印传导断言不变)
|
||||
func _seal_propagation(db) -> bool:
|
||||
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
var rng_a = RngScript.new()
|
||||
rng_a.set_master_seed(5)
|
||||
var oc_bg = OrchestratorScript.new(db, rng_a)
|
||||
@@ -92,7 +92,7 @@ func _seal_propagation(db) -> bool:
|
||||
|
||||
# ②wr6(p6):未知用途标签 → handler 记录位为真(判据主体)+拿不到可用 RNG;合法标签对照另行验过
|
||||
func _unknown_tag_channel() -> bool:
|
||||
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
var rng_bad = RngScript.new()
|
||||
rng_bad.set_master_seed(7)
|
||||
if not _inject_handler(rng_bad, "wr6"):
|
||||
@@ -111,7 +111,7 @@ func _unknown_tag_channel() -> bool:
|
||||
|
||||
# ③wr7(p7):未播种取数 → handler 记录位为真+拿不到可用 RNG;同标签播种对照另行验过
|
||||
func _unseeded_channel() -> bool:
|
||||
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
var rng_bad = RngScript.new() # 不播种(_master_seed 缺省 -1)
|
||||
if not _inject_handler(rng_bad, "wr7"):
|
||||
return false
|
||||
@@ -129,7 +129,7 @@ func _unseeded_channel() -> bool:
|
||||
|
||||
# 成员在位前提检查(成员被移除时走干净退出 2,不落「赋值不存在的属性」式中途中断)
|
||||
func _inject_handler(rng, label: String) -> bool:
|
||||
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
if not (rng.get("violation_handler") is Callable):
|
||||
push_error("[run_time_fatal_test/%s] violation_handler 成员缺失(p6/p7 形态未实装——回归)" % label)
|
||||
return false
|
||||
|
||||
@@ -22,7 +22,7 @@ func _init() -> void:
|
||||
|
||||
# ① 无参实例(autoload 实例化形态)=无激活态+默认合法空态(sv2 类级)
|
||||
func _test_no_arg_instance_has_no_activation() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/存档序列化.md:61 (启动态无激活不读写组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/存档序列化.md:61 (启动态无激活不读写组)
|
||||
var sm := SaveManagerScript.new()
|
||||
Kit.check_quiet(sm.slots.active_slot() == 0 and not sm.slots.has_write_target(),
|
||||
"sv2 无参实例无激活态(路径空/激活号 0/写盘目标空)〔卡① sv2〕")
|
||||
@@ -41,7 +41,7 @@ func _test_no_arg_instance_has_no_activation() -> void:
|
||||
# 判别锚:空 _path 的退化写目标=裸 ".tmp"(落 res://,拆闸实验实测杂散落点)——
|
||||
# 断言先清退化目标再核零新增,闸短路缺陷(拒绝口旁路、写盘真实发生)必红。
|
||||
func _test_write_rejected_without_target() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/存档序列化.md:61 (无激活态写盘拒绝组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/存档序列化.md:61 (无激活态写盘拒绝组)
|
||||
var sm := SaveManagerScript.new()
|
||||
var before := {}
|
||||
for i in range(1, 4):
|
||||
@@ -81,7 +81,7 @@ func _test_write_rejected_without_target() -> void:
|
||||
# (生产结算链全段在位:mark_first_adventure_done→apply_adventure_summary→refresh_treasure_unlocks
|
||||
# →write_to_disk;无激活态下落账内存、写盘拒绝、盘面零触碰——已知中间态的机检面)
|
||||
func _test_settle_chain_write_rejected() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/存档序列化.md:61 (结算链写盘拒绝组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/存档序列化.md:61 (结算链写盘拒绝组)
|
||||
var db = load("res://core/config_db.gd").new()
|
||||
if not db.load_tables("res://data/tables", false):
|
||||
Kit.check_quiet(false, "sv3 链测试前置:数值表加载失败〔卡① sv3〕")
|
||||
@@ -104,7 +104,7 @@ func _test_settle_chain_write_rejected() -> void:
|
||||
|
||||
# ③ 越界槽号三原语拒绝+窥视越界返回不可读(原语边界 fail-safe)
|
||||
func _test_out_of_range_slots_rejected() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/存档序列化.md:61 (三存档位域界——越域槽拒绝组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/存档序列化.md:61 (三存档位域界——越域槽拒绝组)
|
||||
var sm := SaveManagerScript.new()
|
||||
sm.slots.attach_slot_template("user://smoke_boot_noact_%d.json")
|
||||
Kit.check_quiet(not sm.slots.activate_slot(0) and not sm.slots.activate_slot(4)
|
||||
|
||||
@@ -19,7 +19,7 @@ var _db
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
_db = ConfigDBScript.new()
|
||||
# 表加载失败不中止(承 G2–G5 同款:表损坏的红落在具体断言上)
|
||||
var load_ok: bool = _db.load_tables("res://data/tables", true)
|
||||
@@ -47,7 +47,7 @@ func _fresh_rng(seed_val: int):
|
||||
# 损坏兜底/迁移链/域界干净/摘要 cap/摘要跨系统块/生涯终身计数域 v6)。测试文件统一 user://smoke_save_*.json 隔离,
|
||||
# 段首清场+段尾清理——绝不碰 user://save.json(红线)。
|
||||
func _smoke_save() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/存档序列化.md:176 (存档域读写往返/迁移链/损坏兜底/域界组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/存档序列化.md:176 (存档域读写往返/迁移链/损坏兜底/域界组)
|
||||
print("--- SaveManager 存档读写/迁移/兜底 ---")
|
||||
var sm_script = load("res://core/save_manager.gd")
|
||||
var paths := ["user://smoke_save_roundtrip.json", "user://smoke_save_fresh.json",
|
||||
@@ -340,7 +340,7 @@ func _smoke_save() -> void:
|
||||
# 测试文件统一 user://smoke_save_slot_%d.json 模板隔离(attach_slot_template 注入口——禁各自拼槽名),
|
||||
# 段首清场+段尾清理——绝不碰 user://save.json 与真实槽 user://save_slot_N.json(红线)。
|
||||
func _smoke_save_slots() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/存档序列化.md:61 (三槽位与摘要 cap——槽位域组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/存档序列化.md:61 (三槽位与摘要 cap——槽位域组)
|
||||
print("--- SaveManager 三存档位承载(迭代 33 卡①)---")
|
||||
var sm_script = load("res://core/save_manager.gd")
|
||||
var slot_tpl := "user://smoke_save_slot_%d.json"
|
||||
|
||||
@@ -38,7 +38,7 @@ func _write_v7_keystone(path: String, keystone: int) -> void:
|
||||
|
||||
# ① 三槽往返:各槽写异值 → 写盘 → 新实例按位读回逐域相等;改槽 1 槽 2 字节不动(sv1)
|
||||
func _test_three_slot_roundtrip_and_isolation() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/存档序列化.md:61 (三槽位独立文件隔离组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/存档序列化.md:61 (三槽位独立文件隔离组)
|
||||
var t := SaveManagerScript.new()
|
||||
t.slots.attach_slot_template(TPL)
|
||||
for i in range(1, 4):
|
||||
@@ -66,7 +66,7 @@ func _test_three_slot_roundtrip_and_isolation() -> void:
|
||||
|
||||
# ② 高版本槽进档失败:拒绝+恢复切换前三态(路径/激活号/数据)+拒读槽零误写(sv9/sv10)
|
||||
func _test_activate_failure_restores_previous_slot() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/存档序列化.md:61 (激活失败回滚组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/存档序列化.md:61 (激活失败回滚组)
|
||||
var t := SaveManagerScript.new()
|
||||
t.slots.attach_slot_template(TPL)
|
||||
_write_v7_keystone(TPL % 1, 311)
|
||||
@@ -88,7 +88,7 @@ func _test_activate_failure_restores_previous_slot() -> void:
|
||||
|
||||
# ③ MetaProgression 宿主链切槽:引用同一性保持+写盘落当前槽(sv10——禁换实例先例)
|
||||
func _test_metaprogression_reference_identity() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/存档序列化.md:176 (元进度引用同一性——往返一致组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/存档序列化.md:176 (元进度引用同一性——往返一致组)
|
||||
var t := SaveManagerScript.new()
|
||||
t.slots.attach_slot_template(TPL)
|
||||
_write_v7_keystone(TPL % 1, 533)
|
||||
|
||||
@@ -36,7 +36,7 @@ func _write_slot_json(path: String, content: String) -> void:
|
||||
|
||||
# ① 三槽异值逐槽三项正确+不激活不写内存(sv7)
|
||||
func _test_peek_three_slots_distinct_values() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/存档序列化.md:174 (槽位摘要按槽窥视组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/存档序列化.md:174 (槽位摘要按槽窥视组)
|
||||
var t := SaveManagerScript.new()
|
||||
t.slots.attach_slot_template(TPL)
|
||||
for i in range(1, 4):
|
||||
@@ -58,7 +58,7 @@ func _test_peek_three_slots_distinct_values() -> void:
|
||||
|
||||
# ② 空槽零值合法态+不可读三态显式标记(sv7/契约 9)
|
||||
func _test_peek_empty_and_unreadable_states() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/存档序列化.md:174 (坏槽三态组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/存档序列化.md:174 (坏槽三态组)
|
||||
var t := SaveManagerScript.new()
|
||||
t.slots.attach_slot_template(TPL)
|
||||
DirAccess.remove_absolute(TPL % 3) # 清①段残留——空槽夹具
|
||||
@@ -79,7 +79,7 @@ func _test_peek_empty_and_unreadable_states() -> void:
|
||||
|
||||
# ③ 窥视零副作用:字节+mtime 双比对不变+零 .corrupt.bak 改名(sv8——真实入口冒烟段同判据)
|
||||
func _test_peek_zero_side_effect() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/存档序列化.md:174 (窥视零副作用组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/存档序列化.md:174 (窥视零副作用组)
|
||||
var t := SaveManagerScript.new()
|
||||
t.slots.attach_slot_template(TPL)
|
||||
_write_slot_json(TPL % 1, "corrupt bytes {{{{")
|
||||
|
||||
@@ -9,7 +9,7 @@ const Kit := preload("res://tests/unit/test_kit.gd")
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/战斗结算.md:362 (结算五段 Segment——五段全序组)
|
||||
# 判据出处: design/gdd/systems/战斗循环/战斗结算.md:362 (结算五段 Segment——五段全序组)
|
||||
var db = load("res://core/config_db.gd").new()
|
||||
if not db.load_tables("res://data/tables", false):
|
||||
print("[settle_segments_test] FAIL — 数值表加载失败")
|
||||
|
||||
@@ -10,7 +10,7 @@ const Kit := preload("res://tests/unit/test_kit.gd")
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/gdd/systems/跨局循环/秘宝.md:246 (秘宝 12 件逐件行为——效果行为组)
|
||||
# 判据出处: design/gdd/systems/跨局循环/秘宝.md:246 (秘宝 12 件逐件行为——效果行为组)
|
||||
var db = load("res://core/config_db.gd").new()
|
||||
if not db.load_tables("res://data/tables", false):
|
||||
Kit.check_quiet(false, "数值表加载")
|
||||
@@ -144,6 +144,7 @@ func _init() -> void:
|
||||
ae6c.request_stop()
|
||||
ae6c.finish_round_stop(125)
|
||||
Kit.check_quiet(ae6c.rap == 3, "储备罐边界:3 AP→3(floor 3.75——不越参数面直改停手结算)")
|
||||
_test_rap_transfer_row_e2e(db) # ⑥-补 生产行 e2e(迭代 55 T2——装配链行值主证,替上文字面量路径作主证并存)
|
||||
|
||||
# ⑦ 巢穴预告(装板后一次报数量——明细行经 step_row 信号收集)
|
||||
var rng7 = load("res://core/rng_service.gd").new(); rng7.set_master_seed(271)
|
||||
@@ -417,3 +418,26 @@ class StubAutoInvRunner:
|
||||
extends RefCounted
|
||||
func query_inventory(_cb: Callable) -> Array:
|
||||
return []
|
||||
|
||||
|
||||
# ⑥-补 储备罐生产行 e2e(迭代 55「QA覆盖余量收口批」T2/V5):装配链行值主证——上文字面量路径(125 直调)
|
||||
# 保留并存;本函数断言全链依赖生产行值:add_item 入库→装配集直注(套件既定调试注入口径)→attune 参数和
|
||||
# ==行值 125→转存兑付按 grant_segment 生产入参形态(装配面读数为入参)兑现。行值变更即红。
|
||||
func _test_rap_transfer_row_e2e(db) -> void:
|
||||
# 判据出处: design/gdd/systems/跨局循环/秘宝.md:246 (秘宝逐件行为——储备罐转存率行值)
|
||||
var inv = load("res://core/treasure_items.gd").new()
|
||||
var got: Dictionary = inv.add_item("reserve_jar", "clear_reward", Vector2i(-1, -1), 0)
|
||||
Kit.check_quiet(not got.is_empty() and String(got.get("item_id", "")) == "reserve_jar",
|
||||
"储备罐 e2e:add_item 入库(真实入口——受理记录 item_id 命中)〔QA余 V5〕")
|
||||
var rng = load("res://core/rng_service.gd").new(); rng.set_master_seed(281)
|
||||
var ob = load("res://core/battle_orchestrator.gd").new(db, rng)
|
||||
Kit.check_quiet(bool(ob.begin_battle({"level_index": 1, "node_depth": 0, "hero_hp": int(db.combat.hero.max_hp), "attunement": ["reserve_jar"]})),
|
||||
"储备罐 e2e:装配集直注起战〔QA余 V5〕")
|
||||
Kit.check_quiet(int(ob.attune_param_sum("rap_transfer_mult")) == 125,
|
||||
"储备罐 e2e:装配→attune 参数和==生产行值 125(行值主证)〔QA余 V5〕")
|
||||
var ae = ob.action_economy
|
||||
var ap_left: int = int(ae.ap)
|
||||
Kit.check_quiet(bool(ae.request_stop()), "储备罐 e2e:停手记账受理〔QA余 V5〕")
|
||||
ae.finish_round_stop(int(ob.attune_param_sum("rap_transfer_mult"))) # grant_segment 生产入参形态(装配面读数→转存率入参)
|
||||
Kit.check_quiet(int(ae.rap) == ap_left * 125 / 100,
|
||||
"储备罐 e2e:转存增量==行值倍率(%d AP→RAP %d=125%% 整除口径;行值或接线变更即红)〔QA余 V5〕" % [ap_left, int(ae.rap)])
|
||||
@@ -53,7 +53,7 @@ func _all_revealed(_c: Vector2i) -> bool:
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/gdd/systems/跨局循环/秘宝.md:246 (秘宝结构冻结与护栏校验——结构组)
|
||||
# 判据出处: design/gdd/systems/跨局循环/秘宝.md:246 (秘宝结构冻结与护栏校验——结构组)
|
||||
# ① 授予归因+受理序+Used 不可逆
|
||||
var inv = _new_inv()
|
||||
var r1: Dictionary = inv.add_item("sapper_order", "safe_cell", Vector2i(3, 4), 2)
|
||||
|
||||
@@ -7,7 +7,7 @@ extends SceneTree
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/gdd/systems/跨局循环/天赋树.md:195 (等级阈值前置:schema 四拒一过+判定三态+投影两态组)
|
||||
# 判据出处: design/gdd/systems/跨局循环/天赋树.md:195 (等级阈值前置:schema 四拒一过+判定三态+投影两态组)
|
||||
var failures := 0
|
||||
var db = load("res://core/config_db.gd").new()
|
||||
var mt = load("res://data/schemas/meta_tree_table.gd").new()
|
||||
|
||||
@@ -12,7 +12,7 @@ extends SceneTree
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/gdd/systems/跨局循环/天赋树.md:194 (结构封闭:六键合成行闭集校验组)
|
||||
# 判据出处: design/gdd/systems/跨局循环/天赋树.md:194 (结构封闭:六键合成行闭集校验组)
|
||||
var eng = load("res://core/effect_engine.gd")
|
||||
var tree_tbl = load("res://data/schemas/meta_tree_table.gd")
|
||||
var db = load("res://core/config_db.gd").new()
|
||||
@@ -89,7 +89,7 @@ func _init() -> void:
|
||||
# ---- 规则键参数域校验(架构整改批 R1——六键参数字典):正例全过/缺参数拒/多参数拒/越域拒/非字典拒 ----
|
||||
|
||||
func _test_rule_key_params(eng, tree_tbl, db) -> Array:
|
||||
# oracle: design/gdd/systems/跨局循环/天赋树.md:194 (规则键参数字典——参数名/取值域合规组)
|
||||
# 判据出处: design/gdd/systems/跨局循环/天赋树.md:194 (规则键参数字典——参数名/取值域合规组)
|
||||
var errs: Array = []
|
||||
var params_tbl: Dictionary = tree_tbl.RULE_KEY_PARAMS
|
||||
# 正例:meta_tree.tres 现行 Fork 行逐键过 validate(加载期已验——此处显式复核)
|
||||
|
||||
@@ -20,7 +20,7 @@ var _db
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
_db = ConfigDBScript.new()
|
||||
# 表加载失败不中止(承 G2–G5 同款:表损坏的红落在具体断言上)
|
||||
var load_ok: bool = _db.load_tables("res://data/tables", true)
|
||||
@@ -47,7 +47,7 @@ func _fresh_rng(seed_val: int):
|
||||
# ---- ① RNG 事件日志会话落盘(源 `_initialize` :70,主会话裁决一改判跨系统闭环承载——
|
||||
# 驱动沿源 :60–70 取号序列自持:master 种子 42 → 前置取号 → new_adventure 开日志会话 → 会话内取号)----
|
||||
func _rng_event_log_session() -> void:
|
||||
# oracle: design/engineering/architecture/architecture.md:90 (事件日志落盘会话组)
|
||||
# 判据出处: design/engineering/architecture/architecture.md「模块归属·基础层」RngService 行 (事件日志落盘会话组)
|
||||
var rng = load("res://core/rng_service.gd").new()
|
||||
rng.set_master_seed(42)
|
||||
rng.open_stream("board_gen")
|
||||
@@ -62,7 +62,7 @@ func _rng_event_log_session() -> void:
|
||||
# _on_level_finished(victory)+breakpoint_snapshot 取读+clear_breakpoint;
|
||||
# 起局与兑付两步的受理断言留单元宿主(adventure_feats_test),此处纯驱动——前置失败自然落红)----
|
||||
func _feats_breakpoint_roundtrip() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/存档序列化.md:180 (专长断点附随件往返组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/存档序列化.md:180 (专长断点附随件往返组)
|
||||
var rt := FeatTable.new()
|
||||
rt.rows = [
|
||||
{"feat_id": "x1", "display_name": "试一", "rarity": "common", "desc": "测试揭示", "cap": 1,
|
||||
@@ -108,7 +108,7 @@ func _feats_breakpoint_roundtrip() -> void:
|
||||
# level_autoplay 真局摘要 → apply 原语 → 域读数 → 写盘读回保持;临时档 user://smoke_save_integration.json
|
||||
# 段首清场+段尾清理(源 _smoke_save 隔离纪律),绝不碰 user://save.json(红线))----
|
||||
func _save_summary_integration() -> void:
|
||||
# oracle: design/gdd/systems/跨局循环/元进度.md:150 (摘要跨系统块与钥石只增不减组)
|
||||
# 判据出处: design/gdd/systems/跨局循环/元进度.md:150 (摘要跨系统块与钥石只增不减组)
|
||||
var sm_script = load("res://core/save_manager.gd")
|
||||
var path9 := "user://smoke_save_integration.json" # 源 paths[8]
|
||||
if FileAccess.file_exists(path9):
|
||||
@@ -141,7 +141,7 @@ func _save_summary_integration() -> void:
|
||||
# ---- ④ 试玩记录仪(源 `_smoke_playtest_logger` 整套八条,主会话裁决七改判跨系统闭环承载——
|
||||
# PlaytestLogger 挂真实结算流:会话文件/逐结算快照/字段齐备/spawn_count 回归/诊断转储/JSONL 回读)----
|
||||
func _smoke_playtest_logger() -> void:
|
||||
# oracle: design/engineering/code-conventions.md:40 (试玩日志器跨系统接线组)
|
||||
# 判据出处: design/engineering/code-conventions.md:40 (试玩日志器跨系统接线组)
|
||||
print("--- PlaytestLogger(结算快照 JSONL + 诊断转储)---")
|
||||
var rng = load("res://core/rng_service.gd").new()
|
||||
rng.set_master_seed(20260912)
|
||||
|
||||
@@ -12,7 +12,7 @@ var _db
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/数据配置.md:200 致命真的会死:正式通道加载进程非 0 退出(装载前提组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/数据配置.md:200 致命真的会死:正式通道加载进程非 0 退出(装载前提组)
|
||||
_db = ConfigDBScript.new()
|
||||
var load_ok: bool = _db.load_tables("res://data/tables", true)
|
||||
if not load_ok:
|
||||
@@ -24,7 +24,7 @@ func _init() -> void:
|
||||
|
||||
|
||||
func _trigger_oob() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/数据配置.md:204 越域请求在进入解析前致命中止(Fatal 级诊断+进程非 0,无兜底)(越域组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/数据配置.md:204 越域请求在进入解析前致命中止(Fatal 级诊断+进程非 0,无兜底)(越域组)
|
||||
# 共享 .tres 上的 resolver 归最后装载的实例——autoload ConfigDB 的 _ready 在测试 _init 之后
|
||||
# 二次注入覆盖私有实例,故触发前把解析通道归还被测实例(诊断落 _db,退出码判据才成立)
|
||||
_db.combat.board_gen._resolver = _db._resolver
|
||||
|
||||
@@ -9,7 +9,7 @@ const Kit := preload("res://tests/unit/test_kit.gd") # T6 收口:_check/汇
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/数据配置.md:198 干净检出:表清单内全部表加载成功、就绪态断言(装载组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/数据配置.md:198 干净检出:表清单内全部表加载成功、就绪态断言(装载组)
|
||||
var db = ConfigDBScript.new()
|
||||
var load_ok: bool = db.load_tables("res://data/tables", true)
|
||||
Kit.check("全部表加载 + 校验通过(含跨表护栏零 error 诊断)〔fv6〕", load_ok and db.diagnostics_count("error") == 0)
|
||||
@@ -31,7 +31,7 @@ func _init() -> void:
|
||||
# ---- 进度坐标 ceil 解析(R1:向上下取整保证偏难;越最大行 clamp 取最大值 + 警告去重)----
|
||||
|
||||
func _test_progress_resolution(db) -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/数据配置.md:203 向上取整解析:请求无精确行返回不低于请求坐标的最近行(进度查行组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/数据配置.md:203 向上取整解析:请求无精确行返回不低于请求坐标的最近行(进度查行组)
|
||||
var bg_rows: Array = db.combat.board_gen.rows
|
||||
# 行键全集自表推导(禁写死关卡/深度数值)
|
||||
var level_keys: Array = []
|
||||
@@ -88,7 +88,7 @@ func _test_progress_resolution(db) -> void:
|
||||
# ---- 变异池门控(unlock_level 闸 + ∅ board_types 通配 + 节点身份第三维)----
|
||||
|
||||
func _test_variant_pool_gate(db) -> void:
|
||||
# oracle: design/gdd/systems/排雷循环/地形变异.md:153 候选池空合法:门控筛空时静默零布置(变异门控组)
|
||||
# 判据出处: design/gdd/systems/排雷循环/地形变异.md:153 候选池空合法:门控筛空时静默零布置(变异门控组)
|
||||
var rows: Array = db.combat.variation.rows
|
||||
var gated_row := {}
|
||||
var wildcard_row := {}
|
||||
@@ -131,7 +131,7 @@ func _rows_contain(pool: Array, row: Dictionary) -> bool:
|
||||
# ---- ShopTable 消费面(行集解析单点——runner/编排禁自扫表的契约消费侧验证)----
|
||||
|
||||
func _test_shop_accessors(db) -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/数据配置.md:199 校验类别全覆盖:行结构与引用闭合(商店访问器行结构组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/数据配置.md:199 校验类别全覆盖:行结构与引用闭合(商店访问器行结构组)
|
||||
var products: Array = db.run.shop.product_rows()
|
||||
if products.is_empty():
|
||||
Kit.check("ShopTable products 行族非空(表已已入库前提)", false)
|
||||
@@ -159,7 +159,7 @@ func _test_shop_accessors(db) -> void:
|
||||
# ---- MetaTable 消费面 ----
|
||||
|
||||
func _test_meta_accessors(db) -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/数据配置.md:199 校验类别全覆盖:行结构与引用闭合(元进度访问器行结构组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/数据配置.md:199 校验类别全覆盖:行结构与引用闭合(元进度访问器行结构组)
|
||||
# 无 forge_products 行族断言(行族/访问器不在表,V20 清单)
|
||||
var key_rows: Array = db.crossrun.meta.boss_keystone
|
||||
if not key_rows.is_empty():
|
||||
@@ -179,7 +179,7 @@ func _test_meta_accessors(db) -> void:
|
||||
# ---- TABLE_MANIFEST ↔ data/tables 目录同步(各系统→D2 单点契约的登记面)----
|
||||
|
||||
func _test_manifest_dir_sync(db) -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/数据配置.md:198 表目录无清单外文件(清单对账组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/数据配置.md:198 表目录无清单外文件(清单对账组)
|
||||
var manifest: Dictionary = db.TABLE_MANIFEST
|
||||
var dir := DirAccess.open("res://data/tables")
|
||||
var disk_files: Array = []
|
||||
@@ -204,7 +204,7 @@ func _test_manifest_dir_sync(db) -> void:
|
||||
# (rowset_rule——行序即契约,R6 加载期兜底)③行集实例总数 ≤4(骰后值运行面,spawn 守卫——冒烟侧验证)。
|
||||
|
||||
func _test_rowset_discipline() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/数据配置.md:199 校验类别:重复键与缺锚行逐类断言(行集纪律组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/数据配置.md:199 校验类别:重复键与缺锚行逐类断言(行集纪律组)
|
||||
# 行集用 rows.assign([...]) 而非直接赋值:rows 是 Array[Dictionary] 定型属性,直接赋 untyped
|
||||
# 数组字面量会触发「Invalid assignment」脚本错误——该错误不改退出码,却会**中断本函数剩余断言**
|
||||
#(本函数五条纪律断言曾因此全部静默不跑,套件仍报 all green,是零覆盖假象)。
|
||||
|
||||
@@ -20,7 +20,7 @@ const PROBE_REF := ' {\n "feat_id": "ref_probe",\n "display_name": "引用探
|
||||
|
||||
|
||||
func _inject(dir: String, probe: String) -> bool:
|
||||
# oracle: design/gdd/systems/表现与支撑/数据配置.md:199 校验类别全覆盖:前缀违规夹具注入(前缀夹具组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/数据配置.md:199 校验类别全覆盖:前缀违规夹具注入(前缀夹具组)
|
||||
var path := dir + "/feats.tres"
|
||||
var f := FileAccess.open(path, FileAccess.READ)
|
||||
if f == null:
|
||||
@@ -45,7 +45,7 @@ func _inject(dir: String, probe: String) -> bool:
|
||||
|
||||
|
||||
func _assert_rejected(bad_dir: String, probe: String, label: String) -> bool:
|
||||
# oracle: design/gdd/systems/表现与支撑/数据配置.md:200 致命真的会死:违规表拒绝且进程非 0(拒绝判定组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/数据配置.md:200 致命真的会死:违规表拒绝且进程非 0(拒绝判定组)
|
||||
# 返回 true=正确拒绝;false=放行或夹具失败(调用方据此退出 2)
|
||||
if not Kit.copy_tables(SRC_DIR, bad_dir) or not _inject(bad_dir, probe):
|
||||
push_error("[%s] 夹具目录准备失败" % label)
|
||||
|
||||
@@ -39,7 +39,7 @@ const PROBE_TRADE_KEY := ' {
|
||||
|
||||
|
||||
func _inject(dir: String, probe: String) -> bool:
|
||||
# oracle: design/gdd/systems/表现与支撑/数据配置.md:199 校验类别全覆盖:退役词夹具注入(退役词夹具组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/数据配置.md:199 校验类别全覆盖:退役词夹具注入(退役词夹具组)
|
||||
var path := dir + "/feats.tres"
|
||||
var f := FileAccess.open(path, FileAccess.READ)
|
||||
if f == null:
|
||||
@@ -64,7 +64,7 @@ func _inject(dir: String, probe: String) -> bool:
|
||||
|
||||
|
||||
func _assert_rejected(bad_dir: String, probe: String, label: String) -> bool:
|
||||
# oracle: design/gdd/systems/表现与支撑/数据配置.md:200 致命真的会死:退役词加载致命中止(拒绝判定组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/数据配置.md:200 致命真的会死:退役词加载致命中止(拒绝判定组)
|
||||
# 返回 true=正确拒绝;false=放行或夹具失败(调用方据此退出 2)
|
||||
if not Kit.copy_tables(SRC_DIR, bad_dir) or not _inject(bad_dir, probe):
|
||||
push_error("[%s] 夹具目录准备失败" % label)
|
||||
|
||||
@@ -58,7 +58,7 @@ func _count_cat(errs: Array, cat: String) -> int:
|
||||
|
||||
# ---- R1:elite 行类合法与字段互锁、引用校验(合法零错+十一违例各自「含」预期类别)----
|
||||
func _test_elite_validation_rejects() -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/敌人系统.md:325 elite 行字段互锁与基础引用违例加载期返回错误
|
||||
# 判据出处: design/gdd/systems/战斗循环/敌人系统.md:325 elite 行字段互锁与基础引用违例加载期返回错误
|
||||
var t_ok = EnemyTableScript.new()
|
||||
t_ok.rows.assign(_legal_rows())
|
||||
var errs: Array = t_ok.validate()
|
||||
@@ -130,7 +130,7 @@ func _validate_rows(rows: Array) -> Array:
|
||||
|
||||
# ---- R2:有效池=基础池+附加技,基础池取深度带最小行(深带行在前防取首行假绿)----
|
||||
func _test_effective_pool_merge() -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/敌人系统.md:149 精英有效池=同关战斗行类基础敌种深度带最小行的池+附加技
|
||||
# 判据出处: design/gdd/systems/战斗循环/敌人系统.md:149 精英有效池=同关战斗行类基础敌种深度带最小行的池+附加技
|
||||
var rows := [
|
||||
_mk_row(1, "battle", 3, "goblin"), # 深带行在前(取首行的错误实现必红)
|
||||
_mk_row(1, "battle", 0, "goblin"),
|
||||
@@ -155,7 +155,7 @@ func _test_effective_pool_merge() -> void:
|
||||
|
||||
# ---- R3:继承不复制——基础池改表传到精英、改写返回数组不回灌 ----
|
||||
func _test_base_pool_change_propagates() -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/敌人系统.md:149 继承不复制——基础池调参自动传到精英
|
||||
# 判据出处: design/gdd/systems/战斗循环/敌人系统.md:149 继承不复制——基础池调参自动传到精英
|
||||
var rows := [
|
||||
_mk_row(1, "battle", 3, "goblin"),
|
||||
_mk_row(1, "battle", 0, "goblin"),
|
||||
@@ -181,7 +181,7 @@ func _test_base_pool_change_propagates() -> void:
|
||||
|
||||
# ---- R4:合并池过池构成纪律(跨表件校验对象=合并后的池)----
|
||||
func _test_merged_pool_composition() -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/敌人系统.md:150 池构成纪律校验对象是合并后的池
|
||||
# 判据出处: design/gdd/systems/战斗循环/敌人系统.md:150 池构成纪律校验对象是合并后的池
|
||||
var skills := SkillTableScript.new()
|
||||
skills.rows.assign([
|
||||
{"skill_id": "attack", "type": "basic"},
|
||||
@@ -239,7 +239,7 @@ func _msgs(errs: Array) -> Array[String]:
|
||||
|
||||
# ---- R5:暂不接线——表里有 elite 行时寻址拿不到它(行类门改动特有判据)----
|
||||
func _test_elite_unreachable_by_encounter() -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/敌人系统.md:151 精英行接线前不会被任何节点读到
|
||||
# 判据出处: design/gdd/systems/战斗循环/敌人系统.md:151 精英行接线前不会被任何节点读到
|
||||
_diags.clear()
|
||||
var resolver = ResolverScript.new(
|
||||
func(level, category, message) -> void:
|
||||
@@ -272,7 +272,7 @@ func _test_elite_unreachable_by_encounter() -> void:
|
||||
|
||||
# ---- R6:生产表地精精英落表(真实加载零错+行值逐位+有效池字典)----
|
||||
func _test_production_goblin_elite() -> void:
|
||||
# oracle: design/gdd/systems/战斗循环/敌人系统.md:147 精英=敌人表行类为 elite 的独立一行
|
||||
# 判据出处: design/gdd/systems/战斗循环/敌人系统.md:147 精英=敌人表行类为 elite 的独立一行
|
||||
var db = load("res://core/config_db.gd").new()
|
||||
var ok: bool = db.load_tables("res://data/tables", true)
|
||||
Kit.check("生产表 ConfigDB.load_tables 装载(is_ready=%s)〔精英批 V6〕" % str(db.is_ready), ok and db.is_ready)
|
||||
|
||||
@@ -12,7 +12,7 @@ const Kit := preload("res://tests/unit/test_kit.gd")
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/数据配置.md:198 锻造表装载与就绪态(装载组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/数据配置.md:198 锻造表装载与就绪态(装载组)
|
||||
var db = ConfigDBScript.new()
|
||||
var load_ok: bool = db.load_tables("res://data/tables", true)
|
||||
Kit.check("生产 forge.tres 随全表加载 + validate 零违例〔锻造工坊批 V1〕", load_ok and db.diagnostics_count("error") == 0)
|
||||
@@ -27,7 +27,7 @@ func _init() -> void:
|
||||
# ---- 生产表六行正向(V1 生产面 + V11 行字段闭集)----
|
||||
|
||||
func _test_production_rows(db) -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/数据配置.md:199 校验类别全覆盖:行 schema 正反逐类断言(生产行结构组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/数据配置.md:199 校验类别全覆盖:行 schema 正反逐类断言(生产行结构组)
|
||||
var rows: Array = db.crossrun.forge.route_rows()
|
||||
Kit.check("六路线行族规模恰 6(三武器+三甲)〔锻造工坊批 V1〕", rows.size() == 6)
|
||||
var ids := []
|
||||
@@ -91,7 +91,7 @@ func _test_production_rows(db) -> void:
|
||||
# ---- 几何价格梯(V2 价格面单测半边——购买写在 T3 冒烟)----
|
||||
|
||||
func _test_price_ladder(db) -> void:
|
||||
# oracle: design/gdd/systems/跨局循环/锻造工坊.md:220 定价结构:每级价格随已购级数几何递增、基价与公比读表单点(价格梯组)
|
||||
# 判据出处: design/gdd/systems/跨局循环/锻造工坊.md:220 定价结构:每级价格随已购级数几何递增、基价与公比读表单点(价格梯组)
|
||||
var row: Dictionary = db.crossrun.forge.route("sword")
|
||||
var prices := []
|
||||
for owned in range(5):
|
||||
@@ -150,7 +150,7 @@ func _mut_spear_initial(t) -> void:
|
||||
|
||||
|
||||
func _test_negative_cases() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/数据配置.md:199 校验类别全覆盖(负向 schema 组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/数据配置.md:199 校验类别全覆盖(负向 schema 组)
|
||||
# 夹具本身须零违例(否则负向判读失效)
|
||||
var t_ok = ForgeTableScript.new()
|
||||
t_ok.routes = _base_rows()
|
||||
@@ -219,7 +219,7 @@ func _test_negative_cases() -> void:
|
||||
# 32→31:改写族清退批摘 spike(CI run 87 追绿——词表合法收窄的断言随动)。
|
||||
|
||||
func _test_effect_kinds_closed() -> void:
|
||||
# oracle: design/engineering/architecture/architecture.md:89 事件/事实/效果类型/算子四词表封闭(特效闭集组)
|
||||
# 判据出处: design/engineering/architecture/architecture.md「模块归属·基础层」effect_engine 行 事件/事实/效果类型/算子四词表封闭(特效闭集组)
|
||||
Kit.check("效果引擎效果类型闭集 31 种(甲案词表退役 overflow/attack_segments+改写族清退摘 spike)〔锻造工坊批 V11〕",
|
||||
EffectEngineScript.EFFECT_KINDS.size() == 31)
|
||||
Kit.check("效果引擎事件词表在册 21 种(charge_full 位摘名——蓄力层删键批)〔锻造工坊批 V11〕",
|
||||
|
||||
@@ -11,7 +11,7 @@ const Kit := preload("res://tests/unit/test_kit.gd")
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/数据配置.md:198 参数档表装载与就绪态(装载组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/数据配置.md:198 参数档表装载与就绪态(装载组)
|
||||
var db = ConfigDBScript.new()
|
||||
var load_ok: bool = db.load_tables("res://data/tables", true)
|
||||
Kit.check("生产 level_gen_params.tres 随全表加载 + validate 零违例〔fv2〕",
|
||||
@@ -43,7 +43,7 @@ func _table(rows: Array) -> Variant:
|
||||
# ---- 生产三行正向(V2)----
|
||||
|
||||
func _test_production(db) -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/关卡节点图生成.md:127 产出合格:任意参数档产出的图必过产出约束(生产档组)
|
||||
# 判据出处: design/gdd/systems/冒险循环/关卡节点图生成.md:127 产出合格:任意参数档产出的图必过产出约束(生产档组)
|
||||
var r1: Dictionary = db.run.level_gen_params.row_by_level_index(1)
|
||||
var r2: Dictionary = db.run.level_gen_params.row_by_level_index(2)
|
||||
var r3: Dictionary = db.run.level_gen_params.row_by_level_index(3)
|
||||
@@ -61,7 +61,7 @@ func _test_production(db) -> void:
|
||||
# ---- 访问器缺行防御 ----
|
||||
|
||||
func _test_accessor() -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/关卡节点图生成.md:127 参数档访问器读回与产出约束(访问器组)
|
||||
# 判据出处: design/gdd/systems/冒险循环/关卡节点图生成.md:127 参数档访问器读回与产出约束(访问器组)
|
||||
var t = _table([_row(1, 6, 0.3, [0.5, 0.2, 0.2, 0.1], 1, 1, 4)])
|
||||
Kit.check("row_by_level_index 缺行返回空字典〔fv2〕", t.row_by_level_index(9).is_empty())
|
||||
|
||||
@@ -69,7 +69,7 @@ func _test_accessor() -> void:
|
||||
# ---- 域校验逐类负向(V2)----
|
||||
|
||||
func _test_negative() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/数据配置.md:199 校验类别全覆盖:参数档 schema 拒绝(负向组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/数据配置.md:199 校验类别全覆盖:参数档 schema 拒绝(负向组)
|
||||
# 合法基线三行
|
||||
var base := [_row(1, 6, 0.3, [0.5, 0.2, 0.2, 0.1], 1, 1, 4),
|
||||
_row(2, 9, 0.5, [0.5, 0.2, 0.15, 0.15], 2, 2, 6),
|
||||
|
||||
@@ -14,7 +14,7 @@ const BAD_DIR := "user://treasure_fixture_dangling"
|
||||
|
||||
|
||||
func _inject_violation() -> bool:
|
||||
# oracle: design/gdd/systems/表现与支撑/数据配置.md:199 校验类别:悬空引用夹具(悬空引用组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/数据配置.md:199 校验类别:悬空引用夹具(悬空引用组)
|
||||
var path := BAD_DIR + "/reward.tres"
|
||||
var f := FileAccess.open(path, FileAccess.READ)
|
||||
if f == null:
|
||||
@@ -37,7 +37,7 @@ func _inject_violation() -> bool:
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/数据配置.md:200 致命真的会死:悬空引用被拒绝、进程非 0(拒绝组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/数据配置.md:200 致命真的会死:悬空引用被拒绝、进程非 0(拒绝组)
|
||||
if not Kit.copy_tables(SRC_DIR, BAD_DIR) or not _inject_violation():
|
||||
push_error("[reward_dangling_treasure_test] 夹具目录准备失败")
|
||||
quit(2)
|
||||
|
||||
@@ -12,7 +12,7 @@ const TEST_SEED := 20260928 # 夹具种子(任意固定值;语义 = 可复
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/数据配置.md:201 黄金向量:登记向量逐个复算(装载组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/数据配置.md:201 黄金向量:登记向量逐个复算(装载组)
|
||||
_test_derive_determinism()
|
||||
_test_counter_ledger()
|
||||
_test_stream_draw_determinism()
|
||||
@@ -28,7 +28,7 @@ func _fresh_service():
|
||||
|
||||
# 单盐派生纯函数:两全新实例同种子逐位一致(derive_at 不推计数器——纯函数契约)
|
||||
func _test_derive_determinism() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/数据配置.md:201 黄金向量:随机派生纯函数对登记向量逐个复算相等、同输入恒同输出(派生确定性组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/数据配置.md:201 黄金向量:随机派生纯函数对登记向量逐个复算相等、同输入恒同输出(派生确定性组)
|
||||
var a = _fresh_service()
|
||||
var b = _fresh_service()
|
||||
var same := true
|
||||
@@ -44,7 +44,7 @@ func _test_derive_determinism() -> void:
|
||||
|
||||
# 全局取数计数器:唯一账本——open_stream 逐次 +1(scope 单调)
|
||||
func _test_counter_ledger() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/数据配置.md:202 计数器纪律:连续取号跨模拟关卡切换单调不重置(计数账本组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/数据配置.md:202 计数器纪律:连续取号跨模拟关卡切换单调不重置(计数账本组)
|
||||
var rng = _fresh_service()
|
||||
var c0: int = rng.counter()
|
||||
rng.open_stream("armor_roll")
|
||||
@@ -62,7 +62,7 @@ func _test_counter_ledger() -> void:
|
||||
|
||||
# 同种子两实例同标签流的实际取数序列逐位一致(回放确定性契约——存档快照=主种子+计数器的前提)
|
||||
func _test_stream_draw_determinism() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/数据配置.md:201 同输入恒同输出:流取号确定性(流取号组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/数据配置.md:201 同输入恒同输出:流取号确定性(流取号组)
|
||||
var faces := 20 # d20 探针面数(夹具值,非玩法表值——只用于产生可比较序列)
|
||||
var a = _fresh_service()
|
||||
var b = _fresh_service()
|
||||
@@ -77,7 +77,7 @@ func _test_stream_draw_determinism() -> void:
|
||||
|
||||
# 负输入 abs 归一化(警告通道)——|−s| 与 s 同派生
|
||||
func _test_negative_seed_normalization() -> void:
|
||||
# oracle: design/engineering/architecture/architecture.md:90 RngService:master_seed 与各流 scope 计数器单点(种子规约组)
|
||||
# 判据出处: design/engineering/architecture/architecture.md「模块归属·基础层」RngService 行 RngService:master_seed 与各流 scope 计数器单点(种子规约组)
|
||||
var pos = _fresh_service()
|
||||
var neg = RngServiceScript.new()
|
||||
neg.set_master_seed(-TEST_SEED)
|
||||
|
||||
@@ -22,7 +22,7 @@ func _init() -> void:
|
||||
# 单元宿主改由 `_init` 显式调用,断言逐字未动;源 :70 事件日志落盘条经主会话裁决一
|
||||
# 改判跨系统闭环套件——不随本宿主,见迁移账本转办清单)----
|
||||
func _smoke_initialize() -> void:
|
||||
# oracle: design/engineering/architecture/architecture.md:90 RngService autoload:open_stream 有状态门面与启动初始化(启动流组)
|
||||
# 判据出处: design/engineering/architecture/architecture.md「模块归属·基础层」RngService 行 RngService autoload:open_stream 有状态门面与启动初始化(启动流组)
|
||||
print("=== Dungeon Sweeper 垂直切片冒烟 ===")
|
||||
var db = ConfigDBScript.new()
|
||||
var load_ok: bool = db.load_tables("res://data/tables", true)
|
||||
|
||||
@@ -29,7 +29,7 @@ var _db
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/数据配置.md:198 表结构守卫装载与就绪态(装载组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/数据配置.md:198 表结构守卫装载与就绪态(装载组)
|
||||
_db = ConfigDBScript.new()
|
||||
# 表加载失败不中止(沿被迁断言面的源语义:表损坏的红落在具体断言上——
|
||||
# 诊断在此展开,后续断言按坏表实况逐条验红;collect_only=true 下成员照常赋值可评)
|
||||
@@ -50,6 +50,10 @@ func _init() -> void:
|
||||
_smoke_nine_types_gp_stream()
|
||||
_smoke_adventure_table()
|
||||
_smoke_meta()
|
||||
_check_settlement_pin_values()
|
||||
_check_meta_per_level_pins()
|
||||
_check_curio_cap_segment_pins()
|
||||
_check_reward_row_pins()
|
||||
quit(Kit.finish("table_structure_guard_test"))
|
||||
|
||||
|
||||
@@ -92,7 +96,7 @@ func _first_encounter_band(level_index: int, enemy_id: String) -> int:
|
||||
|
||||
# ① 冒险表(单行全局 Rule 4 豁免——建表与接线闸;TP0 令牌池:配额结构/件值对/池总量域检)
|
||||
func _smoke_adventure_table() -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/冒险结构.md:281 冒险结构表校验(冒险表组)
|
||||
# 判据出处: design/gdd/systems/冒险循环/冒险结构.md:281 冒险结构表校验(冒险表组)
|
||||
print("--- 冒险表(AdventureTable——令牌池行值单点:回血/配额/件值对/同调位)---")
|
||||
Kit.check("db.adventure 已加载(adventure.tres 入 manifest)", _db.run.adventure != null)
|
||||
if _db.run.adventure == null:
|
||||
@@ -211,7 +215,7 @@ func _smoke_adventure_table() -> void:
|
||||
# ---- 敌人表深度带键结构(迭代 34 建·L1 混编批换代:30 行键序=覆盖表序·(1,3) 混编行集·Boss 行集带键清单·精英行)----
|
||||
|
||||
func _smoke_enemy_bands() -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/难度曲线与Boss.md:316 难度与敌人带校验(敌人带组)
|
||||
# 判据出处: design/gdd/systems/冒险循环/难度曲线与Boss.md:316 难度与敌人带校验(敌人带组)
|
||||
print("--- 敌人表深度带键结构(30 行键序 · (1,3) 混编 · Boss 带键覆盖表 · 精英行)---")
|
||||
# ① 30 行计数 + 键序 = 覆盖表序(文件行序不变——行序即契约;(1,3) 两行相邻、熊地精在前;精英行随精英专属池批)
|
||||
# 迭代 35 生成图深度扩面覆盖批随动:战斗带顶扩到各关最大生成深度(L1→6/L2→9/L3→13)、
|
||||
@@ -300,7 +304,7 @@ func _smoke_enemy_bands() -> void:
|
||||
# ④ 掷点参数域:档位边界升序/档位值降序无零档/骰面与暴击参数在域(加载校验双保险)。
|
||||
|
||||
func _smoke_ac_swap_guard() -> void:
|
||||
# oracle: design/gdd/systems/跨局循环/锻造工坊.md:219 切换语义:一次冒险=一个武器类型+一个甲型,冒险开始后锁死(AC 换装守卫组)
|
||||
# 判据出处: design/gdd/systems/跨局循环/锻造工坊.md:219 切换语义:一次冒险=一个武器类型+一个甲型,冒险开始后锁死(AC 换装守卫组)
|
||||
print("--- 护甲等级掷点批·换装护栏 ---")
|
||||
# ① 三处无双轨
|
||||
Kit.check("状态表 defense_up 行已退役(无双轨)", _db.combat.status_effects.row_by_id("defense_up").is_empty())
|
||||
@@ -421,7 +425,7 @@ func _smoke_ac_swap_guard() -> void:
|
||||
# ---- 地形变异 框架面(四行两轴×两关 + 门控 + 跨字段互锁)----
|
||||
|
||||
func _smoke_variation_framework() -> void:
|
||||
# oracle: design/gdd/systems/排雷循环/地形变异.md:153 候选池门控与静默零布置(变异框架组)
|
||||
# 判据出处: design/gdd/systems/排雷循环/地形变异.md:153 候选池门控与静默零布置(变异框架组)
|
||||
print("--- 地形变异 框架面(首批四类型 + 候选池门控 + 跨字段互锁)---")
|
||||
Kit.check("变异清单 6 行(荆棘丛/藤蔓缠绕/岩浆/烬覆 + 落石/矿尘——首批类型定稿批 + 第三关内容批;贫矿脉 2026-10-16 用户令屏蔽删行)",
|
||||
_db.combat.variation.rows.size() == 6)
|
||||
@@ -457,7 +461,7 @@ func _smoke_variation_framework() -> void:
|
||||
# ---- 难度曲线与Boss DifficultyTable 结构位冒烟(AC-10:空表零行为 + 套乘点 = 行值快照前)----
|
||||
|
||||
func _smoke_difficulty_table() -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/难度曲线与Boss.md:316 难度修饰套乘(难度表组)
|
||||
# 判据出处: design/gdd/systems/冒险循环/难度曲线与Boss.md:316 难度修饰套乘(难度表组)
|
||||
print("--- 难度曲线与Boss·DifficultyTable 结构位 ---")
|
||||
Kit.check("DifficultyTable MVP 空表加载(结构位)", _db.run.difficulty != null and _db.run.difficulty.rows.is_empty())
|
||||
# ① 空表零行为:敌人系统.spawn 敌值 = EnemyTable 行值原样
|
||||
@@ -488,7 +492,7 @@ func _smoke_difficulty_table() -> void:
|
||||
# ---- ⑤ 格面资源九型 + gp_drop 流(黄金向量)----
|
||||
|
||||
func _smoke_nine_types_gp_stream() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/数据配置.md:199 校验类别全覆盖:九型 GP 流校验(九型流组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/数据配置.md:199 校验类别全覆盖:九型 GP 流校验(九型流组)
|
||||
print("--- 格面资源九型 + gp_drop 流(奖励字典 18 格 + 黄金向量)---")
|
||||
Kit.check("格面资源十型封闭(第九型 gp 九型批 2026-09-20 / 第十型 treasure_item 秘宝内容批 2026-09-29——词表 v1.2)",
|
||||
RewardTable.CELL_TYPES.size() == 10 and RewardTable.CELL_TYPES.has("gp") and RewardTable.CELL_TYPES.has("treasure_item"))
|
||||
@@ -538,7 +542,7 @@ func _smoke_nine_types_gp_stream() -> void:
|
||||
# ---- 元进度·MetaTable 表结构(行值全 provisional 归元进度设计文档配读数;
|
||||
# conversion 覆盖恰三,Boss 两档 first ≥ repeat)----
|
||||
func _smoke_meta() -> void:
|
||||
# oracle: design/gdd/systems/跨局循环/元进度.md:148 元进度行族校验(meta 行族组)
|
||||
# 判据出处: design/gdd/systems/跨局循环/元进度.md:148 元进度行族校验(meta 行族组)
|
||||
print("--- 元进度·MetaTable 表结构 ---")
|
||||
var meta_tbl = _db.crossrun.meta
|
||||
Kit.check("MetaTable 非空 + validate 零错(三行族词表封闭/唯一/连续/域界/first≥repeat/ratio 全过)",
|
||||
@@ -578,7 +582,7 @@ func _conversion_row_of(rows: Array, attr: String) -> Dictionary:
|
||||
# ---- V7 节点图三新键域校验(架构整改——缺新键行拒/width_max 越域拒/两 pct 越域拒/表值全过)----
|
||||
|
||||
func _test_level_gen_new_fields() -> void:
|
||||
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
var tbl = _db.run.level_gen_params
|
||||
var mk := func(mod: Dictionary) -> Dictionary:
|
||||
var row := {"level_index": 1, "layers": 6, "branch_density": 0.3, "battle_ratio": 0.5, "reward_ratio": 0.2,
|
||||
@@ -615,7 +619,7 @@ func _test_level_gen_new_fields() -> void:
|
||||
# ---- 变异表头域校验 / feat cap 必填(架构整改——丁案三件的数据面守卫)----
|
||||
|
||||
func _test_variation_header_domains() -> void:
|
||||
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
var inst = load("res://data/schemas/variation_table.gd").new()
|
||||
inst.percent_of_cells = 5
|
||||
inst.per_level_bonus = 0
|
||||
@@ -633,7 +637,7 @@ func _test_variation_header_domains() -> void:
|
||||
|
||||
|
||||
func _test_feat_cap_required() -> void:
|
||||
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
var inst = load("res://data/schemas/feat_table.gd").new()
|
||||
# 生产表全行 cap 在场(加载期已验——此处显式复核 37 行)
|
||||
var all_cap := true
|
||||
@@ -660,3 +664,73 @@ func _test_feat_cap_required() -> void:
|
||||
var cap_v: int = int(some_feat["cap"])
|
||||
Kit.check("V12 cap_ok 在场行直索引读 cap(首行 %s cap=%d——零兜底路径)〔架构整改 V12〕" % [String(some_feat["feat_id"]), cap_v],
|
||||
deck.cap_ok("relic", String(some_feat["feat_id"])) == (int(deck.taken.get("relic:" + String(some_feat["feat_id"]), 0)) < cap_v))
|
||||
|
||||
|
||||
# ---- G2 结算数值钉(迭代 55「QA覆盖余量收口批」T2——equivalence_ref_damage/p_perfect 钉+p_perfect 读数活性)----
|
||||
|
||||
func _check_settlement_pin_values() -> void:
|
||||
# 判据出处: design/engineering/test-conventions.md:119 (表值钉值——漂移即红)
|
||||
# 判据出处: design/gdd/systems/战斗循环/战斗结算.md:124 (完美第二份兑付读表——接线后该句由声称变属实)
|
||||
var core = _db.combat.settlement.core
|
||||
var roll = _db.combat.settlement.damage_roll
|
||||
Kit.check("equivalence_ref_damage 钉 4(离线审计镜像 tools/clear_dominance_audit.py D_REF 同源——表值漂移即审计口径静默失效)〔QA余 V2〕",
|
||||
int(roll.equivalence_ref_damage) == 4)
|
||||
Kit.check("p_perfect 钉 200(完美兑付载体域 {100,200} 在役档)〔QA余 V2〕", int(core.p_perfect) == 200)
|
||||
# 读数活性断言:注入域内另一档 p_perfect=100 → 完美兑付份数随表值变化(防「接线但弃用」死接线假绿)。
|
||||
# 构造沿 seal_polarity 零兑付段同款(真实编排器;敌人存活首份必兑付——弱形式登记:直调 grant_clear_reward
|
||||
# 构造完美谓词,真值在正常流由封印判定累加)。表值注入限本函数作用域、读后即还原(同进程后续断言免污染)。
|
||||
var old_pp: int = int(core.p_perfect)
|
||||
core.p_perfect = 100
|
||||
var rng = load("res://core/rng_service.gd").new()
|
||||
rng.set_master_seed(20261107)
|
||||
var s = load("res://core/battle_orchestrator.gd").new(_db, rng)
|
||||
var rows: Array = []
|
||||
s.step_row.connect(func(row): rows.append(row))
|
||||
Kit.check("活性断言构造战斗启动(level_index 1 / depth 4 / boss)", bool(s.begin_battle({"level_index": 1, "node_depth": 4, "hero_hp": 80, "hero_max_hp": 80, "node_type": "boss"})))
|
||||
s._facts.perfect_hit_true = 1
|
||||
s._facts.perfect_hit_false = 0
|
||||
s.reward_grant().grant_clear_reward(1, s._facts.board_from_midround, s.perfect_sweep_earned())
|
||||
var dmg := 0
|
||||
for r in rows:
|
||||
if String(r.get("type", "")) == "damage" and String(r.get("source_class", "")) == "clear_reward":
|
||||
dmg += 1
|
||||
Kit.check("p_perfect=100 注入 → 清图奖励单份兑付(clear_reward 伤害行 ×1——第二份不随 perfect 谓词发出)〔QA余 V2〕",
|
||||
dmg == 1)
|
||||
core.p_perfect = old_pp
|
||||
Kit.check("p_perfect 表值还原 200(共享面注入窗口闭合)〔QA余 V2〕", int(_db.combat.settlement.core.p_perfect) == 200)
|
||||
|
||||
|
||||
# ---- G10 换算率钉(迭代 55「QA覆盖余量收口批」T2——per_level_shift_pct 三关档 6/9/1)----
|
||||
|
||||
func _check_meta_per_level_pins() -> void:
|
||||
# 判据出处: design/engineering/test-conventions.md:119 (表值钉值——漂移即红;离线 verifier 消费端同源)
|
||||
var pins := {"max_hp": 6, "atk": 9, "armor_class": 1}
|
||||
for attr in pins.keys():
|
||||
var row: Dictionary = {}
|
||||
for r in (_db.crossrun.meta.conversion as Array):
|
||||
if String(r.get("attr", "")) == String(attr):
|
||||
row = r
|
||||
Kit.check("per_level_shift_pct 钉:%s==%d(换算率三关档——balance_verifier CONVERSION 同源)〔QA余 V16〕" % [String(attr), int(pins[attr])],
|
||||
not row.is_empty() and int(row.get("per_level_shift_pct", -1)) == int(pins[attr]))
|
||||
|
||||
|
||||
# ---- G3 cap 段七槽钉值(迭代 55「QA覆盖余量收口批」T3——与 gp/attack 同款占位形态)----
|
||||
|
||||
func _check_curio_cap_segment_pins() -> void:
|
||||
# 判据出处: design/engineering/test-conventions.md:119 (表值钉值——漂移即红)
|
||||
for kind in ["ap", "armor", "heal", "max_hp", "rap", "refresh", "time_sand"]:
|
||||
Kit.check("curio_cap 七槽钉值:%s==99(案②提前退役占位——99=实质无上限,实数归调参批)〔QA余 V3〕" % kind,
|
||||
int(_db.run.adventure.curio_cap(String(kind))) == 99)
|
||||
|
||||
|
||||
# ---- G8 reward stacks 行钉(迭代 55「QA覆盖余量收口批」T8——生产行 stacks 值面,合成行键未读生产行的缺口补钉)----
|
||||
|
||||
func _check_reward_row_pins() -> void:
|
||||
# 判据出处: design/engineering/test-conventions.md:119 (表值钉值——漂移即红)
|
||||
Kit.check("reward 行扫描面非空(18 行在册——防空表静默绿)", (_db.run.reward.rows as Array).size() > 0)
|
||||
var stacks_drift: Array[String] = []
|
||||
for row in _db.run.reward.rows:
|
||||
if int(row.get("stacks", 1)) != 1:
|
||||
stacks_drift.append(String(row.get("reward_id", "")))
|
||||
Kit.check("reward 生产行 stacks 全 1(schema 缺省值面——合成行 stacks 键的行值主证;行值面变更须自觉换代本钉)〔QA余 V8〕",
|
||||
stacks_drift.is_empty())
|
||||
@@ -15,7 +15,7 @@ const BAD_DIR := "user://treasure_fixture_target_violation"
|
||||
|
||||
|
||||
func _inject_violation() -> bool:
|
||||
# oracle: design/gdd/systems/表现与支撑/数据配置.md:199 校验类别全覆盖:违规夹具逐类注入(真视窄域夹具组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/数据配置.md:199 校验类别全覆盖:违规夹具逐类注入(真视窄域夹具组)
|
||||
var path := BAD_DIR + "/treasure_items.tres"
|
||||
var f := FileAccess.open(path, FileAccess.READ)
|
||||
if f == null:
|
||||
@@ -40,7 +40,7 @@ func _inject_violation() -> bool:
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/gdd/systems/表现与支撑/数据配置.md:200 致命真的会死:违规表被拒绝、进程非 0(违规拒绝组)
|
||||
# 判据出处: design/gdd/systems/表现与支撑/数据配置.md:200 致命真的会死:违规表被拒绝、进程非 0(违规拒绝组)
|
||||
if not Kit.copy_tables(SRC_DIR, BAD_DIR) or not _inject_violation():
|
||||
push_error("[treasure_target_violation_test] 夹具目录准备失败")
|
||||
quit(2)
|
||||
|
||||
@@ -10,7 +10,7 @@ extends SceneTree
|
||||
const Kit := preload("res://tests/unit/test_kit.gd") # T6 收口:check/汇总退出样板单点
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/engineering/test-conventions.md:37 (套件协议模板——执行载体与退出码范例组)
|
||||
# 判据出处: design/engineering/test-conventions.md:37 (套件协议模板——执行载体与退出码范例组)
|
||||
Kit.check("headless SceneTree 协议可用(quit(code) 通道)", _test_headless_protocol())
|
||||
Kit.check("命名规范可执行:test_[scenario]_[expected]", _test_naming_convention_followed())
|
||||
quit(Kit.finish("example_test"))
|
||||
|
||||
@@ -19,7 +19,7 @@ var _bp_path := "user://smoke_node_map_save.json"
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
# 判据出处: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
|
||||
_db = load("res://core/config_db.gd").new()
|
||||
# 表加载失败不中止(承 G2/G3/G4/G5 同款:表损坏的红落在具体断言上)
|
||||
var load_ok: bool = _db.load_tables("res://data/tables", true)
|
||||
@@ -34,7 +34,7 @@ func _init() -> void:
|
||||
|
||||
# ---- 关卡节点图生成·确定性承载断言面 ----
|
||||
func _smoke_generated_map_determinism() -> void:
|
||||
# oracle: design/gdd/systems/冒险循环/关卡节点图生成.md:134 (起关/选路/断点重生成同图组)
|
||||
# 判据出处: design/gdd/systems/冒险循环/关卡节点图生成.md:134 (起关/选路/断点重生成同图组)
|
||||
# ① 纯函数复现:图 = f(参数档, 种子)——同档同种子两次产图逐位同图,换种子必变图
|
||||
var gen = NodeMapGenerator.new(_fresh_rng(910001))
|
||||
var params2: Dictionary = _db.run.level_gen_params.row_by_level_index(2)
|
||||
|
||||
@@ -20,7 +20,7 @@ const CURRENT_FORM := {
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# oracle: design/art/art-bible.md:539 (帧数规格条——样张冻结定值组)
|
||||
# 判据出处: design/art/art-bible.md:539 (帧数规格条——样张冻结定值组)
|
||||
_test_spec_table()
|
||||
_test_current_form()
|
||||
quit(Kit.finish("animation_frame_spec_test"))
|
||||
@@ -28,7 +28,7 @@ func _init() -> void:
|
||||
|
||||
# ① 规格表冻结面(帧数档定值单点=本表)
|
||||
func _test_spec_table() -> void:
|
||||
# oracle: design/art/art-bible.md:539 (帧数规格表冻结组)
|
||||
# 判据出处: design/art/art-bible.md:539 (帧数规格表冻结组)
|
||||
Kit.check("待机循环 4 帧在表〔帧规格 V1〕", SPEC.get("idle", 0) == 4)
|
||||
Kit.check("攻击准备 6 帧单次在表〔帧规格 V2〕", SPEC.get("attack", 0) == 6)
|
||||
Kit.check("受击 2 帧在表〔帧规格 V3〕", SPEC.get("hit", 0) == 2)
|
||||
@@ -36,7 +36,7 @@ func _test_spec_table() -> void:
|
||||
|
||||
# ② 现役单帧形制锚定(真实加载链——资产被换形制而未走规格表时此处红)
|
||||
func _test_current_form() -> void:
|
||||
# oracle: design/art/art-bible.md:539 (现役单帧形制组)
|
||||
# 判据出处: design/art/art-bible.md:539 (现役单帧形制组)
|
||||
for fn: String in CURRENT_FORM:
|
||||
var tex: Texture2D = load("res://assets/art/character/" + fn)
|
||||
var want := Vector2(CURRENT_FORM[fn])
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
uid://c50sjeyp58jcj
|
||||