Files
spellforge/addons/game_designer/attribute_tab.gd
T
joywayerandClaude Opus 5 a0e205a031 feat(attr): 游戏设计器新增「属性」页,字段化编辑 attributes.json
按 CLAUDE.md 编辑器插件规范:控件经 designer_ui 工厂创建、标签中英双语中文在前、
combine 下拉存 key 不存显示串、_save 合并式写回防丢键、UI 在 _ready 而非 _init 构建。

另修:SpinBox 按 step 吸附会引入双精度噪声(0.01 → 0.00999999999476),
直接写回会把噪声固化进 JSON;_collect 用 snappedf(v, 1e-6) 抹平,
使「不改任何值就保存」得到逐位一致的 attributes.json。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 12:05:50 +08:00

71 lines
3.3 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## 属性编辑器标签 — 玩家属性的 base / soft / hard / combine
## 数据:res://data/attributes.json(权威属性定义见 docs/design/numerical_design.md §1.1
@tool
extends VBoxContainer
const UI = preload("res://addons/game_designer/designer_ui.gd")
const PATH = "res://data/attributes.json"
# 显示与存储解耦:LABELS 仅供下拉显示,KEYS 才写进 JSON,二者 index 对齐
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"]
var _data: Dictionary = {}
var _rows: Dictionary = {} # attr_id → {"base": SpinBox, "soft": SpinBox, "hard": SpinBox, "combine": OptionButton}
var _status: Label
func _ready() -> void:
add_child(UI.header("📊 属性编辑器 — 玩家属性 base / soft / hard / combine"))
_load()
var grid := GridContainer.new(); grid.columns = 5; add_child(grid)
for h in ["属性", "基准 base", "软上限 soft", "硬上限 hard", "合成 combine"]:
var l := Label.new(); l.text = h; l.modulate = Color(0.7, 0.8, 1.0)
grid.add_child(l)
for attr_id in ATTR_ORDER:
var d: Dictionary = _data.get(attr_id, {})
grid.add_child(UI.cell_label(String(d.get("display_name", attr_id))))
var sp_base := UI.spin(-99999.0, 99999.0, 0.01, float(d.get("base", 0.0)))
var sp_soft := UI.spin(-99999.0, 99999.0, 0.01, float(d.get("soft", 0.0)))
var sp_hard := UI.spin(-99999.0, 99999.0, 0.01, float(d.get("hard", 0.0)))
var idx: int = COMBINE_KEYS.find(String(d.get("combine", "hybrid")))
var op := UI.opt(COMBINE_LABELS, idx if idx >= 0 else 0)
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())
var tip := Label.new()
tip.text = "hard=0 在 hybrid 下表示不钳制;inverse 的 hard 是「下限」,填 0 即钳到 0(与 hybrid 相反);add_int 拒绝 pct 加成。"
tip.modulate = Color(0.7, 0.7, 0.7)
add_child(tip)
add_child(UI.btn("💾 保存 attributes.json", _save))
_status = UI.status_label(); add_child(_status)
func _load() -> void:
var d = UI.load_json(PATH)
_data = d if d is Dictionary else {}
## SpinBox 按 step 吸附会引入双精度噪声(0.01 → 0.00999999999476),
## 直接写回会把噪声固化进 JSON;抹到 1e-6 精度,远细于本页任何有意义的调参粒度。
static func _clean(v: float) -> float:
return snappedf(v, 0.000001)
## 表单 → 字典(合并式:以 _data 为基底保留未知键)
func _collect() -> Dictionary:
var out: Dictionary = _data.duplicate(true)
for attr_id in ATTR_ORDER:
var r: Dictionary = _rows.get(attr_id, {})
if r.is_empty():
continue
var entry: Dictionary = out.get(attr_id, {}).duplicate(true)
entry["base"] = _clean(r["base"].value)
entry["soft"] = _clean(r["soft"].value)
entry["hard"] = _clean(r["hard"].value)
entry["combine"] = COMBINE_KEYS[r["combine"].selected] # 存 key,不存双语显示串
out[attr_id] = entry
return out
func _save() -> void:
if UI.save_json(PATH, _collect()):
UI.set_status(_status, "💾 已保存(重启 F5 生效)")
else:
UI.set_status(_status, "✗ 保存失败", true)