Implement prototype: venue-driven simulation core, scenarios, tests, and playable entrance scene
CI / headless-tests (push) Canceled after 49s

- Godot 4.3 project scaffold with first-person guard, procedural world,
  interaction HUD, access inspection, radio forms, debug overlay, shift review
- Simulation core: ShiftCoordinator, VenueScenario, EventScheduler, NPCController/FSM,
  AccessVerifier, EscalationManager, RadioSystem, IncidentRecorder,
  ReputationEvaluator, CrowdSystem, EventBus
- Three authored scenarios (Hollywood Blvd building, The Joke Shop, TV studio)
- Custom headless test runner (25 tests) replacing editor-dependent GUT,
  plus GitHub Actions CI
- Documentation workflow live: README, PLAN (tech stack), TODO/DONE, MEMORY
This commit is contained in:
zwitschi committed 2026-10-31 11:37:54 +01:00
1 parent e23f284ad4
commit 439e0a4281
45 files changed
+4368 -7

No files matched your search

+139
View File
@@ -0,0 +1,139 @@
class_name Rules
## Shared constants and helpers for the simulation rules.
func _init() -> void:
push_error("Rules is a static utility class and must not be instantiated.")
## Escalation levels (0..5). Names must stay in sync with PLAN.md.
enum Level {
OBSERVATION = 0,
SIMPLE_DETERRENCE = 1,
FORMAL_WARNING = 2,
BLOCKING = 3,
RADIO_ASSIST = 4,
EMERGENCY = 5,
}
const LEVEL_NAMES := {
Level.OBSERVATION: "Observation",
Level.SIMPLE_DETERRENCE: "Simple Deterrence",
Level.FORMAL_WARNING: "Formal Warning",
Level.BLOCKING: "Blocking",
Level.RADIO_ASSIST: "Radio Assist",
Level.EMERGENCY: "Emergency",
}
## NPC behavioral archetypes from PLAN.md.
enum NpcType {
TOURIST,
PERFORMER,
INFLUENCER,
CONFUSED_VISITOR,
INTOXICATED,
TRESPASSER,
VIOLENT_THREAT,
MENTAL_CRISIS, # "51/50" style mental-health crisis character
AUTHORIZED_STAFF,
AUTHORIZED_GUEST,
DELIVERY,
RESPONDER,
}
const NPC_TYPE_NAMES := {
NpcType.TOURIST: "Tourist",
NpcType.PERFORMER: "Street Performer",
NpcType.INFLUENCER: "Influencer",
NpcType.CONFUSED_VISITOR: "Confused Visitor",
NpcType.INTOXICATED: "Intoxicated Pedestrian",
NpcType.TRESPASSER: "Trespasser",
NpcType.VIOLENT_THREAT: "Violent Threat",
NpcType.MENTAL_CRISIS: "Mental Health Crisis",
NpcType.AUTHORIZED_STAFF: "Authorized Staff",
NpcType.AUTHORIZED_GUEST: "Authorized Guest",
NpcType.DELIVERY: "Delivery",
NpcType.RESPONDER: "Responder",
}
## Access artifact kinds used by AccessVerifier.
enum AccessKind {
NONE = -1,
RECOGNITION = 0,
WRISTBAND = 1,
ID = 2,
TICKET = 3,
CREDENTIAL = 4,
GUEST_LIST = 5,
}
const ACCESS_KIND_NAMES := {
AccessKind.RECOGNITION: "Recognition",
AccessKind.WRISTBAND: "Wristband",
AccessKind.ID: "ID",
AccessKind.TICKET: "Ticket",
AccessKind.CREDENTIAL: "Credential",
AccessKind.GUEST_LIST: "Guest List",
}
## Artifact validity states.
enum ArtifactStatus {
VALID = 0,
EXPIRED = 1,
COUNTERFEIT = 2,
MISMATCHED = 3,
MISSING = 4,
AMBIGUOUS = 5,
}
const ARTIFACT_STATUS_NAMES := {
ArtifactStatus.VALID: "Valid",
ArtifactStatus.EXPIRED: "Expired",
ArtifactStatus.COUNTERFEIT: "Counterfeit",
ArtifactStatus.MISMATCHED: "Mismatched",
ArtifactStatus.MISSING: "Missing",
ArtifactStatus.AMBIGUOUS: "Ambiguous",
}
## NPC reaction outcomes after player actions.
enum Reaction {
COMPLY = 0,
LEAVE = 1,
ARGUE = 2,
IGNORE = 3,
RECORD = 4,
THREATEN = 5,
RETURN_LATER = 6,
FORCE_ENTRY = 7,
}
## Player dialogue tones.
enum Tone {
PROFESSIONAL = 0,
FRIENDLY = 1,
FIRM = 2,
SUSPICIOUS = 3,
}
const TONE_NAMES := {
Tone.PROFESSIONAL: "Professional",
Tone.FRIENDLY: "Friendly",
Tone.FIRM: "Firm",
Tone.SUSPICIOUS: "Suspicious",
}
## Shift phases by time of day.
enum Phase {
EARLY = 0,
MIDDAY = 1,
EVENING = 2,
LATE = 3,
}
const PHASE_NAMES := {
Phase.EARLY: "Early",
Phase.MIDDAY: "Midday",
Phase.EVENING: "Evening",
Phase.LATE: "Late",
}
static func level_name(level: int) -> String:
return LEVEL_NAMES.get(level, "Unknown")