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

@@ -232,7 +232,7 @@ function lemuen(bb, chess, def) {
return keys;
};
// #428: extra attack range also opens her skill under the existing content-trigger contract.
unit.skill?.addTriggerRange(() => [{ keys: wantedKeys(), profile: unit.profile }]);
unit.skill?.addTriggerRange(() => [{ keys: wantedKeys(), profile: unit.profile }], { attackOnly: true });
let sig = null;
const refresh = () => { // update immediately when the last source leaves, before another attack can run
const keys = live(unit) ? wantedKeys() : [];
+12 -3
View File
@@ -380,12 +380,21 @@ export default {
} },
],
install(battle, unit) {
// PRTS 卫戍协议/帮助: special targeting also supplies the DEFAULT attack opportunity (#440).
// Query live owned blockers for both paths; S2's links still use its own effect range, S3 keeps SKILL_RANGE.
const blockedKeys = () => {
if (!up(unit)) return [];
const keys = [];
for (const s of skelsUp(unit)) for (const e of s.blocking) if (e.alive && e.blockedBy === s) keys.push(tileKey(Math.round(e.y), Math.round(e.x)));
return [...new Set(keys)].sort((a, b) => a - b);
};
if (unit.skill?.id === S1 || unit.skill?.id === S2) {
unit.skill.addTriggerRange(() => [{ keys: blockedKeys(), profile: unit.profile }], { attackOnly: true });
}
// trait: the enemies her summons block are her targets too
battle.on('tick', () => {
if (!up(unit)) return;
const keys = [];
for (const s of skelsUp(unit)) for (const e of s.blocking) if (e.alive && e.blockedBy === s) keys.push(tileKey(Math.round(e.y), Math.round(e.x)));
const uniq = [...new Set(keys)].sort((a, b) => a - b);
const uniq = blockedKeys();
const cur = unit.extraRangeKeys ?? [];
if (uniq.length === cur.length && uniq.every((k, i) => k === cur[i])) return;
battle.setExtraRange(unit, uniq.length ? uniq : null);