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:
sganggs committed 2026-09-30 05:35:45 +08:00
1 parent 0aba602ecf
commit d4f868cd19
107 files changed
+4510 -1653

No files matched your search

+14 -6
View File
@@ -256,7 +256,8 @@ test('solo: no deadline outside combat (INFO_CHECK, strategy draft, 机变, prep
});
test('co-op keeps its guards: INFO_CHECK 25 s, prep timer, transition deadlines', () => {
const h = makeMatch({ mode: 'coop', humans: 1, bots: 1, seed: 12 }).start();
// two humans: a single human is untimed like solo (Match.soloUntimed, user playtest #4 item 3)
const h = makeMatch({ mode: 'coop', humans: 2, bots: 1, seed: 12 }).start();
const m = h.m;
const pub = h.lastBc('m.public');
assert.equal(pub.phase, PHASE.INFO_CHECK);
@@ -268,13 +269,15 @@ test('co-op keeps its guards: INFO_CHECK 25 s, prep timer, transition deadlines'
// ---- review fixes -----------------------------------------------------------------------------------------------------------
test('a skill summon comes only with that skill: 赫默 S2 (default) gets the 医疗无人机 token, 赫默 on S1 does not', async () => {
test('a skill summon is never a hand card (user playtest #4): 赫默 on any skill gets no 医疗无人机 token in the prep phase', async () => {
const { give } = await import('./harness.js');
const SILENCE = 'chess_char_2_02_a';
const DRONE = 'token_10000_silent_healrb';
assert.equal(chess(SILENCE).skills.find((s) => s.overrideTokenKey === DRONE)?.isDefault, true, 'fixture: the drone is the default S2 summon');
const tokensIn = (ps) => [...ps.hand, ...ps.temp, ...ps.board.values()].filter((p) => p && p.kind === 'token' && p.id === DRONE);
for (const [skill, want] of [[null, 1], [0, 0], [1, 1]]) {
// "获得一个医疗无人机": the drone appears in battle when S2 fires (test/content/tokens_devices.test.js), never before
const tokensIn = (ps, id = DRONE) => [...ps.hand, ...ps.temp, ...ps.board.values()].filter((p) => p && p.kind === 'token' && p.id === id);
assert.equal(DATA.tokens[DRONE].placeable, false, 'tokens.json: a skill summon is not placeable');
for (const skill of [null, 0, 1]) {
const lo = skill == null ? null : checkLoadout({ [SILENCE]: { skill } }, chess).loadout;
const h = makeMatch({ mode: 'solo', seats: seatsWith(lo), seed: 5 }).start();
h.toPrep(1);
@@ -283,11 +286,16 @@ test('a skill summon comes only with that skill: 赫默 S2 (default) gets the
ps.board.clear();
ps.hand.fill(null);
give(h.m, ps, SILENCE, 'board', [9, 3]);
assert.equal(tokensIn(ps).length, want, `normal 赫默, skill ${skill ?? 'default'}`);
assert.equal(tokensIn(ps).length, 0, `normal 赫默, skill ${skill ?? 'default'}`);
ps.board.clear();
ps.hand.fill(null);
give(h.m, ps, chess(SILENCE).goldenId, 'board', [9, 4]);
assert.equal(tokensIn(ps).length, want, `elite 赫默, skill ${skill ?? 'default'}`);
assert.equal(tokensIn(ps).length, 0, `elite 赫默, skill ${skill ?? 'default'}`);
// a talent summon still is a hand card: 伺夜 → 狼群
ps.board.clear();
ps.hand.fill(null);
give(h.m, ps, 'chess_char_3_19_a', 'board', [9, 5]);
assert.equal(tokensIn(ps, 'token_10028_vigil_wolf').length, 1, '伺夜 sends its 狼群 to the hand');
h.m.dispose();
}
});