forked from newt/gmtk-2024
feat: player movement
This commit is contained in:
commit
183ef59826
104 changed files with 8997 additions and 0 deletions
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
# Normalize EOL for all files that Git considers text files.
|
||||
* text=auto eol=lf
|
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
# Godot 4+ specific ignores
|
||||
.godot/
|
||||
/android/
|
BIN
Layer 2.png
Normal file
BIN
Layer 2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 134 B |
34
Layer 2.png.import
Normal file
34
Layer 2.png.import
Normal file
|
@ -0,0 +1,34 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://bfvlix1nyb3j5"
|
||||
path="res://.godot/imported/Layer 2.png-ac1279a7a9bf1062890d6c96fd5ce0c8.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://Layer 2.png"
|
||||
dest_files=["res://.godot/imported/Layer 2.png-ac1279a7a9bf1062890d6c96fd5ce0c8.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
BIN
Layer 3.png
Normal file
BIN
Layer 3.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
34
Layer 3.png.import
Normal file
34
Layer 3.png.import
Normal file
|
@ -0,0 +1,34 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://cxdjxd2sv21hu"
|
||||
path="res://.godot/imported/Layer 3.png-cd7bea8c5ddca19297a526bc9b9b3d56.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://Layer 3.png"
|
||||
dest_files=["res://.godot/imported/Layer 3.png-cd7bea8c5ddca19297a526bc9b9b3d56.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
222
Player.tscn
Normal file
222
Player.tscn
Normal file
|
@ -0,0 +1,222 @@
|
|||
[gd_scene load_steps=30 format=3 uid="uid://cjiuycwqqxaxn"]
|
||||
|
||||
[ext_resource type="Script" path="res://player.gd" id="1_oy25y"]
|
||||
[ext_resource type="Texture2D" uid="uid://cxdjxd2sv21hu" path="res://Layer 3.png" id="2_u5vg1"]
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_8d47g"]
|
||||
atlas = ExtResource("2_u5vg1")
|
||||
region = Rect2(0, 0, 32, 64)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_e4ysm"]
|
||||
atlas = ExtResource("2_u5vg1")
|
||||
region = Rect2(32, 0, 32, 64)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_4rhkl"]
|
||||
atlas = ExtResource("2_u5vg1")
|
||||
region = Rect2(64, 0, 32, 64)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_arcxo"]
|
||||
atlas = ExtResource("2_u5vg1")
|
||||
region = Rect2(96, 0, 32, 64)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_00jl2"]
|
||||
atlas = ExtResource("2_u5vg1")
|
||||
region = Rect2(128, 0, 32, 64)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_0hb6a"]
|
||||
atlas = ExtResource("2_u5vg1")
|
||||
region = Rect2(0, 64, 32, 64)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_yn35a"]
|
||||
atlas = ExtResource("2_u5vg1")
|
||||
region = Rect2(32, 64, 32, 64)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_f27hx"]
|
||||
atlas = ExtResource("2_u5vg1")
|
||||
region = Rect2(64, 64, 32, 64)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_si5lp"]
|
||||
atlas = ExtResource("2_u5vg1")
|
||||
region = Rect2(96, 64, 32, 64)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_mmtke"]
|
||||
atlas = ExtResource("2_u5vg1")
|
||||
region = Rect2(128, 64, 32, 64)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_dkmnd"]
|
||||
atlas = ExtResource("2_u5vg1")
|
||||
region = Rect2(0, 128, 32, 64)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_cermf"]
|
||||
atlas = ExtResource("2_u5vg1")
|
||||
region = Rect2(32, 128, 32, 64)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_hdhbl"]
|
||||
atlas = ExtResource("2_u5vg1")
|
||||
region = Rect2(64, 128, 32, 64)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_frlcl"]
|
||||
atlas = ExtResource("2_u5vg1")
|
||||
region = Rect2(96, 128, 32, 64)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_rkycb"]
|
||||
atlas = ExtResource("2_u5vg1")
|
||||
region = Rect2(128, 128, 32, 64)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_kgrgd"]
|
||||
atlas = ExtResource("2_u5vg1")
|
||||
region = Rect2(0, 192, 32, 64)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_ulb1s"]
|
||||
atlas = ExtResource("2_u5vg1")
|
||||
region = Rect2(32, 192, 32, 64)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_cpo0y"]
|
||||
atlas = ExtResource("2_u5vg1")
|
||||
region = Rect2(64, 192, 32, 64)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_51m40"]
|
||||
atlas = ExtResource("2_u5vg1")
|
||||
region = Rect2(96, 192, 32, 64)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_aqja2"]
|
||||
atlas = ExtResource("2_u5vg1")
|
||||
region = Rect2(128, 192, 32, 64)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_m750d"]
|
||||
atlas = ExtResource("2_u5vg1")
|
||||
region = Rect2(0, 256, 32, 64)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_psi6a"]
|
||||
atlas = ExtResource("2_u5vg1")
|
||||
region = Rect2(32, 256, 32, 64)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_0s74t"]
|
||||
atlas = ExtResource("2_u5vg1")
|
||||
region = Rect2(64, 256, 32, 64)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_qjvpa"]
|
||||
atlas = ExtResource("2_u5vg1")
|
||||
region = Rect2(96, 256, 32, 64)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_fveui"]
|
||||
atlas = ExtResource("2_u5vg1")
|
||||
region = Rect2(128, 256, 32, 64)
|
||||
|
||||
[sub_resource type="SpriteFrames" id="SpriteFrames_iat2j"]
|
||||
animations = [{
|
||||
"frames": [{
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("AtlasTexture_8d47g")
|
||||
}, {
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("AtlasTexture_e4ysm")
|
||||
}, {
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("AtlasTexture_4rhkl")
|
||||
}, {
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("AtlasTexture_arcxo")
|
||||
}, {
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("AtlasTexture_00jl2")
|
||||
}, {
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("AtlasTexture_0hb6a")
|
||||
}, {
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("AtlasTexture_yn35a")
|
||||
}, {
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("AtlasTexture_f27hx")
|
||||
}, {
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("AtlasTexture_si5lp")
|
||||
}, {
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("AtlasTexture_mmtke")
|
||||
}],
|
||||
"loop": true,
|
||||
"name": &"idle",
|
||||
"speed": 10.0
|
||||
}, {
|
||||
"frames": [{
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("AtlasTexture_dkmnd")
|
||||
}, {
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("AtlasTexture_cermf")
|
||||
}, {
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("AtlasTexture_hdhbl")
|
||||
}, {
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("AtlasTexture_frlcl")
|
||||
}, {
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("AtlasTexture_rkycb")
|
||||
}, {
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("AtlasTexture_kgrgd")
|
||||
}, {
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("AtlasTexture_ulb1s")
|
||||
}, {
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("AtlasTexture_cpo0y")
|
||||
}, {
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("AtlasTexture_51m40")
|
||||
}, {
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("AtlasTexture_aqja2")
|
||||
}, {
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("AtlasTexture_m750d")
|
||||
}, {
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("AtlasTexture_psi6a")
|
||||
}, {
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("AtlasTexture_0s74t")
|
||||
}, {
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("AtlasTexture_qjvpa")
|
||||
}, {
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("AtlasTexture_fveui")
|
||||
}],
|
||||
"loop": true,
|
||||
"name": &"walk",
|
||||
"speed": 10.0
|
||||
}]
|
||||
|
||||
[sub_resource type="RectangleShape2D" id="RectangleShape2D_0on8q"]
|
||||
size = Vector2(28, 64)
|
||||
|
||||
[node name="Player" type="CharacterBody2D"]
|
||||
script = ExtResource("1_oy25y")
|
||||
|
||||
[node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="."]
|
||||
texture_filter = 1
|
||||
sprite_frames = SubResource("SpriteFrames_iat2j")
|
||||
animation = &"walk"
|
||||
frame_progress = 0.300851
|
||||
metadata/_aseprite_wizard_config_ = {
|
||||
"layer": "Layer 3",
|
||||
"o_ex_p": "",
|
||||
"o_folder": "",
|
||||
"o_name": "",
|
||||
"only_visible": false,
|
||||
"slice": "",
|
||||
"source": "res://assets/PlayerCharacter.aseprite"
|
||||
}
|
||||
metadata/_aseprite_wizard_source_file_hash_ = "c2f10613af41076ed14f7dd74dccbea5"
|
||||
metadata/_aseprite_wizard_interface_config_ = {
|
||||
"layer_section": true
|
||||
}
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
|
||||
position = Vector2(2, 0)
|
||||
shape = SubResource("RectangleShape2D_0on8q")
|
||||
debug_color = Color(1, 0.254902, 0.415686, 0.105882)
|
BIN
PlayerCharacter.png
Normal file
BIN
PlayerCharacter.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
34
PlayerCharacter.png.import
Normal file
34
PlayerCharacter.png.import
Normal file
|
@ -0,0 +1,34 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dvnohr8gjh56t"
|
||||
path="res://.godot/imported/PlayerCharacter.png-ab2ae3be5ec39b25693dfd99826d05ab.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://PlayerCharacter.png"
|
||||
dest_files=["res://.godot/imported/PlayerCharacter.png-ab2ae3be5ec39b25693dfd99826d05ab.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
23
World.tscn
Normal file
23
World.tscn
Normal file
|
@ -0,0 +1,23 @@
|
|||
[gd_scene load_steps=4 format=3 uid="uid://cei5gjgfoginb"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://cjiuycwqqxaxn" path="res://Player.tscn" id="1_4tauq"]
|
||||
|
||||
[sub_resource type="QuadMesh" id="QuadMesh_seics"]
|
||||
size = Vector2(480, 20)
|
||||
|
||||
[sub_resource type="RectangleShape2D" id="RectangleShape2D_h5wua"]
|
||||
size = Vector2(480, 20)
|
||||
|
||||
[node name="World" type="Node2D"]
|
||||
position = Vector2(272, 240)
|
||||
|
||||
[node name="Player" parent="." instance=ExtResource("1_4tauq")]
|
||||
position = Vector2(-37, -59)
|
||||
|
||||
[node name="Floor" type="StaticBody2D" parent="."]
|
||||
|
||||
[node name="MeshInstance2D" type="MeshInstance2D" parent="Floor"]
|
||||
mesh = SubResource("QuadMesh_seics")
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="Floor"]
|
||||
shape = SubResource("RectangleShape2D_h5wua")
|
280
addons/AsepriteWizard/aseprite/aseprite.gd
Normal file
280
addons/AsepriteWizard/aseprite/aseprite.gd
Normal file
|
@ -0,0 +1,280 @@
|
|||
@tool
|
||||
extends RefCounted
|
||||
|
||||
var _config = preload("../config/config.gd").new()
|
||||
|
||||
#
|
||||
# Output:
|
||||
# {
|
||||
# "data_file": file path to the json file
|
||||
# "sprite_sheet": file path to the raw image file
|
||||
# }
|
||||
func export_file(file_name: String, output_folder: String, options: Dictionary) -> Dictionary:
|
||||
var exception_pattern = options.get('exception_pattern', "")
|
||||
var only_visible_layers = options.get('only_visible_layers', false)
|
||||
var output_name = file_name if options.get('output_filename') == "" else options.get('output_filename', file_name)
|
||||
var first_frame_only = options.get("first_frame_only", false)
|
||||
var basename = _get_file_basename(output_name)
|
||||
var output_dir = ProjectSettings.globalize_path(output_folder)
|
||||
var data_file = "%s/%s.json" % [output_dir, basename]
|
||||
var sprite_sheet = "%s/%s.png" % [output_dir, basename]
|
||||
var output = []
|
||||
var arguments = _export_command_common_arguments(file_name, data_file, sprite_sheet)
|
||||
|
||||
if not only_visible_layers:
|
||||
arguments.push_front("--all-layers")
|
||||
|
||||
if first_frame_only:
|
||||
arguments.push_front("'[0, 0]'")
|
||||
arguments.push_front("--frame-range")
|
||||
|
||||
_add_sheet_type_arguments(arguments, options)
|
||||
|
||||
_add_ignore_layer_arguments(file_name, arguments, exception_pattern)
|
||||
|
||||
var local_sprite_sheet_path = ProjectSettings.localize_path(sprite_sheet)
|
||||
var is_new = not ResourceLoader.exists(local_sprite_sheet_path)
|
||||
|
||||
var exit_code = _execute(arguments, output)
|
||||
if exit_code != 0:
|
||||
printerr('aseprite: failed to export spritesheet')
|
||||
printerr(output)
|
||||
return {}
|
||||
|
||||
return {
|
||||
"data_file": ProjectSettings.localize_path(data_file),
|
||||
"sprite_sheet": local_sprite_sheet_path,
|
||||
"is_first_import": is_new,
|
||||
}
|
||||
|
||||
|
||||
func export_layers(file_name: String, output_folder: String, options: Dictionary) -> Array:
|
||||
var exception_pattern = options.get('exception_pattern', "")
|
||||
var only_visible_layers = options.get('only_visible_layers', false)
|
||||
var basename = _get_file_basename(file_name)
|
||||
var layers = list_layers(file_name, only_visible_layers)
|
||||
var exception_regex = _compile_regex(exception_pattern)
|
||||
|
||||
var output = []
|
||||
|
||||
for layer in layers:
|
||||
if layer != "" and (not exception_regex or exception_regex.search(layer) == null):
|
||||
output.push_back(export_layer(file_name, layer, output_folder, options))
|
||||
|
||||
return output
|
||||
|
||||
|
||||
func export_layer(file_name: String, layer_name: String, output_folder: String, options: Dictionary) -> Dictionary:
|
||||
var output_prefix = options.get('output_filename', "").strip_edges()
|
||||
var output_dir = output_folder.replace("res://", "./").strip_edges()
|
||||
var data_file = "%s/%s%s.json" % [output_dir, output_prefix, layer_name]
|
||||
var sprite_sheet = "%s/%s%s.png" % [output_dir, output_prefix, layer_name]
|
||||
var first_frame_only = options.get("first_frame_only", false)
|
||||
var output = []
|
||||
var arguments = _export_command_common_arguments(file_name, data_file, sprite_sheet)
|
||||
arguments.push_front(layer_name)
|
||||
arguments.push_front("--layer")
|
||||
|
||||
if first_frame_only:
|
||||
arguments.push_front("'[0, 0]'")
|
||||
arguments.push_front("--frame-range")
|
||||
|
||||
_add_sheet_type_arguments(arguments, options)
|
||||
|
||||
var local_sprite_sheet_path = ProjectSettings.localize_path(sprite_sheet)
|
||||
var is_new = not ResourceLoader.exists(local_sprite_sheet_path)
|
||||
|
||||
var exit_code = _execute(arguments, output)
|
||||
if exit_code != 0:
|
||||
print('aseprite: failed to export layer spritesheet')
|
||||
print(output)
|
||||
return {}
|
||||
|
||||
return {
|
||||
"data_file": ProjectSettings.localize_path(data_file),
|
||||
"sprite_sheet": local_sprite_sheet_path,
|
||||
"is_first_import": is_new,
|
||||
}
|
||||
|
||||
|
||||
func _add_ignore_layer_arguments(file_name: String, arguments: Array, exception_pattern: String):
|
||||
var layers = _get_exception_layers(file_name, exception_pattern)
|
||||
if not layers.is_empty():
|
||||
for l in layers:
|
||||
arguments.push_front(l)
|
||||
arguments.push_front('--ignore-layer')
|
||||
|
||||
func _add_sheet_type_arguments(arguments: Array, options : Dictionary):
|
||||
var column_count : int = options.get("column_count", 0)
|
||||
if column_count > 0:
|
||||
arguments.push_back("--merge-duplicates") # Yes, this is undocumented
|
||||
arguments.push_back("--sheet-columns")
|
||||
arguments.push_back(column_count)
|
||||
else:
|
||||
arguments.push_back("--sheet-pack")
|
||||
|
||||
|
||||
func _get_exception_layers(file_name: String, exception_pattern: String) -> Array:
|
||||
var layers = list_layers(file_name)
|
||||
var regex = _compile_regex(exception_pattern)
|
||||
if regex == null:
|
||||
return []
|
||||
|
||||
var exception_layers = []
|
||||
for layer in layers:
|
||||
if regex.search(layer) != null:
|
||||
exception_layers.push_back(layer)
|
||||
|
||||
return exception_layers
|
||||
|
||||
|
||||
func list_layers(file_name: String, only_visible = false) -> Array:
|
||||
var output = []
|
||||
var arguments = ["-b", "--list-layers", file_name]
|
||||
|
||||
if not only_visible:
|
||||
arguments.push_front("--all-layers")
|
||||
|
||||
var exit_code = _execute(arguments, output)
|
||||
|
||||
if exit_code != 0:
|
||||
printerr('aseprite: failed listing layers')
|
||||
printerr(output)
|
||||
return []
|
||||
|
||||
if output.is_empty():
|
||||
return output
|
||||
|
||||
var raw = output[0].split('\n')
|
||||
var sanitized = []
|
||||
for s in raw:
|
||||
sanitized.append(s.strip_edges())
|
||||
return sanitized
|
||||
|
||||
|
||||
func list_slices(file_name: String) -> Array:
|
||||
var output = []
|
||||
var arguments = ["-b", "--list-slices", file_name]
|
||||
|
||||
var exit_code = _execute(arguments, output)
|
||||
|
||||
if exit_code != 0:
|
||||
printerr('aseprite: failed listing slices')
|
||||
printerr(output)
|
||||
return []
|
||||
|
||||
if output.is_empty():
|
||||
return output
|
||||
|
||||
var raw = output[0].split('\n')
|
||||
var sanitized = []
|
||||
for s in raw:
|
||||
sanitized.append(s.strip_edges())
|
||||
return sanitized
|
||||
|
||||
|
||||
func _export_command_common_arguments(source_name: String, data_path: String, spritesheet_path: String) -> Array:
|
||||
return [
|
||||
"-b",
|
||||
"--list-tags",
|
||||
"--list-slices",
|
||||
"--data",
|
||||
data_path,
|
||||
"--format",
|
||||
"json-array",
|
||||
"--sheet",
|
||||
spritesheet_path,
|
||||
source_name
|
||||
]
|
||||
|
||||
|
||||
func _execute(arguments, output):
|
||||
return OS.execute(_aseprite_command(), arguments, output, true, true)
|
||||
|
||||
|
||||
func _aseprite_command() -> String:
|
||||
return _config.is_command_or_control_pressed()
|
||||
|
||||
|
||||
func _get_file_basename(file_path: String) -> String:
|
||||
return file_path.get_file().trim_suffix('.%s' % file_path.get_extension())
|
||||
|
||||
|
||||
func _compile_regex(pattern):
|
||||
if pattern == "":
|
||||
return
|
||||
|
||||
var rgx = RegEx.new()
|
||||
if rgx.compile(pattern) == OK:
|
||||
return rgx
|
||||
|
||||
printerr('exception regex error')
|
||||
|
||||
|
||||
func test_command():
|
||||
var exit_code = OS.execute(_aseprite_command(), ['--version'], [], true)
|
||||
return exit_code == 0
|
||||
|
||||
|
||||
func is_valid_spritesheet(content):
|
||||
return content.has("frames") and content.has("meta") and content.meta.has('image')
|
||||
|
||||
|
||||
func get_content_frames(content):
|
||||
return content.frames if typeof(content.frames) == TYPE_ARRAY else content.frames.values()
|
||||
|
||||
|
||||
func get_slice_rect(content: Dictionary, slice_name: String) -> Variant:
|
||||
if not content.has("meta") or not content.meta.has("slices"):
|
||||
return null
|
||||
for slice in content.meta.slices:
|
||||
if slice.name == slice_name:
|
||||
if slice.keys.size() > 0:
|
||||
var p = slice.keys[0].bounds
|
||||
return Rect2(p.x, p.y, p.w, p.h)
|
||||
return null
|
||||
|
||||
|
||||
##
|
||||
## Exports tileset layers
|
||||
##
|
||||
## Return (dictionary):
|
||||
## data_file: path to aseprite generated JSON file
|
||||
## sprite_sheet: localized path to spritesheet file
|
||||
func export_tileset_texture(file_name: String, output_folder: String, options: Dictionary) -> Dictionary:
|
||||
var exception_pattern = options.get('exception_pattern', "")
|
||||
var only_visible_layers = options.get('only_visible_layers', false)
|
||||
var output_name = file_name if options.get('output_filename') == "" else options.get('output_filename', file_name)
|
||||
var basename = _get_file_basename(output_name)
|
||||
var output_dir = ProjectSettings.globalize_path(output_folder)
|
||||
var data_path = "%s/%s.json" % [output_dir, basename]
|
||||
var sprite_sheet = "%s/%s.png" % [output_dir, basename]
|
||||
var output = []
|
||||
|
||||
var arguments = [
|
||||
"-b",
|
||||
"--export-tileset",
|
||||
"--data",
|
||||
data_path,
|
||||
"--format",
|
||||
"json-array",
|
||||
"--sheet",
|
||||
sprite_sheet,
|
||||
file_name
|
||||
]
|
||||
|
||||
if not only_visible_layers:
|
||||
arguments.push_front("--all-layers")
|
||||
|
||||
_add_ignore_layer_arguments(file_name, arguments, exception_pattern)
|
||||
|
||||
var exit_code = _execute(arguments, output)
|
||||
if exit_code != 0:
|
||||
printerr('aseprite: failed to export spritesheet')
|
||||
printerr(output)
|
||||
return {}
|
||||
|
||||
return {
|
||||
"data_file": ProjectSettings.localize_path(data_path),
|
||||
"sprite_sheet": ProjectSettings.localize_path(sprite_sheet)
|
||||
}
|
143
addons/AsepriteWizard/aseprite/file_exporter.gd
Normal file
143
addons/AsepriteWizard/aseprite/file_exporter.gd
Normal file
|
@ -0,0 +1,143 @@
|
|||
@tool
|
||||
extends RefCounted
|
||||
|
||||
var result_code = preload("../config/result_codes.gd")
|
||||
var _aseprite = preload("aseprite.gd").new()
|
||||
|
||||
enum {
|
||||
FILE_EXPORT_MODE,
|
||||
LAYERS_EXPORT_MODE
|
||||
}
|
||||
|
||||
##
|
||||
## Generate Aseprite spritesheet and data files for source.
|
||||
##
|
||||
## Options:
|
||||
## output_folder (string)
|
||||
## output_filename (string, optional)
|
||||
## export_mode (FILE_EXPORT_MODE, LAYERS_EXPORT_MODE) default: FILE_EXPORT_MODE
|
||||
## exception_pattern (string, optional)
|
||||
## only_visible_layers (boolean, optional)
|
||||
##
|
||||
## Return:
|
||||
## Array
|
||||
## Dictionary
|
||||
## sprite_sheet: sprite sheet path
|
||||
## data_file: json file path
|
||||
##
|
||||
func generate_aseprite_files(source_file: String, options: Dictionary):
|
||||
var check = _initial_checks(source_file, options)
|
||||
|
||||
if check != result_code.SUCCESS:
|
||||
return result_code.error(check)
|
||||
|
||||
match options.get('export_mode', FILE_EXPORT_MODE):
|
||||
FILE_EXPORT_MODE:
|
||||
var output = _aseprite.export_file(source_file, options.output_folder, options)
|
||||
if output.is_empty():
|
||||
return result_code.error(result_code.ERR_ASEPRITE_EXPORT_FAILED)
|
||||
return result_code.result([output])
|
||||
LAYERS_EXPORT_MODE:
|
||||
var output = _aseprite.export_layers(source_file, options.output_folder, options)
|
||||
if output.is_empty():
|
||||
return result_code.error(result_code.ERR_NO_VALID_LAYERS_FOUND)
|
||||
return result_code.result(output)
|
||||
_:
|
||||
return result_code.error(result_code.ERR_UNKNOWN_EXPORT_MODE)
|
||||
|
||||
|
||||
##
|
||||
## Generate Aseprite spritesheet and data file for source.
|
||||
##
|
||||
## Options:
|
||||
## output_folder (string)
|
||||
## output_filename (string, optional)
|
||||
## layer (string, optional)
|
||||
## exception_pattern (string, optional)
|
||||
## only_visible_layers (boolean, optional)
|
||||
##
|
||||
## Return:
|
||||
## Dictionary
|
||||
## sprite_sheet: sprite sheet path
|
||||
## data_file: json file path
|
||||
##
|
||||
func generate_aseprite_file(source_file: String, options: Dictionary) -> Dictionary:
|
||||
var check = _initial_checks(source_file, options)
|
||||
|
||||
if check != result_code.SUCCESS:
|
||||
return result_code.error(check)
|
||||
|
||||
var output
|
||||
|
||||
if options.get("layer", "") == "":
|
||||
output = _aseprite.export_file(source_file, options.output_folder, options)
|
||||
else:
|
||||
output = _aseprite.export_layer(source_file, options.layer, options.output_folder, options)
|
||||
|
||||
if output.is_empty():
|
||||
return result_code.error(result_code.ERR_ASEPRITE_EXPORT_FAILED)
|
||||
|
||||
return result_code.result(output)
|
||||
|
||||
|
||||
##
|
||||
## Generate a spritesheet with all tilesets in the file
|
||||
##
|
||||
## Options:
|
||||
## exception_pattern (string)
|
||||
## only_visible_layers (boolean)
|
||||
## output_filename (string)
|
||||
## output_folder (string)
|
||||
##
|
||||
## Return:
|
||||
## Dictionary
|
||||
## sprite_sheet: sprite sheet path
|
||||
## data_file: json file path
|
||||
##
|
||||
func generate_tileset_files(source_file: String, options = {}) -> Dictionary:
|
||||
var check = _initial_checks(source_file, options)
|
||||
|
||||
if check != result_code.SUCCESS:
|
||||
return result_code.error(check)
|
||||
|
||||
var output = _aseprite.export_tileset_texture(source_file, options.output_folder, options)
|
||||
|
||||
if output.is_empty():
|
||||
return result_code.error(result_code.ERR_ASEPRITE_EXPORT_FAILED)
|
||||
|
||||
return result_code.result(output)
|
||||
|
||||
|
||||
##
|
||||
## Perform initial source file and output folder checks
|
||||
##
|
||||
func _initial_checks(source: String, options: Dictionary) -> int:
|
||||
if not _aseprite.test_command():
|
||||
return result_code.ERR_ASEPRITE_CMD_NOT_FOUND
|
||||
|
||||
if not FileAccess.file_exists(source):
|
||||
return result_code.ERR_SOURCE_FILE_NOT_FOUND
|
||||
|
||||
if not DirAccess.dir_exists_absolute(options.output_folder):
|
||||
return result_code.ERR_OUTPUT_FOLDER_NOT_FOUND
|
||||
|
||||
return result_code.SUCCESS
|
||||
|
||||
|
||||
##
|
||||
## Load Aseprite source data file and fails if the
|
||||
## content is not valid
|
||||
##
|
||||
func load_json_content(source_file: String) -> Dictionary:
|
||||
var file = FileAccess.open(source_file, FileAccess.READ)
|
||||
if file == null:
|
||||
return result_code.error(FileAccess.get_open_error())
|
||||
var test_json_conv = JSON.new()
|
||||
test_json_conv.parse(file.get_as_text())
|
||||
|
||||
var content = test_json_conv.get_data()
|
||||
|
||||
if not _aseprite.is_valid_spritesheet(content):
|
||||
return result_code.error(result_code.ERR_INVALID_ASEPRITE_SPRITESHEET)
|
||||
|
||||
return result_code.result(content)
|
261
addons/AsepriteWizard/config/config.gd
Normal file
261
addons/AsepriteWizard/config/config.gd
Normal file
|
@ -0,0 +1,261 @@
|
|||
@tool
|
||||
extends RefCounted
|
||||
|
||||
# GLOBAL SETTINGS
|
||||
const _CONFIG_SECTION_KEY = 'aseprite'
|
||||
const _COMMAND_KEY = 'aseprite/general/command_path'
|
||||
|
||||
# PROJECT SETTINGS
|
||||
|
||||
# animation import defaults
|
||||
const _DEFAULT_EXCLUSION_PATTERN_KEY = 'aseprite/animation/layers/exclusion_pattern'
|
||||
const _DEFAULT_ONLY_VISIBLE_LAYERS = 'aseprite/animation/layers/only_include_visible_layers_by_default'
|
||||
const _DEFAULT_LOOP_EX_PREFIX = '_'
|
||||
const _LOOP_ENABLED = 'aseprite/animation/loop/enabled'
|
||||
const _LOOP_EXCEPTION_PREFIX = 'aseprite/animation/loop/exception_prefix'
|
||||
const _USE_METADATA = 'aseprite/animation/storage/use_metadata'
|
||||
|
||||
# cleanup
|
||||
const _REMOVE_SOURCE_FILES_KEY = 'aseprite/import/cleanup/remove_json_file'
|
||||
const _SET_VISIBLE_TRACK_AUTOMATICALLY = 'aseprite/import/cleanup/automatically_hide_sprites_not_in_animation'
|
||||
|
||||
# automatic importer
|
||||
const _IMPORTER_ENABLE_KEY = 'aseprite/import/import_plugin/enable_automatic_importer'
|
||||
const _DEFAULT_IMPORTER_KEY = 'aseprite/import/import_plugin/default_automatic_importer'
|
||||
|
||||
const IMPORTER_SPRITEFRAMES_NAME = "SpriteFrames"
|
||||
const IMPORTER_NOOP_NAME = "No Import"
|
||||
const IMPORTER_TILESET_TEXTURE_NAME = "Tileset Texture"
|
||||
const IMPORTER_STATIC_TEXTURE_NAME = "Static Texture"
|
||||
|
||||
# wizard history
|
||||
const _WIZARD_HISTORY = "wizard_history"
|
||||
const _HISTORY_MAX_ENTRIES = 'aseprite/wizard/history/max_history_entries'
|
||||
const _HISTORY_DEFAULT_MAX_ENTRIES = 100
|
||||
|
||||
## DEPRECATED (v7.4.0): remove in a next major version
|
||||
const _HISTORY_CONFIG_FILE_CFG_KEY = 'aseprite/wizard/history/cache_file_path'
|
||||
## DEPRECATED (v7.4.0): remove in a next major version
|
||||
const _DEFAULT_HISTORY_CONFIG_FILE_PATH = 'res://.aseprite_wizard_history'
|
||||
|
||||
|
||||
# SpriteFrames import last config
|
||||
const _STANDALONE_SPRITEFRAMES_LAST_IMPORT_CFG = "standalone_sf_last_import_cfg"
|
||||
|
||||
# export
|
||||
const _EXPORTER_ENABLE_KEY = 'aseprite/animation/storage/enable_metadata_removal_on_export'
|
||||
|
||||
var _editor_settings: EditorSettings = EditorInterface.get_editor_settings()
|
||||
|
||||
#######################################################
|
||||
# GLOBAL CONFIGS
|
||||
######################################################
|
||||
|
||||
func default_command() -> String:
|
||||
match OS.get_name():
|
||||
"Windows":
|
||||
return "C:\\\\Steam\\steamapps\\common\\Aseprite\\aseprite.exe"
|
||||
"macOS":
|
||||
return "/Applications/Aseprite.app/Contents/MacOS/aseprite"
|
||||
_:
|
||||
return 'aseprite'
|
||||
|
||||
|
||||
func is_command_or_control_pressed() -> String:
|
||||
var command = _editor_settings.get(_COMMAND_KEY) if _editor_settings.has_setting(_COMMAND_KEY) else ""
|
||||
return command if command != "" else default_command()
|
||||
|
||||
|
||||
#######################################################
|
||||
# PROJECT SETTINGS
|
||||
######################################################
|
||||
|
||||
# remove this config in the next major version
|
||||
func is_importer_enabled() -> bool:
|
||||
return _get_project_setting(_IMPORTER_ENABLE_KEY, false)
|
||||
|
||||
|
||||
func get_default_importer() -> String:
|
||||
return _get_project_setting(_DEFAULT_IMPORTER_KEY, IMPORTER_SPRITEFRAMES_NAME if is_importer_enabled() else IMPORTER_NOOP_NAME)
|
||||
|
||||
|
||||
func is_exporter_enabled() -> bool:
|
||||
return _get_project_setting(_EXPORTER_ENABLE_KEY, true)
|
||||
|
||||
|
||||
func should_remove_source_files() -> bool:
|
||||
return _get_project_setting(_REMOVE_SOURCE_FILES_KEY, true)
|
||||
|
||||
|
||||
func is_default_animation_loop_enabled() -> bool:
|
||||
return _get_project_setting(_LOOP_ENABLED, true)
|
||||
|
||||
|
||||
func get_animation_loop_exception_prefix() -> String:
|
||||
return _get_project_setting(_LOOP_EXCEPTION_PREFIX, _DEFAULT_LOOP_EX_PREFIX)
|
||||
|
||||
func is_use_metadata_enabled() -> bool:
|
||||
return _get_project_setting(_USE_METADATA, true)
|
||||
|
||||
|
||||
func get_default_exclusion_pattern() -> String:
|
||||
return _get_project_setting(_DEFAULT_EXCLUSION_PATTERN_KEY, "")
|
||||
|
||||
|
||||
func should_include_only_visible_layers_by_default() -> bool:
|
||||
return _get_project_setting(_DEFAULT_ONLY_VISIBLE_LAYERS, false)
|
||||
|
||||
|
||||
func get_history_max_entries() -> int:
|
||||
return _get_project_setting(_HISTORY_MAX_ENTRIES, _HISTORY_DEFAULT_MAX_ENTRIES)
|
||||
|
||||
|
||||
func get_import_history() -> Array:
|
||||
return get_plugin_metadata(_WIZARD_HISTORY, [])
|
||||
|
||||
|
||||
func get_old_import_history() -> Array:
|
||||
var history = []
|
||||
var history_path := _get_history_file_path()
|
||||
|
||||
if not FileAccess.file_exists(history_path):
|
||||
return history
|
||||
|
||||
var file_object = FileAccess.open(history_path, FileAccess.READ)
|
||||
|
||||
while not file_object.eof_reached():
|
||||
var line = file_object.get_line()
|
||||
if line:
|
||||
var test_json_conv = JSON.new()
|
||||
test_json_conv.parse(line)
|
||||
history.push_back(test_json_conv.get_data())
|
||||
|
||||
return history
|
||||
|
||||
|
||||
func is_set_visible_track_automatically_enabled() -> bool:
|
||||
return _get_project_setting(_SET_VISIBLE_TRACK_AUTOMATICALLY, false)
|
||||
|
||||
|
||||
func save_import_history(history: Array):
|
||||
set_plugin_metadata(_WIZARD_HISTORY, history)
|
||||
|
||||
|
||||
## DEPRECATED
|
||||
func _get_history_file_path() -> String:
|
||||
return _get_project_setting(_HISTORY_CONFIG_FILE_CFG_KEY, _DEFAULT_HISTORY_CONFIG_FILE_PATH)
|
||||
|
||||
|
||||
## used for old history migration. Should be removed together with the history cleanup
|
||||
func has_old_history() -> bool:
|
||||
return ProjectSettings.has_setting(_HISTORY_CONFIG_FILE_CFG_KEY) or FileAccess.file_exists(_DEFAULT_HISTORY_CONFIG_FILE_PATH)
|
||||
|
||||
## used for old history migration. Should be removed together with the history cleanup
|
||||
func remove_old_history_setting() -> void:
|
||||
DirAccess.remove_absolute(_get_history_file_path())
|
||||
if ProjectSettings.has_setting(_HISTORY_CONFIG_FILE_CFG_KEY):
|
||||
ProjectSettings.clear(_HISTORY_CONFIG_FILE_CFG_KEY)
|
||||
|
||||
#=========================================================
|
||||
# IMPORT CONFIGS
|
||||
#=========================================================
|
||||
## Return config for last import done via standalone SpriteFrames import dock
|
||||
func get_standalone_spriteframes_last_import_config() -> Dictionary:
|
||||
return get_plugin_metadata(_STANDALONE_SPRITEFRAMES_LAST_IMPORT_CFG, {})
|
||||
|
||||
## Set config for last import done via standalone SpriteFrames import dock
|
||||
func set_standalone_spriteframes_last_import_config(data: Dictionary) -> void:
|
||||
set_plugin_metadata(_STANDALONE_SPRITEFRAMES_LAST_IMPORT_CFG, data)
|
||||
|
||||
|
||||
func clear_standalone_spriteframes_last_import_config() -> void:
|
||||
set_plugin_metadata(_STANDALONE_SPRITEFRAMES_LAST_IMPORT_CFG, {})
|
||||
|
||||
|
||||
func get_plugin_metadata(key: String, default: Variant = null) -> Variant:
|
||||
return _editor_settings.get_project_metadata(_CONFIG_SECTION_KEY, key, default)
|
||||
|
||||
|
||||
func set_plugin_metadata(key: String, data: Variant):
|
||||
_editor_settings.set_project_metadata(_CONFIG_SECTION_KEY, key, data)
|
||||
|
||||
|
||||
#######################################################
|
||||
# INITIALIZATION
|
||||
######################################################
|
||||
func initialize_project_settings():
|
||||
_initialize_project_cfg(_DEFAULT_EXCLUSION_PATTERN_KEY, "", TYPE_STRING)
|
||||
_initialize_project_cfg(_DEFAULT_ONLY_VISIBLE_LAYERS, false, TYPE_BOOL)
|
||||
_initialize_project_cfg(_LOOP_ENABLED, true, TYPE_BOOL)
|
||||
_initialize_project_cfg(_LOOP_EXCEPTION_PREFIX, _DEFAULT_LOOP_EX_PREFIX, TYPE_STRING)
|
||||
_initialize_project_cfg(_USE_METADATA, true, TYPE_BOOL)
|
||||
|
||||
_initialize_project_cfg(_REMOVE_SOURCE_FILES_KEY, true, TYPE_BOOL)
|
||||
_initialize_project_cfg(
|
||||
_DEFAULT_IMPORTER_KEY,
|
||||
IMPORTER_SPRITEFRAMES_NAME if is_importer_enabled() else IMPORTER_NOOP_NAME,
|
||||
TYPE_STRING,
|
||||
PROPERTY_HINT_ENUM,
|
||||
"%s,%s,%s,%s" % [IMPORTER_NOOP_NAME, IMPORTER_SPRITEFRAMES_NAME, IMPORTER_TILESET_TEXTURE_NAME, IMPORTER_STATIC_TEXTURE_NAME]
|
||||
)
|
||||
|
||||
_initialize_project_cfg(_EXPORTER_ENABLE_KEY, true, TYPE_BOOL)
|
||||
|
||||
# TODO remove (history max entries)
|
||||
#_initialize_project_cfg(_HISTORY_CONFIG_FILE_CFG_KEY, _DEFAULT_HISTORY_CONFIG_FILE_PATH, TYPE_STRING, PROPERTY_HINT_GLOBAL_FILE)
|
||||
_initialize_project_cfg(_HISTORY_MAX_ENTRIES, _HISTORY_DEFAULT_MAX_ENTRIES, TYPE_INT)
|
||||
|
||||
_initialize_project_cfg(_SET_VISIBLE_TRACK_AUTOMATICALLY, false, TYPE_BOOL)
|
||||
|
||||
ProjectSettings.save()
|
||||
|
||||
_initialize_editor_cfg(_COMMAND_KEY, default_command(), TYPE_STRING)
|
||||
|
||||
|
||||
func clear_project_settings():
|
||||
var _all_settings = [
|
||||
_DEFAULT_EXCLUSION_PATTERN_KEY,
|
||||
_LOOP_ENABLED,
|
||||
_LOOP_EXCEPTION_PREFIX,
|
||||
_USE_METADATA,
|
||||
_REMOVE_SOURCE_FILES_KEY,
|
||||
_DEFAULT_IMPORTER_KEY,
|
||||
_EXPORTER_ENABLE_KEY,
|
||||
_HISTORY_MAX_ENTRIES,
|
||||
_SET_VISIBLE_TRACK_AUTOMATICALLY,
|
||||
_DEFAULT_ONLY_VISIBLE_LAYERS,
|
||||
]
|
||||
for key in _all_settings:
|
||||
ProjectSettings.clear(key)
|
||||
ProjectSettings.save()
|
||||
|
||||
|
||||
func _initialize_project_cfg(key: String, default_value, type: int, hint: int = PROPERTY_HINT_NONE, hint_string = null):
|
||||
if not ProjectSettings.has_setting(key):
|
||||
ProjectSettings.set(key, default_value)
|
||||
ProjectSettings.set_initial_value(key, default_value)
|
||||
ProjectSettings.add_property_info({
|
||||
"name": key,
|
||||
"type": type,
|
||||
"hint": hint,
|
||||
"hint_string": hint_string,
|
||||
})
|
||||
|
||||
|
||||
func _get_project_setting(key: String, default_value):
|
||||
if not ProjectSettings.has_setting(key):
|
||||
return default_value
|
||||
|
||||
var p = ProjectSettings.get(key)
|
||||
return p if p != null else default_value
|
||||
|
||||
|
||||
func _initialize_editor_cfg(key: String, default_value, type: int, hint: int = PROPERTY_HINT_NONE):
|
||||
if not _editor_settings.has_setting(key):
|
||||
_editor_settings.set(key, default_value)
|
||||
_editor_settings.set_initial_value(key, default_value, false)
|
||||
_editor_settings.add_property_info({
|
||||
"name": key,
|
||||
"type": type,
|
||||
"hint": hint,
|
||||
})
|
29
addons/AsepriteWizard/config/config_dialog.gd
Normal file
29
addons/AsepriteWizard/config/config_dialog.gd
Normal file
|
@ -0,0 +1,29 @@
|
|||
@tool
|
||||
extends PopupPanel
|
||||
|
||||
var _config = preload("./config.gd").new()
|
||||
|
||||
@onready var _aseprite_command_field = $MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer/aseprite_command
|
||||
@onready var _version_label = $MarginContainer/VBoxContainer/VBoxContainer/version_found
|
||||
|
||||
func _ready():
|
||||
_aseprite_command_field.text = _config.is_command_or_control_pressed()
|
||||
_version_label.modulate.a = 0
|
||||
|
||||
|
||||
func _on_close_button_up():
|
||||
self.hide()
|
||||
|
||||
|
||||
func _on_test_pressed():
|
||||
var output = []
|
||||
if _test_command(output):
|
||||
_version_label.text = "%s found." % "\n".join(PackedStringArray(output)).strip_edges()
|
||||
else:
|
||||
_version_label.text = "Command not found."
|
||||
_version_label.modulate.a = 1
|
||||
|
||||
|
||||
func _test_command(output):
|
||||
var exit_code = OS.execute(_aseprite_command_field.text, ['--version'], output, true, true)
|
||||
return exit_code == 0
|
80
addons/AsepriteWizard/config/config_dialog.tscn
Normal file
80
addons/AsepriteWizard/config/config_dialog.tscn
Normal file
|
@ -0,0 +1,80 @@
|
|||
[gd_scene load_steps=2 format=3 uid="uid://d0whlywijwa6s"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/AsepriteWizard/config/config_dialog.gd" id="1"]
|
||||
|
||||
[node name="config_dialog" type="PopupPanel"]
|
||||
title = "Aseprite Wizard Config"
|
||||
size = Vector2i(624, 236)
|
||||
visible = true
|
||||
unresizable = false
|
||||
borderless = false
|
||||
min_size = Vector2i(624, 236)
|
||||
content_scale_mode = 1
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="."]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = 4.0
|
||||
offset_top = 4.0
|
||||
offset_right = -532.0
|
||||
offset_bottom = -416.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_override_constants/margin_left = 10
|
||||
theme_override_constants/margin_top = 10
|
||||
theme_override_constants/margin_right = 10
|
||||
theme_override_constants/margin_bottom = 10
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="Label" type="Label" parent="MarginContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
text = "This configuration moved.
|
||||
- To edit the aseprite command path, go to Editor > Editor Settings > Aseprite.
|
||||
- To edit project specific settings, go to Project > Project Settings > Aseprite."
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Aseprite Command" type="Label" parent="MarginContainer/VBoxContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Define the path for Aseprite command"
|
||||
mouse_filter = 1
|
||||
text = "Aseprite Command Path"
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="aseprite_command" type="LineEdit" parent="MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
editable = false
|
||||
caret_blink = true
|
||||
|
||||
[node name="test" type="Button" parent="MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
text = "Test"
|
||||
|
||||
[node name="version_found" type="Label" parent="MarginContainer/VBoxContainer/VBoxContainer"]
|
||||
modulate = Color(1, 1, 1, 0)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Aseprite version found"
|
||||
|
||||
[node name="VBoxContainer2" type="HBoxContainer" parent="MarginContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
alignment = 2
|
||||
|
||||
[node name="close" type="Button" parent="MarginContainer/VBoxContainer/VBoxContainer2"]
|
||||
layout_mode = 2
|
||||
text = "Close"
|
||||
|
||||
[connection signal="pressed" from="MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer/test" to="." method="_on_test_pressed"]
|
||||
[connection signal="button_up" from="MarginContainer/VBoxContainer/VBoxContainer2/close" to="." method="_on_close_button_up"]
|
37
addons/AsepriteWizard/config/result_codes.gd
Normal file
37
addons/AsepriteWizard/config/result_codes.gd
Normal file
|
@ -0,0 +1,37 @@
|
|||
@tool
|
||||
extends RefCounted
|
||||
|
||||
const SUCCESS = 0
|
||||
const ERR_ASEPRITE_CMD_NOT_FOUND = 1
|
||||
const ERR_SOURCE_FILE_NOT_FOUND = 2
|
||||
const ERR_OUTPUT_FOLDER_NOT_FOUND = 3
|
||||
const ERR_ASEPRITE_EXPORT_FAILED = 4
|
||||
const ERR_UNKNOWN_EXPORT_MODE = 5
|
||||
const ERR_NO_VALID_LAYERS_FOUND = 6
|
||||
const ERR_INVALID_ASEPRITE_SPRITESHEET = 7
|
||||
|
||||
|
||||
static func get_error_message(code: int):
|
||||
match code:
|
||||
ERR_ASEPRITE_CMD_NOT_FOUND:
|
||||
return "Aseprite command failed. Please, check if the right command is in your PATH or configured through \"Editor > Editor Settings > Aseprite > General > Command Path\"."
|
||||
ERR_SOURCE_FILE_NOT_FOUND:
|
||||
return "source file does not exist"
|
||||
ERR_OUTPUT_FOLDER_NOT_FOUND:
|
||||
return "output location does not exist"
|
||||
ERR_ASEPRITE_EXPORT_FAILED:
|
||||
return "unable to import file"
|
||||
ERR_INVALID_ASEPRITE_SPRITESHEET:
|
||||
return "aseprite generated bad data file"
|
||||
ERR_NO_VALID_LAYERS_FOUND:
|
||||
return "no valid layers found"
|
||||
_:
|
||||
return "import failed with code %d" % code
|
||||
|
||||
|
||||
static func error(error_code: int):
|
||||
return { "code": error_code, "content": null, "is_ok": false }
|
||||
|
||||
|
||||
static func result(result):
|
||||
return { "code": SUCCESS, "content": result, "is_ok": true }
|
85
addons/AsepriteWizard/config/wizard_config.gd
Normal file
85
addons/AsepriteWizard/config/wizard_config.gd
Normal file
|
@ -0,0 +1,85 @@
|
|||
@tool
|
||||
extends RefCounted
|
||||
|
||||
const WIZARD_CONFIG_META_NAME = "_aseprite_wizard_config_"
|
||||
const WIZARD_CONFIG_MARKER = "aseprite_wizard_config"
|
||||
const WIZARD_INTERFACE_CONFIG_META_NAME = "_aseprite_wizard_interface_config_"
|
||||
const SOURCE_FILE_HASH_META_NAME = "_aseprite_wizard_source_file_hash_"
|
||||
const SEPARATOR = "|="
|
||||
|
||||
static func encode(object: Dictionary):
|
||||
var text = "%s\n" % WIZARD_CONFIG_MARKER
|
||||
|
||||
for prop in object:
|
||||
text += "%s%s%s\n" % [prop, SEPARATOR, object[prop]]
|
||||
|
||||
return Marshalls.utf8_to_base64(text)
|
||||
|
||||
|
||||
static func decode(string: String):
|
||||
var decoded = _decode_base64(string)
|
||||
if not _is_wizard_config(decoded):
|
||||
return null
|
||||
|
||||
var cfg = decoded.split("\n")
|
||||
var config = {}
|
||||
for c in cfg:
|
||||
var parts = c.split(SEPARATOR, 1)
|
||||
if parts.size() == 2:
|
||||
var key = parts[0].strip_edges()
|
||||
var value = parts[1].strip_edges()
|
||||
|
||||
#Convert bool properties
|
||||
if key == "only_visible" or key == "op_exp":
|
||||
match value:
|
||||
"True":
|
||||
config[key] = true
|
||||
"False":
|
||||
config[key] = false
|
||||
_:
|
||||
config[key] = false
|
||||
else:
|
||||
config[key] = value
|
||||
|
||||
return config
|
||||
|
||||
|
||||
static func _decode_base64(string: String):
|
||||
if string != "":
|
||||
return Marshalls.base64_to_utf8(string)
|
||||
return null
|
||||
|
||||
|
||||
static func _is_wizard_config(cfg) -> bool:
|
||||
return cfg != null and cfg.begins_with(WIZARD_CONFIG_MARKER)
|
||||
|
||||
|
||||
static func load_config(node: Object):
|
||||
if node.has_meta(WIZARD_CONFIG_META_NAME):
|
||||
return node.get_meta(WIZARD_CONFIG_META_NAME)
|
||||
|
||||
return decode(node.editor_description)
|
||||
|
||||
|
||||
static func save_config(node: Object, cfg: Dictionary):
|
||||
node.set_meta(WIZARD_CONFIG_META_NAME, cfg)
|
||||
|
||||
|
||||
static func load_interface_config(node: Node, default: Dictionary = {}) -> Dictionary:
|
||||
if node.has_meta(WIZARD_INTERFACE_CONFIG_META_NAME):
|
||||
return node.get_meta(WIZARD_INTERFACE_CONFIG_META_NAME)
|
||||
return default
|
||||
|
||||
|
||||
static func save_interface_config(node: Node, cfg:Dictionary) -> void:
|
||||
node.set_meta(WIZARD_INTERFACE_CONFIG_META_NAME, cfg)
|
||||
|
||||
|
||||
static func set_source_hash(node: Object, hash: String) -> void:
|
||||
node.set_meta(SOURCE_FILE_HASH_META_NAME, hash)
|
||||
|
||||
|
||||
static func get_source_hash(node: Object) -> String:
|
||||
if node.has_meta(SOURCE_FILE_HASH_META_NAME):
|
||||
return node.get_meta(SOURCE_FILE_HASH_META_NAME)
|
||||
return ""
|
|
@ -0,0 +1,306 @@
|
|||
@tool
|
||||
extends "../base_sprite_resource_creator.gd"
|
||||
|
||||
var _DEFAULT_ANIMATION_LIBRARY = "" # GLOBAL
|
||||
|
||||
func create_animations(target_node: Node, player: AnimationPlayer, aseprite_files: Dictionary, options: Dictionary):
|
||||
var result = _import(target_node, player, aseprite_files, options)
|
||||
|
||||
if result != result_code.SUCCESS:
|
||||
printerr(result_code.get_error_message(result))
|
||||
|
||||
|
||||
func _import(target_node: Node, player: AnimationPlayer, aseprite_files: Dictionary, options: Dictionary):
|
||||
var source_file = aseprite_files.data_file
|
||||
var sprite_sheet = aseprite_files.sprite_sheet
|
||||
var data = _aseprite_file_exporter.load_json_content(source_file)
|
||||
|
||||
if not data.is_ok:
|
||||
return data.code
|
||||
|
||||
var content = data.content
|
||||
|
||||
var context = {}
|
||||
|
||||
if target_node is CanvasItem:
|
||||
target_node.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
|
||||
else:
|
||||
target_node.texture_filter = BaseMaterial3D.TEXTURE_FILTER_NEAREST
|
||||
|
||||
_setup_texture(target_node, sprite_sheet, content, context, options.slice != "")
|
||||
var result = _configure_animations(target_node, player, content, context, options)
|
||||
if result != result_code.SUCCESS:
|
||||
return result
|
||||
|
||||
return _cleanup_animations(target_node, player, content, options)
|
||||
|
||||
|
||||
func _load_texture(sprite_sheet: String) -> Texture2D:
|
||||
var texture = ResourceLoader.load(sprite_sheet, 'Image', ResourceLoader.CACHE_MODE_IGNORE)
|
||||
texture.take_over_path(sprite_sheet)
|
||||
return texture
|
||||
|
||||
|
||||
func _configure_animations(target_node: Node, player: AnimationPlayer, content: Dictionary, context: Dictionary, options: Dictionary):
|
||||
var frames = _aseprite.get_content_frames(content)
|
||||
var slice_rect = null
|
||||
if options.slice != "":
|
||||
options["slice_rect"] = _aseprite.get_slice_rect(content, options.slice)
|
||||
|
||||
if not player.has_animation_library(_DEFAULT_ANIMATION_LIBRARY):
|
||||
player.add_animation_library(_DEFAULT_ANIMATION_LIBRARY, AnimationLibrary.new())
|
||||
|
||||
if content.meta.has("frameTags") and content.meta.frameTags.size() > 0:
|
||||
var result = result_code.SUCCESS
|
||||
for tag in content.meta.frameTags:
|
||||
var selected_frames = frames.slice(tag.from, tag.to + 1)
|
||||
result = _add_animation_frames(target_node, player, tag.name, selected_frames, context, options, tag.direction, int(tag.get("repeat", -1)))
|
||||
if result != result_code.SUCCESS:
|
||||
break
|
||||
return result
|
||||
else:
|
||||
return _add_animation_frames(target_node, player, "default", frames, context, options)
|
||||
|
||||
|
||||
func _add_animation_frames(target_node: Node, player: AnimationPlayer, anim_name: String, frames: Array, context: Dictionary, options: Dictionary, direction = 'forward', repeat = -1):
|
||||
var animation_name = anim_name
|
||||
var library_name = _DEFAULT_ANIMATION_LIBRARY
|
||||
var is_loopable = _config.is_default_animation_loop_enabled()
|
||||
var slice_rect = options.get("slice_rect")
|
||||
var is_importing_slice: bool = slice_rect != null
|
||||
|
||||
var anim_tokens := anim_name.split("/")
|
||||
|
||||
if anim_tokens.size() > 2:
|
||||
push_error("Invalid animation name: %s" % animation_name)
|
||||
return
|
||||
elif anim_tokens.size() == 2:
|
||||
library_name = anim_tokens[0]
|
||||
animation_name = anim_tokens[1]
|
||||
|
||||
if not _validate_animation_name(animation_name):
|
||||
push_error("Invalid animation name: %s" % animation_name)
|
||||
return
|
||||
|
||||
# Create library if doesn't exist
|
||||
if library_name != _DEFAULT_ANIMATION_LIBRARY and not player.has_animation_library(library_name):
|
||||
player.add_animation_library(library_name, AnimationLibrary.new())
|
||||
|
||||
# Check loop
|
||||
if animation_name.begins_with(_config.get_animation_loop_exception_prefix()):
|
||||
animation_name = animation_name.substr(_config.get_animation_loop_exception_prefix().length())
|
||||
is_loopable = not is_loopable
|
||||
|
||||
# Add library
|
||||
if not player.get_animation_library(library_name).has_animation(animation_name):
|
||||
player.get_animation_library(library_name).add_animation(animation_name, Animation.new())
|
||||
|
||||
var full_name = (
|
||||
animation_name if library_name == "" else "%s/%s" % [library_name, animation_name]
|
||||
)
|
||||
|
||||
var animation = player.get_animation(full_name)
|
||||
_cleanup_tracks(target_node, player, animation)
|
||||
_create_meta_tracks(target_node, player, animation)
|
||||
|
||||
var frame_track = _get_property_track_path(player, target_node, _get_frame_property(is_importing_slice))
|
||||
var frame_track_index = _create_track(target_node, animation, frame_track)
|
||||
|
||||
if direction == "reverse" or direction == "pingpong_reverse":
|
||||
frames.reverse()
|
||||
|
||||
var animation_length = 0
|
||||
|
||||
var repetition = 1
|
||||
|
||||
if repeat != -1:
|
||||
is_loopable = false
|
||||
repetition = repeat
|
||||
|
||||
for i in range(repetition):
|
||||
for frame in frames:
|
||||
var frame_key = _get_frame_key(target_node, frame, context, slice_rect)
|
||||
animation.track_insert_key(frame_track_index, animation_length, frame_key)
|
||||
animation_length += frame.duration / 1000
|
||||
|
||||
# Godot 4 has an Animation.LOOP_PINGPONG mode, however it does not
|
||||
# behave like in Aseprite, so I'm keeping the custom implementation
|
||||
if direction.begins_with("pingpong"):
|
||||
var working_frames = frames.duplicate()
|
||||
working_frames.remove_at(working_frames.size() - 1)
|
||||
if is_loopable or (repetition > 1 and i < repetition - 1):
|
||||
working_frames.remove_at(0)
|
||||
working_frames.reverse()
|
||||
|
||||
for frame in working_frames:
|
||||
var frame_key = _get_frame_key(target_node, frame, context, slice_rect)
|
||||
animation.track_insert_key(frame_track_index, animation_length, frame_key)
|
||||
animation_length += frame.duration / 1000
|
||||
|
||||
# if keep_anim_length is enabled only adjust length if
|
||||
# - there aren't other tracks besides metas and frame
|
||||
# - the current animation is shorter than new one
|
||||
if not options.keep_anim_length or (animation.get_track_count() == (_get_meta_prop_names().size() + 1) or animation.length < animation_length):
|
||||
animation.length = animation_length
|
||||
|
||||
animation.loop_mode = Animation.LOOP_LINEAR if is_loopable else Animation.LOOP_NONE
|
||||
|
||||
return result_code.SUCCESS
|
||||
|
||||
|
||||
const _INVALID_TOKENS := ["/", ":", ",", "["]
|
||||
|
||||
|
||||
func _validate_animation_name(name: String) -> bool:
|
||||
return not _INVALID_TOKENS.any(func(token: String): return token in name)
|
||||
|
||||
|
||||
func _create_track(target_node: Node, animation: Animation, track: String):
|
||||
var track_index = animation.find_track(track, Animation.TYPE_VALUE)
|
||||
|
||||
if track_index != -1:
|
||||
animation.remove_track(track_index)
|
||||
|
||||
track_index = animation.add_track(Animation.TYPE_VALUE)
|
||||
animation.track_set_path(track_index, track)
|
||||
animation.track_set_interpolation_loop_wrap(track_index, false)
|
||||
animation.value_track_set_update_mode(track_index, Animation.UPDATE_DISCRETE)
|
||||
|
||||
return track_index
|
||||
|
||||
|
||||
func _get_property_track_path(player: AnimationPlayer, target_node: Node, prop: String) -> String:
|
||||
var node_path = player.get_node(player.root_node).get_path_to(target_node)
|
||||
return "%s:%s" % [node_path, prop]
|
||||
|
||||
|
||||
func _cleanup_animations(target_node: Node, player: AnimationPlayer, content: Dictionary, options: Dictionary):
|
||||
if not (content.meta.has("frameTags") and content.meta.frameTags.size() > 0):
|
||||
return result_code.SUCCESS
|
||||
|
||||
_remove_unused_animations(content, player)
|
||||
|
||||
if options.get("cleanup_hide_unused_nodes", false):
|
||||
_hide_unused_nodes(target_node, player, content)
|
||||
|
||||
return result_code.SUCCESS
|
||||
|
||||
|
||||
func _remove_unused_animations(content: Dictionary, player: AnimationPlayer):
|
||||
pass # FIXME it's not removing unused animations anymore. Sample impl bellow
|
||||
# var tags = ["RESET"]
|
||||
# for t in content.meta.frameTags:
|
||||
# var a = t.name
|
||||
# if a.begins_with(_config.get_animation_loop_exception_prefix()):
|
||||
# a = a.substr(_config.get_animation_loop_exception_prefix().length())
|
||||
# tags.push_back(a)
|
||||
|
||||
# var track = _get_frame_track_path(player, sprite)
|
||||
# for a in player.get_animation_list():
|
||||
# if tags.has(a):
|
||||
# continue
|
||||
#
|
||||
# var animation = player.get_animation(a)
|
||||
# if animation.get_track_count() != 1:
|
||||
# var t = animation.find_track(track)
|
||||
# if t != -1:
|
||||
# animation.remove_track(t)
|
||||
# continue
|
||||
#
|
||||
# if animation.find_track(track) != -1:
|
||||
# player.remove_animation(a)
|
||||
|
||||
|
||||
func _hide_unused_nodes(target_node: Node, player: AnimationPlayer, content: Dictionary):
|
||||
var root_node := player.get_node(player.root_node)
|
||||
var all_animations := player.get_animation_list()
|
||||
var all_sprite_nodes := []
|
||||
var animation_sprites := {}
|
||||
|
||||
for a in all_animations:
|
||||
var animation := player.get_animation(a)
|
||||
var sprite_nodes := []
|
||||
|
||||
for track_idx in animation.get_track_count():
|
||||
var raw_path := animation.track_get_path(track_idx)
|
||||
|
||||
if raw_path.get_subname(0) == "visible":
|
||||
continue
|
||||
|
||||
var path := _remove_properties_from_path(raw_path)
|
||||
var sprite_node := root_node.get_node(path)
|
||||
|
||||
if !(sprite_node is Sprite2D || sprite_node is Sprite3D):
|
||||
continue
|
||||
|
||||
if sprite_nodes.has(sprite_node):
|
||||
continue
|
||||
sprite_nodes.append(sprite_node)
|
||||
|
||||
animation_sprites[animation] = sprite_nodes
|
||||
for sn in sprite_nodes:
|
||||
if all_sprite_nodes.has(sn):
|
||||
continue
|
||||
all_sprite_nodes.append(sn)
|
||||
|
||||
for animation in animation_sprites:
|
||||
var sprite_nodes : Array = animation_sprites[animation]
|
||||
for node in all_sprite_nodes:
|
||||
if sprite_nodes.has(node):
|
||||
continue
|
||||
var visible_track = _get_property_track_path(player, node, "visible")
|
||||
if animation.find_track(visible_track, Animation.TYPE_VALUE) != -1:
|
||||
continue
|
||||
var visible_track_index = _create_track(node, animation, visible_track)
|
||||
animation.track_insert_key(visible_track_index, 0, false)
|
||||
|
||||
|
||||
func list_layers(file: String, only_visibles = false) -> Array:
|
||||
return _aseprite.list_layers(file, only_visibles)
|
||||
|
||||
|
||||
func list_slices(file: String) -> Array:
|
||||
return _aseprite.list_slices(file)
|
||||
|
||||
|
||||
func _remove_properties_from_path(path: NodePath) -> NodePath:
|
||||
var string_path := path as String
|
||||
if !(":" in string_path):
|
||||
return string_path as NodePath
|
||||
|
||||
var property_path := path.get_concatenated_subnames() as String
|
||||
string_path = string_path.substr(0, string_path.length() - property_path.length() - 1)
|
||||
|
||||
return string_path as NodePath
|
||||
|
||||
|
||||
func _create_meta_tracks(target_node: Node, player: AnimationPlayer, animation: Animation):
|
||||
for prop in _get_meta_prop_names():
|
||||
var track = _get_property_track_path(player, target_node, prop)
|
||||
var track_index = _create_track(target_node, animation, track)
|
||||
animation.track_insert_key(track_index, 0, true if prop == "visible" else target_node.get(prop))
|
||||
|
||||
|
||||
func _cleanup_tracks(target_node: Node, player: AnimationPlayer, animation: Animation):
|
||||
for track_key in ["texture", "hframes", "vframes", "region_rect", "frame"]:
|
||||
var track = _get_property_track_path(player, target_node, track_key)
|
||||
var track_index = animation.find_track(track, Animation.TYPE_VALUE)
|
||||
if track_index != -1:
|
||||
animation.remove_track(track_index)
|
||||
|
||||
|
||||
func _setup_texture(target_node: Node, sprite_sheet: String, content: Dictionary, context: Dictionary, is_importing_slice: bool):
|
||||
push_error("_setup_texture not implemented!")
|
||||
|
||||
|
||||
func _get_frame_property(is_importing_slice: bool) -> String:
|
||||
push_error("_get_frame_property not implemented!")
|
||||
return ""
|
||||
|
||||
|
||||
func _get_frame_key(target_node: Node, frame: Dictionary, context: Dictionary, slice_info: Variant):
|
||||
push_error("_get_frame_key not implemented!")
|
||||
|
||||
|
||||
func _get_meta_prop_names():
|
||||
push_error("_get_meta_prop_names not implemented!")
|
|
@ -0,0 +1,48 @@
|
|||
extends "animation_creator.gd"
|
||||
|
||||
func _get_meta_prop_names():
|
||||
return [ "visible" ]
|
||||
|
||||
|
||||
func _setup_texture(sprite: Node, sprite_sheet: String, content: Dictionary, context: Dictionary, is_importing_slice: bool):
|
||||
var texture = _load_texture(sprite_sheet)
|
||||
sprite.texture = texture
|
||||
|
||||
if content.frames.is_empty():
|
||||
return
|
||||
|
||||
if is_importing_slice:
|
||||
sprite.region_enabled = true
|
||||
sprite.hframes = 1
|
||||
sprite.vframes = 1
|
||||
sprite.frame = 0
|
||||
else:
|
||||
sprite.region_enabled = false
|
||||
sprite.hframes = content.meta.size.w / content.frames[0].sourceSize.w
|
||||
sprite.vframes = content.meta.size.h / content.frames[0].sourceSize.h
|
||||
|
||||
|
||||
func _get_frame_property(is_importing_slice: bool) -> String:
|
||||
return "frame" if not is_importing_slice else "region_rect"
|
||||
|
||||
|
||||
func _get_frame_key(sprite: Node, frame: Dictionary, context: Dictionary, slice_info: Variant):
|
||||
if slice_info != null:
|
||||
return _create_slice_rect(frame, slice_info)
|
||||
return _calculate_frame_index(sprite,frame)
|
||||
|
||||
|
||||
func _calculate_frame_index(sprite: Node, frame: Dictionary) -> int:
|
||||
var column = floor(frame.frame.x * sprite.hframes / sprite.texture.get_width())
|
||||
var row = floor(frame.frame.y * sprite.vframes / sprite.texture.get_height())
|
||||
return (row * sprite.hframes) + column
|
||||
|
||||
|
||||
func _create_slice_rect(frame_data: Dictionary, slice_rect: Rect2) -> Rect2:
|
||||
var frame = frame_data.frame
|
||||
return Rect2(
|
||||
frame.x + slice_rect.position.x,
|
||||
frame.y + slice_rect.position.y,
|
||||
slice_rect.size.x,
|
||||
slice_rect.size.y
|
||||
)
|
|
@ -0,0 +1,32 @@
|
|||
extends "animation_creator.gd"
|
||||
|
||||
|
||||
func _get_meta_prop_names():
|
||||
return [ "visible" ]
|
||||
|
||||
|
||||
func _setup_texture(target_node: Node, sprite_sheet: String, content: Dictionary, context: Dictionary, _is_importing_slice: bool):
|
||||
context["base_texture"] = _load_texture(sprite_sheet)
|
||||
|
||||
|
||||
func _get_frame_property(_is_importing_slice: bool) -> String:
|
||||
return "texture"
|
||||
|
||||
|
||||
func _get_frame_key(target_node: Node, frame: Dictionary, context: Dictionary, slice_info: Variant):
|
||||
return _get_atlas_texture(context["base_texture"], frame, slice_info)
|
||||
|
||||
|
||||
func _get_atlas_texture(base_texture: Texture2D, frame_data: Dictionary, slice_info: Variant) -> AtlasTexture:
|
||||
var tex = AtlasTexture.new()
|
||||
tex.atlas = base_texture
|
||||
tex.region = Rect2(Vector2(frame_data.frame.x, frame_data.frame.y), Vector2(frame_data.frame.w, frame_data.frame.h))
|
||||
tex.filter_clip = true
|
||||
|
||||
if slice_info != null:
|
||||
tex.region.position.x += slice_info.position.x
|
||||
tex.region.position.y += slice_info.position.y
|
||||
tex.region.size.x = slice_info.size.x
|
||||
tex.region.size.y = slice_info.size.y
|
||||
|
||||
return tex
|
|
@ -0,0 +1,8 @@
|
|||
@tool
|
||||
extends RefCounted
|
||||
|
||||
var result_code = preload("../config/result_codes.gd")
|
||||
var _aseprite = preload("../aseprite/aseprite.gd").new()
|
||||
var _aseprite_file_exporter = preload("../aseprite/file_exporter.gd").new()
|
||||
|
||||
var _config = preload("../config/config.gd").new()
|
|
@ -0,0 +1,234 @@
|
|||
@tool
|
||||
extends "../base_sprite_resource_creator.gd"
|
||||
|
||||
enum {
|
||||
FILE_EXPORT_MODE,
|
||||
LAYERS_EXPORT_MODE
|
||||
}
|
||||
|
||||
###
|
||||
### Create SpriteFrames from aseprite files and insert
|
||||
### them to the animated_sprite node
|
||||
###
|
||||
func create_animations(animated_sprite: Node, aseprite_files: Dictionary, options: Dictionary) -> void:
|
||||
var sprite_frames_result = _create_sprite_frames(aseprite_files, options)
|
||||
if not sprite_frames_result.is_ok:
|
||||
printerr(result_code.get_error_message(sprite_frames_result.code))
|
||||
return
|
||||
|
||||
animated_sprite.frames = sprite_frames_result.content
|
||||
|
||||
if animated_sprite is CanvasItem:
|
||||
animated_sprite.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
|
||||
else:
|
||||
animated_sprite.texture_filter = BaseMaterial3D.TEXTURE_FILTER_NEAREST
|
||||
|
||||
|
||||
func create_resources(source_files: Array, options: Dictionary = {}) -> Dictionary:
|
||||
var resources = []
|
||||
|
||||
for o in source_files:
|
||||
if o.is_empty():
|
||||
return result_code.error(result_code.ERR_ASEPRITE_EXPORT_FAILED)
|
||||
|
||||
var resource = _create_sprite_frames(o, options)
|
||||
|
||||
if not resource.is_ok:
|
||||
return resource
|
||||
|
||||
resources.push_back({
|
||||
"data_file": o.data_file,
|
||||
"resource": resource.content,
|
||||
})
|
||||
|
||||
return result_code.result(resources)
|
||||
|
||||
|
||||
func _create_sprite_frames(data: Dictionary, options: Dictionary) -> Dictionary:
|
||||
var aseprite_resources = _load_aseprite_resources(data)
|
||||
if not aseprite_resources.is_ok:
|
||||
return aseprite_resources
|
||||
|
||||
return result_code.result(
|
||||
_create_sprite_frames_with_animations(
|
||||
aseprite_resources.content.metadata,
|
||||
aseprite_resources.content.texture,
|
||||
options,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
func _load_aseprite_resources(aseprite_data: Dictionary):
|
||||
var content_result = _aseprite_file_exporter.load_json_content(aseprite_data.data_file)
|
||||
|
||||
if not content_result.is_ok:
|
||||
return content_result
|
||||
|
||||
var texture = _load_texture(aseprite_data.sprite_sheet)
|
||||
|
||||
return result_code.result({
|
||||
"metadata": content_result.content,
|
||||
"texture": texture
|
||||
})
|
||||
|
||||
|
||||
func save_resources(resources: Array) -> int:
|
||||
for resource in resources:
|
||||
var code = _save_resource(resource.resource, resource.data_file)
|
||||
if code != OK:
|
||||
return code
|
||||
return OK
|
||||
|
||||
|
||||
func _save_resource(resource, source_path: String) -> int:
|
||||
var save_path = "%s.%s" % [source_path.get_basename(), "res"]
|
||||
var code = ResourceSaver.save(resource, save_path, ResourceSaver.FLAG_REPLACE_SUBRESOURCE_PATHS)
|
||||
resource.take_over_path(save_path)
|
||||
return code
|
||||
|
||||
|
||||
func _create_sprite_frames_with_animations(content: Dictionary, texture, options: Dictionary) -> SpriteFrames:
|
||||
var frame_cache = {}
|
||||
var frames = _aseprite.get_content_frames(content)
|
||||
var sprite_frames := SpriteFrames.new()
|
||||
sprite_frames.remove_animation("default")
|
||||
|
||||
var frame_rect: Variant = null
|
||||
|
||||
# currently, aseprite does not work with the --slice option, so we need to manually
|
||||
# do it. https://github.com/aseprite/aseprite/issues/2469
|
||||
if options.get("slice", "") != "":
|
||||
frame_rect = _aseprite.get_slice_rect(content, options.slice)
|
||||
|
||||
if content.meta.has("frameTags") and content.meta.frameTags.size() > 0:
|
||||
for tag in content.meta.frameTags:
|
||||
var selected_frames = frames.slice(tag.from, tag.to + 1)
|
||||
_add_animation_frames(sprite_frames, tag.name, selected_frames, texture, frame_rect, tag.direction, int(tag.get("repeat", -1)), frame_cache)
|
||||
else:
|
||||
_add_animation_frames(sprite_frames, "default", frames, texture, frame_rect)
|
||||
|
||||
return sprite_frames
|
||||
|
||||
|
||||
func _add_animation_frames(
|
||||
sprite_frames: SpriteFrames,
|
||||
anim_name: String,
|
||||
frames: Array,
|
||||
texture,
|
||||
frame_rect: Variant,
|
||||
direction = 'forward',
|
||||
repeat = -1,
|
||||
frame_cache = {}
|
||||
):
|
||||
var animation_name := anim_name
|
||||
var is_loopable = _config.is_default_animation_loop_enabled()
|
||||
|
||||
var loop_prefix = _config.get_animation_loop_exception_prefix()
|
||||
if animation_name.begins_with(loop_prefix):
|
||||
animation_name = anim_name.trim_prefix(loop_prefix)
|
||||
is_loopable = not is_loopable
|
||||
|
||||
sprite_frames.add_animation(animation_name)
|
||||
|
||||
var min_duration = _get_min_duration(frames)
|
||||
var fps = _calculate_fps(min_duration)
|
||||
|
||||
if direction == "reverse" or direction == "pingpong_reverse":
|
||||
frames.reverse()
|
||||
|
||||
var repetition = 1
|
||||
|
||||
if repeat != -1:
|
||||
is_loopable = false
|
||||
repetition = repeat
|
||||
|
||||
for i in range(repetition):
|
||||
for frame in frames:
|
||||
_add_to_sprite_frames(sprite_frames, animation_name, texture, frame, min_duration, frame_cache, frame_rect)
|
||||
|
||||
if direction.begins_with("pingpong"):
|
||||
var working_frames = frames.duplicate()
|
||||
working_frames.remove_at(working_frames.size() - 1)
|
||||
if is_loopable or (repetition > 1 and i < repetition - 1):
|
||||
working_frames.remove_at(0)
|
||||
working_frames.reverse()
|
||||
|
||||
for frame in working_frames:
|
||||
_add_to_sprite_frames(sprite_frames, animation_name, texture, frame, min_duration, frame_cache, frame_rect)
|
||||
|
||||
sprite_frames.set_animation_loop(animation_name, is_loopable)
|
||||
sprite_frames.set_animation_speed(animation_name, fps)
|
||||
|
||||
|
||||
func _calculate_fps(min_duration: int) -> float:
|
||||
return ceil(1000.0 / min_duration)
|
||||
|
||||
|
||||
func _get_min_duration(frames) -> int:
|
||||
var min_duration = 100000
|
||||
for frame in frames:
|
||||
if frame.duration < min_duration:
|
||||
min_duration = frame.duration
|
||||
return min_duration
|
||||
|
||||
|
||||
func _load_texture(path) -> CompressedTexture2D:
|
||||
return ResourceLoader.load(path, "CompressedTexture2D", ResourceLoader.CACHE_MODE_REPLACE)
|
||||
|
||||
|
||||
func _add_to_sprite_frames(
|
||||
sprite_frames,
|
||||
animation_name: String,
|
||||
texture,
|
||||
frame: Dictionary,
|
||||
min_duration: int,
|
||||
frame_cache: Dictionary,
|
||||
frame_rect: Variant,
|
||||
):
|
||||
var atlas : AtlasTexture = _create_atlastexture_from_frame(texture, frame, sprite_frames, frame_cache, frame_rect)
|
||||
var duration = frame.duration / min_duration
|
||||
sprite_frames.add_frame(animation_name, atlas, duration)
|
||||
|
||||
|
||||
func _create_atlastexture_from_frame(
|
||||
image,
|
||||
frame_data,
|
||||
sprite_frames: SpriteFrames,
|
||||
frame_cache: Dictionary,
|
||||
frame_rect: Variant,
|
||||
) -> AtlasTexture:
|
||||
var frame = frame_data.frame
|
||||
var region := Rect2(frame.x, frame.y, frame.w, frame.h)
|
||||
|
||||
# this is to manually set the slice
|
||||
if frame_rect != null:
|
||||
region.position.x += frame_rect.position.x
|
||||
region.position.y += frame_rect.position.y
|
||||
region.size.x = frame_rect.size.x
|
||||
region.size.y = frame_rect.size.y
|
||||
|
||||
var key := "%s_%s_%s_%s" % [frame.x, frame.y, frame.w, frame.h]
|
||||
var texture = frame_cache.get(key)
|
||||
|
||||
if texture != null and texture.atlas == image:
|
||||
return texture
|
||||
|
||||
var atlas_texture := AtlasTexture.new()
|
||||
atlas_texture.atlas = image
|
||||
atlas_texture.region = region
|
||||
|
||||
frame_cache[key] = atlas_texture
|
||||
|
||||
return atlas_texture
|
||||
|
||||
|
||||
func list_layers(file: String, only_visibles = false) -> Array:
|
||||
return _aseprite.list_layers(file, only_visibles)
|
||||
|
||||
|
||||
func list_slices(file: String) -> Array:
|
||||
return _aseprite.list_slices(file)
|
||||
|
||||
|
||||
func _get_file_basename(file_path: String) -> String:
|
||||
return file_path.get_file().trim_suffix('.%s' % file_path.get_extension())
|
|
@ -0,0 +1,26 @@
|
|||
@tool
|
||||
extends "../base_sprite_resource_creator.gd"
|
||||
|
||||
func load_texture(target_node: Node, aseprite_files: Dictionary, options: Dictionary) -> void:
|
||||
var source_file = aseprite_files.data_file
|
||||
var sprite_sheet = aseprite_files.sprite_sheet
|
||||
var data = _aseprite_file_exporter.load_json_content(source_file)
|
||||
var texture = ResourceLoader.load(sprite_sheet)
|
||||
|
||||
if not data.is_ok:
|
||||
printerr("Failed to load aseprite source %s" % source_file)
|
||||
return
|
||||
|
||||
if options.slice == "":
|
||||
target_node.texture = texture
|
||||
else:
|
||||
var region = _aseprite.get_slice_rect(data.content, options.slice)
|
||||
var atlas_texture := AtlasTexture.new()
|
||||
atlas_texture.atlas = texture
|
||||
atlas_texture.region = region
|
||||
target_node.texture = atlas_texture
|
||||
|
||||
if target_node is CanvasItem:
|
||||
target_node.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
|
||||
else:
|
||||
target_node.texture_filter = BaseMaterial3D.TEXTURE_FILTER_NEAREST
|
91
addons/AsepriteWizard/export/metadata_export_plugin.gd
Normal file
91
addons/AsepriteWizard/export/metadata_export_plugin.gd
Normal file
|
@ -0,0 +1,91 @@
|
|||
## Some animations might have been imported from outside the project folder,
|
||||
## potentially leaking the host's path, which is not ideal.
|
||||
## This plugin removes the aseprite metadata from scenes and spriteframes before
|
||||
## generating the game export.
|
||||
extends EditorExportPlugin
|
||||
|
||||
const wizard_config = preload("../config/wizard_config.gd")
|
||||
|
||||
func _get_name():
|
||||
return "aseprite_wizard_metadata_export_plugin"
|
||||
|
||||
|
||||
func _export_file(path: String, type: String, features: PackedStringArray) -> void:
|
||||
match type:
|
||||
"PackedScene":
|
||||
_cleanup_scene(path, type)
|
||||
"SpriteFrames":
|
||||
_cleanup_spriteframes(path, type)
|
||||
|
||||
|
||||
func _cleanup_scene(path: String, type: String):
|
||||
var scene : PackedScene = ResourceLoader.load(path, type, ResourceLoader.CACHE_MODE_IGNORE)
|
||||
var scene_changed := false
|
||||
var root_node := scene.instantiate(PackedScene.GEN_EDIT_STATE_INSTANCE)
|
||||
var nodes := [root_node]
|
||||
|
||||
#remove_at metadata from scene
|
||||
while not nodes.is_empty():
|
||||
var node : Node = nodes.pop_front()
|
||||
|
||||
for child in node.get_children():
|
||||
nodes.push_back(child)
|
||||
|
||||
if _remove_meta(node):
|
||||
scene_changed = true
|
||||
|
||||
#save scene if changed
|
||||
if scene_changed:
|
||||
var filtered_scene := PackedScene.new()
|
||||
if filtered_scene.pack(root_node) != OK:
|
||||
print("Error updating scene")
|
||||
return
|
||||
|
||||
var content := _get_scene_content(path, filtered_scene)
|
||||
|
||||
add_file(path, content, true)
|
||||
|
||||
root_node.free()
|
||||
|
||||
|
||||
func _remove_meta(node: Object) -> bool:
|
||||
if node.has_meta(wizard_config.WIZARD_CONFIG_META_NAME):
|
||||
node.remove_meta(wizard_config.WIZARD_CONFIG_META_NAME)
|
||||
return true
|
||||
|
||||
return false
|
||||
|
||||
|
||||
func _get_scene_content(path:String, scene:PackedScene) -> PackedByteArray:
|
||||
var tmp_path = OS.get_cache_dir() + "tmp_scene." + path.get_extension()
|
||||
ResourceSaver.save(scene, tmp_path)
|
||||
|
||||
var tmp_file = FileAccess.open(tmp_path, FileAccess.READ)
|
||||
var content : PackedByteArray = tmp_file.get_buffer(tmp_file.get_length())
|
||||
tmp_file.close()
|
||||
|
||||
if FileAccess.file_exists(tmp_path):
|
||||
DirAccess.remove_absolute(tmp_path)
|
||||
|
||||
return content
|
||||
|
||||
|
||||
func _cleanup_spriteframes(path: String, type: String):
|
||||
var resource : SpriteFrames = ResourceLoader.load(path, type, ResourceLoader.CACHE_MODE_IGNORE)
|
||||
if _remove_meta(resource):
|
||||
var content := _create_temp_resource(path, resource)
|
||||
add_file(path, content, true)
|
||||
|
||||
|
||||
func _create_temp_resource(path: String, resource: SpriteFrames) -> PackedByteArray:
|
||||
var tmp_path = OS.get_cache_dir() + "tmp_spriteframes_resource." + path.get_extension()
|
||||
ResourceSaver.save(resource, tmp_path)
|
||||
|
||||
var tmp_file = FileAccess.open(tmp_path, FileAccess.READ)
|
||||
var content : PackedByteArray = tmp_file.get_buffer(tmp_file.get_length())
|
||||
tmp_file.close()
|
||||
|
||||
if FileAccess.file_exists(tmp_path):
|
||||
DirAccess.remove_absolute(tmp_path)
|
||||
|
||||
return content
|
57
addons/AsepriteWizard/importers/noop_import_plugin.gd
Normal file
57
addons/AsepriteWizard/importers/noop_import_plugin.gd
Normal file
|
@ -0,0 +1,57 @@
|
|||
@tool
|
||||
extends EditorImportPlugin
|
||||
|
||||
##
|
||||
## No-op importer to allow files to be seen and
|
||||
## managed, but without triggering a real import
|
||||
##
|
||||
|
||||
var config = preload("../config/config.gd").new()
|
||||
|
||||
func _get_importer_name():
|
||||
return "aseprite_wizard.plugin.noop"
|
||||
|
||||
|
||||
func _get_visible_name():
|
||||
return "Aseprite (No Import)"
|
||||
|
||||
|
||||
func _get_recognized_extensions():
|
||||
return ["aseprite", "ase"]
|
||||
|
||||
|
||||
func _get_save_extension():
|
||||
return "res"
|
||||
|
||||
|
||||
func _get_resource_type():
|
||||
return "PackedDataContainer"
|
||||
|
||||
|
||||
func _get_preset_count():
|
||||
return 1
|
||||
|
||||
|
||||
func _get_preset_name(i):
|
||||
return "Default"
|
||||
|
||||
|
||||
func _get_priority():
|
||||
return 2.0 if config.get_default_importer() == config.IMPORTER_NOOP_NAME else 1.0
|
||||
|
||||
|
||||
func _get_import_order():
|
||||
return 1
|
||||
|
||||
|
||||
func _get_import_options(_path, _i):
|
||||
return []
|
||||
|
||||
|
||||
func _get_option_visibility(path, option, options):
|
||||
return true
|
||||
|
||||
|
||||
func _import(source_file, save_path, options, platform_variants, gen_files):
|
||||
var container = PackedDataContainer.new()
|
||||
return ResourceSaver.save(container, "%s.%s" % [save_path, _get_save_extension()])
|
150
addons/AsepriteWizard/importers/sprite_frames_import_plugin.gd
Normal file
150
addons/AsepriteWizard/importers/sprite_frames_import_plugin.gd
Normal file
|
@ -0,0 +1,150 @@
|
|||
@tool
|
||||
extends EditorImportPlugin
|
||||
|
||||
const result_codes = preload("../config/result_codes.gd")
|
||||
|
||||
var config = preload("../config/config.gd").new()
|
||||
var _aseprite_file_exporter = preload("../aseprite/file_exporter.gd").new()
|
||||
var _sf_creator = preload("../creators/sprite_frames/sprite_frames_creator.gd").new()
|
||||
var file_system: EditorFileSystem = EditorInterface.get_resource_filesystem()
|
||||
|
||||
func _get_importer_name():
|
||||
# ideally this should be called aseprite_wizard.plugin.spriteframes
|
||||
# but I'm keeping it like this to avoid unnecessary breaking changes
|
||||
return "aseprite_wizard.plugin"
|
||||
|
||||
|
||||
func _get_visible_name():
|
||||
return "Aseprite SpriteFrames"
|
||||
|
||||
|
||||
func _get_recognized_extensions():
|
||||
return ["aseprite", "ase"]
|
||||
|
||||
|
||||
func _get_save_extension():
|
||||
return "res"
|
||||
|
||||
|
||||
func _get_resource_type():
|
||||
return "SpriteFrames"
|
||||
|
||||
|
||||
func _get_preset_count():
|
||||
return 1
|
||||
|
||||
|
||||
func _get_preset_name(i):
|
||||
return "Default"
|
||||
|
||||
|
||||
func _get_priority():
|
||||
return 2.0 if config.get_default_importer() == config.IMPORTER_SPRITEFRAMES_NAME else 1.0
|
||||
|
||||
|
||||
func _get_import_order():
|
||||
return 1
|
||||
|
||||
|
||||
func _get_import_options(_path, _i):
|
||||
return [
|
||||
{"name": "split_layers", "default_value": false},
|
||||
{"name": "exclude_layers_pattern", "default_value": config.get_default_exclusion_pattern()},
|
||||
{"name": "only_visible_layers", "default_value": false},
|
||||
{
|
||||
"name": "sheet_type",
|
||||
"default_value": "Packed",
|
||||
"property_hint": PROPERTY_HINT_ENUM,
|
||||
"hint_string": get_sheet_type_hint_string()
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
func _get_option_visibility(path, option, options):
|
||||
return true
|
||||
|
||||
|
||||
static func get_sheet_type_hint_string() -> String:
|
||||
var hint_string := "Packed"
|
||||
for number in [2, 4, 8, 16, 32]:
|
||||
hint_string += ",%s columns" % number
|
||||
hint_string += ",Strip"
|
||||
return hint_string
|
||||
|
||||
|
||||
func _import(source_file, save_path, options, platform_variants, gen_files):
|
||||
var absolute_source_file = ProjectSettings.globalize_path(source_file)
|
||||
var absolute_save_path = ProjectSettings.globalize_path(save_path)
|
||||
|
||||
var source_path = source_file.get_base_dir()
|
||||
var source_basename = source_file.substr(source_path.length()+1, -1)
|
||||
source_basename = source_basename.substr(0, source_basename.rfind('.'))
|
||||
|
||||
var export_mode = _sf_creator.LAYERS_EXPORT_MODE if options['split_layers'] else _sf_creator.FILE_EXPORT_MODE
|
||||
|
||||
var aseprite_opts = {
|
||||
"export_mode": export_mode,
|
||||
"exception_pattern": options['exclude_layers_pattern'],
|
||||
"only_visible_layers": options['only_visible_layers'],
|
||||
"output_filename": '' if export_mode == _sf_creator.FILE_EXPORT_MODE else '%s_' % source_basename,
|
||||
"column_count" : int(options['sheet_type']) if options['sheet_type'] != "Strip" else 128,
|
||||
"output_folder": source_path,
|
||||
}
|
||||
|
||||
var source_files = _aseprite_file_exporter.generate_aseprite_files(absolute_source_file, aseprite_opts)
|
||||
if not source_files.is_ok:
|
||||
printerr("ERROR - Could not import aseprite file: %s" % result_codes.get_error_message(source_files.code))
|
||||
return FAILED
|
||||
|
||||
|
||||
var should_trigger_scan = false
|
||||
|
||||
for sf in source_files.content:
|
||||
if sf.is_first_import:
|
||||
file_system.update_file(sf.sprite_sheet)
|
||||
append_import_external_resource(sf.sprite_sheet)
|
||||
else:
|
||||
should_trigger_scan = true
|
||||
|
||||
if should_trigger_scan:
|
||||
file_system.scan()
|
||||
|
||||
var resources = _sf_creator.create_resources(source_files.content)
|
||||
|
||||
if not resources.is_ok:
|
||||
printerr("ERROR - Could not import aseprite file: %s" % result_codes.get_error_message(resources.code))
|
||||
return FAILED
|
||||
|
||||
if export_mode == _sf_creator.LAYERS_EXPORT_MODE:
|
||||
# each layer is saved as one resource using base file name to prevent collisions
|
||||
# the first layer will be saved in the default resource path to prevent
|
||||
# godot from keeping re-importing it
|
||||
for resource in resources.content:
|
||||
var resource_path = "%s.res" % resource.data_file.get_basename();
|
||||
var exit_code = ResourceSaver.save(resource.resource, resource_path)
|
||||
resource.resource.take_over_path(resource_path)
|
||||
|
||||
if exit_code != OK:
|
||||
printerr("ERROR - Could not persist aseprite file: %s" % result_codes.get_error_message(exit_code))
|
||||
return FAILED
|
||||
|
||||
var resource = resources.content[0]
|
||||
var resource_path = "%s.res" % save_path
|
||||
var exit_code = ResourceSaver.save(resource.resource, resource_path)
|
||||
resource.resource.take_over_path(resource_path)
|
||||
|
||||
if config.should_remove_source_files():
|
||||
_remove_source_files(source_files.content)
|
||||
|
||||
if exit_code != OK:
|
||||
printerr("ERROR - Could not persist aseprite file: %s" % result_codes.get_error_message(exit_code))
|
||||
return FAILED
|
||||
|
||||
return OK
|
||||
|
||||
|
||||
func _remove_source_files(source_files: Array):
|
||||
for s in source_files:
|
||||
DirAccess.remove_absolute(s.data_file)
|
||||
|
||||
file_system.call_deferred("scan")
|
136
addons/AsepriteWizard/importers/static_texture_import_plugin.gd
Normal file
136
addons/AsepriteWizard/importers/static_texture_import_plugin.gd
Normal file
|
@ -0,0 +1,136 @@
|
|||
@tool
|
||||
extends EditorImportPlugin
|
||||
|
||||
##
|
||||
## Static texture importer.
|
||||
## Imports first frame from Aseprite file as texture
|
||||
##
|
||||
|
||||
const result_codes = preload("../config/result_codes.gd")
|
||||
var _aseprite_file_exporter = preload("../aseprite/file_exporter.gd").new()
|
||||
|
||||
var config = preload("../config/config.gd").new()
|
||||
var file_system: EditorFileSystem = EditorInterface.get_resource_filesystem()
|
||||
|
||||
|
||||
func _get_importer_name():
|
||||
return "aseprite_wizard.plugin.static-texture"
|
||||
|
||||
|
||||
func _get_visible_name():
|
||||
return "Aseprite Texture"
|
||||
|
||||
|
||||
func _get_recognized_extensions():
|
||||
return ["aseprite", "ase"]
|
||||
|
||||
|
||||
func _get_save_extension():
|
||||
return "res"
|
||||
|
||||
|
||||
func _get_resource_type():
|
||||
return "AtlasTexture"
|
||||
|
||||
|
||||
func _get_preset_count():
|
||||
return 1
|
||||
|
||||
|
||||
func _get_preset_name(i):
|
||||
return "Default"
|
||||
|
||||
|
||||
func _get_priority():
|
||||
return 2.0 if config.get_default_importer() == config.IMPORTER_STATIC_TEXTURE_NAME else 0.8
|
||||
|
||||
|
||||
func _get_import_order():
|
||||
return 1
|
||||
|
||||
|
||||
func _get_import_options(_path, _i):
|
||||
return [
|
||||
{"name": "exclude_layers_pattern", "default_value": config.get_default_exclusion_pattern()},
|
||||
{"name": "only_visible_layers", "default_value": false},
|
||||
]
|
||||
|
||||
|
||||
func _get_option_visibility(path, option, options):
|
||||
return true
|
||||
|
||||
|
||||
func _import(source_file, save_path, options, platform_variants, gen_files):
|
||||
var absolute_source_file = ProjectSettings.globalize_path(source_file)
|
||||
var absolute_save_path = ProjectSettings.globalize_path(save_path)
|
||||
var source_path = source_file.get_base_dir()
|
||||
var source_basename = source_file.substr(source_path.length()+1, -1)
|
||||
source_basename = source_basename.substr(0, source_basename.rfind('.'))
|
||||
|
||||
var aseprite_opts = {
|
||||
"exception_pattern": options['exclude_layers_pattern'],
|
||||
"only_visible_layers": options['only_visible_layers'],
|
||||
"output_filename": '',
|
||||
"output_folder": source_path,
|
||||
"first_frame_only": true,
|
||||
}
|
||||
|
||||
var result = _generate_texture(absolute_source_file, aseprite_opts)
|
||||
|
||||
if not result.is_ok:
|
||||
printerr("ERROR - Could not import aseprite file: %s" % result_codes.get_error_message(result.code))
|
||||
return FAILED
|
||||
|
||||
var sprite_sheet = result.content.sprite_sheet
|
||||
var data = result.content.data
|
||||
|
||||
if ResourceLoader.exists(sprite_sheet):
|
||||
file_system.scan()
|
||||
else:
|
||||
file_system.update_file(sprite_sheet)
|
||||
append_import_external_resource(sprite_sheet)
|
||||
|
||||
var texture: CompressedTexture2D = ResourceLoader.load(sprite_sheet, "CompressedTexture2D", ResourceLoader.CACHE_MODE_REPLACE)
|
||||
|
||||
return _save_resource(texture, save_path, result.content.data_file, data.meta.size)
|
||||
|
||||
|
||||
func _generate_texture(absolute_source_file: String, options: Dictionary) -> Dictionary:
|
||||
var result = _aseprite_file_exporter.generate_aseprite_file(absolute_source_file, options)
|
||||
|
||||
if not result.is_ok:
|
||||
return result
|
||||
|
||||
var sprite_sheet = result.content.sprite_sheet
|
||||
|
||||
var data_result = _aseprite_file_exporter.load_json_content(result.content.data_file)
|
||||
|
||||
if not data_result.is_ok:
|
||||
return data_result
|
||||
|
||||
var data = data_result.content
|
||||
|
||||
return result_codes.result({
|
||||
"data_file": result.content.data_file,
|
||||
"sprite_sheet": sprite_sheet,
|
||||
"data": data
|
||||
})
|
||||
|
||||
|
||||
func _save_resource(texture: CompressedTexture2D, save_path: String, data_file_path: String, size: Dictionary) -> int:
|
||||
var resource = AtlasTexture.new()
|
||||
resource.atlas = texture
|
||||
resource.region = Rect2(0, 0, size.w, size.h)
|
||||
|
||||
var resource_path = "%s.res" % save_path
|
||||
var exit_code = ResourceSaver.save(resource, resource_path)
|
||||
resource.take_over_path(resource_path)
|
||||
|
||||
if config.should_remove_source_files():
|
||||
DirAccess.remove_absolute(data_file_path)
|
||||
file_system.call_deferred("scan")
|
||||
|
||||
if exit_code != OK:
|
||||
printerr("ERROR - Could not persist aseprite file: %s" % result_codes.get_error_message(exit_code))
|
||||
return FAILED
|
||||
return OK
|
135
addons/AsepriteWizard/importers/tileset_texture_import_plugin.gd
Normal file
135
addons/AsepriteWizard/importers/tileset_texture_import_plugin.gd
Normal file
|
@ -0,0 +1,135 @@
|
|||
@tool
|
||||
extends EditorImportPlugin
|
||||
|
||||
##
|
||||
## Tileset texture importer.
|
||||
## Imports Aseprite tileset layers as an AtlasTexture
|
||||
##
|
||||
|
||||
const result_codes = preload("../config/result_codes.gd")
|
||||
var _aseprite_file_exporter = preload("../aseprite/file_exporter.gd").new()
|
||||
|
||||
var config = preload("../config/config.gd").new()
|
||||
var file_system: EditorFileSystem = EditorInterface.get_resource_filesystem()
|
||||
|
||||
|
||||
func _get_importer_name():
|
||||
return "aseprite_wizard.plugin.tileset-texture"
|
||||
|
||||
|
||||
func _get_visible_name():
|
||||
return "Aseprite Tileset Texture"
|
||||
|
||||
|
||||
func _get_recognized_extensions():
|
||||
return ["aseprite", "ase"]
|
||||
|
||||
|
||||
func _get_save_extension():
|
||||
return "res"
|
||||
|
||||
|
||||
func _get_resource_type():
|
||||
return "AtlasTexture"
|
||||
|
||||
|
||||
func _get_preset_count():
|
||||
return 1
|
||||
|
||||
|
||||
func _get_preset_name(i):
|
||||
return "Default"
|
||||
|
||||
|
||||
func _get_priority():
|
||||
return 2.0 if config.get_default_importer() == config.IMPORTER_TILESET_TEXTURE_NAME else 0.9
|
||||
|
||||
|
||||
func _get_import_order():
|
||||
return 1
|
||||
|
||||
|
||||
func _get_import_options(_path, _i):
|
||||
return [
|
||||
{"name": "exclude_layers_pattern", "default_value": config.get_default_exclusion_pattern()},
|
||||
{"name": "only_visible_layers", "default_value": false},
|
||||
]
|
||||
|
||||
|
||||
func _get_option_visibility(path, option, options):
|
||||
return true
|
||||
|
||||
|
||||
func _import(source_file, save_path, options, platform_variants, gen_files):
|
||||
var absolute_source_file = ProjectSettings.globalize_path(source_file)
|
||||
var absolute_save_path = ProjectSettings.globalize_path(save_path)
|
||||
var source_path = source_file.get_base_dir()
|
||||
var source_basename = source_file.substr(source_path.length()+1, -1)
|
||||
source_basename = source_basename.substr(0, source_basename.rfind('.'))
|
||||
|
||||
var aseprite_opts = {
|
||||
"exception_pattern": options['exclude_layers_pattern'],
|
||||
"only_visible_layers": options['only_visible_layers'],
|
||||
"output_filename": '',
|
||||
"output_folder": source_path,
|
||||
}
|
||||
|
||||
var result = _generate_texture(absolute_source_file, aseprite_opts)
|
||||
|
||||
if not result.is_ok:
|
||||
printerr("ERROR - Could not import aseprite file: %s" % result_codes.get_error_message(result.code))
|
||||
return FAILED
|
||||
|
||||
var sprite_sheet = result.content.sprite_sheet
|
||||
var data = result.content.data
|
||||
|
||||
if ResourceLoader.exists(sprite_sheet):
|
||||
file_system.scan()
|
||||
else:
|
||||
file_system.update_file(sprite_sheet)
|
||||
append_import_external_resource(sprite_sheet)
|
||||
|
||||
var texture: CompressedTexture2D = ResourceLoader.load(sprite_sheet, "CompressedTexture2D", ResourceLoader.CACHE_MODE_REPLACE)
|
||||
|
||||
return _save_resource(texture, save_path, result.content.data_file, data.meta.size)
|
||||
|
||||
|
||||
func _generate_texture(absolute_source_file: String, options: Dictionary) -> Dictionary:
|
||||
var result = _aseprite_file_exporter.generate_tileset_files(absolute_source_file, options)
|
||||
|
||||
if not result.is_ok:
|
||||
return result
|
||||
|
||||
var sprite_sheet = result.content.sprite_sheet
|
||||
|
||||
var data_result = _aseprite_file_exporter.load_json_content(result.content.data_file)
|
||||
|
||||
if not data_result.is_ok:
|
||||
return data_result
|
||||
|
||||
var data = data_result.content
|
||||
|
||||
return result_codes.result({
|
||||
"data_file": result.content.data_file,
|
||||
"sprite_sheet": sprite_sheet,
|
||||
"data": data
|
||||
})
|
||||
|
||||
|
||||
func _save_resource(texture: CompressedTexture2D, save_path: String, data_file_path: String, size: Dictionary) -> int:
|
||||
var resource = AtlasTexture.new()
|
||||
resource.atlas = texture
|
||||
resource.region = Rect2(0, 0, size.w, size.h)
|
||||
|
||||
var resource_path = "%s.res" % save_path
|
||||
var exit_code = ResourceSaver.save(resource, resource_path)
|
||||
resource.take_over_path(resource_path)
|
||||
|
||||
if config.should_remove_source_files():
|
||||
DirAccess.remove_absolute(data_file_path)
|
||||
file_system.call_deferred("scan")
|
||||
|
||||
if exit_code != OK:
|
||||
printerr("ERROR - Could not persist aseprite file: %s" % result_codes.get_error_message(exit_code))
|
||||
return FAILED
|
||||
return OK
|
|
@ -0,0 +1,44 @@
|
|||
@tool
|
||||
extends "../base_inspector_dock.gd"
|
||||
|
||||
var sprite_frames_creator = preload("../../../creators/sprite_frames/sprite_frames_creator.gd").new()
|
||||
|
||||
|
||||
func _get_available_layers(global_source_path: String) -> Array:
|
||||
return sprite_frames_creator.list_layers(global_source_path)
|
||||
|
||||
|
||||
func _get_available_slices(global_source_path: String) -> Array:
|
||||
return sprite_frames_creator.list_slices(global_source_path)
|
||||
|
||||
|
||||
func _do_import():
|
||||
var root = get_tree().get_edited_scene_root()
|
||||
|
||||
var source_path = ProjectSettings.globalize_path(_source)
|
||||
var options = {
|
||||
"output_folder": _output_folder if _output_folder != "" else root.scene_file_path.get_base_dir(),
|
||||
"exception_pattern": _ex_pattern_field.text,
|
||||
"only_visible_layers": _visible_layers_field.button_pressed,
|
||||
"output_filename": _out_filename_field.text,
|
||||
"layer": _layer,
|
||||
}
|
||||
|
||||
_save_config()
|
||||
|
||||
var aseprite_output = _aseprite_file_exporter.generate_aseprite_file(source_path, options)
|
||||
|
||||
if not aseprite_output.is_ok:
|
||||
var error = result_code.get_error_message(aseprite_output.code)
|
||||
printerr(error)
|
||||
_show_message(error)
|
||||
return
|
||||
|
||||
file_system.scan()
|
||||
await file_system.filesystem_changed
|
||||
|
||||
sprite_frames_creator.create_animations(target_node, aseprite_output.content, { "slice": _slice })
|
||||
|
||||
wizard_config.set_source_hash(target_node, FileAccess.get_md5(source_path))
|
||||
|
||||
_handle_cleanup(aseprite_output.content)
|
|
@ -0,0 +1,12 @@
|
|||
[gd_scene load_steps=3 format=3 uid="uid://vej7yqkbtd5f"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/AsepriteWizard/interface/docks/animated_sprite/animated_sprite_inspector_dock.gd" id="1"]
|
||||
[ext_resource type="PackedScene" uid="uid://uxm7b02wry10" path="res://addons/AsepriteWizard/interface/docks/dock_fields.tscn" id="2_2ilip"]
|
||||
|
||||
[node name="animated_sprite_inspector_dock" type="PanelContainer"]
|
||||
offset_right = 14.0
|
||||
offset_bottom = 14.0
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="dock_fields" parent="." instance=ExtResource("2_2ilip")]
|
||||
layout_mode = 2
|
|
@ -0,0 +1,13 @@
|
|||
@tool
|
||||
extends EditorInspectorPlugin
|
||||
|
||||
const ASInspectorDock = preload("./animated_sprite_inspector_dock.tscn")
|
||||
|
||||
func _can_handle(object):
|
||||
return object is AnimatedSprite2D || object is AnimatedSprite3D
|
||||
|
||||
|
||||
func _parse_end(object):
|
||||
var dock = ASInspectorDock.instantiate()
|
||||
dock.target_node = object
|
||||
add_custom_control(dock)
|
429
addons/AsepriteWizard/interface/docks/base_inspector_dock.gd
Normal file
429
addons/AsepriteWizard/interface/docks/base_inspector_dock.gd
Normal file
|
@ -0,0 +1,429 @@
|
|||
@tool
|
||||
extends PanelContainer
|
||||
|
||||
const wizard_config = preload("../../config/wizard_config.gd")
|
||||
const result_code = preload("../../config/result_codes.gd")
|
||||
var _aseprite_file_exporter = preload("../../aseprite/file_exporter.gd").new()
|
||||
var config = preload("../../config/config.gd").new()
|
||||
|
||||
var scene: Node
|
||||
var target_node: Node
|
||||
var file_system: EditorFileSystem = EditorInterface.get_resource_filesystem()
|
||||
|
||||
var _layer: String = ""
|
||||
var _slice: String = ""
|
||||
var _source: String = ""
|
||||
var _file_dialog_aseprite: EditorFileDialog
|
||||
var _output_folder_dialog: EditorFileDialog
|
||||
var _importing := false
|
||||
var _output_folder := ""
|
||||
var _out_folder_default := "[Same as scene]"
|
||||
var _layer_default := "[all]"
|
||||
|
||||
var _interface_section_state
|
||||
|
||||
@onready var _section_title := $dock_fields/VBoxContainer/title as Button
|
||||
|
||||
# general
|
||||
@onready var _source_field := $dock_fields/VBoxContainer/source/button as Button
|
||||
# layers
|
||||
@onready var _layer_section_header := $dock_fields/VBoxContainer/extra/sections/layers/section_header as Button
|
||||
@onready var _layer_section_container := $dock_fields/VBoxContainer/extra/sections/layers/section_content as MarginContainer
|
||||
@onready var _layer_field := $dock_fields/VBoxContainer/extra/sections/layers/section_content/content/layer/options as OptionButton
|
||||
@onready var _visible_layers_field := $dock_fields/VBoxContainer/extra/sections/layers/section_content/content/visible_layers/CheckBox as CheckBox
|
||||
@onready var _ex_pattern_field := $dock_fields/VBoxContainer/extra/sections/layers/section_content/content/ex_pattern/LineEdit as LineEdit
|
||||
# slice
|
||||
@onready var _slice_section_header := $dock_fields/VBoxContainer/extra/sections/slices/section_header as Button
|
||||
@onready var _slice_section_container := $dock_fields/VBoxContainer/extra/sections/slices/section_content as MarginContainer
|
||||
@onready var _slice_field := $dock_fields/VBoxContainer/extra/sections/slices/section_content/content/slice/options as OptionButton
|
||||
# output
|
||||
@onready var _output_section_header := $dock_fields/VBoxContainer/extra/sections/output/section_header as Button
|
||||
@onready var _output_section_container := $dock_fields/VBoxContainer/extra/sections/output/section_content as MarginContainer
|
||||
@onready var _out_folder_field := $dock_fields/VBoxContainer/extra/sections/output/section_content/content/out_folder/button as Button
|
||||
@onready var _out_filename_field := $dock_fields/VBoxContainer/extra/sections/output/section_content/content/out_filename/LineEdit as LineEdit
|
||||
|
||||
@onready var _import_button := $dock_fields/VBoxContainer/import as Button
|
||||
|
||||
const INTERFACE_SECTION_KEY_LAYER = "layer_section"
|
||||
const INTERFACE_SECTION_KEY_SLICE = "slice_section"
|
||||
const INTERFACE_SECTION_KEY_OUTPUT = "output_section"
|
||||
|
||||
@onready var _expandable_sections = {
|
||||
INTERFACE_SECTION_KEY_LAYER: { "header": _layer_section_header, "content": _layer_section_container},
|
||||
INTERFACE_SECTION_KEY_SLICE: { "header": _slice_section_header, "content": _slice_section_container},
|
||||
INTERFACE_SECTION_KEY_OUTPUT: { "header": _output_section_header, "content": _output_section_container},
|
||||
}
|
||||
|
||||
func _ready():
|
||||
_pre_setup()
|
||||
_setup_interface()
|
||||
_setup_config()
|
||||
_setup_field_listeners()
|
||||
_setup()
|
||||
_check_for_changes()
|
||||
|
||||
|
||||
func _check_for_changes():
|
||||
if not _source or _source == "":
|
||||
return
|
||||
|
||||
var saved_hash = wizard_config.get_source_hash(target_node)
|
||||
|
||||
if saved_hash == "":
|
||||
return
|
||||
|
||||
if saved_hash != FileAccess.get_md5(_source):
|
||||
$dock_fields.show_source_change_warning()
|
||||
|
||||
|
||||
func _setup_interface():
|
||||
_hide_fields()
|
||||
_show_specific_fields()
|
||||
var cfg = wizard_config.load_interface_config(target_node)
|
||||
_interface_section_state = cfg
|
||||
|
||||
_section_title.add_theme_stylebox_override("normal", _section_title.get_theme_stylebox("hover"))
|
||||
|
||||
for key in _expandable_sections:
|
||||
_adjust_section_visibility(key)
|
||||
|
||||
|
||||
func _setup_config():
|
||||
var cfg = wizard_config.load_config(target_node)
|
||||
if cfg == null:
|
||||
_load_common_default_config()
|
||||
else:
|
||||
_load_common_config(cfg)
|
||||
|
||||
|
||||
func _load_common_config(cfg):
|
||||
if cfg.has("source"):
|
||||
_set_source(cfg.source)
|
||||
|
||||
if cfg.get("layer", "") != "":
|
||||
_layer_field.clear()
|
||||
_set_layer(cfg.layer)
|
||||
|
||||
if cfg.get("slice", "") != "":
|
||||
_slice_field.clear()
|
||||
_set_slice(cfg.slice)
|
||||
|
||||
_set_out_folder(cfg.get("o_folder", ""))
|
||||
_out_filename_field.text = cfg.get("o_name", "")
|
||||
_visible_layers_field.button_pressed = cfg.get("only_visible", false)
|
||||
_ex_pattern_field.text = cfg.get("o_ex_p", "")
|
||||
|
||||
_load_config(cfg)
|
||||
|
||||
|
||||
func _load_common_default_config():
|
||||
_ex_pattern_field.text = config.get_default_exclusion_pattern()
|
||||
_visible_layers_field.button_pressed = config.should_include_only_visible_layers_by_default()
|
||||
#_cleanup_hide_unused_nodes.button_pressed = config.is_set_visible_track_automatically_enabled()
|
||||
_load_default_config()
|
||||
|
||||
|
||||
func _set_source(source):
|
||||
_source = source
|
||||
_source_field.text = _source
|
||||
_source_field.tooltip_text = _source
|
||||
|
||||
|
||||
func _set_layer(layer):
|
||||
_layer = layer
|
||||
_layer_field.add_item(_layer)
|
||||
|
||||
|
||||
func _set_slice(slice):
|
||||
_slice = slice
|
||||
_slice_field.add_item(_slice)
|
||||
|
||||
|
||||
func _set_out_folder(path):
|
||||
_output_folder = path
|
||||
_out_folder_field.text = _output_folder if _output_folder != "" else _out_folder_default
|
||||
_out_folder_field.tooltip_text = _out_folder_field.text
|
||||
|
||||
|
||||
func _toggle_section_visibility(key: String) -> void:
|
||||
_interface_section_state[key] = not _interface_section_state.get(key, false)
|
||||
_adjust_section_visibility(key)
|
||||
wizard_config.save_interface_config(target_node, _interface_section_state)
|
||||
|
||||
|
||||
func _adjust_section_visibility(key: String) -> void:
|
||||
var section = _expandable_sections[key]
|
||||
var is_visible = _interface_section_state.get(key, false)
|
||||
_adjust_icon(section.header, is_visible)
|
||||
section.content.visible = is_visible
|
||||
|
||||
|
||||
func _adjust_icon(section: Button, is_visible: bool = true) -> void:
|
||||
var icon_name = "GuiTreeArrowDown" if is_visible else "GuiTreeArrowRight"
|
||||
section.icon = get_theme_icon(icon_name, "EditorIcons")
|
||||
|
||||
|
||||
func _setup_field_listeners():
|
||||
_layer_section_header.button_down.connect(_on_layer_header_button_down)
|
||||
_slice_section_header.button_down.connect(_on_slice_header_button_down)
|
||||
_output_section_header.button_down.connect(_on_output_header_button_down)
|
||||
|
||||
_source_field.pressed.connect(_on_source_pressed)
|
||||
_source_field.aseprite_file_dropped.connect(_on_source_aseprite_file_dropped)
|
||||
|
||||
_layer_field.button_down.connect(_on_layer_button_down)
|
||||
_layer_field.item_selected.connect(_on_layer_item_selected)
|
||||
|
||||
_slice_field.button_down.connect(_on_slice_button_down)
|
||||
_slice_field.item_selected.connect(_on_slice_item_selected)
|
||||
|
||||
_out_folder_field.dir_dropped.connect(_on_out_dir_dropped)
|
||||
_out_folder_field.pressed.connect(_on_out_folder_pressed)
|
||||
|
||||
_import_button.pressed.connect(_on_import_pressed)
|
||||
|
||||
|
||||
func _on_layer_header_button_down():
|
||||
_toggle_section_visibility(INTERFACE_SECTION_KEY_LAYER)
|
||||
|
||||
|
||||
func _on_slice_header_button_down():
|
||||
_toggle_section_visibility(INTERFACE_SECTION_KEY_SLICE)
|
||||
|
||||
|
||||
func _on_output_header_button_down():
|
||||
_toggle_section_visibility(INTERFACE_SECTION_KEY_OUTPUT)
|
||||
|
||||
|
||||
func _on_layer_button_down():
|
||||
if _source == "":
|
||||
_show_message("Please. Select source file first.")
|
||||
return
|
||||
|
||||
var layers = _get_available_layers(ProjectSettings.globalize_path(_source))
|
||||
_populate_options_field(_layer_field, layers, _layer)
|
||||
|
||||
|
||||
func _on_layer_item_selected(index):
|
||||
if index == 0:
|
||||
_layer = ""
|
||||
return
|
||||
_layer = _layer_field.get_item_text(index)
|
||||
_save_config()
|
||||
|
||||
|
||||
func _on_slice_item_selected(index):
|
||||
if index == 0:
|
||||
_slice = ""
|
||||
return
|
||||
_slice = _slice_field.get_item_text(index)
|
||||
_save_config()
|
||||
|
||||
|
||||
func _on_slice_button_down():
|
||||
if _source == "":
|
||||
_show_message("Please, select source file first.")
|
||||
return
|
||||
|
||||
var slices = _get_available_slices(ProjectSettings.globalize_path(_source))
|
||||
var current = 0
|
||||
_slice_field.clear()
|
||||
_slice_field.add_item(_layer_default)
|
||||
|
||||
for s in slices:
|
||||
if s == "":
|
||||
continue
|
||||
|
||||
_slice_field.add_item(s)
|
||||
if s == _slice:
|
||||
current = _slice_field.get_item_count() - 1
|
||||
_slice_field.select(current)
|
||||
|
||||
|
||||
func _on_source_pressed():
|
||||
_open_source_dialog()
|
||||
|
||||
##
|
||||
## Save current import options to node metadata
|
||||
##
|
||||
func _save_config():
|
||||
var child_config = _get_current_field_values()
|
||||
|
||||
var cfg := {
|
||||
"source": _source,
|
||||
"layer": _layer,
|
||||
"slice": _slice,
|
||||
"o_folder": _output_folder,
|
||||
"o_name": _out_filename_field.text,
|
||||
"only_visible": _visible_layers_field.button_pressed,
|
||||
"o_ex_p": _ex_pattern_field.text,
|
||||
}
|
||||
|
||||
for c in child_config:
|
||||
cfg[c] = child_config[c]
|
||||
|
||||
wizard_config.save_config(target_node, cfg)
|
||||
|
||||
|
||||
func _get_import_options(default_folder: String):
|
||||
return {
|
||||
"output_folder": _output_folder if _output_folder != "" else default_folder,
|
||||
"exception_pattern": _ex_pattern_field.text,
|
||||
"only_visible_layers": _visible_layers_field.button_pressed,
|
||||
"output_filename": _out_filename_field.text,
|
||||
"layer": _layer
|
||||
}
|
||||
|
||||
|
||||
func _open_source_dialog():
|
||||
_file_dialog_aseprite = _create_aseprite_file_selection()
|
||||
get_parent().add_child(_file_dialog_aseprite)
|
||||
if _source != "":
|
||||
_file_dialog_aseprite.current_dir = ProjectSettings.globalize_path(_source.get_base_dir())
|
||||
_file_dialog_aseprite.popup_centered_ratio()
|
||||
|
||||
|
||||
func _create_aseprite_file_selection():
|
||||
var file_dialog = EditorFileDialog.new()
|
||||
file_dialog.file_mode = EditorFileDialog.FILE_MODE_OPEN_FILE
|
||||
file_dialog.access = EditorFileDialog.ACCESS_FILESYSTEM
|
||||
file_dialog.connect("file_selected",Callable(self,"_on_aseprite_file_selected"))
|
||||
file_dialog.set_filters(PackedStringArray(["*.ase","*.aseprite"]))
|
||||
return file_dialog
|
||||
|
||||
|
||||
func _on_aseprite_file_selected(path):
|
||||
_set_source(ProjectSettings.localize_path(path))
|
||||
_save_config()
|
||||
_file_dialog_aseprite.queue_free()
|
||||
|
||||
|
||||
func _on_source_aseprite_file_dropped(path):
|
||||
_set_source(path)
|
||||
_save_config()
|
||||
|
||||
|
||||
## Helper method to populate field with values
|
||||
func _populate_options_field(field: OptionButton, values: Array, current_name: String):
|
||||
var current = 0
|
||||
field.clear()
|
||||
field.add_item("[all]")
|
||||
|
||||
for v in values:
|
||||
if v == "":
|
||||
continue
|
||||
|
||||
field.add_item(v)
|
||||
if v == current_name:
|
||||
current = field.get_item_count() - 1
|
||||
field.select(current)
|
||||
|
||||
|
||||
func _on_out_folder_pressed():
|
||||
_output_folder_dialog = _create_output_folder_selection()
|
||||
get_parent().add_child(_output_folder_dialog)
|
||||
if _output_folder != _out_folder_default:
|
||||
_output_folder_dialog.current_dir = _output_folder
|
||||
_output_folder_dialog.popup_centered_ratio()
|
||||
|
||||
|
||||
func _create_output_folder_selection():
|
||||
var file_dialog = EditorFileDialog.new()
|
||||
file_dialog.file_mode = EditorFileDialog.FILE_MODE_OPEN_DIR
|
||||
file_dialog.access = EditorFileDialog.ACCESS_RESOURCES
|
||||
file_dialog.connect("dir_selected",Callable(self,"_on_output_folder_selected"))
|
||||
return file_dialog
|
||||
|
||||
|
||||
func _on_output_folder_selected(path):
|
||||
_set_out_folder(path)
|
||||
_output_folder_dialog.queue_free()
|
||||
|
||||
|
||||
func _on_out_dir_dropped(path):
|
||||
_set_out_folder(path)
|
||||
|
||||
|
||||
func _show_message(message: String):
|
||||
var _warning_dialog = AcceptDialog.new()
|
||||
get_parent().add_child(_warning_dialog)
|
||||
_warning_dialog.dialog_text = message
|
||||
_warning_dialog.popup_centered()
|
||||
_warning_dialog.connect("popup_hide",Callable(_warning_dialog,"queue_free"))
|
||||
|
||||
|
||||
func _notify_aseprite_error(aseprite_error_code):
|
||||
var error = result_code.get_error_message(aseprite_error_code)
|
||||
printerr(error)
|
||||
_show_message(error)
|
||||
|
||||
|
||||
func _handle_cleanup(aseprite_content):
|
||||
if config.should_remove_source_files():
|
||||
DirAccess.remove_absolute(aseprite_content.data_file)
|
||||
file_system.call_deferred("scan")
|
||||
|
||||
|
||||
func _on_import_pressed():
|
||||
if _importing:
|
||||
return
|
||||
_importing = true
|
||||
|
||||
if _source == "":
|
||||
_show_message("Aseprite file not selected")
|
||||
_importing = false
|
||||
return
|
||||
|
||||
await _do_import()
|
||||
_importing = false
|
||||
$dock_fields.hide_source_change_warning()
|
||||
EditorInterface.save_scene()
|
||||
|
||||
|
||||
# This is a little bit leaky as this base scene contains fields only relevant to animation players.
|
||||
# However, this is the simplest thing I can do without overcomplicating stuff.
|
||||
func _hide_fields():
|
||||
$dock_fields/VBoxContainer/modes.hide()
|
||||
$dock_fields/VBoxContainer/animation_player.hide()
|
||||
$dock_fields/VBoxContainer/extra/sections/animation.hide()
|
||||
|
||||
|
||||
## this will be called before base class does its setup
|
||||
func _pre_setup():
|
||||
pass
|
||||
|
||||
|
||||
## this will be called after base class setup is complete
|
||||
func _setup():
|
||||
pass
|
||||
|
||||
|
||||
func _load_default_config():
|
||||
pass
|
||||
|
||||
|
||||
func _load_config(cfg: Dictionary):
|
||||
pass
|
||||
|
||||
|
||||
## Override to return available layers
|
||||
func _get_available_layers(global_source_path: String) -> Array:
|
||||
return []
|
||||
|
||||
|
||||
## Override to return available slices
|
||||
func _get_available_slices(global_source_path: String) -> Array:
|
||||
return []
|
||||
|
||||
|
||||
## Override this method for extra import options to add to node metadata
|
||||
func _get_current_field_values() -> Dictionary:
|
||||
return {}
|
||||
|
||||
|
||||
func _do_import():
|
||||
pass
|
||||
|
||||
|
||||
func _show_specific_fields() -> void:
|
||||
pass
|
345
addons/AsepriteWizard/interface/docks/base_inspector_dock.tscn
Normal file
345
addons/AsepriteWizard/interface/docks/base_inspector_dock.tscn
Normal file
|
@ -0,0 +1,345 @@
|
|||
[gd_scene load_steps=8 format=3 uid="uid://ljeu0l1ld6v5"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/AsepriteWizard/interface/docks/base_inspector_dock.gd" id="1_0bpq8"]
|
||||
[ext_resource type="PackedScene" uid="uid://x1f1t87m582u" path="res://addons/AsepriteWizard/interface/shared/animation_player_drop_button.tscn" id="2_pge1b"]
|
||||
[ext_resource type="PackedScene" uid="uid://dj1uo3blocr8e" path="res://addons/AsepriteWizard/interface/shared/source_drop_button.tscn" id="3_nt1oj"]
|
||||
[ext_resource type="PackedScene" uid="uid://cwvgnm3o7eed2" path="res://addons/AsepriteWizard/interface/shared/dir_drop_button.tscn" id="4_r7t2l"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_x6usu"]
|
||||
content_margin_left = 4.0
|
||||
content_margin_top = 4.0
|
||||
content_margin_right = 4.0
|
||||
content_margin_bottom = 4.0
|
||||
bg_color = Color(0.225, 0.225, 0.225, 0.6)
|
||||
corner_radius_top_left = 3
|
||||
corner_radius_top_right = 3
|
||||
corner_radius_bottom_right = 3
|
||||
corner_radius_bottom_left = 3
|
||||
corner_detail = 5
|
||||
|
||||
[sub_resource type="Image" id="Image_46k7x"]
|
||||
data = {
|
||||
"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 93, 93, 41, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),
|
||||
"format": "RGBA8",
|
||||
"height": 16,
|
||||
"mipmaps": false,
|
||||
"width": 16
|
||||
}
|
||||
|
||||
[sub_resource type="ImageTexture" id="ImageTexture_dxtgh"]
|
||||
image = SubResource("Image_46k7x")
|
||||
|
||||
[node name="base_inspector_dock" type="PanelContainer"]
|
||||
offset_right = 14.0
|
||||
offset_bottom = 14.0
|
||||
script = ExtResource("1_0bpq8")
|
||||
|
||||
[node name="margin" type="MarginContainer" parent="."]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="margin"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="title" type="Button" parent="margin/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
focus_mode = 0
|
||||
theme_override_styles/normal = SubResource("StyleBoxFlat_x6usu")
|
||||
button_mask = 0
|
||||
text = "Aseprite"
|
||||
|
||||
[node name="section_title" type="PanelContainer" parent="margin/VBoxContainer"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="margin/VBoxContainer/section_title"]
|
||||
layout_mode = 2
|
||||
alignment = 1
|
||||
|
||||
[node name="icon" type="TextureRect" parent="margin/VBoxContainer/section_title/HBoxContainer"]
|
||||
custom_minimum_size = Vector2(16, 16)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="title" type="Label" parent="margin/VBoxContainer/section_title/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
text = "Aseprite"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="modes" type="HBoxContainer" parent="margin/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Import mode.
|
||||
Animation mode (default): set spritesheet as texture and imports animations to the selected AnimationPlayer.
|
||||
Image mode: Import only first frame and set as texture."
|
||||
|
||||
[node name="Label" type="Label" parent="margin/VBoxContainer/modes"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 2.0
|
||||
text = "Mode"
|
||||
|
||||
[node name="options" type="OptionButton" parent="margin/VBoxContainer/modes"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 2.0
|
||||
item_count = 2
|
||||
selected = 0
|
||||
popup/item_0/text = "Animation"
|
||||
popup/item_0/id = 0
|
||||
popup/item_1/text = "Image"
|
||||
popup/item_1/id = 1
|
||||
|
||||
[node name="animation_player" type="HBoxContainer" parent="margin/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "AnimationPlayer node where animations should be added to."
|
||||
|
||||
[node name="Label" type="Label" parent="margin/VBoxContainer/animation_player"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 2.0
|
||||
text = "AnimationPlayer"
|
||||
|
||||
[node name="options" parent="margin/VBoxContainer/animation_player" instance=ExtResource("2_pge1b")]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="source" type="HBoxContainer" parent="margin/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Location of the Aseprite (*.ase, *.aseprite) source file."
|
||||
|
||||
[node name="Label" type="Label" parent="margin/VBoxContainer/source"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 2.0
|
||||
text = "Aseprite File"
|
||||
|
||||
[node name="button" parent="margin/VBoxContainer/source" instance=ExtResource("3_nt1oj")]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="extra" type="MarginContainer" parent="margin/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/margin_left = 10
|
||||
|
||||
[node name="sections" type="VBoxContainer" parent="margin/VBoxContainer/extra"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="layers" type="VBoxContainer" parent="margin/VBoxContainer/extra/sections"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="section_header" type="Button" parent="margin/VBoxContainer/extra/sections/layers"]
|
||||
layout_mode = 2
|
||||
focus_mode = 0
|
||||
text = "Layers"
|
||||
icon = SubResource("ImageTexture_dxtgh")
|
||||
alignment = 0
|
||||
|
||||
[node name="section_content" type="MarginContainer" parent="margin/VBoxContainer/extra/sections/layers"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
theme_override_constants/margin_left = 10
|
||||
|
||||
[node name="content" type="VBoxContainer" parent="margin/VBoxContainer/extra/sections/layers/section_content"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="layer" type="HBoxContainer" parent="margin/VBoxContainer/extra/sections/layers/section_content/content"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Aseprite layer to be used in the animation. By default all layers are included."
|
||||
|
||||
[node name="Label" type="Label" parent="margin/VBoxContainer/extra/sections/layers/section_content/content/layer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 2.0
|
||||
text = "Layer"
|
||||
|
||||
[node name="options" type="OptionButton" parent="margin/VBoxContainer/extra/sections/layers/section_content/content/layer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 2.0
|
||||
item_count = 1
|
||||
selected = 0
|
||||
popup/item_0/text = "[all]"
|
||||
popup/item_0/id = 0
|
||||
|
||||
[node name="ex_pattern" type="HBoxContainer" parent="margin/VBoxContainer/extra/sections/layers/section_content/content"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Exclude layers with name matching this pattern (regex)."
|
||||
|
||||
[node name="Label" type="Label" parent="margin/VBoxContainer/extra/sections/layers/section_content/content/ex_pattern"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 2.0
|
||||
text = "Exclude Pattern"
|
||||
|
||||
[node name="LineEdit" type="LineEdit" parent="margin/VBoxContainer/extra/sections/layers/section_content/content/ex_pattern"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 2.0
|
||||
|
||||
[node name="visible_layers" type="HBoxContainer" parent="margin/VBoxContainer/extra/sections/layers/section_content/content"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "If active, layers not visible in the source file won't be included in the final image."
|
||||
|
||||
[node name="Label" type="Label" parent="margin/VBoxContainer/extra/sections/layers/section_content/content/visible_layers"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 2.0
|
||||
text = "Only Visible Layers"
|
||||
|
||||
[node name="CheckBox" type="CheckBox" parent="margin/VBoxContainer/extra/sections/layers/section_content/content/visible_layers"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 2.0
|
||||
text = "On"
|
||||
|
||||
[node name="slices" type="VBoxContainer" parent="margin/VBoxContainer/extra/sections"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="section_header" type="Button" parent="margin/VBoxContainer/extra/sections/slices"]
|
||||
layout_mode = 2
|
||||
focus_mode = 0
|
||||
text = "Slices"
|
||||
icon = SubResource("ImageTexture_dxtgh")
|
||||
alignment = 0
|
||||
|
||||
[node name="section_content" type="MarginContainer" parent="margin/VBoxContainer/extra/sections/slices"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
theme_override_constants/margin_left = 10
|
||||
|
||||
[node name="content" type="VBoxContainer" parent="margin/VBoxContainer/extra/sections/slices/section_content"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="slice" type="HBoxContainer" parent="margin/VBoxContainer/extra/sections/slices/section_content/content"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Aseprite slice to be used in the animation. By default, the whole file is included."
|
||||
|
||||
[node name="Label" type="Label" parent="margin/VBoxContainer/extra/sections/slices/section_content/content/slice"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 2.0
|
||||
text = "Slice"
|
||||
|
||||
[node name="options" type="OptionButton" parent="margin/VBoxContainer/extra/sections/slices/section_content/content/slice"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 2.0
|
||||
item_count = 1
|
||||
selected = 0
|
||||
popup/item_0/text = "[all]"
|
||||
popup/item_0/id = 0
|
||||
|
||||
[node name="animation" type="VBoxContainer" parent="margin/VBoxContainer/extra/sections"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="section_header" type="Button" parent="margin/VBoxContainer/extra/sections/animation"]
|
||||
layout_mode = 2
|
||||
focus_mode = 0
|
||||
text = "Animation"
|
||||
icon = SubResource("ImageTexture_dxtgh")
|
||||
alignment = 0
|
||||
|
||||
[node name="section_content" type="MarginContainer" parent="margin/VBoxContainer/extra/sections/animation"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
theme_override_constants/margin_left = 10
|
||||
|
||||
[node name="content" type="VBoxContainer" parent="margin/VBoxContainer/extra/sections/animation/section_content"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="keep_length" type="HBoxContainer" parent="margin/VBoxContainer/extra/sections/animation/section_content/content"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "When this is active the animation length won't be adjusted if other properties were added and the resulting imported animation is shorter."
|
||||
|
||||
[node name="Label" type="Label" parent="margin/VBoxContainer/extra/sections/animation/section_content/content/keep_length"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 2.0
|
||||
text = "Keep Manual Animation Length"
|
||||
|
||||
[node name="CheckBox" type="CheckBox" parent="margin/VBoxContainer/extra/sections/animation/section_content/content/keep_length"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 2.0
|
||||
text = "On"
|
||||
|
||||
[node name="auto_visible_track" type="HBoxContainer" parent="margin/VBoxContainer/extra/sections/animation/section_content/content"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "If active, it will automatically determine unused Sprite2D and Sprite3D nodes in each animation and hide them."
|
||||
|
||||
[node name="Label" type="Label" parent="margin/VBoxContainer/extra/sections/animation/section_content/content/auto_visible_track"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 2.0
|
||||
text = "Hide Unused Sprites"
|
||||
|
||||
[node name="CheckBox" type="CheckBox" parent="margin/VBoxContainer/extra/sections/animation/section_content/content/auto_visible_track"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 2.0
|
||||
text = "On"
|
||||
|
||||
[node name="output" type="VBoxContainer" parent="margin/VBoxContainer/extra/sections"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="section_header" type="Button" parent="margin/VBoxContainer/extra/sections/output"]
|
||||
layout_mode = 2
|
||||
focus_mode = 0
|
||||
text = "Output"
|
||||
icon = SubResource("ImageTexture_dxtgh")
|
||||
alignment = 0
|
||||
|
||||
[node name="section_content" type="MarginContainer" parent="margin/VBoxContainer/extra/sections/output"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
theme_override_constants/margin_left = 10
|
||||
|
||||
[node name="content" type="VBoxContainer" parent="margin/VBoxContainer/extra/sections/output/section_content"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="out_folder" type="HBoxContainer" parent="margin/VBoxContainer/extra/sections/output/section_content/content"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Location where the spritesheet file should be saved."
|
||||
|
||||
[node name="Label" type="Label" parent="margin/VBoxContainer/extra/sections/output/section_content/content/out_folder"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 2.0
|
||||
text = "Output Folder"
|
||||
|
||||
[node name="button" parent="margin/VBoxContainer/extra/sections/output/section_content/content/out_folder" instance=ExtResource("4_r7t2l")]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="out_filename" type="HBoxContainer" parent="margin/VBoxContainer/extra/sections/output/section_content/content"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Base filename for spritesheet. In case the layer option is used, this works as a prefix to the layer name."
|
||||
|
||||
[node name="Label" type="Label" parent="margin/VBoxContainer/extra/sections/output/section_content/content/out_filename"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 2.0
|
||||
text = "Output File Name"
|
||||
|
||||
[node name="LineEdit" type="LineEdit" parent="margin/VBoxContainer/extra/sections/output/section_content/content/out_filename"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 2.0
|
||||
|
||||
[node name="import" type="Button" parent="margin/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
text = "Import"
|
||||
|
||||
[connection signal="item_selected" from="margin/VBoxContainer/modes/options" to="." method="_on_modes_item_selected"]
|
||||
[connection signal="button_down" from="margin/VBoxContainer/animation_player/options" to="." method="_on_options_button_down"]
|
||||
[connection signal="item_selected" from="margin/VBoxContainer/animation_player/options" to="." method="_on_options_item_selected"]
|
||||
[connection signal="node_dropped" from="margin/VBoxContainer/animation_player/options" to="." method="_on_animation_player_node_dropped"]
|
||||
[connection signal="aseprite_file_dropped" from="margin/VBoxContainer/source/button" to="." method="_on_source_aseprite_file_dropped"]
|
||||
[connection signal="pressed" from="margin/VBoxContainer/source/button" to="." method="_on_source_pressed"]
|
||||
[connection signal="button_down" from="margin/VBoxContainer/extra/sections/layers/section_header" to="." method="_on_layer_header_button_down"]
|
||||
[connection signal="button_down" from="margin/VBoxContainer/extra/sections/layers/section_content/content/layer/options" to="." method="_on_layer_button_down"]
|
||||
[connection signal="item_selected" from="margin/VBoxContainer/extra/sections/layers/section_content/content/layer/options" to="." method="_on_layer_item_selected"]
|
||||
[connection signal="button_down" from="margin/VBoxContainer/extra/sections/slices/section_header" to="." method="_on_slice_header_button_down"]
|
||||
[connection signal="button_down" from="margin/VBoxContainer/extra/sections/slices/section_content/content/slice/options" to="." method="_on_slice_button_down"]
|
||||
[connection signal="item_selected" from="margin/VBoxContainer/extra/sections/slices/section_content/content/slice/options" to="." method="_on_slice_item_selected"]
|
||||
[connection signal="button_down" from="margin/VBoxContainer/extra/sections/animation/section_header" to="." method="_on_animation_header_button_down"]
|
||||
[connection signal="button_down" from="margin/VBoxContainer/extra/sections/output/section_header" to="." method="_on_output_header_button_down"]
|
||||
[connection signal="dir_dropped" from="margin/VBoxContainer/extra/sections/output/section_content/content/out_folder/button" to="." method="_on_out_dir_dropped"]
|
||||
[connection signal="pressed" from="margin/VBoxContainer/extra/sections/output/section_content/content/out_folder/button" to="." method="_on_out_folder_pressed"]
|
||||
[connection signal="pressed" from="margin/VBoxContainer/import" to="." method="_on_import_pressed"]
|
26
addons/AsepriteWizard/interface/docks/dock_fields.gd
Normal file
26
addons/AsepriteWizard/interface/docks/dock_fields.gd
Normal file
|
@ -0,0 +1,26 @@
|
|||
@tool
|
||||
extends MarginContainer
|
||||
|
||||
|
||||
@onready
|
||||
var _source_changed_warning_container = $VBoxContainer/source_changed_warning
|
||||
@onready
|
||||
var _source_changed_warning_icon = $VBoxContainer/source_changed_warning/MarginContainer/HBoxContainer/Icon
|
||||
|
||||
|
||||
func _ready():
|
||||
var sb = _source_changed_warning_container.get_theme_stylebox("panel")
|
||||
var color = EditorInterface.get_editor_settings().get_setting("interface/theme/accent_color")
|
||||
color.a = 0.2
|
||||
sb.bg_color = color
|
||||
|
||||
_source_changed_warning_icon.texture = get_theme_icon("NodeInfo", "EditorIcons")
|
||||
hide_source_change_warning()
|
||||
|
||||
|
||||
func show_source_change_warning():
|
||||
_source_changed_warning_container.show()
|
||||
|
||||
|
||||
func hide_source_change_warning():
|
||||
_source_changed_warning_container.hide()
|
370
addons/AsepriteWizard/interface/docks/dock_fields.tscn
Normal file
370
addons/AsepriteWizard/interface/docks/dock_fields.tscn
Normal file
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,14 @@
|
|||
@tool
|
||||
extends EditorInspectorPlugin
|
||||
|
||||
const APInspectorDock = preload("./sprite_inspector_dock.tscn")
|
||||
|
||||
|
||||
func _can_handle(object):
|
||||
return object is Sprite2D || object is Sprite3D || object is TextureRect
|
||||
|
||||
|
||||
func _parse_end(object):
|
||||
var dock = APInspectorDock.instantiate()
|
||||
dock.target_node = object
|
||||
add_custom_control(dock)
|
|
@ -0,0 +1,255 @@
|
|||
@tool
|
||||
extends "../base_inspector_dock.gd"
|
||||
|
||||
const AnimationCreator = preload("../../../creators/animation_player/animation_creator.gd")
|
||||
const SpriteAnimationCreator = preload("../../../creators/animation_player/sprite_animation_creator.gd")
|
||||
const TextureRectAnimationCreator = preload("../../../creators/animation_player/texture_rect_animation_creator.gd")
|
||||
const StaticTextureCreator = preload("../../../creators/static_texture/texture_creator.gd")
|
||||
|
||||
enum ImportMode {
|
||||
ANIMATION = 0,
|
||||
IMAGE = 1
|
||||
}
|
||||
|
||||
var animation_creator: AnimationCreator
|
||||
var static_texture_creator: StaticTextureCreator
|
||||
|
||||
var _import_mode = -1
|
||||
var _animation_player_path: String
|
||||
|
||||
@onready var _import_mode_options_field := $dock_fields/VBoxContainer/modes/options as OptionButton
|
||||
@onready var _animation_player_field := $dock_fields/VBoxContainer/animation_player/options as OptionButton
|
||||
@onready var _animation_player_container := $dock_fields/VBoxContainer/animation_player as HBoxContainer
|
||||
|
||||
# animation
|
||||
@onready var _animation_section := $dock_fields/VBoxContainer/extra/sections/animation as VBoxContainer
|
||||
@onready var _animation_section_header := $dock_fields/VBoxContainer/extra/sections/animation/section_header as Button
|
||||
@onready var _animation_section_container := $dock_fields/VBoxContainer/extra/sections/animation/section_content as MarginContainer
|
||||
@onready var _cleanup_hide_unused_nodes := $dock_fields/VBoxContainer/extra/sections/animation/section_content/content/auto_visible_track/CheckBox as CheckBox
|
||||
@onready var _keep_length := $dock_fields/VBoxContainer/extra/sections/animation/section_content/content/keep_length/CheckBox as CheckBox
|
||||
|
||||
const INTERFACE_SECTION_KEY_ANIMATION = "animation_section"
|
||||
|
||||
|
||||
func _pre_setup():
|
||||
_expandable_sections[INTERFACE_SECTION_KEY_ANIMATION] = { "header": _animation_section_header, "content": _animation_section_container}
|
||||
|
||||
|
||||
func _setup():
|
||||
if target_node is Sprite2D || target_node is Sprite3D:
|
||||
animation_creator = SpriteAnimationCreator.new()
|
||||
if target_node is TextureRect:
|
||||
animation_creator = TextureRectAnimationCreator.new()
|
||||
|
||||
static_texture_creator = StaticTextureCreator.new()
|
||||
|
||||
_setup_animation_fields_listeners()
|
||||
|
||||
|
||||
func _load_config(cfg):
|
||||
if cfg.has("player"):
|
||||
_animation_player_field.clear()
|
||||
_set_animation_player(cfg.player)
|
||||
|
||||
_cleanup_hide_unused_nodes.button_pressed = cfg.get("set_vis_track", config.is_set_visible_track_automatically_enabled())
|
||||
_keep_length.button_pressed = cfg.get("keep_anim_length", false)
|
||||
_set_import_mode(int(cfg.get("i_mode", 0)))
|
||||
|
||||
|
||||
func _load_default_config():
|
||||
_cleanup_hide_unused_nodes.button_pressed = config.is_set_visible_track_automatically_enabled()
|
||||
|
||||
|
||||
func _set_animation_player(player):
|
||||
_animation_player_path = player
|
||||
_animation_player_field.add_item(_animation_player_path)
|
||||
|
||||
|
||||
func _set_import_mode(import_mode):
|
||||
if _import_mode == import_mode:
|
||||
return
|
||||
|
||||
_import_mode = import_mode
|
||||
var index = _import_mode_options_field.get_item_index(import_mode)
|
||||
_import_mode_options_field.select(index)
|
||||
_handle_import_mode()
|
||||
|
||||
|
||||
func _handle_import_mode():
|
||||
match _import_mode:
|
||||
ImportMode.ANIMATION:
|
||||
_animation_player_container.show()
|
||||
_animation_section.show()
|
||||
ImportMode.IMAGE:
|
||||
_animation_player_container.hide()
|
||||
_animation_section.hide()
|
||||
|
||||
|
||||
func _setup_animation_fields_listeners():
|
||||
_animation_section_header.button_down.connect(_on_animation_header_button_down)
|
||||
_animation_player_field.node_dropped.connect(_on_animation_player_node_dropped)
|
||||
_animation_player_field.button_down.connect(_on_animation_player_button_down)
|
||||
_animation_player_field.item_selected.connect(_on_animation_player_item_selected)
|
||||
|
||||
_import_mode_options_field.item_selected.connect(_on_modes_item_selected)
|
||||
|
||||
|
||||
func _on_animation_player_button_down():
|
||||
_refresh_animation_players()
|
||||
|
||||
|
||||
func _refresh_animation_players():
|
||||
var animation_players = []
|
||||
var root = get_tree().get_edited_scene_root()
|
||||
_find_animation_players(root, root, animation_players)
|
||||
|
||||
var current = 0
|
||||
_animation_player_field.clear()
|
||||
_animation_player_field.add_item("[empty]")
|
||||
|
||||
for ap in animation_players:
|
||||
_animation_player_field.add_item(ap)
|
||||
if ap.get_concatenated_names() == _animation_player_path:
|
||||
current = _animation_player_field.get_item_count() - 1
|
||||
|
||||
_animation_player_field.select(current)
|
||||
|
||||
|
||||
func _find_animation_players(root: Node, node: Node, players: Array):
|
||||
if node is AnimationPlayer:
|
||||
players.push_back(root.get_path_to(node))
|
||||
|
||||
for c in node.get_children():
|
||||
_find_animation_players(root, c, players)
|
||||
|
||||
|
||||
func _on_animation_player_item_selected(index):
|
||||
if index == 0:
|
||||
_animation_player_path = ""
|
||||
return
|
||||
_animation_player_path = _animation_player_field.get_item_text(index)
|
||||
_save_config()
|
||||
|
||||
|
||||
func _do_import():
|
||||
if _import_mode == ImportMode.IMAGE:
|
||||
await _import_static()
|
||||
return
|
||||
|
||||
await _import_for_animation_player()
|
||||
|
||||
##
|
||||
## Import aseprite animations to target AnimationPlayer and set
|
||||
## spritesheet as the node's texture
|
||||
##
|
||||
func _import_for_animation_player():
|
||||
var root = get_tree().get_edited_scene_root()
|
||||
|
||||
if _animation_player_path == "" or not root.has_node(_animation_player_path):
|
||||
_show_message("AnimationPlayer not found")
|
||||
_importing = false
|
||||
return
|
||||
|
||||
var source_path = ProjectSettings.globalize_path(_source)
|
||||
|
||||
var options = _get_import_options(root.scene_file_path.get_base_dir())
|
||||
|
||||
_save_config()
|
||||
|
||||
var aseprite_output = _aseprite_file_exporter.generate_aseprite_file(source_path, options)
|
||||
|
||||
if not aseprite_output.is_ok:
|
||||
_notify_aseprite_error(aseprite_output.code)
|
||||
return
|
||||
|
||||
file_system.scan()
|
||||
await file_system.filesystem_changed
|
||||
|
||||
var anim_options = {
|
||||
"keep_anim_length": _keep_length.button_pressed,
|
||||
"cleanup_hide_unused_nodes": _cleanup_hide_unused_nodes.button_pressed,
|
||||
"slice": _slice,
|
||||
}
|
||||
|
||||
animation_creator.create_animations(target_node, root.get_node(_animation_player_path), aseprite_output.content, anim_options)
|
||||
_importing = false
|
||||
|
||||
wizard_config.set_source_hash(target_node, FileAccess.get_md5(source_path))
|
||||
_handle_cleanup(aseprite_output.content)
|
||||
|
||||
##
|
||||
## Import first frame from aseprite file as node texture
|
||||
##
|
||||
func _import_static():
|
||||
var source_path = ProjectSettings.globalize_path(_source)
|
||||
var root = get_tree().get_edited_scene_root()
|
||||
|
||||
var options = _get_import_options(root.scene_file_path.get_base_dir())
|
||||
options["first_frame_only"] = true
|
||||
|
||||
_save_config()
|
||||
|
||||
var aseprite_output = _aseprite_file_exporter.generate_aseprite_file(source_path, options)
|
||||
|
||||
if not aseprite_output.is_ok:
|
||||
_notify_aseprite_error(aseprite_output.code)
|
||||
return
|
||||
|
||||
file_system.scan()
|
||||
await file_system.filesystem_changed
|
||||
|
||||
static_texture_creator.load_texture(target_node, aseprite_output.content, { "slice": _slice })
|
||||
|
||||
_importing = false
|
||||
wizard_config.set_source_hash(target_node, FileAccess.get_md5(source_path))
|
||||
_handle_cleanup(aseprite_output.content)
|
||||
|
||||
|
||||
func _get_current_field_values() -> Dictionary:
|
||||
var cfg := {
|
||||
"i_mode": _import_mode,
|
||||
"player": _animation_player_path,
|
||||
"keep_anim_length": _keep_length.button_pressed,
|
||||
}
|
||||
|
||||
if _cleanup_hide_unused_nodes.button_pressed != config.is_set_visible_track_automatically_enabled():
|
||||
cfg["set_vis_track"] = _cleanup_hide_unused_nodes.button_pressed
|
||||
|
||||
return cfg
|
||||
|
||||
|
||||
func _get_available_layers(global_source_path: String) -> Array:
|
||||
return animation_creator.list_layers(global_source_path)
|
||||
|
||||
|
||||
func _get_available_slices(global_source_path: String) -> Array:
|
||||
return animation_creator.list_slices(global_source_path)
|
||||
|
||||
|
||||
func _on_animation_player_node_dropped(node_path):
|
||||
var node = get_node(node_path)
|
||||
var root = get_tree().get_edited_scene_root()
|
||||
|
||||
_animation_player_path = root.get_path_to(node)
|
||||
|
||||
for i in range(_animation_player_field.get_item_count()):
|
||||
if _animation_player_field.get_item_text(i) == _animation_player_path:
|
||||
_animation_player_field.select(i)
|
||||
break
|
||||
_save_config()
|
||||
|
||||
|
||||
func _on_modes_item_selected(index):
|
||||
var id = _import_mode_options_field.get_item_id(index)
|
||||
_import_mode = id
|
||||
_handle_import_mode()
|
||||
|
||||
|
||||
func _on_animation_header_button_down():
|
||||
_toggle_section_visibility(INTERFACE_SECTION_KEY_ANIMATION)
|
||||
|
||||
|
||||
func _show_specific_fields():
|
||||
_import_mode_options_field.get_parent().show()
|
||||
_animation_player_container.show()
|
||||
_animation_section.show()
|
|
@ -0,0 +1,12 @@
|
|||
[gd_scene load_steps=3 format=3 uid="uid://biyshalfalqqw"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/AsepriteWizard/interface/docks/sprite/sprite_inspector_dock.gd" id="1_ku6wj"]
|
||||
[ext_resource type="PackedScene" uid="uid://uxm7b02wry10" path="res://addons/AsepriteWizard/interface/docks/dock_fields.tscn" id="2_wkqx4"]
|
||||
|
||||
[node name="sprite_inspector_dock" type="PanelContainer"]
|
||||
offset_right = 14.0
|
||||
offset_bottom = 14.0
|
||||
script = ExtResource("1_ku6wj")
|
||||
|
||||
[node name="dock_fields" parent="." instance=ExtResource("2_wkqx4")]
|
||||
layout_mode = 2
|
|
@ -0,0 +1,39 @@
|
|||
@tool
|
||||
extends TabContainer
|
||||
|
||||
signal close_requested
|
||||
|
||||
const WizardWindow = preload("./as_wizard_window.tscn")
|
||||
|
||||
func _ready():
|
||||
$Import.close_requested.connect(emit_signal.bind("close_requested"))
|
||||
$Import.import_success.connect(_on_import_success)
|
||||
$History.request_edit.connect(_on_edit_request)
|
||||
$History.request_import.connect(_on_import_request)
|
||||
$ImportedSpriteFrames.import_success.connect($History.add_entry)
|
||||
|
||||
self.set_tab_title(1, "Imported Resources")
|
||||
|
||||
|
||||
func _on_AsWizardDockContainer_tab_changed(tab: int):
|
||||
match tab:
|
||||
1:
|
||||
$ImportedSpriteFrames.init_resources()
|
||||
2:
|
||||
$History.reload()
|
||||
|
||||
|
||||
func _on_edit_request(import_cfg: Dictionary):
|
||||
$Import.load_import_config(import_cfg)
|
||||
self.current_tab = 0
|
||||
|
||||
|
||||
func _on_import_request(import_cfg: Dictionary):
|
||||
$Import.load_import_config(import_cfg)
|
||||
$Import.trigger_import()
|
||||
|
||||
|
||||
func _on_import_success(settings: Dictionary):
|
||||
$ImportedSpriteFrames.init_resources()
|
||||
$ImportedSpriteFrames.reload_tree()
|
||||
$History.add_entry(settings)
|
|
@ -0,0 +1,28 @@
|
|||
[gd_scene load_steps=5 format=3 uid="uid://b844j1tk3vxer"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://c5dwobjd34w3p" path="res://addons/AsepriteWizard/interface/docks/wizard/as_wizard_window.tscn" id="1"]
|
||||
[ext_resource type="Script" path="res://addons/AsepriteWizard/interface/docks/wizard/as_wizard_dock_container.gd" id="2"]
|
||||
[ext_resource type="PackedScene" uid="uid://cyoin5ncul0fm" path="res://addons/AsepriteWizard/interface/docks/wizard/sprite_frames_import_history.tscn" id="3"]
|
||||
[ext_resource type="PackedScene" uid="uid://bfhsdslj8kt7b" path="res://addons/AsepriteWizard/interface/docks/wizard/imported_sprite_frames.tscn" id="3_25qb4"]
|
||||
|
||||
[node name="AsWizardDockContainer" type="TabContainer"]
|
||||
offset_right = 281.0
|
||||
offset_bottom = 36.0
|
||||
tab_alignment = 2
|
||||
use_hidden_tabs_for_min_size = true
|
||||
script = ExtResource("2")
|
||||
|
||||
[node name="Import" parent="." instance=ExtResource("1")]
|
||||
layout_mode = 2
|
||||
tooltip_text = "SpriteFrames Importer"
|
||||
|
||||
[node name="ImportedSpriteFrames" parent="." instance=ExtResource("3_25qb4")]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
metadata/_tab_name = "Imported Resources"
|
||||
|
||||
[node name="History" parent="." instance=ExtResource("3")]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
|
||||
[connection signal="tab_changed" from="." to="." method="_on_AsWizardDockContainer_tab_changed"]
|
274
addons/AsepriteWizard/interface/docks/wizard/as_wizard_window.gd
Normal file
274
addons/AsepriteWizard/interface/docks/wizard/as_wizard_window.gd
Normal file
|
@ -0,0 +1,274 @@
|
|||
@tool
|
||||
extends PanelContainer
|
||||
|
||||
signal close_requested
|
||||
signal import_success(file_settings)
|
||||
|
||||
const result_code = preload("../../../config/result_codes.gd")
|
||||
var _config = preload("../../../config/config.gd").new()
|
||||
|
||||
var _import_helper = preload("./wizard_import_helper.gd").new()
|
||||
|
||||
var _file_system: EditorFileSystem = EditorInterface.get_resource_filesystem()
|
||||
|
||||
var _file_dialog_aseprite: EditorFileDialog
|
||||
var _output_folder_dialog: EditorFileDialog
|
||||
var _warning_dialog: AcceptDialog
|
||||
|
||||
@onready var _layer_section_btn: Button = $container/options/layer_section/header/section_header
|
||||
@onready var _layer_section_content: MarginContainer = $container/options/layer_section/section_content
|
||||
|
||||
@onready var _output_section_btn: Button = $container/options/output_section/header/section_header
|
||||
@onready var _output_section_content: MarginContainer = $container/options/output_section/section_content
|
||||
|
||||
@onready var _file_location_field: LineEdit = $container/options/file_location/HBoxContainer/file_location_path
|
||||
|
||||
@onready var _output_folder_field: LineEdit = $container/options/output_section/section_content/items/output_folder/HBoxContainer/file_location_path
|
||||
|
||||
@onready var _exception_pattern_field: LineEdit = $container/options/layer_section/section_content/items/exclude_pattern/pattern
|
||||
|
||||
@onready var _split_mode_field: CheckBox = $container/options/layer_section/section_content/items/split_layers/field
|
||||
|
||||
@onready var _only_visible_layers_field: CheckBox = $container/options/layer_section/section_content/items/visible_layers/field
|
||||
|
||||
@onready var _custom_name_field: LineEdit = $container/options/output_section/section_content/items/custom_filename/pattern
|
||||
|
||||
@onready var _do_not_create_res_field: CheckBox = $container/options/output_section/section_content/items/disable_resource_creation/field
|
||||
|
||||
const INTERFACE_SECTION_KEY_LAYER = "layer_section"
|
||||
const INTERFACE_SECTION_KEY_OUTPUT = "output_section"
|
||||
|
||||
@onready var _expandable_sections = {
|
||||
INTERFACE_SECTION_KEY_LAYER: { "header": _layer_section_btn, "content": _layer_section_content},
|
||||
INTERFACE_SECTION_KEY_OUTPUT: { "header": _output_section_btn, "content": _output_section_content},
|
||||
}
|
||||
|
||||
var _interface_section_state = {}
|
||||
|
||||
|
||||
func _ready():
|
||||
_configure_sections()
|
||||
_load_persisted_config()
|
||||
|
||||
|
||||
func _exit_tree():
|
||||
if is_instance_valid(_file_dialog_aseprite):
|
||||
_file_dialog_aseprite.queue_free()
|
||||
if is_instance_valid(_output_folder_dialog):
|
||||
_output_folder_dialog.queue_free()
|
||||
if is_instance_valid(_warning_dialog):
|
||||
_warning_dialog.queue_free()
|
||||
|
||||
|
||||
func _init_aseprite_file_selection_dialog():
|
||||
_file_dialog_aseprite = _create_aseprite_file_selection()
|
||||
get_parent().get_parent().add_child(_file_dialog_aseprite)
|
||||
|
||||
|
||||
func _init_output_folder_selection_dialog():
|
||||
_output_folder_dialog = _create_outuput_folder_selection()
|
||||
get_parent().get_parent().add_child(_output_folder_dialog)
|
||||
|
||||
|
||||
func _init_warning_dialog():
|
||||
_warning_dialog = AcceptDialog.new()
|
||||
_warning_dialog.exclusive = false
|
||||
get_parent().get_parent().add_child(_warning_dialog)
|
||||
|
||||
|
||||
func _load_persisted_config() -> void:
|
||||
var cfg = _load_last_import_cfg()
|
||||
_load_config(cfg)
|
||||
|
||||
|
||||
func _load_config(cfg: Dictionary) -> void:
|
||||
_split_mode_field.button_pressed = cfg.split_layers
|
||||
_only_visible_layers_field.button_pressed = cfg.only_visible_layers
|
||||
_exception_pattern_field.text = cfg.layer_exclusion_pattern
|
||||
_custom_name_field.text = cfg.output_name
|
||||
_file_location_field.text = cfg.source_file
|
||||
_do_not_create_res_field.button_pressed = cfg.do_not_create_resource
|
||||
_output_folder_field.text = cfg.output_location if cfg.output_location != "" else "res://"
|
||||
|
||||
|
||||
func _save_config() -> void:
|
||||
_config.set_standalone_spriteframes_last_import_config(_get_field_values())
|
||||
|
||||
|
||||
func _get_field_values() -> Dictionary:
|
||||
return {
|
||||
"split_layers": _split_mode_field.button_pressed,
|
||||
"only_visible_layers": _only_visible_layers_field.button_pressed,
|
||||
"layer_exclusion_pattern": _exception_pattern_field.text,
|
||||
"output_name": _custom_name_field.text,
|
||||
"source_file": _file_location_field.text,
|
||||
"do_not_create_resource": _do_not_create_res_field.button_pressed,
|
||||
"output_location": _output_folder_field.text if _output_folder_field.text != "" else "res://",
|
||||
}
|
||||
|
||||
|
||||
func _clear_config() -> void:
|
||||
_config.clear_standalone_spriteframes_last_import_config()
|
||||
var default = _get_default_config()
|
||||
_load_config(default)
|
||||
|
||||
|
||||
## This is used by the other tabs to set the form fields
|
||||
func load_import_config(field_values: Dictionary):
|
||||
_split_mode_field.button_pressed = field_values.split_layers
|
||||
_only_visible_layers_field.button_pressed = field_values.only_visible_layers
|
||||
_exception_pattern_field.text = field_values.layer_exclusion_pattern
|
||||
_custom_name_field.text = field_values.output_name
|
||||
_file_location_field.text = field_values.source_file
|
||||
_do_not_create_res_field.button_pressed = field_values.do_not_create_resource
|
||||
_output_folder_field.text = field_values.output_location
|
||||
|
||||
|
||||
func _open_aseprite_file_selection_dialog():
|
||||
if not is_instance_valid(_file_dialog_aseprite):
|
||||
_init_aseprite_file_selection_dialog()
|
||||
|
||||
var current_selection = _file_location_field.text
|
||||
if current_selection != "":
|
||||
_file_dialog_aseprite.current_dir = current_selection.get_base_dir()
|
||||
_file_dialog_aseprite.popup_centered_ratio()
|
||||
|
||||
|
||||
func _open_output_folder_selection_dialog():
|
||||
if not is_instance_valid(_output_folder_dialog):
|
||||
_init_output_folder_selection_dialog()
|
||||
var current_selection = _output_folder_field.text
|
||||
if current_selection != "":
|
||||
_output_folder_dialog.current_dir = current_selection
|
||||
_output_folder_dialog.popup_centered_ratio()
|
||||
|
||||
|
||||
func _create_aseprite_file_selection():
|
||||
var file_dialog = EditorFileDialog.new()
|
||||
file_dialog.file_mode = EditorFileDialog.FILE_MODE_OPEN_FILE
|
||||
file_dialog.access = EditorFileDialog.ACCESS_FILESYSTEM
|
||||
file_dialog.connect("file_selected", _on_aseprite_file_selected)
|
||||
file_dialog.set_filters(PackedStringArray(["*.ase","*.aseprite"]))
|
||||
return file_dialog
|
||||
|
||||
|
||||
func _create_outuput_folder_selection():
|
||||
var file_dialog = EditorFileDialog.new()
|
||||
file_dialog.file_mode = EditorFileDialog.FILE_MODE_OPEN_DIR
|
||||
file_dialog.access = EditorFileDialog.ACCESS_RESOURCES
|
||||
file_dialog.connect("dir_selected", _on_output_folder_selected)
|
||||
return file_dialog
|
||||
|
||||
|
||||
func _on_aseprite_file_selected(path):
|
||||
var localized_path = ProjectSettings.localize_path(path)
|
||||
_file_location_field.text = localized_path
|
||||
|
||||
|
||||
func _on_output_folder_selected(path):
|
||||
_output_folder_field.text = path
|
||||
|
||||
|
||||
func _on_next_btn_up():
|
||||
var aseprite_file = _file_location_field.text
|
||||
var fields = _get_field_values()
|
||||
var exit_code = await _import_helper.import_and_create_resources(aseprite_file, _get_field_values())
|
||||
|
||||
if exit_code != OK:
|
||||
_show_error(exit_code)
|
||||
return
|
||||
|
||||
emit_signal("import_success", fields)
|
||||
|
||||
if _config.should_remove_source_files():
|
||||
_file_system.call_deferred("scan")
|
||||
|
||||
_show_import_success_message()
|
||||
|
||||
|
||||
func trigger_import():
|
||||
_on_next_btn_up()
|
||||
|
||||
|
||||
func _on_close_btn_up():
|
||||
_close_window()
|
||||
|
||||
|
||||
func _close_window():
|
||||
_save_config()
|
||||
self.emit_signal("close_requested")
|
||||
|
||||
|
||||
func _on_clear_button_up():
|
||||
_clear_config()
|
||||
|
||||
|
||||
func _show_error(code: int):
|
||||
_show_error_message(result_code.get_error_message(code))
|
||||
|
||||
|
||||
func _show_error_message(message: String):
|
||||
if not is_instance_valid(_warning_dialog):
|
||||
_init_warning_dialog()
|
||||
_warning_dialog.dialog_text = "Error: %s" % message
|
||||
_warning_dialog.popup_centered()
|
||||
|
||||
|
||||
func _show_import_success_message():
|
||||
if not is_instance_valid(_warning_dialog):
|
||||
_init_warning_dialog()
|
||||
_warning_dialog.dialog_text = "Aseprite import succeeded"
|
||||
_warning_dialog.popup_centered()
|
||||
_save_config()
|
||||
|
||||
|
||||
func _configure_sections():
|
||||
for key in _expandable_sections:
|
||||
_adjust_section_visibility(key)
|
||||
|
||||
|
||||
func _adjust_section_visibility(key: String) -> void:
|
||||
var section = _expandable_sections[key]
|
||||
var is_visible = _interface_section_state.get(key, true)
|
||||
_adjust_icon(section.header, is_visible)
|
||||
section.content.visible = is_visible
|
||||
|
||||
|
||||
func _adjust_icon(section: Button, is_visible: bool = true) -> void:
|
||||
var icon_name = "GuiTreeArrowDown" if is_visible else "GuiTreeArrowRight"
|
||||
section.icon = get_theme_icon(icon_name, "EditorIcons")
|
||||
|
||||
|
||||
func _toggle_section_visibility(key: String) -> void:
|
||||
_interface_section_state[key] = not _interface_section_state.get(key, true)
|
||||
_adjust_section_visibility(key)
|
||||
|
||||
|
||||
func _on_layer_section_header_button_down():
|
||||
_toggle_section_visibility(INTERFACE_SECTION_KEY_LAYER)
|
||||
|
||||
|
||||
func _on_output_section_header_button_down():
|
||||
_toggle_section_visibility(INTERFACE_SECTION_KEY_OUTPUT)
|
||||
|
||||
|
||||
func _load_last_import_cfg() -> Dictionary:
|
||||
var cfg = _config.get_standalone_spriteframes_last_import_config()
|
||||
|
||||
if cfg.is_empty():
|
||||
return _get_default_config()
|
||||
|
||||
return cfg
|
||||
|
||||
|
||||
func _get_default_config() -> Dictionary:
|
||||
return {
|
||||
"split_layers": false,
|
||||
"only_visible_layers": _config.should_include_only_visible_layers_by_default(),
|
||||
"layer_exclusion_pattern": _config.get_default_exclusion_pattern(),
|
||||
"output_name": "",
|
||||
"source_file": "",
|
||||
"do_not_create_resource": false,
|
||||
"output_location": "res://",
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,304 @@
|
|||
@tool
|
||||
extends PanelContainer
|
||||
|
||||
signal import_success(fields)
|
||||
|
||||
const result_code = preload("../../../config/result_codes.gd")
|
||||
const wizard_config = preload("../../../config/wizard_config.gd")
|
||||
var _import_helper = preload("./wizard_import_helper.gd").new()
|
||||
|
||||
@onready var _tree_container = $MarginContainer/HSplitContainer/tree
|
||||
@onready var _resource_tree = _tree_container.get_resource_tree()
|
||||
@onready var _nothing_container = $MarginContainer/HSplitContainer/MarginContainer/VBoxContainer/nothing
|
||||
@onready var _single_item_container = $MarginContainer/HSplitContainer/MarginContainer/VBoxContainer/single_item
|
||||
@onready var _multiple_items_container = $MarginContainer/HSplitContainer/MarginContainer/VBoxContainer/multiple_items
|
||||
@onready var _confirmation_warning_container = $MarginContainer/HSplitContainer/MarginContainer/VBoxContainer/confirmation_warning
|
||||
|
||||
var _selection_count = 0
|
||||
|
||||
var _current_container = null
|
||||
var _resources_to_process = null
|
||||
|
||||
var _is_loaded = false
|
||||
|
||||
var _groups = {}
|
||||
|
||||
func init_resources():
|
||||
if _is_loaded:
|
||||
return
|
||||
_is_loaded = true
|
||||
var file_tree = _get_file_tree("res://")
|
||||
_setup_tree(file_tree)
|
||||
|
||||
|
||||
func _get_file_tree(base_path: String, dir_name: String = "") -> Dictionary:
|
||||
var dir_path = base_path.path_join(dir_name)
|
||||
var dir = DirAccess.open(dir_path)
|
||||
var dir_data = { "path": dir_path, "name": dir_name, "children": [], "type": "dir", }
|
||||
if not dir:
|
||||
return dir_data
|
||||
|
||||
dir.list_dir_begin()
|
||||
var file_name = dir.get_next()
|
||||
|
||||
while file_name != "":
|
||||
if dir.current_is_dir() and _is_importable_folder(dir_path, file_name):
|
||||
var child_data = _get_file_tree(dir_path, file_name)
|
||||
if not child_data.children.is_empty():
|
||||
dir_data.children.push_back(child_data)
|
||||
elif file_name.ends_with(".res"):
|
||||
var resource_path = dir_path.path_join(file_name)
|
||||
var resource = ResourceLoader.load(resource_path)
|
||||
if resource is SpriteFrames:
|
||||
if resource.has_meta(wizard_config.WIZARD_CONFIG_META_NAME):
|
||||
var meta = resource.get_meta(wizard_config.WIZARD_CONFIG_META_NAME)
|
||||
var parent_node = dir_data
|
||||
|
||||
if meta.group != "":
|
||||
var group
|
||||
if _groups.has(meta.group):
|
||||
group = _groups[meta.group]
|
||||
else:
|
||||
group = { "folders": {} }
|
||||
_groups[meta.group] = group
|
||||
|
||||
if not group.folders.has(dir_path):
|
||||
group.folders[dir_path] = {
|
||||
"node": {
|
||||
"type": "group",
|
||||
"name": "Split: %s" % meta.fields.source_file.get_file(),
|
||||
"has_moved": meta.fields.output_location != dir_path,
|
||||
"path": meta.fields.source_file,
|
||||
"folder": dir_path,
|
||||
"has_changes": _has_source_changes(resource, meta),
|
||||
"children": [],
|
||||
}
|
||||
}
|
||||
parent_node.children.push_back(group.folders[dir_path].node)
|
||||
|
||||
parent_node = group.folders[dir_path].node
|
||||
|
||||
parent_node.children.push_back({
|
||||
"type": "resource",
|
||||
"resource_type": "SpriteFrames",
|
||||
"name": resource.resource_path.get_file(),
|
||||
"path": resource.resource_path,
|
||||
"meta": meta,
|
||||
"has_changes": _has_source_changes(resource, meta),
|
||||
"has_moved": meta.fields.output_location != dir_path,
|
||||
})
|
||||
|
||||
file_name = dir.get_next()
|
||||
|
||||
return dir_data
|
||||
|
||||
|
||||
func _is_importable_folder(dir_path: String, dir_name: String) -> bool:
|
||||
return dir_path != "res://" or dir_name != "addons"
|
||||
|
||||
|
||||
func _setup_tree(resource_tree: Dictionary) -> void:
|
||||
var root = _resource_tree.create_item()
|
||||
_add_items_to_tree(root, resource_tree.children)
|
||||
|
||||
|
||||
func _add_items_to_tree(root: TreeItem, children: Array):
|
||||
for node in children:
|
||||
var item: TreeItem = _resource_tree.create_item(root)
|
||||
item.set_text(0, node.name)
|
||||
item.set_meta("node", node)
|
||||
match node.type:
|
||||
"dir":
|
||||
item.set_icon(0, get_theme_icon("Folder", "EditorIcons"))
|
||||
_add_items_to_tree(item, node.children)
|
||||
#
|
||||
"group":
|
||||
item.set_icon(0, get_theme_icon("CompressedTexture2D", "EditorIcons"))
|
||||
_add_items_to_tree(item, node.children)
|
||||
if node.has_changes:
|
||||
item.set_text(0, "%s (*)" % node.name)
|
||||
if node.has_moved:
|
||||
item.set_suffix(0, "(moved)")
|
||||
"resource":
|
||||
item.set_icon(0, get_theme_icon(node.resource_type, "EditorIcons"))
|
||||
if node.has_changes:
|
||||
item.set_text(0, "%s (*)" % node.name)
|
||||
if node.meta.group != "":
|
||||
item.set_custom_color(0, item.get_icon_modulate(0).darkened(0.5))
|
||||
item.set_selectable(0, false)
|
||||
elif node.has_moved:
|
||||
item.set_suffix(0, "(moved)")
|
||||
|
||||
|
||||
func _has_source_changes(resource: Object, meta: Dictionary) -> bool:
|
||||
var current_hash = FileAccess.get_md5(meta.fields.source_file)
|
||||
var saved_hash = wizard_config.get_source_hash(resource)
|
||||
|
||||
return saved_hash != current_hash
|
||||
|
||||
|
||||
func _is_supported_type(resource_type: String) -> bool:
|
||||
return resource_type == "SpriteFrames"
|
||||
|
||||
|
||||
func reload_tree():
|
||||
_confirmation_warning_container.hide()
|
||||
_resources_to_process = null
|
||||
if _current_container != null:
|
||||
_current_container.show_buttons()
|
||||
_current_container = null
|
||||
|
||||
_groups = {}
|
||||
_selection_count = 0
|
||||
_resource_tree.clear()
|
||||
var file_tree = _get_file_tree("res://")
|
||||
_setup_tree(file_tree)
|
||||
|
||||
|
||||
func _set_empty_details_state():
|
||||
_nothing_container.show()
|
||||
_single_item_container.hide()
|
||||
_multiple_items_container.hide()
|
||||
|
||||
|
||||
func _on_tree_multi_selected(item: TreeItem, column: int, selected: bool):
|
||||
_confirmation_warning_container.hide()
|
||||
_resources_to_process = null
|
||||
if _current_container != null:
|
||||
_current_container.show_buttons()
|
||||
#
|
||||
if selected:
|
||||
_selection_count += 1
|
||||
else:
|
||||
_selection_count -= 1
|
||||
#
|
||||
_nothing_container.hide()
|
||||
_single_item_container.hide()
|
||||
_multiple_items_container.hide()
|
||||
#
|
||||
match _selection_count:
|
||||
0:
|
||||
_nothing_container.show()
|
||||
1:
|
||||
_single_item_container.show()
|
||||
_set_item_details(_resource_tree.get_selected())
|
||||
_current_container = _single_item_container
|
||||
_:
|
||||
_multiple_items_container.show()
|
||||
_multiple_items_container.set_selected_count(_selection_count)
|
||||
_current_container = _multiple_items_container
|
||||
|
||||
|
||||
func _set_item_details(item: TreeItem) -> void:
|
||||
if not item.has_meta("node"):
|
||||
return
|
||||
var data = item.get_meta("node")
|
||||
_single_item_container.set_resource_details(data)
|
||||
|
||||
|
||||
func _on_single_item_import_triggered():
|
||||
var selected = _resource_tree.get_selected()
|
||||
var meta = selected.get_meta("node")
|
||||
|
||||
match meta.type:
|
||||
"dir":
|
||||
var selected_item = _resource_tree.get_selected()
|
||||
var all_resources = []
|
||||
var scenes_to_open = _set_all_resources(selected_item.get_meta("node"), all_resources)
|
||||
_resources_to_process = all_resources
|
||||
_show_confirmation_message(all_resources.size())
|
||||
"resource":
|
||||
var code = await _do_import(meta.path, meta.meta)
|
||||
_set_tree_item_as_saved(_resource_tree.get_selected())
|
||||
_single_item_container.hide_source_change_warning()
|
||||
if code == OK:
|
||||
EditorInterface.get_resource_filesystem().scan()
|
||||
"group":
|
||||
var first_item = meta.children[0]
|
||||
var code = await _do_import(first_item.path, first_item.meta)
|
||||
_set_tree_item_as_saved(selected)
|
||||
_single_item_container.hide_source_change_warning()
|
||||
if code == OK:
|
||||
EditorInterface.get_resource_filesystem().scan()
|
||||
_on_tree_refresh_triggered()
|
||||
|
||||
|
||||
func _on_confirmation_warning_warning_confirmed():
|
||||
_confirmation_warning_container.hide()
|
||||
_current_container.show_buttons()
|
||||
|
||||
for resource in _resources_to_process:
|
||||
await _do_import(resource.path, resource.meta)
|
||||
|
||||
_resources_to_process = null
|
||||
EditorInterface.get_resource_filesystem().scan()
|
||||
_on_tree_refresh_triggered()
|
||||
|
||||
|
||||
func _on_confirmation_warning_warning_declined():
|
||||
_confirmation_warning_container.hide()
|
||||
_current_container.show_buttons()
|
||||
_resources_to_process = null
|
||||
|
||||
|
||||
func _on_tree_refresh_triggered():
|
||||
_set_empty_details_state()
|
||||
reload_tree()
|
||||
|
||||
|
||||
func _set_tree_item_as_saved(item: TreeItem) -> void:
|
||||
var meta = item.get_meta("node")
|
||||
meta.has_changes = false
|
||||
meta.has_moved = false
|
||||
item.set_meta("node", meta)
|
||||
item.set_text(0, meta.name)
|
||||
item.set_suffix(0, "")
|
||||
|
||||
|
||||
func _do_import(resource_path: String, metadata: Dictionary) -> int:
|
||||
var resource_base_dir = resource_path.get_base_dir()
|
||||
|
||||
if resource_base_dir != metadata.fields.output_location:
|
||||
print("Resource has moved. Changing output folder from %s to %s" % [resource_base_dir, metadata.fields.output_location])
|
||||
metadata.fields.output_location = resource_base_dir
|
||||
|
||||
var exit_code := await _import_helper.import_and_create_resources(metadata.fields.source_file, metadata.fields)
|
||||
|
||||
if exit_code == OK:
|
||||
print("Import complete: %s" % resource_path)
|
||||
import_success.emit(metadata.fields)
|
||||
else:
|
||||
printerr("Failed to import %s. Error: %s" % [resource_path, result_code.get_error_message(exit_code)])
|
||||
|
||||
return exit_code
|
||||
|
||||
|
||||
func _set_all_resources(meta: Dictionary, resources: Array):
|
||||
match meta.type:
|
||||
"dir":
|
||||
for c in meta.children:
|
||||
_set_all_resources(c, resources)
|
||||
"resource":
|
||||
if not resources.has(meta):
|
||||
resources.push_back(meta)
|
||||
"group":
|
||||
var first_item = meta.children[0]
|
||||
resources.push_back(first_item)
|
||||
|
||||
|
||||
func _show_confirmation_message(resources: int):
|
||||
_current_container.hide_buttons()
|
||||
_confirmation_warning_container.set_message("You are about to re-import %s resources. Do you wish to continue?" % resources)
|
||||
_confirmation_warning_container.show()
|
||||
|
||||
|
||||
func _on_multiple_items_import_triggered():
|
||||
var selected_item = _resource_tree.get_next_selected(null)
|
||||
var all_resources = []
|
||||
|
||||
while selected_item != null:
|
||||
_set_all_resources(selected_item.get_meta("node"), all_resources)
|
||||
selected_item = _resource_tree.get_next_selected(selected_item)
|
||||
|
||||
_resources_to_process = all_resources
|
||||
_show_confirmation_message(all_resources.size())
|
|
@ -0,0 +1,67 @@
|
|||
[gd_scene load_steps=6 format=3 uid="uid://bfhsdslj8kt7b"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/AsepriteWizard/interface/docks/wizard/imported_sprite_frames.gd" id="1_dk2ra"]
|
||||
[ext_resource type="PackedScene" uid="uid://cisgsfvp4nf1g" path="res://addons/AsepriteWizard/interface/shared/tree/resource_tree.tscn" id="2_svrqo"]
|
||||
[ext_resource type="PackedScene" uid="uid://q7eyyg2kvvv2" path="res://addons/AsepriteWizard/interface/docks/wizard/resource_tree_single_item.tscn" id="3_pe1cg"]
|
||||
[ext_resource type="PackedScene" uid="uid://fscemkx5w1dw" path="res://addons/AsepriteWizard/interface/docks/wizard/resource_tree_multiple_items.tscn" id="4_vteew"]
|
||||
[ext_resource type="PackedScene" uid="uid://qgmln507kjnm" path="res://addons/AsepriteWizard/interface/shared/tree/tree_selection_confirmation_warning.tscn" id="5_jigim"]
|
||||
|
||||
[node name="ImportedSpriteFrames" type="PanelContainer"]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("1_dk2ra")
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="."]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="HSplitContainer" type="HSplitContainer" parent="MarginContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="tree" parent="MarginContainer/HSplitContainer" instance=ExtResource("2_svrqo")]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="MarginContainer/HSplitContainer"]
|
||||
custom_minimum_size = Vector2(200, 0)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/HSplitContainer/MarginContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="MarginContainer/HSplitContainer/MarginContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="MarginContainer/HSplitContainer/MarginContainer/VBoxContainer/MarginContainer"]
|
||||
layout_mode = 2
|
||||
text = "Details"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="MarginContainer/HSplitContainer/MarginContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="nothing" type="Label" parent="MarginContainer/HSplitContainer/MarginContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 6
|
||||
text = "Nothing selected"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="single_item" parent="MarginContainer/HSplitContainer/MarginContainer/VBoxContainer" instance=ExtResource("3_pe1cg")]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
|
||||
[node name="multiple_items" parent="MarginContainer/HSplitContainer/MarginContainer/VBoxContainer" instance=ExtResource("4_vteew")]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
|
||||
[node name="confirmation_warning" parent="MarginContainer/HSplitContainer/MarginContainer/VBoxContainer" instance=ExtResource("5_jigim")]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
|
||||
[connection signal="multi_selected" from="MarginContainer/HSplitContainer/tree" to="." method="_on_tree_multi_selected"]
|
||||
[connection signal="refresh_triggered" from="MarginContainer/HSplitContainer/tree" to="." method="_on_tree_refresh_triggered"]
|
||||
[connection signal="import_triggered" from="MarginContainer/HSplitContainer/MarginContainer/VBoxContainer/single_item" to="." method="_on_single_item_import_triggered"]
|
||||
[connection signal="import_triggered" from="MarginContainer/HSplitContainer/MarginContainer/VBoxContainer/multiple_items" to="." method="_on_multiple_items_import_triggered"]
|
||||
[connection signal="warning_confirmed" from="MarginContainer/HSplitContainer/MarginContainer/VBoxContainer/confirmation_warning" to="." method="_on_confirmation_warning_warning_confirmed"]
|
||||
[connection signal="warning_declined" from="MarginContainer/HSplitContainer/MarginContainer/VBoxContainer/confirmation_warning" to="." method="_on_confirmation_warning_warning_declined"]
|
|
@ -0,0 +1,22 @@
|
|||
@tool
|
||||
extends VBoxContainer
|
||||
|
||||
signal import_triggered
|
||||
|
||||
@onready var _import_message = $message
|
||||
@onready var _import_button = $buttons
|
||||
|
||||
func set_selected_count(number_of_items: int) -> void:
|
||||
_import_message.text = "%2d items selected" % number_of_items
|
||||
|
||||
|
||||
func show_buttons():
|
||||
_import_button.show()
|
||||
|
||||
|
||||
func hide_buttons():
|
||||
_import_button.hide()
|
||||
|
||||
|
||||
func _on_import_selected_button_up():
|
||||
import_triggered.emit()
|
|
@ -0,0 +1,24 @@
|
|||
[gd_scene load_steps=2 format=3 uid="uid://fscemkx5w1dw"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/AsepriteWizard/interface/docks/wizard/resource_tree_multiple_items.gd" id="1_lg81l"]
|
||||
|
||||
[node name="multiple_items" type="VBoxContainer"]
|
||||
size_flags_vertical = 3
|
||||
script = ExtResource("1_lg81l")
|
||||
|
||||
[node name="message" type="Label" parent="."]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 6
|
||||
text = "Multiple items selected
|
||||
"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="buttons" type="HFlowContainer" parent="."]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="import_selected" type="Button" parent="buttons"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Re-Import all"
|
||||
|
||||
[connection signal="button_up" from="buttons/import_selected" to="." method="_on_import_selected_button_up"]
|
|
@ -0,0 +1,171 @@
|
|||
@tool
|
||||
extends VBoxContainer
|
||||
|
||||
signal import_triggered
|
||||
|
||||
@onready var _file_name = $GridContainer/file_name_value
|
||||
@onready var _type = $GridContainer/type_value
|
||||
@onready var _path = $GridContainer/path_value
|
||||
|
||||
@onready var _source_label = $GridContainer/source_file_label
|
||||
@onready var _source = $GridContainer/source_file_value
|
||||
@onready var _only_visible_layers_label = $GridContainer/only_visible_layers_label
|
||||
@onready var _only_visible_layers = $GridContainer/only_visible_layers_value
|
||||
@onready var _layer_ex_pattern_label = $GridContainer/layer_ex_patt_label
|
||||
@onready var _layer_ex_pattern = $GridContainer/layer_ex_patt_value
|
||||
|
||||
@onready var _o_name_label = $GridContainer/o_name_label
|
||||
@onready var _o_name = $GridContainer/o_name_value
|
||||
@onready var _o_folder_label = $GridContainer/o_folder_label
|
||||
@onready var _o_folder_value = $GridContainer/o_folder_value
|
||||
|
||||
@onready var _resource_list_label = $GridContainer/resource_list_label
|
||||
@onready var _resource_list = $GridContainer/resource_list
|
||||
@onready var _resource_list_separator_1 = $GridContainer/HSeparator3
|
||||
@onready var _resource_list_separator_2 = $GridContainer/HSeparator4
|
||||
|
||||
@onready var _resource_buttons = $resource_buttons
|
||||
@onready var _dir_buttons = $dir_buttons
|
||||
@onready var _group_buttons = $group_buttons
|
||||
|
||||
@onready var _source_change_warning = $source_changed_warning
|
||||
|
||||
@onready var _resource_only_fields = [
|
||||
_source_label,
|
||||
_source,
|
||||
_only_visible_layers_label,
|
||||
_only_visible_layers,
|
||||
_layer_ex_pattern_label,
|
||||
_layer_ex_pattern,
|
||||
_o_name_label,
|
||||
_o_name,
|
||||
_o_folder_label,
|
||||
_o_folder_value,
|
||||
_source_change_warning,
|
||||
]
|
||||
|
||||
var _current_resource_type = "resource"
|
||||
var _resource_config: Dictionary = {}
|
||||
|
||||
func _ready():
|
||||
_source_change_warning.set_text("Source file changed since last import")
|
||||
_source_change_warning.hide()
|
||||
|
||||
|
||||
func set_resource_details(resource_details: Dictionary) -> void:
|
||||
_resource_config = resource_details
|
||||
_resource_buttons.hide()
|
||||
_dir_buttons.hide()
|
||||
_group_buttons.hide()
|
||||
_hide_resource_list()
|
||||
|
||||
_source_change_warning.hide()
|
||||
|
||||
_file_name.text = resource_details.name
|
||||
_path.text = resource_details.path
|
||||
|
||||
_current_resource_type = resource_details.type
|
||||
match resource_details.type:
|
||||
"resource":
|
||||
_type.text = resource_details.resource_type
|
||||
_show_resource_fields()
|
||||
_resource_buttons.show()
|
||||
|
||||
var fields = resource_details.meta.fields
|
||||
_load_fields(fields)
|
||||
_resource_buttons.show()
|
||||
_source_change_warning.visible = resource_details.has_changes
|
||||
"group":
|
||||
_type.text = "Split Group"
|
||||
_show_resource_fields()
|
||||
_load_fields(resource_details.children[0].meta.fields)
|
||||
_source_change_warning.visible = resource_details.children[0].has_changes
|
||||
_group_buttons.show()
|
||||
_show_resource_list()
|
||||
for c in _resource_list.get_children():
|
||||
c.queue_free()
|
||||
|
||||
for child_resource in resource_details.children:
|
||||
var label = Label.new()
|
||||
label.text = child_resource.name
|
||||
_resource_list.add_child(label)
|
||||
_:
|
||||
_type.text = "Folder"
|
||||
_hide_resource_fields()
|
||||
_dir_buttons.show()
|
||||
return
|
||||
|
||||
|
||||
func _load_fields(fields: Dictionary):
|
||||
_only_visible_layers.text = "Yes" if fields.only_visible_layers else "No"
|
||||
_layer_ex_pattern.text = fields.layer_exclusion_pattern
|
||||
|
||||
_o_name.text = fields.output_name
|
||||
_o_folder_value.text = fields.output_location
|
||||
|
||||
_source.text = fields.source_file
|
||||
|
||||
|
||||
func _hide_resource_fields():
|
||||
for f in _resource_only_fields:
|
||||
f.hide()
|
||||
|
||||
|
||||
func _show_resource_fields():
|
||||
for f in _resource_only_fields:
|
||||
f.show()
|
||||
|
||||
|
||||
func show_buttons():
|
||||
match _current_resource_type:
|
||||
"resource":
|
||||
_resource_buttons.show()
|
||||
_:
|
||||
_dir_buttons.show()
|
||||
|
||||
|
||||
func hide_buttons():
|
||||
_resource_buttons.hide()
|
||||
_dir_buttons.hide()
|
||||
|
||||
|
||||
func hide_source_change_warning():
|
||||
_source_change_warning.hide()
|
||||
|
||||
|
||||
func _on_show_in_fs_button_up():
|
||||
EditorInterface.get_file_system_dock().navigate_to_path(_path.text)
|
||||
|
||||
|
||||
func _on_show_dir_in_fs_button_up():
|
||||
EditorInterface.get_file_system_dock().navigate_to_path(_path.text)
|
||||
|
||||
|
||||
func _on_import_all_button_up():
|
||||
import_triggered.emit()
|
||||
|
||||
|
||||
func _on_import_button_up():
|
||||
import_triggered.emit()
|
||||
|
||||
|
||||
func _hide_resource_list():
|
||||
_resource_list_separator_1.hide()
|
||||
_resource_list_separator_2.hide()
|
||||
_resource_list_label.hide()
|
||||
_resource_list.hide()
|
||||
|
||||
|
||||
func _show_resource_list():
|
||||
_resource_list_separator_1.show()
|
||||
_resource_list_separator_2.show()
|
||||
_resource_list_label.show()
|
||||
_resource_list.show()
|
||||
|
||||
|
||||
func _on_import_all_pressed():
|
||||
import_triggered.emit()
|
||||
|
||||
|
||||
func _on_show_in_fs_pressed():
|
||||
EditorInterface.get_file_system_dock().navigate_to_path(_resource_config.children[0].path)
|
|
@ -0,0 +1,172 @@
|
|||
[gd_scene load_steps=3 format=3 uid="uid://q7eyyg2kvvv2"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/AsepriteWizard/interface/docks/wizard/resource_tree_single_item.gd" id="1_h1q4t"]
|
||||
[ext_resource type="PackedScene" uid="uid://c1l0bk12iwln3" path="res://addons/AsepriteWizard/interface/shared/tree/inline_warning_panel.tscn" id="2_hmv61"]
|
||||
|
||||
[node name="single_item" type="VBoxContainer"]
|
||||
script = ExtResource("1_h1q4t")
|
||||
|
||||
[node name="GridContainer" type="GridContainer" parent="."]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
theme_override_constants/h_separation = 10
|
||||
columns = 2
|
||||
|
||||
[node name="type_label" type="Label" parent="GridContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 0
|
||||
text = "Type"
|
||||
|
||||
[node name="type_value" type="Label" parent="GridContainer"]
|
||||
layout_mode = 2
|
||||
text = "-"
|
||||
|
||||
[node name="file_name_label" type="Label" parent="GridContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 0
|
||||
text = "File"
|
||||
|
||||
[node name="file_name_value" type="Label" parent="GridContainer"]
|
||||
custom_minimum_size = Vector2(50, 0)
|
||||
layout_mode = 2
|
||||
text = "-"
|
||||
autowrap_mode = 1
|
||||
|
||||
[node name="path_label" type="Label" parent="GridContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 0
|
||||
text = "Path"
|
||||
|
||||
[node name="path_value" type="Label" parent="GridContainer"]
|
||||
custom_minimum_size = Vector2(50, 0)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "-"
|
||||
autowrap_mode = 1
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="GridContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="HSeparator2" type="HSeparator" parent="GridContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="source_file_label" type="Label" parent="GridContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 0
|
||||
text = "Aseprite File"
|
||||
|
||||
[node name="source_file_value" type="Label" parent="GridContainer"]
|
||||
custom_minimum_size = Vector2(50, 0)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "-"
|
||||
autowrap_mode = 1
|
||||
|
||||
[node name="only_visible_layers_label" type="Label" parent="GridContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 0
|
||||
text = "Only Visible Layers"
|
||||
|
||||
[node name="only_visible_layers_value" type="Label" parent="GridContainer"]
|
||||
layout_mode = 2
|
||||
text = "-"
|
||||
|
||||
[node name="layer_ex_patt_label" type="Label" parent="GridContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 0
|
||||
text = "Slice"
|
||||
|
||||
[node name="layer_ex_patt_value" type="Label" parent="GridContainer"]
|
||||
layout_mode = 2
|
||||
text = "-"
|
||||
|
||||
[node name="o_folder_label" type="Label" parent="GridContainer"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 0
|
||||
text = "Output Folder"
|
||||
|
||||
[node name="o_folder_value" type="Label" parent="GridContainer"]
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(50, 0)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "-"
|
||||
autowrap_mode = 2
|
||||
|
||||
[node name="o_name_label" type="Label" parent="GridContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 0
|
||||
text = "Output Name"
|
||||
|
||||
[node name="o_name_value" type="Label" parent="GridContainer"]
|
||||
layout_mode = 2
|
||||
text = "-"
|
||||
|
||||
[node name="HSeparator3" type="HSeparator" parent="GridContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="HSeparator4" type="HSeparator" parent="GridContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="resource_list_label" type="Label" parent="GridContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 0
|
||||
text = "Resources"
|
||||
|
||||
[node name="resource_list" type="VBoxContainer" parent="GridContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="source_changed_warning" parent="." instance=ExtResource("2_hmv61")]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="resource_buttons" type="HFlowContainer" parent="."]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
|
||||
[node name="import" type="Button" parent="resource_buttons"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Re-Import"
|
||||
|
||||
[node name="show_in_fs" type="Button" parent="resource_buttons"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Show In FileSystem"
|
||||
|
||||
[node name="group_buttons" type="HFlowContainer" parent="."]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
|
||||
[node name="import_all" type="Button" parent="group_buttons"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Re-Import all"
|
||||
|
||||
[node name="show_in_fs" type="Button" parent="group_buttons"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Show In FileSystem"
|
||||
|
||||
[node name="dir_buttons" type="HFlowContainer" parent="."]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
|
||||
[node name="import_all" type="Button" parent="dir_buttons"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Re-Import all"
|
||||
|
||||
[node name="show_dir_in_fs" type="Button" parent="dir_buttons"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Show In FileSystem"
|
||||
|
||||
[connection signal="button_up" from="resource_buttons/import" to="." method="_on_import_button_up"]
|
||||
[connection signal="button_up" from="resource_buttons/show_in_fs" to="." method="_on_show_in_fs_button_up"]
|
||||
[connection signal="pressed" from="group_buttons/import_all" to="." method="_on_import_all_pressed"]
|
||||
[connection signal="pressed" from="group_buttons/show_in_fs" to="." method="_on_show_in_fs_pressed"]
|
||||
[connection signal="button_up" from="dir_buttons/import_all" to="." method="_on_import_all_button_up"]
|
||||
[connection signal="button_up" from="dir_buttons/show_dir_in_fs" to="." method="_on_show_dir_in_fs_button_up"]
|
|
@ -0,0 +1,260 @@
|
|||
@tool
|
||||
extends PanelContainer
|
||||
|
||||
signal request_edit(import_cfg)
|
||||
signal request_import(import_cfg)
|
||||
|
||||
const SourcePathField = preload("./wizard_nodes/source_path.tscn")
|
||||
const OutputPathField = preload("./wizard_nodes/output_path.tscn")
|
||||
const ImportDateField = preload("./wizard_nodes/import_date.tscn")
|
||||
const ItemActions = preload("./wizard_nodes/list_actions.tscn")
|
||||
const DetailsField = preload("./wizard_nodes/details.tscn")
|
||||
|
||||
const SORT_BY_DATE := 0
|
||||
const SORT_BY_PATH := 1
|
||||
const INITIAL_GRID_INDEX := 4
|
||||
|
||||
var _config = preload("../../../config/config.gd").new()
|
||||
var _history: Array
|
||||
var _history_nodes := {}
|
||||
var _history_nodes_list := []
|
||||
var _is_busy := false
|
||||
var _import_requested_for := -1
|
||||
var _sort_by = SORT_BY_DATE
|
||||
|
||||
@onready var grid = $MarginContainer/VBoxContainer/ScrollContainer/GridContainer
|
||||
@onready var loading_warning = $MarginContainer/VBoxContainer/loading_warning
|
||||
@onready var no_history_warning = $MarginContainer/VBoxContainer/no_history_warning
|
||||
|
||||
|
||||
func reload():
|
||||
if _history:
|
||||
return
|
||||
|
||||
if _config.has_old_history():
|
||||
_migrate_history()
|
||||
|
||||
_history = _config.get_import_history()
|
||||
|
||||
for index in range(_history.size()):
|
||||
var entry = _history[index]
|
||||
_create_node_list_entry(entry, index)
|
||||
|
||||
loading_warning.hide()
|
||||
if _history.is_empty():
|
||||
no_history_warning.show()
|
||||
else:
|
||||
grid.get_parent().show()
|
||||
|
||||
|
||||
func _create_node_list_entry(entry: Dictionary, index: int):
|
||||
_add_to_node_list(entry, _create_nodes(entry, index))
|
||||
|
||||
|
||||
func _create_nodes(entry: Dictionary, index: int) -> Dictionary:
|
||||
var import_date = ImportDateField.instantiate()
|
||||
import_date.set_date(entry.import_date)
|
||||
|
||||
var source_path = SourcePathField.instantiate()
|
||||
source_path.set_entry(entry)
|
||||
|
||||
var output_path = OutputPathField.instantiate()
|
||||
output_path.text = entry.output_location
|
||||
output_path.tooltip_text = entry.output_location
|
||||
|
||||
var details = DetailsField.instantiate()
|
||||
details.set_details(entry)
|
||||
|
||||
var actions = ItemActions.instantiate()
|
||||
actions.history_index = index
|
||||
actions.connect("import_clicked",Callable(self,"_on_entry_reimport_clicked"))
|
||||
actions.connect("edit_clicked",Callable(self,"_on_entry_edit_clicked"))
|
||||
actions.connect("removed_clicked",Callable(self,"_on_entry_remove_clicked"))
|
||||
|
||||
|
||||
grid.get_child(INITIAL_GRID_INDEX).add_sibling(import_date)
|
||||
import_date.add_sibling(source_path)
|
||||
source_path.add_sibling(output_path)
|
||||
output_path.add_sibling(details)
|
||||
details.add_sibling(actions)
|
||||
|
||||
return {
|
||||
"history_index": index,
|
||||
"timestamp": entry.import_date,
|
||||
"source_file": entry.source_file,
|
||||
"source_path_node": source_path,
|
||||
"output_path_node": output_path,
|
||||
"import_date_node": import_date,
|
||||
"actions_node": actions,
|
||||
"details_node": details,
|
||||
}
|
||||
|
||||
|
||||
func _add_to_node_list(entry: Dictionary, node: Dictionary):
|
||||
if not _history_nodes.has(entry.source_file):
|
||||
_history_nodes[entry.source_file] = []
|
||||
_history_nodes[entry.source_file].push_front(node)
|
||||
_history_nodes_list.push_front(node)
|
||||
|
||||
|
||||
func add_entry(file_settings: Dictionary):
|
||||
if _history == null:
|
||||
reload()
|
||||
#
|
||||
var dt = Time.get_datetime_dict_from_system()
|
||||
file_settings["import_date"] = "%04d-%02d-%02d %02d:%02d:%02d" % [dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second]
|
||||
#
|
||||
if _import_requested_for != -1:
|
||||
_remove_item(_import_requested_for)
|
||||
_import_requested_for = -1
|
||||
elif _history.size() > _config.get_history_max_entries():
|
||||
_remove_entries(_history[0].source_file, 0)
|
||||
#
|
||||
_history.push_back(file_settings)
|
||||
_config.save_import_history(_history)
|
||||
_create_node_list_entry(file_settings, _history.size() - 1)
|
||||
|
||||
if _sort_by == SORT_BY_PATH:
|
||||
_trigger_sort()
|
||||
|
||||
no_history_warning.hide()
|
||||
loading_warning.hide()
|
||||
grid.get_parent().show()
|
||||
_is_busy = false
|
||||
|
||||
|
||||
func _on_entry_reimport_clicked(entry_index: int):
|
||||
if _is_busy:
|
||||
return
|
||||
_is_busy = true
|
||||
_import_requested_for = entry_index
|
||||
emit_signal("request_import", _history[entry_index])
|
||||
|
||||
|
||||
func _on_entry_edit_clicked(entry_index: int):
|
||||
if _is_busy:
|
||||
return
|
||||
emit_signal("request_edit", _history[entry_index])
|
||||
|
||||
|
||||
func _on_entry_remove_clicked(entry_index: int):
|
||||
if _is_busy:
|
||||
return
|
||||
_is_busy = true
|
||||
|
||||
_remove_item(entry_index)
|
||||
_config.save_import_history(_history)
|
||||
|
||||
if (_history.is_empty()):
|
||||
grid.get_parent().hide()
|
||||
no_history_warning.show()
|
||||
|
||||
_is_busy = false
|
||||
|
||||
|
||||
func _remove_item(entry_index: int):
|
||||
var entry = _history[entry_index]
|
||||
_remove_entries(entry.source_file, entry_index)
|
||||
|
||||
|
||||
# removes nodes and entry from history. If entry_index is not provided, all
|
||||
# entries for path are removed.
|
||||
func _remove_entries(source_file_path: String, entry_index: int = -1):
|
||||
var files_entries = _history_nodes[source_file_path]
|
||||
var indexes_to_remove = []
|
||||
|
||||
for f in files_entries:
|
||||
if entry_index == -1 or f.history_index == entry_index:
|
||||
_free_entry_nodes(f)
|
||||
_history_nodes_list.erase(f)
|
||||
|
||||
if entry_index != -1:
|
||||
files_entries.erase(f)
|
||||
_remove_from_history(f.history_index)
|
||||
return
|
||||
|
||||
indexes_to_remove.push_back(f.history_index)
|
||||
|
||||
for i in indexes_to_remove:
|
||||
_remove_from_history(i)
|
||||
|
||||
_history_nodes[source_file_path] = []
|
||||
|
||||
|
||||
func _remove_from_history(entry_index: int):
|
||||
var _already_adjusted = []
|
||||
# to avoid re-creating the whole nodes list, I just decrement
|
||||
# the index from items newer than the excluded one
|
||||
for index in range(entry_index + 1, _history.size()):
|
||||
if _already_adjusted.has(_history[index].source_file):
|
||||
continue
|
||||
_already_adjusted.push_back(_history[index].source_file)
|
||||
var nodes = _history_nodes[_history[index].source_file]
|
||||
for f in nodes:
|
||||
if f.history_index > entry_index:
|
||||
f.history_index -= 1
|
||||
f.actions_node.history_index = f.history_index
|
||||
|
||||
_history.remove_at(entry_index)
|
||||
|
||||
|
||||
func _free_entry_nodes(entry_history_node: Dictionary):
|
||||
entry_history_node.source_path_node.queue_free()
|
||||
entry_history_node.output_path_node.queue_free()
|
||||
entry_history_node.import_date_node.queue_free()
|
||||
entry_history_node.actions_node.queue_free()
|
||||
entry_history_node.details_node.queue_free()
|
||||
|
||||
|
||||
func _on_SortOptions_item_selected(index):
|
||||
if index == _sort_by:
|
||||
return
|
||||
|
||||
_trigger_sort(index)
|
||||
|
||||
|
||||
func _trigger_sort(sort_type: int = _sort_by):
|
||||
if sort_type == SORT_BY_DATE:
|
||||
_history_nodes_list.sort_custom(Callable(self,"_sort_by_date"))
|
||||
else:
|
||||
_history_nodes_list.sort_custom(Callable(self,"_sort_by_path"))
|
||||
_reorganise_nodes()
|
||||
_sort_by = sort_type
|
||||
|
||||
|
||||
func _sort_by_date(a, b):
|
||||
return a.timestamp < b.timestamp
|
||||
|
||||
|
||||
func _sort_by_path(a, b):
|
||||
return a.source_file > b.source_file
|
||||
|
||||
|
||||
func _reorganise_nodes():
|
||||
for entry in _history_nodes_list:
|
||||
grid.move_child(entry.import_date_node, INITIAL_GRID_INDEX + 1)
|
||||
grid.move_child(entry.source_path_node, INITIAL_GRID_INDEX + 2)
|
||||
grid.move_child(entry.output_path_node, INITIAL_GRID_INDEX + 3)
|
||||
grid.move_child(entry.details_node, INITIAL_GRID_INDEX + 4)
|
||||
grid.move_child(entry.actions_node, INITIAL_GRID_INDEX + 5)
|
||||
|
||||
|
||||
func _migrate_history():
|
||||
var history = _config.get_old_import_history()
|
||||
var new_history = []
|
||||
|
||||
for index in range(history.size()):
|
||||
var entry = history[index]
|
||||
new_history.push_back({
|
||||
"split_layers": true if entry.options.export_mode else false,
|
||||
"only_visible_layers": entry.options.only_visible_layers,
|
||||
"layer_exclusion_pattern": entry.options.exception_pattern,
|
||||
"output_name": entry.options.output_filename,
|
||||
"source_file": entry.source_file,
|
||||
"do_not_create_resource": entry.options.do_not_create_resource,
|
||||
"output_location": entry.output_location,
|
||||
"import_date": entry.import_date,
|
||||
})
|
||||
|
||||
_config.save_import_history(new_history)
|
||||
_config.remove_old_history_setting()
|
|
@ -0,0 +1,85 @@
|
|||
[gd_scene load_steps=2 format=3 uid="uid://cyoin5ncul0fm"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/AsepriteWizard/interface/docks/wizard/sprite_frames_import_history.gd" id="1"]
|
||||
|
||||
[node name="SpriteFramesImportHistory" type="PanelContainer"]
|
||||
anchors_preset = 10
|
||||
anchor_right = 1.0
|
||||
grow_horizontal = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="."]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="list actions" type="HBoxContainer" parent="MarginContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 0
|
||||
alignment = 2
|
||||
|
||||
[node name="divider" type="Label" parent="MarginContainer/VBoxContainer/list actions"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="sort_label" type="Label" parent="MarginContainer/VBoxContainer/list actions"]
|
||||
layout_mode = 2
|
||||
text = "Sort by:"
|
||||
|
||||
[node name="SortOptions" type="OptionButton" parent="MarginContainer/VBoxContainer/list actions"]
|
||||
layout_mode = 2
|
||||
item_count = 2
|
||||
selected = 0
|
||||
popup/item_0/text = "Date"
|
||||
popup/item_0/id = 0
|
||||
popup/item_1/text = "Source File"
|
||||
popup/item_1/id = 1
|
||||
|
||||
[node name="loading_warning" type="Label" parent="MarginContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 6
|
||||
text = "Loading..."
|
||||
|
||||
[node name="no_history_warning" type="Label" parent="MarginContainer/VBoxContainer"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 6
|
||||
text = "No import history"
|
||||
|
||||
[node name="ScrollContainer" type="ScrollContainer" parent="MarginContainer/VBoxContainer"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="GridContainer" type="GridContainer" parent="MarginContainer/VBoxContainer/ScrollContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
columns = 5
|
||||
|
||||
[node name="date_label" type="Label" parent="MarginContainer/VBoxContainer/ScrollContainer/GridContainer"]
|
||||
layout_mode = 2
|
||||
text = "Date"
|
||||
|
||||
[node name="source_label" type="Label" parent="MarginContainer/VBoxContainer/ScrollContainer/GridContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 2.0
|
||||
text = "Source File"
|
||||
|
||||
[node name="output_label" type="Label" parent="MarginContainer/VBoxContainer/ScrollContainer/GridContainer"]
|
||||
layout_mode = 2
|
||||
text = "Output Folder"
|
||||
|
||||
[node name="details_label" type="Label" parent="MarginContainer/VBoxContainer/ScrollContainer/GridContainer"]
|
||||
layout_mode = 2
|
||||
text = "Details"
|
||||
|
||||
[node name="actions_label" type="Label" parent="MarginContainer/VBoxContainer/ScrollContainer/GridContainer"]
|
||||
layout_mode = 2
|
||||
text = "Actions"
|
||||
|
||||
[connection signal="item_selected" from="MarginContainer/VBoxContainer/list actions/SortOptions" to="." method="_on_SortOptions_item_selected"]
|
|
@ -0,0 +1,69 @@
|
|||
@tool
|
||||
extends Node
|
||||
|
||||
const wizard_meta = preload("../../../config/wizard_config.gd")
|
||||
const result_code = preload("../../../config/result_codes.gd")
|
||||
var _aseprite_file_exporter = preload("../../../aseprite/file_exporter.gd").new()
|
||||
var _sf_creator = preload("../../../creators/sprite_frames/sprite_frames_creator.gd").new()
|
||||
var _config = preload("../../../config/config.gd").new()
|
||||
|
||||
var _file_system: EditorFileSystem = EditorInterface.get_resource_filesystem()
|
||||
|
||||
|
||||
# fields
|
||||
# "split_layers"
|
||||
# "only_visible_layers"
|
||||
# "layer_exclusion_pattern"
|
||||
# "output_name"
|
||||
# "source_file"
|
||||
# "do_not_create_resource"
|
||||
# "output_location"
|
||||
func import_and_create_resources(aseprite_file: String, fields: Dictionary) -> int:
|
||||
var export_mode = _aseprite_file_exporter.LAYERS_EXPORT_MODE if fields.split_layers else _aseprite_file_exporter.FILE_EXPORT_MODE
|
||||
var options = {
|
||||
"export_mode": export_mode,
|
||||
"exception_pattern": fields.layer_exclusion_pattern,
|
||||
"only_visible_layers": fields.only_visible_layers,
|
||||
"output_filename": fields.output_name,
|
||||
"do_not_create_resource": fields.do_not_create_resource,
|
||||
"output_folder": fields.output_location,
|
||||
}
|
||||
|
||||
var aseprite_output = _aseprite_file_exporter.generate_aseprite_files(
|
||||
ProjectSettings.globalize_path(aseprite_file),
|
||||
options
|
||||
)
|
||||
|
||||
if not aseprite_output.is_ok:
|
||||
return aseprite_output.code
|
||||
|
||||
_file_system.scan()
|
||||
|
||||
await _file_system.filesystem_changed
|
||||
|
||||
var exit_code = OK
|
||||
|
||||
if !options.get("do_not_create_resource", false):
|
||||
var resources = _sf_creator.create_resources(aseprite_output.content)
|
||||
if resources.is_ok:
|
||||
_add_metadata(resources.content, aseprite_file, fields, options)
|
||||
exit_code = _sf_creator.save_resources(resources.content)
|
||||
|
||||
if _config.should_remove_source_files():
|
||||
_remove_source_files(aseprite_output.content)
|
||||
|
||||
return exit_code
|
||||
|
||||
|
||||
func _add_metadata(resources: Array, aseprite_file: String, fields: Dictionary, options: Dictionary) -> void:
|
||||
var source_hash = FileAccess.get_md5(aseprite_file)
|
||||
var group = str(ResourceUID.create_id()) if options.export_mode == _aseprite_file_exporter.LAYERS_EXPORT_MODE else ""
|
||||
|
||||
for r in resources:
|
||||
wizard_meta.set_source_hash(r.resource, source_hash)
|
||||
wizard_meta.save_config(r.resource, { "fields": fields, "group": group })
|
||||
|
||||
|
||||
func _remove_source_files(source_files: Array):
|
||||
for s in source_files:
|
||||
DirAccess.remove_absolute(s.data_file)
|
|
@ -0,0 +1,42 @@
|
|||
@tool
|
||||
extends VBoxContainer
|
||||
|
||||
@onready var _details_btn = $label
|
||||
@onready var _details_container = $MarginContainer/GridContainer
|
||||
|
||||
@onready var _split_layers_field = $MarginContainer/GridContainer/split_layers
|
||||
@onready var _only_visible_layers = $MarginContainer/GridContainer/only_visible_layers
|
||||
@onready var _layer_exclusion_pattern = $MarginContainer/GridContainer/layer_exclusion_pattern
|
||||
@onready var _output_name = $MarginContainer/GridContainer/output_name
|
||||
@onready var _do_not_create_resource = $MarginContainer/GridContainer/do_not_create_resource
|
||||
|
||||
var _entry
|
||||
|
||||
func _ready():
|
||||
_adjust_icon(false)
|
||||
_details_container.hide()
|
||||
_load_fields()
|
||||
|
||||
|
||||
func set_details(entry: Dictionary):
|
||||
_entry = entry
|
||||
|
||||
|
||||
func _load_fields():
|
||||
_split_layers_field.text = "Yes" if _entry.split_layers else "No"
|
||||
_only_visible_layers.text = "Yes" if _entry.only_visible_layers else "No"
|
||||
_layer_exclusion_pattern.text = _entry.layer_exclusion_pattern
|
||||
_output_name.text = _entry.output_name
|
||||
_output_name.text = _entry.output_name
|
||||
_do_not_create_resource.text = "Yes" if _entry.do_not_create_resource else "No"
|
||||
|
||||
|
||||
func _adjust_icon(is_visible: bool) -> void:
|
||||
var icon_name = "GuiTreeArrowDown" if is_visible else "GuiTreeArrowRight"
|
||||
_details_btn.icon = get_theme_icon(icon_name, "EditorIcons")
|
||||
|
||||
|
||||
func _on_label_pressed():
|
||||
_details_container.visible = not _details_container.visible
|
||||
_adjust_icon(_details_container.visible)
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
[gd_scene load_steps=2 format=3 uid="uid://b1jb5yierm2bq"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/AsepriteWizard/interface/docks/wizard/wizard_nodes/details.gd" id="1_1v4dc"]
|
||||
|
||||
[node name="Details" type="VBoxContainer"]
|
||||
offset_right = 40.0
|
||||
offset_bottom = 40.0
|
||||
script = ExtResource("1_1v4dc")
|
||||
|
||||
[node name="label" type="Button" parent="."]
|
||||
layout_mode = 2
|
||||
text = "Details"
|
||||
flat = true
|
||||
alignment = 0
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="."]
|
||||
layout_mode = 2
|
||||
theme_override_constants/margin_left = 10
|
||||
|
||||
[node name="GridContainer" type="GridContainer" parent="MarginContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/h_separation = 5
|
||||
columns = 2
|
||||
|
||||
[node name="split_layers_label" type="Label" parent="MarginContainer/GridContainer"]
|
||||
layout_mode = 2
|
||||
text = "Split layers:"
|
||||
|
||||
[node name="split_layers" type="Label" parent="MarginContainer/GridContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="only_visible_layers_label" type="Label" parent="MarginContainer/GridContainer"]
|
||||
layout_mode = 2
|
||||
text = "Only visible layers:"
|
||||
|
||||
[node name="only_visible_layers" type="Label" parent="MarginContainer/GridContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="layer_exclusion_pattern_label" type="Label" parent="MarginContainer/GridContainer"]
|
||||
layout_mode = 2
|
||||
text = "Layer Ex Pattern:"
|
||||
|
||||
[node name="layer_exclusion_pattern" type="Label" parent="MarginContainer/GridContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="output_name_label" type="Label" parent="MarginContainer/GridContainer"]
|
||||
layout_mode = 2
|
||||
text = "Output name:"
|
||||
|
||||
[node name="output_name" type="Label" parent="MarginContainer/GridContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="do_not_create_resource_label" type="Label" parent="MarginContainer/GridContainer"]
|
||||
layout_mode = 2
|
||||
text = "Do not create resource:"
|
||||
|
||||
[node name="do_not_create_resource" type="Label" parent="MarginContainer/GridContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[connection signal="pressed" from="label" to="." method="_on_label_pressed"]
|
|
@ -0,0 +1,6 @@
|
|||
@tool
|
||||
extends Label
|
||||
|
||||
|
||||
func set_date(timestamp: String):
|
||||
self.text = timestamp
|
|
@ -0,0 +1,12 @@
|
|||
[gd_scene load_steps=2 format=3 uid="uid://bs0i357m5d7ho"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/AsepriteWizard/interface/docks/wizard/wizard_nodes/import_date.gd" id="1"]
|
||||
|
||||
[node name="import_date" type="Label"]
|
||||
offset_left = 660.0
|
||||
offset_top = 39.0
|
||||
offset_right = 774.0
|
||||
offset_bottom = 53.0
|
||||
size_flags_vertical = 0
|
||||
text = "2022-07-18 18:01"
|
||||
script = ExtResource("1")
|
|
@ -0,0 +1,20 @@
|
|||
@tool
|
||||
extends HBoxContainer
|
||||
|
||||
signal import_clicked(index)
|
||||
signal edit_clicked(index)
|
||||
signal removed_clicked(index)
|
||||
|
||||
var history_index = -1
|
||||
|
||||
|
||||
func _on_edit_pressed():
|
||||
emit_signal("edit_clicked", history_index)
|
||||
|
||||
|
||||
func _on_reimport_pressed():
|
||||
emit_signal("import_clicked", history_index)
|
||||
|
||||
|
||||
func _on_remove_pressed():
|
||||
emit_signal("removed_clicked", history_index)
|
|
@ -0,0 +1,35 @@
|
|||
[gd_scene load_steps=2 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/AsepriteWizard/interface/docks/wizard/wizard_nodes/list_actions.gd" id="1"]
|
||||
|
||||
[node name="actions" type="HBoxContainer"]
|
||||
offset_left = 784.0
|
||||
offset_top = 34.0
|
||||
offset_right = 950.0
|
||||
offset_bottom = 58.0
|
||||
custom_constants/separation = 5
|
||||
script = ExtResource( 1 )
|
||||
|
||||
[node name="edit" type="Button" parent="."]
|
||||
offset_right = 36.0
|
||||
offset_bottom = 20.0
|
||||
size_flags_vertical = 0
|
||||
text = "Edit"
|
||||
|
||||
[node name="reimport" type="Button" parent="."]
|
||||
offset_left = 41.0
|
||||
offset_right = 97.0
|
||||
offset_bottom = 20.0
|
||||
size_flags_vertical = 0
|
||||
text = "Import"
|
||||
|
||||
[node name="remove_at" type="Button" parent="."]
|
||||
offset_left = 102.0
|
||||
offset_right = 166.0
|
||||
offset_bottom = 20.0
|
||||
size_flags_vertical = 0
|
||||
text = "Remove"
|
||||
|
||||
[connection signal="pressed" from="edit" to="." method="_on_edit_pressed"]
|
||||
[connection signal="pressed" from="reimport" to="." method="_on_reimport_pressed"]
|
||||
[connection signal="pressed" from="remove_at" to="." method="_on_remove_pressed"]
|
|
@ -0,0 +1,12 @@
|
|||
[gd_scene format=2]
|
||||
|
||||
[node name="output_path" type="LineEdit"]
|
||||
offset_left = 450.0
|
||||
offset_top = 34.0
|
||||
offset_right = 650.0
|
||||
offset_bottom = 58.0
|
||||
minimum_size = Vector2( 200, 0 )
|
||||
size_flags_vertical = 0
|
||||
text = "/some/output"
|
||||
align = 3
|
||||
editable = false
|
|
@ -0,0 +1,6 @@
|
|||
@tool
|
||||
extends LineEdit
|
||||
|
||||
func set_entry(entry: Dictionary):
|
||||
self.text = entry.source_file
|
||||
self.tooltip_text = entry.source_file
|
|
@ -0,0 +1,18 @@
|
|||
[gd_scene load_steps=2 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/AsepriteWizard/interface/docks/wizard/wizard_nodes/source_path.gd" id="1"]
|
||||
|
||||
[node name="source_path" type="LineEdit"]
|
||||
offset_top = 34.0
|
||||
offset_right = 440.0
|
||||
offset_bottom = 58.0
|
||||
minimum_size = Vector2( 200, 0 )
|
||||
tooltip_text = "Output name / prefix: some_name
|
||||
Ex. Pattern: $_
|
||||
Split: Yes
|
||||
Only Visible: No
|
||||
No Resource: No"
|
||||
size_flags_vertical = 0
|
||||
text = "/some/very/long/source/path/to/test/field"
|
||||
editable = false
|
||||
script = ExtResource( 1 )
|
|
@ -0,0 +1,5 @@
|
|||
@tool
|
||||
extends Window
|
||||
|
||||
func _on_close_requested():
|
||||
self.queue_free()
|
|
@ -0,0 +1,14 @@
|
|||
[gd_scene load_steps=2 format=3 uid="uid://0x3mlxsex7eb"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/AsepriteWizard/interface/imports_manager/aseprite_imports_manager.gd" id="1_wrm15"]
|
||||
|
||||
[node name="AsepriteDockImportsWindow" type="Window"]
|
||||
title = "Aseprite Imports Manager"
|
||||
initial_position = 1
|
||||
size = Vector2i(612, 458)
|
||||
wrap_controls = true
|
||||
transient = true
|
||||
min_size = Vector2i(600, 400)
|
||||
script = ExtResource("1_wrm15")
|
||||
|
||||
[connection signal="close_requested" from="." to="." method="_on_close_requested"]
|
|
@ -0,0 +1,330 @@
|
|||
@tool
|
||||
extends Panel
|
||||
|
||||
const wizard_config = preload("../../config/wizard_config.gd")
|
||||
|
||||
var _import_helper = preload("./import_helper.gd").new()
|
||||
|
||||
@onready var _tree_container = $MarginContainer/VBoxContainer/HSplitContainer/tree
|
||||
@onready var _resource_tree = _tree_container.get_resource_tree()
|
||||
@onready var _details = $MarginContainer/VBoxContainer/HSplitContainer/MarginContainer/VBoxContainer
|
||||
|
||||
@onready var _nothing_container = $MarginContainer/VBoxContainer/HSplitContainer/MarginContainer/VBoxContainer/nothing
|
||||
@onready var _single_item_container = $MarginContainer/VBoxContainer/HSplitContainer/MarginContainer/VBoxContainer/single_item
|
||||
@onready var _multiple_items_container = $MarginContainer/VBoxContainer/HSplitContainer/MarginContainer/VBoxContainer/multiple_items
|
||||
|
||||
@onready var _confirmation_warning_container = $MarginContainer/VBoxContainer/HSplitContainer/MarginContainer/VBoxContainer/confirmation_warning
|
||||
|
||||
const supported_types = [
|
||||
"Sprite2D",
|
||||
"Sprite3D",
|
||||
"AnimatedSprite2D",
|
||||
"AnimatedSprite3D",
|
||||
"TextureRect",
|
||||
]
|
||||
|
||||
var _selection_count = 0
|
||||
var _current_buttons_container
|
||||
var _resources_to_process
|
||||
|
||||
var _should_save_in = 0
|
||||
|
||||
|
||||
func _ready():
|
||||
_set_empty_details_state()
|
||||
|
||||
var file_tree = _get_file_tree("res://")
|
||||
_setup_tree(file_tree)
|
||||
|
||||
|
||||
# Unfortunately godot throws some nasty warnings when trying to save after
|
||||
# multiple import operations. I implemented this late save as a workaround
|
||||
func _process(delta):
|
||||
if _should_save_in > 0:
|
||||
_should_save_in -= delta
|
||||
if _should_save_in <= 0:
|
||||
_should_save_in = 0
|
||||
_save_all_scenes()
|
||||
|
||||
|
||||
func _get_file_tree(base_path: String, dir_name: String = "") -> Dictionary:
|
||||
var dir_path = base_path.path_join(dir_name)
|
||||
var dir = DirAccess.open(dir_path)
|
||||
var dir_data = { "path": dir_path, "name": dir_name, "children": [], "type": "dir", }
|
||||
if not dir:
|
||||
return dir_data
|
||||
|
||||
dir.list_dir_begin()
|
||||
var file_name = dir.get_next()
|
||||
|
||||
while file_name != "":
|
||||
if dir.current_is_dir() and _is_importable_folder(dir_path, file_name):
|
||||
var child_data = _get_file_tree(dir_path, file_name)
|
||||
if not child_data.children.is_empty():
|
||||
dir_data.children.push_back(child_data)
|
||||
elif file_name.ends_with(".tscn"):
|
||||
var file_path = dir_path.path_join(file_name)
|
||||
var metadata = _get_aseprite_metadata(file_path)
|
||||
if not metadata.is_empty():
|
||||
dir_data.children.push_back({
|
||||
"name": file_name,
|
||||
"path": file_path,
|
||||
"resources": metadata,
|
||||
"type": "file",
|
||||
})
|
||||
|
||||
file_name = dir.get_next()
|
||||
|
||||
return dir_data
|
||||
|
||||
|
||||
func _is_importable_folder(dir_path: String, dir_name: String) -> bool:
|
||||
return dir_path != "res://" or dir_name != "addons"
|
||||
|
||||
|
||||
func _setup_tree(resource_tree: Dictionary) -> void:
|
||||
_resource_tree.set_column_title(0, "Resource")
|
||||
|
||||
var root = _resource_tree.create_item()
|
||||
|
||||
_add_items_to_tree(root, resource_tree.children)
|
||||
|
||||
|
||||
func _add_items_to_tree(root: TreeItem, children: Array):
|
||||
for node in children:
|
||||
var item: TreeItem = _resource_tree.create_item(root)
|
||||
item.set_text(0, node.name)
|
||||
item.set_meta("node", node)
|
||||
match node.type:
|
||||
"dir":
|
||||
item.set_icon(0, get_theme_icon("Folder", "EditorIcons"))
|
||||
_add_items_to_tree(item, node.children)
|
||||
|
||||
"file":
|
||||
item.set_icon(0, get_theme_icon("PackedScene", "EditorIcons"))
|
||||
_add_items_to_tree(item, node.resources)
|
||||
|
||||
"resource":
|
||||
item.set_icon(0, get_theme_icon(node.node_type, "EditorIcons"))
|
||||
if node.has_changes:
|
||||
item.set_text(0, "%s (*)" % node.name)
|
||||
|
||||
|
||||
func _get_aseprite_metadata(file_path: String) -> Array:
|
||||
var scene: PackedScene = load(file_path)
|
||||
var root = scene.instantiate()
|
||||
var state = scene.get_state()
|
||||
|
||||
var resources = []
|
||||
for i in range(state.get_node_count()):
|
||||
var node_type = state.get_node_type(i)
|
||||
if _is_supported_type(node_type):
|
||||
var node_path = state.get_node_path(i)
|
||||
var target_node = root.get_node(node_path)
|
||||
var meta = wizard_config.load_config(target_node)
|
||||
if meta != null:
|
||||
resources.push_back({
|
||||
"type": "resource",
|
||||
"node_type": node_type,
|
||||
"name": node_path,
|
||||
"node_path": node_path,
|
||||
"node_name": state.get_node_name(i),
|
||||
"meta": meta,
|
||||
"scene_path": file_path,
|
||||
"has_changes": _has_source_changes(target_node, meta.get("source"))
|
||||
})
|
||||
|
||||
return resources
|
||||
|
||||
|
||||
func _has_source_changes(target_node: Node, source_path: String) -> bool:
|
||||
if not source_path or source_path == "":
|
||||
return false
|
||||
var saved_hash = wizard_config.get_source_hash(target_node)
|
||||
if saved_hash == "":
|
||||
return false
|
||||
var current_hash = FileAccess.get_md5(source_path)
|
||||
|
||||
return saved_hash != current_hash
|
||||
|
||||
|
||||
func _is_supported_type(node_type: String) -> bool:
|
||||
return supported_types.has(node_type)
|
||||
|
||||
|
||||
func _open_scene(item: TreeItem) -> void:
|
||||
var meta = item.get_meta("node")
|
||||
if meta:
|
||||
EditorInterface.open_scene_from_path(meta.path)
|
||||
|
||||
|
||||
func _trigger_import(meta: Dictionary) -> void:
|
||||
# A more elegant way would have been to change the PackedScene directly, however
|
||||
# during my attempts changing external resources this way was buggy. I decided
|
||||
# to open and edit the scene via editor with the caveat of having to keep it open afterwards.
|
||||
EditorInterface.open_scene_from_path(meta.scene_path)
|
||||
|
||||
var root_node = EditorInterface.get_edited_scene_root()
|
||||
|
||||
if not root_node:
|
||||
printerr("couldn´t open scene %s" % meta.scene_path)
|
||||
|
||||
await _import_helper.import_node(root_node, meta)
|
||||
|
||||
print("Import complete: %s (%s) node from %s" % [ meta.node_path, meta.meta.source, meta.scene_path])
|
||||
|
||||
|
||||
func _on_resource_tree_multi_selected(_item: TreeItem, _column: int, selected: bool) -> void:
|
||||
_confirmation_warning_container.hide()
|
||||
_resources_to_process = null
|
||||
if _current_buttons_container != null:
|
||||
_current_buttons_container.show_buttons()
|
||||
|
||||
if selected:
|
||||
_selection_count += 1
|
||||
else:
|
||||
_selection_count -= 1
|
||||
|
||||
_nothing_container.hide()
|
||||
_single_item_container.hide()
|
||||
_multiple_items_container.hide()
|
||||
|
||||
match _selection_count:
|
||||
0:
|
||||
_nothing_container.show()
|
||||
1:
|
||||
_single_item_container.show()
|
||||
_set_item_details(_resource_tree.get_selected())
|
||||
_current_buttons_container = _single_item_container
|
||||
_:
|
||||
_multiple_items_container.show()
|
||||
_multiple_items_container.set_selected_count(_selection_count)
|
||||
_current_buttons_container = _multiple_items_container
|
||||
|
||||
|
||||
func _set_item_details(item: TreeItem) -> void:
|
||||
if not item.has_meta("node"):
|
||||
return
|
||||
var data = item.get_meta("node")
|
||||
_single_item_container.set_resource_details(data)
|
||||
|
||||
|
||||
func _on_multiple_items_import_triggered():
|
||||
var selected_item = _resource_tree.get_next_selected(null)
|
||||
var all_resources = []
|
||||
var scenes_to_open = 0
|
||||
|
||||
while selected_item != null:
|
||||
scenes_to_open += _set_all_resources(selected_item.get_meta("node"), all_resources)
|
||||
selected_item = _resource_tree.get_next_selected(selected_item)
|
||||
|
||||
_resources_to_process = all_resources
|
||||
_show_confirmation_message(scenes_to_open, all_resources.size())
|
||||
|
||||
|
||||
func _on_single_item_import_triggered():
|
||||
var selected = _resource_tree.get_selected()
|
||||
var meta = selected.get_meta("node")
|
||||
|
||||
if meta.type == "resource":
|
||||
await _trigger_import(_resource_tree.get_selected().get_meta("node"))
|
||||
_set_tree_item_as_saved(_resource_tree.get_selected())
|
||||
_single_item_container.hide_source_change_warning()
|
||||
EditorInterface.save_scene()
|
||||
else:
|
||||
var selected_item = _resource_tree.get_selected()
|
||||
var all_resources = []
|
||||
var scenes_to_open = _set_all_resources(selected_item.get_meta("node"), all_resources)
|
||||
_resources_to_process = all_resources
|
||||
_show_confirmation_message(scenes_to_open, all_resources.size())
|
||||
|
||||
|
||||
func _on_single_item_open_scene_triggered():
|
||||
var selected_item = _resource_tree.get_selected()
|
||||
var meta = selected_item.get_meta("node")
|
||||
if meta.type == "file":
|
||||
EditorInterface.open_scene_from_path(meta.path)
|
||||
else:
|
||||
EditorInterface.open_scene_from_path(meta.scene_path)
|
||||
|
||||
|
||||
func _set_all_resources(meta: Dictionary, resources: Array):
|
||||
var scenes_to_open = 0
|
||||
match meta.type:
|
||||
"dir":
|
||||
for c in meta.children:
|
||||
scenes_to_open += _set_all_resources(c, resources)
|
||||
"file":
|
||||
scenes_to_open += 1
|
||||
for r in meta.resources:
|
||||
if not resources.has(r):
|
||||
resources.push_back(r)
|
||||
"resource":
|
||||
if not resources.has(meta):
|
||||
resources.push_back(meta)
|
||||
return scenes_to_open
|
||||
|
||||
|
||||
func _save_all_scenes():
|
||||
EditorInterface.save_all_scenes()
|
||||
_reload_tree()
|
||||
|
||||
|
||||
func _show_confirmation_message(scenes: int, resources: int):
|
||||
_current_buttons_container.hide_buttons()
|
||||
if scenes > 1:
|
||||
_confirmation_warning_container.set_message("You are about to open %s scenes and re-import %s resources. Do you wish to continue?" % [scenes, resources])
|
||||
else:
|
||||
_confirmation_warning_container.set_message("You are about to re-import %s resources. Do you wish to continue?" % resources)
|
||||
|
||||
_confirmation_warning_container.show()
|
||||
|
||||
|
||||
func _on_resource_tree_refresh_triggered():
|
||||
_set_empty_details_state()
|
||||
_reload_tree()
|
||||
|
||||
|
||||
func _reload_tree():
|
||||
_confirmation_warning_container.hide()
|
||||
_resources_to_process = null
|
||||
if _current_buttons_container != null:
|
||||
_current_buttons_container.show_buttons()
|
||||
_current_buttons_container = null
|
||||
|
||||
_selection_count = 0
|
||||
_resource_tree.clear()
|
||||
var file_tree = _get_file_tree("res://")
|
||||
_setup_tree(file_tree)
|
||||
|
||||
|
||||
func _set_empty_details_state():
|
||||
_nothing_container.show()
|
||||
_single_item_container.hide()
|
||||
_multiple_items_container.hide()
|
||||
_confirmation_warning_container.hide()
|
||||
|
||||
|
||||
func _set_tree_item_as_saved(item: TreeItem) -> void:
|
||||
var meta = item.get_meta("node")
|
||||
meta.has_changes = false
|
||||
item.set_meta("node", meta)
|
||||
item.set_text(0, meta.name)
|
||||
|
||||
|
||||
func _on_confirmation_warning_warning_confirmed():
|
||||
_confirmation_warning_container.hide()
|
||||
_current_buttons_container.show_buttons()
|
||||
|
||||
for resource in _resources_to_process:
|
||||
await _trigger_import(resource)
|
||||
EditorInterface.mark_scene_as_unsaved()
|
||||
_resources_to_process = null
|
||||
|
||||
_should_save_in = 1
|
||||
|
||||
|
||||
func _on_confirmation_warning_warning_declined():
|
||||
_confirmation_warning_container.hide()
|
||||
_current_buttons_container.show_buttons()
|
||||
_resources_to_process = null
|
|
@ -0,0 +1,87 @@
|
|||
[gd_scene load_steps=7 format=3 uid="uid://ci67r2f2btg5"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/AsepriteWizard/interface/imports_manager/dock_imports_panel.gd" id="1_1xyeb"]
|
||||
[ext_resource type="PackedScene" uid="uid://cisgsfvp4nf1g" path="res://addons/AsepriteWizard/interface/shared/tree/resource_tree.tscn" id="2_d1s4o"]
|
||||
[ext_resource type="PackedScene" uid="uid://qgmln507kjnm" path="res://addons/AsepriteWizard/interface/shared/tree/tree_selection_confirmation_warning.tscn" id="3_2us73"]
|
||||
[ext_resource type="PackedScene" uid="uid://dnlk2yep7teea" path="res://addons/AsepriteWizard/interface/imports_manager/tree_selection_single_item.tscn" id="3_4ufqa"]
|
||||
[ext_resource type="PackedScene" uid="uid://bhtu6mlwmthqo" path="res://addons/AsepriteWizard/interface/imports_manager/tree_selection_multiple_items.tscn" id="3_pw8ds"]
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_edrvq"]
|
||||
|
||||
[node name="dock_imports" type="Panel"]
|
||||
custom_minimum_size = Vector2(600, 400)
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
theme_override_styles/panel = SubResource("StyleBoxEmpty_edrvq")
|
||||
script = ExtResource("1_1xyeb")
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="."]
|
||||
custom_minimum_size = Vector2(400, 300)
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
theme_override_constants/margin_left = 10
|
||||
theme_override_constants/margin_top = 10
|
||||
theme_override_constants/margin_right = 10
|
||||
theme_override_constants/margin_bottom = 10
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="HSplitContainer" type="HSplitContainer" parent="MarginContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="tree" parent="MarginContainer/VBoxContainer/HSplitContainer" instance=ExtResource("2_d1s4o")]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="MarginContainer/VBoxContainer/HSplitContainer"]
|
||||
custom_minimum_size = Vector2(200, 0)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/VBoxContainer/HSplitContainer/MarginContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="MarginContainer/VBoxContainer/HSplitContainer/MarginContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="MarginContainer/VBoxContainer/HSplitContainer/MarginContainer/VBoxContainer/MarginContainer"]
|
||||
layout_mode = 2
|
||||
text = "Details"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="MarginContainer/VBoxContainer/HSplitContainer/MarginContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="nothing" type="Label" parent="MarginContainer/VBoxContainer/HSplitContainer/MarginContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 6
|
||||
text = "Nothing selected"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="single_item" parent="MarginContainer/VBoxContainer/HSplitContainer/MarginContainer/VBoxContainer" instance=ExtResource("3_4ufqa")]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="multiple_items" parent="MarginContainer/VBoxContainer/HSplitContainer/MarginContainer/VBoxContainer" instance=ExtResource("3_pw8ds")]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="confirmation_warning" parent="MarginContainer/VBoxContainer/HSplitContainer/MarginContainer/VBoxContainer" instance=ExtResource("3_2us73")]
|
||||
layout_mode = 2
|
||||
|
||||
[connection signal="multi_selected" from="MarginContainer/VBoxContainer/HSplitContainer/tree" to="." method="_on_resource_tree_multi_selected"]
|
||||
[connection signal="refresh_triggered" from="MarginContainer/VBoxContainer/HSplitContainer/tree" to="." method="_on_resource_tree_refresh_triggered"]
|
||||
[connection signal="import_triggered" from="MarginContainer/VBoxContainer/HSplitContainer/MarginContainer/VBoxContainer/single_item" to="." method="_on_single_item_import_triggered"]
|
||||
[connection signal="open_scene_triggered" from="MarginContainer/VBoxContainer/HSplitContainer/MarginContainer/VBoxContainer/single_item" to="." method="_on_single_item_open_scene_triggered"]
|
||||
[connection signal="import_triggered" from="MarginContainer/VBoxContainer/HSplitContainer/MarginContainer/VBoxContainer/multiple_items" to="." method="_on_multiple_items_import_triggered"]
|
||||
[connection signal="warning_confirmed" from="MarginContainer/VBoxContainer/HSplitContainer/MarginContainer/VBoxContainer/confirmation_warning" to="." method="_on_confirmation_warning_warning_confirmed"]
|
||||
[connection signal="warning_declined" from="MarginContainer/VBoxContainer/HSplitContainer/MarginContainer/VBoxContainer/confirmation_warning" to="." method="_on_confirmation_warning_warning_declined"]
|
123
addons/AsepriteWizard/interface/imports_manager/import_helper.gd
Normal file
123
addons/AsepriteWizard/interface/imports_manager/import_helper.gd
Normal file
|
@ -0,0 +1,123 @@
|
|||
@tool
|
||||
extends RefCounted
|
||||
|
||||
const wizard_config = preload("../../config/wizard_config.gd")
|
||||
const result_code = preload("../../config/result_codes.gd")
|
||||
|
||||
var _sprite_animation_creator = preload("../../creators/animation_player/sprite_animation_creator.gd").new()
|
||||
var _texture_rect_animation_creator = preload("../../creators/animation_player/texture_rect_animation_creator.gd").new()
|
||||
var _static_texture_creator = preload("../../creators/static_texture/texture_creator.gd").new()
|
||||
var _sprite_frames_creator = preload("../../creators/sprite_frames/sprite_frames_creator.gd").new()
|
||||
var _aseprite_file_exporter = preload("../../aseprite/file_exporter.gd").new()
|
||||
var _config = preload("../../config/config.gd").new()
|
||||
|
||||
|
||||
func import_node(root_node: Node, meta: Dictionary) -> void:
|
||||
var node = root_node.get_node(meta.node_path)
|
||||
|
||||
if node == null:
|
||||
printerr("Node not found: %s" % meta.node_path)
|
||||
return
|
||||
|
||||
if node is AnimatedSprite2D or node is AnimatedSprite3D:
|
||||
await _sprite_frames_import(node, meta)
|
||||
else:
|
||||
await _animation_import(node, root_node, meta)
|
||||
|
||||
|
||||
func _sprite_frames_import(node: Node, resource_config: Dictionary) -> void:
|
||||
var config = resource_config.meta
|
||||
if not config.source:
|
||||
printerr("Node config missing information.")
|
||||
return
|
||||
|
||||
var source = ProjectSettings.globalize_path(config.source)
|
||||
var options = _parse_import_options(config, resource_config.scene_path.get_base_dir())
|
||||
|
||||
var aseprite_output = _aseprite_file_exporter.generate_aseprite_file(source, options)
|
||||
|
||||
if not aseprite_output.is_ok:
|
||||
printerr(result_code.get_error_message(aseprite_output.code))
|
||||
return
|
||||
|
||||
EditorInterface.get_resource_filesystem().scan()
|
||||
|
||||
await EditorInterface.get_resource_filesystem().filesystem_changed
|
||||
|
||||
_sprite_frames_creator.create_animations(node, aseprite_output.content, { "slice": options.slice })
|
||||
|
||||
wizard_config.set_source_hash(node, FileAccess.get_md5(source))
|
||||
_handle_cleanup(aseprite_output.content)
|
||||
|
||||
|
||||
func _animation_import(node: Node, root_node: Node, resource_config: Dictionary) -> void:
|
||||
if not resource_config.meta.source:
|
||||
printerr("Node config missing information.")
|
||||
return
|
||||
|
||||
if resource_config.meta.get("i_mode", 0) == 0:
|
||||
await _import_to_animation_player(node, root_node, resource_config)
|
||||
else:
|
||||
await _import_static(node, resource_config)
|
||||
|
||||
|
||||
func _import_to_animation_player(node: Node, root: Node, resource_config: Dictionary) -> void:
|
||||
var config = resource_config.meta
|
||||
var source = ProjectSettings.globalize_path(config.source)
|
||||
var options = _parse_import_options(config, resource_config.scene_path.get_base_dir())
|
||||
|
||||
var aseprite_output = _aseprite_file_exporter.generate_aseprite_file(source, options)
|
||||
if not aseprite_output.is_ok:
|
||||
printerr(result_code.get_error_message(aseprite_output.code))
|
||||
return
|
||||
|
||||
EditorInterface.get_resource_filesystem().scan()
|
||||
await EditorInterface.get_resource_filesystem().filesystem_changed
|
||||
|
||||
var anim_options = {
|
||||
"keep_anim_length": config.keep_anim_length,
|
||||
"cleanup_hide_unused_nodes": config.get("set_vis_track"),
|
||||
"slice": config.get("slice", ""),
|
||||
}
|
||||
var animation_creator = _texture_rect_animation_creator if node is TextureRect else _sprite_animation_creator
|
||||
animation_creator.create_animations(node, root.get_node(config.player), aseprite_output.content, anim_options)
|
||||
|
||||
wizard_config.set_source_hash(node, FileAccess.get_md5(source))
|
||||
_handle_cleanup(aseprite_output.content)
|
||||
|
||||
|
||||
func _import_static(node: Node, resource_config: Dictionary) -> void:
|
||||
var config = resource_config.meta
|
||||
var source = ProjectSettings.globalize_path(config.source)
|
||||
var options = _parse_import_options(config, resource_config.scene_path.get_base_dir())
|
||||
options["first_frame_only"] = true
|
||||
|
||||
var aseprite_output = _aseprite_file_exporter.generate_aseprite_file(source, options)
|
||||
|
||||
if not aseprite_output.is_ok:
|
||||
printerr(result_code.get_error_message(aseprite_output.code))
|
||||
return
|
||||
|
||||
EditorInterface.get_resource_filesystem().scan()
|
||||
await EditorInterface.get_resource_filesystem().filesystem_changed
|
||||
|
||||
_static_texture_creator.load_texture(node, aseprite_output.content, { "slice": options.slice })
|
||||
|
||||
wizard_config.set_source_hash(node, FileAccess.get_md5(source))
|
||||
_handle_cleanup(aseprite_output.content)
|
||||
|
||||
|
||||
func _parse_import_options(config: Dictionary, scene_base_path: String) -> Dictionary:
|
||||
return {
|
||||
"output_folder": config.o_folder if config.o_folder != "" else scene_base_path,
|
||||
"exception_pattern": config.o_ex_p,
|
||||
"only_visible_layers": config.only_visible,
|
||||
"output_filename": config.o_name,
|
||||
"layer": config.get("layer"),
|
||||
"slice": config.get("slice", ""),
|
||||
}
|
||||
|
||||
func _handle_cleanup(aseprite_content):
|
||||
if _config.should_remove_source_files():
|
||||
DirAccess.remove_absolute(aseprite_content.data_file)
|
||||
EditorInterface.get_resource_filesystem().scan()
|
|
@ -0,0 +1,28 @@
|
|||
@tool
|
||||
extends MarginContainer
|
||||
|
||||
signal dock_requested
|
||||
|
||||
enum Tabs {
|
||||
DOCK_IMPORTS = 0,
|
||||
}
|
||||
|
||||
@onready var _tabs: TabContainer = $TabContainer
|
||||
@onready var _dock_button: Button = $dock_button
|
||||
|
||||
func _ready():
|
||||
_tabs.set_tab_title(Tabs.DOCK_IMPORTS, "Dock Imports")
|
||||
_dock_button.icon = get_theme_icon("MakeFloating", "EditorIcons")
|
||||
set_as_floating()
|
||||
|
||||
|
||||
func _on_dock_button_pressed():
|
||||
dock_requested.emit()
|
||||
|
||||
|
||||
func set_as_floating():
|
||||
_dock_button.tooltip_text = "Dock window"
|
||||
|
||||
|
||||
func set_as_docked():
|
||||
_dock_button.tooltip_text = "Undock window"
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,22 @@
|
|||
@tool
|
||||
extends LineEdit
|
||||
|
||||
signal change_finished(text: String)
|
||||
|
||||
@export var debounce_time_in_seconds: float = 0.3
|
||||
|
||||
var _time_since_last_change: float = 0.0
|
||||
var _has_pending_changes: bool = false
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
if _has_pending_changes:
|
||||
_time_since_last_change += delta
|
||||
if _time_since_last_change > debounce_time_in_seconds:
|
||||
_has_pending_changes = false
|
||||
change_finished.emit(self.text)
|
||||
|
||||
|
||||
func _on_text_changed(_new_text: String) -> void:
|
||||
_has_pending_changes = true
|
||||
_time_since_last_change = 0
|
|
@ -0,0 +1,22 @@
|
|||
@tool
|
||||
extends VBoxContainer
|
||||
|
||||
signal import_triggered
|
||||
|
||||
@onready var _import_message = $message
|
||||
@onready var _import_button = $buttons
|
||||
|
||||
func set_selected_count(number_of_items: int) -> void:
|
||||
_import_message.text = "%2d items selected" % number_of_items
|
||||
|
||||
|
||||
func show_buttons():
|
||||
_import_button.show()
|
||||
|
||||
|
||||
func hide_buttons():
|
||||
_import_button.hide()
|
||||
|
||||
|
||||
func _on_import_selected_button_up():
|
||||
import_triggered.emit()
|
|
@ -0,0 +1,25 @@
|
|||
[gd_scene load_steps=2 format=3 uid="uid://bhtu6mlwmthqo"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/AsepriteWizard/interface/imports_manager/tree_selection_multiple_items.gd" id="1_beamo"]
|
||||
|
||||
[node name="multiple_items" type="VBoxContainer"]
|
||||
visible = false
|
||||
size_flags_vertical = 3
|
||||
script = ExtResource("1_beamo")
|
||||
|
||||
[node name="message" type="Label" parent="."]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 6
|
||||
text = "Multiple items selected
|
||||
"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="buttons" type="HFlowContainer" parent="."]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="import_selected" type="Button" parent="buttons"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Re-Import all"
|
||||
|
||||
[connection signal="button_up" from="buttons/import_selected" to="." method="_on_import_selected_button_up"]
|
|
@ -0,0 +1,141 @@
|
|||
@tool
|
||||
extends VBoxContainer
|
||||
|
||||
signal import_triggered
|
||||
signal open_scene_triggered
|
||||
|
||||
@onready var _name = $GridContainer/name_value
|
||||
@onready var _type = $GridContainer/type_value
|
||||
@onready var _path = $GridContainer/path_value
|
||||
|
||||
@onready var _source_label = $GridContainer/source_file_label
|
||||
@onready var _source = $GridContainer/source_file_value
|
||||
@onready var _layer_label = $GridContainer/layer_label
|
||||
@onready var _layer = $GridContainer/layer_value
|
||||
@onready var _slice_label = $GridContainer/slice_label
|
||||
@onready var _slice = $GridContainer/slice_value
|
||||
|
||||
@onready var _o_name_label = $GridContainer/o_name_label
|
||||
@onready var _o_name = $GridContainer/o_name_value
|
||||
@onready var _o_folder_label = $GridContainer/o_folder_label
|
||||
@onready var _o_folder_value = $GridContainer/o_folder_value
|
||||
|
||||
|
||||
@onready var _resource_buttons = $resource_buttons
|
||||
@onready var _dir_buttons = $dir_buttons
|
||||
@onready var _scene_buttons = $scene_buttons
|
||||
|
||||
@onready var _source_change_warning = $source_changed_warning
|
||||
|
||||
@onready var _resource_only_fields = [
|
||||
_source_label,
|
||||
_source,
|
||||
_layer_label,
|
||||
_layer,
|
||||
_slice_label,
|
||||
_slice,
|
||||
_o_name_label,
|
||||
_o_name,
|
||||
_o_folder_label,
|
||||
_o_folder_value,
|
||||
_source_change_warning,
|
||||
]
|
||||
|
||||
var _current_resource_type = "resource"
|
||||
var _resource_config: Dictionary = {}
|
||||
|
||||
func _ready():
|
||||
_source_change_warning.set_text("Source file changed since last import")
|
||||
_source_change_warning.hide()
|
||||
|
||||
|
||||
func set_resource_details(resource_details: Dictionary) -> void:
|
||||
_resource_config = resource_details
|
||||
_resource_buttons.hide()
|
||||
_dir_buttons.hide()
|
||||
_scene_buttons.hide()
|
||||
_source_change_warning.hide()
|
||||
|
||||
_current_resource_type = resource_details.type
|
||||
match resource_details.type:
|
||||
"dir":
|
||||
_name.text = resource_details.name
|
||||
_type.text = "Folder"
|
||||
_path.text = resource_details.path
|
||||
_hide_resource_fields()
|
||||
_dir_buttons.show()
|
||||
"file":
|
||||
_name.text = resource_details.name
|
||||
_type.text = "File"
|
||||
_path.text = resource_details.path
|
||||
_hide_resource_fields()
|
||||
_scene_buttons.show()
|
||||
"resource":
|
||||
_name.text = resource_details.node_name
|
||||
_type.text = resource_details.node_type
|
||||
_path.text = resource_details.node_path
|
||||
|
||||
var meta = resource_details.meta
|
||||
_source.text = meta.source
|
||||
_layer.text = "All" if meta.get("layer", "") == "" else meta.layer
|
||||
_slice.text = "All" if meta.get("slice", "") == "" else meta.slice
|
||||
|
||||
var folder = resource_details.scene_path.get_base_dir() if meta.get("o_folder", "") == "" else meta.o_folder
|
||||
var file_name = "" if meta.get("o_name", "") == "" else meta.o_name
|
||||
|
||||
if _layer.text != "All":
|
||||
file_name += _layer.text
|
||||
elif file_name == "":
|
||||
file_name = meta.source.get_basename().get_file()
|
||||
_o_name.text = "%s/%s.png" % [folder, file_name]
|
||||
|
||||
_show_resource_fields()
|
||||
_resource_buttons.show()
|
||||
|
||||
_source_change_warning.visible = resource_details.has_changes
|
||||
|
||||
|
||||
func _hide_resource_fields():
|
||||
for f in _resource_only_fields:
|
||||
f.hide()
|
||||
|
||||
|
||||
func _show_resource_fields():
|
||||
for f in _resource_only_fields:
|
||||
f.show()
|
||||
|
||||
|
||||
func show_buttons():
|
||||
match _current_resource_type:
|
||||
"resource":
|
||||
_resource_buttons.show()
|
||||
"scene":
|
||||
_scene_buttons.show()
|
||||
_:
|
||||
_dir_buttons.show()
|
||||
|
||||
|
||||
func hide_buttons():
|
||||
_resource_buttons.hide()
|
||||
_dir_buttons.hide()
|
||||
_scene_buttons.hide()
|
||||
|
||||
|
||||
func hide_source_change_warning():
|
||||
_source_change_warning.hide()
|
||||
|
||||
|
||||
func _on_show_dir_in_fs_button_up():
|
||||
EditorInterface.get_file_system_dock().navigate_to_path(_path.text)
|
||||
|
||||
|
||||
func _on_import_all_button_up():
|
||||
import_triggered.emit()
|
||||
|
||||
|
||||
func _on_import_button_up():
|
||||
import_triggered.emit()
|
||||
|
||||
|
||||
func _on_open_scene_button_up():
|
||||
open_scene_triggered.emit()
|
|
@ -0,0 +1,158 @@
|
|||
[gd_scene load_steps=3 format=3 uid="uid://dnlk2yep7teea"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/AsepriteWizard/interface/imports_manager/tree_selection_single_item.gd" id="1_bb3ui"]
|
||||
[ext_resource type="PackedScene" uid="uid://c1l0bk12iwln3" path="res://addons/AsepriteWizard/interface/shared/tree/inline_warning_panel.tscn" id="2_weuqf"]
|
||||
|
||||
[node name="single_item" type="VBoxContainer"]
|
||||
visible = false
|
||||
script = ExtResource("1_bb3ui")
|
||||
|
||||
[node name="GridContainer" type="GridContainer" parent="."]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
theme_override_constants/h_separation = 10
|
||||
columns = 2
|
||||
|
||||
[node name="type_label" type="Label" parent="GridContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 0
|
||||
text = "Type"
|
||||
|
||||
[node name="type_value" type="Label" parent="GridContainer"]
|
||||
layout_mode = 2
|
||||
text = "-"
|
||||
|
||||
[node name="name_label" type="Label" parent="GridContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 0
|
||||
text = "Name"
|
||||
|
||||
[node name="name_value" type="Label" parent="GridContainer"]
|
||||
custom_minimum_size = Vector2(50, 0)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "-"
|
||||
autowrap_mode = 2
|
||||
|
||||
[node name="path_label" type="Label" parent="GridContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 0
|
||||
text = "Path"
|
||||
|
||||
[node name="path_value" type="Label" parent="GridContainer"]
|
||||
custom_minimum_size = Vector2(50, 0)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "-"
|
||||
autowrap_mode = 1
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="GridContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="HSeparator2" type="HSeparator" parent="GridContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="source_file_label" type="Label" parent="GridContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 0
|
||||
text = "Aseprite File"
|
||||
|
||||
[node name="source_file_value" type="Label" parent="GridContainer"]
|
||||
custom_minimum_size = Vector2(50, 0)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "-"
|
||||
autowrap_mode = 1
|
||||
|
||||
[node name="layer_label" type="Label" parent="GridContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 0
|
||||
text = "Layer"
|
||||
|
||||
[node name="layer_value" type="Label" parent="GridContainer"]
|
||||
layout_mode = 2
|
||||
text = "-"
|
||||
|
||||
[node name="slice_label" type="Label" parent="GridContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 0
|
||||
text = "Slice"
|
||||
|
||||
[node name="slice_value" type="Label" parent="GridContainer"]
|
||||
layout_mode = 2
|
||||
text = "-"
|
||||
|
||||
[node name="o_folder_label" type="Label" parent="GridContainer"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 0
|
||||
text = "Output folder"
|
||||
|
||||
[node name="o_folder_value" type="Label" parent="GridContainer"]
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(50, 0)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "-"
|
||||
|
||||
[node name="o_name_label" type="Label" parent="GridContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 0
|
||||
text = "Spritesheet name"
|
||||
|
||||
[node name="o_name_value" type="Label" parent="GridContainer"]
|
||||
layout_mode = 2
|
||||
text = "-"
|
||||
|
||||
[node name="source_changed_warning" parent="." instance=ExtResource("2_weuqf")]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="resource_buttons" type="HFlowContainer" parent="."]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
|
||||
[node name="import" type="Button" parent="resource_buttons"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Re-Import"
|
||||
|
||||
[node name="open_scene" type="Button" parent="resource_buttons"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Open Scene"
|
||||
|
||||
[node name="scene_buttons" type="HFlowContainer" parent="."]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
|
||||
[node name="import_all" type="Button" parent="scene_buttons"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Re-Import all"
|
||||
|
||||
[node name="open_scene" type="Button" parent="scene_buttons"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Open Scene"
|
||||
|
||||
[node name="dir_buttons" type="HFlowContainer" parent="."]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
|
||||
[node name="import_all" type="Button" parent="dir_buttons"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Re-Import all"
|
||||
|
||||
[node name="show_dir_in_fs" type="Button" parent="dir_buttons"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Show In FileSystem"
|
||||
|
||||
[connection signal="button_up" from="resource_buttons/import" to="." method="_on_import_button_up"]
|
||||
[connection signal="button_up" from="resource_buttons/open_scene" to="." method="_on_open_scene_button_up"]
|
||||
[connection signal="button_up" from="scene_buttons/import_all" to="." method="_on_import_all_button_up"]
|
||||
[connection signal="button_up" from="scene_buttons/open_scene" to="." method="_on_open_scene_button_up"]
|
||||
[connection signal="button_up" from="dir_buttons/import_all" to="." method="_on_import_all_button_up"]
|
||||
[connection signal="button_up" from="dir_buttons/show_dir_in_fs" to="." method="_on_show_dir_in_fs_button_up"]
|
|
@ -0,0 +1,15 @@
|
|||
@tool
|
||||
extends Button
|
||||
|
||||
signal node_dropped(node_path)
|
||||
|
||||
func _can_drop_data(_pos, data):
|
||||
if data.type == "nodes":
|
||||
var node = get_node(data.nodes[0])
|
||||
return node is AnimationPlayer
|
||||
return false
|
||||
|
||||
|
||||
func _drop_data(_pos, data):
|
||||
var path = data.nodes[0]
|
||||
node_dropped.emit(path)
|
|
@ -0,0 +1,12 @@
|
|||
[gd_scene load_steps=2 format=3 uid="uid://x1f1t87m582u"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/AsepriteWizard/interface/shared/animation_player_drop_button.gd" id="1_tfmxw"]
|
||||
|
||||
[node name="options" type="OptionButton"]
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 2.0
|
||||
item_count = 1
|
||||
selected = 0
|
||||
popup/item_0/text = "[empty]"
|
||||
popup/item_0/id = 0
|
||||
script = ExtResource("1_tfmxw")
|
14
addons/AsepriteWizard/interface/shared/dir_drop_button.gd
Normal file
14
addons/AsepriteWizard/interface/shared/dir_drop_button.gd
Normal file
|
@ -0,0 +1,14 @@
|
|||
@tool
|
||||
extends Button
|
||||
|
||||
signal dir_dropped(path)
|
||||
|
||||
func _can_drop_data(_pos, data):
|
||||
if data.type == "files_and_dirs":
|
||||
var dir_access = DirAccess.open(data.files[0])
|
||||
return dir_access != null
|
||||
return false
|
||||
|
||||
|
||||
func _drop_data(_pos, data):
|
||||
dir_dropped.emit(data.files[0])
|
10
addons/AsepriteWizard/interface/shared/dir_drop_button.tscn
Normal file
10
addons/AsepriteWizard/interface/shared/dir_drop_button.tscn
Normal file
|
@ -0,0 +1,10 @@
|
|||
[gd_scene load_steps=2 format=3 uid="uid://cwvgnm3o7eed2"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/AsepriteWizard/interface/shared/dir_drop_button.gd" id="1_7uqph"]
|
||||
|
||||
[node name="button" type="Button"]
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 2.0
|
||||
text = "[empty]"
|
||||
clip_text = true
|
||||
script = ExtResource("1_7uqph")
|
15
addons/AsepriteWizard/interface/shared/source_drop_button.gd
Normal file
15
addons/AsepriteWizard/interface/shared/source_drop_button.gd
Normal file
|
@ -0,0 +1,15 @@
|
|||
@tool
|
||||
extends Button
|
||||
|
||||
signal aseprite_file_dropped(path)
|
||||
|
||||
func _can_drop_data(_pos, data):
|
||||
if data.type == "files":
|
||||
var extension = data.files[0].get_extension()
|
||||
return extension == "ase" or extension == "aseprite"
|
||||
return false
|
||||
|
||||
|
||||
func _drop_data(_pos, data):
|
||||
var path = data.files[0]
|
||||
aseprite_file_dropped.emit(path)
|
|
@ -0,0 +1,10 @@
|
|||
[gd_scene load_steps=2 format=3 uid="uid://dj1uo3blocr8e"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/AsepriteWizard/interface/shared/source_drop_button.gd" id="1_smfgi"]
|
||||
|
||||
[node name="source_drop_button" type="Button"]
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 2.0
|
||||
text = "[empty]"
|
||||
clip_text = true
|
||||
script = ExtResource("1_smfgi")
|
|
@ -0,0 +1,19 @@
|
|||
@tool
|
||||
extends PanelContainer
|
||||
|
||||
@onready var _message = $MarginContainer/HBoxContainer/Label
|
||||
|
||||
func _ready():
|
||||
_configure_source_warning()
|
||||
|
||||
|
||||
func _configure_source_warning():
|
||||
var sb = self.get_theme_stylebox("panel")
|
||||
var color = EditorInterface.get_editor_settings().get_setting("interface/theme/accent_color")
|
||||
color.a = 0.2
|
||||
sb.bg_color = color
|
||||
self.get_node("MarginContainer/HBoxContainer/Icon").texture = get_theme_icon("NodeInfo", "EditorIcons")
|
||||
|
||||
|
||||
func set_text(text: String):
|
||||
_message.text = text
|
File diff suppressed because one or more lines are too long
55
addons/AsepriteWizard/interface/shared/tree/resource_tree.gd
Normal file
55
addons/AsepriteWizard/interface/shared/tree/resource_tree.gd
Normal file
|
@ -0,0 +1,55 @@
|
|||
@tool
|
||||
extends VBoxContainer
|
||||
|
||||
signal refresh_triggered
|
||||
signal multi_selected(item: TreeItem, column: int, selected: bool)
|
||||
|
||||
@onready var _tree: Tree = $Tree
|
||||
|
||||
func _on_tree_filter_change_finished(text):
|
||||
var tree_root: TreeItem = _tree.get_root()
|
||||
|
||||
if text == "":
|
||||
tree_root.call_recursive("set", "visible", true)
|
||||
return
|
||||
tree_root.call_recursive("set", "visible", false)
|
||||
|
||||
_make_matching_children_visible(tree_root, text.to_lower())
|
||||
|
||||
|
||||
func _make_matching_children_visible(tree_root: TreeItem, text: String) -> void:
|
||||
for c in tree_root.get_children():
|
||||
if c.get_text(0).to_lower().contains(text):
|
||||
c.visible = true
|
||||
_ensure_parent_visible(c)
|
||||
_make_matching_children_visible(c, text)
|
||||
|
||||
|
||||
func _ensure_parent_visible(tree_item: TreeItem) -> void:
|
||||
var node_parent = tree_item.get_parent()
|
||||
if node_parent != null and not node_parent.visible:
|
||||
node_parent.visible = true
|
||||
_ensure_parent_visible(node_parent)
|
||||
|
||||
|
||||
func _on_expand_all_pressed():
|
||||
var tree_root: TreeItem = _tree.get_root()
|
||||
tree_root.set_collapsed_recursive(false)
|
||||
|
||||
|
||||
func _on_collapse_all_pressed():
|
||||
var tree_root: TreeItem = _tree.get_root()
|
||||
tree_root.set_collapsed_recursive(true)
|
||||
tree_root.collapsed = false
|
||||
|
||||
|
||||
func _on_refresh_tree_pressed():
|
||||
refresh_triggered.emit()
|
||||
|
||||
|
||||
func _on_tree_multi_selected(item: TreeItem, column: int, selected: bool):
|
||||
multi_selected.emit(item, column, selected)
|
||||
|
||||
|
||||
func get_resource_tree() -> Tree:
|
||||
return _tree
|
|
@ -0,0 +1,51 @@
|
|||
[gd_scene load_steps=3 format=3 uid="uid://cisgsfvp4nf1g"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/AsepriteWizard/interface/shared/tree/resource_tree.gd" id="1_io4rc"]
|
||||
[ext_resource type="Script" path="res://addons/AsepriteWizard/interface/imports_manager/tree_filter_field.gd" id="1_q7epo"]
|
||||
|
||||
[node name="resource_tree" type="VBoxContainer"]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
size_flags_horizontal = 3
|
||||
script = ExtResource("1_io4rc")
|
||||
|
||||
[node name="buttons" type="HFlowContainer" parent="."]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="tree_filter" type="LineEdit" parent="buttons"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
placeholder_text = "Filter..."
|
||||
clear_button_enabled = true
|
||||
script = ExtResource("1_q7epo")
|
||||
|
||||
[node name="expand_all" type="Button" parent="buttons"]
|
||||
custom_minimum_size = Vector2(150, 0)
|
||||
layout_mode = 2
|
||||
text = "Expand All"
|
||||
|
||||
[node name="collapse_all" type="Button" parent="buttons"]
|
||||
custom_minimum_size = Vector2(150, 0)
|
||||
layout_mode = 2
|
||||
text = "Collapse All"
|
||||
|
||||
[node name="refresh_tree" type="Button" parent="buttons"]
|
||||
custom_minimum_size = Vector2(150, 0)
|
||||
layout_mode = 2
|
||||
text = "Refresh Tree"
|
||||
|
||||
[node name="Tree" type="Tree" parent="."]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
hide_root = true
|
||||
select_mode = 2
|
||||
|
||||
[connection signal="change_finished" from="buttons/tree_filter" to="." method="_on_tree_filter_change_finished"]
|
||||
[connection signal="text_changed" from="buttons/tree_filter" to="buttons/tree_filter" method="_on_text_changed"]
|
||||
[connection signal="pressed" from="buttons/expand_all" to="." method="_on_expand_all_pressed"]
|
||||
[connection signal="pressed" from="buttons/collapse_all" to="." method="_on_collapse_all_pressed"]
|
||||
[connection signal="pressed" from="buttons/refresh_tree" to="." method="_on_refresh_tree_pressed"]
|
||||
[connection signal="multi_selected" from="Tree" to="." method="_on_tree_multi_selected"]
|
|
@ -0,0 +1,18 @@
|
|||
@tool
|
||||
extends VBoxContainer
|
||||
|
||||
signal warning_confirmed
|
||||
signal warning_declined
|
||||
|
||||
@onready var _warning_message = $MarginContainer/warning_message
|
||||
|
||||
func set_message(text: String) -> void:
|
||||
_warning_message.text = text
|
||||
|
||||
|
||||
func _on_confirm_button_up():
|
||||
warning_confirmed.emit()
|
||||
|
||||
|
||||
func _on_cancel_button_up():
|
||||
warning_declined.emit()
|
|
@ -0,0 +1,34 @@
|
|||
[gd_scene load_steps=2 format=3 uid="uid://qgmln507kjnm"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/AsepriteWizard/interface/shared/tree/tree_selection_confirmation_warning.gd" id="1_7gtu1"]
|
||||
|
||||
[node name="confirmation_warning" type="VBoxContainer"]
|
||||
script = ExtResource("1_7gtu1")
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="."]
|
||||
layout_mode = 2
|
||||
theme_override_constants/margin_left = 10
|
||||
theme_override_constants/margin_top = 10
|
||||
theme_override_constants/margin_right = 10
|
||||
theme_override_constants/margin_bottom = 10
|
||||
|
||||
[node name="warning_message" type="Label" parent="MarginContainer"]
|
||||
custom_minimum_size = Vector2(100, 0)
|
||||
layout_mode = 2
|
||||
autowrap_mode = 2
|
||||
|
||||
[node name="buttons" type="HFlowContainer" parent="."]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="confirm" type="Button" parent="buttons"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Confirm"
|
||||
|
||||
[node name="cancel" type="Button" parent="buttons"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Cancel"
|
||||
|
||||
[connection signal="button_up" from="buttons/confirm" to="." method="_on_confirm_button_up"]
|
||||
[connection signal="button_up" from="buttons/cancel" to="." method="_on_cancel_button_up"]
|
7
addons/AsepriteWizard/plugin.cfg
Normal file
7
addons/AsepriteWizard/plugin.cfg
Normal file
|
@ -0,0 +1,7 @@
|
|||
[plugin]
|
||||
|
||||
name="Aseprite Wizard"
|
||||
description="Import Aseprite files to Godot in many different ways."
|
||||
author="Vinicius Gerevini"
|
||||
version="7.5.0"
|
||||
script="plugin.gd"
|
208
addons/AsepriteWizard/plugin.gd
Normal file
208
addons/AsepriteWizard/plugin.gd
Normal file
|
@ -0,0 +1,208 @@
|
|||
@tool
|
||||
extends EditorPlugin
|
||||
|
||||
# importers
|
||||
const NoopImportPlugin = preload("importers/noop_import_plugin.gd")
|
||||
const SpriteFramesImportPlugin = preload("importers/sprite_frames_import_plugin.gd")
|
||||
const TilesetTextureImportPlugin = preload("importers/tileset_texture_import_plugin.gd")
|
||||
const TextureImportPlugin = preload("importers/static_texture_import_plugin.gd")
|
||||
|
||||
# export
|
||||
const ExportPlugin = preload("export/metadata_export_plugin.gd")
|
||||
# interface
|
||||
const ConfigDialog = preload('config/config_dialog.tscn')
|
||||
const WizardWindow = preload("interface/docks/wizard/as_wizard_dock_container.tscn")
|
||||
const AsepriteDockImportsWindow = preload('interface/imports_manager/aseprite_imports_manager.tscn')
|
||||
const ImportsManagerPanels = preload('interface/imports_manager/import_panels.tscn')
|
||||
|
||||
const AnimatedSpriteInspectorPlugin = preload("interface/docks/animated_sprite/inspector_plugin.gd")
|
||||
const SpriteInspectorPlugin = preload("interface/docks/sprite/inspector_plugin.gd")
|
||||
|
||||
const tool_menu_name = "Aseprite Wizard"
|
||||
const menu_item_name = "Spritesheet Wizard Dock..."
|
||||
const config_menu_item_name = "Config..."
|
||||
const import_menu_item_name = "Imports Manager..."
|
||||
|
||||
var config = preload("config/config.gd").new()
|
||||
var window: TabContainer
|
||||
var config_window: PopupPanel
|
||||
var imports_list_window: Window
|
||||
var imports_list_panel: MarginContainer
|
||||
var export_plugin : EditorExportPlugin
|
||||
var sprite_inspector_plugin: EditorInspectorPlugin
|
||||
var animated_sprite_inspector_plugin: EditorInspectorPlugin
|
||||
|
||||
var _exporter_enabled = false
|
||||
|
||||
var _importers = []
|
||||
|
||||
var _is_import_list_docked = false
|
||||
|
||||
func _enter_tree():
|
||||
_load_config()
|
||||
_setup_menu_entries()
|
||||
_setup_importer()
|
||||
_setup_exporter()
|
||||
_setup_animated_sprite_inspector_plugin()
|
||||
_setup_sprite_inspector_plugin()
|
||||
|
||||
|
||||
func _exit_tree():
|
||||
_disable_plugin()
|
||||
|
||||
|
||||
func _disable_plugin():
|
||||
_remove_menu_entries()
|
||||
_remove_importer()
|
||||
_remove_exporter()
|
||||
_remove_wizard_dock()
|
||||
_remove_inspector_plugins()
|
||||
|
||||
|
||||
func _load_config():
|
||||
config.initialize_project_settings()
|
||||
|
||||
|
||||
func _setup_menu_entries():
|
||||
var submenu = PopupMenu.new()
|
||||
add_tool_submenu_item(tool_menu_name, submenu)
|
||||
submenu.add_item(menu_item_name)
|
||||
submenu.add_item(import_menu_item_name)
|
||||
submenu.add_item(config_menu_item_name)
|
||||
submenu.index_pressed.connect(_on_tool_menu_pressed)
|
||||
|
||||
|
||||
func _remove_menu_entries():
|
||||
remove_tool_menu_item(tool_menu_name)
|
||||
|
||||
|
||||
func _setup_importer():
|
||||
_importers = [
|
||||
NoopImportPlugin.new(),
|
||||
SpriteFramesImportPlugin.new(),
|
||||
TilesetTextureImportPlugin.new(),
|
||||
TextureImportPlugin.new(),
|
||||
]
|
||||
|
||||
for i in _importers:
|
||||
add_import_plugin(i)
|
||||
|
||||
|
||||
func _remove_importer():
|
||||
for i in _importers:
|
||||
remove_import_plugin(i)
|
||||
|
||||
|
||||
func _setup_exporter():
|
||||
if config.is_exporter_enabled():
|
||||
export_plugin = ExportPlugin.new()
|
||||
add_export_plugin(export_plugin)
|
||||
_exporter_enabled = true
|
||||
|
||||
|
||||
func _remove_exporter():
|
||||
if _exporter_enabled:
|
||||
remove_export_plugin(export_plugin)
|
||||
_exporter_enabled = false
|
||||
|
||||
|
||||
func _setup_sprite_inspector_plugin():
|
||||
sprite_inspector_plugin = SpriteInspectorPlugin.new()
|
||||
add_inspector_plugin(sprite_inspector_plugin)
|
||||
|
||||
|
||||
func _setup_animated_sprite_inspector_plugin():
|
||||
animated_sprite_inspector_plugin = AnimatedSpriteInspectorPlugin.new()
|
||||
add_inspector_plugin(animated_sprite_inspector_plugin)
|
||||
|
||||
|
||||
func _remove_inspector_plugins():
|
||||
remove_inspector_plugin(sprite_inspector_plugin)
|
||||
remove_inspector_plugin(animated_sprite_inspector_plugin)
|
||||
|
||||
|
||||
func _remove_wizard_dock():
|
||||
if window:
|
||||
remove_control_from_bottom_panel(window)
|
||||
window.queue_free()
|
||||
window = null
|
||||
|
||||
|
||||
func _open_window():
|
||||
if window:
|
||||
make_bottom_panel_item_visible(window)
|
||||
return
|
||||
|
||||
window = WizardWindow.instantiate()
|
||||
window.connect("close_requested",Callable(self,"_on_window_closed"))
|
||||
add_control_to_bottom_panel(window, "Aseprite Wizard")
|
||||
make_bottom_panel_item_visible(window)
|
||||
|
||||
|
||||
func _open_config_dialog():
|
||||
if is_instance_valid(config_window):
|
||||
config_window.queue_free()
|
||||
|
||||
config_window = ConfigDialog.instantiate()
|
||||
get_editor_interface().get_base_control().add_child(config_window)
|
||||
config_window.popup_centered()
|
||||
|
||||
|
||||
func _open_import_list_dialog():
|
||||
if is_instance_valid(imports_list_window):
|
||||
imports_list_window.queue_free()
|
||||
|
||||
if is_instance_valid(imports_list_panel):
|
||||
if _is_import_list_docked:
|
||||
remove_control_from_bottom_panel(imports_list_panel)
|
||||
_is_import_list_docked = false
|
||||
imports_list_panel.queue_free()
|
||||
imports_list_panel = null
|
||||
|
||||
imports_list_panel = ImportsManagerPanels.instantiate()
|
||||
imports_list_panel.dock_requested.connect(_on_import_list_dock_requested)
|
||||
_create_imports_manager_window(imports_list_panel)
|
||||
|
||||
|
||||
func _on_window_closed():
|
||||
if window:
|
||||
remove_control_from_bottom_panel(window)
|
||||
window.queue_free()
|
||||
window = null
|
||||
|
||||
|
||||
func _on_tool_menu_pressed(index):
|
||||
match index:
|
||||
0: # wizard dock
|
||||
_open_window()
|
||||
1: # imports
|
||||
_open_import_list_dialog()
|
||||
2: # config
|
||||
_open_config_dialog()
|
||||
|
||||
|
||||
func _on_import_list_dock_requested():
|
||||
if _is_import_list_docked:
|
||||
remove_control_from_bottom_panel(imports_list_panel)
|
||||
_is_import_list_docked = false
|
||||
_create_imports_manager_window(imports_list_panel)
|
||||
imports_list_panel.show()
|
||||
imports_list_panel.anchors_preset = Control.PRESET_FULL_RECT
|
||||
imports_list_panel.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
imports_list_panel.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
imports_list_panel.set_as_floating()
|
||||
return
|
||||
|
||||
_is_import_list_docked = true
|
||||
imports_list_panel.set_as_docked()
|
||||
imports_list_window.remove_child(imports_list_panel)
|
||||
imports_list_window.queue_free()
|
||||
add_control_to_bottom_panel(imports_list_panel, "Aseprite Imports Manager")
|
||||
make_bottom_panel_item_visible(imports_list_panel)
|
||||
|
||||
|
||||
func _create_imports_manager_window(panel: MarginContainer):
|
||||
imports_list_window = AsepriteDockImportsWindow.instantiate()
|
||||
imports_list_window.add_child(panel)
|
||||
get_editor_interface().get_base_control().add_child(imports_list_window)
|
||||
imports_list_window.popup_centered_ratio(0.5)
|
21
addons/godot-git-plugin/LICENSE
Normal file
21
addons/godot-git-plugin/LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2016-2023 The Godot Engine community
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
1349
addons/godot-git-plugin/THIRDPARTY.md
Normal file
1349
addons/godot-git-plugin/THIRDPARTY.md
Normal file
File diff suppressed because it is too large
Load diff
12
addons/godot-git-plugin/git_plugin.gdextension
Normal file
12
addons/godot-git-plugin/git_plugin.gdextension
Normal file
|
@ -0,0 +1,12 @@
|
|||
[configuration]
|
||||
|
||||
entry_symbol = "git_plugin_init"
|
||||
compatibility_minimum = "4.1.0"
|
||||
|
||||
[libraries]
|
||||
|
||||
macos.editor = "macos/libgit_plugin.macos.editor.universal.dylib"
|
||||
windows.editor.x86_64 = "win64/libgit_plugin.windows.editor.x86_64.dll"
|
||||
linux.editor.x86_64 = "linux/libgit_plugin.linux.editor.x86_64.so"
|
||||
linux.editor.arm64 = "linux/libgit_plugin.linux.editor.arm64.so"
|
||||
linux.editor.rv64 = ""
|
Binary file not shown.
Binary file not shown.
7
addons/godot-git-plugin/plugin.cfg
Normal file
7
addons/godot-git-plugin/plugin.cfg
Normal file
|
@ -0,0 +1,7 @@
|
|||
[plugin]
|
||||
|
||||
name="Godot Git Plugin"
|
||||
description="This plugin lets you interact with Git without leaving the Godot editor. More information can be found at https://github.com/godotengine/godot-git-plugin/wiki"
|
||||
author="twaritwaikar"
|
||||
version="v3.1.1"
|
||||
script="godot-git-plugin.gd"
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
assets/PlayerCharacter.aseprite
Normal file
BIN
assets/PlayerCharacter.aseprite
Normal file
Binary file not shown.
14
assets/PlayerCharacter.aseprite.import
Normal file
14
assets/PlayerCharacter.aseprite.import
Normal file
|
@ -0,0 +1,14 @@
|
|||
[remap]
|
||||
|
||||
importer="aseprite_wizard.plugin.noop"
|
||||
type="PackedDataContainer"
|
||||
uid="uid://bye3fycptipre"
|
||||
path="res://.godot/imported/PlayerCharacter.aseprite-1bf420fc00cecceb6a9caea06a8e4ec7.res"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/PlayerCharacter.aseprite"
|
||||
dest_files=["res://.godot/imported/PlayerCharacter.aseprite-1bf420fc00cecceb6a9caea06a8e4ec7.res"]
|
||||
|
||||
[params]
|
||||
|
1
icon.svg
Normal file
1
icon.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128"><rect width="124" height="124" x="2" y="2" fill="#363d52" stroke="#212532" stroke-width="4" rx="14"/><g fill="#fff" transform="translate(12.322 12.322)scale(.101)"><path d="M105 673v33q407 354 814 0v-33z"/><path fill="#478cbf" d="m105 673 152 14q12 1 15 14l4 67 132 10 8-61q2-11 15-15h162q13 4 15 15l8 61 132-10 4-67q3-13 15-14l152-14V427q30-39 56-81-35-59-83-108-43 20-82 47-40-37-88-64 7-51 8-102-59-28-123-42-26 43-46 89-49-7-98 0-20-46-46-89-64 14-123 42 1 51 8 102-48 27-88 64-39-27-82-47-48 49-83 108 26 42 56 81zm0 33v39c0 276 813 276 814 0v-39l-134 12-5 69q-2 10-14 13l-162 11q-12 0-16-11l-10-65H446l-10 65q-4 11-16 11l-162-11q-12-3-14-13l-5-69z"/><path d="M483 600c0 34 58 34 58 0v-86c0-34-58-34-58 0z"/><circle cx="725" cy="526" r="90"/><circle cx="299" cy="526" r="90"/></g><g fill="#414042" transform="translate(12.322 12.322)scale(.101)"><circle cx="307" cy="532" r="60"/><circle cx="717" cy="532" r="60"/></g></svg>
|
After Width: | Height: | Size: 994 B |
37
icon.svg.import
Normal file
37
icon.svg.import
Normal file
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dp6yumdudwpwh"
|
||||
path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://icon.svg"
|
||||
dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue