Files
gplan-2/game/sim/compendium_dump.gd
T

109 lines
4.6 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## 怪物图鉴外置工具·采集端:经 ConfigDB 单点读 enemy + skills 两表全行,行数据原样导出 JSON
## 到 sim-data/compendium/dump.json(渲染端 tools/compendium_gen.py 消费,生成自包含 HTML 图鉴)。
## 运行:Godot_console.exe --headless --path . --script res://sim/compendium_dump.gd -- [out=<绝对路径>] [tables_root=<res:// 路径>]
## out 缺省 = 仓库根 sim-data/compendium/dump.json(res:// 的上一级);目录不存在则创建。
## tables_root 缺省 = res://data/tables;fail-fast 路径验证时可指向临时表目录。
## 导出形态:顶层 counts 带两表行数计数 + enemy_rows/skill_rows 行字典原样(零新增字段、零改名;
## 表内可省略字段缺席即缺席)。本件零随机、零数值硬编码、零判定逻辑。
## fail-fast:表加载失败(缺表/行域校验/跨表悬空引用)、行缺关键字段、pool.skill_id 在技能表无对应行
## → 逐条报错并以退出码 1 中止,禁软回退。
extends SceneTree
const DEFAULT_OUT := "../sim-data/compendium/dump.json" # 相对 res://(game/ 上一级 = 仓库根)
const DEFAULT_TABLES_ROOT := "res://data/tables"
# 行关键字段(缺一即 fail-fast):取两表 schema 加载期必在的键——
# enemy 的 unit_count_max/atk_min/atk_max/attack_type/armor_type/armor_class 与技能行的
# trigger/buff_ref/buff_duration/damage_mult/rider_mult 均为 schema 可省略字段,不列入。
const ENEMY_REQUIRED_KEYS := ["enemy_id", "display_name", "row_kind", "level_index", "depth_band", "unit_count", "hp", "atk", "pool"]
const SKILL_REQUIRED_KEYS := ["skill_id", "display_name", "type", "cooldown_beats"]
func _initialize() -> void:
var opt := _parse_args(OS.get_cmdline_user_args())
var db = load("res://core/config_db.gd").new()
if not db.load_tables(String(opt.tables_root), false):
_print_diags(db)
push_error("compendium_dump: 表加载失败(fail-fast)")
quit(1)
return
var enemy_rows: Array = db.combat.enemy.rows
var skill_rows: Array = db.combat.skill.rows
var errs := _validate_rows(enemy_rows, skill_rows)
if not errs.is_empty():
for e in errs:
push_error("compendium_dump: %s" % e)
quit(1)
return
var payload := {
"counts": {"enemy_rows": enemy_rows.size(), "skill_rows": skill_rows.size()},
"enemy_rows": enemy_rows,
"skill_rows": skill_rows,
}
var out_path := String(opt.out)
var dir := out_path.get_base_dir()
if not DirAccess.dir_exists_absolute(dir):
var mk := DirAccess.make_dir_recursive_absolute(dir)
if mk != OK:
push_error("compendium_dump: 目录创建失败 %s(err=%d)" % [dir, mk])
quit(1)
return
var f := FileAccess.open(out_path, FileAccess.WRITE)
if f == null:
push_error("compendium_dump: 导出文件打开失败 %s(err=%d)" % [out_path, FileAccess.get_open_error()])
quit(1)
return
f.store_string(JSON.stringify(payload))
f.close()
print("图鉴导出完成: enemy_rows=%d skill_rows=%d → %s" % [enemy_rows.size(), skill_rows.size(), out_path])
quit(0)
func _parse_args(args: PackedStringArray) -> Dictionary:
var opt := {"out": "", "tables_root": DEFAULT_TABLES_ROOT}
for a in args:
var kv := String(a).split("=", true, 1)
if kv.size() != 2:
continue
if kv[0] == "out":
opt["out"] = String(kv[1])
elif kv[0] == "tables_root":
opt["tables_root"] = String(kv[1])
if String(opt.out).is_empty():
opt["out"] = ProjectSettings.globalize_path("res://").path_join(DEFAULT_OUT)
return opt
# 行面 fail-fast 校验:关键字段在位 + pool.skill_id 引用闭合(技能表行集先建索引再对账)
func _validate_rows(enemy_rows: Array, skill_rows: Array) -> Array:
var errs: Array = []
var skill_ids := {}
for row_v in skill_rows:
var row: Dictionary = row_v
var sid := String(row.get("skill_id", ""))
if sid.is_empty():
errs.append("技能行缺 skill_id")
for k in SKILL_REQUIRED_KEYS:
if not row.has(k):
errs.append("技能行 %s 缺关键字段: %s" % [sid, k])
skill_ids[sid] = true
for row_v in enemy_rows:
var row: Dictionary = row_v
var eid := String(row.get("enemy_id", ""))
if eid.is_empty():
errs.append("敌行缺 enemy_id")
for k in ENEMY_REQUIRED_KEYS:
if not row.has(k):
errs.append("敌行 %s 缺关键字段: %s" % [eid, k])
for entry_v in row.get("pool", []):
var entry: Dictionary = entry_v
var sid := String(entry.get("skill_id", ""))
if not skill_ids.has(sid):
errs.append("敌行 %s pool 引用不存在的 skill_id: %s(悬空致命)" % [eid, sid])
return errs
func _print_diags(db) -> void:
for d in db.diagnostics():
print("[ConfigDB/%s] %s: %s" % [d.category, d.severity, d.message])