Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refs #37: Rooms Aseprite importer preparatory code and PopochiuObjects creation refactoring #64

Merged
merged 3 commits into from
Dec 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Addons other than Popochiu
# (we use some for development)
# addons/**
# !addons/popochiu/**
addons/*
!addons/popochiu/

# Godot-specific ignores
.import/
Expand Down
19 changes: 19 additions & 0 deletions addons/popochiu/editor/config/config.gd
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@
extends RefCounted

# EDITOR SETTINGS
const _ASEPRITE_IMPORTER_ENABLED_KEY = 'popochiu/import/aseprite/enable_aseprite_importer'
const _ASEPRITE_COMMAND_KEY = 'popochiu/import/aseprite/command_path'
const _REMOVE_SOURCE_FILES_KEY = 'popochiu/import/aseprite/remove_json_file'

# PROJECT SETTINGS
const _DEFAULT_IMPORT_ENABLED = 'popochiu/import/aseprite/import_animation_by_default'
const _DEFAULT_LOOP_ENABLED = 'popochiu/import/aseprite/loop_animation_by_default'
const _DEFAULT_PROP_VISIBLE_ENABLED = 'popochiu/import/aseprite/new_props_visible_by_default'
const _DEFAULT_PROP_CLICKABLE_ENABLED = 'popochiu/import/aseprite/new_props_clickable_by_default'
const _DEFAULT_WIPE_OLD_ANIMS_ENABLED = 'popochiu/import/aseprite/wipe_old_animations'


Expand All @@ -20,20 +23,27 @@ var _plugin_icons: Dictionary
# ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ PUBLIC ░░░░
func initialize_editor_settings():
editor_settings = ei.get_editor_settings()
_initialize_editor_cfg(_ASEPRITE_IMPORTER_ENABLED_KEY, false, TYPE_BOOL)
_initialize_editor_cfg(_ASEPRITE_COMMAND_KEY, _default_command(), TYPE_STRING)
_initialize_editor_cfg(_REMOVE_SOURCE_FILES_KEY, true, TYPE_BOOL)


func initialize_project_settings():
_initialize_project_cfg(_DEFAULT_IMPORT_ENABLED, true, TYPE_BOOL)
_initialize_project_cfg(_DEFAULT_LOOP_ENABLED, true, TYPE_BOOL)
_initialize_project_cfg(_DEFAULT_PROP_VISIBLE_ENABLED, true, TYPE_BOOL)
_initialize_project_cfg(_DEFAULT_PROP_CLICKABLE_ENABLED, true, TYPE_BOOL)
_initialize_project_cfg(_DEFAULT_WIPE_OLD_ANIMS_ENABLED, true, TYPE_BOOL)

_set_icons()
ProjectSettings.save()


# ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ SET & GET ░░░░
func aseprite_importer_enabled() -> bool:
return _get_editor_setting(_ASEPRITE_IMPORTER_ENABLED_KEY, false)


func get_command() -> String:
return _get_editor_setting(_ASEPRITE_COMMAND_KEY, _default_command())

Expand All @@ -54,6 +64,14 @@ func is_default_animation_loop_enabled() -> bool:
return _get_project_setting(_DEFAULT_LOOP_ENABLED, true)


func is_default_animation_prop_visible() -> bool:
return _get_project_setting(_DEFAULT_PROP_VISIBLE_ENABLED, true)


func is_default_animation_prop_clickable() -> bool:
return _get_project_setting(_DEFAULT_PROP_CLICKABLE_ENABLED, true)


func is_default_wipe_old_anims_enabled() -> bool:
return _get_project_setting(_DEFAULT_WIPE_OLD_ANIMS_ENABLED, true)

Expand Down Expand Up @@ -81,6 +99,7 @@ func _initialize_editor_cfg(key: String, default_value, type: int, hint: int = P
"hint": hint,
})


func _initialize_project_cfg(key: String, default_value, type: int, hint: int = PROPERTY_HINT_NONE):
if not ProjectSettings.has_setting(key):
ProjectSettings.set_setting(key, default_value)
Expand Down
56 changes: 41 additions & 15 deletions addons/popochiu/editor/config/result_codes.gd
Original file line number Diff line number Diff line change
@@ -1,28 +1,40 @@
@tool
extends RefCounted
class_name ResultCodes


const SUCCESS = 0
const ERR_ASEPRITE_CMD_NOT_FULL_PATH = 1
const ERR_ASEPRITE_CMD_NOT_FOUND = 2
const ERR_SOURCE_FILE_NOT_FOUND = 3
const ERR_OUTPUT_FOLDER_NOT_FOUND = 4
const ERR_ASEPRITE_EXPORT_FAILED = 5
const ERR_UNKNOWN_EXPORT_MODE = 6
const ERR_NO_VALID_LAYERS_FOUND = 7
const ERR_INVALID_ASEPRITE_SPRITESHEET = 8
const ERR_NO_ANIMATION_PLAYER_FOUND = 9
const ERR_NO_SPRITE_FOUND = 10
const ERR_UNNAMED_TAG_DETECTED = 11
const ERR_TAGS_OPTIONS_ARRAY_EMPTY = 12

enum {
## Base codes
FAILURE, # generic failure state
SUCCESS, # generic success state
## Aseprite importer errors
ERR_ASEPRITE_CMD_NOT_FULL_PATH,
ERR_ASEPRITE_CMD_NOT_FOUND,
ERR_SOURCE_FILE_NOT_FOUND,
ERR_OUTPUT_FOLDER_NOT_FOUND,
ERR_ASEPRITE_EXPORT_FAILED,
ERR_UNKNOWN_EXPORT_MODE,
ERR_NO_VALID_LAYERS_FOUND,
ERR_INVALID_ASEPRITE_SPRITESHEET,
ERR_NO_ANIMATION_PLAYER_FOUND,
ERR_NO_SPRITE_FOUND,
ERR_UNNAMED_TAG_DETECTED,
ERR_TAGS_OPTIONS_ARRAY_EMPTY,
## Popochiu Object factories errors
ERR_CANT_CREATE_OBJ_FOLDER,
ERR_CANT_CREATE_OBJ_STATE,
ERR_CANT_OPEN_OBJ_SCRIPT_TEMPLATE,
ERR_CANT_CREATE_OBJ_SCRIPT,
ERR_CANT_SAVE_OBJ_SCENE,
ERR_CANT_SAVE_OBJ_RESOURCE,
}


# ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ PUBLIC ░░░░
static func get_error_message(code: int):
## TODO: these messages are a bit dull, having params would be better.
## Maybe add a param argument
match code:
# Aseprite importers error messages
ERR_ASEPRITE_CMD_NOT_FULL_PATH:
return "Aseprite command not found at given path. Please check \"Editor Settings > Popochiu > Import > Command Path\" to hold the FULL path to a valid Aseprite executable."
ERR_ASEPRITE_CMD_NOT_FOUND:
Expand All @@ -45,5 +57,19 @@ static func get_error_message(code: int):
return "Unnamed tag detected"
ERR_TAGS_OPTIONS_ARRAY_EMPTY:
return "Tags options array is empty"
# Popochiu object factories error messages
ERR_CANT_CREATE_OBJ_FOLDER:
return "Can't create folder to host new Popochiu object"
ERR_CANT_CREATE_OBJ_STATE:
return "Can't create new Popochiu object's state resource (_state.tres, _state.gd)"
ERR_CANT_OPEN_OBJ_SCRIPT_TEMPLATE:
return "Can't open script template for new Popochiu object"
ERR_CANT_CREATE_OBJ_SCRIPT:
return "Can't create new Popochiu object's script file (.gd)"
ERR_CANT_SAVE_OBJ_SCENE:
return "Can't create new Popochiu object's scene (.tscn)"
ERR_CANT_SAVE_OBJ_RESOURCE:
return "Can't create new Popochiu object's resource (.tres)"
# Generic error message
_:
return "Import failed with code %d" % code
234 changes: 234 additions & 0 deletions addons/popochiu/editor/factories/factory_base_popochiu_obj.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
extends RefCounted

const BASE_STATE_TEMPLATE := 'res://addons/popochiu/engine/templates/%s_state_template.gd'
const BASE_SCRIPT_TEMPLATE := 'res://addons/popochiu/engine/templates/%s_template.gd'
const BASE_SCENE_PATH := 'res://addons/popochiu/engine/objects/%s/popochiu_%s.tscn'
const EMPTY_SCRIPT := 'res://addons/popochiu/engine/templates/empty_script_template.gd'

const Constants := preload('res://addons/popochiu/popochiu_resources.gd')
const MainDock := preload('res://addons/popochiu/editor/main_dock/popochiu_dock.gd')
const PopochiuObjectRow := preload('res://addons/popochiu/editor/main_dock/object_row/popochiu_object_row.gd')

var _main_dock: Panel = null

# The following variables are setup on creation
# Names variants and name parameter passed to
# the create method.
var _path_template := '' # always set by child class
var _snake_name := ''
var _pascal_name := ''
var _path_base := ''
var _path_scene = ''
var _path_resource = ''
var _path_state = ''
var _path_script := ''
# The following variables are setup by the sub-class constructor
# to define the type of object to be processed
# TODO: reduce this to just "type", too much redundancy
var _type := -1
var _type_label := ''
var _type_target := ''
# The following variable is needed because the room factory
# must set a property on the dock row if the room is the
# primary one.
# TODO: remove the need for this using signals #67
var _dock_row: PopochiuObjectRow
# The following variables are references to the elements
# generated for the creation of the new Popochiu object,
# such as resources, scenes, scripts, state scripts, etc
var _scene: Node
var _resource: Resource
var _state_resource: Resource
var _script: Resource


# ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ VIRTUAL ░░░░
func _init(main_dock: Panel) -> void:
_main_dock = main_dock


# ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ SET & GET ░░░░
func get_obj_scene() -> Node:
return _scene


func get_obj_resource() -> Resource:
return _resource


func get_state_resource() -> Resource:
return _state_resource


func get_obj_script() -> Resource:
return _script

# ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ PRIVATE ░░░
func _setup_name(obj_name: String) -> void:
_pascal_name = obj_name.to_pascal_case()
_snake_name = obj_name.to_snake_case()
_path_base = _path_template % [_snake_name, _snake_name]
_path_script = _path_base + '.gd'
_path_state = _path_base + '_state.gd'
_path_resource = _path_base + '.tres'
_path_scene = _path_base + '.tscn'


func _create_obj_folder() -> int:
# TODO: Check if another object was created in the same PATH.
# TODO: Remove created files if the creation process failed.
if DirAccess.make_dir_recursive_absolute(_path_base.get_base_dir()) != OK:
push_error(
'[Popochiu] Could not create %s directory: %s' %
[_path_base.get_base_dir(), _pascal_name]
)
return ResultCodes.ERR_CANT_CREATE_OBJ_FOLDER
return ResultCodes.SUCCESS


func _create_state_resource() -> int:
var state_template: Script = load(
BASE_STATE_TEMPLATE % _type_label
).duplicate()

if ResourceSaver.save(state_template, _path_state) != OK:
push_error(
'[Popochiu] Could not create %s state script: %s' %
[_type_label, _pascal_name]
)
return ResultCodes.FAILURE

_state_resource = load(_path_state).new()
_state_resource.script_name = _pascal_name
_state_resource.scene = _path_scene
_state_resource.resource_name = _pascal_name

if ResourceSaver.save(_state_resource, _path_resource) != OK:
push_error(
"[Popochiu] Couldn't create state resource for %s: %s" %
[_type_label, _pascal_name]
)
return ResultCodes.ERR_CANT_CREATE_OBJ_STATE

return ResultCodes.SUCCESS


func _copy_script_template() -> int:
var _script: Script = load(
BASE_SCRIPT_TEMPLATE % _type_label
).duplicate()

if ResourceSaver.save( _script, _path_script) != OK:
push_error(
"[Popochiu] Couldn't create %s script: %s" %
[_type_label, _path_script]
)
return ResultCodes.ERR_CANT_CREATE_OBJ_SCRIPT

return ResultCodes.SUCCESS


## Create the script for the object based on the template of its type.
func _create_script_from_template() -> int:
var script_template_file = FileAccess.open(
BASE_SCRIPT_TEMPLATE % _type_label, FileAccess.READ
)

if script_template_file == null:
push_error(
"[Popochiu] Couldn't read script template from %s" %
[BASE_SCRIPT_TEMPLATE % _type_label]
)
return ResultCodes.ERR_CANT_OPEN_OBJ_SCRIPT_TEMPLATE

var new_code: String = script_template_file.get_as_text()
script_template_file.close()

new_code = new_code.replace(
'%s_state_template' % _type_label,
'%s_%s_state' % [_type_label, _snake_name]
)

new_code = new_code.replace(
'Data = null',
"Data = load('%s.tres')" % _path_base
)

_script = load(EMPTY_SCRIPT).duplicate()
_script.source_code = new_code

if ResourceSaver.save( _script, _path_script) != OK:
push_error(
"[Popochiu] Couldn't create %s script: %s" %
[_type_label, _path_script]
)
return ResultCodes.ERR_CANT_CREATE_OBJ_SCRIPT

return ResultCodes.SUCCESS


func _save_obj_scene(obj: Node) -> int:
var packed_scene: PackedScene = PackedScene.new()
packed_scene.pack(obj)
if ResourceSaver.save(packed_scene, _path_scene) != OK:
push_error(
"[Popochiu] Couldn't create %s: %s" %
[_type_label, _path_script]
)
return ResultCodes.ERR_CANT_SAVE_OBJ_SCENE

# Load the scene to be get by the calling code
# Instancing the created .tscn file fixes #58
_scene = load(_path_scene).instantiate()

return ResultCodes.SUCCESS


func _save_obj_resource(obj: Resource) -> int:
if ResourceSaver.save(obj, _path_resource) != OK:
push_error(
"[Popochiu] Couldn't create %s: %s" %
[_type_label, _pascal_name]
)
return ResultCodes.ERR_CANT_SAVE_OBJ_RESOURCE

# Load the resource to be get by the calling code
_resource = load(_path_resource)

return ResultCodes.SUCCESS


## Makes a copy of the base scene for the object (e.g. popochiu_room.tscn,
## popochiu_inventory_item.tscn, popochiu_prop.tscn).
func _load_obj_base_scene() -> Node:
var obj = load(
BASE_SCENE_PATH % [_type_label, _type_label]
).instantiate()

# The script is assigned first so that other properties will not be
# overwritten by that assignment.
if _script != null:
obj.set_script(load(_path_script))

return obj


func _add_resource_to_popochiu() -> void:
# Add the created obj to Popochiu's correct list
if _main_dock.add_resource_to_popochiu(
_type_target,
ResourceLoader.load(_path_resource)
) != OK:
push_error(
"[Popochiu] Couldn't add the created %s to Popochiu: %s" %
[_type_label, _pascal_name]
)
return

# Add the object to the proper singleton
PopochiuResources.update_autoloads(true)

# Update the related list in the dock
_dock_row = (_main_dock as MainDock).add_to_list(
_type, _pascal_name
)
Loading