diff --git a/addons/escoria-core/game/core-scripts/esc/commands/set_animations.gd b/addons/escoria-core/game/core-scripts/esc/commands/set_animations.gd index ae62d2f0..627008bf 100644 --- a/addons/escoria-core/game/core-scripts/esc/commands/set_animations.gd +++ b/addons/escoria-core/game/core-scripts/esc/commands/set_animations.gd @@ -40,6 +40,9 @@ func validate(arguments: Array): % [get_command_name(), arguments[1]] ) return false + + (escoria.object_manager.get_object(arguments[0]).node as ESCPlayer).validate_animations(load(arguments[1])) + return true diff --git a/addons/escoria-core/game/core-scripts/esc_item.gd b/addons/escoria-core/game/core-scripts/esc_item.gd index 8cde1d9b..a3a8f1c1 100644 --- a/addons/escoria-core/game/core-scripts/esc_item.gd +++ b/addons/escoria-core/game/core-scripts/esc_item.gd @@ -147,7 +147,7 @@ export(NodePath) var camera_node # ESCAnimationsResource (for walking, idling...) -var animations: ESCAnimationResource +var animations: ESCAnimationResource setget set_animations # Reference to the animation node (null if none was found) var animation_sprite = null @@ -171,6 +171,9 @@ var _animation_player: ESCAnimationPlayer = null # Whether to force regsitration with the object manager. Defaults to false. var _force_registration: bool = false +# Warnings for scene. +var _scene_warnings: PoolStringArray = [] + # Add the movable node, connect signals, detect child nodes # and register this item @@ -183,6 +186,8 @@ func _ready(): # items in a scene tree. add_to_group(GROUP_ITEM_CAN_COLLIDE) + validate_animations(animations) + if not self.is_connected("mouse_entered", self, "_on_mouse_entered"): connect("mouse_entered", self, "_on_mouse_entered") if not self.is_connected("mouse_exited", self, "_on_mouse_exited"): @@ -341,6 +346,12 @@ func _unhandled_input(input_event: InputEvent) -> void: get_tree().set_input_as_handled() +# To display warnings in the scene tree should there be any. +func _get_configuration_warning(): + validate_animations(animations) + return _scene_warnings.join("\n") + + func _is_in_shape(position: Vector2) -> bool: var colliders = get_world_2d().direct_space_state.intersect_point( position, @@ -358,6 +369,61 @@ func _is_in_shape(position: Vector2) -> bool: return false +# Validates the ESCAnimationResource if it exists. Note that we pass in the +# ESCAnimationResource as an argument so that it can also be used to validate +# an ESCAnimationResource prior to being set. +# +# #### Parameters +# +# - animation_resource: the ESCAnimationResource to validate. +func validate_animations(animations_resource: ESCAnimationResource) -> void: + if not is_instance_valid(animations_resource): + return + + # This initialization must always be here since this is a tool script. + _scene_warnings = [] + + if is_instance_valid(animations_resource): + _validate_animations_property_all_not_null(animations_resource.dir_angles, "dir_angles") + + var num_dir_angles = animations_resource.dir_angles.size() + + if animations_resource.directions.size() != num_dir_angles: + _scene_warnings.append("%s animation angles specified but %s 'directions' animation(s) given." \ + % [num_dir_angles, animations_resource.directions.size()]) + else: + _validate_animations_property_all_not_null(animations_resource.directions, "directions") + + if animations_resource.idles.size() != num_dir_angles: + _scene_warnings.append("%s animation angles specified but %s 'idles' animation(s) given." \ + % [num_dir_angles, animations_resource.idles.size()]) + else: + _validate_animations_property_all_not_null(animations_resource.idles, "idles") + + if animations_resource.speaks.size() != num_dir_angles: + _scene_warnings.append("%s animation angles specified but %s 'speaks' animation(s) given." \ + % [num_dir_angles, animations_resource.speaks.size()]) + else: + _validate_animations_property_all_not_null(animations_resource.speaks, "speaks") + + if Engine.is_editor_hint(): + update_configuration_warning() + elif _scene_warnings.size() > 0: + escoria.logger.error( + self, + _scene_warnings.join(", ") + ) + + +# Setter for the animations property. +func set_animations(p_animations: ESCAnimationResource) -> void: + if p_animations == null: + return + + animations = p_animations + + if not animations.is_connected("changed", self, "_validate_animations"): + animations.connect("changed", self, "_validate_animations") # Return the animation player node @@ -589,7 +655,7 @@ func get_camera_node(): escoria.logger.debug( self, "Camera node found - directing camera to the camera_node on %s." - % global_id + % global_id ) return get_node(camera_node) return self @@ -653,3 +719,23 @@ func _get_inventory_texture() -> Texture: return null else: return inventory_texture + + +# Checks whether the given ESCAnimationResource property array has all non-null entries, and adds +# to the scene's warnings if not. +# +# #### Parameters +# +# - property: ESCAnimationResource property. Must be an array. +# - property_name: the name of the property being passed in. +func _validate_animations_property_all_not_null(property: Array, property_name: String) -> void: + var has_empty_entry: bool = false + + for item in property: + if item == null: + has_empty_entry = true + break + + if has_empty_entry: + _scene_warnings.append("At least one entry in '%s' is empty." % property_name) + diff --git a/addons/escoria-core/game/core-scripts/resources/esc_animationresource.gd b/addons/escoria-core/game/core-scripts/resources/esc_animationresource.gd index d11b57b3..3e339c96 100644 --- a/addons/escoria-core/game/core-scripts/resources/esc_animationresource.gd +++ b/addons/escoria-core/game/core-scripts/resources/esc_animationresource.gd @@ -10,21 +10,59 @@ class_name ESCAnimationResource # start_angle must be between 0 and 360. # Angles 0 and 360 are the same and correspond to UP/NORTH, # 90 is RIGHT/EAST, 180 is DOWN/SOUTH, 270 is LEFT/WEST etc. -export(Array, Resource) var dir_angles: Array = [] +export(Array, Resource) var dir_angles: Array = [] setget set_dir_angles # Array of animations for each direction, from UP to RIGHT_UP clockwise # [animation_name, scale]: scale parameter can be set to -1 to mirror # the animation -export(Array, Resource) var directions: Array = [] - +export(Array, Resource) var directions: Array = [] setget set_directions # Array containing the idle animations for each direction (in the # order defined by dir_angles): scale parameter can be set to -1 to mirror # the animation -export(Array, Resource) var idles: Array = [] +export(Array, Resource) var idles: Array = [] setget set_idles # Array containing the speak animations for each direction (in the # order defined by dir_angles): scale parameter can be set to -1 to mirror # the animation -export(Array, Resource) var speaks: Array = [] +export(Array, Resource) var speaks: Array = [] setget set_speaks + +# Sets the dir_angles property. +# +# #### Parameters +# +# - p_dir_angles: array of direction angle resources to set. +func set_dir_angles(p_dir_angles: Array) -> void: + dir_angles = p_dir_angles + emit_changed() + + +# Sets the directions property. +# +# #### Parameters +# +# - p_directions: array of direction resources to set. +func set_directions(p_set_directions: Array) -> void: + directions = p_set_directions + emit_changed() + + +# Sets the idles property. +# +# #### Parameters +# +# - p_set_idles: array of idle resources to set. +func set_idles(p_set_idles: Array) -> void: + idles = p_set_idles + emit_changed() + + +# Sets the speaks property. +# +# #### Parameters +# +# - p_set_idles: array of speak resources to set. +func set_speaks(p_set_speaks: Array) -> void: + speaks = p_set_speaks + emit_changed()