Implement NPC perception, returning trespassers, responder dispatch, wand screening, and accessibility polish
CI / headless-tests (push) Successful in 2m11s

This commit is contained in:
zwitschi committed 2026-10-31 12:31:13 +01:00
1 parent 439e0a4281
commit 81b5d5ce5a
22 files changed
+879 -73

No files matched your search

+35 -1
View File
@@ -22,6 +22,10 @@ var battery_cost_assist: float = 10.0
var accepting_calls := true
var event_bus: EventBus = null
## Dispatched calls awaiting their responder's arrival. Each entry is the call
## dictionary with a mutable `arrival_delay_s` that `tick` decrements.
var pending_calls: Array[Dictionary] = []
func set_event_bus(bus: EventBus) -> void:
event_bus = bus
@@ -49,6 +53,7 @@ func request_assistance(report: Dictionary) -> Dictionary:
}
result.ok = true
result.call = call
pending_calls.append(call)
call_made.emit(call)
if event_bus:
event_bus.radio_call.emit(call)
@@ -83,6 +88,7 @@ func request_emergency(report: Dictionary) -> Dictionary:
}
result.ok = true
result.call = call
pending_calls.append(call)
call_made.emit(call)
if event_bus:
event_bus.radio_call.emit(call)
@@ -99,8 +105,36 @@ static func credibility_hits(report: Dictionary, truth: Dictionary) -> int:
hits += 1
return hits
## Advance cooldown; called by the simulation tick.
## Advance cooldown and pending-call arrival timers; called by the simulation
## tick. When a call's arrival delay elapses, the responder arrives and the
## `responder_arrived` signal fires with the call data.
func tick(delta: float) -> void:
if emergency_cooldown_remaining > 0.0:
emergency_cooldown_remaining = maxf(0.0, emergency_cooldown_remaining - delta)
# Battery could slowly recharge over a shift; keep it static for now.
var arrived: Array[Dictionary] = []
for call in pending_calls:
call["arrival_delay_s"] = call.get("arrival_delay_s", 0.0) - delta
if call["arrival_delay_s"] <= 0.0:
arrived.append(call)
for call in arrived:
pending_calls.erase(call)
responder_arrived.emit(call)
if event_bus:
event_bus.responder_arrived.emit(call)
## Determine the responder's resolution outcome for a call. The responder
## commands, restrains, evacuates, or arrests based on the reported danger
## level and whether a weapon was seen (PLAN.md: Level 5 Emergency Response).
## Returns a resolution string: "command", "restraint", "evacuation", "arrest".
static func responder_outcome(call: Dictionary) -> String:
var report: Dictionary = call.get("report", {})
var danger: int = report.get("danger_level", 0)
var weapons: bool = report.get("weapons_seen", false)
if weapons or danger >= 3:
return "arrest"
if danger == 2:
return "restraint"
if call.get("type", "") == "emergency":
return "evacuation"
return "command"