feat(tree): 迭代21天赋树实装批 T1-T6 主体——12节点换代+六键接线+营地四柜台(2026-10-12 U0)

T1 schema 换代:MetaTreeTable FORK_GROUPS 4→3(fg_mastery 退役)/RULE_KEYS 2→6(sense/economy 四键扩签)/
  fork 单行双态(effects_a/b+name_a/b、每组恰一校验)/row 字段退役(行门槛主语=节点等级 level_gate)/
  requires 只留点亮前置(R1 裁撤案执行);meta_tree.tres 33→12 行重排(脊柱2/锻造2含彩蛋/钥石2/同调6,
  att_socket1 改根节点无前置,easter_ley 摘 atk/armor_class 分量=契约9)
T2 购买与彩蛋面:tree_can_buy easter 拒购+行门槛换轴;tree_buy_fork=解锁默认A;tree_toggle_fork 零币切态;
  tree_rule_keys 按激活态 effects_a/b 投影;_check_easter_egg 特化判定(两线拉满+socket全解锁);营地树行板双态钮
T3 六键接线:EVENTS+flag_missed/FACTS+battle_index(L2 扩签两处);rebuild 六键合成订阅
  (posture 窗=battle_index&&round_index 双闸/sense 两组=counter 闸+记账双行 domain:adventure 限次2次 provisional);
  level_runner battle_index 注入(visited battle 型计数);_judge_flag 去重闸后 flag_missed 发射(契约12);
  聚=_roll_gp_drop 入账前×1.5 provisional(escrow 不乘);散=_spend_discounted ×80%(current_price/pool_price
  双计价点,刷新价不折=§11②定义边界);_start_current_level 限次按关清零(_reset_tree_limited_counters 可测口)
T4 注入换代:hero_override=树淬炼flat+FORGE_ROUTE_BONUS预留0(命中不注入=契约6);Attunement.slots 判定源切
  树点亮数(谓词表退役);tree_buy(ks_refresh) 同账路由 buy_attune_refresh(V15 双入口)
T5 旧档废弃与删除:save_manager tree_struct=2 结构版本标记+_normalize 白名单重建+树域废弃重置(禁迁移折算);
  migrate_tree/buy_forge/forge_flat/forge_level/forge_price/meta_forge_row/forge_products 行族/
  attunement_growth 表+访问器/生涯页三投影/career forge_total 全删;treasure_item_table UNLOCK_COUNTER_KEYS 内联;
  verifier_batch 花费策略换代树节点;MC slots 画像常量3
T6 营地统一:四柜台并排横滚(树淬炼主界面/锻造工坊钥匙门+零交互占位面板/秘宝匣钥匙门+内容迁入/图鉴常开);
  暂停层锻造段连带删;camp data forge 投影删
存量红修复:_flush_pending_reveals 三处空 source_class 补 fx_reveal+类级豁免(a57489e9 落地件,冒烟51行)
测试随批:smoke 树段全重写(V1/V2/V3/V8/V9/V16)+_smoke_tree_rules V10 行为段(窗/限次/守侧五场景编排rig/聚散
  同种子对拍)+单元 tree_rules_v11_test(六键合成行闭集校验)+run_all 22 套;ui_probe/ui_click_probe 换代树点击路径
轻闸全绿:冒烟886/0+run_all 22套+probe --click 全绿
This commit is contained in:
DKChan committed 2026-10-12 07:54:38 +08:00
1 parent 8093ff7ab2
commit 96fc61c701
30 files changed
+907 -1198

No files matched your search

@@ -1,81 +0,0 @@
# VERTICAL SLICE - NOT FOR PRODUCTION
# Validation Question: 同调位增长是否由「通关次数 + Boss 击杀数」经数据驱动的里程碑谓词表实时求值,
# 试玩后调增长曲线 = 改表不动码(proposal-magic-item-attunement-2026-09-22 §2.1 用户点名要求)
# Date: 2026-10-03(迭代 11 第①批 秘宝同调实施批 T1——需求卡片 v1.1 §六 T1;设计权威底稿 proposal §2)
# 数据配置 AttunementGrowthTable schema(秘宝 回填 owner)。
# 单行全局常量表(slot_cap + milestones 行族——AdventureTable 单行全局先例,Rule 4 豁免)。
# 行 = Dictionary:{id: String 唯一, grant: int, requires: [{key: String, threshold: int}]}——
# requires 为 (计数键, 阈值) 对的 AND 列表,全部满足即授予 grant 槽;
# 同调位读数唯一口径 = Σ已达成 grant clamp slot_cap(slots())。
# 承重契约(需求卡片 §三 7 + 底稿 §2.1):
# 计数键封闭枚举(COUNTER_KEYS——预留可插拔位 COUNTER_KEYS_RESERVED 本期不实现、加行即扩展不破坏
# schema);阈值 [1,999];Σgrant ≤ slot_cap(超出部分永不可达,禁);数值全 provisional 归
# 难度曲线与Boss——本表只锁形态,试玩调平衡的全部动作落在表上(改阈值/换计数键/slot_cap 加行)。
# OR/复合需求出现时升级谓词形态走需求卡片修订(底稿 §2.1 同款约束)。
class_name AttunementGrowthTable
extends Resource
@export var slot_cap: int = 3
@export var milestones: Array = [] # Array[Dictionary] 键 id/grant/requires
# 计数键封闭枚举(底稿 §2.1——save_file 逐域记账的键词表;T2 落 boss_kill_count 记账)
const COUNTER_KEYS := ["clear_count", "boss_kill_count"]
# 预留可插拔位(本期不实现,schema 枚举不收——底稿 §2.1 明列四件)
const COUNTER_KEYS_RESERVED := ["boss_firstkill_count", "adventure_count", "no_damage_clear_count", "run_depth_best"]
func validate() -> Array:
var errs: Array = []
if slot_cap < 0 or slot_cap > 9:
errs.append({"category": "domain", "message": "slot_cap 越域 [0,9]: %d" % slot_cap})
var seen := {}
var grant_sum := 0
for row in milestones:
var mid: String = String(row.get("id", ""))
if mid == "":
errs.append({"category": "domain", "message": "milestone id 空"})
continue
if seen.has(mid):
errs.append({"category": "dup_key", "message": "milestone id 重复: %s" % mid})
continue
seen[mid] = true
var grant: int = int(row.get("grant", 0))
if grant < 1 or grant > maxi(1, slot_cap):
errs.append({"category": "domain", "message": "%s grant 越域 [1,slot_cap=%d]: %d" % [mid, slot_cap, grant]})
grant_sum += maxi(0, grant)
var reqs: Array = row.get("requires", [])
if reqs.is_empty():
errs.append({"category": "domain", "message": "%s requires 空(无条件授予非法——里程碑必有谓词)" % mid})
var key_seen := {}
for req in reqs:
var key: String = String(req.get("key", ""))
if not COUNTER_KEYS.has(key):
errs.append({"category": "enum", "message": "%s 计数键出域 %s: %s" % [mid, str(COUNTER_KEYS), key]})
if key_seen.has(key):
errs.append({"category": "dup_key", "message": "%s 计数键重复: %s" % [mid, key]})
key_seen[key] = true
var th: int = int(req.get("threshold", 0))
if th < 1 or th > 999:
errs.append({"category": "domain", "message": "%s 阈值越域 [1,999]: %d" % [mid, th]})
if grant_sum > slot_cap:
errs.append({"category": "cross_field", "message": "Σgrant(%d) > slot_cap(%d)——超出部分永不可达,schema 禁" % [grant_sum, slot_cap]})
return errs
# 实时求值:Σ已达成 grant(谓词 = requires 全键达标;counters = {计数键: int},缺键按 0 计)
func granted_slots(counters: Dictionary) -> int:
var total := 0
for row in milestones:
var ok := true
for req in row.get("requires", []):
if int(counters.get(String(req.get("key", "")), 0)) < int(req.get("threshold", 0)):
ok = false
break
if ok:
total += maxi(0, int(row.get("grant", 0)))
return total
# 同调位读数唯一口径:Σ达成 grant clamp slot_cap(求值服务/装配覆盖层/运行期上限共读此口径)
func slots(counters: Dictionary) -> int:
return mini(granted_slots(counters), slot_cap)
@@ -1 +0,0 @@
uid://tfgsbs87xakg
+7 -32
View File
@@ -2,29 +2,28 @@
# Validation Question: 玩家能否在营地用钥石换永久强化、且强化在下一局开局生效——存档变厚成为玩家可感的现实
#(元进度 GDD 验收标准,本批 = 其实现批)
# Date: 2026-09-21(元进度实现批——设计权威 design/gdd/systems/冒险循环/元进度.md 定稿 v1.2)
# 数据配置 MetaTable schema(元进度/永久强化 行族四轴)。
# 数据配置 MetaTable schema(元进度行族)。
# 行族四轴(数据契约对齐 ConfigDB.meta_* 访问器——消费面一律走访问器,禁直读行集):
# forge_products = 属性锻造商品行(键 attr:{max_hp, atk, armor_class} 恰三唯一——注入契约三字段必齐;
# 投值语言锁定 flat 整数;几何定价 tier 级价格 = 迭代 p = floor(p × price_ratio_pct/100),tier 0 = base_price)。
# boss_keystone = Boss 钥石两档行(level_index 连续从 1;first_kill ≥ repeat_kill——首杀全量、
# 重复杀固定小额恒定不递减,两档数值归元进度设计批配读数)。
# workshop_items = 道具工坊行(item_id = 秘宝六件词表封闭恰全;首版解锁渠道 = 总通关次数
# 达标 clear_threshold——2026-09-20 排期裁决;局内效果整体归 Alpha 秘宝重设计批,本表只有解锁状态面)。
# conversion = 区间平移换算率行(attr 与 forge 词表一致恰三;per_level_shift_pct ∈ [0,100]——
# conversion = 区间平移换算率行(attr 与三属性轴词表一致恰三;per_level_shift_pct ∈ [0,100]——
# registry 键 meta_progression_conversion_table;护甲行强条目:验收读数必须按解锁后参数重测)。
# attune_refresh_upgrade = Y 刷新升级行族(迭代 11 第①批 T4——P1 甲:每冒险开局给 Y 次、冒险周期池、
# 用完即无;level 连续从 1,等级 L → refresh_charges=Y 表值,钥石直购 keystone_cost;数值全 provisional)。
# 〔forge_products 属性锻造行族已退役——迭代 21 T5 攻防所有权切分:生命唯一入口=树脊柱、攻/AC
# 唯一入口=锻造工坊路线;行族、访问器与购买面随批删除,历史投值记载归 git 考古。〕
# 行值全部 provisional 归 元进度设计批配读数(用户拍板骨架先行,先例 = GP+商店房批)。
class_name MetaTable
extends Resource
@export var forge_products: Array[Dictionary] = [] # Array[Dictionary] 键 attr
@export var boss_keystone: Array[Dictionary] = [] # Array[Dictionary] 键 level_index
@export var workshop_items: Array[Dictionary] = [] # Array[Dictionary] 键 item_id
@export var conversion: Array[Dictionary] = [] # Array[Dictionary] 键 attr
@export var attune_refresh_upgrade: Array[Dictionary] = [] # Array[Dictionary] 键 level——Y 刷新升级行族(迭代 11 第①批 T4)
const FORGE_ATTRS := ["max_hp", "atk", "armor_class"]
const FORGE_ATTRS := ["max_hp", "atk", "armor_class"] # 三属性轴词表(conversion 行键域——原 forge 词表名沿用作轴名)
const WORKSHOP_ITEM_IDS := ["probe_dust", "sapper_order", "loot_compass", "nest_pendulum", "catacomb_scroll", "blast_kit"]
# 工坊解锁渠道词表(可替换轴——2026-09-21 用户拍板:解锁条件须抽象、可替换、可扩展。
# 现行唯一合法值 total_clears = 总通关次数;扩新渠道 = 词表加值 + MetaProgression._workshop_progress
@@ -34,34 +33,10 @@ const WORKSHOP_CHANNELS := ["total_clears"]
func validate() -> Array:
var errs: Array[Dictionary] = []
# 行族空护栏(五族皆为 ConfigDB 消费面——缺族即致命)
for family in ["forge_products", "boss_keystone", "workshop_items", "conversion", "attune_refresh_upgrade"]:
# 行族空护栏(四族皆为 ConfigDB 消费面——缺族即致命)
for family in ["boss_keystone", "workshop_items", "conversion", "attune_refresh_upgrade"]:
if get(family).is_empty():
errs.append({"category": "empty_table", "message": "MetaTable 行族为空: %s" % family})
# forge_products:attr 词表封闭 + 唯一 + 覆盖恰三(注入契约三字段必齐)+ flat/价格域 + 公比域 [100,400]
var seen_attr := {}
for row in forge_products:
var attr: String = row.get("attr", "")
if not FORGE_ATTRS.has(attr):
errs.append({"category": "enum", "message": "forge_products attr 出域 {max_hp, atk, armor_class}: %s" % attr})
if seen_attr.has(attr):
errs.append({"category": "dup_key", "message": "forge_products attr 重复: %s" % attr})
seen_attr[attr] = true
if String(row.get("display_name", "")) == "":
errs.append({"category": "domain", "message": "forge_products %s display_name 空" % attr})
var flat: int = int(row.get("flat_value", 0))
if flat < 1 or flat > 999:
errs.append({"category": "domain", "message": "forge_products %s flat_value 越域 [1,999]: %d" % [attr, flat]})
var base: int = int(row.get("base_price", 0))
if base < 1 or base > 999:
errs.append({"category": "domain", "message": "forge_products %s base_price 越域 [1,999]: %d" % [attr, base]})
var ratio: int = int(row.get("price_ratio_pct", -1))
if ratio < 100 or ratio > 400:
errs.append({"category": "domain", "message": "forge_products %s price_ratio_pct 越域 [100,400]: %d" % [attr, ratio]})
if not forge_products.is_empty():
for attr_key in FORGE_ATTRS:
if not seen_attr.has(attr_key):
errs.append({"category": "coverage", "message": "forge_products 缺属性键: %s(注入契约三字段必齐——恰三)" % attr_key})
# boss_keystone:level_index 唯一 + 连续从 1 + 两档域 + first ≥ repeat(首杀全量、重复小额恒定)
var seen_li := {}
for row in boss_keystone:
+52 -39
View File
@@ -1,23 +1,27 @@
# VERTICAL SLICE - NOT FOR PRODUCTION
# Validation Question: 三旧体系(锻造/钥石/同调)收敛为一棵营地天赋树后,双币消费、row-gating、
# A/B 免费切换、彩蛋觉醒能否机械承载(P0——迭代 12 批次二 T8,R1 甲融合案)
# Date: 2026-10-04(双层 BD 实施批 T8;设计权威 双层BD体系-整合定稿-2026-10-03 §1.4)
# 数据配置 MetaTreeTable schema(元进度 回填 owner 批末落文;行 = Dictionary 键 node_id,Rule 4 豁免
# 定义表——FeatTable 先例)。
# 行字段:node_id / display_name / vein ∈ VEINS(spine=主干脊柱 row-gating,钥石买深度〔2026-10-07
# 用户裁甲——树全钥石单币,原双币铁律骨架披露销账〕;forge=锻造脉
# 数值专精;keystone=钥石脉内容解锁;attune=同调脉槽位与规则)/ kind ∈ KINDS(stat=四线数值/
# rule=全局规则/unlock=内容解锁/socket=同调位/fork=A/B 互斥对/easter=彩蛋觉醒)/
# currency ∈ {keystone, free}(树全钥石单币——2026-10-07 用户裁甲;free=迁移点亮与彩蛋)/
# price [0,9999] / cap [1,9](等级上限;fork/unlock 恒 1)/
# row(脊柱行号 1..N——row-gating:行 N 可购条件 = 全树已购等级总和 ≥ SPINE_ROW_GATE×(N-1))/
# requires Array[node_id](显式前置——父开子脉内链)/ effects Dictionary(stat 四线 flat:
# atk/max_hp/hit/armor_class;rule 键封闭 RULE_KEYS)/ fork_group/fork_side(a/b——同组互斥、
# 营地免费切换)。
# 🔴 层级纪律(需求卡片红线 2):局外树只放全局规则件——effects 禁流派标签(relic 特性永不入树,
# archetype 键出域=加载致命);规则改写型特性只走局内随机渠道。
# A/B 只放规则面(定稿 1.3——全树互斥面 ≤10-15% 首期 4 组);两侧「不同强非强弱」MC 验差 <~3pp(T10)。
# 彩蛋 ≤3(定稿 1.4——点满某脉某排免费觉醒;发现率 <5% 改灰色预告——T10 追认单呈报)。
# 数据配置 MetaTreeTable schema(迭代 21 天赋树实装批 T1 换代——设计权威
# design/gdd/systems/跨局循环/天赋树.md v0.8,2026-10-11 三轮换代终态)。
# 结构:12 节点 = 脊柱 2(生命淬炼/命中淬炼——一线一节点 cap4,2026-10-11 用户裁:一个内容一个
# 节点、等级在节点内)+ 锻造脉 2(锻造工坊钥匙 free 无前置 + 彩蛋地脉共鸣——彩蛋含在此 2 内)
# + 钥石脉 2(秘宝匣/刷新升级)+ 同调脉 6(同调位 1/2/3 + Fork 3 双态节点)。
# 行字段:node_id / display_name / vein ∈ VEINS(spine=主干脊柱;forge=锻造脉;keystone=钥石脉内容
# 解锁;attune=同调脉)/ kind ∈ KINDS(stat=数值线/rule=全局规则/unlock=内容解锁/socket=同调位/
# fork=一节点双态开关/easter=彩蛋觉醒)/ currency ∈ {keystone, free}(树全钥石单币——2026-10-07
# 用户裁甲;free=工作间钥匙与彩蛋)/ price [0,9999] / cap [1,9](等级上限;unlock/socket/fork/
# easter 恒 1)/ requires Array[node_id](点亮前置——前置节点 tree_level>=1 即满足;
# 「等级阈值前置」新形态经 R1 裁撤〔2026-10-12 卡片 v1.7 默认案执行〕,schema 只保留点亮前置)/
# effects Dictionary(stat 线 flat:max_hp/hit;rule 键封闭 RULE_KEYS)。
# Fork 一节点双态(2026-10-11 用户裁,原 A/B 双行结构收编):fork_group ∈ FORK_GROUPS 每组恰一节点;
# 行持 effects_a/effects_b 双效果键(A/B 只放规则面——键 ⊆ RULE_KEYS)+ name_a/name_b 双态呈现名;
# 购买解锁默认 A 态、营地零币切换(tree_toggle_fork),效果按激活态(save.fork_sides)取值。
# 行门槛(一线一节点换代):脊柱节点第 N 级可购需全树已购总等级 ≥ SPINE_ROW_GATE×(N−1)(总等级口径
# =tree_nodes 全部节点等级之和,免费点亮与 Fork 解锁计入——2026-10-11 歧义扫描裁甲);
# row 字段随层行退役。
# 🔴 层级纪律(需求卡片红线 2):局外树只放全局规则件——effects 禁流派标签(archetype 键出域=加载
# 致命);规则改写型特性只走局内随机渠道,永不入树。
# 彩蛋 ≤3(schema 校验);觉醒判定=MetaProgression._check_easter_egg 特化(生命/命中两线拉满+同调位
# 全解锁),不依赖 requires 点亮形态。
# 数值全 provisional 归 M4 数值批(契约 7);两侧「不同强非强弱」MC 验差 <~3pp(V19)。
class_name MetaTreeTable
extends Resource
@@ -27,21 +31,23 @@ const VEINS := ["spine", "forge", "keystone", "attune"]
const KINDS := ["stat", "rule", "unlock", "socket", "fork", "easter"]
const CURRENCIES := ["keystone", "free"] # 树全钥石单币(2026-10-07 用户裁甲——原 gp 枚举值退役,字段保留为呈现元数据)
const STAT_KEYS := ["atk", "max_hp", "hit", "armor_class"]
## 规则键封闭词表(零裸 flat 同族护栏——effects.rule 键出域=加载致命;首期两键=T8 Fork 面真实接线)
const RULE_KEYS := ["posture_first_strike", "posture_bulwark"]
## 规则键封闭词表(零裸 flat 同族护栏——effects.rule 键出域=加载致命;六键=三组 Fork 双态真实接线,
## 迭代 21 T1 扩签:posture 两键现役沿袭,sense/economy 四键随危险直觉/财富之道两组建效面新增)
const RULE_KEYS := ["posture_first_strike", "posture_bulwark", "sense_sharp", "sense_guard", "economy_hoard", "economy_spend"]
const KEY_WHITELIST := ["node_id", "display_name", "vein", "kind", "currency", "price", "cap",
"row", "requires", "effects", "fork_group", "fork_side", "note"]
## 脊柱行门槛(row-gating:行 N 可购需全树已购总等级 ≥ GATE×(N−1)——「投入点数累计解锁新行」)
"requires", "effects", "effects_a", "effects_b", "name_a", "name_b", "fork_group", "note"]
## 脊柱行门槛(一线一节点:节点第 N 级可购需全树总等级 ≥ GATE×(N−1)——「投入点数累计解锁深度」)
const SPINE_ROW_GATE := 4
## A/B 组词表(同组恰两侧 a/b——互斥对;营地免费切换承载面)
const FORK_GROUPS := ["fg_posture", "fg_sense", "fg_economy", "fg_mastery"]
## Fork 双态组词表(每组恰一节点——互斥由单态承载;营地零币切换承载面;
## fg_mastery 克制专精整组砍除——2026-10-11 裁,类型表达归锻造工坊)
const FORK_GROUPS := ["fg_posture", "fg_sense", "fg_economy"]
func validate() -> Array:
var errs: Array = []
if rows.is_empty():
return errs # 空表合法态(T8 建表先行——内容随迁移批落)
return errs # 空表合法态(建表先行——内容随换代批落)
var seen := {}
var fork_seen := {}
for row in rows:
@@ -76,22 +82,28 @@ func validate() -> Array:
var fx: Dictionary = row.get("effects", {}) as Dictionary
for ek in fx.keys():
if not (STAT_KEYS.has(String(ek)) or RULE_KEYS.has(String(ek))):
errs.append({"category": "zero_flat", "message": "%s effects 键出域(四线/rule 词表封闭): %s" % [nid, String(ek)]})
errs.append({"category": "zero_flat", "message": "%s effects 键出域(stat 线/rule 词表封闭): %s" % [nid, String(ek)]})
# 🔴 层级纪律:局外树禁规则改写型特性(archetype 键出域=加载致命)
if fx.has("archetype"):
errs.append({"category": "load_bearing", "message": "%s effects 含 archetype(🔴 层级纪律——特性禁入局外树)" % nid})
# A/B 互斥对结构:fork 行必带 fork_group+side;同组恰 a/b 各一
# Fork 一节点双态结构:fork 行必带 fork_group(每组恰一节点);主 effects 须空,效果只放
# a/b 双键且键 ⊆ RULE_KEYS(A/B 只放规则面——层级纪律)
if kind == "fork":
var fg := String(row.get("fork_group", ""))
if not FORK_GROUPS.has(fg):
errs.append({"category": "enum", "message": "%s fork_group 出域" % nid})
var side := String(row.get("fork_side", ""))
if side != "a" and side != "b":
errs.append({"category": "enum", "message": "%s fork_side 出域: %s" % [nid, side]})
var fk := fg + "|" + side
if fork_seen.has(fk):
errs.append({"category": "dup_key", "message": "%s fork 对重复: %s" % [nid, fk]})
fork_seen[fk] = true
if fork_seen.has(fg):
errs.append({"category": "dup_key", "message": "%s fork 组重复(一组恰一节点): %s" % [nid, fg]})
fork_seen[fg] = true
if not fx.is_empty():
errs.append({"category": "cross_field", "message": "%s fork 行主 effects 须空(效果只放 a/b 双键)" % nid})
for skey in ["effects_a", "effects_b"]:
var sd: Dictionary = row.get(skey, {}) as Dictionary
if sd.is_empty():
errs.append({"category": "domain", "message": "%s %s 缺失或空(双态节点须双侧效果)" % [nid, skey]})
for k in sd.keys():
if not RULE_KEYS.has(String(k)):
errs.append({"category": "zero_flat", "message": "%s %s 键出域(A/B 只放规则面): %s" % [nid, skey, String(k)]})
# requires 悬空引用
for row in rows:
for req in (row.get("requires", []) as Array):
@@ -103,7 +115,7 @@ func validate() -> Array:
if String(row.get("kind", "")) == "easter":
easter += 1
if easter > 3:
errs.append({"category": "load_bearing", "message": "彩蛋节点 %d > 3(定稿 §1.4 上限)" % easter})
errs.append({"category": "load_bearing", "message": "彩蛋节点 %d > 3(上限 3)" % easter})
return errs
@@ -114,6 +126,7 @@ func row_by_id(node_id: String) -> Dictionary:
return {}
## row-gating 门槛(行 N 可购需全树总等级 ≥ GATE×(N−1);非脊柱节点跟随其 vein 父行——requires 主导)
func row_gate(row_num: int) -> int:
return SPINE_ROW_GATE * maxi(0, row_num - 1)
## 行门槛(一线一节点:脊柱节点第 N 级可购需全树总等级 ≥ GATE×(N−1);原 row-gating 行号主语
## 随层行退役平移到节点等级轴)
func level_gate(level: int) -> int:
return SPINE_ROW_GATE * maxi(0, level - 1)
+8 -3
View File
@@ -22,7 +22,8 @@
# 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]}](空 = 已解锁——
# unlock_requires 谓词行 [{key ∈ 计数键封闭枚举〔原 AttunementGrowthTable.COUNTER_KEYS,随迭代 21
# T5 谓词表退役就地内联为 UNLOCK_COUNTER_KEYS〕, threshold ∈ [1,999]}](空 = 已解锁——
# 首批全解锁合法态,底稿 §2.2 用户裁;正式解锁渠道归 元进度 道具工坊柜台);
# effective_rounds(前 N 轮限时件轮数——P4 甲表值化;仅 ap_rounds_front 行消费);
# charge_n(充能进度阈值 N——仅 ap_charge_safe 行消费)。
@@ -34,6 +35,10 @@ extends Resource
@export var rows: Array = [] # Array[Dictionary] 键 item_id
# 计数键封闭枚举(原 AttunementGrowthTable.COUNTER_KEYS——迭代 21 T5 谓词表退役后就地内联;
# boss_kill_count/clear_count 计数域保留〔各有非槽位硬消费,卡片 T0⑥〕)
const UNLOCK_COUNTER_KEYS := ["clear_count", "boss_kill_count"]
const DIMENSIONS := ["info", "risk", "action", "plan", "counter"]
# 同调批扩 enum(迭代 11 第①批 T1)——新增九 kind 全部条件触发/机制修正类;mine_reveal/ap_grant 语义不变。
# cell_removal = 爆破工兵包中立移除格(T7 原位换装时启用——T6 影响评估前置);mine_hit_halve 系固定
@@ -164,8 +169,8 @@ func validate() -> Array:
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 not UNLOCK_COUNTER_KEYS.has(ukey):
errs.append({"category": "enum", "message": "%s unlock_requires 计数键出域 %s: %s" % [iid, str(UNLOCK_COUNTER_KEYS), ukey]})
if ukeys.has(ukey):
errs.append({"category": "dup_key", "message": "%s unlock_requires 计数键重复: %s" % [iid, ukey]})
ukeys[ukey] = true
-34
View File
@@ -1,34 +0,0 @@
[gd_resource type="Resource" script_class="AttunementGrowthTable" load_steps=2 format=3]
[ext_resource type="Script" path="res://data/schemas/attunement_growth_table.gd" id="1"]
; 迭代 11 第①批 同调实施批 T1(2026-10-03)——同调位里程碑谓词表。
; 数值全 provisional 归 难度曲线与Boss(需求卡片 §三 7);行形态 = 底稿 §2.1 示意形态。
[resource]
script = ExtResource("1")
slot_cap = 3
milestones = [{
"id": "m1",
"grant": 1,
"requires": [{
"key": "clear_count",
"threshold": 1
}]
}, {
"id": "m2",
"grant": 1,
"requires": [{
"key": "boss_kill_count",
"threshold": 3
}]
}, {
"id": "m3",
"grant": 1,
"requires": [{
"key": "clear_count",
"threshold": 3
}, {
"key": "boss_kill_count",
"threshold": 6
}]
}]
+4 -21
View File
@@ -4,25 +4,6 @@
[resource]
script = ExtResource("1")
forge_products = [{
"attr": "max_hp",
"display_name": "生命锻造",
"flat_value": 3,
"base_price": 20,
"price_ratio_pct": 150
}, {
"attr": "atk",
"display_name": "攻击锻造",
"flat_value": 1,
"base_price": 20,
"price_ratio_pct": 150
}, {
"attr": "armor_class",
"display_name": "护甲锻造",
"flat_value": 1,
"base_price": 20,
"price_ratio_pct": 150
}]
boss_keystone = [{
"level_index": 1,
"first_kill": 3,
@@ -89,8 +70,10 @@ attune_refresh_upgrade = [{
; 元进度实现批(2026-09-21,设计权威 design/gdd/systems/冒险循环/元进度.md 定稿 v1.2):
; 数值全 provisional 归元进度设计批配读数(骨架先行——元进度实现批 2026-09-21,先例 = GP+商店房批)。
; forge flat 档与局内 GP 商店同族指数值档位非货币(血+3/攻+1/甲+1),base_price 现值 20(2026-09-22 对齿批投值)/ ratio 150 三行同;boss 两档 L1 5/1 · L2 8/2 · L3 12/3
; 〔2026-10-07 天赋树货币甲案修复批订正——原句「GP 商店同族…base 10」陈旧:GP 字样误导(锻造消费货币=钥石,meta_progression.gd:59 spend_keystone),base 20 经 2026-09-22 对齿批改值〕
; 〔forge_products 属性锻造行族已退役——迭代 21 T5 攻防所有权切分:生命唯一入口=树脊柱淬炼、
; 攻/AC 唯一入口=锻造工坊路线(契约 9 红线),meta_forge_row 访问器与 buy_forge 购买面随批删除;
; 历史投值记载(2026-09-22 对齿批 base 20/ratio 150、2026-09-23 标定批 6/9/1)系 conversion 行
; 与 ForgeGDD 考古用,行族本体不再在表。〕boss 两档 L1 5/1 · L2 8/2 · L3 12/3
; (首杀全量、重复杀固定小额恒定不递减);工坊六件阈值 1/1/2/2/3/3 按总通关次数(首版只挂通关次数渠道,
; 钥石直购价随定价批后补);conversion 全 0 = 未标定占位(标定归设计批,护甲行强条目:验收读数必须按
; 解锁后参数重测)。
+72 -319
View File
@@ -5,242 +5,63 @@
[resource]
script = ExtResource("1")
rows = [{
"node_id": "sp_hp_r1",
"display_name": "生命淬炼·1 层",
"node_id": "sp_hp",
"display_name": "生命淬炼",
"vein": "spine",
"kind": "stat",
"currency": "keystone",
"price": 10,
"cap": 3,
"row": 1,
"cap": 4,
"requires": [],
"effects": {
"max_hp": 5
}
}, {
"node_id": "sp_atk_r1",
"display_name": "攻击淬炼·1 层",
"node_id": "sp_hit",
"display_name": "命中淬炼",
"vein": "spine",
"kind": "stat",
"currency": "keystone",
"price": 10,
"cap": 3,
"row": 1,
"effects": {
"atk": 1
}
}, {
"node_id": "sp_hit_r1",
"display_name": "命中淬炼·1 层",
"vein": "spine",
"kind": "stat",
"currency": "keystone",
"price": 10,
"cap": 3,
"row": 1,
"cap": 4,
"requires": [],
"effects": {
"hit": 1
}
}, {
"node_id": "sp_ac_r1",
"display_name": "护甲淬炼·1 层",
"vein": "spine",
"kind": "stat",
"currency": "keystone",
"price": 10,
"cap": 3,
"row": 1,
"effects": {
"armor_class": 1
}
}, {
"node_id": "sp_hp_r2",
"display_name": "生命淬炼·2 层",
"vein": "spine",
"kind": "stat",
"currency": "keystone",
"price": 20,
"cap": 3,
"row": 2,
"effects": {
"max_hp": 5
}
}, {
"node_id": "sp_atk_r2",
"display_name": "攻击淬炼·2 层",
"vein": "spine",
"kind": "stat",
"currency": "keystone",
"price": 20,
"cap": 3,
"row": 2,
"effects": {
"atk": 1
}
}, {
"node_id": "sp_hit_r2",
"display_name": "命中淬炼·2 层",
"vein": "spine",
"kind": "stat",
"currency": "keystone",
"price": 20,
"cap": 3,
"row": 2,
"effects": {
"hit": 1
}
}, {
"node_id": "sp_ac_r2",
"display_name": "护甲淬炼·2 层",
"vein": "spine",
"kind": "stat",
"currency": "keystone",
"price": 20,
"cap": 3,
"row": 2,
"effects": {
"armor_class": 1
}
}, {
"node_id": "sp_hp_r3",
"display_name": "生命淬炼·3 层",
"vein": "spine",
"kind": "stat",
"currency": "keystone",
"price": 30,
"cap": 3,
"row": 3,
"effects": {
"max_hp": 5
}
}, {
"node_id": "sp_atk_r3",
"display_name": "攻击淬炼·3 层",
"vein": "spine",
"kind": "stat",
"currency": "keystone",
"price": 30,
"cap": 3,
"row": 3,
"effects": {
"atk": 1
}
}, {
"node_id": "sp_hit_r3",
"display_name": "命中淬炼·3 层",
"vein": "spine",
"kind": "stat",
"currency": "keystone",
"price": 30,
"cap": 3,
"row": 3,
"effects": {
"hit": 1
}
}, {
"node_id": "sp_ac_r3",
"display_name": "护甲淬炼·3 层",
"vein": "spine",
"kind": "stat",
"currency": "keystone",
"price": 30,
"cap": 3,
"row": 3,
"effects": {
"armor_class": 1
}
}, {
"node_id": "sp_hp_r4",
"display_name": "生命淬炼·4 层",
"vein": "spine",
"kind": "stat",
"currency": "keystone",
"price": 40,
"cap": 3,
"row": 4,
"effects": {
"max_hp": 5
}
}, {
"node_id": "sp_atk_r4",
"display_name": "攻击淬炼·4 层",
"vein": "spine",
"kind": "stat",
"currency": "keystone",
"price": 40,
"cap": 3,
"row": 4,
"effects": {
"atk": 1
}
}, {
"node_id": "sp_hit_r4",
"display_name": "命中淬炼·4 层",
"vein": "spine",
"kind": "stat",
"currency": "keystone",
"price": 40,
"cap": 3,
"row": 4,
"effects": {
"hit": 1
}
}, {
"node_id": "sp_ac_r4",
"display_name": "护甲淬炼·4 层",
"vein": "spine",
"kind": "stat",
"currency": "keystone",
"price": 40,
"cap": 3,
"row": 4,
"effects": {
"armor_class": 1
}
}, {
"node_id": "forge_atk",
"display_name": "武器锻造",
"node_id": "forge_workshop",
"display_name": "锻造工坊",
"vein": "forge",
"kind": "stat",
"currency": "keystone",
"price": 3,
"cap": 5,
"requires": ["sp_atk_r1"],
"effects": {
"atk": 1
}
"kind": "unlock",
"currency": "free",
"price": 0,
"cap": 1,
"requires": [],
"note": "工作间钥匙——无前置直接可点亮(2026-10-11 攻防所有权切分裁);点亮展开锻造工坊柜台"
}, {
"node_id": "forge_hp",
"display_name": "护身锻造",
"node_id": "easter_ley",
"display_name": "地脉共鸣",
"vein": "forge",
"kind": "stat",
"currency": "keystone",
"price": 3,
"cap": 5,
"requires": ["sp_hp_r1"],
"kind": "easter",
"currency": "free",
"price": 0,
"cap": 1,
"requires": [],
"note": "彩蛋觉醒——判定归 _check_easter_egg 特化(生命/命中两线拉满+同调位全解锁),不经购买;🔴 effects 只持生命/命中两键(契约 9 攻防所有权切分——攻/护分量已摘除)",
"effects": {
"max_hp": 1
}
}, {
"node_id": "forge_ac",
"display_name": "甲胄锻造",
"vein": "forge",
"kind": "stat",
"currency": "keystone",
"price": 3,
"cap": 5,
"requires": ["sp_ac_r1"],
"effects": {
"armor_class": 1
"max_hp": 5,
"hit": 1
}
}, {
"node_id": "ks_workshop",
"display_name": "道具工坊",
"display_name": "秘宝匣",
"vein": "keystone",
"kind": "unlock",
"currency": "free",
"price": 0,
"cap": 1,
"requires": ["sp_hp_r1"]
"requires": ["sp_hp"],
"note": "工作间钥匙——前置=脊柱生命第 1 级(v0.8 节点总表);点亮展开秘宝匣柜台(解锁+同调装配)"
}, {
"node_id": "ks_refresh",
"display_name": "刷新升级",
@@ -249,7 +70,8 @@ rows = [{
"currency": "keystone",
"price": 30,
"cap": 2,
"requires": ["ks_workshop"]
"requires": ["ks_workshop"],
"note": "树上刷新升级——购买写 attune_refresh_level 同账(迭代 21 T4 接线,双入口并存 Q1 裁乙)"
}, {
"node_id": "att_socket1",
"display_name": "同调位 1",
@@ -258,7 +80,8 @@ rows = [{
"currency": "keystone",
"price": 20,
"cap": 1,
"requires": ["sp_atk_r1"]
"requires": [],
"note": "同调脉根节点——无前置(v0.8 节点总表换代,原 sp_atk_r1 前置随攻防切分砍除)"
}, {
"node_id": "att_socket2",
"display_name": "同调位 2",
@@ -278,140 +101,70 @@ rows = [{
"cap": 1,
"requires": ["att_socket2"]
}, {
"node_id": "fork_posture_a",
"display_name": "先手压制",
"node_id": "fork_posture",
"display_name": "开局姿态",
"vein": "attune",
"kind": "fork",
"currency": "keystone",
"price": 25,
"cap": 1,
"requires": ["att_socket1"],
"effects": {
"posture_first_strike": 1
},
"fork_group": "fg_posture",
"fork_side": "a"
"name_a": "先手压制",
"name_b": "稳健拓荒",
"effects_a": {
"posture_first_strike": 1
},
"effects_b": {
"posture_bulwark": 1
}
}, {
"node_id": "fork_posture_b",
"display_name": "稳健拓荒",
"node_id": "fork_sense",
"display_name": "危险直觉",
"vein": "attune",
"kind": "fork",
"currency": "keystone",
"price": 25,
"cap": 1,
"requires": ["att_socket1"],
"effects": {
"posture_bulwark": 1
},
"fork_group": "fg_posture",
"fork_side": "b"
}, {
"node_id": "fork_sense_a",
"display_name": "雷感直觉·锋",
"vein": "attune",
"kind": "fork",
"currency": "keystone",
"price": 25,
"cap": 1,
"requires": ["att_socket1"],
"effects": {
"posture_bulwark": 1
},
"fork_group": "fg_sense",
"fork_side": "a"
"name_a": "锋",
"name_b": "守",
"effects_a": {
"sense_sharp": 1
},
"effects_b": {
"sense_guard": 1
}
}, {
"node_id": "fork_sense_b",
"display_name": "雷感直觉·守",
"node_id": "fork_economy",
"display_name": "财富之道",
"vein": "attune",
"kind": "fork",
"currency": "keystone",
"price": 25,
"cap": 1,
"requires": ["att_socket1"],
"effects": {
"posture_first_strike": 1
},
"fork_group": "fg_sense",
"fork_side": "b"
}, {
"node_id": "fork_economy_a",
"display_name": "经济人格·聚",
"vein": "attune",
"kind": "fork",
"currency": "keystone",
"price": 25,
"cap": 1,
"requires": ["att_socket1"],
"effects": {
"posture_first_strike": 1
},
"fork_group": "fg_economy",
"fork_side": "a"
}, {
"node_id": "fork_economy_b",
"display_name": "经济人格·散",
"vein": "attune",
"kind": "fork",
"currency": "keystone",
"price": 25,
"cap": 1,
"requires": ["att_socket1"],
"effects": {
"posture_bulwark": 1
"name_a": "聚",
"name_b": "散",
"effects_a": {
"economy_hoard": 1
},
"fork_group": "fg_economy",
"fork_side": "b"
}, {
"node_id": "fork_mastery_a",
"display_name": "克制专精·穿刺",
"vein": "attune",
"kind": "fork",
"currency": "keystone",
"price": 25,
"cap": 1,
"requires": ["att_socket1"],
"effects": {
"posture_bulwark": 1
},
"fork_group": "fg_mastery",
"fork_side": "a"
}, {
"node_id": "fork_mastery_b",
"display_name": "克制专精·魔法",
"vein": "attune",
"kind": "fork",
"currency": "keystone",
"price": 25,
"cap": 1,
"requires": ["att_socket1"],
"effects": {
"posture_first_strike": 1
},
"fork_group": "fg_mastery",
"fork_side": "b"
}, {
"node_id": "easter_ley",
"display_name": "地脉共鸣",
"vein": "forge",
"kind": "easter",
"currency": "free",
"price": 0,
"cap": 1,
"requires": ["forge_atk", "forge_hp", "forge_ac"],
"effects": {
"atk": 1,
"max_hp": 5,
"hit": 1,
"armor_class": 1
"effects_b": {
"economy_spend": 1
}
}]
; 双层 BD 实施批 T8(2026-10-04,R1 甲融合案——整合定稿 §1.4 一树双币三脉一脊+装配区+彩蛋):
; 脊柱 4 行×4 线(生命/攻击/命中/护甲——钥石买深度 cap3 价 10×row〔2026-10-07 用户裁甲 gp→keystone〕;
; row-gating=全树已购总等级
; ≥ 4×(row−1));锻造脉 3 节点收编 forge_levels(钥石付价=现行迁移零痛);钥石脉=工坊解锁收编
; (free 迁移点亮)+刷新升级移植;同调脉 socket 3(钥石=规则面首站)+Fork 4 对(A/B 集中同调脉
; 营地免费切换——effects 首期两 rule 键真实接线余组 stat 面 provisional);彩蛋 1(锻造满排觉醒)。
; 迭代 21 天赋树实装批 T1 换代(2026-10-12,设计权威 design/gdd/systems/跨局循环/天赋树.md v0.8):
; 12 节点 = 脊柱 2(生命淬炼/命中淬炼——一线一节点 cap4,2026-10-11 用户裁,原 4 层×4 线 16 行收编;
; 攻/护淬炼线随攻防所有权切分砍除——攻唯一入口=锻造工坊武器路线、AC 唯一入口=甲路线〔契约 9 红线〕)
; + 锻造脉 2(forge_workshop 钥匙 free 无前置 + easter_ley 彩蛋——effects 只持生命/命中两键,
; :402/:405 旧攻/护分量已摘除)+ 钥石脉 2(秘宝匣 ks_workshop〔display 旧名「道具工坊」退役〕/
; 刷新升级 ks_refresh)+ 同调脉 6(同调位 1/2/3——socket1 改根节点无前置 + Fork 3 单行双态节点,
; 原 8 行 A/B 双行结构与 fg_mastery 克制专精组退役)。
; 行门槛=一线一节点主语:脊柱节点第 N 级需全树总等级 ≥4×(N−1)(SPINE_ROW_GATE=4 公式平移,
; row 字段退役);requires=点亮前置(R1 裁撤案执行,schema 无等级阈值形态)。
; 🔴 层级纪律:局外树只放全局规则件——零 archetype 键(schema 校验强制);规则改写型特性只走局内随机渠道。
; 数值全 provisional 归 M-4 数值批;A/B 两侧差 <~3pp 验收=T10 MC。
; 2026-10-07 用户裁甲:树全钥石单币落定(脊柱 16 行 currency gp→keystone;T8「GP 余额域缺位」双币骨架披露销账——GP 局内清零不入存档永不互兑条款不动,元进度.md:55)。数值 provisional 归 M4。
; 数值全 provisional 归 M4 数值批(契约 7——价格沿旧量纲锚:脊柱 10=旧 r1 价、socket 20/40/60、
; Fork 25、刷新 30 沿旧;生命 +5/级、命中 +1/级沿旧量纲);A/B 两侧差 <~3pp 验收=V19 MC。