Skip to main content

Player State

Client-side player state is managed through Charm atoms in player.store.ts.

Core Atoms

AtomTypePurpose
inputVectorAtomVector3Current movement input
zoomDistanceAtomnumberCamera zoom level
hiddenAtomstring | falseActive hiding location ("bush", "log", or false)
controlsEnabledAtombooleanWhether player can move
cameraStateAtomstringCamera mode: disabled, firstPerson, thirdPerson, rotational
firstPersonCameraAtomCamera | undefinedFirstPersonCamera module reference
dangerZoneAtombooleanWhether player is in a danger zone
safeZoneAtombooleanWhether player is in a safe zone
insanityAtomnumberCurrent insanity level (0-1)
insanityFactorsAtomRecordActive 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

ConstantValueLocation
SANITY_LOST_DURATION10 secondsglobal/shared/settings