Player State
Client-side player state is managed through Charm atoms in player.store.ts.
Core Atoms
| Atom | Type | Purpose |
|---|---|---|
inputVectorAtom | Vector3 | Current movement input |
zoomDistanceAtom | number | Camera zoom level |
hiddenAtom | string | false | Active hiding location ("bush", "log", or false) |
controlsEnabledAtom | boolean | Whether player can move |
cameraStateAtom | string | Camera mode: disabled, firstPerson, thirdPerson, rotational |
firstPersonCameraAtom | Camera | undefined | FirstPersonCamera module reference |
dangerZoneAtom | boolean | Whether player is in a danger zone |
safeZoneAtom | boolean | Whether player is in a safe zone |
insanityAtom | number | Current insanity level (0-1) |
insanityFactorsAtom | Record | Active insanity contributors |
Insanity Factors
interface InsanityFactors {
dangerZone: boolean;
monsterNearby: boolean;
watcherActive: boolean;
}
Each active factor adds +0.005 per heartbeat tick to insanity.
Camera State Machine
gamePhase === "lobby"
→ 3rd person (min 3, max 5 zoom)
→ Mouse unlocked
gamePhase === "game"
→ debugMode? → Mouse unlocked, default camera
→ hiddenAtom? → Disabled camera (interaction handles it)
→ cameraState === "thirdPerson" → Custom camera, zoom 5
→ cameraState === "firstPerson" → FirstPersonCamera module
Interaction Store
interaction.store.ts — Single atom tracking the current interaction target:
export const interactionTargetAtom = atom<BasePart | undefined>(undefined);
Used by the interaction controller to track which object the player is targeting.
Movement Hook
getMoveVector() provides a hook into the Roblox PlayerModule's move vector, used by the interaction controller to detect when a hidden player starts moving (triggering exit from hiding).
Game Constants
| Constant | Value | Location |
|---|---|---|
SANITY_LOST_DURATION | 10 seconds | global/shared/settings |