Files
gplan-2/game/data/schemas/feat_table.gd
T
DKChan b605022001 feat(迭代27 T5): 波五a 类型闸拉起+存量收口——审计器 0 存量+warning 配置在位
P1-6 类型闸(须 T1-T4 全落后——本波按执行顺序锁殿后):
- tools/audit_type_hints.py 新机器闸(V7): R1 无类型容器注解/R2 有值返回缺返回类型/
  R3 参数缺类型三规则+行级 # type-hint: ignore 显式白名单通道;产品码 core/data/presentation
- 存量收口: 368 处显式 Array/Dictionary 非参数化注解→机械参数化(Array[Variant]/
  Dictionary[Variant, Variant] 零行为面);13 处参数真类型(SaveManagerCore/ConfigDb/
  BattleOrchestrator/MinesweeperRules/LevelRunner/SettingsStore 等)
- 白名单豁免四处: board_generator generate/_attempt/legality/_finalize 的 db 参——
  测量面 _BoardRowStub 冒烟夹具鸭子型注入(typed 参拒 stub)
- project.godot 拉起 gdscript/warnings/untyped_declaration=1(V7 源码断言在 smoke _smoke_type_gate)
- static_scan 新 S9 类型闸规则;顺修历史 cd 错位(9-24 布局迁移后 /.. 落 game/ 致守卫
  恒跳过静默空转——修复后 S1-S4/S9 首次实跑全绿)+S1/S2 增 ADR-0007-exempt 标记豁免通道
  (main _seed_value 引擎熵源=文档化豁免面)

验收: 冒烟 1081/0(+V7×2)+run_all 27 套+static_scan 全绿+probe click+render golden+对拍三变体全同
2026-10-15 01:05:17 +08:00

74 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.
# VERTICAL SLICE - NOT FOR PRODUCTION
# Validation Question: 专长 27 件订阅行能否经效果引擎统一解释执行、逐件行为与声明一致(P0——
# 迭代 20 专长实装批 T3;前史:迭代 12 双层 BD 实施批 T1 建表/迭代 17 内容数据驱动扩展批 T3 行换代)
# Date: 2026-10-11(迭代 20 专长实装批 T3——重设计换代:流派标签/档位激活机制退役〔奇物.md v1
# 设计原则「不设流派标签、不设档位机制」〕;rarity 四级〔跨层框架〕;cap 已拿上限字段〔池结构案D
# 概率与上限解耦——每件标注两个独立属性:概率位=档+rarity、上限=cap(拿了才计、刷出不计)〕)
# 概念指路:本表的专长,与 adventure.tres 的属性值提升、资源,同属一个对象——
# 「奇物」(一次冒险内各渠道给予玩家的增益总称)。定义见 design/gdd/systems/冒险循环/奇物.md。
# 它们是它分类轴上的枚举(专长 / 属性值提升 / 资源),不是三套系统;本表承载「专长」(专长 Feat)的逐项定义。
# (2026-10-05 命名统一 + 分类重构:旧称「流派件」→「规则改写型特性」→ 现定名「专长」;
# 表名与 feat_id 沿用历史命名,与统一词族对齐归后续批次——见 06-待办清单「命名统一」条目。
# 目标渠道:专长走关底 Boss 击败后专属三选一(不新增节点类型),见奇物文档 2.1 节。)
# 数据配置 FeatTable schema(奇物·专长定义表;行 = Dictionary 键 feat_id,Rule 4 豁免
# 定义表——TreasureItemTable 先例)。
# 行字段:feat_id 主键 / display_name / rarity ∈ {common, excellent, rare, legendary}(四级稀有度,
# 与令牌池档位词表同名词族——令牌档 = rarity 直映射)/ cap 已拿上限(正整数,缺省 1——「同一件
# 最多拿 N 次」;概率位与上限解耦见奇物.md 池结构节)/ desc 效果说明(文案表值化——呈现层读行,
# 零硬编码)/ triggers 订阅行数组(订阅哪个事件 on / 什么条件 when / 什么效果 do——效果引擎
# EffectEngine 统一解释执行;四词表封闭校验=EffectEngine.validate_trigger_row 单一事实源)。
# 🔴 层级纪律(需求卡片红线 2):专长只走局内随机渠道(Boss 三选一/凭证开启)——本表行永不入同调位
# (treasure_items 表)/局外树(T8 节点表)。
# 退役注记(V14 旧实现摘除):ARCHETYPES 流派标签/档位激活(FeatsEngine 档位质变)随重设计退役——
# 事件族(开格/插旗/踩雷/节奏/战斗系)仅系设计侧分组工具不上数据行不上卡面(命名铁律)。
class_name FeatTable
extends Resource
@export var rows: Array[Variant] = [] # Array[Dictionary] 键 feat_id
## 稀有度词表(四级——与令牌池 TIERS 同名词族;令牌档 = rarity 直映射)
const RARITIES := ["common", "excellent", "rare", "legendary"]
## 行键白名单(🔴 零裸 flat 同族护栏——键出域=加载致命)
const ROW_KEYS := ["feat_id", "display_name", "rarity", "cap", "triggers", "desc", "note"]
## trigger 行键(订阅行三字段:on/when?/do)
const TRIGGER_KEYS := ["on", "when", "do"]
func validate() -> Array:
var errs: Array[Variant] = []
if rows.is_empty():
return errs # 空表合法态(T1 基建先行——逐件填行)
var seen := {}
for row in rows:
var rid: String = row.get("feat_id", "")
if rid == "":
errs.append({"category": "domain", "message": "feat_id 空"})
continue
if seen.has(rid):
errs.append({"category": "dup_key", "message": "feat_id 重复: %s" % rid})
continue
seen[rid] = true
if String(row.get("display_name", "")) == "":
errs.append({"category": "domain", "message": "%s display_name 空" % rid})
if not RARITIES.has(String(row.get("rarity", ""))):
errs.append({"category": "enum", "message": "%s rarity 出域 %s" % [rid, str(RARITIES)]})
if row.has("cap") and int(row.get("cap", 1)) < 1:
errs.append({"category": "domain", "message": "%s cap 出域 [1,∞): %d" % [rid, int(row.get("cap", 1))]})
if not row.has("triggers"):
errs.append({"category": "domain", "message": "%s 缺 triggers(订阅形态唯一合法行形)" % rid})
else:
EffectEngine.validate_trigger_row(rid, row, row.get("triggers", []) as Array, errs)
for k in row.keys():
if not ROW_KEYS.has(String(k)):
errs.append({"category": "zero_flat", "message": "%s 行键出白名单: %s" % [rid, String(k)]})
return errs
func row_by_id(feat_id: String) -> Dictionary:
for row in rows:
if String(row.get("feat_id", "")) == feat_id:
return row
return {}