- CLAUDE.md 硬性红线新增「文件头零工作日志」;code-conventions.md 新增 §1.4.1(含反面案例) - 全仓 .gd 文件头注清偿:删除全部带日期条目、批次叙事段与迁移退役叙述,保留并改写为 现在时的职责、接口契约、GDD 锚内容(main.gd 头注 42→31 行、save_manager.gd 49→45 行) - 纯注释改动零行为变更,按用户令免测试免冒烟
70 lines
4.0 KiB
GDScript
70 lines
4.0 KiB
GDScript
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||
# Validation Question: 专长订阅行能否经效果引擎统一解释执行、逐件行为与声明一致(P0)
|
||
# Date: 2026-10-11(设计权威 design/gdd/systems/冒险循环/奇物.md——不设流派标签、不设档位机制;
|
||
# rarity 四级;概率位=档+rarity 与上限=cap 两独立属性解耦——cap 拿了才计、刷出不计)
|
||
# 概念指路:本表的专长,与 adventure.tres 的属性值提升、资源,同属一个对象——
|
||
# 「奇物」(一次冒险内各渠道给予玩家的增益总称)。定义见 design/gdd/systems/冒险循环/奇物.md。
|
||
# 它们是它分类轴上的枚举(专长 / 属性值提升 / 资源),不是三套系统;本表承载「专长」(专长 Feat)的逐项定义
|
||
# (表名与 feat_id 沿用历史命名)。目标渠道:专长走关底 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 节点表)。
|
||
# 注记:事件族(开格/插旗/踩雷/节奏/战斗系)仅系设计侧分组工具——不上数据行、不上卡面(命名铁律)。
|
||
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 {}
|