Implement prototype: venue-driven simulation core, scenarios, tests, and playable entrance scene
CI / headless-tests (push) Canceled after 49s
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:
1 parent
e23f284ad4
commit
439e0a4281
45 files changed
+4368
-7
No files matched your search
@@ -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")
|
||||
Reference in new issue
Block a user