「自选干员放入后模拟中的补给池随机范围也将被相应扩大」: player/diy.js diyStockEntries (every slotted piece with copies left; the roll's own tier rules, no 调度中心 gate) joins effectsMeta ctx.rollChess / ctx.rollPool (spDraft rollPool extra + chessOf: bonds through the player's view) and the 驰援 fallback in choices.js. Granted from the player's own stock (poolOf). Docs: META, PLAYING. Golden: no scenario moved.
79 lines
4.4 KiB
JavaScript
79 lines
4.4 KiB
JavaScript
// test/match/feedback5-we2-diy-grants.test.js — 0.2.0 WE2 #9: an effect / reward / 机变 card that grants a player a random
|
|
// operator draws that player's 自选 stock too (「自选干员放入后模拟中的补给池随机范围也将被相应扩大」; PRTS 新手教程 "调度中心随机资源
|
|
// 的范围将被扩大"): effectsMeta ctx.rollChess / ctx.rollPool (player/diy.js diyStockEntries), choices.js 驰援 fallback —
|
|
// the roll's own tier rules, bonds through the player's view; never another player's stock.
|
|
// Run: node --test test/match/feedback5-we2-diy-grants.test.js
|
|
|
|
import { test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { makeCtx } from '../../server/match/effectsMeta.js';
|
|
import { applyCard, tacticCard } from '../../server/match/choices.js';
|
|
import { makeMatch, checkInvariants } from './harness.js';
|
|
|
|
const T5A = 'chess_char_5_diy1_a';
|
|
const SIEGE = 'char_112_siege'; // 推进之王 (维多利亚)
|
|
const PICK = { charId: SIEGE, skillIndex: 2, uniEquipId: 'uniequip_002_siege' };
|
|
|
|
/** A co-op match: p_0 slots 推进之王 at T5A, p_1 slots nothing; first prep. */
|
|
function prep(seed = 911) {
|
|
const seats = [
|
|
{ seat: 0, playerId: 'p_0', name: 'P0', isBot: false, connected: true, diy: { [T5A]: PICK } },
|
|
{ seat: 1, playerId: 'p_1', name: 'P1', isBot: false, connected: true },
|
|
];
|
|
const h = makeMatch({ mode: 'coop', difficulty: 'NORMAL', seats, seed }).start();
|
|
h.toPrep(1);
|
|
return { h, m: h.m, p0: h.ps('p_0'), p1: h.ps('p_1') };
|
|
}
|
|
/** Take every shared-pool copy of the chess `pred` admits (the draw then sees only what is left); m.restore() gives them back. */
|
|
function drain(m, pred) {
|
|
const back = (m.drained ??= []);
|
|
for (const [id, e] of m.pool.entries) if (pred(id, e) && e.left > 0) back.push([id, m.pool.take(id, e.left)]);
|
|
m.restore = () => { for (const [id, n] of back.splice(0)) m.pool.give(id, n); };
|
|
}
|
|
|
|
test('#9 effect rolls (ctx.rollChess, any tier / bond filter) draw the player\'s 自选 stock — its own player only', () => {
|
|
const { m, p0, p1 } = prep();
|
|
assert.equal(p0.diyStock.left(T5A), 8);
|
|
drain(m, (id, e) => e.tier === 5);
|
|
const c0 = makeCtx(m, p0, { key: 'test' }, 'onTest');
|
|
const c1 = makeCtx(m, p1, { key: 'test' }, 'onTest');
|
|
assert.equal(c0.rollChess({ tier: 5 }), T5A, 'the only tier-5 copies left: her stock');
|
|
assert.equal(c1.rollChess({ tier: 5 }), null, 'another player never draws it');
|
|
// the 调度中心 level is not the shop's gate here: an effect's tier rule alone (p_0 is at level 1)
|
|
assert.ok(p0.shop.level < 5);
|
|
// a bond filter reads the operator: 推进之王 is 维多利亚
|
|
drain(m, (id) => (m.gd.chess(id)?.bonds || []).includes('victoriaShip'));
|
|
assert.equal(c0.rollChess({ bond: 'victoriaShip' }), T5A);
|
|
assert.equal(c0.rollChess({ bond: 'yanShip', tier: 5 }), null, 'not a 炎 operator');
|
|
// granting it takes one copy of her own stock
|
|
assert.ok(c0.grantChess(T5A));
|
|
assert.equal(p0.diyStock.left(T5A), 7);
|
|
m.restore();
|
|
checkInvariants(m);
|
|
m.dispose();
|
|
});
|
|
|
|
test('#9 reward pools (ctx.rollPool: a shared-pool chess pool) and the 维多利亚驰援 card draw it too', () => {
|
|
const { m, p0, p1 } = prep(910);
|
|
drain(m, (id, e) => e.tier === 5);
|
|
const c0 = makeCtx(m, p0, { key: 'test' }, 'onTest');
|
|
const c1 = makeCtx(m, p1, { key: 'test' }, 'onTest');
|
|
assert.deepEqual(c0.rollPool('pool_chess_shop_5_reward'), { kind: 'chess', id: T5A, golden: false });
|
|
assert.equal(c1.rollPool('pool_chess_shop_5_reward'), null);
|
|
// 维多利亚驰援 (allybuff_select_7_7, single_special_choice_gain_bond_chess) with every 维多利亚 copy of the pool taken:
|
|
// the card grants her 推进之王 from her own stock
|
|
drain(m, (id) => (m.gd.chess(id)?.bonds || []).includes('victoriaShip'));
|
|
const card = tacticCard(m.gd.choices.cards.tactic.find((c) => c.effectId === 'allybuff_select_7_7'));
|
|
const owned = (ps) => [...ps.board.values(), ...ps.hand, ...ps.temp].filter((x) => x && x.kind === 'chess' && x.id === T5A).length;
|
|
const before = p0.diyStock.left(T5A);
|
|
applyCard(m, p0, card);
|
|
assert.equal(owned(p0), 1, 'she got her 自选 piece');
|
|
assert.equal(p0.diyStock.left(T5A), before - 1, 'from her stock');
|
|
const had1 = [...p1.hand, ...p1.temp].filter(Boolean).length;
|
|
applyCard(m, p1, card);
|
|
assert.equal([...p1.hand, ...p1.temp].filter(Boolean).length, had1, 'p_1 has no 自选 piece to draw: nothing');
|
|
m.restore();
|
|
checkInvariants(m);
|
|
m.dispose();
|
|
});
|