Merge branch 'spirit-of-the-elid' into 'master'

Draft: Spirit of the Elid Quest

See merge request 2009scape/2009scape!2032
This commit is contained in:
Oven Bread 2025-10-19 12:40:48 +00:00
commit b403cb0ba0
6 changed files with 555 additions and 2 deletions

View file

@ -8,7 +8,7 @@ import core.plugin.Initializable;
* Handles Awusah pre-quest
* @author ceik
*/
@Initializable
public class Awusah extends DialoguePlugin {
//TODO: Add dialogue for after the quest Spirits of Elid
public Awusah(){

View file

@ -10,7 +10,7 @@ import core.game.node.entity.player.Player;
*/
//TODO: Add post-quest dialogue
@Initializable
public class Ghaslor extends DialoguePlugin {
public Ghaslor(){
/**

View file

@ -0,0 +1,112 @@
package content.region.desert.quest.spiritsoftheelid
import core.api.*
import core.game.dialogue.*
import core.game.node.entity.player.Player
import core.plugin.Initializable
import org.rs09.consts.NPCs
@Initializable
class AwusahDialogue (player: Player? = null) : DialoguePlugin(player) {
override fun newInstance(player: Player): DialoguePlugin {
return AwusahDialogue(player)
}
override fun handle(interfaceId: Int, buttonId: Int): Boolean {
openDialogue(player, AwusahDialogueFile(), npc)
return false
}
override fun getIds(): IntArray {
return intArrayOf(NPCs.AWUSAH_THE_MAYOR_3040)
}
}
class AwusahDialogueFile : DialogueLabeller() {
override fun addConversation() {
assignToIds(NPCs.AWUSAH_THE_MAYOR_3040)
exec { player, npc ->
loadLabel(player, "spiritsoftheelidqueststage" + getQuestStage(player, SpiritsOfTheElid.questName))
}
label("spiritsoftheelidqueststage0")
npc(ChatAnim.HAPPY, "Many salutations to you @g[sir,madam], what brings you to", "our little town?")
options(
DialogueOption("searchingquest", "I am an adventurer in search of quests.", expression = ChatAnim.HAPPY),
DialogueOption("justlooking", "I'm just looking around."),
DialogueOption("menaphos", "I'm passing through on my way to Menaphos."),
)
label("justlooking")
player("I'm just looking around.")
npc("Well we've not fallen on the best of times here, but there are a few shops to look around and we have someone selling a marvelous new invention called a choc ice..")
npc("Oh, and there's the old temple if you're interested in a bit of history.")
label("menaphos") {
player("I'm passing through on my way to Menaphos.")
npc("Oh you don't want to go there. I hear rumours of plagues and the like.")
player("Thanks for the warning, but I might go and investigate anyway.")
}
label("searchingquest") {
npc("Hmm, well that could be quite fortuitous. We really", "could do with some help here. Our town seems to have", "come under a curse, and we are desperate to get it", "removed.")
player(ChatAnim.THINKING, "A curse? What sort of curse?")
npc(ChatAnim.SAD, "Well first of all our fountain, which has served us for", "many years, suddenly dried up. We then started", "transporting water from the river, but what we carried", "would always have completely dried up by the time we")
npc(ChatAnim.SAD, "got it back to the village.")
goto("moredetails")
}
label("moredetails") {
options(
DialogueOption("whatwater", "So what do you do for water now?", expression = ChatAnim.THINKING),
DialogueOption("whycursed", "Any idea how you got this curse?", expression = ChatAnim.THINKING),
DialogueOption("lolnop", "I'm sorry to hear of your plight, but I must be going."),
)
}
label("whatwater") {
npc(ChatAnim.SAD, "Well it's tough. There is a guy who calls himself Ali the", "carter down in the market. He's from Pollnivneach, so", "the curse doesn't seem to have affected him. He's got a", "cart full of water which he's selling to the people here.")
npc(ChatAnim.SAD, "Unfortunately in typical Pollnivneach style he's taking", "the opportunity to sell the water at outrageous prices", "because he knows we are desperate. Oh and Rokuh has", "developed something quite remarkable, called a choc ice")
npc(ChatAnim.SAD, "which is helping people stay cool.")
options(
DialogueOption("whycursed", "Any idea how you got this curse?", expression = ChatAnim.THINKING),
DialogueOption("lolnop", "I'm sorry to hear of your plight, but I must be going."),
)
}
label("whycursed")
npc("Well we think it was because we were negligent in our", "duty to protect a priest of Saradomin. He came to us a", "few weeks ago from the north and we welcomed him", "into our town. He taught us much of the wisdom of")
npc("Saradomin. Then about a week ago, we found him dead", "by the river. It seems unlikely that it is coincidence we", "were then cursed. If you look around the town you", "might be able to find out more.")
options(
DialogueOption("startquest", "Ok I will have a look around and see what I can do.", expression = ChatAnim.FRIENDLY),
DialogueOption("whatwater", "So what do you do for water now?", expression = ChatAnim.THINKING),
DialogueOption("lolnop", "I'm sorry to hear of your plight, but I must be going."),
)
label("startquest")
exec { player, _ ->
if(getQuestStage(player, SpiritsOfTheElid.questName) == 0) {
setQuestStage(player, SpiritsOfTheElid.questName, 10)
}
}
npc(ChatAnim.FRIENDLY, "Good luck to you.")
label("spiritsoftheelidqueststage10")
npc("Many salutations to you @g[sir,madam], have you made any progress in uncovering the reasons for our curse yet?")
options(
DialogueOption("workingonit", "I am working on it."),
DialogueOption("whattodoagain", "What was I meant to do again?", expression = ChatAnim.THINKING),
DialogueOption("notimetoinvestigate", "I haven't really had the time to investigate."),
)
label("workingonit")
npc("Please try your best, that water salesman is not doing much good for the economy of our town.")
label("whattodoagain")
npc("Look around this town, see if you can uncover any information to why we were cursed and how the curse might be lifted. I suspect that it might be because a priest of Saradomin died while staying at our town, but")
npc("that isn't much help in getting the curse lifted and it is possible I'm wrong anyway.")
label("notimetoinvestigate")
npc("Well I hope somebody can find the time soon, the despair of the people of this town increases daily.")
}
}

View file

@ -0,0 +1,84 @@
package content.region.desert.quest.spiritsoftheelid
import core.api.*
import core.game.dialogue.*
import core.game.node.entity.player.Player
import core.game.node.item.Item
import core.plugin.Initializable
import org.rs09.consts.Items
import org.rs09.consts.NPCs
@Initializable
class GhaslorDialogue (player: Player? = null) : DialoguePlugin(player) {
override fun newInstance(player: Player): DialoguePlugin {
return GhaslorDialogue(player)
}
override fun handle(interfaceId: Int, buttonId: Int): Boolean {
openDialogue(player, GhaslorDialogueFile(), npc)
return false
}
override fun getIds(): IntArray {
return intArrayOf(NPCs.GHASLOR_THE_ELDER_3029)
}
}
class GhaslorDialogueFile : DialogueLabeller() {
override fun addConversation() {
assignToIds(NPCs.GHASLOR_THE_ELDER_3029)
exec { player, npc ->
loadLabel(player, "spiritsoftheelidqueststage" + getQuestStage(player, SpiritsOfTheElid.questName))
}
label("spiritsoftheelidqueststage0")
player("Hello, I'm an adventurer in search of err.. adventure.")
npc("Well I'm Ghaslor, the oldest person in this here town and keeper of some of the lores and histories of our village. If you're new around here I'd go talk to the mayor.")
player("Thanks, I might go and look him up then.")
label("spiritsoftheelidqueststage10")
npc("Good day to you young @g[man,lady].")
options(
DialogueOption("causeofcurse", "I am trying to find out the cause of this town's curse.", expression = ChatAnim.THINKING),
DialogueOption("howarethings", "How are things?"),
)
label("howarethings")
npc("Lousy, young people have no respect for their elders, there's too many confusing newfangled magic carpets and snake charmers around these days and now we don't even have any water.")
player("Well I hope things pick up for you.")
label("causeofcurse")
npc("Oh that's something which no one but the gods and the river spirits seem to know.")
options(
DialogueOption("whycursed", "River spirits, what are they?", expression = ChatAnim.THINKING),
DialogueOption("lolnop", "Thank you very much for your help."),
)
label("whycursed")
player("River spirits, what are they?")
npc("They're the guardians of the source of the river Elid, they'll know of our curse, seeing as it's related to water we bring from the river. It's said that nothing happens on the river that they don't know about.")
player("So would it be possible for me to find these spirits and ask them what is going on?")
npc("Ahh now the spirits are something which no one from here have seen in a good long time, certainly not in my lifetime, I don't think even in my own grandfather's lifetime.")
npc("Most the younger people of this town don't even remember that the spirits even exist.")
exec { player, _ ->
if(inInventory(player, Items.BALLAD_6793)) {
loadLabel(player, "alreadyhaveballad")
} else {
loadLabel(player, "hereisballad")
}
}
label("hereisballad")
npc("Here I have something around here somewhere which may be of interest to you.")
npc("Aha yes here it is, the ballad of Jareesh. This tells of a previous quest to try and find the spirits.")
exec { player, _ ->
setAttribute(player, SpiritsOfTheElid.attributeObtainedBallad, true)
addItemOrDrop(player, Items.BALLAD_6793)
}
item(Item(Items.BALLAD_6793), "Ghaslor hands you a scroll.")
label("alreadyhaveballad")
npc("You already have the ballad of Jareesh, which tells of a previous quest to try and find the spirits.")
player("Thank you very much for your help.")
}
}

View file

@ -0,0 +1,228 @@
package content.region.desert.quest.spiritsoftheelid
import core.api.*
import core.game.node.entity.player.Player
import core.game.node.entity.player.link.quest.Quest
import core.game.node.entity.skill.Skills
import core.plugin.Initializable
import org.rs09.consts.Items
/**
* Spirit of the Elid Quest
*
* The quest log is as close as I can get due to lots of videos not being interested in this quest.
*/
@Initializable
class SpiritsOfTheElid : Quest("Spirits of the Elid", 116, 115, 2,616, 1444, 0, 1, 60) {
companion object {
const val questName = "Spirits of the Elid"
const val attributeObtainedBallad = "/save:quest:spiritsoftheelid-obtainedballad"
const val attributeTalkedToCustodian = "/save:quest:spiritsoftheelid-talkedtocustodian"
const val attributeCastGrabForKey = "/save:quest:spiritsoftheelid-castgrabforkey"
const val attributeFoundRobeTop = "/save:quest:spiritsoftheelid-foundrobetop"
const val attributeFoundRobeBottom = "/save:quest:spiritsoftheelid-foundrobebottom"
const val attributeClimbedRiver = "/save:quest:spiritsoftheelid-climbedriver"
}
override fun drawJournal(player: Player, stage: Int) {
super.drawJournal(player, stage)
var line = 12
var stage = getStage(player)
var started = getQuestStage(player, questName) > 0
if (!started) {
line(player, "I can start this quest by speaking to !!Awusah?? who is the", line++, false)
line(player, "mayor in the desert town of !!Nardah??.", line++, false)
line++
line(player, "To complete this quest I will need:", line++, false)
line(player, "!!Level 33 Magic.??", line++, hasLevelStat(player, Skills.MAGIC, 33))
line(player, "!!Level 37 Ranged.??", line++, hasLevelStat(player, Skills.RANGE, 37))
line(player, "!!Level 37 Mining.??", line++, hasLevelStat(player, Skills.MINING, 37))
line(player, "!!Level 37 Thieving.??", line++, hasLevelStat(player, Skills.THIEVING, 37))
line(player, "I must be able to !!defeat three level 75 enemies.", line++, false)
} else {
line(player, "I spoke to Awusah the mayor of Nardah.", line++, true)
if (stage >= 20) {
line++
line(player, "Awusah asked me to look around Nardah for clues as to", line++, true)
line(player, "why the town's water source had dried up.", line++, true)
line++
line(player, "Ghastor the village elder gave me an item called the Ballad", line++, true)
line(player, "of Jaressh that seems to contain lots of clues.", line++, true)
line++
line(player, "I worked out that I needed to enter a cave at the source of", line++, true)
line(player, "the river Elid.", line++, true)
line++
line(player, "I used an ancestral key and wore some robes of Eldinis", line++, true) // Eldinis is sic
line(player, "that I had found in the shrine in Nardah to get through", line++, true)
line(player, "some double doors in the cave.", line++, true)
} else if (stage >= 10) {
line++
line(player, "Awusah tells me that the !!towns water supply?? has dried up.", line++, false)
line(player, "I should look around town to see if I can find any clues as", line++, false)
line(player, "to what can be done about this.", line++, false)
line++
// Following sections appear as you do it. No line spaces.
// https://www.youtube.com/watch?v=P1l03jKSZGw
if (getAttribute(player, attributeObtainedBallad, false)) {
line(player, "Ghastor the village elder gave me !!The Ballad of Jaressh??", line++, false)
line(player, "which provides some useful information about a previous", line++, false)
line(player, "trip to see some !!river spirits??.", line++, false)
}
if (getAttribute(player, attributeTalkedToCustodian, false)) {
line(player, "The shrine custodian let slip that the !!ancestral key?? she", line++, false)
line(player, "had on display is for the chambers of the !!Spirits of the??", line++, false)
line(player, "!!Elid??.", line++, false)
}
if (getAttribute(player, attributeCastGrabForKey, false)) {
line(player, "I cast telekinetic grab to steal the !!ancestral key?? from the", line++, false)
line(player, "shrine.", line++, false)
}
if (getAttribute(player, attributeFoundRobeTop, false)) {
line(player, "I found a !!robe of Elidinis top?? in a cupboard and repaired it", line++, false)
line(player, "with a needle and thread.", line++, false)
}
if (getAttribute(player, attributeFoundRobeBottom, false)) {
line(player, "I found a !!robe of Elidinis bottom?? in a cupboard and", line++, false)
line(player, "repaired it with a needle and thread.", line++, false)
}
if (getAttribute(player, attributeClimbedRiver, false)) {
// http://youtu.be/vZ5P08cGSbA
line(player, "I managed to climb up to a !!cave at the source of the river??.", line++, false)
line(player, "Further progress is blocked by a !!door??.", line++, false)
}
}
if (stage >= 30) {
line++
line(player, "I killed 3 golems and unblocked three waterchannels in", line++, true)
line(player, "various ways. This allowed me to get through another pair", line++, true)
line(player, "of double doors.", line++, true)
line++
line(player, "I have spoken to the Spirits of the Elid.", line++, true)
} else if (stage >= 20) {
// http://youtu.be/vZ5P08cGSbA
line++
line(player, "I have come to a second pair of !!double doors?? I need to get", line++, false)
line(player, "through. There are !!3 single doors here too.", line++, false)
line++
if (getAttribute(player, attributeClimbedRiver, false)) {
line(player, "I have not got through the most northern single door yet.", line++, false)
} else {
// Segment
line(player, "I have defeated a black golem.", line++, true)
line(player, "I shot a target and unblocked a waterchannel.", line++, true)
}
line++
if (getAttribute(player, attributeClimbedRiver, false)) {
line(player, "I have not got through the central single door yet.", line++, false)
} else {
line(player, "I have defeated a grey golem.", line++, true)
line(player, "I mined away some rocks and unblocked a waterchannel.", line++, true)
}
line++
if (getAttribute(player, attributeClimbedRiver, false)) {
line(player, "I have not got through the most southern single door yet.", line++, false)
} else {
line(player, "I have defeated a white golem.", line++, true)
line(player, "I disabled a trap and unblocked a waterchannel.", line++, true)
}
}
// http://youtu.be/QgiKtgo8toE
if (stage >= 40) {
// For weird reasons "statuette" becomes "statue". http://youtu.be/SWjuatbJvxI 12:35
line++
line(player, "This spirits told me the townspeople of Nardah threw away", line++, true)
line(player, "a statue of Elidinis. As a result they were cursed to have", line++, true)
line(player, "no water. As long as they do not have the statue the", line++, true)
line(player, "curse will stand.", line++, true)
} else if (stage >= 30) {
line++
line(player, "This spirits tell me the townspeople of Nardah threw away a", line++, false)
line(player, "!!statuette of Elidinis??. As a result they were cursed to have", line++, false)
line(player, "no water. As long as they do not have the statuette the", line++, false)
line(player, "curse will stand.", line++, false)
}
if (stage >= 50) {
line++
line(player, "I spoke to Awusah again. He told me the statuette was", line++, true)
line(player, "thrown down a crevice to the west of Nardah.", line++, true)
line++
line(player, "I met a genie down the crevice. He told me that he had", line++, true)
line(player, "the statuette which he would give it to me exchange", line++, true)
line(player, "for the sole of Awusah!", line++, true)
line++
line(player, "I have cut the soles from Awusah's shoes. Fortunately", line++, true)
line(player, "the genie wanted Awusah's sole and not Awusah's soul.", line++, true) // Authentically, its sole not soles
line(player, "We have completed the trade.", line++, true)
} else if (stage >= 40) {
// VivJYfgyshU
line++
line(player, "I spoke to Awusah again. He told me the !!statuette?? was", line++, false)
line(player, "thrown down a !!crevice to the west of Nardah??.", line++, false)
// Derived foot fetish
line++
line(player, "I met a genie down the crevice. He told me that he had", line++, false)
line(player, "the statuette which he would give it to me exchange", line++, false)
line(player, "for the sole of Awusah!", line++, false)
// Derived
line++
line(player, "I have cut the soles from Awusah's shoes. Fortunately", line++, false)
line(player, "the genie wanted Awusah's sole and not Awusah's soul.", line++, false) // Authentically, its sole not soles
line(player, "We have completed the trade.", line++, false)
}
// http://youtu.be/VivJYfgyshU
if (stage >= 100) {
line++
line(player, "I have returned the statuette to the shrine of Elidinis.", line++, true)
line(player, "The curse is lifted and the town has its water supply back.", line++, true)
} else if (stage >= 50) {
line++
line(player, "I have !!the statuette of Elidinis?? I should return it to its", line++, false)
line(player, "rightful place.", line++, false)
}
if (stage >= 100) {
line++
line(player,"<col=FF0000>QUEST COMPLETE!</col>", line)
}
}
}
override fun reset(player: Player) {
removeAttribute(player, attributeObtainedBallad)
removeAttribute(player, attributeTalkedToCustodian)
removeAttribute(player, attributeCastGrabForKey)
removeAttribute(player, attributeFoundRobeTop)
removeAttribute(player, attributeFoundRobeBottom)
removeAttribute(player, attributeClimbedRiver)
}
override fun finish(player: Player) {
var ln = 10
super.finish(player)
player.packetDispatch.sendString("You have completed the Spirits of the Elid Quest!!", 277, 4)
player.packetDispatch.sendItemZoomOnInterface(Items.STATUETTE_6785,230,277,5)
drawReward(player, "2 Quest Points", ln++)
drawReward(player, "Use of the shrine to pray at.", ln++)
drawReward(player, "8,000 Prayer XP", ln++)
drawReward(player, "1,000 Thieving XP", ln++)
drawReward(player, "1,000 Magic XP", ln++)
rewardXP(player, Skills.PRAYER, 8000.0)
rewardXP(player, Skills.THIEVING, 1000.0)
rewardXP(player, Skills.MAGIC, 1000.0)
}
override fun newInstance(`object`: Any?): Quest {
return this
}
}

View file

@ -0,0 +1,129 @@
package content.region.desert.quest.spiritsoftheelid
import core.api.*
import core.game.global.action.DoorActionHandler
import core.game.interaction.IntType
import core.game.interaction.InteractionListener
import org.rs09.consts.Items
import org.rs09.consts.Scenery
class SpiritsOfTheElidListeners : InteractionListener {
override fun defineListeners() {
on(Items.BALLAD_6793, ITEM, "read") { player, node ->
openInterface(player, 116)
player.packetDispatch.sendString("The Ballad of Jareesh",116, 2)
player.packetDispatch.sendString("Clad all in the robes for She<br>" +
"we travelled north expediently.<br>" +
"<br>" +
"Go beseech the guardians we were sent,<br>" +
"so to the river's source we went.<br>" +
"<br>" +
"Through the cave we had to travel.<br>" +
"There was mystery to unravel.<br>" +
"<br>" +
"We came upon a great stone door<br>" +
"but our priestess knew the score.<br>" +
"<br>" +
"She used the great ancestral key.<br>" +
"We passed through very easily.<br>" +
"<br>" +
"Three great men all hewn of rock,<br>" +
"three small chambers they did block.<br>" +
"<br>" +
"The first all made of rock quite black,<br>" +
"our fighting men went to attack.<br>" +
"<br>" +
"They went with arrow, blade and spell,<br>" +
"but the man they could not fell<br>" +
"<br>" +
"Many men did fall that day,<br>" +
"while others turned and ran away<br>" +
"<br>" +
"I had little hope at all,<br>" +
"as I watched my kinsmen fall.<br>" +
"<br>" +
"My warhammer I boldy heft<br>" +
"and struck a blow which while was deft,<br>" +
"<br>" +
"should give scant harm to one so strong<br>" +
"or so I thought, nay I was wrong.<br>" +
"<br>" +
"The rockman fell down with a crash,<br>" +
"which sent up clouds of dust and ash.<br>" +
"<br>" +
"Just myself left and alas,<br>" +
"two more rockmen left to pass.<br>" +
"<br>" +
"Still buoyed on by my first success<br>" +
"onwards bravely I did press.<br>" +
"<br>" +
"Next man was of rock hued grey<br>" +
"and this one next I planned to slay<br>" +
"<br>" +
"Crushing blows I rained on him,<br>" +
"but things were looking very grim<br>" +
"<br>" +
"Not a scratch could I make<br>" +
"Anon my limbs began to ache.<br>" +
"<br>" +
"The rockman gave a mighty roar<br>" +
"so fierce I made fast for the door.<br>" +
"<br>" +
"How to beat that second man<br>" +
"to this day I have no plan.<br>" +
"<br>" +
"My hammer never left a dent.<br>" +
"I never reached whence I was sent.",
116,
5)
return@on true
}
// This cupboard has both.
on(Scenery.CUPBOARD_10385, SCENERY, "search") { player, node ->
if (getQuestStage(player, SpiritsOfTheElid.questName) >= 1) {
sendItemDialogue(player, Items.TORN_ROBE_6788 ,"You find a torn robe top.")
addItemOrDrop(player, Items.TORN_ROBE_6788)
sendItemDialogue(player, Items.TORN_ROBE_6789 ,"You find some torn robe bottoms.")
addItemOrDrop(player, Items.TORN_ROBE_6789)
}
return@on true
}
on(Items.TORN_ROBE_6788, ITEM, "wear") { player, node ->
sendDialogue(player,"You need some Thread to repair the robe.")
return@on true
}
on(Items.TORN_ROBE_6789, ITEM, "wear") { player, node ->
sendDialogue(player, "You need some Thread to repair the robe.")
return@on true
}
onUseWith(IntType.ITEM, Items.TORN_ROBE_6788, Items.NEEDLE_1733) { player, used, with ->
if(removeItem(player, used.asItem())) {
sendItemDialogue(player, Items.ROBE_OF_ELIDINIS_6786 ,"You mend the robes.")
return@onUseWith addItem(player, Items.ROBE_OF_ELIDINIS_6786)
}
return@onUseWith false
}
onUseWith(IntType.ITEM, Items.TORN_ROBE_6789, Items.NEEDLE_1733) { player, used, with ->
if(removeItem(player, used.asItem())) {
sendItemDialogue(player, Items.ROBE_OF_ELIDINIS_6787 ,"You mend the robes.")
return@onUseWith addItem(player, Items.ROBE_OF_ELIDINIS_6787)
}
return@onUseWith false
}
on(Scenery.DOOR_10431, SCENERY, "open") { player, node ->
// Door to Genie
// http://youtu.be/SWjuatbJvxI
if (getQuestStage(player, SpiritsOfTheElid.questName) >= 4) {
DoorActionHandler.handleAutowalkDoor(player, node as core.game.node.scenery.Scenery)
} else {
sendDialogueLines(player, "There seems to be a strange magical force preventing you from", "opening the door.")
}
return@on true
}
}
}