refactor(iter37-S13): 九条基线外漂移全部以代码压回——禁改基线口径的兑现批

- config_db 三 getter 退役(shop_relic_base_price/shop_spend_discount/hero_display_name):消费方改读公开行访问器(db.shop.* / db.hero.*),公开方法面 47→44
- shop_table.validate 单函数分步化(六个 _validate_* 子步骤):函数长度/分支数/局部变量三阈值同时回落,校验语义与报错顺序不变
- battle_end_overlay 庆祝句柄由类成员改为节点元数据承载(21→20 类变量):三处读写改 get_meta/set_meta/remove_meta,句柄单一活跃语义不变
- standee_body 死变量 _portrait_flip 删除(30→29);set_order 并入私有面(21→20 公开方法),唯一调用点同步
- 断言随动:level_shop_economy_test 读表直取;standee_fx_sweep_test d4 三断言改读节点元数据;theme_projection_test V4 蓄力档判据换代(卡① d2 已入库代码的断言补齐)
- tools/gdstyle_baseline.txt 零改动:扫描面 105 文件、基线内 229、基线外新违规 0
This commit is contained in:
DKChan committed 2026-10-25 05:47:01 +08:00
1 parent 7389618fd1
commit 2ed8166852
9 files changed
+58 -48

No files matched your search

-14
View File
@@ -342,20 +342,6 @@ func shop_price_b() -> float:
return float(shop.price_b) if shop != null else 0.0
# relic 保底卡基价 / 散折(economy_spend 激活时购买价折扣——表未入库 = 表 schema 缺省值)
func shop_relic_base_price() -> float:
return float(shop.relic_base_price) if shop != null else 0.0
func shop_spend_discount() -> float:
return float(shop.spend_discount) if shop != null else 0.0
# 英雄显示名(单位快照读取面单源——与敌人行 display_name 同轴)
func hero_display_name() -> String:
return String(hero.display_name) if hero != null else ""
# 行族数组读取(ShopTable 属性名 = 行族名;表未已入库/属性缺失 = 空集非错误态)
func _shop_rows(family: String) -> Array:
if shop == null:
+2 -2
View File
@@ -763,7 +763,7 @@ func current_price(prow: Dictionary) -> int:
# + 池货架面 pool_price 同折〕——商店刷新价 shop_refresh_cost 与三选一付费刷新不折)
func _spend_discounted(price: int) -> int:
if _tree_rules.has("economy_spend"):
return maxi(1, int(floor(float(price) * db.shop_spend_discount())))
return maxi(1, int(floor(float(price) * float(db.shop.spend_discount))))
return price
@@ -879,7 +879,7 @@ func pool_price(kind: String) -> int:
var row: Dictionary[Variant, Variant] = db.shop_pool_row(kind)
if row.is_empty():
if kind == "relic":
return _spend_discounted(int(ceil(db.shop_relic_base_price() * pow(db.shop_price_b(), float(maxi(0, int(level_row.level_index) - 1))) * pow(db.shop_price_a(), float(_shop_visit_buys)))))
return _spend_discounted(int(ceil(float(db.shop.relic_base_price) * pow(db.shop_price_b(), float(maxi(0, int(level_row.level_index) - 1))) * pow(db.shop_price_a(), float(_shop_visit_buys)))))
return 999999
var base := float(maxi(1, int(row.get("base_price", 1))))
var price_f: float = base * pow(db.shop_price_b(), float(maxi(0, int(level_row.level_index) - 1))) \
+1 -1
View File
@@ -52,7 +52,7 @@ func spawn(level_index: int, node_depth: int, hero_hp: int, unit_count_overrides
return false
# max_hp = 生效上限(非入参 hero_hp)——跨战斗携带 HP 时上限不得压扁
# (恢复点/治疗包的满血守卫与治疗上限随之失效)
_hero = {"hp": hero_hp, "max_hp": eff_max_hp, "atk": eff_atk, "is_hero": true, "alive": true, "display_name": db.hero_display_name(), "enemy_id": "hero", "armor_type": hero_armor_type if hero_armor_type != "" else db.forge_default_armor_type(), "attack_type": hero_attack_type if hero_attack_type != "" else db.forge_default_attack_type()} # 护甲/攻击类型轴快照(读注入值,空串回退锻造工坊配置默认值 normal/light;甲效矩阵消费面)
_hero = {"hp": hero_hp, "max_hp": eff_max_hp, "atk": eff_atk, "is_hero": true, "alive": true, "display_name": String(db.hero.display_name), "enemy_id": "hero", "armor_type": hero_armor_type if hero_armor_type != "" else db.forge_default_armor_type(), "attack_type": hero_attack_type if hero_attack_type != "" else db.forge_default_attack_type()} # 护甲/攻击类型轴快照(读注入值,空串回退锻造工坊配置默认值 normal/light;甲效矩阵消费面)
_enemies.clear() # 难度曲线与Boss DifficultyTable 修饰:施加于行值、行值快照之前——MVP 空表 = 恒 1.0 无行为(AC-10);混编行集内全部敌种同乘((L,D) 精确匹配一次取值——F9 同乘不改变异,敌人系统 v0.15)
var mod: Dictionary[Variant, Variant] = db.resolve_difficulty_modifiers(level_index, node_depth)
var hp_mult: float = float(mod.get("hp_mult", 1.0))
+31 -7
View File
@@ -22,11 +22,24 @@ const SHELF_KINDS := ["attack", "max_hp", "armor", "ap", "feat_ticket", "heal"]
func validate() -> Array:
var errs: Array[Dictionary] = []
# 行族空护栏(legacy 四轴 + pool_shelf 皆为 ConfigDB 消费面——缺族即致命)
_validate_families(errs)
_validate_gp_drops(errs)
_validate_level_bonus(errs)
_validate_products(errs)
_validate_pool_shelf(errs)
_validate_price_factors(errs)
return errs
# 行族空护栏(legacy 四轴 + pool_shelf 皆为 ConfigDB 消费面——缺族即致命)
func _validate_families(errs: Array[Dictionary]) -> void:
for family in ["gp_drops", "level_gp_bonus", "products", "price_level_bonus", "pool_shelf"]:
if get(family).is_empty():
errs.append({"category": "empty_table", "message": "ShopTable 行族为空: %s" % family})
# gp_drops:tier 词表封闭 + 掷定域 + 均值锚 + tier 唯一
# gp_drops:tier 词表封闭 + 掷定域 + 均值锚 + tier 唯一
func _validate_gp_drops(errs: Array[Dictionary]) -> void:
var seen_tier := {}
for row in gp_drops:
var tier: String = row.get("tier", "")
@@ -44,7 +57,10 @@ func validate() -> Array:
errs.append({"category": "domain", "message": "gp_drops %s 掷定域越域 [0,999]: %d..%d..%d" % [tier, rmin, mean, rmax]})
if rmin > mean or mean > rmax:
errs.append({"category": "cross_field", "message": "gp_drops %s 违反 roll_min(%d) ≤ mean(%d) ≤ roll_max(%d)——均值恒等期望锚" % [tier, rmin, mean, rmax]})
# level_gp_bonus / price_level_bonus:level_index 连续从 1 递增 + 加值域
# level_gp_bonus / price_level_bonus:level_index 连续从 1 递增 + 加值域
func _validate_level_bonus(errs: Array[Dictionary]) -> void:
for family in ["level_gp_bonus", "price_level_bonus"]:
var arr: Array[Variant] = get(family)
var seen := {}
@@ -61,7 +77,10 @@ func validate() -> Array:
if li != i + 1:
errs.append({"category": "structural", "message": "%s level_index 不连续(须从 1 连续递增): 位 %d = %d" % [family, i + 1, li]})
break
# products:product_id 唯一 + attr 词表 + 价格域 [1,999]
# products:product_id 唯一 + attr 词表 + 价格域 [1,999]
func _validate_products(errs: Array[Dictionary]) -> void:
var seen_pid := {}
for row in products:
var pid: String = row.get("product_id", "")
@@ -85,7 +104,10 @@ func validate() -> Array:
var inc: int = int(row.get("price_increment", -1))
if inc < 0 or inc > 999:
errs.append({"category": "domain", "message": "%s price_increment 越域 [0,999]: %d" % [pid, inc]})
# pool_shelf:kind_id 词表封闭(四切边后种)+ 唯一 + display_name + base_price 域 [1,999]
# pool_shelf:kind_id 词表封闭(四切边后种)+ 唯一 + display_name + base_price 域 [1,999]
func _validate_pool_shelf(errs: Array[Dictionary]) -> void:
var seen_kind := {}
for row in pool_shelf:
var kind: String = row.get("kind_id", "")
@@ -99,7 +121,10 @@ func validate() -> Array:
var base: int = int(row.get("base_price", 0))
if base < 1 or base > 999:
errs.append({"category": "domain", "message": "pool_shelf %s base_price 越域 [1,999]: %d" % [kind, base]})
# 价格因子域(乘法语义——A/b 均 ≥1 单调不降;上界防手滑投值;provisional 值本体归难度曲线与Boss)
# 价格因子域(乘法语义——A/b 均 ≥1 单调不降;上界防手滑投值;provisional 值本体归难度曲线与Boss)
func _validate_price_factors(errs: Array[Dictionary]) -> void:
if price_a < 1.0 or price_a > 3.0:
errs.append({"category": "domain", "message": "price_a 越域 [1.0,3.0]: %f" % price_a})
if price_b < 1.0 or price_b > 2.0:
@@ -108,4 +133,3 @@ func validate() -> Array:
errs.append({"category": "domain", "message": "relic_base_price 越域 [1.0,99.0]: %f" % relic_base_price})
if spend_discount <= 0.0 or spend_discount > 1.0:
errs.append({"category": "domain", "message": "spend_discount 越域 (0.0,1.0]: %f" % spend_discount})
return errs
@@ -24,7 +24,6 @@ var _bonus_row_l: HBoxContainer
var _bonus_name: Label
var _bonus_val: Label
var _cele: TextureRect # 庆祝立绘(idle 占位浮动)
var _celebrate_tw: Tween # 庆祝浮动循环句柄(每调先 kill,close 必杀——无限循环禁裸创建)
var _btn: Button
var _board_rect := Rect2()
@@ -159,9 +158,10 @@ func close() -> void:
_seal.visible = false
_seal.modulate = Color.WHITE
_cele.visible = false
if _celebrate_tw != null and _celebrate_tw.is_valid():
_celebrate_tw.kill()
_celebrate_tw = null
var tw: Tween = get_meta(&"celebrate_tw", null) # 循环句柄随节点存(跨 open/close,close 必杀)
if tw != null and tw.is_valid():
tw.kill()
remove_meta(&"celebrate_tw")
_clear_crest_bricks()
@@ -285,10 +285,11 @@ func _play_dust_sadle() -> void:
func _play_celebrate() -> void:
if UiKit.motion_scale < 1.0:
return
if _celebrate_tw != null and _celebrate_tw.is_valid():
_celebrate_tw.kill()
_celebrate_tw = _cele.create_tween()
var tw := _celebrate_tw
var prev: Tween = get_meta(&"celebrate_tw", null)
if prev != null and prev.is_valid():
prev.kill()
var tw := _cele.create_tween()
set_meta(&"celebrate_tw", tw)
tw.set_loops()
tw.tween_property(_cele, "position:y", _cele.position.y - 4.0, UiKit.motion(0.6)).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
tw.tween_property(_cele, "position:y", _cele.position.y, UiKit.motion(0.6)).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
+2 -4
View File
@@ -19,7 +19,6 @@ var slot_row: StatusSlotRow # 状态效果唯一展示面 = 壳层伴随
var _kind := "enemy"
var _portrait_sprite: Sprite2D = null # 资产挂点(null = 未落位,字形在位)
var _portrait_tex: Texture2D = null # 落位纹理引用(set_portrait_texture 装载;字形回退判据)
var _portrait_flip := false
# 主角三态切换:state 名 → 纹理(battle_stage_view 组合点注册);缺态=保持现纹理
# (缺位回退链延伸——三态素材缺任一不炸不空)。敌站桩不注册不消费。
var _hero_tex := {}
@@ -96,7 +95,7 @@ func set_data(d: Dictionary) -> void:
_armor = String(d.get("armor_type", "light"))
_hp_max = float(maxi(1, int(d.get("max_hp", 1))))
_set_hp(int(d.get("hp", 0)))
set_order(int(d.get("order", 1)))
_set_order(int(d.get("order", 1)))
set_pending(String(d.get("pending_type", "")), String(d.get("pending_skill", "")))
set_effects(d.get("effects", []))
_refresh_portrait_glyph()
@@ -149,7 +148,7 @@ func set_effects_cleared_feedback() -> void:
tw.tween_callback(note.queue_free)
func set_order(i: int) -> void:
func _set_order(i: int) -> void:
# 顺位徽标 = 行主序生成序(固定顺位——敌人系统 顺位标记义务)
_order = i
queue_redraw()
@@ -207,7 +206,6 @@ func _play_intent_motion() -> void:
# flip = 立绘朝向翻转(素材基准朝左时传 true 呈现朝右)。盒高 96px = 48px 立绘源整数 2×
# (圣经 §8.2 禁非整数缩放)——源换代时随源重算。
func set_portrait_texture(tex: Texture2D, flip: bool = false) -> void:
_portrait_flip = flip
if tex == null:
if _portrait_sprite != null:
_portrait_sprite.visible = false
@@ -259,14 +259,14 @@ func _init() -> void:
func _smoke_table_migration() -> void:
print("--- 数值入表(shop 两 knob + hero 显示名 · 原值迁移)---")
Kit.check("shop.relic_base_price 表值 == 迁移前字面量(15.0)〔wd11〕", _db.shop_relic_base_price() == 15.0)
Kit.check("shop.spend_discount 表值 == 迁移前字面量(0.8)〔wd11〕", _db.shop_spend_discount() == 0.8)
Kit.check("hero.display_name 表值 == 迁移前字面量(探险者)〔wd16〕", _db.hero_display_name() == "探险者")
Kit.check("shop.relic_base_price 表值 == 迁移前字面量(15.0)〔wd11〕", _db.shop.relic_base_price == 15.0)
Kit.check("shop.spend_discount 表值 == 迁移前字面量(0.8)〔wd11〕", _db.shop.spend_discount == 0.8)
Kit.check("hero.display_name 表值 == 迁移前字面量(探险者)〔wd16〕", _db.hero.display_name == "探险者")
# 消费端读表:spawn 真实构造链的英雄单位显示名与表值同
var units = load("res://core/unit_container.gd").new(_db)
var spawned: bool = units.spawn(1, 0, int(_db.hero.max_hp))
Kit.check("spawn 后英雄单位 display_name 读表同值(探险者)〔wd16〕",
spawned and String(units.hero_unit().get("display_name", "")) == _db.hero_display_name())
spawned and String(units.hero_unit().get("display_name", "")) == String(_db.hero.display_name))
# ---- 生成图深度扩面覆盖断言(迭代 35 卡② S5/V11——节点源=卡① 生成器产图,
@@ -1301,7 +1301,7 @@ func _smoke_shop_pool() -> void:
Kit.check("货架 kind 在封闭词表或保底 relic 卡(%s)" % ckind, is_relic or shelf_kinds.has(ckind))
var expect_p: int
if is_relic:
expect_p = int(ceil(_db.shop_relic_base_price() * pow(_db.shop_price_b(), float(l2 - 1)) * 1.0))
expect_p = int(ceil(float(_db.shop.relic_base_price) * pow(_db.shop_price_b(), float(l2 - 1)) * 1.0))
else:
var prow: Dictionary = _db.shop_pool_row(ckind)
expect_p = int(ceil(float(int(prow.get("base_price", 0))) * pow(_db.shop_price_b(), float(l2 - 1)) * 1.0))
@@ -104,13 +104,14 @@ func _init() -> void:
var ov = BattleEndScript.new()
root.add_child(ov)
ov.open_victory("判定行", "继续", Rect2(0, 0, 100, 100))
var first: Tween = ov._celebrate_tw
var first: Tween = ov.get_meta(&"celebrate_tw", null)
Kit.check("首开胜利画面创建庆祝句柄〔wd4〕", first != null and first.is_valid())
ov.open_victory("判定行", "继续", Rect2(0, 0, 100, 100))
var second: Tween = ov.get_meta(&"celebrate_tw", null)
Kit.check("重开胜利画面:旧句柄已 kill、新句柄在位(至多一个活跃)〔wd4〕",
first != null and not first.is_valid() and ov._celebrate_tw != null and ov._celebrate_tw.is_valid())
first != null and not first.is_valid() and second != null and second.is_valid())
ov.close()
Kit.check("close 必杀庆祝句柄(句柄清空)〔wd4〕", ov._celebrate_tw == null)
Kit.check("close 必杀庆祝句柄(句柄清空)〔wd4〕", ov.get_meta(&"celebrate_tw", null) == null)
# ---- ③ 互斥四口(d5/d8)----
var dbg = DebugOverlayScript.new(null, 0, 0)
@@ -690,8 +690,8 @@ func _smoke_contrast_inks() -> void:
sb_src.contains("HORIZONTAL_ALIGNMENT_LEFT, -1, 13, UiKit.GOLD_BRIGHT"))
Kit.check("V4 魔法护甲徽标墨=HERO_BLUE_BRIGHT", sb_src.contains("col = UiKit.HERO_BLUE_BRIGHT"))
Kit.check("V4 重甲徽标墨=GOLD_BRIGHT", sb_src.contains("col = UiKit.GOLD_BRIGHT"))
Kit.check("V4 GOLD_DEEP 语义岗不动(standee:199 意图动画判据)",
sb_src.contains("if _intent_col == UiKit.GOLD_DEEP:"))
Kit.check("V4 意图蓄力档判据=分档布尔(standee_body:179 CHARGE_SKILL_IDS 判定;旧 GOLD_DEEP 恒不可达判据清零)〔wd2〕",
sb_src.contains("_intent_charge = pid in CHARGE_SKILL_IDS") and not sb_src.contains("if _intent_col == UiKit.GOLD_DEEP:"))
var ap_src: String = FileAccess.get_file_as_string("res://presentation/views/ap_bar_view.gd")
Kit.check("V4 RAP 反馈行墨=STONE_DEEP(原位行锚定)",
ap_src.contains('UiKit.label("",UiKit.FS_CAPTION, false, UiKit.STONE_DEEP)'))
@@ -807,7 +807,7 @@ func _all_labels(root: Node) -> Array:
# ---- 生涯页只读投影一致性(汇总段六类九项逐行 == SaveManager 计数访问器/
# MetaProgression 访问器读数;投影拼装与 main._career_data 同构(同访问器族同序)。
# MetaProgression 访问器读数;投影拼装与 HudPresenter.career_data 同构(同访问器族同序)。
# 只读性连接巡检与零动效断言归 ui_struct_probe 呈现侧,本段只管数据面+旧档载入显示
# (v5 样档升 v6——完成定义 6「计数域缺省补零、页正常显示」))----
func _career_overlay_projection() -> void:
@@ -822,7 +822,7 @@ func _career_overlay_projection() -> void:
sv.apply_adventure_summary({"result": "abandoned", "cleared": false, "levels_cleared": 1,
"stopped_at": 2, "keystone_total": 0, "master_seed": 22})
sv.write_to_disk()
# 投影拼装(main._career_data 同构)+ 视图喂入
# 投影拼装(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()}