feat(敌人表深度带): 遭遇行集寻址改为 (关卡, row_kind, 深度带)
敌人行集不再按 (level_index, node_depth) 精确点寻址,改为 (level_index, row_kind, depth_band): 带切法单点 EnemyTable.band_of 取首个不小于请求深度的带(偏难一侧),高于顶带返回 -1 交 resolver 取顶带并发 resolve_depth_clamp(四元组去重不变);resolver 与 ConfigDb 门面同步 加 row_kind 第三参;unit_container.spawn 与 BattleOrchestrator 透传行类(battle/boss)。 敌人表 20 行键迁移(原 node_depth 数值就地成为新带键,值列零变更),新增 row_kind 列; schema 校验随键换代:row_kind 封闭枚举、dup_key/rowset_rule 新键串、每关 battle 带 0 锚 与每关 boss 行集 ≥1;跨表校验 ④ 敌侧改带寻址,新增同日图多 Boss 同带报错与顶带消解报错。 读数:基线冒烟 1156 项 0 失败;本轮冒烟 1161 项 0 失败;单元 35 套件全绿;探针 --click 全绿。
This commit is contained in:
1 parent
603eba3117
commit
07f3eb55e3
8 files changed
+191
-72
No files matched your search
@@ -324,9 +324,11 @@ func begin_battle(enc: Dictionary) -> bool:
|
||||
# 开流条件、消费序、消费次数逐位一致——零内容期随机流零变化;区间宽度为零的行不掷 = 「定值不骰」
|
||||
# 既有口径;atk 区间逐行同构 = 行值语义完整,属「每行一骰」语义内的
|
||||
# 行字段区间骰逐行化,非新增随机源形态)。
|
||||
var erowset_pre: Array[Variant] = db.resolve_enemy_rowset(int(enc.level_index), int(enc.node_depth))
|
||||
# 行类维度:节点身份 battle/boss 决定行集键域(缺省 battle 只服务测试直呼面——生产路径恒传 node_type)
|
||||
var enc_kind := String(enc.get("node_type", "battle"))
|
||||
var erowset_pre: Array[Variant] = db.resolve_enemy_rowset(int(enc.level_index), int(enc.node_depth), enc_kind)
|
||||
if erowset_pre.is_empty():
|
||||
_fatal("敌人表 行集解析为空(%d,%d)" % [int(enc.level_index), int(enc.node_depth)])
|
||||
_fatal("敌人表 行集解析为空(%d,%d,%s)" % [int(enc.level_index), int(enc.node_depth), enc_kind])
|
||||
return false
|
||||
var count_overrides: Array[Variant] = []
|
||||
var atk_overrides: Array[Variant] = []
|
||||
@@ -354,7 +356,7 @@ func begin_battle(enc: Dictionary) -> bool:
|
||||
_hero_atk = int(enc.get("hero_atk", int(db.hero.atk)))
|
||||
if not _enemy_system.spawn(int(enc.level_index), int(enc.node_depth), int(enc.hero_hp), count_overrides,
|
||||
_hero_atk, int(enc.get("hero_max_hp", -1)), atk_overrides,
|
||||
String(enc.get("attack_type", "")), String(enc.get("armor_type", ""))): # 两根类型轴(空串=锻造配置默认值回退,行为连续)
|
||||
String(enc.get("attack_type", "")), String(enc.get("armor_type", "")), enc_kind): # 两根类型轴(空串=锻造配置默认值回退,行为连续)+行类维(battle/boss)
|
||||
_fatal("敌人系统 spawn 被拒")
|
||||
return false
|
||||
if not _install_new_board():
|
||||
|
||||
@@ -190,8 +190,8 @@ func resolve_board_gen(L: int, D: int) -> Dictionary:
|
||||
return _resolver.resolve_board_gen(L, D)
|
||||
|
||||
|
||||
func resolve_enemy_rowset(L: int, D: int) -> Array[Dictionary]:
|
||||
return _resolver.resolve_enemy_rowset(L, D) # 行集数组形态(非单行 Dictionary)
|
||||
func resolve_enemy_rowset(L: int, D: int, row_kind: String) -> Array[Dictionary]:
|
||||
return _resolver.resolve_enemy_rowset(L, D, row_kind) # 行集数组形态(非单行 Dictionary;D 维带寻址)
|
||||
|
||||
|
||||
# 难度曲线与Boss DifficultyTable 消费面(敌人系统.spawn 调用):(L,D) 精确匹配修饰乘数,越域值 clamp(GDD Edge Case——组合事故非配置事故)
|
||||
|
||||
@@ -51,8 +51,9 @@ func cross_validate(loaded: Dictionary, push_diag: Callable) -> void:
|
||||
for s in required_sources:
|
||||
if not st_tab.source_label_map.has(s):
|
||||
push_diag.call("error", "segorder_violation", "source_label_map 缺必需键: %s" % s)
|
||||
# ④ LevelTable 节点悬空引用(battle/boss 的 (L,node_depth) 须在 board_gen 与 enemy 两表有精确行——
|
||||
# 线性推进器按精确深度行跑,floor 兜底静默降难度属调参事故,加载期直接拦)
|
||||
# ④ LevelTable 节点悬空引用(battle/boss 节点:board_gen 侧须有精确行;enemy 侧按
|
||||
# (关卡序号, 行类) 带寻址——落带即设计语义,节点深度高于顶带键=顶 clamp 消解属调参事故,加载期直接拦。
|
||||
# 同关同一张图多终点 Boss 节点解析带键须互异(共带=两终点命中同一 Boss 行集,「走哪条路打谁」落空))
|
||||
var lv: LevelTable = loaded.get("level")
|
||||
var bg: BoardGenTable = loaded.get("board_gen")
|
||||
var en: EnemyTable = loaded.get("enemy")
|
||||
@@ -60,9 +61,16 @@ func cross_validate(loaded: Dictionary, push_diag: Callable) -> void:
|
||||
var bg_keys := {}
|
||||
for row in bg.rows:
|
||||
bg_keys["%d_%d" % [int(row.level_index), int(row.node_depth)]] = true
|
||||
var en_keys := {}
|
||||
var en_bands := {} # "%d_%s" % [L, row_kind] → 升序带键数组
|
||||
for row in en.rows:
|
||||
en_keys["%d_%d" % [int(row.level_index), int(row.node_depth)]] = true
|
||||
var ek := "%d_%s" % [int(row.level_index), String(row.get("row_kind", ""))]
|
||||
if not en_bands.has(ek):
|
||||
en_bands[ek] = []
|
||||
var eb := int(row.get("depth_band", -1))
|
||||
if not (en_bands[ek] as Array).has(eb):
|
||||
(en_bands[ek] as Array).append(eb)
|
||||
for ek in en_bands.keys():
|
||||
(en_bands[ek] as Array).sort()
|
||||
for lrow in lv.rows:
|
||||
var li: int = int(lrow.level_index)
|
||||
# 模板池感知(关卡节点图生成 v0.4——池内全模板跨表校验,不只 row.nodes)
|
||||
@@ -73,16 +81,29 @@ func cross_validate(loaded: Dictionary, push_diag: Callable) -> void:
|
||||
else:
|
||||
for tpl in tpls:
|
||||
node_lists.append(tpl.get("nodes", []))
|
||||
for nodes_arr in node_lists:
|
||||
for node in nodes_arr:
|
||||
for tpl_i in range(node_lists.size()):
|
||||
var boss_bands := {} # 本图已占 Boss 带键
|
||||
for node in node_lists[tpl_i]:
|
||||
var t: String = String(node.get("type", ""))
|
||||
if t != "battle" and t != "boss":
|
||||
continue
|
||||
var key := "%d_%d" % [li, int(node.get("node_depth", -1))]
|
||||
var nd: int = int(node.get("node_depth", -1))
|
||||
var key := "%d_%d" % [li, nd]
|
||||
if not bg_keys.has(key):
|
||||
push_diag.call("error", "level_node_dangling", "level %d %s 节点 (%s) 在 board_gen 无精确行" % [li, t, key])
|
||||
if not en_keys.has(key):
|
||||
push_diag.call("error", "level_node_dangling", "level %d %s 节点 (%s) 在 enemy 无精确行" % [li, t, key])
|
||||
var nk := "%d_%s" % [li, t]
|
||||
if not en_bands.has(nk):
|
||||
push_diag.call("error", "level_node_dangling", "level %d %s 节点 (%d) 在 enemy 无该行类行集" % [li, t, nd])
|
||||
continue
|
||||
var hit_band: int = EnemyTable.band_of(en_bands[nk], nd)
|
||||
if hit_band < 0:
|
||||
push_diag.call("error", "level_node_dangling", "level %d %s 节点深度 %d 高于 (level %d, %s) 顶带键 %d——顶 clamp 消解" % [
|
||||
li, t, nd, li, t, int((en_bands[nk] as Array)[-1])])
|
||||
continue
|
||||
if t == "boss":
|
||||
if boss_bands.has(hit_band):
|
||||
push_diag.call("error", "rowset_rule", "level %d 模板 %d 多终点 Boss 节点同带(band %d)——共带使「走哪条路打谁」落空" % [li, tpl_i, hit_band])
|
||||
boss_bands[hit_band] = true
|
||||
# ⑤ 技能池引用完整性(敌人技能池批——敌人表池 × 技能表;独立入口 validate_skill_pool_links 供冒烟直验)
|
||||
var skt = loaded.get("skill")
|
||||
if skt != null and en != null:
|
||||
|
||||
@@ -30,11 +30,63 @@ func resolve_board_gen(L: int, D: int) -> Dictionary:
|
||||
return _resolve_progress(_board_gen_rows_get.call(), L, D, "board_gen")
|
||||
|
||||
|
||||
func resolve_enemy_rowset(L: int, D: int) -> Array[Dictionary]:
|
||||
# 行集数组形态:ceil 命中 (l*,d*) 后
|
||||
# 返回该坐标全部行、按表文件行序(行序即契约——同坐标多行文件内相邻由 EnemyTable.validate 兜底)。
|
||||
# 空集表达 = 空数组(生产表下 ceil 命中坐标恒有行,空集仅表空时可达——validate 已拦)。
|
||||
return _resolve_progress(_enemy_rows_get.call(), L, D, "enemy", true)
|
||||
func resolve_enemy_rowset(L: int, D: int, row_kind: String) -> Array[Dictionary]:
|
||||
# 带寻址:L 维沿进度行解析现状(ceil 向上取整保证偏难、越最大行 clamp),
|
||||
# D 维经 EnemyTable.band_of 落带——命中该 (L, row_kind) 的某一个 depth_band,
|
||||
# 返回该带全部行、按表文件行序(行序即契约——同带键多行文件内相邻由 EnemyTable.validate 兜底)。
|
||||
# 空集表达 = 空数组(band_keys 空时可达,交消费侧既有空行集 fatal 路径——validate 已拦无 Boss 行集)。
|
||||
if L < 1 or D < 0:
|
||||
_diag.call("fatal", "request_oob", "enemy 请求越域 (%d,%d)——进度坐标低于合法域(关卡序号≥1、节点深度≥0)" % [L, D])
|
||||
return []
|
||||
if not EnemyTable.ROW_KINDS.has(row_kind):
|
||||
_diag.call("fatal", "request_oob", "enemy 请求行类非法 (%d,%d,\"%s\")——row_kind 域 {battle, boss}" % [L, D, row_kind])
|
||||
return []
|
||||
var rows: Array = _enemy_rows_get.call()
|
||||
var level_keys: Array[int] = []
|
||||
for row in rows:
|
||||
var lv := int(row.get("level_index", 0))
|
||||
if not level_keys.has(lv):
|
||||
level_keys.append(lv)
|
||||
if level_keys.is_empty():
|
||||
_diag.call("fatal", "structural", "enemy 行集为空——锚行校验应已拦截")
|
||||
return []
|
||||
level_keys.sort()
|
||||
var l_star := _ceil_key(level_keys, L)
|
||||
if l_star < 0:
|
||||
l_star = int(level_keys[-1]) # 超出最大行 → 取最大行封顶
|
||||
_warn_resolve("enemy", "resolve_level_clamp", "enemy 请求 (%d,%d) clamp 解析(超出最大关卡取最大值)→ 关卡 %d" % [L, D, l_star], l_star, D)
|
||||
elif l_star != L:
|
||||
_warn_resolve("enemy", "resolve_level_ceil", "enemy 请求 (%d,%d) ceil 解析(向上取整保证偏难)→ 关卡 %d" % [L, D, l_star], l_star, D)
|
||||
var band_keys: Array[int] = []
|
||||
for row in rows:
|
||||
if int(row.get("level_index", 0)) == l_star and String(row.get("row_kind", "")) == row_kind:
|
||||
var bv := int(row.get("depth_band", -1))
|
||||
if not band_keys.has(bv):
|
||||
band_keys.append(bv)
|
||||
if band_keys.is_empty():
|
||||
return []
|
||||
band_keys.sort()
|
||||
var d_star := EnemyTable.band_of(band_keys, D)
|
||||
if d_star < 0:
|
||||
d_star = int(band_keys[-1]) # 越顶 clamp 顶带(调参事故面,跨表校验另报)
|
||||
_warn_resolve("enemy", "resolve_depth_clamp", "enemy 请求 (%d,%d) clamp 解析(超出顶带取顶带)→ (%d,band %d)" % [L, D, l_star, d_star], l_star, d_star)
|
||||
elif d_star != D:
|
||||
_warn_resolve("enemy", "resolve_depth_ceil", "enemy 请求 (%d,%d) ceil 解析(向上取整保证偏难)→ (%d,band %d)" % [L, D, l_star, d_star], l_star, d_star)
|
||||
var all_out: Array[Dictionary] = []
|
||||
for row in rows:
|
||||
if int(row.get("level_index", 0)) == l_star and String(row.get("row_kind", "")) == row_kind \
|
||||
and int(row.get("depth_band", -1)) == d_star:
|
||||
all_out.append(row)
|
||||
return all_out
|
||||
|
||||
|
||||
# 解析告警单点(类别 + 命中键四元组去重——R1 既有去重注册表)
|
||||
func _warn_resolve(table_id: String, category: String, message: String, l_key: int, d_key: int) -> void:
|
||||
var dedup_key := "%s|%s|%d|%d" % [table_id, category, l_key, d_key]
|
||||
if _warn_dedup.has(dedup_key):
|
||||
return
|
||||
_warn_dedup[dedup_key] = true
|
||||
_diag.call("warning", category, message)
|
||||
|
||||
|
||||
# 难度曲线与Boss DifficultyTable 消费面(敌人系统.spawn 调用):(L,D) 精确匹配修饰乘数,越域值 clamp(GDD Edge Case——组合事故非配置事故)
|
||||
@@ -51,7 +103,7 @@ func resolve_difficulty_modifiers(L: int, D: int) -> Dictionary:
|
||||
return {}
|
||||
|
||||
|
||||
func _resolve_progress(rows: Array, L: int, D: int, table_id: String, return_all := false) -> Variant:
|
||||
func _resolve_progress(rows: Array, L: int, D: int, table_id: String) -> Variant:
|
||||
if L < 1 or D < 0:
|
||||
_diag.call("fatal", "request_oob", "%s 请求越域 (%d,%d)——进度坐标低于合法域(关卡序号≥1、节点深度≥0)" % [table_id, L, D])
|
||||
return {}
|
||||
@@ -98,12 +150,6 @@ func _resolve_progress(rows: Array, L: int, D: int, table_id: String, return_all
|
||||
_warn_dedup[dedup_key] = true
|
||||
var verb := "clamp 解析(超出最大行取最大值)" if missing.contains("clamp") else "ceil 解析(向上取整保证偏难)"
|
||||
_diag.call("warning", "resolve_" + missing, "%s 请求 (%d,%d) %s → (%d,%d) [%s]——2026-09-18 拍板" % [table_id, L, D, verb, l_star, d_star, missing])
|
||||
if return_all:
|
||||
var all_out: Array[Dictionary] = []
|
||||
for row in rows:
|
||||
if int(row.get("level_index", 0)) == l_star and int(row.get("node_depth", 0)) == d_star:
|
||||
all_out.append(row)
|
||||
return all_out
|
||||
for row in rows:
|
||||
if int(row.get("level_index", 0)) == l_star and int(row.get("node_depth", 0)) == d_star:
|
||||
return row
|
||||
|
||||
@@ -35,14 +35,14 @@ func _init(db_ref: ConfigDb) -> void:
|
||||
# ≤ ENEMY_ROWSET_INSTANCE_MAX 守卫 fail-fast。
|
||||
func spawn(level_index: int, node_depth: int, hero_hp: int, unit_count_overrides: Array = [],
|
||||
atk_override: int = -1, max_hp_override: int = -1, enemy_atk_overrides: Array = [],
|
||||
hero_attack_type: String = "", hero_armor_type: String = "") -> bool:
|
||||
hero_attack_type: String = "", hero_armor_type: String = "", row_kind: String = "battle") -> bool:
|
||||
if _spawned:
|
||||
push_warning("[敌人系统] Alive 相位重复 spawn(相位守卫)")
|
||||
return false
|
||||
# 行集数组口径(resolver 返回该坐标全部行、文件序)
|
||||
var rowset: Array[Variant] = db.resolve_enemy_rowset(level_index, node_depth)
|
||||
# 行集数组口径(resolver 按 (L,band) 带寻址返回全部行、文件序)
|
||||
var rowset: Array[Variant] = db.resolve_enemy_rowset(level_index, node_depth, row_kind)
|
||||
if rowset.is_empty():
|
||||
push_error("[敌人系统] 遭遇行解析失败/越域 (%d,%d)" % [level_index, node_depth])
|
||||
push_error("[敌人系统] 遭遇行解析失败/越域 (%d,%d,%s)" % [level_index, node_depth, row_kind])
|
||||
assert(false, "遭遇行悬空(R1 前置守卫)")
|
||||
return false
|
||||
var eff_atk: int = atk_override if atk_override >= 1 else int(db.hero.atk)
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
## VERTICAL SLICE - NOT FOR PRODUCTION
|
||||
## 敌人表 schema:逐 (level_index, node_depth, enemy_id) 行集的类型化读取(行集可多行混编;垂直切片验证:完整战斗逐行归因)。
|
||||
## 行序即契约:同坐标多行必须文件内相邻——resolver 返回序/生成序/叙述取首行全依赖文件行序。
|
||||
## 敌人表 schema:逐 (level_index, row_kind, depth_band, enemy_id) 行集的类型化读取(行集可多行混编;垂直切片验证:完整战斗逐行归因)。
|
||||
## 行序即契约:同带键多行必须文件内相邻——resolver 返回序/生成序/叙述取首行全依赖文件行序。
|
||||
## 深度带 = 节点深度所在的区间:配池按带不按点,难度坡随带递增;row_kind 承载「Boss 行按终点配置」。
|
||||
## 行集实例总数 ≤4 是开战骰后守卫(加载期不可判,闸在单位容器 spawn);技能池构成纪律需跨表视野,校验在 ConfigDB 跨表通道。
|
||||
class_name EnemyTable
|
||||
extends Resource
|
||||
|
||||
@export var rows: Array[Dictionary] = [] # Array[Dictionary]
|
||||
|
||||
# 行类封闭枚举(对齐关卡节点 type 词表):battle = 战斗节点配池,boss = 终点行集
|
||||
const ROW_KINDS := ["battle", "boss"]
|
||||
|
||||
# 护甲类型封闭枚举(克制矩阵本体在 SettlementTable;
|
||||
# 语义键 {light, ward, heavy} = 轻甲 / 魔法护甲 / 重甲)
|
||||
const ARMOR_TYPES := ["light", "ward", "heavy"]
|
||||
@@ -35,19 +39,23 @@ func validate() -> Array:
|
||||
errs.append({"category": "empty_table", "message": "EnemyTable 为空"})
|
||||
return errs
|
||||
var seen := {}
|
||||
var last_rk := ""
|
||||
var closed_rk := {}
|
||||
var last_rowset_key := ""
|
||||
var closed_rowset := {}
|
||||
var anchor_ok := {}
|
||||
var boss_ok := {}
|
||||
for row in rows:
|
||||
var L: int = int(row.get("level_index", 0))
|
||||
var D: int = int(row.get("node_depth", -1))
|
||||
var rk: String = String(row.get("row_kind", ""))
|
||||
var band: int = int(row.get("depth_band", -1))
|
||||
var eid: String = row.get("enemy_id", "")
|
||||
var key := "%d_%d_%s" % [L, D, eid]
|
||||
var key := "%d_%s_%d_%s" % [L, rk, band, eid]
|
||||
if seen.has(key):
|
||||
errs.append({"category": "dup_key", "message": "EnemyTable 键重复: %s" % key})
|
||||
seen[key] = true
|
||||
if L < 1 or D < 0:
|
||||
errs.append({"category": "domain", "message": "进度键越域 (%d,%d)" % [L, D]})
|
||||
if L < 1 or band < 0:
|
||||
errs.append({"category": "domain", "message": "进度键越域 (%d,band %d)" % [L, band]})
|
||||
if not ROW_KINDS.has(rk):
|
||||
errs.append({"category": "domain", "message": "%s row_kind 未知枚举: \"%s\"(域 {battle, boss})" % [eid, rk]})
|
||||
if eid == "":
|
||||
errs.append({"category": "domain", "message": "enemy_id 空"})
|
||||
if String(row.get("display_name", "")) == "":
|
||||
@@ -118,23 +126,41 @@ func validate() -> Array:
|
||||
var ac: int = int(row.get("armor_class", 0))
|
||||
if ac < 0 or ac > 999:
|
||||
errs.append({"category": "domain", "message": "%s armor_class 越域 [0,999]: %d" % [eid, ac]})
|
||||
# 行集构成纪律②(行序即契约,敌人系统 v0.15):同坐标多行必须
|
||||
# 文件内相邻。迭代中记录上一行坐标;当前坐标曾出现过但 ≠ 上一行坐标 = 同坐标行被隔断,报错。
|
||||
# 同坐标多敌种混编合法;同坐标同敌种第二行仍由
|
||||
# 行集构成纪律②(行序即契约,敌人系统 v0.15):同带键多行必须
|
||||
# 文件内相邻。迭代中记录上一行行集键;当前行集键曾出现过但 ≠ 上一行行集键 = 同带键行被隔断,报错。
|
||||
# 同带键多敌种混编合法;同带键同敌种第二行仍由
|
||||
# dup_key 拦截(键含 enemy_id)。错误类别沿用 rowset_rule(定位面不变)。
|
||||
var rk := "%d_%d" % [L, D]
|
||||
if rk != last_rk:
|
||||
if closed_rk.has(rk):
|
||||
errs.append({"category": "rowset_rule", "message": "(%d,%d) 行集行序违例:同坐标多行必须文件内相邻(行序即契约——resolver 返回序/生成序/叙述取首行全依赖文件行序)" % [L, D]})
|
||||
closed_rk[rk] = true
|
||||
last_rk = rk
|
||||
anchor_ok[L] = anchor_ok.get(L, false) or D == 0
|
||||
var rowset_key := "%d_%s_%d" % [L, rk, band]
|
||||
if rowset_key != last_rowset_key:
|
||||
if closed_rowset.has(rowset_key):
|
||||
errs.append({"category": "rowset_rule", "message": "(%d,%s,%d) 行集行序违例:同带键多行必须文件内相邻(行序即契约——resolver 返回序/生成序/叙述取首行全依赖文件行序)" % [L, rk, band]})
|
||||
closed_rowset[rowset_key] = true
|
||||
last_rowset_key = rowset_key
|
||||
if rk == "battle" and band == 0:
|
||||
anchor_ok[L] = true
|
||||
elif rk == "boss":
|
||||
boss_ok[L] = true
|
||||
if not anchor_ok.has(L):
|
||||
anchor_ok[L] = false
|
||||
for L in anchor_ok.keys():
|
||||
if not anchor_ok[L]:
|
||||
errs.append({"category": "anchor", "message": "EnemyTable level %d 缺 depth-0 锚行" % L})
|
||||
errs.append({"category": "anchor", "message": "EnemyTable level %d 缺 (battle, band 0) 锚行" % L})
|
||||
if not boss_ok.has(L):
|
||||
errs.append({"category": "anchor", "message": "EnemyTable level %d 缺 Boss 行集(row_kind=boss 行 ≥1)" % L})
|
||||
return errs
|
||||
|
||||
|
||||
# 深度带切法单点(唯一实现,禁第二份算法):band_keys = 该 (level_index, row_kind) 的
|
||||
# depth_band 键升序数组;返回首个 ≥ D 的键(请求落带取偏难一侧)。
|
||||
# D 高于全部键 → 返回 -1——resolver 侧 clamp 顶带+warning,跨表校验侧判「顶 clamp 消解」违例;
|
||||
# band_keys 空 → 同样返回 -1(resolver 随之返回空行集,交消费侧既有空行集 fatal 路径)。
|
||||
static func band_of(band_keys: Array, D: int) -> int:
|
||||
for k in band_keys:
|
||||
if int(k) >= D:
|
||||
return int(k)
|
||||
return -1
|
||||
|
||||
|
||||
# 进度键解析单点收口(全仓统走
|
||||
# ConfigDbResolver.resolve_enemy_rowset ceil 版行集数组:ceil 保证偏难,
|
||||
# 禁双算法并存〔状态记录 312〕;坐标须全精确命中)。
|
||||
# ConfigDbResolver.resolve_enemy_rowset 带寻址行集数组:D 维经本表 band_of 落带(偏难一侧),
|
||||
# 禁双算法并存〔状态记录 312〕;L 维 ceil/clamp 与越顶 clamp 语义归 resolver)。
|
||||
+44
-20
@@ -6,7 +6,8 @@
|
||||
script = ExtResource("1")
|
||||
rows = [{
|
||||
"level_index": 1,
|
||||
"node_depth": 0,
|
||||
"row_kind": "battle",
|
||||
"depth_band": 0,
|
||||
"enemy_id": "goblin",
|
||||
"display_name": "地精",
|
||||
"unit_count": 1,
|
||||
@@ -27,7 +28,8 @@ rows = [{
|
||||
"armor_class": 0
|
||||
}, {
|
||||
"level_index": 1,
|
||||
"node_depth": 1,
|
||||
"row_kind": "battle",
|
||||
"depth_band": 1,
|
||||
"enemy_id": "goblin",
|
||||
"display_name": "地精一伙",
|
||||
"unit_count": 2,
|
||||
@@ -48,7 +50,8 @@ rows = [{
|
||||
"armor_class": 1
|
||||
}, {
|
||||
"level_index": 1,
|
||||
"node_depth": 2,
|
||||
"row_kind": "battle",
|
||||
"depth_band": 2,
|
||||
"enemy_id": "bugbear",
|
||||
"display_name": "熊地精",
|
||||
"unit_count": 1,
|
||||
@@ -71,7 +74,8 @@ rows = [{
|
||||
"armor_class": 2
|
||||
}, {
|
||||
"level_index": 1,
|
||||
"node_depth": 3,
|
||||
"row_kind": "battle",
|
||||
"depth_band": 3,
|
||||
"enemy_id": "hobgoblin",
|
||||
"display_name": "大地精",
|
||||
"unit_count": 2,
|
||||
@@ -95,7 +99,8 @@ rows = [{
|
||||
"armor_class": 2
|
||||
}, {
|
||||
"level_index": 1,
|
||||
"node_depth": 4,
|
||||
"row_kind": "boss",
|
||||
"depth_band": 4,
|
||||
"enemy_id": "ogre_chief",
|
||||
"display_name": "食人魔酋长",
|
||||
"unit_count": 1,
|
||||
@@ -122,7 +127,8 @@ rows = [{
|
||||
"armor_class": 8
|
||||
}, {
|
||||
"level_index": 2,
|
||||
"node_depth": 0,
|
||||
"row_kind": "battle",
|
||||
"depth_band": 0,
|
||||
"enemy_id": "bugbear",
|
||||
"display_name": "熊地精",
|
||||
"unit_count": 1,
|
||||
@@ -145,7 +151,8 @@ rows = [{
|
||||
"armor_class": 1
|
||||
}, {
|
||||
"level_index": 2,
|
||||
"node_depth": 1,
|
||||
"row_kind": "battle",
|
||||
"depth_band": 1,
|
||||
"enemy_id": "bugbear",
|
||||
"display_name": "熊地精猎群",
|
||||
"unit_count": 2,
|
||||
@@ -169,7 +176,8 @@ rows = [{
|
||||
"armor_class": 2
|
||||
}, {
|
||||
"level_index": 2,
|
||||
"node_depth": 4,
|
||||
"row_kind": "battle",
|
||||
"depth_band": 4,
|
||||
"enemy_id": "giant_spider",
|
||||
"display_name": "巨蜘蛛",
|
||||
"unit_count": 2,
|
||||
@@ -192,7 +200,8 @@ rows = [{
|
||||
"armor_class": 2
|
||||
}, {
|
||||
"level_index": 2,
|
||||
"node_depth": 5,
|
||||
"row_kind": "battle",
|
||||
"depth_band": 5,
|
||||
"enemy_id": "bugbear",
|
||||
"display_name": "熊地精重击手",
|
||||
"unit_count": 1,
|
||||
@@ -215,7 +224,8 @@ rows = [{
|
||||
"armor_class": 8
|
||||
}, {
|
||||
"level_index": 2,
|
||||
"node_depth": 5,
|
||||
"row_kind": "battle",
|
||||
"depth_band": 5,
|
||||
"enemy_id": "giant_spider",
|
||||
"display_name": "巨蜘蛛巢群",
|
||||
"unit_count": 2,
|
||||
@@ -242,7 +252,8 @@ rows = [{
|
||||
"armor_class": 2
|
||||
}, {
|
||||
"level_index": 2,
|
||||
"node_depth": 6,
|
||||
"row_kind": "battle",
|
||||
"depth_band": 6,
|
||||
"enemy_id": "giant_spider",
|
||||
"display_name": "巨蜘蛛巢群",
|
||||
"unit_count": 2,
|
||||
@@ -269,7 +280,8 @@ rows = [{
|
||||
"armor_class": 2
|
||||
}, {
|
||||
"level_index": 2,
|
||||
"node_depth": 7,
|
||||
"row_kind": "battle",
|
||||
"depth_band": 7,
|
||||
"enemy_id": "giant_spider",
|
||||
"display_name": "巨蜘蛛巢群",
|
||||
"unit_count": 2,
|
||||
@@ -296,7 +308,8 @@ rows = [{
|
||||
"armor_class": 3
|
||||
}, {
|
||||
"level_index": 2,
|
||||
"node_depth": 9,
|
||||
"row_kind": "boss",
|
||||
"depth_band": 9,
|
||||
"enemy_id": "drider",
|
||||
"display_name": "蛛化卓尔精灵",
|
||||
"unit_count": 1,
|
||||
@@ -326,7 +339,8 @@ rows = [{
|
||||
"armor_class": 10
|
||||
}, {
|
||||
"level_index": 3,
|
||||
"node_depth": 0,
|
||||
"row_kind": "battle",
|
||||
"depth_band": 0,
|
||||
"enemy_id": "duergar",
|
||||
"display_name": "灰矮人",
|
||||
"unit_count": 1,
|
||||
@@ -352,7 +366,8 @@ rows = [{
|
||||
"armor_class": 2
|
||||
}, {
|
||||
"level_index": 3,
|
||||
"node_depth": 1,
|
||||
"row_kind": "battle",
|
||||
"depth_band": 1,
|
||||
"enemy_id": "hook_horror",
|
||||
"display_name": "钩爪恐魔",
|
||||
"unit_count": 2,
|
||||
@@ -375,7 +390,8 @@ rows = [{
|
||||
"armor_class": 3
|
||||
}, {
|
||||
"level_index": 3,
|
||||
"node_depth": 4,
|
||||
"row_kind": "battle",
|
||||
"depth_band": 4,
|
||||
"enemy_id": "hook_horror",
|
||||
"display_name": "钩爪恐魔",
|
||||
"unit_count": 2,
|
||||
@@ -399,7 +415,8 @@ rows = [{
|
||||
"armor_class": 3
|
||||
}, {
|
||||
"level_index": 3,
|
||||
"node_depth": 5,
|
||||
"row_kind": "battle",
|
||||
"depth_band": 5,
|
||||
"enemy_id": "duergar",
|
||||
"display_name": "灰矮人重装手",
|
||||
"unit_count": 1,
|
||||
@@ -425,7 +442,8 @@ rows = [{
|
||||
"armor_class": 9
|
||||
}, {
|
||||
"level_index": 3,
|
||||
"node_depth": 6,
|
||||
"row_kind": "battle",
|
||||
"depth_band": 6,
|
||||
"enemy_id": "hook_horror",
|
||||
"display_name": "钩爪恐魔巢群",
|
||||
"unit_count": 2,
|
||||
@@ -449,7 +467,8 @@ rows = [{
|
||||
"armor_class": 3
|
||||
}, {
|
||||
"level_index": 3,
|
||||
"node_depth": 7,
|
||||
"row_kind": "battle",
|
||||
"depth_band": 7,
|
||||
"enemy_id": "duergar",
|
||||
"display_name": "灰矮人小队",
|
||||
"unit_count": 2,
|
||||
@@ -476,7 +495,8 @@ rows = [{
|
||||
"armor_class": 4
|
||||
}, {
|
||||
"level_index": 3,
|
||||
"node_depth": 9,
|
||||
"row_kind": "boss",
|
||||
"depth_band": 9,
|
||||
"enemy_id": "duergar_king",
|
||||
"display_name": "灰矮人石君王",
|
||||
"unit_count": 1,
|
||||
@@ -669,3 +689,7 @@ rows = [{
|
||||
; N3 敌方攻击类型轴开列(2026-10-13 迭代 24 卡①,承接锻造工坊实装批迁移面⑥):20 行全填 normal 挂敌种行——
|
||||
; 不做差异化(pierce/magic 属新设计另立项)。全局平移效应=主角穿重甲受伤 −1 / 魔法护甲 +1 / 轻甲 0,
|
||||
; 锻造甲路线首次具备战斗意义;克制矩阵数值零触碰(settlement.tres armor_effectiveness 不改)。
|
||||
;
|
||||
; 行键 = (level_index, row_kind, depth_band, enemy_id);row_kind ∈ {battle, boss}——battle=战斗节点配池,boss=终点行集;
|
||||
; depth_band=节点深度所在的带键,请求落带取首个 ≥ 深度的键(偏难一侧);越顶带由 resolver clamp、跨表校验判为调参事故。
|
||||
; hobgoblin 行为缺口行:无立绘、不在敌种表——留任在役,补齐三件套(立绘+语义模板+敌种表行)归敌种内容批。
|
||||
@@ -73,7 +73,7 @@ static func boss_name(runner, db) -> String:
|
||||
if runner != null:
|
||||
var node: Dictionary[Variant, Variant] = runner.current_node()
|
||||
if String(node.get("type", "")) == "boss":
|
||||
var rowset: Array[Variant] = db.resolve_enemy_rowset(int(runner.level_row.level_index), int(node.get("node_depth", -1)))
|
||||
var rowset: Array[Variant] = db.resolve_enemy_rowset(int(runner.level_row.level_index), int(node.get("node_depth", -1)), "boss")
|
||||
if not rowset.is_empty():
|
||||
return String(rowset[0].get("display_name", ""))
|
||||
return "遗迹守卫"
|
||||
|
||||
Reference in new issue
Block a user