## VERTICAL SLICE - NOT FOR PRODUCTION ## 数值表单点访问入口:全部 .tres 表的加载、校验诊断与门面(垂直切片验证:改表即改玩法,数值零硬编码)。 ## 玩法代码禁止绕过本单点自建 load();行查询归各表类(表管自己的行), ## 表持有按域收进四容器(combat 战斗编排域/run 冒险 runner 域/crossrun 跨局元进度域/ ## presentation 表现层布局域),消费面经 db.<容器>.<表> 直连。 ## 「经 ConfigDB 单点读取」=经其持有的容器与表对象(架构 :88)。 ## 冒烟脚本可显式实例化本类(headless --script 亦会加载 autoload——共享 .tres 上的 resolver 归最后装载的实例); ## 加载失败 is_ready=false 加 fatal 封印,调用方须查。 class_name ConfigDb extends Node const TABLE_MANIFEST := { "action_economy": "action_economy.tres", "variation": "variation.tres", "status_effects": "status_effects.tres", "board_gen": "board_gen.tres", "enemy": "enemy.tres", "skill": "skills.tres", "hero": "hero.tres", "reward": "reward.tres", "settlement": "settlement.tres", "level": "level.tres", "difficulty": "difficulty.tres", "level_gen_params": "level_gen_params.tres", # LevelGenParams 参数档三行(节点图生成器核心批 卡①——五旋钮 provisional) "shop": "shop.tres", # ShopTable(表与 schema 已入库归数据面——本处只接线加载与消费) "meta": "meta.tres", # MetaTable 四行族(schema 与表同源已入库) "meta_tree": "meta_tree.tres", # MetaTreeTable 12 节点(定义表 Rule 4 豁免——天赋树.md v0.8 结构事实源) "treasure_items": "treasure_items.tres", # TreasureItemTable 六行(定义表 Rule 4 豁免;罗盘 retired/失义件 deferred 标记见 schema 头注) "adventure": "adventure.tres", # AdventureTable(大奖励与钥石行值单点) "feats": "feats.tres", # FeatTable 规则改写型特性(定义表 Rule 4 豁免——TreasureItemTable 先例;空表合法态=主时间线零扰动) "forge": "forge.tres", # ForgeTable——两根类型轴默认值+六路线行族;攻防所有权切分唯一数据面 "battle_layout": "battle_layout.tres", # BattleLayoutTable——战斗屏分区几何与明细带高度单行 "camp_scene_layout": "camp_scene_layout.tres", # CampSceneLayoutTable——营地场景 10 层贴图/命中矩形与门洞缺口单行 } var combat: ConfigDomainCombat # 战斗编排域七表(board_gen/variation/status_effects/enemy/skill/hero/settlement) var run: ConfigDomainRun # 冒险 runner 域七表(level/difficulty/level_gen_params/reward/adventure/action_economy/shop) var crossrun: ConfigDomainCrossrun # 跨局元进度域五表(meta/meta_tree/treasure_items/feats/forge) var presentation: ConfigDomainPresentation # 表现层布局域两表(battle_layout/camp_scene_layout) var is_ready := false var _diagnostics: Array[Dictionary] = [] # {severity, category, message} var _resolver: ConfigDbResolver # R1 难度行解析协作类(门面公有签名不变;load_tables 装配段注入三表) var _validator := ConfigDbCrossValidator.new() # 跨表校验协作类(无状态——声明期即建) var _reload_gate := false # 数据配置 EC8——战斗结算 唯一调用方 func _init() -> void: combat = ConfigDomainCombat.new() run = ConfigDomainRun.new() crossrun = ConfigDomainCrossrun.new() presentation = ConfigDomainPresentation.new() _resolver = ConfigDbResolver.new(push_diag, func() -> Variant: return combat.board_gen.rows, func() -> Variant: return combat.enemy.rows, func() -> Variant: return run.difficulty) # 协作类持门面引用(诊断/致命通道回呼) func push_diag(severity: String, category: String, message: String) -> void: _diagnostics.append({"severity": severity, "category": category, "message": message}) if severity == "warning": push_warning("[ConfigDB/%s] %s" % [category, message]) else: push_error("[ConfigDB/%s] %s" % [category, message]) # 运行期数据致命(fail-fast;退出执行归根:核心层不自携 # quit/crash,越域记 Fatal 级日志后置 _fatal_flag,进程退出决策归组合根(main._ready 表加载 # 失败已有同通道 fatal 屏+fail-fast 边界)——查询期消费方按返回值安全面走既有默认值回退) func _fatal(category: String, message: String) -> void: push_diag("fatal", category, message) push_error("[ConfigDB/%s] FATAL: %s —— 数据配置致命(退出决策归组合根)" % [category, message]) _fatal_flag = true var _fatal_flag := false # 运行期致命封印(退出归根承载;组合根/闸读面) func is_fatal() -> bool: return _fatal_flag func reset_diagnostics() -> void: _diagnostics.clear() _resolver.reset_warn_dedup() # R1 告警去重注册表(协作类持钥——清空时点与拆分前一致) func diagnostics() -> Array: return _diagnostics.duplicate() func diagnostics_count(severity: String) -> int: var n := 0 for d in _diagnostics: if d.severity == severity: n += 1 return n func _ready() -> void: load_tables("res://data/tables", false) func load_tables(root: String, collect_only: bool) -> bool: is_ready = false _diagnostics.clear() _resolver.reset_warn_dedup() var loaded := {} for key in TABLE_MANIFEST.keys(): var path := root.path_join(TABLE_MANIFEST[key]) if not ResourceLoader.exists(path): push_diag("error", "missing_file", "表文件缺失: %s" % path) continue var res: Resource = load(path) if res == null: push_diag("error", "load_failed", "表加载失败: %s" % path) continue loaded[key] = res for e in res.validate(): push_diag("error", e.category, "%s → %s" % [key, e.message]) # 跨表校验(悬空引用 / 池护栏——全部加载期致命) _cross_validate(loaded) var has_errors := diagnostics_count("error") > 0 if has_errors and not collect_only: # 游戏通道:致命(Rule 6)——is_ready 恒 false,调用方(主场景)中止构建 return false # 容器装配(表行按域收进四容器+resolver 注入)归私有协函数——load_tables 只留加载/校验/致命门面 _assign_loaded(loaded) is_ready = not has_errors return not has_errors # 容器装配协函数:20 表成员按域收进四容器(2026-10-27 全清设计定稿,2026-10-31 增表现层布局域) func _assign_loaded(loaded: Dictionary) -> void: combat.board_gen = loaded.get("board_gen") combat.variation = loaded.get("variation") combat.status_effects = loaded.get("status_effects") combat.enemy = loaded.get("enemy") combat.skill = loaded.get("skill") combat.hero = loaded.get("hero") combat.settlement = loaded.get("settlement") run.level = loaded.get("level") run.difficulty = loaded.get("difficulty") run.level_gen_params = loaded.get("level_gen_params") run.reward = loaded.get("reward") run.adventure = loaded.get("adventure") run.action_economy = loaded.get("action_economy") run.shop = loaded.get("shop") crossrun.meta = loaded.get("meta") crossrun.meta_tree = loaded.get("meta_tree") crossrun.treasure_items = loaded.get("treasure_items") crossrun.feats = loaded.get("feats") crossrun.forge = loaded.get("forge") presentation.battle_layout = loaded.get("battle_layout") presentation.camp_scene_layout = loaded.get("camp_scene_layout") for problem: String in presentation.camp_scene_layout.validate(): _fatal("camp_scene_layout", problem) # 布局几何红线(命中区越画面/相交/侵顶栏)=数据致命 # resolver 协作件注入三表(resolve 委托承载——resolver 文件与键零搬移;5.1 封装修复改经 bind_resolver 公开挂载口) combat.board_gen.bind_resolver(_resolver) combat.enemy.bind_resolver(_resolver) run.difficulty.bind_resolver(_resolver) # 跨表校验(悬空引用 / 池护栏——全部加载期致命)本体: # ConfigDbCrossValidator(core/config_db_cross_validator.gd)承载,门面公有签名不变。 func _cross_validate(loaded: Dictionary) -> void: _validator.cross_validate(loaded, push_diag) # 池模板 × 技能行跨表校验(冒烟直验独立入口——签名与消费面零变化,实现归协作类;门面私有—— # 消费面经门面者改 _ 前缀直调,cross_validator 直验入口公开面不变) func _validate_skill_pool_links(skill_table: SkillTable, enemy_table: EnemyTable, status_table: StatusEffectTable = null) -> Array: return _validator.validate_skill_pool_links(skill_table, enemy_table, status_table) # ---- 数据配置 EC8:debug 热重载门(战斗结算 唯一调用方;切片实现 = 门标志 + 拒绝语义)---- func set_reload_gate(gated: bool) -> void: _reload_gate = gated func reload() -> bool: if _reload_gate: push_diag("warning", "reload_gated", "战斗中热重载被拒绝(数据配置 EC8)") return false var ok := load_tables("res://data/tables", false) if ok: push_diag("debug", "config_reloaded", "表热重载完成") return ok