fix(designer): 属性页仅为已有 shop 段的属性写回该段

_collect() 此前无条件为 ATTR_ORDER 里每个属性写出 entry["shop"](因为 _shop_rows
在 _ready() 里对全部 4 个属性都无条件建了控件行,哪怕原属性没有 shop 段也会以 0 值
占位)。若某属性本来没有 shop 段,保存会写出 price_base=0 的空段,PriceFormula 会把
0 价地板钳到 1G,等于把一个零效果属性变成 1G 无限购买,直接反转货架 C「无 shop 段
即不可售」的核心不变量。

加一道 had_shop 判定:只有 _data 里原本就带 shop 段的属性才写回该段;已有段的属性
继续沿用原有的 duplicate(true) 合并式防丢键行为不变。当前 4 个 ATTR_ORDER 属性都带
shop 段,故此路径今天不可达,需设计器保存才会触发;已用手工构造的 _data/_rows/_shop_rows
调用 _collect() 验证:摘掉 shop 段的属性不再被写回该段,其余属性不受影响。
This commit is contained in:
2026-08-03 12:51:23 +08:00
parent 5889b3c1e5
commit 8680e8c0ef
+7 -1
View File
@@ -125,7 +125,13 @@ func _collect() -> Dictionary:
# 存 key,不存双语显示串;selected 恒为 0..2UI.opt 构造时已 clampi,本页此后不再赋值)
entry["combine"] = COMBINE_KEYS[r["combine"].selected]
var r2: Dictionary = _shop_rows.get(attr_id, {})
if not r2.is_empty():
# 「无 shop 段即不可售」是货架 C 的核心不变量(ShopManager.get_sellable_attrs 靠它筛选
# 可售集合)。_shop_rows 对 ATTR_ORDER 里每个属性都无条件建了控件行(哪怕原本没有 shop
# 段,行以 0 值占位),所以不能只凭 r2 非空就写回;必须再确认 _data 里原本就有这一段,
# 否则会把 price_base=0 的空段写进 JSON——PriceFormula 把 0 价地板钳到 1G
# 一个零效果属性从此以 1G 可无限购买,直接违反上述不变量。
var had_shop: bool = _data.get(attr_id, {}).get("shop", null) is Dictionary
if not r2.is_empty() and had_shop:
var shop_entry: Dictionary = entry.get("shop", {}).duplicate(true)
shop_entry["mode"] = MODE_KEYS[r2["mode"].selected] # 存 key 不存显示串
shop_entry["step"] = _clean(float(r2["step"].value))