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
@@ -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 行为缺口行:无立绘、不在敌种表——留任在役,补齐三件套(立绘+语义模板+敌种表行)归敌种内容批。
|
||||
Reference in new issue
Block a user