diff --git a/addons/game_designer/designer_panel.gd b/addons/game_designer/designer_panel.gd index d8d504c..a191bab 100644 --- a/addons/game_designer/designer_panel.gd +++ b/addons/game_designer/designer_panel.gd @@ -9,7 +9,7 @@ func _init() -> void: add_child(tabs) # 法术(复用现有法术编辑器停靠脚本) - _add_tab(tabs, "✨ 法术", preload("res://addons/spell_editor/spell_dock.gd").new()) + _add_tab(tabs, "✨ 法术", preload("res://addons/game_designer/spell_tab.gd").new()) _add_tab(tabs, "👾 敌人", preload("res://addons/game_designer/enemy_tab.gd").new()) _add_tab(tabs, "🌊 波次", preload("res://addons/game_designer/wave_tab.gd").new()) _add_tab(tabs, "🪄 Core", preload("res://addons/game_designer/core_tab.gd").new()) diff --git a/addons/spell_editor/plugin.cfg b/addons/spell_editor/plugin.cfg deleted file mode 100644 index 2e569b4..0000000 --- a/addons/spell_editor/plugin.cfg +++ /dev/null @@ -1,6 +0,0 @@ -[plugin] -name="Spell Editor (法术编辑器)" -description="可视化法术编辑器:策划/开发无需改代码即可新增、编辑、删除法术,保存为 res://data/spells.json,游戏运行时自动加载。" -author="Base Games" -version="1.0.0" -script="spell_editor.gd" diff --git a/addons/spell_editor/spell_dock.gd b/addons/spell_editor/spell_dock.gd deleted file mode 100644 index 6813815..0000000 --- a/addons/spell_editor/spell_dock.gd +++ /dev/null @@ -1,214 +0,0 @@ -## 法术编辑器停靠面板(@tool)— 策划/开发可视化新增·编辑·删除法术 -## 数据持久化到 res://data/spells.json,游戏运行时由 SpellRegistry 加载 -@tool -extends VBoxContainer - -const DATA_PATH: String = "res://data/spells.json" -const TYPE_NAMES: Array = ["ACTION", "MODIFIER", "TRIGGER", "LOGIC"] - -## meta 模板(按类型“插入模板”用) -const META_TEMPLATES: Dictionary = { - 0: '{ "base_damage": 5.0, "speed": 350.0, "lifetime": 4.0, "radius": 6.0, "damage_type": 0, "mana_cost": 5, "shop_cost": 20 }', - 1: '{ "damage_add": 10.0, "mana_cost": 10, "shop_cost": 20 }', - 2: '{ "base_damage": 3.0, "speed": 250.0, "lifetime": 4.0, "radius": 6.0, "damage_type": 0, "mana_cost": 10, "shop_cost": 30 }', - 3: '{ "logic_op": "every_n_shots", "n": 3, "reg": 0, "mana_cost": 0, "shop_cost": 24 }', -} - -var _spells: Dictionary = {} # { id: {type, display_name, description, element_tags, meta} } -var _cur_id: String = "" - -# UI 引用 -var _list: ItemList -var _id_edit: LineEdit -var _type_opt: OptionButton -var _name_edit: LineEdit -var _desc_edit: LineEdit -var _tags_edit: LineEdit -var _meta_edit: TextEdit -var _status: Label - -func _ready() -> void: - _build_ui() - _load_data() - _refresh_list() - -func _build_ui() -> void: - custom_minimum_size = Vector2(300, 400) - add_theme_constant_override("separation", 4) - - var title := Label.new() - title.text = "✨ 法术编辑器" - title.add_theme_font_size_override("font_size", 16) - add_child(title) - - # 法术列表 - _list = ItemList.new() - _list.custom_minimum_size = Vector2(0, 120) - _list.size_flags_vertical = Control.SIZE_EXPAND_FILL - _list.item_selected.connect(_on_select) - add_child(_list) - - var row := HBoxContainer.new() - add_child(row) - row.add_child(_btn("➕ 新增", _on_new)) - row.add_child(_btn("⧉ 复制", _on_dup)) - row.add_child(_btn("🗑 删除", _on_delete)) - - add_child(HSeparator.new()) - - # 表单 - _id_edit = _field("ID(唯一)") - _type_opt = OptionButton.new() - for n in TYPE_NAMES: - _type_opt.add_item(n) - _labeled("类型", _type_opt) - _name_edit = _field("显示名(tr 键或文本)") - _desc_edit = _field("描述(tr 键或文本)") - _tags_edit = _field("元素标签(逗号分隔,如 tag:water)") - - var meta_lbl := Label.new() - meta_lbl.text = "meta(JSON,因类型而异)" - add_child(meta_lbl) - _meta_edit = TextEdit.new() - _meta_edit.custom_minimum_size = Vector2(0, 90) - _meta_edit.placeholder_text = '{ "base_damage": 5.0, ... }' - add_child(_meta_edit) - add_child(_btn("插入该类型 meta 模板", _on_template)) - - add_child(HSeparator.new()) - - var save_row := HBoxContainer.new() - add_child(save_row) - save_row.add_child(_btn("✓ 应用到列表", _on_apply)) - save_row.add_child(_btn("💾 保存文件", _on_save_file)) - save_row.add_child(_btn("↻ 重载", func(): _load_data(); _refresh_list(); _set_status("已重载"))) - - _status = Label.new() - _status.add_theme_font_size_override("font_size", 11) - _status.modulate = Color(0.6, 0.9, 0.6) - add_child(_status) - -func _btn(text: String, cb: Callable) -> Button: - var b := Button.new() - b.text = text - b.pressed.connect(cb) - return b - -func _field(placeholder: String) -> LineEdit: - var le := LineEdit.new() - le.placeholder_text = placeholder - add_child(le) - return le - -func _labeled(label: String, ctrl: Control) -> void: - var l := Label.new() - l.text = label - add_child(l) - add_child(ctrl) - -# ── 数据 ──────────────────────────────────────────────────── -func _load_data() -> void: - _spells = {} - if FileAccess.file_exists(DATA_PATH): - var parsed = JSON.parse_string(FileAccess.get_file_as_string(DATA_PATH)) - if parsed is Dictionary: - _spells = parsed - -func _refresh_list() -> void: - _list.clear() - var ids: Array = _spells.keys() - ids.sort() - for sid in ids: - var t: int = int(_spells[sid].get("type", 0)) - _list.add_item("[%s] %s" % [TYPE_NAMES[clampi(t, 0, 3)], sid]) - _list.set_item_metadata(_list.item_count - 1, sid) - -func _on_select(idx: int) -> void: - _cur_id = String(_list.get_item_metadata(idx)) - var d: Dictionary = _spells.get(_cur_id, {}) - _id_edit.text = _cur_id - _type_opt.selected = clampi(int(d.get("type", 0)), 0, 3) - _name_edit.text = String(d.get("display_name", "")) - _desc_edit.text = String(d.get("description", "")) - _tags_edit.text = ",".join(d.get("element_tags", [])) - _meta_edit.text = JSON.stringify(d.get("meta", {}), " ") - -func _on_apply() -> void: - var sid: String = _id_edit.text.strip_edges() - if sid == "": - _set_status("✗ ID 不能为空", true); return - var meta = JSON.parse_string(_meta_edit.text) - if not (meta is Dictionary): - _set_status("✗ meta 不是合法 JSON 对象", true); return - var tags: Array = [] - for t in _tags_edit.text.split(",", false): - var s: String = t.strip_edges() - if s != "": - tags.append(s) - # 改了 ID:移除旧键 - if _cur_id != "" and _cur_id != sid and _spells.has(_cur_id): - _spells.erase(_cur_id) - _spells[sid] = { - "type": _type_opt.selected, - "display_name": _name_edit.text, - "description": _desc_edit.text, - "element_tags": tags, - "meta": meta, - } - _cur_id = sid - _refresh_list() - _set_status("✓ 已应用『%s』到列表(记得保存文件)" % sid) - -func _on_save_file() -> void: - DirAccess.make_dir_recursive_absolute("res://data") - var f := FileAccess.open(DATA_PATH, FileAccess.WRITE) - if f: - f.store_string(JSON.stringify(_spells, " ")) - f.close() - _set_status("💾 已保存 %d 条法术 → %s" % [_spells.size(), DATA_PATH]) - else: - _set_status("✗ 写入失败", true) - -func _on_new() -> void: - var base := "new_spell" - var sid := base - var i := 1 - while _spells.has(sid): - sid = "%s_%d" % [base, i]; i += 1 - _spells[sid] = {"type": 0, "display_name": sid, "description": "", - "element_tags": [], "meta": JSON.parse_string(META_TEMPLATES[0])} - _refresh_list() - _select_id(sid) - -func _on_dup() -> void: - if _cur_id == "" or not _spells.has(_cur_id): - return - var sid := _cur_id + "_copy" - var i := 1 - while _spells.has(sid): - sid = "%s_copy%d" % [_cur_id, i]; i += 1 - _spells[sid] = _spells[_cur_id].duplicate(true) - _refresh_list() - _select_id(sid) - -func _on_delete() -> void: - if _cur_id != "" and _spells.has(_cur_id): - _spells.erase(_cur_id) - _cur_id = "" - _refresh_list() - _set_status("已删除(记得保存文件)") - -func _on_template() -> void: - _meta_edit.text = String(META_TEMPLATES.get(_type_opt.selected, "{}")) - -func _select_id(sid: String) -> void: - for i in _list.item_count: - if String(_list.get_item_metadata(i)) == sid: - _list.select(i) - _on_select(i) - return - -func _set_status(msg: String, is_err: bool = false) -> void: - if _status: - _status.text = msg - _status.modulate = Color(1.0, 0.5, 0.5) if is_err else Color(0.6, 0.9, 0.6) diff --git a/addons/spell_editor/spell_dock.gd.uid b/addons/spell_editor/spell_dock.gd.uid deleted file mode 100644 index a95158d..0000000 --- a/addons/spell_editor/spell_dock.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dkk2bspn83h8s diff --git a/addons/spell_editor/spell_editor.gd b/addons/spell_editor/spell_editor.gd deleted file mode 100644 index 15ec956..0000000 --- a/addons/spell_editor/spell_editor.gd +++ /dev/null @@ -1,17 +0,0 @@ -## 法术编辑器插件入口(EditorPlugin) -## 在编辑器左下停靠区添加「法术编辑器」面板。 -@tool -extends EditorPlugin - -var _dock: Control = null - -func _enter_tree() -> void: - _dock = preload("res://addons/spell_editor/spell_dock.gd").new() - _dock.name = "法术编辑器" - add_control_to_dock(EditorPlugin.DOCK_SLOT_LEFT_BR, _dock) - -func _exit_tree() -> void: - if _dock: - remove_control_from_docks(_dock) - _dock.free() - _dock = null diff --git a/addons/spell_editor/spell_editor.gd.uid b/addons/spell_editor/spell_editor.gd.uid deleted file mode 100644 index e948aca..0000000 --- a/addons/spell_editor/spell_editor.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://qmds48f8kkm0 diff --git a/project.godot b/project.godot index 069bc73..df97170 100644 --- a/project.godot +++ b/project.godot @@ -65,7 +65,7 @@ project/assembly_name="Rogue" [editor_plugins] -enabled=PackedStringArray("res://addons/game_designer/plugin.cfg", "res://addons/godot_mcp/plugin.cfg", "res://addons/spell_editor/plugin.cfg") +enabled=PackedStringArray("res://addons/game_designer/plugin.cfg", "res://addons/godot_mcp/plugin.cfg") [gui]