Spaces:
Running
Running
Fix hero spinning while walking/attacking: face only when moving, keep attack facing, stop walking into target
Browse files- index.html +20 -7
index.html
CHANGED
|
@@ -851,7 +851,10 @@ button.big.ghost { background:rgba(20,16,10,.8); color:var(--gold); }
|
|
| 851 |
|
| 852 |
// movement: joystick / keyboard overrides path
|
| 853 |
const sp = HERO_SPEED * (t < H.slowUntil ? 0.6 : 1) * (t < H.stunUntil ? 0 : 1);
|
| 854 |
-
let manual = false;
|
|
|
|
|
|
|
|
|
|
| 855 |
if (S.joy.on || S.keyDir) {
|
| 856 |
const [vx, vy] = S.joy.on ? [S.joy.vx, S.joy.vy] : S.keyDir;
|
| 857 |
const len = Math.hypot(vx, vy);
|
|
@@ -860,9 +863,16 @@ button.big.ghost { background:rgba(20,16,10,.8); color:var(--gold); }
|
|
| 860 |
const wx = (vx / HW + vy / HH) / 2, wy = (vy / HH - vx / HW) / 2;
|
| 861 |
const wl = Math.hypot(wx, wy) || 1;
|
| 862 |
const k = sp * dt * Math.min(1, len);
|
| 863 |
-
|
| 864 |
-
|
| 865 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 866 |
manual = true;
|
| 867 |
H.path = []; H.chase = false; H.pending = null;
|
| 868 |
}
|
|
@@ -880,9 +890,10 @@ button.big.ghost { background:rgba(20,16,10,.8); color:var(--gold); }
|
|
| 880 |
}
|
| 881 |
if (H.path.length) {
|
| 882 |
if (followPath(M, H, H.path, sp, dt)) {
|
|
|
|
| 883 |
const nx = H.path.length ? H.path[0] : null;
|
| 884 |
-
if (nx) H.dir = dirOf(nx[0] - H.x, nx[1] - H.y);
|
| 885 |
-
setAnim(H, 'run');
|
| 886 |
}
|
| 887 |
} else if (t >= H.lockUntil) setAnim(H, 'stance');
|
| 888 |
}
|
|
@@ -898,6 +909,8 @@ button.big.ghost { background:rgba(20,16,10,.8); color:var(--gold); }
|
|
| 898 |
if (H.target && !H.target.dead) {
|
| 899 |
const inRange = dist(H.x, H.y, H.target.x, H.target.y) <= S.world.classes[H.cls].range + 0.25;
|
| 900 |
if (inRange) {
|
|
|
|
|
|
|
| 901 |
if (S.auto) autoFight();
|
| 902 |
else if (S.atkOn && H.atkCd <= 0) doBasic();
|
| 903 |
}
|
|
@@ -1467,7 +1480,7 @@ button.big.ghost { background:rgba(20,16,10,.8); color:var(--gold); }
|
|
| 1467 |
get state() {
|
| 1468 |
const H = S.H;
|
| 1469 |
return H && S.M ? {
|
| 1470 |
-
map: S.M.id, hero: { x: H.x, y: H.y, hp: H.hp, mp: H.mp, lvl: H.lvl, xp: H.xp, dead: H.dead, potions: H.potions, target: H.target ? H.target.def.name : null, tx: H.target ? H.target.x : null, ty: H.target ? H.target.y : null,
|
| 1471 |
enemies: S.enemies.filter((e) => !e.dead).length, kills: S.kills, auto: S.auto, lock: S.lock,
|
| 1472 |
} : null;
|
| 1473 |
},
|
|
|
|
| 851 |
|
| 852 |
// movement: joystick / keyboard overrides path
|
| 853 |
const sp = HERO_SPEED * (t < H.slowUntil ? 0.6 : 1) * (t < H.stunUntil ? 0 : 1);
|
| 854 |
+
let manual = false, walking = false;
|
| 855 |
+
// while an attack animation is playing, keep the attack facing and do not replace the swing with run/stance
|
| 856 |
+
const attacking = t < H.lockUntil;
|
| 857 |
+
const inReach = H.target && !H.target.dead && dist(H.x, H.y, H.target.x, H.target.y) <= S.world.classes[H.cls].range + 0.25;
|
| 858 |
if (S.joy.on || S.keyDir) {
|
| 859 |
const [vx, vy] = S.joy.on ? [S.joy.vx, S.joy.vy] : S.keyDir;
|
| 860 |
const len = Math.hypot(vx, vy);
|
|
|
|
| 863 |
const wx = (vx / HW + vy / HH) / 2, wy = (vy / HH - vx / HW) / 2;
|
| 864 |
const wl = Math.hypot(wx, wy) || 1;
|
| 865 |
const k = sp * dt * Math.min(1, len);
|
| 866 |
+
let mx = wx / wl * k, my = wy / wl * k;
|
| 867 |
+
// already in reach of the locked target: do not walk into it (that made the hero overshoot back and forth)
|
| 868 |
+
if (inReach && mx * (H.target.x - H.x) + my * (H.target.y - H.y) > 0) { mx = 0; my = 0; }
|
| 869 |
+
const x0 = H.x, y0 = H.y;
|
| 870 |
+
moveEnt(M, H, mx, my, H.r);
|
| 871 |
+
// face the walking direction only when we actually moved (pushing against a blocker must not spin the sprite)
|
| 872 |
+
const moved = Math.hypot(H.x - x0, H.y - y0);
|
| 873 |
+
walking = moved > k * 0.3;
|
| 874 |
+
if (!attacking && walking) H.dir = dirOf(wx, wy);
|
| 875 |
+
if (!attacking) setAnim(H, walking ? 'run' : 'stance');
|
| 876 |
manual = true;
|
| 877 |
H.path = []; H.chase = false; H.pending = null;
|
| 878 |
}
|
|
|
|
| 890 |
}
|
| 891 |
if (H.path.length) {
|
| 892 |
if (followPath(M, H, H.path, sp, dt)) {
|
| 893 |
+
walking = true;
|
| 894 |
const nx = H.path.length ? H.path[0] : null;
|
| 895 |
+
if (nx && !attacking) H.dir = dirOf(nx[0] - H.x, nx[1] - H.y);
|
| 896 |
+
if (!attacking) setAnim(H, 'run');
|
| 897 |
}
|
| 898 |
} else if (t >= H.lockUntil) setAnim(H, 'stance');
|
| 899 |
}
|
|
|
|
| 909 |
if (H.target && !H.target.dead) {
|
| 910 |
const inRange = dist(H.x, H.y, H.target.x, H.target.y) <= S.world.classes[H.cls].range + 0.25;
|
| 911 |
if (inRange) {
|
| 912 |
+
// standing and fighting: keep facing the target (no turning back and forth while attacking)
|
| 913 |
+
if (!walking && !attacking) faceHero(H.target.x - H.x, H.target.y - H.y);
|
| 914 |
if (S.auto) autoFight();
|
| 915 |
else if (S.atkOn && H.atkCd <= 0) doBasic();
|
| 916 |
}
|
|
|
|
| 1480 |
get state() {
|
| 1481 |
const H = S.H;
|
| 1482 |
return H && S.M ? {
|
| 1483 |
+
map: S.M.id, hero: { x: H.x, y: H.y, hp: H.hp, mp: H.mp, lvl: H.lvl, xp: H.xp, dead: H.dead, potions: H.potions, target: H.target ? H.target.def.name : null, tx: H.target ? H.target.x : null, ty: H.target ? H.target.y : null, chase: H.chase, atkCd: H.atkCd, dir: H.dir, anim: H.anim.n, path: H.path.length },
|
| 1484 |
enemies: S.enemies.filter((e) => !e.dead).length, kills: S.kills, auto: S.auto, lock: S.lock,
|
| 1485 |
} : null;
|
| 1486 |
},
|