diff --git a/simulation/definitions/ContentCatalog.gd b/simulation/definitions/ContentCatalog.gd index 9fdc69a..158ab18 100644 --- a/simulation/definitions/ContentCatalog.gd +++ b/simulation/definitions/ContentCatalog.gd @@ -26,6 +26,8 @@ var _animals: Array[AnimalDefinition] = [] var _situations: Array[SituationDefinition] = [] var _dialogue_intents: Array[ConversationIntentDefinition] = [] var _dialogue_template_catalogs: Array[ConversationTemplateCatalog] = [] +var _event_types: Array[EventTypeDefinition] = [] +var _social_consequences: Array[SocialConsequenceDefinition] = [] var _packs_by_id: Dictionary = {} var _actions_by_id: Dictionary = {} var _professions_by_id: Dictionary = {} @@ -38,6 +40,8 @@ var _animals_by_id: Dictionary = {} var _situations_by_id: Dictionary = {} var _dialogue_intents_by_id: Dictionary = {} var _dialogue_template_catalogs_by_id: Dictionary = {} +var _event_types_by_id: Dictionary = {} +var _social_consequences_by_id: Dictionary = {} static func create_core( @@ -128,6 +132,8 @@ func rebuild( var situations_by_id := {} var dialogue_intents_by_id := {} var dialogue_template_catalogs_by_id := {} + var event_types_by_id := {} + var social_consequences_by_id := {} var actions: Array[ActionDefinition] = [] var professions: Array[ProfessionDefinition] = [] var capability_tags: Array[CapabilityTagDefinition] = [] @@ -139,6 +145,8 @@ func rebuild( var situations: Array[SituationDefinition] = [] var dialogue_intents: Array[ConversationIntentDefinition] = [] var dialogue_template_catalogs: Array[ConversationTemplateCatalog] = [] + var event_types: Array[EventTypeDefinition] = [] + var social_consequences: Array[SocialConsequenceDefinition] = [] for pack in pack_list: if pack == null: @@ -163,6 +171,8 @@ func rebuild( _collect_dialogue_template_catalogs( pack, dialogue_template_catalogs, dialogue_template_catalogs_by_id, errors ) + _collect_event_types(pack, event_types, event_types_by_id, errors) + _collect_social_consequences(pack, social_consequences, social_consequences_by_id, errors) for pack in pack_list: if pack == null: @@ -211,6 +221,7 @@ func rebuild( actions_by_id, items_by_id, dialogue_intents_by_id, + event_types_by_id, supported_event_predicates, supported_state_predicates, supported_expiry_predicates, @@ -219,6 +230,7 @@ func rebuild( _validate_dialogue_references( dialogue_intents, dialogue_intents_by_id, dialogue_template_catalogs_by_id, errors ) + _validate_social_consequence_references(social_consequences, event_types_by_id, errors) errors.sort() _errors = errors if not errors.is_empty(): @@ -235,6 +247,8 @@ func rebuild( situations.sort_custom(_sort_situations) dialogue_intents.sort_custom(_sort_dialogue_intents) dialogue_template_catalogs.sort_custom(_sort_dialogue_template_catalogs) + event_types.sort_custom(_sort_event_types) + social_consequences.sort_custom(_sort_social_consequences) _packs = pack_list _actions = actions _professions = professions @@ -247,6 +261,8 @@ func rebuild( _situations = situations _dialogue_intents = dialogue_intents _dialogue_template_catalogs = dialogue_template_catalogs + _event_types = event_types + _social_consequences = social_consequences _packs_by_id = packs_by_id _actions_by_id = actions_by_id _professions_by_id = professions_by_id @@ -259,6 +275,8 @@ func rebuild( _situations_by_id = situations_by_id _dialogue_intents_by_id = dialogue_intents_by_id _dialogue_template_catalogs_by_id = dialogue_template_catalogs_by_id + _event_types_by_id = event_types_by_id + _social_consequences_by_id = social_consequences_by_id _valid = true return [] @@ -319,6 +337,14 @@ func get_dialogue_template_catalogs() -> Array[ConversationTemplateCatalog]: return _dialogue_template_catalogs.duplicate() +func get_event_types() -> Array[EventTypeDefinition]: + return _event_types.duplicate() + + +func get_social_consequences() -> Array[SocialConsequenceDefinition]: + return _social_consequences.duplicate() + + func get_pack(pack_id: StringName) -> SimulationContentPack: return _packs_by_id.get(pack_id) as SimulationContentPack @@ -367,6 +393,14 @@ func get_dialogue_template_catalog(catalog_id: StringName) -> ConversationTempla return _dialogue_template_catalogs_by_id.get(catalog_id) as ConversationTemplateCatalog +func get_event_type(event_type_id: StringName) -> EventTypeDefinition: + return _event_types_by_id.get(event_type_id) as EventTypeDefinition + + +func get_social_consequence(consequence_id: StringName) -> SocialConsequenceDefinition: + return _social_consequences_by_id.get(consequence_id) as SocialConsequenceDefinition + + func get_authoring_report() -> Dictionary: return { "valid": _valid, @@ -383,6 +417,8 @@ func get_authoring_report() -> Dictionary: "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"), + "event_types": _ids_for(_event_types, &"event_type_id"), + "social_consequences": _ids_for(_social_consequences, &"consequence_id"), } @@ -401,6 +437,8 @@ func _clear_published_content() -> void: _situations.clear() _dialogue_intents.clear() _dialogue_template_catalogs.clear() + _event_types.clear() + _social_consequences.clear() _packs_by_id.clear() _actions_by_id.clear() _professions_by_id.clear() @@ -413,6 +451,8 @@ func _clear_published_content() -> void: _situations_by_id.clear() _dialogue_intents_by_id.clear() _dialogue_template_catalogs_by_id.clear() + _event_types_by_id.clear() + _social_consequences_by_id.clear() static func _collect_actions( @@ -593,6 +633,38 @@ static func _collect_dialogue_template_catalogs( definitions.append(definition) +static func _collect_event_types( + pack: SimulationContentPack, + definitions: Array[EventTypeDefinition], + definitions_by_id: Dictionary, + errors: Array[String] +) -> void: + for definition in pack.event_types: + if definition == null: + continue + if definitions_by_id.has(definition.event_type_id): + errors.append("Duplicate event_type_id '%s'" % definition.event_type_id) + continue + definitions_by_id[definition.event_type_id] = definition + definitions.append(definition) + + +static func _collect_social_consequences( + pack: SimulationContentPack, + definitions: Array[SocialConsequenceDefinition], + definitions_by_id: Dictionary, + errors: Array[String] +) -> void: + for definition in pack.social_consequences: + if definition == null: + continue + if definitions_by_id.has(definition.consequence_id): + errors.append("Duplicate social consequence_id '%s'" % definition.consequence_id) + continue + definitions_by_id[definition.consequence_id] = definition + definitions.append(definition) + + static func _validate_action_references( actions: Array[ActionDefinition], actions_by_id: Dictionary, @@ -897,6 +969,7 @@ static func _validate_situation_references( actions_by_id: Dictionary, items_by_id: Dictionary, dialogue_intents_by_id: Dictionary, + event_types_by_id: Dictionary, supported_event_predicates: Dictionary, supported_state_predicates: Dictionary, supported_expiry_predicates: Dictionary, @@ -918,6 +991,17 @@ static func _validate_situation_references( % [definition.situation_definition_id, intent_id] ) ) + for predicate in definition.trigger_predicates + definition.resolution_predicates: + if predicate == null or predicate.evaluator_id != &"event_type": + continue + var event_type_id := StringName(predicate.parameters.get("event_type", "")) + if not event_types_by_id.has(event_type_id): + errors.append( + ( + "Situation predicate '%s' references unknown event type '%s'" + % [predicate.predicate_id, event_type_id] + ) + ) for alternative in definition.alternatives: if alternative == null: continue @@ -983,6 +1067,21 @@ static func _validate_dialogue_references( errors.append("Dialogue template catalog '%s': %s" % [catalog_id, error]) +static func _validate_social_consequence_references( + definitions: Array[SocialConsequenceDefinition], + event_types_by_id: Dictionary, + errors: Array[String] +) -> void: + for definition in definitions: + if not event_types_by_id.has(definition.event_type_id): + errors.append( + ( + "Social consequence '%s' references unknown event type '%s'" + % [definition.consequence_id, definition.event_type_id] + ) + ) + + static func _validated_id_set( provided_ids: Array[StringName], default_ids: Array[StringName], @@ -1069,6 +1168,16 @@ static func _sort_dialogue_template_catalogs( return String(first.catalog_id) < String(second.catalog_id) +static func _sort_event_types(first: EventTypeDefinition, second: EventTypeDefinition) -> bool: + return String(first.event_type_id) < String(second.event_type_id) + + +static func _sort_social_consequences( + first: SocialConsequenceDefinition, second: SocialConsequenceDefinition +) -> bool: + return String(first.consequence_id) < String(second.consequence_id) + + static func _ids_for(definitions: Array, id_property: StringName) -> Array[StringName]: var ids: Array[StringName] = [] for definition in definitions: diff --git a/simulation/definitions/DefinitionParameterValidator.gd b/simulation/definitions/DefinitionParameterValidator.gd new file mode 100644 index 0000000..f33ef5d --- /dev/null +++ b/simulation/definitions/DefinitionParameterValidator.gd @@ -0,0 +1,24 @@ +class_name DefinitionParameterValidator +extends RefCounted + + +static func validate(parameters: Dictionary, owner: String) -> Array[String]: + var errors: Array[String] = [] + for raw_key in parameters: + if not raw_key is String and not raw_key is StringName: + errors.append("%s has a non-string parameter key" % owner) + continue + var key := StringName(raw_key) + if key.is_empty(): + errors.append("%s has an empty parameter key" % owner) + var value: Variant = parameters[raw_key] + if not _is_primitive(value): + errors.append("%s parameter '%s' must be primitive" % [owner, key]) + elif value is float and not is_finite(value): + errors.append("%s parameter '%s' must be finite" % [owner, key]) + errors.sort() + return errors + + +static func _is_primitive(value: Variant) -> bool: + return value is bool or value is int or value is float or value is String or value is StringName diff --git a/simulation/definitions/DefinitionParameterValidator.gd.uid b/simulation/definitions/DefinitionParameterValidator.gd.uid new file mode 100644 index 0000000..080c552 --- /dev/null +++ b/simulation/definitions/DefinitionParameterValidator.gd.uid @@ -0,0 +1 @@ +uid://dbiju2k8f11kw diff --git a/simulation/definitions/EventTypeDefinition.gd b/simulation/definitions/EventTypeDefinition.gd new file mode 100644 index 0000000..7e60bad --- /dev/null +++ b/simulation/definitions/EventTypeDefinition.gd @@ -0,0 +1,39 @@ +class_name EventTypeDefinition +extends Resource + +const CATEGORY_ECONOMY := &"economy" +const CATEGORY_ACTIVITY := &"activity" +const CATEGORY_WELFARE := &"welfare" +const CATEGORY_ANIMAL := &"animal" +const CATEGORY_SOCIAL := &"social" +const CATEGORY_CONFLICT := &"conflict" +const CATEGORIES: Array[StringName] = [ + CATEGORY_ECONOMY, + CATEGORY_ACTIVITY, + CATEGORY_WELFARE, + CATEGORY_ANIMAL, + CATEGORY_SOCIAL, + CATEGORY_CONFLICT, +] + +@export var event_type_id: StringName +@export var display_name: String +@export_multiline var description: String +@export_enum("economy", "activity", "welfare", "animal", "social", "conflict") +var category: String = String(CATEGORY_ECONOMY) +@export var parameters: Dictionary = {} + + +func validate() -> Array[String]: + var errors: Array[String] = [] + if event_type_id.is_empty(): + errors.append("event_type_id is empty") + if display_name.is_empty(): + errors.append("display_name is empty for event type '%s'" % event_type_id) + if StringName(category) not in CATEGORIES: + errors.append("event type '%s' has unknown category '%s'" % [event_type_id, category]) + for error in DefinitionParameterValidator.validate( + parameters, "Event type '%s'" % event_type_id + ): + errors.append(error) + return errors diff --git a/simulation/definitions/EventTypeDefinition.gd.uid b/simulation/definitions/EventTypeDefinition.gd.uid new file mode 100644 index 0000000..43edc6b --- /dev/null +++ b/simulation/definitions/EventTypeDefinition.gd.uid @@ -0,0 +1 @@ +uid://086ryxc2o5ot diff --git a/simulation/definitions/SimulationContentPack.gd b/simulation/definitions/SimulationContentPack.gd index 1f70090..71bb430 100644 --- a/simulation/definitions/SimulationContentPack.gd +++ b/simulation/definitions/SimulationContentPack.gd @@ -16,6 +16,8 @@ extends Resource @export var situations: Array[SituationDefinition] = [] @export var dialogue_intents: Array[ConversationIntentDefinition] = [] @export var dialogue_template_catalogs: Array[ConversationTemplateCatalog] = [] +@export var event_types: Array[EventTypeDefinition] = [] +@export var social_consequences: Array[SocialConsequenceDefinition] = [] func validate() -> Array[String]: @@ -39,6 +41,8 @@ func validate() -> Array[String]: _validate_situations(errors) _validate_dialogue_intents(errors) _validate_dialogue_template_catalogs(errors) + _validate_event_types(errors) + _validate_social_consequences(errors) return errors @@ -140,6 +144,24 @@ func _validate_dialogue_template_catalogs(errors: Array[String]) -> void: errors.append("ConversationTemplateCatalog: catalog_id is empty") +func _validate_event_types(errors: Array[String]) -> void: + for definition in event_types: + if definition == null: + errors.append("event_types contains a null definition") + continue + for error in definition.validate(): + errors.append("EventTypeDefinition: " + error) + + +func _validate_social_consequences(errors: Array[String]) -> void: + for definition in social_consequences: + if definition == null: + errors.append("social_consequences contains a null definition") + continue + for error in definition.validate(): + errors.append("SocialConsequenceDefinition: " + error) + + static func _validate_dependency_ids( ids: Array[StringName], label: String, errors: Array[String] ) -> void: diff --git a/simulation/definitions/SocialConsequenceDefinition.gd b/simulation/definitions/SocialConsequenceDefinition.gd new file mode 100644 index 0000000..a2c77ad --- /dev/null +++ b/simulation/definitions/SocialConsequenceDefinition.gd @@ -0,0 +1,31 @@ +class_name SocialConsequenceDefinition +extends Resource + +@export var consequence_id: StringName +@export var display_name: String +@export_multiline var description: String +@export var event_type_id: StringName +@export var relationship_dimension_id: StringName +@export var parameters: Dictionary = {} + + +func validate() -> Array[String]: + var errors: Array[String] = [] + if consequence_id.is_empty(): + errors.append("consequence_id is empty") + if display_name.is_empty(): + errors.append("display_name is empty for social consequence '%s'" % consequence_id) + if event_type_id.is_empty(): + errors.append("event_type_id is empty for social consequence '%s'" % consequence_id) + if relationship_dimension_id not in RelationshipStateRecord.DIMENSIONS: + errors.append( + ( + "social consequence '%s' references unknown relationship dimension '%s'" + % [consequence_id, relationship_dimension_id] + ) + ) + for error in DefinitionParameterValidator.validate( + parameters, "Social consequence '%s'" % consequence_id + ): + errors.append(error) + return errors diff --git a/simulation/definitions/SocialConsequenceDefinition.gd.uid b/simulation/definitions/SocialConsequenceDefinition.gd.uid new file mode 100644 index 0000000..4c0de61 --- /dev/null +++ b/simulation/definitions/SocialConsequenceDefinition.gd.uid @@ -0,0 +1 @@ +uid://og634er0h3qh diff --git a/simulation/definitions/event_types/animal_fed.tres b/simulation/definitions/event_types/animal_fed.tres new file mode 100644 index 0000000..86f03f3 --- /dev/null +++ b/simulation/definitions/event_types/animal_fed.tres @@ -0,0 +1,9 @@ +[gd_resource type="Resource" script_class="EventTypeDefinition" load_steps=2 format=3] + +[ext_resource type="Script" path="res://simulation/definitions/EventTypeDefinition.gd" id="1"] + +[resource] +script = ExtResource("1") +event_type_id = &"animal_fed" +display_name = "Animal Fed" +category = "animal" diff --git a/simulation/definitions/event_types/combatant_hurt.tres b/simulation/definitions/event_types/combatant_hurt.tres new file mode 100644 index 0000000..128fce2 --- /dev/null +++ b/simulation/definitions/event_types/combatant_hurt.tres @@ -0,0 +1,9 @@ +[gd_resource type="Resource" script_class="EventTypeDefinition" load_steps=2 format=3] + +[ext_resource type="Script" path="res://simulation/definitions/EventTypeDefinition.gd" id="1"] + +[resource] +script = ExtResource("1") +event_type_id = &"combatant_hurt" +display_name = "Combatant Hurt" +category = "conflict" diff --git a/simulation/definitions/event_types/combatant_killed.tres b/simulation/definitions/event_types/combatant_killed.tres new file mode 100644 index 0000000..6454826 --- /dev/null +++ b/simulation/definitions/event_types/combatant_killed.tres @@ -0,0 +1,9 @@ +[gd_resource type="Resource" script_class="EventTypeDefinition" load_steps=2 format=3] + +[ext_resource type="Script" path="res://simulation/definitions/EventTypeDefinition.gd" id="1"] + +[resource] +script = ExtResource("1") +event_type_id = &"combatant_killed" +display_name = "Combatant Killed" +category = "conflict" diff --git a/simulation/definitions/event_types/commitment_accepted.tres b/simulation/definitions/event_types/commitment_accepted.tres new file mode 100644 index 0000000..a376781 --- /dev/null +++ b/simulation/definitions/event_types/commitment_accepted.tres @@ -0,0 +1,9 @@ +[gd_resource type="Resource" script_class="EventTypeDefinition" load_steps=2 format=3] + +[ext_resource type="Script" path="res://simulation/definitions/EventTypeDefinition.gd" id="1"] + +[resource] +script = ExtResource("1") +event_type_id = &"commitment_accepted" +display_name = "Commitment Accepted" +category = "social" diff --git a/simulation/definitions/event_types/commitment_broken.tres b/simulation/definitions/event_types/commitment_broken.tres new file mode 100644 index 0000000..4a1531d --- /dev/null +++ b/simulation/definitions/event_types/commitment_broken.tres @@ -0,0 +1,9 @@ +[gd_resource type="Resource" script_class="EventTypeDefinition" load_steps=2 format=3] + +[ext_resource type="Script" path="res://simulation/definitions/EventTypeDefinition.gd" id="1"] + +[resource] +script = ExtResource("1") +event_type_id = &"commitment_broken" +display_name = "Commitment Broken" +category = "social" diff --git a/simulation/definitions/event_types/commitment_fulfilled.tres b/simulation/definitions/event_types/commitment_fulfilled.tres new file mode 100644 index 0000000..5e9577a --- /dev/null +++ b/simulation/definitions/event_types/commitment_fulfilled.tres @@ -0,0 +1,9 @@ +[gd_resource type="Resource" script_class="EventTypeDefinition" load_steps=2 format=3] + +[ext_resource type="Script" path="res://simulation/definitions/EventTypeDefinition.gd" id="1"] + +[resource] +script = ExtResource("1") +event_type_id = &"commitment_fulfilled" +display_name = "Commitment Fulfilled" +category = "social" diff --git a/simulation/definitions/event_types/commitment_released.tres b/simulation/definitions/event_types/commitment_released.tres new file mode 100644 index 0000000..db8e3f2 --- /dev/null +++ b/simulation/definitions/event_types/commitment_released.tres @@ -0,0 +1,9 @@ +[gd_resource type="Resource" script_class="EventTypeDefinition" load_steps=2 format=3] + +[ext_resource type="Script" path="res://simulation/definitions/EventTypeDefinition.gd" id="1"] + +[resource] +script = ExtResource("1") +event_type_id = &"commitment_released" +display_name = "Commitment Released" +category = "social" diff --git a/simulation/definitions/event_types/commitment_superseded.tres b/simulation/definitions/event_types/commitment_superseded.tres new file mode 100644 index 0000000..a8bd71a --- /dev/null +++ b/simulation/definitions/event_types/commitment_superseded.tres @@ -0,0 +1,9 @@ +[gd_resource type="Resource" script_class="EventTypeDefinition" load_steps=2 format=3] + +[ext_resource type="Script" path="res://simulation/definitions/EventTypeDefinition.gd" id="1"] + +[resource] +script = ExtResource("1") +event_type_id = &"commitment_superseded" +display_name = "Commitment Superseded" +category = "social" diff --git a/simulation/definitions/event_types/home_damaged.tres b/simulation/definitions/event_types/home_damaged.tres new file mode 100644 index 0000000..d7c6384 --- /dev/null +++ b/simulation/definitions/event_types/home_damaged.tres @@ -0,0 +1,9 @@ +[gd_resource type="Resource" script_class="EventTypeDefinition" load_steps=2 format=3] + +[ext_resource type="Script" path="res://simulation/definitions/EventTypeDefinition.gd" id="1"] + +[resource] +script = ExtResource("1") +event_type_id = &"home_damaged" +display_name = "Home Damaged" +category = "welfare" diff --git a/simulation/definitions/event_types/item_consumed.tres b/simulation/definitions/event_types/item_consumed.tres new file mode 100644 index 0000000..375d8b0 --- /dev/null +++ b/simulation/definitions/event_types/item_consumed.tres @@ -0,0 +1,9 @@ +[gd_resource type="Resource" script_class="EventTypeDefinition" load_steps=2 format=3] + +[ext_resource type="Script" path="res://simulation/definitions/EventTypeDefinition.gd" id="1"] + +[resource] +script = ExtResource("1") +event_type_id = &"item_consumed" +display_name = "Item Consumed" +category = "economy" diff --git a/simulation/definitions/event_types/npc_died.tres b/simulation/definitions/event_types/npc_died.tres new file mode 100644 index 0000000..fad89d1 --- /dev/null +++ b/simulation/definitions/event_types/npc_died.tres @@ -0,0 +1,9 @@ +[gd_resource type="Resource" script_class="EventTypeDefinition" load_steps=2 format=3] + +[ext_resource type="Script" path="res://simulation/definitions/EventTypeDefinition.gd" id="1"] + +[resource] +script = ExtResource("1") +event_type_id = &"npc_died" +display_name = "Villager Died" +category = "welfare" diff --git a/simulation/definitions/event_types/npc_slept.tres b/simulation/definitions/event_types/npc_slept.tres new file mode 100644 index 0000000..b39addc --- /dev/null +++ b/simulation/definitions/event_types/npc_slept.tres @@ -0,0 +1,9 @@ +[gd_resource type="Resource" script_class="EventTypeDefinition" load_steps=2 format=3] + +[ext_resource type="Script" path="res://simulation/definitions/EventTypeDefinition.gd" id="1"] + +[resource] +script = ExtResource("1") +event_type_id = &"npc_slept" +display_name = "Villager Slept" +category = "activity" diff --git a/simulation/definitions/event_types/raid_started.tres b/simulation/definitions/event_types/raid_started.tres new file mode 100644 index 0000000..1b31603 --- /dev/null +++ b/simulation/definitions/event_types/raid_started.tres @@ -0,0 +1,9 @@ +[gd_resource type="Resource" script_class="EventTypeDefinition" load_steps=2 format=3] + +[ext_resource type="Script" path="res://simulation/definitions/EventTypeDefinition.gd" id="1"] + +[resource] +script = ExtResource("1") +event_type_id = &"raid_started" +display_name = "Raid Started" +category = "conflict" diff --git a/simulation/definitions/event_types/resource_depleted.tres b/simulation/definitions/event_types/resource_depleted.tres new file mode 100644 index 0000000..fb737ec --- /dev/null +++ b/simulation/definitions/event_types/resource_depleted.tres @@ -0,0 +1,9 @@ +[gd_resource type="Resource" script_class="EventTypeDefinition" load_steps=2 format=3] + +[ext_resource type="Script" path="res://simulation/definitions/EventTypeDefinition.gd" id="1"] + +[resource] +script = ExtResource("1") +event_type_id = &"resource_depleted" +display_name = "Resource Depleted" +category = "economy" diff --git a/simulation/definitions/event_types/resource_extracted.tres b/simulation/definitions/event_types/resource_extracted.tres new file mode 100644 index 0000000..f3dc38d --- /dev/null +++ b/simulation/definitions/event_types/resource_extracted.tres @@ -0,0 +1,9 @@ +[gd_resource type="Resource" script_class="EventTypeDefinition" load_steps=2 format=3] + +[ext_resource type="Script" path="res://simulation/definitions/EventTypeDefinition.gd" id="1"] + +[resource] +script = ExtResource("1") +event_type_id = &"resource_extracted" +display_name = "Resource Extracted" +category = "economy" diff --git a/simulation/definitions/event_types/storage_deposited.tres b/simulation/definitions/event_types/storage_deposited.tres new file mode 100644 index 0000000..48ca7fc --- /dev/null +++ b/simulation/definitions/event_types/storage_deposited.tres @@ -0,0 +1,9 @@ +[gd_resource type="Resource" script_class="EventTypeDefinition" load_steps=2 format=3] + +[ext_resource type="Script" path="res://simulation/definitions/EventTypeDefinition.gd" id="1"] + +[resource] +script = ExtResource("1") +event_type_id = &"storage_deposited" +display_name = "Storage Deposited" +category = "economy" diff --git a/simulation/definitions/event_types/storage_withdrawn.tres b/simulation/definitions/event_types/storage_withdrawn.tres new file mode 100644 index 0000000..4849803 --- /dev/null +++ b/simulation/definitions/event_types/storage_withdrawn.tres @@ -0,0 +1,9 @@ +[gd_resource type="Resource" script_class="EventTypeDefinition" load_steps=2 format=3] + +[ext_resource type="Script" path="res://simulation/definitions/EventTypeDefinition.gd" id="1"] + +[resource] +script = ExtResource("1") +event_type_id = &"storage_withdrawn" +display_name = "Storage Withdrawn" +category = "economy" diff --git a/simulation/definitions/event_types/task_blocked.tres b/simulation/definitions/event_types/task_blocked.tres new file mode 100644 index 0000000..1b05c44 --- /dev/null +++ b/simulation/definitions/event_types/task_blocked.tres @@ -0,0 +1,9 @@ +[gd_resource type="Resource" script_class="EventTypeDefinition" load_steps=2 format=3] + +[ext_resource type="Script" path="res://simulation/definitions/EventTypeDefinition.gd" id="1"] + +[resource] +script = ExtResource("1") +event_type_id = &"task_blocked" +display_name = "Task Blocked" +category = "activity" diff --git a/simulation/definitions/event_types/task_started.tres b/simulation/definitions/event_types/task_started.tres new file mode 100644 index 0000000..5899b69 --- /dev/null +++ b/simulation/definitions/event_types/task_started.tres @@ -0,0 +1,9 @@ +[gd_resource type="Resource" script_class="EventTypeDefinition" load_steps=2 format=3] + +[ext_resource type="Script" path="res://simulation/definitions/EventTypeDefinition.gd" id="1"] + +[resource] +script = ExtResource("1") +event_type_id = &"task_started" +display_name = "Task Started" +category = "activity" diff --git a/simulation/definitions/event_types/villager_weak.tres b/simulation/definitions/event_types/villager_weak.tres new file mode 100644 index 0000000..d1a36c7 --- /dev/null +++ b/simulation/definitions/event_types/villager_weak.tres @@ -0,0 +1,9 @@ +[gd_resource type="Resource" script_class="EventTypeDefinition" load_steps=2 format=3] + +[ext_resource type="Script" path="res://simulation/definitions/EventTypeDefinition.gd" id="1"] + +[resource] +script = ExtResource("1") +event_type_id = &"villager_weak" +display_name = "Villager Weak" +category = "welfare" diff --git a/simulation/definitions/event_types/war_aborted.tres b/simulation/definitions/event_types/war_aborted.tres new file mode 100644 index 0000000..6fae185 --- /dev/null +++ b/simulation/definitions/event_types/war_aborted.tres @@ -0,0 +1,9 @@ +[gd_resource type="Resource" script_class="EventTypeDefinition" load_steps=2 format=3] + +[ext_resource type="Script" path="res://simulation/definitions/EventTypeDefinition.gd" id="1"] + +[resource] +script = ExtResource("1") +event_type_id = &"war_aborted" +display_name = "War Aborted" +category = "conflict" diff --git a/simulation/definitions/event_types/war_resolved.tres b/simulation/definitions/event_types/war_resolved.tres new file mode 100644 index 0000000..25c0d2a --- /dev/null +++ b/simulation/definitions/event_types/war_resolved.tres @@ -0,0 +1,9 @@ +[gd_resource type="Resource" script_class="EventTypeDefinition" load_steps=2 format=3] + +[ext_resource type="Script" path="res://simulation/definitions/EventTypeDefinition.gd" id="1"] + +[resource] +script = ExtResource("1") +event_type_id = &"war_resolved" +display_name = "War Resolved" +category = "conflict" diff --git a/simulation/definitions/event_types/wolf_hunt.tres b/simulation/definitions/event_types/wolf_hunt.tres new file mode 100644 index 0000000..94ae5b0 --- /dev/null +++ b/simulation/definitions/event_types/wolf_hunt.tres @@ -0,0 +1,9 @@ +[gd_resource type="Resource" script_class="EventTypeDefinition" load_steps=2 format=3] + +[ext_resource type="Script" path="res://simulation/definitions/EventTypeDefinition.gd" id="1"] + +[resource] +script = ExtResource("1") +event_type_id = &"wolf_hunt" +display_name = "Wolf Hunt" +category = "conflict" diff --git a/simulation/definitions/packs/core.tres b/simulation/definitions/packs/core.tres index cecad48..1d5d78b 100644 --- a/simulation/definitions/packs/core.tres +++ b/simulation/definitions/packs/core.tres @@ -1,4 +1,4 @@ -[gd_resource type="Resource" script_class="SimulationContentPack" load_steps=60 format=3] +[gd_resource type="Resource" script_class="SimulationContentPack" load_steps=93 format=3] [ext_resource type="Script" path="res://simulation/definitions/SimulationContentPack.gd" id="1_pack"] [ext_resource type="Resource" path="res://simulation/definitions/actions/defend.tres" id="2_defend"] @@ -58,6 +58,39 @@ [ext_resource type="Resource" path="res://simulation/dialogue/resources/intents/share_fact.tres" id="56_share"] [ext_resource type="Resource" path="res://simulation/dialogue/resources/intents/thank.tres" id="57_thank"] [ext_resource type="Resource" path="res://simulation/dialogue/resources/jajce_conversation_templates.tres" id="58_templates"] +[ext_resource type="Resource" path="res://simulation/definitions/event_types/resource_extracted.tres" id="59_resource_extracted"] +[ext_resource type="Resource" path="res://simulation/definitions/event_types/storage_deposited.tres" id="60_storage_deposited"] +[ext_resource type="Resource" path="res://simulation/definitions/event_types/storage_withdrawn.tres" id="61_storage_withdrawn"] +[ext_resource type="Resource" path="res://simulation/definitions/event_types/item_consumed.tres" id="62_item_consumed"] +[ext_resource type="Resource" path="res://simulation/definitions/event_types/npc_slept.tres" id="63_npc_slept"] +[ext_resource type="Resource" path="res://simulation/definitions/event_types/npc_died.tres" id="64_npc_died"] +[ext_resource type="Resource" path="res://simulation/definitions/event_types/task_started.tres" id="65_task_started"] +[ext_resource type="Resource" path="res://simulation/definitions/event_types/task_blocked.tres" id="66_task_blocked"] +[ext_resource type="Resource" path="res://simulation/definitions/event_types/resource_depleted.tres" id="67_resource_depleted"] +[ext_resource type="Resource" path="res://simulation/definitions/event_types/animal_fed.tres" id="68_animal_fed"] +[ext_resource type="Resource" path="res://simulation/definitions/event_types/villager_weak.tres" id="69_villager_weak"] +[ext_resource type="Resource" path="res://simulation/definitions/event_types/home_damaged.tres" id="70_home_damaged"] +[ext_resource type="Resource" path="res://simulation/definitions/event_types/commitment_accepted.tres" id="71_commitment_accepted"] +[ext_resource type="Resource" path="res://simulation/definitions/event_types/commitment_fulfilled.tres" id="72_commitment_fulfilled"] +[ext_resource type="Resource" path="res://simulation/definitions/event_types/commitment_broken.tres" id="73_commitment_broken"] +[ext_resource type="Resource" path="res://simulation/definitions/event_types/commitment_released.tres" id="74_commitment_released"] +[ext_resource type="Resource" path="res://simulation/definitions/event_types/commitment_superseded.tres" id="75_commitment_superseded"] +[ext_resource type="Resource" path="res://simulation/definitions/event_types/combatant_hurt.tres" id="76_combatant_hurt"] +[ext_resource type="Resource" path="res://simulation/definitions/event_types/combatant_killed.tres" id="77_combatant_killed"] +[ext_resource type="Resource" path="res://simulation/definitions/event_types/raid_started.tres" id="78_raid_started"] +[ext_resource type="Resource" path="res://simulation/definitions/event_types/war_resolved.tres" id="79_war_resolved"] +[ext_resource type="Resource" path="res://simulation/definitions/event_types/war_aborted.tres" id="80_war_aborted"] +[ext_resource type="Resource" path="res://simulation/definitions/event_types/wolf_hunt.tres" id="81_wolf_hunt"] +[ext_resource type="Resource" path="res://simulation/definitions/social_consequences/storage_food_aid_trust.tres" id="82_storage_food_aid"] +[ext_resource type="Resource" path="res://simulation/definitions/social_consequences/player_food_aid_trust.tres" id="83_player_food_aid"] +[ext_resource type="Resource" path="res://simulation/definitions/social_consequences/commitment_accepted_obligation.tres" id="84_accepted_obligation"] +[ext_resource type="Resource" path="res://simulation/definitions/social_consequences/commitment_fulfilled_obligation.tres" id="85_fulfilled_obligation"] +[ext_resource type="Resource" path="res://simulation/definitions/social_consequences/commitment_fulfilled_trust.tres" id="86_fulfilled_trust"] +[ext_resource type="Resource" path="res://simulation/definitions/social_consequences/commitment_broken_obligation.tres" id="87_broken_obligation"] +[ext_resource type="Resource" path="res://simulation/definitions/social_consequences/commitment_broken_trust.tres" id="88_broken_trust"] +[ext_resource type="Resource" path="res://simulation/definitions/social_consequences/commitment_broken_hostility.tres" id="89_broken_hostility"] +[ext_resource type="Resource" path="res://simulation/definitions/social_consequences/commitment_released_obligation.tres" id="90_released_obligation"] +[ext_resource type="Resource" path="res://simulation/definitions/social_consequences/commitment_superseded_obligation.tres" id="91_superseded_obligation"] [resource] script = ExtResource("1_pack") @@ -74,3 +107,5 @@ animals = [ExtResource("39_goat"), ExtResource("40_sheep")] situations = [ExtResource("41_pantry_situation")] dialogue_intents = [ExtResource("42_accept"), ExtResource("43_acknowledge"), ExtResource("44_wellbeing"), ExtResource("45_happened"), ExtResource("46_who"), ExtResource("47_work"), ExtResource("48_decline"), ExtResource("49_need"), ExtResource("50_goodbye"), ExtResource("51_greet"), ExtResource("52_offer"), ExtResource("53_renegotiate"), ExtResource("54_progress"), ExtResource("55_reproach"), ExtResource("56_share"), ExtResource("57_thank")] dialogue_template_catalogs = [ExtResource("58_templates")] +event_types = [ExtResource("59_resource_extracted"), ExtResource("60_storage_deposited"), ExtResource("61_storage_withdrawn"), ExtResource("62_item_consumed"), ExtResource("63_npc_slept"), ExtResource("64_npc_died"), ExtResource("65_task_started"), ExtResource("66_task_blocked"), ExtResource("67_resource_depleted"), ExtResource("68_animal_fed"), ExtResource("69_villager_weak"), ExtResource("70_home_damaged"), ExtResource("71_commitment_accepted"), ExtResource("72_commitment_fulfilled"), ExtResource("73_commitment_broken"), ExtResource("74_commitment_released"), ExtResource("75_commitment_superseded"), ExtResource("76_combatant_hurt"), ExtResource("77_combatant_killed"), ExtResource("78_raid_started"), ExtResource("79_war_resolved"), ExtResource("80_war_aborted"), ExtResource("81_wolf_hunt")] +social_consequences = [ExtResource("82_storage_food_aid"), ExtResource("83_player_food_aid"), ExtResource("84_accepted_obligation"), ExtResource("85_fulfilled_obligation"), ExtResource("86_fulfilled_trust"), ExtResource("87_broken_obligation"), ExtResource("88_broken_trust"), ExtResource("89_broken_hostility"), ExtResource("90_released_obligation"), ExtResource("91_superseded_obligation")] diff --git a/simulation/definitions/social_consequences/commitment_accepted_obligation.tres b/simulation/definitions/social_consequences/commitment_accepted_obligation.tres new file mode 100644 index 0000000..9e219f4 --- /dev/null +++ b/simulation/definitions/social_consequences/commitment_accepted_obligation.tres @@ -0,0 +1,11 @@ +[gd_resource type="Resource" script_class="SocialConsequenceDefinition" load_steps=2 format=3] + +[ext_resource type="Script" path="res://simulation/definitions/SocialConsequenceDefinition.gd" id="1"] + +[resource] +script = ExtResource("1") +consequence_id = &"commitment_accepted_obligation" +display_name = "Accepted Commitment Obligation" +event_type_id = &"commitment_accepted" +relationship_dimension_id = &"obligation" +parameters = {"fallback_delta": 0.25, "multiplier": 1.0, "payload_key": "obligation_delta"} diff --git a/simulation/definitions/social_consequences/commitment_broken_hostility.tres b/simulation/definitions/social_consequences/commitment_broken_hostility.tres new file mode 100644 index 0000000..ad8c3cd --- /dev/null +++ b/simulation/definitions/social_consequences/commitment_broken_hostility.tres @@ -0,0 +1,11 @@ +[gd_resource type="Resource" script_class="SocialConsequenceDefinition" load_steps=2 format=3] + +[ext_resource type="Script" path="res://simulation/definitions/SocialConsequenceDefinition.gd" id="1"] + +[resource] +script = ExtResource("1") +consequence_id = &"commitment_broken_hostility" +display_name = "Broken Commitment Hostility" +event_type_id = &"commitment_broken" +relationship_dimension_id = &"hostility" +parameters = {"delta": 0.15} diff --git a/simulation/definitions/social_consequences/commitment_broken_obligation.tres b/simulation/definitions/social_consequences/commitment_broken_obligation.tres new file mode 100644 index 0000000..4cbaa80 --- /dev/null +++ b/simulation/definitions/social_consequences/commitment_broken_obligation.tres @@ -0,0 +1,11 @@ +[gd_resource type="Resource" script_class="SocialConsequenceDefinition" load_steps=2 format=3] + +[ext_resource type="Script" path="res://simulation/definitions/SocialConsequenceDefinition.gd" id="1"] + +[resource] +script = ExtResource("1") +consequence_id = &"commitment_broken_obligation" +display_name = "Broken Commitment Obligation" +event_type_id = &"commitment_broken" +relationship_dimension_id = &"obligation" +parameters = {"fallback_delta": 0.25, "multiplier": -1.0, "payload_key": "obligation_delta"} diff --git a/simulation/definitions/social_consequences/commitment_broken_trust.tres b/simulation/definitions/social_consequences/commitment_broken_trust.tres new file mode 100644 index 0000000..34a6d38 --- /dev/null +++ b/simulation/definitions/social_consequences/commitment_broken_trust.tres @@ -0,0 +1,11 @@ +[gd_resource type="Resource" script_class="SocialConsequenceDefinition" load_steps=2 format=3] + +[ext_resource type="Script" path="res://simulation/definitions/SocialConsequenceDefinition.gd" id="1"] + +[resource] +script = ExtResource("1") +consequence_id = &"commitment_broken_trust" +display_name = "Broken Commitment Trust" +event_type_id = &"commitment_broken" +relationship_dimension_id = &"trust" +parameters = {"delta": -0.2} diff --git a/simulation/definitions/social_consequences/commitment_fulfilled_obligation.tres b/simulation/definitions/social_consequences/commitment_fulfilled_obligation.tres new file mode 100644 index 0000000..015a795 --- /dev/null +++ b/simulation/definitions/social_consequences/commitment_fulfilled_obligation.tres @@ -0,0 +1,11 @@ +[gd_resource type="Resource" script_class="SocialConsequenceDefinition" load_steps=2 format=3] + +[ext_resource type="Script" path="res://simulation/definitions/SocialConsequenceDefinition.gd" id="1"] + +[resource] +script = ExtResource("1") +consequence_id = &"commitment_fulfilled_obligation" +display_name = "Fulfilled Commitment Obligation" +event_type_id = &"commitment_fulfilled" +relationship_dimension_id = &"obligation" +parameters = {"fallback_delta": 0.25, "multiplier": -1.0, "payload_key": "obligation_delta"} diff --git a/simulation/definitions/social_consequences/commitment_fulfilled_trust.tres b/simulation/definitions/social_consequences/commitment_fulfilled_trust.tres new file mode 100644 index 0000000..aba39ef --- /dev/null +++ b/simulation/definitions/social_consequences/commitment_fulfilled_trust.tres @@ -0,0 +1,11 @@ +[gd_resource type="Resource" script_class="SocialConsequenceDefinition" load_steps=2 format=3] + +[ext_resource type="Script" path="res://simulation/definitions/SocialConsequenceDefinition.gd" id="1"] + +[resource] +script = ExtResource("1") +consequence_id = &"commitment_fulfilled_trust" +display_name = "Fulfilled Commitment Trust" +event_type_id = &"commitment_fulfilled" +relationship_dimension_id = &"trust" +parameters = {"delta": 0.1} diff --git a/simulation/definitions/social_consequences/commitment_released_obligation.tres b/simulation/definitions/social_consequences/commitment_released_obligation.tres new file mode 100644 index 0000000..e114fa6 --- /dev/null +++ b/simulation/definitions/social_consequences/commitment_released_obligation.tres @@ -0,0 +1,11 @@ +[gd_resource type="Resource" script_class="SocialConsequenceDefinition" load_steps=2 format=3] + +[ext_resource type="Script" path="res://simulation/definitions/SocialConsequenceDefinition.gd" id="1"] + +[resource] +script = ExtResource("1") +consequence_id = &"commitment_released_obligation" +display_name = "Released Commitment Obligation" +event_type_id = &"commitment_released" +relationship_dimension_id = &"obligation" +parameters = {"fallback_delta": 0.25, "multiplier": -1.0, "payload_key": "obligation_delta"} diff --git a/simulation/definitions/social_consequences/commitment_superseded_obligation.tres b/simulation/definitions/social_consequences/commitment_superseded_obligation.tres new file mode 100644 index 0000000..9a5584c --- /dev/null +++ b/simulation/definitions/social_consequences/commitment_superseded_obligation.tres @@ -0,0 +1,11 @@ +[gd_resource type="Resource" script_class="SocialConsequenceDefinition" load_steps=2 format=3] + +[ext_resource type="Script" path="res://simulation/definitions/SocialConsequenceDefinition.gd" id="1"] + +[resource] +script = ExtResource("1") +consequence_id = &"commitment_superseded_obligation" +display_name = "Superseded Commitment Obligation" +event_type_id = &"commitment_superseded" +relationship_dimension_id = &"obligation" +parameters = {"fallback_delta": 0.25, "multiplier": -1.0, "payload_key": "obligation_delta"} diff --git a/simulation/definitions/social_consequences/player_food_aid_trust.tres b/simulation/definitions/social_consequences/player_food_aid_trust.tres new file mode 100644 index 0000000..9597afe --- /dev/null +++ b/simulation/definitions/social_consequences/player_food_aid_trust.tres @@ -0,0 +1,12 @@ +[gd_resource type="Resource" script_class="SocialConsequenceDefinition" load_steps=2 format=3] + +[ext_resource type="Script" path="res://simulation/definitions/SocialConsequenceDefinition.gd" id="1"] + +[resource] +script = ExtResource("1") +consequence_id = &"player_food_aid_trust" +display_name = "Player Food Aid Trust" +description = "A knowledgeable hungry observer trusts the player for supplying pantry food." +event_type_id = &"resource_extracted" +relationship_dimension_id = &"trust" +parameters = {"actor_id": -1, "delta": 0.15, "destination_id": "village_pantry", "item_id": "food"} diff --git a/simulation/definitions/social_consequences/storage_food_aid_trust.tres b/simulation/definitions/social_consequences/storage_food_aid_trust.tres new file mode 100644 index 0000000..2a87abd --- /dev/null +++ b/simulation/definitions/social_consequences/storage_food_aid_trust.tres @@ -0,0 +1,12 @@ +[gd_resource type="Resource" script_class="SocialConsequenceDefinition" load_steps=2 format=3] + +[ext_resource type="Script" path="res://simulation/definitions/SocialConsequenceDefinition.gd" id="1"] + +[resource] +script = ExtResource("1") +consequence_id = &"storage_food_aid_trust" +display_name = "Storage Food Aid Trust" +description = "A knowledgeable hungry observer trusts a familiar contributor who supplies food." +event_type_id = &"storage_deposited" +relationship_dimension_id = &"trust" +parameters = {"delta": 0.15, "item_id": "food"} diff --git a/tests/unit/test_event_social_content.gd b/tests/unit/test_event_social_content.gd new file mode 100644 index 0000000..c5fbeed --- /dev/null +++ b/tests/unit/test_event_social_content.gd @@ -0,0 +1,220 @@ +extends GutTest + +const RelationshipSystemScript := preload("res://simulation/relationships/RelationshipSystem.gd") + +const CORE_EVENT_TYPE_IDS: Array[StringName] = [ + SimulationIds.EVENT_RESOURCE_EXTRACTED, + SimulationIds.EVENT_STORAGE_DEPOSITED, + SimulationIds.EVENT_STORAGE_WITHDRAWN, + SimulationIds.EVENT_ITEM_CONSUMED, + SimulationIds.EVENT_NPC_SLEPT, + SimulationIds.EVENT_NPC_DIED, + SimulationIds.EVENT_TASK_STARTED, + SimulationIds.EVENT_TASK_BLOCKED, + SimulationIds.EVENT_RESOURCE_DEPLETED, + SimulationIds.EVENT_ANIMAL_FED, + SimulationIds.EVENT_VILLAGER_WEAK, + SimulationIds.EVENT_HOME_DAMAGED, + SimulationIds.EVENT_COMMITMENT_ACCEPTED, + SimulationIds.EVENT_COMMITMENT_FULFILLED, + SimulationIds.EVENT_COMMITMENT_BROKEN, + SimulationIds.EVENT_COMMITMENT_RELEASED, + SimulationIds.EVENT_COMMITMENT_SUPERSEDED, + SimulationIds.EVENT_COMBATANT_HURT, + SimulationIds.EVENT_COMBATANT_KILLED, + SimulationIds.EVENT_RAID_STARTED, + SimulationIds.EVENT_WAR_RESOLVED, + SimulationIds.EVENT_WAR_ABORTED, + SimulationIds.EVENT_WOLF_HUNT, +] +const CORE_SOCIAL_CONSEQUENCE_IDS: Array[StringName] = [ + &"commitment_accepted_obligation", + &"commitment_broken_hostility", + &"commitment_broken_obligation", + &"commitment_broken_trust", + &"commitment_fulfilled_obligation", + &"commitment_fulfilled_trust", + &"commitment_released_obligation", + &"commitment_superseded_obligation", + &"player_food_aid_trust", + &"storage_food_aid_trust", +] + + +func test_core_catalog_authors_every_canonical_event_and_existing_social_effect() -> void: + var catalog := ContentCatalog.create_core() + assert_true(catalog.is_valid(), "%s" % [catalog.get_errors()]) + assert_eq(_event_ids(catalog.get_event_types()), _sorted(CORE_EVENT_TYPE_IDS)) + assert_eq( + _consequence_ids(catalog.get_social_consequences()), _sorted(CORE_SOCIAL_CONSEQUENCE_IDS) + ) + for consequence in catalog.get_social_consequences(): + assert_not_null(catalog.get_event_type(consequence.event_type_id)) + assert_has(RelationshipStateRecord.DIMENSIONS, consequence.relationship_dimension_id) + assert_true(consequence.validate().is_empty(), "%s" % [consequence.validate()]) + + assert_eq( + float(catalog.get_social_consequence(&"storage_food_aid_trust").parameters["delta"]), + RelationshipSystemScript.FOOD_AID_TRUST_GAIN + ) + assert_eq( + float(catalog.get_social_consequence(&"player_food_aid_trust").parameters["delta"]), + RelationshipSystemScript.FOOD_AID_TRUST_GAIN + ) + assert_eq( + float( + ( + catalog + . get_social_consequence(&"commitment_accepted_obligation") + . parameters["fallback_delta"] + ) + ), + CommitmentLifecycleService.ACCEPTED_OBLIGATION + ) + assert_eq( + float(catalog.get_social_consequence(&"commitment_fulfilled_trust").parameters["delta"]), + CommitmentLifecycleService.FULFILLED_TRUST + ) + assert_eq( + float(catalog.get_social_consequence(&"commitment_broken_trust").parameters["delta"]), + CommitmentLifecycleService.BROKEN_TRUST + ) + assert_eq( + float(catalog.get_social_consequence(&"commitment_broken_hostility").parameters["delta"]), + CommitmentLifecycleService.BROKEN_HOSTILITY + ) + var report := catalog.get_authoring_report() + assert_eq(report["event_types"], _event_ids(catalog.get_event_types())) + assert_eq(report["social_consequences"], _consequence_ids(catalog.get_social_consequences())) + + +func test_addon_event_and_consequence_are_data_only_and_order_independent() -> void: + var core := load(ContentCatalog.CORE_PACK_PATH) as SimulationContentPack + var addon := _pack(&"festival_addon") + addon.required_pack_ids = [&"core"] + var event := _event(&"festival_shared_meal", "Festival Shared Meal") + event.category = String(EventTypeDefinition.CATEGORY_SOCIAL) + event.parameters = {"public": true, "salience": 0.5, "topic": &"festival"} + var consequence := _consequence( + &"festival_shared_meal_affection", + event.event_type_id, + RelationshipStateRecord.DIMENSION_AFFECTION + ) + consequence.parameters = {"delta": 0.05, "observer_role": "guest"} + addon.event_types = [event] + addon.social_consequences = [consequence] + + var first := ContentCatalog.new() + var first_packs: Array[SimulationContentPack] = [addon, core] + assert_true(first.rebuild(first_packs).is_empty(), "%s" % [first.get_errors()]) + assert_same(first.get_event_type(event.event_type_id), event) + assert_same(first.get_social_consequence(consequence.consequence_id), consequence) + + var second := ContentCatalog.new() + var second_packs: Array[SimulationContentPack] = [core, addon] + assert_true(second.rebuild(second_packs).is_empty(), "%s" % [second.get_errors()]) + assert_eq(first.get_authoring_report(), second.get_authoring_report()) + assert_eq(_event_ids(first.get_event_types()), _sorted(_event_ids(first.get_event_types()))) + assert_eq( + _consequence_ids(first.get_social_consequences()), + _sorted(_consequence_ids(first.get_social_consequences())) + ) + + +func test_invalid_event_and_consequence_pack_publishes_nothing() -> void: + var core := load(ContentCatalog.CORE_PACK_PATH) as SimulationContentPack + var broken := _pack(&"broken_event_content") + broken.required_pack_ids = [&"core"] + var invalid_event := _event(&"invalid_event", "Invalid Event") + invalid_event.parameters = {"nested": {"not": "primitive"}} + broken.event_types = [core.event_types[0], invalid_event] + var predicate := SituationPredicate.new() + predicate.predicate_id = &"missing_event_predicate" + predicate.evaluator_id = &"event_type" + predicate.parameters = {"event_type": "missing_situation_event"} + var situation := SituationDefinition.new() + situation.situation_definition_id = &"missing_event_situation" + situation.display_name = "Missing event situation" + situation.trigger_predicates = [predicate] + broken.situations = [situation] + var invalid_consequence := _consequence(&"invalid_consequence", &"missing_event", &"rumour") + invalid_consequence.parameters = {"weights": [0.1, 0.2]} + broken.social_consequences = [core.social_consequences[0], invalid_consequence] + + var catalog := ContentCatalog.create_core() + assert_true(catalog.is_valid()) + var packs: Array[SimulationContentPack] = [broken, core] + var errors := catalog.rebuild(packs) + for expected in [ + "Duplicate event_type_id", + "Duplicate social consequence_id", + "references unknown relationship dimension 'rumour'", + "references unknown event type 'missing_event'", + "references unknown event type 'missing_situation_event'", + "parameter 'nested' must be primitive", + "parameter 'weights' must be primitive", + ]: + assert_true(_has_error(errors, expected), "%s missing from %s" % [expected, errors]) + assert_false(catalog.is_valid()) + assert_true(catalog.get_packs().is_empty()) + assert_true(catalog.get_actions().is_empty()) + assert_true(catalog.get_event_types().is_empty()) + assert_true(catalog.get_social_consequences().is_empty()) + assert_null(catalog.get_event_type(&"invalid_event")) + assert_null(catalog.get_social_consequence(&"invalid_consequence")) + + +func _pack(pack_id: StringName) -> SimulationContentPack: + var pack := SimulationContentPack.new() + pack.pack_id = pack_id + pack.display_name = String(pack_id).capitalize() + return pack + + +func _event(event_type_id: StringName, display_name: String) -> EventTypeDefinition: + var definition := EventTypeDefinition.new() + definition.event_type_id = event_type_id + definition.display_name = display_name + return definition + + +func _consequence( + consequence_id: StringName, event_type_id: StringName, dimension_id: StringName +) -> SocialConsequenceDefinition: + var definition := SocialConsequenceDefinition.new() + definition.consequence_id = consequence_id + definition.display_name = String(consequence_id).capitalize() + definition.event_type_id = event_type_id + definition.relationship_dimension_id = dimension_id + return definition + + +func _event_ids(definitions: Array[EventTypeDefinition]) -> Array[StringName]: + var ids: Array[StringName] = [] + for definition in definitions: + ids.append(definition.event_type_id) + return ids + + +func _consequence_ids( + definitions: Array[SocialConsequenceDefinition], +) -> Array[StringName]: + var ids: Array[StringName] = [] + for definition in definitions: + ids.append(definition.consequence_id) + return ids + + +func _sorted(ids: Array[StringName]) -> Array[StringName]: + var result := ids.duplicate() + result.sort_custom( + func(left: StringName, right: StringName) -> bool: return String(left) < String(right) + ) + return result + + +func _has_error(errors: Array[String], fragment: String) -> bool: + for error in errors: + if fragment in error: + return true + return false diff --git a/tests/unit/test_event_social_content.gd.uid b/tests/unit/test_event_social_content.gd.uid new file mode 100644 index 0000000..14a748e --- /dev/null +++ b/tests/unit/test_event_social_content.gd.uid @@ -0,0 +1 @@ +uid://w24xte28x0k7