## 状态效果编辑器标签 — DoT/层数/持续时间/VFX @tool extends VBoxContainer const UI = preload("res://addons/game_designer/designer_ui.gd") const PATH = "res://data/status_effects.json" const STACK = ["刷新 REFRESH", "叠层 INTENSITY", "独立 INDEPENDENT"] var _data: Dictionary = {} var _cur: String = "" var _id: SpinBox var _name: LineEdit var _dur: SpinBox var _tick: SpinBox var _stack: OptionButton var _max: SpinBox var _dot: SpinBox var _vfx: LineEdit var _combo: CheckBox var _list: ItemList var _status: Label func _ready() -> void: add_child(UI.header("🔥 状态效果 — 燃烧/中毒/连击等 DoT 与叠层")) _load() _list = ItemList.new() _list.custom_minimum_size = Vector2(0, 80) _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(整数,1=燃烧 3=中毒…)", UI.spin(1, 999, 1, 9)) _name = _row("名称", UI.line("")) _dur = _row("持续时间(s)", UI.spin(0.1, 999, 0.5, 3.0)) _tick = _row("跳字间隔(s)", UI.spin(0.1, 999, 0.5, 1.0)) _stack = _row("叠层模式", UI.opt(STACK, 0)) _max = _row("最大层数(0=无限)", UI.spin(0, 999, 1, 1)) _dot = _row("每跳伤害", UI.spin(0, 9999, 0.5, 0.0)) _vfx = _row("VFX ID", UI.line("", "status_burn")) _combo = CheckBox.new(); _combo.text = "连击标记追踪器" add_child(_combo) add_child(HSeparator.new()) var srow := HBoxContainer.new(); add_child(srow) srow.add_child(UI.btn("✓应用", _on_apply)) srow.add_child(UI.btn("💾保存 status_effects.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 k in ids: _list.add_item("[%s] %s" % [k, _data[k].get("name", "")]) _list.set_item_metadata(_list.item_count - 1, k) func _on_select(idx: int) -> void: _cur = String(_list.get_item_metadata(idx)) var d: Dictionary = _data[_cur] _id.value = float(int(_cur)) _name.text = String(d.get("name", "")) _dur.value = float(d.get("duration", 3.0)) _tick.value = float(d.get("tick_interval", 1.0)) _stack.selected = int(d.get("stack_mode", 0)) _max.value = float(d.get("max_stacks", 1)) _dot.value = float(d.get("dot_damage", 0.0)) _vfx.text = String(d.get("vfx_id", "")) _combo.button_pressed = bool(d.get("is_combo_tracker", false)) func _on_apply() -> void: var sid := str(int(_id.value)) if _cur != "" and _cur != sid and _data.has(_cur): _data.erase(_cur) _data[sid] = { "name": _name.text, "duration": _dur.value, "tick_interval": _tick.value, "stack_mode": _stack.selected, "max_stacks": int(_max.value), "dot_damage": _dot.value, "dot_damage_type": 0, "vfx_id": _vfx.text, "is_combo_tracker": _combo.button_pressed, } _cur = sid _refresh() UI.set_status(_status, "✓ 已应用 ID %s(记得保存)" % sid) func _on_new() -> void: var sid := 9 while _data.has(str(sid)): sid += 1 _data[str(sid)] = {"name": "新状态", "duration": 3.0, "tick_interval": 1.0, "stack_mode": 0, "max_stacks": 1, "dot_damage": 0.0, "dot_damage_type": 0, "vfx_id": "", "is_combo_tracker": false} _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 个状态(重启 F5 生效)" % _data.size()) else: UI.set_status(_status, "✗ 保存失败", true)