From 6e4861117f2bf8ddcc3b0c1e133903b065a73ccc Mon Sep 17 00:00:00 2001 From: markb5 Date: Thu, 12 Mar 2026 13:17:31 -0400 Subject: [PATCH] fix:Npcs facing target too soon --- src/engine/World.ts | 24 +++++------- src/engine/entity/Npc.ts | 10 ++--- src/engine/entity/PathingEntity.ts | 39 +++++++++++-------- src/engine/entity/Player.ts | 25 +++++++++--- .../game/client/handler/OpHeldHandler.ts | 2 - .../game/client/handler/OpHeldTHandler.ts | 2 - .../game/client/handler/OpHeldUHandler.ts | 2 - 7 files changed, 55 insertions(+), 49 deletions(-) diff --git a/src/engine/World.ts b/src/engine/World.ts index 5e8e25c12..0e1a2d408 100644 --- a/src/engine/World.ts +++ b/src/engine/World.ts @@ -617,11 +617,6 @@ class World { continue; } - if ((!player.target || player.target instanceof Loc || player.target instanceof Obj) && player.faceEntity !== -1) { - player.faceEntity = -1; - player.masks |= player.entitymask; - } - if (!player.busy() && player.opcalled) { player.moveClickRequest = false; } else { @@ -721,6 +716,8 @@ class World { } // - engine queue player.processEngineQueue(); + // Update target facing + player.setFaceEntity(); // - interactions // - movement player.processInteraction(); @@ -906,11 +903,13 @@ class World { player.client.state = 1; - player.client.send(Uint8Array.from([ - 2, - Math.min(player.staffModLevel, 2), - 1 // mouse tracking can only be enabled on login - ])); + player.client.send( + Uint8Array.from([ + 2, + Math.min(player.staffModLevel, 2), + 1 // mouse tracking can only be enabled on login + ]) + ); const remote = player.client.remoteAddress; if (remote.indexOf('.') !== -1) { @@ -1874,10 +1873,7 @@ class World { } else if (reply === 10) { // hop timer const { remaining } = msg; - client.send(Uint8Array.from([ - 21, - Math.min(255, remaining! / 1000) - ])); + client.send(Uint8Array.from([21, Math.min(255, remaining! / 1000)])); client.close(); return; } diff --git a/src/engine/entity/Npc.ts b/src/engine/entity/Npc.ts index 5d0ff0ec7..6385fa860 100644 --- a/src/engine/entity/Npc.ts +++ b/src/engine/entity/Npc.ts @@ -179,6 +179,8 @@ export default class Npc extends PathingEntity { this.processQueue(); // Movement-Interactions this.processMovementInteraction(); + // Update target facing + this.setFaceEntity(); // Dev note: Is this necessary? this.validateDistanceWalked(); } @@ -403,16 +405,12 @@ export default class Npc extends PathingEntity { clearInteraction(): void { super.clearInteraction(); this.targetOp = NpcMode.NONE; - this.faceEntity = -1; - this.masks |= NpcInfoProt.FACE_ENTITY; } resetDefaults(): void { this.clearInteraction(); const type: NpcType = NpcType.get(this.type); this.targetOp = type.defaultmode; - this.faceEntity = -1; - this.masks |= this.entitymask; const npcType: NpcType = NpcType.get(this.type); this.huntMode = npcType.huntmode; @@ -432,7 +430,7 @@ export default class Npc extends PathingEntity { this.uid = (type << 16) | this.nid; this.resetOnRevert = reset; - if(reset) { + if (reset) { const npcType = NpcType.get(type); for (let index = 0; index < npcType.stats.length; index++) { const level = npcType.stats[index]; @@ -989,7 +987,7 @@ export default class Npc extends PathingEntity { // --- Other - private getTrigger(type : NpcType): ScriptFile | null { + private getTrigger(type: NpcType): ScriptFile | null { const trigger: ServerTriggerType | null = this.getTriggerForMode(this.targetOp); if (trigger) { return ScriptProvider.getByTrigger(trigger, this.type, type.category) ?? null; diff --git a/src/engine/entity/PathingEntity.ts b/src/engine/entity/PathingEntity.ts index 6224b1501..3200230a1 100644 --- a/src/engine/entity/PathingEntity.ts +++ b/src/engine/entity/PathingEntity.ts @@ -510,6 +510,26 @@ export default abstract class PathingEntity extends Entity { } } + setFaceEntity(): void { + const oldEntity = this.faceEntity; + if (this.target instanceof Player) { + const playerSlot: number = this.target.slot + 32768; + if (this.faceEntity !== playerSlot) { + this.faceEntity = playerSlot; + } + } else if (this.target instanceof Npc) { + const nid: number = this.target.nid; + if (this.faceEntity !== nid) { + this.faceEntity = nid; + } + } else { + this.faceEntity = -1; + } + if (this.faceEntity !== oldEntity) { + this.masks |= this.entitymask; + } + } + setInteraction(interaction: Interaction, target: Entity, op: TargetOp, com?: number): boolean { if (!target.isValid(this instanceof Player ? this.hash64 : undefined)) { return false; @@ -530,19 +550,7 @@ export default abstract class PathingEntity extends Entity { this.focus(CoordGrid.fine(target.x, target.width), CoordGrid.fine(target.z, target.length), target instanceof NonPathingEntity && interaction === Interaction.ENGINE); - if (target instanceof Player) { - const playerSlot: number = target.slot + 32768; - if (this.faceEntity !== playerSlot) { - this.faceEntity = playerSlot; - this.masks |= this.entitymask; - } - } else if (target instanceof Npc) { - const nid: number = target.nid; - if (this.faceEntity !== nid) { - this.faceEntity = nid; - this.masks |= this.entitymask; - } - } else { + if (target instanceof NonPathingEntity) { this.targetX = CoordGrid.fine(target.x, target.width); this.targetZ = CoordGrid.fine(target.z, target.length); } @@ -614,10 +622,7 @@ export default abstract class PathingEntity extends Entity { this.faceSquareX = -1; this.faceSquareZ = -1; - if (!this.target && this.faceEntity !== -1) { - this.masks |= this.entitymask; - this.faceEntity = -1; - } + this.setFaceEntity(); } private takeStep(): number | null { diff --git a/src/engine/entity/Player.ts b/src/engine/entity/Player.ts index 47722bbaf..bcd666110 100644 --- a/src/engine/entity/Player.ts +++ b/src/engine/entity/Player.ts @@ -409,9 +409,17 @@ export default class Player extends PathingEntity { constructor(username: string, username37: bigint, hash64: bigint) { super( - 0, 3094, 3106, // tutorial island - 1, 1, - EntityLifeCycle.FOREVER, MoveRestrict.NORMAL, BlockWalk.NPC, MoveStrategy.SMART, PlayerInfoProt.FACE_COORD, PlayerInfoProt.FACE_ENTITY + 0, + 3094, + 3106, // tutorial island + 1, + 1, + EntityLifeCycle.FOREVER, + MoveRestrict.NORMAL, + BlockWalk.NPC, + MoveStrategy.SMART, + PlayerInfoProt.FACE_COORD, + PlayerInfoProt.FACE_ENTITY ); this.username = username; @@ -1747,7 +1755,7 @@ export default class Player extends PathingEntity { const { basevar, startbit, endbit } = varbit; const mask = Packet.bitmask[endbit - startbit + 1]; - return this.vars[basevar] >> startbit & mask; + return (this.vars[basevar] >> startbit) & mask; } setVarBit(id: number, value: number) { @@ -1764,7 +1772,7 @@ export default class Player extends PathingEntity { } mask <<= startbit; - this.setVar(basevar, mask & value << startbit | this.vars[basevar] & ~mask); + this.setVar(basevar, (mask & (value << startbit)) | (this.vars[basevar] & ~mask)); } private writeVarp(id: number, value: number): void { @@ -1946,7 +1954,12 @@ export default class Player extends PathingEntity { // todo: make compiler do this at pack time playSong(name: string) { // todo: don't rely on MidiPack (server should be runnable using only packed content) - const id = MidiPack.getByName(name.toLowerCase().replaceAll(' ', '_').replace(/[^a-z0-9_-]/g, '')); + const id = MidiPack.getByName( + name + .toLowerCase() + .replaceAll(' ', '_') + .replace(/[^a-z0-9_-]/g, '') + ); if (id !== -1) { this.write(new MidiSong(id)); } diff --git a/src/network/game/client/handler/OpHeldHandler.ts b/src/network/game/client/handler/OpHeldHandler.ts index 7779224d6..e3386d56c 100644 --- a/src/network/game/client/handler/OpHeldHandler.ts +++ b/src/network/game/client/handler/OpHeldHandler.ts @@ -56,8 +56,6 @@ export default class OpHeldHandler extends ClientGameMessageHandler { } player.moveClickRequest = false; // uses the dueling ring op to move whilst busy & queue pending: https://youtu.be/GPfN3Isl2rM - player.faceEntity = -1; - player.masks |= player.entitymask; // opheld5 gets wealth logged in content if (message.op !== 5) { diff --git a/src/network/game/client/handler/OpHeldTHandler.ts b/src/network/game/client/handler/OpHeldTHandler.ts index 2cf2ff3dc..f747a54e4 100644 --- a/src/network/game/client/handler/OpHeldTHandler.ts +++ b/src/network/game/client/handler/OpHeldTHandler.ts @@ -55,8 +55,6 @@ export default class OpHeldTHandler extends ClientGameMessageHandler { player.lastSlot = slot; player.clearPendingAction(); - player.faceEntity = -1; - player.masks |= player.entitymask; player.addSessionLog(LoggerEventType.MODERATOR, `Cast ${spellCom.comName} on ${ObjType.get(obj).debugname}`); diff --git a/src/network/game/client/handler/OpHeldUHandler.ts b/src/network/game/client/handler/OpHeldUHandler.ts index 1a5db0544..ca5d0aa3e 100644 --- a/src/network/game/client/handler/OpHeldUHandler.ts +++ b/src/network/game/client/handler/OpHeldUHandler.ts @@ -84,8 +84,6 @@ export default class OpHeldUHandler extends ClientGameMessageHandler { const useObjType = ObjType.get(player.lastUseItem); player.clearPendingAction(); - player.faceEntity = -1; - player.masks |= player.entitymask; if ((objType.members || useObjType.members) && !Environment.NODE_MEMBERS) { player.messageGame("To use this item please login to a members' server.");