refactor: separate content from presentation assets
This commit is contained in:
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user