一、里程碑语义立规:M3=Alpha 完整实现/M4=Beta 完整实现,未明令关闭 M3 前只排 M3 需求(roadmap M-4 节头立规注+M-3 节头口径指针+迭代 21+ 行注+06 倍率差条注);迭代 20/21/22 规划冻结(roadmap 迭代 20 行注——先解决眼前问题,重启=用户直接发令) 二、文档正确性审查批三波:全 GDD 29 份过审——状态记录按代码证据订正/里程碑删前缀(M-x+迭代N→迭代N,裸 M-4→Beta 阶段(未排期))/退役词退役件当现役清零/在役旧实现节加隔离横幅;重件=秘宝.md v1.7 老正文深层清算(横幅重写消「过渡形态已退役」误读/沿革节换代/六件旧件登记摘除/验收与 charges 矛盾订正)+奇物 v3.8+冒险结构 v0.18+关卡节点地图 v1.7+图生成 v0.7+难度曲线与Boss v0.26+战斗结算 v0.27+敌人系统 v0.16+地图战斗资源 v0.21+增益状态 v0.13+战斗表现 v0.6+元进度 v1.9+生涯 v0.3+00 v1.3+02 v1.16+03 v1.1+ux 八份(battle 23 处/camp/shop/node-reward/level-clear/interaction-patterns/settlement-summary/level-map) 三、去毒批:奇物 v3.7(魔法道具→秘宝/档名三档→四级/门票与刷新「尚未入表」假话订正/验收第 3 条改令牌口径/资源成员指路凭证件表)+秘宝 v1.6(速览旧目录退役标注/退役件示例换现役) 四、道具退役素材库新建(design/research/道具退役素材库.md,案乙已追认):秘宝退役 9 件含去向+专长退役 27 件名单+待核 4 行;魔法提灯(fog_lantern)裁退役(06 条目已裁+秘宝.md 注记换代+素材库迁表一;拆线归实装批) 五、B2/B3/B4 裁决落文:元进度.md 道具工坊行删旧写新(v1.10——旧六行登记作废、指路秘宝新目录+解锁门槛谓词分级);奇物.md P1「两项合成高阶项」注销(v3.9——用户定性脏信息);§3 刷新句按最新口径换代(刷新券替代、不进持有集) 六、天赋树独立成文(B1 裁决):新建 design/gdd/systems/跨局循环/天赋树.md v0.1(33 节点机制全按 meta_tree.tres 实态+迭代 12 落籍裁决+被删底稿 git 取证——脊柱行门槛/钥石单币/Fork A/B 面/存档 v5 域/注入双通道)+元进度.md 定名节换代(v1.11)+02-系统清单跨局循环区加行(v1.17) 七、A 类卫生包:版本历史表理排四份(秘宝/奇物含 v2.7 断裂行修复/敌人系统/图生成,只调行序)+缺号注记三处(增益状态 v0.6 未考/冒险结构 v0.15/关卡节点地图 v1.6=MC-1 批)+node-reward v2.2 双号改 a/b+07 补建版本历史表+06 删「设置与本地化」空节+「文档/结构闸存量欠账」块挪文末「文档与结构」节+「L1 商店编排」两悬空指针改指 GP 换算失衡条+难度曲线两处过期指向核销注记(v0.27)+B5「现役 UI/文案两处自相冲突」登记 06 待办(battle_narrator.gd:160 旧话/hero_info_panel 两段同标「特性」——随后续迭代修) 八、命名统一(用户令「任务书」全库改「需求卡片」):167 文件约 800 处(design/ 全部文档+roadmap+CLAUDE.md+active.md+entities.yaml+game/ 注释与数据批注);两文件改名(需求卡片-M4数值批/需求卡片-冒险前资源配比预设批);残留=自造短语全量清单(黑话证据文档故意跳过) 九、CLAUDE.md:批次工作流标准清单收口段新增第 12 条「文档同步闸」(需求卡片必列关联文档同步面、合并前同步完毕方算收口,原 12/13 顺延);呈报六问铁规适用范围补立(前序批随批落籍) 十、active.md 快照节+工作日志 414-421(并行会话 416 黑话清扫条目按共享工作日志惯例一并入库);06 中并行会话「active.md 定性与裁剪」条目未提交,按零吞并纪律留在工作区未入库 零测试(文档批两入口铁规);主区前序脏件(.gitignore/.zcode/config.json/playnote/project.godot/.zcodeignore/美术审计报告 html 等)未带
211 lines
14 KiB
GDScript
211 lines
14 KiB
GDScript
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||
# Validation Question: 挖掘期撞上推理断头时,玩家能否从掉落池捡到秘宝、在关键时刻花掉买回情报或行动点(P0)
|
||
# Date: 2026-09-29(迭代 7 T1 数据面——秘宝内容批;设计权威 design/gdd/systems/排雷循环/秘宝.md v0.4+
|
||
# + registry 秘宝条目六行 mvp_content;2026-09-29 用户拍板:①乙 探宝罗盘退役留痕 ④甲 显影尘/地穴全图卷失义件
|
||
# 六行全建零投放「已解锁·能力封存中」)
|
||
# 数据配置 TreasureItemTable schema(秘宝 回填 owner)。
|
||
# 行 = Dictionary,键 item_id(Rule 4 豁免:定义表——registry 秘宝条目 Rule 4 豁免先例)。
|
||
# 七字段(registry fields 全量):item_id 主键 / display_name / dimension_tags ⊆ 五维 非空 /
|
||
# effect_kinds ⊆ {mine_reveal, reward_preview, ap_grant}(活性校验见下)/ target ∈ {none, area_3x3, board_wide} /
|
||
# magnitude [0,999](仅 ap_grant 行消费,加载校验 ≥1)/ use_phase ∈ {active_dig}。
|
||
# additive 两标记(迭代 7 T1——拍板①④的表内表达,非 registry fields 预登记字段的扩列随批登记):
|
||
# retired = 退役留痕行(loot_compass——未开格类型微光常显覆盖预读价值,2026-09-29 用户拍板退役;
|
||
# 行名保留=meta.tres workshop_items 六件词表恰全校验兼容,存档 item_unlocks 旧键无害;
|
||
# 免活性校验、不入投放校验、不入 F2 覆盖面、奖励节点/掉落池排除)
|
||
# deferred = 效果失义待重定行(probe_dust/catacomb_scroll——原显影半边随雾格移除失义,效果与目标
|
||
# 随秘宝整体重设计归 Alpha(2026-09-20 用户拍板);免 effect_kinds 非空校验、不入投放与 F2)
|
||
# 承重护栏(秘宝 GDD §结构冻结,schema 校验强制不靠运行时拦截):
|
||
# 真视窄域——effect_kinds 含 mine_reveal 的活行 target 必须 area_3x3(真视一次最多 3×3,禁全图看雷);
|
||
# board_wide 仅恢复推理原料类——target=board_wide 的活行 effect_kinds 不得含 mine_reveal(现行无此类活行,
|
||
# 显影载体待 Alpha 重定后落位)。
|
||
# 同调实施批扩列(迭代 11 第①批 T1,2026-10-03——需求卡片 v1.1;设计权威 proposal-magic-item-attunement-2026-09-22):
|
||
# form ∈ {"", "passive", "active_charged"}(空 = 切片期过渡形态行——掉落/停点渠道现行承载,T7 退役;
|
||
# passive = 装配整局生效;active_charged = 每冒险充能、挖掘期发起);
|
||
# charges_per_adventure(active_charged ≥1 / passive 恒 0);slot_cost(同调行恒 1——底稿裁决 5,schema 强制);
|
||
# unlock_requires 谓词行 [{key ∈ AttunementGrowthTable.COUNTER_KEYS, threshold ∈ [1,999]}](空 = 已解锁——
|
||
# 首批全解锁合法态,底稿 §2.2 用户裁;正式解锁渠道归 元进度 道具工坊柜台);
|
||
# effective_rounds(前 N 轮限时件轮数——P4 甲表值化;仅 ap_rounds_front 行消费);
|
||
# charge_n(充能进度阈值 N——仅 ap_charge_safe 行消费)。
|
||
# 🔴 零裸 flat 结构护栏(需求卡片 §三 1——机制轴禁无条件属性加项):行键白名单封闭(KEY_WHITELIST 外键 =
|
||
# 加载致命)+ effect_kinds 封闭枚举只收条件触发/机制修正类 kind——任何「无条件属性加项」类效果不得以
|
||
# 新 kind 或新键入表(返工红线)。
|
||
class_name TreasureItemTable
|
||
extends Resource
|
||
|
||
@export var rows: Array = [] # Array[Dictionary] 键 item_id
|
||
|
||
const DIMENSIONS := ["info", "risk", "action", "plan", "counter"]
|
||
# 同调批扩 enum(迭代 11 第①批 T1)——新增九 kind 全部条件触发/机制修正类;mine_reveal/ap_grant 语义不变。
|
||
# cell_removal = 爆破工兵包中立移除格(T7 原位换装时启用——T6 影响评估前置);mine_hit_halve 系固定
|
||
# 「减半」语义不消费 magnitude(反弹 N 由 reflect_damage 行 magnitude 承载)。
|
||
# 内容数据驱动扩展批 T2 挂账:主动件 kind 词族换代(reveal_tiles/preview/grant_ap/remove_mines/
|
||
# refresh_pick——效果引擎 ACTIVE_KINDS 同名闭集),被动件 effect_kinds 词族随 T4 秘宝迁移退役
|
||
# (passive 行改 triggers 事件订阅形态);旧词保留共存至迁移完成。
|
||
const EFFECT_KINDS := ["mine_reveal", "reward_preview", "ap_grant", "venom_rider", "flag_burst",
|
||
"mine_hit_halve", "reflect_damage", "ap_rounds_front", "ap_charge_safe", "cell_removal",
|
||
"reward_weight_bias", "bonus_refresh",
|
||
"reveal_tiles", "preview", "grant_ap", "remove_mines", "refresh_pick"]
|
||
const TARGETS := ["none", "area_3x3", "board_wide"]
|
||
const USE_PHASES := ["active_dig"]
|
||
const FORMS := ["", "passive", "active_charged"]
|
||
# magnitude 消费面(≥1 校验):grant_ap 与同调 N 值 kind;其余 kind 活行 magnitude 恒 0
|
||
const MAGNITUDE_KINDS := ["ap_grant", "venom_rider", "flag_burst", "reflect_damage", "ap_rounds_front",
|
||
"ap_charge_safe", "reward_weight_bias", "bonus_refresh", "grant_ap"]
|
||
# 🔴 零裸 flat 结构护栏:行键白名单封闭(键出域=加载致命)——机制轴禁无条件属性加项的表结构承载
|
||
const KEY_WHITELIST := ["item_id", "display_name", "dimension_tags", "effect_kinds", "target",
|
||
"magnitude", "use_phase", "retired", "deferred", "form", "charges_per_adventure", "slot_cost",
|
||
"unlock_requires", "effective_rounds", "charge_n", "triggers", "desc"]
|
||
|
||
|
||
func validate() -> Array:
|
||
var errs: Array = []
|
||
if rows.is_empty():
|
||
# 空表合法态(2026-09-17 用户拍板——切片期无道具内容;F2 覆盖检查仅对非空清单执行)
|
||
return errs
|
||
var seen := {}
|
||
for row in rows:
|
||
var iid: String = row.get("item_id", "")
|
||
if iid == "":
|
||
errs.append({"category": "domain", "message": "item_id 空"})
|
||
continue
|
||
if seen.has(iid):
|
||
errs.append({"category": "dup_key", "message": "item_id 重复: %s" % iid})
|
||
continue
|
||
seen[iid] = true
|
||
if String(row.get("display_name", "")) == "":
|
||
errs.append({"category": "domain", "message": "%s display_name 空" % iid})
|
||
var retired: bool = bool(row.get("retired", false))
|
||
var deferred: bool = bool(row.get("deferred", false))
|
||
# dimension_tags:五维词表 + 非空(退役/失义行仍须登记维度——语义面保留)
|
||
var tags: Array = row.get("dimension_tags", [])
|
||
if tags.is_empty():
|
||
errs.append({"category": "domain", "message": "%s dimension_tags 空" % iid})
|
||
for tag in tags:
|
||
if not DIMENSIONS.has(String(tag)):
|
||
errs.append({"category": "enum", "message": "%s dimension_tag 出域 %s: %s" % [iid, str(DIMENSIONS), str(tag)]})
|
||
var kinds: Array = row.get("effect_kinds", [])
|
||
var target: String = String(row.get("target", "none"))
|
||
if not TARGETS.has(target):
|
||
errs.append({"category": "enum", "message": "%s target 出域 %s: %s" % [iid, str(TARGETS), target]})
|
||
var phase: String = String(row.get("use_phase", "active_dig"))
|
||
if not USE_PHASES.has(phase):
|
||
errs.append({"category": "enum", "message": "%s use_phase 出域 %s: %s" % [iid, str(USE_PHASES), phase]})
|
||
# 🔴 零裸 flat 结构护栏:键白名单封闭(同调批 T1——机制轴禁无条件属性加项)
|
||
for k in row.keys():
|
||
if not KEY_WHITELIST.has(String(k)):
|
||
errs.append({"category": "zero_flat", "message": "%s 行键出白名单: %s(KEY_WHITELIST 外键禁入)" % [iid, String(k)]})
|
||
# triggers 事件订阅行(内容数据驱动扩展批 T2/T4——被动件换代形态;与 effect_kinds 互斥校验
|
||
# 面=passive 行(换代单形态——效果行为只走订阅);active_charged 行两形合法并存
|
||
# (effect_kinds=主动分发通道 + triggers=受理闸订阅,掘进蓄能先例))
|
||
if row.has("triggers"):
|
||
if (row.get("effect_kinds", []) as Array).size() > 0 and String(row.get("form", "")) != "active_charged" and not bool(row.get("retired", false)):
|
||
errs.append({"category": "domain", "message": "%s triggers 与 effect_kinds 并存(换代单形态——被动件迁移期禁双挂)" % iid})
|
||
EffectEngine.validate_trigger_row(iid, row, row.get("triggers", []) as Array, errs)
|
||
var form: String = String(row.get("form", ""))
|
||
if not FORMS.has(form):
|
||
errs.append({"category": "enum", "message": "%s form 出域 %s: %s" % [iid, str(FORMS), form]})
|
||
if retired or deferred:
|
||
# 退役/失义行:免 effect_kinds 非空与承重护栏校验(登记面保留,不入投放与 F2)
|
||
continue
|
||
# ---- 活行校验 ----
|
||
if kinds.is_empty() and not row.has("triggers"):
|
||
errs.append({"category": "domain", "message": "%s effect_kinds 空(活行;失义件应标 deferred,被动件应挂 triggers 订阅)" % iid})
|
||
var kind_seen := {}
|
||
for k in kinds:
|
||
var kind: String = String(k)
|
||
if not EFFECT_KINDS.has(kind):
|
||
errs.append({"category": "enum", "message": "%s effect_kind 出域 %s: %s" % [iid, str(EFFECT_KINDS), kind]})
|
||
if kind_seen.has(kind):
|
||
errs.append({"category": "dup_key", "message": "%s effect_kind 重复: %s" % [iid, kind]})
|
||
kind_seen[kind] = true
|
||
# 承重护栏①真视窄域:mine_reveal 活行 target 必须 area_3x3(一次最多 3×3,禁全图看雷)
|
||
if kind_seen.has("mine_reveal") and target != "area_3x3":
|
||
errs.append({"category": "load_bearing", "message": "%s 真视窄域护栏:mine_reveal 行 target 必须 area_3x3,实得 %s(秘宝 GDD 结构冻结)" % [iid, target]})
|
||
# 承重护栏②board_wide 仅恢复推理类:全图域行禁替推理效果
|
||
if target == "board_wide" and kind_seen.has("mine_reveal"):
|
||
errs.append({"category": "load_bearing", "message": "%s board_wide 行 effect_kinds 不得含 mine_reveal(全图域仅恢复推理原料类——Alpha 重定后落位)" % iid})
|
||
# magnitude:MAGNITUDE_KINDS 行消费(≥1);其余活行恒 0(同调批 T1——消费面自 ap_grant 扩至 N 值 kind)
|
||
var mag: int = int(row.get("magnitude", 0))
|
||
if mag < 0 or mag > 999:
|
||
errs.append({"category": "domain", "message": "%s magnitude 越域 [0,999]: %d" % [iid, mag]})
|
||
var mag_consuming := false
|
||
for k in kind_seen.keys():
|
||
if MAGNITUDE_KINDS.has(String(k)):
|
||
mag_consuming = true
|
||
if mag_consuming and mag < 1:
|
||
errs.append({"category": "cross_field", "message": "%s N 值 kind 行 magnitude 应 ≥1,实得 %d" % [iid, mag]})
|
||
if not mag_consuming and mag != 0 and not row.has("triggers"):
|
||
errs.append({"category": "cross_field", "message": "%s 非 N 值 kind 活行 magnitude 应 0(trigger 行 magnitude=value_from 取数源豁免),实得 %d" % [iid, mag]})
|
||
# ---- 同调行约束(form 非空——装配面;过渡行 form="" 不校验)----
|
||
if form != "":
|
||
if int(row.get("slot_cost", 1)) != 1:
|
||
errs.append({"category": "load_bearing", "message": "%s 同调占位统一 1 槽(slot_cost 恒 1——底稿裁决 5,schema 强制)" % iid})
|
||
var charges: int = int(row.get("charges_per_adventure", 0))
|
||
if form == "passive" and charges != 0:
|
||
errs.append({"category": "cross_field", "message": "%s passive 行 charges_per_adventure 应 0,实得 %d" % [iid, charges]})
|
||
if form == "active_charged" and charges < 1:
|
||
errs.append({"category": "cross_field", "message": "%s active_charged 行 charges_per_adventure 应 ≥1,实得 %d" % [iid, charges]})
|
||
var eff_rounds: int = int(row.get("effective_rounds", 0))
|
||
if eff_rounds < 0 or eff_rounds > 999:
|
||
errs.append({"category": "domain", "message": "%s effective_rounds 越域 [0,999]: %d" % [iid, eff_rounds]})
|
||
if kind_seen.has("ap_rounds_front") and eff_rounds < 1:
|
||
errs.append({"category": "cross_field", "message": "%s ap_rounds_front 行 effective_rounds 应 ≥1(P4 甲表值化),实得 %d" % [iid, eff_rounds]})
|
||
if not kind_seen.has("ap_rounds_front") and eff_rounds != 0 and not row.has("triggers"):
|
||
errs.append({"category": "cross_field", "message": "%s 非 ap_rounds_front 行 effective_rounds 应 0(trigger 行=when value_from 取数源豁免),实得 %d" % [iid, eff_rounds]})
|
||
var charge_n: int = int(row.get("charge_n", 0))
|
||
if charge_n < 0 or charge_n > 999:
|
||
errs.append({"category": "domain", "message": "%s charge_n 越域 [0,999]: %d" % [iid, charge_n]})
|
||
if kind_seen.has("ap_charge_safe") and charge_n < 1:
|
||
errs.append({"category": "cross_field", "message": "%s ap_charge_safe 行 charge_n 应 ≥1(每次充能需 N 安全格),实得 %d" % [iid, charge_n]})
|
||
if not kind_seen.has("ap_charge_safe") and charge_n != 0:
|
||
errs.append({"category": "cross_field", "message": "%s 非 ap_charge_safe 行 charge_n 应 0,实得 %d" % [iid, charge_n]})
|
||
# unlock_requires 谓词行:空 = 已解锁(首批全解锁合法态,底稿 §2.2);非空键 ∈ 计数键封闭枚举
|
||
var ureqs: Array = row.get("unlock_requires", [])
|
||
var ukeys := {}
|
||
for req in ureqs:
|
||
var ukey: String = String(req.get("key", ""))
|
||
if not AttunementGrowthTable.COUNTER_KEYS.has(ukey):
|
||
errs.append({"category": "enum", "message": "%s unlock_requires 计数键出域 %s: %s" % [iid, str(AttunementGrowthTable.COUNTER_KEYS), ukey]})
|
||
if ukeys.has(ukey):
|
||
errs.append({"category": "dup_key", "message": "%s unlock_requires 计数键重复: %s" % [iid, ukey]})
|
||
ukeys[ukey] = true
|
||
var uth: int = int(req.get("threshold", 0))
|
||
if uth < 1 or uth > 999:
|
||
errs.append({"category": "domain", "message": "%s unlock_requires 阈值越域 [1,999]: %d" % [iid, uth]})
|
||
return errs
|
||
|
||
|
||
# 同调行词表(form 非空)——装配覆盖层/效果接线/同调位上限的唯一取数口径(迭代 11 第①批 T1)
|
||
func attunement_rows() -> Array:
|
||
var out: Array = []
|
||
for row in rows:
|
||
if String(row.get("form", "")) != "" and not bool(row.get("retired", false)) and not bool(row.get("deferred", false)):
|
||
out.append(row)
|
||
return out
|
||
|
||
|
||
# 过渡行词表(form 空 = 切片期过渡形态活行)——掉落投放/停点池/过渡覆盖面的取数口径;
|
||
# 同调行永不入过渡渠道。T7 退役面切换后本口径随渠道消亡。
|
||
func transitional_rows() -> Array:
|
||
var out: Array = []
|
||
for row in rows:
|
||
if String(row.get("form", "")) == "" and not bool(row.get("retired", false)) and not bool(row.get("deferred", false)):
|
||
out.append(row)
|
||
return out
|
||
|
||
|
||
# 活行词表(非 retired 非 deferred)——投放校验/奖励节点池/F2 覆盖面的唯一取数口径
|
||
func active_rows() -> Array:
|
||
var out: Array = []
|
||
for row in rows:
|
||
if not bool(row.get("retired", false)) and not bool(row.get("deferred", false)):
|
||
out.append(row)
|
||
return out
|
||
|
||
|
||
func row_by_id(item_id: String) -> Dictionary:
|
||
for row in rows:
|
||
if String(row.get("item_id", "")) == item_id:
|
||
return row
|
||
return {}
|