Files
spellforge/addons/game_designer/core_tab.gd
T
joywayerandClaude Opus 4.8 7bc13a7be6 feat(mana): 游戏设计器支持 mana 字段(补 Mana MVP 配套)
- Core 编辑器加「蓝上限/回蓝」两栏(_on_select 读 / _on_apply 写 / _on_new 默认),
  修复此前编辑 Core 保存会静默丢失 mana_max/mana_regen 的数据丢失洞
- 法术编辑器 4 个 meta 模板加 mana_cost,新法术默认有合理蓝耗
- handbook 07 补 Core 蓝池字段 + 法术 mana_cost 说明

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 10:31:01 +08:00

134 lines
4.8 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.
## Core(法杖)编辑器标签 — 槽位/拓扑/CPU/施法间隔/特性/矩阵·电路结构
@tool
extends VBoxContainer
const UI = preload("res://addons/game_designer/designer_ui.gd")
const PATH = "res://data/cores.json"
const TOPO = ["LINEAR 线性", "MATRIX 矩阵", "CIRCUIT 电路"]
const FEAT = ["无", "PERSISTENT_MEMORY 记忆", "DUAL_STREAM 双流", "ALWAYS_CAST_LAST", "SHUFFLE_DECK", "INFINITE_SPELLS"]
var _data: Dictionary = {}
var _cur: String = ""
var _list: ItemList
var _id: LineEdit
var _name: LineEdit
var _slots: SpinBox
var _topo: OptionButton
var _cpu: SpinBox
var _interval: SpinBox
var _mana_max: SpinBox
var _mana_regen: SpinBox
var _feat: OptionButton
var _rows: SpinBox
var _cols: SpinBox
var _edges: TextEdit
var _status: Label
func _ready() -> void:
add_child(UI.header("🪄 Core 编辑器 — 法杖拓扑与特性"))
_load()
_list = ItemList.new()
_list.custom_minimum_size = Vector2(0, 90)
_list.item_selected.connect(_on_select)
add_child(_list)
var brow := HBoxContainer.new(); add_child(brow)
brow.add_child(UI.btn("➕新增", _on_new))
brow.add_child(UI.btn("🗑删除", _on_delete))
add_child(HSeparator.new())
_id = _row("ID", UI.line(""))
_name = _row("显示名", UI.line("", "tr 键或文本"))
_slots = _row("槽位数", UI.spin(1, 20, 1, 5))
_topo = _row("拓扑", UI.opt(TOPO, 0))
_cpu = _row("CPU 上限", UI.spin(1, 40, 1, 5))
_interval = _row("施法间隔(s)", UI.spin(0.05, 5, 0.05, 0.5))
_mana_max = _row("蓝上限(Mana Max)", UI.spin(0, 1000, 5, 100))
_mana_regen = _row("回蓝/秒(Mana Regen)", UI.spin(0, 100, 0.5, 5))
_feat = _row("特性", UI.opt(FEAT, 0))
_rows = _row("矩阵行(MATRIX)", UI.spin(1, 8, 1, 1))
_cols = _row("矩阵列(MATRIX)", UI.spin(1, 16, 1, 5))
add_child(Label.new())
var el := Label.new(); el.text = "电路边(CIRCUITJSON)"; add_child(el)
_edges = TextEdit.new(); _edges.custom_minimum_size = Vector2(0, 50)
_edges.placeholder_text = '[{"from":0,"to":1},{"from":0,"to":2}]'
add_child(_edges)
add_child(HSeparator.new())
var srow := HBoxContainer.new(); add_child(srow)
srow.add_child(UI.btn("✓应用", _on_apply))
srow.add_child(UI.btn("💾保存 cores.json", _save))
_status = UI.status_label(); add_child(_status)
_refresh()
func _row(label: String, ctrl: Control) -> Control:
var h := HBoxContainer.new()
h.add_child(UI.cell_label(label))
h.add_child(ctrl)
add_child(h)
return ctrl
func _load() -> void:
var d = UI.load_json(PATH)
_data = d if d is Dictionary else {}
func _refresh() -> void:
_list.clear()
var ids: Array = _data.keys(); ids.sort()
for cid in ids:
_list.add_item("%s%s" % [cid, _data[cid].get("display_name", "")])
_list.set_item_metadata(_list.item_count - 1, cid)
func _on_select(idx: int) -> void:
_cur = String(_list.get_item_metadata(idx))
var d: Dictionary = _data.get(_cur, {})
_id.text = _cur
_name.text = String(d.get("display_name", ""))
_slots.value = float(d.get("slot_count", 5))
_topo.selected = int(d.get("topology", 0))
_cpu.value = float(d.get("cpu_limit", 5))
_interval.value = float(d.get("cast_interval", 0.5))
_mana_max.value = float(d.get("mana_max", 100))
_mana_regen.value = float(d.get("mana_regen", 5))
_feat.selected = int(d.get("feature_tags", 0))
_rows.value = float(d.get("grid_rows", 1))
_cols.value = float(d.get("grid_cols", 5))
_edges.text = JSON.stringify(d.get("edges", []))
func _on_apply() -> void:
var cid: String = _id.text.strip_edges()
if cid == "":
UI.set_status(_status, "✗ ID 不能为空", true); return
var edges = JSON.parse_string(_edges.text) if _edges.text.strip_edges() != "" else []
if not (edges is Array):
edges = []
if _cur != "" and _cur != cid and _data.has(_cur):
_data.erase(_cur)
_data[cid] = {
"display_name": _name.text, "slot_count": int(_slots.value),
"topology": _topo.selected, "cpu_limit": int(_cpu.value),
"cast_interval": _interval.value, "feature_tags": _feat.selected,
"mana_max": int(_mana_max.value), "mana_regen": _mana_regen.value,
"grid_rows": int(_rows.value), "grid_cols": int(_cols.value), "edges": edges,
}
_cur = cid
_refresh()
UI.set_status(_status, "✓ 已应用『%s』(记得保存)" % cid)
func _on_new() -> void:
var cid := "new_core"
var i := 1
while _data.has(cid): cid = "new_core_%d" % i; i += 1
_data[cid] = {"display_name": cid, "slot_count": 5, "topology": 0,
"cpu_limit": 5, "cast_interval": 0.5, "feature_tags": 0,
"mana_max": 100, "mana_regen": 5, "edges": []}
_refresh()
func _on_delete() -> void:
if _cur != "" and _data.has(_cur):
_data.erase(_cur); _cur = ""; _refresh()
UI.set_status(_status, "已删除(记得保存)")
func _save() -> void:
if UI.save_json(PATH, _data):
UI.set_status(_status, "💾 已保存 %d 个 Core(重启 F5 生效)" % _data.size())
else:
UI.set_status(_status, "✗ 保存失败", true)