Hit tiles: positions round half to even (PRTS 作战机制); enemies held just inside the field edge
This commit is contained in:
1 parent
bc50cac01f
commit
119a04888a
13 files changed
+113
-24
No files matched your search
@@ -177,8 +177,8 @@ for (const kind of ['boss', 'hidden']) for (const side of ['L', 'R']) {
|
||||
});
|
||||
}
|
||||
|
||||
test('#475: even buildable hand, temporary-storage, side-margin and off-rect tiles remain invalid Raid landings', () => {
|
||||
for (const pos of [[0, 3], [1, 3], [0, 17], [1, 17], [3, 1], [3, 19], [6, 17]]) {
|
||||
test('#475: even buildable hand, temporary-storage and side-margin tiles remain invalid Raid landings', () => {
|
||||
for (const pos of [[0, 3], [1, 3], [0, 17], [1, 17], [3, 1], [3, 19]]) {
|
||||
const h = emptyHalfLanding({ pos, ranged: true });
|
||||
const u = h.unit('probe'), home = [u.tileR, u.tileC];
|
||||
h.run(11);
|
||||
@@ -187,6 +187,16 @@ test('#475: even buildable hand, temporary-storage, side-margin and off-rect til
|
||||
}
|
||||
});
|
||||
|
||||
test('#475: no Raid lands off the rectangle — an enemy spawned on (6,17) is held on the edge row and stands on (5,17)', () => {
|
||||
// Battle._clampPos keeps an enemy FIELD_EDGE_INSET inside the field, so its tile is the edge tile (body.js); the Raid
|
||||
// lands where it would for an enemy put on (5,17), never on row 6
|
||||
const h = emptyHalfLanding({ pos: [6, 17], ranged: true });
|
||||
const u = h.unit('probe');
|
||||
h.run(11);
|
||||
assert.deepEqual([u.tileR, u.tileC], [5, 17]);
|
||||
checkInvariants(h.b);
|
||||
});
|
||||
|
||||
test('#475: empty-half Raid still respects melee/high-ground, water and occupied/downed-unit reservations', () => {
|
||||
for (const glyph of ['h', 'd']) {
|
||||
const h = emptyHalfLanding({ pos: [3, 17], glyph });
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
// The tile of a target is its position rounded half to even — PRTS 作战机制 「受击方坐标经round函数取整(四舍六入五成双)
|
||||
// 得到其所属格坐标,按是否属于该格判定」 — not Math.round, which rounds every tie up: an enemy exactly halfway between
|
||||
// columns 10 and 11 (x = 10.5) is on column 10, one at x = 11.5 on column 12.
|
||||
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { makeBattle, chessRec, enemyRec } from '../helpers/battleHarness.js';
|
||||
import { roundHalfEven } from '../../server/sim/detmath.js';
|
||||
import { bodyKeys, bodyOnTile, bodyTileReach } from '../../server/sim/body.js';
|
||||
import { tileKeyOf } from '../../server/sim/targeting.js';
|
||||
import { COLS } from '../../server/sim/constants.js';
|
||||
|
||||
test('roundHalfEven: ties to the even integer, the rest to the nearest; never −0', () => {
|
||||
const cases = [[0.5, 0], [1.5, 2], [2.5, 2], [3.5, 4], [10.5, 10], [11.5, 12], [-0.5, 0], [-1.5, -2], [-2.5, -2],
|
||||
[2.4999999999999996, 2], [2.5000000000000004, 3], [-0.4, 0], [-0, 0], [7, 7], [4503599627370495.5, 4503599627370496]];
|
||||
for (const [x, want] of cases) assert.ok(Object.is(roundHalfEven(x), want), `${x} → ${roundHalfEven(x)}, want ${want}`);
|
||||
for (let i = -2000; i <= 2000; i++) {
|
||||
const x = i / 8, v = roundHalfEven(x), d = Math.abs(x - v);
|
||||
assert.ok(Number.isInteger(v) && (d < 0.5 || (d === 0.5 && v % 2 === 0)), `${x} → ${v}`);
|
||||
if (d !== 0.5) assert.equal(v, Math.round(x) + 0, `${x}: away from a tie it is Math.round`);
|
||||
}
|
||||
});
|
||||
|
||||
test('a point target exactly halfway is on the even tile: bodyKeys, bodyOnTile, bodyTileReach, tileKeyOf', () => {
|
||||
const e = { x: 10.5, y: 3.5 };
|
||||
assert.deepEqual(bodyKeys(e), [4 * COLS + 10]);
|
||||
assert.ok(bodyOnTile(e, 4, 10) && !bodyOnTile(e, 4, 11) && !bodyOnTile(e, 3, 10));
|
||||
assert.equal(bodyTileReach(e, 4, 10), 0);
|
||||
assert.equal(bodyTileReach(e, 4, 12), 2);
|
||||
assert.equal(tileKeyOf(e), 4 * COLS + 10);
|
||||
assert.equal(tileKeyOf({ x: 11.5, y: 2.5 }), 2 * COLS + 12);
|
||||
});
|
||||
|
||||
test('the battle\'s enemy index puts an enemy at x = 10.5 on column 10, not 11', () => {
|
||||
const h = makeBattle({
|
||||
kind: 'boss', content: 'none', autoFinish: false, timeLimit: 60,
|
||||
defs: {
|
||||
chess: { t_one: chessRec({ id: 't_one', stats: { atk: 100, blockCnt: 0, maxHp: 1e7 }, rangeGrid: [[0, 0]], skill: null }) },
|
||||
enemies: { enemy_still: enemyRec({ key: 'enemy_still', hp: 1e8, speed: 0 }) },
|
||||
},
|
||||
players: [{ playerId: 'p1', seat: 0, side: 'L', coords: 'field', units: [{ uid: 1, kind: 'chess', chessId: 't_one', row: 1, col: 1 }], bonds: {} }],
|
||||
});
|
||||
h.step();
|
||||
const e = h.spawn('enemy_still', { pos: [3, 10], routeIndex: 0 });
|
||||
e.x = 10.5;
|
||||
e.y = 3;
|
||||
h.step();
|
||||
e.x = 10.5;
|
||||
e.y = 3;
|
||||
const u = h.unit('t_one');
|
||||
assert.deepEqual(h.b.enemiesInKeys([3 * COLS + 10], u, { canHitFly: true }).map((x) => x.id), [e.id]);
|
||||
assert.deepEqual(h.b.enemiesInKeys([3 * COLS + 11], u, { canHitFly: true }).map((x) => x.id), []);
|
||||
});
|
||||
Reference in new issue
Block a user