Add ambient visual reactivity and location visit variety

- #app's filter (hue-rotate/saturate) now tracks dread continuously,
  and a fixed vignette bleeds red as hemlighet drops below 40 — no
  new assets, just CSS driven by existing state.
- A kaos log entry now triggers a brief screen-glitch shake.
- Location visits pick from 3 flavor-text variants per site instead
  of a single fixed line, so repeat visits don't repeat verbatim.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Fredrik Johansson
2026-07-13 00:01:51 +02:00
parent 80fdebf102
commit 6ed9d2dac3
3 changed files with 74 additions and 9 deletions

View File

@@ -213,7 +213,7 @@ export function resolveEventChoice(state: State, choiceIndex: number): State {
}
interface LocationVisit {
text: string;
texts: string[];
effect: Partial<{ andakt: number; dread: number; kaos: number; ritual: number; hemlighet: number }>;
requiresArea?: keyof State['areas'];
lockedText: string;
@@ -221,40 +221,68 @@ interface LocationVisit {
const LOCATION_VISITS: Record<string, LocationVisit> = {
rosengard: {
text: 'Du sitter i källaren. Lamporna flimrar i takt med ditt hjärta. Eller tvärtom. +andakt',
texts: [
'Du sitter i källaren. Lamporna flimrar i takt med ditt hjärta. Eller tvärtom. +andakt',
'Någon har städat källaren medan ingen var där. Ingen av er erkänner sig skyldig. +andakt',
'Den varma stolen är varm igen. Ingen har suttit i den. +andakt',
],
effect: { andakt: 15 },
requiresArea: 'rosengard',
lockedText: 'En fuktig källarlokal i Rosengård. Du kan se ljuset under dörren. Ni är inte redo än.',
},
varnhem: {
text: 'Cirkeln är lite större sedan du var här sist. Du ritade den inte. +dread +ritual',
texts: [
'Cirkeln är lite större sedan du var här sist. Du ritade den inte. +dread +ritual',
'Kritan på asfalten har inte suddats trots regnet i natt. +dread +ritual',
'Torget är tomt förutom en duva som går i exakt cirkelns mönster. +dread +ritual',
],
effect: { dread: 8, ritual: 2 },
requiresArea: 'varnhem',
lockedText: 'Värnhemstorget ser normalt ut. Det är ett dåligt tecken.',
},
torso: {
text: 'Byggnaden pekar. Du tittar dit den pekar. Du tittar bort igen. Det hjälper inte. +dread +ritual',
texts: [
'Byggnaden pekar. Du tittar dit den pekar. Du tittar bort igen. Det hjälper inte. +dread +ritual',
'Ett fönster högt uppe är tänt som det aldrig varit förut. Ingen bor där. Ingen har någonsin bott där. +dread +ritual',
'Torso vrider sig en grad till i vinden som inte finns. +dread +ritual',
],
effect: { dread: 15, ritual: 5 },
requiresArea: 'torso',
lockedText: 'Turning Torso vrider sig i vinden. Inte i vinden. I något annat.',
},
oresund: {
text: 'Vattnet sjunker lite långsammare just nu. Det vet att du stod här. +dread',
texts: [
'Vattnet sjunker lite långsammare just nu. Det vet att du stod här. +dread',
'En färja passerar utan ljud. Ingen ombord vinkar. Alla ombord tittar rakt på dig. +dread',
'Bron surrar på en frekvens du känner i tänderna snarare än öronen. +dread',
],
effect: { dread: 5 },
lockedText: 'Öresund. Danskarna på andra sidan verkar inte märka någonting. Det är det mest oroande av allt.',
},
ribersborg: {
text: 'Änderna tittar på dig. En av dem nickar. Du nickar tillbaka. Det var ett misstag. +andakt',
texts: [
'Änderna tittar på dig. En av dem nickar. Du nickar tillbaka. Det var ett misstag. +andakt',
'Alla änderna vänder sig samtidigt mot Turning Torso när du kommer. +andakt',
'Sanden är kall trots solen. Ingen annan verkar märka det. +andakt',
],
effect: { andakt: 8 },
lockedText: 'Stranden är tom nu. Änderna sitter stilla. De väntar på rätt tidpunkt.',
},
davidshall: {
text: 'Källarlokalen luktar fortfarande. Du andas djupare än du borde. +andakt',
texts: [
'Källarlokalen luktar fortfarande. Du andas djupare än du borde. +andakt',
'Kaklet från 1923 är kallt att röra vid, oavsett årstid. +andakt',
'Grannen genom väggen sjunger något som nästan, men inte riktigt, är en psalm. +andakt',
],
effect: { andakt: 10 },
lockedText: 'Davidshallsgatan. Kakel från 1923. Bakom det finns något som väljer att vänta.',
},
stortorget: {
text: 'Du vet inte varför du kom hit. Du vet inte hur länge du stod här. +kaos',
texts: [
'Du vet inte varför du kom hit. Du vet inte hur länge du stod här. +kaos',
'Statyn på torget har flyttat en centimeter sedan i förra veckan. Du är den enda som mätt. +kaos',
'Alla klockor på torget visar olika tid. Ingen av dem är fel. +kaos',
],
effect: { kaos: 8 },
lockedText: 'Stortorget är fullt av folk som inte vet vad de gör här heller. Du passar in.',
},
@@ -272,7 +300,7 @@ export function visitLocation(state: State, id: string, cooldowns: Map<string, n
const s = { ...state, areas: { ...state.areas }, upgrades: new Set(state.upgrades), log: [...state.log], usedEventIds: new Set(state.usedEventIds), flags: new Set(state.flags) };
const areaLocked = visit.requiresArea && !s.areas[visit.requiresArea];
const text = areaLocked ? visit.lockedText : visit.text;
const text = areaLocked ? visit.lockedText : visit.texts[Math.floor(Math.random() * visit.texts.length)];
if (!areaLocked) {
if (visit.effect.andakt) s.andakt += visit.effect.andakt;