Add addons
|
|
@ -0,0 +1,208 @@
|
|||
@tool
|
||||
extends PanelContainer
|
||||
|
||||
const wizard_config = preload("../../config/wizard_config.gd")
|
||||
const result_code = preload("../../config/result_codes.gd")
|
||||
var sprite_frames_creator = preload("../sprite_frames_creator.gd").new()
|
||||
|
||||
var scene: Node
|
||||
var sprite: Node
|
||||
|
||||
var config
|
||||
var file_system: EditorFileSystem
|
||||
|
||||
var _layer: String = ""
|
||||
var _source: String = ""
|
||||
var _file_dialog_aseprite: FileDialog
|
||||
var _output_folder_dialog: FileDialog
|
||||
var _importing := false
|
||||
|
||||
var _output_folder := ""
|
||||
var _out_folder_default := "[Same as scene]"
|
||||
var _layer_default := "[all]"
|
||||
|
||||
@onready var _source_field = $margin/VBoxContainer/source/button
|
||||
@onready var _layer_field = $margin/VBoxContainer/layer/options
|
||||
@onready var _options_title = $margin/VBoxContainer/options_title/options_title
|
||||
@onready var _options_container = $margin/VBoxContainer/options
|
||||
@onready var _out_folder_field = $margin/VBoxContainer/options/out_folder/button
|
||||
@onready var _out_filename_field = $margin/VBoxContainer/options/out_filename/LineEdit
|
||||
@onready var _visible_layers_field = $margin/VBoxContainer/options/visible_layers/CheckButton
|
||||
@onready var _ex_pattern_field = $margin/VBoxContainer/options/ex_pattern/LineEdit
|
||||
|
||||
func _ready():
|
||||
var cfg = wizard_config.load_config(sprite)
|
||||
|
||||
if cfg == null:
|
||||
_load_default_config()
|
||||
else:
|
||||
_load_config(cfg)
|
||||
|
||||
sprite_frames_creator.init(config, file_system)
|
||||
|
||||
|
||||
func _load_config(cfg):
|
||||
if cfg.has("source"):
|
||||
_set_source(cfg.source)
|
||||
|
||||
if cfg.get("layer", "") != "":
|
||||
_layer_field.clear()
|
||||
_set_layer(cfg.layer)
|
||||
|
||||
_output_folder = cfg.get("o_folder", "")
|
||||
_out_folder_field.text = _output_folder if _output_folder != "" else _out_folder_default
|
||||
_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", "")
|
||||
|
||||
_set_options_visible(cfg.get("op_exp", false))
|
||||
|
||||
|
||||
func _load_default_config():
|
||||
_ex_pattern_field.text = config.get_default_exclusion_pattern()
|
||||
_set_options_visible(false)
|
||||
|
||||
|
||||
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 _on_layer_button_down():
|
||||
if _source == "":
|
||||
_show_message("Please. Select source file first.")
|
||||
return
|
||||
|
||||
var layers = sprite_frames_creator.list_layers(ProjectSettings.globalize_path(_source))
|
||||
var current = 0
|
||||
_layer_field.clear()
|
||||
_layer_field.add_item(_layer_default)
|
||||
|
||||
for l in layers:
|
||||
if l == "":
|
||||
continue
|
||||
|
||||
_layer_field.add_item(l)
|
||||
if l == _layer:
|
||||
current = _layer_field.get_item_count() - 1
|
||||
_layer_field.select(current)
|
||||
|
||||
|
||||
func _on_layer_item_selected(index):
|
||||
if index == 0:
|
||||
_layer = ""
|
||||
return
|
||||
_layer = _layer_field.get_item_text(index)
|
||||
_save_config()
|
||||
|
||||
|
||||
func _on_source_pressed():
|
||||
_open_source_dialog()
|
||||
|
||||
|
||||
func _on_import_pressed():
|
||||
if _importing:
|
||||
return
|
||||
_importing = true
|
||||
|
||||
var root = get_tree().get_edited_scene_root()
|
||||
|
||||
if _source == "":
|
||||
_show_message("Aseprite file not selected.")
|
||||
_importing = false
|
||||
return
|
||||
|
||||
var options = {
|
||||
"source": ProjectSettings.globalize_path(_source),
|
||||
"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()
|
||||
|
||||
await sprite_frames_creator.create_animations(sprite, options)
|
||||
_importing = false
|
||||
|
||||
|
||||
func _save_config():
|
||||
wizard_config.save_config(sprite, config.is_use_metadata_enabled(), {
|
||||
"source": _source,
|
||||
"layer": _layer,
|
||||
"op_exp": _options_title.button_pressed,
|
||||
"o_folder": _output_folder,
|
||||
"o_name": _out_filename_field.text,
|
||||
"only_visible": _visible_layers_field.button_pressed,
|
||||
"o_ex_p": _ex_pattern_field.text
|
||||
})
|
||||
|
||||
|
||||
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 = _source.get_base_dir()
|
||||
_file_dialog_aseprite.popup_centered_ratio()
|
||||
|
||||
|
||||
func _create_aseprite_file_selection():
|
||||
var file_dialog = FileDialog.new()
|
||||
file_dialog.file_mode = FileDialog.FILE_MODE_OPEN_FILE
|
||||
file_dialog.access = FileDialog.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 _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 _on_options_title_toggled(button_pressed):
|
||||
_set_options_visible(button_pressed)
|
||||
_save_config()
|
||||
|
||||
|
||||
func _set_options_visible(is_visible):
|
||||
_options_container.visible = is_visible
|
||||
_options_title.icon = config.get_icon("expanded") if is_visible else config.get_icon("collapsed")
|
||||
|
||||
|
||||
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 = FileDialog.new()
|
||||
file_dialog.file_mode = FileDialog.FILE_MODE_OPEN_DIR
|
||||
file_dialog.access = FileDialog.ACCESS_RESOURCES
|
||||
file_dialog.connect("dir_selected",Callable(self,"_on_output_folder_selected"))
|
||||
return file_dialog
|
||||
|
||||
|
||||
func _on_output_folder_selected(path):
|
||||
_output_folder = path
|
||||
_out_folder_field.text = _output_folder if _output_folder != "" else _out_folder_default
|
||||
_output_folder_dialog.queue_free()
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
[gd_scene load_steps=2 format=3 uid="uid://vej7yqkbtd5f"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/AsepriteWizard/animated_sprite/docks/animated_sprite_inspector_dock.gd" id="1"]
|
||||
|
||||
[node name="animated_sprite_inspector_dock" type="PanelContainer"]
|
||||
offset_right = 14.0
|
||||
offset_bottom = 14.0
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="margin" type="MarginContainer" parent="."]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="margin"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="section_title" type="PanelContainer" parent="margin/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="title" type="Label" parent="margin/VBoxContainer/section_title"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Aseprite"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[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" type="Button" parent="margin/VBoxContainer/source"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 2.0
|
||||
text = "[empty]"
|
||||
clip_text = true
|
||||
|
||||
[node name="layer" type="HBoxContainer" parent="margin/VBoxContainer"]
|
||||
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/layer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 2.0
|
||||
text = "Layer"
|
||||
|
||||
[node name="options" type="OptionButton" parent="margin/VBoxContainer/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="options_title" type="PanelContainer" parent="margin/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="options_title" type="Button" parent="margin/VBoxContainer/options_title"]
|
||||
layout_mode = 2
|
||||
toggle_mode = true
|
||||
text = "Options"
|
||||
|
||||
[node name="options" type="VBoxContainer" parent="margin/VBoxContainer"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
|
||||
[node name="out_folder" type="HBoxContainer" parent="margin/VBoxContainer/options"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Location where the spritesheet file should be saved."
|
||||
|
||||
[node name="Label" type="Label" parent="margin/VBoxContainer/options/out_folder"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 2.0
|
||||
text = "Output folder"
|
||||
|
||||
[node name="button" type="Button" parent="margin/VBoxContainer/options/out_folder"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 2.0
|
||||
text = "[empty]"
|
||||
clip_text = true
|
||||
|
||||
[node name="out_filename" type="HBoxContainer" parent="margin/VBoxContainer/options"]
|
||||
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/options/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/options/out_filename"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 2.0
|
||||
|
||||
[node name="ex_pattern" type="HBoxContainer" parent="margin/VBoxContainer/options"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Exclude layers with name matching this pattern (regex)."
|
||||
|
||||
[node name="Label" type="Label" parent="margin/VBoxContainer/options/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/options/ex_pattern"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 2.0
|
||||
|
||||
[node name="visible_layers" type="HBoxContainer" parent="margin/VBoxContainer/options"]
|
||||
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/options/visible_layers"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 2.0
|
||||
text = "Only visible layers"
|
||||
|
||||
[node name="CheckButton" type="CheckButton" parent="margin/VBoxContainer/options/visible_layers"]
|
||||
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="pressed" from="margin/VBoxContainer/source/button" to="." method="_on_source_pressed"]
|
||||
[connection signal="button_down" from="margin/VBoxContainer/layer/options" to="." method="_on_layer_button_down"]
|
||||
[connection signal="item_selected" from="margin/VBoxContainer/layer/options" to="." method="_on_layer_item_selected"]
|
||||
[connection signal="toggled" from="margin/VBoxContainer/options_title/options_title" to="." method="_on_options_title_toggled"]
|
||||
[connection signal="pressed" from="margin/VBoxContainer/options/out_folder/button" to="." method="_on_out_folder_pressed"]
|
||||
[connection signal="pressed" from="margin/VBoxContainer/import" to="." method="_on_import_pressed"]
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
@tool
|
||||
extends TabContainer
|
||||
|
||||
signal close_requested
|
||||
|
||||
const WizardWindow = preload("./as_wizard_window.tscn")
|
||||
|
||||
var _config
|
||||
var _file_system: EditorFileSystem
|
||||
|
||||
func _ready():
|
||||
$Import.init(_config, _file_system)
|
||||
$Import.connect("close_requested",Callable(self,"emit_signal").bind("close_requested"))
|
||||
$Import.connect("import_success",Callable($History,"add_entry"))
|
||||
$History.init(_config)
|
||||
$History.connect("request_edit",Callable(self,"_on_edit_request"))
|
||||
$History.connect("request_import",Callable(self,"_on_import_request"))
|
||||
|
||||
|
||||
func init(config, editor_file_system: EditorFileSystem):
|
||||
_config = config
|
||||
_file_system = editor_file_system
|
||||
|
||||
|
||||
func _on_AsWizardDockContainer_tab_changed(tab: int):
|
||||
if tab == 1:
|
||||
$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()
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
[gd_scene load_steps=4 format=2]
|
||||
|
||||
[ext_resource path="res://addons/AsepriteWizard/animated_sprite/docks/as_wizard_window.tscn" type="PackedScene" id=1]
|
||||
[ext_resource path="res://addons/AsepriteWizard/animated_sprite/docks/as_wizard_dock_container.gd" type="Script" id=2]
|
||||
[ext_resource path="res://addons/AsepriteWizard/animated_sprite/docks/sprite_frames_import_history.tscn" type="PackedScene" id=3]
|
||||
|
||||
[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 )]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = 4.0
|
||||
offset_top = 32.0
|
||||
offset_right = -4.0
|
||||
offset_bottom = -4.0
|
||||
tooltip_text = "SpriteFrames Importer"
|
||||
|
||||
[node name="History" parent="." instance=ExtResource( 3 )]
|
||||
visible = false
|
||||
|
||||
[connection signal="tab_changed" from="." to="." method="_on_AsWizardDockContainer_tab_changed"]
|
||||
|
|
@ -0,0 +1,196 @@
|
|||
@tool
|
||||
extends PanelContainer
|
||||
|
||||
signal close_requested
|
||||
signal import_success(file_settings)
|
||||
|
||||
var result_code = preload("../../config/result_codes.gd")
|
||||
var _sf_creator = preload("../sprite_frames_creator.gd").new()
|
||||
|
||||
var _config
|
||||
var _file_system: EditorFileSystem
|
||||
|
||||
var _file_dialog_aseprite: FileDialog
|
||||
var _output_folder_dialog: FileDialog
|
||||
var _warning_dialog: AcceptDialog
|
||||
|
||||
func _exit_tree():
|
||||
_file_dialog_aseprite.queue_free()
|
||||
_output_folder_dialog.queue_free()
|
||||
_warning_dialog.queue_free()
|
||||
|
||||
|
||||
func init(config, editor_file_system: EditorFileSystem):
|
||||
_config = config
|
||||
_file_system = editor_file_system
|
||||
_file_dialog_aseprite = _create_aseprite_file_selection()
|
||||
_output_folder_dialog = _create_outuput_folder_selection()
|
||||
_warning_dialog = AcceptDialog.new()
|
||||
_warning_dialog.exclusive = false
|
||||
|
||||
_sf_creator.init(_config, _file_system)
|
||||
|
||||
get_parent().get_parent().add_child(_file_dialog_aseprite)
|
||||
get_parent().get_parent().add_child(_output_folder_dialog)
|
||||
get_parent().get_parent().add_child(_warning_dialog)
|
||||
|
||||
_load_persisted_config()
|
||||
|
||||
|
||||
func _load_persisted_config():
|
||||
_split_mode_field().button_pressed = _config.should_split_layers()
|
||||
_only_visible_layers_field().button_pressed = _config.should_include_only_visible_layers()
|
||||
_exception_pattern_field().text = _config.get_exception_pattern()
|
||||
_custom_name_field().text = _config.get_last_custom_name()
|
||||
_file_location_field().text = _config.get_last_source_path()
|
||||
_do_not_create_res_field().button_pressed = _config.should_not_create_resource()
|
||||
|
||||
var output_folder = _config.get_last_output_path()
|
||||
_output_folder_field().text = output_folder if output_folder != "" else "res://"
|
||||
|
||||
|
||||
func load_import_config(import_config: Dictionary):
|
||||
_split_mode_field().button_pressed = import_config.options.export_mode == _sf_creator.LAYERS_EXPORT_MODE
|
||||
_only_visible_layers_field().button_pressed = import_config.options.only_visible_layers
|
||||
_exception_pattern_field().text = import_config.options.exception_pattern
|
||||
_custom_name_field().text = import_config.options.output_filename
|
||||
_file_location_field().text = import_config.source_file
|
||||
_do_not_create_res_field().button_pressed = import_config.options.do_not_create_resource
|
||||
_output_folder_field().text = import_config.output_location if import_config.output_location != "" else "res://"
|
||||
|
||||
|
||||
func _open_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():
|
||||
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 = FileDialog.new()
|
||||
file_dialog.file_mode = FileDialog.FILE_MODE_OPEN_FILE
|
||||
file_dialog.access = FileDialog.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 = FileDialog.new()
|
||||
file_dialog.file_mode = FileDialog.FILE_MODE_OPEN_DIR
|
||||
file_dialog.access = FileDialog.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
|
||||
_config.set_last_source_path(localized_path)
|
||||
|
||||
|
||||
func _on_output_folder_selected(path):
|
||||
_output_folder_field().text = path
|
||||
_config.set_last_output_path(path)
|
||||
|
||||
|
||||
func _on_next_btn_up():
|
||||
var aseprite_file = _file_location_field().text
|
||||
var output_location = _output_folder_field().text
|
||||
var split_layers = _split_mode_field().button_pressed
|
||||
|
||||
var export_mode = _sf_creator.LAYERS_EXPORT_MODE if split_layers else _sf_creator.FILE_EXPORT_MODE
|
||||
var options = {
|
||||
"export_mode": export_mode,
|
||||
"exception_pattern": _exception_pattern_field().text,
|
||||
"only_visible_layers": _only_visible_layers_field().button_pressed,
|
||||
"output_filename": _custom_name_field().text,
|
||||
"do_not_create_resource": _do_not_create_res_field().button_pressed,
|
||||
"remove_source_files_allowed": true,
|
||||
"output_folder": output_location,
|
||||
}
|
||||
var exit_code = await _sf_creator.create_and_save_resources(
|
||||
ProjectSettings.globalize_path(aseprite_file),
|
||||
options
|
||||
)
|
||||
|
||||
if exit_code != OK:
|
||||
_show_error(exit_code)
|
||||
return
|
||||
emit_signal("import_success", {
|
||||
"source_file": aseprite_file,
|
||||
"output_location": output_location,
|
||||
"options": options,
|
||||
})
|
||||
_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 _save_config():
|
||||
_config.set_split_layers(_split_mode_field().button_pressed)
|
||||
_config.set_exception_pattern(_exception_pattern_field().text)
|
||||
_config.set_custom_name(_custom_name_field().text)
|
||||
_config.set_include_only_visible_layers(_only_visible_layers_field().button_pressed)
|
||||
_config.set_do_not_create_resource(_do_not_create_res_field().button_pressed)
|
||||
|
||||
|
||||
func _show_error(code: int):
|
||||
_show_error_message(result_code.get_error_message(code))
|
||||
|
||||
|
||||
func _show_error_message(message: String):
|
||||
_warning_dialog.dialog_text = "Error: %s" % message
|
||||
_warning_dialog.popup_centered()
|
||||
|
||||
|
||||
func _show_import_success_message():
|
||||
_warning_dialog.dialog_text = "Aseprite import succeeded"
|
||||
_warning_dialog.popup_centered()
|
||||
_save_config()
|
||||
|
||||
|
||||
func _file_location_field() -> LineEdit:
|
||||
return $container/options/file_location/HBoxContainer/file_location_path as LineEdit
|
||||
|
||||
|
||||
func _output_folder_field() -> LineEdit:
|
||||
return $container/options/output_folder/HBoxContainer/file_location_path as LineEdit
|
||||
|
||||
|
||||
func _exception_pattern_field() -> LineEdit:
|
||||
return $container/options/exclude_pattern/pattern as LineEdit
|
||||
|
||||
|
||||
func _split_mode_field() -> CheckBox:
|
||||
return $container/options/layer_importing_mode/split_layers/field as CheckBox
|
||||
|
||||
|
||||
func _only_visible_layers_field() -> CheckBox:
|
||||
return $container/options/layer_importing_mode/visible_layers/field as CheckBox
|
||||
|
||||
|
||||
func _custom_name_field() -> LineEdit:
|
||||
return $container/options/custom_filename/pattern as LineEdit
|
||||
|
||||
|
||||
func _do_not_create_res_field() -> CheckBox:
|
||||
return $container/options/layer_importing_mode/disable_resource_creation/field as CheckBox
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
[gd_scene load_steps=2 format=3 uid="uid://c5dwobjd34w3p"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/AsepriteWizard/animated_sprite/docks/as_wizard_window.gd" id="1"]
|
||||
|
||||
[node name="ASWizardWindow" type="PanelContainer"]
|
||||
offset_right = 600.0
|
||||
offset_bottom = 600.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 0
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="container" type="MarginContainer" parent="."]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="options" type="VBoxContainer" parent="container"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="file_location" type="VBoxContainer" parent="container/options"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="file_location_label" type="Label" parent="container/options/file_location"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Location of the Aseprite *.ase source file"
|
||||
mouse_filter = 1
|
||||
text = "Aseprite File Location:*"
|
||||
clip_text = true
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="container/options/file_location"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="file_location_path" type="LineEdit" parent="container/options/file_location/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
caret_blink = true
|
||||
|
||||
[node name="file_location_btn" type="Button" parent="container/options/file_location/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
text = "Select"
|
||||
|
||||
[node name="output_folder" type="VBoxContainer" parent="container/options"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="label" type="Label" parent="container/options/output_folder"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Folder where the SpriteSheet resource is going to be saved"
|
||||
mouse_filter = 1
|
||||
text = "Output folder:*"
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="container/options/output_folder"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="file_location_path" type="LineEdit" parent="container/options/output_folder/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "res://"
|
||||
caret_blink = true
|
||||
|
||||
[node name="output_folder_btn" type="Button" parent="container/options/output_folder/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
text = "Select"
|
||||
|
||||
[node name="exclude_pattern" type="VBoxContainer" parent="container/options"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="label" type="Label" parent="container/options/exclude_pattern"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "If layer name matches this pattern, it won't be exported."
|
||||
mouse_filter = 1
|
||||
text = "Exclude layers with name matching pattern:"
|
||||
|
||||
[node name="pattern" type="LineEdit" parent="container/options/exclude_pattern"]
|
||||
layout_mode = 2
|
||||
caret_blink = true
|
||||
|
||||
[node name="custom_filename" type="VBoxContainer" parent="container/options"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="label" type="Label" parent="container/options/custom_filename"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Output filename. In case layers are not being merged, this is used as file prefix.
|
||||
If not set, source filename is used."
|
||||
mouse_filter = 1
|
||||
text = "Output file name / prefix:"
|
||||
|
||||
[node name="pattern" type="LineEdit" parent="container/options/custom_filename"]
|
||||
layout_mode = 2
|
||||
caret_blink = true
|
||||
|
||||
[node name="layer_importing_mode" type="VBoxContainer" parent="container/options"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="label" type="Label" parent="container/options/layer_importing_mode"]
|
||||
layout_mode = 2
|
||||
mouse_filter = 1
|
||||
text = "Options:"
|
||||
|
||||
[node name="split_layers" type="HBoxContainer" parent="container/options/layer_importing_mode"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="field" type="CheckBox" parent="container/options/layer_importing_mode/split_layers"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "If selected, one resource will be created for each layer.
|
||||
If not selected, layers will be merged and exported as one SpriteSheet."
|
||||
text = "Split layers in multiple resources"
|
||||
|
||||
[node name="visible_layers" type="HBoxContainer" parent="container/options/layer_importing_mode"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="field" type="CheckBox" parent="container/options/layer_importing_mode/visible_layers"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "If selected, layers not visible in the source file won't be included in the final image."
|
||||
text = "Only include visible layers"
|
||||
|
||||
[node name="disable_resource_creation" type="HBoxContainer" parent="container/options/layer_importing_mode"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="field" type="CheckBox" parent="container/options/layer_importing_mode/disable_resource_creation"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Does not create SpriteFrames resource. Useful if you are only interested in the .json and .png output from Aseprite."
|
||||
text = "Do not create resource file"
|
||||
|
||||
[node name="buttons" type="HBoxContainer" parent="container/options"]
|
||||
layout_mode = 2
|
||||
alignment = 2
|
||||
|
||||
[node name="close" type="Button" parent="container/options/buttons"]
|
||||
layout_mode = 2
|
||||
text = "Close"
|
||||
|
||||
[node name="next" type="Button" parent="container/options/buttons"]
|
||||
layout_mode = 2
|
||||
text = "Import"
|
||||
|
||||
[connection signal="button_up" from="container/options/file_location/HBoxContainer/file_location_btn" to="." method="_open_aseprite_file_selection_dialog"]
|
||||
[connection signal="button_up" from="container/options/output_folder/HBoxContainer/output_folder_btn" to="." method="_open_output_folder_selection_dialog"]
|
||||
[connection signal="button_up" from="container/options/buttons/close" to="." method="_on_close_btn_up"]
|
||||
[connection signal="button_up" from="container/options/buttons/next" to="." method="_on_next_btn_up"]
|
||||
|
|
@ -0,0 +1,226 @@
|
|||
@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 SORT_BY_DATE := 0
|
||||
const SORT_BY_PATH := 1
|
||||
const INITIAL_GRID_INDEX := 3
|
||||
|
||||
var _config
|
||||
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 init(config):
|
||||
_config = config
|
||||
|
||||
|
||||
func reload():
|
||||
if _history:
|
||||
return
|
||||
|
||||
_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 source_path = SourcePathField.instantiate()
|
||||
source_path.set_entry(entry)
|
||||
|
||||
grid.get_child(INITIAL_GRID_INDEX).add_sibling(source_path)
|
||||
|
||||
var output_path = OutputPathField.instantiate()
|
||||
output_path.text = entry.output_location
|
||||
source_path.add_sibling(output_path)
|
||||
var import_date = ImportDateField.instantiate()
|
||||
import_date.set_date(entry.import_date)
|
||||
output_path.add_sibling(import_date)
|
||||
var actions = ItemActions.instantiate()
|
||||
actions.history_index = index
|
||||
import_date.add_sibling(actions)
|
||||
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"))
|
||||
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,
|
||||
}
|
||||
|
||||
|
||||
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 _config.is_single_file_history() and _history_nodes.has(file_settings.source_file):
|
||||
_remove_entries(file_settings.source_file)
|
||||
|
||||
_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()
|
||||
|
||||
|
||||
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.source_path_node, INITIAL_GRID_INDEX + 1)
|
||||
grid.move_child(entry.output_path_node, INITIAL_GRID_INDEX + 2)
|
||||
grid.move_child(entry.import_date_node, INITIAL_GRID_INDEX + 3)
|
||||
grid.move_child(entry.actions_node, INITIAL_GRID_INDEX + 4)
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
[gd_scene load_steps=2 format=3 uid="uid://cyoin5ncul0fm"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/AsepriteWizard/animated_sprite/docks/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 = "Name"
|
||||
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 = 4
|
||||
|
||||
[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="date_label" type="Label" parent="MarginContainer/VBoxContainer/ScrollContainer/GridContainer"]
|
||||
layout_mode = 2
|
||||
text = "Date"
|
||||
|
||||
[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,12 @@
|
|||
@tool
|
||||
extends Label
|
||||
|
||||
|
||||
func set_date(timestamp: String):
|
||||
self.text = timestamp
|
||||
# self.text = _format_date(timestamp)
|
||||
|
||||
|
||||
#func _format_date(timestamp: int) -> String:
|
||||
# var dt = Time.get_datetime_dict_from_system_from_unix_time(timestamp)
|
||||
# return "%04d-%02d-%02d %02d:%02d:%02d" % [dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second]
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
[gd_scene load_steps=2 format=2]
|
||||
|
||||
[ext_resource path="res://addons/AsepriteWizard/animated_sprite/docks/wizard_nodes/import_date.gd" type="Script" id=1]
|
||||
|
||||
[node name="import_date" type="Label"]
|
||||
offset_left = 660.0
|
||||
offset_top = 39.0
|
||||
offset_right = 774.0
|
||||
offset_bottom = 53.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=2]
|
||||
|
||||
[ext_resource path="res://addons/AsepriteWizard/animated_sprite/docks/wizard_nodes/list_actions.gd" type="Script" 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,22 @@
|
|||
@tool
|
||||
extends LineEdit
|
||||
|
||||
|
||||
func set_entry(entry: Dictionary):
|
||||
self.text = entry.source_file
|
||||
self.tooltip_text = _format_hint(entry)
|
||||
|
||||
|
||||
func _format_hint(entry: Dictionary) -> String:
|
||||
return """Output filename/prefix: %s
|
||||
Ex. pattern: %s
|
||||
Split: %s
|
||||
Only visible: %s
|
||||
No Resource: %s
|
||||
""" % [
|
||||
entry.options.output_filename,
|
||||
entry.options.exception_pattern,
|
||||
"yes" if entry.options.export_mode else "no",
|
||||
"yes" if entry.options.only_visible_layers else "no",
|
||||
"yes" if entry.options.do_not_create_resource else "no",
|
||||
]
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
[gd_scene load_steps=2 format=2]
|
||||
|
||||
[ext_resource path="res://addons/AsepriteWizard/animated_sprite/docks/wizard_nodes/source_path.gd" type="Script" 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 )
|
||||
129
Sprout/addons/AsepriteWizard/animated_sprite/import_plugin.gd
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
@tool
|
||||
extends EditorImportPlugin
|
||||
|
||||
const result_codes = preload("../config/result_codes.gd")
|
||||
|
||||
var config
|
||||
var _sf_creator = preload("sprite_frames_creator.gd").new()
|
||||
var file_system: EditorFileSystem
|
||||
|
||||
func _get_importer_name():
|
||||
return "aseprite_wizard.plugin"
|
||||
|
||||
|
||||
func _get_visible_name():
|
||||
return "Aseprite SpriteFrames Importer"
|
||||
|
||||
|
||||
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 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": ''},
|
||||
{"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 replace_vars(pattern : String, vars : Dictionary):
|
||||
var result = pattern;
|
||||
for k in vars:
|
||||
var v = vars[k]
|
||||
result = result.replace("{%s}" % k, v)
|
||||
return result
|
||||
|
||||
|
||||
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.substr(0, source_file.rfind('/'))
|
||||
var source_basename = source_file.substr(source_path.length()+1, -1)
|
||||
source_basename = source_basename.substr(0, source_basename.rfind('.'))
|
||||
|
||||
_sf_creator.init(config, file_system)
|
||||
|
||||
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 resources = await _sf_creator.create_resources(absolute_source_file, aseprite_opts)
|
||||
|
||||
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 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,18 @@
|
|||
@tool
|
||||
extends EditorInspectorPlugin
|
||||
|
||||
const ASInspectorDock = preload("./docks/animated_sprite_inspector_dock.tscn")
|
||||
|
||||
var config
|
||||
var file_system: EditorFileSystem
|
||||
|
||||
func _can_handle(object):
|
||||
return object is AnimatedSprite2D || object is AnimatedSprite3D
|
||||
|
||||
|
||||
func _parse_end(object):
|
||||
var dock = ASInspectorDock.instantiate()
|
||||
dock.sprite = object
|
||||
dock.config = config
|
||||
dock.file_system = file_system
|
||||
add_custom_control(dock)
|
||||
|
|
@ -0,0 +1,352 @@
|
|||
@tool
|
||||
extends RefCounted
|
||||
|
||||
var result_code = preload("../config/result_codes.gd")
|
||||
var _aseprite = preload("../aseprite/aseprite.gd").new()
|
||||
|
||||
enum {
|
||||
FILE_EXPORT_MODE,
|
||||
LAYERS_EXPORT_MODE
|
||||
}
|
||||
|
||||
var _config
|
||||
var _file_system: EditorFileSystem
|
||||
var _should_check_file_system := false
|
||||
|
||||
|
||||
func init(config, editor_file_system: EditorFileSystem = null):
|
||||
_config = config
|
||||
_file_system = editor_file_system
|
||||
_should_check_file_system = _file_system != null
|
||||
_aseprite.init(config)
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
func create_animations(sprite: Node, options: Dictionary) -> void:
|
||||
var input_check = _initial_checks(options.source, options)
|
||||
|
||||
if input_check != result_code.SUCCESS:
|
||||
printerr(result_code.get_error_message(input_check))
|
||||
return
|
||||
|
||||
var result = await _create_animations_from_file(sprite, options)
|
||||
|
||||
if result != result_code.SUCCESS:
|
||||
printerr(result_code.get_error_message(result))
|
||||
|
||||
|
||||
func _create_animations_from_file(animated_sprite: Node, options: Dictionary) -> int:
|
||||
var output = _export_aseprite_file(options)
|
||||
|
||||
if not output.is_ok:
|
||||
return output.code
|
||||
|
||||
if _config.is_import_preset_enabled():
|
||||
_config.create_import_file(output.content)
|
||||
|
||||
await _scan_filesystem()
|
||||
|
||||
var sprite_frames_result = _create_sprite_frames(output.content)
|
||||
if not sprite_frames_result.is_ok:
|
||||
return sprite_frames_result.code
|
||||
|
||||
animated_sprite.frames = sprite_frames_result.content
|
||||
animated_sprite.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
|
||||
|
||||
if _config.should_remove_source_files():
|
||||
DirAccess.remove_absolute(output.content.data_file)
|
||||
|
||||
return result_code.SUCCESS
|
||||
|
||||
|
||||
func _export_aseprite_file(options: Dictionary) -> Dictionary:
|
||||
var output
|
||||
|
||||
if options.get("layer", "") == "":
|
||||
output = _aseprite.export_file(options.source, options.output_folder, options)
|
||||
else:
|
||||
output = _aseprite.export_layer(options.source, 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)
|
||||
|
||||
|
||||
func create_and_save_resources(source_file: String, options: Dictionary) -> int:
|
||||
var resources = await create_resources(source_file, options)
|
||||
if resources.is_ok:
|
||||
return _save_resources(resources.content)
|
||||
|
||||
return resources.code
|
||||
|
||||
|
||||
func create_resources(source_file: String, options = {}) -> Dictionary:
|
||||
var input_check = _initial_checks(source_file, options)
|
||||
|
||||
if input_check != result_code.SUCCESS:
|
||||
return result_code.error(input_check)
|
||||
|
||||
var output = _create_aseprite_output_files(source_file, options)
|
||||
|
||||
if not output.is_ok:
|
||||
return output
|
||||
|
||||
await _scan_filesystem()
|
||||
|
||||
var result = await _create_sprite_frames_from_source(output.content, options)
|
||||
|
||||
if not result.is_ok:
|
||||
return result
|
||||
|
||||
var should_remove_source = options.get("remove_source_files_allowed", false) and _config.should_remove_source_files()
|
||||
|
||||
if should_remove_source:
|
||||
_remove_source_files(result.content)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
func _remove_source_files(source_files: Array):
|
||||
for s in source_files:
|
||||
DirAccess.remove_absolute(s.data_file)
|
||||
|
||||
await _scan_filesystem()
|
||||
|
||||
|
||||
func _create_aseprite_output_files(source_file: String, options: Dictionary):
|
||||
match options.get('export_mode', FILE_EXPORT_MODE):
|
||||
FILE_EXPORT_MODE:
|
||||
return result_code.result(
|
||||
[_aseprite.export_file(source_file, options.output_folder, options)]
|
||||
)
|
||||
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)
|
||||
|
||||
|
||||
func _create_sprite_frames_from_source(source_files: Array, options: Dictionary) -> Dictionary:
|
||||
var should_remove_source = options.get("remove_source_files_allowed", false) and _config.should_remove_source_files()
|
||||
|
||||
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)
|
||||
|
||||
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:
|
||||
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
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
func _load_aseprite_resources(aseprite_data: Dictionary):
|
||||
var content_result = _load_json_content(aseprite_data.data_file)
|
||||
|
||||
if not content_result.is_ok:
|
||||
return content_result
|
||||
|
||||
var texture = _parse_texture_path(aseprite_data.sprite_sheet)
|
||||
|
||||
return result_code.result({
|
||||
"metadata": content_result.content,
|
||||
"texture": texture
|
||||
})
|
||||
|
||||
|
||||
func _load_json_content(source_file: String) -> Dictionary:
|
||||
var file = FileAccess.open(source_file, FileAccess.READ)
|
||||
if file == null:
|
||||
return result_code.error(file.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)
|
||||
|
||||
|
||||
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, texture) -> SpriteFrames:
|
||||
var frame_cache = {}
|
||||
var frames = _aseprite.get_content_frames(content)
|
||||
var sprite_frames := SpriteFrames.new()
|
||||
sprite_frames.remove_animation("default")
|
||||
|
||||
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, tag.direction, frame_cache)
|
||||
else:
|
||||
_add_animation_frames(sprite_frames, "default", frames, texture)
|
||||
|
||||
return sprite_frames
|
||||
|
||||
|
||||
func _add_animation_frames(
|
||||
sprite_frames: SpriteFrames,
|
||||
anim_name: String,
|
||||
frames : Array,
|
||||
texture,
|
||||
direction = 'forward',
|
||||
frame_cache = {}
|
||||
):
|
||||
var animation_name := anim_name
|
||||
var is_loopable = _is_loop_config_enabled()
|
||||
|
||||
var loop_prefix := _loop_config_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':
|
||||
frames.reverse()
|
||||
|
||||
for frame in frames:
|
||||
_add_to_sprite_frames(sprite_frames, animation_name, texture, frame, min_duration, frame_cache)
|
||||
|
||||
if direction == 'pingpong':
|
||||
frames.remove_at(frames.size() - 1)
|
||||
if is_loopable:
|
||||
frames.remove_at(0)
|
||||
frames.reverse()
|
||||
|
||||
for frame in frames:
|
||||
_add_to_sprite_frames(sprite_frames, animation_name, texture, frame, min_duration, frame_cache)
|
||||
|
||||
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 _parse_texture_path(path):
|
||||
return ResourceLoader.load(path, 'Image', ResourceLoader.CACHE_MODE_REPLACE)
|
||||
|
||||
|
||||
func _add_to_sprite_frames(
|
||||
sprite_frames,
|
||||
animation_name: String,
|
||||
texture,
|
||||
frame: Dictionary,
|
||||
min_duration: int,
|
||||
frame_cache: Dictionary
|
||||
):
|
||||
var atlas : AtlasTexture = _create_atlastexture_from_frame(texture, frame, sprite_frames, frame_cache)
|
||||
|
||||
var number_of_sprites = ceil(frame.duration / min_duration)
|
||||
for _i in range(number_of_sprites):
|
||||
sprite_frames.add_frame(animation_name, atlas)
|
||||
|
||||
|
||||
func _create_atlastexture_from_frame(
|
||||
image,
|
||||
frame_data,
|
||||
sprite_frames: SpriteFrames,
|
||||
frame_cache: Dictionary
|
||||
) -> AtlasTexture:
|
||||
var frame = frame_data.frame
|
||||
var region := Rect2(frame.x, frame.y, frame.w, frame.h)
|
||||
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 _scan_filesystem():
|
||||
_file_system.scan()
|
||||
await _file_system.filesystem_changed
|
||||
|
||||
|
||||
func list_layers(file: String, only_visibles = false) -> Array:
|
||||
return _aseprite.list_layers(file, only_visibles)
|
||||
|
||||
|
||||
func _get_file_basename(file_path: String) -> String:
|
||||
return file_path.get_file().trim_suffix('.%s' % file_path.get_extension())
|
||||
|
||||
|
||||
func _loop_config_prefix() -> String:
|
||||
return _config.get_animation_loop_exception_prefix()
|
||||
|
||||
|
||||
func _is_loop_config_enabled() -> bool:
|
||||
return _config.is_default_animation_loop_enabled()
|
||||
|
|
@ -0,0 +1,286 @@
|
|||
extends RefCounted
|
||||
|
||||
var result_code = preload("../config/result_codes.gd")
|
||||
var _aseprite = preload("../aseprite/aseprite.gd").new()
|
||||
|
||||
var _config
|
||||
var _file_system
|
||||
|
||||
var _DEFAULT_ANIMATION_LIBRARY = "" # GLOBAL
|
||||
|
||||
|
||||
func init(config, editor_file_system: EditorFileSystem = null):
|
||||
_config = config
|
||||
_file_system = editor_file_system
|
||||
_aseprite.init(config)
|
||||
|
||||
|
||||
func create_animations(target_node: Node, player: AnimationPlayer, options: Dictionary):
|
||||
if not _aseprite.test_command():
|
||||
return result_code.ERR_ASEPRITE_CMD_NOT_FOUND
|
||||
|
||||
if not FileAccess.file_exists(options.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
|
||||
|
||||
var result = await _create_animations_from_file(target_node, player, options)
|
||||
|
||||
if result != result_code.SUCCESS:
|
||||
printerr(result_code.get_error_message(result))
|
||||
|
||||
|
||||
func _create_animations_from_file(target_node: Node, player: AnimationPlayer, options: Dictionary):
|
||||
var output
|
||||
|
||||
if options.get("layer", "") == "":
|
||||
output = _aseprite.export_file(options.source, options.output_folder, options)
|
||||
else:
|
||||
output = _aseprite.export_layer(options.source, options.layer, options.output_folder, options)
|
||||
|
||||
if output.is_empty():
|
||||
return result_code.ERR_ASEPRITE_EXPORT_FAILED
|
||||
|
||||
if _config.is_import_preset_enabled():
|
||||
_config.create_import_file(output)
|
||||
|
||||
await _scan_filesystem()
|
||||
|
||||
var result = _import(target_node, player, output, options)
|
||||
|
||||
if _config.should_remove_source_files():
|
||||
DirAccess.remove_absolute(output.data_file)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
func _import(target_node: Node, player: AnimationPlayer, data: Dictionary, options: Dictionary):
|
||||
var source_file = data.data_file
|
||||
var sprite_sheet = data.sprite_sheet
|
||||
|
||||
var file = FileAccess.open(source_file, FileAccess.READ)
|
||||
if file == null:
|
||||
return file.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.ERR_INVALID_ASEPRITE_SPRITESHEET
|
||||
|
||||
var context = {}
|
||||
|
||||
target_node.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
|
||||
_setup_texture(target_node, sprite_sheet, content, context)
|
||||
var result = _configure_animations(target_node, player, content, context)
|
||||
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', true)
|
||||
texture.take_over_path(sprite_sheet)
|
||||
return texture
|
||||
|
||||
|
||||
func _configure_animations(target_node: Node, player: AnimationPlayer, content: Dictionary, context: Dictionary):
|
||||
var frames = _aseprite.get_content_frames(content)
|
||||
|
||||
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, tag.direction)
|
||||
if result != result_code.SUCCESS:
|
||||
break
|
||||
return result
|
||||
else:
|
||||
return _add_animation_frames(target_node, player, "default", frames, context)
|
||||
|
||||
|
||||
func _add_animation_frames(target_node: Node, player: AnimationPlayer, anim_name: String, frames: Array, context: Dictionary, direction = 'forward'):
|
||||
var animation_name = anim_name
|
||||
var is_loopable = _config.is_default_animation_loop_enabled()
|
||||
|
||||
if animation_name.begins_with(_config.get_animation_loop_exception_prefix()):
|
||||
animation_name = anim_name.substr(_config.get_animation_loop_exception_prefix().length())
|
||||
is_loopable = not is_loopable
|
||||
|
||||
if not player.has_animation(animation_name):
|
||||
player.get_animation_library(_DEFAULT_ANIMATION_LIBRARY).add_animation(animation_name, Animation.new())
|
||||
|
||||
var animation = player.get_animation(animation_name)
|
||||
_create_meta_tracks(target_node, player, animation)
|
||||
var frame_track = _get_property_track_path(player, target_node, _get_frame_property())
|
||||
var frame_track_index = _create_track(target_node, animation, frame_track)
|
||||
|
||||
if direction == 'reverse':
|
||||
frames.reverse()
|
||||
|
||||
var animation_length = 0
|
||||
|
||||
for frame in frames:
|
||||
var frame_key = _get_frame_key(target_node, frame, context)
|
||||
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 == 'pingpong':
|
||||
frames.remove_at(frames.size() - 1)
|
||||
if is_loopable:
|
||||
frames.remove_at(0)
|
||||
frames.reverse()
|
||||
|
||||
for frame in frames:
|
||||
var frame_key = _get_frame_key(target_node, frame, context)
|
||||
animation.track_insert_key(frame_track_index, animation_length, frame_key)
|
||||
animation_length += frame.duration / 1000
|
||||
|
||||
animation.length = animation_length
|
||||
animation.loop_mode = Animation.LOOP_LINEAR if is_loopable else Animation.LOOP_NONE
|
||||
|
||||
return result_code.SUCCESS
|
||||
|
||||
|
||||
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) != -1:
|
||||
continue
|
||||
var visible_track_index = _create_track(node, animation, visible_track)
|
||||
animation.track_insert_key(visible_track_index, 0, false)
|
||||
|
||||
|
||||
func _scan_filesystem():
|
||||
_file_system.scan()
|
||||
await _file_system.filesystem_changed
|
||||
|
||||
|
||||
func list_layers(file: String, only_visibles = false) -> Array:
|
||||
return _aseprite.list_layers(file, only_visibles)
|
||||
|
||||
|
||||
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 _setup_texture(target_node: Node, sprite_sheet: String, content: Dictionary, context: Dictionary):
|
||||
push_error("_setup_texture not implemented!")
|
||||
|
||||
|
||||
func _get_frame_property() -> String:
|
||||
push_error("_get_frame_property not implemented!")
|
||||
return ""
|
||||
|
||||
|
||||
func _get_frame_key(target_node: Node, frame: Dictionary, context: Dictionary):
|
||||
push_error("_get_frame_key not implemented!")
|
||||
|
||||
|
||||
func _create_meta_tracks(target_node: Node, player: AnimationPlayer, animation: Animation):
|
||||
push_error("_create_meta_tracks not implemented!")
|
||||
|
|
@ -0,0 +1,281 @@
|
|||
@tool
|
||||
extends PanelContainer
|
||||
|
||||
const wizard_config = preload("../../config/wizard_config.gd")
|
||||
const result_code = preload("../../config/result_codes.gd")
|
||||
|
||||
const AnimationCreator = preload("../animation_creator.gd")
|
||||
const SpriteAnimationCreator = preload("../sprite_animation_creator.gd")
|
||||
const TextureRectAnimationCreator = preload("../texture_rect_animation_creator.gd")
|
||||
|
||||
var animation_creator: AnimationCreator
|
||||
|
||||
var scene: Node
|
||||
var target_node: Node
|
||||
|
||||
var config
|
||||
var file_system: EditorFileSystem
|
||||
|
||||
var _layer: String = ""
|
||||
var _source: String = ""
|
||||
var _animation_player_path: String
|
||||
var _file_dialog_aseprite: FileDialog
|
||||
var _output_folder_dialog: FileDialog
|
||||
var _importing := false
|
||||
|
||||
var _output_folder := ""
|
||||
var _out_folder_default := "[Same as scene]"
|
||||
var _layer_default := "[all]"
|
||||
|
||||
@onready var _options_field = $margin/VBoxContainer/animation_player/options
|
||||
@onready var _source_field = $margin/VBoxContainer/source/button
|
||||
@onready var _layer_field = $margin/VBoxContainer/layer/options
|
||||
@onready var _options_title = $margin/VBoxContainer/options_title/options_title
|
||||
@onready var _options_container = $margin/VBoxContainer/options
|
||||
@onready var _out_folder_field = $margin/VBoxContainer/options/out_folder/button
|
||||
@onready var _out_filename_field = $margin/VBoxContainer/options/out_filename/LineEdit
|
||||
@onready var _visible_layers_field = $margin/VBoxContainer/options/visible_layers/CheckButton
|
||||
@onready var _ex_pattern_field = $margin/VBoxContainer/options/ex_pattern/LineEdit
|
||||
@onready var _cleanup_hide_unused_nodes = $margin/VBoxContainer/options/auto_visible_track/CheckButton
|
||||
|
||||
|
||||
func _ready():
|
||||
var cfg = wizard_config.load_config(target_node)
|
||||
if cfg == null:
|
||||
_load_default_config()
|
||||
else:
|
||||
_load_config(cfg)
|
||||
|
||||
if target_node is Sprite2D || target_node is Sprite3D:
|
||||
animation_creator = SpriteAnimationCreator.new()
|
||||
if target_node is TextureRect:
|
||||
animation_creator = TextureRectAnimationCreator.new()
|
||||
|
||||
animation_creator.init(config, file_system)
|
||||
|
||||
|
||||
func _load_config(cfg):
|
||||
if cfg.has("source"):
|
||||
_set_source(cfg.source)
|
||||
|
||||
if cfg.has("player"):
|
||||
_options_field.clear()
|
||||
_set_animation_player(cfg.player)
|
||||
|
||||
if cfg.get("layer", "") != "":
|
||||
_layer_field.clear()
|
||||
_set_layer(cfg.layer)
|
||||
|
||||
_output_folder = cfg.get("o_folder", "")
|
||||
_out_folder_field.text = _output_folder if _output_folder != "" else _out_folder_default
|
||||
_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", "")
|
||||
_cleanup_hide_unused_nodes.button_pressed = cfg.get("set_vis_track", config.is_set_visible_track_automatically_enabled())
|
||||
|
||||
_set_options_visible(cfg.get("op_exp", false))
|
||||
|
||||
|
||||
func _load_default_config():
|
||||
_ex_pattern_field.text = config.get_default_exclusion_pattern()
|
||||
_cleanup_hide_unused_nodes.button_pressed = config.is_set_visible_track_automatically_enabled()
|
||||
_set_options_visible(false)
|
||||
|
||||
|
||||
func _set_source(source):
|
||||
_source = source
|
||||
_source_field.text = _source
|
||||
_source_field.tooltip_text = _source
|
||||
|
||||
|
||||
func _set_animation_player(player):
|
||||
_animation_player_path = player
|
||||
_options_field.add_item(_animation_player_path)
|
||||
|
||||
|
||||
func _set_layer(layer):
|
||||
_layer = layer
|
||||
_layer_field.add_item(_layer)
|
||||
|
||||
|
||||
func _on_options_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
|
||||
_options_field.clear()
|
||||
_options_field.add_item("[empty]")
|
||||
|
||||
for ap in animation_players:
|
||||
_options_field.add_item(ap)
|
||||
if ap.get_concatenated_names() == _animation_player_path:
|
||||
current = _options_field.get_item_count() - 1
|
||||
|
||||
_options_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_options_item_selected(index):
|
||||
if index == 0:
|
||||
_animation_player_path = ""
|
||||
return
|
||||
_animation_player_path = _options_field.get_item_text(index)
|
||||
_save_config()
|
||||
|
||||
|
||||
func _on_layer_button_down():
|
||||
if _source == "":
|
||||
_show_message("Please. Select source file first.")
|
||||
return
|
||||
|
||||
var layers = animation_creator.list_layers(ProjectSettings.globalize_path(_source))
|
||||
var current = 0
|
||||
_layer_field.clear()
|
||||
_layer_field.add_item("[all]")
|
||||
|
||||
for l in layers:
|
||||
if l == "":
|
||||
continue
|
||||
|
||||
_layer_field.add_item(l)
|
||||
if l == _layer:
|
||||
current = _layer_field.get_item_count() - 1
|
||||
_layer_field.select(current)
|
||||
|
||||
|
||||
func _on_layer_item_selected(index):
|
||||
if index == 0:
|
||||
_layer = ""
|
||||
return
|
||||
_layer = _layer_field.get_item_text(index)
|
||||
_save_config()
|
||||
|
||||
|
||||
func _on_source_pressed():
|
||||
_open_source_dialog()
|
||||
|
||||
|
||||
func _on_import_pressed():
|
||||
if _importing:
|
||||
return
|
||||
_importing = true
|
||||
|
||||
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
|
||||
|
||||
if _source == "":
|
||||
_show_message("Aseprite file not selected")
|
||||
_importing = false
|
||||
return
|
||||
|
||||
var options = {
|
||||
"source": ProjectSettings.globalize_path(_source),
|
||||
"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,
|
||||
"cleanup_hide_unused_nodes": _cleanup_hide_unused_nodes.button_pressed,
|
||||
"layer": _layer
|
||||
}
|
||||
|
||||
_save_config()
|
||||
|
||||
await animation_creator.create_animations(target_node, root.get_node(_animation_player_path), options)
|
||||
_importing = false
|
||||
|
||||
|
||||
func _save_config():
|
||||
var cfg := {
|
||||
"player": _animation_player_path,
|
||||
"source": _source,
|
||||
"layer": _layer,
|
||||
"op_exp": _options_title.button_pressed,
|
||||
"o_folder": _output_folder,
|
||||
"o_name": _out_filename_field.text,
|
||||
"only_visible": _visible_layers_field.button_pressed,
|
||||
"o_ex_p": _ex_pattern_field.text,
|
||||
}
|
||||
|
||||
if _cleanup_hide_unused_nodes.button_pressed != config.is_set_visible_track_automatically_enabled():
|
||||
cfg["set_vis_track"] = _cleanup_hide_unused_nodes.button_pressed
|
||||
|
||||
wizard_config.save_config(target_node, config.is_use_metadata_enabled(), cfg)
|
||||
|
||||
|
||||
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 = _source.get_base_dir()
|
||||
_file_dialog_aseprite.popup_centered_ratio()
|
||||
|
||||
|
||||
func _create_aseprite_file_selection():
|
||||
var file_dialog = FileDialog.new()
|
||||
file_dialog.file_mode = FileDialog.FILE_MODE_OPEN_FILE
|
||||
file_dialog.access = FileDialog.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 _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 _on_options_title_toggled(button_pressed):
|
||||
_set_options_visible(button_pressed)
|
||||
_save_config()
|
||||
|
||||
|
||||
func _set_options_visible(is_visible):
|
||||
_options_container.visible = is_visible
|
||||
_options_title.icon = config.get_icon("expanded") if is_visible else config.get_icon("collapsed")
|
||||
|
||||
|
||||
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 = FileDialog.new()
|
||||
file_dialog.file_mode = FileDialog.FILE_MODE_OPEN_DIR
|
||||
file_dialog.access = FileDialog.ACCESS_RESOURCES
|
||||
file_dialog.connect("dir_selected",Callable(self,"_on_output_folder_selected"))
|
||||
return file_dialog
|
||||
|
||||
|
||||
func _on_output_folder_selected(path):
|
||||
_output_folder = path
|
||||
_out_folder_field.text = _output_folder if _output_folder != "" else _out_folder_default
|
||||
_output_folder_dialog.queue_free()
|
||||
|
|
@ -0,0 +1,181 @@
|
|||
[gd_scene load_steps=2 format=3 uid="uid://biyshalfalqqw"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/AsepriteWizard/animation_player/docks/animation_player_inspector_dock.gd" id="1"]
|
||||
|
||||
[node name="sprite_inspector_dock" type="PanelContainer"]
|
||||
offset_right = 14.0
|
||||
offset_bottom = 14.0
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="margin" type="MarginContainer" parent="."]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="margin"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="section_title" type="PanelContainer" parent="margin/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="title" type="Label" parent="margin/VBoxContainer/section_title"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Aseprite"
|
||||
horizontal_alignment = 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" type="OptionButton" parent="margin/VBoxContainer/animation_player"]
|
||||
layout_mode = 2
|
||||
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
|
||||
|
||||
[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" type="Button" parent="margin/VBoxContainer/source"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 2.0
|
||||
text = "[empty]"
|
||||
clip_text = true
|
||||
|
||||
[node name="layer" type="HBoxContainer" parent="margin/VBoxContainer"]
|
||||
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/layer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 2.0
|
||||
text = "Layer"
|
||||
|
||||
[node name="options" type="OptionButton" parent="margin/VBoxContainer/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="options_title" type="PanelContainer" parent="margin/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="options_title" type="Button" parent="margin/VBoxContainer/options_title"]
|
||||
layout_mode = 2
|
||||
toggle_mode = true
|
||||
text = "Options"
|
||||
|
||||
[node name="options" type="VBoxContainer" parent="margin/VBoxContainer"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
|
||||
[node name="out_folder" type="HBoxContainer" parent="margin/VBoxContainer/options"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Location where the spritesheet file should be saved."
|
||||
|
||||
[node name="Label" type="Label" parent="margin/VBoxContainer/options/out_folder"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 2.0
|
||||
text = "Output folder"
|
||||
|
||||
[node name="button" type="Button" parent="margin/VBoxContainer/options/out_folder"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 2.0
|
||||
text = "[empty]"
|
||||
clip_text = true
|
||||
|
||||
[node name="out_filename" type="HBoxContainer" parent="margin/VBoxContainer/options"]
|
||||
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/options/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/options/out_filename"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 2.0
|
||||
|
||||
[node name="ex_pattern" type="HBoxContainer" parent="margin/VBoxContainer/options"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Exclude layers with name matching this pattern (regex)."
|
||||
|
||||
[node name="Label" type="Label" parent="margin/VBoxContainer/options/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/options/ex_pattern"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 2.0
|
||||
|
||||
[node name="visible_layers" type="HBoxContainer" parent="margin/VBoxContainer/options"]
|
||||
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/options/visible_layers"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 2.0
|
||||
text = "Only visible layers"
|
||||
|
||||
[node name="CheckButton" type="CheckButton" parent="margin/VBoxContainer/options/visible_layers"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 2.0
|
||||
|
||||
[node name="auto_visible_track" type="HBoxContainer" parent="margin/VBoxContainer/options"]
|
||||
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/options/auto_visible_track"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 2.0
|
||||
text = "Hide unused sprites"
|
||||
|
||||
[node name="CheckButton" type="CheckButton" parent="margin/VBoxContainer/options/auto_visible_track"]
|
||||
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="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="pressed" from="margin/VBoxContainer/source/button" to="." method="_on_source_pressed"]
|
||||
[connection signal="button_down" from="margin/VBoxContainer/layer/options" to="." method="_on_layer_button_down"]
|
||||
[connection signal="item_selected" from="margin/VBoxContainer/layer/options" to="." method="_on_layer_item_selected"]
|
||||
[connection signal="toggled" from="margin/VBoxContainer/options_title/options_title" to="." method="_on_options_title_toggled"]
|
||||
[connection signal="pressed" from="margin/VBoxContainer/options/out_folder/button" to="." method="_on_out_folder_pressed"]
|
||||
[connection signal="pressed" from="margin/VBoxContainer/import" to="." method="_on_import_pressed"]
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
@tool
|
||||
extends EditorInspectorPlugin
|
||||
|
||||
const APInspectorDock = preload("./docks/animation_player_inspector_dock.tscn")
|
||||
|
||||
var config
|
||||
var file_system: EditorFileSystem
|
||||
|
||||
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
|
||||
dock.config = config
|
||||
dock.file_system = file_system
|
||||
add_custom_control(dock)
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
extends "animation_creator.gd"
|
||||
|
||||
|
||||
func _setup_texture(sprite: Node, sprite_sheet: String, content: Dictionary, context: Dictionary):
|
||||
var texture = _load_texture(sprite_sheet)
|
||||
sprite.texture = texture
|
||||
|
||||
if content.frames.is_empty():
|
||||
return
|
||||
|
||||
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() -> String:
|
||||
return "frame"
|
||||
|
||||
|
||||
func _create_meta_tracks(sprite: Node, player: AnimationPlayer, animation: Animation):
|
||||
var texture_track = _get_property_track_path(player, sprite, "texture")
|
||||
var texture_track_index = _create_track(sprite, animation, texture_track)
|
||||
animation.track_insert_key(texture_track_index, 0, sprite.texture)
|
||||
|
||||
var hframes_track = _get_property_track_path(player, sprite, "hframes")
|
||||
var hframes_track_index = _create_track(sprite, animation, hframes_track)
|
||||
animation.track_insert_key(hframes_track_index, 0, sprite.hframes)
|
||||
|
||||
var vframes_track = _get_property_track_path(player, sprite, "vframes")
|
||||
var vframes_track_index = _create_track(sprite, animation, vframes_track)
|
||||
animation.track_insert_key(vframes_track_index, 0, sprite.vframes)
|
||||
|
||||
var visible_track = _get_property_track_path(player, sprite, "visible")
|
||||
var visible_track_index = _create_track(sprite, animation, visible_track)
|
||||
animation.track_insert_key(visible_track_index, 0, true)
|
||||
|
||||
|
||||
func _get_frame_key(sprite: Node, frame: Dictionary, context: Dictionary):
|
||||
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
|
||||
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
extends "animation_creator.gd"
|
||||
|
||||
|
||||
func _setup_texture(target_node: Node, sprite_sheet: String, content: Dictionary, context: Dictionary):
|
||||
context["base_texture"] = _load_texture(sprite_sheet)
|
||||
|
||||
|
||||
func _get_frame_property() -> String:
|
||||
return "texture"
|
||||
|
||||
|
||||
func _get_frame_key(target_node: Node, frame: Dictionary, context: Dictionary):
|
||||
return _get_atlas_texture(context["base_texture"], frame)
|
||||
|
||||
|
||||
func _create_meta_tracks(target_node: Node, player: AnimationPlayer, animation: Animation):
|
||||
var texture_track = _get_property_track_path(player, target_node, "texture")
|
||||
var texture_track_index = _create_track(target_node, animation, texture_track)
|
||||
animation.track_insert_key(texture_track_index, 0, target_node.texture)
|
||||
|
||||
var visible_track = _get_property_track_path(player, target_node, "visible")
|
||||
var visible_track_index = _create_track(target_node, animation, visible_track)
|
||||
animation.track_insert_key(visible_track_index, 0, true)
|
||||
|
||||
|
||||
func _get_atlas_texture(base_texture: Texture2D, frame_data: Dictionary) -> 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
|
||||
return tex
|
||||
187
Sprout/addons/AsepriteWizard/aseprite/aseprite.gd
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
@tool
|
||||
extends RefCounted
|
||||
|
||||
var _config
|
||||
|
||||
func init(config):
|
||||
_config = config
|
||||
|
||||
#
|
||||
# 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 basename = _get_file_basename(output_name)
|
||||
var output_dir = output_folder.replace("res://", "./")
|
||||
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")
|
||||
|
||||
_add_sheet_type_arguments(arguments, options)
|
||||
|
||||
_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': data_file.replace("./", "res://"),
|
||||
"sprite_sheet": sprite_sheet.replace("./", "res://")
|
||||
}
|
||||
|
||||
|
||||
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 output = []
|
||||
var arguments = _export_command_common_arguments(file_name, data_file, sprite_sheet)
|
||||
arguments.push_front(layer_name)
|
||||
arguments.push_front("--layer")
|
||||
|
||||
_add_sheet_type_arguments(arguments, options)
|
||||
|
||||
var exit_code = _execute(arguments, output)
|
||||
if exit_code != 0:
|
||||
print('aseprite: failed to export layer spritesheet')
|
||||
print(output)
|
||||
return {}
|
||||
|
||||
return {
|
||||
'data_file': data_file.replace("./", "res://"),
|
||||
"sprite_sheet": sprite_sheet.replace("./", "res://")
|
||||
}
|
||||
|
||||
|
||||
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 _export_command_common_arguments(source_name: String, data_path: String, spritesheet_path: String) -> Array:
|
||||
return [
|
||||
"-b",
|
||||
"--list-tags",
|
||||
"--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()
|
||||
19
Sprout/addons/AsepriteWizard/config/2d_pixel_preset.cfg
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
[preset]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/bptc_ldr=0
|
||||
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
|
||||
311
Sprout/addons/AsepriteWizard/config/config.gd
Normal file
|
|
@ -0,0 +1,311 @@
|
|||
@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_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'
|
||||
|
||||
# custom preset
|
||||
const _IMPORT_PRESET_ENABLED = 'aseprite/import/preset/enable_custom_preset'
|
||||
const _IMPORT_PRESET_KEY = 'aseprite/import/preset/preset'
|
||||
const _PIXEL_2D_PRESET_CFG = 'res://addons/AsepriteWizard/config/2d_pixel_preset.cfg'
|
||||
|
||||
# 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'
|
||||
|
||||
# wizard history
|
||||
const _HISTORY_CONFIG_FILE_CFG_KEY = 'aseprite/wizard/history/cache_file_path'
|
||||
const _HISTORY_SINGLE_ENTRY_KEY = 'aseprite/wizard/history/keep_one_entry_per_source_file'
|
||||
const _DEFAULT_HISTORY_CONFIG_FILE_PATH = 'res://.aseprite_wizard_history'
|
||||
|
||||
# IMPORT SETTINGS
|
||||
const _I_LAST_SOURCE_PATH_KEY = 'i_source'
|
||||
const _I_LAST_OUTPUT_DIR_KEY = 'i_output'
|
||||
const _I_SHOULD_SPLIT_LAYERS_KEY = 'i_split_layers'
|
||||
const _I_EXCEPTIONS_KEY = 'i_exceptions_key'
|
||||
const _I_ONLY_VISIBLE_LAYERS_KEY = 'i_only_visible_layers'
|
||||
const _I_CUSTOM_NAME_KEY = 'i_custom_name'
|
||||
const _I_DO_NOT_CREATE_RES_KEY = 'i_disable_resource_creation'
|
||||
|
||||
# export
|
||||
const _EXPORTER_ENABLE_KEY = 'aseprite/animation/storage/enable_metadata_removal_on_export'
|
||||
|
||||
var _editor_settings: EditorSettings
|
||||
|
||||
# INTERFACE SETTINGS
|
||||
var _plugin_icons: Dictionary
|
||||
|
||||
#######################################################
|
||||
# GLOBAL CONFIGS
|
||||
######################################################
|
||||
|
||||
func default_command() -> String:
|
||||
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
|
||||
######################################################
|
||||
func is_importer_enabled() -> bool:
|
||||
return _get_project_setting(_IMPORTER_ENABLE_KEY, false)
|
||||
|
||||
|
||||
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 is_import_preset_enabled() -> bool:
|
||||
return _get_project_setting(_IMPORT_PRESET_ENABLED, false)
|
||||
|
||||
|
||||
func is_single_file_history() -> bool:
|
||||
return ProjectSettings.get(_HISTORY_SINGLE_ENTRY_KEY) == true
|
||||
|
||||
|
||||
func get_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)
|
||||
|
||||
# history is saved and retrieved line-by-line so
|
||||
# file becomes version control friendly
|
||||
func save_import_history(history: Array):
|
||||
var file = FileAccess.open(_get_history_file_path(), FileAccess.WRITE)
|
||||
for entry in history:
|
||||
file.store_line(JSON.new().stringify(entry))
|
||||
file = null
|
||||
|
||||
|
||||
func _get_history_file_path() -> String:
|
||||
return _get_project_setting(_HISTORY_CONFIG_FILE_CFG_KEY, _DEFAULT_HISTORY_CONFIG_FILE_PATH)
|
||||
|
||||
|
||||
func create_import_preset_setting() -> void:
|
||||
if ProjectSettings.has_setting(_IMPORT_PRESET_KEY) && (ProjectSettings.get_setting(_IMPORT_PRESET_KEY) as Dictionary).size() > 0:
|
||||
return
|
||||
|
||||
var preset := ConfigFile.new()
|
||||
preset.load(_PIXEL_2D_PRESET_CFG)
|
||||
|
||||
var dict = {}
|
||||
for key in preset.get_section_keys("preset"):
|
||||
dict[key] = preset.get_value("preset", key)
|
||||
|
||||
_initialize_project_cfg(_IMPORT_PRESET_KEY, dict, TYPE_DICTIONARY)
|
||||
|
||||
|
||||
#######################################################
|
||||
# IMPORT CONFIGS
|
||||
######################################################
|
||||
func get_last_source_path() -> String:
|
||||
return _editor_settings.get_project_metadata(_CONFIG_SECTION_KEY, _I_LAST_SOURCE_PATH_KEY, "")
|
||||
|
||||
|
||||
func set_last_source_path(source_path: String) -> void:
|
||||
_editor_settings.set_project_metadata(_CONFIG_SECTION_KEY, _I_LAST_SOURCE_PATH_KEY, source_path)
|
||||
|
||||
|
||||
func get_last_output_path() -> String:
|
||||
return _editor_settings.get_project_metadata(_CONFIG_SECTION_KEY, _I_LAST_OUTPUT_DIR_KEY, "")
|
||||
|
||||
|
||||
func set_last_output_path(output_path: String) -> void:
|
||||
_editor_settings.set_project_metadata(_CONFIG_SECTION_KEY, _I_LAST_OUTPUT_DIR_KEY, output_path)
|
||||
|
||||
|
||||
func should_split_layers() -> bool:
|
||||
return _editor_settings.get_project_metadata(_CONFIG_SECTION_KEY, _I_SHOULD_SPLIT_LAYERS_KEY, false)
|
||||
|
||||
|
||||
func set_split_layers(should_split: bool) -> void:
|
||||
_editor_settings.set_project_metadata(_CONFIG_SECTION_KEY, _I_SHOULD_SPLIT_LAYERS_KEY, false)
|
||||
|
||||
|
||||
func get_exception_pattern() -> String:
|
||||
return _editor_settings.get_project_metadata(_CONFIG_SECTION_KEY, _I_EXCEPTIONS_KEY, "")
|
||||
|
||||
|
||||
func set_exception_pattern(pattern: String) -> void:
|
||||
_editor_settings.set_project_metadata(_CONFIG_SECTION_KEY, _I_EXCEPTIONS_KEY, pattern)
|
||||
|
||||
|
||||
func should_include_only_visible_layers() -> bool:
|
||||
return _editor_settings.get_project_metadata(_CONFIG_SECTION_KEY, _I_ONLY_VISIBLE_LAYERS_KEY, false)
|
||||
|
||||
|
||||
func set_include_only_visible_layers(include_only_visible: bool) -> void:
|
||||
_editor_settings.set_project_metadata(_CONFIG_SECTION_KEY, _I_ONLY_VISIBLE_LAYERS_KEY, include_only_visible)
|
||||
|
||||
|
||||
func get_last_custom_name() -> String:
|
||||
return _editor_settings.get_project_metadata(_CONFIG_SECTION_KEY, _I_CUSTOM_NAME_KEY, "")
|
||||
|
||||
|
||||
func set_custom_name(custom_name: String) -> void:
|
||||
_editor_settings.set_project_metadata(_CONFIG_SECTION_KEY, _I_CUSTOM_NAME_KEY, custom_name)
|
||||
|
||||
|
||||
func should_not_create_resource() -> bool:
|
||||
return _editor_settings.get_project_metadata(_CONFIG_SECTION_KEY, _I_DO_NOT_CREATE_RES_KEY, false)
|
||||
|
||||
|
||||
func set_do_not_create_resource(do_no_create: bool) -> void:
|
||||
_editor_settings.set_project_metadata(_CONFIG_SECTION_KEY, _I_DO_NOT_CREATE_RES_KEY, do_no_create)
|
||||
|
||||
#######################################################
|
||||
# INTERFACE SETTINGS
|
||||
######################################################
|
||||
|
||||
func set_icons(plugin_icons: Dictionary) -> void:
|
||||
_plugin_icons = plugin_icons
|
||||
|
||||
|
||||
func get_icon(icon_name: String) -> Texture2D:
|
||||
return _plugin_icons[icon_name]
|
||||
|
||||
|
||||
#######################################################
|
||||
# INITIALIZATION
|
||||
######################################################
|
||||
func initialize_project_settings():
|
||||
_initialize_project_cfg(_DEFAULT_EXCLUSION_PATTERN_KEY, "", TYPE_STRING)
|
||||
_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(_IMPORT_PRESET_ENABLED, false, TYPE_BOOL)
|
||||
|
||||
_initialize_project_cfg(_REMOVE_SOURCE_FILES_KEY, true, TYPE_BOOL)
|
||||
_initialize_project_cfg(_IMPORTER_ENABLE_KEY, false, TYPE_BOOL)
|
||||
|
||||
_initialize_project_cfg(_EXPORTER_ENABLE_KEY, true, TYPE_BOOL)
|
||||
|
||||
_initialize_project_cfg(_HISTORY_CONFIG_FILE_CFG_KEY, _DEFAULT_HISTORY_CONFIG_FILE_PATH, TYPE_STRING, PROPERTY_HINT_GLOBAL_FILE)
|
||||
_initialize_project_cfg(_HISTORY_SINGLE_ENTRY_KEY, false, TYPE_BOOL)
|
||||
|
||||
_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,
|
||||
_IMPORT_PRESET_ENABLED,
|
||||
_IMPORT_PRESET_KEY,
|
||||
_REMOVE_SOURCE_FILES_KEY,
|
||||
_IMPORTER_ENABLE_KEY,
|
||||
_EXPORTER_ENABLE_KEY,
|
||||
_HISTORY_CONFIG_FILE_CFG_KEY,
|
||||
_HISTORY_SINGLE_ENTRY_KEY,
|
||||
_SET_VISIBLE_TRACK_AUTOMATICALLY
|
||||
]
|
||||
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):
|
||||
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,
|
||||
})
|
||||
|
||||
|
||||
func _get_project_setting(key: String, default_value):
|
||||
var p = ProjectSettings.get(key)
|
||||
return p if p else default_value
|
||||
|
||||
|
||||
func create_import_file(data: Dictionary) -> void:
|
||||
if !ProjectSettings.has_setting(_IMPORT_PRESET_KEY):
|
||||
push_warning("no import settings found for 'aseprite_texture' in Project Settings")
|
||||
return
|
||||
|
||||
var file_path := "%s.import" % [data.sprite_sheet]
|
||||
var import_file := ConfigFile.new()
|
||||
if import_file.load(file_path) == OK:
|
||||
return
|
||||
|
||||
import_file.set_value("remap", "importer", "texture")
|
||||
import_file.set_value("remap", "type", "CompressedTexture2D")
|
||||
import_file.set_value("deps", "source_file", data.sprite_sheet)
|
||||
var preset: Dictionary = ProjectSettings.get_setting(_IMPORT_PRESET_KEY)
|
||||
for key in preset:
|
||||
import_file.set_value("params", key, preset[key])
|
||||
import_file.save(file_path)
|
||||
|
||||
|
||||
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,
|
||||
})
|
||||
33
Sprout/addons/AsepriteWizard/config/config_dialog.gd
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
@tool
|
||||
extends PopupPanel
|
||||
|
||||
var _config
|
||||
|
||||
@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 init(config):
|
||||
_config = config
|
||||
|
||||
|
||||
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
Sprout/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 Path3D"
|
||||
|
||||
[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
Sprout/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 \"Project > Tools > Aseprite Wizard Config\"."
|
||||
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 }
|
||||
71
Sprout/addons/AsepriteWizard/config/wizard_config.gd
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
@tool
|
||||
extends RefCounted
|
||||
|
||||
const WIZARD_CONFIG_META_NAME = "_aseprite_wizard_config_"
|
||||
const WIZARD_CONFIG_MARKER = "aseprite_wizard_config"
|
||||
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:Node):
|
||||
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:Node, use_metadata:bool, cfg:Dictionary):
|
||||
if use_metadata:
|
||||
node.set_meta(WIZARD_CONFIG_META_NAME, cfg)
|
||||
|
||||
#Delete config from editor_description
|
||||
var decoded = _decode_base64(node.editor_description)
|
||||
if _is_wizard_config(decoded):
|
||||
node.editor_description = ""
|
||||
else:
|
||||
node.editor_description = encode(cfg)
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
extends EditorExportPlugin
|
||||
|
||||
const wizard_config = preload("../config/wizard_config.gd")
|
||||
|
||||
func _export_file(path: String, type: String, features: PackedStringArray) -> void:
|
||||
if type != "PackedScene": return
|
||||
|
||||
var scene : PackedScene = ResourceLoader.load(path, type, true)
|
||||
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, path):
|
||||
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:Node, path: String) -> bool:
|
||||
if node.has_meta(wizard_config.WIZARD_CONFIG_META_NAME):
|
||||
node.remove_meta(wizard_config.WIZARD_CONFIG_META_NAME)
|
||||
print("Aseprite Wizard: Removed metadata from scene %s" % path)
|
||||
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
|
||||
7
Sprout/addons/AsepriteWizard/plugin.cfg
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
[plugin]
|
||||
|
||||
name="Aseprite Wizard (Godot 4)"
|
||||
description="Import Aseprite files to AnimationPlayers, AnimatedSprites and SpriteFrames."
|
||||
author="Vinicius Gerevini"
|
||||
version="6.0.0"
|
||||
script="plugin.gd"
|
||||
150
Sprout/addons/AsepriteWizard/plugin.gd
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
@tool
|
||||
extends EditorPlugin
|
||||
|
||||
const ConfigDialog = preload('config/config_dialog.tscn')
|
||||
const WizardWindow = preload("animated_sprite/docks/as_wizard_dock_container.tscn")
|
||||
const ImportPlugin = preload("animated_sprite/import_plugin.gd")
|
||||
const ExportPlugin = preload("export/metadata_export_plugin.gd")
|
||||
const AnimatedSpriteInspectorPlugin = preload("animated_sprite/inspector_plugin.gd")
|
||||
const SpriteInspectorPlugin = preload("animation_player/inspector_plugin.gd")
|
||||
const menu_item_name = "Aseprite Spritesheet Wizard"
|
||||
const config_menu_item_name = "Aseprite Wizard Config"
|
||||
|
||||
var config = preload("config/config.gd").new()
|
||||
var window: TabContainer
|
||||
var config_window: PopupPanel
|
||||
var import_plugin : EditorImportPlugin
|
||||
var export_plugin : EditorExportPlugin
|
||||
var sprite_inspector_plugin: EditorInspectorPlugin
|
||||
var animated_sprite_inspector_plugin: EditorInspectorPlugin
|
||||
|
||||
var _importer_enabled = false
|
||||
var _exporter_enabled = false
|
||||
|
||||
|
||||
func _enter_tree():
|
||||
_load_config()
|
||||
_setup_menu_entries()
|
||||
_setup_importer()
|
||||
_setup_exporter()
|
||||
_configure_preset()
|
||||
_setup_animated_sprite_inspector_plugin()
|
||||
_setup_sprite_inspector_plugin()
|
||||
|
||||
|
||||
func _disable_plugin():
|
||||
_remove_menu_entries()
|
||||
_remove_importer()
|
||||
_remove_exporter()
|
||||
_remove_wizard_dock()
|
||||
_remove_inspector_plugins()
|
||||
config.clear_project_settings()
|
||||
config.set_icons({})
|
||||
|
||||
|
||||
func _load_config():
|
||||
var editor_gui = get_editor_interface().get_base_control()
|
||||
config._editor_settings = get_editor_interface().get_editor_settings()
|
||||
config.set_icons({
|
||||
"collapsed": editor_gui.get_theme_icon("GuiTreeArrowRight", "EditorIcons"),
|
||||
"expanded": editor_gui.get_theme_icon("GuiTreeArrowDown", "EditorIcons"),
|
||||
})
|
||||
config.initialize_project_settings()
|
||||
|
||||
|
||||
func _setup_menu_entries():
|
||||
add_tool_menu_item(menu_item_name, _open_window)
|
||||
add_tool_menu_item(config_menu_item_name, _open_config_dialog)
|
||||
|
||||
|
||||
func _remove_menu_entries():
|
||||
remove_tool_menu_item(menu_item_name)
|
||||
remove_tool_menu_item(config_menu_item_name)
|
||||
|
||||
|
||||
func _setup_importer():
|
||||
if config.is_importer_enabled():
|
||||
import_plugin = ImportPlugin.new()
|
||||
import_plugin.file_system = get_editor_interface().get_resource_filesystem()
|
||||
import_plugin.config = config
|
||||
add_import_plugin(import_plugin)
|
||||
_importer_enabled = true
|
||||
|
||||
|
||||
func _configure_preset():
|
||||
if config.is_import_preset_enabled():
|
||||
config.create_import_preset_setting()
|
||||
|
||||
|
||||
func _remove_importer():
|
||||
if _importer_enabled:
|
||||
remove_import_plugin(import_plugin)
|
||||
_importer_enabled = false
|
||||
|
||||
|
||||
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()
|
||||
sprite_inspector_plugin.file_system = get_editor_interface().get_resource_filesystem()
|
||||
sprite_inspector_plugin.config = config
|
||||
add_inspector_plugin(sprite_inspector_plugin)
|
||||
|
||||
|
||||
func _setup_animated_sprite_inspector_plugin():
|
||||
animated_sprite_inspector_plugin = AnimatedSpriteInspectorPlugin.new()
|
||||
animated_sprite_inspector_plugin.file_system = get_editor_interface().get_resource_filesystem()
|
||||
animated_sprite_inspector_plugin.config = config
|
||||
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.init(config, get_editor_interface().get_resource_filesystem())
|
||||
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()
|
||||
config_window.init(config)
|
||||
get_editor_interface().get_base_control().add_child(config_window)
|
||||
config_window.popup_centered()
|
||||
|
||||
|
||||
func _on_window_closed():
|
||||
if window:
|
||||
remove_control_from_bottom_panel(window)
|
||||
window.queue_free()
|
||||
window = null
|
||||
21
Sprout/addons/beehave/LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2023 bitbrain
|
||||
|
||||
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.
|
||||
27
Sprout/addons/beehave/blackboard.gd
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
## The blackboard is an object that can be used to store and access data between
|
||||
## multiple nodes of the behaviour tree.
|
||||
class_name Blackboard extends RefCounted
|
||||
|
||||
var blackboard: Dictionary = {}
|
||||
|
||||
|
||||
func set_value(key: Variant, value: Variant, blackboard_name: String = 'default') -> void:
|
||||
if not blackboard.has(blackboard_name):
|
||||
blackboard[blackboard_name] = {}
|
||||
|
||||
blackboard[blackboard_name][key] = value
|
||||
|
||||
|
||||
func get_value(key: Variant, default_value: Variant = null, blackboard_name: String = 'default') -> Variant:
|
||||
if has_value(key, blackboard_name):
|
||||
return blackboard[blackboard_name].get(key, default_value)
|
||||
return default_value
|
||||
|
||||
|
||||
func has_value(key: Variant, blackboard_name: String = 'default') -> bool:
|
||||
return blackboard.has(blackboard_name) and blackboard[blackboard_name].has(key) and blackboard[blackboard_name][key] != null
|
||||
|
||||
|
||||
func erase_value(key: Variant, blackboard_name: String = 'default') -> void:
|
||||
if blackboard.has(blackboard_name):
|
||||
blackboard[blackboard_name][key] = null
|
||||
38
Sprout/addons/beehave/icons/action.svg
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
height="18.512016"
|
||||
width="14.055721"
|
||||
version="1.1"
|
||||
id="svg8741"
|
||||
sodipodi:docname="action.svg"
|
||||
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs8745" />
|
||||
<sodipodi:namedview
|
||||
id="namedview8743"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
showgrid="false"
|
||||
inkscape:zoom="7.6367532"
|
||||
inkscape:cx="-17.284833"
|
||||
inkscape:cy="19.183545"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1051"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg8741" />
|
||||
<path
|
||||
d="M 10.176432,3.6912688 Q 9.4137999,3.5779847 8.9573199,2.9622304 8.5018279,2.346623 8.6151119,1.5839909 q 0.113137,-0.76164287 0.727903,-1.21827047 0.615754,-0.45648072 1.3783861,-0.34319658 0.762632,0.1132841 1.218124,0.72889148 0.45648,0.61575427 0.343343,1.37739717 -0.113284,0.7626321 -0.729038,1.2191128 -0.614766,0.4566276 -1.377398,0.3433435 z M 8.3557339,18.50048 q -0.412474,-0.06127 -0.675246,-0.415727 -0.263613,-0.355592 -0.2024895,-0.767077 l 0.5938965,-3.998131 -1.1673025,-1.499791 -0.492278,1.19059 Q 6.067908,13.906466 5.2412215,14.299263 4.4143881,14.693049 3.5141916,14.369273 L 0.68012778,13.316428 q -0.3971933,-0.164141 -0.5825748,-0.549563 -0.1843923,-0.385274 -0.0202507,-0.782468 0.1463082,-0.420062 0.5218382,-0.606912 0.3753831,-0.185862 0.79544492,-0.03955 L 4.2327238,12.370146 6.3483399,6.4922013 5.1950516,6.7424632 4.9226408,8.5763411 Q 4.8584317,9.0085983 4.5028393,9.2722113 4.1483829,9.534983 3.7359088,9.473712 3.3234346,9.412442 3.0497828,9.0563693 2.7772671,8.6994546 2.8414761,8.2671975 L 3.0925819,6.5767458 Q 3.1935238,5.897202 3.6684284,5.3884589 4.1443222,4.8798627 4.8023572,4.703636 L 6.9923039,4.1443377 q 0.743073,-0.1838143 1.140791,-0.2409975 0.398708,-0.057036 0.728094,-0.00811 0.515345,0.076551 0.87859,0.3943727 0.3633911,0.3168322 0.5670941,0.7898969 l 0.629089,1.4410733 q 0.295795,0.6970267 0.885176,1.2688312 0.589381,0.5718045 1.447044,0.8892676 0.421051,0.1464552 0.62705,0.4611386 0.205852,0.3156725 0.147667,0.7073745 -0.07347,0.494573 -0.448694,0.754259 -0.376218,0.259539 -0.813966,0.08937 Q 11.753166,10.370427 10.957563,9.778099 10.159981,9.1854775 9.5990659,8.4288493 l -0.661707,1.9024227 0.845301,1.115306 q 0.2835431,0.357542 0.3859911,0.804445 0.102448,0.446903 0.03501,0.900921 l -0.6642771,4.471933 q -0.06112,0.411485 -0.416716,0.675098 -0.355445,0.262624 -0.76693,0.201501 z"
|
||||
id="path8739"
|
||||
style="fill:#ffb649;fill-opacity:1" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
37
Sprout/addons/beehave/icons/action.svg.import
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://bbia60elqjuti"
|
||||
path="res://.godot/imported/action.svg-e8a91246d0ba9ba3cf84290d65648f06.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/beehave/icons/action.svg"
|
||||
dest_files=["res://.godot/imported/action.svg-e8a91246d0ba9ba3cf84290d65648f06.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/bptc_ldr=0
|
||||
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
|
||||
38
Sprout/addons/beehave/icons/blackboard.svg
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
height="14.416"
|
||||
width="15.416"
|
||||
version="1.1"
|
||||
id="svg1061"
|
||||
sodipodi:docname="blackboard.svg"
|
||||
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1065" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1063"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
showgrid="false"
|
||||
inkscape:zoom="30.547013"
|
||||
inkscape:cx="-0.42557352"
|
||||
inkscape:cy="10.62297"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1051"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg1061" />
|
||||
<path
|
||||
d="M 0,4.333 V 1.937 Q 0,1.125 0.573,0.562 1.146,0 1.937,0 h 11.542 q 0.791,0 1.364,0.562 0.573,0.563 0.573,1.375 v 2.396 z m 4.354,1.396 v 8.687 H 1.937 q -0.791,0 -1.364,-0.573 Q 0,13.27 0,12.479 v -6.75 z m 6.708,0 h 4.354 v 6.75 q 0,0.791 -0.573,1.364 -0.573,0.573 -1.364,0.573 h -2.417 z m -1.396,0 v 8.687 H 5.75 V 5.729 Z"
|
||||
id="path1059"
|
||||
style="fill:#c689ff;fill-opacity:1;stroke:none;stroke-opacity:1" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
37
Sprout/addons/beehave/icons/blackboard.svg.import
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dd4bmjexgxxu0"
|
||||
path="res://.godot/imported/blackboard.svg-18d4dfd4f6de558de250b67251ff1e69.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/beehave/icons/blackboard.svg"
|
||||
dest_files=["res://.godot/imported/blackboard.svg-18d4dfd4f6de558de250b67251ff1e69.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/bptc_ldr=0
|
||||
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
|
||||
38
Sprout/addons/beehave/icons/category_bt.svg
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
height="16.375999"
|
||||
width="14.126321"
|
||||
version="1.1"
|
||||
id="svg4"
|
||||
sodipodi:docname="category_bt.svg"
|
||||
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs8" />
|
||||
<sodipodi:namedview
|
||||
id="namedview6"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
showgrid="false"
|
||||
inkscape:zoom="15.273506"
|
||||
inkscape:cx="7.071068"
|
||||
inkscape:cy="10.377447"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1051"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg4" />
|
||||
<path
|
||||
d="m 11.48968,7.688 q -0.354,0 -0.76,-0.219 -0.406,-0.219 -0.552,-0.51 L 9.5736798,6 q -0.167,-0.291 -0.167,-0.729 0,-0.437 0.167,-0.729 L 10.07368,3.667 q 0.146,-0.291 0.552,-0.531 0.406,-0.24 0.76,-0.24 h 0.875 q 0.375,0 0.792,0.219 0.417,0.219 0.562,0.552 l 0.355,0.854 q 0.145,0.334 0.156,0.761 0.01,0.427 -0.136,0.76 l -0.396,0.875 q -0.145,0.333 -0.552,0.552 -0.406,0.219 -0.781,0.219 z m -4.8120002,2.917 q -0.354,0 -0.761,-0.219 -0.406,-0.219 -0.573,-0.531 l -0.374,-0.917 q -0.146,-0.312 -0.146,-0.75 0,-0.438 0.146,-0.75 l 0.374,-0.917 q 0.167,-0.312 0.573,-0.531 0.407,-0.219 0.761,-0.219 h 0.771 q 0.375,0 0.781,0.219 0.406,0.219 0.552,0.531 l 0.396,0.917 q 0.146,0.312 0.146,0.75 0,0.438 -0.146,0.75 l -0.396,0.917 q -0.146,0.312 -0.552,0.531 -0.406,0.219 -0.781,0.219 z m 0,-5.813 q -0.354,0 -0.761,-0.219 -0.406,-0.218 -0.573,-0.531 l -0.374,-0.875 q -0.146,-0.333 -0.146,-0.771 0,-0.437 0.146,-0.75 l 0.374,-0.896 q 0.167,-0.312 0.573,-0.531 0.407,-0.219 0.761,-0.219 h 0.771 q 0.375,0 0.781,0.219 0.406,0.219 0.552,0.531 l 0.396,0.896 q 0.146,0.313 0.146,0.75 0,0.438 -0.146,0.771 l -0.396,0.875 q -0.146,0.313 -0.552,0.531 -0.406,0.219 -0.781,0.219 z m -4.813,2.896 q -0.375,0 -0.771,-0.219 Q 0.69867978,7.25 0.53167978,6.917 l -0.375,-0.875 Q -0.01032022,5.709 6.7977528e-4,5.282 0.01067978,4.855 0.17767978,4.521 l 0.333,-0.854 q 0.167,-0.333 0.57300002,-0.552 0.406,-0.219 0.781,-0.219 h 0.771 q 0.375,0 0.781,0.219 0.407,0.219 0.573,0.552 l 0.334,0.854 q 0.166,0.334 0.177,0.761 0.01,0.427 -0.157,0.76 l -0.374,0.875 q -0.167,0.333 -0.563,0.552 -0.396,0.219 -0.771,0.219 z m 0,5.75 q -0.375,0 -0.781,-0.219 Q 0.67767978,13 0.51067978,12.667 l -0.333,-0.854 Q 0.01067978,11.48 6.7977528e-4,11.053 -0.01032022,10.626 0.15667978,10.292 l 0.375,-0.875 q 0.167,-0.333 0.56200002,-0.552 0.396,-0.219 0.771,-0.219 h 0.771 q 0.375,0 0.771,0.219 0.396,0.219 0.563,0.552 l 0.374,0.875 q 0.167,0.334 0.157,0.761 -0.011,0.427 -0.177,0.76 l -0.334,0.854 q -0.166,0.333 -0.573,0.552 -0.406,0.219 -0.781,0.219 z m 4.855,2.938 q -0.355,0 -0.761,-0.219 -0.406,-0.219 -0.594,-0.531 l -0.375,-0.896 q -0.166,-0.313 -0.166,-0.75 0,-0.438 0.146,-0.771 l 0.374,-0.875 q 0.167,-0.313 0.573,-0.531 0.407,-0.219 0.761,-0.219 h 0.771 q 0.375,0 0.781,0.219 0.406,0.218 0.552,0.531 l 0.396,0.875 q 0.146,0.333 0.146,0.771 0,0.437 -0.146,0.75 l -0.396,0.896 q -0.146,0.312 -0.552,0.531 -0.406,0.219 -0.781,0.219 z M 11.48968,13.438 q -0.354,0 -0.77,-0.219 -0.417,-0.219 -0.563,-0.51 L 9.5936798,11.771 q -0.166,-0.291 -0.177,-0.718 -0.01,-0.427 0.157,-0.719 L 10.17768,9.376 q 0.146,-0.292 0.552,-0.511 0.406,-0.219 0.76,-0.219 h 0.771 q 0.375,0 0.781,0.219 0.407,0.219 0.552,0.552 l 0.396,0.875 q 0.146,0.334 0.136,0.761 -0.011,0.427 -0.156,0.76 l -0.355,0.854 q -0.145,0.333 -0.562,0.552 -0.417,0.219 -0.792,0.219 z"
|
||||
id="path2"
|
||||
style="fill:#c689ff;fill-opacity:1" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
37
Sprout/addons/beehave/icons/category_bt.svg.import
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dnm43qa88fdpo"
|
||||
path="res://.godot/imported/category_bt.svg-8537bebd1c5f62dca3d7ee7f17efeed4.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/beehave/icons/category_bt.svg"
|
||||
dest_files=["res://.godot/imported/category_bt.svg-8537bebd1c5f62dca3d7ee7f17efeed4.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/bptc_ldr=0
|
||||
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
|
||||
38
Sprout/addons/beehave/icons/category_composite.svg
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
height="16.375999"
|
||||
width="14.126321"
|
||||
version="1.1"
|
||||
id="svg4"
|
||||
sodipodi:docname="category_composite.svg"
|
||||
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs8" />
|
||||
<sodipodi:namedview
|
||||
id="namedview6"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
showgrid="false"
|
||||
inkscape:zoom="15.273506"
|
||||
inkscape:cx="7.071068"
|
||||
inkscape:cy="10.377447"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1051"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg4" />
|
||||
<path
|
||||
d="m 11.48968,7.688 q -0.354,0 -0.76,-0.219 -0.406,-0.219 -0.552,-0.51 L 9.5736798,6 q -0.167,-0.291 -0.167,-0.729 0,-0.437 0.167,-0.729 L 10.07368,3.667 q 0.146,-0.291 0.552,-0.531 0.406,-0.24 0.76,-0.24 h 0.875 q 0.375,0 0.792,0.219 0.417,0.219 0.562,0.552 l 0.355,0.854 q 0.145,0.334 0.156,0.761 0.01,0.427 -0.136,0.76 l -0.396,0.875 q -0.145,0.333 -0.552,0.552 -0.406,0.219 -0.781,0.219 z m -4.8120002,2.917 q -0.354,0 -0.761,-0.219 -0.406,-0.219 -0.573,-0.531 l -0.374,-0.917 q -0.146,-0.312 -0.146,-0.75 0,-0.438 0.146,-0.75 l 0.374,-0.917 q 0.167,-0.312 0.573,-0.531 0.407,-0.219 0.761,-0.219 h 0.771 q 0.375,0 0.781,0.219 0.406,0.219 0.552,0.531 l 0.396,0.917 q 0.146,0.312 0.146,0.75 0,0.438 -0.146,0.75 l -0.396,0.917 q -0.146,0.312 -0.552,0.531 -0.406,0.219 -0.781,0.219 z m 0,-5.813 q -0.354,0 -0.761,-0.219 -0.406,-0.218 -0.573,-0.531 l -0.374,-0.875 q -0.146,-0.333 -0.146,-0.771 0,-0.437 0.146,-0.75 l 0.374,-0.896 q 0.167,-0.312 0.573,-0.531 0.407,-0.219 0.761,-0.219 h 0.771 q 0.375,0 0.781,0.219 0.406,0.219 0.552,0.531 l 0.396,0.896 q 0.146,0.313 0.146,0.75 0,0.438 -0.146,0.771 l -0.396,0.875 q -0.146,0.313 -0.552,0.531 -0.406,0.219 -0.781,0.219 z m -4.813,2.896 q -0.375,0 -0.771,-0.219 Q 0.69867978,7.25 0.53167978,6.917 l -0.375,-0.875 Q -0.01032022,5.709 6.7977528e-4,5.282 0.01067978,4.855 0.17767978,4.521 l 0.333,-0.854 q 0.167,-0.333 0.57300002,-0.552 0.406,-0.219 0.781,-0.219 h 0.771 q 0.375,0 0.781,0.219 0.407,0.219 0.573,0.552 l 0.334,0.854 q 0.166,0.334 0.177,0.761 0.01,0.427 -0.157,0.76 l -0.374,0.875 q -0.167,0.333 -0.563,0.552 -0.396,0.219 -0.771,0.219 z m 0,5.75 q -0.375,0 -0.781,-0.219 Q 0.67767978,13 0.51067978,12.667 l -0.333,-0.854 Q 0.01067978,11.48 6.7977528e-4,11.053 -0.01032022,10.626 0.15667978,10.292 l 0.375,-0.875 q 0.167,-0.333 0.56200002,-0.552 0.396,-0.219 0.771,-0.219 h 0.771 q 0.375,0 0.771,0.219 0.396,0.219 0.563,0.552 l 0.374,0.875 q 0.167,0.334 0.157,0.761 -0.011,0.427 -0.177,0.76 l -0.334,0.854 q -0.166,0.333 -0.573,0.552 -0.406,0.219 -0.781,0.219 z m 4.855,2.938 q -0.355,0 -0.761,-0.219 -0.406,-0.219 -0.594,-0.531 l -0.375,-0.896 q -0.166,-0.313 -0.166,-0.75 0,-0.438 0.146,-0.771 l 0.374,-0.875 q 0.167,-0.313 0.573,-0.531 0.407,-0.219 0.761,-0.219 h 0.771 q 0.375,0 0.781,0.219 0.406,0.218 0.552,0.531 l 0.396,0.875 q 0.146,0.333 0.146,0.771 0,0.437 -0.146,0.75 l -0.396,0.896 q -0.146,0.312 -0.552,0.531 -0.406,0.219 -0.781,0.219 z M 11.48968,13.438 q -0.354,0 -0.77,-0.219 -0.417,-0.219 -0.563,-0.51 L 9.5936798,11.771 q -0.166,-0.291 -0.177,-0.718 -0.01,-0.427 0.157,-0.719 L 10.17768,9.376 q 0.146,-0.292 0.552,-0.511 0.406,-0.219 0.76,-0.219 h 0.771 q 0.375,0 0.781,0.219 0.407,0.219 0.552,0.552 l 0.396,0.875 q 0.146,0.334 0.136,0.761 -0.011,0.427 -0.156,0.76 l -0.355,0.854 q -0.145,0.333 -0.562,0.552 -0.417,0.219 -0.792,0.219 z"
|
||||
id="path2"
|
||||
style="fill:#40d29f;fill-opacity:1" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
37
Sprout/addons/beehave/icons/category_composite.svg.import
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dpxxgndj5jbe7"
|
||||
path="res://.godot/imported/category_composite.svg-43f66e63a7ccfa5ac8ec6da0583b3246.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/beehave/icons/category_composite.svg"
|
||||
dest_files=["res://.godot/imported/category_composite.svg-43f66e63a7ccfa5ac8ec6da0583b3246.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/bptc_ldr=0
|
||||
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
|
||||
38
Sprout/addons/beehave/icons/category_decorator.svg
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
height="16.375999"
|
||||
width="14.126321"
|
||||
version="1.1"
|
||||
id="svg4"
|
||||
sodipodi:docname="category_decorator.svg"
|
||||
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs8" />
|
||||
<sodipodi:namedview
|
||||
id="namedview6"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
showgrid="false"
|
||||
inkscape:zoom="15.273506"
|
||||
inkscape:cx="7.071068"
|
||||
inkscape:cy="10.377447"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1051"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg4" />
|
||||
<path
|
||||
d="m 11.48968,7.688 q -0.354,0 -0.76,-0.219 -0.406,-0.219 -0.552,-0.51 L 9.5736798,6 q -0.167,-0.291 -0.167,-0.729 0,-0.437 0.167,-0.729 L 10.07368,3.667 q 0.146,-0.291 0.552,-0.531 0.406,-0.24 0.76,-0.24 h 0.875 q 0.375,0 0.792,0.219 0.417,0.219 0.562,0.552 l 0.355,0.854 q 0.145,0.334 0.156,0.761 0.01,0.427 -0.136,0.76 l -0.396,0.875 q -0.145,0.333 -0.552,0.552 -0.406,0.219 -0.781,0.219 z m -4.8120002,2.917 q -0.354,0 -0.761,-0.219 -0.406,-0.219 -0.573,-0.531 l -0.374,-0.917 q -0.146,-0.312 -0.146,-0.75 0,-0.438 0.146,-0.75 l 0.374,-0.917 q 0.167,-0.312 0.573,-0.531 0.407,-0.219 0.761,-0.219 h 0.771 q 0.375,0 0.781,0.219 0.406,0.219 0.552,0.531 l 0.396,0.917 q 0.146,0.312 0.146,0.75 0,0.438 -0.146,0.75 l -0.396,0.917 q -0.146,0.312 -0.552,0.531 -0.406,0.219 -0.781,0.219 z m 0,-5.813 q -0.354,0 -0.761,-0.219 -0.406,-0.218 -0.573,-0.531 l -0.374,-0.875 q -0.146,-0.333 -0.146,-0.771 0,-0.437 0.146,-0.75 l 0.374,-0.896 q 0.167,-0.312 0.573,-0.531 0.407,-0.219 0.761,-0.219 h 0.771 q 0.375,0 0.781,0.219 0.406,0.219 0.552,0.531 l 0.396,0.896 q 0.146,0.313 0.146,0.75 0,0.438 -0.146,0.771 l -0.396,0.875 q -0.146,0.313 -0.552,0.531 -0.406,0.219 -0.781,0.219 z m -4.813,2.896 q -0.375,0 -0.771,-0.219 Q 0.69867978,7.25 0.53167978,6.917 l -0.375,-0.875 Q -0.01032022,5.709 6.7977528e-4,5.282 0.01067978,4.855 0.17767978,4.521 l 0.333,-0.854 q 0.167,-0.333 0.57300002,-0.552 0.406,-0.219 0.781,-0.219 h 0.771 q 0.375,0 0.781,0.219 0.407,0.219 0.573,0.552 l 0.334,0.854 q 0.166,0.334 0.177,0.761 0.01,0.427 -0.157,0.76 l -0.374,0.875 q -0.167,0.333 -0.563,0.552 -0.396,0.219 -0.771,0.219 z m 0,5.75 q -0.375,0 -0.781,-0.219 Q 0.67767978,13 0.51067978,12.667 l -0.333,-0.854 Q 0.01067978,11.48 6.7977528e-4,11.053 -0.01032022,10.626 0.15667978,10.292 l 0.375,-0.875 q 0.167,-0.333 0.56200002,-0.552 0.396,-0.219 0.771,-0.219 h 0.771 q 0.375,0 0.771,0.219 0.396,0.219 0.563,0.552 l 0.374,0.875 q 0.167,0.334 0.157,0.761 -0.011,0.427 -0.177,0.76 l -0.334,0.854 q -0.166,0.333 -0.573,0.552 -0.406,0.219 -0.781,0.219 z m 4.855,2.938 q -0.355,0 -0.761,-0.219 -0.406,-0.219 -0.594,-0.531 l -0.375,-0.896 q -0.166,-0.313 -0.166,-0.75 0,-0.438 0.146,-0.771 l 0.374,-0.875 q 0.167,-0.313 0.573,-0.531 0.407,-0.219 0.761,-0.219 h 0.771 q 0.375,0 0.781,0.219 0.406,0.218 0.552,0.531 l 0.396,0.875 q 0.146,0.333 0.146,0.771 0,0.437 -0.146,0.75 l -0.396,0.896 q -0.146,0.312 -0.552,0.531 -0.406,0.219 -0.781,0.219 z M 11.48968,13.438 q -0.354,0 -0.77,-0.219 -0.417,-0.219 -0.563,-0.51 L 9.5936798,11.771 q -0.166,-0.291 -0.177,-0.718 -0.01,-0.427 0.157,-0.719 L 10.17768,9.376 q 0.146,-0.292 0.552,-0.511 0.406,-0.219 0.76,-0.219 h 0.771 q 0.375,0 0.781,0.219 0.407,0.219 0.552,0.552 l 0.396,0.875 q 0.146,0.334 0.136,0.761 -0.011,0.427 -0.156,0.76 l -0.355,0.854 q -0.145,0.333 -0.562,0.552 -0.417,0.219 -0.792,0.219 z"
|
||||
id="path2"
|
||||
style="fill:#46c0e1;fill-opacity:1" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
37
Sprout/addons/beehave/icons/category_decorator.svg.import
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://b3anr3wkwja3y"
|
||||
path="res://.godot/imported/category_decorator.svg-79d598d6456f32724156248e09d6eaf3.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/beehave/icons/category_decorator.svg"
|
||||
dest_files=["res://.godot/imported/category_decorator.svg-79d598d6456f32724156248e09d6eaf3.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/bptc_ldr=0
|
||||
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
|
||||
38
Sprout/addons/beehave/icons/category_leaf.svg
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
height="16.375999"
|
||||
width="14.126321"
|
||||
version="1.1"
|
||||
id="svg4"
|
||||
sodipodi:docname="category_leaf.svg"
|
||||
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs8" />
|
||||
<sodipodi:namedview
|
||||
id="namedview6"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
showgrid="false"
|
||||
inkscape:zoom="15.273506"
|
||||
inkscape:cx="7.071068"
|
||||
inkscape:cy="10.377447"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1051"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg4" />
|
||||
<path
|
||||
d="m 11.48968,7.688 q -0.354,0 -0.76,-0.219 -0.406,-0.219 -0.552,-0.51 L 9.5736798,6 q -0.167,-0.291 -0.167,-0.729 0,-0.437 0.167,-0.729 L 10.07368,3.667 q 0.146,-0.291 0.552,-0.531 0.406,-0.24 0.76,-0.24 h 0.875 q 0.375,0 0.792,0.219 0.417,0.219 0.562,0.552 l 0.355,0.854 q 0.145,0.334 0.156,0.761 0.01,0.427 -0.136,0.76 l -0.396,0.875 q -0.145,0.333 -0.552,0.552 -0.406,0.219 -0.781,0.219 z m -4.8120002,2.917 q -0.354,0 -0.761,-0.219 -0.406,-0.219 -0.573,-0.531 l -0.374,-0.917 q -0.146,-0.312 -0.146,-0.75 0,-0.438 0.146,-0.75 l 0.374,-0.917 q 0.167,-0.312 0.573,-0.531 0.407,-0.219 0.761,-0.219 h 0.771 q 0.375,0 0.781,0.219 0.406,0.219 0.552,0.531 l 0.396,0.917 q 0.146,0.312 0.146,0.75 0,0.438 -0.146,0.75 l -0.396,0.917 q -0.146,0.312 -0.552,0.531 -0.406,0.219 -0.781,0.219 z m 0,-5.813 q -0.354,0 -0.761,-0.219 -0.406,-0.218 -0.573,-0.531 l -0.374,-0.875 q -0.146,-0.333 -0.146,-0.771 0,-0.437 0.146,-0.75 l 0.374,-0.896 q 0.167,-0.312 0.573,-0.531 0.407,-0.219 0.761,-0.219 h 0.771 q 0.375,0 0.781,0.219 0.406,0.219 0.552,0.531 l 0.396,0.896 q 0.146,0.313 0.146,0.75 0,0.438 -0.146,0.771 l -0.396,0.875 q -0.146,0.313 -0.552,0.531 -0.406,0.219 -0.781,0.219 z m -4.813,2.896 q -0.375,0 -0.771,-0.219 Q 0.69867978,7.25 0.53167978,6.917 l -0.375,-0.875 Q -0.01032022,5.709 6.7977528e-4,5.282 0.01067978,4.855 0.17767978,4.521 l 0.333,-0.854 q 0.167,-0.333 0.57300002,-0.552 0.406,-0.219 0.781,-0.219 h 0.771 q 0.375,0 0.781,0.219 0.407,0.219 0.573,0.552 l 0.334,0.854 q 0.166,0.334 0.177,0.761 0.01,0.427 -0.157,0.76 l -0.374,0.875 q -0.167,0.333 -0.563,0.552 -0.396,0.219 -0.771,0.219 z m 0,5.75 q -0.375,0 -0.781,-0.219 Q 0.67767978,13 0.51067978,12.667 l -0.333,-0.854 Q 0.01067978,11.48 6.7977528e-4,11.053 -0.01032022,10.626 0.15667978,10.292 l 0.375,-0.875 q 0.167,-0.333 0.56200002,-0.552 0.396,-0.219 0.771,-0.219 h 0.771 q 0.375,0 0.771,0.219 0.396,0.219 0.563,0.552 l 0.374,0.875 q 0.167,0.334 0.157,0.761 -0.011,0.427 -0.177,0.76 l -0.334,0.854 q -0.166,0.333 -0.573,0.552 -0.406,0.219 -0.781,0.219 z m 4.855,2.938 q -0.355,0 -0.761,-0.219 -0.406,-0.219 -0.594,-0.531 l -0.375,-0.896 q -0.166,-0.313 -0.166,-0.75 0,-0.438 0.146,-0.771 l 0.374,-0.875 q 0.167,-0.313 0.573,-0.531 0.407,-0.219 0.761,-0.219 h 0.771 q 0.375,0 0.781,0.219 0.406,0.218 0.552,0.531 l 0.396,0.875 q 0.146,0.333 0.146,0.771 0,0.437 -0.146,0.75 l -0.396,0.896 q -0.146,0.312 -0.552,0.531 -0.406,0.219 -0.781,0.219 z M 11.48968,13.438 q -0.354,0 -0.77,-0.219 -0.417,-0.219 -0.563,-0.51 L 9.5936798,11.771 q -0.166,-0.291 -0.177,-0.718 -0.01,-0.427 0.157,-0.719 L 10.17768,9.376 q 0.146,-0.292 0.552,-0.511 0.406,-0.219 0.76,-0.219 h 0.771 q 0.375,0 0.781,0.219 0.407,0.219 0.552,0.552 l 0.396,0.875 q 0.146,0.334 0.136,0.761 -0.011,0.427 -0.156,0.76 l -0.355,0.854 q -0.145,0.333 -0.562,0.552 -0.417,0.219 -0.792,0.219 z"
|
||||
id="path2"
|
||||
style="fill:#ffb649;fill-opacity:1" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
37
Sprout/addons/beehave/icons/category_leaf.svg.import
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://cgo0oieom3sqk"
|
||||
path="res://.godot/imported/category_leaf.svg-c740ecab6cfae632574ca5e39e46fd2e.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/beehave/icons/category_leaf.svg"
|
||||
dest_files=["res://.godot/imported/category_leaf.svg-c740ecab6cfae632574ca5e39e46fd2e.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/bptc_ldr=0
|
||||
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
|
||||
38
Sprout/addons/beehave/icons/condition.svg
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
height="18.084"
|
||||
width="18.084"
|
||||
version="1.1"
|
||||
id="svg4180"
|
||||
sodipodi:docname="condition.svg"
|
||||
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs4184" />
|
||||
<sodipodi:namedview
|
||||
id="namedview4182"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
showgrid="false"
|
||||
inkscape:zoom="7.6367532"
|
||||
inkscape:cx="-10.737547"
|
||||
inkscape:cy="24.290428"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1051"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg4180" />
|
||||
<path
|
||||
d="m 8.938,14.063 q 0.542,0 0.927,-0.386 0.385,-0.385 0.385,-0.927 0,-0.541 -0.385,-0.916 -0.385,-0.375 -0.927,-0.375 -0.542,0 -0.927,0.375 -0.386,0.375 -0.386,0.916 0,0.542 0.386,0.927 0.385,0.386 0.927,0.386 z M 9.042,5.938 q 0.396,0 0.646,0.229 0.25,0.229 0.25,0.542 0,0.229 -0.177,0.562 Q 9.584,7.604 9.292,7.834 8.646,8.417 8.323,8.927 8,9.438 8,9.917 q 0,0.375 0.261,0.635 0.26,0.261 0.656,0.261 0.396,0 0.698,-0.24 0.302,-0.239 0.469,-0.698 0.104,-0.25 0.291,-0.51 0.188,-0.261 0.542,-0.615 0.604,-0.562 0.833,-1.073 0.23,-0.51 0.23,-1.135 0,-1.167 -0.761,-1.865 Q 10.459,3.98 9.188,3.98 8.25,3.98 7.552,4.365 6.854,4.75 6.292,5.48 6.084,5.75 6.198,6.125 6.313,6.5 6.584,6.709 6.938,6.98 7.365,6.886 7.792,6.792 8.104,6.417 8.313,6.209 8.563,6.073 8.813,5.938 9.042,5.938 Z m 0,12.146 q -1.875,0 -3.531,-0.709 Q 3.854,16.667 2.636,15.448 1.417,14.23 0.709,12.584 0,10.938 0,9.042 0,7.146 0.709,5.5 1.417,3.854 2.636,2.636 3.854,1.417 5.511,0.709 7.167,0 9.042,0 q 1.896,0 3.542,0.709 1.646,0.708 2.864,1.927 1.219,1.218 1.927,2.864 0.709,1.646 0.709,3.542 0,1.896 -0.709,3.542 -0.708,1.646 -1.927,2.864 -1.218,1.219 -2.864,1.927 -1.646,0.709 -3.542,0.709 z"
|
||||
id="path4178"
|
||||
style="fill:#ffb649;fill-opacity:1" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
37
Sprout/addons/beehave/icons/condition.svg.import
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dn4rf1wddvr3w"
|
||||
path="res://.godot/imported/condition.svg-57892684b10a64086f68c09c388b17e5.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/beehave/icons/condition.svg"
|
||||
dest_files=["res://.godot/imported/condition.svg-57892684b10a64086f68c09c388b17e5.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/bptc_ldr=0
|
||||
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
|
||||
38
Sprout/addons/beehave/icons/failer.svg
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
height="17.666"
|
||||
width="17.666"
|
||||
version="1.1"
|
||||
id="svg6751"
|
||||
sodipodi:docname="failer.svg"
|
||||
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs6755" />
|
||||
<sodipodi:namedview
|
||||
id="namedview6753"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
showgrid="false"
|
||||
inkscape:zoom="30.547013"
|
||||
inkscape:cx="12.308896"
|
||||
inkscape:cy="5.3851419"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1051"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg6751" />
|
||||
<path
|
||||
d="m 8.854,9.666 q 0.5,0 0.896,-0.395 0.395,-0.396 0.395,-0.917 v -2.75 q 0,-0.5 -0.395,-0.886 Q 9.354,4.333 8.854,4.333 8.333,4.333 7.937,4.729 7.541,5.125 7.541,5.625 v 2.75 q 0,0.52 0.396,0.906 0.396,0.385 0.917,0.385 z m -0.021,3.959 q 0.583,0 1.01,-0.407 0.428,-0.406 0.428,-1.031 0,-0.583 -0.428,-1.021 -0.427,-0.437 -1.01,-0.437 -0.583,0 -1.01,0.437 -0.428,0.438 -0.428,1.021 0,0.625 0.428,1.031 0.427,0.407 1.01,0.407 z m 0,4.041 q -1.854,0 -3.458,-0.687 Q 3.771,16.291 2.573,15.093 1.375,13.895 0.687,12.291 0,10.687 0,8.833 0,6.958 0.687,5.364 1.375,3.771 2.573,2.573 3.771,1.375 5.375,0.687 6.979,0 8.833,0 q 1.875,0 3.469,0.687 1.593,0.688 2.791,1.886 1.198,1.198 1.886,2.791 0.687,1.594 0.687,3.469 0,1.854 -0.687,3.458 -0.688,1.604 -1.886,2.802 -1.198,1.198 -2.791,1.886 -1.594,0.687 -3.469,0.687 z"
|
||||
id="path6749"
|
||||
style="fill:#46c0e1;fill-opacity:1" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
37
Sprout/addons/beehave/icons/failer.svg.import
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://d4ddhrw2xriwo"
|
||||
path="res://.godot/imported/failer.svg-9a62b840e1eacc0437e7a67b14a302e4.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/beehave/icons/failer.svg"
|
||||
dest_files=["res://.godot/imported/failer.svg-9a62b840e1eacc0437e7a67b14a302e4.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/bptc_ldr=0
|
||||
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
|
||||
38
Sprout/addons/beehave/icons/inverter.svg
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
height="13.907205"
|
||||
width="18.437"
|
||||
version="1.1"
|
||||
id="svg68"
|
||||
sodipodi:docname="inverter.svg"
|
||||
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs72" />
|
||||
<sodipodi:namedview
|
||||
id="namedview70"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
showgrid="false"
|
||||
inkscape:zoom="29.521708"
|
||||
inkscape:cx="6.3343218"
|
||||
inkscape:cy="7.8078138"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1051"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg68" />
|
||||
<path
|
||||
d="m 12.937,8.48 q -0.458,-0.459 -0.458,-1.032 0,-0.572 0.458,-1.031 L 13.416,5.938 H 9.812 Q 9.229,5.938 8.791,5.5 8.354,5.063 8.354,4.48 8.354,3.876 8.791,3.448 9.229,3.021 9.812,3.021 h 3.604 l -0.5,-0.5 Q 12.458,2.063 12.468,1.49 12.479,0.917 12.937,0.48 13.396,0 13.979,0 14.562,0 15,0.459 l 2.979,2.979 q 0.229,0.229 0.344,0.5 0.114,0.271 0.114,0.521 0,0.271 -0.114,0.541 -0.115,0.271 -0.344,0.5 l -3,3 Q 14.52,8.959 13.958,8.948 13.396,8.938 12.937,8.48 Z M 3.416,13.438 0.437,10.459 Q 0.229,10.25 0.114,9.98 0,9.709 0,9.438 0,9.167 0.114,8.896 0.229,8.626 0.437,8.396 l 3.021,-3 Q 3.916,4.938 4.468,4.948 5.02,4.959 5.479,5.417 5.937,5.876 5.937,6.459 5.937,7.042 5.479,7.48 L 5,7.959 h 3.604 q 0.583,0 1.021,0.437 0.437,0.438 0.437,1.021 0,0.604 -0.437,1.031 -0.438,0.428 -1.021,0.428 H 5 l 0.5,0.52 q 0.458,0.459 0.448,1.021 -0.011,0.563 -0.469,1.021 -0.459,0.458 -1.021,0.469 -0.562,0.01 -1.042,-0.469 z"
|
||||
id="path66"
|
||||
style="fill:#46c0e1;fill-opacity:1" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2 KiB |
37
Sprout/addons/beehave/icons/inverter.svg.import
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://biyjvaibehsld"
|
||||
path="res://.godot/imported/inverter.svg-1f1b976d95de42c4ad99a92fa9a6c5d0.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/beehave/icons/inverter.svg"
|
||||
dest_files=["res://.godot/imported/inverter.svg-1f1b976d95de42c4ad99a92fa9a6c5d0.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/bptc_ldr=0
|
||||
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
|
||||
38
Sprout/addons/beehave/icons/limiter.svg
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
height="14.75"
|
||||
width="14.916"
|
||||
version="1.1"
|
||||
id="svg3323"
|
||||
sodipodi:docname="repeater.svg"
|
||||
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs3327" />
|
||||
<sodipodi:namedview
|
||||
id="namedview3325"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
showgrid="false"
|
||||
inkscape:zoom="7.6367532"
|
||||
inkscape:cx="-37.057633"
|
||||
inkscape:cy="27.56407"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1051"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg3323" />
|
||||
<path
|
||||
d="M 7.375,14.75 Q 4.312,14.75 2.156,12.594 0,10.437 0,7.375 0,4.313 2.156,2.156 4.312,0 7.375,0 8.979,0 10.385,0.646 11.791,1.292 12.833,2.5 V 1.042 q 0,-0.417 0.313,-0.729 Q 13.458,0 13.875,0 q 0.416,0 0.729,0.313 0.312,0.312 0.312,0.729 v 4.583 q 0,0.542 -0.406,0.948 -0.406,0.406 -0.948,0.406 H 8.958 Q 8.541,6.979 8.229,6.677 7.916,6.375 7.916,5.958 7.916,5.542 8.229,5.229 8.541,4.917 8.958,4.917 h 2.354 Q 10.646,3.917 9.614,3.313 8.583,2.708 7.375,2.708 q -1.938,0 -3.302,1.365 -1.365,1.364 -1.365,3.302 0,1.938 1.365,3.302 1.364,1.365 3.302,1.365 1.25,0 2.291,-0.615 1.042,-0.614 1.688,-1.677 0.208,-0.313 0.562,-0.521 0.354,-0.208 0.688,-0.208 0.854,0 1.239,0.573 0.386,0.573 0.011,1.26 -0.979,1.771 -2.698,2.833 -1.719,1.063 -3.781,1.063 z"
|
||||
id="path3321"
|
||||
style="fill:#46c0e1;fill-opacity:1" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
37
Sprout/addons/beehave/icons/limiter.svg.import
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://b23nxd75yrbwm"
|
||||
path="res://.godot/imported/limiter.svg-b4c7646605c46f53c5e403fe21d8f584.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/beehave/icons/limiter.svg"
|
||||
dest_files=["res://.godot/imported/limiter.svg-b4c7646605c46f53c5e403fe21d8f584.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/bptc_ldr=0
|
||||
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
|
||||
38
Sprout/addons/beehave/icons/selector.svg
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
height="13.906356"
|
||||
width="17.791"
|
||||
version="1.1"
|
||||
id="svg5037"
|
||||
sodipodi:docname="selector.svg"
|
||||
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs5041" />
|
||||
<sodipodi:namedview
|
||||
id="namedview5039"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
showgrid="false"
|
||||
inkscape:zoom="7.6367532"
|
||||
inkscape:cx="61.282588"
|
||||
inkscape:cy="33.849463"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1051"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg5037" />
|
||||
<path
|
||||
d="M 4.52,13.645 Q 4.25,13.374 4.25,13 4.25,12.624 4.5,12.354 L 5.208,11.666 Q 2.979,11.395 1.489,9.802 0,8.208 0,5.895 0,3.437 1.718,1.718 3.437,0 5.895,0 H 7.562 Q 7.916,0 8.187,0.27 8.458,0.541 8.458,0.895 8.458,1.27 8.187,1.541 7.916,1.812 7.562,1.812 H 5.895 Q 4.187,1.812 3,3 1.812,4.187 1.812,5.895 1.812,7.437 2.76,8.52 3.708,9.604 5.187,9.916 L 4.5,9.229 Q 4.229,8.937 4.239,8.583 4.25,8.229 4.52,7.958 4.77,7.687 5.145,7.687 q 0.375,0 0.646,0.271 l 2.167,2.187 q 0.125,0.125 0.208,0.302 0.084,0.177 0.084,0.365 0,0.167 -0.084,0.344 -0.083,0.177 -0.208,0.323 L 5.75,13.645 Q 5.479,13.916 5.124,13.906 4.77,13.895 4.52,13.645 Z M 12.041,5.958 Q 11.25,5.958 10.708,5.416 10.166,4.874 10.166,4.083 V 1.874 q 0,-0.77 0.542,-1.322 Q 11.25,0 12.041,0 h 3.875 q 0.771,0 1.323,0.552 0.552,0.552 0.552,1.322 v 2.209 q 0,0.791 -0.552,1.333 -0.552,0.542 -1.323,0.542 z m 0,-1.875 h 3.875 V 1.874 h -3.875 z m 0,9.541 q -0.791,0 -1.333,-0.552 Q 10.166,12.52 10.166,11.75 V 9.541 q 0,-0.791 0.542,-1.333 0.542,-0.542 1.333,-0.542 h 3.875 q 0.771,0 1.323,0.542 0.552,0.542 0.552,1.333 v 2.209 q 0,0.77 -0.552,1.322 -0.552,0.552 -1.323,0.552 z"
|
||||
id="path5035"
|
||||
style="fill:#40d29f;fill-opacity:1" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
37
Sprout/addons/beehave/icons/selector.svg.import
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://fxbfp8glftm"
|
||||
path="res://.godot/imported/selector.svg-78bccfc448bd1676b5a29bfde4b08e5b.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/beehave/icons/selector.svg"
|
||||
dest_files=["res://.godot/imported/selector.svg-78bccfc448bd1676b5a29bfde4b08e5b.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/bptc_ldr=0
|
||||
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
|
||||
35
Sprout/addons/beehave/icons/selector_random.svg
Normal file
|
After Width: | Height: | Size: 7.3 KiB |
37
Sprout/addons/beehave/icons/selector_random.svg.import
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://ca3k3maygmx7r"
|
||||
path="res://.godot/imported/selector_random.svg-d52fea1352c24483ecd9dc8609cf00f3.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/beehave/icons/selector_random.svg"
|
||||
dest_files=["res://.godot/imported/selector_random.svg-d52fea1352c24483ecd9dc8609cf00f3.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/bptc_ldr=0
|
||||
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
|
||||
45
Sprout/addons/beehave/icons/selector_reactive.svg
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
height="14.543364"
|
||||
width="17.791"
|
||||
version="1.1"
|
||||
id="svg5037"
|
||||
sodipodi:docname="selector_reactive.svg"
|
||||
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
|
||||
xml:space="preserve"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs5041" /><sodipodi:namedview
|
||||
id="namedview5039"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
showgrid="false"
|
||||
inkscape:zoom="15.273506"
|
||||
inkscape:cx="18.59429"
|
||||
inkscape:cy="12.014268"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1051"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg5037" /><path
|
||||
style="fill:#40d29f;fill-opacity:1;color:#000000;stroke-width:1.7354;-inkscape-stroke:none;paint-order:markers stroke fill"
|
||||
d="m 15.63424,18.518399 q -0.218955,0 -0.373746,-0.150649 -0.155154,-0.150651 -0.155154,-0.363746 v -1.161448 l -0.860595,0.49993 q -0.196481,0.103026 -0.400572,0.05152 -0.20373,-0.05152 -0.309582,-0.227915 l -0.181253,-0.308712 q -0.105496,-0.183813 -0.05655,-0.385972 0.04894,-0.202159 0.237805,-0.312236 l 0.868569,-0.492527 -0.883432,-0.485112 q -0.188867,-0.110433 -0.241793,-0.312236 -0.05292,-0.202161 0.05292,-0.385974 l 0.196116,-0.301299 q 0.113465,-0.183814 0.321183,-0.238852 0.207716,-0.05539 0.388971,0.05504 l 0.868208,0.492523 v -1.161448 q 0,-0.213096 0.155154,-0.363747 0.154791,-0.150648 0.373746,-0.150648 h 0.347283 q 0.218955,0 0.373747,0.150648 0.155152,0.150651 0.155152,0.363747 v 1.161448 l 0.868209,-0.499931 q 0.188867,-0.103026 0.388973,-0.04763 0.200104,0.05504 0.313568,0.224034 l 0.211343,0.323526 q 0.113466,0.183813 0.05655,0.38562 -0.05655,0.20216 -0.253031,0.312589 l -0.87582,0.477704 0.86857,0.492521 q 0.181255,0.09561 0.245418,0.304826 0.06416,0.209569 -0.0493,0.385974 l -0.196119,0.316117 q -0.113464,0.176404 -0.313569,0.235323 -0.200105,0.05857 -0.388972,-0.05151 l -0.875822,-0.50734 v 1.161449 q 0,0.213096 -0.155152,0.363745 -0.154792,0.15065 -0.373747,0.15065 z"
|
||||
id="path5291"
|
||||
transform="matrix(1.2719165,0,0,1.2110461,-6.1455464,-7.8832739)"
|
||||
inkscape:label="asterisk" /><path
|
||||
style="fill:#40d29f;fill-opacity:1"
|
||||
d="M 12.041,5.958 Q 11.25,5.958 10.708,5.416 10.166,4.874 10.166,4.083 V 1.874 q 0,-0.77 0.542,-1.322 Q 11.25,0 12.041,0 h 3.875 q 0.771,0 1.323,0.552 0.552,0.552 0.552,1.322 v 2.209 q 0,0.791 -0.552,1.333 -0.552,0.542 -1.323,0.542 z m 0,-1.875 h 3.875 V 1.874 h -3.875 z"
|
||||
id="path923"
|
||||
inkscape:label="square" /><path
|
||||
style="fill:#40d29f;fill-opacity:1"
|
||||
d="M 4.52,13.645 Q 4.25,13.374 4.25,13 4.25,12.624 4.5,12.354 L 5.208,11.666 Q 2.979,11.395 1.489,9.802 0,8.208 0,5.895 0,3.437 1.718,1.718 3.437,0 5.895,0 H 7.562 Q 7.916,0 8.187,0.27 8.458,0.541 8.458,0.895 8.458,1.27 8.187,1.541 7.916,1.812 7.562,1.812 H 5.895 Q 4.187,1.812 3,3 1.812,4.187 1.812,5.895 1.812,7.437 2.76,8.52 3.708,9.604 5.187,9.916 L 4.5,9.229 Q 4.229,8.937 4.239,8.583 4.25,8.229 4.52,7.958 4.77,7.687 5.145,7.687 q 0.375,0 0.646,0.271 l 2.167,2.187 q 0.125,0.125 0.208,0.302 0.084,0.177 0.084,0.365 0,0.167 -0.084,0.344 -0.083,0.177 -0.208,0.323 L 5.75,13.645 Q 5.479,13.916 5.124,13.906 4.77,13.895 4.52,13.645 Z"
|
||||
id="path5035"
|
||||
inkscape:label="arrow" /></svg>
|
||||
|
After Width: | Height: | Size: 3.6 KiB |
37
Sprout/addons/beehave/icons/selector_reactive.svg.import
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://bwxolb55cox2v"
|
||||
path="res://.godot/imported/selector_reactive.svg-dd3b8fb8cd2ffe331605aaad1e021cc0.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/beehave/icons/selector_reactive.svg"
|
||||
dest_files=["res://.godot/imported/selector_reactive.svg-dd3b8fb8cd2ffe331605aaad1e021cc0.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/bptc_ldr=0
|
||||
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
|
||||
38
Sprout/addons/beehave/icons/sequence.svg
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
height="13.187"
|
||||
width="16.583"
|
||||
version="1.1"
|
||||
id="svg5894"
|
||||
sodipodi:docname="sequence.svg"
|
||||
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs5898" />
|
||||
<sodipodi:namedview
|
||||
id="namedview5896"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
showgrid="false"
|
||||
inkscape:zoom="43.2"
|
||||
inkscape:cx="13.449074"
|
||||
inkscape:cy="7.974537"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1051"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg5894" />
|
||||
<path
|
||||
d="M 1.709,8.313 Q 1,8.313 0.5,7.823 0,7.333 0,6.604 0,5.896 0.5,5.396 q 0.5,-0.5 1.209,-0.5 0.708,0 1.208,0.5 0.5,0.5 0.5,1.229 0,0.708 -0.5,1.198 -0.5,0.49 -1.208,0.49 z m 0,-4.896 Q 1,3.417 0.5,2.927 0,2.437 0,1.708 0,1 0.5,0.5 1,0 1.709,0 q 0.708,0 1.208,0.5 0.5,0.5 0.5,1.229 0,0.708 -0.5,1.198 -0.5,0.49 -1.208,0.49 z m 0,9.77 Q 1,13.187 0.5,12.698 0,12.208 0,11.479 q 0,-0.708 0.5,-1.208 0.5,-0.5 1.209,-0.5 0.708,0 1.208,0.5 0.5,0.5 0.5,1.229 0,0.708 -0.5,1.198 -0.5,0.489 -1.208,0.489 z m 4.604,-0.395 q -0.5,0 -0.896,-0.396 Q 5.021,12 5.021,11.479 q 0,-0.5 0.396,-0.896 0.396,-0.396 0.896,-0.396 h 8.979 q 0.5,0 0.896,0.396 0.395,0.396 0.395,0.896 0,0.521 -0.395,0.917 -0.396,0.396 -0.896,0.396 z m 0,-4.875 q -0.5,0 -0.896,-0.396 -0.396,-0.396 -0.396,-0.917 0,-0.5 0.396,-0.896 0.396,-0.395 0.896,-0.395 h 8.979 q 0.5,0 0.896,0.395 0.395,0.396 0.395,0.896 0,0.521 -0.395,0.917 -0.396,0.396 -0.896,0.396 z m 0,-4.896 q -0.5,0 -0.896,-0.396 -0.396,-0.396 -0.396,-0.917 0,-0.5 0.396,-0.895 0.396,-0.396 0.896,-0.396 h 8.979 q 0.5,0 0.896,0.396 0.395,0.395 0.395,0.895 0,0.521 -0.395,0.917 -0.396,0.396 -0.896,0.396 z"
|
||||
id="path5892"
|
||||
style="fill:#40d29f;fill-opacity:1" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
37
Sprout/addons/beehave/icons/sequence.svg.import
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dxub8qg1o201c"
|
||||
path="res://.godot/imported/sequence.svg-76e5600611900cc81e9ec286977b8c6a.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/beehave/icons/sequence.svg"
|
||||
dest_files=["res://.godot/imported/sequence.svg-76e5600611900cc81e9ec286977b8c6a.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/bptc_ldr=0
|
||||
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
|
||||
38
Sprout/addons/beehave/icons/sequence_random.svg
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
height="12.183594"
|
||||
width="16.582031"
|
||||
version="1.1"
|
||||
id="svg5894"
|
||||
sodipodi:docname="sequence_random.svg"
|
||||
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs5898" />
|
||||
<sodipodi:namedview
|
||||
id="namedview5896"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="true"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
showgrid="false"
|
||||
inkscape:zoom="32"
|
||||
inkscape:cx="-1.59375"
|
||||
inkscape:cy="7.828125"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1051"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg5894" />
|
||||
<path
|
||||
id="path414"
|
||||
style="color:#000000;fill:#40d29f;stroke-width:0.4;stroke-linecap:round;paint-order:fill markers stroke"
|
||||
d="M 1.7089844,0 C 1.2363186,0 0.83333267,0.16666733 0.5,0.5 0.16666733,0.83333267 0,1.2350322 0,1.7070312 c 0,0.4859992 0.16666733,0.8940371 0.5,1.2207032 0.33333267,0.3266659 0.7363186,0.4902343 1.2089844,0.4902344 0.471999,0 0.8756516,-0.1635685 1.2089844,-0.4902344 0.3333326,-0.3266661 0.4999999,-0.7272196 0.5,-1.1992188 0,-0.485999 -0.1666674,-0.89518293 -0.5,-1.2285156 C 2.5846361,0.16666733 2.1809834,0 1.7089844,0 Z M 6.3125,0.41796875 c -0.3333327,0 -0.6305318,0.13053177 -0.8945312,0.39453125 -0.2639996,0.2633328 -0.3964844,0.5611987 -0.3964844,0.8945312 0,0.3473327 0.1324848,0.6539694 0.3964844,0.9179688 C 5.6819682,2.8889994 5.9791673,3.0214844 6.3125,3.0214844 h 8.980469 C 15.626301,3.0214844 15.9235,2.8889994 16.1875,2.625 16.450833,2.3610006 16.582031,2.0543639 16.582031,1.7070312 16.582031,1.3736987 16.450833,1.0758328 16.1875,0.8125 15.9235,0.54850052 15.626301,0.41796875 15.292969,0.41796875 Z M 9.0878906,4.3007812 c -0.2828275,0 -0.5270087,0.1055895 -0.7324218,0.3164063 -0.2050874,0.2104687 -0.3066407,0.4597345 -0.3066407,0.75 v 5.7480465 c 0,0.290266 0.101563,0.543098 0.3066407,0.753907 0.2054131,0.210442 0.4495943,0.314453 0.7324218,0.314453 h 5.6015624 c 0.282829,0 0.527005,-0.103984 0.732422,-0.314453 0.20504,-0.210809 0.310547,-0.463641 0.310547,-0.753907 V 5.3671875 c 0,-0.2902655 -0.105468,-0.5395313 -0.310547,-0.75 -0.192576,-0.1976408 -0.419166,-0.3040153 -0.679687,-0.3164063 -0.01736,-8.263e-4 -0.03505,0 -0.05274,0 z M 1.9257812,5.3535156 C 1.6720837,5.3535062 1.4450691,5.4533683 1.2441406,5.6542969 1.043212,5.8547182 0.94335938,6.0822399 0.94335938,6.3359375 c 0,0.2643529 0.0998526,0.4982903 0.30078122,0.6992187 0.2009285,0.2009285 0.4279431,0.3007813 0.6816406,0.3007813 H 6.8886719 V 5.7070312 c 0,-0.1182132 0.017785,-0.2363441 0.048828,-0.3515624 z m 11.7246098,0.041016 c 0.275278,0 0.533582,0.1086393 0.724609,0.3046876 0.191017,0.1960469 0.294922,0.4582008 0.294922,0.7363281 0,0.2781252 -0.10358,0.53844 -0.294922,0.734375 -0.191911,0.1969508 -0.45114,0.3027343 -0.724609,0.3027343 -0.273472,0 -0.532817,-0.1054585 -0.72461,-0.3027343 -0.192232,-0.1968435 -0.291015,-0.4579656 -0.291015,-0.734375 0,-0.2764085 0.09911,-0.5393695 0.291015,-0.7363281 0.190906,-0.1963654 0.449327,-0.3046876 0.72461,-0.3046876 z M 11.888672,7.203125 c 0.0068,0 0.0244,-0.00128 0.05273,0 0.256852,0.011652 0.493747,0.1218832 0.671875,0.3046875 0.191019,0.1960465 0.296876,0.4562463 0.296875,0.734375 10e-7,0.2781245 -0.105853,0.5402803 -0.296875,0.7363281 C 12.42137,9.1754773 12.162139,9.28125 11.888672,9.28125 c -0.273461,0 -0.532699,-0.1057738 -0.72461,-0.3027344 -0.191923,-0.1969574 -0.291015,-0.4599183 -0.291015,-0.7363281 0,-0.2764094 0.0991,-0.5374165 0.291015,-0.734375 0.191024,-0.1960448 0.449335,-0.3046875 0.72461,-0.3046875 z m -1.75,1.8066406 c 0.0068,0 0.0244,6.732e-4 0.05273,0.00195 0.256848,0.011652 0.49375,0.1238283 0.671875,0.3066406 0.189491,0.1948428 0.294922,0.4530828 0.294922,0.7304688 0,0.278128 -0.103894,0.538327 -0.294922,0.734375 -0.191019,0.196026 -0.449319,0.306641 -0.724609,0.306641 -0.2750313,0 -0.5324,-0.111387 -0.7226564,-0.306641 C 9.2249894,10.587155 9.1210938,10.326955 9.1210938,10.048828 9.1210937,9.7714422 9.2265406,9.5132022 9.4160156,9.3183594 9.6062729,9.1231217 9.8636398,9.0097656 10.138672,9.0097656 Z m -8.216797,0.1875 c -0.2536975,-9.4e-6 -0.478759,0.0979 -0.6796875,0.2988282 -0.2009286,0.2004212 -0.30273437,0.4279427 -0.30273438,0.6816402 0,0.264353 0.10180578,0.498291 0.30273438,0.699219 0.2009285,0.200929 0.42599,0.300781 0.6796875,0.300781 H 6.8867188 V 9.5488281 c 0,-0.1182133 0.017785,-0.2363441 0.048828,-0.3515625 z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.7 KiB |
37
Sprout/addons/beehave/icons/sequence_random.svg.import
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://b6ub4t6q50iou"
|
||||
path="res://.godot/imported/sequence_random.svg-58cee9098c622ef87db941279206422a.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/beehave/icons/sequence_random.svg"
|
||||
dest_files=["res://.godot/imported/sequence_random.svg-58cee9098c622ef87db941279206422a.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/bptc_ldr=0
|
||||
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
|
||||
60
Sprout/addons/beehave/icons/sequence_reactive.svg
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
height="14.874624"
|
||||
width="16.583984"
|
||||
version="1.1"
|
||||
id="svg5894"
|
||||
sodipodi:docname="sequence_reactive.svg"
|
||||
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
|
||||
xml:space="preserve"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs5898"><linearGradient
|
||||
id="linearGradient3589"
|
||||
inkscape:swatch="solid"><stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3587" /></linearGradient><inkscape:path-effect
|
||||
effect="fill_between_many"
|
||||
method="originald"
|
||||
linkedpaths="#path5892,0,1|#path3531,0,1"
|
||||
id="path-effect3720"
|
||||
is_visible="true"
|
||||
lpeversion="0"
|
||||
join="true"
|
||||
close="true"
|
||||
autoreverse="true" /></defs><sodipodi:namedview
|
||||
id="namedview5896"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
showgrid="false"
|
||||
inkscape:zoom="21.599999"
|
||||
inkscape:cx="16.805556"
|
||||
inkscape:cy="4.3287039"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1051"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg5894" /><path
|
||||
d="M 0,0"
|
||||
id="path3722"
|
||||
class="UnoptimicedTransforms"
|
||||
inkscape:original-d="M 0,0"
|
||||
inkscape:path-effect="#path-effect3720"
|
||||
transform="translate(-1.7285156,-3.375)" /><path
|
||||
id="path5892"
|
||||
style="fill:#40d29f;fill-opacity:1"
|
||||
class="UnoptimicedTransforms"
|
||||
d="m 3.4375,3.375 c -0.4726662,0 -0.8756514,0.166667 -1.2089844,0.5 -0.333333,0.333333 -0.5,0.7350317 -0.5,1.2070312 0,0.4859996 0.166667,0.8940368 0.5,1.2207032 0.333333,0.3266663 0.7363182,0.4902344 1.2089844,0.4902344 0.4719995,-10e-8 0.8756514,-0.1635681 1.2089844,-0.4902344 0.333333,-0.3266664 0.5,-0.7272192 0.5,-1.1992188 0,-0.4859995 -0.166667,-0.8951826 -0.5,-1.2285156 C 4.3131514,3.541667 3.9094995,3.375 3.4375,3.375 Z M 8.0429688,3.7929688 C 7.7096358,3.7929687 7.4104841,3.9235003 7.1464844,4.1875 6.8824846,4.4508331 6.75,4.7486983 6.75,5.0820312 6.75,5.4293642 6.8824846,5.7360003 7.1464844,6 7.4104841,6.2639997 7.7096358,6.3964844 8.0429688,6.3964844 H 17.021484 C 17.354817,6.3964844 17.653969,6.2639997 17.917969,6 18.181302,5.7360003 18.3125,5.4293642 18.3125,5.0820312 18.3125,4.7486983 18.181302,4.4508331 17.917969,4.1875 17.653969,3.9235003 17.354817,3.7929688 17.021484,3.7929688 Z M 3.4375,8.2714844 c -0.4726662,0 -0.8756514,0.166667 -1.2089844,0.5 -0.333333,0.333333 -0.5,0.7350317 -0.5,1.2070312 0,0.4859994 0.166667,0.8920834 0.5,1.2187504 0.333333,0.326666 0.7363182,0.490234 1.2089844,0.490234 0.4719995,0 0.8756514,-0.163568 1.2089844,-0.490234 0.333333,-0.326667 0.5,-0.725266 0.5,-1.197266 0,-0.4859995 -0.166667,-0.8951826 -0.5,-1.2285156 -0.333333,-0.333333 -0.7369849,-0.5 -1.2089844,-0.5 z M 8.0429688,8.6875 c -0.333333,0 -0.6324847,0.1311982 -0.8964844,0.3945312 C 6.8824846,9.346031 6.75,9.6451826 6.75,9.9785156 c 0,0.3473334 0.1324846,0.6539694 0.3964844,0.9179684 0.2639997,0.264 0.5631514,0.396485 0.8964844,0.396485 H 12.582031 C 12.667811,11.029759 12.810428,10.78417 13,10.615234 c 0.314158,-0.280616 0.764135,-0.439453 1.171875,-0.439453 h 0.46875 c 0.407741,0 0.855765,0.158985 1.169922,0.439453 0.189414,0.168797 0.331535,0.414214 0.416015,0.677735 h 0.794922 c 0.333333,0 0.632485,-0.132485 0.896485,-0.396485 0.263333,-0.263999 0.394531,-0.570635 0.394531,-0.9179684 0,-0.333333 -0.131198,-0.6324846 -0.394531,-0.8964844 C 17.653969,8.8186982 17.354817,8.6875 17.021484,8.6875 Z M 3.4375,13.146484 c -0.4726662,0 -0.8756514,0.166667 -1.2089844,0.5 -0.333333,0.333333 -0.5,0.735032 -0.5,1.207032 0,0.485999 0.166667,0.892083 0.5,1.21875 0.333333,0.325999 0.7363182,0.490234 1.2089844,0.490234 0.4719995,0 0.8756514,-0.164235 1.2089844,-0.490234 0.333333,-0.326667 0.5,-0.725266 0.5,-1.197266 0,-0.486 -0.166667,-0.895183 -0.5,-1.228516 -0.333333,-0.333333 -0.7369849,-0.5 -1.2089844,-0.5 z M 8.0429688,13.5625 c -0.333333,0 -0.6324847,0.130532 -0.8964844,0.394531 C 6.8824846,14.221031 6.75,14.520183 6.75,14.853516 c 0,0.347333 0.1324846,0.653969 0.3964844,0.917968 0.2639997,0.264 0.5631514,0.396485 0.8964844,0.396485 H 10.03125 C 9.985538,15.92966 9.986027,15.678358 10.04492,15.455078 10.12589,15.148084 10.389523,14.941651 10.626951,14.732422 10.380403,14.52554 10.114609,14.310259 10.025389,13.998047 9.986266,13.860916 9.972079,13.712745 9.974607,13.5625 Z m 10.2500002,1.083984 c -0.03323,0.02743 -0.06681,0.05476 -0.09961,0.08203 0.03855,0.03356 0.07792,0.06815 0.117188,0.101562 -0.0012,-0.06277 -0.007,-0.123307 -0.01758,-0.183594 z"
|
||||
transform="translate(-1.7285156,-3.375)" /><path
|
||||
style="color:#000000;fill:#40d29f;stroke-width:2;paint-order:stroke markers fill;fill-opacity:1"
|
||||
d="m 12.442996,14.874619 q -0.295876,0 -0.505045,-0.186837 -0.209662,-0.186838 -0.209662,-0.451125 v -1.440446 l -1.16293,0.620023 q -0.265504,0.127774 -0.541296,0.06389 -0.2753016,-0.06389 -0.4183406,-0.282664 l -0.244932,-0.382867 q -0.142553,-0.227969 -0.07642,-0.47869 0.06613,-0.250722 0.321348,-0.387241 L 10.779424,11.33783 9.5856344,10.736185 q -0.255217,-0.136961 -0.326737,-0.38724 -0.07153,-0.250722 0.07153,-0.478691 l 0.265013,-0.373676 q 0.153328,-0.227967 0.4340186,-0.296228 0.28069,-0.0687 0.52562,0.06827 l 1.173216,0.610833 V 8.439007 q 0,-0.264287 0.209662,-0.451125 0.209169,-0.186837 0.505047,-0.186837 h 0.469287 q 0.295874,0 0.505047,0.186837 0.209659,0.186838 0.209659,0.451125 v 1.440446 l 1.173216,-0.620024 q 0.255218,-0.127774 0.525622,-0.05908 0.270404,0.06827 0.423731,0.277851 l 0.285589,0.401242 q 0.153327,0.227969 0.07642,0.478254 -0.07642,0.25072 -0.341923,0.387677 l -1.183505,0.592455 1.173707,0.610833 q 0.24493,0.118573 0.331635,0.378052 0.08671,0.259911 -0.06662,0.478692 l -0.265012,0.392052 q -0.153326,0.21878 -0.423729,0.291853 -0.270405,0.07263 -0.525621,-0.06389 l -1.183504,-0.629204 v 1.440446 q 0,0.264286 -0.20966,0.451125 -0.209172,0.186837 -0.505048,0.186837 z"
|
||||
id="path6912" /></svg>
|
||||
|
After Width: | Height: | Size: 6.2 KiB |
37
Sprout/addons/beehave/icons/sequence_reactive.svg.import
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://yqyy2jgimlgp"
|
||||
path="res://.godot/imported/sequence_reactive.svg-7d384ca290f7934adb9e17d9e7116b6c.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/beehave/icons/sequence_reactive.svg"
|
||||
dest_files=["res://.godot/imported/sequence_reactive.svg-7d384ca290f7934adb9e17d9e7116b6c.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/bptc_ldr=0
|
||||
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
|
||||
38
Sprout/addons/beehave/icons/succeeder.svg
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
height="17.666"
|
||||
width="17.666"
|
||||
version="1.1"
|
||||
id="svg7608"
|
||||
sodipodi:docname="succedeer.svg"
|
||||
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs7612" />
|
||||
<sodipodi:namedview
|
||||
id="namedview7610"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
showgrid="false"
|
||||
inkscape:zoom="10.8"
|
||||
inkscape:cx="-0.092592592"
|
||||
inkscape:cy="15.509259"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1051"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg7608" />
|
||||
<path
|
||||
d="M 7.666,9.479 6.479,8.271 Q 6.104,7.916 5.604,7.916 q -0.5,0 -0.875,0.375 -0.375,0.375 -0.375,0.865 0,0.489 0.375,0.865 l 2,2.02 q 0.416,0.396 0.927,0.396 0.51,0 0.906,-0.396 L 12.833,7.75 Q 13.166,7.416 13.052,6.958 12.937,6.5 12.833,6.041 12.458,5.666 11.968,5.666 q -0.489,0 -0.864,0.375 z m 1.167,8.187 q -1.854,0 -3.458,-0.687 Q 3.771,16.291 2.573,15.093 1.375,13.895 0.687,12.291 0,10.687 0,8.833 0,6.958 0.687,5.364 1.375,3.771 2.573,2.573 3.771,1.375 5.375,0.687 6.979,0 8.833,0 q 1.875,0 3.469,0.687 1.593,0.688 2.791,1.886 1.198,1.198 1.886,2.791 0.687,1.594 0.687,3.469 0,1.854 -0.687,3.458 -0.688,1.604 -1.886,2.802 -1.198,1.198 -2.791,1.886 -1.594,0.687 -3.469,0.687 z"
|
||||
id="path7606"
|
||||
style="fill:#46c0e1;fill-opacity:1" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
37
Sprout/addons/beehave/icons/succeeder.svg.import
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://bfwgqq5jr46jm"
|
||||
path="res://.godot/imported/succeeder.svg-e5cf6f6e04b9b862b82fd2cb479272aa.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/beehave/icons/succeeder.svg"
|
||||
dest_files=["res://.godot/imported/succeeder.svg-e5cf6f6e04b9b862b82fd2cb479272aa.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/bptc_ldr=0
|
||||
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
|
||||
38
Sprout/addons/beehave/icons/tree.svg
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
height="15.063"
|
||||
width="16.875999"
|
||||
version="1.1"
|
||||
id="svg12"
|
||||
sodipodi:docname="tree.svg"
|
||||
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs16" />
|
||||
<sodipodi:namedview
|
||||
id="namedview14"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
showgrid="false"
|
||||
inkscape:zoom="10.8"
|
||||
inkscape:cx="-26.481481"
|
||||
inkscape:cy="9.3981481"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1051"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg12" />
|
||||
<path
|
||||
d="m 12.709,15.063 q -0.771,0 -1.313,-0.521 -0.541,-0.521 -0.562,-1.271 V 12.604 H 9.376 q -0.792,0 -1.344,-0.552 Q 7.48,11.5 7.48,10.73 V 4.313 H 6.021 V 4.938 Q 6.021,5.709 5.469,6.23 4.917,6.75 4.146,6.75 H 1.876 Q 1.105,6.75 0.553,6.198 0,5.646 0,4.875 v -3 Q 0,1.104 0.553,0.552 1.105,0 1.876,0 h 2.27 q 0.771,0 1.323,0.521 0.552,0.521 0.552,1.271 v 0.646 h 4.813 V 1.792 q 0,-0.75 0.552,-1.271 Q 11.938,0 12.709,0 H 15 q 0.771,0 1.323,0.552 0.553,0.552 0.553,1.323 v 3 q 0,0.771 -0.553,1.323 Q 15.771,6.75 15,6.75 H 12.709 Q 11.938,6.75 11.386,6.23 10.834,5.709 10.834,4.938 V 4.313 H 9.376 v 6.417 h 1.458 v -0.626 q 0,-0.77 0.552,-1.291 0.552,-0.521 1.323,-0.521 H 15 q 0.771,0 1.323,0.552 0.553,0.552 0.553,1.323 v 3.021 q 0,0.771 -0.553,1.323 -0.552,0.552 -1.323,0.552 z"
|
||||
id="path10"
|
||||
style="fill:#c689ff;fill-opacity:1" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
37
Sprout/addons/beehave/icons/tree.svg.import
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dl3sb2gdybwgs"
|
||||
path="res://.godot/imported/tree.svg-c0b20ed88b2fe300c0296f7236049076.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/beehave/icons/tree.svg"
|
||||
dest_files=["res://.godot/imported/tree.svg-c0b20ed88b2fe300c0296f7236049076.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/bptc_ldr=0
|
||||
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
|
||||
14
Sprout/addons/beehave/nodes/beehave_node.gd
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
## A node in the behaviour tree. Every node must return `SUCCESS`, `FAILURE` or
|
||||
## `RUNNING` when ticked.
|
||||
@tool
|
||||
class_name BeehaveNode extends BeehaveTree
|
||||
|
||||
## Executes this node and returns a status code.
|
||||
## This method must be overwritten.
|
||||
func tick(actor: Node, blackboard: Blackboard) -> int:
|
||||
return SUCCESS
|
||||
|
||||
|
||||
## Called when this node needs to be interrupted before it can return FAILURE or SUCCESS.
|
||||
func interrupt(actor: Node, blackboard: Blackboard) -> void:
|
||||
pass
|
||||
93
Sprout/addons/beehave/nodes/beehave_root.gd
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
## Controls the flow of execution of the entire behaviour tree.
|
||||
@tool
|
||||
class_name BeehaveRoot extends BeehaveTree
|
||||
@icon("../icons/tree.svg")
|
||||
|
||||
|
||||
@export var enabled: bool = true:
|
||||
set(value):
|
||||
enabled = value
|
||||
set_physics_process(enabled)
|
||||
get:
|
||||
return enabled
|
||||
|
||||
@export_node_path var actor_node_path : NodePath
|
||||
|
||||
var actor : Node
|
||||
|
||||
@onready var blackboard: Blackboard = Blackboard.new()
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
if Engine.is_editor_hint():
|
||||
return
|
||||
|
||||
if self.get_child_count() != 1:
|
||||
push_error("Beehave error: Root %s should have one child (NodePath: %s)" % [self.name, self.get_path()])
|
||||
disable()
|
||||
return
|
||||
|
||||
actor = get_parent()
|
||||
if actor_node_path:
|
||||
actor = get_node(actor_node_path)
|
||||
|
||||
set_physics_process(enabled)
|
||||
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
if Engine.is_editor_hint():
|
||||
return
|
||||
|
||||
blackboard.set_value("delta", delta)
|
||||
var status = self.get_child(0).tick(actor, blackboard)
|
||||
|
||||
# Updates the current running action.
|
||||
var running_action = get_running_action() if status == RUNNING else null
|
||||
blackboard.set_value("running_action", running_action)
|
||||
|
||||
|
||||
func _get_configuration_warnings() -> PackedStringArray:
|
||||
var warnings: PackedStringArray = super._get_configuration_warnings()
|
||||
|
||||
if get_child_count() != 1:
|
||||
warnings.append("BeehaveRoot should have exactly one child node.")
|
||||
|
||||
return warnings
|
||||
|
||||
|
||||
func get_running_action() -> ActionLeaf:
|
||||
var node = get_child(0)
|
||||
while node != null:
|
||||
if node is Composite:
|
||||
node = (node as Composite).running_child
|
||||
elif node is ActionLeaf:
|
||||
return node
|
||||
|
||||
push_error("Beehave error: Could not find running action in tree root '%s'." % name)
|
||||
return null
|
||||
|
||||
|
||||
func get_last_condition() -> Variant:
|
||||
if blackboard.has_value("last_condition"):
|
||||
return blackboard.get_value("last_condition")
|
||||
return null
|
||||
|
||||
|
||||
func get_last_condition_status() -> String:
|
||||
if blackboard.has_value("last_condition_status"):
|
||||
var status = blackboard.get_value("last_condition_status")
|
||||
if status == SUCCESS:
|
||||
return "SUCCESS"
|
||||
elif status == FAILURE:
|
||||
return "FAILURE"
|
||||
else:
|
||||
return "RUNNING"
|
||||
return ""
|
||||
|
||||
|
||||
func enable() -> void:
|
||||
enabled = true
|
||||
|
||||
|
||||
func disable() -> void:
|
||||
enabled = false
|
||||
20
Sprout/addons/beehave/nodes/beehave_tree.gd
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
## Base class for all parts of the behaviour tree.
|
||||
@tool
|
||||
class_name BeehaveTree extends Node
|
||||
@icon("../icons/category_bt.svg")
|
||||
|
||||
|
||||
enum {
|
||||
SUCCESS,
|
||||
FAILURE,
|
||||
RUNNING
|
||||
}
|
||||
|
||||
|
||||
func _get_configuration_warnings() -> PackedStringArray:
|
||||
var warnings: PackedStringArray = []
|
||||
|
||||
if get_children().any(func(x): return not (x is BeehaveNode)):
|
||||
warnings.append("All children of this node should inherit from BeehaveNode class.")
|
||||
|
||||
return warnings
|
||||
30
Sprout/addons/beehave/nodes/composites/composite.gd
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
## A Composite node controls the flow of execution of its children in a specific manner.
|
||||
@tool
|
||||
class_name Composite extends BeehaveNode
|
||||
@icon("../../icons/category_composite.svg")
|
||||
|
||||
|
||||
var running_child: BeehaveNode = null
|
||||
|
||||
|
||||
func _ready():
|
||||
if Engine.is_editor_hint():
|
||||
return
|
||||
|
||||
if self.get_child_count() < 1:
|
||||
push_error("BehaviorTree Error: Composite %s should have at least one child (NodePath: %s)" % [self.name, self.get_path()])
|
||||
|
||||
|
||||
func _get_configuration_warnings() -> PackedStringArray:
|
||||
var warnings: PackedStringArray = super._get_configuration_warnings()
|
||||
|
||||
if get_children().filter(func(x): return x is BeehaveNode).size() < 2:
|
||||
warnings.append("Any composite node should have at least two children. Otherwise it is not useful.")
|
||||
|
||||
return warnings
|
||||
|
||||
|
||||
func interrupt(actor: Node, blackboard: Blackboard) -> void:
|
||||
if running_child != null:
|
||||
running_child.interrupt(actor, blackboard)
|
||||
running_child = null
|
||||
25
Sprout/addons/beehave/nodes/composites/selector.gd
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
## Selector nodes will attempt to execute each of its children until one of
|
||||
## them return `SUCCESS`. If all children return `FAILURE`, this node will also
|
||||
## return `FAILURE`. This node will attempt to process all its children every
|
||||
## single tick, even if one of them is currently `RUNNING` already.
|
||||
@tool
|
||||
class_name SelectorComposite extends Composite
|
||||
@icon("../../icons/selector.svg")
|
||||
|
||||
func tick(actor: Node, blackboard: Blackboard) -> int:
|
||||
for c in get_children():
|
||||
var response = c.tick(actor, blackboard)
|
||||
|
||||
if c is ConditionLeaf:
|
||||
blackboard.set_value("last_condition", c)
|
||||
blackboard.set_value("last_condition_status", response)
|
||||
|
||||
if response != FAILURE:
|
||||
if response == SUCCESS:
|
||||
# Interrupt any child that was RUNNING before.
|
||||
interrupt(actor, blackboard)
|
||||
else: # RUNNING
|
||||
running_child = c
|
||||
return response
|
||||
|
||||
return FAILURE
|
||||
54
Sprout/addons/beehave/nodes/composites/selector_random.gd
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
## This node will attempt to execute all of its children just like a
|
||||
## [code]SelectorStar[/code] would, with the exception that the children
|
||||
## will be executed in a random order.
|
||||
@tool
|
||||
class_name SelectorRandomComposite extends Composite
|
||||
@icon("../../icons/selector_random.svg")
|
||||
|
||||
|
||||
## A shuffled list of the children that will be executed in reverse order.
|
||||
var _children_bag: Array[Node] = []
|
||||
var c: Node
|
||||
|
||||
|
||||
func tick(actor: Node, blackboard: Blackboard) -> int:
|
||||
if _children_bag.is_empty():
|
||||
_reset()
|
||||
|
||||
# We need to traverse the array in reverse since we will be manipulating it.
|
||||
for i in _get_reversed_indexes():
|
||||
c = _children_bag[i]
|
||||
var response = c.tick(actor, blackboard)
|
||||
|
||||
if c is ConditionLeaf:
|
||||
blackboard.set_value("last_condition", c)
|
||||
blackboard.set_value("last_condition_status", response)
|
||||
|
||||
if response == RUNNING:
|
||||
running_child = c
|
||||
else:
|
||||
_children_bag.erase(c)
|
||||
|
||||
if response != FAILURE:
|
||||
if response == SUCCESS:
|
||||
_reset()
|
||||
return response
|
||||
|
||||
return FAILURE
|
||||
|
||||
|
||||
func interrupt(actor: Node, blackboard: Blackboard) -> void:
|
||||
_reset()
|
||||
super(actor, blackboard)
|
||||
|
||||
|
||||
func _get_reversed_indexes() -> Array[int]:
|
||||
var reversed = range(_children_bag.size())
|
||||
reversed.reverse()
|
||||
return reversed
|
||||
|
||||
|
||||
## Generates a new shuffled list of the children.
|
||||
func _reset() -> void:
|
||||
_children_bag = get_children().duplicate()
|
||||
_children_bag.shuffle()
|
||||
32
Sprout/addons/beehave/nodes/composites/selector_star.gd
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
## Selector Star nodes will attempt to execute each of its children until one of
|
||||
## them return `SUCCESS`. If all children return `FAILURE`, this node will also
|
||||
## return `FAILURE`. This node will skip all previous child nodes that were
|
||||
## executed prior, in case one of the children is currently in `RUNNING` state.
|
||||
@tool
|
||||
class_name SelectorStarComposite extends Composite
|
||||
@icon("../../icons/selector_reactive.svg")
|
||||
|
||||
var last_execution_index = 0
|
||||
|
||||
func tick(actor: Node, blackboard: Blackboard) -> int:
|
||||
for c in get_children():
|
||||
if c.get_index() < last_execution_index:
|
||||
continue
|
||||
|
||||
var response = c.tick(actor, blackboard)
|
||||
|
||||
if c is ConditionLeaf:
|
||||
blackboard.set_value("last_condition", c)
|
||||
blackboard.set_value("last_condition_status", response)
|
||||
|
||||
if response != FAILURE:
|
||||
if response == SUCCESS:
|
||||
last_execution_index = 0
|
||||
else: # RUNNING
|
||||
running_child = c
|
||||
return response
|
||||
else:
|
||||
last_execution_index += 1
|
||||
|
||||
last_execution_index = 0
|
||||
return FAILURE
|
||||
26
Sprout/addons/beehave/nodes/composites/sequence.gd
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
## Sequence nodes will attempt to execute all of its children and report
|
||||
## `SUCCESS` in case all of the children report a `SUCCESS` status code.
|
||||
## If at least one child reports a `FAILURE` status code, this node will also
|
||||
## return `FAILURE`. This node will attempt to process all its children every
|
||||
## single tick, even if one of them is currently `RUNNING` already.
|
||||
@tool
|
||||
class_name SequenceComposite extends Composite
|
||||
@icon("../../icons/sequence.svg")
|
||||
|
||||
func tick(actor: Node, blackboard: Blackboard) -> int:
|
||||
for c in get_children():
|
||||
var response = c.tick(actor, blackboard)
|
||||
|
||||
if c is ConditionLeaf:
|
||||
blackboard.set_value("last_condition", c)
|
||||
blackboard.set_value("last_condition_status", response)
|
||||
|
||||
if response != SUCCESS:
|
||||
if response == FAILURE:
|
||||
# Interrupt any child that was RUNNING before.
|
||||
interrupt(actor, blackboard)
|
||||
else: # RUNNING
|
||||
running_child = c
|
||||
return response
|
||||
|
||||
return SUCCESS
|
||||
60
Sprout/addons/beehave/nodes/composites/sequence_random.gd
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
## This node will attempt to execute all of its children just like a
|
||||
## [code]SequenceStar[/code] would, with the exception that the children
|
||||
## will be executed in a random order.
|
||||
@tool
|
||||
class_name SequenceRandomComposite extends Composite
|
||||
@icon("../../icons/sequence_random.svg")
|
||||
|
||||
|
||||
## Whether the sequence should start where it left off after a previous failure.
|
||||
@export var resume_on_failure: bool = false
|
||||
## Whether the sequence should start where it left off after a previous interruption.
|
||||
@export var resume_on_interrupt: bool = false
|
||||
|
||||
## A shuffled list of the children that will be executed in reverse order.
|
||||
var _children_bag: Array[Node] = []
|
||||
var c: Node
|
||||
|
||||
|
||||
func tick(actor: Node, blackboard: Blackboard) -> int:
|
||||
if _children_bag.is_empty():
|
||||
_reset()
|
||||
|
||||
# We need to traverse the array in reverse since we will be manipulating it.
|
||||
for i in _get_reversed_indexes():
|
||||
c = _children_bag[i]
|
||||
var response = c.tick(actor, blackboard)
|
||||
|
||||
if c is ConditionLeaf:
|
||||
blackboard.set_value("last_condition", c)
|
||||
blackboard.set_value("last_condition_status", response)
|
||||
|
||||
if response == RUNNING:
|
||||
running_child = c
|
||||
else:
|
||||
_children_bag.erase(c)
|
||||
|
||||
if response != SUCCESS:
|
||||
if not resume_on_failure and response == FAILURE:
|
||||
_reset()
|
||||
return response
|
||||
|
||||
return SUCCESS
|
||||
|
||||
|
||||
func interrupt(actor: Node, blackboard: Blackboard) -> void:
|
||||
if not resume_on_interrupt:
|
||||
_reset()
|
||||
super(actor, blackboard)
|
||||
|
||||
|
||||
func _get_reversed_indexes() -> Array[int]:
|
||||
var reversed = range(_children_bag.size())
|
||||
reversed.reverse()
|
||||
return reversed
|
||||
|
||||
|
||||
## Generates a new shuffled list of the children.
|
||||
func _reset() -> void:
|
||||
_children_bag = get_children().duplicate()
|
||||
_children_bag.shuffle()
|
||||
37
Sprout/addons/beehave/nodes/composites/sequence_star.gd
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
## Sequence Star nodes will attempt to execute all of its children and report
|
||||
## `SUCCESS` in case all of the children report a `SUCCESS` status code.
|
||||
## If at least one child reports a `FAILURE` status code, this node will also
|
||||
## return `FAILURE`. This node will skip all previous child nodes that succeeded
|
||||
## prior, in case one of the children is currently in `RUNNING` state
|
||||
@tool
|
||||
class_name SequenceStarComposite extends Composite
|
||||
@icon("../../icons/sequence_reactive.svg")
|
||||
|
||||
var successful_index = 0
|
||||
|
||||
func tick(actor: Node, blackboard: Blackboard) -> int:
|
||||
for c in get_children():
|
||||
if c.get_index() < successful_index:
|
||||
continue
|
||||
|
||||
var response = c.tick(actor, blackboard)
|
||||
|
||||
if c is ConditionLeaf:
|
||||
blackboard.set_value("last_condition", c)
|
||||
blackboard.set_value("last_condition_status", response)
|
||||
|
||||
if response != SUCCESS:
|
||||
if response == FAILURE:
|
||||
successful_index = 0
|
||||
else: # RUNNING
|
||||
running_child = c
|
||||
return response
|
||||
else:
|
||||
successful_index += 1
|
||||
|
||||
if successful_index == get_child_count():
|
||||
successful_index = 0
|
||||
return SUCCESS
|
||||
else:
|
||||
successful_index = 0
|
||||
return FAILURE
|
||||
22
Sprout/addons/beehave/nodes/decorators/decorator.gd
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
## Decorator nodes are used to transform the result received by its child.
|
||||
## Must only have one child.
|
||||
@tool
|
||||
class_name Decorator extends BeehaveNode
|
||||
@icon("../../icons/category_decorator.svg")
|
||||
|
||||
|
||||
func _ready():
|
||||
if Engine.is_editor_hint():
|
||||
return
|
||||
|
||||
if self.get_child_count() != 1:
|
||||
push_error("Beehave Error: Decorator %s should have only one child (NodePath: %s)" % [self.name, self.get_path()])
|
||||
|
||||
|
||||
func _get_configuration_warnings() -> PackedStringArray:
|
||||
var warnings: PackedStringArray = super._get_configuration_warnings()
|
||||
|
||||
if get_child_count() != 1:
|
||||
warnings.append("Decorator should have exactly one child node.")
|
||||
|
||||
return warnings
|
||||
12
Sprout/addons/beehave/nodes/decorators/failer.gd
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
## A Failer node will always return a `FAILURE` status code.
|
||||
@tool
|
||||
class_name AlwaysFailDecorator extends Decorator
|
||||
@icon("../../icons/failer.svg")
|
||||
|
||||
|
||||
func tick(actor: Node, blackboard: Blackboard) -> int:
|
||||
for c in get_children():
|
||||
var response = c.tick(actor, blackboard)
|
||||
if response == RUNNING:
|
||||
return RUNNING
|
||||
return FAILURE
|
||||
21
Sprout/addons/beehave/nodes/decorators/inverter.gd
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
## An inverter will return `FAILURE` in case it's child returns a `SUCCESS` status
|
||||
## code or `SUCCESS` in case its child returns a `FAILURE` status code.
|
||||
@tool
|
||||
class_name InverterDecorator extends Decorator
|
||||
@icon("../../icons/inverter.svg")
|
||||
|
||||
func tick(actor: Node, blackboard: Blackboard) -> int:
|
||||
for c in get_children():
|
||||
var response = c.tick(actor, blackboard)
|
||||
|
||||
if response == SUCCESS:
|
||||
return FAILURE
|
||||
if response == FAILURE:
|
||||
return SUCCESS
|
||||
|
||||
if c is Leaf and response == RUNNING:
|
||||
blackboard.set_value("running_action", c)
|
||||
return RUNNING
|
||||
|
||||
# Decorators must have a child. This should be unreachable code.
|
||||
return FAILURE
|
||||
21
Sprout/addons/beehave/nodes/decorators/limiter.gd
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
## The limiter will execute its child `x` amount of times. When the number of
|
||||
## maximum ticks is reached, it will return a `FAILURE` status code.
|
||||
@tool
|
||||
class_name LimiterDecorator extends Decorator
|
||||
@icon("../../icons/limiter.svg")
|
||||
|
||||
@onready var cache_key = 'limiter_%s' % self.get_instance_id()
|
||||
|
||||
@export var max_count : float = 0
|
||||
|
||||
func tick(actor: Node, blackboard: Blackboard) -> int:
|
||||
var current_count = blackboard.get_value(cache_key)
|
||||
|
||||
if current_count == null:
|
||||
current_count = 0
|
||||
|
||||
if current_count <= max_count:
|
||||
blackboard.set_value(cache_key, current_count + 1)
|
||||
return self.get_child(0).tick(actor, blackboard)
|
||||
else:
|
||||
return FAILURE
|
||||
12
Sprout/addons/beehave/nodes/decorators/succeeder.gd
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
## A succeeder node will always return a `SUCCESS` status code.
|
||||
@tool
|
||||
class_name AlwaysSucceedDecorator extends Decorator
|
||||
@icon("../../icons/succeeder.svg")
|
||||
|
||||
|
||||
func tick(actor: Node, blackboard: Blackboard) -> int:
|
||||
for c in get_children():
|
||||
var response = c.tick(actor, blackboard)
|
||||
if response == RUNNING:
|
||||
return RUNNING
|
||||
return SUCCESS
|
||||
7
Sprout/addons/beehave/nodes/leaves/action.gd
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
## Actions are leaf nodes that define a task to be performed by an actor.
|
||||
## Their execution can be long running, potentially being called across multiple
|
||||
## frame executions. In this case, the node should return `RUNNING` until the
|
||||
## action is completed.
|
||||
@tool
|
||||
class_name ActionLeaf extends Leaf
|
||||
@icon("../../icons/action.svg")
|
||||
5
Sprout/addons/beehave/nodes/leaves/condition.gd
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
## Conditions are leaf nodes that either return SUCCESS or FAILURE depending on
|
||||
## a single simple condition. They should never return `RUNNING`.
|
||||
@tool
|
||||
class_name ConditionLeaf extends Leaf
|
||||
@icon("../../icons/condition.svg")
|
||||
15
Sprout/addons/beehave/nodes/leaves/leaf.gd
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
## Base class for all leaf nodes of the tree.
|
||||
@tool
|
||||
class_name Leaf extends BeehaveNode
|
||||
@icon("../../icons/category_leaf.svg")
|
||||
|
||||
|
||||
func _get_configuration_warnings() -> PackedStringArray:
|
||||
var warnings: PackedStringArray = []
|
||||
|
||||
var children: Array[Node] = get_children()
|
||||
|
||||
if children.any(func(x): return x is BeehaveNode):
|
||||
warnings.append("Leaf nodes should not have any child nodes. They won't be ticked.")
|
||||
|
||||
return warnings
|
||||
7
Sprout/addons/beehave/plugin.cfg
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
[plugin]
|
||||
|
||||
name="Beehave"
|
||||
description="🐝 Behaviour Tree addon for Godot Engine"
|
||||
author="bitbrain"
|
||||
version="2.5.0"
|
||||
script="plugin.gd"
|
||||
5
Sprout/addons/beehave/plugin.gd
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
@tool
|
||||
extends EditorPlugin
|
||||
|
||||
func _init():
|
||||
print("Beehave initialized!")
|
||||
|
|
@ -0,0 +1,511 @@
|
|||
@tool
|
||||
extends DialogicEditor
|
||||
|
||||
## Editor for editing character resources.
|
||||
|
||||
signal character_loaded(resource_path:String)
|
||||
signal portrait_selected()
|
||||
|
||||
|
||||
# Enums
|
||||
enum PreviewModes {Full, Real}
|
||||
|
||||
# Current state
|
||||
var current_preview_mode = PreviewModes.Full
|
||||
var loading = false
|
||||
var current_previewed_scene = null
|
||||
|
||||
# References
|
||||
var selected_item: TreeItem
|
||||
|
||||
##############################################################################
|
||||
## RESOURCE LOGIC
|
||||
##############################################################################
|
||||
|
||||
# Method is called once editors manager is ready to accept registers.
|
||||
func _register() -> void:
|
||||
# Makes the editor open this when a .dch file is selected.
|
||||
# Then _open_resource() is called.
|
||||
editors_manager.register_resource_editor("dch", self)
|
||||
# Add an "add character" button
|
||||
var add_character_button = editors_manager.add_icon_button(
|
||||
load("res://addons/dialogic/Editor/Images/Toolbar/add-character.svg"),
|
||||
'Add Character',
|
||||
self)
|
||||
add_character_button.pressed.connect(
|
||||
editors_manager.show_add_resource_dialog.bind(
|
||||
new_character,
|
||||
'*.dch; DialogicCharacter',
|
||||
'Create new character',
|
||||
'character',
|
||||
))
|
||||
|
||||
|
||||
# Called when a character is opened somehow
|
||||
func _open_resource(resource:Resource) -> void:
|
||||
# update resource
|
||||
current_resource = (resource as DialogicCharacter)
|
||||
|
||||
# make sure changes in the ui won't trigger saving
|
||||
loading = true
|
||||
|
||||
## Load other main tabs
|
||||
for main_edit in %MainEditTabs.get_children():
|
||||
main_edit._load_character(current_resource)
|
||||
|
||||
%DefaultPortraitPicker.set_value(resource.default_portrait)
|
||||
|
||||
%MainScale.value = 100*resource.scale
|
||||
%MainOffsetX.value = resource.offset.x
|
||||
%MainOffsetY.value = resource.offset.y
|
||||
%MainMirror.button_pressed = resource.mirror
|
||||
|
||||
# Portrait section
|
||||
%PortraitSearch.text = ""
|
||||
load_portrait_tree()
|
||||
|
||||
loading = false
|
||||
character_loaded.emit(resource.resource_path)
|
||||
|
||||
|
||||
func _save_resource() -> void:
|
||||
if ! visible or not current_resource:
|
||||
return
|
||||
|
||||
# Portrait list
|
||||
current_resource.portraits = get_updated_portrait_dict()
|
||||
|
||||
# Portrait settings
|
||||
if %DefaultPortraitPicker.current_value in current_resource.portraits.keys():
|
||||
current_resource.default_portrait = %DefaultPortraitPicker.current_value
|
||||
elif !current_resource.portraits.is_empty():
|
||||
current_resource.default_portrait = current_resource.portraits.keys()[0]
|
||||
else:
|
||||
current_resource.default_portrait = ""
|
||||
|
||||
current_resource.scale = %MainScale.value/100.0
|
||||
current_resource.offset = Vector2(%MainOffsetX.value, %MainOffsetY.value)
|
||||
current_resource.mirror = %MainMirror.button_pressed
|
||||
|
||||
# Main tabs
|
||||
for main_edit in %MainEditTabs.get_children():
|
||||
current_resource = main_edit._save_changes(current_resource)
|
||||
|
||||
ResourceSaver.save(current_resource, current_resource.resource_path)
|
||||
current_resource_state = ResourceStates.Saved
|
||||
editors_manager.resource_helper.rebuild_character_directory()
|
||||
|
||||
|
||||
# Saves a new empty character to the given path
|
||||
func new_character(path: String) -> void:
|
||||
var resource := DialogicCharacter.new()
|
||||
resource.resource_path = path
|
||||
resource.display_name = path.get_file().trim_suffix("."+path.get_extension())
|
||||
resource.color = Color(1,1,1,1)
|
||||
resource.default_portrait = ""
|
||||
resource.custom_info = {}
|
||||
ResourceSaver.save(resource, path)
|
||||
editors_manager.edit_resource(resource)
|
||||
|
||||
|
||||
##############################################################################
|
||||
## INTERFACE
|
||||
##############################################################################
|
||||
|
||||
func _ready() -> void:
|
||||
setup_portrait_list_tab()
|
||||
|
||||
setup_portrait_settings_tab()
|
||||
|
||||
%PreviewMode.item_selected.connect(_on_PreviewMode_item_selected)
|
||||
%PreviewMode.select(DialogicUtil.get_project_setting('dialogic/editor/character_preview_mode', 0))
|
||||
_on_PreviewMode_item_selected(%PreviewMode.selected)
|
||||
|
||||
## General Styling
|
||||
var panel_style = DCSS.inline({
|
||||
'border-radius': 5,
|
||||
'border': 1,
|
||||
'border_color':get_theme_color("dark_color_3", "Editor"),
|
||||
'background': get_theme_color("base_color", "Editor"),
|
||||
'padding': [10, 10],
|
||||
})
|
||||
|
||||
var tab_panel :StyleBoxFlat = get_theme_stylebox('tab_selected', 'TabContainer').duplicate()
|
||||
tab_panel.bg_color = get_theme_color("base_color", "Editor")
|
||||
|
||||
%MainEditTabs.add_theme_stylebox_override('panel', panel_style)
|
||||
%MainEditTabs.add_theme_stylebox_override('tab_selected', tab_panel)
|
||||
%MainEditTabs.add_theme_constant_override('side_margin', 5)
|
||||
%PortraitListSection.add_theme_stylebox_override('panel', panel_style)
|
||||
%PortraitListSection.add_theme_stylebox_override('tab_selected', tab_panel)
|
||||
%PortraitListSection.add_theme_constant_override('side_margin', 5)
|
||||
%PortraitPreviewSection.add_theme_stylebox_override('panel', panel_style)
|
||||
%PortraitSettingsSection.add_theme_stylebox_override('panel', panel_style)
|
||||
%PortraitSettingsSection.add_theme_stylebox_override('tab_selected', tab_panel)
|
||||
%PortraitSettingsSection.add_theme_constant_override('side_margin', 5)
|
||||
|
||||
%RealPreviewPivot.texture = get_theme_icon("EditorPivot", "EditorIcons")
|
||||
|
||||
# Add general tab
|
||||
add_main_tab("res://addons/dialogic/Editor/CharacterEditor/character_editor_tab_general.tscn")
|
||||
|
||||
# Load main tabs from subsystems/events
|
||||
for indexer in DialogicUtil.get_indexers():
|
||||
for main_tab in indexer._get_character_editor_tabs():
|
||||
add_main_tab(main_tab)
|
||||
|
||||
for child in %PortraitSettingsSection.get_children():
|
||||
if !child is DialogicCharacterEditorPortraitSettingsTab:
|
||||
printerr("[Dialogic Editor] Portrait settings tabs should extend the right class!")
|
||||
else:
|
||||
child.character_editor = self
|
||||
child.changed.connect(something_changed)
|
||||
child.update_preview.connect(update_preview)
|
||||
|
||||
|
||||
func add_main_tab(scene_path:String) -> void:
|
||||
var edit: DialogicCharacterEditorMainTab = load(scene_path).instantiate()
|
||||
edit.changed.connect(something_changed)
|
||||
edit.character_editor = self
|
||||
%MainEditTabs.add_child(edit)
|
||||
|
||||
|
||||
func something_changed(fake_argument = "", fake_arg2 = null) -> void:
|
||||
if not loading:
|
||||
current_resource_state = ResourceStates.Unsaved
|
||||
editors_manager.save_current_resource() #TODO, should this happen?
|
||||
|
||||
|
||||
##############################################################################
|
||||
## PORTRAIT SECTION
|
||||
##############################################################################
|
||||
|
||||
func setup_portrait_list_tab() -> void:
|
||||
%PortraitTree.editor = self
|
||||
|
||||
## Portrait section styling/connections
|
||||
%AddPortraitButton.icon = get_theme_icon("Add", "EditorIcons")
|
||||
%AddPortraitButton.pressed.connect(add_portrait)
|
||||
%AddPortraitGroupButton.icon = get_theme_icon("Groups", "EditorIcons")
|
||||
%AddPortraitGroupButton.pressed.connect(add_portrait_group)
|
||||
%ImportPortraitsButton.icon = get_theme_icon("Folder", "EditorIcons")
|
||||
%ImportPortraitsButton.pressed.connect(open_portrait_folder_select)
|
||||
%PortraitSearch.right_icon = get_theme_icon("Search", "EditorIcons")
|
||||
%PortraitSearch.text_changed.connect(filter_portrait_list)
|
||||
|
||||
%PortraitTree.item_selected.connect(load_selected_portrait)
|
||||
%PortraitTree.item_edited.connect(_on_item_edited)
|
||||
|
||||
func open_portrait_folder_select() -> void:
|
||||
find_parent("EditorView").godot_file_dialog(
|
||||
import_portraits_from_folder, "*",
|
||||
EditorFileDialog.FILE_MODE_OPEN_DIR)
|
||||
|
||||
|
||||
func import_portraits_from_folder(path:String) -> void:
|
||||
var parent: TreeItem = %PortraitTree.get_root()
|
||||
if %PortraitTree.get_selected() and %PortraitTree.get_selected().get_metadata(0).has('group'):
|
||||
parent = %PortraitTree.get_selected()
|
||||
|
||||
var dir := DirAccess.open(path)
|
||||
dir.list_dir_begin()
|
||||
var file_name :String = dir.get_next()
|
||||
while file_name != "":
|
||||
if not dir.current_is_dir():
|
||||
var file_lower = file_name.to_lower()
|
||||
if '.svg' in file_lower or '.png' in file_lower:
|
||||
if not '.import' in file_lower:
|
||||
var final_name: String= path+ "/" + file_name
|
||||
%PortraitTree.add_portrait_item(file_name.trim_suffix('.'+file_name.get_extension()),
|
||||
{'scene':"",'image':final_name, 'scale':1, 'offset':Vector2(), 'mirror':false}, parent)
|
||||
file_name = dir.get_next()
|
||||
something_changed()
|
||||
|
||||
|
||||
func add_portrait(portrait_name:String='New portrait', portrait_data:Dictionary={'scene':"", 'image':'', 'scale':1, 'offset':Vector2(), 'mirror':false}) -> void:
|
||||
var parent: TreeItem = %PortraitTree.get_root()
|
||||
if %PortraitTree.get_selected():
|
||||
if %PortraitTree.get_selected().get_metadata(0).has('group'):
|
||||
parent = %PortraitTree.get_selected()
|
||||
else:
|
||||
parent = %PortraitTree.get_selected().get_parent()
|
||||
%PortraitTree.add_portrait_item(portrait_name, portrait_data, parent).select(0)
|
||||
something_changed()
|
||||
|
||||
|
||||
func add_portrait_group() -> void:
|
||||
var parent_item :TreeItem = %PortraitTree.get_root()
|
||||
if %PortraitTree.get_selected() and %PortraitTree.get_selected().get_metadata(0).has('group'):
|
||||
parent_item = %PortraitTree.get_selected()
|
||||
%PortraitTree.add_portrait_group("Group", parent_item)
|
||||
|
||||
|
||||
func load_portrait_tree() -> void:
|
||||
%PortraitTree.clear_tree()
|
||||
var root:TreeItem = %PortraitTree.create_item()
|
||||
|
||||
for portrait in current_resource.portraits.keys():
|
||||
var portrait_label = portrait
|
||||
var parent = %PortraitTree.get_root()
|
||||
if '/' in portrait:
|
||||
parent = %PortraitTree.create_necessary_group_items(portrait)
|
||||
portrait_label = portrait.split('/')[-1]
|
||||
|
||||
%PortraitTree.add_portrait_item(portrait_label, current_resource.portraits[portrait], parent)
|
||||
|
||||
update_default_portrait_star(current_resource.default_portrait)
|
||||
|
||||
if root.get_child_count():
|
||||
root.get_first_child().select(0)
|
||||
else:
|
||||
# Call anyways to clear preview and hide portrait settings section
|
||||
load_selected_portrait()
|
||||
|
||||
|
||||
func filter_portrait_list(filter_term:String = '') -> void:
|
||||
filter_branch(%PortraitTree.get_root(), filter_term)
|
||||
|
||||
|
||||
func filter_branch(parent:TreeItem, filter_term:String) -> bool:
|
||||
var anything_visible := false
|
||||
for item in parent.get_children():
|
||||
if item.get_metadata(0).has('group'):
|
||||
item.visible = filter_branch(item, filter_term)
|
||||
anything_visible = item.visible
|
||||
elif filter_term.is_empty() or filter_term.to_lower() in item.get_text(0).to_lower():
|
||||
item.visible = true
|
||||
anything_visible = true
|
||||
else:
|
||||
item.visible = false
|
||||
return anything_visible
|
||||
|
||||
|
||||
# this is used to save the portrait data
|
||||
func get_updated_portrait_dict() -> Dictionary:
|
||||
return list_portraits(%PortraitTree.get_root().get_children())
|
||||
|
||||
|
||||
func list_portraits(tree_items:Array[TreeItem], dict:Dictionary = {}, path_prefix = "") -> Dictionary:
|
||||
for item in tree_items:
|
||||
if item.get_metadata(0).has('group'):
|
||||
dict = list_portraits(item.get_children(), dict, path_prefix+item.get_text(0)+"/")
|
||||
else:
|
||||
dict[path_prefix +item.get_text(0)] = item.get_metadata(0)
|
||||
return dict
|
||||
|
||||
|
||||
func load_selected_portrait():
|
||||
if selected_item and is_instance_valid(selected_item):
|
||||
selected_item.set_editable(0, false)
|
||||
|
||||
selected_item = %PortraitTree.get_selected()
|
||||
|
||||
|
||||
if selected_item and !selected_item.get_metadata(0).has('group'):
|
||||
%PortraitSettingsSection.show()
|
||||
var current_portrait_data :Dictionary = selected_item.get_metadata(0)
|
||||
portrait_selected.emit(%PortraitTree.get_full_item_name(selected_item), current_portrait_data)
|
||||
|
||||
update_preview()
|
||||
|
||||
for tab in %PortraitSettingsSection.get_children():
|
||||
if !tab is DialogicCharacterEditorPortraitSettingsTab:
|
||||
printerr("[Dialogic Editor] Portrait settings tabs should extend the right class!")
|
||||
else:
|
||||
tab.selected_item = selected_item
|
||||
tab._load_portrait_data(current_portrait_data)
|
||||
|
||||
# switch tabs if the current one is hidden (until the next not hidden tab)
|
||||
for i in range(%PortraitSettingsSection.get_tab_count()):
|
||||
if %PortraitSettingsSection.is_tab_hidden(%PortraitSettingsSection.current_tab):
|
||||
if %PortraitSettingsSection.current_tab == %PortraitSettingsSection.get_tab_count()-1:
|
||||
%PortraitSettingsSection.current_tab = 0
|
||||
else:
|
||||
%PortraitSettingsSection.current_tab += 1
|
||||
else:
|
||||
break
|
||||
else:
|
||||
%PortraitSettingsSection.hide()
|
||||
update_preview()
|
||||
|
||||
if selected_item:
|
||||
await get_tree().create_timer(0.01).timeout
|
||||
selected_item.set_editable(0, true)
|
||||
|
||||
|
||||
func delete_portrait_item(item:TreeItem) -> void:
|
||||
item.free()
|
||||
something_changed()
|
||||
|
||||
|
||||
func duplicate_item(item:TreeItem) -> void:
|
||||
%PortraitTree.add_portrait_item(item.get_text(0)+'_duplicated', item.get_metadata(0), item.get_parent()).select(0)
|
||||
|
||||
|
||||
func _on_portrait_tree_button_clicked(item:TreeItem, column:int, id:int, mouse_button_index:int):
|
||||
# DELETE BUTTON
|
||||
if id == 1:
|
||||
delete_portrait_item(item)
|
||||
# DUPLICATE ITEM
|
||||
if id == 3:
|
||||
duplicate_item(item)
|
||||
|
||||
|
||||
# this removes/and adds the DEFAULT star on the portrait list
|
||||
func update_default_portrait_star(default_portrait_name:String) -> void:
|
||||
var item_list : Array = %PortraitTree.get_root().get_children()
|
||||
while true:
|
||||
var item = item_list.pop_back()
|
||||
if item.get_button_by_id(0, 2) != -1:
|
||||
item.erase_button(0, item.get_button_by_id(0, 2))
|
||||
if %PortraitTree.get_full_item_name(item) == default_portrait_name:
|
||||
item.erase_button(0, item.get_button_by_id(0, 1))
|
||||
item.erase_button(0, item.get_button_by_id(0, 3))
|
||||
item.add_button(0, get_theme_icon('Favorites', 'EditorIcons'), 2, true, 'Default')
|
||||
item.add_button(0, get_theme_icon('Duplicate', 'EditorIcons'), 3, false, 'Duplicate')
|
||||
item.add_button(0, get_theme_icon('Remove', 'EditorIcons'), 1, false, 'Remove')
|
||||
item_list.append_array(item.get_children())
|
||||
|
||||
if item_list.is_empty():
|
||||
break
|
||||
|
||||
|
||||
func _on_item_edited():
|
||||
selected_item = %PortraitTree.get_selected()
|
||||
something_changed()
|
||||
if selected_item:
|
||||
if %PreviewLabel.text.trim_prefix('Preview of "').trim_suffix('"') == current_resource.default_portrait:
|
||||
current_resource.default_portrait = %PortraitTree.get_full_item_name(selected_item)
|
||||
update_preview()
|
||||
|
||||
|
||||
|
||||
##############################################################################
|
||||
## PORTRAIT SETTINGS TAB
|
||||
##############################################################################
|
||||
|
||||
func setup_portrait_settings_tab() -> void:
|
||||
%DefaultPortraitPicker.value_changed.connect(default_portrait_changed)
|
||||
%MainScale.value_changed.connect(main_portrait_settings_update)
|
||||
%MainOffsetX.value_changed.connect(main_portrait_settings_update)
|
||||
%MainOffsetY.value_changed.connect(main_portrait_settings_update)
|
||||
%MainMirror.toggled.connect(main_portrait_settings_update)
|
||||
|
||||
# Setting up Default Portrait Picker
|
||||
%DefaultPortraitPicker.resource_icon = load("res://addons/dialogic/Editor/Images/Resources/portrait.svg")
|
||||
%DefaultPortraitPicker.get_suggestions_func = suggest_portraits
|
||||
%DefaultPortraitPicker.set_left_text("")
|
||||
|
||||
|
||||
# Make sure preview get's updated when portrait settings change
|
||||
func main_portrait_settings_update(value = null) -> void:
|
||||
current_resource.scale = %MainScale.value/100.0
|
||||
current_resource.offset = Vector2(%MainOffsetX.value, %MainOffsetY.value)
|
||||
current_resource.mirror = %MainMirror.button_pressed
|
||||
update_preview()
|
||||
something_changed()
|
||||
|
||||
|
||||
func default_portrait_changed(property:String, value:String) -> void:
|
||||
current_resource.default_portrait = value
|
||||
update_default_portrait_star(value)
|
||||
|
||||
|
||||
# Get suggestions for DefaultPortraitPicker
|
||||
func suggest_portraits(search:String) -> Dictionary:
|
||||
var suggestions := {}
|
||||
for portrait in get_updated_portrait_dict().keys():
|
||||
suggestions[portrait] = {'value':portrait}
|
||||
return suggestions
|
||||
|
||||
|
||||
##############################################################################
|
||||
## PREVIEW
|
||||
##############################################################################
|
||||
|
||||
func update_preview() -> void:
|
||||
%ScenePreviewWarning.hide()
|
||||
if selected_item and is_instance_valid(selected_item) and !selected_item.get_metadata(0).has('group'):
|
||||
%PreviewLabel.text = 'Preview of "'+%PortraitTree.get_full_item_name(selected_item)+'"'
|
||||
var current_portrait_data: Dictionary = selected_item.get_metadata(0)
|
||||
var mirror:bool = current_portrait_data.get('mirror', false) != current_resource.mirror
|
||||
var scale:float = current_portrait_data.get('scale', 1) * current_resource.scale
|
||||
var offset:Vector2 =current_portrait_data.get('offset', Vector2()) + current_resource.offset
|
||||
|
||||
if current_previewed_scene != null \
|
||||
and current_previewed_scene.get_meta('path', null) == current_portrait_data.get('scene') \
|
||||
and current_previewed_scene.has_method('_should_do_portrait_update') \
|
||||
and is_instance_valid(current_previewed_scene.get_script()) \
|
||||
and current_previewed_scene._should_do_portrait_update(current_resource, selected_item.get_text(0)):
|
||||
pass # we keep the same scene
|
||||
else:
|
||||
for node in %RealPreviewPivot.get_children():
|
||||
node.queue_free()
|
||||
current_previewed_scene = null
|
||||
if current_portrait_data.get('scene', '').is_empty():
|
||||
if FileAccess.file_exists("res://addons/dialogic/Events/Character/default_portrait.tscn"):
|
||||
current_previewed_scene = load("res://addons/dialogic/Events/Character/default_portrait.tscn").instantiate()
|
||||
current_previewed_scene.set_meta('path', '')
|
||||
else:
|
||||
if FileAccess.file_exists(current_portrait_data.get('scene')):
|
||||
current_previewed_scene = load(current_portrait_data.get('scene')).instantiate()
|
||||
current_previewed_scene.set_meta('path', current_portrait_data.get('scene'))
|
||||
if current_previewed_scene:
|
||||
%RealPreviewPivot.add_child(current_previewed_scene)
|
||||
|
||||
if current_previewed_scene != null:
|
||||
var scene = current_previewed_scene
|
||||
scene.show_behind_parent = true
|
||||
|
||||
for prop in current_portrait_data.get('export_overrides', {}).keys():
|
||||
scene.set(prop, str_to_var(current_portrait_data['export_overrides'][prop]))
|
||||
|
||||
if is_instance_valid(scene.get_script()) and scene.script.is_tool():
|
||||
if scene.has_method('_update_portrait'):
|
||||
scene._update_portrait(current_resource, %PortraitTree.get_full_item_name(selected_item))
|
||||
if scene.has_method('_set_mirror'):
|
||||
scene._set_mirror(mirror)
|
||||
if current_preview_mode == PreviewModes.Real:
|
||||
scene.position = Vector2() + offset
|
||||
|
||||
if current_portrait_data.get('scene', '').is_empty() or !current_portrait_data.get('ignore_char_scale', false):
|
||||
scene.scale = Vector2(1,1)*scale
|
||||
else:
|
||||
if is_instance_valid(scene.get_script()) and scene.script.is_tool() and scene.has_method('_get_covered_rect'):
|
||||
var rect :Rect2= scene._get_covered_rect()
|
||||
var available_rect:Rect2 = %FullPreviewAvailableRect.get_rect()
|
||||
scene.scale = Vector2(1,1) * min(available_rect.size.x/rect.size.x, available_rect.size.y/rect.size.y)
|
||||
%RealPreviewPivot.position = (rect.position)*-1*scene.scale
|
||||
%RealPreviewPivot.position.x = %FullPreviewAvailableRect.size.x/2
|
||||
scene.position = Vector2()
|
||||
else:
|
||||
%ScenePreviewWarning.show()
|
||||
else:
|
||||
%PreviewRealRect.texture = null
|
||||
%PreviewFullRect.texture = null
|
||||
%PreviewLabel.text = 'Nothing to preview'
|
||||
|
||||
else:
|
||||
%PreviewLabel.text = 'No portrait to preview.'
|
||||
for node in %RealPreviewPivot.get_children():
|
||||
node.queue_free()
|
||||
current_previewed_scene = null
|
||||
|
||||
|
||||
func _on_PreviewMode_item_selected(index:int) -> void:
|
||||
current_preview_mode = index
|
||||
# FULL VIEW
|
||||
if index == PreviewModes.Full:
|
||||
%RealSizeRemotePivotTransform.update_position = false
|
||||
# REAL SIZE
|
||||
if index == PreviewModes.Real or index == null:
|
||||
%RealSizeRemotePivotTransform.update_position = true
|
||||
update_preview()
|
||||
ProjectSettings.set_setting('dialogic/editor/character_preview_mode', index)
|
||||
ProjectSettings.save()
|
||||
|
||||
|
||||
func _on_full_preview_available_rect_resized():
|
||||
if current_preview_mode == PreviewModes.Full:
|
||||
update_preview()
|
||||
|
||||
|
|
@ -0,0 +1,493 @@
|
|||
[gd_scene load_steps=15 format=3 uid="uid://dlskc36c5hrwv"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/dialogic/Editor/CharacterEditor/character_editor.gd" id="2"]
|
||||
[ext_resource type="PackedScene" uid="uid://dpwhshre1n4t6" path="res://addons/dialogic/Editor/Events/Fields/ComplexPicker.tscn" id="2_01va3"]
|
||||
[ext_resource type="Script" path="res://addons/dialogic/Editor/CharacterEditor/character_editor_ptab_image.gd" id="2_cusvj"]
|
||||
[ext_resource type="Script" path="res://addons/dialogic/Editor/CharacterEditor/character_editor_portrait_tree.gd" id="2_vad0i"]
|
||||
[ext_resource type="Script" path="res://addons/dialogic/Editor/CharacterEditor/character_editor_ptab_scene.gd" id="4_6tltp"]
|
||||
[ext_resource type="PackedScene" uid="uid://7mvxuaulctcq" path="res://addons/dialogic/Editor/Events/Fields/FilePicker.tscn" id="4_a4bcu"]
|
||||
[ext_resource type="Script" path="res://addons/dialogic/Editor/CharacterEditor/character_editor_ptab_layout.gd" id="5_eicvp"]
|
||||
[ext_resource type="Script" path="res://addons/dialogic/Editor/CharacterEditor/character_editor_ptab_exports.gd" id="7_4enie"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_kxihh"]
|
||||
content_margin_left = 10.0
|
||||
content_margin_top = 4.0
|
||||
content_margin_right = 10.0
|
||||
content_margin_bottom = 4.0
|
||||
bg_color = Color(0, 0, 0, 1)
|
||||
border_width_top = 2
|
||||
border_color = Color(1, 1, 1, 0.75)
|
||||
corner_detail = 1
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_1pdx5"]
|
||||
content_margin_left = 7.5
|
||||
content_margin_top = 7.5
|
||||
content_margin_right = 7.5
|
||||
content_margin_bottom = 7.5
|
||||
bg_color = Color(0, 0, 0, 1)
|
||||
corner_radius_top_left = 3
|
||||
corner_radius_top_right = 3
|
||||
corner_radius_bottom_right = 3
|
||||
corner_radius_bottom_left = 3
|
||||
|
||||
[sub_resource type="Image" id="Image_8q5e2"]
|
||||
data = {
|
||||
"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 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, 93, 93, 131, 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, 93, 93, 131, 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, 93, 93, 131, 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, 252, 255, 93, 93, 252, 255, 93, 93, 252, 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, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 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, 93, 93, 55, 255, 97, 97, 58, 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, 97, 97, 42, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 98, 98, 47, 255, 97, 97, 42, 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, 93, 93, 233, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 94, 94, 46, 255, 93, 93, 236, 255, 93, 93, 233, 255, 97, 97, 42, 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, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 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_uf4x7"]
|
||||
image = SubResource("Image_8q5e2")
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_4xgdx"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_81xk6"]
|
||||
content_margin_left = 4.0
|
||||
content_margin_top = 4.0
|
||||
content_margin_right = 4.0
|
||||
content_margin_bottom = 4.0
|
||||
bg_color = Color(1, 0.365, 0.365, 1)
|
||||
draw_center = false
|
||||
border_width_left = 2
|
||||
border_width_top = 2
|
||||
border_width_right = 2
|
||||
border_width_bottom = 2
|
||||
corner_detail = 1
|
||||
|
||||
[node name="CharacterEditor" type="MarginContainer"]
|
||||
self_modulate = Color(0, 0, 0, 1)
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_constants/margin_left = 0
|
||||
theme_override_constants/margin_top = 0
|
||||
theme_override_constants/margin_right = 0
|
||||
theme_override_constants/margin_bottom = 0
|
||||
script = ExtResource("2")
|
||||
|
||||
[node name="Split" type="HSplitContainer" parent="."]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="Editor" type="VSplitContainer" parent="Split"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 11
|
||||
|
||||
[node name="EditorScroll" type="ScrollContainer" parent="Split/Editor"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 0.0
|
||||
vertical_scroll_mode = 0
|
||||
|
||||
[node name="MainEditTabs" type="TabContainer" parent="Split/Editor/EditorScroll"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
theme_override_constants/side_margin = 5
|
||||
theme_override_styles/tab_selected = SubResource("StyleBoxFlat_kxihh")
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_1pdx5")
|
||||
|
||||
[node name="PortraitListSection" type="TabContainer" parent="Split/Editor"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
theme_override_constants/side_margin = 5
|
||||
theme_override_styles/tab_selected = SubResource("StyleBoxFlat_kxihh")
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_1pdx5")
|
||||
|
||||
[node name="Portraits" type="VBoxContainer" parent="Split/Editor/PortraitListSection"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="PortraitListTools" type="HBoxContainer" parent="Split/Editor/PortraitListSection/Portraits"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="AddPortraitButton" type="Button" parent="Split/Editor/PortraitListSection/Portraits/PortraitListTools"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
tooltip_text = "Add portrait"
|
||||
icon = SubResource("ImageTexture_uf4x7")
|
||||
|
||||
[node name="AddPortraitGroupButton" type="Button" parent="Split/Editor/PortraitListSection/Portraits/PortraitListTools"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
tooltip_text = "Add Group"
|
||||
icon = SubResource("ImageTexture_uf4x7")
|
||||
|
||||
[node name="ImportPortraitsButton" type="Button" parent="Split/Editor/PortraitListSection/Portraits/PortraitListTools"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
tooltip_text = "Import images from folder"
|
||||
icon = SubResource("ImageTexture_uf4x7")
|
||||
|
||||
[node name="PortraitSearch" type="LineEdit" parent="Split/Editor/PortraitListSection/Portraits/PortraitListTools"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 4
|
||||
placeholder_text = "Search"
|
||||
expand_to_text_length = true
|
||||
clear_button_enabled = true
|
||||
right_icon = SubResource("ImageTexture_uf4x7")
|
||||
caret_blink = true
|
||||
caret_blink_interval = 0.5
|
||||
|
||||
[node name="Panel" type="PanelContainer" parent="Split/Editor/PortraitListSection/Portraits"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
theme_override_styles/panel = SubResource("StyleBoxEmpty_4xgdx")
|
||||
|
||||
[node name="PortraitTree" type="Tree" parent="Split/Editor/PortraitListSection/Portraits/Panel"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
allow_rmb_select = true
|
||||
hide_root = true
|
||||
drop_mode_flags = 1
|
||||
script = ExtResource("2_vad0i")
|
||||
|
||||
[node name="P Settings" type="VBoxContainer" parent="Split/Editor/PortraitListSection"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
|
||||
[node name="DefaultPortrait" type="HBoxContainer" parent="Split/Editor/PortraitListSection/P Settings"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="Label5" type="Label" parent="Split/Editor/PortraitListSection/P Settings/DefaultPortrait"]
|
||||
layout_mode = 2
|
||||
text = "Default:"
|
||||
|
||||
[node name="DefaultPortraitPicker" parent="Split/Editor/PortraitListSection/P Settings/DefaultPortrait" instance=ExtResource("2_01va3")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
placeholder_text = "Select Portrait"
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="Split/Editor/PortraitListSection/P Settings"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="PortraitMainSettings" type="HFlowContainer" parent="Split/Editor/PortraitListSection/P Settings"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="Label" type="Label" parent="Split/Editor/PortraitListSection/P Settings/PortraitMainSettings"]
|
||||
layout_mode = 2
|
||||
text = "Scale"
|
||||
|
||||
[node name="MainScale" type="SpinBox" parent="Split/Editor/PortraitListSection/P Settings/PortraitMainSettings"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
value = 100.0
|
||||
allow_greater = true
|
||||
suffix = "%"
|
||||
|
||||
[node name="Offset" type="HBoxContainer" parent="Split/Editor/PortraitListSection/P Settings/PortraitMainSettings"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label2" type="Label" parent="Split/Editor/PortraitListSection/P Settings/PortraitMainSettings/Offset"]
|
||||
layout_mode = 2
|
||||
text = "Offset"
|
||||
|
||||
[node name="MainOffsetX" type="SpinBox" parent="Split/Editor/PortraitListSection/P Settings/PortraitMainSettings/Offset"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
allow_greater = true
|
||||
allow_lesser = true
|
||||
suffix = "X"
|
||||
|
||||
[node name="MainOffsetY" type="SpinBox" parent="Split/Editor/PortraitListSection/P Settings/PortraitMainSettings/Offset"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
allow_greater = true
|
||||
allow_lesser = true
|
||||
suffix = "Y"
|
||||
|
||||
[node name="MirrorOption" type="HBoxContainer" parent="Split/Editor/PortraitListSection/P Settings/PortraitMainSettings"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="Split/Editor/PortraitListSection/P Settings/PortraitMainSettings/MirrorOption"]
|
||||
layout_mode = 2
|
||||
text = "Mirror"
|
||||
|
||||
[node name="MainMirror" type="CheckBox" parent="Split/Editor/PortraitListSection/P Settings/PortraitMainSettings/MirrorOption"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
|
||||
[node name="RightSection" type="VSplitContainer" parent="Split"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
size_flags_stretch_ratio = 1.5
|
||||
|
||||
[node name="PortraitPreviewSection" type="Panel" parent="Split/RightSection"]
|
||||
unique_name_in_owner = true
|
||||
clip_contents = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_1pdx5")
|
||||
|
||||
[node name="Node2D" type="Node2D" parent="Split/RightSection/PortraitPreviewSection"]
|
||||
position = Vector2(13, 17)
|
||||
|
||||
[node name="RealPreviewPivot" type="Sprite2D" parent="Split/RightSection/PortraitPreviewSection/Node2D"]
|
||||
unique_name_in_owner = true
|
||||
position = Vector2(330, 405)
|
||||
texture = SubResource("ImageTexture_uf4x7")
|
||||
|
||||
[node name="ScenePreviewWarning" type="Label" parent="Split/RightSection/PortraitPreviewSection"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 1
|
||||
anchors_preset = 8
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
offset_left = -143.0
|
||||
offset_top = -44.5
|
||||
offset_right = 143.0
|
||||
offset_bottom = 85.5
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
text = "Custom scenes can only be viewed in \"Full mode\" if they are in @tool mode and override _get_covered_rect"
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
autowrap_mode = 3
|
||||
metadata/_edit_layout_mode = 1
|
||||
|
||||
[node name="PreviewReal" type="CenterContainer" parent="Split/RightSection/PortraitPreviewSection"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 1
|
||||
anchors_preset = 7
|
||||
anchor_left = 0.5
|
||||
anchor_top = 1.0
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 1.0
|
||||
offset_left = -302.0
|
||||
offset_top = -139.0
|
||||
offset_right = 302.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 0
|
||||
metadata/_edit_layout_mode = 1
|
||||
|
||||
[node name="Control" type="Control" parent="Split/RightSection/PortraitPreviewSection/PreviewReal"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="RealSizeRemotePivotTransform" type="RemoteTransform2D" parent="Split/RightSection/PortraitPreviewSection/PreviewReal/Control"]
|
||||
unique_name_in_owner = true
|
||||
remote_path = NodePath("../../../Node2D/RealPreviewPivot")
|
||||
update_position = false
|
||||
update_rotation = false
|
||||
update_scale = false
|
||||
|
||||
[node name="FullPreviewAvailableRect" type="Control" parent="Split/RightSection/PortraitPreviewSection"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = 10.0
|
||||
offset_top = 13.0
|
||||
offset_right = -10.0
|
||||
offset_bottom = -16.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
metadata/_edit_layout_mode = 1
|
||||
|
||||
[node name="PreviewLabel" type="Label" parent="Split/RightSection/PortraitPreviewSection"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 0
|
||||
offset_right = 40.0
|
||||
offset_bottom = 23.0
|
||||
text = "No portrait to preview."
|
||||
|
||||
[node name="PreviewMode" type="OptionButton" parent="Split/RightSection/PortraitPreviewSection"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 1
|
||||
anchors_preset = 1
|
||||
anchor_left = 1.0
|
||||
anchor_right = 1.0
|
||||
offset_left = -121.0
|
||||
offset_top = 9.0
|
||||
offset_right = -9.0
|
||||
offset_bottom = 40.0
|
||||
grow_horizontal = 0
|
||||
item_count = 2
|
||||
selected = 0
|
||||
popup/item_0/text = "Full View"
|
||||
popup/item_0/id = 0
|
||||
popup/item_1/text = "Real Size"
|
||||
popup/item_1/id = 1
|
||||
metadata/_edit_layout_mode = 1
|
||||
|
||||
[node name="TextureRect" type="TextureRect" parent="Split/RightSection/PortraitPreviewSection"]
|
||||
layout_mode = 0
|
||||
offset_top = -12.0
|
||||
offset_right = 597.0
|
||||
offset_bottom = -12.0
|
||||
|
||||
[node name="PortraitSettingsSection" type="TabContainer" parent="Split/RightSection"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
size_flags_stretch_ratio = 0.3
|
||||
theme_override_constants/side_margin = 5
|
||||
theme_override_styles/tab_selected = SubResource("StyleBoxFlat_kxihh")
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_1pdx5")
|
||||
|
||||
[node name="Image" type="ScrollContainer" parent="Split/RightSection/PortraitSettingsSection"]
|
||||
custom_minimum_size = Vector2(0, 35)
|
||||
layout_mode = 2
|
||||
horizontal_scroll_mode = 0
|
||||
script = ExtResource("2_cusvj")
|
||||
|
||||
[node name="Flow" type="HFlowContainer" parent="Split/RightSection/PortraitSettingsSection/Image"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="GridContainer" type="GridContainer" parent="Split/RightSection/PortraitSettingsSection/Image/Flow"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
columns = 2
|
||||
|
||||
[node name="Label" type="Label" parent="Split/RightSection/PortraitSettingsSection/Image/Flow/GridContainer"]
|
||||
layout_mode = 2
|
||||
text = "Image: "
|
||||
|
||||
[node name="ImagePicker" parent="Split/RightSection/PortraitSettingsSection/Image/Flow/GridContainer" instance=ExtResource("4_a4bcu")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
file_filter = "*.png, *.svg"
|
||||
resource_icon = SubResource("ImageTexture_uf4x7")
|
||||
|
||||
[node name="Scene" type="ScrollContainer" parent="Split/RightSection/PortraitSettingsSection"]
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(0, 35)
|
||||
layout_mode = 2
|
||||
horizontal_scroll_mode = 0
|
||||
script = ExtResource("4_6tltp")
|
||||
|
||||
[node name="Flow" type="HFlowContainer" parent="Split/RightSection/PortraitSettingsSection/Scene"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="GridContainer" type="GridContainer" parent="Split/RightSection/PortraitSettingsSection/Scene/Flow"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
columns = 2
|
||||
|
||||
[node name="Label" type="Label" parent="Split/RightSection/PortraitSettingsSection/Scene/Flow/GridContainer"]
|
||||
layout_mode = 2
|
||||
text = "Scene: "
|
||||
|
||||
[node name="ScenePicker" parent="Split/RightSection/PortraitSettingsSection/Scene/Flow/GridContainer" instance=ExtResource("4_a4bcu")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
file_filter = "*.tscn"
|
||||
placeholder = "Default scene"
|
||||
resource_icon = SubResource("ImageTexture_uf4x7")
|
||||
|
||||
[node name="Label2" type="Label" parent="Split/RightSection/PortraitSettingsSection/Scene/Flow/GridContainer"]
|
||||
layout_mode = 2
|
||||
text = "Ignore scale: "
|
||||
|
||||
[node name="IgnoreScale" type="CheckBox" parent="Split/RightSection/PortraitSettingsSection/Scene/Flow/GridContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Layout" type="ScrollContainer" parent="Split/RightSection/PortraitSettingsSection"]
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(0, 35)
|
||||
layout_mode = 2
|
||||
horizontal_scroll_mode = 0
|
||||
script = ExtResource("5_eicvp")
|
||||
|
||||
[node name="Flow" type="HFlowContainer" parent="Split/RightSection/PortraitSettingsSection/Layout"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="Split/RightSection/PortraitSettingsSection/Layout/Flow"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="Split/RightSection/PortraitSettingsSection/Layout/Flow/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
text = "Scale:"
|
||||
|
||||
[node name="PortraitScale" type="SpinBox" parent="Split/RightSection/PortraitSettingsSection/Layout/Flow/HBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
value = 100.0
|
||||
allow_greater = true
|
||||
suffix = "%"
|
||||
|
||||
[node name="HBoxContainer2" type="HBoxContainer" parent="Split/RightSection/PortraitSettingsSection/Layout/Flow"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label2" type="Label" parent="Split/RightSection/PortraitSettingsSection/Layout/Flow/HBoxContainer2"]
|
||||
layout_mode = 2
|
||||
text = "Offset:"
|
||||
|
||||
[node name="PortraitOffsetX" type="SpinBox" parent="Split/RightSection/PortraitSettingsSection/Layout/Flow/HBoxContainer2"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
allow_greater = true
|
||||
allow_lesser = true
|
||||
suffix = "X"
|
||||
|
||||
[node name="PortraitOffsetY" type="SpinBox" parent="Split/RightSection/PortraitSettingsSection/Layout/Flow/HBoxContainer2"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
allow_greater = true
|
||||
allow_lesser = true
|
||||
suffix = "Y"
|
||||
|
||||
[node name="MirrorOption" type="HBoxContainer" parent="Split/RightSection/PortraitSettingsSection/Layout/Flow"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="Split/RightSection/PortraitSettingsSection/Layout/Flow/MirrorOption"]
|
||||
layout_mode = 2
|
||||
text = "Mirror:"
|
||||
|
||||
[node name="PortraitMirror" type="CheckBox" parent="Split/RightSection/PortraitSettingsSection/Layout/Flow/MirrorOption"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Exports" type="ScrollContainer" parent="Split/RightSection/PortraitSettingsSection"]
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(0, 35)
|
||||
layout_mode = 2
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_81xk6")
|
||||
horizontal_scroll_mode = 0
|
||||
script = ExtResource("7_4enie")
|
||||
|
||||
[node name="Grid" type="GridContainer" parent="Split/RightSection/PortraitSettingsSection/Exports"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
theme_override_constants/h_separation = 10
|
||||
columns = 2
|
||||
|
||||
[connection signal="button_clicked" from="Split/Editor/PortraitListSection/Portraits/Panel/PortraitTree" to="." method="_on_portrait_tree_button_clicked"]
|
||||
[connection signal="resized" from="Split/RightSection/PortraitPreviewSection/FullPreviewAvailableRect" to="." method="_on_full_preview_available_rect_resized"]
|
||||
[connection signal="value_changed" from="Split/RightSection/PortraitSettingsSection/Image/Flow/GridContainer/ImagePicker" to="Split/RightSection/PortraitSettingsSection/Image" method="_on_image_picker_value_changed"]
|
||||
[connection signal="value_changed" from="Split/RightSection/PortraitSettingsSection/Scene/Flow/GridContainer/ScenePicker" to="Split/RightSection/PortraitSettingsSection/Scene" method="_on_scene_picker_value_changed"]
|
||||
[connection signal="toggled" from="Split/RightSection/PortraitSettingsSection/Scene/Flow/GridContainer/IgnoreScale" to="Split/RightSection/PortraitSettingsSection/Scene" method="_on_ignore_scale_toggled"]
|
||||
[connection signal="value_changed" from="Split/RightSection/PortraitSettingsSection/Layout/Flow/HBoxContainer/PortraitScale" to="Split/RightSection/PortraitSettingsSection/Layout" method="_on_portrait_scale_value_changed"]
|
||||
[connection signal="value_changed" from="Split/RightSection/PortraitSettingsSection/Layout/Flow/HBoxContainer2/PortraitOffsetX" to="Split/RightSection/PortraitSettingsSection/Layout" method="_on_portrait_offset_x_value_changed"]
|
||||
[connection signal="value_changed" from="Split/RightSection/PortraitSettingsSection/Layout/Flow/HBoxContainer2/PortraitOffsetY" to="Split/RightSection/PortraitSettingsSection/Layout" method="_on_portrait_offset_y_value_changed"]
|
||||
[connection signal="toggled" from="Split/RightSection/PortraitSettingsSection/Layout/Flow/MirrorOption/PortraitMirror" to="Split/RightSection/PortraitSettingsSection/Layout" method="_on_portrait_mirror_toggled"]
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
@tool
|
||||
class_name DialogicCharacterEditorMainTab
|
||||
extends Control
|
||||
|
||||
## Base class for all character editor main tabs. Methods should be overriden.
|
||||
|
||||
|
||||
# Emit this, if something changed
|
||||
signal changed
|
||||
|
||||
|
||||
var character_editor:Control
|
||||
|
||||
|
||||
func _load_character(resource:DialogicCharacter) -> void:
|
||||
pass
|
||||
|
||||
|
||||
func _save_changes(resource:DialogicCharacter) -> DialogicCharacter:
|
||||
return resource
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
@tool
|
||||
class_name DialogicCharacterEditorPortraitSettingsTab
|
||||
extends ScrollContainer
|
||||
|
||||
## Base class for all portrait settings tabs. Methods should be overriden.
|
||||
|
||||
# Emit this, if something changed
|
||||
signal changed
|
||||
signal update_preview
|
||||
|
||||
var character_editor:Control
|
||||
|
||||
var selected_item :TreeItem = null
|
||||
|
||||
func _load_portrait_data(data:Dictionary) -> void:
|
||||
pass
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
@tool
|
||||
extends Tree
|
||||
|
||||
## Tree that displays the portrait list as a hirarchy
|
||||
|
||||
var editor := find_parent('Character Editor')
|
||||
var current_group_nodes := {}
|
||||
|
||||
|
||||
func clear_tree() -> void:
|
||||
clear()
|
||||
current_group_nodes = {}
|
||||
|
||||
|
||||
func add_portrait_item(portrait_name:String, portrait_data:Dictionary, parent_item:TreeItem) -> TreeItem:
|
||||
var item :TreeItem = %PortraitTree.create_item(parent_item)
|
||||
item.set_text(0, portrait_name)
|
||||
item.set_metadata(0, portrait_data)
|
||||
if portrait_name == editor.current_resource.default_portrait:
|
||||
item.add_button(0, get_theme_icon('Favorites', 'EditorIcons'), 2, true, 'Default')
|
||||
item.add_button(0, get_theme_icon('Duplicate', 'EditorIcons'), 3, false, 'Duplicate')
|
||||
item.add_button(0, get_theme_icon('Remove', 'EditorIcons'), 1, false, 'Remove')
|
||||
return item
|
||||
|
||||
|
||||
func add_portrait_group(goup_name:String = "Group", parent_item:TreeItem = get_root()):
|
||||
var item :TreeItem = %PortraitTree.create_item(parent_item)
|
||||
item.set_icon(0, get_theme_icon("Groups", "EditorIcons"))
|
||||
item.set_text(0, goup_name)
|
||||
item.set_metadata(0, {'group':true})
|
||||
item.add_button(0, get_theme_icon('Remove', 'EditorIcons'), 1, false, 'Remove')
|
||||
return item
|
||||
|
||||
|
||||
func get_full_item_name(item:TreeItem) -> String:
|
||||
var item_name := item.get_text(0)
|
||||
while item.get_parent() != get_root() and item != get_root():
|
||||
item_name = item.get_parent().get_text(0)+"/"+item_name
|
||||
item = item.get_parent()
|
||||
return item_name
|
||||
|
||||
|
||||
# Will create all not yet existing folders in the given path.
|
||||
# Returns the last folder (the parent of the portrait item of this path).
|
||||
func create_necessary_group_items(path:String) -> void:
|
||||
var last_item := get_root()
|
||||
var item_path := ""
|
||||
|
||||
for i in Array(path.split('/')).slice(0, -1):
|
||||
item_path += "/"+i
|
||||
item_path = item_path.trim_prefix('/')
|
||||
if current_group_nodes.has(item_path+"/"+i):
|
||||
last_item = current_group_nodes[item_path+"/"+i]
|
||||
else:
|
||||
var new_item := add_portrait_group(i, last_item)
|
||||
current_group_nodes[item_path+"/"+i] = new_item
|
||||
last_item = new_item
|
||||
return last_item
|
||||
|
||||
|
||||
################################################################################
|
||||
## DRAG AND DROP
|
||||
################################################################################
|
||||
|
||||
func _get_drag_data(position:Vector2) -> Variant:
|
||||
set_drop_mode_flags(DROP_MODE_ON_ITEM)
|
||||
|
||||
var preview := Label.new()
|
||||
preview.text = " "+get_selected().get_text(0)
|
||||
preview.add_theme_stylebox_override('normal', get_theme_stylebox("Background", "EditorStyles"))
|
||||
set_drag_preview(preview)
|
||||
|
||||
return get_selected()
|
||||
|
||||
|
||||
func _can_drop_data(position:Vector2, data:Variant) -> bool:
|
||||
return data is TreeItem
|
||||
|
||||
|
||||
func _drop_data(position:Vector2, item:Variant) -> void:
|
||||
var to_item := get_item_at_position(position)
|
||||
if to_item:
|
||||
var test_item:= to_item
|
||||
while true:
|
||||
if test_item == item:
|
||||
return
|
||||
test_item = test_item.get_parent()
|
||||
if test_item == get_root():
|
||||
break
|
||||
|
||||
var parent := get_root()
|
||||
if to_item:
|
||||
parent = to_item.get_parent()
|
||||
|
||||
if to_item and to_item.get_metadata(0).has('group'):
|
||||
parent = to_item
|
||||
|
||||
var new_item := copy_branch_or_item(item, parent)
|
||||
|
||||
if to_item and !to_item.get_metadata(0).has('group'):
|
||||
new_item.move_after(to_item)
|
||||
|
||||
item.free()
|
||||
|
||||
|
||||
func copy_branch_or_item(item:TreeItem, new_parent:TreeItem) -> TreeItem:
|
||||
var new_item :TreeItem = null
|
||||
if item.get_metadata(0).has('group'):
|
||||
new_item = add_portrait_group(item.get_text(0), new_parent)
|
||||
else:
|
||||
new_item = add_portrait_item(item.get_text(0), item.get_metadata(0), new_parent)
|
||||
|
||||
for child in item.get_children():
|
||||
copy_branch_or_item(child, new_item)
|
||||
return new_item
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
@tool
|
||||
extends DialogicCharacterEditorPortraitSettingsTab
|
||||
|
||||
## Tab that allows setting values of exported scene variables
|
||||
## for custom portrait scenes
|
||||
|
||||
var current_portrait_data := {}
|
||||
|
||||
func _ready() -> void:
|
||||
get_parent().set_tab_icon(get_index(), get_theme_icon("EditInternal", "EditorIcons"))
|
||||
add_theme_stylebox_override('panel', get_theme_stylebox("Background", "EditorStyles"))
|
||||
|
||||
func _load_portrait_data(data:Dictionary) -> void:
|
||||
if data.get('scene', '').is_empty():
|
||||
get_parent().set_tab_hidden(get_index(), true)
|
||||
else:
|
||||
get_parent().set_tab_hidden(get_index(), false)
|
||||
|
||||
current_portrait_data = data
|
||||
load_portrait_scene_export_variables()
|
||||
|
||||
func load_portrait_scene_export_variables():
|
||||
var scene = null
|
||||
if !current_portrait_data.get('scene', '').is_empty():
|
||||
scene = load(current_portrait_data.get('scene'))
|
||||
|
||||
if !scene:
|
||||
return
|
||||
|
||||
for child in $Grid.get_children():
|
||||
child.queue_free()
|
||||
|
||||
scene = scene.instantiate()
|
||||
for i in scene.script.get_script_property_list():
|
||||
if i['usage'] & PROPERTY_USAGE_EDITOR:
|
||||
var label = Label.new()
|
||||
label.text = i['name']
|
||||
label.add_theme_stylebox_override('normal', get_theme_stylebox("CanvasItemInfoOverlay", "EditorStyles"))
|
||||
label.size_flags_horizontal = SIZE_EXPAND_FILL
|
||||
$Grid.add_child(label)
|
||||
|
||||
|
||||
var input = null
|
||||
var current_value :String = ""
|
||||
if current_portrait_data.has('export_overrides') and current_portrait_data['export_overrides'].has(i['name']):
|
||||
current_value = current_portrait_data['export_overrides'][i['name']]
|
||||
match typeof(scene.get(i['name'])):
|
||||
TYPE_BOOL:
|
||||
input = CheckBox.new()
|
||||
if current_value:
|
||||
input.button_pressed = current_value == "true"
|
||||
input.toggled.connect(_on_export_bool_submitted.bind(i['name']))
|
||||
TYPE_COLOR:
|
||||
input = ColorPickerButton.new()
|
||||
if current_value:
|
||||
input.color = str_to_var(current_value)
|
||||
input.color_changed.connect(_on_export_color_submitted.bind(i['name']))
|
||||
input.custom_minimum_size.x = DialogicUtil.get_editor_scale()*50
|
||||
TYPE_INT:
|
||||
if i['hint'] & PROPERTY_HINT_ENUM:
|
||||
input = OptionButton.new()
|
||||
for x in i['hint_string'].split(','):
|
||||
input.add_item(x)
|
||||
input.item_selected.connect(_on_export_int_enum_submitted.bind(i['name']))
|
||||
_:
|
||||
input = LineEdit.new()
|
||||
if current_value:
|
||||
input.text = current_value
|
||||
input.text_submitted.connect(_on_export_input_text_submitted.bind(i['name']))
|
||||
input.size_flags_horizontal = SIZE_EXPAND_FILL
|
||||
$Grid.add_child(input)
|
||||
if i['usage'] & PROPERTY_USAGE_GROUP:
|
||||
var title := Label.new()
|
||||
title.text = i['name']
|
||||
title.add_theme_stylebox_override('normal', get_theme_stylebox("ContextualToolbar", "EditorStyles"))
|
||||
$Grid.add_child(title)
|
||||
$Grid.add_child(Control.new())
|
||||
|
||||
func set_export_override(property_name:String, value:String = "") -> void:
|
||||
var data:Dictionary = selected_item.get_metadata(0)
|
||||
if !data.has('export_overrides'):
|
||||
data['export_overrides'] = {}
|
||||
|
||||
if !value.is_empty():
|
||||
data['export_overrides'][property_name] = value
|
||||
else:
|
||||
data['export_overrides'].erase(property_name)
|
||||
|
||||
changed.emit()
|
||||
update_preview.emit()
|
||||
|
||||
func _on_export_input_text_submitted(text:String, property_name:String) -> void:
|
||||
set_export_override(property_name, var_to_str(text))
|
||||
|
||||
func _on_export_bool_submitted(value:bool, property_name:String) -> void:
|
||||
set_export_override(property_name, var_to_str(value))
|
||||
|
||||
func _on_export_color_submitted(color:Color, property_name:String) -> void:
|
||||
set_export_override(property_name, var_to_str(color))
|
||||
|
||||
func _on_export_int_enum_submitted(item:int, property_name:String) -> void:
|
||||
set_export_override(property_name, var_to_str(item))
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
@tool
|
||||
extends DialogicCharacterEditorPortraitSettingsTab
|
||||
|
||||
## Tab that allows setting an image file on a portrait.
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
get_parent().set_tab_icon(get_index(), get_theme_icon('Image', 'EditorIcons'))
|
||||
%ImagePicker.file_filter = "*.png, *.svg"
|
||||
%ImagePicker.resource_icon = get_theme_icon('Image', 'EditorIcons')
|
||||
|
||||
|
||||
func _load_portrait_data(data:Dictionary) -> void:
|
||||
# hides/shows this tab based on the scene value of this portrait
|
||||
# (only shown if the default scene is used)
|
||||
get_parent().set_tab_hidden(get_index(), !data.get('scene', '').is_empty())
|
||||
|
||||
%ImagePicker.set_value(data.get('image', ''))
|
||||
|
||||
|
||||
func _on_image_picker_value_changed(prop_name:String, value:String):
|
||||
var data:Dictionary = selected_item.get_metadata(0)
|
||||
data['image'] = value
|
||||
changed.emit()
|
||||
update_preview.emit()
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
@tool
|
||||
extends DialogicCharacterEditorPortraitSettingsTab
|
||||
|
||||
## Tab that allows setting size, offset and mirror of a portrait.
|
||||
|
||||
|
||||
func _ready():
|
||||
get_parent().set_tab_icon(get_index(), get_theme_icon('EditorPivot', 'EditorIcons'))
|
||||
|
||||
|
||||
func _load_portrait_data(data:Dictionary) -> void:
|
||||
%PortraitScale.value = data.get('scale', 1.0)*100
|
||||
%PortraitOffsetX.value = data.get('offset', Vector2()).x
|
||||
%PortraitOffsetY.value = data.get('offset', Vector2()).y
|
||||
%PortraitMirror.button_pressed = data.get('mirror', false)
|
||||
|
||||
|
||||
func _on_portrait_scale_value_changed(value) -> void:
|
||||
var data:Dictionary = selected_item.get_metadata(0)
|
||||
data['scale'] = value/100.0
|
||||
update_preview.emit()
|
||||
changed.emit()
|
||||
|
||||
|
||||
func _on_portrait_offset_x_value_changed(value) -> void:
|
||||
var data:Dictionary = selected_item.get_metadata(0)
|
||||
data['offset'] = data.get('offset', Vector2())
|
||||
data['offset'].x = value
|
||||
update_preview.emit()
|
||||
changed.emit()
|
||||
|
||||
func _on_portrait_offset_y_value_changed(value)-> void:
|
||||
var data:Dictionary = selected_item.get_metadata(0)
|
||||
data['offset'] = data.get('offset', Vector2())
|
||||
data['offset'].y = value
|
||||
update_preview.emit()
|
||||
changed.emit()
|
||||
|
||||
func _on_portrait_mirror_toggled(button_pressed:bool)-> void:
|
||||
var data:Dictionary = selected_item.get_metadata(0)
|
||||
data['mirror'] = button_pressed
|
||||
update_preview.emit()
|
||||
changed.emit()
|
||||