From d69389e09d563803317f391ed4048454d202f858 Mon Sep 17 00:00:00 2001 From: Joywayer Date: Mon, 3 Aug 2026 11:56:55 +0800 Subject: [PATCH] =?UTF-8?q?feat(shop):=20=E8=AE=BE=E8=AE=A1=E5=99=A8?= =?UTF-8?q?=E3=80=8C=E5=B1=9E=E6=80=A7=E3=80=8D=E9=A1=B5=E6=89=A9=E5=B1=95?= =?UTF-8?q?=20shop=20=E6=AE=B5=E4=BA=94=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mode/curve 下拉存 key 不存双语显示串;UI.spin 的 min 取 0 以免 Range 的 抵消误差把浮点噪声写进权威数据文件(E3-① 实测);price_base 存 float 而非 int——JSON.parse_string 对所有 JSON 数字一律解析为 float,存 int 会使往返逐位比较恒假失败,且与 price_formula.gd 的 float() 读取一致。 Co-Authored-By: Claude Opus 5 --- addons/game_designer/attribute_tab.gd | 39 ++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/addons/game_designer/attribute_tab.gd b/addons/game_designer/attribute_tab.gd index 1890120..6dd12db 100644 --- a/addons/game_designer/attribute_tab.gd +++ b/addons/game_designer/attribute_tab.gd @@ -9,9 +9,14 @@ const PATH = "res://data/attributes.json" const COMBINE_KEYS = ["hybrid", "inverse", "add_int"] const COMBINE_LABELS = ["混合 hybrid", "反向 inverse", "整数加 add_int"] const ATTR_ORDER = ["cpu_limit", "move_speed", "cast_delay_mod", "hp_max"] +const MODE_KEYS = ["flat", "pct"] +const MODE_LABELS = ["固定量 flat", "百分比 pct"] +const CURVE_KEYS = ["geometric", "linear", "flat"] +const CURVE_LABELS = ["几何 geometric", "线性 linear", "固定 flat"] var _data: Dictionary = {} var _rows: Dictionary = {} # attr_id → {"base": SpinBox, "soft": SpinBox, "hard": SpinBox, "combine": OptionButton} +var _shop_rows: Dictionary = {} # attr_id → {"mode","step","price_base","price_growth","curve"} 控件 var _status: Label var _loaded: bool = false # 加载失败时禁止写回,见 _save @@ -49,8 +54,28 @@ func _ready() -> void: grid.add_child(sp_base); grid.add_child(sp_soft); grid.add_child(sp_hard); grid.add_child(op) _rows[attr_id] = {"base": sp_base, "soft": sp_soft, "hard": sp_hard, "combine": op} add_child(HSeparator.new()) + add_child(UI.header("🛒 货架 C — 可售配置(无 shop 段 = 不可购买)")) + var g2 := GridContainer.new(); g2.columns = 6; add_child(g2) + for h2 in ["属性", "增量类型 mode", "每级 step", "首价 price_base", "涨幅 growth", "曲线 curve"]: + var l2 := Label.new(); l2.text = h2; l2.modulate = Color(0.7, 0.8, 1.0) + g2.add_child(l2) + for attr_id2 in ATTR_ORDER: + var sp: Dictionary = _data.get(attr_id2, {}).get("shop", {}) + g2.add_child(UI.cell_label(String(_data.get(attr_id2, {}).get("display_name", attr_id2)))) + var mi: int = MODE_KEYS.find(String(sp.get("mode", "flat"))) + var op_mode := UI.opt(MODE_LABELS, mi if mi >= 0 else 0) + var sp_step := UI.spin(0.0, 99999.0, 0.01, float(sp.get("step", 0.0))) + var sp_pbase := UI.spin(0.0, 99999.0, 1.0, float(sp.get("price_base", 0.0))) + var sp_grow := UI.spin(0.0, 99999.0, 0.01, float(sp.get("price_growth", 1.0))) + var ci: int = CURVE_KEYS.find(String(sp.get("curve", "geometric"))) + var op_curve := UI.opt(CURVE_LABELS, ci if ci >= 0 else 0) + g2.add_child(op_mode); g2.add_child(sp_step); g2.add_child(sp_pbase) + g2.add_child(sp_grow); g2.add_child(op_curve) + _shop_rows[attr_id2] = {"mode": op_mode, "step": sp_step, + "price_base": sp_pbase, "price_growth": sp_grow, "curve": op_curve} + add_child(HSeparator.new()) var tip := Label.new() - tip.text = "hard=0 在 hybrid / add_int 下表示不钳制;inverse 的 hard 是「下限」,填 0 即钳到 0(与 hybrid 相反);add_int 拒绝 pct 加成;soft 本期不参与计算(留给货架 C)。" + tip.text = "hard=0 在 hybrid / add_int 下表示不钳制;inverse 的 hard 是「下限」,填 0 即钳到 0(与 hybrid 相反);add_int 拒绝 pct 加成;soft 本期不参与计算(留给货架 C)。cpu_limit 的 mode 必须是 flat(add_int 拒绝 pct);shop 段缺失即该属性不可购买。" # dock 的 ScrollContainer 禁用横向滚动,不换行的话这句会被右侧裁掉(正是最需要看到的半句) tip.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART tip.modulate = Color(0.7, 0.7, 0.7) @@ -99,6 +124,18 @@ func _collect() -> Dictionary: entry["hard"] = _clean(r["hard"].value) # 存 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(): + 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)) + # 注意:不用 int()——JSON.parse_string 对所有 JSON 数字(含无小数点的字面量, + # 如源文件的 "price_base": 120)一律解析为 float,若此处存 int 则往返比较时 + # str(120) != str(120.0) 恒假失败,与 price_formula.gd 的 float() 读取方式一致 + shop_entry["price_base"] = _clean(float(r2["price_base"].value)) + shop_entry["price_growth"] = _clean(float(r2["price_growth"].value)) + shop_entry["curve"] = CURVE_KEYS[r2["curve"].selected] + entry["shop"] = shop_entry out[attr_id] = entry return out