From 177f3974d083c3402691115f1afd6bb69b1506d7 Mon Sep 17 00:00:00 2001 From: Beck Date: Mon, 1 Jun 2026 11:19:03 -0700 Subject: [PATCH 01/19] Initial commit --- .../dialogue/FatherAereckDefaultDialogue.kt | 202 ++++++++++ .../dialogue/FatherUrhneyDefaultDialogue.kt | 141 +++++++ .../FatherAereckDialogue.java | 345 ------------------ .../FatherAereckQuestDialogueFile.kt | 99 +++++ .../FatherUhrneyDialogue.java | 285 --------------- .../FatherUrhneyQuestDialogueFile.kt | 95 +++++ .../quest/therestlessghost/RestlessGhost.java | 2 +- .../RestlessGhostDialogue.java | 252 ------------- .../RestlessGhostListeners.kt | 195 ++++++++++ .../RestlessGhostNPCDialogue.kt | 156 ++++++++ .../therestlessghost/RestlessGhostPlugin.java | 273 -------------- .../therestlessghost/RestlessGhostSkull.java | 51 --- .../therestlessghost/SkullDropPlugin.java | 32 -- 13 files changed, 889 insertions(+), 1239 deletions(-) create mode 100644 Server/src/main/content/region/misthalin/lumbridge/dialogue/FatherAereckDefaultDialogue.kt create mode 100644 Server/src/main/content/region/misthalin/lumbridge/dialogue/FatherUrhneyDefaultDialogue.kt delete mode 100644 Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherAereckDialogue.java create mode 100644 Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherAereckQuestDialogueFile.kt delete mode 100644 Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherUhrneyDialogue.java create mode 100644 Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherUrhneyQuestDialogueFile.kt delete mode 100644 Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostDialogue.java create mode 100644 Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostListeners.kt create mode 100644 Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostNPCDialogue.kt delete mode 100644 Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostPlugin.java delete mode 100644 Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostSkull.java delete mode 100644 Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/SkullDropPlugin.java diff --git a/Server/src/main/content/region/misthalin/lumbridge/dialogue/FatherAereckDefaultDialogue.kt b/Server/src/main/content/region/misthalin/lumbridge/dialogue/FatherAereckDefaultDialogue.kt new file mode 100644 index 000000000..11a1eb9f3 --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/FatherAereckDefaultDialogue.kt @@ -0,0 +1,202 @@ +package content.region.misthalin.lumbridge.dialogue; + +import content.data.Quests +import content.region.misthalin.lumbridge.quest.therestlessghost.FatherAereckQuestDialogueFile +import core.api.* +import core.game.dialogue.ChatAnim +import core.game.dialogue.DialogueLabeller +import core.game.dialogue.DialogueOption +import core.game.dialogue.FacialExpression +import core.game.interaction.InteractionListener +import core.game.interaction.IntType +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.link.diary.DiaryType +import org.rs09.consts.Components +import org.rs09.consts.NPCs + +/** +* Handles the main entry dialogue for Father Aereck in Lumbridge. +**/ +class FatherAereckDefaultDialogue : InteractionListener { + override fun defineListeners() { + on(NPCs.FATHER_AERECK_456, IntType.NPC, "talk-to") { player, node -> + DialogueLabeller.open(player, FatherAereckDefaultDialogueFile(), node as NPC) + return@on true + } + } +} + +/** +* This includes the default dialogue and gravestones. +*/ +class FatherAereckDefaultDialogueFile : DialogueLabeller() { + override fun addConversation() { + + //allows skipping the main dialogue while Restless Ghost quest is started + exec { player, npc -> + when (getQuestStage(player, Quests.THE_RESTLESS_GHOST)) { + 0 -> { + loadLabel(player, "main_entry_transcript") + } + 10 -> { + open(player, FatherAereckQuestDialogueFile(), npc) + loadLabel(player, "quest_stage_10") + } + 20 -> { + open(player, FatherAereckQuestDialogueFile(), npc) + loadLabel(player, "quest_stage_20") + } + 30 -> { + open(player, FatherAereckQuestDialogueFile(), npc) + loadLabel(player, "quest_stage_30") + } + 40 -> { + open(player, FatherAereckQuestDialogueFile(), npc) + loadLabel(player, "quest_stage_40") + } + } + } + + label("main_entry_transcript") { + npc("Welcome to the church of holy Saradomin") + goto("main_opts") + } + + label("main_opts") { + exec { player, npc -> + if (getQuestStage(player, Quests.THE_RESTLESS_GHOST) > 0) { + options( + DialogueOption( + "change_grave", + "Can you change my gravestone now?", + expression = ChatAnim.HALF_ASKING + ), + DialogueOption( + "who_sara", + "Who's Saradomin?", + expression = ChatAnim.HALF_ASKING + ), + DialogueOption( + "nice_place", + "Nice place you've got here.", + expression = ChatAnim.FRIENDLY + ) + ) + } else { + options( + DialogueOption( + "change_grave", + "Can you change my gravestone now?", + expression = ChatAnim.HALF_ASKING + ), + DialogueOption( + "who_sara", + "Who's Saradomin?", + expression = ChatAnim.HALF_ASKING + ), + DialogueOption( + "nice_place", + "Nice place you've got here.", + expression = ChatAnim.FRIENDLY + ), + DialogueOption( + "start_restless_ghost_quest", + "I'm looking for a quest.", + expression = ChatAnim.HALF_ASKING + ) + ) + } + } + } + + label("change_grave") { + npc("Certainly. All proceeds are donated to the", "Varrockian Guards' Widows & Orphans Fund.") + goto("gravestone_iface") + } + + label("who_sara") { + npc(FacialExpression.HALF_ASKING, "Surely you have heard of our god, Saradomin?") + npc(FacialExpression.AMAZED, "He who created the forces of goodness and purity in this", "world? I cannot believe your ignorance!") + npc("This is the god with more followers than any other ...at", "least in this part of the world.") + npc("He who forged the world as we know it, along with his", "brothers Guthix and Zamorak?") + + goto("oh_that_sara") + + } + + label("oh_that_sara") { + options( + DialogueOption( + "that_sara", + "Oh, THAT Saradomin.", + expression = FacialExpression.GUILTY + ), + DialogueOption( + "not_from_this_world", + "Oh, sorry, I'm not from this world.", + expression = FacialExpression.GUILTY + ) + ) + } + + label("that_sara") { + npc("There is only one Saradomin.") + player("Yeah. I, uh, thought you said something else.") + end() + } + + label("not_from_this_world") { + npc("..."); + npc("That's...strange.") + npc("I thought things not from this world were all, you know,", "slime and tentacles.") + goto("like_my_disguise") + } + + label("like_my_disguise") { + options( + DialogueOption( + "disguise_not_me", + "Not me." + ), + DialogueOption( + "like_disguise", + "I am! Do you like my disguise?" + ), + ) + } + + label("disguise_not_me") { + npc("Well, I can see that. Still, there's something special about", "you.") + player("Thanks, I think.") + end() + } + + label("like_disguise") { + npc("Argh! Avaunt, foul creature from another dimension!", "Avaunt! Begone in the name of Saradomin!") + player("Okay, okay, I was only joking!") + end() + } + + label("nice_place") { + npc("It is, isn't it? It was built over two centuries ago."); + end() + } + + label("gravestone_iface") { + + exec { player, npc -> + openInterface(player, Components.GRAVESTONE_SHOP_652) + player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 0, 15); + end() + } + } + + label("start_restless_ghost") { + exec { player, npc -> + open(player, FatherAereckQuestDialogueFile(), npc) + loadLabel(player, "quest_main_entry") + } + } + + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/lumbridge/dialogue/FatherUrhneyDefaultDialogue.kt b/Server/src/main/content/region/misthalin/lumbridge/dialogue/FatherUrhneyDefaultDialogue.kt new file mode 100644 index 000000000..5acd45f58 --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/FatherUrhneyDefaultDialogue.kt @@ -0,0 +1,141 @@ +package content.region.misthalin.lumbridge.dialogue; + +import content.data.Quests +import content.region.misthalin.lumbridge.quest.therestlessghost.FatherUrhneyQuestDialogueFile +import core.api.addItem +import core.api.freeSlots +import core.api.getQuestStage +import core.api.sendItemDialogue +import core.api.sendMessage +import core.api.setQuestStage +import core.game.dialogue.DialogueLabeller +import core.game.dialogue.DialogueOption +import core.game.dialogue.FacialExpression +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.npc.NPC +import core.game.node.item.Item +import org.rs09.consts.Items +import org.rs09.consts.NPCs + + +class FatherUrhneyDefaultDialogue : InteractionListener { + override fun defineListeners() { + on(NPCs.FATHER_URHNEY_458, IntType.NPC, "talk-to") { player, node -> + DialogueLabeller.open(player, FatherUrhneyDefaultDialogueFile(), node as NPC) + return@on true + } + } +} + +class FatherUrhneyDefaultDialogueFile: DialogueLabeller() { + override fun addConversation() { + + npc(FacialExpression.ANGRY, "Go away! I'm meditating!"); + + goto("main_opts") + + label("main_opts") { + + exec {player, npc -> + when (getQuestStage(player, Quests.THE_RESTLESS_GHOST)) { + 0 -> { + options( + DialogueOption( + "thats_friendly", + "Well, that's friendly" + ), + DialogueOption( + "repo_house", + "I've come to repossess your house." + ), + + ) + } + 10 -> { + options( + DialogueOption( + "thats_friendly", + "Well, that's friendly" + ), + DialogueOption( + "father_aereck_sent", + "Father Aereck sent me to talk to you." + ), + DialogueOption( + "repo_house", + "I've come to repossess your house." + ) + ) + } + + else -> { + options( + DialogueOption( + "thats_friendly", + "Well, that's friendly" + ), + DialogueOption( + "ive_lost_amulet", + "I've lost the Amulet of Ghostspeak." + ), + DialogueOption( + "repo_house", + "I've come to repossess your house." + ) + ) + } + } + } + } + + label("father_aereck_sent") { + open(player!!, FatherUrhneyQuestDialogueFile(), npc!!) + loadLabel(player!!, "father_aereck_sent_me") + } + + label("ive_lost_amulet") { + open(player!!, FatherUrhneyQuestDialogueFile(), npc!!) + loadLabel(player!!, "lost_amulet") + } + + label("thats_friendly") { + npc("I SAID go AWAY.") + player("Okay, okay... sheesh, what a grouch.") + end() + } + + label("repo_house") { + npc("Under what grounds???") + goto("repo_opts") + } + + label("repo_opts") { + options( + DialogueOption( + "mtg_repay", + "Repeated failure on mortgage repayments." + ), + DialogueOption( + "want_house", + "I don't know, I just wanted this house." + ) + ) + } + + label("mtg_repay") { + npc("What?") + npc("I don't have a mortgage! I built this house.") + player("Sorry. I must have got the wrong address. All the", "houses look the same around here.") + npc("What? What houses? What ARE you talking about???") + player("Never mind.") + end() + } + + label("want_house") { + npc("Oh... go away and stop wasting my time!") + end() + } + + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherAereckDialogue.java b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherAereckDialogue.java deleted file mode 100644 index acf0ca8c2..000000000 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherAereckDialogue.java +++ /dev/null @@ -1,345 +0,0 @@ -package content.region.misthalin.lumbridge.quest.therestlessghost; - -import core.game.component.Component; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.link.diary.DiaryType; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; -import core.game.dialogue.DialoguePlugin; -import content.data.Quests; - - -/** - * Represents the father aereck dialogue plugin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class FatherAereckDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code FatherAereckDialogue} {@code Object}. - */ - public FatherAereckDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code FatherAereckDialogue} {@code Object}. - * - * @param player the player. - */ - public FatherAereckDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new FatherAereckDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - int questStage = player.getQuestRepository().getQuest(Quests.THE_RESTLESS_GHOST).getStage(player); - if (questStage == 10) { - npc("Have you got rid of the ghost yet?"); - stage = 520; - return true; - } - if (questStage == 20) { - player("I had a talk with Father Urhney. He has given me this", "funny amulet to talk to the ghost with."); - stage = 530; - return true; - } - if (questStage == 30) { - player("I've found out that the ghost's corpse has lost its skull.", "If I can find the skull, the ghost should leave."); - stage = 540; - return true; - } - if (questStage == 40) { - player("I've finally found the ghost's skull!"); - stage = 550; - return true; - } - npc("Welcome to the church of holy Saradomin, my", "friend! What can I do for you today?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - if (player.getQuestRepository().isComplete(Quests.THE_RESTLESS_GHOST)) { - interpreter.sendOptions("What would you like to say?", "Can you change my gravestone now?", "Who's Saradomin?", "Nice place you've got here."); - stage = 1; - } else { - interpreter.sendOptions("What would you like to say?", "Can you change my gravestone now?", "Who's Saradomin?", "Nice place you've got here.", "I'm looking for a quest."); - stage = 500; - } - break; - case 500: - switch (buttonId) { - case 1: - npc("Certainly. All proceeds are donated to the", "Varrockian Guards' Widows & Orphans Fund."); - stage = 10; - break; - case 2: - npc("Surely you have heard of our god, Saradomin?"); - stage = 20; - break; - case 3: - npc("It is, isn't it? It was built over two centuries ago."); - stage = 300; - break; - case 4: - player("I'm looking for a quest."); - stage = 505; - break; - } - break; - case 505: - npc("That's lucky, I need someone to do a quest for me."); - stage = 506; - break; - case 506: - options("Ok, let me help then.", "Sorry, I don't have time right now."); - stage = 507; - break; - case 507: - switch (buttonId) { - case 1: - player("Ok, let me help then."); - stage = 510; - break; - case 2: - player("Sorry, I don't have time right now."); - stage = 508; - break; - } - break; - case 508: - npc("Oh well. If you do have some spare time on your", "hands, come back and talk to me."); - stage = 509; - break; - case 509: - end(); - break; - case 510: - player.getQuestRepository().getQuest(Quests.THE_RESTLESS_GHOST).start(player); - player.getQuestRepository().syncronizeTab(player); - npc("Thank you. The problem is, there is a ghost in the", "church graveyard. I would like you to get rid of it."); - stage = 511; - break; - case 511: - npc("If you need any help, my friend Father Urhney is an", "expert on ghosts."); - stage = 512; - break; - case 512: - npc("I believe he is currently living as a hermit in Lumbridge", "swamp. He has a little shack in the south-west of the", "swamps."); - stage = 513; - break; - case 513: - npc("Exit the graveyard through the south gate to reach the", "swamp. I'm sure if you told him that I sent you he'd", "be willing to help."); - stage = 514; - break; - case 514: - npc("My name is Father Aereck by the way. Pleased to", "meet you."); - stage = 515; - break; - case 515: - player("Likewise."); - stage = 516; - break; - case 516: - npc("Take care travelling through the swamps, I have heard", "they can be quite dangerous."); - stage = 517; - break; - case 517: - player("I will, thanks."); - stage = 518; - break; - case 518: - end(); - break; - case 520: - if (!player.getGameAttributes().getAttributes().containsKey("restless-ghost:urhney")) { - player("I can't find Father Urhney at the moment."); - stage = 521; - break; - } - break; - case 521: - npc("Well, you can get to the swamp he lives in by going", "south through the cemetery."); - stage = 522; - break; - case 522: - npc("You'll have to go right into the western depths of the", "swamp, near the coastline. That is where his house is."); - stage = 523; - break; - case 523: - end(); - break; - case 530: - npc("I always wondered what that amulet was... Well, I hope", "it's useful. Tell me when you get rid of the ghost!"); - stage = 531; - break; - case 531: - end(); - break; - case 540: - npc("That WOULD explain it."); - stage = 541; - break; - case 541: - npc("Hmmmmm. Well, I haven't seen any skulls."); - stage = 542; - break; - case 542: - player("Yes, I think a warlock has stolen it."); - stage = 543; - break; - case 543: - npc("I hate warlocks."); - stage = 544; - break; - case 544: - npc("Ah well, good luck!"); - stage = 545; - break; - case 545: - end(); - break; - case 550: - npc("Great! Put it in the ghost's coffin and see what", "happens!"); - stage = 545; - break; - case 1: - switch (buttonId) { - case 1: - npc("Certainly. All proceeds are donated to the", "Varrockian Guards' Widows & Orphans Fund."); - stage = 10; - break; - case 2: - npc("Surely you have heard of our god, Saradomin?"); - stage = 20; - break; - case 3: - npc("It is, isn't it? It was built over two centuries ago."); - stage = 300; - break; - } - - break; - case 10: - end(); - player.getInterfaceManager().open(new Component(652)); - player.getAchievementDiaryManager().finishTask(player, DiaryType.LUMBRIDGE, 0, 15); - break; - case 20: - npc("He who created the forces of goodness and purity in this", "world? I cannot believe your ignorance!"); - stage = 21; - break; - case 21: - npc("This is the god with more followers than any other ...at", "least in this part of the world."); - stage = 22; - break; - case 22: - npc("He who forged the world as we know it, along with his", "brothers Guthix and Zamorak?"); - stage = 23; - break; - case 23: - options("Oh, THAT Saradomin.", "Oh, sorry, I'm not from this world."); - stage = 24; - break; - case 24: - switch (buttonId) { - case 1: - npc("There is only one Saradomin."); - stage = 200; - break; - case 2: - npc("..."); - stage = 250; - break; - } - - break; - case 200: - player("Yeah. I, uh, thought you said something else."); - stage = 201; - break; - case 201: - end(); - break; - case 250: - npc("That's...strange."); - stage = 251; - break; - case 251: - npc("I thought things not from this world were all, you know,", "slime and tentacles."); - stage = 270; - break; - case 270: - options("Not me.", "I am! Do you like my disguise?"); - stage = 271; - break; - case 271: - - switch (buttonId) { - case 1: - npc("Well, I can see that. Still, there's something special about", "you."); - stage = 253; - break; - case 2: - npc("Argh! Avaunt, foul creature from another dimension!", "Avaunt! Begone in the name of Saradomin!"); - stage = 291; - break; - } - break; - case 291: - player("Okay, okay, I was only joking!"); - stage = 292; - break; - case 292: - end(); - break; - case 253: - player("Thanks, I think."); - stage = 254; - break; - case 254: - end(); - break; - case 300: - end(); - break; - case 570: - interpreter.sendDialogues(player, null, "Yes, I have!"); - stage = 571; - break; - case 571: - interpreter.sendDialogues(npc, null, "Thank you for getting rid of that awful ghost for me!", "May Saradomin always smile upon you!"); - stage = 572; - break; - case 572: - interpreter.sendDialogues(player, null, "I'm looking for a new quest."); - stage = 573; - break; - case 573: - interpreter.sendDialogues(npc, null, "Sorry, I only had the one quest."); - stage = 300; - break; - } - - return true; - } - - @Override - public int[] getIds() { - return new int[]{456}; - } -} diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherAereckQuestDialogueFile.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherAereckQuestDialogueFile.kt new file mode 100644 index 000000000..4d85b152d --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherAereckQuestDialogueFile.kt @@ -0,0 +1,99 @@ +package content.region.misthalin.lumbridge.quest.therestlessghost; + +import content.data.Quests +import core.api.getQuestStage +import core.api.setQuestStage +import core.game.dialogue.DialogueLabeller +import core.game.dialogue.DialogueOption +import core.game.dialogue.FacialExpression +import core.game.interaction.InteractionListener +import core.game.interaction.IntType +import core.game.node.entity.npc.NPC +import org.rs09.consts.NPCs + +/** +* Handles the Dialogue for Father Aereck in Lumbridge. +* Specifically for The Restless Ghost quest. +* // https://runescape.fandom.com/wiki/Transcript:The_Restless_Ghost#Starting_out +*/ + +class FatherAereckQuestDialogueFile : DialogueLabeller() { + + override fun addConversation() { + + label("quest_main_entry") { + npc("That's lucky, I need someone to do a quest for me.") + goto("start_quest_opts") + } + + label("start_quest_opts") { + options( + DialogueOption( + "let_me_help", + "Ok, let me help then." + ), + DialogueOption( + "no_time", + "Sorry, I don't have time right now." + ) + ) + } + + label("let_me_help") { + exec { player, npc -> + setQuestStage(player, Quests.THE_RESTLESS_GHOST, 10) + } + + npc("Thank you. The problem is, there is a ghost in the", "church graveyard. I would like you to get rid of it.") + npc("If you need any help, my friend Father Urhney is an", "expert on ghosts.") + npc("I believe he is currently living as a hermit in Lumbridge", "swamp. He has a little shack in the south-west of the", "swamps.") + npc("Exit the graveyard through the south gate to reach the", "swamp. I'm sure if you told him that I sent you he'd", "be willing to help.") + + npc("My name is Father Aereck by the way. Pleased to", "meet you.") + player("Likewise.") + + npc("Take care travelling through the swamps, I have heard", "they can be quite dangerous.") + player("I will, thanks.") + + end() + } + + label("no_time") { + npc("Oh well. If you do have some spare time on your", "hands, come back and talk to me.") + end() + } + + //mid quest dialogue + label("quest_stage_10") { + npc(FacialExpression.HALF_ASKING, "Have you got rid of the ghost yet?") + player(FacialExpression.GUILTY, "I can't find Father Urhney at the moment." ) + + npc(FacialExpression.THINKING, "Well, you can get to the swamp he lives in by going", "south through the cemetery.") + npc("You'll have to go right into the western depths of the", "swamp, near the coastline. That is where his house is.") + } + + //mid quest dialogue + label("quest_stage_20") { + player("I had a talk with Father Urhney. He has given me this", "funny amulet to talk to the ghost with.") + npc(FacialExpression.HALF_ASKING, "I always wondered what that amulet was... Well, I hope", "it's useful. Tell me when you get rid of the ghost!") + } + + //mid quest dialogue + label("quest_stage_30") { + player("I've found out that the ghost's corpse has lost its skull.", "If I can find the skull, the ghost should leave.") + npc(FacialExpression.AMAZED, "That WOULD explain it.") + npc(FacialExpression.THINKING, "Hmmmmm. Well, I haven't seen any skulls.") + + player("Yes, I think a warlock has stolen it.") + npc("I hate warlocks.") + npc("Ah well, good luck!") + } + + //mid quest dialogue + label("quest_stage_40") { + player(FacialExpression.AMAZED, "I've finally found the ghost's skull!") + npc("Great! Put it in the ghost's coffin and see what", "happens!"); + } + + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherUhrneyDialogue.java b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherUhrneyDialogue.java deleted file mode 100644 index 219c0c562..000000000 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherUhrneyDialogue.java +++ /dev/null @@ -1,285 +0,0 @@ -package content.region.misthalin.lumbridge.quest.therestlessghost; - -import content.data.Quests; -import core.game.dialogue.FacialExpression; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.diary.DiaryType; -import core.plugin.Initializable; -import core.game.node.item.Item; -import core.game.dialogue.DialoguePlugin; - -/** - * Represents the father urgney dialogue plugin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class FatherUhrneyDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code FatherUhrneyDialogue} {@code Object}. - */ - public FatherUhrneyDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code FatherUhrneyDialogue} {@code Object}. - * @param player the player. - */ - public FatherUhrneyDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new FatherUhrneyDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - npc(FacialExpression.ANGRY, "Go away! I'm meditating!"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - if (player.getQuestRepository().getQuest(Quests.THE_RESTLESS_GHOST).getStage(player) == 0) { - options("Well, that's friendly.", "I've come to repossess your house."); - stage = 1; - } else if (player.getQuestRepository().getQuest(Quests.THE_RESTLESS_GHOST).getStage(player) == 10) { - options("Well, that's friendly.", "Father Aereck sent me to talk to you.", "I've come to repossess your house."); - stage = 500; - } else if (player.getGameAttributes().getAttributes().containsKey("restless-ghost:urhney") || player.getQuestRepository().isComplete(Quests.THE_RESTLESS_GHOST)) { - options("Well, that's friendly.", "I've lost the Amulet of Ghostspeak.", "I've come to repossess your house."); - stage = 514; - } - break; - case 500: - switch (buttonId) { - case 1: - player("Well, that's friendly."); - stage = 10; - break; - case 2: - player("Father Aereck sent me to talk to you."); - stage = 501; - break; - case 3: - player("I've come to repossess your house."); - stage = 20; - break; - } - break; - case 501: - npc(FacialExpression.ANGRY, "I suppose I'd better talk to you then. What problems", "has he got himself into this time?"); - stage = 502; - break; - case 502: - player("He's got a ghost haunting his graveyard."); - stage = 503; - break; - case 503: - npc(FacialExpression.ANGRY, "Oh, the silly fool."); - stage = 504; - break; - case 504: - npc(FacialExpression.ANGRY, "I leave town for just five months, and ALREADY he", "can't manage."); - stage = 505; - break; - case 505: - npc(FacialExpression.SAD, "(sigh)"); - stage = 506; - break; - case 506: - npc(FacialExpression.ANGRY, "Well, I can't go back and exorcise it. I vowed not to", "leave this place. Until I had done a full two years of", "prayer and meditation."); - stage = 507; - break; - case 507: - npc(FacialExpression.NEUTRAL, "Tell you what I can do though; take this amulet."); - stage = 508; - break; - case 508: - if (player.getInventory().freeSlots() == 0) { - end(); - player.getPacketDispatch().sendMessage("You don't have enough inventory space to accept this amulet."); - break; - } - interpreter.sendItemMessage(552, "Father Urhney hands you an amulet."); - player.getInventory().add(new Item(552, 1)); - player.getQuestRepository().getQuest(Quests.THE_RESTLESS_GHOST).setStage(player, 20); - player.getGameAttributes().setAttribute("/save:restless-ghost:urhney", true); - stage = 509; - break; - case 509: - npc(FacialExpression.NEUTRAL, "It is an Amulet of Ghostspeak."); - stage = 510; - break; - case 510: - npc(FacialExpression.NEUTRAL, "So called, because when you wear it you can speak to", "ghosts. A lot of ghosts are doomed to be ghosts because", "they have left some important task uncompleted."); - stage = 511; - break; - case 511: - npc(FacialExpression.NEUTRAL, "Maybe if you know what this task is, you can get rid of", "the ghost. I'm not making any guarantees mind you,", "but it is the best I can do right now."); - stage = 512; - break; - case 512: - player("Thank you. I'll give it a try!"); - stage = 513; - break; - case 513: - end(); - break; - case 514: - switch (buttonId) { - case 1: - player("Well, that's friendly."); - stage = 10; - break; - case 2: - player(FacialExpression.NEUTRAL, "I've lost the Amulet of Ghostspeak."); - stage = 515; - break; - case 3: - player("I've come to repossess your house."); - stage = 20; - break; - } - break; - case 515: - if (player.getInventory().contains(552, 1) || player.getEquipment().contains(552, 1)) { - interpreter.sendDialogue("Father Urhney sighs."); - stage = 516; - break; - } - if (player.getBank().contains(552, 1)) { - interpreter.sendDialogue("Father Urhney sighs."); - stage = 517; - break; - } - interpreter.sendDialogue("Father Urhney sighs."); - stage = 519; - break; - case 516: - npc("What are you talking about? I can see you've got it", "with you!"); - stage = 518; - break; - case 517: - npc(FacialExpression.ANGRY, "You come here wasting my time... Has it even", "occurred to you to look in your bank? Now GO", "AWAY!"); - stage = 518; - break; - case 518: - end(); - break; - case 519: - npc(FacialExpression.ANGRY, "How careless can you get? Those things aren't easy to", "come by you know! It's a good job I've got a spare."); - stage = 520; - break; - case 520: - player.getInventory().add(new Item(552)); - interpreter.sendItemMessage(552, "Father Urhney hands you an amulet."); - player.getAchievementDiaryManager().finishTask(player, DiaryType.LUMBRIDGE, 1, 12); - stage = 521; - break; - case 521: - npc(FacialExpression.ANGRY, "Be more careful this time."); - stage = 522; - break; - case 522: - player("Ok, I'll try to be."); - stage = 523; - break; - case 523: - end(); - break; - case 1: - switch (buttonId) { - case 1: - player("Well, that's friendly."); - stage = 10; - break; - case 2: - player("I've come to repossess your house."); - stage = 20; - break; - - } - break; - case 10: - npc("I SAID go AWAY."); - stage = 11; - break; - case 11: - player("Ok, ok... sheesh, what a grouch."); - stage = 12; - break; - case 12: - end(); - break; - case 20: - npc("Under what grounds???"); - stage = 21; - break; - case 21: - options("Repeated failure on mortgage repayments.", "I don't know, I just wanted this house."); - stage = 22; - break; - case 22: - switch (buttonId) { - case 1: - player("Repeated failure on mortgage repayments."); - stage = 100; - break; - case 2: - player("I don't know. I just wanted this house..."); - stage = 200; - break; - - } - break; - case 100: - npc("What?"); - stage = 101; - break; - case 101: - npc("I don't have a mortgage! I built this house."); - stage = 102; - break; - case 102: - player("Sorry. I must have got the wrong address. All the", "houses look the same around here."); - stage = 103; - break; - case 103: - npc("What? What houses? What ARE you talking about???"); - stage = 104; - break; - case 104: - player("Never mind."); - stage = 105; - break; - case 105: - end(); - break; - case 200: - npc("Oh... go away and stop wasting my time!"); - stage = 201; - break; - case 201: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 458 }; - } -} diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherUrhneyQuestDialogueFile.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherUrhneyQuestDialogueFile.kt new file mode 100644 index 000000000..9f0ea450c --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherUrhneyQuestDialogueFile.kt @@ -0,0 +1,95 @@ +package content.region.misthalin.lumbridge.quest.therestlessghost; + +import content.data.Quests +import core.api.addItem +import core.api.anyInEquipment +import core.api.anyInInventory +import core.api.freeSlots +import core.api.inBank +import core.api.inInventory +import core.api.sendDialogue +import core.api.sendItemDialogue +import core.api.sendMessage +import core.api.setQuestStage +import core.game.dialogue.DialogueLabeller +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.link.diary.DiaryType +import core.game.node.item.Item +import org.rs09.consts.Items + +class FatherUrhneyQuestDialogueFile : DialogueLabeller() { + override fun addConversation() { + label("father_aereck_sent_me") { + npc(FacialExpression.ANGRY, "I suppose I'd better talk to you then. What problems", "has he got himself into this time?"); + player("He's got a ghost haunting his graveyard."); + + npc(FacialExpression.ANGRY, "Oh, the silly fool."); + npc(FacialExpression.ANGRY, "I leave town for just five months, and ALREADY he", "can't manage."); + npc(FacialExpression.SAD, "(sigh)"); + npc(FacialExpression.ANGRY, "Well, I can't go back and exorcise it. I vowed not to", "leave this place. Until I had done a full two years of", "prayer and meditation."); + npc(FacialExpression.NEUTRAL, "Tell you what I can do though; take this amulet."); + + goto("take_amulet") + } + + label("take_amulet") { + exec { player, npc -> + if (freeSlots(player) >= 1) { + if (addItem(player, Items.GHOSTSPEAK_AMULET_552, 1)) { + sendItemDialogue(player, Item(Items.GHOSTSPEAK_AMULET_552), "Father Urhney hands you an amulet.") + setQuestStage(player, Quests.THE_RESTLESS_GHOST, 20) + + npc(FacialExpression.NEUTRAL, "It is an Amulet of Ghostspeak."); + npc(FacialExpression.NEUTRAL, "So called, because when you wear it you can speak to", "ghosts. A lot of ghosts are doomed to be ghosts because", "they have left some important task uncompleted."); + npc(FacialExpression.NEUTRAL, "Maybe if you know what this task is, you can get rid of", "the ghost. I'm not making any guarantees mind you,", "but it is the best I can do right now."); + player("Thank you. I'll give it a try!"); + } + } else { + // Closes the dialogue prematurely to reveal message + end() + sendMessage(player, "You don't have enough inventory space to accept this amulet.") + } + } + + end() + } + + label("lost_amulet") { + + exec { player, npc -> + sendDialogue(player, "Father Urhney sighs.") + + if (anyInEquipment(player, Items.GHOSTSPEAK_AMULET_552) || anyInInventory(player, Items.GHOSTSPEAK_AMULET_552)) { + npc("What are you talking about? I can see you've got it", "with you!"); + goto("nowhere") + } + + if (inBank(player, Items.GHOSTSPEAK_AMULET_552)) { + npc(FacialExpression.ANGRY, "You come here wasting my time... Has it even", "occurred to you to look in your bank? Now GO", "AWAY!"); + goto("nowhere") + } + + npc(FacialExpression.ANGRY, "How careless can you get? Those things aren't easy to", "come by you know! It's a good job I've got a spare."); + + if (freeSlots(player) >= 1) { + if (addItem(player, Items.GHOSTSPEAK_AMULET_552, 1)) { + sendItemDialogue(player, Item(Items.GHOSTSPEAK_AMULET_552), "Father Urhney hands you an amulet.") + + player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 1, 12) + + npc(FacialExpression.ANGRY, "Be more careful this time."); + player("Ok, I'll try to be."); + end() + } + } else { + // Closes the dialogue prematurely to reveal message + end() + sendMessage(player, "You don't have enough inventory space to accept this amulet.") + } + + //last effort dialogue end if somehow it screws up + end() + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhost.java b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhost.java index 11fd553b1..99d3cf017 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhost.java +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhost.java @@ -105,7 +105,7 @@ public class RestlessGhost extends Quest { player.getSkills().addExperience(Skills.PRAYER, 1125); player.getInterfaceManager().closeChatbox(); player.getPacketDispatch().sendString("You have completed The Restless Ghost Quest!", 277, 2 + 2); - setVarp(player, 728, 31, true); + setVarp(player, 728, 31, true); player.getGameAttributes().removeAttribute("restless-ghost:urhney"); } diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostDialogue.java b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostDialogue.java deleted file mode 100644 index 41b9f046b..000000000 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostDialogue.java +++ /dev/null @@ -1,252 +0,0 @@ -package content.region.misthalin.lumbridge.quest.therestlessghost; - -import content.data.Quests; -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.FacialExpression; - -/** - * Handles the RestlessGhostDialogue dialogue. - * @author 'Vexia - */ -@Initializable -public class RestlessGhostDialogue extends DialoguePlugin { - - public RestlessGhostDialogue() { - - } - - public RestlessGhostDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - - return new RestlessGhostDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello ghost, how are you?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - if (!player.getEquipment().contains(552, 1)) { - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Wooo wooo wooooo!"); - stage = 1; - } else { - if (player.getQuestRepository().getQuest(Quests.THE_RESTLESS_GHOST).getStage(player) == 20) { - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Not very good actually."); - stage = 500; - break; - } - if (player.getQuestRepository().getQuest(Quests.THE_RESTLESS_GHOST).getStage(player) == 30) { - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "How are you doing finding my skull?"); - stage = 520; - break; - } - if (player.getQuestRepository().getQuest(Quests.THE_RESTLESS_GHOST).getStage(player) == 40) { - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "How are you doing finding my skull?"); - stage = 550; - break; - } - interpreter.sendDialogues(npc, null, "Fine, thanks."); - stage = 990; - } - break; - case 990: - end(); - break; - case 500: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "What's the problem then?"); - stage = 501; - break; - case 501: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Did you just understand what I said???"); - stage = 502; - break; - case 502: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Yep, now tell me what the problem is."); - stage = 503; - break; - case 503: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "WOW! This is INCREDIBLE! I didn't expect anyone", "to ever understand me again!"); - stage = 504; - break; - case 504: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Ok, Ok, I can understand you!"); - stage = 505; - break; - case 505: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "But have you any idea WHY you're doomed to be a", "ghost?"); - stage = 506; - break; - case 506: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Well, to be honest... I'm not sure."); - stage = 507; - break; - case 507: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I've been told a certain task may need to be completed", "so you can rest in peace."); - stage = 508; - break; - case 508: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I should think it is probably because a warlock has come", "along and stolen my skull. If you look inside my coffin", "there, you'll find my corpse without a head on it."); - stage = 509; - break; - case 509: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Do you know where this warlock might be now?"); - stage = 510; - break; - case 510: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I think it was one of the warlocks who lives in the big", "tower by the sea south-west from here."); - stage = 511; - break; - case 511: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Ok. I will try and get the skull back for you, then you", "can rest in peace."); - player.getQuestRepository().getQuest(Quests.THE_RESTLESS_GHOST).setStage(player, 30); - stage = 512; - break; - case 512: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Ooh, thank you. That would be such a great relief!"); - stage = 513; - break; - case 513: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "It is so dull being a ghost..."); - stage = 514; - break; - case 514: - end(); - break; - case 520: - if (player.getInventory().contains(964, 1)) { - - break; - } else { - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Sorry, I can't find it at the moment."); - stage = 521; - } - break; - case 521: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Ah well. Keep on looking."); - stage = 522; - break; - case 522: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I'm pretty sure it's somewhere in the tower south-west", "from here. There's a lot of levels to the tower, though. I", "suppose it might take a little while to find."); - stage = 523; - break; - case 523: - end(); - break; - case 550: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I have found it!"); - stage = 551; - break; - case 551: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hurrah! Now I can stop being a ghost! You just need", "to put it on my coffin there, and I will be free!"); - stage = 523; - break; - case 1: - interpreter.sendOptions("Select an Option", "Sorry, I don't speak ghost.", "Ooh... THAT'S interesting.", "Any hints where I can find some treasure?"); - stage = 2; - break; - case 2: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Sorry, I don't speak ghost."); - stage = 10; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Ooh... THAT'S interesting."); - stage = 20; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Any hints where I can find some treasure?"); - stage = 30; - break; - - } - break; - case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Woo woo?"); - stage = 11; - break; - case 11: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Nope, still don't understand you."); - stage = 12; - break; - case 12: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "WOOOOOOOOO!"); - stage = 13; - break; - case 13: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Never mind."); - stage = 14; - break; - case 14: - end(); - break; - case 30: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Wooooooo woo! Wooooo woo wooooo woowoowoo wooo", "Woo woooo. Wooooo woo woo? Wooooooooooooooooooo!"); - stage = 31; - break; - case 31: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Sorry, I don't speak ghost."); - stage = 32; - break; - case 32: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Woo woo?"); - stage = 11; - break; - case 20: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Woo woooo. Woooooooooooooooooo!"); - stage = 21; - break; - case 21: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Did he really?"); - stage = 22; - break; - case 22: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Woo."); - stage = 23; - break; - case 23: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "My brother had EXACTLY the same problem."); - stage = 24; - break; - case 24: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Woo Wooooo!"); - stage = 25; - break; - case 25: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Wooooo Woo woo woo!"); - stage = 26; - break; - case 26: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Goodbye. Thanks for the chat."); - stage = 27; - break; - case 27: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Wooo wooo?"); - stage = 28; - break; - case 28: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 457 }; - } -} diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostListeners.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostListeners.kt new file mode 100644 index 000000000..9f7954753 --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostListeners.kt @@ -0,0 +1,195 @@ +package content.region.misthalin.lumbridge.quest.therestlessghost; + +import content.data.Quests +import core.api.addItem +import core.api.animate +import core.api.finishQuest +import core.api.freeSlots +import core.api.getQuestStage +import core.api.inBank +import core.api.inEquipmentOrInventory +import core.api.inInventory +import core.api.isQuestComplete +import core.api.lock +import core.api.playGlobalAudio +import core.api.queueScript +import core.api.removeItem +import core.api.sendMessage +import core.api.setAttribute +import core.api.setQuestStage +import core.api.stopExecuting +import core.api.visualize +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.Entity +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.node.scenery.SceneryBuilder +import core.game.world.map.Direction +import core.game.world.map.Location +import core.game.world.map.RegionManager +import core.game.world.map.zone.ZoneBorders +import org.rs09.consts.Animations +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery +import org.rs09.consts.Sounds + +class RestlessGhostListeners : InteractionListener { + + private fun createRestlessGhostNPC() : NPC { + val restlessGhostNPC = NPC(NPCs.RESTLESS_GHOST_457, Location(3250, 3195, 0)) + restlessGhostNPC.setAttribute("spawned:npc", true) + restlessGhostNPC.isRespawn = false + restlessGhostNPC.direction = Direction.SOUTH + restlessGhostNPC.init() + restlessGhostNPC.isWalks = false + restlessGhostNPC.isNeverWalks = true + return restlessGhostNPC + } + + private fun createLevel13SkeletonNPC(player: Player) : NPC { + val skele = NPC(NPCs.SKELETON_459, Location(3120, 9569, 0)) + skele.setAttribute("spawned:npc", true) + skele.setAttribute("target", player) + skele.isRespawn = false + skele.init() + skele.attack(player) + + return skele + } + + private fun toggleCoffinSceneryObj(player: Player, coffin: core.game.node.scenery.Scenery) { + + lock(player, 2) + + when (coffin.id) { + //opened + Scenery.COFFIN_15053, + Scenery.COFFIN_15052 -> { + //close it + visualize(player, 535, -1) + SceneryBuilder.replace(coffin, coffin.transform(Scenery.COFFIN_2145)) + } + Scenery.COFFIN_2145 -> { + //open it + visualize(player, Animations.HUMAN_OPEN_CHEST_536, -1) + + if (isQuestComplete(player, Quests.THE_RESTLESS_GHOST)) { + //full skele with skull + SceneryBuilder.replace(coffin, coffin.transform(Scenery.COFFIN_15053)) + } else { + SceneryBuilder.replace(coffin, coffin.transform(Scenery.COFFIN_15052)) + } + } + } + + } + + override fun defineListeners() { + on(Scenery.COFFIN_2145, IntType.SCENERY, "open") { player, node -> + + sendMessage(player, "You open the coffin.") + + playGlobalAudio(player.location, Sounds.COFFIN_OPEN_54) + + toggleCoffinSceneryObj(player, node as core.game.node.scenery.Scenery) + + if (!isQuestComplete(player, Quests.THE_RESTLESS_GHOST)) { + //is this from the center of the scenery/node object? + val ghostExists = RegionManager.getLocalNpcs(node.centerLocation).find { it.id == NPCs.RESTLESS_GHOST_457 } + + //not within rendering distance or doesn't exist at all + if (ghostExists == null) { + //spawn the ghost in + val ghost = createRestlessGhostNPC() + + //despawn ghost after awhile and shut the coffin + queueScript(player, 100) { _ -> + ghost.clear() + toggleCoffinSceneryObj(player, node) + return@queueScript stopExecuting(player) + } + } + } + + return@on true + } + + on(Scenery.COFFIN_15053, IntType.SCENERY, "close") { player, node -> + + sendMessage(player, "You close the coffin.") + playGlobalAudio(player.location, Sounds.COFFIN_CLOSE_53) + toggleCoffinSceneryObj(player, node as core.game.node.scenery.Scenery) + + return@on true + } + + on(Scenery.COFFIN_15052, IntType.SCENERY, "examine") { player, node -> + sendMessage(player, "You search the coffin and find some human remains.") + return@on true + } + + on(Scenery.COFFIN_15053, IntType.SCENERY, "examine") { player, node -> + sendMessage(player, "There's a nice and complete skeleton in here!") + return@on true + } + + + on(Scenery.COFFIN_15052, IntType.SCENERY, "close") { player, node -> + + sendMessage(player, "You close the coffin.") + playGlobalAudio(player.location, Sounds.COFFIN_CLOSE_53) + toggleCoffinSceneryObj(player, node as core.game.node.scenery.Scenery) + + return@on true + } + + onUseWith(IntType.SCENERY, Items.GHOSTS_SKULL_553, Scenery.COFFIN_15052) { player, used, with -> + if (removeItem(player, used)) { + sendMessage(player, "You put the skull in the coffin.") + finishQuest(player, Quests.THE_RESTLESS_GHOST) + + with as core.game.node.scenery.Scenery + SceneryBuilder.replace(with, with.transform(Scenery.COFFIN_15053)) + + } + + return@onUseWith true + } + + onUseWith(IntType.SCENERY, Items.GHOSTS_SKULL_553, Scenery.COFFIN_2145) { player, used, with -> + sendMessage(player, "Maybe I should open it first.") + return@onUseWith true + } + + on(Scenery.ALTAR_15051, IntType.SCENERY, "search") { player, node -> + + if (!inInventory(player, Items.GHOSTS_SKULL_553)) { + if (getQuestStage(player, Quests.THE_RESTLESS_GHOST) < 30) { + sendMessage(player, "You search the altar and find nothing.") + } else { + if (freeSlots(player) != 0) { + if (addItem(player, Items.GHOSTS_SKULL_553)) { + setQuestStage(player, Quests.THE_RESTLESS_GHOST, 40) + sendMessage(player, "The skeleton in the corner suddenly comes to life!") + + Location.create(3120, 9569, 0) + //spawn in skeleton + val s = createLevel13SkeletonNPC(player) + + // despawn after awhile if it never dies + // so there isnt a group of them for next visitor lol + queueScript(player, 200) { _ -> + s.clear() + return@queueScript stopExecuting(player) + } + } + } + } + } + + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostNPCDialogue.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostNPCDialogue.kt new file mode 100644 index 000000000..7fdf5b242 --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostNPCDialogue.kt @@ -0,0 +1,156 @@ +package content.region.misthalin.lumbridge.quest.therestlessghost; + +import content.data.Quests +import core.api.getQuestStage +import core.api.inEquipment +import core.api.inInventory +import core.api.setQuestStage +import core.game.dialogue.ChatAnim +import core.game.dialogue.DialogueLabeller +import core.game.dialogue.DialogueOption +import core.game.dialogue.FacialExpression +import core.game.interaction.InteractionListener +import core.game.interaction.IntType +import core.game.node.entity.npc.NPC +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +class RestlessGhostNPCDialogue : InteractionListener { + override fun defineListeners() { + on(NPCs.RESTLESS_GHOST_457, IntType.NPC, "talk-to") { player, node -> + DialogueLabeller.open(player, RestlessGhostNPCDialogueFile(), node as NPC) + return@on true + } + } +} + +class RestlessGhostNPCDialogueFile : DialogueLabeller() { + override fun addConversation() { + player(FacialExpression.HALF_GUILTY, "Hello ghost, how are you?") + + exec { player, npc -> + if (!inEquipment(player, Items.GHOSTSPEAK_AMULET_552)) { + npc(ChatAnim.HALF_GUILTY, "Wooo wooo wooooo!") + goto("no_amulet_opts") + } else { + when (getQuestStage(player, Quests.THE_RESTLESS_GHOST)) { + 20 -> { + goto("not_very_good") + } + 30 -> { + goto("finding_my_skull") + } + else -> { + npc("Fine, thanks") + end() + } + } + } + } + + label("finding_my_skull") { + npc("How are you doing finding my skull?") + + exec { player, npc -> + if (inInventory(player, Items.GHOSTS_SKULL_553)) { + player(FacialExpression.FRIENDLY, "I have found it!") + npc(FacialExpression.HAPPY, "Hurrah! Now I can stop being a ghost! You just need", "to put it on my coffin there, and I will be free!") + + } else { + player(FacialExpression.HALF_GUILTY, "Sorry, I can't find it at the moment.") + npc(FacialExpression.HALF_GUILTY, "Ah well. Keep on looking.") + npc(FacialExpression.HALF_GUILTY, "I'm pretty sure it's somewhere in the tower south-west", "from here. There's a lot of levels to the tower, though. I", "suppose it might take a little while to find.") + + } + end() + } + } + + label("not_very_good") { + npc(FacialExpression.HALF_GUILTY, "Not very good actually.") + player(FacialExpression.HALF_GUILTY, "What's the problem then?") + npc(FacialExpression.HALF_GUILTY, "Did you understand what I said???") + player(FacialExpression.HALF_GUILTY, "Yep, now tell me what the problem is.") + npc(FacialExpression.HALF_GUILTY, "WOW! This is INCREDIBLE! I didn't expect anyone", "to ever understand me again!") + player(FacialExpression.HALF_GUILTY, "Ok, ok, I can understand you!") + player(FacialExpression.HALF_GUILTY, "But have you any idea WHY you're doomed to be a", "ghost?") + npc(FacialExpression.HALF_GUILTY, "Well, to be honest... I'm not sure.") + player(FacialExpression.HALF_GUILTY, "I've been told a certain task may need to be completed", "so you can rest in peace.") + npc(FacialExpression.HALF_GUILTY, "I should think it is probably because a warlock has come", "along and stolen my skull. If you look inside my coffin", "there, you'll find my corpse without a head on it.") + player("FacialExpression.HALF_GUILTY, Do you know where this warlock might be now?") + npc(FacialExpression.HALF_GUILTY, "I think it was one of the warlocks who lives in the big", "tower by the sea south-west from here.") + player(FacialExpression.HALF_GUILTY, "Ok. I will try and get the skull back for you, then you", "can rest in peace.") + + exec { player, npc -> + setQuestStage(player, Quests.THE_RESTLESS_GHOST, 30) + goto("great_relief") + } + } + + label("great_relief") { + npc(FacialExpression.HALF_GUILTY, "Ooh, thank you. That would be such a great relief!") + npc(FacialExpression.HALF_GUILTY, "It is so dull being a ghost...") + end() + } + + label("no_amulet_opts") { + options( + DialogueOption( + "dont_speak_ghost", + "Sorry, I don't speak ghost.", + expression = ChatAnim.HALF_GUILTY + ), + DialogueOption( + "thats_interesting", + "Ooh... THAT's interesting.", + expression = ChatAnim.HALF_GUILTY + ), + DialogueOption( + "find_treasure", + "Any hints where I can find some treasure?", + ) + ) + } + + label("dont_speak_ghost") { + npc(FacialExpression.HALF_GUILTY, "Woo woo?") + player(FacialExpression.HALF_GUILTY, "Nope, still don't understand you.") + + npc(FacialExpression.HALF_GUILTY, "WOOOOOOOOO!") + player(FacialExpression.HALF_GUILTY, "Never mind.") + + end() + } + + label("thats_interesting") { + npc(FacialExpression.HALF_GUILTY, "Woo woooo. Woooooooooooooooooo!") + player(FacialExpression.HALF_GUILTY, "Did he really?") + + npc(FacialExpression.HALF_GUILTY, "Woo.") + player(FacialExpression.HALF_GUILTY, "My brother had EXACTLY the same problem.") + + npc(FacialExpression.HALF_GUILTY, "Woo Wooooo!") + npc(FacialExpression.HALF_GUILTY, "Wooooo Woo woo woo!") + + player(FacialExpression.HALF_GUILTY, "Goodbye. Thanks for the chat.") + npc(FacialExpression.HALF_GUILTY, "Wooo wooo?") + + end() + } + + label("find_treasure") { + npc(FacialExpression.HALF_GUILTY, "Wooooooo woo! Wooooo woo wooooo woowoowoo wooo", "Woo woooo. Wooooo woo woo? Wooooooooooooooooooo!") + player(FacialExpression.HALF_GUILTY, "Sorry, I don't speak ghost.") + + npc(FacialExpression.HALF_GUILTY, "Woo woo?") + player(FacialExpression.HALF_GUILTY, "Nope, still don't understand you.") + + npc(FacialExpression.HALF_GUILTY, "WOOOOOOOOO!") + player(FacialExpression.HALF_GUILTY, "Never mind.") + + end() + } + + } + +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostPlugin.java b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostPlugin.java deleted file mode 100644 index d19f260a1..000000000 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostPlugin.java +++ /dev/null @@ -1,273 +0,0 @@ -package content.region.misthalin.lumbridge.quest.therestlessghost; - -import content.data.Quests; -import core.cache.def.impl.SceneryDefinition; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.Entity; -import core.game.node.entity.combat.CombatStyle; -import core.game.node.entity.npc.AbstractNPC; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.game.node.item.GroundItemManager; -import core.game.node.item.Item; -import core.game.node.scenery.Scenery; -import core.game.node.scenery.SceneryBuilder; -import core.game.system.task.Pulse; -import core.game.world.GameWorld; -import core.game.world.map.Location; -import core.game.world.update.flag.context.Animation; -import core.plugin.Initializable; -import core.plugin.Plugin; - -import static core.api.ContentAPIKt.*; - -/** - * Represents the restless ghost plugin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class RestlessGhostPlugin extends OptionHandler { - - /** - * Represents the close animation of a content. - */ - private static final Animation OPEN_ANIM = new Animation(536); - - /** - * Represents the close animation of a content. - */ - private static final Animation CLOSE_ANIM = new Animation(535); - - /** - * Represents the skull item. - */ - private static final Item SKULL = new Item(964); - - /** - * Represents the ghost npc spawned. - */ - private static RestlessGhostNPC GHOST; - - /** - * Represents the coffin inds. - */ - private static final int[] COFFIN_IDS = new int[] { 2145, 15061, 15052, 15053, 15050, 15051 }; - - /** - * Represents the options. - */ - private static final String[] OPTIONS = new String[] { "open", "close", "search" }; - - @Override - public Plugin newInstance(Object arg) throws Throwable { - for (int coffin : COFFIN_IDS) { - for (String option : OPTIONS) { - SceneryDefinition.forId(coffin).getHandlers().put("option:" + option, this); - } - } - new RestlessGhostNPC().newInstance(arg); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - if (GHOST == null) { - GHOST = new RestlessGhostNPC(457, Location.create(3250, 3195, 0)); - GHOST.init(); - GHOST.setInvisible(true); - } - final int id = ((Scenery) node).getId(); - final Scenery object = (Scenery) node; - switch (option) { - case "open": - switch (id) { - default: - toggleCoffin(player, object); - break; - } - case "close": - switch (id) { - case 15052: - case 15053: - toggleCoffin(player, object); - break; - } - break; - case "search": - switch (id) { - case 15052: - player.getPacketDispatch().sendMessage("You search the coffin and find some human remains."); - break; - case 15053: - player.getDialogueInterpreter().sendDialogue("There's a nice and complete skeleton in here!"); - break; - case 15050: - searchAltar(player, object); - break; - case 15051: - if (!player.getQuestRepository().isComplete(Quests.THE_RESTLESS_GHOST) && !player.getBank().containsItem(SKULL) && !player.getInventory().containsItem(SKULL)) { - player.getInventory().add(SKULL); - player.getPacketDispatch().sendMessage("You find another skull."); - } - player.getQuestRepository().getQuest(Quests.THE_RESTLESS_GHOST).setStage(player, 40); - break; - case 2145: - toggleCoffin(player, object); - break; - } - break; - } - return true; - } - - /** - * Method used to toggle the coffin. - * @param player the player. - * @param object the object. - */ - private void toggleCoffin(final Player player, final Scenery object) { - final boolean open = object.getId() == 2145; - player.lock(2); - player.animate(open ? OPEN_ANIM : CLOSE_ANIM); - SceneryBuilder.replace(object, object.transform(open ? 15061 : 2145)); - player.getPacketDispatch().sendMessage("You " + (open ? "open" : "close") + " the coffin."); - if (open && !player.getQuestRepository().isComplete(Quests.THE_RESTLESS_GHOST)) { - sendGhost(); - } - } - - /** - * Method used to send the restless ghost npc. - */ - private void sendGhost() { - if (!GHOST.isInvisible()) { - return; - } - GHOST.setInvisible(false); - GameWorld.getPulser().submit(new Pulse(100, GHOST) { - @Override - public boolean pulse() { - GHOST.setInvisible(true); - return true; - } - }); - } - - /** - * Method used to search the altar. - * @param player the player. - * @param object the object. - */ - private void searchAltar(final Player player, final Scenery object) { - final boolean hasSkull = object.getId() == 15051; - if (player.getQuestRepository().getQuest(Quests.THE_RESTLESS_GHOST).getStage(player) != 30) { - player.getPacketDispatch().sendMessage("You search the altar and find nothing."); - return; - } - if (!hasSkull) { - if (!player.getInventory().add(SKULL)) { - GroundItemManager.create(SKULL, player); - } - setVarp(player, 728, 5, true); - player.getQuestRepository().getQuest(Quests.THE_RESTLESS_GHOST).setStage(player, 40); - player.getPacketDispatch().sendMessage("The skeleton in the corner suddenly comes to life!"); - sendSkeleton(player); - } - } - - /** - * Method used to send the skeleton. - * @param player the player. - */ - private void sendSkeleton(final Player player) { - final NPC skeleton = NPC.create(459, Location.create(3120, 9568, 0)); - skeleton.setWalks(false); - skeleton.setRespawn(false); - skeleton.setAttribute("player", player); - skeleton.init(); - skeleton.getProperties().getCombatPulse().setStyle(CombatStyle.MELEE); - skeleton.getProperties().getCombatPulse().attack(player); - } - - /** - * Handles the temple guardian npc. - * @author 'Vexia - */ - public static final class RestlessGhostNPC extends AbstractNPC { - - /** - * The NPC ids of NPCs using this plugin. - */ - private static final int[] ID = { 459, 457 }; - - /** - * Constructs a new {@code AlKharidWarriorPlugin} {@code Object}. - */ - public RestlessGhostNPC() { - super(0, null); - } - - /** - * Constructs a new {@code AlKharidWarriorPlugin} {@code Object}. - * @param id The NPC id. - * @param location The location. - */ - private RestlessGhostNPC(int id, Location location) { - super(id, location, false); - } - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new RestlessGhostNPC(id, location); - } - - @Override - public void init() { - super.init(); - this.getProperties().getCombatPulse().setStyle(CombatStyle.MELEE); - } - - @Override - public void tick() { - super.tick(); - final Player pl = getAttribute("player", null); - if (pl != null) { - if (getAttribute("dead", false) || !getLocation().withinDistance(pl.getLocation(), 16)) { - clear(); - return; - } - } - } - - @Override - public boolean isAttackable(Entity entity, CombatStyle style, boolean message) { - final Player player = ((Player) entity); - final Player pl = getAttribute("player", null); - return pl == null ? false : pl == player ? true : false; - } - - @Override - public void finalizeDeath(Entity killer) { - super.finalizeDeath(killer); - removeAttribute("player"); - } - - @Override - public boolean isHidden(final Player player) { - final Player pl = getAttribute("player", null); - if (this.getRespawnTick() > GameWorld.getTicks()) { - return true; - } - return player.getQuestRepository().isComplete(Quests.THE_RESTLESS_GHOST) || (pl != null && player != pl); - } - - @Override - public int[] getIds() { - return ID; - } - - } - -} diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostSkull.java b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostSkull.java deleted file mode 100644 index 4a899c64a..000000000 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostSkull.java +++ /dev/null @@ -1,51 +0,0 @@ -package content.region.misthalin.lumbridge.quest.therestlessghost; - -import content.data.Quests; -import core.api.Container; -import core.game.interaction.NodeUsageEvent; -import core.game.interaction.UseWithHandler; -import core.game.node.scenery.Scenery; -import core.plugin.Initializable; -import core.plugin.Plugin; -import org.rs09.consts.Items; - -import static core.api.ContentAPIKt.removeItem; - -/** - * Represents the plugin used to add the skull to the cofin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class RestlessGhostSkull extends UseWithHandler { - - /** - * Constructs a new {@code RestlessGhostSkull} {@code Object}. - */ - public RestlessGhostSkull() { - super(964); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - addHandler(2145, OBJECT_TYPE, this); - addHandler(15052, OBJECT_TYPE, this); - addHandler(15061, OBJECT_TYPE, this); - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - final Scenery object = (Scenery) event.getUsedWith(); - if (object.getId() == 2145) { - event.getPlayer().getDialogueInterpreter().sendDialogue("Maybe I should open it first."); - return true; - } - if (removeItem(event.getPlayer(), Items.SKULL_964, Container.INVENTORY)) { - event.getPlayer().getPacketDispatch().sendMessage("You put the skull in the coffin."); - event.getPlayer().getQuestRepository().getQuest(Quests.THE_RESTLESS_GHOST).finish(event.getPlayer()); - } - return true; - } - -} diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/SkullDropPlugin.java b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/SkullDropPlugin.java deleted file mode 100644 index 0fdaf0b68..000000000 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/SkullDropPlugin.java +++ /dev/null @@ -1,32 +0,0 @@ -package content.region.misthalin.lumbridge.quest.therestlessghost; - -import core.cache.def.impl.ItemDefinition; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.player.Player; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/* - * 8 - */ -@Initializable -public class SkullDropPlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ItemDefinition.forId(964).getHandlers().put("option:drop", this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - player.getPacketDispatch().sendMessage("You can't drop this! Return it to the ghost."); - return true; - } - - @Override - public boolean isWalk() { - return false; - } -} From 64234417dc2453bc062b048b7637c9271d87754e Mon Sep 17 00:00:00 2001 From: Beck Date: Tue, 2 Jun 2026 01:18:27 -0700 Subject: [PATCH 02/19] Update RestlessGhostListeners.kt --- .../RestlessGhostListeners.kt | 123 ++++++++++-------- 1 file changed, 66 insertions(+), 57 deletions(-) diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostListeners.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostListeners.kt index 9f7954753..71977d393 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostListeners.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostListeners.kt @@ -14,6 +14,8 @@ import core.api.lock import core.api.playGlobalAudio import core.api.queueScript import core.api.removeItem +import core.api.sendDialogue +import core.api.sendItemDialogue import core.api.sendMessage import core.api.setAttribute import core.api.setQuestStage @@ -37,57 +39,65 @@ import org.rs09.consts.Sounds class RestlessGhostListeners : InteractionListener { - private fun createRestlessGhostNPC() : NPC { - val restlessGhostNPC = NPC(NPCs.RESTLESS_GHOST_457, Location(3250, 3195, 0)) - restlessGhostNPC.setAttribute("spawned:npc", true) - restlessGhostNPC.isRespawn = false - restlessGhostNPC.direction = Direction.SOUTH - restlessGhostNPC.init() - restlessGhostNPC.isWalks = false - restlessGhostNPC.isNeverWalks = true - return restlessGhostNPC - } + companion object { + private const val Animations_HUMAN_CLOSE_CHEST_535 = 535 - private fun createLevel13SkeletonNPC(player: Player) : NPC { - val skele = NPC(NPCs.SKELETON_459, Location(3120, 9569, 0)) - skele.setAttribute("spawned:npc", true) - skele.setAttribute("target", player) - skele.isRespawn = false - skele.init() - skele.attack(player) + private const val CLOSED_COFFIN = Scenery.COFFIN_2145 + private const val INCOMPLETE_COFFIN_OPEN = Scenery.COFFIN_15052 + private const val COMPLETE_COFFIN_OPEN = Scenery.COFFIN_15053 - return skele - } + private fun createRestlessGhostNPC() : NPC { + val restlessGhostNPC = NPC(NPCs.RESTLESS_GHOST_457, Location(3250, 3195, 0)) + restlessGhostNPC.setAttribute("spawned:npc", true) + restlessGhostNPC.isRespawn = false + restlessGhostNPC.direction = Direction.SOUTH + restlessGhostNPC.init() + restlessGhostNPC.isWalks = false + restlessGhostNPC.isNeverWalks = true + return restlessGhostNPC + } - private fun toggleCoffinSceneryObj(player: Player, coffin: core.game.node.scenery.Scenery) { - - lock(player, 2) + private fun createLevel13SkeletonNPC(player: Player) : NPC { + val skele = NPC(NPCs.SKELETON_459, Location(3120, 9569, 0)) + skele.setAttribute("spawned:npc", true) + skele.setAttribute("target", player) + skele.isRespawn = false + skele.init() + skele.attack(player) - when (coffin.id) { - //opened - Scenery.COFFIN_15053, - Scenery.COFFIN_15052 -> { - //close it - visualize(player, 535, -1) - SceneryBuilder.replace(coffin, coffin.transform(Scenery.COFFIN_2145)) - } - Scenery.COFFIN_2145 -> { - //open it - visualize(player, Animations.HUMAN_OPEN_CHEST_536, -1) + return skele + } - if (isQuestComplete(player, Quests.THE_RESTLESS_GHOST)) { - //full skele with skull - SceneryBuilder.replace(coffin, coffin.transform(Scenery.COFFIN_15053)) - } else { - SceneryBuilder.replace(coffin, coffin.transform(Scenery.COFFIN_15052)) + private fun toggleCoffinSceneryObj(player: Player, coffin: core.game.node.scenery.Scenery) { + + lock(player, 2) + + when (coffin.id) { + //opened + COMPLETE_COFFIN_OPEN, + INCOMPLETE_COFFIN_OPEN -> { + //close it + visualize(player, Animations_HUMAN_CLOSE_CHEST_535, -1) + SceneryBuilder.replace(coffin, coffin.transform(CLOSED_COFFIN)) + } + CLOSED_COFFIN -> { + //open it + visualize(player, Animations.HUMAN_OPEN_CHEST_536, -1) + + if (isQuestComplete(player, Quests.THE_RESTLESS_GHOST)) { + //full skele with skull + SceneryBuilder.replace(coffin, coffin.transform(COMPLETE_COFFIN_OPEN)) + } else { + SceneryBuilder.replace(coffin, coffin.transform(INCOMPLETE_COFFIN_OPEN)) + } } } + } - } override fun defineListeners() { - on(Scenery.COFFIN_2145, IntType.SCENERY, "open") { player, node -> + on(CLOSED_COFFIN, IntType.SCENERY, "open") { player, node -> sendMessage(player, "You open the coffin.") @@ -116,7 +126,12 @@ class RestlessGhostListeners : InteractionListener { return@on true } - on(Scenery.COFFIN_15053, IntType.SCENERY, "close") { player, node -> + onUseWith(IntType.SCENERY, Items.GHOSTS_SKULL_553, CLOSED_COFFIN) { player, used, with -> + sendMessage(player, "Maybe I should open it first.") + return@onUseWith true + } + + on(COMPLETE_COFFIN_OPEN, IntType.SCENERY, "close") { player, node -> sendMessage(player, "You close the coffin.") playGlobalAudio(player.location, Sounds.COFFIN_CLOSE_53) @@ -125,18 +140,17 @@ class RestlessGhostListeners : InteractionListener { return@on true } - on(Scenery.COFFIN_15052, IntType.SCENERY, "examine") { player, node -> + on(COMPLETE_COFFIN_OPEN, IntType.SCENERY, "search") { player, node -> + sendDialogue(player, "There's a nice and complete skeleton in here!") + return@on true + } + + on(INCOMPLETE_COFFIN_OPEN, IntType.SCENERY, "search") { player, node -> sendMessage(player, "You search the coffin and find some human remains.") return@on true } - on(Scenery.COFFIN_15053, IntType.SCENERY, "examine") { player, node -> - sendMessage(player, "There's a nice and complete skeleton in here!") - return@on true - } - - - on(Scenery.COFFIN_15052, IntType.SCENERY, "close") { player, node -> + on(INCOMPLETE_COFFIN_OPEN, IntType.SCENERY, "close") { player, node -> sendMessage(player, "You close the coffin.") playGlobalAudio(player.location, Sounds.COFFIN_CLOSE_53) @@ -145,24 +159,19 @@ class RestlessGhostListeners : InteractionListener { return@on true } - onUseWith(IntType.SCENERY, Items.GHOSTS_SKULL_553, Scenery.COFFIN_15052) { player, used, with -> + onUseWith(IntType.SCENERY, Items.GHOSTS_SKULL_553, INCOMPLETE_COFFIN_OPEN) { player, used, with -> if (removeItem(player, used)) { - sendMessage(player, "You put the skull in the coffin.") + sendItemDialogue(player, Items.GHOSTS_SKULL_553, "You put the skull in the coffin.") finishQuest(player, Quests.THE_RESTLESS_GHOST) with as core.game.node.scenery.Scenery - SceneryBuilder.replace(with, with.transform(Scenery.COFFIN_15053)) + SceneryBuilder.replace(with, with.transform(COMPLETE_COFFIN_OPEN)) } return@onUseWith true } - onUseWith(IntType.SCENERY, Items.GHOSTS_SKULL_553, Scenery.COFFIN_2145) { player, used, with -> - sendMessage(player, "Maybe I should open it first.") - return@onUseWith true - } - on(Scenery.ALTAR_15051, IntType.SCENERY, "search") { player, node -> if (!inInventory(player, Items.GHOSTS_SKULL_553)) { From d14e4ac3a66479063b6b5f535a74fc86c0f3fb45 Mon Sep 17 00:00:00 2001 From: Beck Date: Wed, 3 Jun 2026 23:52:29 -0700 Subject: [PATCH 03/19] dialogue fixes --- .../dialogue/FatherAereckDefaultDialogue.kt | 154 +++++++++--------- .../FatherAereckQuestDialogueFile.kt | 33 +++- 2 files changed, 110 insertions(+), 77 deletions(-) diff --git a/Server/src/main/content/region/misthalin/lumbridge/dialogue/FatherAereckDefaultDialogue.kt b/Server/src/main/content/region/misthalin/lumbridge/dialogue/FatherAereckDefaultDialogue.kt index 11a1eb9f3..77606f0ae 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/dialogue/FatherAereckDefaultDialogue.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/FatherAereckDefaultDialogue.kt @@ -31,84 +31,86 @@ class FatherAereckDefaultDialogue : InteractionListener { */ class FatherAereckDefaultDialogueFile : DialogueLabeller() { override fun addConversation() { - - //allows skipping the main dialogue while Restless Ghost quest is started - exec { player, npc -> - when (getQuestStage(player, Quests.THE_RESTLESS_GHOST)) { - 0 -> { - loadLabel(player, "main_entry_transcript") - } - 10 -> { - open(player, FatherAereckQuestDialogueFile(), npc) - loadLabel(player, "quest_stage_10") - } - 20 -> { - open(player, FatherAereckQuestDialogueFile(), npc) - loadLabel(player, "quest_stage_20") - } - 30 -> { - open(player, FatherAereckQuestDialogueFile(), npc) - loadLabel(player, "quest_stage_30") - } - 40 -> { - open(player, FatherAereckQuestDialogueFile(), npc) - loadLabel(player, "quest_stage_40") + + goto("entry_logic") + + label("entry_logic") + //allows skipping the main dialogue while Restless Ghost quest is started + exec { player, npc -> + when (getQuestStage(player, Quests.THE_RESTLESS_GHOST)) { + 0 -> { + loadLabel(player, "main_entry_transcript") + } + 10, 20, 30, 40 -> { + //passthrough for mid-quest dialogue skip + open(player, FatherAereckQuestDialogueFile(), npc) + } + else -> { + loadLabel(player, "main_entry_transcript") + } } } - } label("main_entry_transcript") { - npc("Welcome to the church of holy Saradomin") + npc("Welcome to the church of holy Saradomin.") goto("main_opts") } label("main_opts") { exec { player, npc -> if (getQuestStage(player, Quests.THE_RESTLESS_GHOST) > 0) { - options( - DialogueOption( - "change_grave", - "Can you change my gravestone now?", - expression = ChatAnim.HALF_ASKING - ), - DialogueOption( - "who_sara", - "Who's Saradomin?", - expression = ChatAnim.HALF_ASKING - ), - DialogueOption( - "nice_place", - "Nice place you've got here.", - expression = ChatAnim.FRIENDLY - ) - ) + loadLabel(player, "no_quest_main_opts") } else { - options( - DialogueOption( - "change_grave", - "Can you change my gravestone now?", - expression = ChatAnim.HALF_ASKING - ), - DialogueOption( - "who_sara", - "Who's Saradomin?", - expression = ChatAnim.HALF_ASKING - ), - DialogueOption( - "nice_place", - "Nice place you've got here.", - expression = ChatAnim.FRIENDLY - ), - DialogueOption( - "start_restless_ghost_quest", - "I'm looking for a quest.", - expression = ChatAnim.HALF_ASKING - ) - ) + loadLabel(player, "not_started_quest_main_opts") } } } + label("no_quest_main_opts") { + options( + DialogueOption( + "change_grave", + "Can you change my gravestone now?", + expression = ChatAnim.HALF_ASKING + ), + DialogueOption( + "who_sara", + "Who's Saradomin?", + expression = ChatAnim.HALF_ASKING + ), + DialogueOption( + "nice_place", + "Nice place you've got here.", + expression = ChatAnim.FRIENDLY + ) + ) + } + + label("not_started_quest_main_opts") { + options( + DialogueOption( + "change_grave", + "Can you change my gravestone now?", + expression = ChatAnim.HALF_ASKING + ), + DialogueOption( + "who_sara", + "Who's Saradomin?", + expression = ChatAnim.HALF_ASKING + ), + DialogueOption( + "nice_place", + "Nice place you've got here.", + expression = ChatAnim.FRIENDLY + ), + DialogueOption( + "start_restless_ghost_quest", + "I'm looking for a quest.", + expression = ChatAnim.HALF_ASKING + ) + ) + } + label("change_grave") { npc("Certainly. All proceeds are donated to the", "Varrockian Guards' Widows & Orphans Fund.") goto("gravestone_iface") @@ -142,7 +144,8 @@ class FatherAereckDefaultDialogueFile : DialogueLabeller() { label("that_sara") { npc("There is only one Saradomin.") player("Yeah. I, uh, thought you said something else.") - end() + + goto("nowhere") } label("not_from_this_world") { @@ -168,35 +171,38 @@ class FatherAereckDefaultDialogueFile : DialogueLabeller() { label("disguise_not_me") { npc("Well, I can see that. Still, there's something special about", "you.") player("Thanks, I think.") - end() + + goto("nowhere") } label("like_disguise") { npc("Argh! Avaunt, foul creature from another dimension!", "Avaunt! Begone in the name of Saradomin!") player("Okay, okay, I was only joking!") - end() + + goto("nowhere") } label("nice_place") { npc("It is, isn't it? It was built over two centuries ago."); - end() + + goto("nowhere") } label("gravestone_iface") { - exec { player, npc -> openInterface(player, Components.GRAVESTONE_SHOP_652) player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 0, 15); - end() + loadLabel(player, "nowhere") } } - label("start_restless_ghost") { - exec { player, npc -> - open(player, FatherAereckQuestDialogueFile(), npc) - loadLabel(player, "quest_main_entry") - } + //compile this too see if it works + label("start_restless_ghost_quest") { + open(player!!, FatherAereckQuestDialogueFile(), npc!!) } + // Because using end() breaks shit + label("nowhere") {} + } } \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherAereckQuestDialogueFile.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherAereckQuestDialogueFile.kt index 4d85b152d..8ffd9774c 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherAereckQuestDialogueFile.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherAereckQuestDialogueFile.kt @@ -21,6 +21,27 @@ class FatherAereckQuestDialogueFile : DialogueLabeller() { override fun addConversation() { + //passthrough for mid-quest dialogue skip + exec { player, npc -> + when (getQuestStage(player, Quests.THE_RESTLESS_GHOST)) { + 10 -> { + loadLabel(player, "quest_stage_10") + } + 20 -> { + loadLabel(player, "quest_stage_20") + } + 30 -> { + loadLabel(player, "quest_stage_30") + } + 40 -> { + loadLabel(player, "quest_stage_40") + } + else -> { + loadLabel(player, "quest_main_entry") + } + } + } + label("quest_main_entry") { npc("That's lucky, I need someone to do a quest for me.") goto("start_quest_opts") @@ -54,13 +75,15 @@ class FatherAereckQuestDialogueFile : DialogueLabeller() { npc("Take care travelling through the swamps, I have heard", "they can be quite dangerous.") player("I will, thanks.") - - end() + + goto("nowhere") + } label("no_time") { npc("Oh well. If you do have some spare time on your", "hands, come back and talk to me.") - end() + + goto("nowhere") } //mid quest dialogue @@ -95,5 +118,9 @@ class FatherAereckQuestDialogueFile : DialogueLabeller() { npc("Great! Put it in the ghost's coffin and see what", "happens!"); } + + //Because doing end() breaks + label("nowhere") {} + } } \ No newline at end of file From 23fda5fbc93ff94c7c1a4d7821c0a34b56256349 Mon Sep 17 00:00:00 2001 From: Beck Date: Thu, 4 Jun 2026 00:36:22 -0700 Subject: [PATCH 04/19] more dialogue --- .../dialogue/FatherUrhneyDefaultDialogue.kt | 114 +++++++++------- .../FatherUrhneyQuestDialogueFile.kt | 125 ++++++++++-------- 2 files changed, 130 insertions(+), 109 deletions(-) diff --git a/Server/src/main/content/region/misthalin/lumbridge/dialogue/FatherUrhneyDefaultDialogue.kt b/Server/src/main/content/region/misthalin/lumbridge/dialogue/FatherUrhneyDefaultDialogue.kt index 5acd45f58..154586294 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/dialogue/FatherUrhneyDefaultDialogue.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/FatherUrhneyDefaultDialogue.kt @@ -1,7 +1,7 @@ package content.region.misthalin.lumbridge.dialogue; import content.data.Quests -import content.region.misthalin.lumbridge.quest.therestlessghost.FatherUrhneyQuestDialogueFile +import content.region.misthalin.lumbridge.quest.therestlessghost.* import core.api.addItem import core.api.freeSlots import core.api.getQuestStage @@ -36,73 +36,72 @@ class FatherUrhneyDefaultDialogueFile: DialogueLabeller() { goto("main_opts") label("main_opts") { - exec {player, npc -> when (getQuestStage(player, Quests.THE_RESTLESS_GHOST)) { 0 -> { - options( - DialogueOption( - "thats_friendly", - "Well, that's friendly" - ), - DialogueOption( - "repo_house", - "I've come to repossess your house." - ), - - ) + loadLabel(player, "pre_quest_opts") } 10 -> { - options( - DialogueOption( - "thats_friendly", - "Well, that's friendly" - ), - DialogueOption( - "father_aereck_sent", - "Father Aereck sent me to talk to you." - ), - DialogueOption( - "repo_house", - "I've come to repossess your house." - ) - ) + loadLabel(player, "mid_quest_opts") } - else -> { - options( - DialogueOption( - "thats_friendly", - "Well, that's friendly" - ), - DialogueOption( - "ive_lost_amulet", - "I've lost the Amulet of Ghostspeak." - ), - DialogueOption( - "repo_house", - "I've come to repossess your house." - ) - ) + loadLabel(player, "post_quest_opts") } } } } - label("father_aereck_sent") { - open(player!!, FatherUrhneyQuestDialogueFile(), npc!!) - loadLabel(player!!, "father_aereck_sent_me") + label("pre_quest_opts") { + options( + DialogueOption( + "thats_friendly", + "Well, that's friendly" + ), + DialogueOption( + "repo_house", + "I've come to repossess your house." + ) + ) } - label("ive_lost_amulet") { - open(player!!, FatherUrhneyQuestDialogueFile(), npc!!) - loadLabel(player!!, "lost_amulet") + label("mid_quest_opts") { + options( + DialogueOption( + "thats_friendly", + "Well, that's friendly" + ), + DialogueOption( + "father_aereck_sent", + "Father Aereck sent me to talk to you." + ), + DialogueOption( + "repo_house", + "I've come to repossess your house." + ) + ) + } + + label("post_quest_opts") { + options( + DialogueOption( + "thats_friendly", + "Well, that's friendly." + ), + DialogueOption( + "ive_lost_amulet", + "I've lost the Amulet of Ghostspeak." + ), + DialogueOption( + "repo_house", + "I've come to repossess your house." + ) + ) } label("thats_friendly") { npc("I SAID go AWAY.") player("Okay, okay... sheesh, what a grouch.") - end() + goto("nowhere") } label("repo_house") { @@ -129,13 +128,26 @@ class FatherUrhneyDefaultDialogueFile: DialogueLabeller() { player("Sorry. I must have got the wrong address. All the", "houses look the same around here.") npc("What? What houses? What ARE you talking about???") player("Never mind.") - end() + goto("nowhere") } label("want_house") { npc("Oh... go away and stop wasting my time!") - end() + goto("nowhere") } + //needs to be down here for some stupid reason + label("father_aereck_sent") { + open(player!!, FatherUrhneyQuestDialogueFile(), npc!!) + } + + //needs to be down here for some stupid reason + label("ive_lost_amulet") { + open(player!!, FatherUrhneyQuestAmuletDialogueFile(), npc!!) + } + + //because end() breaks shit + label("nowhere") {} + } } \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherUrhneyQuestDialogueFile.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherUrhneyQuestDialogueFile.kt index 9f0ea450c..fc9cd596b 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherUrhneyQuestDialogueFile.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherUrhneyQuestDialogueFile.kt @@ -5,6 +5,7 @@ import core.api.addItem import core.api.anyInEquipment import core.api.anyInInventory import core.api.freeSlots +import core.api.getQuestStage import core.api.inBank import core.api.inInventory import core.api.sendDialogue @@ -19,77 +20,85 @@ import org.rs09.consts.Items class FatherUrhneyQuestDialogueFile : DialogueLabeller() { override fun addConversation() { - label("father_aereck_sent_me") { - npc(FacialExpression.ANGRY, "I suppose I'd better talk to you then. What problems", "has he got himself into this time?"); - player("He's got a ghost haunting his graveyard."); - - npc(FacialExpression.ANGRY, "Oh, the silly fool."); - npc(FacialExpression.ANGRY, "I leave town for just five months, and ALREADY he", "can't manage."); - npc(FacialExpression.SAD, "(sigh)"); - npc(FacialExpression.ANGRY, "Well, I can't go back and exorcise it. I vowed not to", "leave this place. Until I had done a full two years of", "prayer and meditation."); - npc(FacialExpression.NEUTRAL, "Tell you what I can do though; take this amulet."); + + npc(FacialExpression.ANGRY, "I suppose I'd better talk to you then. What problems", "has he got himself into this time?"); + player("He's got a ghost haunting his graveyard."); + + npc(FacialExpression.ANGRY, "Oh, the silly fool."); + npc(FacialExpression.ANGRY, "I leave town for just five months, and ALREADY he", "can't manage."); + npc(FacialExpression.SAD, "(sigh)"); + npc(FacialExpression.ANGRY, "Well, I can't go back and exorcise it. I vowed not to", "leave this place. Until I had done a full two years of", "prayer and meditation."); + npc(FacialExpression.NEUTRAL, "Tell you what I can do though; take this amulet."); - goto("take_amulet") - } + goto("take_amulet") label("take_amulet") { exec { player, npc -> - if (freeSlots(player) >= 1) { - if (addItem(player, Items.GHOSTSPEAK_AMULET_552, 1)) { - sendItemDialogue(player, Item(Items.GHOSTSPEAK_AMULET_552), "Father Urhney hands you an amulet.") - setQuestStage(player, Quests.THE_RESTLESS_GHOST, 20) - - npc(FacialExpression.NEUTRAL, "It is an Amulet of Ghostspeak."); - npc(FacialExpression.NEUTRAL, "So called, because when you wear it you can speak to", "ghosts. A lot of ghosts are doomed to be ghosts because", "they have left some important task uncompleted."); - npc(FacialExpression.NEUTRAL, "Maybe if you know what this task is, you can get rid of", "the ghost. I'm not making any guarantees mind you,", "but it is the best I can do right now."); - player("Thank you. I'll give it a try!"); + if (getQuestStage(player, Quests.THE_RESTLESS_GHOST) in 10..19) { + item(Item(Items.GHOSTSPEAK_AMULET_552), "Father Urhney hands you an amulet.") + if (freeSlots(player) >= 1) { + if (addItem(player, Items.GHOSTSPEAK_AMULET_552, 1)) { + + setQuestStage(player, Quests.THE_RESTLESS_GHOST, 20) + + loadLabel(player, "post_take_amulet") + } + } else { + sendMessage(player, "You don't have enough inventory space to accept this amulet.") } - } else { - // Closes the dialogue prematurely to reveal message - end() - sendMessage(player, "You don't have enough inventory space to accept this amulet.") + } } - - end() + + goto("nowhere") } - label("lost_amulet") { + label("post_take_amulet") { + npc(FacialExpression.NEUTRAL, "It is an Amulet of Ghostspeak.") + npc(FacialExpression.NEUTRAL, "So called, because when you wear it you can speak to", "ghosts. A lot of ghosts are doomed to be ghosts because", "they have left some important task uncompleted.") + npc(FacialExpression.NEUTRAL, "Maybe if you know what this task is, you can get rid of", "the ghost. I'm not making any guarantees mind you,", "but it is the best I can do right now.") + player("Thank you. I'll give it a try!") + + goto("nowhere") + } - exec { player, npc -> - sendDialogue(player, "Father Urhney sighs.") + //because end() breaks shit + label("nowhere") {} + } +} - if (anyInEquipment(player, Items.GHOSTSPEAK_AMULET_552) || anyInInventory(player, Items.GHOSTSPEAK_AMULET_552)) { - npc("What are you talking about? I can see you've got it", "with you!"); - goto("nowhere") +class FatherUrhneyQuestAmuletDialogueFile : DialogueLabeller() { + override fun addConversation() { + npc("Father Urhney sighs.") + + exec { player, npc -> + if (anyInEquipment(player, Items.GHOSTSPEAK_AMULET_552) || anyInInventory(player, Items.GHOSTSPEAK_AMULET_552)) { + npc("What are you talking about? I can see you've got it", "with you!") + } + + if (inBank(player, Items.GHOSTSPEAK_AMULET_552)) { + npc(FacialExpression.ANGRY, "You come here wasting my time... Has it even", "occurred to you to look in your bank? Now GO", "AWAY!") + } + + npc(FacialExpression.ANGRY, "How careless can you get? Those things aren't easy to", "come by you know! It's a good job I've got a spare.") + + if (freeSlots(player) >= 1) { + if (addItem(player, Items.GHOSTSPEAK_AMULET_552, 1)) { + item(Item(Items.GHOSTSPEAK_AMULET_552), "Father Urhney hands you an amulet.") + + player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 1, 12) + + npc(FacialExpression.ANGRY, "Be more careful this time.") + player("Ok, I'll try to be."); } - - if (inBank(player, Items.GHOSTSPEAK_AMULET_552)) { - npc(FacialExpression.ANGRY, "You come here wasting my time... Has it even", "occurred to you to look in your bank? Now GO", "AWAY!"); - goto("nowhere") - } - - npc(FacialExpression.ANGRY, "How careless can you get? Those things aren't easy to", "come by you know! It's a good job I've got a spare."); - - if (freeSlots(player) >= 1) { - if (addItem(player, Items.GHOSTSPEAK_AMULET_552, 1)) { - sendItemDialogue(player, Item(Items.GHOSTSPEAK_AMULET_552), "Father Urhney hands you an amulet.") - - player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 1, 12) - - npc(FacialExpression.ANGRY, "Be more careful this time."); - player("Ok, I'll try to be."); - end() - } - } else { - // Closes the dialogue prematurely to reveal message - end() - sendMessage(player, "You don't have enough inventory space to accept this amulet.") - } - - //last effort dialogue end if somehow it screws up - end() + } else { + sendMessage(player, "You don't have enough inventory space to accept this amulet.") } } + + goto("nowhere") + + //because end() breaks shit + label("nowhere") {} } } \ No newline at end of file From 7f3e4283cdefbe66be37c6f9e37177d85494a6ec Mon Sep 17 00:00:00 2001 From: Beck Date: Thu, 4 Jun 2026 02:02:37 -0700 Subject: [PATCH 05/19] woOoOOoO --- .../RestlessGhostNPCDialogue.kt | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostNPCDialogue.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostNPCDialogue.kt index 7fdf5b242..355ed584d 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostNPCDialogue.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostNPCDialogue.kt @@ -30,42 +30,44 @@ class RestlessGhostNPCDialogueFile : DialogueLabeller() { exec { player, npc -> if (!inEquipment(player, Items.GHOSTSPEAK_AMULET_552)) { - npc(ChatAnim.HALF_GUILTY, "Wooo wooo wooooo!") - goto("no_amulet_opts") + loadLabel(player, "woo") } else { when (getQuestStage(player, Quests.THE_RESTLESS_GHOST)) { 20 -> { - goto("not_very_good") + loadLabel(player, "not_very_good") } - 30 -> { - goto("finding_my_skull") - } - else -> { - npc("Fine, thanks") - end() + 30, 40 -> { + loadLabel(player, "finding_my_skull") } } } } + npc("Fine, thanks.") + label("finding_my_skull") { npc("How are you doing finding my skull?") exec { player, npc -> if (inInventory(player, Items.GHOSTS_SKULL_553)) { - player(FacialExpression.FRIENDLY, "I have found it!") - npc(FacialExpression.HAPPY, "Hurrah! Now I can stop being a ghost! You just need", "to put it on my coffin there, and I will be free!") - + loadLabel(player, "found_it") } else { - player(FacialExpression.HALF_GUILTY, "Sorry, I can't find it at the moment.") - npc(FacialExpression.HALF_GUILTY, "Ah well. Keep on looking.") - npc(FacialExpression.HALF_GUILTY, "I'm pretty sure it's somewhere in the tower south-west", "from here. There's a lot of levels to the tower, though. I", "suppose it might take a little while to find.") - + loadLabel(player, "not_found_it") } - end() } } + label("found_it") { + player(FacialExpression.FRIENDLY, "I have found it!") + npc(FacialExpression.HAPPY, "Hurrah! Now I can stop being a ghost! You just need", "to put it on my coffin there, and I will be free!") + } + + label("not_found_it") { + player(FacialExpression.HALF_GUILTY, "Sorry, I can't find it at the moment.") + npc(FacialExpression.HALF_GUILTY, "Ah well. Keep on looking.") + npc(FacialExpression.HALF_GUILTY, "I'm pretty sure it's somewhere in the tower south-west", "from here. There's a lot of levels to the tower, though. I", "suppose it might take a little while to find.") + } + label("not_very_good") { npc(FacialExpression.HALF_GUILTY, "Not very good actually.") player(FacialExpression.HALF_GUILTY, "What's the problem then?") @@ -77,20 +79,24 @@ class RestlessGhostNPCDialogueFile : DialogueLabeller() { npc(FacialExpression.HALF_GUILTY, "Well, to be honest... I'm not sure.") player(FacialExpression.HALF_GUILTY, "I've been told a certain task may need to be completed", "so you can rest in peace.") npc(FacialExpression.HALF_GUILTY, "I should think it is probably because a warlock has come", "along and stolen my skull. If you look inside my coffin", "there, you'll find my corpse without a head on it.") - player("FacialExpression.HALF_GUILTY, Do you know where this warlock might be now?") + player(FacialExpression.HALF_GUILTY, "Do you know where this warlock might be now?") npc(FacialExpression.HALF_GUILTY, "I think it was one of the warlocks who lives in the big", "tower by the sea south-west from here.") player(FacialExpression.HALF_GUILTY, "Ok. I will try and get the skull back for you, then you", "can rest in peace.") exec { player, npc -> setQuestStage(player, Quests.THE_RESTLESS_GHOST, 30) - goto("great_relief") + loadLabel(player, "great_relief") } } label("great_relief") { npc(FacialExpression.HALF_GUILTY, "Ooh, thank you. That would be such a great relief!") npc(FacialExpression.HALF_GUILTY, "It is so dull being a ghost...") - end() + } + + label("woo") { + npc(ChatAnim.HALF_GUILTY, "Wooo wooo wooooo!") + goto("no_amulet_opts") } label("no_amulet_opts") { @@ -118,8 +124,6 @@ class RestlessGhostNPCDialogueFile : DialogueLabeller() { npc(FacialExpression.HALF_GUILTY, "WOOOOOOOOO!") player(FacialExpression.HALF_GUILTY, "Never mind.") - - end() } label("thats_interesting") { @@ -134,8 +138,6 @@ class RestlessGhostNPCDialogueFile : DialogueLabeller() { player(FacialExpression.HALF_GUILTY, "Goodbye. Thanks for the chat.") npc(FacialExpression.HALF_GUILTY, "Wooo wooo?") - - end() } label("find_treasure") { @@ -147,8 +149,6 @@ class RestlessGhostNPCDialogueFile : DialogueLabeller() { npc(FacialExpression.HALF_GUILTY, "WOOOOOOOOO!") player(FacialExpression.HALF_GUILTY, "Never mind.") - - end() } } From 50172d052cede60ec76d76b6a660aeb4f0a0d714 Mon Sep 17 00:00:00 2001 From: Beck Date: Thu, 4 Jun 2026 02:05:15 -0700 Subject: [PATCH 06/19] altar stuff --- .../RestlessGhostListeners.kt | 57 +++++++++---------- 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostListeners.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostListeners.kt index 71977d393..56296260b 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostListeners.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostListeners.kt @@ -1,29 +1,11 @@ package content.region.misthalin.lumbridge.quest.therestlessghost; import content.data.Quests -import core.api.addItem -import core.api.animate -import core.api.finishQuest -import core.api.freeSlots -import core.api.getQuestStage -import core.api.inBank -import core.api.inEquipmentOrInventory -import core.api.inInventory -import core.api.isQuestComplete -import core.api.lock -import core.api.playGlobalAudio -import core.api.queueScript -import core.api.removeItem -import core.api.sendDialogue -import core.api.sendItemDialogue -import core.api.sendMessage -import core.api.setAttribute -import core.api.setQuestStage -import core.api.stopExecuting -import core.api.visualize +import core.api.* import core.game.interaction.IntType import core.game.interaction.InteractionListener import core.game.node.entity.Entity +import core.game.node.entity.combat.CombatStyle import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.game.node.scenery.SceneryBuilder @@ -57,18 +39,30 @@ class RestlessGhostListeners : InteractionListener { return restlessGhostNPC } + //client side only visual, removes the skeleton on ground, resets in ~2mins + private fun animSkeleAlive(player: Player, skeleScenery: core.game.node.scenery.Scenery) { + + //Missing: find animation and maybe sfx + + queueScript(player, 4) {_ -> + SceneryBuilder.remove(skeleScenery, 200) + return@queueScript stopExecuting(player) + } + } + private fun createLevel13SkeletonNPC(player: Player) : NPC { - val skele = NPC(NPCs.SKELETON_459, Location(3120, 9569, 0)) + val skele = NPC(NPCs.SKELETON_459, Location(3120, 9568, 0)) skele.setAttribute("spawned:npc", true) skele.setAttribute("target", player) skele.isRespawn = false skele.init() + skele.properties.combatPulse.style = CombatStyle.MELEE skele.attack(player) return skele } - private fun toggleCoffinSceneryObj(player: Player, coffin: core.game.node.scenery.Scenery) { + fun toggleCoffinSceneryObj(player: Player, coffin: core.game.node.scenery.Scenery) { lock(player, 2) @@ -78,7 +72,7 @@ class RestlessGhostListeners : InteractionListener { INCOMPLETE_COFFIN_OPEN -> { //close it visualize(player, Animations_HUMAN_CLOSE_CHEST_535, -1) - SceneryBuilder.replace(coffin, coffin.transform(CLOSED_COFFIN)) + SceneryBuilder.replace(coffin, coffin.transform(CLOSED_COFFIN), 200) } CLOSED_COFFIN -> { //open it @@ -86,9 +80,9 @@ class RestlessGhostListeners : InteractionListener { if (isQuestComplete(player, Quests.THE_RESTLESS_GHOST)) { //full skele with skull - SceneryBuilder.replace(coffin, coffin.transform(COMPLETE_COFFIN_OPEN)) + SceneryBuilder.replace(coffin, coffin.transform(COMPLETE_COFFIN_OPEN), 200) } else { - SceneryBuilder.replace(coffin, coffin.transform(INCOMPLETE_COFFIN_OPEN)) + SceneryBuilder.replace(coffin, coffin.transform(INCOMPLETE_COFFIN_OPEN), 200) } } } @@ -117,7 +111,7 @@ class RestlessGhostListeners : InteractionListener { //despawn ghost after awhile and shut the coffin queueScript(player, 100) { _ -> ghost.clear() - toggleCoffinSceneryObj(player, node) + //toggleCoffinSceneryObj(player, node) return@queueScript stopExecuting(player) } } @@ -172,18 +166,21 @@ class RestlessGhostListeners : InteractionListener { return@onUseWith true } - on(Scenery.ALTAR_15051, IntType.SCENERY, "search") { player, node -> + on(Scenery.ALTAR_15050, IntType.SCENERY, "search") { player, node -> if (!inInventory(player, Items.GHOSTS_SKULL_553)) { - if (getQuestStage(player, Quests.THE_RESTLESS_GHOST) < 30) { + if (getQuestStage(player, Quests.THE_RESTLESS_GHOST) !in 30..40) { sendMessage(player, "You search the altar and find nothing.") } else { - if (freeSlots(player) != 0) { + if (freeSlots(player) >= 1) { if (addItem(player, Items.GHOSTS_SKULL_553)) { setQuestStage(player, Quests.THE_RESTLESS_GHOST, 40) sendMessage(player, "The skeleton in the corner suddenly comes to life!") - Location.create(3120, 9569, 0) + //skele comes to live from scenery 15057 with an animation, + //but idk how to query for it to at least remove it from the world briefly + //animSkeleAlive(player, node as core.game.node.scenery.Scenery) + //spawn in skeleton val s = createLevel13SkeletonNPC(player) From 02e3ef26bd3838383f7f66c68934a09336b49a9c Mon Sep 17 00:00:00 2001 From: Beck Date: Thu, 4 Jun 2026 18:30:11 -0700 Subject: [PATCH 07/19] coffin fixes --- .../quest/therestlessghost/RestlessGhostListeners.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostListeners.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostListeners.kt index 56296260b..9bbdcea78 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostListeners.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostListeners.kt @@ -72,7 +72,7 @@ class RestlessGhostListeners : InteractionListener { INCOMPLETE_COFFIN_OPEN -> { //close it visualize(player, Animations_HUMAN_CLOSE_CHEST_535, -1) - SceneryBuilder.replace(coffin, coffin.transform(CLOSED_COFFIN), 200) + SceneryBuilder.replace(coffin, coffin.transform(CLOSED_COFFIN)) } CLOSED_COFFIN -> { //open it @@ -80,9 +80,9 @@ class RestlessGhostListeners : InteractionListener { if (isQuestComplete(player, Quests.THE_RESTLESS_GHOST)) { //full skele with skull - SceneryBuilder.replace(coffin, coffin.transform(COMPLETE_COFFIN_OPEN), 200) + SceneryBuilder.replace(coffin, coffin.transform(COMPLETE_COFFIN_OPEN)) } else { - SceneryBuilder.replace(coffin, coffin.transform(INCOMPLETE_COFFIN_OPEN), 200) + SceneryBuilder.replace(coffin, coffin.transform(INCOMPLETE_COFFIN_OPEN)) } } } @@ -111,7 +111,7 @@ class RestlessGhostListeners : InteractionListener { //despawn ghost after awhile and shut the coffin queueScript(player, 100) { _ -> ghost.clear() - //toggleCoffinSceneryObj(player, node) + toggleCoffinSceneryObj(player, node) return@queueScript stopExecuting(player) } } From 064ad43c553afa7b1b5710aa01011d26883023c7 Mon Sep 17 00:00:00 2001 From: Beck Date: Thu, 4 Jun 2026 19:22:52 -0700 Subject: [PATCH 08/19] update despawn timer --- .../lumbridge/quest/therestlessghost/RestlessGhostListeners.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostListeners.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostListeners.kt index 9bbdcea78..4b7a74478 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostListeners.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostListeners.kt @@ -109,7 +109,7 @@ class RestlessGhostListeners : InteractionListener { val ghost = createRestlessGhostNPC() //despawn ghost after awhile and shut the coffin - queueScript(player, 100) { _ -> + queueScript(player, 200) { _ -> ghost.clear() toggleCoffinSceneryObj(player, node) return@queueScript stopExecuting(player) From 6eb1174db89748cf522e937de123a7c4a6642191 Mon Sep 17 00:00:00 2001 From: Beck Date: Thu, 4 Jun 2026 21:08:59 -0700 Subject: [PATCH 09/19] amulet dialogue logic --- .../FatherUrhneyQuestDialogueFile.kt | 63 ++++++++++++------- 1 file changed, 42 insertions(+), 21 deletions(-) diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherUrhneyQuestDialogueFile.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherUrhneyQuestDialogueFile.kt index fc9cd596b..be92515a4 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherUrhneyQuestDialogueFile.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherUrhneyQuestDialogueFile.kt @@ -20,7 +20,8 @@ import org.rs09.consts.Items class FatherUrhneyQuestDialogueFile : DialogueLabeller() { override fun addConversation() { - + //mid quest asking for amulet + npc(FacialExpression.ANGRY, "I suppose I'd better talk to you then. What problems", "has he got himself into this time?"); player("He's got a ghost haunting his graveyard."); @@ -69,34 +70,54 @@ class FatherUrhneyQuestDialogueFile : DialogueLabeller() { class FatherUrhneyQuestAmuletDialogueFile : DialogueLabeller() { override fun addConversation() { + + //mid or after finishing quest and needing another one or doing the drop trick + npc("Father Urhney sighs.") exec { player, npc -> if (anyInEquipment(player, Items.GHOSTSPEAK_AMULET_552) || anyInInventory(player, Items.GHOSTSPEAK_AMULET_552)) { - npc("What are you talking about? I can see you've got it", "with you!") - } - - if (inBank(player, Items.GHOSTSPEAK_AMULET_552)) { - npc(FacialExpression.ANGRY, "You come here wasting my time... Has it even", "occurred to you to look in your bank? Now GO", "AWAY!") - } - - npc(FacialExpression.ANGRY, "How careless can you get? Those things aren't easy to", "come by you know! It's a good job I've got a spare.") - - if (freeSlots(player) >= 1) { - if (addItem(player, Items.GHOSTSPEAK_AMULET_552, 1)) { - item(Item(Items.GHOSTSPEAK_AMULET_552), "Father Urhney hands you an amulet.") - - player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 1, 12) - - npc(FacialExpression.ANGRY, "Be more careful this time.") - player("Ok, I'll try to be."); - } + loadLabel(player, "amulet_equip_resp") + } else if (inBank(player, Items.GHOSTSPEAK_AMULET_552)) { + loadLabel(player, "amulet_in_bank_resp") } else { - sendMessage(player, "You don't have enough inventory space to accept this amulet.") + loadLabel(player, "give_amulet") } } - goto("nowhere") + label("amulet_equip_resp") { + npc("What are you talking about? I can see you've got it", "with you!") + goto("nowhere") + } + + label("amulet_in_bank_resp") { + npc(FacialExpression.ANGRY, "You come here wasting my time... Has it even", "occurred to you to look in your bank? Now GO", "AWAY!") + goto("nowhere") + } + + label("give_amulet") { + npc(FacialExpression.ANGRY, "How careless can you get? Those things aren't easy to", "come by you know! It's a good job I've got a spare.") + + exec { player, npc -> + if (freeSlots(player) >= 1) { + if (addItem(player, Items.GHOSTSPEAK_AMULET_552, 1)) { + item(Item(Items.GHOSTSPEAK_AMULET_552), "Father Urhney hands you an amulet.") + + player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 1, 12) + loadLabel(player, "give_amulet_response") + } + } else { + sendMessage(player, "You don't have enough inventory space to accept this amulet.") + loadLabel(player, "nowhere") + } + } + } + + label("give_amulet_resp") { + npc(FacialExpression.ANGRY, "Be more careful this time.") + player("Ok, I'll try to be.") + goto("nowhere") + } //because end() breaks shit label("nowhere") {} From 531699b124009c0d49ae5ac7c811a437fa5abbaa Mon Sep 17 00:00:00 2001 From: Beck Date: Thu, 4 Jun 2026 21:18:15 -0700 Subject: [PATCH 10/19] misc stuff --- .../quest/therestlessghost/RestlessGhostListeners.kt | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostListeners.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostListeners.kt index 4b7a74478..80462b39a 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostListeners.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostListeners.kt @@ -19,9 +19,15 @@ import org.rs09.consts.NPCs import org.rs09.consts.Scenery import org.rs09.consts.Sounds +/** +* Authenticity source Aug 18 2009 +* https://www.youtube.com/watch?v=ikbX12Xywxs +* * Author Beck, Vexia +**/ class RestlessGhostListeners : InteractionListener { companion object { + //This is not in the Animations consts private const val Animations_HUMAN_CLOSE_CHEST_535 = 535 private const val CLOSED_COFFIN = Scenery.COFFIN_2145 @@ -39,10 +45,10 @@ class RestlessGhostListeners : InteractionListener { return restlessGhostNPC } - //client side only visual, removes the skeleton on ground, resets in ~2mins + //removes the skeleton on ground, resets in ~2mins private fun animSkeleAlive(player: Player, skeleScenery: core.game.node.scenery.Scenery) { - //Missing: find animation and maybe sfx + //Missing: find animation and maybe sfx of the skeleton coming alive queueScript(player, 4) {_ -> SceneryBuilder.remove(skeleScenery, 200) @@ -178,7 +184,7 @@ class RestlessGhostListeners : InteractionListener { sendMessage(player, "The skeleton in the corner suddenly comes to life!") //skele comes to live from scenery 15057 with an animation, - //but idk how to query for it to at least remove it from the world briefly + //but idk how to select it to at least remove/hide it from the world briefly //animSkeleAlive(player, node as core.game.node.scenery.Scenery) //spawn in skeleton From d33f019273f760236b3475a40e0aa4f1c2797740 Mon Sep 17 00:00:00 2001 From: Beck Date: Thu, 4 Jun 2026 22:41:16 -0700 Subject: [PATCH 11/19] add skull spawns in wildy --- Server/data/configs/ground_spawns.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Server/data/configs/ground_spawns.json b/Server/data/configs/ground_spawns.json index 1893b6df3..a2f7b1173 100644 --- a/Server/data/configs/ground_spawns.json +++ b/Server/data/configs/ground_spawns.json @@ -251,6 +251,10 @@ "item_id": "960", "loc_data": "{1,2851,3239,0,160}-{1,2847,3238,0,160}-{1,2845,3232,0,160}-{1,2856,3231,0,160}-{1,2857,3236,0,160}-{1,2846,3384,0,160}-{1,2848,3383,0,160}-{1,2550,3575,0,200}-{1,2553,3576,0,200}-{1,2554,3575,0,200}-{1,2556,3573,0,200}-{1,3216,3665,0,200}-{1,3224,3668,0,200}-{1,3245,3678,0,200}-{1,3230,3686,0,200}-{1,3216,3677,0,200}-{1,3219,3680,0,200}-" }, + { + "item_id": "964", + "loc_data": "{1,2977,3529,0,90}-{1,2978,3531,0,90}-" + }, { "item_id": "966", "loc_data": "{1,3237,3696,0,150}-" From 88bbc55c3ec6c4dfd5e7926492602db17f80bdae Mon Sep 17 00:00:00 2001 From: Beck Date: Sun, 7 Jun 2026 22:51:58 -0700 Subject: [PATCH 12/19] fix for !2369 --- .../lumbridge/quest/therestlessghost/RestlessGhostListeners.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostListeners.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostListeners.kt index 80462b39a..438230efb 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostListeners.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostListeners.kt @@ -107,7 +107,7 @@ class RestlessGhostListeners : InteractionListener { if (!isQuestComplete(player, Quests.THE_RESTLESS_GHOST)) { //is this from the center of the scenery/node object? - val ghostExists = RegionManager.getLocalNpcs(node.centerLocation).find { it.id == NPCs.RESTLESS_GHOST_457 } + val ghostExists = RegionManager.getLocalNPCs(node.centerLocation).find { it.id == NPCs.RESTLESS_GHOST_457 } //not within rendering distance or doesn't exist at all if (ghostExists == null) { From c4acd317d6655b5d14ca21d01720058687d9887e Mon Sep 17 00:00:00 2001 From: Beck Date: Sun, 7 Jun 2026 22:52:05 -0700 Subject: [PATCH 13/19] port to kotlin --- .../quest/therestlessghost/RestlessGhost.java | 112 ------------------ .../quest/therestlessghost/RestlessGhost.kt | 99 ++++++++++++++++ 2 files changed, 99 insertions(+), 112 deletions(-) delete mode 100644 Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhost.java create mode 100644 Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhost.kt diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhost.java b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhost.java deleted file mode 100644 index 99d3cf017..000000000 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhost.java +++ /dev/null @@ -1,112 +0,0 @@ -package content.region.misthalin.lumbridge.quest.therestlessghost; - -import content.region.misthalin.draynor.quest.anma.OldCronDialogue; -import core.game.node.entity.skill.Skills; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.quest.Quest; -import core.game.node.item.Item; -import core.plugin.Initializable; -import core.plugin.ClassScanner; - -import static core.api.ContentAPIKt.*; -import content.data.Quests; - - -/** - * Represents the restless ghost quest. - * @author Vexia - * - */ -@Initializable -public class RestlessGhost extends Quest { - /** - * The ghost speak amulet. - */ - public static final Item AMULET = new Item(552); - - /** - * Constructs a new {@Code RestlessGhost} {@Code Object} - */ - public RestlessGhost() { - super(Quests.THE_RESTLESS_GHOST, 25, 24, 1, 107, 0, 4, 5); - } - - @Override - public Quest newInstance(Object object) { - ClassScanner.definePlugin(new OldCronDialogue()); - return this; - } - - @Override - public void drawJournal(Player player, int stage) { - super.drawJournal(player, stage); - if (stage == 0) { - player.getPacketDispatch().sendString("I can start this quest by speaking to Father Aereck in the", 275, 4+ 7); - player.getPacketDispatch().sendString("church next to Lumbridge Castle.", 275, 5+ 7); - player.getPacketDispatch().sendString("I must be unafraid of a Level 13 Skeleton ", 275, 6+ 7); - } - if (stage == 10) { - player.getPacketDispatch().sendString("Father Aereck asked me to help him deal with the Ghost in", 275, 4+ 7); - player.getPacketDispatch().sendString("the graveyard next to the church.", 275, 5+ 7); - player.getPacketDispatch().sendString("I should find Father Urhney who is an expert on ghosts. ", 275, 6+ 7); - player.getPacketDispatch().sendString("He lives in a shack in Lumbridge Swamp.", 275, 7+ 7); - - } - if (stage == 20) { - player.getPacketDispatch().sendString("Father Aereck asked me to help him deal with the Ghost in", 275, 4+ 7); - player.getPacketDispatch().sendString("the graveyard next to the church.", 275, 5+ 7); - player.getPacketDispatch().sendString("I should find Father Urhney who is an expert on ghosts. ", 275, 6+ 7); - player.getPacketDispatch().sendString("He lives in a shack in Lumbridge Swamp.", 275, 7+ 7); - player.getPacketDispatch().sendString("I should talk to the Ghost to find out why it is haunting the", 275, 8+ 7); - player.getPacketDispatch().sendString("graveyard crypt", 275, 9+ 7); - } - if (stage == 30) { - player.getPacketDispatch().sendString("Father Aereck asked me to help him deal with the Ghost in", 275, 4+ 7); - player.getPacketDispatch().sendString("the graveyard next to the church.", 275, 5+ 7); - player.getPacketDispatch().sendString("I found Father Urhney in the swamp south of Lumbridge.He ", 275, 6+ 7); - player.getPacketDispatch().sendString("gave me an Amulet of Ghostspeak to talk to the ghost.", 275, 7+ 7); - player.getPacketDispatch().sendString("I spoke to the Ghost and he told me he could not rest in", 275, 8+ 7); - player.getPacketDispatch().sendString("peace because an evil wizard had stolen his skull.", 275, 9+ 7); - player.getPacketDispatch().sendString("I should go and search the Wizard's Tower South West of", 275, 10+ 7); - player.getPacketDispatch().sendString("Lumbridge for the Ghost's Skull.", 275, 11+ 7); - } - if (stage == 40) { - player.getPacketDispatch().sendString("Father Aereck asked me to help him deal with the Ghost in", 275, 4+ 7); - player.getPacketDispatch().sendString("the graveyard next to the church.", 275, 5+ 7); - player.getPacketDispatch().sendString("I found Father Urhney in the swamp south of Lumbridge.He ", 275, 6+ 7); - player.getPacketDispatch().sendString("gave me an Amulet of Ghostspeak to talk to the ghost.", 275, 7+ 7); - player.getPacketDispatch().sendString("I spoke to the Ghost and he told me he could not rest in", 275, 8+ 7); - player.getPacketDispatch().sendString("peace because an evil wizard had stolen his skull.", 275, 9+ 7); - player.getPacketDispatch().sendString("I found the Ghost's Skull in the basement of the Wizards'", 275, 10+ 7); - player.getPacketDispatch().sendString("Tower. It was guarded by a skeleton, but I took it anyways.", 275, 11+ 7); - player.getPacketDispatch().sendString("I should take the Skull back to the Ghost so it can rest.", 275, 12+ 7); - } - if (stage == 100) { - player.getPacketDispatch().sendString("Father Aereck asked me to help him deal with the Ghost in", 275, 4+ 7); - player.getPacketDispatch().sendString("the graveyard next to the church.", 275, 5+ 7); - player.getPacketDispatch().sendString("I found Father Urhney in the swamp south of Lumbridge.He ", 275, 6+ 7); - player.getPacketDispatch().sendString("gave me an Amulet of Ghostspeak to talk to the ghost.", 275, 7+ 7); - player.getPacketDispatch().sendString("I spoke to the Ghost and he told me he could not rest in", 275, 8+ 7); - player.getPacketDispatch().sendString("peace because an evil wizard had stolen his skull.", 275, 9+ 7); - player.getPacketDispatch().sendString("I found the Ghost's Skull in the basement of the Wizards'", 275, 10+ 7); - player.getPacketDispatch().sendString("Tower. It was guarded by a skeleton, but I took it anyways.", 275, 11+ 7); - player.getPacketDispatch().sendString("I placed the Skull in the Ghost's coffin, and allowed it to", 275, 12+ 7); - player.getPacketDispatch().sendString("rest in peace once more, with gratitude for my help.", 275, 13+ 7); - player.getPacketDispatch().sendString("QUEST COMPLETE!", 275, 16+ 7); - } - } - - @Override - public void finish(Player player) { - super.finish(player); - player.getPacketDispatch().sendString("1 Quest Point", 277, 8 + 2); - player.getPacketDispatch().sendString("1125 Prayer XP", 277, 9 + 2); - player.getPacketDispatch().sendItemZoomOnInterface(964, 240, 277, 3 + 2); - player.getSkills().addExperience(Skills.PRAYER, 1125); - player.getInterfaceManager().closeChatbox(); - player.getPacketDispatch().sendString("You have completed The Restless Ghost Quest!", 277, 2 + 2); - setVarp(player, 728, 31, true); - player.getGameAttributes().removeAttribute("restless-ghost:urhney"); - } - -} diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhost.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhost.kt new file mode 100644 index 000000000..af86df641 --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhost.kt @@ -0,0 +1,99 @@ +package content.region.misthalin.lumbridge.quest.therestlessghost + +import content.region.misthalin.draynor.quest.anma.OldCronDialogue +import core.game.node.entity.skill.Skills +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.quest.Quest +import core.game.node.item.Item +import core.plugin.Initializable +import core.plugin.ClassScanner +import core.api.* +import content.data.Quests +import org.rs09.consts.Items + +/** + * Represents the restless ghost quest. + * @author Vexia + */ +@Initializable +class RestlessGhost : Quest(Quests.THE_RESTLESS_GHOST, 25, 24, 1, 107, 0, 4, 5) { + + override fun newInstance(obj: Any?): Quest { + ClassScanner.definePlugin(OldCronDialogue()) + return this + } + + override fun drawJournal(player: Player, stage: Int) { + super.drawJournal(player, stage) + + when (stage) { + 0 -> { + player.packetDispatch.sendString("I can start this quest by speaking to Father Aereck in the", 275, 4 + 7) + player.packetDispatch.sendString("church next to Lumbridge Castle.", 275, 5 + 7) + player.packetDispatch.sendString("I must be unafraid of a Level 13 Skeleton ", 275, 6 + 7) + } + 10 -> { + player.packetDispatch.sendString("Father Aereck asked me to help him deal with the Ghost in", 275, 4 + 7) + player.packetDispatch.sendString("the graveyard next to the church.", 275, 5 + 7) + player.packetDispatch.sendString("I should find Father Urhney who is an expert on ghosts. ", 275, 6 + 7) + player.packetDispatch.sendString("He lives in a shack in Lumbridge Swamp.", 275, 7 + 7) + } + 20 -> { + player.packetDispatch.sendString("Father Aereck asked me to help him deal with the Ghost in", 275, 4 + 7) + player.packetDispatch.sendString("the graveyard next to the church.", 275, 5 + 7) + player.packetDispatch.sendString("I should find Father Urhney who is an expert on ghosts. ", 275, 6 + 7) + player.packetDispatch.sendString("He lives in a shack in Lumbridge Swamp.", 275, 7 + 7) + player.packetDispatch.sendString("I should talk to the Ghost to find out why it is haunting the", 275, 8 + 7) + player.packetDispatch.sendString("graveyard crypt", 275, 9 + 7) + } + 30 -> { + player.packetDispatch.sendString("Father Aereck asked me to help him deal with the Ghost in", 275, 4 + 7) + player.packetDispatch.sendString("the graveyard next to the church.", 275, 5 + 7) + player.packetDispatch.sendString("I found Father Urhney in the swamp south of Lumbridge.He ", 275, 6 + 7) + player.packetDispatch.sendString("gave me an Amulet of Ghostspeak to talk to the ghost.", 275, 7 + 7) + player.packetDispatch.sendString("I spoke to the Ghost and he told me he could not rest in", 275, 8 + 7) + player.packetDispatch.sendString("peace because an evil wizard had stolen his skull.", 275, 9 + 7) + player.packetDispatch.sendString("I should go and search the Wizard's Tower South West of", 275, 10 + 7) + player.packetDispatch.sendString("Lumbridge for the Ghost's Skull.", 275, 11 + 7) + } + 40 -> { + player.packetDispatch.sendString("Father Aereck asked me to help him deal with the Ghost in", 275, 4 + 7) + player.packetDispatch.sendString("the graveyard next to the church.", 275, 5 + 7) + player.packetDispatch.sendString("I found Father Urhney in the swamp south of Lumbridge.He ", 275, 6 + 7) + player.packetDispatch.sendString("gave me an Amulet of Ghostspeak to talk to the ghost.", 275, 7 + 7) + player.packetDispatch.sendString("I spoke to the Ghost and he told me he could not rest in", 275, 8 + 7) + player.packetDispatch.sendString("peace because an evil wizard had stolen his skull.", 275, 9 + 7) + player.packetDispatch.sendString("I found the Ghost's Skull in the basement of the Wizards'", 275, 10 + 7) + player.packetDispatch.sendString("Tower. It was guarded by a skeleton, but I took it anyways.", 275, 11 + 7) + player.packetDispatch.sendString("I should take the Skull back to the Ghost so it can rest.", 275, 12 + 7) + } + 100 -> { + player.packetDispatch.sendString("Father Aereck asked me to help him deal with the Ghost in", 275, 4 + 7) + player.packetDispatch.sendString("the graveyard next to the church.", 275, 5 + 7) + player.packetDispatch.sendString("I found Father Urhney in the swamp south of Lumbridge.He ", 275, 6 + 7) + player.packetDispatch.sendString("gave me an Amulet of Ghostspeak to talk to the ghost.", 275, 7 + 7) + player.packetDispatch.sendString("I spoke to the Ghost and he told me he could not rest in", 275, 8 + 7) + player.packetDispatch.sendString("peace because an evil wizard had stolen his skull.", 275, 9 + 7) + player.packetDispatch.sendString("I found the Ghost's Skull in the basement of the Wizards'", 275, 10 + 7) + player.packetDispatch.sendString("Tower. It was guarded by a skeleton, but I took it anyways.", 275, 11 + 7) + player.packetDispatch.sendString("I placed the Skull in the Ghost's coffin, and allowed it to", 275, 12 + 7) + player.packetDispatch.sendString("rest in peace once more, with gratitude for my help.", 275, 13 + 7) + player.packetDispatch.sendString("QUEST COMPLETE!", 275, 16 + 7) + } + } + } + + override fun finish(player: Player) { + super.finish(player) + + player.packetDispatch.sendString("1 Quest Point", 277, 8 + 2) + player.packetDispatch.sendString("1125 Prayer XP", 277, 9 + 2) + player.packetDispatch.sendItemZoomOnInterface(964, 240, 277, 3 + 2) + + player.skills.addExperience(Skills.PRAYER, 1125.0) + player.interfaceManager.closeChatbox() + + player.packetDispatch.sendString("You have completed The Restless Ghost Quest!", 277, 2 + 2) + setVarp(player, 728, 31, true) + } +} \ No newline at end of file From f8eec8511b427ab3b12b7e3993a30b24e2efb317 Mon Sep 17 00:00:00 2001 From: Beck Date: Tue, 9 Jun 2026 00:40:25 -0700 Subject: [PATCH 14/19] remove oldCrone related stuff --- .../lumbridge/quest/therestlessghost/RestlessGhost.kt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhost.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhost.kt index af86df641..a0bc51016 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhost.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhost.kt @@ -1,25 +1,23 @@ package content.region.misthalin.lumbridge.quest.therestlessghost -import content.region.misthalin.draynor.quest.anma.OldCronDialogue import core.game.node.entity.skill.Skills import core.game.node.entity.player.Player import core.game.node.entity.player.link.quest.Quest import core.game.node.item.Item import core.plugin.Initializable -import core.plugin.ClassScanner import core.api.* import content.data.Quests import org.rs09.consts.Items /** * Represents the restless ghost quest. - * @author Vexia + * @author Beck, Vexia */ @Initializable class RestlessGhost : Quest(Quests.THE_RESTLESS_GHOST, 25, 24, 1, 107, 0, 4, 5) { + //This is required for some reason override fun newInstance(obj: Any?): Quest { - ClassScanner.definePlugin(OldCronDialogue()) return this } From b4a2fb785416b6a06304ca9e9003dbf7b75ac014 Mon Sep 17 00:00:00 2001 From: Beck Date: Tue, 9 Jun 2026 00:40:57 -0700 Subject: [PATCH 15/19] port Old Crone Animal Magnetism dialogue to kt --- .../draynor/quest/anma/OldCronDialogue.java | 214 ------------------ .../draynor/quest/anma/OldCroneDialogue.kt | 169 ++++++++++++++ 2 files changed, 169 insertions(+), 214 deletions(-) delete mode 100644 Server/src/main/content/region/misthalin/draynor/quest/anma/OldCronDialogue.java create mode 100644 Server/src/main/content/region/misthalin/draynor/quest/anma/OldCroneDialogue.kt diff --git a/Server/src/main/content/region/misthalin/draynor/quest/anma/OldCronDialogue.java b/Server/src/main/content/region/misthalin/draynor/quest/anma/OldCronDialogue.java deleted file mode 100644 index eb76716f4..000000000 --- a/Server/src/main/content/region/misthalin/draynor/quest/anma/OldCronDialogue.java +++ /dev/null @@ -1,214 +0,0 @@ -package content.region.misthalin.draynor.quest.anma; - -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.FacialExpression; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.quest.Quest; -import core.game.node.item.Item; -import content.region.misthalin.lumbridge.quest.therestlessghost.RestlessGhost; -import content.data.Quests; - -/** - * Handles the dialogue used for the old crone. - * @author Vexia - */ -public final class OldCronDialogue extends DialoguePlugin { - - /** - * The crone made amulet item. - */ - public static final Item CRONE_AMULET = new Item(10500); - - /** - * The quest used. - */ - private Quest quest; - - /** - * Constructs a new {@code OldCronDialogue} {@code Object}. - */ - public OldCronDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code OldCronDialogue} {@code Object}. - * @param player the player. - */ - public OldCronDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new OldCronDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest(Quests.ANIMAL_MAGNETISM); - switch (quest.getStage(player)) { - case 16: - case 17: - case 18: - player("I'm here about the farmers east of here."); - break; - default: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello, old woman."); - stage = 1; - break; - } - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (quest.getStage(player)) { - case 18: - switch (stage) { - case 0: - if (!player.hasItem(CRONE_AMULET)) { - player("Would you be able to replace the amulet you made? I", "seem to have lost it."); - stage = 4; - break; - } - npc("Yes?"); - stage++; - break; - case 1: - player("Well, to tell the truth, I just came back to chat with", "you. Any news?"); - stage++; - break; - case 2: - npc("Disgraceful! Deliver that amulet; a young lady's", "happiness depends upon it."); - stage++; - break; - case 3: - end(); - break; - case 4: - npc("Here you are; luckily, I saved some of Alice's hair in", "case you were careless. Which you were."); - stage++; - break; - case 5: - if (!player.getInventory().hasSpaceFor(RestlessGhost.AMULET)) { - npc("You don't have enough space in your inventory."); - stage = 3; - break; - } - if (!player.getInventory().containsItem(RestlessGhost.AMULET) && !player.getEquipment().containsItem(RestlessGhost.AMULET)) { - npc("You need to bring me a ghostspeak amulet."); - stage = 3; - break; - } - player.getInventory().add(CRONE_AMULET); - end(); - break; - } - break; - case 17: - switch (stage) { - case 0: - player("I'm here to see if you are ready to do your mystical", "stuff with my ghostspeak amulet."); - stage++; - break; - case 1: - if (!player.getInventory().hasSpaceFor(RestlessGhost.AMULET)) { - npc("I most certainly am, but you don't have enough", "space in your backpack."); - stage++; - break; - } - if (!player.getInventory().containsItem(RestlessGhost.AMULET) && !player.getEquipment().containsItem(RestlessGhost.AMULET)) { - npc("I most certainly am, but you don't have an ghostspeak", "amulet."); - stage++; - } else { - npc("I most certainly am; there you go."); - stage += 2; - } - break; - case 2: - end(); - break; - case 3: - player("Wow, that was quick and painless."); - stage++; - break; - case 4: - npc("Just being a good neighbour."); - stage++; - break; - case 5: - player.getInventory().add(CRONE_AMULET); - quest.setStage(player, 18); - end(); - break; - } - break; - case 16: - switch (stage) { - case 0: - player("Alice and her husband are having trouble talking to one", "another and said you might be able to help."); - stage++; - break; - case 1: - npc("Ah, I know them; shame about those cows. Why would", "they think that I could help?"); - stage++; - break; - case 2: - player("Alice seems to think you could alter a ghostspeak amulet", "in order to allow them to communicate."); - stage++; - break; - case 3: - npc("Well, the poor young lady has such family problems; I", "quite feel her pain. I'd be happy to help."); - stage++; - break; - case 4: - npc("You seem to have one of her golden hairs on your", "shoulder, so I can use that..."); - stage++; - break; - case 5: - interpreter.sendDialogue("In a flash, the crone whisks away an unseen hair from your", "shoulder."); - stage++; - break; - case 6: - npc("Talk to me again with a ghostspeak amulet and some", "space in your backpack and I'll be ready to work on", "the little good deed. The way I plan is quite simple,", "really."); - stage++; - break; - case 7: - npc("I can mirror part of the unused mystical essence of the", "ghostspeak amulet, bind it with Alice's hair and thus", "create a second amulet."); - stage++; - break; - case 8: - npc("The second amulet will be useful for the purpose you", "desire, thought it won't work for any other ghost or", "human other than the farmer and his wife."); - stage++; - break; - case 9: - quest.setStage(player, 17); - end(); - break; - } - break; - default: - switch (stage) { - case 1: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I lived here when this was all just fields, you know."); - stage = 2; - break; - case 2: - end(); - break; - } - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 1695 }; - } -} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/draynor/quest/anma/OldCroneDialogue.kt b/Server/src/main/content/region/misthalin/draynor/quest/anma/OldCroneDialogue.kt new file mode 100644 index 000000000..5468b8f75 --- /dev/null +++ b/Server/src/main/content/region/misthalin/draynor/quest/anma/OldCroneDialogue.kt @@ -0,0 +1,169 @@ +package content.region.misthalin.draynor.quest.anma + +import content.data.Quests +import core.api.addItem +import core.api.freeSlots +import core.api.getQuestStage +import core.api.inEquipmentOrInventory +import core.api.sendDialogue +import core.api.sendMessage +import core.api.setQuestStage +import core.game.dialogue.DialogueLabeller +import core.game.dialogue.FacialExpression +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.npc.NPC +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +class OldCroneDialogue : InteractionListener { + + override fun defineListeners() { + on(NPCs.OLD_CRONE_1695, IntType.NPC, "talk-to") { player, node -> + DialogueLabeller.open(player, OldCroneDialogueFile(), node as NPC) + return@on true + } + } +} + +class OldCroneDialogueFile : DialogueLabeller() { + + override fun addConversation() { + + goto("anma_entry") + + label("anma_entry") { + exec { player, npc -> + when (getQuestStage(player, Quests.ANIMAL_MAGNETISM)) { + 16, 17, 18 -> { + loadLabel(player, "about_the_farmers") + } + else -> { + loadLabel(player, "hello_old") + } + } + } + } + + label("hello_old") { + player(FacialExpression.HALF_GUILTY, "Hello, old woman.") + npc("I lived here when this was all just fields, you know.") + goto("nowhere") + } + + label("about_the_farmers") { + player("I'm here about the farmers east of here.") + + exec { player, npc -> + when (getQuestStage(player, Quests.ANIMAL_MAGNETISM)) { + 16 -> { + loadLabel(player, "pre_hair_stage_16") + } + 17 -> { + loadLabel(player, "pre_stage_17") + } + 18 -> { + //get a replacement crone amulet + if (!inEquipmentOrInventory(player, Items.CRONE_MADE_AMULET_10500)) { + loadLabel(player, "replace_crone_amulet") + } else { + //chat + loadLabel(player, "post_crone_amulet_chat") + } + } + } + + } + } + + label("pre_hair_stage_16") { + player("Alice and her husband are having trouble talking to one", "another and said you might be able to help.") + npc("Ah, I know them; shame about those cows. Why would", "they think that I could help?") + player("Alice seems to think you could alter a ghostspeak amulet", "in order to allow them to communicate.") + npc("Well, the poor young lady has such family problems; I", "quite feel her pain. I'd be happy to help.") + npc("You seem to have one of her golden hairs on your", "shoulder, so I can use that...") + + exec { player, npc -> + sendDialogue(player, "In a flash, the crone whisks away an unseen hair from your shoulder.") + loadLabel(player, "post_hair_stage_16") + } + } + + label("post_hair_stage_16") { + npc("Talk to me again with a ghostspeak amulet and some", "space in your backpack and I'll be ready to work on", "the little good deed. The way I plan is quite simple,", "really.") + npc("I can mirror part of the unused mystical essence of the", "ghostspeak amulet, bind it with Alice's hair and thus", "create a second amulet.") + npc("The second amulet will be useful for the purpose you", "desire, thought it won't work for any other ghost or", "human other than the farmer and his wife.") + + exec { player, npc -> + setQuestStage(player, Quests.ANIMAL_MAGNETISM, 17) + loadLabel(player, "nowhere") + } + } + + label("pre_stage_17") { + player("I'm here to see if you are ready to do your mystical", "stuff with my ghostspeak amulet.") + + exec { player, npc -> + if (!inEquipmentOrInventory(player, Items.GHOSTSPEAK_AMULET_552)) { + loadLabel(player, "no_ghostspeak_amulet") + } else if (freeSlots(player) == 0) { + loadLabel(player, "no_free_space") + } else { + loadLabel(player, "crone_made_amulet") + } + } + } + + label("no_ghostspeak_amulet") { + npc("I most certainly am, but you don't have an ghostspeak", "amulet.") + goto("nowhere") + } + + label("no_free_space") { + npc("I most certainly am, but you don't have enough", "space in your backpack.") + goto("nowhere") + } + + label("crone_made_amulet") { + npc("I most certainly am; there you go.") + player("Wow, that was quick and painless.") + npc("Just being a good neighbour.") + + exec { player, npc -> + if (addItem(player, Items.CRONE_MADE_AMULET_10500)) { + setQuestStage(player, Quests.ANIMAL_MAGNETISM, 18) + loadLabel(player, "nowhere") + } + } + } + + label("replace_crone_amulet") { + player("Would you be able to replace the amulet you made? I", "seem to have lost it.") + npc("Here you are; luckily, I saved some of Alice's hair in", "case you were careless. Which you were.") + + exec { player, npc -> + if (freeSlots(player) > 0) { + if (addItem(player, Items.CRONE_MADE_AMULET_10500)) { + loadLabel(player, "nowhere") + } else { + sendMessage(player, "You don't have enough inventory space to accept this amulet.") + } + } else { + sendMessage(player, "You don't have enough inventory space to accept this amulet.") + } + } + + goto("nowhere") + } + + label("post_crone_amulet_chat") { + npc("Yes?") + player("Well, to tell the truth, I just came back to chat with", "you. Any news?") + npc("Disgraceful! Deliver that amulet; a young lady's", "happiness depends upon it.") + goto("nowhere") + } + + //because end() breaks shit + label("nowhere") {} + } +} \ No newline at end of file From d8c7b681bb46c44542cdd5f61b20f451ec9381a8 Mon Sep 17 00:00:00 2001 From: Beck Date: Mon, 13 Jul 2026 23:08:22 -0700 Subject: [PATCH 16/19] various suggested fixes 1 --- .../misthalin/draynor/quest/anma/OldCroneDialogue.kt | 11 ++++------- .../lumbridge/dialogue/FatherAereckDefaultDialogue.kt | 7 ------- .../lumbridge/dialogue/FatherUrhneyDefaultDialogue.kt | 6 ------ .../therestlessghost/FatherAereckQuestDialogueFile.kt | 7 ------- .../therestlessghost/FatherUrhneyQuestDialogueFile.kt | 3 --- .../quest/therestlessghost/RestlessGhostListeners.kt | 2 +- 6 files changed, 5 insertions(+), 31 deletions(-) diff --git a/Server/src/main/content/region/misthalin/draynor/quest/anma/OldCroneDialogue.kt b/Server/src/main/content/region/misthalin/draynor/quest/anma/OldCroneDialogue.kt index 5468b8f75..22a7a8bbd 100644 --- a/Server/src/main/content/region/misthalin/draynor/quest/anma/OldCroneDialogue.kt +++ b/Server/src/main/content/region/misthalin/draynor/quest/anma/OldCroneDialogue.kt @@ -30,8 +30,6 @@ class OldCroneDialogueFile : DialogueLabeller() { override fun addConversation() { - goto("anma_entry") - label("anma_entry") { exec { player, npc -> when (getQuestStage(player, Quests.ANIMAL_MAGNETISM)) { @@ -85,6 +83,7 @@ class OldCroneDialogueFile : DialogueLabeller() { exec { player, npc -> sendDialogue(player, "In a flash, the crone whisks away an unseen hair from your shoulder.") + setQuestStage(player, Quests.ANIMAL_MAGNETISM, 17) loadLabel(player, "post_hair_stage_16") } } @@ -92,10 +91,9 @@ class OldCroneDialogueFile : DialogueLabeller() { label("post_hair_stage_16") { npc("Talk to me again with a ghostspeak amulet and some", "space in your backpack and I'll be ready to work on", "the little good deed. The way I plan is quite simple,", "really.") npc("I can mirror part of the unused mystical essence of the", "ghostspeak amulet, bind it with Alice's hair and thus", "create a second amulet.") - npc("The second amulet will be useful for the purpose you", "desire, thought it won't work for any other ghost or", "human other than the farmer and his wife.") + npc("The second amulet will be useful for the purpose you", "desire, though it won't work for any other ghost or", "human other than the farmer and his wife.") exec { player, npc -> - setQuestStage(player, Quests.ANIMAL_MAGNETISM, 17) loadLabel(player, "nowhere") } } @@ -133,6 +131,8 @@ class OldCroneDialogueFile : DialogueLabeller() { if (addItem(player, Items.CRONE_MADE_AMULET_10500)) { setQuestStage(player, Quests.ANIMAL_MAGNETISM, 18) loadLabel(player, "nowhere") + } else { + loadLabel(player, "no_free_space") } } } @@ -162,8 +162,5 @@ class OldCroneDialogueFile : DialogueLabeller() { npc("Disgraceful! Deliver that amulet; a young lady's", "happiness depends upon it.") goto("nowhere") } - - //because end() breaks shit - label("nowhere") {} } } \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/lumbridge/dialogue/FatherAereckDefaultDialogue.kt b/Server/src/main/content/region/misthalin/lumbridge/dialogue/FatherAereckDefaultDialogue.kt index 77606f0ae..cc2b813af 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/dialogue/FatherAereckDefaultDialogue.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/FatherAereckDefaultDialogue.kt @@ -32,8 +32,6 @@ class FatherAereckDefaultDialogue : InteractionListener { class FatherAereckDefaultDialogueFile : DialogueLabeller() { override fun addConversation() { - goto("entry_logic") - label("entry_logic") //allows skipping the main dialogue while Restless Ghost quest is started exec { player, npc -> @@ -196,13 +194,8 @@ class FatherAereckDefaultDialogueFile : DialogueLabeller() { } } - //compile this too see if it works label("start_restless_ghost_quest") { open(player!!, FatherAereckQuestDialogueFile(), npc!!) } - - // Because using end() breaks shit - label("nowhere") {} - } } \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/lumbridge/dialogue/FatherUrhneyDefaultDialogue.kt b/Server/src/main/content/region/misthalin/lumbridge/dialogue/FatherUrhneyDefaultDialogue.kt index 154586294..ecd83b018 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/dialogue/FatherUrhneyDefaultDialogue.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/FatherUrhneyDefaultDialogue.kt @@ -33,8 +33,6 @@ class FatherUrhneyDefaultDialogueFile: DialogueLabeller() { npc(FacialExpression.ANGRY, "Go away! I'm meditating!"); - goto("main_opts") - label("main_opts") { exec {player, npc -> when (getQuestStage(player, Quests.THE_RESTLESS_GHOST)) { @@ -145,9 +143,5 @@ class FatherUrhneyDefaultDialogueFile: DialogueLabeller() { label("ive_lost_amulet") { open(player!!, FatherUrhneyQuestAmuletDialogueFile(), npc!!) } - - //because end() breaks shit - label("nowhere") {} - } } \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherAereckQuestDialogueFile.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherAereckQuestDialogueFile.kt index 8ffd9774c..7c2e43828 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherAereckQuestDialogueFile.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherAereckQuestDialogueFile.kt @@ -77,12 +77,10 @@ class FatherAereckQuestDialogueFile : DialogueLabeller() { player("I will, thanks.") goto("nowhere") - } label("no_time") { npc("Oh well. If you do have some spare time on your", "hands, come back and talk to me.") - goto("nowhere") } @@ -117,10 +115,5 @@ class FatherAereckQuestDialogueFile : DialogueLabeller() { player(FacialExpression.AMAZED, "I've finally found the ghost's skull!") npc("Great! Put it in the ghost's coffin and see what", "happens!"); } - - - //Because doing end() breaks - label("nowhere") {} - } } \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherUrhneyQuestDialogueFile.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherUrhneyQuestDialogueFile.kt index be92515a4..7157f5577 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherUrhneyQuestDialogueFile.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherUrhneyQuestDialogueFile.kt @@ -118,8 +118,5 @@ class FatherUrhneyQuestAmuletDialogueFile : DialogueLabeller() { player("Ok, I'll try to be.") goto("nowhere") } - - //because end() breaks shit - label("nowhere") {} } } \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostListeners.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostListeners.kt index 438230efb..30ad706c0 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostListeners.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostListeners.kt @@ -48,7 +48,7 @@ class RestlessGhostListeners : InteractionListener { //removes the skeleton on ground, resets in ~2mins private fun animSkeleAlive(player: Player, skeleScenery: core.game.node.scenery.Scenery) { - //Missing: find animation and maybe sfx of the skeleton coming alive + // TODO: find animation and maybe sfx of the skeleton coming alive, from a still scenery of a skeleton slumped on the ground against the wall and then rises to life queueScript(player, 4) {_ -> SceneryBuilder.remove(skeleScenery, 200) From b22525e20461897c6c49b3944d03bcd5b548ae72 Mon Sep 17 00:00:00 2001 From: Beck Date: Wed, 15 Jul 2026 01:06:10 -0700 Subject: [PATCH 17/19] various suggested fixes 2 --- Server/data/configs/item_configs.json | 2 - .../draynor/quest/anma/OldCroneDialogue.kt | 16 +-- .../dialogue/FatherAereckDefaultDialogue.kt | 27 ++-- .../dialogue/FatherUrhneyDefaultDialogue.kt | 22 ++- .../FatherAereckQuestDialogueFile.kt | 4 + .../quest/therestlessghost/RestlessGhost.kt | 7 +- .../RestlessGhostListeners.kt | 127 ++++++++++++++---- 7 files changed, 140 insertions(+), 65 deletions(-) diff --git a/Server/data/configs/item_configs.json b/Server/data/configs/item_configs.json index b928857e7..1c38971dc 100644 --- a/Server/data/configs/item_configs.json +++ b/Server/data/configs/item_configs.json @@ -4736,8 +4736,6 @@ "name": "Ghost's skull", "examine": "Ooooh spooky!", "archery_ticket_price": "0", - "destroy": "true", - "destroy_message": "If you get rid of the skull you'll have to search for it again.", "durability": null, "tradeable": "false" }, diff --git a/Server/src/main/content/region/misthalin/draynor/quest/anma/OldCroneDialogue.kt b/Server/src/main/content/region/misthalin/draynor/quest/anma/OldCroneDialogue.kt index 22a7a8bbd..46f47c201 100644 --- a/Server/src/main/content/region/misthalin/draynor/quest/anma/OldCroneDialogue.kt +++ b/Server/src/main/content/region/misthalin/draynor/quest/anma/OldCroneDialogue.kt @@ -30,15 +30,13 @@ class OldCroneDialogueFile : DialogueLabeller() { override fun addConversation() { - label("anma_entry") { - exec { player, npc -> - when (getQuestStage(player, Quests.ANIMAL_MAGNETISM)) { - 16, 17, 18 -> { - loadLabel(player, "about_the_farmers") - } - else -> { - loadLabel(player, "hello_old") - } + exec { player, npc -> + when (getQuestStage(player, Quests.ANIMAL_MAGNETISM)) { + 16, 17, 18 -> { + loadLabel(player, "about_the_farmers") + } + else -> { + loadLabel(player, "hello_old") } } } diff --git a/Server/src/main/content/region/misthalin/lumbridge/dialogue/FatherAereckDefaultDialogue.kt b/Server/src/main/content/region/misthalin/lumbridge/dialogue/FatherAereckDefaultDialogue.kt index cc2b813af..902514bf1 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/dialogue/FatherAereckDefaultDialogue.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/FatherAereckDefaultDialogue.kt @@ -32,22 +32,21 @@ class FatherAereckDefaultDialogue : InteractionListener { class FatherAereckDefaultDialogueFile : DialogueLabeller() { override fun addConversation() { - label("entry_logic") - //allows skipping the main dialogue while Restless Ghost quest is started - exec { player, npc -> - when (getQuestStage(player, Quests.THE_RESTLESS_GHOST)) { - 0 -> { - loadLabel(player, "main_entry_transcript") - } - 10, 20, 30, 40 -> { - //passthrough for mid-quest dialogue skip - open(player, FatherAereckQuestDialogueFile(), npc) - } - else -> { - loadLabel(player, "main_entry_transcript") - } + //allows skipping the main dialogue while Restless Ghost quest is started + exec { player, npc -> + when (getQuestStage(player, Quests.THE_RESTLESS_GHOST)) { + 0 -> { + loadLabel(player, "main_entry_transcript") + } + 10, 20, 30, 40 -> { + //passthrough for mid-quest dialogue skip + open(player, FatherAereckQuestDialogueFile(), npc) + } + else -> { + loadLabel(player, "main_entry_transcript") } } + } label("main_entry_transcript") { npc("Welcome to the church of holy Saradomin.") diff --git a/Server/src/main/content/region/misthalin/lumbridge/dialogue/FatherUrhneyDefaultDialogue.kt b/Server/src/main/content/region/misthalin/lumbridge/dialogue/FatherUrhneyDefaultDialogue.kt index ecd83b018..bbc6ac05f 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/dialogue/FatherUrhneyDefaultDialogue.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/FatherUrhneyDefaultDialogue.kt @@ -33,18 +33,16 @@ class FatherUrhneyDefaultDialogueFile: DialogueLabeller() { npc(FacialExpression.ANGRY, "Go away! I'm meditating!"); - label("main_opts") { - exec {player, npc -> - when (getQuestStage(player, Quests.THE_RESTLESS_GHOST)) { - 0 -> { - loadLabel(player, "pre_quest_opts") - } - 10 -> { - loadLabel(player, "mid_quest_opts") - } - else -> { - loadLabel(player, "post_quest_opts") - } + exec { player, npc -> + when (getQuestStage(player, Quests.THE_RESTLESS_GHOST)) { + 0 -> { + loadLabel(player, "pre_quest_opts") + } + 10 -> { + loadLabel(player, "mid_quest_opts") + } + else -> { + loadLabel(player, "post_quest_opts") } } } diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherAereckQuestDialogueFile.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherAereckQuestDialogueFile.kt index 7c2e43828..21a963614 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherAereckQuestDialogueFile.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherAereckQuestDialogueFile.kt @@ -3,6 +3,7 @@ package content.region.misthalin.lumbridge.quest.therestlessghost; import content.data.Quests import core.api.getQuestStage import core.api.setQuestStage +import core.api.setVarbit import core.game.dialogue.DialogueLabeller import core.game.dialogue.DialogueOption import core.game.dialogue.FacialExpression @@ -63,6 +64,9 @@ class FatherAereckQuestDialogueFile : DialogueLabeller() { label("let_me_help") { exec { player, npc -> setQuestStage(player, Quests.THE_RESTLESS_GHOST, 10) + //ensures the varbits is set to default state + setVarbit(player, 2128, 0, true) + setVarbit(player, 2130, 0, true) } npc("Thank you. The problem is, there is a ghost in the", "church graveyard. I would like you to get rid of it.") diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhost.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhost.kt index a0bc51016..05af5aeb0 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhost.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhost.kt @@ -92,6 +92,11 @@ class RestlessGhost : Quest(Quests.THE_RESTLESS_GHOST, 25, 24, 1, 107, 0, 4, 5) player.interfaceManager.closeChatbox() player.packetDispatch.sendString("You have completed The Restless Ghost Quest!", 277, 2 + 2) - setVarp(player, 728, 31, true) + + //post quest skeleton scenery and altar visually correct + setVarbit(player, 2128, 1, true) + setVarbit(player, 2130, 1, true) + + //setVarp(player, 728, 31, true) } } \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostListeners.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostListeners.kt index 30ad706c0..3647f6253 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostListeners.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostListeners.kt @@ -2,6 +2,7 @@ package content.region.misthalin.lumbridge.quest.therestlessghost; import content.data.Quests import core.api.* +import core.game.global.action.DropListener import core.game.interaction.IntType import core.game.interaction.InteractionListener import core.game.node.entity.Entity @@ -9,6 +10,7 @@ import core.game.node.entity.combat.CombatStyle import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.game.node.scenery.SceneryBuilder +import core.game.system.task.Pulse import core.game.world.map.Direction import core.game.world.map.Location import core.game.world.map.RegionManager @@ -22,17 +24,20 @@ import org.rs09.consts.Sounds /** * Authenticity source Aug 18 2009 * https://www.youtube.com/watch?v=ikbX12Xywxs +* https://www.youtube.com/watch?v=gC3qR9tKJMI * * Author Beck, Vexia **/ class RestlessGhostListeners : InteractionListener { companion object { - //This is not in the Animations consts + //This is not in the Animations consts, added in Constlib MR !22 private const val Animations_HUMAN_CLOSE_CHEST_535 = 535 private const val CLOSED_COFFIN = Scenery.COFFIN_2145 private const val INCOMPLETE_COFFIN_OPEN = Scenery.COFFIN_15052 private const val COMPLETE_COFFIN_OPEN = Scenery.COFFIN_15053 + private const val ALTAR_WITH_SKULL = Scenery.ALTAR_15050 + private const val ALTAR_NO_SKULL = Scenery.ALTAR_15051 private fun createRestlessGhostNPC() : NPC { val restlessGhostNPC = NPC(NPCs.RESTLESS_GHOST_457, Location(3250, 3195, 0)) @@ -45,14 +50,26 @@ class RestlessGhostListeners : InteractionListener { return restlessGhostNPC } - //removes the skeleton on ground, resets in ~2mins - private fun animSkeleAlive(player: Player, skeleScenery: core.game.node.scenery.Scenery) { - - // TODO: find animation and maybe sfx of the skeleton coming alive, from a still scenery of a skeleton slumped on the ground against the wall and then rises to life - - queueScript(player, 4) {_ -> - SceneryBuilder.remove(skeleScenery, 200) - return@queueScript stopExecuting(player) + //Scenery 15057 changes to 15060 hidden and plays animation, resets in ~2mins if player still doing quest, otherwise it stays at 15060 forever + private fun animSkeleAlive(player: Player) { + + //set scenery to 15060 which hides the ground skeleton + setVarbit(player, 2128, 1, true) + + // TODO: Scenery skeleton id 15057 turns into a hidden 15060 then with an animation, uses varbit 2128, client side only + // TODO: find animation and maybe sfx of the skeleton coming alive, from a still scenery 15057 (15060 is the post animation?) of a skeleton slumped on the ground against the wall and then rises to life + // TODO: convert to queuescript on Scenery objects when engine is capable to be attached to them. + // https://www.youtube.com/watch?v=gC3qR9tKJMI + + // Use world pulse in the mean time, in the edge case where a player logs off in middle of the script + if (getQuestStage(player, Quests.THE_RESTLESS_GHOST) in 30..40) { + submitWorldPulse(object : Pulse(200) { + override fun pulse(): Boolean { + //resets the skeleton on the ground + setVarbit(player, 2128, 0, true) + return true + } + }) } } @@ -106,8 +123,8 @@ class RestlessGhostListeners : InteractionListener { toggleCoffinSceneryObj(player, node as core.game.node.scenery.Scenery) if (!isQuestComplete(player, Quests.THE_RESTLESS_GHOST)) { - //is this from the center of the scenery/node object? val ghostExists = RegionManager.getLocalNPCs(node.centerLocation).find { it.id == NPCs.RESTLESS_GHOST_457 } + //val ghostExists = RegionManager.getLocalNpcs(node.centerLocation).find { it.id == NPCs.RESTLESS_GHOST_457 } //not within rendering distance or doesn't exist at all if (ghostExists == null) { @@ -115,11 +132,21 @@ class RestlessGhostListeners : InteractionListener { val ghost = createRestlessGhostNPC() //despawn ghost after awhile and shut the coffin - queueScript(player, 200) { _ -> + // TODO: convert to queuescript on Scenery objects when engine is capable to be attached to them. + // Use world pulse in the mean time, in the edge case where a player logs off in middle of the script + /* queueScript(player, 200) { _ -> ghost.clear() toggleCoffinSceneryObj(player, node) return@queueScript stopExecuting(player) - } + } */ + + submitWorldPulse(object : Pulse(200) { + override fun pulse(): Boolean { + ghost.clear() + toggleCoffinSceneryObj(player, node) + return true + } + }) } } @@ -166,42 +193,88 @@ class RestlessGhostListeners : InteractionListener { with as core.game.node.scenery.Scenery SceneryBuilder.replace(with, with.transform(COMPLETE_COFFIN_OPEN)) - } return@onUseWith true } - on(Scenery.ALTAR_15050, IntType.SCENERY, "search") { player, node -> + on(ALTAR_WITH_SKULL, IntType.SCENERY, "search") { player, node -> - if (!inInventory(player, Items.GHOSTS_SKULL_553)) { + if (!inInventory(player, Items.GHOSTS_SKULL_553) && !inBank(player, Items.GHOSTS_SKULL_553)) { if (getQuestStage(player, Quests.THE_RESTLESS_GHOST) !in 30..40) { sendMessage(player, "You search the altar and find nothing.") } else { if (freeSlots(player) >= 1) { if (addItem(player, Items.GHOSTS_SKULL_553)) { setQuestStage(player, Quests.THE_RESTLESS_GHOST, 40) - sendMessage(player, "The skeleton in the corner suddenly comes to life!") - //skele comes to live from scenery 15057 with an animation, - //but idk how to select it to at least remove/hide it from the world briefly - //animSkeleAlive(player, node as core.game.node.scenery.Scenery) - - //spawn in skeleton - val s = createLevel13SkeletonNPC(player) + //update altar to without the skull, altar is now scenery 15051 + setVarbit(player, 2130, 1, true) + + //if skeleton scenery is visible such first time coming alive + //so that it can only be killed/spawned to attack once to prevent some sort of griefing exploit + if (getVarbit(player, 2128) == 0) { + sendMessage(player, "The skeleton in the corner suddenly comes to life!") - // despawn after awhile if it never dies - // so there isnt a group of them for next visitor lol - queueScript(player, 200) { _ -> - s.clear() - return@queueScript stopExecuting(player) + animSkeleAlive(player) + + //spawn in skeleton + val s = createLevel13SkeletonNPC(player) + + // despawn after awhile if it never dies so there isnt a group of them for next visitor lol + + // TODO: convert to queuescript on Scenery objects when engine is capable to be attached to them. + // Use world pulse in the mean time, in the edge case where a player logs off in middle of the script + + /* queueScript(player, 200) { _ -> + s.clear() + return@queueScript stopExecuting(player) + } */ + + submitWorldPulse(object : Pulse(200) { + override fun pulse(): Boolean { + s.clear() + return true + } + }) } } + } else { + sendMessage(player, "You don't have enough inventory space.") } } } return@on true } + + on(ALTAR_NO_SKULL, IntType.SCENERY, "search") { player, node -> + //Mainly for a weird edge case if some how the varbit wasn't set correctly + if (getQuestStage(player, Quests.THE_RESTLESS_GHOST) !in 30..40) { + sendMessage(player, "You search the altar and find nothing.") + } else { + if (!inInventory(player, Items.GHOSTS_SKULL_553) && !inBank(player, Items.GHOSTS_SKULL_553)) { + //guard clause if for when player does the drop trick, etc + if (!addItem(player, Items.GHOSTS_SKULL_553)) { + sendMessage(player, "You don't have enough inventory space.") + } + } else { + // TODO: Authenticity check needed for exact message + sendMessage(player, "You already have the Ghost's Skull!") + } + } + return@on true + } + + on(Items.GHOSTS_SKULL_553, IntType.ITEM, "drop") { player, node -> + DropListener.drop(player, node.asItem()) + + if (getQuestStage(player, Quests.THE_RESTLESS_GHOST) in 30..40) { + //update altar to one with the skull, altar is now scenery 15060 + setVarbit(player, 2130, 0, true) + } + + return@on true + } } } \ No newline at end of file From afad2cabda14cc6fa670026bf7429fee70f12b86 Mon Sep 17 00:00:00 2001 From: Beck Date: Wed, 15 Jul 2026 16:28:30 -0700 Subject: [PATCH 18/19] Constlib Anim 535 and comment cleanup --- .../quest/therestlessghost/RestlessGhostListeners.kt | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostListeners.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostListeners.kt index 3647f6253..2a98a16c0 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostListeners.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostListeners.kt @@ -30,9 +30,6 @@ import org.rs09.consts.Sounds class RestlessGhostListeners : InteractionListener { companion object { - //This is not in the Animations consts, added in Constlib MR !22 - private const val Animations_HUMAN_CLOSE_CHEST_535 = 535 - private const val CLOSED_COFFIN = Scenery.COFFIN_2145 private const val INCOMPLETE_COFFIN_OPEN = Scenery.COFFIN_15052 private const val COMPLETE_COFFIN_OPEN = Scenery.COFFIN_15053 @@ -94,7 +91,7 @@ class RestlessGhostListeners : InteractionListener { COMPLETE_COFFIN_OPEN, INCOMPLETE_COFFIN_OPEN -> { //close it - visualize(player, Animations_HUMAN_CLOSE_CHEST_535, -1) + visualize(player, Animations.HUMAN_CLOSE_CHEST_535, -1) SceneryBuilder.replace(coffin, coffin.transform(CLOSED_COFFIN)) } CLOSED_COFFIN -> { @@ -124,7 +121,6 @@ class RestlessGhostListeners : InteractionListener { if (!isQuestComplete(player, Quests.THE_RESTLESS_GHOST)) { val ghostExists = RegionManager.getLocalNPCs(node.centerLocation).find { it.id == NPCs.RESTLESS_GHOST_457 } - //val ghostExists = RegionManager.getLocalNpcs(node.centerLocation).find { it.id == NPCs.RESTLESS_GHOST_457 } //not within rendering distance or doesn't exist at all if (ghostExists == null) { From 83854c3305782a20ece072766e17fe09d0eaa066 Mon Sep 17 00:00:00 2001 From: Beck Date: Sat, 1 Aug 2026 00:06:16 -0700 Subject: [PATCH 19/19] even more fixes 1 --- .../dialogue/FatherAereckDefaultDialogue.kt | 55 ++++++++++++------- .../dialogue/FatherUrhneyDefaultDialogue.kt | 16 +++--- .../FatherAereckQuestDialogueFile.kt | 15 +++-- .../FatherUrhneyQuestDialogueFile.kt | 23 +++++--- .../quest/therestlessghost/RestlessGhost.kt | 2 +- .../RestlessGhostNPCDialogue.kt | 24 ++++---- 6 files changed, 77 insertions(+), 58 deletions(-) diff --git a/Server/src/main/content/region/misthalin/lumbridge/dialogue/FatherAereckDefaultDialogue.kt b/Server/src/main/content/region/misthalin/lumbridge/dialogue/FatherAereckDefaultDialogue.kt index 902514bf1..fd66eb1e4 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/dialogue/FatherAereckDefaultDialogue.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/FatherAereckDefaultDialogue.kt @@ -32,7 +32,7 @@ class FatherAereckDefaultDialogue : InteractionListener { class FatherAereckDefaultDialogueFile : DialogueLabeller() { override fun addConversation() { - //allows skipping the main dialogue while Restless Ghost quest is started + //allows skipping the main dialogue authentically while Restless Ghost quest is started exec { player, npc -> when (getQuestStage(player, Quests.THE_RESTLESS_GHOST)) { 0 -> { @@ -49,21 +49,24 @@ class FatherAereckDefaultDialogueFile : DialogueLabeller() { } label("main_entry_transcript") { - npc("Welcome to the church of holy Saradomin.") + npc(FacialExpression.FRIENDLY, "Welcome to the church of holy Saradomin.") goto("main_opts") } label("main_opts") { exec { player, npc -> - if (getQuestStage(player, Quests.THE_RESTLESS_GHOST) > 0) { - loadLabel(player, "no_quest_main_opts") - } else { + if (getQuestStage(player, Quests.THE_RESTLESS_GHOST) == 0) { loadLabel(player, "not_started_quest_main_opts") + } else if (isQuestComplete(player, Quests.THE_RESTLESS_GHOST)) { + loadLabel(player, "post_quest_main_opts") + } else { + //this shouldn't be needed although in case some how we get here + loadLabel(player, "mid_quest_main_opts") } } } - label("no_quest_main_opts") { + label("post_quest_main_opts") { options( DialogueOption( "change_grave", @@ -85,11 +88,6 @@ class FatherAereckDefaultDialogueFile : DialogueLabeller() { label("not_started_quest_main_opts") { options( - DialogueOption( - "change_grave", - "Can you change my gravestone now?", - expression = ChatAnim.HALF_ASKING - ), DialogueOption( "who_sara", "Who's Saradomin?", @@ -102,8 +100,23 @@ class FatherAereckDefaultDialogueFile : DialogueLabeller() { ), DialogueOption( "start_restless_ghost_quest", - "I'm looking for a quest.", + "I'm looking for a quest!", + expression = ChatAnim.FRIENDLY + ) + ) + } + + label("mid_quest_main_opts") { + options( + DialogueOption( + "who_sara", + "Who's Saradomin?", expression = ChatAnim.HALF_ASKING + ), + DialogueOption( + "nice_place", + "Nice place you've got here.", + expression = ChatAnim.FRIENDLY ) ) } @@ -117,10 +130,9 @@ class FatherAereckDefaultDialogueFile : DialogueLabeller() { npc(FacialExpression.HALF_ASKING, "Surely you have heard of our god, Saradomin?") npc(FacialExpression.AMAZED, "He who created the forces of goodness and purity in this", "world? I cannot believe your ignorance!") npc("This is the god with more followers than any other ...at", "least in this part of the world.") - npc("He who forged the world as we know it, along with his", "brothers Guthix and Zamorak?") + npc(FacialExpression.HALF_ASKING, "He who forged the world as we know it, along with his", "brothers Guthix and Zamorak?") goto("oh_that_sara") - } label("oh_that_sara") { @@ -128,7 +140,7 @@ class FatherAereckDefaultDialogueFile : DialogueLabeller() { DialogueOption( "that_sara", "Oh, THAT Saradomin.", - expression = FacialExpression.GUILTY + expression = FacialExpression.AMAZED ), DialogueOption( "not_from_this_world", @@ -146,8 +158,8 @@ class FatherAereckDefaultDialogueFile : DialogueLabeller() { } label("not_from_this_world") { - npc("..."); - npc("That's...strange.") + npc(FacialExpression.SUSPICIOUS, "..."); + npc(FacialExpression.SUSPICIOUS, "That's...strange.") npc("I thought things not from this world were all, you know,", "slime and tentacles.") goto("like_my_disguise") } @@ -160,7 +172,8 @@ class FatherAereckDefaultDialogueFile : DialogueLabeller() { ), DialogueOption( "like_disguise", - "I am! Do you like my disguise?" + "I am! Do you like my disguise?", + expression = FacialExpression.FRIENDLY ), ) } @@ -180,7 +193,7 @@ class FatherAereckDefaultDialogueFile : DialogueLabeller() { } label("nice_place") { - npc("It is, isn't it? It was built over two centuries ago."); + npc(FacialExpression.HALF_ASKING, "It is, isn't it? It was built over two centuries ago."); goto("nowhere") } @@ -188,7 +201,9 @@ class FatherAereckDefaultDialogueFile : DialogueLabeller() { label("gravestone_iface") { exec { player, npc -> openInterface(player, Components.GRAVESTONE_SHOP_652) - player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 0, 15); + if (isQuestComplete(Quests.THE_RESTLESS_GHOST)) { + player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 0, 15); + } loadLabel(player, "nowhere") } } diff --git a/Server/src/main/content/region/misthalin/lumbridge/dialogue/FatherUrhneyDefaultDialogue.kt b/Server/src/main/content/region/misthalin/lumbridge/dialogue/FatherUrhneyDefaultDialogue.kt index bbc6ac05f..ac522db5f 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/dialogue/FatherUrhneyDefaultDialogue.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/FatherUrhneyDefaultDialogue.kt @@ -51,7 +51,7 @@ class FatherUrhneyDefaultDialogueFile: DialogueLabeller() { options( DialogueOption( "thats_friendly", - "Well, that's friendly" + "Well, that's friendly." ), DialogueOption( "repo_house", @@ -64,7 +64,7 @@ class FatherUrhneyDefaultDialogueFile: DialogueLabeller() { options( DialogueOption( "thats_friendly", - "Well, that's friendly" + "Well, that's friendly." ), DialogueOption( "father_aereck_sent", @@ -95,13 +95,13 @@ class FatherUrhneyDefaultDialogueFile: DialogueLabeller() { } label("thats_friendly") { - npc("I SAID go AWAY.") + npc(FacialExpression.ANGRY, "I SAID go AWAY.") player("Okay, okay... sheesh, what a grouch.") goto("nowhere") } label("repo_house") { - npc("Under what grounds???") + npc(FacialExpression.ANGRY, "Under what grounds???") goto("repo_opts") } @@ -119,16 +119,16 @@ class FatherUrhneyDefaultDialogueFile: DialogueLabeller() { } label("mtg_repay") { - npc("What?") - npc("I don't have a mortgage! I built this house.") + npc(FacialExpression.ANNOYED, "What?") + npc(FacialExpression.ANGRY, "I don't have a mortgage! I built this house.") player("Sorry. I must have got the wrong address. All the", "houses look the same around here.") - npc("What? What houses? What ARE you talking about???") + npc(FacialExpression.ANGRY, "What? What houses? What ARE you talking about???") player("Never mind.") goto("nowhere") } label("want_house") { - npc("Oh... go away and stop wasting my time!") + npc(FacialExpression.ANGRY, "Oh... go away and stop wasting my time!") goto("nowhere") } diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherAereckQuestDialogueFile.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherAereckQuestDialogueFile.kt index 21a963614..6d8cce233 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherAereckQuestDialogueFile.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherAereckQuestDialogueFile.kt @@ -45,6 +45,12 @@ class FatherAereckQuestDialogueFile : DialogueLabeller() { label("quest_main_entry") { npc("That's lucky, I need someone to do a quest for me.") + exec { player, npc -> + setQuestStage(player, Quests.THE_RESTLESS_GHOST, 10) + //ensures the varbits is set to default state + setVarbit(player, 2128, 0, true) + setVarbit(player, 2130, 0, true) + } goto("start_quest_opts") } @@ -56,19 +62,12 @@ class FatherAereckQuestDialogueFile : DialogueLabeller() { ), DialogueOption( "no_time", - "Sorry, I don't have time right now." + "Sorry, I don't have time right now" ) ) } label("let_me_help") { - exec { player, npc -> - setQuestStage(player, Quests.THE_RESTLESS_GHOST, 10) - //ensures the varbits is set to default state - setVarbit(player, 2128, 0, true) - setVarbit(player, 2130, 0, true) - } - npc("Thank you. The problem is, there is a ghost in the", "church graveyard. I would like you to get rid of it.") npc("If you need any help, my friend Father Urhney is an", "expert on ghosts.") npc("I believe he is currently living as a hermit in Lumbridge", "swamp. He has a little shack in the south-west of the", "swamps.") diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherUrhneyQuestDialogueFile.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherUrhneyQuestDialogueFile.kt index 7157f5577..7b9b2c971 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherUrhneyQuestDialogueFile.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherUrhneyQuestDialogueFile.kt @@ -73,20 +73,25 @@ class FatherUrhneyQuestAmuletDialogueFile : DialogueLabeller() { //mid or after finishing quest and needing another one or doing the drop trick - npc("Father Urhney sighs.") + exec { player, npc -> + line("Father Urhney sighs.") + loadLabel(player, "amulet_main_flow") + } - exec { player, npc -> - if (anyInEquipment(player, Items.GHOSTSPEAK_AMULET_552) || anyInInventory(player, Items.GHOSTSPEAK_AMULET_552)) { - loadLabel(player, "amulet_equip_resp") - } else if (inBank(player, Items.GHOSTSPEAK_AMULET_552)) { - loadLabel(player, "amulet_in_bank_resp") - } else { - loadLabel(player, "give_amulet") + label("amulet_main_flow") { + exec { player, npc -> + if (anyInEquipment(player, Items.GHOSTSPEAK_AMULET_552) || anyInInventory(player, Items.GHOSTSPEAK_AMULET_552)) { + loadLabel(player, "amulet_equip_resp") + } else if (inBank(player, Items.GHOSTSPEAK_AMULET_552)) { + loadLabel(player, "amulet_in_bank_resp") + } else { + loadLabel(player, "give_amulet") + } } } label("amulet_equip_resp") { - npc("What are you talking about? I can see you've got it", "with you!") + npc(FacialExpression.ANNOYED, "What are you talking about? I can see you've got it", "with you!") goto("nowhere") } diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhost.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhost.kt index 05af5aeb0..a5d15a0d3 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhost.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhost.kt @@ -28,7 +28,7 @@ class RestlessGhost : Quest(Quests.THE_RESTLESS_GHOST, 25, 24, 1, 107, 0, 4, 5) 0 -> { player.packetDispatch.sendString("I can start this quest by speaking to Father Aereck in the", 275, 4 + 7) player.packetDispatch.sendString("church next to Lumbridge Castle.", 275, 5 + 7) - player.packetDispatch.sendString("I must be unafraid of a Level 13 Skeleton ", 275, 6 + 7) + player.packetDispatch.sendString("I must be unafraid of a Level 13 Skeleton.", 275, 6 + 7) } 10 -> { player.packetDispatch.sendString("Father Aereck asked me to help him deal with the Ghost in", 275, 4 + 7) diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostNPCDialogue.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostNPCDialogue.kt index 355ed584d..619db934d 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostNPCDialogue.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostNPCDialogue.kt @@ -69,19 +69,19 @@ class RestlessGhostNPCDialogueFile : DialogueLabeller() { } label("not_very_good") { - npc(FacialExpression.HALF_GUILTY, "Not very good actually.") - player(FacialExpression.HALF_GUILTY, "What's the problem then?") - npc(FacialExpression.HALF_GUILTY, "Did you understand what I said???") - player(FacialExpression.HALF_GUILTY, "Yep, now tell me what the problem is.") - npc(FacialExpression.HALF_GUILTY, "WOW! This is INCREDIBLE! I didn't expect anyone", "to ever understand me again!") - player(FacialExpression.HALF_GUILTY, "Ok, ok, I can understand you!") - player(FacialExpression.HALF_GUILTY, "But have you any idea WHY you're doomed to be a", "ghost?") + npc(FacialExpression.HALF_WORRIED, "Not very good actually.") + player(FacialExpression.HALF_ASKING, "What's the problem then?") + npc(FacialExpression.THINKING, "Did you understand what I said???") + player(FacialExpression.FRIENDLY, "Yep, now tell me what the problem is.") + npc(FacialExpression.AMAZED, "WOW! This is INCREDIBLE! I didn't expect anyone", "to ever understand me again!") + player(FacialExpression.FRIENDLY, "Ok, Ok, I can understand you!") + player(FacialExpression.HALF_ASKING, "But have you any idea WHY you're doomed to be a", "ghost?") npc(FacialExpression.HALF_GUILTY, "Well, to be honest... I'm not sure.") - player(FacialExpression.HALF_GUILTY, "I've been told a certain task may need to be completed", "so you can rest in peace.") + player(FacialExpression.FRIENDLY, "I've been told a certain task may need to be completed", "so you can rest in peace.") npc(FacialExpression.HALF_GUILTY, "I should think it is probably because a warlock has come", "along and stolen my skull. If you look inside my coffin", "there, you'll find my corpse without a head on it.") - player(FacialExpression.HALF_GUILTY, "Do you know where this warlock might be now?") + player(FacialExpression.HALF_ASKING, "Do you know where this warlock might be now?") npc(FacialExpression.HALF_GUILTY, "I think it was one of the warlocks who lives in the big", "tower by the sea south-west from here.") - player(FacialExpression.HALF_GUILTY, "Ok. I will try and get the skull back for you, then you", "can rest in peace.") + player(FacialExpression.FRIENDLY, "Ok. I will try and get the skull back for you, then you", "can rest in peace.") exec { player, npc -> setQuestStage(player, Quests.THE_RESTLESS_GHOST, 30) @@ -90,8 +90,8 @@ class RestlessGhostNPCDialogueFile : DialogueLabeller() { } label("great_relief") { - npc(FacialExpression.HALF_GUILTY, "Ooh, thank you. That would be such a great relief!") - npc(FacialExpression.HALF_GUILTY, "It is so dull being a ghost...") + npc(FacialExpression.HALF_WORRIED, "Ooh, thank you. That would be such a great relief!") + npc(FacialExpression.SAD, "It is so dull being a ghost...") } label("woo") {