Skip to content
195 changes: 195 additions & 0 deletions src/Autoload/Import.gd
Original file line number Diff line number Diff line change
Expand Up @@ -222,3 +222,198 @@ func import_patterns(priority_ordered_search_path: Array) -> void:
image.convert(Image.FORMAT_RGBA8)
var tooltip_name = pattern.get_basename()
Global.patterns_popup.add(image, tooltip_name)


## Gets frame [member indices] of [member from_project] and dumps it in the current project.
func copy_frames_to_current_project(
from_project: Project, indices: Array, destination: int, new_tag_from: AnimationTag = null
) -> void:
var project: Project = Global.current_project
if !project:
return
if from_project == project: ## If we are copying tags within project
Global.animation_timeline.copy_frames(indices, destination, true, new_tag_from)
return
var new_animation_tags := project.animation_tags.duplicate()
# Loop through the tags to create new classes for them, so that they won't be the same
# as project.animation_tags's classes. Needed for undo/redo to work properly.
for i in new_animation_tags.size():
new_animation_tags[i] = AnimationTag.new(
new_animation_tags[i].name,
new_animation_tags[i].color,
new_animation_tags[i].from,
new_animation_tags[i].to
)
var imported_frames: Array[Frame] = [] # The copied frames
# the indices of newly copied frames
var copied_indices: PackedInt32Array = range(
destination + 1, (destination + 1) + indices.size()
)
project.undo_redo.create_action("Import Frames")
# Step 1: calculate layers to generate
var layer_to_names := PackedStringArray() # names of currently existing layers
for l in project.layers:
layer_to_names.append(l.name)

# the goal of this section is to mark existing layers with their indices else with -1
var layer_from_to := {} # indices of layers from and to
for from in from_project.layers.size():
var to := -1
var pos := 0
for i in layer_to_names.count(from_project.layers[from].name):
pos = layer_to_names.find(from_project.layers[from].name, pos)
# if layer types don't match, the destination is invalid.
if project.layers[pos].get_layer_type() != from_project.layers[from].get_layer_type():
# Don't give up if there is another layer with the same name, check that one as well
pos += 1
continue
# if destination is already assigned to another layer, then don't use it here.
if pos in layer_from_to.values():
# Don't give up if there is another layer with the same name, check that one as well
pos += 1
continue
to = pos
break
layer_from_to[from] = to

# Step 2: generate required layers
var combined_copy := Array() # Makes calculations easy (contains preview of final layer order).
combined_copy.append_array(project.layers)
var added_layers := Array() # Array of layers
# Array of indices to add the respective layers (in added_layers) to
var added_idx := PackedInt32Array()
var added_cels := Array() # Array of an Array of cels (added in same order as their layer)

# Create destinations for layers that don't have one yet
if layer_from_to.values().count(-1) > 0:
# As it is extracted from a dictionary, so i assume the keys aren't sorted
var from_layers_size = layer_from_to.keys().duplicate(true)
from_layers_size.sort() # it's values should now be from (layer size - 1) to zero
for i in from_layers_size:
if layer_from_to[i] == -1:
var from_layer := from_project.layers[i]
var type = from_layer.get_layer_type()
var l: BaseLayer
match type:
Global.LayerTypes.PIXEL:
l = PixelLayer.new(project)
Global.LayerTypes.GROUP:
l = GroupLayer.new(project)
Global.LayerTypes.THREE_D:
l = Layer3D.new(project)
Global.LayerTypes.TILEMAP:
l = LayerTileMap.new(project, from_layer.tileset)
l.place_only_mode = from_layer.place_only_mode
l.tile_size = from_layer.tile_size
l.tile_shape = from_layer.tile_shape
l.tile_layout = from_layer.tile_layout
l.tile_offset_axis = from_layer.tile_offset_axis
Global.LayerTypes.AUDIO:
l = AudioLayer.new(project)
l.audio = from_layer.audio
if l == null: # Ignore copying this layer if it isn't supported
continue
var cels := []
for f in project.frames:
cels.append(l.new_empty_cel())
l.name = from_project.layers[i].name # this will set it to the required layer name

# Set an appropriate parent
var new_layer_idx = combined_copy.size()
layer_from_to[i] = new_layer_idx
var from_children = from_project.layers[i].get_children(false)
for from_child in from_children: # If this layer had children
var child_to_idx = layer_from_to[from_project.layers.find(from_child)]
var to_child = combined_copy[child_to_idx]
if to_child in added_layers: # if child was added recently
to_child.parent = l

combined_copy.insert(new_layer_idx, l)
added_layers.append(l) # layer is now added
added_idx.append(new_layer_idx) # at index new_layer_idx
added_cels.append(cels) # with cels

# Now initiate import
for f in indices:
var src_frame: Frame = from_project.frames[f]
var new_frame := Frame.new()
imported_frames.append(new_frame)
new_frame.duration = src_frame.duration
for to in combined_copy.size():
var new_cel: BaseCel
if to in layer_from_to.values(): # We have data to Import to this layer index
var from = layer_from_to.find_key(to)
# Cel we're copying from, the source
var src_cel: BaseCel = from_project.frames[f].cels[from]
new_cel = src_cel.duplicate_cel()
if src_cel is Cel3D:
new_cel.size_changed(project.size)
elif src_cel is CelTileMap:
var copied_content := src_cel.copy_content() as Array
var src_img: ImageExtended = copied_content[0]
var empty := project.new_empty_image()
var copy := ImageExtended.new()
copy.copy_from_custom(empty, project.is_indexed())
copy.blit_rect(src_img, Rect2(Vector2.ZERO, src_img.get_size()), Vector2.ZERO)
new_cel.set_content([copy, copied_content[1]])
new_cel.set_indexed_mode(project.is_indexed())
else:
# Add more types here if they have a copy_content() method.
if src_cel is PixelCel:
var src_img: ImageExtended = src_cel.copy_content()
var empty := project.new_empty_image()
var copy := ImageExtended.new()
copy.copy_from_custom(empty, project.is_indexed())
copy.blit_rect(
src_img, Rect2(Vector2.ZERO, src_img.get_size()), Vector2.ZERO
)
new_cel.set_content(copy)
new_cel.set_indexed_mode(project.is_indexed())

else:
new_cel = combined_copy[to].new_empty_cel()
new_frame.cels.append(new_cel)

for tag in new_animation_tags: # Loop through the tags to see if the frame is in one
if copied_indices[0] >= tag.from && copied_indices[0] <= tag.to:
tag.to += 1
elif copied_indices[0] < tag.from:
tag.from += 1
tag.to += 1
if new_tag_from:
new_animation_tags.append(
AnimationTag.new(
new_tag_from.name, new_tag_from.color, copied_indices[0] + 1, copied_indices[-1] + 1
)
)
project.undo_redo.add_undo_method(project.remove_frames.bind(copied_indices))
project.undo_redo.add_do_method(project.add_layers.bind(added_layers, added_idx, added_cels))
project.undo_redo.add_do_method(project.add_frames.bind(imported_frames, copied_indices))
project.undo_redo.add_undo_method(project.remove_layers.bind(added_idx))
# Note: temporarily set the selected cels to an empty array (needed for undo/redo)
project.undo_redo.add_do_property(Global.current_project, "selected_cels", [])
project.undo_redo.add_undo_property(Global.current_project, "selected_cels", [])

var all_new_cels := []
# Select all the new frames so that it is easier to move/offset collectively if user wants
# To ease animation workflow, new current frame is the first copied frame instead of the last
var range_start: int = copied_indices[-1]
var range_end: int = copied_indices[0]
var frame_diff_sign := signi(range_end - range_start)
if frame_diff_sign == 0:
frame_diff_sign = 1
for i in range(range_start, range_end + frame_diff_sign, frame_diff_sign):
for j in range(0, combined_copy.size()):
var frame_layer := [i, j]
if !all_new_cels.has(frame_layer):
all_new_cels.append(frame_layer)
project.undo_redo.add_do_property(Global.current_project, "selected_cels", all_new_cels)
project.undo_redo.add_undo_method(
project.change_cel.bind(project.current_frame, project.current_layer)
)
project.undo_redo.add_do_method(project.change_cel.bind(range_end))
project.undo_redo.add_do_property(project, "animation_tags", new_animation_tags)
project.undo_redo.add_undo_property(project, "animation_tags", project.animation_tags)
project.undo_redo.add_do_method(Global.undo_or_redo.bind(false))
project.undo_redo.add_undo_method(Global.undo_or_redo.bind(true))
project.undo_redo.commit_action()
Loading