Files
gamedev-the-steward/simulation/SimulationManager.gd
T
admin 9cd38a1adb feat: add ResourceNode system with target selection and reservation
Phase 1-2 of the ResourceNode migration plan:

- Create ResourceNode class (world/resource_nodes/) with extraction,
  reservation, nearest-available selection, and debug label
- Place 4 resource instances in main.tscn (2 berry bushes, 2 trees)
- Add target_id to SimNPC for tracking which node an NPC is using
- Add set_npc_target_id and release_npc_reservation to SimulationManager
  with cleanup on task change, completion, and death
- Update WorldViewManager to query available ResourceNodes first,
  falling back to zone markers with a logged warning
2026-07-03 18:18:51 +02:00

196 lines
4.5 KiB
GDScript

extends Node
signal npc_task_changed(npc: SimNPC, old_task: String, new_task: String)
signal village_changed(village: SimVillage)
signal npc_died(npc: SimNPC)
var village := SimVillage.new()
const DEBUG_LOGS := true
var npcs: Array[SimNPC] = []
var tick_interval := 1.2
var tick_timer := 0.0
var tick_count := 0
var names := [
"Amina", "Tarik", "Jasmin"
]
var professions := [
"farmer",
"woodcutter",
"guard",
"scholar",
"wanderer"
]
func _ready() -> void:
randomize()
village.update_modifiers()
village.update_priorities()
generate_npcs()
print("--- Simulation started ---")
func _process(delta: float) -> void:
tick_timer += delta
if tick_timer >= tick_interval:
tick_timer -= tick_interval
simulate_tick()
func generate_npcs() -> void:
for i in range(names.size()):
var npc := SimNPC.new(
i,
names[i],
professions.pick_random(),
randf_range(1.0, 10.0),
randf_range(1.0, 10.0)
)
npcs.append(npc)
func simulate_tick() -> void:
tick_count += 1
print("--- Tick ", tick_count, " ---")
var village_was_changed := false
for npc in npcs:
var old_task := npc.current_task
var old_target := npc.target_id
var old_state := npc.task_state
var was_complete := npc.task_complete
var was_dead := npc.is_dead
npc.simulate_tick(village)
if old_task != npc.current_task and old_target != &"":
release_npc_reservation(npc.id)
if not was_dead and npc.is_dead:
if old_target != &"":
release_npc_reservation(npc.id)
npc_died.emit(npc)
npc_task_changed.emit(npc, old_task, npc.current_task)
if DEBUG_LOGS:
print("[SimulationManager] NPC died: ", npc.npc_name)
if old_task != npc.current_task and not npc.is_dead:
npc_task_changed.emit(npc, old_task, npc.current_task)
if not was_complete and npc.task_complete and not npc.is_dead:
var completed_task := npc.current_task
var needs_immediate_food_after_gather := npc.should_eat_immediately_after_task()
if npc.target_id != &"":
release_npc_reservation(npc.id)
village.apply_npc_task(npc)
village_was_changed = true
npc.task_complete = true
npc.task_state = SimNPC.TASK_STATE_IDLE
npc.current_task = "idle"
if needs_immediate_food_after_gather and village.food > 0:
npc.set_task("eat", 1.0)
npc_task_changed.emit(npc, completed_task, npc.current_task)
if DEBUG_LOGS:
print("[SimulationManager] ", npc.npc_name, " gathered food while starving and is going to eat immediately.")
if DEBUG_LOGS:
print(
npc.npc_name,
" | ",
npc.profession,
" | task: ",
npc.current_task,
" | state: ",
npc.task_state,
" | progress: ",
npc.task_progress,
"/",
npc.task_duration,
" | complete: ",
npc.task_complete,
" | hunger: ",
round(npc.hunger),
" | energy: ",
round(npc.energy),
" | starving: ",
npc.is_starving,
" | starvation_ticks: ",
npc.starvation_ticks,
" | dead: ",
npc.is_dead
)
if old_state != npc.task_state:
print("[SimulationManager] State changed: ", npc.npc_name, " ", old_state, " -> ", npc.task_state)
if village_was_changed:
village_changed.emit(village)
if DEBUG_LOGS:
print(village.get_summary())
func set_npc_target_id(npc_id: int, target_id: StringName) -> void:
for npc in npcs:
if npc.id == npc_id:
npc.target_id = target_id
return
func release_npc_reservation(npc_id: int) -> void:
for npc in npcs:
if npc.id == npc_id:
if npc.target_id != &"":
var node := ResourceNode.get_by_id(npc.target_id)
if node != null:
node.release(npc.id)
npc.target_id = &""
return
func notify_npc_arrived(npc_id: int) -> void:
for npc in npcs:
if npc.id == npc_id:
if npc.is_dead:
return
if npc.task_state == SimNPC.TASK_STATE_TRAVELING:
npc.start_working()
if DEBUG_LOGS:
print("[SimulationManager] ", npc.npc_name, " arrived and started working on: ", npc.current_task)
return
func eat_food(amount: float) -> void:
village.food -= amount
village_changed.emit(village)
func add_food(amount: float) -> void:
village.food += amount
village_changed.emit(village)
func add_wood(amount: float) -> void:
village.wood += amount
village_changed.emit(village)
func add_safety(amount: float) -> void:
village.safety = clamp(village.safety + amount, 0.0, 100.0)
village_changed.emit(village)
func add_knowledge(amount: float) -> void:
village.knowledge += amount
village_changed.emit(village)
func get_starving_count() -> int:
var count := 0
for npc in npcs:
if npc.is_starving:
count += 1
return count