feat: define reusable animal archetypes
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
class_name AnimalFactory
|
||||
extends RefCounted
|
||||
|
||||
const CONFIGURATION_KEYS := [
|
||||
"initial_hunger",
|
||||
"initial_enabled",
|
||||
"can_npcs_feed",
|
||||
"can_player_feed",
|
||||
"debug_label_enabled",
|
||||
"initial_routine_site_id",
|
||||
"initial_next_routine_tick",
|
||||
]
|
||||
|
||||
var last_error := ""
|
||||
|
||||
var _catalog: AnimalCatalog
|
||||
|
||||
|
||||
func _init(catalog: AnimalCatalog = null) -> void:
|
||||
_catalog = catalog if catalog != null else AnimalCatalog.create_core()
|
||||
if _catalog == null:
|
||||
last_error = "Animal catalog could not be loaded"
|
||||
elif not _catalog.rebuild().is_empty():
|
||||
last_error = "; ".join(_catalog.get_errors())
|
||||
|
||||
|
||||
func get_catalog() -> AnimalCatalog:
|
||||
return _catalog
|
||||
|
||||
|
||||
func create_presentation(
|
||||
animal_definition_id: StringName,
|
||||
animal_id: StringName,
|
||||
display_name: String,
|
||||
configuration: Dictionary = {}
|
||||
) -> AnimalNode:
|
||||
last_error = ""
|
||||
if _catalog == null or not _catalog.has_definition(animal_definition_id):
|
||||
return _fail("Unknown animal definition '%s'" % animal_definition_id)
|
||||
if not AnimalDefinition.is_valid_stable_id(animal_id):
|
||||
return _fail("animal_id is invalid")
|
||||
if display_name.strip_edges().is_empty():
|
||||
return _fail("display_name is empty")
|
||||
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
|
||||
var node := (
|
||||
presentation_scene.instantiate() as AnimalNode if presentation_scene != null else null
|
||||
)
|
||||
if node == null:
|
||||
return _fail(
|
||||
"Presentation scene for '%s' must instantiate AnimalNode" % animal_definition_id
|
||||
)
|
||||
if not node.apply_animal_definition(definition):
|
||||
node.free()
|
||||
return _fail("Presentation rejected definition '%s'" % animal_definition_id)
|
||||
node.animal_id = animal_id
|
||||
node.display_name = display_name
|
||||
_apply_configuration(node, configuration)
|
||||
return node
|
||||
|
||||
|
||||
func _configuration_is_valid(configuration: Dictionary) -> bool:
|
||||
for key in configuration:
|
||||
if (key is not String and key is not StringName) or String(key) not in CONFIGURATION_KEYS:
|
||||
return _fail_bool("Unknown animal presentation configuration '%s'" % key)
|
||||
if configuration.has("initial_hunger"):
|
||||
if (
|
||||
configuration["initial_hunger"] is not int
|
||||
and configuration["initial_hunger"] is not float
|
||||
):
|
||||
return _fail_bool("initial_hunger must be numeric")
|
||||
var hunger := float(configuration["initial_hunger"])
|
||||
if not is_finite(hunger) or hunger < 0.0 or hunger > 100.0:
|
||||
return _fail_bool("initial_hunger must be between 0 and 100")
|
||||
if (
|
||||
configuration.has("initial_routine_site_id")
|
||||
and not AnimalDefinition.is_valid_stable_id(configuration["initial_routine_site_id"])
|
||||
):
|
||||
return _fail_bool("initial_routine_site_id is invalid")
|
||||
if configuration.has("initial_next_routine_tick"):
|
||||
if configuration["initial_next_routine_tick"] is not int:
|
||||
return _fail_bool("initial_next_routine_tick must be an int")
|
||||
if int(configuration["initial_next_routine_tick"]) < 0:
|
||||
return _fail_bool("initial_next_routine_tick must not be negative")
|
||||
for key in ["initial_enabled", "can_npcs_feed", "can_player_feed", "debug_label_enabled"]:
|
||||
if configuration.has(key) and configuration[key] is not bool:
|
||||
return _fail_bool("%s must be a bool" % key)
|
||||
return true
|
||||
|
||||
|
||||
func _apply_configuration(node: AnimalNode, configuration: Dictionary) -> void:
|
||||
if configuration.has("initial_hunger"):
|
||||
node.initial_hunger = float(configuration["initial_hunger"])
|
||||
if configuration.has("initial_enabled"):
|
||||
node.initial_enabled = bool(configuration["initial_enabled"])
|
||||
if configuration.has("can_npcs_feed"):
|
||||
node.can_npcs_feed = bool(configuration["can_npcs_feed"])
|
||||
if configuration.has("can_player_feed"):
|
||||
node.can_player_feed = bool(configuration["can_player_feed"])
|
||||
if configuration.has("debug_label_enabled"):
|
||||
node.debug_label_enabled = bool(configuration["debug_label_enabled"])
|
||||
if configuration.has("initial_routine_site_id"):
|
||||
node.initial_routine_site_id = StringName(configuration["initial_routine_site_id"])
|
||||
if configuration.has("initial_next_routine_tick"):
|
||||
node.initial_next_routine_tick = int(configuration["initial_next_routine_tick"])
|
||||
|
||||
|
||||
func _fail(message: String) -> AnimalNode:
|
||||
last_error = message
|
||||
return null
|
||||
|
||||
|
||||
func _fail_bool(message: String) -> bool:
|
||||
last_error = message
|
||||
return false
|
||||
Reference in New Issue
Block a user