# Horror sanity (horror.sanity) ## What it is for Turns dread into a gauge that drains in the dark and when something hunts the player, and recovers in light and safe rooms, with stages that drive vignette, heartbeat and distortion effects. It needs `survival.needs`, `game.engine`; installing it brings them in. ## Turn it on Install the kit in a project, then change a setting (the values shown are its defaults): ```text gripforge_gamekit_install { project_id: "gkp_…", id: "horror.sanity" } gripforge_gamekit_configure { project_id: "gkp_…", id: "horror.sanity", config: { "need": "sanity", "darkBelow": 0.2 } } ``` ## First example This is the body of the passing test `the_gauge_follows_darkness_light_and_safe_rooms`; the helper functions it calls are defined at the top of the same test file. ```rust #[test] fn the_gauge_follows_darkness_light_and_safe_rooms() { let mut s = session(); // In the dark (ambient 0.1 < darkBelow): 6 units a second. step(&mut s, 60); assert!((value(&mut s) - 44.0).abs() < 1e-6); assert_eq!(call(&mut s, "dark", json!([])), json!(true)); assert_eq!(call(&mut s, "rate", json!([])), json!(-6)); assert_eq!(s.host.state.get("horror.dark"), Some(&json!(true))); // A safe room: still dark there, but it gives back twice what the dark takes. go(&mut s, 20.0); let told = step(&mut s, 60); assert_eq!( told, vec![( "horror.safe".to_string(), json!({ "inside": true, "room": "e2" }) )] ); assert!((value(&mut s) - 50.0).abs() < 1e-6); assert_eq!(call(&mut s, "safe", json!([])), json!(true)); // In the light, out of the room. go(&mut s, 40.0); let told = step(&mut s, 60); assert_eq!( told, vec![( "horror.safe".to_string(), json!({ "inside": false, "room": "e2" }) )] ); assert!((value(&mut s) - 53.0).abs() < 1e-6); assert_eq!(s.host.state.get("horror.rate"), Some(&json!(3))); // Paused gauges do not move. s.apply( &json!({ "op": "call", "service": "survival.needs", "method": "pause", "args": [true] }), ) .unwrap(); step(&mut s, 60); assert!((value(&mut s) - 53.0).abs() < 1e-6); } ``` ## Settings and defaults Descriptions come from the kit manifest and stay in English. | Setting | Type | Default | Range or values | Meaning | |---|---|---|---|---| | `need` | string | `"sanity"` | | The need of survival.needs this kit drives (declare it there, with drain 0 and its stages). | | `darkBelow` | number | `0.2` | 0–1 | Light level at the player (stealth.detection) under which it is dark. | | `darkDrain` | number | `1.5` | 0–100 | Units lost per second in the dark. | | `litAbove` | number | `0.5` | 0–1 | Light level at the player from which it is reassuring. | | `litRestore` | number | `0.4` | 0–100 | Units regained per second in the light. | | `safeRestore` | number | `4` | 0–100 | Units regained per second inside a safe room. | | `fallbackLight` | number | `0.5` | 0–1 | Light level without stealth.detection (a lit flashlight counts as 1). | | `viewFov` | number | `100` | 10–360 | What the player sees: this cone (degrees) around where they face. | | `sights` | array | `[{"tag": "stalker", "drain": 5, "range": 20}]` | | Entities whose sight drains: units per second while one of that tag is in view (line of sight, within range). | | `events` | array | `[{"topic": "stalker.spotted", "amount": -10},…` | | Bus events that change it at once (negative: a scare). | | `saveLabel` | string | `"Save the game"` | | | ## Services, events, variables - Service `horror.sanity`: value(), ratio(), rate() (per second now), dark(), seeing() → entities in view that frighten, safe(), safeAt(point) → boolean, savePoints(), save(point?) → boolean, scare(amount, source?). - Events emitted: - `horror.safe` (`inside`, `room`) — The player entered or left a safe room. - `horror.scare` (`amount`, `source`) — An event or a script changed the gauge at once. - `horror.saved` (`point`, `slot`) — The game was saved at a save point. - Events listened to: `interaction.requested`, `entity.spawned`, `stalker.spotted`, `stealth.caught` - Variables: - `horror.dark` (boolean) — In the dark - `horror.safe` (boolean) — In a safe room - `horror.seeing` (boolean) — Sees something frightening - `horror.rate` (number) — Sanity change per second - Verbs of the scripting layer: - `scare` (action) — Change the gauge at once (negative: a scare). - `in_safe_room` (condition) — The player is inside a safe room. ## Hooks and modding - Hooks (a game or a mod can intercept them): - `sanity.scare` (fields a tap may change: `amount`; cancellable) — The gauge is about to change at once by amount (negative: a scare; positive: a relief): a bus event of the config (source: its topic), the scare action or the service (source: script, or the caller). - `sanity.darken` (fields a tap may change: `drain`; cancellable) — The player entity enters the dark (the light on it fell under darkBelow): drain is what the gauge loses per second while it stays there. - `sanity.sight` (fields a tap may change: `drain`; cancellable) — Something frightening (seen, an entity with the tag of a sights row) comes into the view of the player entity: drain is what the gauge loses per second while one of that tag stays in view. - `sanity.shelter` (fields a tap may change: `restore`; cancellable) — The player entity enters a safe room (room, its entity): restore is what the gauge regains per second inside. - Moddable settings: `need`, `darkBelow`, `darkDrain`, `litAbove`, `litRestore`, `safeRestore`, `fallbackLight`, `viewFov`, `sights`, `events`, `saveLabel` - Components: `safe_room`, `save_point` ## Engines Declared targets: `web`, `rust`. `gripforge kit api horror.sanity` lists the exact settings and services of your version. ## Versions Kit version 1.2.0.