# 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)