feedback6 R21F: an enemy flyer hovers FLY_HOVER above the road whatever tile it crosses (GitHub #277: one high-ground / forbidden block on its way lifted it 0.42 / 0.3 tiles and dropped it again like a step) — UnitView.sync keeps every enemy at z 0, operators and summons keep their tile top, the flyer's shadow lies on the tile under it (shadowZ) on the 2D and the 3D board; golden: unchanged

This commit is contained in:
sganggs committed 2026-10-07 17:49:51 +08:00
1 parent 783bd30afd
commit 14df10a5a3
5 files changed
+81 -11

No files matched your search

+46
View File
@@ -345,3 +345,49 @@ describe('flying units hover FLY_HOVER above the ground, whatever their model',
assert.ok(Math.abs(fly.shadow.position.y - ground.shadow.position.y) < 1e-9, 'the shadow stays on the ground');
});
});
// GitHub #277 (@FrogThai): 飞机经过一格方块时会跟走楼梯一样,有高低差 — a flyer crossing one raised tile (high ground,
// a forbidden block) rose onto the block and dropped back like a step, because the view added the tile's height under
// it before its FLY_HOVER. The official lift is one constant over the route (docs/research/12: Vector3(0, 0.35, 0) added
// while flying), so an enemy flyer hovers from the road (z 0) whatever tile it crosses; its shadow lies on the tile top
// under it. Ground enemies keep to the road as before, and an operator on high ground keeps standing on the block.
describe('an enemy flyer crossing a raised tile keeps its height (GitHub #277)', () => {
const RAISED = { row: 12, col: 6, h: 0.42 }; // one high-ground block ('h', TILE_H.wall) on the flyer's row
const heightAt = (r, c) => (r === RAISED.row && c === RAISED.col ? RAISED.h : 0);
const sample = (x, flags) => ({ x, y: 12, hp: 100, maxHp: 100, sp: 0, spMax: 0, flags, anim: 1, vx: 0.5 });
test('the body stays FLY_HOVER above the road over the block; the shadow lies on the block top', async () => {
const { FLY_HOVER } = await import('../../public/js/render/units.js');
const ctx = fakeViewCtx(fake.P, { assets: store(), cam, heightAt });
const fly = new UnitView(ctx, { id: 1, side: 'enemy', kind: 'enemy', defId: 'enemy_1005_yokai', x: 4, y: 12, maxHp: 100, motion: 'FLY' }, {});
await tick(); await tick();
let t = 0;
const step = (x) => { fly.sync(sample(x, 512), t); fly.update(1 / 60, cam(), t); t += 1 / 60; };
for (let i = 0; i < 180; i++) step(4); // settle the lift on the road
const heights = [];
for (let i = 0; i <= 80; i++) { step(4 + i * 0.05); heights.push(fly.z + fly.hover); } // x 4 → 8 across col 6
const rise = Math.max(...heights) - Math.min(...heights);
assert.ok(rise < 1e-6, `the body height never changes over the block (rose ${rise.toFixed(3)} tiles; before: +${RAISED.h})`);
assert.ok(Math.abs(heights[0] - FLY_HOVER) < 1e-3, `FLY_HOVER above the road (${heights[0].toFixed(3)})`);
for (let i = 0; i < 60; i++) step(6); // hold over the block
assert.equal(fly.z, 0, 'the flyer hovers from the road plane');
const c = cam();
assert.ok(Math.abs(fly.screen.y - c.project(6, 12, FLY_HOVER).y) < 1e-6, 'drawn FLY_HOVER above the road, not above the block');
assert.ok(Math.abs(fly.shadow.position.y - c.project(6, 12, RAISED.h).y) < 0.05, 'its shadow lies on the block top under it');
for (let i = 0; i < 60; i++) step(8); // back over the road
assert.ok(Math.abs(fly.shadow.position.y - c.project(8, 12, 0).y) < 0.05, 'and on the road again past it');
});
test('ground enemies stay on the road and an operator on the block stands on its top', async () => {
const ctx = fakeViewCtx(fake.P, { assets: store(), cam, heightAt });
const walker = new UnitView(ctx, { id: 2, side: 'enemy', kind: 'enemy', defId: 'enemy_1007_slime', x: 6, y: 12, maxHp: 100 }, {});
const op = new UnitView(ctx, { id: 3, side: 'ally', kind: 'chess', defId: 'char_x', tier: 1, x: 6, y: 12, maxHp: 100 }, {});
await tick(); await tick();
for (let i = 0; i < 60; i++) {
walker.sync(sample(6, 0), i / 60); walker.update(1 / 60, cam(), i / 60);
op.sync({ ...sample(6, 0), anim: 0, vx: 0 }, i / 60); op.update(1 / 60, cam(), i / 60);
}
assert.equal(walker.z, 0, 'a ground enemy is never popped onto a block');
assert.ok(Math.abs(op.z - RAISED.h) < 1e-6, `the operator stands on the high ground (${op.z})`);
});
});