Files
gplan-2/game/data/schemas/feat_table.gd
T
DKChan 5f9d8e7b77 chore(代码卫生): 文件头零工作日志红线落文+全仓头注变更叙事清偿(2026-10-15 用户令)
- CLAUDE.md 硬性红线新增「文件头零工作日志」;code-conventions.md 新增 §1.4.1(含反面案例)
- 全仓 .gd 文件头注清偿:删除全部带日期条目、批次叙事段与迁移退役叙述,保留并改写为
  现在时的职责、接口契约、GDD 锚内容(main.gd 头注 42→31 行、save_manager.gd 49→45 行)
- 纯注释改动零行为变更,按用户令免测试免冒烟
2026-10-15 04:08:38 +08:00

70 lines
4.0 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: 专长订阅行能否经效果引擎统一解释执行、逐件行为与声明一致(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 {}