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 small but fully playable Tower Defense game. Create your own adventurer,
collect champions through summons, deploy them to hold back waves of enemies,
and defeat the boss of each stage.
Built with Vite + React + TypeScript and a plain for the board —
no game engine and no runtime libraries beyond React. All sprites, props,
terrain, sound effects and music are drawn or synthesized in code.檢視完整 README ↗A small but fully playable Tower Defense game. Create your own adventurer, collect champions through summons, deploy them to hold back waves of enemies, and defeat the boss of each stage.
Built with Vite + React + TypeScript and a plain <canvas> for the board —
no game engine and no runtime libraries beyond React. All sprites, props,
terrain, sound effects and music are drawn or synthesized in code.
npm install
npm run dev # start the dev server (http://localhost:5174)
npm run build # type-check + production build
npm run lint # eslint
npm run typecheck # tsc only
npm run champions # dev server opened on the champion stats sheet (#champions)
npm run enemies # dev server opened on the enemy stats sheet (#enemies)
npm run balance # headless balance simulator (tools/balance-sim)
npm run editor # content editor (http://localhost:5175), see "Content editor" below
Strict one-directional layering (ui → application → domain, with engine a
self-contained sibling), so new content is mostly a data edit:
src/
domain/ Pure game data & rules (no framework)
units.ts Champion catalog + in-stage upgrades ← add champions here
enemies.ts Enemy catalog + bosses (BOSS_META) ← balance here
levels.ts Chapters, stages, paths/lanes & waves ← add stages here
endless.ts Endless-mode maps and foe pools
mastery.ts Permanent skill trees
armor.ts The adventurer's armor: slots, rarities, stats, sets
playerChampion.ts, playerSprite.ts, proficiency.ts The custom adventurer
rarity.ts Rarity ladder (Common and Rare are summonable)
targeting.ts Targeting strategies
combat.ts Crit rules
atmosphere.ts Stage moods (lighting, weather, grade)
decor.ts Board themes and props
grid.ts, journal.ts
application/ Use-cases / state
gameState.ts Central GameState model + pure transitions
summon.ts Summon roll (rarity weights)
store.tsx React binding: owns state, persists, exposes actions
infrastructure/
storage.ts The ONLY localStorage access point
engine/ Real-time battle simulation (framework-agnostic)
GameEngine.ts The tick loop: spawns, movement, combat, waves, win/lose
renderer.ts Canvas drawing (reads the engine; no rules)
sprites.ts, figure.ts, anatomy.ts Procedural figures + compositor
terrain.ts, props.ts, lighting.ts, vfx.ts, palette.ts, armorArt.ts
types.ts Runtime entity types
ui/ React screens, components and synthesized audio
App.tsx, screens/, components/
UNITS in domain/units.ts (including its
deployLimit, the per-stage placement cap). Give it a sprite in
engine/sprites.ts or it falls back to its emoji.ENEMIES in domain/enemies.ts; a stage boss is a
BOSS_META entry.domain/levels.ts (path or
lanes, and waves). Give it an optional theme, decor and mood to dress
it — open Settings → Developer → Level Designer to draw paths, stamp
props and preview moods, then paste the exported snippet into the spec.EndlessSpec in domain/endless.ts.available: true in domain/rarity.ts and author
champions of that rarity — the summon roll picks it up automatically.TARGETING_STRATEGIES in
domain/targeting.ts; add it to SELECTABLE_TARGETING to expose it in-stage.MASTERY_TREES entry in
domain/mastery.ts.domain/armor.ts: drop chances in
ARMOR_DROPS, rarity odds / stats per piece / salvage gems in
ARMOR_RARITIES, the rollable stats and their ranges in ARMOR_STATS, and
each set's piece names and 4-piece bonus in ARMOR_SETS (one per chapter via
section). Settings → Developer has an armor forge.All persistence flows through application/store.tsx + infrastructure/storage.ts.
Nothing in the UI touches localStorage or game rules directly.
A separate app in tools/editor/ for authoring levels, waves, enemies, bosses,
champions and skill trees. It is not part of the game: nothing in the game
links to it, and npm run build doesn't bundle it. It reuses the game's domain
catalogs, GameEngine, renderer and sprites, so what you edit is drawn and
simulated by the real code.
npm run editor # dev server at http://localhost:5175 (auto-bumps if taken)
npm run editor:build # type-check + static build into tools/editor/dist
npm run editor:preview # serve that build
npm run editor:check # headless self-check of validation, source export and packs
Workflow. The editor opens on the content shipped in src/domain. Edits go
into a working copy with undo/redo (Ctrl+Z, Ctrl+Shift+Z / Ctrl+Y). Save
(Ctrl+S) stores it as the draft in this browser's localStorage (under
aetheria-editor:; the game's aetheria-td: saves are never read or written),
and Discard goes back to the last save. The top bar shows whether there are
unsaved changes. Unsaved work is also kept in a recovery slot, so a closed tab
or crash offers to restore it on the next launch. If src/domain changes
after you saved a draft, the draft is carried onto the new content one entity
at a time. Your version wins where both changed, and a notice lists those
entities.
DISABLED_LEVEL_IDS) or delete stages.UnitDef field (only shapes that have a sprite),
in-stage upgrade tiers and forks, and derived DPS/max stats from the game's
own helpers. The three hero champions are read-only.Validation. Every change is validated by one module
(tools/editor/src/validation/validate.ts): errors, warnings and notes appear
inline at the field and in the Problems drawer, each saying what is wrong and
how to fix it. Click a problem to jump to it. Errors block content packs and
source export, but a backup can always be taken.
Test play. A level's Test play button runs it on the real
GameEngine with the draft content installed. You can set starting gold,
mastery (none or full), which heroes to bring and the RNG seed, then use
pause, 1–8× speed, step, skip to the end of the wave, restart and extra gold.
It never touches progression or game saves.
Content packs. Content Packs → Export writes a versioned JSON file:
{
"format": "aetheria-content-pack",
"version": 1,
"kind": "content", // or "backup": a draft as-is, errors and all
"scope": "full", // or "changes": only what differs from shipped
"createdAt": "2026-10-17T12:00:00.000Z",
"validation": { "errors": 0, "warnings": 1 },
"content": { "levels": [], "enemies": [], "bosses": [], "units": [], "mastery": {} },
"removed": ["enemy:cas_brute"], // changes only: shipped entities deleted
"order": { "levels": ["level:1"] } // changes only: a collection's order, when it changed
}
A content pack is only written when there are no errors. Import (file, drop
or paste) previews the result first. A full pack replaces the working copy or
is merged in; a changes pack is merged in. Fields the game doesn't read can be
stripped. An import is unsaved until you press Save, and Undo reverts it.
Older pack versions are upgraded on import.
Back into the game (source export). Content Packs → Generate source files
patches src/domain/levels.ts, enemies.ts, units.ts and mastery.ts in
memory. Only the changed entries are rewritten, in the files' own style, so
comments and formatting elsewhere survive and unchanged files come out
byte-identical. Review the per-file diff, then copy or download the files. On
the editor's dev server you can also press Write N files to src/domain.
This is an explicit, confirmed action: it saves your draft first, then the dev
server checks each file still matches the version the editor loaded (if one
changed it writes nothing and asks you to reload), then writes every changed
file or none. Only those four files can be written, and only from npm run editor
(never from a build or preview). Review the result with git diff, then run
npm run typecheck.
Reset. Content Packs → Reset to the shipped content throws the draft away after a confirmation that offers a backup download first.