feedback5 WV: a melee 突袭 member that blocks lands where its block applies — a tile ground units pass with an enemy path through it or its enemy on it — before a nearer one where it blocks nothing (a 围墙 tile only when nothing else covers its enemy; never a 高台), GitHub #148 (the owner's decision of 2026-10-06); the #148 regression test updated on purpose; golden: fields boss-boss_2-pair, boss-boss_3-pair, boss-boss_4-pair, boss-boss_6-pair, boss-boss_7-pair, hidden-boss_10-pair moved — their 突袭 members (伊内丝, 缄默德克萨斯, 仇白) now land on a road tile on the enemy path instead of a 围墙 tile (or an off-path road tile)

This commit is contained in:
sganggs committed 2026-10-06 18:12:48 +08:00
1 parent 7aede84be9
commit 9d85467479
3 files changed
+326 -247

No files matched your search

+21 -9
View File
@@ -36,7 +36,9 @@
// next to the most advanced ground enemy it can reach: on a free tile its position may be deployed
// on from which its range covers that enemy (GitHub issue #51 [ASSUMED]: the first of the 8 most
// advanced that has such a tile; none → it stays and the next poll looks again, never a jump that
// hits nothing); grid.canStand: never the 深水区 (player report #3 after 0.1.0, members dropped
// hits nothing) — for a melee member that blocks, a tile where its block applies first (ground
// units pass it, an enemy path runs through it: not a 围墙 — the owner's decision of 2026-10-06,
// GitHub #148); grid.canStand: never the 深水区 (player report #3 after 0.1.0, members dropped
// into 战场#08's pool after an enemy wading in it); Battle.isReservedTile: never a tile a knocked-out
// operator lies on (player report F5, members landed on a fallen teammate). A real redeployment
// (retreat + free redeploy on the landing tile, full HP, `deploy` fires — 部署时 traits such as
@@ -268,25 +270,35 @@ function raidReach(u) {
* Landing tile [row, col] of a jump to enemy `e`, or null: a tile from which the member's range (`reach`, raidReach)
* covers the enemy's body (a huge enemy: any tile it occupies — body.js), within RAID_SEARCH tiles (Chebyshev) of the
* enemy, inside the field rect, that the member's position may be deployed on (grid.canStand: never the 深水区 —
* player report #3 after 0.1.0) and that is free (Battle.isReservedTile: no living unit, no knocked-out operator's
* body — player report F5 —, no waiting piece's tile). The nearest first (Chebyshev, then 0.01·Manhattan); last
* tie-break the offset in the member's facing-RIGHT frame (sim/dir.js localOrder; for a RIGHT-facing unit the plain
* tile-key order), so the landing tile turns with its direction. Only the tiles a range offset leads back to from a
* body tile are looked at, so a search that finds nothing stays cheap.
* player report #3 after 0.1.0 —, and for a melee member low ground only, never a 高台: GitHub #148) and that is free
* (Battle.isReservedTile: no living unit, no knocked-out operator's body — player report F5 —, no waiting piece's tile).
* A melee member that blocks takes a tile where its block applies first — ground units pass it (not a 围墙 / 围栏 tile,
* where a unit blocks no ground enemy: Battle._blockerFor) and an enemy ground path runs through it
* (Battle.groundPathTiles) or its enemy stands on it — over one where it would block nothing (the owner's decision of
* 2026-10-06 after GitHub
* #148: a member landed on a 围墙 tile, drawn raised, while a road tile covered its enemy too [ASSUMED: the official
* landing picks among the legal tiles at random]); then the nearest (Chebyshev, then 0.01·Manhattan); last tie-break
* the offset in the member's facing-RIGHT frame (sim/dir.js localOrder; for a RIGHT-facing unit the plain tile-key
* order), so the landing tile turns with its direction. Only the tiles a range offset leads back to from a body tile
* are looked at, so a search that finds nothing stays cheap.
*/
function raidTile(battle, u, e, reach) {
const er = Math.round(e.y), ec = Math.round(e.x);
const ranged = u.def?.position === 'RANGED';
let best = null, bd = Infinity, bo = null;
for (const k of bodyKeys(e)) {
const body = bodyKeys(e);
const path = !ranged && u.s.blockCnt > 0 ? battle.groundPathTiles() : null;
let best = null, bp = 2, bd = Infinity, bo = null;
for (const k of body) {
const br = Math.floor(k / COLS), bc = k - br * COLS;
for (let i = 0; i < reach.length; i += 2) {
const r = br - reach[i], c = bc - reach[i + 1], dr = r - er, dc = c - ec;
if (Math.abs(dr) > RAID_SEARCH || Math.abs(dc) > RAID_SEARCH) continue;
if (!battle.grid.inRect(r, c) || !battle.grid.canStand(r, c, { ranged }) || battle.isReservedTile(r, c)) continue;
const t = r * COLS + c;
const p = path && battle.grid.tile(r, c).pass === 'ALL' && (path.has(t) || body.includes(t)) ? 0 : 1; // its block applies
const d = Math.max(Math.abs(dr), Math.abs(dc)) + 0.01 * (Math.abs(dr) + Math.abs(dc));
const o = localOrder(dr, dc, u.dir);
if (d < bd - 1e-9 || (Math.abs(d - bd) <= 1e-9 && localBefore(o, bo))) { best = [r, c]; bd = d; bo = o; }
if (p < bp || (p === bp && (d < bd - 1e-9 || (Math.abs(d - bd) <= 1e-9 && localBefore(o, bo))))) { best = [r, c]; bp = p; bd = d; bo = o; }
}
}
return best;