feat: scale village queries and enrich Jajce center
This commit is contained in:
@@ -56,6 +56,7 @@ var latest_decisions: Dictionary = {}
|
||||
var action_selector := ActionSelectionSystem.new()
|
||||
var action_executor := ActionExecutionSystem.new()
|
||||
var target_resolver := ActionTargetResolver.new()
|
||||
var _population_view := SimulationPopulationView.new()
|
||||
var speed_index := 2
|
||||
const SPEED_LEVELS := [0.25, 0.5, 1.0, 2.0, 4.0, 10.0]
|
||||
const KNOWLEDGE_COMMUNICATION_RADIUS := 2.5
|
||||
@@ -134,6 +135,7 @@ func generate_npcs() -> void:
|
||||
if home_count > 0:
|
||||
for i in range(npcs.size()):
|
||||
npcs[i].home_position = home_positions[i % home_count]
|
||||
_population_view.rebuild(npcs)
|
||||
|
||||
|
||||
func _create_random_source(npc_id: int, stream_id: int) -> RandomNumberGenerator:
|
||||
@@ -155,6 +157,7 @@ func simulate_tick() -> void:
|
||||
if debug_logs:
|
||||
print("--- Tick ", tick_count, " ---")
|
||||
var village_was_changed := false
|
||||
_population_view.rebuild(npcs)
|
||||
for npc in npcs:
|
||||
village_was_changed = _simulate_npc_tick(npc) or village_was_changed
|
||||
if village_was_changed:
|
||||
@@ -178,6 +181,7 @@ func _simulate_npc_tick(npc: SimNPC) -> bool:
|
||||
var was_dead := npc.is_dead
|
||||
|
||||
action_executor.advance_npc(npc, village)
|
||||
_population_view.refresh_npc(npc)
|
||||
_select_action_if_idle(npc, previous_state)
|
||||
|
||||
if previous_task != npc.current_task and not previous_target.is_empty():
|
||||
@@ -192,6 +196,7 @@ func _simulate_npc_tick(npc: SimNPC) -> bool:
|
||||
var completed := not was_complete and npc.task_complete and not npc.is_dead
|
||||
if completed:
|
||||
_complete_current_action(npc)
|
||||
_population_view.refresh_npc(npc)
|
||||
|
||||
_debug_npc_tick(npc, previous_state)
|
||||
return completed
|
||||
@@ -205,7 +210,9 @@ func _select_action_if_idle(npc: SimNPC, previous_state: StringName) -> void:
|
||||
if npc.task_state not in [SimNPC.TASK_STATE_IDLE, SimNPC.TASK_STATE_COMPLETE]:
|
||||
return
|
||||
var helper := get_active_opportunity_helper()
|
||||
var selection := action_selector.select_action(npc, village, clock.time_of_day(), npcs, helper)
|
||||
var selection := action_selector.select_action(
|
||||
npc, village, clock.time_of_day(), npcs, helper, _population_view
|
||||
)
|
||||
if selection == null:
|
||||
return
|
||||
latest_decisions[npc.id] = selection
|
||||
@@ -509,7 +516,7 @@ func get_activity_target_claim_count(target_id: StringName, except_npc_id: int =
|
||||
|
||||
|
||||
func _notify_mourning(dead_npc: SimNPC) -> void:
|
||||
var mourner := relationship_system.get_most_familiar_living_subject(dead_npc.id, npcs)
|
||||
var mourner := relationship_system.get_most_familiar_living(dead_npc.id, npcs, _population_view)
|
||||
if mourner == null:
|
||||
return
|
||||
mourner.mourning_ticks = 100
|
||||
@@ -761,10 +768,7 @@ func _get_lasting_event_ids(npc_id: int) -> Array[int]:
|
||||
|
||||
|
||||
func _find_npc_by_id(npc_id: int) -> SimNPC:
|
||||
for npc in npcs:
|
||||
if npc.id == npc_id:
|
||||
return npc
|
||||
return null
|
||||
return _population_view.get_any(npc_id)
|
||||
|
||||
|
||||
static func _sort_npcs_by_id(first: SimNPC, second: SimNPC) -> bool:
|
||||
@@ -1044,11 +1048,7 @@ func add_knowledge(amount: float) -> void:
|
||||
|
||||
|
||||
func get_starving_count() -> int:
|
||||
var count := 0
|
||||
for npc in npcs:
|
||||
if npc.is_starving:
|
||||
count += 1
|
||||
return count
|
||||
return _population_view.starving_count()
|
||||
|
||||
|
||||
func get_state_snapshot() -> Dictionary:
|
||||
@@ -1178,6 +1178,7 @@ func restore_state(record: SimulationStateRecord) -> bool:
|
||||
npcs.clear()
|
||||
for npc_record in record.npcs:
|
||||
npcs.append(npc_record.restore(debug_logs))
|
||||
_population_view.rebuild(npcs)
|
||||
relationship_system.restore(record.relationships)
|
||||
event_knowledge_system.restore(record.event_knowledge)
|
||||
opportunity_system.restore(record.opportunities, int(record.simulation["next_opportunity_id"]))
|
||||
|
||||
@@ -22,7 +22,8 @@ func select_action(
|
||||
village: SimVillage,
|
||||
time_of_day: float = 0.5,
|
||||
all_npcs: Array = [],
|
||||
opportunity_helper: OpportunityHelperResult = null
|
||||
opportunity_helper: OpportunityHelperResult = null,
|
||||
population_view: SimulationPopulationView = null
|
||||
) -> ActionSelectionResult:
|
||||
if npc.is_dead:
|
||||
return null
|
||||
@@ -127,7 +128,7 @@ func select_action(
|
||||
)
|
||||
if village.food <= RELATIONSHIP_AID_PANTRY_THRESHOLD and relationship_system != null:
|
||||
var trusted_starving_npc: SimNPC = relationship_system.get_trusted_starving_subject(
|
||||
npc.id, all_npcs
|
||||
npc.id, all_npcs, population_view
|
||||
)
|
||||
if trusted_starving_npc != null:
|
||||
return ActionSelectionResult.new(
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
class_name SimulationPopulationView
|
||||
extends RefCounted
|
||||
|
||||
var all_by_id: Dictionary = {}
|
||||
var living_by_id: Dictionary = {}
|
||||
var starving_by_id: Dictionary = {}
|
||||
|
||||
|
||||
func _init(npcs: Array = []) -> void:
|
||||
rebuild(npcs)
|
||||
|
||||
|
||||
func rebuild(npcs: Array) -> void:
|
||||
all_by_id.clear()
|
||||
living_by_id.clear()
|
||||
starving_by_id.clear()
|
||||
for value in npcs:
|
||||
var npc := value as SimNPC
|
||||
if npc != null:
|
||||
refresh_npc(npc)
|
||||
|
||||
|
||||
func refresh_npc(npc: SimNPC) -> void:
|
||||
if npc == null:
|
||||
return
|
||||
all_by_id[npc.id] = npc
|
||||
if npc.is_dead:
|
||||
living_by_id.erase(npc.id)
|
||||
starving_by_id.erase(npc.id)
|
||||
return
|
||||
living_by_id[npc.id] = npc
|
||||
if npc.is_starving:
|
||||
starving_by_id[npc.id] = npc
|
||||
else:
|
||||
starving_by_id.erase(npc.id)
|
||||
|
||||
|
||||
func get_living(npc_id: int) -> SimNPC:
|
||||
return living_by_id.get(npc_id) as SimNPC
|
||||
|
||||
|
||||
func get_any(npc_id: int) -> SimNPC:
|
||||
return all_by_id.get(npc_id) as SimNPC
|
||||
|
||||
|
||||
func get_starving(npc_id: int) -> SimNPC:
|
||||
return starving_by_id.get(npc_id) as SimNPC
|
||||
|
||||
|
||||
func living_count() -> int:
|
||||
return living_by_id.size()
|
||||
|
||||
|
||||
func starving_count() -> int:
|
||||
return starving_by_id.size()
|
||||
@@ -0,0 +1 @@
|
||||
uid://btwlag4mpji24
|
||||
@@ -95,16 +95,17 @@ func get_primary_relationship(observer_id: int) -> RelationshipStateRecord:
|
||||
return best
|
||||
|
||||
|
||||
func get_most_familiar_living_subject(observer_id: int, npcs: Array[SimNPC]) -> SimNPC:
|
||||
var living_by_id := {}
|
||||
for npc in npcs:
|
||||
if not npc.is_dead:
|
||||
living_by_id[npc.id] = npc
|
||||
func get_most_familiar_living(
|
||||
observer_id: int, npcs: Array[SimNPC], population_view: SimulationPopulationView = null
|
||||
) -> SimNPC:
|
||||
var effective_view := population_view
|
||||
if effective_view == null:
|
||||
effective_view = SimulationPopulationView.new(npcs)
|
||||
var best_relationship: RelationshipStateRecord
|
||||
for relationship in relationships.values():
|
||||
if relationship.get_observer_id() != observer_id:
|
||||
continue
|
||||
if not living_by_id.has(relationship.get_subject_id()):
|
||||
if effective_view.get_living(relationship.get_subject_id()) == null:
|
||||
continue
|
||||
if (
|
||||
best_relationship == null
|
||||
@@ -117,21 +118,22 @@ func get_most_familiar_living_subject(observer_id: int, npcs: Array[SimNPC]) ->
|
||||
best_relationship = relationship
|
||||
if best_relationship == null:
|
||||
return null
|
||||
return living_by_id[best_relationship.get_subject_id()] as SimNPC
|
||||
return effective_view.get_living(best_relationship.get_subject_id())
|
||||
|
||||
|
||||
func get_trusted_starving_subject(observer_id: int, npcs: Array[SimNPC]) -> SimNPC:
|
||||
var starving_by_id := {}
|
||||
for npc in npcs:
|
||||
if not npc.is_dead and npc.is_starving:
|
||||
starving_by_id[npc.id] = npc
|
||||
func get_trusted_starving_subject(
|
||||
observer_id: int, npcs: Array, population_view: SimulationPopulationView = null
|
||||
) -> SimNPC:
|
||||
var effective_view := population_view
|
||||
if effective_view == null:
|
||||
effective_view = SimulationPopulationView.new(npcs)
|
||||
var best_relationship: RelationshipStateRecord
|
||||
for relationship in relationships.values():
|
||||
if relationship.get_observer_id() != observer_id:
|
||||
continue
|
||||
if relationship.get_trust() < TRUSTED_HELP_THRESHOLD:
|
||||
continue
|
||||
if not starving_by_id.has(relationship.get_subject_id()):
|
||||
if effective_view.get_starving(relationship.get_subject_id()) == null:
|
||||
continue
|
||||
if (
|
||||
best_relationship == null
|
||||
@@ -144,7 +146,7 @@ func get_trusted_starving_subject(observer_id: int, npcs: Array[SimNPC]) -> SimN
|
||||
best_relationship = relationship
|
||||
if best_relationship == null:
|
||||
return null
|
||||
return starving_by_id[best_relationship.get_subject_id()] as SimNPC
|
||||
return effective_view.get_starving(best_relationship.get_subject_id())
|
||||
|
||||
|
||||
func _get_or_create(observer_id: int, subject_id: int) -> RelationshipStateRecord:
|
||||
|
||||
Reference in New Issue
Block a user