feedback5 WK: 自选编队 on the server — room.diy { picks } (lenient: illegal picks dropped, only malformed input is BAD_MSG; stored per session / seat; a match takes the picks its seat had at the start, a change during a match is for the next one; welcome lists diyKitted), PlayerState.diy with the player's data view (a slotted DIY slot is its operator for every rule: bonds from its factions, no 特质, the pick's skill and module, the slot's tier / price / merge), a per-player stock per slotted piece (8 at tier 5, 5 at tier 6 [ASSUMED]; never in the shared pool; none when every bond of it is banned), DIY pieces in that player's shop rolls and reward offers once the 调度中心 reaches the slot's level, battleInput diy, m.private.diy / diyBanned, prep scouting and results name the operator, 信标 never sends a 自选 piece to a teammate [ASSUMED]

The owner's decisions of 2026-10-05 (picks, slots, shop per player, bans); research 0.2.0 §2. Golden unchanged (no scenario slots a DIY pick).
Tests: test/match/diy-shop.test.js (9), test/lobby-diy.test.js (4).
This commit is contained in:
sganggs committed 2026-10-05 23:35:01 +08:00
1 parent 742f478526
commit 4bee542e61
26 files changed
+1026 -105

No files matched your search

+7 -1
View File
@@ -170,6 +170,7 @@ function legacyInvariants(m) {
assert.ok(ps.hand.every((x) => x == null) && ps.temp.every((x) => x == null), 'eliminated player keeps nothing');
}
const chessUids = new Set();
const diyHeld = new Map();
const all = [...ps.board.values(), ...ps.hand.filter(Boolean), ...ps.temp.filter(Boolean)];
for (const p of all) if (p.kind === 'chess') chessUids.add(p.uid);
let deployed = 0;
@@ -195,7 +196,9 @@ function legacyInvariants(m) {
for (const it of p.items) { note(it); assert.equal(it.kind, 'item'); assert.ok(m.gd.item(it.id), `unknown item ${it.id}`); }
assert.ok(Number.isInteger(p.poolCopies) && p.poolCopies >= 0);
const base = m.gd.baseIdOf(p.id);
held.set(base, (held.get(base) || 0) + p.poolCopies);
// a 自选 piece's copies are its player's own stock (0.2.0, server/match/player/diy.js)
const tally = ps.diyStock && ps.diyStock.has(base) ? diyHeld : held;
tally.set(base, (tally.get(base) || 0) + p.poolCopies);
} else if (p.kind === 'item') {
assert.ok(m.gd.item(p.id), `unknown item ${p.id}`);
} else if (p.kind === 'token') {
@@ -210,6 +213,9 @@ function legacyInvariants(m) {
const need = m.gd.mergeCount(b);
if (need > 1 && m.gd.goldenIdOf(b)) assert.ok(n < need, `${ps.playerId} owns ${n} copies of ${b} (merge ${need})`);
}
for (const [base, e] of ps.diyStock ? ps.diyStock.entries : []) {
assert.equal(e.left + (diyHeld.get(base) || 0), e.cap, `${ps.playerId} 自选 stock ${base}: left ${e.left} + held ${diyHeld.get(base) || 0} != cap ${e.cap}`);
}
}
for (const [base, e] of pool.entries) {
assert.ok(e.left >= 0 && e.left <= e.cap, `pool ${base} left ${e.left} cap ${e.cap}`);