feat: add emergent world foundations
This commit is contained in:
@@ -0,0 +1,340 @@
|
||||
class_name ContentCatalog
|
||||
extends RefCounted
|
||||
|
||||
const CORE_PACK_PATH := "res://simulation/definitions/packs/core.tres"
|
||||
|
||||
var _valid := false
|
||||
var _errors: Array[String] = []
|
||||
var _packs: Array[SimulationContentPack] = []
|
||||
var _actions: Array[ActionDefinition] = []
|
||||
var _professions: Array[ProfessionDefinition] = []
|
||||
var _items: Array[ItemDefinition] = []
|
||||
var _enemies: Array[EnemyDefinition] = []
|
||||
var _packs_by_id: Dictionary = {}
|
||||
var _actions_by_id: Dictionary = {}
|
||||
var _professions_by_id: Dictionary = {}
|
||||
var _items_by_id: Dictionary = {}
|
||||
var _enemies_by_id: Dictionary = {}
|
||||
|
||||
|
||||
static func create_core(supported_handler_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)
|
||||
return catalog
|
||||
|
||||
|
||||
func rebuild(
|
||||
content_packs: Array[SimulationContentPack], supported_handler_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 packs_by_id := {}
|
||||
var actions_by_id := {}
|
||||
var professions_by_id := {}
|
||||
var items_by_id := {}
|
||||
var enemies_by_id := {}
|
||||
var actions: Array[ActionDefinition] = []
|
||||
var professions: Array[ProfessionDefinition] = []
|
||||
var items: Array[ItemDefinition] = []
|
||||
var enemies: Array[EnemyDefinition] = []
|
||||
|
||||
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_items(pack, items, items_by_id, errors)
|
||||
_collect_enemies(pack, enemies, enemies_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, errors)
|
||||
_validate_enemy_references(enemies, items_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)
|
||||
items.sort_custom(_sort_items)
|
||||
enemies.sort_custom(_sort_enemies)
|
||||
_packs = pack_list
|
||||
_actions = actions
|
||||
_professions = professions
|
||||
_items = items
|
||||
_enemies = enemies
|
||||
_packs_by_id = packs_by_id
|
||||
_actions_by_id = actions_by_id
|
||||
_professions_by_id = professions_by_id
|
||||
_items_by_id = items_by_id
|
||||
_enemies_by_id = enemies_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_items() -> Array[ItemDefinition]:
|
||||
return _items.duplicate()
|
||||
|
||||
|
||||
func get_enemies() -> Array[EnemyDefinition]:
|
||||
return _enemies.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_item(item_id: StringName) -> ItemDefinition:
|
||||
return _items_by_id.get(item_id) as ItemDefinition
|
||||
|
||||
|
||||
func get_enemy(enemy_id: StringName) -> EnemyDefinition:
|
||||
return _enemies_by_id.get(enemy_id) as EnemyDefinition
|
||||
|
||||
|
||||
func _clear_published_content() -> void:
|
||||
_valid = false
|
||||
_errors.clear()
|
||||
_packs.clear()
|
||||
_actions.clear()
|
||||
_professions.clear()
|
||||
_items.clear()
|
||||
_enemies.clear()
|
||||
_packs_by_id.clear()
|
||||
_actions_by_id.clear()
|
||||
_professions_by_id.clear()
|
||||
_items_by_id.clear()
|
||||
_enemies_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_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 _validate_action_references(
|
||||
actions: Array[ActionDefinition],
|
||||
actions_by_id: Dictionary,
|
||||
professions_by_id: Dictionary,
|
||||
items_by_id: Dictionary,
|
||||
errors: Array[String]
|
||||
) -> void:
|
||||
for definition in actions:
|
||||
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]
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
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 _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_items(first: ItemDefinition, second: ItemDefinition) -> bool:
|
||||
return String(first.item_id) < String(second.item_id)
|
||||
|
||||
|
||||
static func _sort_enemies(first: EnemyDefinition, second: EnemyDefinition) -> bool:
|
||||
return String(first.enemy_id) < String(second.enemy_id)
|
||||
Reference in New Issue
Block a user