feat: define reusable animal archetypes
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
class_name AnimalDefinition
|
||||
extends Resource
|
||||
|
||||
const BEHAVIOR_GRAZER_ROUTINE := &"grazer_routine"
|
||||
|
||||
@export var animal_definition_id: StringName
|
||||
@export var species_id: StringName
|
||||
@export var display_name: String
|
||||
@export var behavior_id: StringName = BEHAVIOR_GRAZER_ROUTINE
|
||||
@export var supported_action_ids: Array[StringName] = []
|
||||
@export_file("*.tscn") var presentation_scene_path: String
|
||||
@export_range(0.1, 10.0, 0.1) var movement_speed := 1.6
|
||||
@export var visual_scale := Vector3.ONE
|
||||
@export var body_scale := Vector3.ONE
|
||||
@export var fluff_scale := Vector3.ONE
|
||||
@export var show_horns := true
|
||||
@export var show_beard := true
|
||||
|
||||
|
||||
func validate() -> Array[String]:
|
||||
var errors: Array[String] = []
|
||||
if not is_valid_stable_id(animal_definition_id):
|
||||
errors.append("animal_definition_id is invalid")
|
||||
if not is_valid_stable_id(species_id):
|
||||
errors.append("species_id is invalid for '%s'" % animal_definition_id)
|
||||
if display_name.strip_edges().is_empty():
|
||||
errors.append("display_name is empty for '%s'" % animal_definition_id)
|
||||
if behavior_id != BEHAVIOR_GRAZER_ROUTINE:
|
||||
errors.append(
|
||||
"behavior_id '%s' is unsupported for '%s'" % [behavior_id, animal_definition_id]
|
||||
)
|
||||
if (
|
||||
presentation_scene_path.is_empty()
|
||||
or not presentation_scene_path.begins_with("res://")
|
||||
or presentation_scene_path.get_extension() != "tscn"
|
||||
or not ResourceLoader.exists(presentation_scene_path, "PackedScene")
|
||||
):
|
||||
errors.append("presentation_scene_path is invalid for '%s'" % animal_definition_id)
|
||||
if not is_finite(movement_speed) or movement_speed <= 0.0:
|
||||
errors.append("movement_speed must be positive for '%s'" % animal_definition_id)
|
||||
if (
|
||||
not visual_scale.is_finite()
|
||||
or visual_scale.x <= 0.0
|
||||
or visual_scale.y <= 0.0
|
||||
or visual_scale.z <= 0.0
|
||||
):
|
||||
errors.append("visual_scale must be finite and positive for '%s'" % animal_definition_id)
|
||||
if (
|
||||
not body_scale.is_finite()
|
||||
or body_scale.x <= 0.0
|
||||
or body_scale.y <= 0.0
|
||||
or body_scale.z <= 0.0
|
||||
):
|
||||
errors.append("body_scale must be finite and positive for '%s'" % animal_definition_id)
|
||||
if (
|
||||
not fluff_scale.is_finite()
|
||||
or fluff_scale.x <= 0.0
|
||||
or fluff_scale.y <= 0.0
|
||||
or fluff_scale.z <= 0.0
|
||||
):
|
||||
errors.append("fluff_scale must be finite and positive for '%s'" % animal_definition_id)
|
||||
var seen: Dictionary = {}
|
||||
for action_id in supported_action_ids:
|
||||
if not is_valid_stable_id(action_id):
|
||||
errors.append("supported action ID is invalid for '%s'" % animal_definition_id)
|
||||
elif seen.has(action_id):
|
||||
errors.append(
|
||||
"duplicate supported action '%s' for '%s'" % [action_id, animal_definition_id]
|
||||
)
|
||||
else:
|
||||
seen[action_id] = true
|
||||
return errors
|
||||
|
||||
|
||||
func supports_action(action_id: StringName) -> bool:
|
||||
return action_id in supported_action_ids
|
||||
|
||||
|
||||
static func is_valid_stable_id(value: Variant) -> bool:
|
||||
if value is not String and value is not StringName:
|
||||
return false
|
||||
var text := String(value)
|
||||
if text.is_empty() or text.length() > 96:
|
||||
return false
|
||||
for character in text:
|
||||
var code := character.unicode_at(0)
|
||||
if not (
|
||||
(code >= 48 and code <= 57)
|
||||
or (code >= 65 and code <= 90)
|
||||
or (code >= 97 and code <= 122)
|
||||
or character in ["_", "-", ".", ":"]
|
||||
):
|
||||
return false
|
||||
return true
|
||||
Reference in New Issue
Block a user