From 8680e8c0ef73298d722b03c796b3619808832048 Mon Sep 17 00:00:00 2001 From: Joywayer Date: Mon, 3 Aug 2026 12:51:23 +0800 Subject: [PATCH] =?UTF-8?q?fix(designer):=20=E5=B1=9E=E6=80=A7=E9=A1=B5?= =?UTF-8?q?=E4=BB=85=E4=B8=BA=E5=B7=B2=E6=9C=89=20shop=20=E6=AE=B5?= =?UTF-8?q?=E7=9A=84=E5=B1=9E=E6=80=A7=E5=86=99=E5=9B=9E=E8=AF=A5=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit _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 段的属性不再被写回该段,其余属性不受影响。 --- addons/game_designer/attribute_tab.gd | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/addons/game_designer/attribute_tab.gd b/addons/game_designer/attribute_tab.gd index 6dd12db..b92c148 100644 --- a/addons/game_designer/attribute_tab.gd +++ b/addons/game_designer/attribute_tab.gd @@ -125,7 +125,13 @@ func _collect() -> Dictionary: # 存 key,不存双语显示串;selected 恒为 0..2(UI.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))