奇物批T1:feature_*→curio_*词族换名(CURIO_KINDS/curio_quota/curio_value/curio_candidate_count+adventure.tres键名+entities.yaml换代登记续写,值零变更实测;T0差异回写:93处/7文件+bonus_aux_carry语义澄清剔除改名面+基线772/0)

This commit is contained in:
DKChan committed 2026-10-05 18:30:44 +08:00
1 parent 413c936b79
commit d4cde73976
10 files changed
+111 -109

No files matched your search

+5 -5
View File
@@ -613,12 +613,12 @@ entities:
fields: | fields: |
退役两字段(迭代 10 奖励重构批,2026-09-30 用户拍板):level_clear_hp_bonus(「血回满+上限提升」件随大奖励三件套退役——回血改 clear_heal_pct 部分回血、上限提升改三选一件值列)、keystone_per_level(钥石退役出大奖励改 Boss/精英击杀掉落)。 退役两字段(迭代 10 奖励重构批,2026-09-30 用户拍板):level_clear_hp_bonus(「血回满+上限提升」件随大奖励三件套退役——回血改 clear_heal_pct 部分回血、上限提升改三选一件值列)、keystone_per_level(钥石退役出大奖励改 Boss/精英击杀掉落)。
clear_heal_pct: int [0,100](knob 默认 20——非末关通关部分回血回复生效上限的百分比,封顶上限不回满、满血回 0 合法;难度上压方向,带重扫义务归 难度曲线与Boss) clear_heal_pct: int [0,100](knob 默认 20——非末关通关部分回血回复生效上限的百分比,封顶上限不回满、满血回 0 合法;难度上压方向,带重扫义务归 难度曲线与Boss)
**2026-10-05 命名统一批**:本表的属性值提升奇物字段与常量整体换名(旧 → 新)——BONUS_IDS → FEATURE_KINDS、bonus_token_count() → feature_quota()、bonus_value() → feature_value()、bonus_candidate_count → feature_candidate_count、token_{kind}_{tier} → feature_quota_{kind}_{tier}、bonus_*_flat/_amount(_large) → feature_value_{kind}_{tier};值零变更(句柄改称不改数),断点附随键名沿用历史形态不动(存档兼容)。换名动因见 curio 条目(v2.0 顶层改名为「奇物」)。 **2026-10-05 命名统一批**:本表的属性值提升奇物字段与常量整体换名(旧 → 新)——BONUS_IDS → CURIO_KINDS、bonus_token_count() → curio_quota()(→ curio_quota(),本批)、bonus_value() → curio_value()、bonus_candidate_count → curio_candidate_count、token_{kind}_{tier} → curio_quota_{kind}_{tier}、bonus_*_flat/_amount(_large) → curio_value_{kind}_{tier};值零变更(句柄改称不改数),断点附随键名沿用历史形态不动(存档兼容)。换名动因见 curio 条目(v2.0 顶层改名为「奇物」)。
feature_value_attack_common / feature_value_max_hp_common / feature_value_armor_common: int [0,999](三选一属性值提升奇物兑付值——加算区攻击/生命上限/护甲轴累加,经覆盖值通道;值锚与上调约束归 难度曲线与Boss) curio_value_attack_common / curio_value_max_hp_common / curio_value_armor_common: int [0,999](三选一属性值提升奇物兑付值——加算区攻击/生命上限/护甲轴累加,经覆盖值通道;值锚与上调约束归 难度曲线与Boss)
feature_value_gp_common: int [0,999](GP 袋件定额入钱包);feature_value_ap_rarest: int [0,10](AP 件每场战斗首轮行动点基线增量);bonus_aux_item_count: int [1,9](魔法道具件授予件数) curio_value_gp_common: int [0,999](GP 袋件定额入钱包);curio_value_ap_rarest: int [0,10](AP 件每场战斗首轮行动点基线增量);bonus_aux_item_count: int [1,9](魔法道具件授予件数)
~~bonus_weight_*(E-S 加权洗牌权重族)~~ / ~~bonus_cap_*(种类次数上限族)~~:**v2.2 令牌池换代退役(迭代 12 TP0——2026-10-03 追认 b 实施批;洗牌改令牌 Fisher-Yates、池消耗即上限)** ~~bonus_weight_*(E-S 加权洗牌权重族)~~ / ~~bonus_cap_*(种类次数上限族)~~:**v2.2 令牌池换代退役(迭代 12 TP0——2026-10-03 追认 b 实施批;洗牌改令牌 Fisher-Yates、池消耗即上限)**
feature_quota_{kind}_{tier}: int [0,99](**TP0 令牌配额族**——kind×tier 计数:attack 18/5 · max_hp 17/5 · armor 17/5 · gp 18/5 · heal 0/5 · ap 0/0/3〔common/rare/rarest〕;未列组合=0;档位结构锚 70/25/3=98 张入 schema validate 硬断言〔罕见档原列「道具件 2」随 aux_item 池种退役收窄——台账自决披露项〕) curio_quota_{kind}_{tier}: int [0,99](**TP0 令牌配额族**——kind×tier 计数:attack 18/5 · max_hp 17/5 · armor 17/5 · gp 18/5 · heal 0/5 · ap 0/0/3〔common/rare/rarest〕;未列组合=0;档位结构锚 70/25/3=98 张入 schema validate 硬断言〔罕见档原列「道具件 2」随 aux_item 池种退役收窄——台账自决披露项〕)
feature_value_attack_rare / feature_value_max_hp_rare / feature_value_armor_rare / feature_value_gp_rare: int [0,999](稀有档大数值对——+2/+10/+2/50);feature_value_heal_rare: int [0,999](恢复件 +6 当即回血——新入池种类 rare 档);MC 策略 316-f(autoplay 1 级最稀有/2 级最左)随批落地 curio_value_attack_rare / curio_value_max_hp_rare / curio_value_armor_rare / curio_value_gp_rare: int [0,999](稀有档大数值对——+2/+10/+2/50);curio_value_heal_rare: int [0,999](恢复件 +6 当即回血——新入池种类 rare 档);MC 策略 316-f(autoplay 1 级最稀有/2 级最左)随批落地
transitional_sync_slots: int [0,9](同调位过渡读数源,默认 1——道具持有上限;正主 = attunement_growth.tres 里程碑谓词表,同调实施批落地后切换读数源) transitional_sync_slots: int [0,9](同调位过渡读数源,默认 1——道具持有上限;正主 = attunement_growth.tres 里程碑谓词表,同调实施批落地后切换读数源)
mvp_content: 单行全局默认值即 MVP 行(provisional 归 曲线调参批——迭代 10 扩列批注) mvp_content: 单行全局默认值即 MVP 行(provisional 归 曲线调参批——迭代 10 扩列批注)
extension_reserve: 大奖励形态扩列位已消费(迭代 10 奖励重构批落地三选一/攻击型——本键使命完成退役;冒险结构 OQ2 收口) extension_reserve: 大奖励形态扩列位已消费(迭代 10 奖励重构批落地三选一/攻击型——本键使命完成退役;冒险结构 OQ2 收口)
@@ -101,6 +101,8 @@
### T1 词族对齐第一批:`feature_*` → `curio_*`(≈0.7d) ### T1 词族对齐第一批:`feature_*` → `curio_*`(≈0.7d)
> ✅ **T0 复核差异回写(2026-10-05 实测,master 413c936)**:①`feature_*` 实测 **93 处/7 文件**(§2.1 的 103 系③秘宝批动 smoke/bonus_interact_controller 前口径);②运行层 `bonus_` 族实测远超 160 处(含信号 bonus_choice_presented/bonus_resolved/bonus_overlay 等全部形态);③**`bonus_aux_carry` 语义澄清=秘宝件跨关携带(③批已对齐 treasure 词族,内部变量现名 _bonus_treasure_carry),非奇物概念——本批改名面剔除,存档键引号字面量照③批先例禁改**;④基线=冒烟 772/0+run_all 14/15(reward_overlay 红=M7 bcard 换控件致 `c is Button` 断言失效,成因已考,T2 触碰该测试时适配);⑤res_ 仅 smoke 局部变量 res_box 4 处(语义无关,资源词族 res_* 不撞)。
- 范围:`FEATURE_KINDS` → `CURIO_KINDS`、`feature_quota()` → `curio_quota()`、`feature_value()` → `curio_value()`、`feature_candidate_count` → `curio_candidate_count`、`feature_quota_{kind}_{tier}` → `curio_quota_*`、`feature_value_{kind}_{tier}` → `curio_value_*` - 范围:`FEATURE_KINDS` → `CURIO_KINDS`、`feature_quota()` → `curio_quota()`、`feature_value()` → `curio_value()`、`feature_candidate_count` → `curio_candidate_count`、`feature_quota_{kind}_{tier}` → `curio_quota_*`、`feature_value_{kind}_{tier}` → `curio_value_*`
- 涉及:`adventure_table.gd` + `adventure.tres`(键名)+ 6 个引用文件 - 涉及:`adventure_table.gd` + `adventure.tres`(键名)+ 6 个引用文件
- **值零变更**(只改键名与常量名) - **值零变更**(只改键名与常量名)
+9 -9
View File
@@ -267,14 +267,14 @@ func _reset_bonus_state() -> void:
_node_bonus_pending = {} _node_bonus_pending = {}
# 令牌池构建(TP0 v2.2——按表 kind×tier 配额展开为持久多重集;构建序确定 = FEATURE_KINDS×TIERS 逐层, # 令牌池构建(TP0 v2.2——按表 kind×tier 配额展开为持久多重集;构建序确定 = CURIO_KINDS×TIERS 逐层,
# 随机性只发生在洗牌消费 reward_pick 封闭流的时刻。迭代 12 T1:规则改写型特性带=relic 活行每件 1 张追加 # 随机性只发生在洗牌消费 reward_pick 封闭流的时刻。迭代 12 T1:规则改写型特性带=relic 活行每件 1 张追加
# (rarity common/rare→稀有令牌档、rarest→罕见令牌档——relic 永不入普通档;空表=零追加零扰动)) # (rarity common/rare→稀有令牌档、rarest→罕见令牌档——relic 永不入普通档;空表=零追加零扰动))
func _build_bonus_deck() -> Array: func _build_bonus_deck() -> Array:
var deck: Array = [] var deck: Array = []
for kind in db.adventure.FEATURE_KINDS: for kind in db.adventure.CURIO_KINDS:
for tier in db.adventure.TIERS: for tier in db.adventure.TIERS:
for i in int(db.adventure.feature_quota(String(kind), String(tier))): for i in int(db.adventure.curio_quota(String(kind), String(tier))):
deck.append({"kind": String(kind), "tier": String(tier)}) deck.append({"kind": String(kind), "tier": String(tier)})
for row in db.relic_rows(): for row in db.relic_rows():
var rtier := "rarest" if String(row.get("rarity", "")) == "rarest" else "rare" var rtier := "rarest" if String(row.get("rarity", "")) == "rarest" else "rare"
@@ -361,7 +361,7 @@ func pick_bonus(kind: String, tier: String = "", relic_id: String = "") -> bool:
if _find_token(candidates, kind, tier, relic_id).is_empty(): if _find_token(candidates, kind, tier, relic_id).is_empty():
push_error("[冒险结构] pick_bonus 拒绝——令牌不在候选: %s|%s|%s" % [kind, tier, relic_id]) push_error("[冒险结构] pick_bonus 拒绝——令牌不在候选: %s|%s|%s" % [kind, tier, relic_id])
return false return false
var value: int = int(db.adventure.feature_value(kind, tier)) var value: int = int(db.adventure.curio_value(kind, tier))
var hp_before := int(ledger.get("hero_hp", 0)) var hp_before := int(ledger.get("hero_hp", 0))
if kind == "treasure_item": if kind == "treasure_item":
return _pick_treasure_item(value) # 道具件分派通关渠道(持有容器 = 跨关携带——与节点渠道分轨;池种退役后不可达) return _pick_treasure_item(value) # 道具件分派通关渠道(持有容器 = 跨关携带——与节点渠道分轨;池种退役后不可达)
@@ -457,10 +457,10 @@ func pool_stock(kind: String) -> int:
# 写加算区+账本+摘牌(与奖励三选一真单点——买一件=奖励面少一件的库存语义由同库成立)。 # 写加算区+账本+摘牌(与奖励三选一真单点——买一件=奖励面少一件的库存语义由同库成立)。
# 未知 kind 拒绝(货架词表封闭的前置防御)。 # 未知 kind 拒绝(货架词表封闭的前置防御)。
func _consume_pool_purchase(kind: String, tier: String) -> bool: func _consume_pool_purchase(kind: String, tier: String) -> bool:
if not db.adventure.FEATURE_KINDS.has(kind): if not db.adventure.CURIO_KINDS.has(kind):
push_error("[冒险结构] 商店购买消费拒绝——件不在池词表: %s" % kind) push_error("[冒险结构] 商店购买消费拒绝——件不在池词表: %s" % kind)
return false return false
return _apply_bonus_pick(kind, tier, int(db.adventure.feature_value(kind, tier))) return _apply_bonus_pick(kind, tier, int(db.adventure.curio_value(kind, tier)))
# 终局池快照(_settle 清账前调用——_reset_bonus_state 会重置 _bonus_taken,摘要 additive 键 # 终局池快照(_settle 清账前调用——_reset_bonus_state 会重置 _bonus_taken,摘要 additive 键
@@ -552,7 +552,7 @@ func apply_node_bonus(kind: String, tier: String = "", relic_id: String = "") ->
if _find_token(candidates, kind, tier, relic_id).is_empty(): if _find_token(candidates, kind, tier, relic_id).is_empty():
push_error("[冒险结构] apply_node_bonus 拒绝——令牌不在候选: %s|%s|%s" % [kind, tier, relic_id]) push_error("[冒险结构] apply_node_bonus 拒绝——令牌不在候选: %s|%s|%s" % [kind, tier, relic_id])
return false return false
var value: int = int(db.adventure.feature_value(kind, tier)) var value: int = int(db.adventure.curio_value(kind, tier))
if kind == "treasure_item": if kind == "treasure_item":
return _apply_node_treasure(value) # 道具件分派节点渠道(持有容器 = 关卡域库存——与通关渠道分轨;池种退役后不可达) return _apply_node_treasure(value) # 道具件分派节点渠道(持有容器 = 关卡域库存——与通关渠道分轨;池种退役后不可达)
if not _apply_bonus_pick(kind, tier, value, relic_id): if not _apply_bonus_pick(kind, tier, value, relic_id):
@@ -573,7 +573,7 @@ func _apply_node_treasure(count: int) -> bool:
var records: Array = [] var records: Array = []
if _runner != null and _runner.treasure_inventory != null: if _runner != null and _runner.treasure_inventory != null:
records = _runner.treasure_inventory.holding_records() records = _runner.treasure_inventory.holding_records()
# 迭代 11 第①批 T7——守卫随过渡键退役拆除(FEATURE_KINDS 已除名 treasure_item,本分支不可达; # 迭代 11 第①批 T7——守卫随过渡键退役拆除(CURIO_KINDS 已除名 treasure_item,本分支不可达;
# 替换子交互路由随容量闸一并退役——能力件容量闸正主 = 营地装配面满位禁选) # 替换子交互路由随容量闸一并退役——能力件容量闸正主 = 营地装配面满位禁选)
if _runner != null: if _runner != null:
for iid in picked: for iid in picked:
@@ -739,7 +739,7 @@ func _draw_bonus_candidates() -> Array:
if ident.begins_with("relic:"): if ident.begins_with("relic:"):
cand["relic_id"] = String((tok as Dictionary).get("relic_id", "")) cand["relic_id"] = String((tok as Dictionary).get("relic_id", ""))
out.append(cand) out.append(cand)
if out.size() >= int(db.adventure.feature_candidate_count): if out.size() >= int(db.adventure.curio_candidate_count):
break break
return out return out
+1 -1
View File
@@ -680,7 +680,7 @@ func set_treasure_unlocked(ids: Dictionary) -> void:
func _grant_routed(snap: Dictionary, polarity: String, source_class: String, cell: Vector2i, ri: int, highlight: String = "", label_suffix: String = "") -> bool: func _grant_routed(snap: Dictionary, polarity: String, source_class: String, cell: Vector2i, ri: int, highlight: String = "", label_suffix: String = "") -> bool:
# 迭代 11 第①批 T7——aux 授予分支随掉落/停点渠道退役拆除(reward 词表第十型 treasure_item 保留但零投放: # 迭代 11 第①批 T7——aux 授予分支随掉落/停点渠道退役拆除(reward 词表第十型 treasure_item 保留但零投放:
# 无 aux 行入池、FEATURE_KINDS 词表已除名);剩余类型走极性字典路由。 # 无 aux 行入池、CURIO_KINDS 词表已除名);剩余类型走极性字典路由。
var row: Dictionary = _reward_economy.polarity_row(String(snap.get("cell_type", "")), polarity) var row: Dictionary = _reward_economy.polarity_row(String(snap.get("cell_type", "")), polarity)
if row.is_empty(): if row.is_empty():
_fatal("极性字典缺行: %s %s" % [String(snap.get("cell_type", "")), polarity]) _fatal("极性字典缺行: %s %s" % [String(snap.get("cell_type", "")), polarity])
+4 -4
View File
@@ -99,18 +99,18 @@ func cross_validate(loaded: Dictionary, push_diag: Callable) -> void:
for e in validate_skill_pool_links(skt, en, loaded.get("status_effects")): for e in validate_skill_pool_links(skt, en, loaded.get("status_effects")):
push_diag.call("error", e.category, e.message) push_diag.call("error", e.category, e.message)
# ⑥ 商店货架 × 冒险共享池跨表校验(M-4 迭代 11 第②批 T1——次数上限域): # ⑥ 商店货架 × 冒险共享池跨表校验(M-4 迭代 11 第②批 T1——次数上限域):
# pool_shelf kind 须命中 adventure.FEATURE_KINDS 且为有限库存种(cap ∈ [1,99]——gp 无上限种/牌库外种 # pool_shelf kind 须命中 adventure.CURIO_KINDS 且为有限库存种(cap ∈ [1,99]——gp 无上限种/牌库外种
# 入货架 = 库存账本无锚,加载致命;gp 袋负交易切边/规则改写型特性三轴切边由 ShopTable 词表封闭另拦) # 入货架 = 库存账本无锚,加载致命;gp 袋负交易切边/规则改写型特性三轴切边由 ShopTable 词表封闭另拦)
var adv = loaded.get("adventure") var adv = loaded.get("adventure")
var shp = loaded.get("shop") var shp = loaded.get("shop")
if adv != null and shp != null: if adv != null and shp != null:
for row in shp.pool_shelf: for row in shp.pool_shelf:
var kind: String = String(row.get("kind_id", "")) var kind: String = String(row.get("kind_id", ""))
if not adv.FEATURE_KINDS.has(kind): if not adv.CURIO_KINDS.has(kind):
push_diag.call("error", "shelf_dangling_kind", "ShopTable.pool_shelf kind %s 不在 AdventureTable.FEATURE_KINDS(共享池牌库外种)" % kind) push_diag.call("error", "shelf_dangling_kind", "ShopTable.pool_shelf kind %s 不在 AdventureTable.CURIO_KINDS(共享池牌库外种)" % kind)
continue continue
# TP0 令牌池——货架件须为有限令牌种(配额 >0;种类次数上限退役后库存=剩余令牌数) # TP0 令牌池——货架件须为有限令牌种(配额 >0;种类次数上限退役后库存=剩余令牌数)
var quota := int(adv.feature_quota(kind, adv.TIER_COMMON)) + int(adv.feature_quota(kind, adv.TIER_RARE)) + int(adv.feature_quota(kind, adv.TIER_RAREST)) var quota := int(adv.curio_quota(kind, adv.TIER_COMMON)) + int(adv.curio_quota(kind, adv.TIER_RARE)) + int(adv.curio_quota(kind, adv.TIER_RAREST))
if quota < 1: if quota < 1:
push_diag.call("error", "shelf_zero_quota", "ShopTable.pool_shelf kind %s 令牌配额为 0(牌库空种入货架 = 库存无锚)" % kind) push_diag.call("error", "shelf_zero_quota", "ShopTable.pool_shelf kind %s 令牌配额为 0(牌库空种入货架 = 库存无锚)" % kind)
+1 -1
View File
@@ -834,7 +834,7 @@ func shop_shelf() -> Array:
var tier := String((tok as Dictionary).get("tier", "")) var tier := String((tok as Dictionary).get("tier", ""))
var row: Dictionary = db.shop_pool_row(kind) var row: Dictionary = db.shop_pool_row(kind)
var display := String(row.get("display_name", kind)) var display := String(row.get("display_name", kind))
var value := int(db.adventure.feature_value(kind, tier)) var value := int(db.adventure.curio_value(kind, tier))
if row.is_empty() and kind == "relic": if row.is_empty() and kind == "relic":
var rrow: Dictionary = db.relic_row(String(tok.get("relic_id", ""))) var rrow: Dictionary = db.relic_row(String(tok.get("relic_id", "")))
display = String(rrow.get("display_name", "特性")) display = String(rrow.get("display_name", "特性"))
+53 -53
View File
@@ -18,13 +18,13 @@ extends Resource
## 它们是它的分类轴上的枚举(专长 / 属性值提升 / 资源),不是三套系统; ## 它们是它的分类轴上的枚举(专长 / 属性值提升 / 资源),不是三套系统;
## 本表承载属性值提升与资源的种类配额与件值。 ## 本表承载属性值提升与资源的种类配额与件值。
## (2026-10-05 命名统一 + v2.0 改名:旧称「数值件」→ 属性提升;顶层「特性」→「奖励」→「奇物」。 ## (2026-10-05 命名统一 + v2.0 改名:旧称「数值件」→ 属性提升;顶层「特性」→「奖励」→「奇物」。
## 常量与字段随之前缀 feature_*,**尚未**随顶层改名换到 reward_*——见 06-待办清单。 ## 常量与字段随之前缀 curio_*,**尚未**随顶层改名换到 reward_*——见 06-待办清单。
## 分类轴三个枚举:属性值提升〔生命上限 / AC / 攻击〕;资源〔行动点上限 / ## 分类轴三个枚举:属性值提升〔生命上限 / AC / 攻击〕;资源〔行动点上限 /
## 刷新次数 / 金币 / 回复,命名待定〕——本表两类混编;刷新次数与专长获取两项尚未入表。 ## 刷新次数 / 金币 / 回复,命名待定〕——本表两类混编;刷新次数与专长获取两项尚未入表。
## 类内异质性:金币与回复是即时兑付,不进持有集、不持续生效。) ## 类内异质性:金币与回复是即时兑付,不进持有集、不持续生效。)
## 三选一提升池 kind 词表(封闭——断点附随件与牌库剔除态依赖它;扩件走本表+registry 修订)。 ## 三选一提升池 kind 词表(封闭——断点附随件与牌库剔除态依赖它;扩件走本表+registry 修订)。
## treasure_item 池种退役(迭代 11 第①批 T7——同调批出池);heal 恢复件新入池(v2.2 令牌池追认 b)。 ## treasure_item 池种退役(迭代 11 第①批 T7——同调批出池);heal 恢复件新入池(v2.2 令牌池追认 b)。
const FEATURE_KINDS: Array[String] = ["attack", "max_hp", "armor", "gp", "ap", "heal"] const CURIO_KINDS: Array[String] = ["attack", "max_hp", "armor", "gp", "ap", "heal"]
## 令牌档位词表(v2.2 三档位——稀有度命名对齐 MC 策略判读序 316-f:1 级最稀有=rarest 优先) ## 令牌档位词表(v2.2 三档位——稀有度命名对齐 MC 策略判读序 316-f:1 级最稀有=rarest 优先)
const TIERS: Array[String] = ["common", "rare", "rarest"] const TIERS: Array[String] = ["common", "rare", "rarest"]
const TIER_COMMON := "common" const TIER_COMMON := "common"
@@ -39,65 +39,65 @@ const TIER_QUOTAS := {"common": 70, "rare": 25, "rarest": 3}
@export var clear_heal_pct: int = 20 # [0,100] knob——回复生效上限的百分比,封顶上限不回满 @export var clear_heal_pct: int = 20 # [0,100] knob——回复生效上限的百分比,封顶上限不回满
# —— 三选一候选数(2026-10-02 修复批 T2 魔法数结构化)—— # —— 三选一候选数(2026-10-02 修复批 T2 魔法数结构化)——
@export var feature_candidate_count: int = 3 # [1,6] knob——令牌洗牌不放回抽取张数上界 @export var curio_candidate_count: int = 3 # [1,6] knob——令牌洗牌不放回抽取张数上界
@export var shop_refresh_free: int = 0 # [0,9] knob(MC-1 provisional——进店免费刷新次数;基础 0=Q2 裁,天赋增益位预留) @export var shop_refresh_free: int = 0 # [0,9] knob(MC-1 provisional——进店免费刷新次数;基础 0=Q2 裁,天赋增益位预留)
@export var shop_refresh_cost_base: int = 20 # [1,999] knob(MC-1 provisional——GP 刷新基价) @export var shop_refresh_cost_base: int = 20 # [1,999] knob(MC-1 provisional——GP 刷新基价)
@export var shop_refresh_cost_curve: float = 1.8 # [1.0,4.0] knob(MC-1 provisional——按已付费刷新数幂曲线,A 链同族) @export var shop_refresh_cost_curve: float = 1.8 # [1.0,4.0] knob(MC-1 provisional——按已付费刷新数幂曲线,A 链同族)
# —— 令牌配额(kind × tier 计数;未列组合 = 0——heal 只入稀有/ap 只入罕见,v2.2 档位构成)—— # —— 令牌配额(kind × tier 计数;未列组合 = 0——heal 只入稀有/ap 只入罕见,v2.2 档位构成)——
@export var feature_quota_attack_common: int = 18 # [0,99] @export var curio_quota_attack_common: int = 18 # [0,99]
@export var feature_quota_attack_rare: int = 5 # [0,99] @export var curio_quota_attack_rare: int = 5 # [0,99]
@export var feature_quota_max_hp_common: int = 17 # [0,99] @export var curio_quota_max_hp_common: int = 17 # [0,99]
@export var feature_quota_max_hp_rare: int = 5 # [0,99] @export var curio_quota_max_hp_rare: int = 5 # [0,99]
@export var feature_quota_armor_common: int = 17 # [0,99] @export var curio_quota_armor_common: int = 17 # [0,99]
@export var feature_quota_armor_rare: int = 5 # [0,99] @export var curio_quota_armor_rare: int = 5 # [0,99]
@export var feature_quota_gp_common: int = 18 # [0,99] @export var curio_quota_gp_common: int = 18 # [0,99]
@export var feature_quota_gp_rare: int = 5 # [0,99] @export var curio_quota_gp_rare: int = 5 # [0,99]
@export var feature_quota_heal_rare: int = 5 # [0,99]——恢复件(新入池种类,当即回血) @export var curio_quota_heal_rare: int = 5 # [0,99]——恢复件(新入池种类,当即回血)
@export var feature_quota_ap_rarest: int = 3 # [0,99]——AP 件(罕见档;道具件 2 席位随池种退役收窄) @export var curio_quota_ap_rarest: int = 3 # [0,99]——AP 件(罕见档;道具件 2 席位随池种退役收窄)
# —— 令牌件值(小/大数值对——v2.2 追认 b 定稿:20/50、+1/+2、+1/+2、+5/+10、恢复+6、AP+1)—— # —— 令牌件值(小/大数值对——v2.2 追认 b 定稿:20/50、+1/+2、+1/+2、+5/+10、恢复+6、AP+1)——
@export var feature_value_attack_common: int = 1 # [0,999] knob——小增伤 @export var curio_value_attack_common: int = 1 # [0,999] knob——小增伤
@export var feature_value_attack_rare: int = 2 # [0,999] knob——大增伤 @export var curio_value_attack_rare: int = 2 # [0,999] knob——大增伤
@export var feature_value_max_hp_common: int = 5 # [0,999] knob——小生命 @export var curio_value_max_hp_common: int = 5 # [0,999] knob——小生命
@export var feature_value_max_hp_rare: int = 10 # [0,999] knob——大生命 @export var curio_value_max_hp_rare: int = 10 # [0,999] knob——大生命
@export var feature_value_armor_common: int = 1 # [0,999] knob——小护甲 @export var curio_value_armor_common: int = 1 # [0,999] knob——小护甲
@export var feature_value_armor_rare: int = 2 # [0,999] knob——大护甲 @export var curio_value_armor_rare: int = 2 # [0,999] knob——大护甲
@export var feature_value_gp_common: int = 20 # [0,999] knob——小GP @export var curio_value_gp_common: int = 20 # [0,999] knob——小GP
@export var feature_value_gp_rare: int = 50 # [0,999] knob——大GP @export var curio_value_gp_rare: int = 50 # [0,999] knob——大GP
@export var feature_value_heal_rare: int = 6 # [0,999] knob——恢复(当即回血,封顶生效上限) @export var curio_value_heal_rare: int = 6 # [0,999] knob——恢复(当即回血,封顶生效上限)
@export var feature_value_ap_rarest: int = 1 # [0,10] knob——AP 件每场战斗首轮行动点基线增量 @export var curio_value_ap_rarest: int = 1 # [0,10] knob——AP 件每场战斗首轮行动点基线增量
## 令牌配额读数(未列 kind×tier 组合 = 0——heal 无普通档/ap 无普通稀有档按 v2.2 构成) ## 令牌配额读数(未列 kind×tier 组合 = 0——heal 无普通档/ap 无普通稀有档按 v2.2 构成)
func feature_quota(kind: String, tier: String) -> int: func curio_quota(kind: String, tier: String) -> int:
match [kind, tier]: match [kind, tier]:
["attack", TIER_COMMON]: return feature_quota_attack_common ["attack", TIER_COMMON]: return curio_quota_attack_common
["attack", TIER_RARE]: return feature_quota_attack_rare ["attack", TIER_RARE]: return curio_quota_attack_rare
["max_hp", TIER_COMMON]: return feature_quota_max_hp_common ["max_hp", TIER_COMMON]: return curio_quota_max_hp_common
["max_hp", TIER_RARE]: return feature_quota_max_hp_rare ["max_hp", TIER_RARE]: return curio_quota_max_hp_rare
["armor", TIER_COMMON]: return feature_quota_armor_common ["armor", TIER_COMMON]: return curio_quota_armor_common
["armor", TIER_RARE]: return feature_quota_armor_rare ["armor", TIER_RARE]: return curio_quota_armor_rare
["gp", TIER_COMMON]: return feature_quota_gp_common ["gp", TIER_COMMON]: return curio_quota_gp_common
["gp", TIER_RARE]: return feature_quota_gp_rare ["gp", TIER_RARE]: return curio_quota_gp_rare
["heal", TIER_RARE]: return feature_quota_heal_rare ["heal", TIER_RARE]: return curio_quota_heal_rare
["ap", TIER_RAREST]: return feature_quota_ap_rarest ["ap", TIER_RAREST]: return curio_quota_ap_rarest
return 0 return 0
## 令牌件值(kind × tier;v2.2 数值对——小/大两档) ## 令牌件值(kind × tier;v2.2 数值对——小/大两档)
func feature_value(kind: String, tier: String = TIER_COMMON) -> int: func curio_value(kind: String, tier: String = TIER_COMMON) -> int:
match [kind, tier]: match [kind, tier]:
["attack", TIER_COMMON]: return feature_value_attack_common ["attack", TIER_COMMON]: return curio_value_attack_common
["attack", TIER_RARE]: return feature_value_attack_rare ["attack", TIER_RARE]: return curio_value_attack_rare
["max_hp", TIER_COMMON]: return feature_value_max_hp_common ["max_hp", TIER_COMMON]: return curio_value_max_hp_common
["max_hp", TIER_RARE]: return feature_value_max_hp_rare ["max_hp", TIER_RARE]: return curio_value_max_hp_rare
["armor", TIER_COMMON]: return feature_value_armor_common ["armor", TIER_COMMON]: return curio_value_armor_common
["armor", TIER_RARE]: return feature_value_armor_rare ["armor", TIER_RARE]: return curio_value_armor_rare
["gp", TIER_COMMON]: return feature_value_gp_common ["gp", TIER_COMMON]: return curio_value_gp_common
["gp", TIER_RARE]: return feature_value_gp_rare ["gp", TIER_RARE]: return curio_value_gp_rare
["heal", TIER_RARE]: return feature_value_heal_rare ["heal", TIER_RARE]: return curio_value_heal_rare
["ap", TIER_RAREST]: return feature_value_ap_rarest ["ap", TIER_RAREST]: return curio_value_ap_rarest
return 0 return 0
@@ -113,8 +113,8 @@ func tier_rank(tier: String) -> int:
## 档位令牌总数(货架/校验读面) ## 档位令牌总数(货架/校验读面)
func tier_total(tier: String) -> int: func tier_total(tier: String) -> int:
var total := 0 var total := 0
for kind in FEATURE_KINDS: for kind in CURIO_KINDS:
total += feature_quota(kind, tier) total += curio_quota(kind, tier)
return total return total
@@ -127,18 +127,18 @@ func validate() -> Array:
var errs: Array[Dictionary] = [] var errs: Array[Dictionary] = []
if clear_heal_pct < 0 or clear_heal_pct > 100: if clear_heal_pct < 0 or clear_heal_pct > 100:
errs.append({"category": "domain", "message": "clear_heal_pct 越域 [0,100]: %d" % clear_heal_pct}) errs.append({"category": "domain", "message": "clear_heal_pct 越域 [0,100]: %d" % clear_heal_pct})
for kind in FEATURE_KINDS: for kind in CURIO_KINDS:
for tier in TIERS: for tier in TIERS:
var c := feature_quota(kind, tier) var c := curio_quota(kind, tier)
if c < 0 or c > 99: if c < 0 or c > 99:
errs.append({"category": "domain", "message": "token_count(%s,%s) 越域 [0,99]: %d" % [kind, tier, c]}) errs.append({"category": "domain", "message": "token_count(%s,%s) 越域 [0,99]: %d" % [kind, tier, c]})
var v := feature_value(kind, tier) var v := curio_value(kind, tier)
if v < 0 or v > 999: if v < 0 or v > 999:
errs.append({"category": "domain", "message": "feature_value(%s,%s) 越域 [0,999]: %d" % [kind, tier, v]}) errs.append({"category": "domain", "message": "curio_value(%s,%s) 越域 [0,999]: %d" % [kind, tier, v]})
for tier in TIERS: for tier in TIERS:
var total := tier_total(tier) var total := tier_total(tier)
if total != int(TIER_QUOTAS.get(tier, -1)): if total != int(TIER_QUOTAS.get(tier, -1)):
errs.append({"category": "domain", "message": "档位配额结构失锚(v2.2 追认 b——%s 应=%d 实=%d;改总量须重裁)" % [tier, int(TIER_QUOTAS.get(tier, -1)), total]}) errs.append({"category": "domain", "message": "档位配额结构失锚(v2.2 追认 b——%s 应=%d 实=%d;改总量须重裁)" % [tier, int(TIER_QUOTAS.get(tier, -1)), total]})
if feature_candidate_count < 1 or feature_candidate_count > 6: if curio_candidate_count < 1 or curio_candidate_count > 6:
errs.append({"category": "domain", "message": "feature_candidate_count 越域 [1,6]: %d" % feature_candidate_count}) errs.append({"category": "domain", "message": "curio_candidate_count 越域 [1,6]: %d" % curio_candidate_count})
return errs return errs
+21 -21
View File
@@ -5,27 +5,27 @@
[resource] [resource]
script = ExtResource("1") script = ExtResource("1")
clear_heal_pct = 20 clear_heal_pct = 20
feature_candidate_count = 3 curio_candidate_count = 3
feature_quota_attack_common = 18 curio_quota_attack_common = 18
feature_quota_attack_rare = 5 curio_quota_attack_rare = 5
feature_quota_max_hp_common = 17 curio_quota_max_hp_common = 17
feature_quota_max_hp_rare = 5 curio_quota_max_hp_rare = 5
feature_quota_armor_common = 17 curio_quota_armor_common = 17
feature_quota_armor_rare = 5 curio_quota_armor_rare = 5
feature_quota_gp_common = 18 curio_quota_gp_common = 18
feature_quota_gp_rare = 5 curio_quota_gp_rare = 5
feature_quota_heal_rare = 5 curio_quota_heal_rare = 5
feature_quota_ap_rarest = 3 curio_quota_ap_rarest = 3
feature_value_attack_common = 1 curio_value_attack_common = 1
feature_value_attack_rare = 2 curio_value_attack_rare = 2
feature_value_max_hp_common = 5 curio_value_max_hp_common = 5
feature_value_max_hp_rare = 10 curio_value_max_hp_rare = 10
feature_value_armor_common = 1 curio_value_armor_common = 1
feature_value_armor_rare = 2 curio_value_armor_rare = 2
feature_value_gp_common = 20 curio_value_gp_common = 20
feature_value_gp_rare = 50 curio_value_gp_rare = 50
feature_value_heal_rare = 6 curio_value_heal_rare = 6
feature_value_ap_rarest = 1 curio_value_ap_rarest = 1
; TP0 令牌池换代(2026-10-03,node-reward v2.2 追认 b 落地):三选一池 = 固定配额令牌池—— ; TP0 令牌池换代(2026-10-03,node-reward v2.2 追认 b 落地):三选一池 = 固定配额令牌池——
; 普通 70/稀有 25/罕见 3 = 98 张(罕见档原列「道具件 2」随 treasure_item 池种退役收窄——台账自决披露项); ; 普通 70/稀有 25/罕见 3 = 98 张(罕见档原列「道具件 2」随 treasure_item 池种退役收窄——台账自决披露项);
@@ -127,9 +127,9 @@ func _bonus_card_texts(candidates: Array, treasure_detail: Array = []) -> Array:
elif kind == "treasure_item": elif kind == "treasure_item":
var iid := String(treasure_detail[treasure_i]) if treasure_i < treasure_detail.size() else "" var iid := String(treasure_detail[treasure_i]) if treasure_i < treasure_detail.size() else ""
treasure_i += 1 treasure_i += 1
text = Narrator.bonus_card_text(kind, tier, int(db.adventure.feature_value(kind, tier)), _treasure_card_line(iid)) text = Narrator.bonus_card_text(kind, tier, int(db.adventure.curio_value(kind, tier)), _treasure_card_line(iid))
else: else:
text = Narrator.bonus_card_text(kind, tier, int(db.adventure.feature_value(kind, tier))) text = Narrator.bonus_card_text(kind, tier, int(db.adventure.curio_value(kind, tier)))
out.append({"id": cid, "text": text}) out.append({"id": cid, "text": text})
return out return out
+13 -13
View File
@@ -220,12 +220,12 @@ func _smoke_adventure_table() -> void:
check(int(_db.adventure.tier_total(tier)) == int(_db.adventure.TIER_QUOTAS[tier]), check(int(_db.adventure.tier_total(tier)) == int(_db.adventure.TIER_QUOTAS[tier]),
"档位配额结构锚(v2.2 追认 b):%s = %d 张" % [tier, int(_db.adventure.TIER_QUOTAS[tier])]) "档位配额结构锚(v2.2 追认 b):%s = %d 张" % [tier, int(_db.adventure.TIER_QUOTAS[tier])])
check(int(_db.adventure.deck_total()) == 98, "令牌池总量 = 98 张(普通 70/稀有 25/罕见 3——罕见档随道具件池种退役收窄)") check(int(_db.adventure.deck_total()) == 98, "令牌池总量 = 98 张(普通 70/稀有 25/罕见 3——罕见档随道具件池种退役收窄)")
check(int(_db.adventure.feature_value("gp", "common")) == 20 and int(_db.adventure.feature_value("gp", "rare")) == 50 check(int(_db.adventure.curio_value("gp", "common")) == 20 and int(_db.adventure.curio_value("gp", "rare")) == 50
and int(_db.adventure.feature_value("attack", "common")) == 1 and int(_db.adventure.feature_value("attack", "rare")) == 2 and int(_db.adventure.curio_value("attack", "common")) == 1 and int(_db.adventure.curio_value("attack", "rare")) == 2
and int(_db.adventure.feature_value("max_hp", "common")) == 5 and int(_db.adventure.feature_value("max_hp", "rare")) == 10 and int(_db.adventure.curio_value("max_hp", "common")) == 5 and int(_db.adventure.curio_value("max_hp", "rare")) == 10
and int(_db.adventure.feature_value("armor", "common")) == 1 and int(_db.adventure.feature_value("armor", "rare")) == 2, and int(_db.adventure.curio_value("armor", "common")) == 1 and int(_db.adventure.curio_value("armor", "rare")) == 2,
"数值对(v2.2):小GP 20/大GP 50 · 小增伤 +1/大 +2 · 小生命 +5/大 +10 · 小护甲 +1/大 +2") "数值对(v2.2):小GP 20/大GP 50 · 小增伤 +1/大 +2 · 小生命 +5/大 +10 · 小护甲 +1/大 +2")
check(int(_db.adventure.feature_value("heal", "rare")) == 6 and int(_db.adventure.feature_value("ap", "rarest")) == 1, check(int(_db.adventure.curio_value("heal", "rare")) == 6 and int(_db.adventure.curio_value("ap", "rarest")) == 1,
"恢复件 +6 当即回血(新入池种类,rare 档)· AP +1(rarest 档)") "恢复件 +6 当即回血(新入池种类,rare 档)· AP +1(rarest 档)")
check(_db.attunement_growth != null and int(_db.attunement_slot_cap()) == 3 check(_db.attunement_growth != null and int(_db.attunement_slot_cap()) == 3
and Attunement.slots(_db, null) == 0, and Attunement.slots(_db, null) == 0,
@@ -349,7 +349,7 @@ func _smoke_adventure() -> void:
var cand_ok := picks.size() == 2 var cand_ok := picks.size() == 2
var ks_gain_ok := picks.size() == 2 var ks_gain_ok := picks.size() == 2
var resolve_ok := level_resolves.size() == 2 var resolve_ok := level_resolves.size() == 2
var max_flat := int(_db.adventure.feature_value_max_hp_common) var max_flat := int(_db.adventure.curio_value_max_hp_common)
var heal_pct := int(_db.adventure.clear_heal_pct) var heal_pct := int(_db.adventure.clear_heal_pct)
var hp_acc := 0 # max_hp 件累加(回血封顶断言的生效上限推导——TP0 起按兑付令牌实值,大小档分别计) var hp_acc := 0 # max_hp 件累加(回血封顶断言的生效上限推导——TP0 起按兑付令牌实值,大小档分别计)
var ks_prefix := 0 var ks_prefix := 0
@@ -386,7 +386,7 @@ func _smoke_adventure() -> void:
var r: Dictionary = level_resolves[i] var r: Dictionary = level_resolves[i]
var rk := String(r.get("picked", "")) var rk := String(r.get("picked", ""))
var rt := String(r.get("tier", "")) var rt := String(r.get("tier", ""))
if _smoke_find_token(p.get("candidates", []) as Array, rk, rt).is_empty() or int(r.get("value", -1)) != int(_db.adventure.feature_value(rk, rt)): if _smoke_find_token(p.get("candidates", []) as Array, rk, rt).is_empty() or int(r.get("value", -1)) != int(_db.adventure.curio_value(rk, rt)):
resolve_ok = false resolve_ok = false
if rk == "max_hp": if rk == "max_hp":
hp_acc += int(r.get("value", max_flat)) hp_acc += int(r.get("value", max_flat))
@@ -399,7 +399,7 @@ func _smoke_adventure() -> void:
var exp_base := maxi(1, int(round(float(int(_db.hero.max_hp)) * k))) var exp_base := maxi(1, int(round(float(int(_db.hero.max_hp)) * k)))
# 模拟档 v3.4 商店贪心购血(数值修复批)合法抬升——上限窗口 = 基础 + 加算区 3 件 + 购血余量; # 模拟档 v3.4 商店贪心购血(数值修复批)合法抬升——上限窗口 = 基础 + 加算区 3 件 + 购血余量;
# TP0 数值对后 max_hp 件有大生命 +10(rare 档)——上界按大件最坏构成 3×10+购血 15=+45(TP0 放宽) # TP0 数值对后 max_hp 件有大生命 +10(rare 档)——上界按大件最坏构成 3×10+购血 15=+45(TP0 放宽)
var hp_ceiling := maxi(1, int(round(float(int(_db.hero.max_hp) + 3 * int(_db.adventure.feature_value("max_hp", "rare")) + 15) * k))) var hp_ceiling := maxi(1, int(round(float(int(_db.hero.max_hp) + 3 * int(_db.adventure.curio_value("max_hp", "rare")) + 15) * k)))
var seq_ok := seq.size() > 0 and int(seq[0]) == exp_base var seq_ok := seq.size() > 0 and int(seq[0]) == exp_base
for v in (seq as Array): for v in (seq as Array):
if int(v) < exp_base or int(v) > hp_ceiling: if int(v) < exp_base or int(v) > hp_ceiling:
@@ -594,8 +594,8 @@ func _smoke_adventure() -> void:
a13.present_node_bonus(ncands13, ndraw.get("aux_detail", []) as Array) a13.present_node_bonus(ncands13, ndraw.get("aux_detail", []) as Array)
a13.present_node_bonus([{"kind": "gp", "tier": "common"}]) a13.present_node_bonus([{"kind": "gp", "tier": "common"}])
var gp13 := int(a13.ledger.get("gp", 0)) var gp13 := int(a13.ledger.get("gp", 0))
check(a13.apply_node_bonus("gp", "common") and int(a13.ledger.get("gp", 0)) == gp13 + int(_db.adventure.feature_value("gp", "common")), check(a13.apply_node_bonus("gp", "common") and int(a13.ledger.get("gp", 0)) == gp13 + int(_db.adventure.curio_value("gp", "common")),
"奖励节点 GP 件入账(钱包「奖励入账」收口——+%d)" % int(_db.adventure.feature_value("gp", "common"))) "奖励节点 GP 件入账(钱包「奖励入账」收口——+%d)" % int(_db.adventure.curio_value("gp", "common")))
check(a13._node_bonus_pending.is_empty(), "节点停点态兑付后清零(恰一次语义同构)") check(a13._node_bonus_pending.is_empty(), "节点停点态兑付后清零(恰一次语义同构)")
var s10: Dictionary = a10.summary var s10: Dictionary = a10.summary
check(String(s10.get("result", "")) == "abandoned" and not bool(s10.get("cleared", true)), check(String(s10.get("result", "")) == "abandoned" and not bool(s10.get("cleared", true)),
@@ -3875,8 +3875,8 @@ func _smoke_shop_pool() -> void:
var val0: int = int(a8.bonus.get(String(val_map[kind]), -999)) var val0: int = int(a8.bonus.get(String(val_map[kind]), -999))
check(r8.buy_pool_item(kind), "购买受理(pool 口——%s|%s)" % [kind, tier]) check(r8.buy_pool_item(kind), "购买受理(pool 口——%s|%s)" % [kind, tier])
check(int(r8.gp) == gp0 - int(buy_c.get("price", 0)), "钱包扣现价(%d → %d)" % [gp0, int(r8.gp)]) check(int(r8.gp) == gp0 - int(buy_c.get("price", 0)), "钱包扣现价(%d → %d)" % [gp0, int(r8.gp)])
check(int(a8.bonus.get(String(val_map[kind]), -999)) == val0 + int(_db.adventure.feature_value(kind, tier)), check(int(a8.bonus.get(String(val_map[kind]), -999)) == val0 + int(_db.adventure.curio_value(kind, tier)),
"加算区 +令牌档位件值(池件=奖励件本体——%s|%s %+d)" % [kind, tier, int(_db.adventure.feature_value(kind, tier))]) "加算区 +令牌档位件值(池件=奖励件本体——%s|%s %+d)" % [kind, tier, int(_db.adventure.curio_value(kind, tier))])
check(int(a8._bonus_taken.get(kind, 0)) == taken0 + 1, "共享池账本 -1(跨渠道单点——taken %d → %d)" % [taken0, int(a8._bonus_taken.get(kind, 0))]) check(int(a8._bonus_taken.get(kind, 0)) == taken0 + 1, "共享池账本 -1(跨渠道单点——taken %d → %d)" % [taken0, int(a8._bonus_taken.get(kind, 0))])
# A 链:同visit 第二购现价 = ceil(base×b^(l2−1)×A^1)(货架已全重抽——新卡新价同公式) # A 链:同visit 第二购现价 = ceil(base×b^(l2−1)×A^1)(货架已全重抽——新卡新价同公式)
var second: Dictionary = {} var second: Dictionary = {}
@@ -3941,7 +3941,7 @@ func _smoke_shop_pool() -> void:
for k in ps.keys(): for k in ps.keys():
var quota := 0 var quota := 0
for tier in _db.adventure.TIERS: for tier in _db.adventure.TIERS:
quota += int(_db.adventure.feature_quota(String(k), tier)) quota += int(_db.adventure.curio_quota(String(k), tier))
if int(ps[k]) < 0 or int(ps[k]) > quota: if int(ps[k]) < 0 or int(ps[k]) > quota:
ps_ok = false ps_ok = false
check(ps_ok, "终局池余量在域 [0,配额](%s)" % str(ps)) check(ps_ok, "终局池余量在域 [0,配额](%s)" % str(ps))