fix: 准备阶段显示外勤医疗预备干员 (#315)
This commit is contained in:
1 parent
3b57f72704
commit
9db9f3ceea
9 files changed
+132
-15
No files matched your search
@@ -784,6 +784,10 @@ PREP ready/acting · COMBAT/boss combat/done · UNITE helping/done · others don
|
||||
`m.private` = DESIGN §8.3 exactly (sent per player whenever it changed). `nextEnemies` = the current round's wave
|
||||
(+ the player's bounty enemies, tag `bounty`; boss rounds: the player's boss field, tag `boss` — the leader's entry with its
|
||||
spawn tile `start`, where the boss-field prep shows it —, + its bounties).
|
||||
During PREP, `prepMapChars` carries the read-only 预备干员-医疗 UnitInfo for every living player's field when any living
|
||||
player holds 外勤医疗. It uses the stage's normal-board map-character slot; the own board draws it separately from
|
||||
`board` (so it cannot be moved or sold), and `prepFieldMeta` includes it for scouts. In a boss prep, the same slot is
|
||||
mapped to each player's half, including `bossMate.units`. Touch replaces this reserve medic only at battle start.
|
||||
|
||||
Bond layers in the views (DESIGN §20.15): from the end of COMBAT (`_finishCombat`, every normal result in) until SETTLE,
|
||||
`m.private bonds` and `m.public players[].bonds` add the finished battle's IN_BATTLE gains (`PlayerState.pendingLayerGains`
|
||||
|
||||
@@ -77,6 +77,6 @@ Target: a **real 3D board layer** rendered with three.js (vendored ESM, no CDN)
|
||||
- One camera model drives both layers: `public/js/render/projection.js` keeps its public API (`worldToScreen`, `screenToWorld`/tile picking, presets, transitions) but derives it from a three.js `PerspectiveCamera` initialised from the official configBlackBoard framing, so Spine units, FX, drag/drop and hit-testing stay aligned with the 3D board.
|
||||
- Must degrade gracefully: when local-client art or WebGL2 features are missing, fall back to the current 2D atlas board; keep 60 fps (static geometry merged into few draw calls; textures mipmapped; DPR-aware).
|
||||
- Implemented (`public/js/render/board3d/*`, `render/app.js`): three.js is downloaded only when the local-art manifest lists the board atlas; the built 3D area, the drawn 2D rows and the lit rect follow `viewKind(kind, {rect})` (the field the camera actually shows: a 'prep' camera on rows ≤ 6 = `bossPrep`); the background plane is extended with mirrored copies and the clear colour matches the fog (no visible edge at any aspect); battle crates live in their own group (survive scene rebuilds) and follow the board mode (`switchableBox`: 3D mesh ⇄ 2D box on a mode switch or a lost WebGL context); quality 'low' drops shadows; `?board=2d|3d` overrides. The direction wheel's range stripes sit between the board canvas and the unit canvas.
|
||||
- Done: Final Assault / Hidden Core prep takes place on your own half of the boss field (research 09 §1.2): pieces shown at boss-field positions (row − 7; right-hand player mirrored col → 20 − col, RIGHT ↔ LEFT), drop targets and wheel directions mapped back to board coordinates (`prepCamera` → `bossPrep` in public/js/ui/gameLogic.js; covered by the playtest2 browser tests). A scout of such a board (`m.field` prep of a boss round) comes in boss-field coordinates with the player's `side` and is framed the same way, the leader standing at its spawn (0.2.0, community report item 55). Both players of a pair are shown together, as in the battle (0.2.0, community report item 51): the partner's board stands on the other half (`m.private.bossMate` units, already in boss-field coordinates: mirrored on the right half, RIGHT ↔ LEFT; read-only `m:<uid>` views of `render/app.js syncMates`), the whole boss field is lit, a scout carries both halves (`mate`), and the team panel frames the pair's avatars in the official green (`ui/battle bg_team_border` tinted, a plain ring without the local art; `gameLogic teamFrameIds`) from the round's start through the fight.
|
||||
- Done: Final Assault / Hidden Core prep takes place on your own half of the boss field (research 09 §1.2): pieces shown at boss-field positions (row − 7; right-hand player mirrored col → 20 − col, RIGHT ↔ LEFT), drop targets and wheel directions mapped back to board coordinates (`prepCamera` → `bossPrep` in public/js/ui/gameLogic.js; covered by the playtest2 browser tests). A scout of such a board (`m.field` prep of a boss round) comes in boss-field coordinates with the player's `side` and is framed the same way, the leader standing at its spawn (0.2.0, community report item 55). Both players of a pair are shown together, as in the battle (0.2.0, community report item 51): the partner's board stands on the other half (`m.private.bossMate` units, already in boss-field coordinates: mirrored on the right half, RIGHT ↔ LEFT; read-only `m:<uid>` views of `render/app.js syncReadOnlyPrepUnits`), the whole boss field is lit, a scout carries both halves (`mate`), and the team panel frames the pair's avatars in the official green (`ui/battle bg_team_border` tinted, a plain ring without the local art; `gameLogic teamFrameIds`) from the round's start through the fight. With 外勤医疗 selected, every living player's own prep, paired boss prep and scouted prep shows a read-only 预备干员-医疗 on its stage slot; the strategy's Touch replacement occurs only when combat begins.
|
||||
|
||||
---
|
||||
@@ -18,7 +18,7 @@ Platform additions (implemented): `welcome.{resumed, version}`, `welcome.diyKitt
|
||||
C→S (all carry `rid`): `g.infoReady` · `g.band {bandId}` · `g.bandSkip` · `g.bandFocus {bandId?}` (the strategy highlighted in the draft screen — what a timed-out turn takes; absent / null clears it; `WRONG_PHASE` outside BAND_DRAFT, `ALREADY` after the pick, `BAD_TARGET` for a band not allowed; §18.2) · `g.buy {slot}` · `g.refresh` · `g.freeze` · `g.levelUp` · `g.sell {uid}` · `g.move {uid, to:{area:'board', row, col}|{area:'hand', idx}, dir?}` · `g.equip {itemUid, targetUid, replaceUid?}` (`replaceUid`: which equipped item a third one replaces, §6.2) · `g.art {itemUid, row, col, dir?}` (`dir` ∈ UP|RIGHT|DOWN|LEFT from the deploy wheel, §3; absent ⇒ `to.dir`, then RIGHT; `g.move` onto the piece's own tile re-orients it; board pieces in `m.private` / `m.field {prep:true}` carry `dir`) · `g.destroy {uid}` (hand / temp items only; equipped items are locked, §6.2) · `g.reward {idx}` · `g.choice {idx, choiceId?}` (with an ID: the recipient's PREP personal choice; without one: the public SP_DRAFT; stale or wrong IDs never fall through to the public draft) · `g.ready {ready}` · `g.emote {id}` · `g.watch {fieldId, playerId?}` (`playerId`: the player tapped — a shared 联防 / boss pair field shows two; what an eliminated viewer follows, §25) · `g.autoplay {on}` · `g.pause {on}` (solo battles only, §14 Solo pause) · `g.unitStats {seq?}` (ROUND_START / SP_DRAFT / PREP, else `WRONG_PHASE`; eliminated: `ELIMINATED`) → push `m.unitStats` (§18.5) · `g.leave`
|
||||
S→C:
|
||||
- `m.public` — full public state on every change (throttled ≤ 10/s): `{ phase, round, lastRound, deadline, serverNow, modeId, difficulty, stageId, factions, disabledBonds, bannedChess, bossId, hiddenBossId?, teamLp?, bossHp?:{hp,max}, overtimeAt? /* FA / HC: ms epoch the overtime drain starts, §4 */, paused /* solo pause, §14 */, draft?:{order,turn,picks,skipsLeft,turnDeadline,turnSeconds /* one turn's length, 0 untimed; deadline = turnDeadline (§18.2) */,untimed}, sp?:{family,cards /* bounty cards carry descRaw?, §20.6 */,turn,picks}, players:[{playerId, seat, name, isBot, connected, alive, lp, bandId, shopLevel, boardCount, ready, bonds:[{bondId,count,active,tier,layers,harmony? /* 调和's +1 is in count, §21.26 */}], fieldId, status, pendingLp? /* COMBAT / 联防 of a normal round: the LP the player's own battle will cost at settlement so far, min(lpCapPerRound, counted leaks) — in 联防 min(lpCapPerRound, uniteLeft) — omitted when 0 and in every other phase (§17.5 live LP) */, uniteLeft? /* 联防: the leaker's enemies still standing — not spawned yet + alive + counted leaks, uncapped, live (§20.6) */}] , fields:[{fieldId, kind, players:[playerId...], progress?:{killed, resolved, total, done} /* §14 顶栏胶囊: resolved = the field's own scheduled enemies knocked down or leaked, null while unknown */}] }` · `m.public.uniteResult` — SETTLE after a 联防 only, its outcome as data: `{ through /* the leakers' enemies that still got through, uncapped */, helpers:[playerId...], leakers:[playerId...], losses:{playerId: the LP settlement charged this round — every alive player} }`; absent when no 联防 resolved (the client's result box then reads the round's own battle; GitHub #235, PR #112)
|
||||
- `m.private` — the recipient's full PlayerState view (§6.2 minus server-only data) on every change; in a boss round's prep also `bossMate { playerId, side, units }` — the pair partner's board on its half of the boss field (UnitInfo, mirrored on the right half), re-sent when the partner moves (0.2.0, community report item 51).
|
||||
- `m.private` — the recipient's full PlayerState view (§6.2 minus server-only data) on every change; during prep, `prepMapChars` shows the 外勤医疗 reserve medic on the stage's pre-existing slot without adding a movable board piece; in a boss round's prep also `bossMate { playerId, side, units }` — the pair partner's board and reserve medic on its half of the boss field (UnitInfo, mirrored on the right half), re-sent when the partner moves (0.2.0, community report item 51).
|
||||
- `m.field` — field meta when watching starts: `{ fieldId, kind, rect, stageId, units: [UnitInfo], effects? }` (the units on the field, knocked-out operators waiting to redeploy included — `Battle.fieldMeta`, §18.3). The prep-scouting field (`Match.prepFieldMeta`) puts the scouted player's **hand (整备区) and 临时整备区 into `units` as well**: held pieces as units on the hand row (row 7, col = hand slot) and temp pieces on the temp row (row 8, cols 4..8 = the temp slots; PRTS 帮助 counts the temp area with the hand; a held item is a `kind: 'item'` unit the client draws as its floating plate) — the scout renders exactly like the own prep bench, for every watcher alike, the spectator seat's copy equal to a teammate's (§23.19's "never a private view" stays the no-`m.private` rule); the scout signature `_prepScoutSig` carries the hand / temp slots, so a piece moved to another slot is a change — and carries `effects`: the scouted player's effects view (策略 / 机变 / 悬赏 …), the watched player's effects column ready-made
|
||||
- NOTE: game time in `b.snap`/`b.ev` is carried as **`gt`** (the frame's `t` is the message type).
|
||||
- Active finite zero-SP timed skills display `sp=timeLeft`, `spMax=duration` and `UF.SKILL`; after end they display
|
||||
|
||||
@@ -1578,8 +1578,8 @@ Notes for the integrator
|
||||
|
||||
### 27.64 外勤医疗's pre-battle map character retains its earlier aggro when Touch replaces it — `content/bands/battle.js installMapChars`
|
||||
|
||||
- **Seen**: the user observes 预备干员-医疗 already visible on the field during preparation. With two elite operators,
|
||||
`battleStart` creates Touch only after all board units have deployed. This gives the sim unit the latest `aggroSeq`;
|
||||
- **Seen**: the [official recording](https://b23.tv/JZLqdSO) at about 4:25 shows 预备干员-医疗 on the field during preparation,
|
||||
while this client's preparation view did not show it. With two elite operators, `battleStart` creates Touch only after all board units have deployed. This gives the sim unit the latest `aggroSeq`;
|
||||
a local boss test makes “萨米的意志” select Touch's column first. In the
|
||||
[official recording](https://b23.tv/JZLqdSO) at about 4:57, Touch stands at the right edge while 冰凌 falls on a
|
||||
different column. The recording supports a mismatch, but does not by itself isolate every other target's taunt.
|
||||
@@ -1587,10 +1587,8 @@ Notes for the integrator
|
||||
describes a 预备干员-医疗 appearing on every player's field and being replaced by Touch at combat start if at least two
|
||||
elite operators stand there. PRTS 索敌 places the last deployed target ahead at equal taunt. [ASSUMED]: the
|
||||
replacement retains the map character's pre-deployment aggro position; the sources do not fix its exact numeric rank.
|
||||
- **Now**: only the 外勤医疗 map character (预备干员-医疗 or Touch) gets an early `aggroSeq` after its `battleStart`
|
||||
spawn. Its actual deployment event and `deploySeq` stay unchanged. Operators, ordinary initial summons and
|
||||
mid-battle summons retain their existing priority.
|
||||
- **Now**: in PREP, every living player's own board and scouted board shows a read-only 预备干员-医疗 from the stage's map-character slot when any living player holds 外勤医疗; a boss pair's two medics appear on the respective halves. The prep unit does not become a movable `board` piece. At `battleStart`, only the 外勤医疗 map character (预备干员-医疗 or its Touch replacement) gets an early `aggroSeq`. Its actual deployment event and `deploySeq` stay unchanged. Operators, ordinary initial summons and mid-battle summons retain their existing priority.
|
||||
- **Golden**: one value moves in `test/golden/matches.json`: `coop2-NORMAL-14-standins`, player `h_0`'s
|
||||
`dmgDealt` 3,000,543 → 2,999,081. That match fields 外勤医疗, so its changed initial target order can alter damage;
|
||||
every other recorded result is unchanged. **Tests**: `test/content/bands.test.js` checks both variants' priority and
|
||||
“萨米的意志” 冰凌 selection on solo and two-player boss fields.
|
||||
“萨米的意志” 冰凌 selection on solo and two-player boss fields; `test/match/touch-prep.test.js` checks own, scouted and paired boss preparation views; the render browser test checks that the medic is visible, mirrored and not draggable.
|
||||
Reference in new issue
Block a user