refactor(迭代35卡②T2·一口): 跨表校验④「关卡节点→敌/板覆盖与多终点 Boss 共带」随静态节点数据消亡整体退役——两条拦截语义改由运行时断言承接:顶 clamp 消解禁=〔v11〕「解析落带 ≥ 请求深度」、多终点 Boss 带键互异=〔v11〕逐终点带键两两比对(拦截面从表内静态代理升级到消费图本体,无削弱);其单测段 _test_level_node_band_rules 与随它而存的四个 preload 一并退役(fv15/fv16/fv17 三条判据的现役等价面在 level_shop_economy〔v11〕与 level_table_validate〔v1〕)
- 覆盖不降级的依据:生成图深度域实测 L1 0–6/L2 0–9/L3 0–13 为模板池深度域(0–4/0–9/0–9)的超集,且 〔v11〕扫描面三档×20 种子逐节点核敌带与板精确行 - save_manager 首次冒险位注释随确定性承载换代改口径(首局固定种子 42+参数档生成图,无兜底模板),键与行为零改动 - run_all 59 套件全绿
This commit is contained in:
1 parent
0fcbcb933b
commit
98c61d50c3
3 files changed
+5
-115
No files matched your search
@@ -51,60 +51,8 @@ func cross_validate(loaded: Dictionary, push_diag: Callable) -> void:
|
||||
for s in required_sources:
|
||||
if not st_tab.source_label_map.has(s):
|
||||
push_diag.call("error", "segorder_violation", "source_label_map 缺必需键: %s" % s)
|
||||
# ④ LevelTable 节点悬空引用(battle/boss 节点:board_gen 侧须有精确行;enemy 侧按
|
||||
# (关卡序号, 行类) 带寻址——落带即设计语义,节点深度高于顶带键=顶 clamp 消解属调参事故,加载期直接拦。
|
||||
# 同关同一张图多终点 Boss 节点解析带键须互异(共带=两终点命中同一 Boss 行集,「走哪条路打谁」落空))
|
||||
var lv: LevelTable = loaded.get("level")
|
||||
var bg: BoardGenTable = loaded.get("board_gen")
|
||||
var en: EnemyTable = loaded.get("enemy")
|
||||
if lv != null and bg != null and en != null:
|
||||
var bg_keys := {}
|
||||
for row in bg.rows:
|
||||
bg_keys["%d_%d" % [int(row.level_index), int(row.node_depth)]] = true
|
||||
var en_bands := {} # "%d_%s" % [L, row_kind] → 升序带键数组
|
||||
for row in en.rows:
|
||||
var ek := "%d_%s" % [int(row.level_index), String(row.get("row_kind", ""))]
|
||||
if not en_bands.has(ek):
|
||||
en_bands[ek] = []
|
||||
var eb := int(row.get("depth_band", -1))
|
||||
if not (en_bands[ek] as Array).has(eb):
|
||||
(en_bands[ek] as Array).append(eb)
|
||||
for ek in en_bands.keys():
|
||||
(en_bands[ek] as Array).sort()
|
||||
for lrow in lv.rows:
|
||||
var li: int = int(lrow.level_index)
|
||||
# 模板池感知(关卡节点图生成 v0.4——池内全模板跨表校验,不只 row.nodes)
|
||||
var node_lists: Array[Array] = []
|
||||
var tpls: Array[Variant] = lrow.get("templates", [])
|
||||
if tpls.is_empty():
|
||||
node_lists.append(lrow.get("nodes", []))
|
||||
else:
|
||||
for tpl in tpls:
|
||||
node_lists.append(tpl.get("nodes", []))
|
||||
for tpl_i in range(node_lists.size()):
|
||||
var boss_bands := {} # 本图已占 Boss 带键
|
||||
for node in node_lists[tpl_i]:
|
||||
var t: String = String(node.get("type", ""))
|
||||
if t != "battle" and t != "boss":
|
||||
continue
|
||||
var nd: int = int(node.get("node_depth", -1))
|
||||
var key := "%d_%d" % [li, nd]
|
||||
if not bg_keys.has(key):
|
||||
push_diag.call("error", "level_node_dangling", "level %d %s 节点 (%s) 在 board_gen 无精确行" % [li, t, key])
|
||||
var nk := "%d_%s" % [li, t]
|
||||
if not en_bands.has(nk):
|
||||
push_diag.call("error", "level_node_dangling", "level %d %s 节点 (%d) 在 enemy 无该行类行集" % [li, t, nd])
|
||||
continue
|
||||
var hit_band: int = EnemyTable.band_of(en_bands[nk], nd)
|
||||
if hit_band < 0:
|
||||
push_diag.call("error", "level_node_dangling", "level %d %s 节点深度 %d 高于 (level %d, %s) 顶带键 %d——顶 clamp 消解" % [
|
||||
li, t, nd, li, t, int((en_bands[nk] as Array)[-1])])
|
||||
continue
|
||||
if t == "boss":
|
||||
if boss_bands.has(hit_band):
|
||||
push_diag.call("error", "rowset_rule", "level %d 模板 %d 多终点 Boss 节点同带(band %d)——共带使「走哪条路打谁」落空" % [li, tpl_i, hit_band])
|
||||
boss_bands[hit_band] = true
|
||||
# ⑤ 技能池引用完整性(敌人技能池批——敌人表池 × 技能表;独立入口 validate_skill_pool_links 供冒烟直验)
|
||||
var en: EnemyTable = loaded.get("enemy")
|
||||
var skt = loaded.get("skill")
|
||||
if skt != null and en != null:
|
||||
for e in validate_skill_pool_links(skt, en, loaded.get("status_effects")):
|
||||
|
||||
@@ -299,9 +299,10 @@ func _default_data() -> Dictionary[String, Variant]:
|
||||
}
|
||||
|
||||
|
||||
# ---- 首次冒险布尔位(关卡节点图生成 v0.4)----
|
||||
# false = 首次脚本模式(全关零抽签取池内模板 0=现行固定图);置位只在冒险收束挂点(MetaProgression.settle_adventure)。
|
||||
# 老档迁移:缺字段 = 该档已有游玩史 → true(不重放教学脚本)。
|
||||
# ---- 首次冒险布尔位(关卡节点图生成 v1.1 确定性承载)----
|
||||
# false = 本档尚无游玩史 → 首局走固定种子 42 + 参数档生成图(教学与存档复现的固定图由此承载,无兜底模板);
|
||||
# 置位只在冒险收束挂点(MetaProgression.settle_adventure)。
|
||||
# 老档迁移:缺字段 = 该档已有游玩史 → true(不重放首局固定图)。
|
||||
|
||||
func is_first_adventure_done() -> bool:
|
||||
return bool(_data["meta"].get("first_adventure_done", true))
|
||||
|
||||
@@ -16,7 +16,6 @@ func _init() -> void:
|
||||
_test_all_links_valid()
|
||||
_test_dangling_skill_id()
|
||||
_test_cast_side_mismatch()
|
||||
_test_level_node_band_rules()
|
||||
quit(Kit.finish("config_db_cross_validator_test"))
|
||||
|
||||
|
||||
@@ -83,61 +82,3 @@ func _test_cast_side_mismatch() -> void:
|
||||
var status := _status([{"effect_id": "st_hero_side", "target_side": "hero"}])
|
||||
var errs: Array = ValidatorScript.new().validate_skill_pool_links(skills, enemies, status)
|
||||
Kit.check("buff 挂主角侧状态被报(施放类 side 守卫)", _cats(errs).has("cast_side"))
|
||||
|
||||
|
||||
# ---- ④ 节点 × 敌人表带寻址(单终点零误报/同带双 Boss 报错/顶 clamp 消解报错)----
|
||||
const LevelTableScript := preload("res://data/schemas/level_table.gd")
|
||||
const BoardGenTableScript := preload("res://data/schemas/board_gen_table.gd")
|
||||
const RewardTableScript := preload("res://data/schemas/reward_table.gd")
|
||||
const SettlementTableScript := preload("res://data/schemas/settlement_table.gd")
|
||||
|
||||
|
||||
func _loaded(level_nodes: Array, bg_depths: Array, boss_bands: Array, battle_bands: Array) -> Dictionary:
|
||||
var lv := LevelTableScript.new()
|
||||
lv.rows.assign([{"level_index": 1, "display_name": "夹具关", "templates": [{"nodes": level_nodes}]}])
|
||||
var bg := BoardGenTableScript.new()
|
||||
var bg_rows: Array = []
|
||||
for d in bg_depths:
|
||||
bg_rows.append({"level_index": 1, "node_depth": int(d), "board_width": 10})
|
||||
bg.rows.assign(bg_rows)
|
||||
var en := EnemyTableScript.new()
|
||||
var en_rows: Array = []
|
||||
for b in battle_bands:
|
||||
en_rows.append({"level_index": 1, "row_kind": "battle", "depth_band": int(b), "enemy_id": "goblin",
|
||||
"unit_count": 1, "hp": 10, "atk": 2, "pool": [{"skill_id": "attack", "weight": 9}]})
|
||||
for b in boss_bands:
|
||||
en_rows.append({"level_index": 1, "row_kind": "boss", "depth_band": int(b), "enemy_id": "ogre_chief",
|
||||
"unit_count": 1, "hp": 10, "atk": 2, "pool": [{"skill_id": "attack", "weight": 9}]})
|
||||
en.rows.assign(en_rows)
|
||||
var st := SettlementTableScript.new()
|
||||
st.source_label_map.assign({"safe_cell": "a", "seal_hit": "b", "mine_hit": "c", "clear_reward": "d",
|
||||
"hero_attack": "e", "enemy_action": "f", "seal_miss": "g"})
|
||||
return {"status_effects": StatusTableScript.new(), "reward": RewardTableScript.new(),
|
||||
"settlement": st, "level": lv, "board_gen": bg, "enemy": en}
|
||||
|
||||
|
||||
func _cross_cats(loaded: Dictionary, needle: String) -> Array[String]:
|
||||
var out: Array[String] = []
|
||||
ValidatorScript.new().cross_validate(loaded,
|
||||
func(_sev, cat, msg) -> void:
|
||||
if String(msg).contains(needle):
|
||||
out.append(String(cat)))
|
||||
return out
|
||||
|
||||
|
||||
func _test_level_node_band_rules() -> void:
|
||||
# 正向:单终点三关同构(battle 节点落 band 0、boss 节点精确落顶带)→ ④ 零误报
|
||||
var ok_nodes := [{"type": "battle", "node_depth": 0}, {"type": "boss", "node_depth": 9}]
|
||||
var ok_cats := _cross_cats(_loaded(ok_nodes, [0, 9], [9], [0]), "level 1")
|
||||
Kit.check("单终点图 ④ 敌侧带寻址零误报〔fv17〕", ok_cats.is_empty())
|
||||
# 负向:一路两终点落同一 Boss 带(深度 3/4 都落带 4)→ rowset_rule
|
||||
var same_band_nodes := [{"type": "battle", "node_depth": 0},
|
||||
{"type": "boss", "node_depth": 3}, {"type": "boss", "node_depth": 4}]
|
||||
var same_band_cats := _cross_cats(_loaded(same_band_nodes, [0, 3, 4], [4, 9], [0]), "同带")
|
||||
Kit.check("同关双终点共带报 rowset_rule〔fv15〕",
|
||||
same_band_cats.has("rowset_rule"))
|
||||
# 负向:Boss 节点深度高于顶带键(顶 clamp 消解)→ level_node_dangling
|
||||
var over_nodes := [{"type": "battle", "node_depth": 0}, {"type": "boss", "node_depth": 10}]
|
||||
var over_cats := _cross_cats(_loaded(over_nodes, [0, 10], [9], [0]), "顶 clamp 消解")
|
||||
Kit.check("Boss 节点越顶带报 level_node_dangling〔fv16〕",
|
||||
over_cats.has("level_node_dangling"))
|
||||
Reference in new issue
Block a user