Horror sanity
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.
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):
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 #
<!-- test: kits/game_engine/host/tests/horror_sanity.rs::the_gauge_follows_darkness_light_and_safe_rooms --> 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.
#[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 darkhorror.safe(boolean) — In a safe roomhorror.seeing(boolean) — Sees something frighteninghorror.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.