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:
1 parent
783bd30afd
commit
14df10a5a3
5 files changed
+81
-11
No files matched your search
@@ -137,8 +137,9 @@ export const SPINE_STUCK_MS = 5000;
|
||||
/** Heights above this count as standing on a raised top (bench pads are the lowest raised tiles, 0.16). */
|
||||
const RAISED_Z = 0.12;
|
||||
/**
|
||||
* Flying units hover this many tiles above the ground they cross (PR #211 by @xcdoge; the owner's decision of
|
||||
* 2026-10-06; docs/research/12-flying-visuals-official.md).
|
||||
* Flying units hover this many tiles up (PR #211 by @xcdoge; the owner's decision of 2026-10-06;
|
||||
* docs/research/12-flying-visuals-official.md): an enemy flyer above the road (z 0) whatever tile it crosses — a raised
|
||||
* block under it is no step (GitHub #277) —, an operator or summon above its tile.
|
||||
*
|
||||
* The official client's fly offset is a **single constant, `Vector3(0, 0.35f, 0)`**: `Torappu.Battle.CharacterAnimator`'s
|
||||
* constructor stores it in the instance field at +0x114 (`GameAssembly.dll` 0x180600555 reads the constant at 0x186a78a50 =
|
||||
@@ -373,9 +374,13 @@ export class UnitView {
|
||||
this.golden = !!info.golden;
|
||||
this.tier = clamp(Number(info.tier) || 1, 1, 6);
|
||||
this.x = Number(info.x) || 0; this.y = Number(info.y) || 0; this.z = 0;
|
||||
this.zTarget = null; // battle: standing height the feet ease towards (tile top under the unit)
|
||||
this.zTarget = null; // battle: standing height the feet ease towards (tile top under the unit; 0 for enemies)
|
||||
/** @type {number|null} */
|
||||
this.shadowZ = null; // an enemy flyer's shadow height (the tile top under it), else null: the shadow is at z
|
||||
/** @type {number|null} */
|
||||
this.shadowZTarget = null;
|
||||
this.flying = info.motion === 'FLY';
|
||||
this.hover = 0; // flying: body height above the ground under it
|
||||
this.hover = 0; // flying: body height above the road / the ground under it
|
||||
this.dir = this.isEnemy ? null : unitDir(info);
|
||||
// whether the direction is known (UnitInfo / piece `dir`), not just the legacy ±1: battle and scouting views show
|
||||
// the ground wedge only then (a derived RIGHT would mislabel an UP / DOWN operator)
|
||||
@@ -704,10 +709,17 @@ export class UnitView {
|
||||
this.elFill = this.el ? s.elFill || 0 : 0; this.elUntil = this.el ? s.elUntil || 0 : 0; this.elDur = this.el ? s.elDur || 0 : 0;
|
||||
this.x = s.x; this.y = s.y;
|
||||
this.flying = !!(s.flags & UF.FLYING) || this.info.motion === 'FLY';
|
||||
// ground enemies only ever walk low tiles (a rounding step onto a block edge must not pop them up)
|
||||
const gz = this.isEnemy && !this.flying ? 0 : groundZ(this.ctx, s.x, s.y);
|
||||
// enemies keep to the road plane: ground enemies only ever walk low tiles (a rounding step onto a block edge must not
|
||||
// pop them up), and a flyer hovers FLY_HOVER above the road whatever tile it crosses — the official lift is one
|
||||
// constant over the route (docs/research/12), so a block under it is no step (GitHub #277: a flyer passing over one
|
||||
// high-ground / forbidden block rose and dropped like stairs). Its shadow still lies on the tile top under it.
|
||||
const floor = groundZ(this.ctx, s.x, s.y);
|
||||
const gz = this.isEnemy ? 0 : floor;
|
||||
if (this.zTarget == null) this.z = gz;
|
||||
this.zTarget = gz;
|
||||
this.shadowZTarget = this.isEnemy && this.flying ? floor : null;
|
||||
if (this.shadowZTarget == null) this.shadowZ = null;
|
||||
else if (this.shadowZ == null) this.shadowZ = this.z;
|
||||
if (s.maxHp > 0) this.maxHp = s.maxHp;
|
||||
const hp = clamp(s.hp, 0, this.maxHp);
|
||||
if (hp < this.hp - 0.5 && this.isBoss) this.shake = 0.25;
|
||||
@@ -941,6 +953,10 @@ export class UnitView {
|
||||
const d = this.zTarget - this.z;
|
||||
this.z = Math.abs(d) < 1e-3 ? this.zTarget : this.z + d * Math.min(1, dt * 12);
|
||||
}
|
||||
if (this.shadowZTarget != null && this.shadowZ !== this.shadowZTarget) {
|
||||
const d = this.shadowZTarget - this.shadowZ;
|
||||
this.shadowZ = Math.abs(d) < 1e-3 ? this.shadowZTarget : this.shadowZ + d * Math.min(1, dt * 12);
|
||||
}
|
||||
const hoverTo = this.flying && this.alive ? FLY_HOVER : 0;
|
||||
if (this.hover !== hoverTo) this.hover = Math.abs(hoverTo - this.hover) < 1e-3 ? hoverTo : this.hover + (hoverTo - this.hover) * Math.min(1, dt * 6);
|
||||
const p = cam.project(this.x, this.y, this.z + this.hover + this.lift, this.screen);
|
||||
@@ -977,9 +993,11 @@ export class UnitView {
|
||||
if (this._cull(bx, by, s, dt)) return;
|
||||
const flip = (this.isEnemy ? (ENEMY_MODEL_FACES_LEFT ? -this.visFacing : this.visFacing) : this.visFacing) * (this.mirrorX ? -1 : 1);
|
||||
|
||||
// shadow (on a raised top it is drawn with that block row, else in the shadow layer under everything)
|
||||
placeOnGround(this.ctx, this.shadow, this.ctx.layers.shadow, this.y, this.z);
|
||||
const sh = cam.project(this.x, this.y, this.z, SH_P);
|
||||
// shadow (on a raised top it is drawn with that block row, else in the shadow layer under everything); an enemy
|
||||
// flyer's lies on the tile under it while its body hovers from the road (shadowZ, GitHub #277)
|
||||
const shz = this.shadowZ ?? this.z;
|
||||
placeOnGround(this.ctx, this.shadow, this.ctx.layers.shadow, this.y, shz);
|
||||
const sh = cam.project(this.x, this.y, shz, SH_P);
|
||||
this.shadow.position.set(sh.x, sh.y);
|
||||
const shw = s * (this.isBoss ? 1.6 : 0.95) / this.shadow.texture.width;
|
||||
this.shadow.scale.set(shw, shw * (this.shadow.texture === shadowTexture() ? 1 : 1.05));
|
||||
|
||||
Reference in new issue
Block a user