refactor: separate content from presentation assets
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
class_name PresentationCueDefinition
|
||||
extends Resource
|
||||
|
||||
const ALLOWED_PARAMETER_KEYS := {
|
||||
"visual_scale": true,
|
||||
"body_scale": true,
|
||||
"fluff_scale": true,
|
||||
"show_horns": true,
|
||||
"show_beard": true,
|
||||
}
|
||||
|
||||
@export var cue_id: StringName
|
||||
@export_file("*.tscn") var scene_path: String
|
||||
@export_file var icon_path: String
|
||||
@export_file var material_path: String
|
||||
@export var animation_hook_id: StringName
|
||||
@export var parameters: Dictionary = {}
|
||||
|
||||
|
||||
func validate_metadata() -> Array[String]:
|
||||
var errors: Array[String] = []
|
||||
if not AnimalDefinition.is_valid_stable_id(cue_id):
|
||||
errors.append("cue_id is invalid")
|
||||
_validate_optional_path(scene_path, "scene_path", ["tscn", "scn"], errors)
|
||||
_validate_optional_path(icon_path, "icon_path", ["png", "svg", "webp"], errors)
|
||||
_validate_optional_path(material_path, "material_path", ["tres", "res", "material"], errors)
|
||||
if (
|
||||
not animation_hook_id.is_empty()
|
||||
and not AnimalDefinition.is_valid_stable_id(animation_hook_id)
|
||||
):
|
||||
errors.append("animation_hook_id is invalid for '%s'" % cue_id)
|
||||
_validate_parameters(errors)
|
||||
return errors
|
||||
|
||||
|
||||
func validate_assets() -> Array[String]:
|
||||
var errors := validate_metadata()
|
||||
if not errors.is_empty():
|
||||
return errors
|
||||
for path_field in [
|
||||
["scene_path", scene_path, "PackedScene"],
|
||||
["icon_path", icon_path, "Texture2D"],
|
||||
["material_path", material_path, "Material"],
|
||||
]:
|
||||
var path: String = path_field[1]
|
||||
if not path.is_empty() and not ResourceLoader.exists(path, path_field[2]):
|
||||
errors.append("%s does not resolve for '%s'" % [path_field[0], cue_id])
|
||||
return errors
|
||||
|
||||
|
||||
func get_parameters() -> Dictionary:
|
||||
return parameters.duplicate(true)
|
||||
|
||||
|
||||
func instantiate_scene() -> Node:
|
||||
if scene_path.is_empty():
|
||||
return null
|
||||
var scene := load(scene_path) as PackedScene
|
||||
return scene.instantiate() if scene != null else null
|
||||
|
||||
|
||||
func _validate_parameters(errors: Array[String]) -> void:
|
||||
for raw_key in parameters:
|
||||
if raw_key is not String and raw_key is not StringName:
|
||||
errors.append("parameter key must be a string for '%s'" % cue_id)
|
||||
continue
|
||||
var key := String(raw_key)
|
||||
if not ALLOWED_PARAMETER_KEYS.has(key):
|
||||
errors.append("unsupported parameter '%s' for '%s'" % [key, cue_id])
|
||||
continue
|
||||
var value: Variant = parameters[raw_key]
|
||||
if key.ends_with("_scale"):
|
||||
if value is not Vector3 or not _is_positive_vector(value):
|
||||
errors.append("parameter '%s' must be a positive Vector3 for '%s'" % [key, cue_id])
|
||||
elif value is not bool:
|
||||
errors.append("parameter '%s' must be bool for '%s'" % [key, cue_id])
|
||||
|
||||
|
||||
func _validate_optional_path(
|
||||
path: String, field: String, extensions: Array[String], errors: Array[String]
|
||||
) -> void:
|
||||
if path.is_empty():
|
||||
return
|
||||
if not path.begins_with("res://") or path.get_extension().to_lower() not in extensions:
|
||||
errors.append("%s is invalid for '%s'" % [field, cue_id])
|
||||
|
||||
|
||||
func _is_positive_vector(value: Vector3) -> bool:
|
||||
return value.is_finite() and value.x > 0.0 and value.y > 0.0 and value.z > 0.0
|
||||
Reference in New Issue
Block a user