@@ -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 )