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。 以下为已保存的摘录。
An army list builder for a custom Warhammer 40,000 / Horus Heresy ruleset, covering 19 factions with full unit data, weapons, options, archetypes, legacies, traits and psychic disciplines.查看完整 README ↗An army list builder for a custom Warhammer 40,000 / Horus Heresy ruleset, covering 19 factions with full unit data, weapons, options, archetypes, legacies, traits and psychic disciplines.
React · TypeScript · Vite · Zustand · Tailwind CSS
git clone https://github.com/Rigzar/custom40k-builder.git
cd custom40k-builder
npm install
npm run build # verify — must pass with zero TypeScript errors
npm run preview # serve the built app at http://localhost:4173
Do not use
npm run dev— always usenpm run buildto verify correctness andnpm run previewto test the built output.
custom40k-builder/
├── src/
│ ├── components/ # React UI components
│ │ ├── LandingPage.tsx # Faction selection + Developer Update announcement
│ │ ├── UnitCard.tsx # Individual unit card (options, armory, traits)
│ │ ├── ArmyConfig.tsx # Archetype / Legacy / Traits panel
│ │ ├── ArmoryModal.tsx # Armory item picker
│ │ └── PrintView.tsx # Printable army sheet
│ │
│ ├── engine/ # Game logic (points, validation, rules)
│ │ ├── points.ts # Points calculation — base cost + options + traits + armory
│ │ ├── resolver.ts # Unit profile resolution — marks, variants, archetypes, FACTION_RESOLVERS dispatch
│ │ ├── validators.ts # Army-wide validation — slot limits, archetype constraints, engagement limits
│ │ ├── keywords.ts # Keyword-derivation seam — armour/mark gating, Ordo/Legacy unlock helpers
│ │ ├── equipMods.ts # Parses stat/ability text out of an armory item's description
│ │ ├── transportGate.ts # "Low Move, no transport" selection-gate helper
│ │ ├── archetypes/ # Archetype rule registry — base.ts (ArchetypeRule shape) + index.ts (all archetypes) + per-faction subfolders
│ │ ├── codex_<faction>/ # Per-faction engine module (one per faction): keywords, legacies, traits, slots, special-abilities, weapon-abilities, resolver, digest.md
│ │ ├── legacies/ # Legacy-gated discipline/prayer rules (index.ts dispatcher + per-faction files)
│ │ ├── resolvers/ # FACTION_RESOLVERS implementations (injected abilities per faction)
│ │ ├── traits/ # Per-faction Army Trait effect tables
│ │ └── validators/ # Per-faction validation add-ons
│ │
│ ├── data/
│ │ ├── loaders.ts # Assembles FactionData from per-faction folder files
│ │ ├── changelog.ts # Version history (English)
│ │ ├── known-issues.ts # Bug and limitation tracker
│ │ └── rules-model/ # Per-faction keyword/rules digest (<faction>.md) — audit reference, not loaded by the app
│ │
│ ├── store/
│ │ └── army.ts # Zustand store — army state and actions
│ │
│ ├── types/
│ │ ├── data.ts # Unit, Weapon, OptionGroup, FactionData, OptionEffect…
│ │ ├── army.ts # RosterEntry, ArmyState, TraitSelection…
│ │ ├── unit-types.ts # Canonical UnitType union (Infantry, Bike, Jump Pack Infantry…)
│ │ └── keywords.ts # ChaosMark, ArmourKeyword, MARK_GLYPHS typed constants
│ │
│ └── i18n/
│ └── index.ts # UI translation strings (EN / DE / ES)
│
├── data/
│ ├── parsed/ # ← FACTION DATA — one folder per faction (all 19 factions)
│ │ ├── chaos_space_marines/
│ │ │ ├── units/ # one .ts file per unit, by slot
│ │ │ │ ├── hq/ troops/ elites/ ...
│ │ │ │ └── index.ts # assembles { faction, slot_to_units, units }
│ │ │ ├── armory/
│ │ │ │ ├── general.json # General armory
│ │ │ │ ├── mark_khorne.json
│ │ │ │ └── legion_*.json # One per legacy (Black Legion, Iron Warriors…)
│ │ │ ├── psychic/
│ │ │ │ ├── pacts.json
│ │ │ │ └── prayers.json
│ │ │ ├── archetypes.json # { archetypes[], legacies[], traits[] }
│ │ │ └── animosity.json # { animosity, allied } — only CSM/CD (marks)
│ │ ├── space_marines/ (same structure, no marks)
│ │ ├── chaos_daemons/
│ │ ├── ... (16 more factions, same units/ + armory/ layout)
│ │ ├── _supplements/ # horus_heresy.json, escalation/…
│ │ └── _scratch/ # Parser audit files — never loaded by the app
│ │
│ └── source/ # Original HTML source files used for auditing
│ ├── Chaos Space Marines ENG/
│ └── ... (one folder per faction)
│
├── Codex/ # Canonical rules sources (not pushed) — Core Rules/Missions .txt, one .ods per faction
│
└── scripts/
├── _html2txt.cjs # Strip HTML source to readable text for auditing
├── _scan_mojibake.cjs # Detect encoding artifacts in JSON/TS files
├── _normalize_unit_types.cjs # Cross-faction unit_type casing normalizer
└── _scan_cost_bugs.cjs # Scan for pricing bugs (per-model, fill-model)
Each faction lives in data/parsed/<faction>/. The loader in src/data/loaders.ts assembles the
individual files into the FactionData shape the engine expects.
units.json — core unit data:
{
"faction": "chaos_space_marines",
"slot_to_units": { "HQ": ["Chaos Lord", ...], "Troops": [...] },
"units": {
"Chaos Lord": {
"name": "Chaos Lord",
"slot": "HQ",
"models": [{ "name": "Chaos Lord", "points": 65, "min": 1, "max": 1, "stats": { "M": "6\"", "WS": "2+", ... } }],
"equipped_with": "Every model is equipped with: Bolt pistol; Power sword.",
"weapons": [...],
"option_groups": [
{
"header": "May replace Power sword with:",
"constraint": { "type": "one" },
"choices": [{ "name": "Power fist", "points": 10 }],
"inline_pts": null,
"per_model": false,
"variant_link": null,
"is_unique_per_army": false
}
],
"abilities": ["..."],
"unit_type": "Character Model",
"is_character": true,
"is_vehicle": false,
"is_psyker": false,
"champion_has_armory": false,
"has_veteran_abilities": true
}
}
}
Every faction uses a
units/folder, not a singleunits.json— the JSON shape above shows the logical structure, but on disk it's split into one.tsfile per unit (units/<slot>/<unit>.ts, each exporting aUnit), with per-slotindex.tsfiles that the faction's top-levelunits/index.tsassembles into the same{ faction, slot_to_units, units }shape. The unit object fields are identical — only the on-disk layout differs.
archetypes.json — Army Customisation data:
{
"archetypes": [{ "name": "Legionnaire Warband", "desc": "Full canonical rule text..." }],
"legacies": [{ "name": "Legacy of the Warmaster", "desc": "...", "armory_key": "Black Legion" }],
"traits": [{ "name": "Siege Experts", "desc": "...", "pts_unit": "5", "pts_char": "0", "pts_monster": "5", "pts_veh": "5" }]
}
armory_keymust match the key insrc/data/loaders.tsfor that faction'sarmory_legionsobject. If they diverge the legacy armory tab will not appear.
| Field | Type | Notes |
|---|---|---|
unit_type | string | Must use Core Rules canonical spelling — see src/types/unit-types.ts |
option_groups[].per_model | boolean | Set true when header says "for +X points per model" (inline options) |
option_groups[].effect | OptionEffect | Structured rule change: stat_mod, adds_unit_types, set_unit_type, grants_abilities |
armourKeyword | string | Innate armour class — drives ᵀ/ᴳ armory gating |
pts_monster / pts_veh | string | Shared "Monstrous Creatures & Vehicles" column — must be equal |
⚠️ This workflow is Alpha. The goal is to automate it, but it is not yet fully automatic.
The maintainer can review faction data directly from the canonical Google Sheets source by pasting the sheet URL. The process:
Codex/core_rules_text.txt, missions_text.txt) before a fix is appliedIf you have the source data for an unaudited faction, open a GitHub Issue with the link or paste the data — that immediately unblocks a full audit. See OPEN_QUESTIONS.md for the current list of open items.
Each archetype, trait, and legacy in the engine files has the canonical rule text as a comment directly above its code block:
// SOURCE — Siege Experts:
// "The unit gains the 'Sunder(1)' ability for all ranged attacks."
// COST: 5 | 0 | 5
'Siege Experts': [
{ type: 'weapon_ability', name: 'Sunder(1)', weapon_type: 'ranged', applies_to: 'all' },
],
This makes it immediately visible if the code drifts from the rule. When the two diverge, fix the code — not the comment.
See CONTRIBUTING.md for the full guide. Short version:
npm installnpm run build — must pass with zero TypeScript errorsOpen data/parsed/<faction>/units/<slot>/<unit>.ts, compare it against your copy of the rules, and fix whatever is wrong. See CONTRIBUTING.md for the fields to check and the file templates for adding missing armory/psychic/archetype files.
If you find an error but are not sure how to fix the JSON, open a GitHub Issue using the Data correction template.
If you know the game and can answer an ambiguous rule, check OPEN_QUESTIONS.md — answering a rules question directly unblocks an engine implementation. Use the Rules question issue template on GitHub.
src/engine/ — see CONTRIBUTING.md for the engine file mapsrc/components/src/store/army.tsnpm run build is the gateSee the Changelog button in the app, or read src/data/changelog.ts / src/data/known-issues.ts directly.
Open questions and help-wanted items: OPEN_QUESTIONS.md.