diff --git a/game/src/main/kotlin/content/area/misthalin/lumbridge/swamp/LumbridgeSwamp.kt b/game/src/main/kotlin/content/area/misthalin/lumbridge/swamp/LumbridgeSwamp.kt index f48593f4bf..645c0fbbce 100644 --- a/game/src/main/kotlin/content/area/misthalin/lumbridge/swamp/LumbridgeSwamp.kt +++ b/game/src/main/kotlin/content/area/misthalin/lumbridge/swamp/LumbridgeSwamp.kt @@ -8,6 +8,7 @@ import world.gregs.voidps.engine.Script import world.gregs.voidps.engine.client.instruction.handle.interactPlayer import world.gregs.voidps.engine.client.message import world.gregs.voidps.engine.entity.character.npc.NPCs +import world.gregs.voidps.engine.entity.character.player.Player import world.gregs.voidps.engine.inv.add import world.gregs.voidps.engine.inv.inventory import world.gregs.voidps.engine.queue.queue @@ -20,47 +21,21 @@ class LumbridgeSwamp : Script { init { objectOperate("Search", "rocks_skull_restless_ghost_quest") { - if (quest("the_restless_ghost") != "mining_spot" && quest("the_restless_ghost") != "found_skull") { - message("There's nothing there of any use to you.") - return@objectOperate - } - if (inventory.isFull()) { - message("You can see the skull under the rocks, but you don't have enough space to carry it.") - return@objectOperate - } - statement("You take the skull from the pile of rocks.") - inventory.add("muddy_skull") - set("rocks_restless_ghost", "no_skull") - set("the_restless_ghost", "found_skull") - val index: Int? = remove("restless_ghost_warlock") - if (index != null) { - val skeleton = NPCs.indexed(index) - if (skeleton != null) { - NPCs.remove(skeleton) - } - } - message("A skeleton warlock has appeared.") - val warlock = NPCs.add("skeleton_warlock", Tile(3236, 3149), Direction.SOUTH, ticks = TimeUnit.SECONDS.toTicks(60), owner = this) - set("restless_ghost_warlock", warlock.index) - warlock.anim("restless_ghost_warlock_spawn") - val player = this - warlock.queue("delayed_attack", 4) { - warlock.interactPlayer(player, "Attack") - } + takeSkull() } objectOperate("Search", "rocks_no_skull_restless_ghost_quest") { if (quest("the_restless_ghost") == "completed") { message("There's nothing of any interest.") - } else { - message("You already have the ghost's skull.") + return@objectOperate } - } - - playerDeath { - if (!ownsItem("muddy_skull")) { - set("rocks_restless_ghost", "skull") + if (ownsItem("muddy_skull")) { + message("You already have the ghost's skull.") + return@objectOperate } + // The skull is no longer held (died with it, dropped it, or let the gravestone + // expire) so it's back under the rocks rather than being lost for good. + takeSkull() } destroyed("muddy_skull") { @@ -89,4 +64,34 @@ class LumbridgeSwamp : Script { ) } } + + suspend fun Player.takeSkull() { + if (quest("the_restless_ghost") != "mining_spot" && quest("the_restless_ghost") != "found_skull") { + message("There's nothing there of any use to you.") + return + } + if (inventory.isFull()) { + message("You can see the skull under the rocks, but you don't have enough space to carry it.") + return + } + statement("You take the skull from the pile of rocks.") + inventory.add("muddy_skull") + set("rocks_restless_ghost", "no_skull") + set("the_restless_ghost", "found_skull") + val index: Int? = remove("restless_ghost_warlock") + if (index != null) { + val skeleton = NPCs.indexed(index) + if (skeleton != null) { + NPCs.remove(skeleton) + } + } + message("A skeleton warlock has appeared.") + val warlock = NPCs.add("skeleton_warlock", Tile(3236, 3149), Direction.SOUTH, ticks = TimeUnit.SECONDS.toTicks(60), owner = this) + set("restless_ghost_warlock", warlock.index) + warlock.anim("restless_ghost_warlock_spawn") + val player = this + warlock.queue("delayed_attack", 4) { + warlock.interactPlayer(player, "Attack") + } + } } diff --git a/game/src/test/kotlin/content/quest/free/restless_ghost/RestlessGhostQuest.kt b/game/src/test/kotlin/content/quest/free/restless_ghost/RestlessGhostQuest.kt index fa194197fe..28018ff1c9 100644 --- a/game/src/test/kotlin/content/quest/free/restless_ghost/RestlessGhostQuest.kt +++ b/game/src/test/kotlin/content/quest/free/restless_ghost/RestlessGhostQuest.kt @@ -97,4 +97,55 @@ class RestlessGhostQuest : WorldTest() { assertEquals("completed", player.quest("the_restless_ghost")) assertEquals(1125.0, player.experience.get(Skill.Prayer)) } + + @Test + fun `Search the rocks again after losing the skull on death`() { + val player = createPlayer(Tile(3234, 3147)) + player["the_restless_ghost"] = "mining_spot" + + val rock = GameObjects.find(Tile(3234, 3145), "rocks_skull_restless_ghost_quest_base") + player.interactObject(rock, "Search") + tick() + player.continueDialogue() + + assertEquals(1, player.inventory.count("muddy_skull")) + assertEquals("no_skull", player["rocks_restless_ghost", ""]) + + // Die in the wilderness so the skull is dropped rather than kept, and never reclaimed. + player.tele(3100, 3550) + tick(2) + player.levels.set(Skill.Constitution, 0) + tick(10) + assertEquals(0, player.inventory.count("muddy_skull")) + + // The rocks now show as emptied but must hand the skull back rather than dead-end the quest. + player.tele(3234, 3147) + tick() + val emptyRock = GameObjects.find(Tile(3234, 3145), "rocks_skull_restless_ghost_quest_base") + player.interactObject(emptyRock, "Search") + tick() + player.continueDialogue() + + assertEquals(1, player.inventory.count("muddy_skull")) + assertEquals("found_skull", player.quest("the_restless_ghost")) + } + + @Test + fun `Rocks stay empty while still carrying the skull`() { + val player = createPlayer(Tile(3234, 3147)) + player["the_restless_ghost"] = "mining_spot" + + val rock = GameObjects.find(Tile(3234, 3145), "rocks_skull_restless_ghost_quest_base") + player.interactObject(rock, "Search") + tick() + player.continueDialogue() + assertEquals(1, player.inventory.count("muddy_skull")) + + player.interactObject(GameObjects.find(Tile(3234, 3145), "rocks_skull_restless_ghost_quest_base"), "Search") + tick() + + // No "You take the skull" statement, no second skull. + assertNull(player.dialogue) + assertEquals(1, player.inventory.count("muddy_skull")) + } }