0.2.3: prepare follow-up fixes and client features (#407, #408, #410, #413, #431, #435, #436, #440, #444)

Add special-target activation without bypassing attack cadence, cooperative
card preferences and emotes, skill sound banks, per-operator voice choices,
text sizing, install prompts, closed-tab recovery and unanimous setup rerolls.
Retain late deployment elapsed time and resolve echo/counter pulses without
recursive hook overflow. Independent final review remains pending.

Golden: roster-016, roster-035, roster-036, roster-045;
boss-boss_1-pair, boss-boss_3-pair, boss-boss_5-pair, boss-boss_7-pair,
hidden-boss_9-pair; solo-HARD-9-boosted: Lemuen attack cadence.
diy-132: Necras extra activation targets.
coop2-NORMAL-14-standins: cooperative purchase preference and simulation fixes.
hidden-boss_10-solo: echo pulse FIFO resolution.
See DESIGN 28.13-28.24 for assumptions, sources and contributor credit.
This commit is contained in:
sganggs committed 2026-10-09 22:55:24 +08:00
1 parent 314564c139
commit f3658a0dd4
104 files changed
+3674 -741

No files matched your search

+2
View File
@@ -7,6 +7,7 @@
import { pieceDir, parseDir, mergeTile } from '../board.js';
import { MAX_OFFER_SLOTS } from './common.js';
import { onMerge } from '../botEmotes.js';
export class PlayerAcquire {
/** Normal copies of a base chess currently owned (board/hand/temp). */
@@ -134,6 +135,7 @@ export class PlayerAcquire {
const shown = rec ? this.fieldRecord(rec) || rec : null;
this.m.tickerFor('GOLDEN_CHAR', [this.name, shown ? shown.name : goldenId], { playerId: this.playerId });
this.m.dispatch(this, 'onMerge', { kind: 'chess', piece: elite, baseId, consumed: consumed.map((l) => l.piece.uid), area: where });
onMerge(this.m, this, { kind: 'chess' }); // AI bot says "害怕" on its 3rd chess merge of a round (enabled by default; SP_BOT_EMOTES=0 silences it)
return elite;
}
+10 -3
View File
@@ -177,7 +177,14 @@ export class PlayerDiy {
* @param {Set<string>} off
*/
initDiyStock(off = new Set()) {
this.diyStock = new DiyStock();
const { stock, banned } = this.buildDiyStock(off);
this.diyStock = stock;
this.diyBanned = banned;
}
/** Prepare stock without mutating this player, so setup rerolls can commit all players together. */
buildDiyStock(off = new Set()) {
const stock = new DiyStock();
const banned = [];
const gd = this.m.gd;
for (const slotId of Object.keys(this.diy || {})) {
@@ -188,9 +195,9 @@ export class PlayerDiy {
if (bonds.length > 0 && bonds.every((b) => off.has(b))) { banned.push(slotId); continue; }
const cap = gd.poolCopies(slotId);
if (!(cap > 0)) continue;
this.diyStock.entries.set(slotId, { cap, left: cap, tier: gd.tierOf(slotId), shopLevel: slot.shopLevel });
stock.entries.set(slotId, { cap, left: cap, tier: gd.tierOf(slotId), shopLevel: slot.shopLevel });
}
this.diyBanned = Object.freeze(banned);
return { stock, banned: Object.freeze(banned) };
}
/** The copy accounting of base chess `baseId` for this player: its own stock for a slotted DIY slot, else the shared pool. */