Files
security-game/src/core/crowd_system.gd
T
zwitschi aa51ee331e
Full build / Import + headless tests (push) Successful in 41s
Full build / Full desktop build + optional release (push) Successful in 4m18s
Ship the 0.8.0 first-hour and campaign milestone
Add the campaign shell, pause-safe checkpoint resume, post-shift campaign
continue, confirmed campaign reset, difficulty presets, searchable Help,
content-matrix validation, recovery events, contextual dialogue, policy/debrief
context and eligible-event previews. Extend persistence with atomic separate
files and add campaign/checkpoint/tutorial smokes. Headless 247/247, Python
94/94, tutorial/campaign/session/scenario smokes green.
2026-11-01 21:21:43 +01:00

76 lines
2.2 KiB
GDScript

class_name CrowdSystem
extends RefCounted
## Models crowd mood and public reaction (PLAN.md: Public Crowd Reaction).
const EventBus := preload("res://src/core/event_bus.gd")
## mood: 0.0 (calm) .. 1.0 (panic). attention: how many people are watching.
signal changed(payload: Dictionary)
var event_bus: EventBus = null
var mood: float = 0.05
var attention: float = 0.0
var crowd_panic := false
func set_event_bus(bus: EventBus) -> void:
event_bus = bus
## Apply a crowd event. type: "calm_professionalism", "aggressive_behavior",
## "visible_weapon", "emergency_responder", "de_escalation".
func apply_event(event_type: String) -> void:
match event_type:
"calm_professionalism":
attention = maxf(0.0, attention - 0.05)
mood = maxf(0.0, mood - 0.02)
"de_escalation":
mood = maxf(0.0, mood - 0.06)
attention = clampf(attention + 0.05, 0.0, 1.0)
"aggressive_behavior":
attention = clampf(attention + 0.20, 0.0, 1.0)
mood = clampf(mood + 0.12, 0.0, 1.0)
"visible_weapon":
attention = 1.0
mood = clampf(mood + 0.35, 0.0, 1.0)
"emergency_responder":
attention = 1.0
mood = clampf(mood + 0.25, 0.0, 1.0)
_:
pass
crowd_panic = mood >= 0.75
if event_bus:
event_bus.crowd_changed.emit({
"mood": mood,
"attention": attention,
"panic": crowd_panic,
"event": event_type,
})
## Presentation cue for the avatars after a crowd event (PLAN: Public Crowd
## Reaction). "watch" = rubberneck at the incident, "flee" = move away from the
## door, "" = no reaction. Pure so it is headless-testable.
static func reaction_for_event(event_type: String) -> String:
match event_type:
"aggressive_behavior", "visible_weapon", "emergency_responder":
return "watch"
_:
return ""
## True once the crowd is panicking hard enough that people should clear out.
func should_flee() -> bool:
return crowd_panic
func summary() -> Dictionary:
return {
"mood": mood,
"attention": attention,
"panic": crowd_panic,
}
func snapshot() -> Dictionary:
return summary()
func restore(data: Dictionary) -> void:
mood = clampf(float(data.get("mood", 0.05)), 0.0, 1.0)
attention = clampf(float(data.get("attention", 0.0)), 0.0, 1.0)
crowd_panic = bool(data.get("panic", mood >= 0.75))