## 关卡节点图参数档 schema(节点图生成器核心批 卡① T1):五旋钮+战斗保底+岔口去路数的行式数据表。 ## 键 = level_index(关卡行集单点),消费面经 ConfigDB.run.level_gen_params.row_by_level_index() 读取;生成器只吃参数档、不认关卡号(契约 6)。 ## 数值全 provisional(定值批落终值);域校验沿关卡节点图生成.md v1.1 调参表已锁条款——层深 L1≥6 递增增幅变大、 ## 配比四类合计 1、出口 L1 恒 1/L2-L3 1-3、岔口去路数恒 2/3、战斗保底≥0。 class_name LevelGenParams extends Resource const RATIO_KEYS := ["battle_ratio", "reward_ratio", "rest_ratio", "shop_ratio"] const ROW_KEYS := ["level_index", "layers", "branch_density", "entry_count", "exit_count", "battle_floor", "fork_ways_min", "fork_ways_max"] + RATIO_KEYS @export var rows: Array[Dictionary] = [] # 每行 = ROW_KEYS 键集(level_index 唯一正整数) func validate() -> Array: var errs: Array[Dictionary] = [] var seen := {} var layers_by_level := {} for row in rows: var head := _validate_row_head(row, seen) errs.append_array(head["errs"]) if not head["ok"]: continue errs.append_array(_validate_row_shape(row, head["L"], head["key"], layers_by_level)) errs.append_array(_validate_row_counts(row, head["L"], head["key"])) # 层深递进尾段(L1 Dictionary: var errs: Array[Dictionary] = [] var L: int = int(row.get("level_index", 0)) var key := "level %d" % L if seen.has(L): errs.append({"category": "dup_key", "message": "LevelGenParams 键重复: %d" % L}) seen[L] = true if L < 1: errs.append({"category": "domain", "message": "%s 关卡序号越域(≥1)" % key}) var ks := (row as Dictionary).keys() ks.sort() var expect := ROW_KEYS.duplicate() expect.sort() if ks != expect: errs.append({"category": "structural", "message": "%s 字段键集非 ROW_KEYS 闭集" % key}) return {"errs": errs, "L": L, "key": key, "ok": false} return {"errs": errs, "L": L, "key": key, "ok": true} ## 行形态域:层深下限(layers_by_level 引用登记——层深递进尾段取数)/分支密度/配比四类合计 1 func _validate_row_shape(row: Dictionary, L: int, key: String, layers_by_level: Dictionary) -> Array[Dictionary]: var errs: Array[Dictionary] = [] var layers: int = int(row.get("layers", 0)) if layers < 6: errs.append({"category": "domain", "message": "%s 层深 %d 低于下限 6(层深 L1≥6 已锁)" % [key, layers]}) layers_by_level[L] = layers var bd: float = float(row.get("branch_density", -1.0)) if bd < 0.0 or bd > 1.0: errs.append({"category": "domain", "message": "%s 分支密度 %f 越域 [0,1]" % [key, bd]}) var ratio_sum := 0.0 for rk in RATIO_KEYS: var v: float = float(row.get(rk, -1.0)) if v < 0.0 or v > 1.0: errs.append({"category": "domain", "message": "%s %s=%f 越域 [0,1]" % [key, rk, v]}) ratio_sum += v if not is_equal_approx(ratio_sum, 1.0): errs.append({"category": "domain", "message": "%s 配比四类合计 %f ≠ 1(已锁)" % [key, ratio_sum]}) return errs ## 行计数域:入口/出口(L1 恒 1)/战斗保底/岔口去路数锁值 func _validate_row_counts(row: Dictionary, L: int, key: String) -> Array[Dictionary]: var errs: Array[Dictionary] = [] var entry_count: int = int(row.get("entry_count", 0)) if entry_count < 1: errs.append({"category": "domain", "message": "%s 入口个数 %d 越域(≥1)" % [key, entry_count]}) var exit_count: int = int(row.get("exit_count", 0)) if exit_count < 1 or exit_count > 3: errs.append({"category": "domain", "message": "%s 出口个数 %d 越域 [1,3](已锁范围)" % [key, exit_count]}) if L == 1 and exit_count != 1: errs.append({"category": "domain", "message": "%s 出口个数 %d ≠ 1(L1 出口恒 1 已锁)" % [key, exit_count]}) var bf: int = int(row.get("battle_floor", -1)) if bf < 0: errs.append({"category": "domain", "message": "%s 战斗保底 %d 越域(≥0)" % [key, bf]}) var fmin: int = int(row.get("fork_ways_min", 0)) var fmax: int = int(row.get("fork_ways_max", 0)) if fmin != 2 or fmax != 3: errs.append({"category": "domain", "message": "%s 岔口去路数 (%d,%d) ≠ (2,3)(已锁)" % [key, fmin, fmax]}) return errs # 参数档单点读取(键 = level_index,缺行返回空字典——沿 level_row() 体例) func row_by_level_index(level_index: int) -> Dictionary: for row in rows: if int(row.get("level_index", 0)) == level_index: return row return {}