Underground 2 Revived
Play Need for Speed: Underground 2 (2004) in Chrome and Firefox with WebGPU graphics, Web Audio, up to 120 fps widescreen, controller support, free roam and Online races with friends. Fan project, not
Schlüpfe in die Rolle von Detective Wimpy, dem Bushbaby, und löse jeden Fall in der Tierstadt.
Translation pending
GamesTypeScript · JavaScriptGitHub @Snaefell18OnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Loading the full README. The saved excerpt is shown below.
Ein Detektivspiel für den Browser und den iPhone-Homescreen. Man spielt Wimpy, ein Bushbaby mit Lupe, reist zwischen Orten, quatscht mit Tieren, befragt und beschuldigt sie - und findet am Ende (hoffentlich) den Täter. Die Charaktere, ihre Alibis, Geheimnisse, Spuren und alle Dialoge kommen live von der Claude-API. Der Täter wird bei jedem neuen Fall zufällig gezogen.Read the full README ↗
Ein Detektivspiel für den Browser und den iPhone-Homescreen. Man spielt Wimpy, ein Bushbaby mit Lupe, reist zwischen Orten, quatscht mit Tieren, befragt und beschuldigt sie - und findet am Ende (hoffentlich) den Täter.
Die Charaktere, ihre Alibis, Geheimnisse, Spuren und alle Dialoge kommen live von der Claude-API. Der Täter wird bei jedem neuen Fall zufällig gezogen.
npm install
cp .env.example .env.local # ANTHROPIC_API_KEY eintragen
npm run dev # http://localhost:3000
ANTHROPIC_API_KEY - der Claude-Key (Pflicht).ANTHROPIC_MODEL - optional, Default ist claude-opus-5.CASE_SECRET - optional, langer Zufallsstring zum Verschlüsseln des Falls.
Ohne Angabe wird der API-Key als Schlüsselbasis benutzt.ADMIN_TOKEN - das Passwort für den Admin-Bereich. Ohne Angabe steht das
Menü jedem offen, der die Adresse kennt (die Akten bleiben trotzdem zu).ELEVENLABS_API_KEY und ELEVENLABS_VOICE_ID - optional, für gesprochene
Erzählertexte (siehe unten). Ohne sie bleibt alles beim Text.OPENAI_API_KEY - optional, für erzeugte Bilder im Admin-Menü (siehe
unten). Ohne ihn legt man Bilder weiterhin von Hand in public/ ab.Tiere, Schauplätze, Dinge und vorbereitete Fälle liegen in Firestore
(Projekt detective-wimpy). Drei Schritte, einmalig:
Firestore anlegen - in der Firebase-Konsole unter Build → Firestore Database → Datenbank erstellen (Region nach Wunsch).
Anonyme Anmeldung aktivieren - unter Build → Authentication → Sign-in method → Anonym. Lesen geht auch ohne; für das Speichern aus dem Admin-Menü ist sie nötig.
Regeln veröffentlichen - entweder den Inhalt von firestore.rules in
der Konsole unter Firestore → Regeln einfügen, oder:
npm run firebase:rules # braucht einmalig: npx firebase-tools login
Danach im Admin-Menü unter Tiere / Orte / Dinge je einmal
„Projektdaten übernehmen“ drücken - damit wandern die Listen aus data/ in
die Datenbank und lassen sich dort bearbeiten.
| Sammlung | Inhalt |
|---|---|
charaktere | die Tiere samt Werten |
orte | Schauplätze mit Stadt und Atmosphäre |
items | Gegenstände und Spuren |
faelle | vorbereitete Fälle („Kampagnen“) |
sagen | Sagas: mehrere Fälle mit gemeinsamem Überthema |
arcs | Arcs: mehrere Sagas unter einem Bogen |
stimmen | gesprochene Erzählertexte (siehe unten) |
Die Sammlungen sagen, arcs und stimmen kamen später dazu. Wer seine
Regeln vor diesen Zeilen veröffentlicht hat, bekommt beim Lesen oder Schreiben
ein „Missing or insufficient permissions“ - dann einmal firestore.rules aus
dem Projekt in der Firebase-Konsole neu veröffentlichen.
Die Regeln begrenzen außerdem die Länge einiger Felder (Name 120, Überthema und Klappentext je 2000 Zeichen). Wird eine Grenze gerissen, lehnt Firestore das Schreiben ebenfalls mit „Missing or insufficient permissions“ ab, obwohl es kein Rechteproblem ist. Deshalb kürzen Server und App diese Felder selbst, bevor sie gespeichert werden.
Die Lösung eines Falls steht nicht im Klartext in der Datenbank: Täter,
Motiv und gelogene Alibis stecken im Feld siegel - AES-256-verschlüsselt mit
einem Schlüssel, den nur der Server kennt. Deshalb dürfen Fälle öffentlich
lesbar sein, ohne dass sich jemand die Lösung erspähen kann.
Ist die Datenbank leer oder nicht erreichbar, spielt die App mit den Listen aus
data/ weiter und sagt das im Admin-Menü.
Bilder gehören nicht in Firestore (ein Dokument darf dort nur 1 MB groß sein) -
sie liegen wie gehabt in public/.
Jeder frisch gestartete Fall kostet einen Modellaufruf. Vorbereitete Fälle kosten ihn genau einmal:
Eine Saga ist eine Reihe von Fällen mit gemeinsamem Überthema und einem
Finale, in dem der Drahtzieher auffliegt. Sie entsteht im Admin-Menü unter
Sagas; zwischen den Kapiteln spricht der Erzähler, wahlweise mit eigener
Tondatei aus public/audio.
Das Motiv je Fall lässt sich dabei selbst schreiben: In jedem Kapitel steht ein Feld „Motiv des Täters“, im Finale eines für den Drahtzieher. Was dort steht, wird wörtlich das Motiv des Falls - Tathergang, Alibis und Spuren werden darum herum gebaut, und im Finale folgt sogar die Wahrheit hinter der ganzen Saga daraus. Leer lassen heißt wie überall: Das Modell denkt sich einen Grund aus, der zum Tier passt. Gespeichert wird das Motiv nur im verschlüsselten Bogen - in der offen liegenden Vorgabenliste steht es nicht, sonst wäre der Täter mit einem Blick in die Datenbank verraten.
Ein Arc fasst mehrere Sagas zu einer langen Reihe zusammen:
Admin-Menü → Arcs. Name, Klappentext, eigener Titelsong (Pfad in
public/audio) und wie viele Sagas es werden sollen - eine bis zehn. Dazu
gleich am Anfang: worauf alles hinausläuft, wer am Ende der Culprit ist und
unter welchem Wort er in den Texten vorkommt („Der Schattenkanzler war
weiterhin auf der Flucht.“). Alles davon lässt sich später ändern.
Zu jeder Station gehören ein Erzählertext (optional mit Tondatei) und eine Saga. „Saga für diesen Teil vorbereiten“ öffnet dieselben Einstellungen wie im Saga-Bereich - Kapitelzahl, Städte, Täter je Kapitel, Reifegrad, Ton, Schwierigkeit. Was der Arc vorgibt (Überthema samt Culprit-Ansage, die Besetzung ohne den Culprit, im letzten Teil der Drahtzieher samt Twist), ist dort vorbelegt und markiert - ändern lässt es sich trotzdem.
Als Entwurf speichern. Das Formular ist lang, und es muss nicht in einem Zug fertig werden: „Entwurf speichern“ legt alles Ausgefüllte an der Station ab, ohne etwas zu bestellen. Danach steht dort „Am Entwurf weiterarbeiten“ samt Datum, und man macht dort weiter, wo man aufgehört hat - auch an einem anderen Gerät, denn der Entwurf liegt am Arc und nicht im Browser. „Entwurf verwerfen“ stellt das Formular zurück auf die Vorgaben des Arcs, und sobald die Saga wirklich erzeugt ist, räumt sich der Entwurf von selbst weg.
Eine bereits vorhandene Saga lässt sich ebenfalls zuordnen, wirkt dann aber nur als Station: Ihre Texte sind längst geschrieben und kennen weder das Ziel des Arcs noch seinen Culprit. Der Admin sagt das an der Station auch - und warnt, wenn diese Saga den Culprit sogar schon als den Verantwortlichen ausweist. Wer die Vorgaben des Arcs in den Texten haben will, erzeugt die Saga aus der Station heraus.
Im Spiel öffnet Arcs zuerst die Übersicht: alle Sagen untereinander, mit Haken an dem, was durch ist. Starten lässt sich immer nur die nächste offene - die Reihenfolge ist die Geschichte. Wer mittendrin pausiert, findet dort „Weiterspielen“, und nach jeder Saga geht es dorthin zurück.
Der Arc muss nicht am Stück entstehen. Sobald die erste Station eine Saga hat, taucht er im Spiel unter Arcs auf und lässt sich spielen. Die übrigen Stationen dürfen nachwachsen, während schon gespielt wird - der Spielstand liegt auf dem Gerät und liest den Arc bei jedem Start neu.
Am Ende steht das große Finale. Zur Wahl stehen der Abschlusstext, ein Video, ein Abspann - und der Showdown: ein spielbarer 3D-Kampf gegen den Culprit (siehe unten). Die Gerichtsverhandlung ist als Art schon vorgesehen und kommt später.
Der Culprit ist dabei mehr als eine Notiz: Bis zur letzten Station bleibt er aus der Besetzung der erzeugten Sagas heraus - dort kommt er nur unter seinem Wort vor. In der letzten Saga ist er der Drahtzieher und betritt erst im Finale die Bühne.
Ein Arc erzeugt keine eigenen Fälle - er verweist auf Sagas, die auch einzeln spielbar bleiben. Löscht man einen Arc, bleiben seine Sagas erhalten.
Den Showdown gibt es an drei Stellen - als Finale-Art eines Arcs (gegen den Culprit), als Finale-Art einer Saga (gegen den Drahtzieher) und im 3D-Labor zum Ausprobieren. Überall ist es derselbe Kampf, live in 3D und mit dem Daumen gesteuert:
Knock_Down,
Catching_Breath, Sleep_Normally …), sonst kippt er einfach in Zeitlupe
um. Der Sieger steht dabei still. Erst knapp eine Sekunde später fängt er an
zu jubeln, und erst nach gut zwei Sekunden kommt die Karte - und mit ihr die
Siegermelodie. Vorher lief beides gleichzeitig: Wimpy tanzte los, während der
Gegner noch fiel.Im Arc steht er zwischen der letzten Saga und dem Abschlusstext. In einer Saga wählt man ihn wie jede andere Finale-Art: Sie läuft dann wie eine klassische in ihren Finalfall - und wer den löst, stellt den Überführten danach noch einmal, diesmal in der Arena. Erst dann kommt der Epilog. Wer den Finalfall nicht löst, sieht keinen Kampf; und brach im Täter eine Gestalt hervor, steht sie in der Arena, nicht ihr Wirt.
Ein geplatztes Verfahren ist nicht das Ende der Saga. Unter dem Urteil steht „Noch einmal verhandeln ›": Die Anklage steht wieder offen, die Versuche sind wieder voll, niemand gilt mehr als abgewiesen, und die Anhörung beginnt als neues Gespräch - mit allem, was in der Beweismitteltasche liegt. Das gilt für jede Verhandlung, ob man selbst anklagt oder verteidigt.
Zwei Dinge bleiben dabei stehen: Öhö zieht nicht noch einmal ein (er ist ja schon da - die Zeremonie ein zweites Mal wäre Wartezeit), und wer schuldig ist, ändert sich nicht; das steht seit der Erzeugung im versiegelten Bogen. Wer lieber weitergeht, nimmt den zweiten Knopf: „Genug für heute - zum Epilog ›". Erst der führt wie früher in den Epilog und danach in den Abspann.
Die Finale-Art „Gericht & Flucht“ ist beides zusammen. Sie beginnt wie „Gerichtssaal“: Der Drahtzieher spielt die ganze Saga über mit Wimpy, im Saal klagt man ihn selbst an, legt vor, was in der Beweismitteltasche liegt, und Öhö spricht das Urteil. Nur lässt sich der Verurteilte nicht abführen - Stühle fallen, eine Tür schlägt, draußen springt ein Motor an. Was folgt, ist die Verfolgungsjagd und danach der Kampf in derselben Arena wie beim Showdown; der Knopf unter dem Urteil heißt dann auch nicht „Weiter“, sondern „Ihm nach“.
Die Texte wissen davon: Schon in den Kapiteln steht beiläufig herum, dass er vorgesorgt hat - ein Wagen, der immer fahrbereit dasteht, ein Weg, den nur er kennt. Im Saal ist er dafür auffällig gelassen. Und der Epilog wird für die Zeit nach Jagd und Kampf geschrieben: Er erzählt, wie es ausging, und nennt weder Sieger noch Verlierer - beides ist möglich.
Platzt das Verfahren, wird nicht gekämpft: Dann geht der Angeklagte ganz ruhig durch die Vordertür, und es kommt der Epilog.
Ohne eingerichtete Arena fällt das Ende nicht aus. Wer diese Finale-Art wählt, hat sich für Urteil, Jagd und Kampf entschieden - und bekommt sie. Steht im Editor keine Arena (oder eine mit weniger als neun Straßenfeldern), springt der Standardkampfplatz ein: freie Mitte, Häuser ringsum, genau der, den der Knopf „Kampfplatz anlegen" legt. War überhaupt nichts eingerichtet, gehört bei dieser Art auch die Jagd dazu. Alles andere - Stufe, Musik, Gegnermodell, Licht - bleibt, wie es eingestellt ist, und wer die Jagd bewusst abgewählt hat, behält sie abgewählt. Dasselbe gilt für den Showdown einer Saga und den eines Arcs.
Der Showdown hängt dabei an nichts mehr: Gegen wen gekämpft wird, steht im Spielstand - Id und Tier kommen aus der Besetzung der Saga, die ohnehin auf dem Gerät liegt. Wer mitten im Kampf neu lädt, steht danach wieder demselben Tier gegenüber, und eine stumme Datenbank ändert daran nichts. (Sie wird trotzdem kurz abgewartet, falls das Tier nur dort steht - aber höchstens sechs Sekunden, dann geht es auch ohne los.)
Und der Knopf unter dem Urteil sagt die Wahrheit: „Ihm nach ›" steht dort nur, wenn danach wirklich eine Verfolgung kommt. Der Twist verträgt sich damit so wenig wie mit jedem anderen Gerichtsfinale: Wer vor Gericht mit Wimpy spielt, kann nicht zugleich unsichtbar bleiben.
Geprobt wird an zwei Stellen: „Showdown proben“ im Editor spielt den Kampf genau so, wie er später laufen wird, samt Jagd davor und ohne dass dafür etwas gespeichert werden muss - und im Admin-Menü unter Pursuit · 3D-Labor gibt es ihn als eigenen Modus V: Arena bauen, Gegner und Wimpys Modell wählen, Stufe, Licht, Wetter, Musik, Jagd - und sofort losspielen, ganz ohne Saga oder Arc.
Die Rechnung dahinter (Leben, Schaden, Pausen, was der Gegner als Nächstes
vorhat) steht in lib/kampf.ts und ist ohne Grafikkarte prüfbar; die Szene in
components/Endkampf.tsx zeichnet sie nur. Die Stadt darunter bauen Kapitel
und Arena mit denselben Funktionen (components/stadtBau.ts), damit die Arena
aussieht wie die Straßen, durch die man vorher gelaufen ist - und auf dem
iPhone genauso wenig Speicher frisst.
Beim Vorbereiten einer Saga aus einer Arc-Station lässt sich in jeder echten Kapitellücke optional genau ein großes Ereignis einrichten:
Versammlung: freies Gruppengespräch mit Vorsitz, Gästen am Rand, verborgener Resonanz und einem möglichen zusätzlichen Beweisstück.
Verfolgungsjagd: zwei wählbare Tiere verfolgen einen unkenntlichen Fahrer im weißen Sportwagen durch eine interaktive 3D-Landschaft. Für beide Verfolger werden das Schaf-mit-Schal- und das Zottel-Yeti-Modell zugeordnet. Nach dem Fang spricht das fliehende Tier das im Editor hinterlegte Statement; anschließend läuft die Saga mit dem nächsten Kapitel weiter.
Die Strecke stellt man ein wie jede andere 3D-Welt des Spiels - im Editor der Jagd und ebenso in der Jagd vor dem Showdown:
/public/3d_locations
Alte Jagden kennen nichts davon und fahren unverändert weiter: Ohne Einstellung gilt die Schneepiste in der Nacht, durch die sie immer führten. Ausprobieren lässt sich alles ohne Speichern - in der 3D-Vorschau des Editors und im Admin-Menü unter Pursuit · Verfolgungsjagd, wo Belag, Licht, Wetter und Häuser direkt auf der Startkarte stehen.
Die Verhaftung steht dazwischen: Ist der Wagen eingeholt, gehen beide mit quietschenden Reifen in die Eisen, der Fluchtwagen stellt sich quer, alle vier Räder qualmen - und dann geht die Tür auf. Der Flüchtige steigt aus, tritt vom Wagen weg und dreht sich zur Kamera um; erst da sieht man, wer gefahren ist. Sein Modell ist dasselbe wie überall (Stammdaten, vor dem Showdown das des Kampfes). Tippen überspringt, wie vor der Jagd auch, und wenn das Modell nicht geladen werden konnte, halten die Wagen trotzdem - nur die Enthüllung fällt dann aus.
Der Editor verhindert, dass Versammlung und Verfolgungsjagd dieselbe Lücke belegen. Die Jagd besitzt dort außerdem eine spielbare 3D-Vorschau, einen frei wählbaren Song und eine kurze Comic-Mutprobe mit zwei langen Cognacflaschen, ohne dass die Saga dafür gespeichert oder erzeugt werden muss.
Eine Saga endete bisher mit dem Epilog: Der Erzähler sagt, wie es ausging, man tippt auf „Zum Hauptmenü“, fertig. Das ist ein Schluss, aber kein Ende - ein Ende hat Musik, und es hat ein Bild, bei dem man sitzen bleibt.
Deshalb gibt es jetzt Abspänne, und zwar in Arten. Eingestellt werden sie im Saga-Editor unter Abspann und genauso im Finale eines Arcs (dort unter der Finale-Art „Credits“); beide benutzen dasselbe Feld, und beide haben darunter den Knopf „🎬 Abspann proben“, der ihn genau so abspielt, wie er später läuft - ohne dass dafür etwas gespeichert werden muss.
Der Song ist die Uhr: Der Abspann endet mit seinem letzten Ton. Ohne Song läuft keiner - dann endet die Saga wie früher. Ältere Arcs mit einem Credits-Song behalten ihre Textrolle, ohne dass jemand etwas umstellen muss.
Und der Weg dorthin ist jetzt vollständig: Nach einem gewonnenen Kampf
spielt die Siegermelodie (public/audio/winner.mp3) schon auf der
„GEWONNEN!“-Karte und läuft in den Epilog hinein - dann erst kommt der
Abspann.
Zu jedem Kapitel einer Saga (und zum Finale) lässt sich eintragen, was Wimpy bekommt, wenn er es löst - freiwillig, und jede Lücke ist erlaubt. Zur Wahl steht alles aus dem Laden, in zwei Gruppen:
Verbucht wird über Saga und Kapitelnummer: Dasselbe Kapitel noch einmal zu spielen bringt kein zweites Exemplar. Steht das Stück später nicht mehr im Laden, bleibt die Übergabe still aus - der Editor sagt das aber schon beim Erzeugen.
Jedes Automodell wird im Spiel auf dieselbe Länge gebracht - sonst stünde ein Spielzeugauto neben einem Lastwagen. Genau das macht aber aus einer Limousine ein Spielzeug: Sie ist länger als ein Sportwagen und sieht erst richtig aus, wenn sie es auch im Spiel sein darf.
Deshalb hat jeder Wagen im Admin-Menü unter Autos eine Größe: 1,0× ist ein gewöhnlicher Wagen (3,5 m in der Verfolgungsjagd), eine Limousine steht bei 1,4× bis 1,6×, ein Kleinwagen bei 0,8×. Die Vorschau zeigt es sofort - die Fahrbahn bleibt gleich breit, der Wagen nicht -, und daneben steht, wie lang er damit wird. Erlaubt ist 0,6× bis 2,2×; Wagen ohne Angabe (alle von früher) bleiben bei 1,0×. Dieselbe Größe gilt überall: in der Jagd, in der 3D-Stadt und in der Vorschau.
Daneben steht dort die Ausrichtung: Jedes Modell liegt anders in seiner Datei, und die Drehung rückt es so, dass die Schnauze in Fahrtrichtung zeigt.
Die 3D-Modelle bringen ihre Animationen mit, und welche davon ein Tier im
Stehen benutzt, entscheidet ruheAuswahl in lib/tiermodelle.ts - für alle
Szenen dieselbe Wahl: 3D-Kapitel, Verfolgungsjagd, Abspann, Probewelt.
Der Haken steckte in restpose. Neun der Modelle bringen sie mit, und sie ist
keine Animation, sondern die Ruhepose des Skeletts: ein einziges Bild. Gesucht
wurde bisher nach „idle" oder „rest" - und weil restpose darauf passt,
stand sie gleichberechtigt neben den echten Leerläufen. Eine Figur mit vier
schönen Idles stand deshalb in jeder dritten Pause reglos herum.
Jetzt gilt der Reihe nach:
Idle_3, Idle_11 …) - viele Modelle haben mehrere,
dann wechseln sie sich ab. Wimpy hat vier, der Yeti zwei.Nicht dabei ist, was kein Herumstehen ist: Schläge, Blocks, Sprünge, Rollen, Sprints. Ein Tier, das am Straßenrand Faustschläge übt, war nie gemeint.
Auch die Spielfigur wechselt jetzt: Wer sieben bis dreizehn Sekunden stehen bleibt, sieht den nächsten Leerlauf. Am Steuer bleibt es bei einem - wer im Wagen sitzt, soll nicht plötzlich tanzen.
Tiere, Schauplätze, Dinge und Ladenzubehör lassen sich im Admin-Menü malen lassen - aus dem, was im Formular steht, plus einem freien Feld für Wünsche ans Aussehen. Das ist die einzige Stelle, an der ein anderes Haus als Anthropic gefragt wird; Fälle, Sagen und Gespräche laufen unverändert über Claude.
OPENAI_API_KEY=…
OPENAI_IMAGE_MODEL=gpt-image-2.5-flare # optional, das ist der Default
OPENAI_IMAGE_QUALITY=high # optional: low, medium, high,
# xhigh, max oder auto
Der Stil steht fest (naiver Comicstil, lib/bildPrompt.ts), damit alles
zusammenpasst; Format und Größe stimmen von selbst: Tiere und Schauplätze
hochkant, Dinge quadratisch, Tiere und Dinge freigestellt. Das fertige Bild
wird verkleinert und landet in der Sammlung bilder; der Eintrag merkt sich
nur bild:<id>. Erzeugt wird einmal - im Spiel wird nur geladen.
Flare ist die schnellere und günstigere der beiden 2.5er-Varianten - für
flächige Comicbilder mit klaren Konturen genau richtig. Die genauere heißt
gpt-image-2.5-sunburst; sie lohnt vor allem beim Nachbearbeiten, und
nachbearbeitet wird hier nichts. Die Qualität steht auf high: deutlich mehr
Sorgfalt als medium, ohne die teuren Sprünge nach xhigh oder max. Weil
jedes Bild nur einmal entsteht und danach für immer im Spiel steht, lohnt
sich diese Stufe. Umstellen genügt in der Umgebungsvariablen.
Für die gpt-image-Modelle muss die Organisation bei OpenAI einmal verifiziert sein;
ist sie es nicht, steht genau das in der Fehlermeldung. Ohne Schlüssel ändert
sich nichts: Der Knopf sagt, was fehlt, und Bilder lassen sich weiterhin von
Hand in public/charaktere, public/orte und public/items ablegen.
Die Texte zwischen den Kapiteln, Sagas und Arc-Stationen kann ElevenLabs sprechen. Dafür im Konto einen Schlüssel und eine Stimme aussuchen und beides in die Umgebungsvariablen legen:
ELEVENLABS_API_KEY=…
ELEVENLABS_VOICE_ID=… # aus der Voice Library kopieren
ELEVENLABS_MODEL=eleven_multilingual_v2 # optional, das ist der Default
Ein anderes Modell trägt man einfach dort ein, etwa eleven_v3. Die App
schickt dann die Regler, die dieses Modell erwartet (v3 kennt für „stability“
nur 0, 0.5 und 1 und kein „style“). Ob das Konto ein Modell über die
Schnittstelle nutzen darf, entscheidet ElevenLabs - sagt es nein, steht der
Modellname in der Fehlermeldung.
Danach steht unter jedem Erzählertext im Admin-Menü ein Knopf „Sprechen lassen“. Wichtig ist, wie das läuft:
stimmen, der Erzählerteil merkt
sich nur stimme:<id>.Eine selbst aufgenommene Datei in public/audio funktioniert unverändert
weiter; das Feld nimmt beides.
Ein Tier der Saga kann die ganze Zeit von einem Dämon besessen gewesen sein. Im Saga-Editor wählt man unter Besessenheit den Wirt und seine Dämonenform - die legt man wie jedes andere Tier unter „Tiere“ an, mit Bild und Werten - und dazu einen Ton.
Was daraus folgt, macht die App von selbst:
Stößt in einem Kapitel jemand zum ersten Mal dazu - ein Nachzügler oder im Finale der Drahtzieher -, läuft davor eine kurze Ansage: „Ein neuer Spieler betritt das Feld!“, darunter Bild und Name.
Wie der Auftritt aussieht, wählt man im Saga-Editor je Tier:
Alles reines CSS, keine zusätzlichen Dateien, und alles hält sich an
prefers-reduced-motion.
Ein Ton dazu lässt sich auf drei Ebenen hinterlegen, und die feinere gewinnt:
je Tier im Saga-Editor (für alle, die laut „Auftritte“ später dazustoßen),
je Saga ebendort, und unter Spiel → Auftritt eines neuen Tiers für
alles Übrige. Steht nirgends etwas, läuft public/audio/newplayer.mp3. Wer
wirklich Stille will, wählt „Ohne Ton“ - das ist etwas anderes als „nichts
eingetragen“. In jedem Fall zur Wahl:
eine eigene Datei (wandert in die Datenbank, damit sie auf jedem Gerät gleich
klingt), ein Pfad in public/audio oder ein Satz vom Sprecher. Ist einer
gesetzt, schält sich die Figur über die ganze Länge des Tons aus dem Dunkel:
Am Anfang ein Schatten, am Ende steht sie da, und der Name kommt zum Schluss.
Ohne Ton dauert dasselbe knapp zweieinhalb Sekunden. „Sofort zeigen“ deckt
auf, geht aber nicht weiter - erst der nächste Tipp.
Der Drahtzieher wird davor nie beim Namen genannt: Der Server streicht Sätze, die ihn als den Kopf hinter allem ausweisen („dahinter steckt …“, „zieht die Fäden“), aus Überthema, Klappentext, Auftakt und allen Kapiteltexten. Andeutungen bleiben - sie sind die halbe Saga.
Wer wann mitspielt, steht im Saga-Editor unter Auftritte: ab welchem Kapitel jemand dabei ist - und in der Zeile darunter, in welchen Kapiteln er pausiert. So kann ein Tier die Saga zwischendurch verlassen und später wieder auftauchen; die Ansage heißt dann „Zurück auf dem Feld!“. Bliebe ein Kapitel dadurch ohne genug Verdächtige, rückt jemand nach - ein spielbarer Fall geht vor.
Die Rückkehr lässt sich einzeln abstellen. Sie bekommt sonst dieselbe Bühne wie der erste Auftritt - derselbe Song, dieselbe Animation, nur eine andere Zeile. Beim ersten Mal ist das ein Auftritt; beim dritten Mal, wenn jemand zwischen zwei Kapiteln nur kurz verreist war, ist es eine Unterbrechung. Sobald für ein Tier eine Pause eingetragen ist, steht deshalb direkt darunter „Kommt zurück: Mit Ansage / Stillschweigend" - wer stillschweigend zurückkommt, steht im nächsten Kapitel einfach wieder da. Der erste Auftritt bleibt davon unberührt; wer ihn ganz loswerden will, stellt die Auftrittsart auf „Kein Auftritt" (das schaltet dann auch jede Rückkehr ab).
Wer neu ist, wird nicht geplant, sondern verglichen: Wer in der Besetzung dieses Falls steht und in der des vorherigen nicht, ist neu. Das stimmt auch bei von Hand nachbearbeiteten Sagas.
Kleine Momente, die nichts am Ablauf ändern und niemanden aufhalten:
Der Fund. Findet Wimpy etwas, blitzt es kurz auf: Gegenstand, Name, was er sieht - als Einblendung über dem Ort, nach vier Sekunden von allein wieder weg, auf Fingertipp sofort. Der Text steht danach wie gehabt im Notizbuch.
Vor dem Urteil. Zwischen Beschuldigung und Auflösung sieht man den Beschuldigten und hört, was er dazu sagt. Erst danach fällt das Urteil - und mit ihm die Siegermusik, die zu einem Satz, der noch nichts entschieden hat, nichts verloren hat.
Hintergrundmusik (Admin → Spiel, je Saga-Kapitel überschreibbar): läuft leise weiter, solange man ermittelt - am Schauplatz und genauso in der 3D-Stadt, denn die steht an derselben Stelle im selben Bildschirm. Szenen mit eigener Musik (Intro, Auftritt, Gerichtseinzug, Auflösung) halten sie an und geben sie danach an derselben Stelle wieder frei, nicht von vorn.
Wetter am Schauplatz (Admin → Spiel): Regen, Nebel, Schnee, Nacht oder „Zufall“ - eine Lage je Fall, damit sie nicht mitten im Herumlaufen umschlägt. Alles reines CSS, keine zusätzlichen Dateien.
Wetter in der 3D-Welt (Städte, 3D-Kapitel, Arena, Verfolgungsjagd,
Probewelt): klar, Sonne, Regen, Schneefall, Schneesturm, Blizzard,
Sandsturm, Nebel oder Feuerwerk. Der Sandsturm färbt Dunst, Licht
und Boden ocker, legt Sand auf die Fahrbahn und treibt die Körner waagerecht
und böig durchs Bild. Alle Szenen rechnen dafür mit denselben Zahlen
(components/stadtBau.ts), damit man überall im selben Wetter steht.
Feuerwerk ist die eine Lage, bei der nichts fällt, sondern etwas aufsteigt: Eine Rakete zieht mit Funkenschweif nach oben, wird langsamer und blüht zu einer Kugel auf, die auseinandertreibt, sinkt und weich ausgeht - dann die nächste, an anderer Stelle, in anderer Farbe. Am schönsten nachts, aber zu jeder Tageszeit erlaubt; die Sicht ändert es nicht. Bewusst ohne Blitzen: Die Szene wird nirgends kurz hell, kein Licht zuckt, nichts pulst schnell - der hellste Augenblick eines Funkens ist sein erster, danach geht er über anderthalb Sekunden aus. Gerechnet wird alles in einem einzigen Punktefeld mit festem Vorrat, das auch auf einem Telefon ruhig durchläuft.
Es steigt über der Straße auf, acht bis sechzig Meter voraus, und blüht fünf bis neun Meter hoch - also vor den Fassaden und knapp unter dem oberen Bildrand, nicht hoch über den Dächern. Das ist ausgerechnet und nicht geraten: Beide Kameras schauen fast waagerecht (die Stadt aus fünf Metern vierzehn Grad nach unten, die Verfolgungsjagd aus sieben Metern noch steiler), und was höher aufblüht, liegt schlicht über dem Bild.
Die Hälfte der Raketen steigt dabei weiter draußen auf, zwischen und hinter den Häusern, damit es aus mehreren Richtungen kommt statt aus einer Reihe. Und es geht nicht im Takt: Mal steigt eine einzelne, mal drei auf einmal, dann ist einen Moment Ruhe - alle paar Zehntelsekunden fällt die Würfelung neu.
Standard ist Claude Sonnet 5 (2 $ / 10 $ je Million Token) - stark genug für Fallkonstruktion und Rollenspiel und deutlich günstiger als Opus (5 $ / 25 $). Umstellen ohne Codeänderung:
| Variable | Wofür | Default |
|---|---|---|
ANTHROPIC_MODEL | Fall erzeugen, Auflösung | claude-sonnet-5 |
ANTHROPIC_MODEL_TALK | nur die Gespräche | wie oben |
Die Gespräche sind der häufigste Aufruf - wer dort noch sparen will, setzt
ANTHROPIC_MODEL_TALK=claude-haiku-4-5 (1 $ / 5 $). Umgekehrt bringt
claude-opus-5 die stärksten Dialoge, kostet aber am meisten.
Sparsam ist die App ohnehin gebaut: Das Weltwissen wird zwischengespeichert
(Prompt-Caching), Gespräche laufen mit niedrigem Aufwand (effort: "low"), und
das Durchsuchen der Orte kommt ganz ohne Modell aus.
Auf dem iPhone: Seite in Safari öffnen → Teilen → „Zum Home-Bildschirm“. Die App
startet dann im Vollbild ohne Safari-Leisten (display: standalone), mit
eigenem Icon und korrekten Abständen zu Dynamic Island und Home-Indicator.
Es gibt zwei Wege - der erste ist der richtige, der zweite der schnelle.
Alle Bilder liegen unter public/, weil Next.js nur von dort ausliefert. Die
URLs sind trotzdem genau die gewünschten:
| Ordner | URL im Spiel | Inhalt |
|---|---|---|
public/charaktere/ | /charaktere/ | Tiere |
public/orte/ | /orte/ | Schauplätze |
public/items/ | /items/ | Gegenstände/Spuren |
Der Dateiname entscheidet, wozu ein Bild gehört: chat.png gehört zu Chat,
cafe.png zum Café Mondlicht. In jedem Ordner liegt eine README.md mit der
vollständigen Liste; im Admin-Menü unter Bilder steht zu jedem Eintrag der
erwartete Dateiname.
So kommen die Dateien ins Projekt:
git add public/charaktere && git commit -m "Bilder" && git push.Empfehlung: Charaktere hochkant freigestellt (PNG mit Transparenz, ca.
900 × 1200 px) - sie werden im Gespräch bildschirmfüllend gezeigt. Orte im
Querformat (ca. 1600 × 1000 px), Gegenstände quadratisch (512 px), das Titelbild
public/start.png im Hochformat (z.B. 1080 × 1920).
Ortsbilder heißen <stadt>-<ort>.png, also venedig-markusplatz.png.
npm run import:orte gibt die erwarteten Namen aus.
Dateiendungen klein schreiben. Vercel läuft auf Linux und unterscheidet
Groß- und Kleinschreibung: Fan.PNG wird unter /charaktere/fan.png nicht
gefunden. Am besten alles kleingeschrieben ablegen.
Bilder klein halten. Zwei Dinge sorgen dafür, dass die App auch über Mobilfunk schnell bleibt. Erstens liefert Next.js die Bilder automatisch als WebP in der Größe aus, die das Gerät wirklich braucht - aus einem 1280-px-PNG werden auf dem iPhone rund 70 kB statt 500. Zweitens sollten die Dateien im Repository nicht riesig sein; aus Bildgeneratoren kommen gern 2-3 MB. Einmal
npm run bilder:optimieren # verkleinert alles unter public/
npm run bilder:optimieren -- --pruefen # zeigt nur, was passieren würde
und die Dateien liegen bei ~300-500 kB, ohne sichtbaren Verlust; Transparenz bleibt erhalten.
3D-Bausteine (Straßen, Häuser, Tankstelle). Die rohen .glb aus dem
Modellierer bleiben auf deinem Rechner - .gitignore lässt aus
public/3d_locations/ nur die Webfassungen (*-web.glb) durch. Schritt für
Schritt:
Rohdatei ablegen. Die große Datei aus Meshy & Co. nach
public/3d_locations/ legen, unter dem Namen, den der Baustein im Spiel
tragen soll - zum Beispiel tankstelle.glb. Ohne -web im Namen; die
Endung hängt der nächste Schritt selbst an.
Abspecken.
npm run locations3d:optimieren
Daraus entsteht tankstelle-web.glb: Geometrie auf rund 220.000 Dreiecke
heruntergerechnet, Texturen auf 1024 Pixel und WebP, alles
meshopt-komprimiert. Aus vier Millionen Dreiecken und 35 MB werden so
typischerweise ein paar hundertausend und 2-3 MB, ohne dass man es dem
Haus ansieht. Am Ende steht im Protokoll, was herausgekommen ist:
tankstelle.glb: 35.5 MB -> 6.7 MB · 4.726.530 -> 492.050 Dreiecke
Reicht das noch nicht, geht es eine Stufe schärfer - halb so große Texturen, halbes Dreiecksbudget:
npm run locations3d:klein
Ins Repository. Nur die -web.glb gehört dorthin, die Rohdatei nicht
(darum kümmert sich .gitignore von allein).
public/3d_locations/ gehen, Add file → Upload files, die
-web.glb hineinziehen, unten eine kurze Beschreibung, Commit
changes. Achtung: auf den richtigen Branch committen, nicht auf
main, wenn gerade ein Zweig läuft.git push statt über den Browser; dort liegt die
Grenze bei 100 MB je Datei.Fertig. Nach dem Deploy taucht der Baustein von selbst überall auf, wo man Bausteine wählt - der Build liest den Ordner neu ein. Heißt die Datei „tankstelle", erkennt das Spiel sie ohne weiteres Zutun als Wimpys Garage.
Und wenn die Rohdatei weg ist? Dann rechnet
npm run locations3d:abspecken
die schon eingecheckten -web.glb noch einmal durch - es nimmt sie selbst
als Quelle. Ein zweiter Durchlauf ändert danach nichts mehr; was schon klein
genug ist, bleibt unangetastet.
Unter Admin → Bilder kannst du zu jedem Eintrag ein Bild direkt vom Handy wählen. Es wird verkleinert, im Browser gespeichert und sofort im Spiel benutzt. Praktisch zum Testen, aber es liegt nur auf diesem einen Gerät - für alle anderen (und nach dem Leeren der Browserdaten) zählt Weg 1.
Solange ein Bild fehlt, zeigt die App einen Platzhalter mit dem Namen - das Spiel funktioniert also auch ganz ohne Grafiken.
Auf dem Startbildschirm oben rechts das Zahnrad (oder direkt /admin).
Der ganze Bereich hängt an einem Passwort: dem ADMIN_TOKEN vom Server -
demselben, mit dem auch die Akten geöffnet werden. Geprüft wird es beim Server,
der Browser bekommt nur ein Ja oder Nein; danach bleibt es auf dem Gerät, bis
man oben rechts wieder abschließt. Ist auf dem Server keines gesetzt, sagt der
Bildschirm das und lässt einen trotzdem hinein - sonst käme man an sein eigenes
Menü nicht mehr heran.
Was das schützt und was nicht: Es hält Neugierige vom Menü fern und ist die einzige Tür zu den Akten - Täter und Lösung gibt der Server ohne dieses Passwort nicht heraus. Die Stammdaten in Firestore hängen dagegen an den Datenbankregeln; dieser Bildschirm ersetzt sie nicht.
Dahinter:
Alles davon liegt im Browser des Geräts, nicht auf dem Server. Beim Start eines Falls schickt die App Besetzung und Einstellungen einmal mit; sie werden in den verschlüsselten Fall eingebacken und gelten für dessen gesamte Laufzeit.
Die Werte der Tiere stehen in data/characters.csv (Semikolon-getrennt, direkt
aus Excel exportierbar). Nach jeder Änderung:
npm run import:csv
Das schreibt lib/characters.generated.ts neu und meldet, welches Bild zu
welchem Tier gehört. Spalten:
Nummer;Name;Tierart;Alter;Charisma;Freundlichkeit;Fitness;Zauberkraft;
Schelmischkeit;Kriminalitätslevel;Intelligenz;Charakter[;Bild]
Charakter ist die Spielfigur
und taucht nicht als Verdächtiger auf.Bild überschreibt den automatischen Pfad.Die Werte sind nicht nur Deko: Sie stehen im Prompt und steuern das Verhalten - hohe Schelmischkeit heißt Ablenkungsmanöver, hohe Intelligenz bessere Ausreden, hohe Freundlichkeit offenere Antworten.
Nach dem Klick auf „Neuen Fall starten“ wird zuerst der Fall erzeugt (ein paar Sekunden, mit Hinweis auf dem Startbildschirm) - bei einer Kampagne entfällt das Warten ganz.
Dann spricht der Prolog (public/audio/introdark.mp3): Die festen Zeilen
des Sprechers erscheinen im Takt der Aufnahme, danach der Anriss dieses Falls,
den Claude beim Erzeugen mitgeschrieben hat. Ohne die Datei läuft der Prolog
stumm in 14 Sekunden durch.
Direkt danach startet der Titelsong (public/audio/intro.mp3) und mit ihm die
Vorstellung:
Titelkarte, Stadt, Fallakte mit Schreibmaschinentext, jeder Verdächtige einzeln
mit seinen Werten, die fünf Schauplätze, „Wer war es?“ - und zum Schluss der
Startschuss. So steht im Intro von der ersten Sekunde an alles fest.
Jede Tafel bleibt so lange stehen, wie ihr Text zum Vorlesen braucht. Früher hatte jeder Abschnitt einen festen Anteil des Songs - die Verdächtigen zusammen ein Viertel, die Schauplätze ein Sechstel. Bei fünf Verdächtigen und fünf Schauplätzen sind das anderthalb Sekunden pro Tafel: Der Text blitzte auf, brach mitten in der Einblendung ab, und vorlesen ließ sich davon nichts.
Jetzt sagt jede Tafel selbst, wie lange sie braucht (lib/introTiming.ts):
gut elf Zeichen je Sekunde, mindestens zwei Sekunden, und ein Steckbrief mit
drei wachsenden Balken bekommt mehr als ein einzelnes Wort. Verteilt wird erst
danach:
Dieselbe Rechnung gilt für den Vorspann einer Saga und den eines Arcs - dort vor allem für Überthema und Klappentext, die ganze Absätze sein können.
Auch die Uhr dahinter läuft anders (lib/introUhr.ts): Sie beginnt erst, wenn
die Musik wirklich spielt (oder feststeht, dass der Browser sie blockiert), und
zählt danach nur noch vorwärts. Vorher wurde zwischen Aufnahme- und Wanduhr
umgeschaltet - begann die Musik einen Moment später, sprang der Vorspann
zurück, und man sah dieselbe Tafel zweimal.
Erzählertexte ohne Tondatei folgen derselben Regel: Sie liefen immer in sechzehn Sekunden durch, egal ob zwei Zeilen oder ein Absatz dastand. Jetzt bekommt jeder Text die Zeit, die er zum Vorlesen braucht.
Damit der Ton trotz der Wartezeit erlaubt bleibt, wird das Audio-Element schon im Klick selbst kurz angetippt - Browser lassen Abspielen nur als Folge einer Nutzergeste zu. Blockiert es doch einmal, läuft das Intro stumm und kürzer; ein „Ton an“-Knopf und ein Tipp auf den Bildschirm holen die Musik nach. Überspringen geht immer, und im Admin-Menü lässt sich das Intro abschalten.
Jeder Fall spielt in einer Stadt aus data/locations.csv; daraus werden fünf
Schauplätze gezogen (im Admin einstellbar, 3 bis 8). Hat eine Stadt mehr Orte
als nötig, wird gemischt - dieselbe Stadt fühlt sich beim nächsten Fall anders
an. Die Atmosphäre-Spalte geht in die Prompts ein und färbt, was an einem Ort
passiert.
Stadt;Location;Atmosphäre
Venedig;Markusplatz;Episch
Longyearbyen;Wildnis;gefährlich
Neue Städte einfach anhängen und npm run import:orte ausführen - jede Stadt
braucht mindestens so viele Orte, wie ein Fall Schauplätze hat. Wer nichts
einchecken will, liest die Tabelle im Admin-Menü unter Orte ein.
POST /api/case): Der Server wählt Stadt und Schauplätze,
würfelt einen Täter aus allen Verdächtigen und lässt Claude die Geschichte
darum herum bauen - Tat, Motiv, Alibis, Geheimnisse, Aufenthaltsorte und
4-6 Spuren, passend zur Stadt.POST /api/search): Findet an einem Ort die nächste unentdeckte
Spur. Braucht kein Modell und ist deshalb sofort da.POST /api/talk): Claude spielt den
jeweiligen Charakter, kennt sein Alibi und sein Geheimnis - und beim Täter
auch den Tathergang. Zurück kommen Antwort, Stimmung, eine mögliche Notiz,
eine eventuell entdeckte Spur und die Änderung des Verdachtswerts.POST /api/accuse): Zwei Versuche. Ob die Beschuldigung stimmt,
entscheidet der Server - Claude erzählt nur die Auflösung.Ist der Fall das Kapitel einer Saga, löst die Auflösung genau diesen Fall
und nicht die ganze Reihe: Stücke mit Fernwirkung gehen ohne ihre Bedeutung in
den Prompt („gehört nicht zu diesem Fall“), und der Drahtzieher wird dort weder
benannt noch erklärt - offen enden darf so ein Kapitel ausdrücklich. Was
trotzdem durchrutscht, streicht der Server danach satzweise, wie es der
Vorspann längst tut (lib/namenSchutz.ts): Nur wo Name und Enttarnung im
selben Satz stehen, fällt der Satz. Im Einzelfall und im Finale einer Saga
bleibt dagegen alles, wie es war - dort gehört die ganze Wahrheit hin.
Der vollständige Fall (Täter, Motiv, gelogene Alibis) verlässt den Server nur AES-256-GCM-verschlüsselt als „Siegel“. Der Browser trägt dieses undurchsichtige Siegel bei jeder Anfrage mit sich, entschlüsseln kann es nur der Server. Dadurch bleibt alles zustandslos - ideal für Vercel, ganz ohne Datenbank.
Der Spielstand selbst (Ort, Inventar, Notizen, Chatverläufe, Verdachtswerte)
liegt im localStorage des Geräts, ein angefangener Fall überlebt also das
Schließen der App.
Farben und Typografie orientieren sich am Detective-Conan-Keyvisual:
#050f22) als Grund, Karten in Stahlblau,#7cc4ff) für Werte, Fokusrahmen und ruhige Akzente.Das Layout ist bewusst rahmenlos: Der Ort füllt den ganzen Bildschirm, die Tiere stehen frei in der Szene, und im Gespräch ist das Gegenüber bildschirmfüllend zu sehen - die Sprechblasen schweben darüber. Statt Kartenrändern gibt es weiche Verläufe und Glasflächen.
Alle Farben liegen als CSS-Variablen ganz oben in app/globals.css - dort einmal
ändern reicht, um das ganze Spiel umzufärben.
app/
layout.tsx Metadaten, PWA-Einstellungen, Viewport (iPhone-tauglich)
page.tsx Hält alles zusammen: Tabs, Overlays, Spielfluss
admin/page.tsx Admin-Menü: Charaktere, Bilder, Einstellungen
globals.css Komplettes Styling (Safe-Areas, Szenen-Layout)
api/case/route.ts Fall erzeugen (Täter würfeln + Claude)
api/talk/route.ts Gespräch mit einem Charakter
api/search/route.ts Umsehen an einem Ort
api/accuse/route.ts Finale Beschuldigung und Auflösung
components/ Bildschirme, Overlays, Prolog, Intro und Abspann
components/admin/ die Bereiche des Admin-Menüs
lib/
firebase.ts Firebase-Anbindung (Firestore + anonyme Anmeldung)
db.ts Lesen und Schreiben der Sammlungen
stammdaten.ts Tiere/Orte/Dinge aus der Datenbank, sonst aus data/
adminStore.ts Gerätedaten: eigene Bilder und Spieloptionen
csv.ts Das CSV-Format - benutzt von App und Import-Skript
bildUpload.ts Bilder verkleinern und als Data-URL ablegen
characters.ts Charaktere + Helfer (aus der CSV erzeugt)
locations.ts Städte und Schauplätze
items.ts Gegenstände
prompts.ts Alle Prompts an Claude
schemas.ts Struktur der Antworten (Zod)
seal.ts Verschlüsselung des Falls
useGame.ts Spielzustand inkl. Speicherung auf dem Gerät
data/characters.csv Die Tier-Tabelle aus Excel
data/locations.csv Städte und ihre Schauplätze
public/audio/intro.mp3 Titelsong - seine Länge ist die Länge des Intros
scripts/import-csv.mjs CSV -> lib/characters.generated.ts
scripts/import-locations.mjs CSV -> lib/locations.generated.ts
scripts/make-icons.mjs Erzeugt die App-Icons
scripts/optimize-images.mjs Verkleinert die Bilder in public/
Die Architektur ist darauf vorbereitet:
lib/prompts.ts und lib/schemas.ts trennen Rolle, Wissen und Antwortformat
sauber - ein zweiter Spieler bekommt einfach eine eigene Rolle („du bist
heimlich Tier X“) statt eines Claude-Charakters.onSnapshot mit.| Befehl | Zweck |
|---|---|
npm run dev | Lokal entwickeln |
npm run build | Produktions-Build (macht auch den Typecheck) |
npm run typecheck | Nur Typen prüfen |
npm run import:csv | Charaktere aus der CSV neu einlesen |
npm run import:orte | Städte und Orte aus der CSV neu einlesen |
npm run bilder:optimieren | Bilder in public/ handytauglich verkleinern |
npm run locations3d:optimieren | Aus 3D-Rohdateien spielbare -web.glb machen |
npm run locations3d:abspecken | Eingecheckte -web.glb nachträglich kleinrechnen |
npm run locations3d:klein | Dasselbe mit kleineren Texturen, für zu große Dateien |
npm run firebase:rules | Sicherheitsregeln und Indizes veröffentlichen |
npm run lint | Linter |
Die Graspiste (Städte, 3D-Kapitel, Arena, Verfolgungsjagd, Abspann, Probewelt): der vierte Belag neben Asphalt, Sand und Schnee - ein Trampelpfad durch die Wiese. Keine Spurrillen, keine Striche, keine Mittellinie: Sie hatte einmal zwei helle Bänder, wo die Räder die Erde freilegen, und das war als Feldweg gedacht; im Bild wurden daraus zwei harte Striche, die die Piste zerschnitten, und im Stadtraster lag dasselbe Muster auf jedem einzelnen Feld. Jetzt ist der Weg dieselbe Wiese wie ringsum, nur flacher, staubiger und mit unregelmäßigen Flecken, an denen die Erde durchkommt - was ihn ausmacht, ist sein Ton, nicht seine Zeichnung. Am durchgehenden Straßenzug franst er obendrein aus: Der Rand blendet über eine Handbreit in die Wiese, statt mit einer Kante aufzuhören.
Ringsum steht, was aus einer grünen Fläche eine Wiese macht: Büsche in zwei
Grüntönen, Laubbäume mit Stamm, Grasbüschel am Wegrand, Findlinge,
umgestürzte Stämme und Blumen in kleinen Nestern (grasLand in
components/stadtBau.ts - dieselbe Rechnung wie das Schneeland, nur in
Grün, und mit derselben festen Saat, damit die Wiese im Editor so aussieht
wie später im Spiel). Davon steht auf der Wiese etwa doppelt so viel wie im
Schnee: Dort trägt die Fläche sich selbst, hier braucht sie das Kleinzeug.
Und die Wiese ist nicht mehr nur eine Farbe - über ihr liegt ein fast weißes
Muster, das sie stellenweise abdunkelt, sodass jede Tageszeit ihre eigene
Wiese bekommt. Der Boden, das zurückgeworfene Licht und die Staubfahne
hinter den Reifen färben sich mit; nachts liegt die Piste im Mondlicht statt
im Mittagsgrün.
Der Blizzard ist kein stärkerer Schneesturm, sondern ein eigener Zustand: Die Welt endet nach wenigen Metern, der Schnee fliegt in zwei Schichten fast waagerecht vorbei, und Böen ziehen die Sicht immer wieder ganz zu - zwischen ihnen sieht man gerade weit genug, um zu ahnen, wohin man läuft. In der Verfolgungsjagd nehmen die Böen etwas weniger, damit der Wagen vor einem ein Schemen bleibt und man nicht einer Zahl im HUD hinterherfährt. Es gibt ihn auch als Wetter über dem Ortsbild (Admin → Spiel) - dort ganz ohne 3D, als CSS.
Verdacht in Bewegung. Ändert sich ein Verdacht, fährt rechts kurz eine Meldung herein - ohne Knopf und ohne Berührungen zu schlucken.
Titelkarten. Vor jedem Kapitel steht zwei Sekunden „Kapitel II“ und der Kapitelname über dem Schauplatz, dann übernimmt der Erzähler.