Skip to content

Commit

Permalink
refs #37: Rooms Aseprite importer preparatory code and PopochiuObject…
Browse files Browse the repository at this point in the history
…s creation refactoring (#64)

* refs #37: Squashed Room Importer and Objects creation refactoring.

refs #37: Preparation for refactoring of importer code.

refs #37: The plugin is now ready for the implementation of the Room importing logic.

refs #37: WIP refactoring the prop creation logic. It works but needs heavy polishing.

refs #37: Open #67 about the heavy polishing, in the meantime this reduce the passing-over of UI elements.

refs #37: Changed commit language.

refs #37: Refactored the region creation following the new template. Added a base class to limit code-duplication.

refs #37: Refactored the walkable areas too.

refs #37: Refactored the hotspots creation too.

refs #37: Project file updated to Godot 4.1

refs #37: Rooms creation have been refactored.

refs #37: Characters creation have been refactored.

refs #37: Inventory items creation have been refactored.

refs #37: Dialog creation have been refactored.

refs #37: some code dedup on type helpers.

refs #37: code dedup in helpers should be over. Also addressed annoying Godot 4.1 bug.

refs #37: all creation helpers have been renamed to factories.

refs #37: factories init methods changed to real constructors.

refs #37: renamed and added internal factories variables for better readability.

refs #37: Fixed inconsistency in inventory items file naming.

refs #37: Renamed local variables in factory classes.

refs #37: Switch to a success state return strategy to bubble feedback up to the popup.

refs #37: Fixed wrong ownership for room objects children bug.

refs #37: Cleaned up useless and hard-to-maintain references to the obj types in comments. Addresses PR comments by @carenalga.

refs #37: Renamed factories private members to reduce unnecessary verbosity.

refs #37: Solved error message about invalid ownership when creating room objects.

refs #37: WIP for rooms importer. This code is broken!

refs #37: WIP for rooms importer. This code is broken!

refs #37: WIP for rooms importer. This code is broken!

refs #37: A lot of code rewriting to make the base feature work. It works :)

refs #37: More work to get rid of strange execution errors.

refs #37: moving stuff in a desperate attempt to make this working in a predictable way.

refs #37: changed the importing flow strategy to circumvent a nasty error on linux with filesystem signals availability.

refs #37: off-by-one error fixed in animation frames range.

refs #37: fixed an error I probably inherited from another branch.

TEST1: messing with importer.

refs #37: worked around the console error when packing a scene with not-owned children (please see Godot GH-81982 for details).

refs #37: visibility toggle in the importer interface is now working as intended.

refs #37: all props now have an attached script, even the non-clickable ones; the importer is now able to update visibility and clickability status of already imported props.

refs #37: InteractionPolygons for props and hotspots have been moved in the scene and an edit button has been exposed in the toolbar to edit them.

Fix missing images when importing room from Aseprite (#110)

Store polygon for interaction in @export var

This will allow to keep the data of the polygon in the .tscn file of the
room so the InteractionPolygon.polygon data of each prop is overwritten
by the room both in runtime and while editing the room.

NOTE: The polygon of the InteractionPolygon in each prop is not saved in
the prop's scene file yet.

Add comments and broke some long lines

refs #37: InteractionPolygons icon is now more readable.

Fixes for post-import error message (#126)

* Fix Output error after importing room

* Fix error message after closing room import popup

The problem was caused because the popup was being added to the Room
tree. Now it is added to Popochiu's main dock.

* Temporary fix for bad row naming after import

* Imported prop image is placed in the prop's folder

Fix Aseprite importer section not showing in Inspector for room

refs #37: Added a feature flag to Editor Settings, to disable Aseprite Importer for those who does not use Aseprite.

refs #37: Fixed a warning related to EditorInterface being a singleton in 4.2.

* refs #37: Removed leftovers after squashing and rebasing over 2.0

* Fix post-rebase issues about main_dock

---------

Co-authored-by: carenalga <[email protected]>
  • Loading branch information
stickgrinder and mapedorr authored Dec 28, 2023
1 parent 38df124 commit 87c52d8
Show file tree
Hide file tree
Showing 50 changed files with 1,856 additions and 1,102 deletions.
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

0 comments on commit 87c52d8

Please sign in to comment.