feedback5 WE2: effects, reward pools and 机变 cards that grant a random operator also draw the player's 自选 pieces (#9)

「自选干员放入后模拟中的补给池随机范围也将被相应扩大」: 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.
This commit is contained in:
sganggs committed 2026-10-06 15:42:59 +08:00
1 parent 7a3d53faf6
commit 7b83028139
7 files changed
+124 -11

No files matched your search

+6 -2
View File
@@ -482,9 +482,13 @@ function applyDefault(m, ps, card) {
handled = true;
break;
case 'single_special_choice_gain_bond_chess': {
// the player's 自选 stock joins the draw, its bonds read through the player's view (player/diy.js diyStockEntries)
const pgd = ps.gd || gd;
const hasBond = (cid) => { const c = pgd.chess(cid); return !!(c && Array.isArray(c.bonds) && c.bonds.includes(bs.bond)); };
for (let i = 0; i < count; i++) {
const id = m.pool.roll(m.rngMeta, { maxTier: Math.max(1, ps.shop.level), filter: (cid) => { const c = gd.chess(cid); return !!(c && Array.isArray(c.bonds) && c.bonds.includes(bs.bond)); } })
|| m.pool.roll(m.rngMeta, { maxTier: 6, filter: (cid) => { const c = gd.chess(cid); return !!(c && Array.isArray(c.bonds) && c.bonds.includes(bs.bond)); } });
const extra = typeof ps.diyStockEntries === 'function' ? ps.diyStockEntries() : null;
const id = m.pool.roll(m.rngMeta, { maxTier: Math.max(1, ps.shop.level), filter: hasBond, extra })
|| m.pool.roll(m.rngMeta, { maxTier: 6, filter: hasBond, extra });
if (id) ps.acquireChess(id, { source: 'choice' });
}
handled = true;
+13 -3
View File
@@ -544,20 +544,30 @@ export function makeCtx(m, ps, source, hook, ev = null) {
});
return p ? view(p) : null;
},
/** Random chess id from the shared pool (copy-weighted). opts: { maxTier, tier, bond, filter(id) } */
/**
* Random chess id from the shared pool (copy-weighted) — and the player's own 自选 stock (player/diy.js
* diyStockEntries, 0.2.0: 「自选干员放入后模拟中的补给池随机范围也将被相应扩大」). opts: { maxTier, tier, bond, filter(id) };
* bonds are read through the player's data view (a slotted slot: its operator's).
*/
rollChess: (opts = {}) => {
const f = (id) => {
if (opts.bond) { const c = gd.chess(id); if (!c || !Array.isArray(c.bonds) || !c.bonds.includes(opts.bond)) return false; }
return typeof opts.filter === 'function' ? !!opts.filter(id) : true;
};
return m.pool.roll(m.rngMeta, { maxTier: Number.isInteger(opts.maxTier) ? opts.maxTier : 6, tier: Number.isInteger(opts.tier) ? opts.tier : null, filter: f });
const extra = typeof ps.diyStockEntries === 'function' ? ps.diyStockEntries() : null;
return m.pool.roll(m.rngMeta, { maxTier: Number.isInteger(opts.maxTier) ? opts.maxTier : 6, tier: Number.isInteger(opts.tier) ? opts.tier : null, filter: f, extra });
},
rollItem: (opts = {}) => m.rollItemId(opts),
/**
* Roll a choices.json pool: equip pools → { kind: 'item', id }; chess pools (items / weighted / shopEligible with
* tier, minTier, bond, golden) → { kind: 'chess', id, golden } (a pool chess needs a free copy). null when empty.
*/
rollPool: (poolId, opts = {}) => m.rollPool(poolId, { shopLevel: ps.shop.level, ...opts }),
rollPool: (poolId, opts = {}) => m.rollPool(poolId, {
shopLevel: ps.shop.level,
// a shared-pool draw also takes the player's 自选 stock, its bonds read through the player's view (rollChess)
extra: typeof ps.diyStockEntries === 'function' ? ps.diyStockEntries() : null, chessOf: (id) => gd.chess(id),
...opts,
}),
/**
* Run another owned chess's 特质 of `eventType` now (SERVER_GAIN / SERVER_PREP_START / SERVER_PREP_FIN /
* SERVER_CHESS_SOLD / SERVER_REFRESH_SHOP). opts.asUid: run them as if they belonged to that piece. Returns the
+6 -2
View File
@@ -152,9 +152,11 @@ export class MatchSpDraft {
* Roll a choices.json pool (ctx.rollPool). Equip pools → rollItemId. Chess pools: an `items` (uniform) or `weighted`
* list — only chess with a free pool copy (or outside the pool) qualify — else a copy-weighted draw from the shared
* pool filtered by `tier` / `minTier` / `maxTier` (number or 'shopLevel') / `bond`; `golden: true` yields the elite id.
* `extra` (the drawing player's 自选 stock, player/diy.js diyStockEntries) joins that shared-pool draw, `chessOf` reads the
* bonds (the player's data view: a slotted slot's operator) — 0.2.0 WE2.
* @returns {{ kind: 'item'|'chess', id: string, golden?: boolean } | null}
*/
rollPool(poolId, { shopLevel = 6 } = {}) {
rollPool(poolId, { shopLevel = 6, extra = null, chessOf = null } = {}) {
const pools = this.gd.choices.pools && typeof this.gd.choices.pools === 'object' ? this.gd.choices.pools : {};
const p = typeof poolId === 'string' && Object.hasOwn(pools, poolId) ? pools[poolId] : null;
if (!p || typeof p !== 'object') return null;
@@ -181,10 +183,12 @@ export class MatchSpDraft {
const maxTier = p.maxTier === 'shopLevel' ? lvl : Number.isInteger(p.maxTier) ? p.maxTier : 6;
const minTier = Number.isInteger(p.minTier) ? p.minTier : 1;
const bond = typeof p.bond === 'string' ? p.bond : null;
const recOf = typeof chessOf === 'function' ? chessOf : (cid) => this.gd.chess(cid);
id = this.pool.roll(rng, {
tier: Number.isInteger(p.tier) ? p.tier : null,
maxTier,
filter: (cid, e) => e.tier >= minTier && (!bond || (Array.isArray(this.gd.chess(cid)?.bonds) && this.gd.chess(cid).bonds.includes(bond))),
filter: (cid, e) => e.tier >= minTier && (!bond || (Array.isArray(recOf(cid)?.bonds) && recOf(cid).bonds.includes(bond))),
extra,
});
}
if (!id) return null;
+16 -1
View File
@@ -25,7 +25,11 @@
// shop's chess slots and the reward offers' temporary refreshes [ASSUMED] — weighted like any chess of its tier, once
// the 调度中心 has reached the slot's shopLevel (activity_table shopLevelDisplayDataDict lists the tier-5 slots at
// level 5 and the tier-6 slots at level 6; PRTS 帮助 "仅在调度中心等级 ≥ 干员所在等阶"). The price is the slot's (any
// chess of its tier: 4). Effects that grant a random operator from the shared pool never draw it.
// chess of its tier: 4).
// * random grants (diyStockEntries, 0.2.0 WE2 #9): an effect, reward or 机变 card that grants this player a random
// operator from the pool draws its stock too — 「自选干员放入后模拟中的补给池随机范围也将被相应扩大」 (bilibili), "调度中心
// 随机资源的范围将被扩大" (PRTS 新手教程) — under the roll's own tier rules (the effect's tier / maxTier: no 调度中心 gate,
// as for a preset chess) and filters (bonds read through the player's data view).
// Installed on PlayerState.prototype by server/match/PlayerState.js (a method container: never instantiated; `this` is
// the player state).
@@ -204,6 +208,17 @@ export class PlayerDiy {
return out.length ? out : null;
}
/**
* The stock entries a random grant of this player draws besides the shared pool (see the header): every slotted piece
* with copies left, whatever the 调度中心 level (the roll's own tier rules apply), or null (none).
* @returns {Array<[string, { cap: number, left: number, tier: number, shopLevel: number }]> | null}
*/
diyStockEntries() {
if (!this.diyStock || !this.diyStock.entries.size) return null;
const out = [...this.diyStock.entries].filter(([, e]) => e.left > 0);
return out.length ? out : null;
}
/**
* The 自选 pick a chess id (normal or elite) of this player fields — its slot's — or null (not a slotted slot).
* @param {string} id