T2(迭代22秘宝批): 核心层解锁服务换代+槽位档位约束——工坊渠道面退役(_workshop_progress/workshop_status/refresh_workshop_unlocks/meta_workshop_items/WORKSHOP_ITEM_IDS/workshop_items行族全删)换解锁混合制(buy_treasure_unlock钥石购买=余额闸+重复购拒+条件件误购拒/refresh_treasure_unlocks条件刷新=AND谓词+幂等/treasure_shop_status柜台投影); 计数读数单点_treasure_unlock_progress(adventure_cleared/boss_kill_count/vision_used); save_manager新增vision_used生涯计数域(add_vision_used收束入账+缺键补零不升版本); toggle_attunement三重校验(未解锁拒+满位禁选+件档rank≤装入位序档位格——卸下恒可效果面只读装配集); pause/camp两视图秘宝匣呈现换代+UiKit计数键与档位标签单点; smoke工坊段换V3/V4/V5断言(独立档[11]防路径污染)+meta表段行族规模换代3/3/2。冒烟895/0+单元21套+探针全绿〔秘宝批 V3/V4/V5/V14/V19数据源〕
This commit is contained in:
1 parent
48b0696158
commit
a7bd4e4132
13 files changed
+235
-194
No files matched your search
@@ -360,8 +360,8 @@ func meta_boss_keystone_row(level_index: int) -> Dictionary:
|
||||
return {}
|
||||
|
||||
|
||||
func meta_workshop_items() -> Array:
|
||||
return _meta_rows("workshop_items")
|
||||
# meta_workshop_items 访问器已删除(迭代 22 T2——工坊行族退役,解锁混合制挂 treasure_items 行内;
|
||||
# 秘宝解锁投影面=MetaProgression.treasure_shop_status)
|
||||
|
||||
|
||||
func meta_conversion_row(attr: String) -> Dictionary:
|
||||
|
||||
@@ -29,59 +29,64 @@ func keystone_balance() -> int:
|
||||
return save.keystone_balance()
|
||||
|
||||
|
||||
# ---- 道具工坊(首版全走通关次数渠道——钥石直购价随定价批后补,最终双渠道并存)----
|
||||
# ---- 秘宝解锁服务(迭代 22 T2 换代——解锁混合制,契约 6;旧工坊渠道面随批删除)----
|
||||
# 〔属性锻造柜台退役——迭代 21 T4/T5:buy_forge/forge_flat/forge_level/forge_price 与 meta_forge
|
||||
# 表族随攻防所有权切分删除;生命唯一入口=树脊柱淬炼、攻/AC 唯一入口=锻造工坊路线(契约 9);
|
||||
# 注入面 hero_override 换树淬炼 flat+锻造路线加值位(预留 0)公式。〕
|
||||
|
||||
# 解锁判定量单点(可替换轴——2026-09-21 用户拍板:解锁条件须抽象、可替换、可扩展,禁与渠道实现耦合)。
|
||||
# 契约:渠道由 meta.tres workshop_items.channel 声明(缺省 total_clears,词表 MetaTable.WORKSHOP_CHANNELS
|
||||
# 封闭);本方法是渠道 → 判定数值的唯一换算点——换/加渠道 = 词表加值 + 此处增一 match 分支,
|
||||
# 阈值比较(refresh/workshop_status)与解锁写档零改动。候选渠道(语义已预留未实现):
|
||||
# adventure_depth 最高通关深度 / keystone 钥石直购双渠道。
|
||||
func _workshop_progress(channel: String) -> int:
|
||||
match channel:
|
||||
"total_clears":
|
||||
var total := 0
|
||||
for i in db.level_count(): # 表访问收口(2026-10-02——行集直扫退役)
|
||||
total += save.clear_count(i + 1)
|
||||
return total
|
||||
_:
|
||||
return 0 # 未知渠道恒 0 = 永不解锁(fail-safe 非致命——渠道词表校验兜底,不静默误解锁)
|
||||
|
||||
# 表族随攻防所有权切分删除。〕〔道具工坊解锁渠道面退役——迭代 22 T2:旧「总通关次数达阈值」
|
||||
# 行族与 _workshop_progress/workshop_status/refresh_workshop_unlocks 删除,解锁条件单一事实源=
|
||||
# treasure_items.tres 行内 unlock_keystone_cost/unlock_requires 谓词(C1 逐件分配锁定表)。〕
|
||||
|
||||
# 道具解锁读数访问器(组合根/呈现层解锁门控查询面——禁再穿透 meta.save 直读 save_manager)
|
||||
func is_item_unlocked(item_id: String) -> bool:
|
||||
return save.item_unlocked(item_id)
|
||||
|
||||
|
||||
# 工坊六行只读投影(呈现层数据源):{item_id, display_name, threshold, unlocked, progress}
|
||||
func workshop_status() -> Array:
|
||||
var out: Array[Dictionary] = []
|
||||
for row in db.meta_workshop_items():
|
||||
var iid := String(row.get("item_id", ""))
|
||||
var arow: Dictionary = db.treasure_item_row(iid) # 迭代 7 T5——失义/退役状态面(呈现「能力封存/已退役」)
|
||||
out.append({
|
||||
"item_id": iid,
|
||||
"display_name": String(row.get("display_name", "")),
|
||||
"threshold": int(row.get("clear_threshold", 0)),
|
||||
"unlocked": save.item_unlocked(iid),
|
||||
"progress": _workshop_progress(String(row.get("channel", "total_clears"))),
|
||||
"deferred": bool(arow.get("deferred", false)),
|
||||
"retired": bool(arow.get("retired", false)),
|
||||
})
|
||||
return out
|
||||
# 解锁条件计数读数单点(谓词 key → 生涯计数——封闭枚举=TreasureItemTable.UNLOCK_COUNTER_KEYS;
|
||||
# 词表加键 = 此处增一 match 分支,阈值比较与解锁写档零改动)
|
||||
func _treasure_unlock_progress(key: String) -> int:
|
||||
match key:
|
||||
"adventure_cleared":
|
||||
return save.adventure_cleared()
|
||||
"boss_kill_count":
|
||||
return save.boss_kill_count()
|
||||
"vision_used":
|
||||
return save.vision_used()
|
||||
_:
|
||||
return 0 # 未知计数键恒 0 = 永不解锁(fail-safe 非致命——schema 词表校验兜底,不静默误解锁)
|
||||
|
||||
|
||||
# 达标未解锁 → unlock_item + 写盘;返回新解锁 item_id 列(冒险收束后与营地打开时各调一次)
|
||||
func refresh_workshop_unlocks() -> Array:
|
||||
# 钥石购买解锁(营地秘宝匣唯一购买写口):仅购买件(行带 unlock_keystone_cost)可购;
|
||||
# 已解锁/余额不足/条件件误购 = 拒绝且零副作用(spend_keystone 内嵌余额闸,失败不扣币)。
|
||||
func buy_treasure_unlock(item_id: String) -> bool:
|
||||
var arow: Dictionary = db.treasure_item_row(item_id)
|
||||
var price := int(arow.get("unlock_keystone_cost", 0))
|
||||
if arow.is_empty() or price < 1 or (arow.get("unlock_requires", []) as Array).size() > 0:
|
||||
return false # 非购买件(条件件/无价行)拒绝
|
||||
if save.item_unlocked(item_id):
|
||||
return false # 已解锁重复购买拒绝
|
||||
if not save.spend_keystone(price):
|
||||
return false # 余额不足(零副作用)
|
||||
save.unlock_item(item_id)
|
||||
return save.write_to_disk()
|
||||
|
||||
|
||||
# 条件解锁刷新(达标未解锁 → unlock_item;返回新解锁 item_id 列——冒险收束后与营地打开时各调一次)。
|
||||
# 全部谓词达标才解锁(多谓词=AND——天眼双条件);幂等:已解锁跳过,重复刷新零重复入账。
|
||||
func refresh_treasure_unlocks() -> Array:
|
||||
var new_unlocks: Array[String] = []
|
||||
for row in db.meta_workshop_items():
|
||||
for row in db.treasure_attunement_rows():
|
||||
var iid := String(row.get("item_id", ""))
|
||||
if save.item_unlocked(iid):
|
||||
continue
|
||||
var progress := _workshop_progress(String(row.get("channel", "total_clears")))
|
||||
if progress >= int(row.get("clear_threshold", 99)):
|
||||
var ureqs: Array = row.get("unlock_requires", []) as Array
|
||||
if ureqs.is_empty():
|
||||
continue # 购买件不入条件刷新面
|
||||
var met := true
|
||||
for req_v in ureqs:
|
||||
var req: Dictionary = req_v as Dictionary
|
||||
if _treasure_unlock_progress(String(req.get("key", ""))) < int(req.get("threshold", 999)):
|
||||
met = false
|
||||
break
|
||||
if met:
|
||||
save.unlock_item(iid)
|
||||
new_unlocks.append(iid)
|
||||
if not new_unlocks.is_empty():
|
||||
@@ -89,6 +94,35 @@ func refresh_workshop_unlocks() -> Array:
|
||||
return new_unlocks
|
||||
|
||||
|
||||
# 秘宝匣柜台只读投影(呈现层数据源——12 件逐行解锁态/价格/条件进度):
|
||||
# {item_id, display_name, desc, tier, unlocked, mode(keystone|condition|owned), price, reqs:[{key,threshold,progress}]}
|
||||
func treasure_shop_status() -> Array:
|
||||
var out: Array[Dictionary] = []
|
||||
for row in db.treasure_attunement_rows():
|
||||
var iid := String(row.get("item_id", ""))
|
||||
var unlocked := save.item_unlocked(iid)
|
||||
var ureqs: Array = row.get("unlock_requires", []) as Array
|
||||
var req_view: Array = []
|
||||
for req_v in ureqs:
|
||||
var req: Dictionary = req_v as Dictionary
|
||||
req_view.append({
|
||||
"key": String(req.get("key", "")),
|
||||
"threshold": int(req.get("threshold", 0)),
|
||||
"progress": _treasure_unlock_progress(String(req.get("key", ""))),
|
||||
})
|
||||
out.append({
|
||||
"item_id": iid,
|
||||
"display_name": String(row.get("display_name", "")),
|
||||
"desc": String(row.get("desc", "")),
|
||||
"tier": String(row.get("tier", "")),
|
||||
"unlocked": unlocked,
|
||||
"mode": "owned" if unlocked else ("condition" if ureqs.size() > 0 else "keystone"),
|
||||
"price": int(row.get("unlock_keystone_cost", 0)),
|
||||
"reqs": req_view,
|
||||
})
|
||||
return out
|
||||
|
||||
|
||||
# ---- Boss 两档与收束入账(结算屏文案数据源)----
|
||||
# 明细 {big_reward, boss_drops, total, new_unlocks};入账与标记经 save.apply_adventure_summary
|
||||
# (keystone_drops 副本随摘要入 recent_summaries)。
|
||||
@@ -100,7 +134,7 @@ func settle_adventure(summary: Dictionary) -> Dictionary:
|
||||
save.mark_first_adventure_done()
|
||||
var drops: Array = (summary.get("keystone_drops", []) as Array).duplicate(true)
|
||||
save.apply_adventure_summary(summary, drops)
|
||||
var new_unlocks := refresh_workshop_unlocks()
|
||||
var new_unlocks := refresh_treasure_unlocks() # 迭代 22 T2——条件解锁刷新换代(旧工坊面退役)
|
||||
save.write_to_disk()
|
||||
var total := int(summary.get("keystone_total", 0))
|
||||
return {
|
||||
@@ -297,16 +331,24 @@ func _check_easter_egg() -> void:
|
||||
|
||||
# ---- 同调位账本(迭代 11 第①批 T4——装配关系/同调位求值/Y 刷新升级;设计权威 proposal §2 + 需求卡片 P1 甲)----
|
||||
|
||||
# 装配切换(营地装配覆盖层唯一写口):已装 → 卸下恒可;未装 → 非同调行拒绝 + 满位禁选;
|
||||
# 装配关系写存档持久(冒险中途不可换装——底稿 §8.5,本口只服务营地)。
|
||||
# 装配切换(营地装配覆盖层唯一写口——冒险开始前配置;冒险中零写口=装配不可变契约 1,本口只服务营地):
|
||||
# 已装 → 卸下恒可(卸下后其余件前移位序——效果面只读装配集不受格位影响);未装 → 非同调行拒绝 +
|
||||
# 满位禁选 + 档位格校验(迭代 22 T2 契约 2:装入件 append 至末位 k=已装数+1,件档 rank ≤ k——
|
||||
# 格 1=普通格/格 2=优秀格/格 3=稀有格递增向下兼容;槽位来源=树同调位节点点亮数,树接口接缝现役)。
|
||||
# 装配关系写存档持久。
|
||||
func toggle_attunement(item_id: String) -> bool:
|
||||
var equipped: Array = save.attunement_equipped()
|
||||
if not equipped.has(item_id):
|
||||
var arow: Dictionary = db.treasure_item_row(item_id)
|
||||
if arow.is_empty() or String(arow.get("form", "")) == "":
|
||||
return false # 非同调行(过渡/退役行)禁装配
|
||||
if not save.item_unlocked(item_id):
|
||||
return false # 未解锁禁装配(解锁混合制契约 6——装配以解锁为前置)
|
||||
if equipped.size() >= Attunement.slots(db, save):
|
||||
return false # 满位禁选
|
||||
var rank: int = int(db.treasure_items.TIER_RANK.get(String(arow.get("tier", "")), 99))
|
||||
if rank > equipped.size() + 1:
|
||||
return false # 件档高于将占格档(第 k 格档=k——普通/优秀/稀有递增)
|
||||
equipped.append(item_id)
|
||||
else:
|
||||
equipped.erase(item_id)
|
||||
@@ -323,8 +365,11 @@ func attune_camp_status() -> Dictionary:
|
||||
items.append({
|
||||
"item_id": iid,
|
||||
"display_name": String(row.get("display_name", "")),
|
||||
"desc": String(row.get("desc", "")),
|
||||
"tier": String(row.get("tier", "")),
|
||||
"form": String(row.get("form", "")),
|
||||
"equipped": equipped.has(iid),
|
||||
"unlocked": save.item_unlocked(iid),
|
||||
})
|
||||
var lvl := save.attune_refresh_level()
|
||||
var next_row: Dictionary = db.meta_attune_refresh_row(lvl + 1)
|
||||
|
||||
@@ -134,6 +134,8 @@ func _default_data() -> Dictionary[String, Variant]:
|
||||
"first_adventure_done": false,
|
||||
"adventure_total": 0, # 生涯终身计数·冒险总局数(迭代 18 卡④——开局即计含放弃与失败,收束唯一入账)
|
||||
"adventure_cleared": 0, # 生涯终身计数·通关局数(三关全通才计 1——判定字段 summary.cleared)
|
||||
"vision_used": 0, # 生涯终身计数·真视类工具累计使用次数(迭代 22 T2——信标网络解锁行为条件;
|
||||
# 受理成功唯一计数点=battle 层经冒险层聚合、收束入账;缺键补零不升版本——迭代 21 先例)
|
||||
},
|
||||
"breakpoint": {}, # 冒险断点快照(空 = 无断点合法态——「继续上局」隐藏;迭代 8 卡①)
|
||||
}
|
||||
@@ -272,6 +274,16 @@ func adventure_cleared() -> int:
|
||||
return int(_data["meta"].get("adventure_cleared", 0))
|
||||
|
||||
|
||||
# 真视类工具累计使用次数(迭代 22 T2——信标网络解锁行为条件;写入唯一经 add_vision_used
|
||||
# 冒险收束聚合入账,受理计数点=battle 层经冒险层聚合)
|
||||
func vision_used() -> int:
|
||||
return int(_data["meta"].get("vision_used", 0))
|
||||
|
||||
|
||||
func add_vision_used(n: int) -> void:
|
||||
_data["meta"]["vision_used"] = maxi(0, vision_used() + maxi(0, n))
|
||||
|
||||
|
||||
# ---- 天赋树域(迭代 12 T9——MetaProgression 树段读写面)----
|
||||
|
||||
func tree_nodes() -> Dictionary:
|
||||
|
||||
@@ -3,38 +3,33 @@
|
||||
#(元进度 GDD 验收标准,本批 = 其实现批)
|
||||
# Date: 2026-09-21(元进度实现批——设计权威 design/gdd/systems/冒险循环/元进度.md 定稿 v1.2)
|
||||
# 数据配置 MetaTable schema(元进度行族)。
|
||||
# 行族四轴(数据契约对齐 ConfigDB.meta_* 访问器——消费面一律走访问器,禁直读行集):
|
||||
# 行族三轴(数据契约对齐 ConfigDB.meta_* 访问器——消费面一律走访问器,禁直读行集):
|
||||
# boss_keystone = Boss 钥石两档行(level_index 连续从 1;first_kill ≥ repeat_kill——首杀全量、
|
||||
# 重复杀固定小额恒定不递减,两档数值归元进度设计批配读数)。
|
||||
# workshop_items = 道具工坊行(item_id = 秘宝六件词表封闭恰全;首版解锁渠道 = 总通关次数
|
||||
# 达标 clear_threshold——2026-09-20 排期裁决;局内效果整体归 Alpha 秘宝重设计批,本表只有解锁状态面)。
|
||||
# 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 考古。〕
|
||||
# 〔workshop_items 道具工坊行族已退役——迭代 22 T2 秘宝实装批:解锁换代混合制(钥石购买价
|
||||
# unlock_keystone_cost + 条件谓词 unlock_requires 全挂 treasure_items.tres 行内,单一事实源),
|
||||
# 旧「总通关次数达阈值」渠道行族、WORKSHOP_ITEM_IDS/WORKSHOP_CHANNELS 词表与校验段随批删除。〕
|
||||
# 行值全部 provisional 归 元进度设计批配读数(用户拍板骨架先行,先例 = GP+商店房批)。
|
||||
class_name MetaTable
|
||||
extends Resource
|
||||
|
||||
@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"] # 三属性轴词表(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
|
||||
# 增一分支,阈值比较与解锁写档零改动。缺省(行无 channel 字段)= total_clears)
|
||||
const WORKSHOP_CHANNELS := ["total_clears"]
|
||||
|
||||
|
||||
func validate() -> Array:
|
||||
var errs: Array[Dictionary] = []
|
||||
# 行族空护栏(四族皆为 ConfigDB 消费面——缺族即致命)
|
||||
for family in ["boss_keystone", "workshop_items", "conversion", "attune_refresh_upgrade"]:
|
||||
# 行族空护栏(三族皆为 ConfigDB 消费面——缺族即致命)
|
||||
for family in ["boss_keystone", "conversion", "attune_refresh_upgrade"]:
|
||||
if get(family).is_empty():
|
||||
errs.append({"category": "empty_table", "message": "MetaTable 行族为空: %s" % family})
|
||||
# boss_keystone:level_index 唯一 + 连续从 1 + 两档域 + first ≥ repeat(首杀全量、重复小额恒定)
|
||||
@@ -55,28 +50,8 @@ func validate() -> Array:
|
||||
if li_at != i + 1:
|
||||
errs.append({"category": "structural", "message": "boss_keystone level_index 不连续(须从 1 连续递增): 位 %d = %d" % [i + 1, li_at]})
|
||||
break
|
||||
# workshop_items:item_id 词表封闭 + 唯一 + 覆盖恰六(秘宝词表)+ 阈值域 [1,99]
|
||||
var seen_item := {}
|
||||
for row in workshop_items:
|
||||
var iid: String = row.get("item_id", "")
|
||||
if not WORKSHOP_ITEM_IDS.has(iid):
|
||||
errs.append({"category": "enum", "message": "workshop_items item_id 出域秘宝六件词表: %s" % iid})
|
||||
if seen_item.has(iid):
|
||||
errs.append({"category": "dup_key", "message": "workshop_items item_id 重复: %s" % iid})
|
||||
seen_item[iid] = true
|
||||
if String(row.get("display_name", "")) == "":
|
||||
errs.append({"category": "domain", "message": "workshop_items %s display_name 空" % iid})
|
||||
var th: int = int(row.get("clear_threshold", -1))
|
||||
if th < 1 or th > 99:
|
||||
errs.append({"category": "domain", "message": "workshop_items %s clear_threshold 越域 [1,99]: %d" % [iid, th]})
|
||||
# channel:可替换解锁渠道(缺省 total_clears——词表封闭,扩渠道 = 词表加值 + 判定量分支)
|
||||
var channel: String = row.get("channel", "total_clears")
|
||||
if not WORKSHOP_CHANNELS.has(channel):
|
||||
errs.append({"category": "enum", "message": "workshop_items %s channel 出域 %s: %s" % [iid, str(WORKSHOP_CHANNELS), channel]})
|
||||
if not workshop_items.is_empty():
|
||||
for iid_key in WORKSHOP_ITEM_IDS:
|
||||
if not seen_item.has(iid_key):
|
||||
errs.append({"category": "coverage", "message": "workshop_items 缺道具键: %s(六件词表恰全)" % iid_key})
|
||||
# workshop_items 行族已退役(迭代 22 T2——解锁混合制换代,条件谓词挂 treasure_items 行内;
|
||||
# 校验段随批删除——秘宝解锁条件校验单点=TreasureItemTable.validate)
|
||||
# conversion:attr 词表与 forge 一致 + 唯一 + 覆盖恰三 + 平移量域 [0,100]
|
||||
var seen_conv := {}
|
||||
for row in conversion:
|
||||
|
||||
@@ -66,6 +66,9 @@ const USE_PHASES := ["active_dig"]
|
||||
const FORMS := ["", "passive", "active_charged"]
|
||||
# 档位词表(迭代 22 T1——槽位档位约束数据面:槽位可装 ≤ 槽位档位的件;词表沿 feats/adventure TIERS)
|
||||
const TIERS := ["common", "excellent", "rare", "legendary"]
|
||||
# 档位序(迭代 22 T2——装配档位校验取数源:件档 rank ≤ 装入位序(格 1=普通格/格 2=优秀格/格 3=稀有格
|
||||
# 递增,向下兼容=高档格可装低档件);rank 越大档越高)
|
||||
const TIER_RANK := {"common": 1, "excellent": 2, "rare": 3, "legendary": 4}
|
||||
# 充能重置周期(迭代 22 T1 扩签——现役仅 adventure=每冒险;battle=每战重置〔线香〕/level=每关重置〔天眼〕)
|
||||
const CHARGE_RESETS := ["adventure", "battle", "level"]
|
||||
# magnitude 消费面(≥1 校验):grant_ap 与同调 N 值 kind;其余 kind 活行 magnitude 恒 0
|
||||
|
||||
@@ -17,37 +17,6 @@ boss_keystone = [{
|
||||
"first_kill": 3,
|
||||
"repeat_kill": 3
|
||||
}]
|
||||
workshop_items = [{
|
||||
"item_id": "probe_dust",
|
||||
"display_name": "显影尘",
|
||||
"clear_threshold": 1,
|
||||
"channel": "total_clears"
|
||||
}, {
|
||||
"item_id": "sapper_order",
|
||||
"display_name": "工兵令",
|
||||
"clear_threshold": 1,
|
||||
"channel": "total_clears"
|
||||
}, {
|
||||
"item_id": "loot_compass",
|
||||
"display_name": "探宝罗盘",
|
||||
"clear_threshold": 2,
|
||||
"channel": "total_clears"
|
||||
}, {
|
||||
"item_id": "nest_pendulum",
|
||||
"display_name": "探巢摆钟",
|
||||
"clear_threshold": 2,
|
||||
"channel": "total_clears"
|
||||
}, {
|
||||
"item_id": "catacomb_scroll",
|
||||
"display_name": "地穴全图卷",
|
||||
"clear_threshold": 3,
|
||||
"channel": "total_clears"
|
||||
}, {
|
||||
"item_id": "blast_kit",
|
||||
"display_name": "爆破工兵包",
|
||||
"clear_threshold": 3,
|
||||
"channel": "total_clears"
|
||||
}]
|
||||
conversion = [{
|
||||
"attr": "max_hp",
|
||||
"per_level_shift_pct": 6
|
||||
@@ -73,9 +42,12 @@ attune_refresh_upgrade = [{
|
||||
; 〔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 = 未标定占位(标定归设计批,护甲行强条目:验收读数必须按
|
||||
; 与 ForgeGDD 考古用,行族本体不再在表。〕
|
||||
; 〔workshop_items 道具工坊行族已退役——迭代 22 T2 秘宝实装批:解锁换代混合制(钥石购买价
|
||||
; unlock_keystone_cost + 条件谓词 unlock_requires 全挂 treasure_items.tres 行内单一事实源),
|
||||
; 旧「总通关次数达阈值」六行(阈值 1/1/2/2/3/3)与渠道面随批删除;历史记载归 git 考古。〕
|
||||
; boss 两档 L1 5/1 · L2 8/2 · L3 12/3
|
||||
; (首杀全量、重复杀固定小额恒定不递减);conversion 全 0 = 未标定占位(标定归设计批,护甲行强条目:验收读数必须按
|
||||
; 解锁后参数重测)。
|
||||
;
|
||||
; 数值设计批·关卡成长×永久成长对齿(2026-09-22——设计权威 qa-data/数值设计_关卡成长×永久成长_v1.md,
|
||||
|
||||
@@ -42,12 +42,12 @@ func _init(db_ref, meta_get: Callable, save_get: Callable, camp_overlay_ref: Cam
|
||||
|
||||
|
||||
# 打开营地(主菜单入口——开合与开始层互斥仿设置面先例:开始覆盖层非模态且树序在后,
|
||||
# 规范 2.3 遮挡纪律;打开时先刷工坊解锁——达标未解锁就地入账)
|
||||
# 规范 2.3 遮挡纪律;打开时先刷条件解锁——达标未解锁就地入账)
|
||||
func open_camp() -> void:
|
||||
var meta = _meta_get.call()
|
||||
if meta == null:
|
||||
return
|
||||
meta.refresh_workshop_unlocks()
|
||||
meta.refresh_treasure_unlocks() # 迭代 22 T2——条件解锁刷新换代(旧工坊面退役)
|
||||
start_overlay.visible = false
|
||||
_open_modal.call(camp_overlay) # 模态栈单口(T3 收口批——登记层开合一律走栈)
|
||||
camp_overlay.open_camp(camp_data())
|
||||
@@ -61,13 +61,13 @@ func on_camp_closed() -> void:
|
||||
_refresh_presentations.call() # 关闭后刷新顶栏(工坊解锁可能变了余额侧读数面)
|
||||
|
||||
|
||||
# 营地快照投影(纯读——工坊六行/同调装配/树面/余额/最近冒险;拼句归 CampOverlay,账面归
|
||||
# MetaProgression。属性锻造三行投影已删——迭代 21 T4 柜台退役)
|
||||
# 营地快照投影(纯读——秘宝匣 12 件/同调装配/树面/余额/最近冒险;拼句归 CampOverlay,账面归
|
||||
# MetaProgression。属性锻造三行投影已删——迭代 21 T4 柜台退役;工坊六行换代=迭代 22 T2 秘宝匣)
|
||||
func camp_data() -> Dictionary:
|
||||
var meta = _meta_get.call()
|
||||
return {
|
||||
"balance": meta.keystone_balance() if meta != null else 0,
|
||||
"workshop": meta.workshop_status() if meta != null else [],
|
||||
"workshop": meta.treasure_shop_status() if meta != null else [], # 键名沿 workshop(视图消费键不变——行内容=秘宝匣 12 件解锁投影)
|
||||
"attune": meta.attune_camp_status() if meta != null else {}, # 迭代 11 第①批 T4——同调装配面投影
|
||||
"tree": meta.tree_camp_rows() if meta != null else [], # 树面投影(含 forge_workshop/ks_workshop 钥匙门判据行)
|
||||
"fork_groups": meta.tree_fork_groups() if meta != null else [],
|
||||
|
||||
@@ -299,11 +299,12 @@ func on_continue() -> void:
|
||||
_fatal_screen.call("关卡推进失败——见输出日志")
|
||||
|
||||
|
||||
# 工坊 item_id → 显示名投影(keystone_settle_text/替换子命名文案数据源——拼句归 Narrator)
|
||||
# 秘宝 item_id → 显示名投影(keystone_settle_text/替换子命名文案数据源——拼句归 Narrator);
|
||||
# 迭代 22 T2 数据源换代:旧工坊行族退役,改读 treasure 装配词表 12 件
|
||||
func item_name_lookup() -> Dictionary:
|
||||
var lookup := {}
|
||||
var meta = _meta_get.call()
|
||||
if meta != null:
|
||||
for row in db.meta_workshop_items():
|
||||
for row in db.treasure_attunement_rows():
|
||||
lookup[String(row.get("item_id", ""))] = String(row.get("display_name", ""))
|
||||
return lookup
|
||||
@@ -1018,7 +1018,7 @@ func _on_settings_closed() -> void:
|
||||
func _pause_data() -> Dictionary:
|
||||
return {
|
||||
"balance": meta.keystone_balance(),
|
||||
"workshop": meta.workshop_status(),
|
||||
"workshop": meta.treasure_shop_status(), # 迭代 22 T2——秘宝匣 12 件解锁投影(旧工坊面退役)
|
||||
"tree": meta.tree_camp_rows(),
|
||||
"fork_groups": meta.tree_fork_groups(),
|
||||
}
|
||||
|
||||
@@ -307,23 +307,29 @@ func refresh_camp(data: Dictionary) -> void:
|
||||
var row: Dictionary = ws_by_id.get(String(card.item_id), {})
|
||||
if row.is_empty():
|
||||
continue
|
||||
var th := int(row.get("threshold", 0))
|
||||
var prog := int(row.get("progress", 0))
|
||||
var unlocked := bool(row.get("unlocked", false))
|
||||
card.progress_l.text = "累计通关 %d / %d 次" % [prog, th]
|
||||
# 迭代 7 T5——失义件(显影尘/地穴全图卷 Alpha 重定)与退役件(探宝罗盘 2026-09-29 拍板①乙)呈现口径;
|
||||
# 状态色随卡⑭ 亮板双色墨制(深墨族替代暖白族——亮板上暖白不可读)
|
||||
# 秘宝匣进度列(迭代 22 T2——解锁混合制三形态:钥石购买价/条件进度拼接/已入手;
|
||||
# 旧「累计通关 N/M 次」单一渠道文案随工坊行族退役)
|
||||
var prog_text := ""
|
||||
var mode := String(row.get("mode", ""))
|
||||
if unlocked:
|
||||
prog_text = "已入手"
|
||||
elif mode == "keystone":
|
||||
prog_text = "钥石 %d" % int(row.get("price", 0))
|
||||
elif mode == "condition":
|
||||
var parts: Array[String] = []
|
||||
for req_v in (row.get("reqs", []) as Array):
|
||||
var req: Dictionary = req_v as Dictionary
|
||||
var lbl := String(UiKit.TREASURE_REQ_LABELS.get(String(req.get("key", "")), "条件"))
|
||||
parts.append("%s %d/%d" % [lbl, int(req.get("progress", 0)), int(req.get("threshold", 0))])
|
||||
prog_text = "、".join(parts)
|
||||
card.progress_l.text = prog_text
|
||||
# 状态色随卡⑭ 亮板双色墨制(深墨族替代暖白族——亮板上暖白不可读);失义/退役行随新目录退役
|
||||
var st_text := "未解锁"
|
||||
var st_col: Color = UiKit.STONE
|
||||
if bool(row.get("retired", false)):
|
||||
st_text = "已退役"
|
||||
st_col = UiKit.STONE
|
||||
elif bool(row.get("deferred", false)) and unlocked:
|
||||
st_text = "已解锁 · 能力封存中"
|
||||
st_col = UiKit.GOLD_INK # 尾裁微批a 2026-10-08——GOLD_DEEP 对行板 3.00:1 越带,换深金墨 5.84:1
|
||||
elif unlocked:
|
||||
if unlocked:
|
||||
st_text = "已解锁"
|
||||
st_col = UiKit.GOLD_INK # 同上
|
||||
st_col = UiKit.GOLD_INK # 尾裁微批a 2026-10-08——GOLD_DEEP 对行板 3.00:1 越带,换深金墨 5.84:1
|
||||
card.status_l.text = st_text
|
||||
card.status_l.add_theme_color_override("font_color", st_col)
|
||||
card.status_l.tooltip_text = st_text # 截断兜底(状态列 EXPAND_FILL 吸余宽——悬停看全文)
|
||||
|
||||
@@ -82,6 +82,8 @@ func open_pause(data: Dictionary) -> void:
|
||||
|
||||
|
||||
# 道具工坊六行(解锁态文案与营地柜台同口径——退役/封存/已解锁/未解锁;进度=解锁判定量读数)
|
||||
# 秘宝匣 12 行只读概览(迭代 22 T2 换代——旧工坊六行退役;数据源=treasure_shop_status 投影,
|
||||
# 解锁态文案与营地柜台同口径;进度列=解锁混合制三形态:钥石价/条件进度拼接/已入手)
|
||||
func _rebuild_workshop(data: Dictionary) -> void:
|
||||
for c in _workshop_holder.get_children():
|
||||
c.queue_free()
|
||||
@@ -93,28 +95,29 @@ func _rebuild_workshop(data: Dictionary) -> void:
|
||||
name_l.clip_text = true
|
||||
name_l.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
||||
line.add_child(name_l)
|
||||
var prog_l := UiKit.label("累计通关 %d/%d 次" % [int(row.get("progress", 0)), int(row.get("threshold", 0))],UiKit.FS_HINT, false, UiKit.INK_HINT)
|
||||
var unlocked := bool(row.get("unlocked", false))
|
||||
var prog_text := "已入手"
|
||||
if not unlocked:
|
||||
if String(row.get("mode", "")) == "keystone":
|
||||
prog_text = "钥石 %d" % int(row.get("price", 0))
|
||||
else:
|
||||
var parts: Array[String] = []
|
||||
for req_v in (row.get("reqs", []) as Array):
|
||||
var req: Dictionary = req_v as Dictionary
|
||||
var lbl := String(UiKit.TREASURE_REQ_LABELS.get(String(req.get("key", "")), "条件"))
|
||||
parts.append("%s %d/%d" % [lbl, int(req.get("progress", 0)), int(req.get("threshold", 0))])
|
||||
prog_text = "、".join(parts)
|
||||
var prog_l := UiKit.label(prog_text,UiKit.FS_HINT, false, UiKit.INK_HINT)
|
||||
prog_l.custom_minimum_size = Vector2(150, 0)
|
||||
prog_l.clip_text = true
|
||||
prog_l.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
||||
line.add_child(prog_l)
|
||||
var st_l := UiKit.label(_workshop_state_text(row),UiKit.FS_HINT, true, UiKit.INK_LABEL)
|
||||
var st_l := UiKit.label("已解锁" if unlocked else "未解锁",UiKit.FS_HINT, true, UiKit.INK_LABEL)
|
||||
st_l.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
line.add_child(st_l)
|
||||
_workshop_holder.add_child(line)
|
||||
|
||||
|
||||
# 解锁态文案(camp_overlay_view.refresh_camp 同款判据——退役 > 封存 > 已解锁 > 未解锁)
|
||||
func _workshop_state_text(row: Dictionary) -> String:
|
||||
if bool(row.get("retired", false)):
|
||||
return "已退役"
|
||||
if bool(row.get("deferred", false)) and bool(row.get("unlocked", false)):
|
||||
return "已解锁 · 能力封存中"
|
||||
if bool(row.get("unlocked", false)):
|
||||
return "已解锁"
|
||||
return "未解锁"
|
||||
|
||||
|
||||
# 天赋树已购概览(单条聚合文案——已购节点等级 + Fork 组激活侧;零已购=空态句)
|
||||
func _rebuild_tree(data: Dictionary) -> void:
|
||||
var owned: Array[String] = []
|
||||
|
||||
@@ -318,6 +318,12 @@ const FS_SUB := 18 # 副标题/小组标题(=棋盘数字下限同值)
|
||||
const FS_TITLE := 24 # 屏标题/横幅/价格
|
||||
const FS_H1 := 34 # 主标题/演算暴击
|
||||
|
||||
# ── 秘宝解锁条件计数键 → 呈现标签(迭代 22 秘宝批 T4——秘宝匣柜台条件进度文案单点;键域=
|
||||
# TreasureItemTable.UNLOCK_COUNTER_KEYS 封闭枚举,键出域走「条件」兜底不炸)──
|
||||
const TREASURE_REQ_LABELS := {"adventure_cleared": "通关", "boss_kill_count": "Boss击杀", "vision_used": "真视使用"}
|
||||
# 秘宝档位 → 呈现名(契约 2 三档;legendary 传说空集合法不在柜)
|
||||
const TREASURE_TIER_LABELS := {"common": "普通", "excellent": "优秀", "rare": "稀有", "legendary": "传说"}
|
||||
|
||||
# ── StyleBox 工厂 ──
|
||||
# margin = (左, 上, 右, 下);border_w = 0 即无边框(根底/遮罩用)
|
||||
# 圆角率单点(UI 设计对齐批 P5 甲 2026-10-06——容器/按钮全站唯一圆角值;数据框/数字牌/AP 点阵/准线/血条
|
||||
|
||||
+66
-48
@@ -4359,10 +4359,10 @@ func _smoke_meta() -> void:
|
||||
print("--- 元进度·MetaTable 表结构 ---")
|
||||
var meta_tbl = _db.meta
|
||||
check(meta_tbl != null and meta_tbl.validate().is_empty(),
|
||||
"MetaTable 非空 + validate 零错(四行族词表封闭/唯一/连续/域界/first≥repeat/ratio 全过)")
|
||||
"MetaTable 非空 + validate 零错(三行族词表封闭/唯一/连续/域界/first≥repeat/ratio 全过)")
|
||||
check(meta_tbl.boss_keystone.size() == 3
|
||||
and meta_tbl.workshop_items.size() == 6 and meta_tbl.conversion.size() == 3,
|
||||
"MetaTable 行族规模 3/6/3(Boss/工坊恰六/换算恰三——forge_products 行族已随迭代 21 T5 退役)")
|
||||
and meta_tbl.conversion.size() == 3 and meta_tbl.attune_refresh_upgrade.size() == 2,
|
||||
"MetaTable 行族规模 3/3/2(Boss/换算恰三/Y 升级两档——forge_products 迭代 21 退役、workshop_items 迭代 22 退役)")
|
||||
var attrs_ok := true
|
||||
for a in ["max_hp", "atk", "armor_class"]:
|
||||
attrs_ok = attrs_ok and not _db.meta_conversion_row(a).is_empty()
|
||||
@@ -4387,22 +4387,21 @@ func _pause_overlay_projection() -> void:
|
||||
_meta_smoke_cleanup()
|
||||
var sv = _meta_new_save(_META_SMOKE_PATHS[0])
|
||||
var mp = load("res://core/meta_progression.gd").new(_db, sv)
|
||||
# 已购态夹具:余额非零 + 工坊首件解锁——投影可辨非全零白板(属性锻造段已随柜台退役删除)
|
||||
# 已购态夹具:余额非零 + 秘宝匣首件解锁——投影可辨非全零白板(属性锻造段已随柜台退役删除)
|
||||
sv.add_keystone(37)
|
||||
var ws_meta: Array = _db.meta_workshop_items()
|
||||
if not ws_meta.is_empty():
|
||||
sv.unlock_item(String(ws_meta[0].get("item_id", "")))
|
||||
sv.unlock_item("morning_order")
|
||||
sv.write_to_disk()
|
||||
# 投影拼装(main._pause_data 同构)+ 视图喂入
|
||||
var data := {"balance": mp.keystone_balance(), "workshop": mp.workshop_status(),
|
||||
var data := {"balance": mp.keystone_balance(), "workshop": mp.treasure_shop_status(),
|
||||
"tree": mp.tree_camp_rows(), "fork_groups": mp.tree_fork_groups()}
|
||||
var po = load("res://presentation/views/pause_overlay.gd").new()
|
||||
po.open_pause(data)
|
||||
check(po.visible, "暂停层开层可见(open_pause 直开——P4 甲零动效路径)")
|
||||
check(String(po._balance_badge.value_label.text) == "钥石 %d" % mp.keystone_balance(),
|
||||
"余额徽投影 = keystone_balance() 读数(%s)" % po._balance_badge.value_label.text)
|
||||
var ws: Array = mp.workshop_status()
|
||||
var ws: Array = mp.treasure_shop_status()
|
||||
var ws_ok: bool = po._workshop_holder.get_child_count() == ws.size()
|
||||
var uikit_s = load("res://presentation/views/ui_kit.gd")
|
||||
for i in mini(ws.size(), po._workshop_holder.get_child_count()):
|
||||
var row: Dictionary = ws[i]
|
||||
var lbls2: Array = []
|
||||
@@ -4410,18 +4409,23 @@ func _pause_overlay_projection() -> void:
|
||||
if ch is Label:
|
||||
lbls2.append(ch)
|
||||
var unlocked: bool = bool(row.get("unlocked", false))
|
||||
var expect_st := "未解锁"
|
||||
if bool(row.get("retired", false)):
|
||||
expect_st = "已退役"
|
||||
elif bool(row.get("deferred", false)) and unlocked:
|
||||
expect_st = "已解锁 · 能力封存中"
|
||||
elif unlocked:
|
||||
expect_st = "已解锁"
|
||||
var expect_prog := "已入手"
|
||||
if not unlocked:
|
||||
if String(row.get("mode", "")) == "keystone":
|
||||
expect_prog = "钥石 %d" % int(row.get("price", 0))
|
||||
elif String(row.get("mode", "")) == "condition":
|
||||
var parts: Array[String] = []
|
||||
for req_v2 in (row.get("reqs", []) as Array):
|
||||
var req2: Dictionary = req_v2 as Dictionary
|
||||
var lbl2 := String(uikit_s.TREASURE_REQ_LABELS.get(String(req2.get("key", "")), "条件"))
|
||||
parts.append("%s %d/%d" % [lbl2, int(req2.get("progress", 0)), int(req2.get("threshold", 0))])
|
||||
expect_prog = "、".join(parts)
|
||||
var expect_st2 := "已解锁" if unlocked else "未解锁"
|
||||
ws_ok = ws_ok and lbls2.size() == 3 \
|
||||
and String(lbls2[0].text) == String(row.get("display_name", "")) \
|
||||
and String(lbls2[1].text) == "累计通关 %d/%d 次" % [int(row.get("progress", 0)), int(row.get("threshold", 0))] \
|
||||
and String(lbls2[2].text) == expect_st
|
||||
check(ws_ok, "工坊六行投影 = workshop_status() 逐项(名称/判定量进度/解锁态——首件解锁夹具在列)")
|
||||
and String(lbls2[1].text) == expect_prog \
|
||||
and String(lbls2[2].text) == expect_st2
|
||||
check(ws_ok, "秘宝匣 12 行投影 = treasure_shop_status() 逐项(名称/钥石价与条件进度/解锁态——首件解锁夹具在列)〔秘宝批 V19 呈现数据源〕")
|
||||
var owned := 0
|
||||
for trow in (mp.tree_camp_rows() as Array):
|
||||
if int(trow.get("level", 0)) > 0:
|
||||
@@ -4507,7 +4511,7 @@ const _META_SMOKE_PATHS := ["user://smoke_meta_buy.json", "user://smoke_meta_dry
|
||||
"user://smoke_meta_cap.json", "user://smoke_meta_white.json",
|
||||
"user://smoke_meta_attune.json", "user://smoke_meta_attune2.json",
|
||||
"user://smoke_meta_tree.json", "user://smoke_meta_career.json",
|
||||
"user://smoke_meta_career_v5.json"]
|
||||
"user://smoke_meta_career_v5.json", "user://smoke_meta_treasure_unlock.json"]
|
||||
|
||||
|
||||
func _meta_smoke_cleanup() -> void:
|
||||
@@ -4638,32 +4642,42 @@ func _meta_settle_and_workshop() -> void:
|
||||
"stopped_at": 1, "keystone_total": 0, "master_seed": 15})
|
||||
check(dz["boss_drops"].is_empty() and sv.keystone_balance() == bal_before and sv.clear_count(1) == cc1_before,
|
||||
"败北零通关摘要:零入账零标记(钥石只增不减——验收1;败北局 clear_counts 不变)")
|
||||
# ⑧ 工坊解锁:settle 内已随 refresh——总通关次数达阈值件数 == unlocked 数;重复 refresh 幂等
|
||||
var total_clears: int = sv.clear_count(1) + sv.clear_count(2) + sv.clear_count(3)
|
||||
var expect_n := 0
|
||||
for row in _db.meta_workshop_items():
|
||||
if total_clears >= int(row.get("clear_threshold", 99)):
|
||||
expect_n += 1
|
||||
var got_n := 0
|
||||
for st_row in mp.workshop_status():
|
||||
got_n += 1 if bool(st_row.get("unlocked", false)) else 0
|
||||
check(got_n == expect_n and mp.refresh_workshop_unlocks().is_empty(),
|
||||
"工坊解锁数 = 总通关 %d 次达阈值件数(%d 件)且重复 refresh 幂等" % [total_clears, expect_n])
|
||||
# 渠道可替换轴(2026-09-21 用户拍板):判定量单点正控 + 未知渠道 fail-safe(恒 0 = 永不解锁,非致命)
|
||||
check(mp._workshop_progress("total_clears") == total_clears and mp._workshop_progress("adventure_depth") == 0,
|
||||
"解锁判定量单点:total_clears 正控 + 未知渠道恒 0(channel 可替换轴 fail-safe)")
|
||||
var sv_r = _meta_new_save(_META_SMOKE_PATHS[2])
|
||||
# ⑧ 秘宝解锁服务(迭代 22 T2——V3 钥石购买/V4 条件解锁;注入=直调解锁写口+直置存档生涯计数,
|
||||
# 不走正常资源积累流程——卡片 7.1 测试方式;旧工坊渠道段随行族退役摘除)
|
||||
var sv_u = _meta_new_save(_META_SMOKE_PATHS[11])
|
||||
var mp_u = load("res://core/meta_progression.gd").new(_db, sv_u)
|
||||
sv_u.add_keystone(10)
|
||||
check(not mp_u.buy_treasure_unlock("sky_eye") and not sv_u.item_unlocked("sky_eye")
|
||||
and not mp_u.buy_treasure_unlock("ranged_pick"),
|
||||
"条件件误购拒绝(购买件专属——契约 8 分配面)〔秘宝批 V3 反例〕")
|
||||
check(not mp_u.buy_treasure_unlock("twin_loader") and sv_u.keystone_balance() == 10,
|
||||
"余额不足购买拒绝且零副作用(余额 10 < 价 25)〔秘宝批 V3〕")
|
||||
check(mp_u.buy_treasure_unlock("morning_order") and sv_u.item_unlocked("morning_order")
|
||||
and sv_u.keystone_balance() == 0,
|
||||
"钥石购买解锁:余额足→解锁态落档+余额扣减正确(价 10)〔秘宝批 V3〕")
|
||||
check(not mp_u.buy_treasure_unlock("morning_order"),
|
||||
"已解锁重复购买拒绝〔秘宝批 V3〕")
|
||||
check(mp_u.refresh_treasure_unlocks().is_empty() and not sv_u.item_unlocked("ranged_pick"),
|
||||
"条件未达不解锁(远程镐=通关 1 次)〔秘宝批 V4〕")
|
||||
sv_u._data["meta"]["adventure_cleared"] = 1 # 直置存档生涯计数(调试注入口径)
|
||||
check((mp_u.refresh_treasure_unlocks() as Array).has("ranged_pick")
|
||||
and sv_u.item_unlocked("ranged_pick") and mp_u.refresh_treasure_unlocks().is_empty(),
|
||||
"远程镐=adventure_cleared≥1 达标恰解锁一次+重复刷新幂等(C6:通关=打通整局冒险)〔秘宝批 V4〕")
|
||||
sv_u._data["meta"]["boss_kill_count"] = 3
|
||||
check((mp_u.refresh_treasure_unlocks() as Array).has("refresh_license"),
|
||||
"刷新执照=boss_kill_count≥3(阈值读 unlock_requires 行值不硬编)〔秘宝批 V4〕")
|
||||
sv_u._data["meta"]["adventure_cleared"] = 3
|
||||
check(not sv_u.item_unlocked("sky_eye"), "天眼单条件不足不解锁(AND 语义——cleared 3 达 boss 4 未达)〔秘宝批 V4〕")
|
||||
sv_u._data["meta"]["boss_kill_count"] = 4
|
||||
check((mp_u.refresh_treasure_unlocks() as Array).has("sky_eye"),
|
||||
"天眼双条件 AND(通关≥3 且 Boss≥4)达标解锁〔秘宝批 V4〕")
|
||||
sv_u._data["meta"]["vision_used"] = 10
|
||||
check((mp_u.refresh_treasure_unlocks() as Array).has("beacon_network"),
|
||||
"信标网络=真视类工具累计使用≥10(行为计数新域——use_treasure 受理成功累计)〔秘宝批 V4〕")
|
||||
var r_n := 0
|
||||
for st_row in load("res://core/meta_progression.gd").new(_db, sv_r).workshop_status():
|
||||
for st_row in mp_u.treasure_shop_status():
|
||||
r_n += 1 if bool(st_row.get("unlocked", false)) else 0
|
||||
check(r_n == expect_n, "工坊解锁持久(reload 后一致)")
|
||||
# ⑧ 干净档:通关 1 次 → 恰两件阈值 1 道具新解锁(new_unlocks 明细)
|
||||
var sv_w = _meta_new_save(_META_SMOKE_PATHS[3])
|
||||
var mp_w = load("res://core/meta_progression.gd").new(_db, sv_w)
|
||||
var u1: Dictionary = mp_w.settle_adventure({"result": "failed", "cleared": false, "levels_cleared": 1,
|
||||
"stopped_at": 2, "keystone_total": 0, "master_seed": 16})
|
||||
check(u1["new_unlocks"].size() == 2 and sv_w.item_unlocked("probe_dust") and sv_w.item_unlocked("sapper_order")
|
||||
and not sv_w.item_unlocked("loot_compass"), "通关 1 次 → 恰两件阈值 1 道具解锁(new_unlocks 明细正确)")
|
||||
check(r_n == 5, "解锁态持久投影一致(reload 面同源读数;5 件=购买 1+条件 4)〔秘宝批 V4〕")
|
||||
# ⑩ recent_summaries:元素含 boss_drops 副本、最新在前、cap 10
|
||||
var rs: Array = sv.recent_summaries()
|
||||
check(rs.size() >= 5 and String(rs[0].get("result", "")) != "" and rs[0].has("boss_drops")
|
||||
@@ -4681,9 +4695,11 @@ func _meta_settle_and_workshop() -> void:
|
||||
var mp_a = load("res://core/meta_progression.gd").new(_db, sv_a)
|
||||
check(Attunement.slots(_db, sv_a) == 0 and int(mp_a.attune_camp_status()["refresh"]["charges"]) == 0,
|
||||
"白板档同调位求值 = 0(0 起步裁决)且 Y=0(未购无刷新能力——P1 甲初始 0 同构)")
|
||||
check(mp_a.toggle_attunement("nest_counter") == false,
|
||||
"满位禁选(Slots 0 时装配拒绝)")
|
||||
check(mp_a.toggle_attunement("morning_order") == false,
|
||||
"满位禁选(Slots 0 时装配拒绝;兼未解锁拒绝前置——直置解锁后下方装配)〔秘宝批 V5〕")
|
||||
sv_a.add_keystone(200)
|
||||
sv_a.unlock_item("morning_order")
|
||||
sv_a.unlock_item("twin_loader")
|
||||
check(mp_a.buy_attune_refresh() and sv_a.attune_refresh_level() == 1
|
||||
and int(mp_a.attune_camp_status()["refresh"]["charges"]) == 1,
|
||||
"Y 升级购买入账(Lv1 → 每冒险 +1 次刷新)")
|
||||
@@ -4694,10 +4710,12 @@ func _meta_settle_and_workshop() -> void:
|
||||
sv_a.set_tree_level("att_socket1", 1)
|
||||
check(Attunement.slots(_db, sv_a) == 1,
|
||||
"树上点亮同调位 1 → Slots=1(槽位收编树节点——Q2 裁甲)")
|
||||
check(not mp_a.toggle_attunement("sky_eye"),
|
||||
"稀有件入普通格拒绝(件档 rank 3 > 装入位序 1——契约 2 档位格校验)〔秘宝批 V5〕")
|
||||
check(mp_a.toggle_attunement("morning_order") and mp_a.toggle_attunement("twin_loader") == false,
|
||||
"装配持久写入 + 满位禁选(Slots 1 装第二件拒绝)〔秘宝批 V5 换主语〕")
|
||||
check(mp_a.toggle_attunement("morning_order") and mp_a.toggle_attunement("twin_loader"),
|
||||
"卸下恒可再装配(换装语义——装配序数组承载)〔秘宝批换主语;营地换装=冒险开始前合法面,装配不可变只锁运行中〕")
|
||||
check(mp_a.toggle_attunement("morning_order") and mp_a.toggle_attunement("morning_order"),
|
||||
"卸下恒可再装配(自卸自装——营地换装=冒险开始前合法面,装配不可变只锁运行中)〔秘宝批换主语〕")
|
||||
var sv_a2 = _meta_new_save(_META_SMOKE_PATHS[7])
|
||||
sv_a2.set_attunement_equipped(["morning_order"])
|
||||
var mp_a2 = load("res://core/meta_progression.gd").new(_db, sv_a2)
|
||||
|
||||
Reference in new issue
Block a user