feat: let villagers witness and trust the player restock
This commit is contained in:
@@ -1,6 +1,8 @@
|
|||||||
class_name SimulationIds
|
class_name SimulationIds
|
||||||
extends RefCounted
|
extends RefCounted
|
||||||
|
|
||||||
|
const PLAYER_ACTOR_ID := -1
|
||||||
|
|
||||||
const ACTION_IDLE := &"idle"
|
const ACTION_IDLE := &"idle"
|
||||||
const ACTION_DEAD := &"dead"
|
const ACTION_DEAD := &"dead"
|
||||||
const ACTION_SLEEP := &"sleep"
|
const ACTION_SLEEP := &"sleep"
|
||||||
|
|||||||
@@ -11,11 +11,14 @@ func observe_event(event: EconomicEventRecord, npcs: Array[SimNPC]) -> Array[Kno
|
|||||||
var learned: Array[KnownEventStateRecord] = []
|
var learned: Array[KnownEventStateRecord] = []
|
||||||
if not is_knowable_event(event):
|
if not is_knowable_event(event):
|
||||||
return learned
|
return learned
|
||||||
var actor := _find_npc(int(event.data["actor_id"]), npcs)
|
var actor_id := int(event.data["actor_id"])
|
||||||
if actor == null:
|
var is_player_actor := actor_id == SimulationIds.PLAYER_ACTOR_ID
|
||||||
|
var actor := null if is_player_actor else _find_npc(actor_id, npcs)
|
||||||
|
if actor == null and not is_player_actor:
|
||||||
return learned
|
return learned
|
||||||
var event_id := int(event.data["event_id"])
|
var event_id := int(event.data["event_id"])
|
||||||
var acquired_tick := int(event.data["tick"])
|
var acquired_tick := int(event.data["tick"])
|
||||||
|
if actor != null:
|
||||||
var actor_record := _remember(
|
var actor_record := _remember(
|
||||||
actor.id, event_id, SimulationIds.KNOWLEDGE_ACQUISITION_PERFORMED, acquired_tick
|
actor.id, event_id, SimulationIds.KNOWLEDGE_ACQUISITION_PERFORMED, acquired_tick
|
||||||
)
|
)
|
||||||
@@ -24,7 +27,9 @@ func observe_event(event: EconomicEventRecord, npcs: Array[SimNPC]) -> Array[Kno
|
|||||||
var witness_radius_squared := WITNESS_RADIUS * WITNESS_RADIUS
|
var witness_radius_squared := WITNESS_RADIUS * WITNESS_RADIUS
|
||||||
var event_position := event.get_world_position()
|
var event_position := event.get_world_position()
|
||||||
for npc in npcs:
|
for npc in npcs:
|
||||||
if npc.id == actor.id or npc.is_dead:
|
if npc.is_dead:
|
||||||
|
continue
|
||||||
|
if not is_player_actor and npc.id == actor.id:
|
||||||
continue
|
continue
|
||||||
if npc.position.distance_squared_to(event_position) > witness_radius_squared:
|
if npc.position.distance_squared_to(event_position) > witness_radius_squared:
|
||||||
continue
|
continue
|
||||||
@@ -247,6 +252,17 @@ static func is_knowable_event(event: EconomicEventRecord) -> bool:
|
|||||||
return false
|
return false
|
||||||
if event_type == SimulationIds.EVENT_STORAGE_DEPOSITED:
|
if event_type == SimulationIds.EVENT_STORAGE_DEPOSITED:
|
||||||
return item_id == SimulationIds.RESOURCE_FOOD
|
return item_id == SimulationIds.RESOURCE_FOOD
|
||||||
|
if event_type == SimulationIds.EVENT_RESOURCE_EXTRACTED:
|
||||||
|
var player_actor_id := int(event.data["actor_id"])
|
||||||
|
if player_actor_id != SimulationIds.PLAYER_ACTOR_ID:
|
||||||
|
return false
|
||||||
|
return (
|
||||||
|
StringName(event.data["destination_id"])
|
||||||
|
in [
|
||||||
|
SimulationIds.STORAGE_VILLAGE_PANTRY,
|
||||||
|
SimulationIds.STORAGE_VILLAGE_WOODPILE,
|
||||||
|
]
|
||||||
|
)
|
||||||
if event_type != SimulationIds.EVENT_STORAGE_WITHDRAWN:
|
if event_type != SimulationIds.EVENT_STORAGE_WITHDRAWN:
|
||||||
return false
|
return false
|
||||||
var actor_id := int(event.data["actor_id"])
|
var actor_id := int(event.data["actor_id"])
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ const AID_HUNGER_THRESHOLD := 80.0
|
|||||||
const TRUSTED_HELP_THRESHOLD := 0.6
|
const TRUSTED_HELP_THRESHOLD := 0.6
|
||||||
const FOOD_AID_TRUST_GAIN := 0.15
|
const FOOD_AID_TRUST_GAIN := 0.15
|
||||||
const SHARED_WORK_FAMILIARITY_GAIN := 0.1
|
const SHARED_WORK_FAMILIARITY_GAIN := 0.1
|
||||||
|
const PLAYER_FAMILIARITY_SEED := 0.5
|
||||||
|
|
||||||
var relationships: Dictionary = {}
|
var relationships: Dictionary = {}
|
||||||
|
|
||||||
@@ -58,23 +59,47 @@ func apply_event(
|
|||||||
event: EconomicEventRecord, npcs: Array[SimNPC], knower_ids: Array[int]
|
event: EconomicEventRecord, npcs: Array[SimNPC], knower_ids: Array[int]
|
||||||
) -> Array[RelationshipStateRecord]:
|
) -> Array[RelationshipStateRecord]:
|
||||||
var changed: Array[RelationshipStateRecord] = []
|
var changed: Array[RelationshipStateRecord] = []
|
||||||
if StringName(event.data["event_type"]) != SimulationIds.EVENT_STORAGE_DEPOSITED:
|
var event_type := StringName(event.data["event_type"])
|
||||||
return changed
|
var item_id := StringName(event.data["item_id"])
|
||||||
if StringName(event.data["item_id"]) != SimulationIds.RESOURCE_FOOD:
|
if item_id != SimulationIds.RESOURCE_FOOD:
|
||||||
return changed
|
return changed
|
||||||
var contributor_id := int(event.data["actor_id"])
|
var contributor_id := int(event.data["actor_id"])
|
||||||
var event_id := int(event.data["event_id"])
|
var event_id := int(event.data["event_id"])
|
||||||
if contributor_id < 0 or event_id < 0 or float(event.data["amount"]) <= 0.0:
|
var is_food_supply := false
|
||||||
|
if event_type == SimulationIds.EVENT_STORAGE_DEPOSITED:
|
||||||
|
is_food_supply = true
|
||||||
|
elif (
|
||||||
|
event_type == SimulationIds.EVENT_RESOURCE_EXTRACTED
|
||||||
|
and contributor_id == SimulationIds.PLAYER_ACTOR_ID
|
||||||
|
):
|
||||||
|
is_food_supply = (
|
||||||
|
StringName(event.data["destination_id"]) == SimulationIds.STORAGE_VILLAGE_PANTRY
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
not is_food_supply
|
||||||
|
or event_id < 0
|
||||||
|
or float(event.data["amount"]) <= 0.0
|
||||||
|
or (contributor_id < 0 and contributor_id != SimulationIds.PLAYER_ACTOR_ID)
|
||||||
|
):
|
||||||
return changed
|
return changed
|
||||||
for observer in npcs:
|
for observer in npcs:
|
||||||
if observer.id == contributor_id or observer.is_dead:
|
if observer.is_dead:
|
||||||
|
continue
|
||||||
|
if contributor_id != SimulationIds.PLAYER_ACTOR_ID and observer.id == contributor_id:
|
||||||
continue
|
continue
|
||||||
if observer.id not in knower_ids:
|
if observer.id not in knower_ids:
|
||||||
continue
|
continue
|
||||||
if observer.hunger < AID_HUNGER_THRESHOLD:
|
if observer.hunger < AID_HUNGER_THRESHOLD:
|
||||||
continue
|
continue
|
||||||
var relationship := get_relationship(observer.id, contributor_id)
|
var relationship := get_relationship(observer.id, contributor_id)
|
||||||
if relationship == null or relationship.get_familiarity() <= 0.0:
|
if relationship == null:
|
||||||
|
if contributor_id != SimulationIds.PLAYER_ACTOR_ID:
|
||||||
|
continue
|
||||||
|
relationship = RelationshipStateRecord.create(
|
||||||
|
observer.id, contributor_id, PLAYER_FAMILIARITY_SEED
|
||||||
|
)
|
||||||
|
_add(relationship)
|
||||||
|
if relationship.get_familiarity() <= 0.0:
|
||||||
continue
|
continue
|
||||||
if event_id <= relationship.get_last_trust_cause_event_id():
|
if event_id <= relationship.get_last_trust_cause_event_id():
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -46,7 +46,10 @@ static func from_dictionary(record_data: Dictionary) -> RelationshipStateRecord:
|
|||||||
var familiarity := float(record_data["familiarity"])
|
var familiarity := float(record_data["familiarity"])
|
||||||
var trust := float(record_data["trust"])
|
var trust := float(record_data["trust"])
|
||||||
var cause_event_id := int(record_data["last_trust_cause_event_id"])
|
var cause_event_id := int(record_data["last_trust_cause_event_id"])
|
||||||
if observer_id < 0 or subject_id < 0 or observer_id == subject_id:
|
var is_player_subject := subject_id == SimulationIds.PLAYER_ACTOR_ID
|
||||||
|
if observer_id < 0 or observer_id == subject_id:
|
||||||
|
return null
|
||||||
|
if not is_player_subject and subject_id < 0:
|
||||||
return null
|
return null
|
||||||
if familiarity < 0.0 or familiarity > 1.0 or trust < 0.0 or trust > 1.0:
|
if familiarity < 0.0 or familiarity > 1.0 or trust < 0.0 or trust > 1.0:
|
||||||
return null
|
return null
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ class_name SimulationStateRecord
|
|||||||
extends RefCounted
|
extends RefCounted
|
||||||
|
|
||||||
const SCHEMA_NAME := "the_steward.simulation"
|
const SCHEMA_NAME := "the_steward.simulation"
|
||||||
const SCHEMA_VERSION := 11
|
const SCHEMA_VERSION := 12
|
||||||
const LEGACY_SCHEMA_VERSION := 1
|
const LEGACY_SCHEMA_VERSION := 1
|
||||||
const EVENT_LEGACY_SCHEMA_VERSION := 2
|
const EVENT_LEGACY_SCHEMA_VERSION := 2
|
||||||
const RELATIONSHIP_LEGACY_SCHEMA_VERSION := 3
|
const RELATIONSHIP_LEGACY_SCHEMA_VERSION := 3
|
||||||
@@ -12,6 +12,7 @@ const RETENTION_LEGACY_SCHEMA_VERSION := 6
|
|||||||
const OPPORTUNITY_LEGACY_SCHEMA_VERSION := 8
|
const OPPORTUNITY_LEGACY_SCHEMA_VERSION := 8
|
||||||
const ANIMAL_LEGACY_SCHEMA_VERSION := 9
|
const ANIMAL_LEGACY_SCHEMA_VERSION := 9
|
||||||
const ROUTINE_LEGACY_SCHEMA_VERSION := 10
|
const ROUTINE_LEGACY_SCHEMA_VERSION := 10
|
||||||
|
const PLAYER_RELATIONSHIP_SCHEMA_VERSION := 11
|
||||||
const PREVIOUS_SCHEMA_VERSION := 7
|
const PREVIOUS_SCHEMA_VERSION := 7
|
||||||
|
|
||||||
var simulation: Dictionary
|
var simulation: Dictionary
|
||||||
@@ -97,6 +98,7 @@ static func from_dictionary(record_data: Dictionary) -> SimulationStateRecord:
|
|||||||
OPPORTUNITY_LEGACY_SCHEMA_VERSION,
|
OPPORTUNITY_LEGACY_SCHEMA_VERSION,
|
||||||
ANIMAL_LEGACY_SCHEMA_VERSION,
|
ANIMAL_LEGACY_SCHEMA_VERSION,
|
||||||
ROUTINE_LEGACY_SCHEMA_VERSION,
|
ROUTINE_LEGACY_SCHEMA_VERSION,
|
||||||
|
PLAYER_RELATIONSHIP_SCHEMA_VERSION,
|
||||||
]
|
]
|
||||||
):
|
):
|
||||||
record_data = _migrate_legacy(record_data, version)
|
record_data = _migrate_legacy(record_data, version)
|
||||||
@@ -386,7 +388,10 @@ static func from_dictionary(record_data: Dictionary) -> SimulationStateRecord:
|
|||||||
return null
|
return null
|
||||||
var observer_id := relationship_record.get_observer_id()
|
var observer_id := relationship_record.get_observer_id()
|
||||||
var subject_id := relationship_record.get_subject_id()
|
var subject_id := relationship_record.get_subject_id()
|
||||||
if not npc_ids.has(observer_id) or not npc_ids.has(subject_id):
|
var is_player_subject := subject_id == SimulationIds.PLAYER_ACTOR_ID
|
||||||
|
if not npc_ids.has(observer_id):
|
||||||
|
return null
|
||||||
|
if not is_player_subject and not npc_ids.has(subject_id):
|
||||||
return null
|
return null
|
||||||
var relationship_key := "%d:%d" % [observer_id, subject_id]
|
var relationship_key := "%d:%d" % [observer_id, subject_id]
|
||||||
if relationship_keys.has(relationship_key):
|
if relationship_keys.has(relationship_key):
|
||||||
@@ -404,15 +409,7 @@ static func from_dictionary(record_data: Dictionary) -> SimulationStateRecord:
|
|||||||
return null
|
return null
|
||||||
if cause_event_id != RelationshipStateRecord.NO_CAUSE_EVENT:
|
if cause_event_id != RelationshipStateRecord.NO_CAUSE_EVENT:
|
||||||
var cause_event := event_records_by_id[cause_event_id] as EconomicEventRecord
|
var cause_event := event_records_by_id[cause_event_id] as EconomicEventRecord
|
||||||
if (
|
if not _is_valid_relationship_cause(cause_event, subject_id, is_player_subject):
|
||||||
int(cause_event.data["actor_id"]) != subject_id
|
|
||||||
or (
|
|
||||||
StringName(cause_event.data["event_type"])
|
|
||||||
!= SimulationIds.EVENT_STORAGE_DEPOSITED
|
|
||||||
)
|
|
||||||
or StringName(cause_event.data["item_id"]) != SimulationIds.RESOURCE_FOOD
|
|
||||||
or float(cause_event.data["amount"]) <= 0.0
|
|
||||||
):
|
|
||||||
return null
|
return null
|
||||||
relationship_keys[relationship_key] = true
|
relationship_keys[relationship_key] = true
|
||||||
record.relationships.append(relationship_record)
|
record.relationships.append(relationship_record)
|
||||||
@@ -672,6 +669,27 @@ static func _is_valid_pantry_empty_trigger(event: EconomicEventRecord, npc_ids:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
static func _is_valid_relationship_cause(
|
||||||
|
cause_event: EconomicEventRecord, subject_id: int, is_player_subject: bool
|
||||||
|
) -> bool:
|
||||||
|
if not is_player_subject:
|
||||||
|
return (
|
||||||
|
int(cause_event.data["actor_id"]) == subject_id
|
||||||
|
and (
|
||||||
|
StringName(cause_event.data["event_type"]) == SimulationIds.EVENT_STORAGE_DEPOSITED
|
||||||
|
)
|
||||||
|
and StringName(cause_event.data["item_id"]) == SimulationIds.RESOURCE_FOOD
|
||||||
|
and float(cause_event.data["amount"]) > 0.0
|
||||||
|
)
|
||||||
|
return (
|
||||||
|
int(cause_event.data["actor_id"]) == SimulationIds.PLAYER_ACTOR_ID
|
||||||
|
and StringName(cause_event.data["event_type"]) == SimulationIds.EVENT_RESOURCE_EXTRACTED
|
||||||
|
and (StringName(cause_event.data["destination_id"]) == SimulationIds.STORAGE_VILLAGE_PANTRY)
|
||||||
|
and StringName(cause_event.data["item_id"]) == SimulationIds.RESOURCE_FOOD
|
||||||
|
and float(cause_event.data["amount"]) > 0.0
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
static func _is_valid_supply_resolution(
|
static func _is_valid_supply_resolution(
|
||||||
event: EconomicEventRecord,
|
event: EconomicEventRecord,
|
||||||
opportunity: OpportunityStateRecord,
|
opportunity: OpportunityStateRecord,
|
||||||
@@ -766,6 +784,7 @@ static func _migrate_legacy(legacy_data: Dictionary, version: int) -> Dictionary
|
|||||||
OPPORTUNITY_LEGACY_SCHEMA_VERSION,
|
OPPORTUNITY_LEGACY_SCHEMA_VERSION,
|
||||||
ANIMAL_LEGACY_SCHEMA_VERSION,
|
ANIMAL_LEGACY_SCHEMA_VERSION,
|
||||||
ROUTINE_LEGACY_SCHEMA_VERSION,
|
ROUTINE_LEGACY_SCHEMA_VERSION,
|
||||||
|
PLAYER_RELATIONSHIP_SCHEMA_VERSION,
|
||||||
]
|
]
|
||||||
):
|
):
|
||||||
migrated["opportunities"] = []
|
migrated["opportunities"] = []
|
||||||
|
|||||||
@@ -0,0 +1,178 @@
|
|||||||
|
extends SceneTree
|
||||||
|
|
||||||
|
var failures: Array[String] = []
|
||||||
|
|
||||||
|
|
||||||
|
func _initialize() -> void:
|
||||||
|
call_deferred("_run")
|
||||||
|
|
||||||
|
|
||||||
|
func _run() -> void:
|
||||||
|
var manager := _create_manager()
|
||||||
|
var witness: SimNPC = manager.npcs[0]
|
||||||
|
var hungry_witness: SimNPC = manager.npcs[1]
|
||||||
|
var distant: SimNPC = manager.npcs[2]
|
||||||
|
witness.hunger = 20.0
|
||||||
|
hungry_witness.hunger = 85.0
|
||||||
|
witness.position = Vector3(4.0, 0.0, 0.0)
|
||||||
|
hungry_witness.position = Vector3(5.0, 0.0, 0.0)
|
||||||
|
distant.position = Vector3(40.0, 0.0, 0.0)
|
||||||
|
|
||||||
|
var berry := ResourceNode.new()
|
||||||
|
berry.name = "PlayerSocialBerry"
|
||||||
|
berry.node_id = &"player_social_berry"
|
||||||
|
berry.action_id = SimulationIds.ACTION_GATHER_FOOD
|
||||||
|
berry.resource_id = SimulationIds.RESOURCE_FOOD
|
||||||
|
berry.initial_amount = 1.0
|
||||||
|
berry.yield_per_action = 1.0
|
||||||
|
berry.debug_label_enabled = false
|
||||||
|
var interaction_point := Marker3D.new()
|
||||||
|
interaction_point.name = "InteractionPoint"
|
||||||
|
interaction_point.position = Vector3.ZERO
|
||||||
|
berry.add_child(interaction_point)
|
||||||
|
root.add_child(berry)
|
||||||
|
await process_frame
|
||||||
|
_check(
|
||||||
|
manager.register_resource_node(berry),
|
||||||
|
"Player supply test should bind a real finite ResourceNode"
|
||||||
|
)
|
||||||
|
_check(
|
||||||
|
is_equal_approx(manager.harvest_resource_node(berry), 1.0),
|
||||||
|
"Player harvesting should supply the real pantry"
|
||||||
|
)
|
||||||
|
|
||||||
|
var supply_event := _latest_event_of_type(manager, SimulationIds.EVENT_RESOURCE_EXTRACTED)
|
||||||
|
var supply_event_id := int(supply_event.data["event_id"])
|
||||||
|
_check(
|
||||||
|
(
|
||||||
|
int(supply_event.data["actor_id"]) == SimulationIds.PLAYER_ACTOR_ID
|
||||||
|
and (
|
||||||
|
StringName(supply_event.data["destination_id"])
|
||||||
|
== SimulationIds.STORAGE_VILLAGE_PANTRY
|
||||||
|
)
|
||||||
|
),
|
||||||
|
"The player supply should be recorded as a pantry food extraction by the player actor"
|
||||||
|
)
|
||||||
|
_check(
|
||||||
|
(
|
||||||
|
manager.npc_knows_event(witness.id, supply_event_id)
|
||||||
|
and manager.npc_knows_event(hungry_witness.id, supply_event_id)
|
||||||
|
and not manager.npc_knows_event(distant.id, supply_event_id)
|
||||||
|
),
|
||||||
|
"Nearby living villagers should witness the player restock while distant ones stay unaware"
|
||||||
|
)
|
||||||
|
|
||||||
|
var calm_relationship: RelationshipStateRecord = manager.relationship_system.get_relationship(
|
||||||
|
witness.id, SimulationIds.PLAYER_ACTOR_ID
|
||||||
|
)
|
||||||
|
_check(
|
||||||
|
calm_relationship == null, "A non-hungry witness should not gain trust toward the player"
|
||||||
|
)
|
||||||
|
var hungry_relationship: RelationshipStateRecord = manager.relationship_system.get_relationship(
|
||||||
|
hungry_witness.id, SimulationIds.PLAYER_ACTOR_ID
|
||||||
|
)
|
||||||
|
_check(
|
||||||
|
(
|
||||||
|
hungry_relationship != null
|
||||||
|
and is_equal_approx(hungry_relationship.get_familiarity(), 0.5)
|
||||||
|
and is_equal_approx(hungry_relationship.get_trust(), 0.65)
|
||||||
|
and hungry_relationship.get_last_trust_cause_event_id() == supply_event_id
|
||||||
|
),
|
||||||
|
"A hungry witness should gain directed trust toward the player from the real supply event"
|
||||||
|
)
|
||||||
|
|
||||||
|
var saved_json: String = manager.serialize_state()
|
||||||
|
var restored := _create_manager(902)
|
||||||
|
_check(
|
||||||
|
restored.restore_state_from_json(saved_json),
|
||||||
|
"A player relationship should survive schema-v12 save and restore"
|
||||||
|
)
|
||||||
|
_check(
|
||||||
|
(
|
||||||
|
restored.get_state_checksum() == manager.get_state_checksum()
|
||||||
|
and restored.npc_knows_event(hungry_witness.id, supply_event_id)
|
||||||
|
and (
|
||||||
|
restored.relationship_system.get_relationship(
|
||||||
|
hungry_witness.id, SimulationIds.PLAYER_ACTOR_ID
|
||||||
|
)
|
||||||
|
!= null
|
||||||
|
)
|
||||||
|
),
|
||||||
|
"Restored player trust should preserve checksum, evidence, and the directed relationship"
|
||||||
|
)
|
||||||
|
restored.free()
|
||||||
|
|
||||||
|
berry.queue_free()
|
||||||
|
manager.free()
|
||||||
|
|
||||||
|
var legacy_v11 := _create_v11_legacy()
|
||||||
|
var migrated := SimulationStateRecord.from_dictionary(legacy_v11)
|
||||||
|
_check(
|
||||||
|
(
|
||||||
|
migrated != null
|
||||||
|
and migrated.simulation.get("next_opportunity_id", -1) >= 0
|
||||||
|
and not migrated.opportunities.is_empty()
|
||||||
|
),
|
||||||
|
"World schema v11 should migrate to v12 without dropping opportunity history"
|
||||||
|
)
|
||||||
|
|
||||||
|
_finish()
|
||||||
|
|
||||||
|
|
||||||
|
func _create_v11_legacy() -> Dictionary:
|
||||||
|
var manager := _create_manager(901)
|
||||||
|
var pantry: StorageStateRecord = manager.get_pantry()
|
||||||
|
pantry.withdraw(SimulationIds.RESOURCE_FOOD, pantry.get_amount(SimulationIds.RESOURCE_FOOD))
|
||||||
|
pantry.deposit(SimulationIds.RESOURCE_FOOD, 1.0)
|
||||||
|
manager.economy.sync_resource(SimulationIds.RESOURCE_FOOD)
|
||||||
|
var actor: SimNPC = manager.npcs[0]
|
||||||
|
actor.position = Vector3.ZERO
|
||||||
|
actor.hunger = 85.0
|
||||||
|
manager.economy.withdraw_to_inventory(actor, SimulationIds.RESOURCE_FOOD, 1.0)
|
||||||
|
manager.economy.deposit_inventory(actor, SimulationIds.RESOURCE_FOOD)
|
||||||
|
var record: Dictionary = manager.create_state_record().to_dictionary()
|
||||||
|
record["schema_version"] = 11
|
||||||
|
manager.free()
|
||||||
|
return record
|
||||||
|
|
||||||
|
|
||||||
|
func _create_manager(seed_value: int = 901) -> Node:
|
||||||
|
var manager: Node = load("res://simulation/SimulationManager.gd").new()
|
||||||
|
manager.simulation_seed = seed_value
|
||||||
|
manager.debug_logs = false
|
||||||
|
var home_positions: Array[Vector3] = [
|
||||||
|
Vector3(0.0, 0.0, 0.0),
|
||||||
|
Vector3(2.0, 0.0, 0.0),
|
||||||
|
Vector3(10.0, 0.0, 0.0),
|
||||||
|
Vector3(12.0, 0.0, 0.0),
|
||||||
|
Vector3(30.0, 0.0, 30.0),
|
||||||
|
Vector3(40.0, 0.0, 40.0),
|
||||||
|
]
|
||||||
|
manager.home_positions = home_positions
|
||||||
|
root.add_child(manager)
|
||||||
|
manager.set_process(false)
|
||||||
|
return manager
|
||||||
|
|
||||||
|
|
||||||
|
func _latest_event_of_type(manager: Node, event_type: StringName) -> EconomicEventRecord:
|
||||||
|
var events: Array = manager.economic_events
|
||||||
|
for index in range(events.size() - 1, -1, -1):
|
||||||
|
var event := events[index] as EconomicEventRecord
|
||||||
|
if StringName(event.data["event_type"]) == event_type:
|
||||||
|
return event
|
||||||
|
return null
|
||||||
|
|
||||||
|
|
||||||
|
func _finish() -> void:
|
||||||
|
if failures.is_empty():
|
||||||
|
print("[TEST] Player social consequence passed: supply -> witness -> trust")
|
||||||
|
quit(0)
|
||||||
|
return
|
||||||
|
for failure in failures:
|
||||||
|
push_error("[TEST] " + failure)
|
||||||
|
quit(1)
|
||||||
|
|
||||||
|
|
||||||
|
func _check(condition: bool, message: String) -> void:
|
||||||
|
if not condition:
|
||||||
|
failures.append(message)
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://bfl0hnlgwvu8a
|
||||||
@@ -34,6 +34,7 @@ func _ready() -> void:
|
|||||||
simulation_manager.event_knowledge_changed.connect(_on_helper_facts_changed)
|
simulation_manager.event_knowledge_changed.connect(_on_helper_facts_changed)
|
||||||
simulation_manager.event_knowledge_forgotten.connect(_on_helper_fact_forgotten)
|
simulation_manager.event_knowledge_forgotten.connect(_on_helper_fact_forgotten)
|
||||||
simulation_manager.relationship_changed.connect(_on_helper_relationship_changed)
|
simulation_manager.relationship_changed.connect(_on_helper_relationship_changed)
|
||||||
|
simulation_manager.relationship_changed.connect(_on_player_trust_changed)
|
||||||
simulation_manager.npc_inventory_changed.connect(_on_helper_inventory_changed)
|
simulation_manager.npc_inventory_changed.connect(_on_helper_inventory_changed)
|
||||||
simulation_manager.village_changed.connect(_on_village_changed)
|
simulation_manager.village_changed.connect(_on_village_changed)
|
||||||
simulation_manager.state_restored.connect(_on_state_restored)
|
simulation_manager.state_restored.connect(_on_state_restored)
|
||||||
@@ -112,6 +113,31 @@ func _on_helper_relationship_changed(
|
|||||||
_refresh_player_response()
|
_refresh_player_response()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_player_trust_changed(
|
||||||
|
relationship: RelationshipStateRecord, cause_event: EconomicEventRecord
|
||||||
|
) -> void:
|
||||||
|
if relationship == null or cause_event == null:
|
||||||
|
return
|
||||||
|
if relationship.get_subject_id() != SimulationIds.PLAYER_ACTOR_ID:
|
||||||
|
return
|
||||||
|
if _is_active_need_supply(cause_event):
|
||||||
|
return
|
||||||
|
var observer_name := _get_npc_name(relationship.get_observer_id())
|
||||||
|
_show_feedback(
|
||||||
|
"Trust grows", "%s trusts you — you stocked the pantry." % observer_name, &"player_trust"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _is_active_need_supply(cause_event: EconomicEventRecord) -> bool:
|
||||||
|
var opportunity: OpportunityStateRecord = simulation_manager.get_active_opportunity()
|
||||||
|
if opportunity == null:
|
||||||
|
return false
|
||||||
|
return (
|
||||||
|
opportunity.get_target_id() == StringName(cause_event.data["destination_id"])
|
||||||
|
and opportunity.get_resource_id() == StringName(cause_event.data["item_id"])
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
func _on_helper_inventory_changed(_npc: SimNPC, _item_id: StringName, _amount: float) -> void:
|
func _on_helper_inventory_changed(_npc: SimNPC, _item_id: StringName, _amount: float) -> void:
|
||||||
_refresh_player_response()
|
_refresh_player_response()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user