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,即可驗證所有權並自行管理介紹。
一分鐘完成驗證正在載入完整 README。 以下為已儲存的摘錄。
A two-player, turn-based tactical battler in the spirit of Heroes of Might & Magic 3,
set in a Slavic dark-fantasy world.
Send a friend a link → you both pick a castle → you each spend a gold budget recruiting
an army → the two armies fight on a hex battlefield until one side is wiped.
There is no adventure map, no hero and no spellbook. The battlefield phase is the
whole game.檢視完整 README ↗A two-player, turn-based tactical battler in the spirit of Heroes of Might & Magic 3, set in a Slavic dark-fantasy world.
Send a friend a link → you both pick a castle → you each spend a gold budget recruiting an army → the two armies fight on a hex battlefield until one side is wiped.
There is no adventure map, no hero and no spellbook. The battlefield phase is the whole game.
| Side | Castle | Flavour |
|---|---|---|
| Явь | Kitezh | The sunken city's druzhina. Human steel, shield walls, discipline. |
| Явь | Borovina | Leshy's old forest. Wolves, wood-wights, things wearing bark. |
| Явь | Gromoboy | Perun's thunder-host. Fast, fragile, hits like a storm. |
| Навь | Kostyanoy Dvor | Koshchei's bone court. Slow, relentless, hard to finish. |
| Навь | Topyla | Vodyanoy's drowned mire. Rusalki, bolotniks, drag-you-under tricks. |
| Навь | Yagaya Pushcha | Baba Yaga's wildwood. Witches, hexes, the hut on chicken legs. |
Full stat tables live in docs/factions.md.
npm install
npm run dev
| Script | What it does |
|---|---|
npm run dev | Vite dev server |
npm run build | Typecheck and build to dist/ |
npm run typecheck | TypeScript only |
npm run lint | oxlint |
npm run test | Vitest unit tests |
npm run e2e | Playwright end-to-end tests |
Cross-device multiplayer works out of the box, no account needed: it connects the two
browsers directly over WebRTC, using a free public broker only to make the initial
introduction. For a connection that does not depend on either player's network (some
restrictive corporate/campus networks block direct peer-to-peer), copy .env.example to
.env, fill in Supabase credentials, and that real server relay is used instead whenever
it's configured.
src/
hex/ axial coordinates, neighbours, pixel<->hex, A* pathfinding
engine/ rule-agnostic core: seeded RNG, Result<T>, pure action reducer
rules/ HoMM3 combat: initiative queue, retaliation, damage, victory
content/ factions, units, battlefield dimensions, balance numbers
net/ rooms, the sync protocol, and its transports (WebRTC, Supabase, BroadcastChannel)
ui/ screens, battle components, theme tokens
Two invariants hold the whole thing together:
Action handlers are pure and total. Same state plus same action always produces the
same result, with no clocks, no Math.random, and no I/O. Every die roll goes through
the seeded RNG in src/engine/rng.ts, whose cursor is part of serialized state.
Nothing outside src/ui/theme/tokens.css hardcodes a colour. Every colour, spacing
step and animation duration is a custom property, so a change to the visual direction
stays in one file.
The match is host-authoritative with verification. The guest sends action
requests; the host resolves them — including every random draw — and broadcasts
{action, rngDraws, stateHash}. The guest replays the action using the host's draws,
then compares its own hash against the host's.
Pure lockstep determinism would also work, and would be more elegant, but it fails silently: one difference in iteration order and the two players are playing subtly different games with no indication anything is wrong. Resolving randomness once and checksumming after every action means a desync is caught immediately and can be reported honestly.
Known limitation: the host's browser is the authority, so if the host closes their tab, the match ends. A guest who drops can reconnect and the host will re-send state, but a host who drops cannot be recovered — there is no database behind this, by design.
There is also no anti-cheat, and there cannot be one without a real server. Play with people you like.
Pushes to main deploy to Vercel automatically; other branches get preview URLs.
CI (typecheck, lint, test, build) runs on every push and pull request.