feedback5 WW: split DESIGN.md into docs/design/ (the current rules) and docs/history/ (the per-release revisions), section numbers kept — a pure move: every ## N. block (heading, text, the --- after it) moves verbatim, each part file only gains a title line and a pointer to the index; docs/DESIGN.md keeps its header and becomes the index (a § → file table). design/overview.md §0 §1 §2 §11 §12, geometry-time.md §3 §4, engine.md §5 §7, match.md §6 §16, network.md §8 §14, client.md §9 §10 §13 §15; history/v2.2-playtest3.md §17, v2.3-playtest4.md §18, v2.4-playtest5.md §19, v2.5-playtest6.md §20, 0.1.1.md §21, 0.1.2.md §22, 0.1.3.md §23, 0.1.4.md §24, 0.2.0.md §25. Round trip: the index header plus the blocks in § order reproduce the pre-split DESIGN.md byte for byte (813 785 bytes, sha256 fe81884c…). Tests: test/helpers/designDocs.js designText() (the split script's splitBlocks / designText) reassembles the text in § order; docs-consistency and feedback1-gaps read it instead of docs/DESIGN.md (every pin unchanged); docs-paths guards the split (each § exactly once in docs/design/ or docs/history/, the index row of every file names its sections, every "DESIGN §N(.M)" cited in the repository is a heading — 779 in 327 files today); package.test keeps docs/design/ and docs/history/ out of the zips; golden unchanged
This commit is contained in:
1 parent
0f9009954d
commit
da1f5830ca
21 files changed
+3517
-3325
No files matched your search
@@ -0,0 +1,159 @@
|
||||
# DESIGN §0, §1, §2, §11, §12 — Scope, stack, repository layout, quality bar
|
||||
|
||||
Part of [DESIGN.md](../DESIGN.md) (the index; section numbers are global).
|
||||
|
||||
## 0. Product scope (v1)
|
||||
|
||||
A faithful, polished, **online co-op** browser remake of Arknights' seasonal auto-chess TD mode 「卫戍协议:盟约」(下半, `act2autochess`).
|
||||
|
||||
In scope:
|
||||
- Title → lobby (nickname, create room / join by 4-letter code or `?room=CODE` link, host picks difficulty, up to 4 players, **AI teammates** can fill seats, ready/start). Solo (独立模拟) and co-op (同盟模拟, 1–4 humans + optional AI).
|
||||
- Difficulties: 标准 FUNNY, 险境 NORMAL, 绝境 HARD, 终极 ABYSS (solo and multi variants from `modeDataDict`). Training/tutorial mode is out of scope.
|
||||
- Pre-game: INFO_CHECK briefing (disabled bonds, banned operators, enemy factions, stage) → BAND_CHECK strategy draft (40 bands, random order, 1 skip in co-op) → BATTLE_CHECK.
|
||||
- Rounds 1–14 (+ hidden 15): prep (shop, hand, board, items, freeze, refresh, level-up, sell, merge + reward pick, ready), 机变 draft rounds, simultaneous auto-combat on each player's own field, 联防 unite phase, LP loss/elimination, Final Assault boss round with merged LP and shared boss HP pool, Hidden Core, settlement with titles.
|
||||
- Full content: 112 visible chess (+ elites), their default skills and talents, 23 bonds with layers, all 43 特质 (garrison) effect keys, 56 equipment + Arts, 40 bands, 机变 cards, enemy factions/special enemies, 10 bosses, stage devices/terrain (crates, blowers, mire, smog, deep sea, infection).
|
||||
- Rendering with the real Spine battle chibis (PixiJS 7 + pixi-spine 4), procedural tiles, VFX, damage numbers, real BGM/SFX, emotes, broadcast ticker.
|
||||
- Reconnect, AI take-over of disconnected players, robust validation of every client intent.
|
||||
|
||||
Out of scope v1: matchmaking queue, training/tutorial, DIY (甄选) slots (the 4 DIY chess are removed from the shared pool — 0.2.0 plays them as 自选编队: the picks of the 干员调配 overlay's 自选编队 tab, each sold only in its player's shop; DATA.md §18, shared/diy.js, server/match/player/diy.js), trophies/progression persistence, reporting.
|
||||
|
||||
---
|
||||
|
||||
## 1. Tech stack
|
||||
|
||||
- **Node.js ≥ 22** (CI: 22 and 24), ESM (`"type": "module"`), single dependency `ws@8`. No bundler, no TypeScript. JSDoc types where helpful.
|
||||
- **Server-authoritative simulation.** Clients send *intents*; the server validates, mutates state and pushes state/snapshots.
|
||||
- **Client:** static files, native ES modules. Vendored libs in `public/vendor/`: `pixi.min.js` (PixiJS **7.4.2** UMD, global `PIXI`), `pixi-spine.js` (**4.0.6** UMD, `PIXI.spine`), `preact.module.js` + `hooks.module.js` + `htm.module.js` (Preact 10 + htm, no build step). No CDN at runtime (LAN play must work offline).
|
||||
- **Shared code** in `shared/` is imported by both server and browser (pure ESM, no Node APIs).
|
||||
- Tests: `node --test` (`test/**/*.test.js`). Browser E2E: `puppeteer-core` driving the system Chrome (dev dependency, optional; puppeteer-core 25 is ESM-only and needs Node ≥ 22.12, so the suites load it with `await import`).
|
||||
|
||||
Run: `npm install && npm run assets && npm start` → `http://localhost:3000`. Friends on LAN use `http://<host-ip>:3000`. Internet play: a tunnel (e.g. `cloudflared tunnel --url http://localhost:3000`) or any Node host (Dockerfile provided).
|
||||
|
||||
---
|
||||
|
||||
## 2. Repository layout & ownership
|
||||
|
||||
```
|
||||
server/
|
||||
index.js process entry: startServer() wires server/http/ (plain node:http + ws); boots when run directly
|
||||
http/
|
||||
config.js ROOT, served dirs, env (PORT, HOST, TRUST_PROXY, DEBUG), options handed to net.js / lobby.js
|
||||
websocket.js session wiring (SessionRegistry → Lobby → Network), WebSocket upgrade at /ws (64 KB frames)
|
||||
static.js mounts / → public, /data/, /shared/, /sim/ (.js only), the /data.js browser stand-in, path guards
|
||||
media.js /media/… extension-less audio → public/assets/audio
|
||||
files.js one file → response: MIME, gzip (.skel/.atlas/.json/.js/.css …), ETag / 304, cache policy, ranges
|
||||
buildTag.js build tag of the served browser runtime (/healthz build, public/js/ui/buildGuard.js)
|
||||
routes.js request listener: 414 / 400 / 405 guards, GET /healthz, then static files
|
||||
common.js security headers, URL split, error page, JSON replies, bare 400 for unparseable requests
|
||||
boot.js boot banner (Local / LAN / tunnel URLs), port-in-use hint, graceful SIGINT / SIGTERM
|
||||
net.js session registry, send helpers, per-connection rate limit, message validation (uses shared/protocol.js)
|
||||
lobby.js rooms (4-letter codes), seats, host, AI seats, ready/start, reconnect tokens, room→Match wiring
|
||||
data.js loads data/*.json once, builds indexes (getChess, getBond, …); frozen objects
|
||||
match/
|
||||
Match.js match state machine, timers, round loop, co-op orchestration, broadcasting views — the class:
|
||||
the lobby⇄match interface (header), constructor + method install
|
||||
match/ Match's methods by concern (platform, infra, messaging, views, watch, intents, pause, phases,
|
||||
spDraft, prep, combat, clientCombat, reports, unitePhase, bossRounds, settle; shared constants
|
||||
in common.js), installed on Match.prototype
|
||||
PlayerState.js per-player economy/shop/hand/board/items/bonds/LP state + all prep-intent handlers — the class:
|
||||
constructor + method install
|
||||
player/ PlayerState's methods by concern (basics, pieces, acquire, economy, placement, items, prep,
|
||||
round, views; common.js), installed on PlayerState.prototype
|
||||
pool.js SharedPool (copies per base chess), banned/disabled bonds, odds & rolls
|
||||
board.js placement legality, deploy cap, hand/temp management, merge detection & execution
|
||||
bondsMeta.js bond member counting (BOARD / BOARD_AND_DECK / golden), activation tiers, persistent layers
|
||||
effectsMeta.js registry + dispatcher of prep-phase ("SERVER_*") effects: garrisons, bands, items, bonds
|
||||
choices.js 机变 draft: card generation per family, pick order, timeouts, application
|
||||
waves.js match enemy factions, per-round spawn list generation, stat scaling, bounties
|
||||
unite.js 联防 helper selection, leaked-enemy aggregation, LP attribution
|
||||
finalAssault.js R14/R15 pairing, merged LP, shared boss HP pool, overtime drain
|
||||
results.js settlement stats, titles (评语)
|
||||
bot.js AI player (plays prep phases; also auto-play for disconnected humans)
|
||||
sim/
|
||||
Battle.js one field simulation (normal / unite / boss); public API §5 — the class: constructor + method install
|
||||
battle/ Battle's methods by concern (players, lifecycle, hooks, spawns, deploy, blocking, status, combat,
|
||||
queries, summons, tiles, displacement, economy, events), installed on Battle.prototype
|
||||
constants.js TICK, conversions, tuning knobs
|
||||
rng.js seeded PRNG (mulberry32) + helpers
|
||||
grid.js field grid, tile queries, passability, official 4-direction SPFA flow field + line-of-sight smoothing, road-over-floor preference only where it crosses fewer floor tiles (§21.15); DEPLOY_REFUSED_TILES (深水区, §21.3)
|
||||
units.js Unit / Operator / Enemy / Token classes, stat aggregation
|
||||
buffs.js Buff model, status catalogue (stun, freeze, cold, sleep, slow, fragile, stealth, …)
|
||||
damage.js damage & heal pipeline, element gauges
|
||||
targeting.js target selection helpers, range tests, priorities
|
||||
body.js hit areas of huge enemies: the one helper behind every range test on enemies (§3, §19.4)
|
||||
projectiles.js projectile flight/impact
|
||||
skills.js skill runtime: SP, triggers, durations, ammo, charges; interprets SkillSpec
|
||||
ai.js enemy movement/attack AI, operator attack loop
|
||||
fear.js 恐惧 movement of enemies (reachable-tile fan, random checkpoints, self-fear flutter; §20.4)
|
||||
snapshot.js compact serialization for clients
|
||||
content/
|
||||
index.js installs all content into a Battle (kits, bonds, garrisons, items, bands, enemies, devices)
|
||||
kits/index.js operator kit registry keyed by base chessId (guide: kits/README.md)
|
||||
kits/ops/*.js one operator kit (skills + talents) per file, <chessId>-<codename>.js
|
||||
kits/shared/*.js helpers the kits share (shared/tier1.js: the general ones)
|
||||
tokens.js summon/token definitions
|
||||
bonds.js battle side of the 23 bonds
|
||||
garrisons.js battle side (IN_BATTLE) garrison effect keys
|
||||
items.js battle side of equipment + Arts
|
||||
bands.js battle side of band (strategy) effects
|
||||
enemies.js enemy ability specs + generic special-type behaviours: dispatch hooks, KITS, STATS_ONLY
|
||||
enemies/ helpers.js, archetypes.js, one kit file per special type (invisible, times, element, dot,
|
||||
reflection, fly, special) and leaders.js — each with its part of KITS
|
||||
bosses.js boss scripts (boss_1 … boss_10)
|
||||
devices.js stage devices & special terrain
|
||||
shared/
|
||||
constants.js enums, phases, areas, error codes, UI-relevant constants
|
||||
protocol.js message catalogue + validators (§8)
|
||||
format.js rich-text description → HTML/plain; number formatting
|
||||
rules.js pure helpers shared by UI & server (e.g. price display, sell value, bond tier from count)
|
||||
data/ generated by tools/build-data.mjs — committed; see docs/DATA.md
|
||||
public/
|
||||
index.html single page; loads /js/main.js as module
|
||||
css/ theme.css (tokens, fonts), components.css, screens/*.css
|
||||
js/
|
||||
main.js boot, router between screens, global store
|
||||
net.js WebSocket client, reconnect, request/response helpers
|
||||
store.js tiny observable store (state from server + local UI state)
|
||||
data.js fetches /data/*.json, same indexes as server/data.js
|
||||
audio.js BGM/SFX manager (Web Audio), volume settings
|
||||
assets.js asset URLs, image cache, Spine loader with LRU + fallback
|
||||
screens/ title.js, lobby.js, room.js, briefing.js, bandDraft.js, game.js (entry; game/*.js plates, marks, early events), result.js
|
||||
ui/ hud.js, bondStrip.js, bondPopup.js, shopBar.js, teamPanel.js, detailPanel.js,
|
||||
tooltip.js, choiceOverlay.js, rewardOverlay.js, ticker.js, emotes.js, settings.js, toasts.js,
|
||||
gameLogic.js (barrel; gameLogic/*.js placement, bonds, phases, shop, loadout)
|
||||
render/ app.js (field view; app/*.js helpers), projection.js, tiles.js, units.js, spine.js, fx.js (effects; fx/*.js), interp.js, drag.js, pick.js, promote.js (API §9)
|
||||
vendor/ pixi, pixi-spine, preact, hooks, htm (copied from node_modules by tools/vendor.mjs)
|
||||
assets/ downloaded art/audio (git-ignored), see docs/ASSETS.md
|
||||
fonts/ self-hosted fonts
|
||||
tools/
|
||||
build-data.mjs official data + research JSON → data/*.json
|
||||
fetch-assets.mjs downloads/optimizes assets → public/assets + data/assets.json
|
||||
vendor.mjs copies vendor libs from node_modules → public/vendor
|
||||
test/ node:test suites; test/e2e/ browser + bot tests
|
||||
docs/ DESIGN.md (this), DATA.md, SIM.md, META.md, ASSETS.md, BALANCE.md, DEPLOY.md, PLAYING.md, research/
|
||||
(the wire protocol is normative in shared/protocol.js itself)
|
||||
LICENSE GPL-3.0-or-later (the project's code); NOTICE.md: scope, non-commercial game assets, the Spine
|
||||
Runtimes linking permission; THIRD-PARTY-NOTICES.md: libraries, fonts, data sources
|
||||
```
|
||||
|
||||
Ownership rule for parallel agents: **only edit files assigned to you**; if you need a change in someone else's file, write it in your final report instead (the integrator applies it). Shared files (`shared/*`, `docs/DESIGN.md`) are owned by the architect.
|
||||
|
||||
---
|
||||
|
||||
## 11. Quality bar & testing
|
||||
|
||||
- **No crash paths**: every server handler guarded; a thrown error inside a Battle tick for one field must not kill the match (log, force-end that battle as timeout, continue). Invariants asserted in tests: no NaN/Infinity in any unit field; hp ∈ [0, maxHp]; positions inside rect; battles terminate within `timeLimit + 1 s` (boss: terminate by pool/force); pool copy counts never negative and never exceed caps; funds never negative; hand/temp sizes respected.
|
||||
- Unit tests per module; content tests per effect; **full-match bot tests**: 1/2/4-player matches of every difficulty with fixed seeds run to RESULT headlessly (fast-forward: no real-time pacing) with zero errors; protocol fuzz (random/invalid intents) never crashes and never corrupts state.
|
||||
- **Browser E2E** (puppeteer-core + system Chrome): open 2 tabs, create/join room, add AI, play through prep/combat of several rounds, zero console errors, screenshots saved to `test/e2e/out/` for visual review.
|
||||
- Performance: a normal battle tick with 60 enemies + 10 ops < 0.5 ms; 4 parallel fields at 2× real time < 15% CPU of one core; the heavy 2-player boss field (18 ops, ~130 enemies alive, `test/sim/robustness.test.js`) < 0.5 ms per tick on a development machine, the best of three runs of the same battle — 1.0 ms when `process.env.CI` is set (GitHub's shared windows runners measured 0.51–0.58 ms and failed the 0.5 ms bar on PR #10, #12, #14 and master's own push; a real regression still shows locally).
|
||||
|
||||
---
|
||||
|
||||
## 12. Implementation phases
|
||||
|
||||
1. **Foundation**: build-data, fetch-assets/vendor, server platform (http/ws/lobby), client shell (screens up to room).
|
||||
2. **Core**: sim engine + harness; match/meta; render engine; game UI.
|
||||
3. **Content**: kits, bonds, garrisons, items, bands, enemies/bosses/devices, choices.
|
||||
4. **Integration & QA**: wire everything, bots, E2E, balance pass, bug-hunt workflows until dry, visual polish.
|
||||
|
||||
---
|
||||
Reference in new issue
Block a user