diff --git a/Server/src/main/core/game/node/entity/Entity.java b/Server/src/main/core/game/node/entity/Entity.java index ffaa8f461..266d9e7a8 100644 --- a/Server/src/main/core/game/node/entity/Entity.java +++ b/Server/src/main/core/game/node/entity/Entity.java @@ -280,7 +280,7 @@ public abstract class Entity extends Node { impactHandler.getImpactQueue().clear(); impactHandler.setDisabledTicks(10); timers.onEntityDeath(); - removeAttribute("combat-time"); + clearCombatDeathState(killer); face(null); //Check if it's a Loar shade and transform back into the shadow version. if(this.getId() == 1240 || this.getId() == 1241){ @@ -288,6 +288,37 @@ public abstract class Entity extends Node { } } + private void clearCombatDeathState(Entity killer) { + Object attacker = getAttribute("combat-attacker"); + Object aggressor = getAttribute("aggressor"); + properties.getCombatPulse().stop(); + removeAttribute("combat-time"); + removeAttribute("combat-attacker"); + removeAttribute("aggressor"); + clearCombatReference(killer); + if (attacker instanceof Entity) { + clearCombatReference((Entity) attacker); + } + if (aggressor instanceof Entity) { + clearCombatReference((Entity) aggressor); + } + } + + private void clearCombatReference(Entity entity) { + if (entity == null) { + return; + } + if (entity.getAttribute("combat-attacker") == this) { + entity.removeAttribute("combat-attacker"); + } + if (entity.getAttribute("aggressor") == this) { + entity.removeAttribute("aggressor"); + } + if (entity.getProperties().getCombatPulse().getVictim() == this) { + entity.getProperties().getCombatPulse().stop(); + } + } + /** * Updates the location of an entity. * @param last the last location. diff --git a/Server/src/test/kotlin/content/CombatMovementTests.kt b/Server/src/test/kotlin/content/CombatMovementTests.kt index ed28d3b1d..a47b914b6 100644 --- a/Server/src/test/kotlin/content/CombatMovementTests.kt +++ b/Server/src/test/kotlin/content/CombatMovementTests.kt @@ -31,6 +31,7 @@ import org.rs09.consts.Items import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertFalse import org.junit.jupiter.api.Assertions.assertNotEquals +import org.junit.jupiter.api.Assertions.assertNull import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test import kotlin.math.abs @@ -808,6 +809,46 @@ class CombatMovementTests { } } + @Test + fun npcRespawnShouldNotKeepPreviousPlayerCombatAggroState() { + TestUtils.getMockPlayer("combat_respawn_previous_player").use { player -> + val origin = arenaOrigin() + place(player, origin) + configureMelee(player) + + val npc = NPC.create(100, origin.transform(1, 0, 0)) + npc.init() + try { + configureMelee(npc) + npc.setAttribute("disable:drop", true) + npc.setAttribute("combat-attacker", player) + player.setAttribute("combat-attacker", npc) + player.setAttribute("aggressor", npc) + player.attack(npc) + npc.attack(player) + + assertEquals(player, npc.getAttribute("combat-attacker")) + assertEquals(npc, player.getAttribute("combat-attacker")) + assertEquals(npc, player.getAttribute("aggressor")) + + npc.finalizeDeath(npc) + npc.respawnTick = GameWorld.ticks + npc.isRespawning = true + npc.tick() + + assertNull(npc.getAttribute("combat-attacker")) + assertNull(npc.getAttribute("aggressor")) + assertNull(player.getAttribute("combat-attacker")) + assertNull(player.getAttribute("aggressor")) + assertFalse(npc.properties.combatPulse.isAttacking) + assertFalse(npc.properties.combatPulse.isInCombat) + } finally { + npc.clear() + CombatMovementIntents.clear() + } + } + } + @Test fun combatMovementShouldOnlyQueueImmediateMovementSteps() { TestUtils.getMockPlayer("combat_short_queue_attacker").use { player ->