111 lines
4.3 KiB
GDScript
111 lines
4.3 KiB
GDScript
@tool
|
|
extends Node
|
|
## UndoRedo apply/revert methods, invoked by name on undo/redo — possibly long
|
|
## after the originating command has returned.
|
|
##
|
|
## These live on a persistent, server-owned Node (reached via server.undo_helpers)
|
|
## because EditorUndoRedoManager.add_do_method / add_undo_method take an
|
|
## (object, method) pair and route undo by that object's editor context — unlike
|
|
## the base UndoRedo class, which takes a Callable. MCPToolkitUndoRedoAction
|
|
## unpacks each Callable into (object, method) to feed the editor-manager form, so
|
|
## the bound object must be a real, named method host. Because the call is
|
|
## deferred, that host must outlive the command — hence this dedicated Node.
|
|
##
|
|
## NOT a pre-Callable workaround. Do NOT inline these into command files or bind
|
|
## them on an ad-hoc / transient instance: a freed host means a broken undo.
|
|
|
|
|
|
func _write_file_silent(path: String, content: String) -> void:
|
|
var file := FileAccess.open(path, FileAccess.WRITE)
|
|
if file == null:
|
|
push_warning("[MCPServer] UndoRedo write of %s failed (err %d)" % [path, FileAccess.get_open_error()])
|
|
return
|
|
file.store_string(content)
|
|
file.close()
|
|
|
|
|
|
func _delete_file_silent(path: String) -> void:
|
|
if not FileAccess.file_exists(path):
|
|
return
|
|
var error := DirAccess.remove_absolute(path)
|
|
if error != OK:
|
|
push_warning("[MCPServer] UndoRedo delete of %s failed (err %d)" % [path, error])
|
|
|
|
|
|
func _set_owner_recursive(node: Node, owner: Node) -> void:
|
|
node.set_owner(owner)
|
|
for child in node.get_children():
|
|
_set_owner_recursive(child, owner)
|
|
|
|
|
|
func _animation_remove_key_at(animation: Animation, track_index: int, time: float) -> void:
|
|
var key_index := animation.track_find_key(track_index, time, Animation.FIND_MODE_EXACT)
|
|
if key_index != -1:
|
|
animation.track_remove_key(track_index, key_index)
|
|
|
|
|
|
func _animation_insert_key_silent(animation: Animation, track_index: int, time: float, value) -> void:
|
|
animation.track_insert_key(track_index, time, value)
|
|
|
|
|
|
func _sm_remove_transition_by_endpoints(sm: AnimationNodeStateMachine, from: String, to: String) -> void:
|
|
for i in range(sm.get_transition_count()):
|
|
if str(sm.get_transition_from(i)) == from and str(sm.get_transition_to(i)) == to:
|
|
sm.remove_transition_by_index(i)
|
|
return
|
|
|
|
|
|
func _tilemap_apply_batch(node: Node, layer: int, cells: Array) -> void:
|
|
var is_layer := node.is_class("TileMapLayer") # dynamic — avoids parse error on < 4.3
|
|
for cell in cells:
|
|
var coord := Vector2i(int(cell["x"]), int(cell["y"]))
|
|
var source_id := int(cell["source_id"])
|
|
var atlas := Vector2i(int(cell["atlas_x"]), int(cell["atlas_y"]))
|
|
var alternative := int(cell.get("alternative_tile", 0))
|
|
if is_layer:
|
|
node.set_cell(coord, source_id, atlas, alternative)
|
|
else:
|
|
(node as TileMap).set_cell(layer, coord, source_id, atlas, alternative)
|
|
|
|
|
|
## Set a compound-path property (colon-chain or slash) on a node.
|
|
## Used by UndoRedo for compound path undo/redo — navigates sub-resources
|
|
## the same way set_property_compound does, but without coercion or readback.
|
|
func compound_set(node: Object, property_name: String, value: Variant) -> void:
|
|
if ":" not in property_name:
|
|
node.set(property_name, value)
|
|
return
|
|
var parts := property_name.split(":")
|
|
# Single-colon: try slash-path first.
|
|
if parts.size() == 2:
|
|
var slash_path := parts[0] + "/" + parts[1]
|
|
node.set(slash_path, value)
|
|
if node.get(slash_path) != null:
|
|
return
|
|
# Navigate to sub-resource and set directly.
|
|
var target: Object = node
|
|
for i in range(parts.size() - 1):
|
|
var sub = target.get(parts[i])
|
|
if sub == null or not (sub is Object):
|
|
return
|
|
target = sub
|
|
var final_prop := parts[-1]
|
|
if final_prop.begins_with("shader_parameter/") and target is ShaderMaterial:
|
|
(target as ShaderMaterial).set_shader_parameter(
|
|
final_prop.trim_prefix("shader_parameter/"), value)
|
|
else:
|
|
target.set(final_prop, value)
|
|
|
|
|
|
func _tilemap_restore_batch(node: Node, layer: int, before_state: Array) -> void:
|
|
var is_layer := node.is_class("TileMapLayer") # dynamic — avoids parse error on < 4.3
|
|
for state in before_state:
|
|
var coord: Vector2i = state["coord"]
|
|
var source_id := int(state["source_id"])
|
|
var atlas: Vector2i = state["atlas"]
|
|
var alternative := int(state["alternative_tile"])
|
|
if is_layer:
|
|
node.set_cell(coord, source_id, atlas, alternative)
|
|
else:
|
|
(node as TileMap).set_cell(layer, coord, source_id, atlas, alternative)
|