Playtest #4 fixes: tile picking, knocked-down state, element gauges, draft timer, live stats, shop items, drones, 赫默, 炎佑, UI flash
- #1 picking by the tile under the pointer (prep, battle, pen, equipment drops); the dragged model stays under the pointer (mouse and touch); pixel probe removed; phone ghost-click on the underframe fixed - #2 two-tap confirm on 机变 cards (like the shop) - #3 a 同盟 room with one human is untimed like 独立模拟 (no countdown outside battles) - #4 strategy draft: one synced countdown, 30 s per human turn, timeout takes the highlighted band - #5 special 维式重锤 (4) and 突变细胞 no longer sold; item card shows where they come from - #6 operator skill-mode sounds no longer play on normal attacks (纯烬艾雅法拉 and 55 others) - #7 detail card shows live battle stats and the prep-time battle-start stats (g.unitStats) - #8 official element gauges on units; burst cooldown (阻回) per PRTS - #9 knocked-down operators stay on their tile with a redeploy countdown ring - #10 drone behaviour per PRTS (暴鸰, 先兆者 shells, 黑云, 萨科塔之眼) - #11 赫默's drone only from her skill; placeable summons limited to talent tokens - #12 炎佑 stays where it is with no enemies; 祛恶之焰 as a continuous channel - #13 the occasional giant bond icon (loading texture scaled ×46) fixed - docs (DESIGN §18), recordings, data and tests updated (2467 unit tests, all browser suites green)
This commit is contained in:
1 parent
0aba602ecf
commit
d4f868cd19
107 files changed
+4510
-1653
No files matched your search
+21
-12
@@ -31,7 +31,7 @@ server/match/
|
||||
## 1. Match flow
|
||||
|
||||
```
|
||||
LOBBY → INFO_CHECK (co-op 25 s, solo untimed; all humans confirmed ⇒ next) → BAND_DRAFT → BATTLE_CHECK (3 s)
|
||||
LOBBY → INFO_CHECK (co-op 25 s; solo and single-human matches untimed; all humans confirmed ⇒ next) → BAND_DRAFT → BATTLE_CHECK (3 s)
|
||||
→ for r = 1..lastRound (+ hidden):
|
||||
ROUND_START (2 s) income + pending coins, upgrade price −1 (r > 1, floor 0), temp NOT wiped (what overflowed after
|
||||
the last prep's deadline — battle-result grants, a SETTLE merge's elite, returned equipment — is
|
||||
@@ -40,7 +40,7 @@ LOBBY → INFO_CHECK (co-op 25 s, solo untimed; all humans confirmed ⇒ next)
|
||||
shop reroll (frozen slots kept in place, then unfrozen), the round's wave generated (preview),
|
||||
onRoundStart dispatch
|
||||
[SP_DRAFT] r ∈ modes[m].spRounds (机变)
|
||||
PREP onPrepStart; co-op timer rounds[r].prepTime, solo untimed; ends when every alive seat is ready
|
||||
PREP onPrepStart; co-op timer rounds[r].prepTime, solo / single human untimed; ends when every alive seat is ready
|
||||
(prep end) onPrepEnd, the temp pieces due at this prep resolved (tempDue; what arrived after Ready or
|
||||
during onPrepEnd waits for the next prep), reward offers expire, unfrozen shop cleared, funds lost
|
||||
(band_cannot keeps them), boss round: Σ activated layers recorded for the hidden-core check
|
||||
@@ -54,28 +54,37 @@ LOBBY → INFO_CHECK (co-op 25 s, solo untimed; all humans confirmed ⇒ next)
|
||||
| Timer (real s, × `opts.timerScale`) | Value |
|
||||
|---|---|
|
||||
| INFO_CHECK | `config.timers.infoCheck` 25 |
|
||||
| band draft turn / whole step | `bandTurn` 12 / `bandDraft` 50 (co-op; solo untimed) |
|
||||
| band draft turn | `Match.BAND_TURN_SECONDS` 30 [ASSUMED] (= `timers.bandTurn`), the step's only countdown: `m.public.deadline` = the current turn's end, no step cap (`timers.bandDraft` 50 = the official whole step, informational) (co-op; solo / single human untimed) |
|
||||
| BATTLE_CHECK | `battleCheck` 3 |
|
||||
| 机变 first / other pickers | `spFirst` 30 / `spTurn` 16 (co-op; solo untimed) |
|
||||
| PREP | `modes[m].rounds[r].prepTime` (co-op; solo untimed) |
|
||||
| 机变 first / other pickers | `spFirst` 30 / `spTurn` 16 (co-op; solo / single human untimed) |
|
||||
| PREP | `modes[m].rounds[r].prepTime` (co-op; solo / single human untimed) |
|
||||
| COMBAT / 联防 | `modes[m].rounds[r].combatTimeLimit` (= the level's `maxPlayTime`) real seconds = 2× that in game seconds |
|
||||
| 最终攻势 / 隐秘核心 | no hard stop: countdown `rounds[r].levelMaxPlayTime` 120 real s; overtime drain from `bossOvertimeAfter` 150 real s, 1 team LP per real s (§3) |
|
||||
| ROUND_START / after combat / SETTLE | 2 / 1.5 / 3 (presentation delays, `Match.DELAYS`) |
|
||||
| bot action delay | 0.9 s (+0.35 s per seat) |
|
||||
|
||||
A match with a single human seat at the start (独立模拟, or a 同盟 room started alone / with AI teammates only:
|
||||
`Match.loneHuman` → `Match.soloUntimed`, user playtest #4 item 3) times nothing outside its battles: no INFO_CHECK /
|
||||
band draft / 机变 / PREP deadline, and BATTLE_CHECK / ROUND_START / SETTLE run silently (deadline 0); the co-op rules
|
||||
(draft order and skip, 6 机变 cards, 联防) stay.
|
||||
`m.public.deadline` is the absolute end of the current timer (ms epoch, 0 = untimed; combat: estimated end at 2×;
|
||||
最终攻势 / 隐秘核心: the boss level's `levelMaxPlayTime` countdown, 120 real s — the battle goes on past it — with
|
||||
`m.public.overtimeAt` = when the overtime drain starts, 150 real s; both on the field clock).
|
||||
|
||||
### 1.1 Band draft
|
||||
Co-op: random order (all seats, bots included), one pick per turn, 12 s per turn (timeout ⇒ `bandDraft.timeoutBandId`
|
||||
华法琳), 50 s cap for the whole step (every unpicked seat gets 华法琳), one skip per player (`g.bandSkip`: the player moves to
|
||||
the end of the order; refused when nobody is left to pass to), duplicates allowed, band must list the mode type in
|
||||
`modeTypeList`. Solo: free pick, no timer, no skip. Starting LP = `bands[id].totalHp`.
|
||||
Co-op: random order (all seats, bots included), one pick per turn, ONE countdown: `BAND_TURN_SECONDS` 30 s per turn,
|
||||
published as `m.public.deadline` (= `draft.turnDeadline`; `draft.turnSeconds` its length) — no step cap; AI seats pick
|
||||
at once. A turn that runs out takes the strategy the player highlights in the draft screen (`g.bandFocus {bandId?}`,
|
||||
`Match.timeoutBand`) while it is allowed and no teammate holds it, else `bandDraft.timeoutBandId` 华法琳, else the first
|
||||
free strategy by sortId (`defaultBand`; a departing seat gets `defaultBand` too). One skip per player (`g.bandSkip`: the
|
||||
player moves to the end of the order; refused when nobody is left to pass to); a strategy a teammate already took is
|
||||
refused (队友已选); band must list the mode type in `modeTypeList`. A single human (co-op with AI teammates only):
|
||||
untimed. Solo: free pick, no timer, no skip. Starting LP = `bands[id].totalHp`.
|
||||
|
||||
### 1.2 机变 (SP draft)
|
||||
Family = weighted pick from `choices.schedule[modeId].rounds[r].families`; cards: co-op 6 shared (each player takes 1,
|
||||
random order, 30 s first / 16 s others, timeout ⇒ a random remaining card), solo 3 untimed. A 驰援 tactic card
|
||||
random order, 30 s first / 16 s others, timeout ⇒ a random remaining card), solo 3; solo and single-human drafts are
|
||||
untimed. The UI picks a card with two taps (select → 确认选择, DESIGN §18.2). A 驰援 tactic card
|
||||
(`single_special_choice_gain_bond_chess`) is only offered while its bond still has chess in this match's pool
|
||||
(`Match.bondInPool`; a bond whose every member is banned would grant nothing); more generally a 驰援 or 盟誓
|
||||
(`global_special_choice_bond_addlayer`) card is offered only while one of its bonds is live (`Match.bondLive`: not in
|
||||
@@ -197,7 +206,7 @@ Every handler method is `(ctx, ev)`; `ev` is shared by all handlers of one dispa
|
||||
| `onMerge` | chess or item merge | `{ kind, piece, baseId|itemId, consumed:[uid] }` |
|
||||
| `onLevelUp` | shop level up | `{ level, price }` |
|
||||
| `onSpend` | a payment's action is complete (buy / refresh / levelUp / reward / effect) | `{ amount, reason, total }` (`total` = funds spent this match) |
|
||||
| `onBattleStart` | a battle input is built (normal / unite / boss / hidden) | `{ input: PlayerBattleInput, kind, round }` — mutate/replace `ev.input` |
|
||||
| `onBattleStart` | a battle input is built (normal / unite / boss / hidden); also for the stats preview (`g.unitStats`, DESIGN §18.5) | `{ input: PlayerBattleInput, kind, round, spawns?, preview? }` — mutate/replace `ev.input`. With `ev.preview` true (no `ev.spawns`) a handler must NOT change match state or draw match rng: the preview only reads the input |
|
||||
| `onBattleResult` | SETTLE (normal rounds) and after boss fields | `{ result, lpLoss, perfect, unite?|boss? }` |
|
||||
| `onChoicePick` | a 机变 card was applied (the card's own `choice:` handler runs first, then every source observes) | `{ card, family, picker, forTeammate }` |
|
||||
| `onEquip` | `g.equip` (item handler only) | `{ item, target, golden, consumed, keep, error }` — see §2.5 |
|
||||
@@ -429,7 +438,7 @@ who sent in its parent, ≤ the parents' data offspring count (磨砻 2, 烹泉
|
||||
drawn set; `disabledBonds` = drawn ∪ the mode's static list), `hiddenBossId`, `bossRound`, `hiddenRound`, `spRound`,
|
||||
`combatMode` (`'client'` | `'server'`), `fields[].progress { killed, total, done }` (teammates' progress UI), `paused`
|
||||
(solo pause, §1.3a),
|
||||
`players[].autoplay`, and per phase: `draft { order, turn, picks, skipsLeft, turnDeadline, untimed }` (BAND_DRAFT),
|
||||
`players[].autoplay`, and per phase: `draft { order, turn, picks, skipsLeft, turnDeadline, turnSeconds, untimed }` (BAND_DRAFT),
|
||||
`sp { family, name, desc, eventId, cards:[{ idx, kind:'bounty'|'item'|'tactic', id, name, desc, tier, coin?, payout?,
|
||||
rounds?, enemyKey?, count?, price?, team?, tacticKind? }], order, turn, picks:{pid: idx}, taken:{idx: pid}, untimed }`
|
||||
(SP_DRAFT), `teamLp` / `bossHp {hp,max}` (Final Assault on), `overtimeAt` (最终攻势 / 隐秘核心: ms epoch when the
|
||||
|
||||
Reference in new issue
Block a user