Files
gamedev-the-steward/tests/unit/test_event_social_content.gd
T
2026-08-12 22:10:59 +02:00

221 lines
8.3 KiB
GDScript

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