iter41-卡1:核心数据面结构收敛批:85键全清——三族拆域+config_db归位容器+存档拆件+meta瘦身+adventure拆段+spawn束化;行为零变化,8新件在阈

This commit is contained in:
DKChan committed 2026-10-29 01:33:50 +08:00
1 parent bda377eb16
commit a613a3cb90
157 files changed
+7453 -4288

No files matched your search

@@ -64,7 +64,7 @@ func _test_boss_name_uses_boss_rowset() -> void:
if not loaded:
return
var boss_row := {}
for row in db.enemy.rows:
for row in db.combat.enemy.rows:
if int(row.level_index) == 1 and String(row.get("row_kind", "")) == "boss":
boss_row = row
break
@@ -72,8 +72,8 @@ func _test_boss_name_uses_boss_rowset() -> void:
if boss_row.is_empty():
return
var boss_depth: int = int(boss_row.depth_band)
var boss_rows: Array = db.resolve_enemy_rowset(1, boss_depth, "boss")
var battle_rows: Array = db.resolve_enemy_rowset(1, boss_depth, "battle")
var boss_rows: Array = db.combat.enemy.resolve_rowset(1, boss_depth, "boss")
var battle_rows: Array = db.combat.enemy.resolve_rowset(1, boss_depth, "battle")
var boss_expect := String(boss_rows[0].get("display_name", ""))
var battle_name := String(battle_rows[0].get("display_name", ""))
var runner := _BossRunner.new()
@@ -58,12 +58,14 @@ func _run() -> void:
and rows.has("light") and rows.has("heavy") and rows.has("ward"))
# ③ 初始持有行(长剑/轻甲):购买钮现形带几何价、选中钮=当前禁用
# 两行同取谓词并集(轻甲行补 select_btn 非空 + buy_btn 可用两子句——原件少写,只增不减)
for pair in [["sword", "长剑"], ["light", "轻甲"]]:
var r: Dictionary = rows.get(pair[0], {})
Kit.check("%s行:购买钮「锻造 20」(0 级位几何价)+选中钮「当前」禁用〔锻造工坊批 V13〕" % pair[1],
r.get("buy_btn") != null and String(r.buy_btn.text) == "锻造 20" and not r.buy_btn.disabled
and r.get("select_btn") != null and String(r.select_btn.text) == "当前" and r.select_btn.disabled)
var rsw = rows.get("sword", {})
var rli = rows.get("light", {})
Kit.check("长剑行:购买钮「锻造 20」(0 级位几何价)+选中钮「当前」禁用〔锻造工坊批 V13〕",
rsw.get("buy_btn") != null and String(rsw.buy_btn.text) == "锻造 20" and not rsw.buy_btn.disabled
and rsw.get("select_btn") != null and String(rsw.select_btn.text) == "当前" and rsw.select_btn.disabled)
Kit.check("轻甲行:购买钮「锻造 20」+选中钮「当前」禁用〔锻造工坊批 V13〕",
rli.get("buy_btn") != null and String(rli.buy_btn.text) == "锻造 20"
and String(rli.select_btn.text) == "当前" and rli.select_btn.disabled)
# ④ 待解锁行(长枪/法杖/重甲/魔法护甲):解锁钮 200、无购买钮、无选中钮、状态含双通道文案
var rsp = rows.get("spear", {})
@@ -83,7 +85,7 @@ func _run() -> void:
rows.sword.buy_btn.disabled and rows.spear.unlock_btn.disabled)
# ⑥ 满级行:无购买钮、状态=已满级(直置存档域构造——两分法调试口径)
sv.set_forge_route_level("sword", 5)
sv.meta_state.set_forge_route_level("sword", 5)
ov.refresh_camp({"balance": 500, "forge": mp.forge_shop_status(),
"tree": [{"node_id": "forge_workshop", "level": 1}]})
rows = _row_by_id(ov)
@@ -51,8 +51,8 @@ func _init() -> void:
if FileAccess.file_exists(p):
DirAccess.remove_absolute(p)
var sm = main.get_node("/root/SaveManager")
sm.attach_path("user://test_pacing_queue_save.json")
sm.mark_first_adventure_done()
sm.slots.attach_path("user://test_pacing_queue_save.json")
sm.meta_state.mark_first_adventure_done()
# 行信号计数器(真实信号路径旁挂计数——零触碰既有接线)
var rows_seen: Array = []
main._start_adventure_from_menu()
@@ -56,8 +56,10 @@ func _sweep_narrator() -> void:
var head := String(narrator.node_curio_header())
Kit.check("奖励点头部含「冒险牌库」(u3 机制事实①同库)〔wa4〕", head.contains("冒险牌库"))
Kit.check("奖励点头部含「属性值提升与补给」(u3 机制事实②渠道分流)〔wa4〕", head.contains("属性值提升与补给"))
for w in ["同权重", "同上限", "提升池"]:
Kit.check("奖励点头部零「%s」旧话(u3 清零)〔wa4〕" % w, not head.contains(w))
Kit.check("奖励点头部含「同库分流」(u3 机制事实②措辞)〔wa4〕", head.contains("同库分流"))
Kit.check("奖励点头部零「同权重」旧话(u3 清零)〔wa4〕", not head.contains("同权重"))
Kit.check("奖励点头部零「同上限」旧话(u3 清零)〔wa4〕", not head.contains("同上限"))
Kit.check("奖励点头部零「提升池」旧话(u3 清零)〔wa4〕", not head.contains("提升池"))
Kit.check("专长卡兜底==「专长\n???」(u11 换代——数据缺行真实兜底路径)〔wa3〕",
String(narrator.feat_card_text({})) == "专长\n???")
@@ -70,11 +72,11 @@ func _sweep_legend() -> void:
if String(e[1]) == "封印":
seal_row = e
break
Kit.check("图例封印行存在(wa5 夹具前提)〔wa5〕", not seal_row.is_empty())
Kit.check("图例封印行含「标空」(u4——与「标中」成对)〔wa5〕", String(seal_row[2] if seal_row.size() > 2 else "").contains("标空"))
Kit.check("图例封印行含「受挖掘惩罚」(u4 雷字清零换代)〔wa5〕", String(seal_row[2] if seal_row.size() > 2 else "").contains("受挖掘惩罚"))
for w in ["插错", "挖雷"]:
Kit.check("图例封印行零「%s」〔wa5〕" % w,
not String(seal_row[2] if seal_row.size() > 2 else "").contains(w))
Kit.check("图例封印行零「插错」(u4 旗族遗留词干清零)〔wa5〕", not String(seal_row[2] if seal_row.size() > 2 else "").contains("插错"))
Kit.check("图例封印行零「挖雷」(u4 雷字清零)〔wa5〕", not String(seal_row[2] if seal_row.size() > 2 else "").contains("挖雷"))
# ⑤⑥ 来源名双源(u5/u6——状态槽消费半+表值 ConfigDB 真装载+battle_log 行消费半)
@@ -94,7 +96,7 @@ func _sweep_source_names() -> void:
row_view._tip_label.text.contains("来源:危险格"))
row_view._hide_tip()
Kit.check("结算表 source_label_map[mine_hit]==「危险格」(u6 换代——ConfigDB 真装载)〔wa7〕",
String(_db.settlement.core.source_label_map.get("mine_hit", "")) == "危险格")
String(_db.combat.settlement.core.source_label_map.get("mine_hit", "")) == "危险格")
var log_view = load("res://presentation/views/battle_log_view.gd").new(_db)
var row_txt := String(log_view._format_row({"type": "effect", "source_class": "mine_hit",
"display_name": "挖掘反噬", "value": -5, "pool_value": -5, "round_index": 1}))
@@ -81,7 +81,7 @@ func _init() -> void:
stage.on_step_row_fx({"type": "damage", "target": "地精", "target_id": "goblin_b"})
await create_timer(0.6).timeout # 弹道到达拍 0.33s + 余量
Kit.check("伤害行按 target_id 命中正确站桩(闪白落点)〔wd12〕",
s2.fx_probe().flash_count == 1 and s1.fx_probe().flash_count == 0)
s2._flash_count == 1 and s1._flash_count == 0)
# 口二:enemy_standee_center —— id 先、display_name 兜底
var c_b = stage.enemy_standee_center("goblin_b", "地精")
var c_b_expect: Vector2 = stage.world_to_shell(s2.position + s2.box * 0.5)
@@ -91,16 +91,14 @@ func _init() -> void:
Kit.check("无 target_id 时按名兜底同旧行为(首命中)〔wd12〕", c_fallback != null and c_fallback == c_fallback_expect)
# ---- ① 意图蓄力档可达(d2)----
# 蓄力判据表:[kind, skill_id, 期望 _intent_charge, 个案标签]
var charge_cases := [
["damage", "heavy", true, "蓄力技能(heavy)置 _intent_charge=true"],
["damage", "goblin_slash", false, "非蓄力攻击技(goblin_slash)零置位"],
["buff", "goblin_rage", false, "增益系零置位"],
["damage", "basic", false, "普攻零表示路径复位 _intent_charge"],
]
for c in charge_cases:
s1.set_pending(c[0], c[1])
Kit.check("%s〔wd2〕" % c[3], s1._intent_charge == c[2])
s1.set_pending("damage", "heavy")
Kit.check("蓄力技能(heavy)置 _intent_charge=true〔wd2〕", s1._intent_charge)
s1.set_pending("damage", "goblin_slash")
Kit.check("非蓄力攻击技(goblin_slash)零置位〔wd2〕", not s1._intent_charge)
s1.set_pending("buff", "goblin_rage")
Kit.check("增益系零置位〔wd2〕", not s1._intent_charge)
s1.set_pending("damage", "basic")
Kit.check("普攻零表示路径复位 _intent_charge〔wd2〕", not s1._intent_charge)
# ---- ② 庆祝 tween 句柄纪律(d4)----
var ov = BattleEndScript.new()
@@ -27,7 +27,7 @@ func _init() -> void:
return
# board_type 词表自 board_gen 行集推导(禁测试侧写死词表)
var types: Array = []
for row in db.board_gen.rows:
for row in db.combat.board_gen.rows:
var bt := String(row.get("board_type", ""))
if bt != "" and not types.has(bt):
types.append(bt)
@@ -611,6 +611,18 @@ func _smoke_button_five_states() -> void:
is_equal_approx((dds as StyleBoxFlat).bg_color.a, 0.45))
Kit.check("V1 危险钮 disabled≠pressed", _sb_content_differs(dpr, dds))
db.free()
# V2 主题红线维持(Button 类型词条零接触+focus 族词条零定义)——与 _smoke_global_theme 同判据复述
var th := ThemeDB.get_project_theme()
if th != null:
Kit.check("V2 红线维持 Button 类型词条零接触", not th.get_type_list().has("Button"))
var focus_hits := ""
for t in th.get_type_list():
for nm in (th.get_stylebox_list(t) + th.get_icon_list(t) + th.get_color_list(t)
+ th.get_font_list(t) + th.get_font_size_list(t) + th.get_constant_list(t)):
if "focus" in String(nm):
focus_hits += "%s/%s " % [t, nm]
Kit.check("V2 红线维持 focus 族词条零定义%s" % ("" if focus_hits == "" else "(违例:" + focus_hits + ")"),
focus_hits == "")
# ---- 全局纹理过滤冒烟(工程设置 Nearest+平滑渐变件 LINEAR 白名单;V3)----
@@ -819,21 +831,21 @@ func _career_overlay_projection() -> void:
var mp = load("res://core/meta_progression.gd").new(_db, sv)
# 已玩态夹具:两局收束(一通一止)+ 钥石非零——投影可辨非全零白板
# (生涯页不统计天赋树与账号成长态,只统计终身计数)
sv.add_keystone(45)
sv.apply_adventure_summary({"result": "completed", "cleared": true, "levels_cleared": 3,
sv.meta_state._add_keystone(45)
sv.meta_state.apply_adventure_summary({"result": "completed", "cleared": true, "levels_cleared": 3,
"stopped_at": 0, "keystone_total": 0, "master_seed": 21})
sv.apply_adventure_summary({"result": "abandoned", "cleared": false, "levels_cleared": 1,
sv.meta_state.apply_adventure_summary({"result": "abandoned", "cleared": false, "levels_cleared": 1,
"stopped_at": 2, "keystone_total": 0, "master_seed": 22})
sv.write_to_disk()
# 投影拼装(HudPresenter.career_data 同构)+ 视图喂入
var data := {"total": sv.adventure_total(), "cleared": sv.adventure_cleared(),
"clears": [], "boss_first_kills": 0, "boss_first_total": _db.level_count(),
"boss_kills": sv.boss_kill_count(), "keystone": mp.keystone_balance()}
for i in _db.level_count():
var data := {"total": sv.meta_state.career_state().adventure_total, "cleared": sv.meta_state.career_state().adventure_cleared,
"clears": [], "boss_first_kills": 0, "boss_first_total": _db.run.level.level_count(),
"boss_kills": sv.meta_state.career_state().boss_kill_count, "keystone": mp.meta_state.keystone_balance()}
for i in _db.run.level.level_count():
var li: int = i + 1
data["clears"].append({"display_name": String(_db.level_row(li).get("display_name", "")),
"count": sv.clear_count(li)})
if sv.boss_first_kill_marked(li):
data["clears"].append({"display_name": String(_db.run.level.row_by_level_index(li).get("display_name", "")),
"count": sv.meta_state.career_state().clear_counts.get(str(li), 0)})
if sv.meta_state.career_state().boss_first_kill.has(str(li)):
data["boss_first_kills"] += 1
var co = load("res://presentation/views/career_overlay.gd").new()
co.open_career(data)
@@ -850,7 +862,7 @@ func _career_overlay_projection() -> void:
Kit.check("Boss 首杀/总杀投影 = 首杀标记计数 + boss_kill_count()(%s / %s)" % [co._boss_first_label.text, co._boss_kills_label.text],
String(co._boss_first_label.text) == "3/3 关" and String(co._boss_kills_label.text) == "4 次")
Kit.check("钥石余额徽投影 = keystone_balance() 读数(%s)" % co._keystone_badge.value_label.text,
String(co._keystone_badge.value_label.text) == "钥石 %d" % mp.keystone_balance())
String(co._keystone_badge.value_label.text) == "钥石 %d" % mp.meta_state.keystone_balance())
# 口径换代(2026-10-26):行标签换代钉子+生涯页零「通关」残留(负断言兜全页)
var career_label_texts: Array[String] = []
for career_lbl: Label in _all_labels(co):
@@ -869,14 +881,14 @@ func _career_overlay_projection() -> void:
fv5c.store_string(JSON.stringify({"version": 5, "meta": {"keystone_balance": 3}}))
fv5c.close()
Kit.check("v5 现役档升 v6 载入成功(计数域缺省补零)",
v5.load_from_disk() and v5.adventure_total() == 0 and v5.adventure_cleared() == 0)
v5.load_from_disk() and v5.meta_state.career_state().adventure_total == 0 and v5.meta_state.career_state().adventure_cleared == 0)
var mp_v5 = load("res://core/meta_progression.gd").new(_db, v5)
var data_v5 := {"total": v5.adventure_total(), "cleared": v5.adventure_cleared(),
"clears": [], "boss_first_kills": 0, "boss_first_total": _db.level_count(),
"boss_kills": v5.boss_kill_count(), "keystone": mp_v5.keystone_balance()}
for i in _db.level_count():
data_v5["clears"].append({"display_name": String(_db.level_row(i + 1).get("display_name", "")),
"count": v5.clear_count(i + 1)})
var data_v5 := {"total": v5.meta_state.career_state().adventure_total, "cleared": v5.meta_state.career_state().adventure_cleared,
"clears": [], "boss_first_kills": 0, "boss_first_total": _db.run.level.level_count(),
"boss_kills": v5.meta_state.career_state().boss_kill_count, "keystone": mp_v5.meta_state.keystone_balance()}
for i in _db.run.level.level_count():
data_v5["clears"].append({"display_name": String(_db.run.level.row_by_level_index(i + 1).get("display_name", "")),
"count": v5.meta_state.career_state().clear_counts.get(str(i + 1), 0)})
var co_v5 = load("res://presentation/views/career_overlay.gd").new()
co_v5.open_career(data_v5)
Kit.check("旧档载入后生涯页正常显示(v5 升 v6:总局数/冒险成功局数显示 0,既有钥石余额原样呈现)",
+10 -3
View File
@@ -64,10 +64,12 @@ func _test_grouping_closed() -> void:
for bad in ["keystone", "", "ATTACK", "aoe", "blast_mine", " heal"]:
Kit.check("清单外值「%s」→ GLOW_NONE(禁静默空光)" % bad,
UiKitScript.glow_group_for_cell_type(bad) == UiKitScript.GLOW_NONE)
# 与卡顶带 tg_band 的归组差异锚定(Q3 甲案两带并存、禁互引):blast 在卡顶带归魔法(E5 回滚既存行为)
# ——格面带 blast→群伤由上方九型封闭循环的 TYPE_EXPECT["blast"] 承载,此处不重述
# 与卡顶带 tg_band 的归组差异锚定(Q3 甲案两带并存、禁互引):blast 在格面带归群伤、
# 在卡顶带归魔法(E5 回滚既存行为)——若将来误把两带统一,本断言红
var band_blast: Color = UiKitScript.tg_band("blast_charge")
Kit.check("两带并存锚定:卡顶带 blast→TG_MAGIC(E5 回滚既存行为未动)", band_blast == UiKitScript.TG_MAGIC)
Kit.check("两带并存锚定:格面微光 blast→群伤组(与卡顶带刻意不同)",
UiKitScript.glow_group_for_cell_type("blast") == UiKitScript.GLOW_AOE)
# 色值出口:五组各取本组常量;无组/越界组号 → 全透明(不画)
Kit.check("glow_color_for_group 群伤组 = TG_GLOW_AOE",
UiKitScript.glow_color_for_group(UiKitScript.GLOW_AOE) == UiKitScript.TG_GLOW_AOE)
@@ -90,8 +92,13 @@ func _test_frozen_hue_face() -> void:
var ds: float = absf(m.s - f.s)
Kit.check("冻结色相保持:%s ΔH=%.3f° ≤0.2° 且 ΔS=%.4f ≤0.01(只动明度档)" % [g, dh * 360.0, ds],
dh <= 0.2 / 360.0 and ds <= 0.01)
# 群伤组专锚(#A61D37):V=0.65 明度档——H/S 两腿由上方逐组循环的 ΔH/ΔS 阈承载
# 群伤组专锚(#A61D37):H/S 与冻结逐位一致、V=0.65 档
var aoe_f: Color = FROZEN["AOE"]
var aoe_m: Color = UiKitScript.TG_GLOW_AOE
Kit.check("群伤组专项:H 与冻结 #FF2D55 差 %.4f°(零移动)" % (absf(aoe_m.h - aoe_f.h) * 360.0),
absf(aoe_m.h - aoe_f.h) < 0.001)
Kit.check("群伤组专项:S 与冻结差 %.4f ≤0.01(8bit 量化噪声级)" % absf(aoe_m.s - aoe_f.s),
absf(aoe_m.s - aoe_f.s) <= 0.01)
Kit.check("群伤组专项:V=0.65 明度档( provisional 域内)", absf(aoe_m.v - 166.0 / 255.0) < 0.002)
@@ -15,19 +15,10 @@ func _init() -> void:
var f: Font = UiKitScript.load_font(path)
Kit.check("随包字体守卫装载非空(res://assets/fonts/ui_font_fusion_pixel_zh_hans.ttf)", f != null)
if f != null:
# 字形集表:[个案标签, 须在集字符]——缺字明细进消息
var glyph_groups := [
["中文字形在集(攻/雷/旗/封)", "攻雷旗封"],
["中文字形在集(危——已开危险格字形换代前置,迭代36 u12/wa9)", "危"],
["ASCII/数字字形在集(A/0/%)", "A0%"],
]
for g in glyph_groups:
var missing := ""
for i in String(g[1]).length():
var ch := String(g[1])[i]
if not f.has_char(ch.unicode_at(0)):
missing += ch
Kit.check("%s(缺=%s)" % [g[0], missing if missing != "" else "无"], missing == "")
Kit.check("中文字形在集(攻/雷/旗/封)", f.has_char("攻".unicode_at(0)) and f.has_char("雷".unicode_at(0)) \
and f.has_char("旗".unicode_at(0)) and f.has_char("封".unicode_at(0)))
Kit.check("中文字形在集(危——已开危险格字形换代前置,迭代36 u12/wa9)", f.has_char("危".unicode_at(0)))
Kit.check("ASCII/数字字形在集(A/0/%)", f.has_char("A".unicode_at(0)) and f.has_char("0".unicode_at(0)) and f.has_char("%".unicode_at(0)))
var sz := 18
var w_cn: float = f.get_string_size("战斗胜利", HORIZONTAL_ALIGNMENT_LEFT, -1, sz).x
var w_label: float = f.get_string_size("攻击 2", HORIZONTAL_ALIGNMENT_LEFT, -1, sz).x