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:
DKChan committed 2026-10-22 04:17:21 +08:00
1 parent 603eba3117
commit 07f3eb55e3
8 files changed
+191 -72

No files matched your search

+5 -3
View File
@@ -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():
+2 -2
View File
@@ -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——组合事故非配置事故)
+30 -9
View File
@@ -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:
+58 -12
View File
@@ -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
+4 -4
View File
@@ -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)
+47 -21
View File
@@ -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
View File
@@ -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 行为缺口行:无立绘、不在敌种表——留任在役,补齐三件套(立绘+语义模板+敌种表行)归敌种内容批。
+1 -1
View File
@@ -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 "遗迹守卫"