feedback1e WP: live attack range on the battle card; 烛煌 S3 splash 1.7

Community report "干员烛煌开启3技能时候攻击范围不会变". The sim already attacked
with S3's 4-11 while it ran (real matches, and every attack-range change of
the mode); what stayed the same was the battle detail card: its 攻击范围
mini-map kept the base 3-1 grid while the live stats beside it moved.

- Battle._refreshRange keeps the relative grid behind rangeKeys as
  unit.liveRangeGrid (targeting.js extendedGrid: rangeExtend grown on);
  shared/protocol.js unitStatsEntry carries it as `range`; the detail card
  draws it (cardRangeGrid) in battle and in the prep preview, and names a
  whole-field range (纯烬艾雅法拉 S3) 全场 instead of drawing it.
- 烛煌 S3 "攻击变为群体攻击" is one target + a 1.7 splash (PRTS 技能3 备注
  "攻击溅射半径1.7"), not every enemy inside 4-11; the burst bonus hits
  every enemy of the attack; a burn burst's +2 ammo stops at the skill's
  ammo ("补充后的弹药数量无法超过上限").
- Tests: test/sim/feedback1e-skillrange.test.js (S3 range, splash, ammo
  cap, live grid, the audit of every attack-range change, a real spec),
  test/ui/feedback1e-range.test.js (the card).
This commit is contained in:
sganggs committed 2026-10-03 05:03:07 +08:00
1 parent 7f134737ce
commit 8b0e672bb8
10 files changed
+471 -20

No files matched your search

+23
View File
@@ -43,6 +43,29 @@ export function absoluteRangeKeys(grid, r, c, dir, extend = 0) {
return keys;
}
/**
* A range grid (`[dRow, dCol]`, facing RIGHT) grown by `extend` (rangeExtend, DESIGN §3): every row gains the whole
* tiles 1 … ⌊extend⌋ beyond its far (+dCol) end — the relative form of what absoluteRangeKeys builds, deduplicated, junk
* entries dropped. Battle._refreshRange keeps it as `unit.liveRangeGrid` (the detail card's live 攻击范围).
* @param {Array<[number, number]>|null|undefined} grid
* @param {number} [extend]
* @returns {Array<[number, number]>}
*/
export function extendedGrid(grid, extend = 0) {
const out = [];
const seen = new Set();
const add = (dr, dc) => { const k = `${dr},${dc}`; if (!seen.has(k)) { seen.add(k); out.push([dr, dc]); } };
if (!Array.isArray(grid)) return out;
const cells = grid.filter((p) => Array.isArray(p) && Number.isInteger(p[0]) && Number.isInteger(p[1]));
for (const [dr, dc] of cells) add(dr, dc);
if (extend > 0 && Number.isFinite(extend)) {
const maxByRow = new Map();
for (const [dr, dc] of cells) maxByRow.set(dr, Math.max(maxByRow.get(dr) ?? -Infinity, dc));
for (const [dr, mx] of maxByRow) for (let k = 1; k <= Math.min(extend, COLS); k++) add(dr, mx + k);
}
return out;
}
/** Tile key of a unit's current position. */
export function tileKeyOf(u) {
const r = Math.round(u.y), c = Math.round(u.x);