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
翻譯準備中
尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證3D-Shooter im Browser im Stil von CS: Kaufmenü, Runden, Klappziele (Three.js)
翻譯準備中
取自儲存庫 README
正在載入完整 README。 以下為已儲存的摘錄。
3D-Shooter im Browser im Stil von CS: Hauptwaffe, Pistole, Messer und zwei Extra-Slots für Granaten, dazu ein Kaufmenü mit Budget zu Beginn jeder Runde. Gebaut mit Three.js, Physik mit Rapier. Spielen: https://finnmarinov-lgtm.github.io/Feuer_Frei/ · auch auf itch.io: https://fm323.itch.io/feuer-frei檢視完整 README ↗
3D-Shooter im Browser im Stil von CS: Hauptwaffe, Pistole, Messer und zwei Extra-Slots für Granaten, dazu ein Kaufmenü mit Budget zu Beginn jeder Runde. Gebaut mit Three.js, Physik mit Rapier.
Spielen: https://feuerfrei.fmcode.de/ (Übersicht aller Spiele: https://fmcode.de/) · auch auf itch.io: https://fm323.itch.io/feuer-frei
Drei Spielarten:
Dazu drei Karten (Hof, Lagerhalle und der große, offene Hafen für Team-Spiele), drei Waffen-Modi (Alle Waffen, Nur Pistolen, Scharfschützen) und Skins für alle Waffen, das Messer und die eigene Figur, die man über Aufgaben freischaltet (Menüpunkt Skins & Aufgaben).
Es läuft am PC (Maus und Tastatur) und auf dem Handy oder Tablet (Touch-Steuerung, quer halten).
Hauptmenü: In der Mitte stehen Mehrspieler, Gegen KI, Training und Skins & Aufgaben, darunter die Karte. Oben rechts sind wie in anderen Spielen das Konto (abgemeldet „Anmelden“, angemeldet der Name mit dem Anfangsbuchstaben im Kreis; auf schmalen Bildschirmen nur der Kreis) und das Zahnrad für die Einstellungen. Die haben zwei Reiter: Allgemein und Steuerung (Tastenbelegung).
npm install
npm run dev
Danach http://localhost:5173 öffnen und auf Mehrspieler, Gegen KI oder Training klicken.
K7P2QX) und ein Link. Den Link mit Link kopieren oder Teilen an die Freunde schicken (bis zu 7, also höchstens 8 Spieler).Zu zweit (ein Mensch pro Team, keine KI) wird daraus das 1 gegen 1, genau wie bisher. Sonst ist es ein Team-Spiel (siehe unten).
Nach der Partie zurück in die Lobby: In der Auswertung führt Zur Lobby in dieselbe Lobby zurück (statt „Nochmal“). Die Verbindung bleibt dabei bestehen, alle sind sofort wieder da. Geht der Host zurück, kommen alle anderen automatisch mit, auch wer noch die Auswertung ansieht. Jeder landet in seinem alten Team, die KI-Spieler bleiben, und wer nicht zurückkommt, verliert seinen Platz nach einer Minute. Der Host kann dann alles neu einstellen und wieder starten; neue Leute können beitreten. Das Hauptmenü verlässt die Lobby ganz.
Zuschauen: Wer einer Lobby beitritt, während dort gespielt wird, schaut zu, bis die Partie vorbei ist, und spielt danach in der Lobby mit. Man sieht durch die Augen der Spieler (wie nach dem eigenen Tod), Leertaste oder Klick wechselt zum nächsten. Lebt gerade niemand, kreist die Kamera über der Karte. Geld, Leben, Munition, Luftschlag und Radar sind dabei ausgeblendet. Der Host meldet „… schaut zu“. Wer aus der Lobby geworfen wurde, darf nicht zuschauen.
F1ck, F.i.c.k oder Fiiick fallen auch darunter. Wer so einen Namen eintippt, sieht unter dem Namensfeld, wie die anderen ihn sehen. Der Filter gilt immer, also auch in Lobbys nur mit Code. Er fängt das Gröbste, nicht alles; für den Rest gibt es das Rauswerfen.Regeln im 1 gegen 1:
Leertaste oder ein Klick wechselt zwischen beiden (auf dem Handy der Feuer- oder Sprungknopf). Hat man keine Leben mehr, kann man dem Gegner bis zum Rundenende zuschauen, im Bombenmodus also auch beim Entschärfen.Esc) läuft das Duell weiter, man steht dann still. Wer das Duell über das Menü verlässt, verliert sofort.T öffnet die Liste, 1 – 6 schickt „gg“, „Nice!“, „Sorry!“, „Glück gehabt!“, „Hahaha“ oder „Warte kurz“. Die Nachrichten erscheinen bei allen unten links und verschwinden nach ein paar Sekunden.Alles wie im 1 gegen 1 (Geld, Kaufen, Leben pro Runde, Kill-Cam, Bombenmodus, Luftschlag), nur mit Teams:
Tab zeigt die Tabelle mit allen Spielern (Abschüsse, Tode, Ping).V, am Handy die Stecknadel): markiert die Stelle im Fadenkreuz für das eigene Team.
pg). Ältere Fassungen übergehen sie, deshalb blieb die Protokollnummer gleich.Leertaste oder Klick wechselt zum nächsten Mitspieler und zurück zur Kill-Cam.Leertaste oder Klick wechselt reihum zwischen ihnen. Wer sie überspringt, kommt auch nicht mehr zurück.Oben links zeigt ein Radar die ganze Karte von oben, wie in CS: Boden hell, Wände und Container dunkel, Kisten und halbhohe Mauern grau.
Gezeichnet wird es in src/ui/radar.js aus den Bauteilen der Karte (arena.boxes, arena.props), einmal pro Karte, darauf dann jedes Bild die Punkte. Welche Gegner zu sehen sind, prüft es zehnmal pro Sekunde mit denselben Sichtlinien wie die KI.
Die Oberfläche ist auf Deutsch oder Englisch, wählbar unter Einstellungen → Sprache (gespeichert unter feuer-frei-sprache). Ohne Wahl richtet sie sich nach dem Browser: Steht dort Deutsch an erster Stelle, ist das Spiel deutsch, sonst englisch. Das Übersetzen durch den Browser ist abgeschaltet (translate="no", <meta name="google" content="notranslate">): Auf dem Handy übersetzte Chrome sonst die englische Seite ins Deutsche, das Spiel übersetzte zurück, und die Texte sprangen hin und her. Geschrieben ist alles auf Deutsch, das bleibt die Quelle und steht so auch im HTML, das Google liest. src/i18n.js tauscht beim Start und bei jeder Änderung die Texte im Dokument gegen die englischen:
EN für feste Texte, dazu Vorlagen für zusammengesetzte ('{} hat dich erwischt' → '{1} got you', {n} steht für eine Zahl). Was in einem Platzhalter steht, wird selbst wieder übersetzt (Kartennamen, „E halten“).·, –, : oder , zerfallen in Teile, die einzeln übersetzt werden („Hof · Nur Pistolen“ → „Yard · Pistols only“). Namen von Spielern bleiben dabei stehen.Space, Ctrl) und Geld ($2,200) kommen direkt in der gewählten Sprache (keyLabel, geld), Texte außerhalb des Dokuments (Teilen, Name im Eingabefeld) über t().Neue Texte im Spiel brauchen einen Eintrag in EN oder eine Vorlage, sonst bleiben sie auf Englisch deutsch.
Im Hauptmenü wählt man die Karte für das Training (und für den Hintergrund des Menüs), bei Gegen KI und in der Lobby jeweils extra. Alle Karten sind punktsymmetrisch: Der Host startet im Westen, der Gast gespiegelt im Osten.
Hof: sandiger Innenhof mit zwei Gassen, einem Haus mit Tunnel in der Mitte, einem Balkon und Containern. Eher weite Blicke.
Lagerhalle: eine große Halle mit Hochregalen (drei Gänge pro Hälfte, mit Lücken zum Durchschlüpfen), einem Büro-Container in der Mitte, der die Sicht von Tor zu Tor versperrt, einem Gabelstapler und einem Laufsteg mit Treppe und Handlauf (in der Westhälfte an der Nordwand, gespiegelt im Osten; die Treppe ist so flach wie die im Hof, damit man nicht herunterrutscht). Gelaufen wird auf allen Treppen über eine unsichtbare Rampe, die Stufen selbst halten nur Kugeln und Granaten auf. Unter dem Laufsteg konnte man deshalb von unten in die Stufen hineinlaufen; jetzt steht am oberen Ende jeder Treppe eine unsichtbare Wand bis knapp unter die Rampe. Auch das Aufstehen nach dem Ducken prüft nur, woran man beim Laufen stößt (Wände, Decken, Rampen): Weil die Stufenkanten ein paar Zentimeter durch die Rampe ragen, blieb man geduckt auf der Treppe hängen und kam nur per Sprung wieder hoch. Das Dach hat drei offene Lichtbänder, durch die die Sonne fällt. Vor der Halle liegen die Höfe mit den Startpunkten, einem Lkw-Anhänger und einem Container, hinein geht es durch drei halb offene Rolltore pro Seite. Enger als der Hof, mehr Nahkampf. Die Bombenplätze liegen in der Halle nah an der Mitte (vom Startpunkt des Verteidigers aus nicht zu sehen, Wege etwa 28 m zu 33 m). In der Lagerhalle gibt es keinen Luftschlag (die Anzeige der Abschussserie ist dort ausgeblendet, auch die KI fordert keinen an).
Hafen: groß und offen, gedacht für 3 gegen 3 und 4 gegen 4 (88 x 59 m statt 60 x 40 m). Eine Mole mit Wasser an beiden Längsseiten, an den Enden je ein Speicher aus Backstein hinter dem Startpunkt. In der Mitte die breite Kranbahn mit einem gelben Portalkran, an dem ein Container hängt, darunter ein Container und Kisten als Deckung, dazu je ein Sattelzug. Auf der einen Seite jeder Hälfte das Stapelfeld (Containerreihen mit Gassen und einem Container mit Treppe als Ausguck), auf der anderen der offene Umschlagplatz mit dem Bombenplatz, einem Gabelstapler und der Hafenmeisterei: auf ihr Dach führt eine Treppe, die Brüstung schützt geduckt. An beiden Kaikanten ein breiter Weg am Wasser mit Pollern; eine unsichtbare Wand hält einen an Land. Vor den Ausgängen der Startbereiche stehen Container, damit man nicht von Startbereich zu Startbereich schauen kann (die längsten Sichtlinien sind Diagonalen um 60 m). Damit man nicht von überall erwischt wird, sind einige Durchgänge zu: Eine Containerreihe teilt die Kranbahn längs (mit einer schmalen Lücke am Kran), die Kaiwege sind hinter dem Stapelfeld und an der Hafenmeisterei unterbrochen, die Bombenplätze haben eine Rück- und eine Seitenwand, und hinter den Startblöcken führt kein Weg mehr zum Kai. Die Wege zu den Bombenplätzen sind dadurch nicht länger geworden (Angreifer etwa 56 m, Verteidiger etwa 36 m). Granaten, die ins Wasser fallen, gehen unter, ohne zu zünden. Der Luftschlag geht hier wieder.
Die Karte wird beim Wechsel komplett neu aufgebaut (Wände, Kollision, Bombenplätze, Schatten). Das Wegenetz der KI rechnet sich für jede Karte einmal selbst aus (auch auf dem großen Hafen in einem Bruchteil einer Sekunde). Wege sucht A* mit leicht überschätzter Restentfernung (Faktor 1,5): auf dem Hafen im Schnitt 4-mal so schnell wie die genaue Suche (0,3 statt 1,4 ms, lange Wege quer über die Karte vorher bis 10 ms am Stück), und nach dem Glätten sind die Wege gleich lang.
Die KI hält sich an denselben Modus (bei „Nur Pistolen“ kauft sie öfter die Kobra).
Unter Skins & Aufgaben im Hauptmenü stehen links die Waffen (Natter, Kobra, Falke, Keiler, Wolf, Luchs, Adler), das Messer und die eigene Figur, daneben ihre Skins. Rechts im Bild dreht sich die Waffe (oder der Soldat) mit dem Skin, zur Vorschau auch mit gesperrten Skins (einfach mit der Maus darüberfahren). Ein Klick auf einen freigeschalteten Skin legt ihn an. Gesperrte Skins zeigen ihre Aufgabe mit Fortschrittsbalken. Der zweite Reiter Alle Aufgaben listet alles, offene zuerst.
| Waffe | Skins (Aufgabe) |
|---|---|
| Natter | Wüstentarn (5 Abschüsse), Kirschrot (3 Kopfschüsse), Neon (25 Abschüsse) |
| Kobra | Nachttarn (5 Abschüsse), Gold (5 Kopfschüsse), Lava (eine Partie „Nur Pistolen“ gewinnen) |
| Falke | Waldtarn (10), Ozeanblau (5 Kopfschüsse), Galaxie (40) |
| Keiler | Waldtarn (5), Kupfer (15), Lava (40) |
| Wolf | Arktis (10), Gold (10 Kopfschüsse), Regenbogen (50) |
| Luchs | Nachttarn (10), Chrom (8 Kopfschüsse), Neon (40) |
| Adler | Arktis (3), Kupfer (15), Galaxie (eine Partie „Scharfschützen“ gewinnen) |
| Messer | Gold (3 Messer-Abschüsse), Galaxie (10), Regenbogen (die KI auf „Schwer“ besiegen) |
| Spieler | Wüstentarn (3 Siege), Waldtarn (5 Bomben legen), Nachttarn (3 Bomben entschärfen), Arktis (100 Klappziele im Training), Gold (3 Luftschlag-Abschüsse), Galaxie (3 Online-Siege) |
src/weapons/finishes.js). Kleine Teile wie Visier, Lauf und Gummigriffe bleiben original.feuer-frei-aufgaben, die getragenen Skins in den Einstellungen. Wer das Regenbogen-Messer schon vor den Aufgaben geschenkt bekommen hatte, behält es. Mit einem Konto (nächster Abschnitt) zusätzlich auf dem Server.Mit Benutzername und Passwort (ohne E-Mail) lassen sich Aufgaben-Fortschritt, getragene Skins und die Notizen vom Notizblock auf jedem Gerät nutzen. Der Konto-Knopf oben rechts im Hauptmenü erscheint erst, wenn die Datenbank eingerichtet ist.
rude() aus src/names.js), Passwort mit 6 bis 72 Zeichen. Was schon auf dem Gerät ist, kommt beim Anlegen ins Konto. Ein vergessenes Passwort lässt sich nicht zurücksetzen (es gibt keine E-Mail).ff_konto und ff_sitzung sind für die Webseite gesperrt (Row Level Security ohne Regeln), alles läuft über Funktionen (security definer), die Eingaben prüfen (Form, höchstens 200.000 Zeichen).supabase/konto.sql einmal im SQL-Editor des Supabase-Projekts ausführen (dasselbe Projekt wie Mitbringliste und Petri Heil, nochmal ausführen schadet nicht). Testen ohne Server: npm i --no-save @electric-sql/pglite und node supabase/konto.test.mjs (Postgres im Speicher, 52 Prüfungen). Der Code im Spiel steht in src/net/konto.js, die Anbindung (Notizen, Skins, Bildschirm) in src/main.js.Gegen KI im Hauptmenü: Schwierigkeit, Modus (Kampf oder Bombe), Karte, Waffen-Modus, Leben und Rundensiege wählen, dann Los geht’s. Die Einstellungen merkt sich das Spiel.
Die KI spielt nach denselben Regeln wie ein Freund im 1 gegen 1 (sie ist dabei der Gast im Osten):
| Stufe | Reaktion | Zielfehler | Besonderheiten |
|---|---|---|---|
| Anfänger | 1,15 s | sehr groß | schießt zögerlich, kauft keine Gewehre, keine Luftschläge, läuft etwas langsamer |
| Leicht | 0,75 s | groß | läuft beim Schießen herum, selten Kopfschüsse |
| Mittel | 0,4 s | mittel | bleibt zum Schießen stehen, hört Schritte weiter |
| Schwer | 0,25 s | klein | dreht sich schnell, oft Kopfschüsse, duckt sich bei langen Salven |
Auf dem Handy spielt die KI in jeder Stufe schwächer, weil Zielen mit dem Finger schwerer ist: 0,3 s längere Reaktion, anderthalbfacher Zielfehler, langsameres Drehen und Schießen, halb so oft Luftschläge. Ohne gespeicherte Wahl beginnt man dort bei den Anfängern, am PC auf Leicht (auch die KI-Spieler in der Lobby).
Fair bleiben (nach einer Rückmeldung auf Reddit, 29.09.2026): Die KI trifft einen Menschen über dieselbe Körperbreite (50 cm), die man selbst bei ihr treffen muss (vorher 64 cm). Sie ahnt zwar ungefähr, wo man steckt, damit sie nicht ewig sucht, schaut aber beim Suchen und beim Bombelegen nur auf eine vermutete Stelle und nicht mehr durch Wände genau auf einen.
Überraschung: Wer die KI auf Schwer besiegt, bekommt die Regenbogen-Klinge fürs Messer (siehe Skins und Aufgaben).
Gegen die KI hält die Pause das Spiel wirklich an. Technisch hängt die KI wie ein zweiter Spieler am Duell: Sie schickt und bekommt dieselben Nachrichten wie ein Gast, nur ohne Netz (src/ai/).
Auf Handy und Tablet schaltet sich die Touch-Steuerung von selbst ein (in den Einstellungen: Automatisch, An oder Aus; zum Testen am PC ?touch=1 an die Adresse hängen). Das Spiel läuft im Querformat, im Hochformat erscheint ein Hinweis zum Drehen.
Beim Start geht das Spiel in den Vollbildmodus und sperrt das Querformat, soweit der Browser das erlaubt (Android ja, iPhone nicht). Wechselt man die App, pausiert das Spiel.
Technik:
src/net/signal.js, nachgebaut nach @trystero-p2p/supabase, aber mit dem RealtimeClient statt des ganzen supabase-js).
src/net/net.js auf jeder Direktverbindung einen zweiten Kanal ohne Nachschicken und ohne Reihenfolge an (wie UDP). Beide Seiten legen ihn mit derselben Nummer an (negotiated, Nummer aus dem Raum).fp). Hat die Gegenseite sie nicht (etwa weil das Anlegen scheiterte), geht alles wie bisher über den sicheren Kanal._firstEvents).Die Tabelle zeigt die Standardbelegung. Eigene Tastenbelegung: In den Einstellungen im Reiter Steuerung (Zahnrad oben rechts im Hauptmenü; in der Pause führt Steuerung direkt dorthin) auf eine Taste klicken und die neue drücken. Das geht auch mit den Maustasten 3 bis 5 (Mitte und Seitentasten). Jede Aktion kann zwei Tasten haben (mit + kommt die zweite dazu). Entf löscht eine Belegung, Esc bricht ab. Ist eine Taste schon woanders belegt, wird sie dort frei (mit Hinweis). Standard wiederherstellen setzt alles zurück. Fest sind nur Schießen (Linksklick), Zielen (Rechtsklick), Waffenwechsel (Mausrad) und Pause (Esc). Die Belegung wird im Browser gespeichert, und alle Hinweise im Spiel (Kaufmenü, Bombe, Luftschlag, Schnellnachrichten) zeigen die eigenen Tasten.
Mausempfindlichkeit (unter Einstellungen › Allgemein): zählt wie in CS (0,022° pro Mausschritt mal Wert) und reicht von 0,2 bis 20. Der Regler ist logarithmisch: In der Mitte steht 2, nach rechts wird jeder Schritt größer. So lässt sich der übliche Bereich fein einstellen, und für Touchpads oder Mäuse mit wenig DPI reicht es trotzdem. Das Spiel liest die Maus ohne die Beschleunigung von Windows (Pointer Lock mit unadjustedMovement).
| Taste | Aktion |
|---|---|
W A S D | Laufen |
| Maus | Umsehen |
| Linksklick | Schießen, Messerhieb, Granate weit werfen |
| Rechtsklick halten | Zielen über Kimme und Korn bzw. Rotpunkt (langsamer, genauer, leichter Zoom), Zielfernrohr (eine Stufe); außerdem Messerstich, Granate kurz werfen |
| Leertaste | Springen |
Strg oder C | Ducken |
Shift halten | Sprinten (nur vorwärts, siehe unten) |
Alt (links) halten | Schleichen (lautlos und genauer) |
R | Nachladen |
1 – 5 | Hauptwaffe, Pistole, Messer, Extra 1, Extra 2 |
Q, Mausrad | Letzte Waffe, Waffe wechseln |
B | Kaufmenü (im Spawn, in der Kaufzeit) |
F | Waffe begutachten |
Tab | Statistik |
T, dann 1 – 6 | Schnellnachricht (im Mehrspieler) |
Mausrad drücken (Maus 3) oder V | Markieren fürs Team (Stelle oder Gegner im Fadenkreuz, im Team-Spiel) |
E halten | Bombe legen bzw. entschärfen (Bombenmodus) |
X | Luftschlag, nach 3 Abschüssen hintereinander |
Esc | Pause; in den Menüs zurück (wie der Zurück-Knopf, in der Auswertung zum Hauptmenü); schließt auch Kaufmenü und Schnellnachrichten |
^ (änderbar) | Notizblock: sofort ein weißes Notizblatt, Spiel pausiert, Ton aus, der Tab heißt wie die Notiz und die Adresse zeigt …/notizen statt des Spiels (die Domain selbst lässt sich nicht ändern); nochmal drücken zum Zurückschalten |
Sprinten geht nur vorwärts und nicht beim Ducken, Schleichen, Schießen, Zielen, Nachladen oder Ausholen zum Granatenwurf. Wie schnell man ist, hängt von der Waffe in der Hand ab (Meter pro Sekunde, Laufen / Sprinten):
| Waffe in der Hand | Laufen | Sprinten |
|---|---|---|
| Messer | 6,2 | 8,8 |
| Natter, Granaten | 6,0 | 8,0 |
| Kobra | 5,8 | 7,8 |
| Falke | 5,8 | 7,6 |
| Keiler, Wolf, Luchs | 5,5 | 7,1 |
| Adler | 5,0 | 6,3 |
Nach dem Sprinten braucht die Waffe einen Moment, bis sie schießt (Natter 0,12 s, Adler 0,3 s, Messer sofort). Wer beim Sprinten klickt, hört auf zu sprinten und schießt, sobald die Waffe bereit ist. Zum Weitersprinten Shift neu drücken.
Weil Strg Ducken ist und das Mausrad die Waffe wechselt, fängt das Spiel Strg + Mausrad (und Strg + Plus/Minus/0) ab, solange man spielt: sonst würde der Browser die ganze Seite zoomen. Ist die Seite schon verkleinert, stellt Strg + 0 sie zurück.
Das Spiel geht beim Start in den Vollbildmodus. Esc verlässt ihn (das macht der Browser immer, zusammen mit der Maus). Mit Weiter oder einem Klick ins Spiel kommt das Vollbild zurück, in der Pause gibt es dafür außerdem den Knopf Vollbild. So fängt Chrome auch Strg+W ab, sonst würde Ducken plus Vorwärtslaufen den Tab schließen. Abschalten lässt sich das in den Einstellungen.
| Waffe | Art | Preis | Besonderheit |
|---|---|---|---|
| Natter | Pistole | 200 $ | Startwaffe, 30 Schaden (Kopf 72); moderne Polymerpistole mit Bronzelauf und Visier mit drei weißen Punkten |
| Kobra | Schwere Pistole | 700 $ | Kopftreffer tödlich |
| Falke | Maschinenpistole | 1.250 $ | Genau auch im Laufen; Geisterring-Visier (dünner Ring mit großer Öffnung), 1,3-facher Zoom |
| Keiler | Pump-Schrotflinte | 1.050 $ | 9 Schrotkugeln, auf kurze Distanz ein Treffer, lädt Patrone für Patrone; erhöhte Laufschiene mit Messingkorn, im Anschlag liegt das Gehäuse knapp 3 cm unter der Visierlinie |
| Wolf | Sturmgewehr | 2.700 $ | Stark, festes Rückstoßmuster; Kimme und Korn 1,6 cm höher als beim Vorbild, schmaler Kimmenfuß: im Anschlag bleibt der Gehäusedeckel unten aus dem Bild |
| Luchs | Sturmgewehr mit Rotpunkt | 3.100 $ | Rotpunktvisier mit dünnwandigem Rohr (14 cm vor dem Auge), ruhigerer Rückstoß |
| Adler | Scharfschützengewehr | 4.500 $ | Ein Kopftreffer reicht immer, auch mit Helm. Am Körper 88 Schaden (wie die Scout in CS): Es bleiben je nach Entfernung und Weste 12 bis 22 Lebenspunkte, ein zweiter Treffer oder ein Pistolenschuss erledigt den Rest. Zielfernrohr nur beim Halten |
| Messer | Nahkampf | frei | Karambit oder Butterfly (siehe unten). Hieb (links, 60) und Stich (rechts, 90): zwei Treffer reichen immer, auch gegen eine Weste |
Dazu Schutzweste, Weste mit Helm und drei Granaten (Splitter, Blend, Rauch) für die zwei Extra-Slots. Die Splittergranate macht direkt daneben 130 Schaden (1 m: 105, 2 m: 82, 3 m: 60, 4 m: 41), eine Weste hält die Hälfte ab.
Die Weste hat 50 Punkte (früher 100, die hat in einem Leben nie jemand aufgebraucht). Jeder Treffer am Körper kostet sie die Hälfte des Schadens, den sie abhält. Bis zum Tod reicht sie trotzdem fast immer: Die Zahl der Treffer bis zum Tod ist mit 50 Punkten dieselbe wie mit 100. Schneller leer ist sie nur bei vielen Treffern hintereinander, etwa im Luftschlag, oder wenn man mit einer angeschlagenen Weste in die nächste Runde geht (nachkaufen füllt sie wieder auf 50).
Aus der Hüfte und im Anschlag: Aus der Hüfte (ohne rechte Maustaste) schlagen alle Schusswaffen 1,6-mal so stark aus und streuen mehr, die Ansicht wackelt stärker (HIP_RECOIL in src/config.js). Im Anschlag bleibt der Rückstoß wie gehabt. Beim Scharfschützengewehr baut sich der Rückstoß schon während des Repetierens ab, so steht der Blick still, wenn man wieder im Zielfernrohr ist (vorher zog er ihn dann noch nach unten). Mit einer Granate in der Hand zeigt die Bildmitte statt des Fadenkreuzes nur einen Punkt, der die Wurfrichtung markiert.
Munition (Magazin + Ersatz, pro Leben): Die Hauptwaffen haben nur zwei Ersatzmagazine, wer viel schießt, muss zwischendurch zur Pistole greifen. Nach dem Wiedereinstieg und zu Beginn jeder Runde ist alles wieder voll.
| Waffe | Munition |
|---|---|
| Falke, Wolf, Luchs | 30 + 60 |
| Keiler | 8 + 16 |
| Adler | 5 + 15 |
| Natter | 20 + 120 |
| Kobra | 7 + 35 |
Beim Nachladen einer Waffe mit Magazin kippt die Waffe zur Seite, das leere Magazin fällt heraus und die linke Hand steckt ein neues ein. Die Keiler lädt weiter Patrone für Patrone.
Messer: Im Mehrspieler hat Team Rot (im 1 gegen 1 der Host) ein Karambit, Team Blau (Gast, auch die KI) ein Butterflymesser. Im Training entscheidet der Zufall. Beim Ziehen und beim Begutachten (F) dreht sich das Karambit einmal um den Zeigefinger, das Butterfly klappt auf: Klinge und zweite Griffhälfte schwingen über die Faust, dann klappt die Griffhälfte unten herum zurück. Ein Hieb bricht das sofort ab. Beide Messer haben dieselben Werte. In der Waffenleiste heißt es weiter „Messer“, welches es ist, steht über der Munitionsanzeige und im Kill-Feed. Wer das Geschenk hat (Sieg gegen die KI auf Schwer), trägt eine Regenbogen-Klinge.
Kill-Feed oben rechts: Schütze, Waffen-Symbol (Kopfschüsse mit eigenem Zeichen), Opfer. Im Mehrspieler stehen die Namen in Teamfarben (Rot und Blau), eigene Abschüsse und der eigene Tod sind gelb umrandet. Wer durch die Bombe stirbt, wird dem angreifenden Team zugeschrieben.
Trefferzonen: Kopf (Faktor je Waffe, meist 2,4), Körper und Arme (1), Beine (0,75, dort schützt die Weste nicht). Die Trefferzonen sind etwas größer als die sichtbaren Figuren und Klappziele, damit man leichter trifft.
Esc → Training beenden).B), auch mitten auf der Karte. Zurückgeben geht mit Rechtsklick.Tab zeigt dazu Zeit, Treffergenauigkeit und Kopfschüsse.Alle Werte (Waffen, Rückstoßmuster, Streuung, Preise, Rundenzeiten, Training) stehen in src/config.js.
| Ordner / Datei | Zweck |
|---|---|
src/config.js | Alle Spielwerte an einem Ort |
src/game/ | Spielkern, Runden und Geld, Klappziele, 1 gegen 1 mit Bombenmodus (duel.js), Team-Spiel (teams.js, Teams und Startplätze in sides.js), die anderen Spieler im eigenen Spiel (remote.js), Kill-Cam und Zuschauen (killcam.js), der Luftschlag (airstrike.js) sowie Skins und Aufgaben (cosmetics.js) |
src/net/ | Verbindung zwischen zwei Browsern (Trystero und Supabase Realtime), Tab-Speicher für den Wiedereinstieg, Liste der offenen Lobbys (lobbylist.js) |
src/names.js | Namensfilter: grobe Wörter werden zu „Spieler NN“ |
src/ai/ | KI-Spieler: Wegenetz (nav.js), Verhalten (bot.js), die Verbindung zum Duell ohne Netz (botnet.js) und die KI-Spieler im Team-Spiel beim Host (squad.js) |
src/player/ | Bewegung wie in der Source-Engine (Beschleunigung, Reibung, Luftsteuerung, Ducken) |
src/weapons/ | Inventar mit 5 Slots, Schießen, Rückstoß, Waffe in der Hand, Granaten, Oberflächen der Skins (finishes.js) |
src/world/ | Karten Hof und Lagerhalle mit Auf- und Abbau (map.js), der Hafen (hafen.js), Bauteile für mehrere Karten wie Seecontainer und Gabelstapler (parts.js), Himmel und Licht, Bombenplätze und Bombe (bombsites.js) |
src/effects/ | Einschusslöcher, Funken, Staub, Leuchtspuren, Explosionen |
src/engine/ | Grafik, Physik, Steuerung, Ton (alle Geräusche werden per WebAudio erzeugt) |
src/ui/ | HUD, Radar (radar.js), Kaufmenü, Lobby, Touch-Steuerung (touch.js) und die Waffenkammer „Skins & Aufgaben“ (locker.js) |
blender/ | Python-Skripte, die alle eigenen 3D-Modelle in Blender bauen |
scripts/ | Laden der Poly-Haven-Dateien und Aufruf von Blender |
Eigene Modelle (Waffen mit Händen, Granaten, Kisten, Klappziel, Soldat für das 1 gegen 1) entstehen per Skript in Blender:
npm run models
npm run models -- --preview
npm run models -- wolf natter
npm run models -- texturen
Mit --preview legt Blender Vorschaubilder in blender/preview/ ab (für die Pistolen zusätzlich eine größere Ansicht von links hinten, so wie man sie im Spiel sieht). Blender wird unter C:\Program Files\Blender Foundation gesucht, sonst über die Umgebungsvariable BLENDER.
Die Oberflächen von Natter, Karambit und Butterfly (geschliffener Stahl, mattierter Kunststoff, Griffnarbung, G10) rechnet das Skript selbst als kleine, kachelbare Texturen aus (tex_brushed, tex_grain, tex_stipple in blender/lib.py), es braucht dafür keine Fremddateien. Schrift wie die Gravur „NATTER 9x19“ entsteht mit text_mesh. Teile, deren Name mit Hand, Wrist oder Sleeve beginnt, gelten als Arm und fehlen bei der Figur des Gegners.
Gemeinsame Oberflächen aller Waffen (seit 02.10.2026): Sechs kachelbare Texturen rechnet npm run models -- texturen aus (weapon_surfaces in blender/lib.py) und legt sie unter public/assets/textures/waffen/ ab, zusammen rund 350 KB: Abnutzung (Waffenstahl mit feinem Korn, Fingerabdrücken, Ölflecken und Kratzern), gebürstet (Stahl), Korn (Kunststoff, Gummi, Handschuh), Holz (Maserung mit Spätholz und Poren), Narbung (Schaft des Adler) und Stoff (Ärmel). Jede hat drei Bilder: col und arm enthalten nur Faktoren knapp unter 1 auf Farbe und Rauheit, Grundfarbe, Rauheit und Metall bleiben im Material aus Blender. So reicht ein Satz für alle Waffen (einmal geladen, einmal im Grafikspeicher), und eine Waffe in fremder Hand, die ohne Texturen zu einem Mesh zusammengefasst wird (flattenParts), sieht fast gleich aus und braucht nicht mehr Zeichenaufrufe. Welches Material welche Oberfläche bekommt, steht in src/weapons/surfaces.js. Dazu kommen abgegriffene Kanten: Der Shader erkennt gerundete Kanten an ihrer Krümmung (Änderung der Flächenrichtung je Meter, gilt auf jede Entfernung gleich) und lässt dort fleckig blanken Stahl, helleres Holz oder abgewetzten Kunststoff durchscheinen (Flecken aus dem roten Kanal von arm). Dafür bekommen alle Teile der Waffen Textur-Koordinaten (Box-Projektion, 10 cm pro Kachel, längs zur Waffe), die Modelldateien sind dadurch zusammen etwa 0,9 MB größer. Skins ersetzen Farbe und Rauheit, das Relief bleibt.
Das Spiel startet auf Niedrig, damit es auch auf schwachen Laptops (Intel-Grafik) flüssig läuft. In den Einstellungen gibt es:
| Stufe | Was sie macht |
|---|---|
| Niedrig | ohne Kantenglättung und Umgebungsverdeckung, Schatten der Arena werden nur einmal berechnet (Bewegliches wirft keinen Schatten), wenig Texturfilterung |
| Mittel | Schatten in jedem Bild, 4-fache Kantenglättung |
| Hoch | dazu Umgebungsverdeckung (weiche Kontaktschatten), schärfere Schatten, höhere Auflösung auf hochauflösenden Bildschirmen |
Dazu die Auflösung (Automatisch, 100 %, 85 %, 70 %, 50 %): weniger Pixel sind der größte Hebel für schwache Grafikchips, das Bild wird dafür etwas unschärfer. Voreingestellt ist Automatisch: Das Spiel misst beim Spielen die Bilder pro Sekunde und zeichnet unter 40 eine Stufe kleiner (100 → 85 → 70 → 50 %). Bringt eine Stufe nichts (dann bremst der Prozessor, nicht die Grafik, oder der Browser zeigt im Stromsparmodus ohnehin nur 30 Bilder), geht es eine zurück und bleibt dabei. Wieder hoch geht es erst beim nächsten Laden der Seite. Wer vorher die Voreinstellung 100 % hatte, ist einmalig auf Automatisch umgestellt; eine eigene Wahl bleibt.
Shader vorab übersetzen: Beim Laden und nach jedem Kartenwechsel übersetzt das Spiel alle Shader auf einmal (precompile in src/game/game.js): Arena, Figuren mit allen Waffen, Mündungsfeuer, Funken, Granaten, Rauch und Jet. Sonst übersetzt sie der Browser erst, wenn etwas zum ersten Mal zu sehen ist, und das Bild hängt kurz (auf einer RTX 4060 13 bis 17 ms, auf schwachen Laptops ein Vielfaches). Übersetzt wird für den Zwischenpuffer, in den das Spiel zeichnet: Die Fassung für die direkte Ausgabe auf den Bildschirm ist eine andere und wurde bis zum 30.09.2026 umsonst übersetzt (gut 20 Shader), die richtigen kamen erst mitten im Spiel dazu. Nach einem Kartenwechsel läuft das Übersetzen neben dem Spiel her; bis es fertig ist (meist Bruchteile einer Sekunde, höchstens 3 s), bleibt das letzte Bild stehen, statt dass alles stillsteht.
Mehr als 60 Bilder pro Sekunde gibt es im Browser nur auf einem Bildschirm, der mehr kann: Der Browser zeichnet höchstens so oft, wie der Bildschirm das Bild erneuert (60, 144, 180 Hz …). Welche Bildrate der Browser liefert, zeigt z. B. https://www.testufo.com.
Die Waffe in der Hand federt bei Mausbewegungen leicht nach. Diese Feder wird in Schritten von höchstens 1/120 Sekunde gerechnet, damit sie auch bei wenigen Bildern pro Sekunde ruhig bleibt (vorher schaukelte sie sich unter 30 Bildern pro Sekunde auf: die Waffe sprang auf und ab und verschwand dann ganz).
Damit das Spiel sparsam bleibt: Teile, die sich gemeinsam bewegen und dasselbe Material haben, werden beim Laden zu einem Mesh zusammengefasst (src/engine/merge.js). Einschusslöcher und Rauchschwaden sind je ein Instanz-Mesh, also ein Zeichenaufruf statt vieler.
Figuren der anderen Spieler sind besonders sparsam gebaut, damit auch 4 gegen 4 flüssig läuft:
SkinnedMesh, skinParts in merge.js): Jedes Teil hängt fest an seinem Gelenk, die Gelenke sind die Knochen. Uniform und Helm haben eigene Materialien (Teamfarbe, Skins), alle anderen Teile teilen sich eins, das Farbe, Rauheit und Metall pro Ecke mitbringt. So braucht ein Körper 3 Zeichenaufrufe statt gut 20. Welcher Arm zur Waffe passt, entscheidet die Größe seines Gelenks (0 = unsichtbar).flattenParts): nur Teile mit Skin oder Textur bleiben für sich, meist 2 bis 4 Zeichenaufrufe statt bis zu 15. In der Hand hängt nur die Waffe, die der Spieler gerade trägt, die anderen sind nicht im Szenengraph.Die Leinwand ist auf geringe Verzögerung eingestellt (desynchronized). Dabei kann der Browser Zwischenstände anzeigen, deshalb wird jedes Bild erst im Hintergrund fertig zusammengesetzt und dann in einem Zug ausgegeben. Direkt in mehreren Durchgängen ins sichtbare Bild zu zeichnen führt zu Flackern.
Seit dem 03.10.2026 gibt es Datenschutzerklärung und Nutzungsbedingungen, beide deutsch und englisch auf einer Seite (öffnen in der Sprache des Spiels, #de/#en erzwingt eine). Verlinkt sind sie im Hauptmenü unter dem Infotext und beim Anlegen eines Kontos. Verantwortlich ist Finn Marinov, Kontakt per E-Mail (siehe Seite).
Die Erklärung beschreibt alle Datenflüsse. Ändert sich daran etwas, muss sie mit:
Schrift: Rajdhani kommt nicht mehr von Google Fonts, sondern aus dem Paket @fontsource/rajdhani. Vite bündelt die Dateien mit, die Lizenz (SIL Open Font License) liegt unter public/lizenzen/. Vorher ging beim Laden die IP-Adresse jedes Besuchers an Google.
src/net/zaehler.js meldet bei jedem Laden der Seite einen Aufruf an die eigene Supabase-Datenbank. Gespeichert werden nur Datum und Herkunft (der Hostname der verweisenden Seite, z. B. itch.io) – keine IP, kein Cookie, nichts Personenbezogenes. Auf localhost zählt es nicht mit.
Einmalig in Supabase anzulegen (SQL-Editor):
create table if not exists aufrufe (
tag date not null default current_date,
quelle text not null default 'direkt',
anzahl integer not null default 0,
primary key (tag, quelle)
);
alter table aufrufe enable row level security;
create or replace function seite_aufgerufen(p_quelle text default 'direkt')
returns void
language plpgsql
security definer
set search_path = public
as $$
begin
insert into aufrufe (tag, quelle, anzahl)
values (current_date, coalesce(nullif(p_quelle, ''), 'direkt'), 1)
on conflict (tag, quelle) do update set anzahl = aufrufe.anzahl + 1;
end;
$$;
grant execute on function seite_aufgerufen(text) to anon;
Auswerten im SQL-Editor:
select tag, sum(anzahl) as aufrufe from aufrufe group by tag order by tag desc;
select quelle, sum(anzahl) as aufrufe from aufrufe group by quelle order by aufrufe desc;
Repository: Feuer_Frei von finnmarinov-lgtm, spielbar unter https://feuerfrei.fmcode.de/
Zweiter Ort: die itch.io-Seite https://fm323.itch.io/feuer-frei mit demselben Build zum Mitspielen. Sie wird nicht automatisch aktualisiert – nach größeren Änderungen dort ein neues Zip aus dist hochladen. Wichtig: Das Zip nicht mit Compress-Archive packen (schreibt Backslashes in die Pfade, itch entpackt es dann falsch und das Spiel bleibt im Ladebildschirm hängen), sondern zum Beispiel mit Python zipfile und Schrägstrichen.
Einladungslinks für den Mehrspieler-Modus funktionieren nur auf GitHub Pages zuverlässig; im itch-Fenster läuft das Spiel in einem Rahmen, dort geht der Lobby-Code zum Eintippen.
Jeder Push auf main baut das Spiel automatisch neu und veröffentlicht es (.github/workflows/pages.yml). Den Stand sieht man im Repo unter Actions.
Lokal bauen und ansehen:
npm run build
npm run preview
Der Ordner dist/ enthält die fertige Seite. Die Pfade sind relativ, sie läuft also auch in einem Unterordner.
src/ui/fehler.js oben als Kasten (am Handy gibt es keine Konsole); ein Bildschirmfoto davon zeigt Datei und Zeile.desynchronized (weniger erprobter Sonderweg).to), Treffer von Mitspielern werden ignoriert.sessionStorage), die beim Neuladen bleibt, während die Kennung im Netz wechselt. Daran erkennt der Host einen zurückkehrenden Spieler.game.simNow), nicht die Uhrzeit beim Senden: Die lag je nach Bildrate bis zu einen Schritt (8 ms) daneben, und bei mehreren Schritten in einem Bild bekamen zwei Zustände denselben Stempel (der zweite ging verloren). Gegner liefen dadurch beim anderen leicht ungleichmäßig; gemessen schwankte ihre Geschwindigkeit von Bild zu Bild bei 9 von 10 Bildern um bis zu 23 %, jetzt um bis zu 7,5 %. Übergeblendet wird zur Zeit des Bilds (Zeitstempel von requestAnimationFrame), nicht zur Uhrzeit mitten in der Berechnung.sessionStorage (übersteht das Neuladen, nicht das Schließen des Tabs). Auch beim Host steht der Lobby-Code in der Adresse. Beim Verlassen der Seite meldet sich der Tab ab, zusätzlich per fetch mit keepalive über die REST-Schnittstelle von Supabase, damit die Abmeldung auch bei schnellem Neuladen ankommt. Wer zurückkommt, bekommt vom anderen den Stand der Partie (Runde, Phase, Zeit, Leben, Siege).ff-offen-v plus Protokollversion) und trägt Änderungen nach (Spielerzahl, Karte, Modus). Das Mehrspieler-Menü hört dort mit, solange es offen ist. Bricht die Verbindung des Hosts ab, streicht der Server den Eintrag von selbst. Einträge anderer werden geprüft (Code, Karte, Modus, Zahlen), Namen laufen durch den Filter.src/names.js räumt jeden Namen auf, bevor er angezeigt wird (Steuer- und unsichtbare Zeichen raus, höchstens 16 Zeichen) und ersetzt grobe Wörter durch „Spieler NN“ (die Zahl hängt am Namen, alle sehen dieselbe). Vor dem Vergleich werden Ziffern und Zeichen zurückgebaut (1 → i oder l, 3 → e, @ → a …), ebenso gleich aussehende kyrillische und griechische Buchstaben. Lange Wörter zählen auch mitten im Namen, kurze nur als ganzes Wort (sonst träfe es „Marsch“, „Cocktail“ oder „Kanal“).spec, wie beim Wiedereinstieg) und reicht alle Zustände weiter (fw, mit der Kennung des Spielers); von den anderen Spielern bekommen sie selbst nichts. Im Team-Spiel stehen sie dazu in der Gruppe des Hosts, zählen aber nicht für den langsamsten Weg (net.watchers). Ein 1 gegen 1 schickt der Host im Format des Team-Spiels (Rot = Host, Blau = Gast, feste Kennungen aus der Lobby). Beim Zuschauer läuft ein Team-Spiel ohne eigenen Spieler (spectate), mit größerem Puffer gegen Ruckeln (220 ms), weil alles über zwei Wege kommt.Game.leaveToLobby, Lobby.returnFromMatch). Der Host schickt den Stand der Lobby alle 2 Sekunden an alle im Raum; wer noch in der Auswertung steht, folgt damit von selbst.PROTOCOL in src/net/net.js). Nach einem Update also beide die Seite neu laden.?netz=server oder ?netz=direkt an die Adresse hängen.public/assets/ | Fertige Modelle, Texturen und Himmel, die das Spiel lädt |
Die Waffe in der Hand wird mit einer entsättigten Kopie des Himmels beleuchtet, damit schwarze Waffen schwarz und nicht dunkelblau wirken.
Texturen, Himmel und Requisiten kommen von Poly Haven (CC0, frei nutzbar). npm run assets lädt alles herunter, danach verkleinert npm run textures die Texturen (JPEG-Qualität 85, gleiche Auflösung, rund ein Viertel der Dateigröße). Die Requisiten landen als Quelle in assets-src/ (nicht im Repo). npm run models macht daraus die optimierte public/assets/models/props.glb (etwa 1.000 Dreiecke und 512er-Texturen pro Requisit).