refactor: separate content from presentation assets

This commit is contained in:
Rijad Zuzo
2026-08-12 21:41:33 +02:00
parent 5a70aaaded
commit bf4bf73675
22 changed files with 480 additions and 70 deletions
+3 -34
View File
@@ -8,13 +8,8 @@ const BEHAVIOR_GRAZER_ROUTINE := &"grazer_routine"
@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 var presentation_cue_id: StringName
@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]:
@@ -29,36 +24,10 @@ func validate() -> Array[String]:
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_valid_stable_id(presentation_cue_id):
errors.append("presentation_cue_id 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):
+21 -4
View File
@@ -14,10 +14,12 @@ const CONFIGURATION_KEYS := [
var last_error := ""
var _catalog: AnimalCatalog
var _presentation_catalog: PresentationCatalog
func _init(catalog: AnimalCatalog = null) -> void:
func _init(catalog: AnimalCatalog = null, presentation_catalog: PresentationCatalog = null) -> void:
_catalog = catalog if catalog != null else AnimalCatalog.create_core()
_presentation_catalog = presentation_catalog
if _catalog == null:
last_error = "Animal catalog could not be loaded"
elif not _catalog.rebuild().is_empty():
@@ -28,6 +30,10 @@ func get_catalog() -> AnimalCatalog:
return _catalog
func get_presentation_catalog() -> PresentationCatalog:
return _presentation_catalog
func create_presentation(
animal_definition_id: StringName,
animal_id: StringName,
@@ -44,15 +50,26 @@ func create_presentation(
if not _configuration_is_valid(configuration):
return null
var definition := _catalog.get_definition(animal_definition_id)
var presentation_scene := load(definition.presentation_scene_path) as PackedScene
if _presentation_catalog == null:
return _fail("Presentation catalog must be injected before creating presentation")
if not _presentation_catalog.is_valid():
return _fail("Injected presentation catalog is invalid")
var cue := _presentation_catalog.get_definition(definition.presentation_cue_id)
if cue == null:
return _fail(
(
"Unknown presentation cue '%s' for '%s'"
% [definition.presentation_cue_id, animal_definition_id]
)
)
var node := (
presentation_scene.instantiate() as AnimalNode if presentation_scene != null else null
_presentation_catalog.instantiate_scene(definition.presentation_cue_id) as AnimalNode
)
if node == null:
return _fail(
"Presentation scene for '%s' must instantiate AnimalNode" % animal_definition_id
)
if not node.apply_animal_definition(definition):
if not node.apply_animal_definition(definition, cue.get_parameters()):
node.free()
return _fail("Presentation rejected definition '%s'" % animal_definition_id)
node.animal_id = animal_id
@@ -9,10 +9,5 @@ species_id = &"goat"
display_name = "Domestic goat"
behavior_id = &"grazer_routine"
supported_action_ids = Array[StringName]([&"feed_animal"])
presentation_scene_path = "res://world/animals/grazer/cozy_grazer.tscn"
presentation_cue_id = &"animal.domestic_goat"
movement_speed = 1.6
visual_scale = Vector3(1, 1, 1)
body_scale = Vector3(1, 1, 1)
fluff_scale = Vector3(1, 1, 1)
show_horns = true
show_beard = true
@@ -9,10 +9,5 @@ species_id = &"sheep"
display_name = "Domestic sheep"
behavior_id = &"grazer_routine"
supported_action_ids = Array[StringName]([&"feed_animal"])
presentation_scene_path = "res://world/animals/grazer/cozy_grazer.tscn"
presentation_cue_id = &"animal.domestic_sheep"
movement_speed = 1.4
visual_scale = Vector3(1.08, 1.04, 1.08)
body_scale = Vector3(1.12, 1.08, 1.12)
fluff_scale = Vector3(1.16, 1.14, 1.16)
show_horns = false
show_beard = false
+11 -2
View File
@@ -148,7 +148,7 @@ func rebuild(
storages, items_by_id, actions_by_id, capability_tags_by_id, supported_cues, errors
)
_validate_enemy_references(enemies, items_by_id, errors)
_validate_animal_references(animals, actions_by_id, errors)
_validate_animal_references(animals, actions_by_id, supported_cues, errors)
errors.sort()
_errors = errors
if not errors.is_empty():
@@ -708,9 +708,18 @@ static func _validate_enemy_references(
static func _validate_animal_references(
animals: Array[AnimalDefinition], actions_by_id: Dictionary, errors: Array[String]
animals: Array[AnimalDefinition],
actions_by_id: Dictionary,
supported_cues: Dictionary,
errors: Array[String]
) -> void:
for definition in animals:
_validate_presentation_cue(
"Animal '%s'" % definition.animal_definition_id,
definition.presentation_cue_id,
supported_cues,
errors
)
for action_id in definition.supported_action_ids:
if not actions_by_id.has(action_id):
errors.append(
@@ -6,6 +6,8 @@ extends RefCounted
const ITEM_FOOD := &"item.food"
const ITEM_HERBS := &"item.herbs"
const ITEM_WOOD := &"item.wood"
const ANIMAL_DOMESTIC_GOAT := &"animal.domestic_goat"
const ANIMAL_DOMESTIC_SHEEP := &"animal.domestic_sheep"
const RESOURCE_BERRY_PATCH := &"resource.berry_patch"
const RESOURCE_HERB_PATCH := &"resource.herb_patch"
const RESOURCE_TREE := &"resource.tree"
@@ -16,6 +18,8 @@ const STORAGE_WOODPILE := &"storage.woodpile"
static func get_all() -> Array[StringName]:
var cue_ids: Array[StringName] = [
ANIMAL_DOMESTIC_GOAT,
ANIMAL_DOMESTIC_SHEEP,
ITEM_FOOD,
ITEM_HERBS,
ITEM_WOOD,