Preserve enemy facing through push and pull slides (#418, #427)

This commit is contained in:
sganggs committed 2026-10-09 17:16:49 +08:00
1 parent 584cedc1b4
commit 4980ee786f
13 files changed
+153 -30

No files matched your search

+14 -7
View File
@@ -52,9 +52,12 @@ export class BattleDisplacement {
* 而改变推动的方向或削减力度" — the < 0.25 tile rule still applies). `inward` = a radial push towards `from` (薄绿 S2's "拖拽", PRTS 备注 "实际为
* 反方向(指向薄绿方向)的推开"), never nearer than PULL_STOP_RADIUS to its centre [ASSUMED: "至面前"]. `effect` = a 特效
* push (PRTS 推与拉: one frame less of travel than a 弹道 push — constants.js PUSH_TILES_EFFECT / PUSH_EFFECT_SKILLS).
* The displace fx keeps the enemy's pre-hit facing by default: official footage shows this for a directional push
* (野鬃 S2, #418) and a radial push (莫斯提马 S3). [ASSUMED] for other push sources; an explicitly different
* client action can pass `keepFacing: false`. Pulls use the same display rule; raw displacements remain unmarked.
* Returns the tiles moved.
*/
push(e, force, { from = null, dir = null, fixed = false, fixedAngle = false, inward = false, effect = false } = {}) {
push(e, force, { from = null, dir = null, fixed = false, fixedAngle = false, inward = false, effect = false, keepFacing = true } = {}) {
if (!this._displaceable(e)) { this._staticForce(e, force, false); return 0; }
let level = this.forceLevel(e, force);
const fx0 = fin(from?.x, e.x), fy0 = fin(from?.y, e.y);
@@ -76,7 +79,7 @@ export class BattleDisplacement {
// 失衡 for the row's 位移时间 — also when the 特效 column or a wall shortens the slide [ASSUMED for the wall]; a slide a
// wall stops at the first step gets the 0.1 s floor, like a body that cannot move [ASSUMED: 碰撞、停止 is 待补充]
const hold = pushUnbalance(level);
const moved = this.displace(e, { x: ux, y: uy }, dist, { dur: hold });
const moved = this.displace(e, { x: ux, y: uy }, dist, { dur: hold, keepFacing });
if (hold > 0) this._unbalance(e, moved > 0 ? hold : UNBALANCE_MIN);
return moved;
}
@@ -85,9 +88,13 @@ export class BattleDisplacement {
* Pull enemy `e` with 力度 `force` towards the point `to` (PRTS 推与拉 §拉力 / §捕网): 受力等级 ≥ 0 — all the way, until it
* is within `stop` tiles of `center` (急停; `center` defaults to `to`, `stop` to PULL_STOP_RADIUS) or reaches `to`;
* −1 — PULL_WEAK_SHARE of its starting distance to `to`; −2 — PULL_CRAWL tiles; ≤ −3 — nothing. `pullToFront` aims at
* the official 拉力起点 in front of an operator. Returns the tiles moved.
* the official 拉力起点 in front of an operator. The displace fx keeps the pre-hit facing by default, as for pushes.
* The supplied 歌蕾蒂娅 S1/S2 clip directly shows the target keeping its facing before and after a hook pull.
* [ASSUMED] for other pull directions/sources: the clip does not isolate travel against the prior facing.
* PRTS distinguishes 薄绿's inward push from a hook pull, not their model facing.
* Pass `keepFacing: false` for a documented exception. Returns the tiles moved.
*/
pull(e, force, { to, center = null, stop = PULL_STOP_RADIUS } = {}) {
pull(e, force, { to, center = null, stop = PULL_STOP_RADIUS, keepFacing = true } = {}) {
if (!to) return 0;
// an enemy the puller itself blocks already stands in front of it (at contact) [ASSUMED: no pull, no unblocking]
if (e && center && center.side === 'ally' && e.blockedBy === center) return 0;
@@ -110,7 +117,7 @@ export class BattleDisplacement {
if (disc >= 0) { const t = -wu - Math.sqrt(disc); if (t >= 0) full = Math.min(full, t); }
}
const dist = level >= 0 ? full : level === -1 ? Math.min(full, PULL_WEAK_SHARE * d0) : Math.min(full, PULL_CRAWL);
const moved = dist > 1e-6 ? this.displace(e, { x: ux, y: uy }, dist, { dur: hold }) : 0;
const moved = dist > 1e-6 ? this.displace(e, { x: ux, y: uy }, dist, { dur: hold, keepFacing }) : 0;
this._unbalance(e, hold);
return moved;
}
@@ -177,7 +184,7 @@ export class BattleDisplacement {
* ⇒ no movement (_displaceable). The tiles it may cross follow its movement (`motion`): a hovering enemy walks the
* ground, so it stays on ground-passable tiles.
*/
displace(e, dir, distance, { dur = 0 } = {}) {
displace(e, dir, distance, { dur = 0, keepFacing = false } = {}) {
if (!this._displaceable(e) || !dir) return 0;
const dxv = fin(dir.x, 0), dyv = fin(dir.y, 0);
const len = hypot(dxv, dyv);
@@ -202,7 +209,7 @@ export class BattleDisplacement {
e.atkStandUntil = -Infinity;
if (e.route) e.route.pts = null;
// `dur` (game s): the 失衡 the push / pull gives — the client's slide takes that long (render/units.js slideTo)
this.fx('displace', dur > 0 ? { x: e.x, y: e.y, id: e.id, dur } : { x: e.x, y: e.y, id: e.id });
this.fx('displace', { x: e.x, y: e.y, id: e.id, ...(dur > 0 ? { dur } : {}), ...(keepFacing ? { keepFacing: true } : {}) });
}
return moved;
}