feat: route resources through typed content
This commit is contained in:
@@ -8,28 +8,39 @@ 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 _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 = {}
|
||||
|
||||
|
||||
static func create_core(supported_handler_ids: Array[StringName] = []) -> ContentCatalog:
|
||||
static func create_core(
|
||||
supported_handler_ids: Array[StringName] = [],
|
||||
supported_presentation_cue_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)
|
||||
catalog.rebuild(packs, supported_handler_ids, supported_presentation_cue_ids)
|
||||
return catalog
|
||||
|
||||
|
||||
func rebuild(
|
||||
content_packs: Array[SimulationContentPack], supported_handler_ids: Array[StringName] = []
|
||||
content_packs: Array[SimulationContentPack],
|
||||
supported_handler_ids: Array[StringName] = [],
|
||||
supported_presentation_cue_ids: Array[StringName] = []
|
||||
) -> Array[String]:
|
||||
_clear_published_content()
|
||||
var errors: Array[String] = []
|
||||
@@ -43,15 +54,34 @@ func rebuild(
|
||||
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 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 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] = []
|
||||
|
||||
for pack in pack_list:
|
||||
@@ -66,7 +96,10 @@ func rebuild(
|
||||
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)
|
||||
|
||||
for pack in pack_list:
|
||||
@@ -93,7 +126,21 @@ func rebuild(
|
||||
)
|
||||
|
||||
_validate_action_references(
|
||||
actions, actions_by_id, professions_by_id, items_by_id, supported_handlers, errors
|
||||
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)
|
||||
errors.sort()
|
||||
@@ -103,17 +150,26 @@ func rebuild(
|
||||
|
||||
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)
|
||||
_packs = pack_list
|
||||
_actions = actions
|
||||
_professions = professions
|
||||
_capability_tags = capability_tags
|
||||
_items = items
|
||||
_resources = resources
|
||||
_storages = storages
|
||||
_enemies = enemies
|
||||
_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
|
||||
_valid = true
|
||||
return []
|
||||
@@ -139,10 +195,22 @@ 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()
|
||||
|
||||
@@ -159,26 +227,59 @@ 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_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"),
|
||||
}
|
||||
|
||||
|
||||
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()
|
||||
_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()
|
||||
|
||||
|
||||
@@ -230,6 +331,54 @@ static func _collect_items(
|
||||
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],
|
||||
@@ -251,10 +400,32 @@ static func _validate_action_references(
|
||||
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],
|
||||
@@ -309,6 +480,174 @@ static func _validate_action_references(
|
||||
% [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(
|
||||
@@ -348,9 +687,30 @@ static func _sort_professions(first: ProfessionDefinition, second: ProfessionDef
|
||||
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 _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
|
||||
|
||||
Reference in New Issue
Block a user