Merge branch 'quest_restless_ghost' into 'master'

The Restless Ghost quest & Wildy Skull spawns & Old Crone Dialogue during Animal Magnetism

Closes #2477

See merge request 2009scape/2009scape!2394
This commit is contained in:
Beck 2026-08-01 07:06:27 +00:00
commit 61bef9d82a
18 changed files with 1311 additions and 1566 deletions

View file

@ -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}-"

View file

@ -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"
},

View file

@ -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 };
}
}

View file

@ -0,0 +1,164 @@
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() {
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.")
setQuestStage(player, Quests.ANIMAL_MAGNETISM, 17)
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, though it won't work for any other ghost or", "human other than the farmer and his wife.")
exec { player, npc ->
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")
} else {
loadLabel(player, "no_free_space")
}
}
}
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")
}
}
}

View file

@ -0,0 +1,215 @@
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 authentically 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(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, "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("post_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(
"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.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
)
)
}
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(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") {
options(
DialogueOption(
"that_sara",
"Oh, THAT Saradomin.",
expression = FacialExpression.AMAZED
),
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.")
goto("nowhere")
}
label("not_from_this_world") {
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")
}
label("like_my_disguise") {
options(
DialogueOption(
"disguise_not_me",
"Not me."
),
DialogueOption(
"like_disguise",
"I am! Do you like my disguise?",
expression = FacialExpression.FRIENDLY
),
)
}
label("disguise_not_me") {
npc("Well, I can see that. Still, there's something special about", "you.")
player("Thanks, I think.")
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!")
goto("nowhere")
}
label("nice_place") {
npc(FacialExpression.HALF_ASKING, "It is, isn't it? It was built over two centuries ago.");
goto("nowhere")
}
label("gravestone_iface") {
exec { player, npc ->
openInterface(player, Components.GRAVESTONE_SHOP_652)
if (isQuestComplete(Quests.THE_RESTLESS_GHOST)) {
player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 0, 15);
}
loadLabel(player, "nowhere")
}
}
label("start_restless_ghost_quest") {
open(player!!, FatherAereckQuestDialogueFile(), npc!!)
}
}
}

View file

@ -0,0 +1,145 @@
package content.region.misthalin.lumbridge.dialogue;
import content.data.Quests
import content.region.misthalin.lumbridge.quest.therestlessghost.*
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!");
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")
}
}
}
label("pre_quest_opts") {
options(
DialogueOption(
"thats_friendly",
"Well, that's friendly."
),
DialogueOption(
"repo_house",
"I've come to repossess your house."
)
)
}
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(FacialExpression.ANGRY, "I SAID go AWAY.")
player("Okay, okay... sheesh, what a grouch.")
goto("nowhere")
}
label("repo_house") {
npc(FacialExpression.ANGRY, "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(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(FacialExpression.ANGRY, "What? What houses? What ARE you talking about???")
player("Never mind.")
goto("nowhere")
}
label("want_house") {
npc(FacialExpression.ANGRY, "Oh... go away and stop wasting my time!")
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!!)
}
}
}

View file

@ -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};
}
}

View file

@ -0,0 +1,122 @@
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
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() {
//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.")
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")
}
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") {
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.")
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")
}
//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!");
}
}
}

View file

@ -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 };
}
}

View file

@ -0,0 +1,127 @@
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.getQuestStage
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() {
//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.");
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 (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.")
}
}
}
goto("nowhere")
}
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")
}
//because end() breaks shit
label("nowhere") {}
}
}
class FatherUrhneyQuestAmuletDialogueFile : DialogueLabeller() {
override fun addConversation() {
//mid or after finishing quest and needing another one or doing the drop trick
exec { player, npc ->
line("Father Urhney sighs.")
loadLabel(player, "amulet_main_flow")
}
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(FacialExpression.ANNOYED, "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")
}
}
}

View file

@ -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("<col=08088A>I can start this quest by speaking to <col=8A0808>Father Aereck</col> <col=08088A>in the", 275, 4+ 7);
player.getPacketDispatch().sendString("<col=8A0808>church</col> <col=08088A>next to <col=8A0808>Lumbridge Castle.<col=8A0808>", 275, 5+ 7);
player.getPacketDispatch().sendString("<col=08088A>I must be unafraid of a <col=8A0808>Level 13 Skeleton ", 275, 6+ 7);
}
if (stage == 10) {
player.getPacketDispatch().sendString("<str>Father Aereck asked me to help him deal with the Ghost in", 275, 4+ 7);
player.getPacketDispatch().sendString("<str>the graveyard next to the church.", 275, 5+ 7);
player.getPacketDispatch().sendString("<col=08088A>I should find <col=8A0808>Father Urhney</col><col=08088A> who is an expert on </col><col=8A0808>ghosts.</col> ", 275, 6+ 7);
player.getPacketDispatch().sendString("<col=08088A>He lives in a <col=8A0808>shack</col><col=08088A> in <col=8A0808>Lumbridge Swamp.", 275, 7+ 7);
}
if (stage == 20) {
player.getPacketDispatch().sendString("<str>Father Aereck asked me to help him deal with the Ghost in", 275, 4+ 7);
player.getPacketDispatch().sendString("<str>the graveyard next to the church.", 275, 5+ 7);
player.getPacketDispatch().sendString("<str>I should find Father Urhney who is an expert on ghosts.</str> ", 275, 6+ 7);
player.getPacketDispatch().sendString("<str>He lives in a shack in Lumbridge Swamp.</str>", 275, 7+ 7);
player.getPacketDispatch().sendString("<col=08088A>I should talk to the</col> <col=8A0808>Ghost</col> <col=08088A>to find out why it is haunting the", 275, 8+ 7);
player.getPacketDispatch().sendString("<col=8A0808>graveyard crypt", 275, 9+ 7);
}
if (stage == 30) {
player.getPacketDispatch().sendString("<str>Father Aereck asked me to help him deal with the Ghost in", 275, 4+ 7);
player.getPacketDispatch().sendString("<str>the graveyard next to the church.", 275, 5+ 7);
player.getPacketDispatch().sendString("<str>I found Father Urhney in the swamp south of Lumbridge.He</str> ", 275, 6+ 7);
player.getPacketDispatch().sendString("<str>gave me an Amulet of Ghostspeak to talk to the ghost.</str>", 275, 7+ 7);
player.getPacketDispatch().sendString("<str>I spoke to the Ghost and he told me he could not rest in</str>", 275, 8+ 7);
player.getPacketDispatch().sendString("<str>peace because an evil wizard had stolen his skull.</str>", 275, 9+ 7);
player.getPacketDispatch().sendString("<col=08088A>I should go and search the <col=8A0808>Wizard's Tower South West of", 275, 10+ 7);
player.getPacketDispatch().sendString("<col=8A0808>Lumbridge</col> <col=08088A>for the <col=8A0808>Ghost's Skull.", 275, 11+ 7);
}
if (stage == 40) {
player.getPacketDispatch().sendString("<str>Father Aereck asked me to help him deal with the Ghost in", 275, 4+ 7);
player.getPacketDispatch().sendString("<str>the graveyard next to the church.", 275, 5+ 7);
player.getPacketDispatch().sendString("<str>I found Father Urhney in the swamp south of Lumbridge.He</str> ", 275, 6+ 7);
player.getPacketDispatch().sendString("<str>gave me an Amulet of Ghostspeak to talk to the ghost.</str>", 275, 7+ 7);
player.getPacketDispatch().sendString("<str>I spoke to the Ghost and he told me he could not rest in</str>", 275, 8+ 7);
player.getPacketDispatch().sendString("<str>peace because an evil wizard had stolen his skull.</str>", 275, 9+ 7);
player.getPacketDispatch().sendString("<str>I found the Ghost's Skull in the basement of the Wizards'", 275, 10+ 7);
player.getPacketDispatch().sendString("<str>Tower. It was guarded by a skeleton, but I took it anyways.</str>", 275, 11+ 7);
player.getPacketDispatch().sendString("<col=08088A>I should take the <col=8A0808>Skull</col> <col=08088A>back to the <col=8A0808>Ghost</col> <col=08088A>so it can rest.", 275, 12+ 7);
}
if (stage == 100) {
player.getPacketDispatch().sendString("<str>Father Aereck asked me to help him deal with the Ghost in", 275, 4+ 7);
player.getPacketDispatch().sendString("<str>the graveyard next to the church.", 275, 5+ 7);
player.getPacketDispatch().sendString("<str>I found Father Urhney in the swamp south of Lumbridge.He</str> ", 275, 6+ 7);
player.getPacketDispatch().sendString("<str>gave me an Amulet of Ghostspeak to talk to the ghost.</str>", 275, 7+ 7);
player.getPacketDispatch().sendString("<str>I spoke to the Ghost and he told me he could not rest in</str>", 275, 8+ 7);
player.getPacketDispatch().sendString("<str>peace because an evil wizard had stolen his skull.</str>", 275, 9+ 7);
player.getPacketDispatch().sendString("<str>I found the Ghost's Skull in the basement of the Wizards'", 275, 10+ 7);
player.getPacketDispatch().sendString("<str>Tower. It was guarded by a skeleton, but I took it anyways.</str>", 275, 11+ 7);
player.getPacketDispatch().sendString("<str>I placed the Skull in the Ghost's coffin, and allowed it to", 275, 12+ 7);
player.getPacketDispatch().sendString("<str>rest in peace once more, with gratitude for my help.", 275, 13+ 7);
player.getPacketDispatch().sendString("<col=FF0000>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");
}
}

View file

@ -0,0 +1,102 @@
package content.region.misthalin.lumbridge.quest.therestlessghost
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.api.*
import content.data.Quests
import org.rs09.consts.Items
/**
* Represents the restless ghost quest.
* @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 {
return this
}
override fun drawJournal(player: Player, stage: Int) {
super.drawJournal(player, stage)
when (stage) {
0 -> {
player.packetDispatch.sendString("<col=08088A>I can start this quest by speaking to <col=8A0808>Father Aereck</col> <col=08088A>in the", 275, 4 + 7)
player.packetDispatch.sendString("<col=8A0808>church</col> <col=08088A>next to <col=8A0808>Lumbridge Castle.<col=8A0808>", 275, 5 + 7)
player.packetDispatch.sendString("<col=08088A>I must be unafraid of a <col=8A0808>Level 13 Skeleton.", 275, 6 + 7)
}
10 -> {
player.packetDispatch.sendString("<str>Father Aereck asked me to help him deal with the Ghost in", 275, 4 + 7)
player.packetDispatch.sendString("<str>the graveyard next to the church.", 275, 5 + 7)
player.packetDispatch.sendString("<col=08088A>I should find <col=8A0808>Father Urhney</col><col=08088A> who is an expert on </col><col=8A0808>ghosts.</col> ", 275, 6 + 7)
player.packetDispatch.sendString("<col=08088A>He lives in a <col=8A0808>shack</col><col=08088A> in <col=8A0808>Lumbridge Swamp.", 275, 7 + 7)
}
20 -> {
player.packetDispatch.sendString("<str>Father Aereck asked me to help him deal with the Ghost in", 275, 4 + 7)
player.packetDispatch.sendString("<str>the graveyard next to the church.", 275, 5 + 7)
player.packetDispatch.sendString("<str>I should find Father Urhney who is an expert on ghosts.</str> ", 275, 6 + 7)
player.packetDispatch.sendString("<str>He lives in a shack in Lumbridge Swamp.</str>", 275, 7 + 7)
player.packetDispatch.sendString("<col=08088A>I should talk to the</col> <col=8A0808>Ghost</col> <col=08088A>to find out why it is haunting the", 275, 8 + 7)
player.packetDispatch.sendString("<col=8A0808>graveyard crypt", 275, 9 + 7)
}
30 -> {
player.packetDispatch.sendString("<str>Father Aereck asked me to help him deal with the Ghost in", 275, 4 + 7)
player.packetDispatch.sendString("<str>the graveyard next to the church.", 275, 5 + 7)
player.packetDispatch.sendString("<str>I found Father Urhney in the swamp south of Lumbridge.He</str> ", 275, 6 + 7)
player.packetDispatch.sendString("<str>gave me an Amulet of Ghostspeak to talk to the ghost.</str>", 275, 7 + 7)
player.packetDispatch.sendString("<str>I spoke to the Ghost and he told me he could not rest in</str>", 275, 8 + 7)
player.packetDispatch.sendString("<str>peace because an evil wizard had stolen his skull.</str>", 275, 9 + 7)
player.packetDispatch.sendString("<col=08088A>I should go and search the <col=8A0808>Wizard's Tower South West of", 275, 10 + 7)
player.packetDispatch.sendString("<col=8A0808>Lumbridge</col> <col=08088A>for the <col=8A0808>Ghost's Skull.", 275, 11 + 7)
}
40 -> {
player.packetDispatch.sendString("<str>Father Aereck asked me to help him deal with the Ghost in", 275, 4 + 7)
player.packetDispatch.sendString("<str>the graveyard next to the church.", 275, 5 + 7)
player.packetDispatch.sendString("<str>I found Father Urhney in the swamp south of Lumbridge.He</str> ", 275, 6 + 7)
player.packetDispatch.sendString("<str>gave me an Amulet of Ghostspeak to talk to the ghost.</str>", 275, 7 + 7)
player.packetDispatch.sendString("<str>I spoke to the Ghost and he told me he could not rest in</str>", 275, 8 + 7)
player.packetDispatch.sendString("<str>peace because an evil wizard had stolen his skull.</str>", 275, 9 + 7)
player.packetDispatch.sendString("<str>I found the Ghost's Skull in the basement of the Wizards'", 275, 10 + 7)
player.packetDispatch.sendString("<str>Tower. It was guarded by a skeleton, but I took it anyways.</str>", 275, 11 + 7)
player.packetDispatch.sendString("<col=08088A>I should take the <col=8A0808>Skull</col> <col=08088A>back to the <col=8A0808>Ghost</col> <col=08088A>so it can rest.", 275, 12 + 7)
}
100 -> {
player.packetDispatch.sendString("<str>Father Aereck asked me to help him deal with the Ghost in", 275, 4 + 7)
player.packetDispatch.sendString("<str>the graveyard next to the church.", 275, 5 + 7)
player.packetDispatch.sendString("<str>I found Father Urhney in the swamp south of Lumbridge.He</str> ", 275, 6 + 7)
player.packetDispatch.sendString("<str>gave me an Amulet of Ghostspeak to talk to the ghost.</str>", 275, 7 + 7)
player.packetDispatch.sendString("<str>I spoke to the Ghost and he told me he could not rest in</str>", 275, 8 + 7)
player.packetDispatch.sendString("<str>peace because an evil wizard had stolen his skull.</str>", 275, 9 + 7)
player.packetDispatch.sendString("<str>I found the Ghost's Skull in the basement of the Wizards'", 275, 10 + 7)
player.packetDispatch.sendString("<str>Tower. It was guarded by a skeleton, but I took it anyways.</str>", 275, 11 + 7)
player.packetDispatch.sendString("<str>I placed the Skull in the Ghost's coffin, and allowed it to", 275, 12 + 7)
player.packetDispatch.sendString("<str>rest in peace once more, with gratitude for my help.", 275, 13 + 7)
player.packetDispatch.sendString("<col=FF0000>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)
//post quest skeleton scenery and altar visually correct
setVarbit(player, 2128, 1, true)
setVarbit(player, 2130, 1, true)
//setVarp(player, 728, 31, true)
}
}

View file

@ -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 };
}
}

View file

@ -0,0 +1,276 @@
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
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
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
/**
* 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 {
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))
restlessGhostNPC.setAttribute("spawned:npc", true)
restlessGhostNPC.isRespawn = false
restlessGhostNPC.direction = Direction.SOUTH
restlessGhostNPC.init()
restlessGhostNPC.isWalks = false
restlessGhostNPC.isNeverWalks = true
return restlessGhostNPC
}
//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
}
})
}
}
private fun createLevel13SkeletonNPC(player: Player) : NPC {
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
}
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(CLOSED_COFFIN, 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)) {
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
// 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
}
})
}
}
return@on true
}
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)
toggleCoffinSceneryObj(player, node as core.game.node.scenery.Scenery)
return@on true
}
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(INCOMPLETE_COFFIN_OPEN, 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, INCOMPLETE_COFFIN_OPEN) { player, used, with ->
if (removeItem(player, used)) {
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(COMPLETE_COFFIN_OPEN))
}
return@onUseWith true
}
on(ALTAR_WITH_SKULL, IntType.SCENERY, "search") { player, node ->
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)
//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!")
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
}
}
}

View file

@ -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)) {
loadLabel(player, "woo")
} else {
when (getQuestStage(player, Quests.THE_RESTLESS_GHOST)) {
20 -> {
loadLabel(player, "not_very_good")
}
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)) {
loadLabel(player, "found_it")
} else {
loadLabel(player, "not_found_it")
}
}
}
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_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.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_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.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)
loadLabel(player, "great_relief")
}
}
label("great_relief") {
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") {
npc(ChatAnim.HALF_GUILTY, "Wooo wooo wooooo!")
goto("no_amulet_opts")
}
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.")
}
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?")
}
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.")
}
}
}

View file

@ -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<Object> 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;
}
}
}

View file

@ -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<Object> 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;
}
}

View file

@ -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<Object> 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;
}
}