From a9d6ce021b6dfb0bb62c3ceb7b499b797b3c408d Mon Sep 17 00:00:00 2001 From: dam <27978131-real_damighty@users.noreply.gitlab.com> Date: Thu, 14 May 2026 20:14:29 +0300 Subject: [PATCH] Telepathic interaction fix, ::to puro --- Server/src/main/core/ServerConstants.kt | 1 + .../core/game/interaction/MovementPulse.java | 20 ++++-- .../src/test/kotlin/core/PathfinderTests.kt | 66 +++++++++++++++++++ 3 files changed, 83 insertions(+), 4 deletions(-) diff --git a/Server/src/main/core/ServerConstants.kt b/Server/src/main/core/ServerConstants.kt index 8c5f493a4..1155841a9 100644 --- a/Server/src/main/core/ServerConstants.kt +++ b/Server/src/main/core/ServerConstants.kt @@ -235,6 +235,7 @@ class ServerConstants { arrayOf(Location.create(2722, 4886, 0), "quest the golem 1"), arrayOf(Location.create(2704, 5349, 0), "dorgeshuun", "dorg"), arrayOf(Location.create(2711, 10132, 0), "brine rats"), + arrayOf(Location.create(2591, 4320, 0), "puro puro", "puro-puro", "puropuro", "puro", "impling maze"), arrayOf(Location.create(2328, 3677, 0), "piscatoris"), arrayOf(Location.create(2660, 3158, 0), "fishing trawler", "trawler"), arrayOf(Location.create(2800, 3667, 0), "mountain camp"), diff --git a/Server/src/main/core/game/interaction/MovementPulse.java b/Server/src/main/core/game/interaction/MovementPulse.java index 9b402e747..cef606aba 100644 --- a/Server/src/main/core/game/interaction/MovementPulse.java +++ b/Server/src/main/core/game/interaction/MovementPulse.java @@ -86,6 +86,8 @@ public abstract class MovementPulse extends Pulse { private Location previousLoc; + private boolean explicitInteractionLocation; + /** * Constructs a new {@code MovementPulse} {@code Object}. * @@ -300,7 +302,7 @@ public abstract class MovementPulse extends Pulse { } private boolean hasExplicitInteractionLocation() { - return optionHandler != null || useHandler != null || overrideMethod != null; + return explicitInteractionLocation; } /** @@ -357,11 +359,14 @@ public abstract class MovementPulse extends Pulse { } Location loc = null; + boolean explicitLocation = false; if (optionHandler != null) { loc = optionHandler.getDestination(mover, destination); + explicitLocation = loc != null; } else if (useHandler != null) { loc = useHandler.getDestination((Player) mover, destination); + explicitLocation = loc != null; } else if (isInsideEntity(mover.getLocation())) { loc = findBorderLocation(); } @@ -370,13 +375,20 @@ public abstract class MovementPulse extends Pulse { loc = destinationFlag.getDestination(mover, destination); } else if (loc == null && overrideMethod != null) { loc = overrideMethod.invoke(mover, destination); - if (loc == destination.getLocation() && destinationFlag != null) loc = destinationFlag.getDestination(mover, destination); - else if (loc == destination.getLocation()) loc = null; + explicitLocation = loc != null; + if (loc == destination.getLocation() && destinationFlag != null) { + loc = destinationFlag.getDestination(mover, destination); + explicitLocation = false; + } else if (loc == destination.getLocation()) { + loc = null; + explicitLocation = false; + } } - if (destination instanceof NPC && mover.getProperties().getCombatPulse().getVictim() != destination) + if (!explicitLocation && destination instanceof NPC && mover.getProperties().getCombatPulse().getVictim() != destination) loc = checkForEntityPathInterrupt(loc != null ? loc : destination.getLocation()); + explicitInteractionLocation = explicitLocation; if (interactLocation == null) interactLocation = loc; if (destination instanceof Entity || interactLocation == null || (mover.getWalkingQueue().getQueue() diff --git a/Server/src/test/kotlin/core/PathfinderTests.kt b/Server/src/test/kotlin/core/PathfinderTests.kt index 721f2abea..ecf5fecc5 100644 --- a/Server/src/test/kotlin/core/PathfinderTests.kt +++ b/Server/src/test/kotlin/core/PathfinderTests.kt @@ -312,6 +312,72 @@ class PathfinderTests { } } + @Test + fun entityOptionHandlerShouldNotInteractFromNpcQueuedDestination() { + TestUtils.getMockPlayer("queuedNpcPredictionGuard").use { p -> + val origin = Location.create(3200, 3600, 0) + val npc = NPC.create(0, origin.transform(0, 1, 0)) + npc.init() + p.location = origin.transform(1, 0, 0) + npc.walkingQueue.reset(false) + npc.walkingQueue.addPath(origin.transform(4, 1, 0).x, origin.transform(4, 1, 0).y) + + val optionHandler = object : OptionHandler() { + override fun newInstance(_arg: Any?): Plugin { + return this + } + + override fun handle(_player: Player?, _node: Node?, _option: String?): Boolean { + return true + } + } + var pulseLocation: Location? = null + var pulseTargetLocation: Location? = null + try { + GameWorld.Pulser.submit(object : MovementPulse(p, npc, optionHandler) { + override fun pulse(): Boolean { + pulseLocation = p.location + pulseTargetLocation = npc.location + return true + } + }) + + TestUtils.advanceTicks(1, false) + Assertions.assertNull( + pulseLocation, + "Option-handler entity interaction must not fire from a tile that only reaches the NPC's queued destination." + ) + + repeat(8) { + if (pulseLocation == null) { + TestUtils.advanceTicks(1, false) + } + } + + val actualPulseLocation = pulseLocation + ?: throw AssertionError("Expected the movement pulse to eventually reach the moving NPC.") + val actualTargetLocation = pulseTargetLocation + ?: throw AssertionError("Expected target location to be captured when the pulse fired.") + Assertions.assertTrue( + Pathfinder.canInteract( + actualPulseLocation.x, + actualPulseLocation.y, + p.size(), + actualTargetLocation.x, + actualTargetLocation.y, + npc.size(), + npc.size(), + 0, + actualPulseLocation.z, + null + ), "Entity interaction must fire only from a currently valid interaction tile." + ) + } finally { + npc.clear() + } + } + } + @Test fun runEnabledEntityMovementPulseShouldCatchWalkingNpcMovingDirectlyAway() { TestUtils.getMockPlayer("runNpcInteractionChaser").use { p ->