1077 lines
33 KiB
GDScript
1077 lines
33 KiB
GDScript
class_name ContentCatalog
|
|
extends RefCounted
|
|
|
|
const CORE_PACK_PATH := "res://simulation/definitions/packs/core.tres"
|
|
const DEFAULT_SITUATION_EVENT_PREDICATE_IDS: Array[StringName] = [
|
|
&"event_type", &"event_payload_equals", &"event_participant_id", &"event_pantry_supplied"
|
|
]
|
|
const DEFAULT_SITUATION_STATE_PREDICATE_IDS: Array[StringName] = [
|
|
&"state_number_lte", &"state_number_gte"
|
|
]
|
|
const DEFAULT_SITUATION_EXPIRY_PREDICATE_IDS: Array[StringName] = [
|
|
&"state_number_gt", &"age_reached"
|
|
]
|
|
|
|
var _valid := false
|
|
var _errors: Array[String] = []
|
|
var _packs: Array[SimulationContentPack] = []
|
|
var _actions: Array[ActionDefinition] = []
|
|
var _professions: Array[ProfessionDefinition] = []
|
|
var _capability_tags: Array[CapabilityTagDefinition] = []
|
|
var _items: Array[ItemDefinition] = []
|
|
var _resources: Array[ResourceDefinition] = []
|
|
var _storages: Array[StorageDefinition] = []
|
|
var _enemies: Array[EnemyDefinition] = []
|
|
var _animals: Array[AnimalDefinition] = []
|
|
var _situations: Array[SituationDefinition] = []
|
|
var _dialogue_intents: Array[ConversationIntentDefinition] = []
|
|
var _dialogue_template_catalogs: Array[ConversationTemplateCatalog] = []
|
|
var _packs_by_id: Dictionary = {}
|
|
var _actions_by_id: Dictionary = {}
|
|
var _professions_by_id: Dictionary = {}
|
|
var _capability_tags_by_id: Dictionary = {}
|
|
var _items_by_id: Dictionary = {}
|
|
var _resources_by_id: Dictionary = {}
|
|
var _storages_by_id: Dictionary = {}
|
|
var _enemies_by_id: Dictionary = {}
|
|
var _animals_by_id: Dictionary = {}
|
|
var _situations_by_id: Dictionary = {}
|
|
var _dialogue_intents_by_id: Dictionary = {}
|
|
var _dialogue_template_catalogs_by_id: Dictionary = {}
|
|
|
|
|
|
static func create_core(
|
|
supported_handler_ids: Array[StringName] = [],
|
|
supported_presentation_cue_ids: Array[StringName] = [],
|
|
supported_situation_event_predicate_ids: Array[StringName] = [],
|
|
supported_situation_state_predicate_ids: Array[StringName] = [],
|
|
supported_situation_expiry_predicate_ids: Array[StringName] = []
|
|
) -> ContentCatalog:
|
|
var catalog := ContentCatalog.new()
|
|
var pack := load(CORE_PACK_PATH) as SimulationContentPack
|
|
if pack == null:
|
|
catalog._errors = ["Core content pack failed to load from '%s'" % CORE_PACK_PATH]
|
|
return catalog
|
|
var packs: Array[SimulationContentPack] = [pack]
|
|
catalog.rebuild(
|
|
packs,
|
|
supported_handler_ids,
|
|
supported_presentation_cue_ids,
|
|
supported_situation_event_predicate_ids,
|
|
supported_situation_state_predicate_ids,
|
|
supported_situation_expiry_predicate_ids
|
|
)
|
|
return catalog
|
|
|
|
|
|
func rebuild(
|
|
content_packs: Array[SimulationContentPack],
|
|
supported_handler_ids: Array[StringName] = [],
|
|
supported_presentation_cue_ids: Array[StringName] = [],
|
|
supported_situation_event_predicate_ids: Array[StringName] = [],
|
|
supported_situation_state_predicate_ids: Array[StringName] = [],
|
|
supported_situation_expiry_predicate_ids: Array[StringName] = []
|
|
) -> Array[String]:
|
|
_clear_published_content()
|
|
var errors: Array[String] = []
|
|
var pack_list := content_packs.duplicate()
|
|
pack_list.sort_custom(_sort_packs)
|
|
var supported_handlers := {}
|
|
for handler_id in supported_handler_ids:
|
|
if handler_id.is_empty():
|
|
errors.append("Supported handler ID is empty")
|
|
elif supported_handlers.has(handler_id):
|
|
errors.append("Duplicate supported handler '%s'" % handler_id)
|
|
else:
|
|
supported_handlers[handler_id] = true
|
|
var cue_ids := (
|
|
supported_presentation_cue_ids
|
|
if not supported_presentation_cue_ids.is_empty()
|
|
else PresentationCueIds.get_all()
|
|
)
|
|
var supported_cues := {}
|
|
for cue_id in cue_ids:
|
|
if cue_id.is_empty():
|
|
errors.append("Supported presentation cue ID is empty")
|
|
elif supported_cues.has(cue_id):
|
|
errors.append("Duplicate supported presentation cue '%s'" % cue_id)
|
|
else:
|
|
supported_cues[cue_id] = true
|
|
var supported_event_predicates := _validated_id_set(
|
|
supported_situation_event_predicate_ids,
|
|
DEFAULT_SITUATION_EVENT_PREDICATE_IDS,
|
|
"situation event predicate",
|
|
errors
|
|
)
|
|
var supported_state_predicates := _validated_id_set(
|
|
supported_situation_state_predicate_ids,
|
|
DEFAULT_SITUATION_STATE_PREDICATE_IDS,
|
|
"situation state predicate",
|
|
errors
|
|
)
|
|
var supported_expiry_predicates := _validated_id_set(
|
|
supported_situation_expiry_predicate_ids,
|
|
DEFAULT_SITUATION_EXPIRY_PREDICATE_IDS,
|
|
"situation expiry predicate",
|
|
errors
|
|
)
|
|
|
|
var packs_by_id := {}
|
|
var actions_by_id := {}
|
|
var professions_by_id := {}
|
|
var capability_tags_by_id := {}
|
|
var items_by_id := {}
|
|
var resources_by_id := {}
|
|
var storages_by_id := {}
|
|
var enemies_by_id := {}
|
|
var animals_by_id := {}
|
|
var situations_by_id := {}
|
|
var dialogue_intents_by_id := {}
|
|
var dialogue_template_catalogs_by_id := {}
|
|
var actions: Array[ActionDefinition] = []
|
|
var professions: Array[ProfessionDefinition] = []
|
|
var capability_tags: Array[CapabilityTagDefinition] = []
|
|
var items: Array[ItemDefinition] = []
|
|
var resources: Array[ResourceDefinition] = []
|
|
var storages: Array[StorageDefinition] = []
|
|
var enemies: Array[EnemyDefinition] = []
|
|
var animals: Array[AnimalDefinition] = []
|
|
var situations: Array[SituationDefinition] = []
|
|
var dialogue_intents: Array[ConversationIntentDefinition] = []
|
|
var dialogue_template_catalogs: Array[ConversationTemplateCatalog] = []
|
|
|
|
for pack in pack_list:
|
|
if pack == null:
|
|
errors.append("Content pack list contains a null pack")
|
|
continue
|
|
for error in pack.validate():
|
|
errors.append("Pack '%s': %s" % [pack.pack_id, error])
|
|
if packs_by_id.has(pack.pack_id):
|
|
errors.append("Duplicate pack_id '%s'" % pack.pack_id)
|
|
else:
|
|
packs_by_id[pack.pack_id] = pack
|
|
_collect_actions(pack, actions, actions_by_id, errors)
|
|
_collect_professions(pack, professions, professions_by_id, errors)
|
|
_collect_capability_tags(pack, capability_tags, capability_tags_by_id, errors)
|
|
_collect_items(pack, items, items_by_id, errors)
|
|
_collect_resources(pack, resources, resources_by_id, errors)
|
|
_collect_storages(pack, storages, storages_by_id, errors)
|
|
_collect_enemies(pack, enemies, enemies_by_id, errors)
|
|
_collect_animals(pack, animals, animals_by_id, errors)
|
|
_collect_situations(pack, situations, situations_by_id, errors)
|
|
_collect_dialogue_intents(pack, dialogue_intents, dialogue_intents_by_id, errors)
|
|
_collect_dialogue_template_catalogs(
|
|
pack, dialogue_template_catalogs, dialogue_template_catalogs_by_id, errors
|
|
)
|
|
|
|
for pack in pack_list:
|
|
if pack == null:
|
|
continue
|
|
for required_pack_id in pack.required_pack_ids:
|
|
if not required_pack_id.is_empty() and not packs_by_id.has(required_pack_id):
|
|
errors.append(
|
|
(
|
|
"Pack '%s' references unknown required pack '%s'"
|
|
% [pack.pack_id, required_pack_id]
|
|
)
|
|
)
|
|
for required_handler_id in pack.required_handler_ids:
|
|
if (
|
|
not required_handler_id.is_empty()
|
|
and not supported_handlers.has(required_handler_id)
|
|
):
|
|
errors.append(
|
|
(
|
|
"Pack '%s' references unknown required handler '%s'"
|
|
% [pack.pack_id, required_handler_id]
|
|
)
|
|
)
|
|
|
|
_validate_action_references(
|
|
actions,
|
|
actions_by_id,
|
|
professions_by_id,
|
|
items_by_id,
|
|
capability_tags_by_id,
|
|
supported_handlers,
|
|
supported_cues,
|
|
errors
|
|
)
|
|
_validate_item_references(items, capability_tags_by_id, supported_cues, errors)
|
|
_validate_resource_references(
|
|
resources, items_by_id, actions_by_id, capability_tags_by_id, supported_cues, errors
|
|
)
|
|
_validate_storage_references(
|
|
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, supported_cues, errors)
|
|
_validate_situation_references(
|
|
situations,
|
|
actions_by_id,
|
|
items_by_id,
|
|
dialogue_intents_by_id,
|
|
supported_event_predicates,
|
|
supported_state_predicates,
|
|
supported_expiry_predicates,
|
|
errors
|
|
)
|
|
_validate_dialogue_references(
|
|
dialogue_intents, dialogue_intents_by_id, dialogue_template_catalogs_by_id, errors
|
|
)
|
|
errors.sort()
|
|
_errors = errors
|
|
if not errors.is_empty():
|
|
return get_errors()
|
|
|
|
actions.sort_custom(_sort_actions)
|
|
professions.sort_custom(_sort_professions)
|
|
capability_tags.sort_custom(_sort_capability_tags)
|
|
items.sort_custom(_sort_items)
|
|
resources.sort_custom(_sort_resources)
|
|
storages.sort_custom(_sort_storages)
|
|
enemies.sort_custom(_sort_enemies)
|
|
animals.sort_custom(_sort_animals)
|
|
situations.sort_custom(_sort_situations)
|
|
dialogue_intents.sort_custom(_sort_dialogue_intents)
|
|
dialogue_template_catalogs.sort_custom(_sort_dialogue_template_catalogs)
|
|
_packs = pack_list
|
|
_actions = actions
|
|
_professions = professions
|
|
_capability_tags = capability_tags
|
|
_items = items
|
|
_resources = resources
|
|
_storages = storages
|
|
_enemies = enemies
|
|
_animals = animals
|
|
_situations = situations
|
|
_dialogue_intents = dialogue_intents
|
|
_dialogue_template_catalogs = dialogue_template_catalogs
|
|
_packs_by_id = packs_by_id
|
|
_actions_by_id = actions_by_id
|
|
_professions_by_id = professions_by_id
|
|
_capability_tags_by_id = capability_tags_by_id
|
|
_items_by_id = items_by_id
|
|
_resources_by_id = resources_by_id
|
|
_storages_by_id = storages_by_id
|
|
_enemies_by_id = enemies_by_id
|
|
_animals_by_id = animals_by_id
|
|
_situations_by_id = situations_by_id
|
|
_dialogue_intents_by_id = dialogue_intents_by_id
|
|
_dialogue_template_catalogs_by_id = dialogue_template_catalogs_by_id
|
|
_valid = true
|
|
return []
|
|
|
|
|
|
func is_valid() -> bool:
|
|
return _valid
|
|
|
|
|
|
func get_errors() -> Array[String]:
|
|
return _errors.duplicate()
|
|
|
|
|
|
func get_packs() -> Array[SimulationContentPack]:
|
|
return _packs.duplicate()
|
|
|
|
|
|
func get_actions() -> Array[ActionDefinition]:
|
|
return _actions.duplicate()
|
|
|
|
|
|
func get_professions() -> Array[ProfessionDefinition]:
|
|
return _professions.duplicate()
|
|
|
|
|
|
func get_capability_tags() -> Array[CapabilityTagDefinition]:
|
|
return _capability_tags.duplicate()
|
|
|
|
|
|
func get_items() -> Array[ItemDefinition]:
|
|
return _items.duplicate()
|
|
|
|
|
|
func get_resources() -> Array[ResourceDefinition]:
|
|
return _resources.duplicate()
|
|
|
|
|
|
func get_storages() -> Array[StorageDefinition]:
|
|
return _storages.duplicate()
|
|
|
|
|
|
func get_enemies() -> Array[EnemyDefinition]:
|
|
return _enemies.duplicate()
|
|
|
|
|
|
func get_animals() -> Array[AnimalDefinition]:
|
|
return _animals.duplicate()
|
|
|
|
|
|
func get_situations() -> Array[SituationDefinition]:
|
|
return _situations.duplicate()
|
|
|
|
|
|
func get_dialogue_intents() -> Array[ConversationIntentDefinition]:
|
|
return _dialogue_intents.duplicate()
|
|
|
|
|
|
func get_dialogue_template_catalogs() -> Array[ConversationTemplateCatalog]:
|
|
return _dialogue_template_catalogs.duplicate()
|
|
|
|
|
|
func get_pack(pack_id: StringName) -> SimulationContentPack:
|
|
return _packs_by_id.get(pack_id) as SimulationContentPack
|
|
|
|
|
|
func get_action(action_id: StringName) -> ActionDefinition:
|
|
return _actions_by_id.get(action_id) as ActionDefinition
|
|
|
|
|
|
func get_profession(profession_id: StringName) -> ProfessionDefinition:
|
|
return _professions_by_id.get(profession_id) as ProfessionDefinition
|
|
|
|
|
|
func get_capability_tag(tag_id: StringName) -> CapabilityTagDefinition:
|
|
return _capability_tags_by_id.get(tag_id) as CapabilityTagDefinition
|
|
|
|
|
|
func get_item(item_id: StringName) -> ItemDefinition:
|
|
return _items_by_id.get(item_id) as ItemDefinition
|
|
|
|
|
|
func get_resource(resource_id: StringName) -> ResourceDefinition:
|
|
return _resources_by_id.get(resource_id) as ResourceDefinition
|
|
|
|
|
|
func get_storage(storage_id: StringName) -> StorageDefinition:
|
|
return _storages_by_id.get(storage_id) as StorageDefinition
|
|
|
|
|
|
func get_enemy(enemy_id: StringName) -> EnemyDefinition:
|
|
return _enemies_by_id.get(enemy_id) as EnemyDefinition
|
|
|
|
|
|
func get_animal(animal_definition_id: StringName) -> AnimalDefinition:
|
|
return _animals_by_id.get(animal_definition_id) as AnimalDefinition
|
|
|
|
|
|
func get_situation(situation_definition_id: StringName) -> SituationDefinition:
|
|
return _situations_by_id.get(situation_definition_id) as SituationDefinition
|
|
|
|
|
|
func get_dialogue_intent(intent_id: StringName) -> ConversationIntentDefinition:
|
|
return _dialogue_intents_by_id.get(intent_id) as ConversationIntentDefinition
|
|
|
|
|
|
func get_dialogue_template_catalog(catalog_id: StringName) -> ConversationTemplateCatalog:
|
|
return _dialogue_template_catalogs_by_id.get(catalog_id) as ConversationTemplateCatalog
|
|
|
|
|
|
func get_authoring_report() -> Dictionary:
|
|
return {
|
|
"valid": _valid,
|
|
"errors": get_errors(),
|
|
"packs": _ids_for(_packs, &"pack_id"),
|
|
"actions": _ids_for(_actions, &"action_id"),
|
|
"professions": _ids_for(_professions, &"profession_id"),
|
|
"capability_tags": _ids_for(_capability_tags, &"tag_id"),
|
|
"items": _ids_for(_items, &"item_id"),
|
|
"resources": _ids_for(_resources, &"resource_id"),
|
|
"storages": _ids_for(_storages, &"storage_id"),
|
|
"enemies": _ids_for(_enemies, &"enemy_id"),
|
|
"animals": _ids_for(_animals, &"animal_definition_id"),
|
|
"situations": _ids_for(_situations, &"situation_definition_id"),
|
|
"dialogue_intents": _ids_for(_dialogue_intents, &"intent_id"),
|
|
"dialogue_template_catalogs": _ids_for(_dialogue_template_catalogs, &"catalog_id"),
|
|
}
|
|
|
|
|
|
func _clear_published_content() -> void:
|
|
_valid = false
|
|
_errors.clear()
|
|
_packs.clear()
|
|
_actions.clear()
|
|
_professions.clear()
|
|
_capability_tags.clear()
|
|
_items.clear()
|
|
_resources.clear()
|
|
_storages.clear()
|
|
_enemies.clear()
|
|
_animals.clear()
|
|
_situations.clear()
|
|
_dialogue_intents.clear()
|
|
_dialogue_template_catalogs.clear()
|
|
_packs_by_id.clear()
|
|
_actions_by_id.clear()
|
|
_professions_by_id.clear()
|
|
_capability_tags_by_id.clear()
|
|
_items_by_id.clear()
|
|
_resources_by_id.clear()
|
|
_storages_by_id.clear()
|
|
_enemies_by_id.clear()
|
|
_animals_by_id.clear()
|
|
_situations_by_id.clear()
|
|
_dialogue_intents_by_id.clear()
|
|
_dialogue_template_catalogs_by_id.clear()
|
|
|
|
|
|
static func _collect_actions(
|
|
pack: SimulationContentPack,
|
|
definitions: Array[ActionDefinition],
|
|
definitions_by_id: Dictionary,
|
|
errors: Array[String]
|
|
) -> void:
|
|
for definition in pack.actions:
|
|
if definition == null:
|
|
continue
|
|
if definitions_by_id.has(definition.action_id):
|
|
errors.append("Duplicate action_id '%s'" % definition.action_id)
|
|
continue
|
|
definitions_by_id[definition.action_id] = definition
|
|
definitions.append(definition)
|
|
|
|
|
|
static func _collect_professions(
|
|
pack: SimulationContentPack,
|
|
definitions: Array[ProfessionDefinition],
|
|
definitions_by_id: Dictionary,
|
|
errors: Array[String]
|
|
) -> void:
|
|
for definition in pack.professions:
|
|
if definition == null:
|
|
continue
|
|
if definitions_by_id.has(definition.profession_id):
|
|
errors.append("Duplicate profession_id '%s'" % definition.profession_id)
|
|
continue
|
|
definitions_by_id[definition.profession_id] = definition
|
|
definitions.append(definition)
|
|
|
|
|
|
static func _collect_items(
|
|
pack: SimulationContentPack,
|
|
definitions: Array[ItemDefinition],
|
|
definitions_by_id: Dictionary,
|
|
errors: Array[String]
|
|
) -> void:
|
|
for definition in pack.items:
|
|
if definition == null:
|
|
continue
|
|
if definitions_by_id.has(definition.item_id):
|
|
errors.append("Duplicate item_id '%s'" % definition.item_id)
|
|
continue
|
|
definitions_by_id[definition.item_id] = definition
|
|
definitions.append(definition)
|
|
|
|
|
|
static func _collect_capability_tags(
|
|
pack: SimulationContentPack,
|
|
definitions: Array[CapabilityTagDefinition],
|
|
definitions_by_id: Dictionary,
|
|
errors: Array[String]
|
|
) -> void:
|
|
for definition in pack.capability_tags:
|
|
if definition == null:
|
|
continue
|
|
if definitions_by_id.has(definition.tag_id):
|
|
errors.append("Duplicate tag_id '%s'" % definition.tag_id)
|
|
continue
|
|
definitions_by_id[definition.tag_id] = definition
|
|
definitions.append(definition)
|
|
|
|
|
|
static func _collect_resources(
|
|
pack: SimulationContentPack,
|
|
definitions: Array[ResourceDefinition],
|
|
definitions_by_id: Dictionary,
|
|
errors: Array[String]
|
|
) -> void:
|
|
for definition in pack.resources:
|
|
if definition == null:
|
|
continue
|
|
if definitions_by_id.has(definition.resource_id):
|
|
errors.append("Duplicate resource_id '%s'" % definition.resource_id)
|
|
continue
|
|
definitions_by_id[definition.resource_id] = definition
|
|
definitions.append(definition)
|
|
|
|
|
|
static func _collect_storages(
|
|
pack: SimulationContentPack,
|
|
definitions: Array[StorageDefinition],
|
|
definitions_by_id: Dictionary,
|
|
errors: Array[String]
|
|
) -> void:
|
|
for definition in pack.storages:
|
|
if definition == null:
|
|
continue
|
|
if definitions_by_id.has(definition.storage_id):
|
|
errors.append("Duplicate storage_id '%s'" % definition.storage_id)
|
|
continue
|
|
definitions_by_id[definition.storage_id] = definition
|
|
definitions.append(definition)
|
|
|
|
|
|
static func _collect_enemies(
|
|
pack: SimulationContentPack,
|
|
definitions: Array[EnemyDefinition],
|
|
definitions_by_id: Dictionary,
|
|
errors: Array[String]
|
|
) -> void:
|
|
for definition in pack.enemies:
|
|
if definition == null:
|
|
continue
|
|
if definitions_by_id.has(definition.enemy_id):
|
|
errors.append("Duplicate enemy_id '%s'" % definition.enemy_id)
|
|
continue
|
|
definitions_by_id[definition.enemy_id] = definition
|
|
definitions.append(definition)
|
|
|
|
|
|
static func _collect_animals(
|
|
pack: SimulationContentPack,
|
|
definitions: Array[AnimalDefinition],
|
|
definitions_by_id: Dictionary,
|
|
errors: Array[String]
|
|
) -> void:
|
|
for definition in pack.animals:
|
|
if definition == null:
|
|
continue
|
|
if definitions_by_id.has(definition.animal_definition_id):
|
|
errors.append("Duplicate animal_definition_id '%s'" % definition.animal_definition_id)
|
|
continue
|
|
definitions_by_id[definition.animal_definition_id] = definition
|
|
definitions.append(definition)
|
|
|
|
|
|
static func _collect_situations(
|
|
pack: SimulationContentPack,
|
|
definitions: Array[SituationDefinition],
|
|
definitions_by_id: Dictionary,
|
|
errors: Array[String]
|
|
) -> void:
|
|
for definition in pack.situations:
|
|
if definition == null:
|
|
continue
|
|
if definitions_by_id.has(definition.situation_definition_id):
|
|
errors.append(
|
|
"Duplicate situation_definition_id '%s'" % definition.situation_definition_id
|
|
)
|
|
continue
|
|
definitions_by_id[definition.situation_definition_id] = definition
|
|
definitions.append(definition)
|
|
|
|
|
|
static func _collect_dialogue_intents(
|
|
pack: SimulationContentPack,
|
|
definitions: Array[ConversationIntentDefinition],
|
|
definitions_by_id: Dictionary,
|
|
errors: Array[String]
|
|
) -> void:
|
|
for definition in pack.dialogue_intents:
|
|
if definition == null:
|
|
continue
|
|
if definitions_by_id.has(definition.intent_id):
|
|
errors.append("Duplicate dialogue intent_id '%s'" % definition.intent_id)
|
|
continue
|
|
definitions_by_id[definition.intent_id] = definition
|
|
definitions.append(definition)
|
|
|
|
|
|
static func _collect_dialogue_template_catalogs(
|
|
pack: SimulationContentPack,
|
|
definitions: Array[ConversationTemplateCatalog],
|
|
definitions_by_id: Dictionary,
|
|
errors: Array[String]
|
|
) -> void:
|
|
for definition in pack.dialogue_template_catalogs:
|
|
if definition == null:
|
|
continue
|
|
if definitions_by_id.has(definition.catalog_id):
|
|
errors.append("Duplicate dialogue template catalog_id '%s'" % definition.catalog_id)
|
|
continue
|
|
definitions_by_id[definition.catalog_id] = definition
|
|
definitions.append(definition)
|
|
|
|
|
|
static func _validate_action_references(
|
|
actions: Array[ActionDefinition],
|
|
actions_by_id: Dictionary,
|
|
professions_by_id: Dictionary,
|
|
items_by_id: Dictionary,
|
|
capability_tags_by_id: Dictionary,
|
|
supported_handlers: Dictionary,
|
|
supported_cues: Dictionary,
|
|
errors: Array[String]
|
|
) -> void:
|
|
for definition in actions:
|
|
_validate_tag_ids(
|
|
"Action '%s' actor" % definition.action_id,
|
|
definition.actor_capability_tags,
|
|
CapabilityTagDefinition.DOMAIN_ACTOR,
|
|
capability_tags_by_id,
|
|
errors
|
|
)
|
|
_validate_tag_ids(
|
|
"Action '%s' target" % definition.action_id,
|
|
definition.target_capability_tags,
|
|
CapabilityTagDefinition.DOMAIN_TARGET,
|
|
capability_tags_by_id,
|
|
errors
|
|
)
|
|
_validate_presentation_cue(
|
|
"Action '%s'" % definition.action_id,
|
|
definition.presentation_cue_id,
|
|
supported_cues,
|
|
errors
|
|
)
|
|
for handler_field in [
|
|
["selection policy", definition.selection_policy_id],
|
|
["target policy", definition.target_policy_id],
|
|
["completion handler", definition.completion_handler_id],
|
|
]:
|
|
var handler_id: StringName = handler_field[1]
|
|
if not handler_id.is_empty() and not supported_handlers.has(handler_id):
|
|
errors.append(
|
|
(
|
|
"Action '%s' references unknown %s '%s'"
|
|
% [definition.action_id, handler_field[0], handler_id]
|
|
)
|
|
)
|
|
if (
|
|
not definition.preferred_profession_id.is_empty()
|
|
and not professions_by_id.has(definition.preferred_profession_id)
|
|
):
|
|
errors.append(
|
|
(
|
|
"Action '%s' references unknown profession '%s'"
|
|
% [definition.action_id, definition.preferred_profession_id]
|
|
)
|
|
)
|
|
if (
|
|
definition.target_type == SimulationIds.TARGET_RESOURCE
|
|
and not actions_by_id.has(definition.resource_action_id)
|
|
):
|
|
errors.append(
|
|
(
|
|
"Action '%s' references unknown resource action '%s'"
|
|
% [definition.action_id, definition.resource_action_id]
|
|
)
|
|
)
|
|
if (
|
|
not definition.completion_cost_resource_id.is_empty()
|
|
and not items_by_id.has(definition.completion_cost_resource_id)
|
|
):
|
|
errors.append(
|
|
(
|
|
"Action '%s' references unknown completion cost item '%s'"
|
|
% [definition.action_id, definition.completion_cost_resource_id]
|
|
)
|
|
)
|
|
elif not definition.completion_cost_resource_id.is_empty():
|
|
var cost_item := (
|
|
items_by_id.get(definition.completion_cost_resource_id) as ItemDefinition
|
|
)
|
|
if cost_item != null and not cost_item.is_resource():
|
|
errors.append(
|
|
(
|
|
"Action '%s' references non-resource completion cost item '%s'"
|
|
% [definition.action_id, definition.completion_cost_resource_id]
|
|
)
|
|
)
|
|
for effect in definition.effects:
|
|
if effect == null:
|
|
continue
|
|
var effect_item_id: StringName = effect.get("item_id")
|
|
if not effect_item_id.is_empty() and not items_by_id.has(effect_item_id):
|
|
errors.append(
|
|
(
|
|
"Action '%s' effect references unknown item '%s'"
|
|
% [definition.action_id, effect_item_id]
|
|
)
|
|
)
|
|
var scheduled_action_id: StringName = effect.get("scheduled_action_id")
|
|
if not scheduled_action_id.is_empty() and not actions_by_id.has(scheduled_action_id):
|
|
errors.append(
|
|
(
|
|
"Action '%s' effect references unknown action '%s'"
|
|
% [definition.action_id, scheduled_action_id]
|
|
)
|
|
)
|
|
|
|
|
|
static func _validate_item_references(
|
|
items: Array[ItemDefinition],
|
|
capability_tags_by_id: Dictionary,
|
|
supported_cues: Dictionary,
|
|
errors: Array[String]
|
|
) -> void:
|
|
for definition in items:
|
|
_validate_tag_ids(
|
|
"Item '%s'" % definition.item_id,
|
|
definition.capability_tag_ids,
|
|
CapabilityTagDefinition.DOMAIN_ITEM,
|
|
capability_tags_by_id,
|
|
errors
|
|
)
|
|
_validate_presentation_cue(
|
|
"Item '%s'" % definition.item_id, definition.presentation_cue_id, supported_cues, errors
|
|
)
|
|
|
|
|
|
static func _validate_resource_references(
|
|
resources: Array[ResourceDefinition],
|
|
items_by_id: Dictionary,
|
|
actions_by_id: Dictionary,
|
|
capability_tags_by_id: Dictionary,
|
|
supported_cues: Dictionary,
|
|
errors: Array[String]
|
|
) -> void:
|
|
for definition in resources:
|
|
var item := items_by_id.get(definition.item_id) as ItemDefinition
|
|
if item == null:
|
|
errors.append(
|
|
(
|
|
"Resource '%s' references unknown item '%s'"
|
|
% [definition.resource_id, definition.item_id]
|
|
)
|
|
)
|
|
elif not item.is_resource():
|
|
errors.append(
|
|
(
|
|
"Resource '%s' references non-resource item '%s'"
|
|
% [definition.resource_id, definition.item_id]
|
|
)
|
|
)
|
|
var action := actions_by_id.get(definition.extraction_action_id) as ActionDefinition
|
|
if action == null:
|
|
errors.append(
|
|
(
|
|
"Resource '%s' references unknown action '%s'"
|
|
% [definition.resource_id, definition.extraction_action_id]
|
|
)
|
|
)
|
|
elif action.target_type != SimulationIds.TARGET_RESOURCE:
|
|
errors.append(
|
|
(
|
|
"Resource '%s' references non-resource-target action '%s'"
|
|
% [definition.resource_id, definition.extraction_action_id]
|
|
)
|
|
)
|
|
_validate_tag_ids(
|
|
"Resource '%s'" % definition.resource_id,
|
|
definition.capability_tag_ids,
|
|
CapabilityTagDefinition.DOMAIN_RESOURCE,
|
|
capability_tags_by_id,
|
|
errors
|
|
)
|
|
_validate_presentation_cue(
|
|
"Resource '%s'" % definition.resource_id,
|
|
definition.presentation_cue_id,
|
|
supported_cues,
|
|
errors
|
|
)
|
|
|
|
|
|
static func _validate_storage_references(
|
|
storages: Array[StorageDefinition],
|
|
items_by_id: Dictionary,
|
|
actions_by_id: Dictionary,
|
|
capability_tags_by_id: Dictionary,
|
|
supported_cues: Dictionary,
|
|
errors: Array[String]
|
|
) -> void:
|
|
for definition in storages:
|
|
for item_id in definition.accepted_item_ids:
|
|
var item := items_by_id.get(item_id) as ItemDefinition
|
|
if item == null:
|
|
errors.append(
|
|
"Storage '%s' references unknown item '%s'" % [definition.storage_id, item_id]
|
|
)
|
|
elif not item.is_resource():
|
|
errors.append(
|
|
"Storage '%s' accepts non-resource item '%s'" % [definition.storage_id, item_id]
|
|
)
|
|
_validate_tag_ids(
|
|
"Storage '%s' accepted item" % definition.storage_id,
|
|
definition.accepted_item_tag_ids,
|
|
CapabilityTagDefinition.DOMAIN_ITEM,
|
|
capability_tags_by_id,
|
|
errors
|
|
)
|
|
_validate_tag_ids(
|
|
"Storage '%s'" % definition.storage_id,
|
|
definition.capability_tag_ids,
|
|
CapabilityTagDefinition.DOMAIN_STORAGE,
|
|
capability_tags_by_id,
|
|
errors
|
|
)
|
|
for action_id in [definition.deposit_action_id, definition.withdrawal_action_id]:
|
|
if not action_id.is_empty() and not actions_by_id.has(action_id):
|
|
errors.append(
|
|
(
|
|
"Storage '%s' references unknown action '%s'"
|
|
% [definition.storage_id, action_id]
|
|
)
|
|
)
|
|
_validate_presentation_cue(
|
|
"Storage '%s'" % definition.storage_id,
|
|
definition.presentation_cue_id,
|
|
supported_cues,
|
|
errors
|
|
)
|
|
|
|
|
|
static func _validate_tag_ids(
|
|
owner: String,
|
|
tag_ids: Array[StringName],
|
|
expected_domain: StringName,
|
|
capability_tags_by_id: Dictionary,
|
|
errors: Array[String]
|
|
) -> void:
|
|
for tag_id in tag_ids:
|
|
var tag := capability_tags_by_id.get(tag_id) as CapabilityTagDefinition
|
|
if tag == null:
|
|
errors.append("%s references unknown tag '%s'" % [owner, tag_id])
|
|
elif tag.domain != expected_domain:
|
|
errors.append(
|
|
(
|
|
"%s references '%s' tag '%s' where '%s' is required"
|
|
% [owner, tag.domain, tag_id, expected_domain]
|
|
)
|
|
)
|
|
|
|
|
|
static func _validate_presentation_cue(
|
|
owner: String, cue_id: StringName, supported_cues: Dictionary, errors: Array[String]
|
|
) -> void:
|
|
if not cue_id.is_empty() and not supported_cues.has(cue_id):
|
|
errors.append("%s references unknown presentation cue '%s'" % [owner, cue_id])
|
|
|
|
|
|
static func _validate_enemy_references(
|
|
enemies: Array[EnemyDefinition], items_by_id: Dictionary, errors: Array[String]
|
|
) -> void:
|
|
for definition in enemies:
|
|
var weapon := items_by_id.get(definition.weapon_id) as ItemDefinition
|
|
if weapon == null:
|
|
errors.append(
|
|
(
|
|
"Enemy '%s' references unknown weapon '%s'"
|
|
% [definition.enemy_id, definition.weapon_id]
|
|
)
|
|
)
|
|
elif not weapon.is_weapon():
|
|
errors.append(
|
|
(
|
|
"Enemy '%s' references non-weapon item '%s'"
|
|
% [definition.enemy_id, definition.weapon_id]
|
|
)
|
|
)
|
|
|
|
|
|
static func _validate_animal_references(
|
|
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(
|
|
(
|
|
"Animal '%s' references unknown action '%s'"
|
|
% [definition.animal_definition_id, action_id]
|
|
)
|
|
)
|
|
|
|
|
|
static func _validate_situation_references(
|
|
situations: Array[SituationDefinition],
|
|
actions_by_id: Dictionary,
|
|
items_by_id: Dictionary,
|
|
dialogue_intents_by_id: Dictionary,
|
|
supported_event_predicates: Dictionary,
|
|
supported_state_predicates: Dictionary,
|
|
supported_expiry_predicates: Dictionary,
|
|
errors: Array[String]
|
|
) -> void:
|
|
var event_predicate_ids := _sorted_string_name_keys(supported_event_predicates)
|
|
var state_predicate_ids := _sorted_string_name_keys(supported_state_predicates)
|
|
var expiry_predicate_ids := _sorted_string_name_keys(supported_expiry_predicates)
|
|
for definition in situations:
|
|
for error in definition.validate(
|
|
event_predicate_ids, state_predicate_ids, expiry_predicate_ids
|
|
):
|
|
errors.append("Situation '%s': %s" % [definition.situation_definition_id, error])
|
|
for intent_id in definition.dialogue_intent_ids:
|
|
if not intent_id.is_empty() and not dialogue_intents_by_id.has(intent_id):
|
|
errors.append(
|
|
(
|
|
"Situation '%s' references unknown dialogue intent '%s'"
|
|
% [definition.situation_definition_id, intent_id]
|
|
)
|
|
)
|
|
for alternative in definition.alternatives:
|
|
if alternative == null:
|
|
continue
|
|
var action_id := StringName(alternative.commitment_terms.get("action_id", ""))
|
|
if not action_id.is_empty() and not actions_by_id.has(action_id):
|
|
errors.append(
|
|
(
|
|
"Situation alternative '%s' references unknown action '%s'"
|
|
% [alternative.alternative_id, action_id]
|
|
)
|
|
)
|
|
var item_id := StringName(alternative.commitment_terms.get("item_id", ""))
|
|
if not item_id.is_empty() and not items_by_id.has(item_id):
|
|
errors.append(
|
|
(
|
|
"Situation alternative '%s' references unknown item '%s'"
|
|
% [alternative.alternative_id, item_id]
|
|
)
|
|
)
|
|
|
|
|
|
static func _validate_dialogue_references(
|
|
intents: Array[ConversationIntentDefinition],
|
|
intents_by_id: Dictionary,
|
|
template_catalogs_by_id: Dictionary,
|
|
errors: Array[String]
|
|
) -> void:
|
|
var intent_ids_by_template_catalog := {}
|
|
for definition in intents:
|
|
for response_intent_id in definition.response_intent_ids:
|
|
if not intents_by_id.has(response_intent_id):
|
|
errors.append(
|
|
(
|
|
"Dialogue intent '%s' references unknown response intent '%s'"
|
|
% [definition.intent_id, response_intent_id]
|
|
)
|
|
)
|
|
if not template_catalogs_by_id.has(definition.template_catalog_id):
|
|
errors.append(
|
|
(
|
|
"Dialogue intent '%s' references unknown template catalog '%s'"
|
|
% [definition.intent_id, definition.template_catalog_id]
|
|
)
|
|
)
|
|
continue
|
|
var catalog_intent_ids: Array[StringName] = []
|
|
for existing_intent_id in intent_ids_by_template_catalog.get(
|
|
definition.template_catalog_id, []
|
|
):
|
|
catalog_intent_ids.append(StringName(existing_intent_id))
|
|
catalog_intent_ids.append(definition.intent_id)
|
|
intent_ids_by_template_catalog[definition.template_catalog_id] = catalog_intent_ids
|
|
for raw_catalog_id in template_catalogs_by_id:
|
|
var catalog_id := StringName(raw_catalog_id)
|
|
var catalog := template_catalogs_by_id.get(catalog_id) as ConversationTemplateCatalog
|
|
var expected_intent_ids: Array[StringName] = []
|
|
for expected_intent_id in intent_ids_by_template_catalog.get(catalog_id, []):
|
|
expected_intent_ids.append(StringName(expected_intent_id))
|
|
expected_intent_ids.sort_custom(
|
|
func(left: StringName, right: StringName) -> bool: return String(left) < String(right)
|
|
)
|
|
for error in catalog.validate(expected_intent_ids):
|
|
errors.append("Dialogue template catalog '%s': %s" % [catalog_id, error])
|
|
|
|
|
|
static func _validated_id_set(
|
|
provided_ids: Array[StringName],
|
|
default_ids: Array[StringName],
|
|
label: String,
|
|
errors: Array[String]
|
|
) -> Dictionary:
|
|
var result := {}
|
|
var ids := provided_ids if not provided_ids.is_empty() else default_ids
|
|
for id in ids:
|
|
if id.is_empty():
|
|
errors.append("Supported %s ID is empty" % label)
|
|
elif result.has(id):
|
|
errors.append("Duplicate supported %s '%s'" % [label, id])
|
|
else:
|
|
result[id] = true
|
|
return result
|
|
|
|
|
|
static func _sorted_string_name_keys(source: Dictionary) -> Array[StringName]:
|
|
var result: Array[StringName] = []
|
|
for key in source:
|
|
result.append(StringName(key))
|
|
result.sort_custom(
|
|
func(left: StringName, right: StringName) -> bool: return String(left) < String(right)
|
|
)
|
|
return result
|
|
|
|
|
|
static func _sort_packs(first: SimulationContentPack, second: SimulationContentPack) -> bool:
|
|
if first == null:
|
|
return second != null
|
|
if second == null:
|
|
return false
|
|
return String(first.pack_id) < String(second.pack_id)
|
|
|
|
|
|
static func _sort_actions(first: ActionDefinition, second: ActionDefinition) -> bool:
|
|
return String(first.action_id) < String(second.action_id)
|
|
|
|
|
|
static func _sort_professions(first: ProfessionDefinition, second: ProfessionDefinition) -> bool:
|
|
return String(first.profession_id) < String(second.profession_id)
|
|
|
|
|
|
static func _sort_capability_tags(
|
|
first: CapabilityTagDefinition, second: CapabilityTagDefinition
|
|
) -> bool:
|
|
return String(first.tag_id) < String(second.tag_id)
|
|
|
|
|
|
static func _sort_items(first: ItemDefinition, second: ItemDefinition) -> bool:
|
|
return String(first.item_id) < String(second.item_id)
|
|
|
|
|
|
static func _sort_resources(first: ResourceDefinition, second: ResourceDefinition) -> bool:
|
|
return String(first.resource_id) < String(second.resource_id)
|
|
|
|
|
|
static func _sort_storages(first: StorageDefinition, second: StorageDefinition) -> bool:
|
|
return String(first.storage_id) < String(second.storage_id)
|
|
|
|
|
|
static func _sort_enemies(first: EnemyDefinition, second: EnemyDefinition) -> bool:
|
|
return String(first.enemy_id) < String(second.enemy_id)
|
|
|
|
|
|
static func _sort_animals(first: AnimalDefinition, second: AnimalDefinition) -> bool:
|
|
return String(first.animal_definition_id) < String(second.animal_definition_id)
|
|
|
|
|
|
static func _sort_situations(first: SituationDefinition, second: SituationDefinition) -> bool:
|
|
return String(first.situation_definition_id) < String(second.situation_definition_id)
|
|
|
|
|
|
static func _sort_dialogue_intents(
|
|
first: ConversationIntentDefinition, second: ConversationIntentDefinition
|
|
) -> bool:
|
|
return String(first.intent_id) < String(second.intent_id)
|
|
|
|
|
|
static func _sort_dialogue_template_catalogs(
|
|
first: ConversationTemplateCatalog, second: ConversationTemplateCatalog
|
|
) -> bool:
|
|
return String(first.catalog_id) < String(second.catalog_id)
|
|
|
|
|
|
static func _ids_for(definitions: Array, id_property: StringName) -> Array[StringName]:
|
|
var ids: Array[StringName] = []
|
|
for definition in definitions:
|
|
ids.append(StringName(definition.get(id_property)))
|
|
return ids
|