mirror of
https://gitlab.com/2009scape/2009scape.git
synced 2025-12-09 16:45:44 -07:00
Implemented Creature of Fenkenstrain quest
This commit is contained in:
parent
8316a492c9
commit
0eecab4057
23 changed files with 1857 additions and 240 deletions
|
|
@ -58965,5 +58965,25 @@
|
|||
"maxAmount": "1"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"charm": [],
|
||||
"ids": "1676",
|
||||
"description": "Experiment (level 51)",
|
||||
"default": [
|
||||
{
|
||||
"minAmount": "1",
|
||||
"weight": "1.0",
|
||||
"id": "526",
|
||||
"maxAmount": "1"
|
||||
},
|
||||
{
|
||||
"minAmount": "1",
|
||||
"weight": "1.0",
|
||||
"id": "4184",
|
||||
"maxAmount": "1"
|
||||
}
|
||||
],
|
||||
"main": []
|
||||
}
|
||||
]
|
||||
|
|
@ -547,6 +547,10 @@
|
|||
"item_id": "4707",
|
||||
"loc_data": "{1,3571,3312,0,3932200}"
|
||||
},
|
||||
{
|
||||
"item_id": "4199",
|
||||
"loc_data": "{1,3492,3474,0,0}"
|
||||
},
|
||||
{
|
||||
"item_id": "5523",
|
||||
"loc_data": "{1,2935,3282,1,7209050}"
|
||||
|
|
|
|||
|
|
@ -38234,7 +38234,7 @@
|
|||
"id": "4189"
|
||||
},
|
||||
{
|
||||
"examine": "See article",
|
||||
"examine": "A typical garden brush.",
|
||||
"durability": null,
|
||||
"name": "Garden brush",
|
||||
"weight": "1",
|
||||
|
|
@ -38242,7 +38242,7 @@
|
|||
"id": "4190"
|
||||
},
|
||||
{
|
||||
"examine": "See article",
|
||||
"examine": "A typical garden brush, with a cane tied to it.",
|
||||
"durability": null,
|
||||
"name": "Extended brush",
|
||||
"weight": "1",
|
||||
|
|
@ -38250,7 +38250,7 @@
|
|||
"id": "4191"
|
||||
},
|
||||
{
|
||||
"examine": "See article",
|
||||
"examine": "A typical garden brush, with two canes tied to it.",
|
||||
"durability": null,
|
||||
"name": "Extended brush",
|
||||
"weight": "1",
|
||||
|
|
@ -38258,7 +38258,7 @@
|
|||
"id": "4192"
|
||||
},
|
||||
{
|
||||
"examine": "See article",
|
||||
"examine": "A typical garden brush, with three canes tied to it.",
|
||||
"durability": null,
|
||||
"name": "Extended brush",
|
||||
"weight": "1",
|
||||
|
|
|
|||
|
|
@ -3827,6 +3827,26 @@
|
|||
"npc_id": "1658",
|
||||
"loc_data": "{2595,3087,1,1,1}"
|
||||
},
|
||||
{
|
||||
"npc_id": "1668",
|
||||
"loc_data": "{3551,3550,0,1,0}"
|
||||
},
|
||||
{
|
||||
"npc_id": "1669",
|
||||
"loc_data": "{3549,3555,2,0,3}"
|
||||
},
|
||||
{
|
||||
"npc_id": "1671",
|
||||
"loc_data": "{3547,3555,2,0,6}"
|
||||
},
|
||||
{
|
||||
"npc_id": "1672",
|
||||
"loc_data": "{3552,3550,0,1,0}"
|
||||
},
|
||||
{
|
||||
"npc_id": "1675",
|
||||
"loc_data": "{3551,3561,0,1,0}"
|
||||
},
|
||||
{
|
||||
"npc_id": "1679",
|
||||
"loc_data": "{2291,3145,0,1,2}"
|
||||
|
|
|
|||
|
|
@ -1,14 +1,17 @@
|
|||
package content.region.desert.quest.thegolem
|
||||
|
||||
import core.api.*
|
||||
import core.game.dialogue.DialoguePlugin
|
||||
import core.game.node.entity.npc.NPC
|
||||
import core.game.node.entity.player.Player
|
||||
import core.plugin.Initializable
|
||||
import org.rs09.consts.NPCs
|
||||
import core.game.dialogue.DialogueBuilder
|
||||
import core.game.dialogue.DialogueBuilderFile
|
||||
|
||||
@Initializable
|
||||
public final class ClayGolemDialoguePlugin(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) {
|
||||
override fun newInstance(player: Player): core.game.dialogue.DialoguePlugin {
|
||||
public final class ClayGolemDialoguePlugin(player: Player? = null) : DialoguePlugin(player) {
|
||||
override fun newInstance(player: Player): DialoguePlugin {
|
||||
return ClayGolemDialoguePlugin(player)
|
||||
}
|
||||
override fun open(vararg objects: Any?): Boolean {
|
||||
|
|
@ -24,8 +27,8 @@ public final class ClayGolemDialoguePlugin(player: Player? = null) : core.game.d
|
|||
}
|
||||
}
|
||||
|
||||
class ClayGolemDialogueFile : core.game.dialogue.DialogueBuilderFile() {
|
||||
override fun create(b: core.game.dialogue.DialogueBuilder) {
|
||||
class ClayGolemDialogueFile : DialogueBuilderFile() {
|
||||
override fun create(b: DialogueBuilder) {
|
||||
val opt1 = b.onQuestStages("The Golem", 0)
|
||||
.npc("Damage... severe...", "task... incomplete...")
|
||||
.options()
|
||||
|
|
@ -33,7 +36,7 @@ class ClayGolemDialogueFile : core.game.dialogue.DialogueBuilderFile() {
|
|||
.optionIf("Shall I try to repair you?") { player -> return@optionIf player.questRepository.getQuest("The Golem").hasRequirements(player) }
|
||||
.playerl("Shall I try to repair you?")
|
||||
.npcl("Repairs... needed...")
|
||||
.endWith(){ player -> if (player.questRepository.getStage("The Golem") < 1 ) { setQuestStage(player, "The Golem", 1) } }
|
||||
.endWith(){ _, player -> if (player.questRepository.getStage("The Golem") < 1 ) { setQuestStage(player, "The Golem", 1) } }
|
||||
opt1
|
||||
.option("I'm not going to find a conversation here!")
|
||||
.playerl("I'm not going to find a conversation here!")
|
||||
|
|
@ -49,7 +52,7 @@ class ClayGolemDialogueFile : core.game.dialogue.DialogueBuilderFile() {
|
|||
.npcl("A great demon. It broke through from its dimension to attack the city.")
|
||||
.npcl("The golem army was created to fight it. Many were destroyed, but we drove the demon back!")
|
||||
.npcl("The demon is still wounded. You must open the portal so that I can strike the final blow and complete my task.")
|
||||
.endWith() { player -> setQuestStage(player, "The Golem", 3) }
|
||||
.endWith() { _, player -> setQuestStage(player, "The Golem", 3) }
|
||||
b.onQuestStages("The Golem", 3)
|
||||
.npcl("The demon is still wounded. You must open the portal so that I can strike the final blow and complete my task.")
|
||||
.end()
|
||||
|
|
@ -59,32 +62,32 @@ class ClayGolemDialogueFile : core.game.dialogue.DialogueBuilderFile() {
|
|||
.npcl("The demon must be defeated...")
|
||||
.playerl("No, you don't understand. I saw the demon's skeleton. It must have died of its wounds.")
|
||||
.npcl("Demon must be defeated! Task incomplete.")
|
||||
.endWith() { player -> setQuestStage(player, "The Golem", 5) }
|
||||
.endWith() { _, player -> setQuestStage(player, "The Golem", 5) }
|
||||
b.onQuestStages("The Golem", 5)
|
||||
.npcl("Task incomplete.")
|
||||
.playerl("Oh, how am I going to convince you?")
|
||||
.endWith() { player -> setQuestStage(player, "The Golem", 6) }
|
||||
.endWith() { _, player -> setQuestStage(player, "The Golem", 6) }
|
||||
b.onQuestStages("The Golem", 6, 7)
|
||||
.npcl("My task is incomplete. You must open the portal so I can defeat the great demon.")
|
||||
.playerl("I already told you, he's dead!")
|
||||
.npcl("Task incomplete.")
|
||||
.playerl("Oh, how am I going to convince you?")
|
||||
.endWith() { player -> if(player.questRepository.getStage("The Golem") < 7) { setQuestStage(player, "The Golem", 7) } }
|
||||
.endWith() { df, player -> if(player.questRepository.getStage("The Golem") < 7) { setQuestStage(player, "The Golem", 7) } }
|
||||
}
|
||||
}
|
||||
|
||||
class ClayGolemProgramDialogueFile : core.game.dialogue.DialogueBuilderFile() {
|
||||
override fun create(b: core.game.dialogue.DialogueBuilder) {
|
||||
class ClayGolemProgramDialogueFile : DialogueBuilderFile() {
|
||||
override fun create(b: DialogueBuilder) {
|
||||
b.onQuestStages("The Golem", 8)
|
||||
.npc("New instructions...", "Updating program...")
|
||||
.npcl("Task complete!")
|
||||
.npcl("Thank you. Now my mind is at rest.")
|
||||
.endWith() { player -> finishQuest(player, "The Golem") }
|
||||
.endWith() { _, player -> finishQuest(player, "The Golem") }
|
||||
}
|
||||
}
|
||||
|
||||
class CuratorHaigHalenGolemDialogue : core.game.dialogue.DialogueBuilderFile() {
|
||||
override fun create(b: core.game.dialogue.DialogueBuilder) {
|
||||
class CuratorHaigHalenGolemDialogue : DialogueBuilderFile() {
|
||||
override fun create(b: DialogueBuilder) {
|
||||
val opt1 = b.onQuestStages("The Golem", 3)
|
||||
.npcl("Ah yes, a very impressive artefact. The people of that city were excellent sculptors.")
|
||||
.npcl("It's in the display case upstairs.")
|
||||
|
|
|
|||
|
|
@ -62,8 +62,9 @@ public final class AnimalMagnetismPlugin extends OptionHandler {
|
|||
public boolean handle(Player player, Node node, String option) {
|
||||
switch (node.getId()) {
|
||||
case 5167:
|
||||
if (!hasRequirement(player, "Creature of Fenkenstrain"))
|
||||
if (!hasRequirement(player, "Creature of Fenkenstrain")) {
|
||||
break;
|
||||
}
|
||||
player.teleport(new Location(3577, 9927));
|
||||
break;
|
||||
case 5198:
|
||||
|
|
|
|||
|
|
@ -1,148 +0,0 @@
|
|||
package content.region.morytania.canifis.dialogue;
|
||||
|
||||
import core.plugin.Initializable;
|
||||
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.item.Item;
|
||||
|
||||
/**
|
||||
* Handles the RoavarDialogue dialogue.
|
||||
* @author 'Vexia
|
||||
*/
|
||||
@Initializable
|
||||
public class RoavarDialogue extends DialoguePlugin {
|
||||
|
||||
public RoavarDialogue() {
|
||||
|
||||
}
|
||||
|
||||
public RoavarDialogue(Player player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DialoguePlugin newInstance(Player player) {
|
||||
|
||||
return new RoavarDialogue(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean open(Object... args) {
|
||||
npc = (NPC) args[0];
|
||||
interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello there!");
|
||||
stage = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(int interfaceId, int buttonId) {
|
||||
switch (stage) {
|
||||
case 0:
|
||||
interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Greeting traveller. Welcome to 'The Hair Of The Dog'", "Tavern. What can I do you for?");
|
||||
stage = 1;
|
||||
break;
|
||||
case 1:
|
||||
interpreter.sendOptions("Select an Option", "Can I buy a beer?", "Can I hear some gossipp?", "Nothing thanks.");
|
||||
stage = 2;
|
||||
break;
|
||||
case 2:
|
||||
switch (buttonId) {
|
||||
case 1:
|
||||
interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Can I buy a beer?");
|
||||
stage = 10;
|
||||
break;
|
||||
case 2:
|
||||
interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Can I hear some gossip?");
|
||||
stage = 20;
|
||||
break;
|
||||
case 3:
|
||||
interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Nothing thanks.");
|
||||
stage = 40;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 10:
|
||||
interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Well that's my speciality! The local brew's named", "'Moonlight Mead' and will set you back 5 gold.", "Waddya say? Fancy a pint?");
|
||||
stage = 11;
|
||||
break;
|
||||
case 11:
|
||||
interpreter.sendOptions("Select an Option", "Yes please.", "Actually, no thanks.");
|
||||
stage = 12;
|
||||
break;
|
||||
case 12:
|
||||
switch (buttonId) {
|
||||
case 1:
|
||||
interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Yes please.");
|
||||
stage = 15;
|
||||
break;
|
||||
case 2:
|
||||
interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Actually, no thanks.");
|
||||
stage = 14;
|
||||
break;
|
||||
|
||||
}
|
||||
break;
|
||||
case 14:
|
||||
end();
|
||||
break;
|
||||
case 15:
|
||||
if (player.getInventory().contains(995, 5)) {
|
||||
if (player.getInventory().remove(new Item(995, 5))) {
|
||||
player.getInventory().add(new Item(2955, 1));
|
||||
interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Here ya go pal. Enjoy!");
|
||||
stage = 16;
|
||||
}
|
||||
} else {
|
||||
end();
|
||||
player.getPacketDispatch().sendMessage("You need 5 gold coins to buy a pint of beer.");
|
||||
}
|
||||
break;
|
||||
case 16:
|
||||
end();
|
||||
break;
|
||||
case 20:
|
||||
interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I am not one to gossip!");
|
||||
stage = 21;
|
||||
break;
|
||||
case 21:
|
||||
end();
|
||||
break;
|
||||
case 30:
|
||||
if (player.getInventory().contains(2963, 1)) {
|
||||
interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I don't have a spare lying around, sorry friend.", "Hopefully you'll find something else that can protect you", "against ghasts!");
|
||||
stage = 31;
|
||||
} else {
|
||||
interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Ah, well I do have one lying around.", "I suppose you could have it.");
|
||||
stage = 42;
|
||||
}
|
||||
break;
|
||||
case 31:
|
||||
end();
|
||||
break;
|
||||
case 40:
|
||||
interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "...I don't know why you talked to me if you don't want", "anything then...");
|
||||
stage = 41;
|
||||
break;
|
||||
case 41:
|
||||
end();
|
||||
break;
|
||||
case 42:
|
||||
if (player.getInventory().freeSlots() < 1) {
|
||||
interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Oh, nevermind. It seems your backpack is full.");
|
||||
} else {
|
||||
interpreter.sendDialogue("The bartender hands you a silver sickle.");
|
||||
player.getInventory().add(new Item(2963, 1));
|
||||
}
|
||||
stage = 31;
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getIds() {
|
||||
return new int[] { 1042 };
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
package content.region.morytania.canifis.dialogue
|
||||
|
||||
import content.region.morytania.quest.creatureoffenkenstrain.RoavarDialogueFile
|
||||
import core.api.*
|
||||
import core.game.dialogue.DialoguePlugin
|
||||
import core.game.dialogue.FacialExpression
|
||||
import core.game.dialogue.IfTopic
|
||||
import core.game.dialogue.Topic
|
||||
import core.game.node.entity.npc.NPC
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.item.Item
|
||||
import core.plugin.Initializable
|
||||
import core.tools.END_DIALOGUE
|
||||
import core.tools.START_DIALOGUE
|
||||
import org.rs09.consts.Items
|
||||
import org.rs09.consts.NPCs
|
||||
|
||||
/**
|
||||
* Roavar dialogue.
|
||||
*/
|
||||
@Initializable
|
||||
class RoavarDialogue (player: Player? = null) : DialoguePlugin(player) {
|
||||
|
||||
override fun open(vararg args: Any): Boolean {
|
||||
npc = args[0] as NPC
|
||||
player(FacialExpression.HALF_GUILTY, "Hello there!")
|
||||
stage = 0
|
||||
return true
|
||||
}
|
||||
|
||||
override fun handle(interfaceId: Int, buttonId: Int): Boolean {
|
||||
when (stage) {
|
||||
0 -> {
|
||||
npc(FacialExpression.HALF_GUILTY, "Greeting traveller. Welcome to 'The Hair Of The Dog'", "Tavern. What can I do you for?")
|
||||
stage = 1
|
||||
}
|
||||
|
||||
1 -> showTopics(
|
||||
Topic<Int?>(FacialExpression.HALF_GUILTY, "Can I buy a beer?", 10, false),
|
||||
Topic<Int?>(FacialExpression.HALF_GUILTY, "Can I hear some gossip", 20, false),
|
||||
IfTopic<RoavarDialogueFile?>(FacialExpression.HALF_GUILTY, "Can I buy something to eat?", RoavarDialogueFile(1), player.getQuestRepository().getQuest("Creature of Fenkenstrain").getStage(player) == 2, false),
|
||||
Topic<Int?>(FacialExpression.HALF_GUILTY, "Nothing thanks.", 40, false)
|
||||
)
|
||||
|
||||
10 -> {
|
||||
npc(FacialExpression.HALF_GUILTY, "Well that's my speciality! The local brew's named", "'Moonlight Mead' and will set you back 5 gold.", "Waddya say? Fancy a pint?")
|
||||
stage = 11
|
||||
}
|
||||
|
||||
11 -> {
|
||||
interpreter.sendOptions("Select an Option", "Yes please.", "Actually, no thanks.")
|
||||
stage = 12
|
||||
}
|
||||
|
||||
12 -> when (buttonId) {
|
||||
1 -> {
|
||||
player(FacialExpression.HALF_GUILTY, "Yes please.")
|
||||
stage = 15
|
||||
}
|
||||
|
||||
2 -> {
|
||||
player(FacialExpression.HALF_GUILTY, "Actually, no thanks.")
|
||||
stage = 14
|
||||
}
|
||||
}
|
||||
14 -> end()
|
||||
15 -> if (inInventory(player, Items.COINS_995, 5)) {
|
||||
if (removeItem(player, Item(Items.COINS_995, 5))) {
|
||||
addItemOrDrop(player, Items.MOONLIGHT_MEAD_2955)
|
||||
npc(FacialExpression.HALF_GUILTY, "Here ya go pal. Enjoy!")
|
||||
stage = 16
|
||||
}
|
||||
} else {
|
||||
end()
|
||||
sendMessage(player, "You need 5 gold coins to buy a pint of beer.")
|
||||
}
|
||||
|
||||
16 -> end()
|
||||
20 -> {
|
||||
npc(FacialExpression.HALF_GUILTY, "I am not one to gossip!")
|
||||
stage = 21
|
||||
}
|
||||
|
||||
21 -> end()
|
||||
30 -> stage = if (inInventory(player, 2963, 1)) {
|
||||
npc(FacialExpression.HALF_GUILTY, "I don't have a spare lying around, sorry friend.", "Hopefully you'll find something else that can protect you", "against ghasts!")
|
||||
31
|
||||
} else {
|
||||
npc(FacialExpression.HALF_GUILTY, "Ah, well I do have one lying around.", "I suppose you could have it.")
|
||||
42
|
||||
}
|
||||
|
||||
31 -> end()
|
||||
40 -> {
|
||||
npc(FacialExpression.HALF_GUILTY, "...I don't know why you talked to me if you don't want", "anything then...")
|
||||
stage = 41
|
||||
}
|
||||
|
||||
41 -> end()
|
||||
42 -> {
|
||||
if (freeSlots(player) < 1) {
|
||||
npc(FacialExpression.HALF_GUILTY, "Oh, nevermind. It seems your backpack is full.")
|
||||
} else {
|
||||
sendDialogue(player, "The bartender hands you a silver sickle.")
|
||||
addItem(player, 2963)
|
||||
}
|
||||
stage = 31
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun newInstance(player: Player): DialoguePlugin {
|
||||
return RoavarDialogue(player)
|
||||
}
|
||||
override fun getIds(): IntArray {
|
||||
return intArrayOf(NPCs.ROAVAR_1042)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
package content.region.morytania.quest.creatureoffenkenstrain
|
||||
|
||||
import content.global.handlers.iface.BookInterface
|
||||
import content.global.handlers.iface.BookLine
|
||||
import content.global.handlers.iface.Page
|
||||
import content.global.handlers.iface.PageSet
|
||||
import core.api.*
|
||||
import core.game.dialogue.DialogueFile
|
||||
import core.game.node.entity.player.Player
|
||||
import core.tools.END_DIALOGUE
|
||||
import core.tools.START_DIALOGUE
|
||||
import org.rs09.consts.Items
|
||||
|
||||
class BookcaseWestDialogueFile : DialogueFile() {
|
||||
override fun handle(componentID: Int, buttonID: Int) {
|
||||
when (stage) {
|
||||
START_DIALOGUE -> sendDialogueOptions(player!!, "Which book would you like to read?", "1001 Ways To Eat Fried Gizzards", "Practical Gardening For The Headless", "Human Taxidermy for Nincompoops", "The Joy of Gravedigging").also {
|
||||
setComponentVisibility(player!!, 232, 9, false)
|
||||
setComponentVisibility(player!!, 232, 8, true)
|
||||
stage++
|
||||
}
|
||||
1 -> when (buttonID) {
|
||||
1 -> sendDialogue(player!!, "This book leaves you contemplating vegetarianism.").also { stage = END_DIALOGUE }
|
||||
2 -> sendDialogue(player!!, "This book has some very enlightening points to make, but you are at a loss to know how anyone without a head could possibly read it.").also { stage = END_DIALOGUE }
|
||||
3 -> sendDialogue(player!!, "This book seems to have been read hundreds of times, and has scribbles and formulae on every page. One such scribble says None good enough - have to lock them in the caverns...").also { stage = END_DIALOGUE }
|
||||
4 -> sendDialogue(player!!, "As you pull the book a hidden latch springs into place, and the bookcase swings open, revealing a secret compartment.").also { stage++ }
|
||||
}
|
||||
2 -> sendItemDialogue(player!!, Items.MARBLE_AMULET_4187, "You find a marble amulet in the secret compartment.").also {
|
||||
addItemOrDrop(player!!, Items.MARBLE_AMULET_4187, 1)
|
||||
stage = END_DIALOGUE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BookcaseEastDialogueFile : DialogueFile() {
|
||||
|
||||
override fun handle(componentID: Int, buttonID: Int) {
|
||||
val opposingGender = if (player!!.isMale) "female" else "male"
|
||||
when (stage) {
|
||||
START_DIALOGUE -> sendDialogueOptions(player!!, "Which book would you like to read?", "Men are from Morytania, Women are from Lumbridge", "Chimney Sweeping on a Budget", "Handy Maggot Avoidance Techniques", "My Family and Other Zombies").also { stage++ }
|
||||
1 -> when (buttonID) {
|
||||
1 -> sendDialogue(player!!, "You discover some fascinating insights into the mind of the $opposingGender kind.").also { stage = END_DIALOGUE }
|
||||
2 -> ChimneySweepingOnABudgetBook.display(player!!, 0, 0).also { end() }
|
||||
3 -> sendDialogue(player!!, "As you pull the book a hidden latch springs into place, and the bookcase swings open, revealing a secret compartment.").also { stage++ }
|
||||
4 -> sendDialogue(player!!, "The book is appallingly dull.").also { stage = END_DIALOGUE }
|
||||
}
|
||||
2 -> {
|
||||
if (getQuestStage(player!!, CreatureOfFenkenstrain.questName) == 2) {
|
||||
sendItemDialogue(player!!, Items.OBSIDIAN_AMULET_4188, "You find an obsidian amulet in the secret compartment.").also {
|
||||
addItemOrDrop(player!!, Items.OBSIDIAN_AMULET_4188, 1)
|
||||
stage = END_DIALOGUE
|
||||
}
|
||||
} else {
|
||||
sendDialogue(player!!, "The secret compartment is empty.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Chimney Sweeping on a Budget Book
|
||||
* @author ovenbreado
|
||||
*/
|
||||
class ChimneySweepingOnABudgetBook {
|
||||
companion object {
|
||||
|
||||
private val TITLE = "Chimney Sweeping on a Budget "
|
||||
val CONTENTS = arrayOf(
|
||||
PageSet(
|
||||
Page(
|
||||
BookLine("Page 26", 55),
|
||||
BookLine("that sometimes a sweep", 56),
|
||||
BookLine("may find themselves", 57),
|
||||
BookLine("brushless and without the", 58),
|
||||
BookLine("funds to purchase the", 59),
|
||||
BookLine("one tool that is most", 60),
|
||||
BookLine("essential to their trade.", 61),
|
||||
BookLine("What is a chimney sweep", 62),
|
||||
BookLine("without his or her brush?", 63),
|
||||
BookLine("In this kind of situation", 64),
|
||||
BookLine("any normal long-handled", 65),
|
||||
),
|
||||
Page(
|
||||
BookLine("brush might be a suitable", 66),
|
||||
BookLine("replacement, although", 67),
|
||||
BookLine("when attaching extensions", 68),
|
||||
BookLine("to the handle make sure", 69),
|
||||
BookLine("to use something sturdy", 70),
|
||||
BookLine("like wire, otherwise a", 71),
|
||||
BookLine("sweep may find", 72),
|
||||
BookLine("themselves losing their", 73),
|
||||
BookLine("brush and livelyhood to", 74),
|
||||
BookLine("the forces of gravity", 75),
|
||||
)
|
||||
),
|
||||
)
|
||||
fun display(player: Player, pageNum: Int, buttonID: Int) : Boolean {
|
||||
BookInterface.pageSetup(player, BookInterface.FANCY_BOOK_3_49, TITLE, CONTENTS)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,187 @@
|
|||
package content.region.morytania.quest.creatureoffenkenstrain
|
||||
|
||||
import content.region.desert.quest.thegolem.TheGolemListeners
|
||||
import core.api.*
|
||||
import core.game.node.entity.player.link.quest.Quest
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.entity.skill.Skills
|
||||
import core.plugin.Initializable
|
||||
import org.rs09.consts.Items
|
||||
|
||||
val CREATURE_OF_FENKENSTRAIN = "Creature of Fenkenstrain"
|
||||
|
||||
/**
|
||||
* Creature of Fenkenstrain Quest
|
||||
*
|
||||
* https://www.youtube.com/watch?v=zmvgzuA31S0
|
||||
* https://www.youtube.com/watch?v=RoR6zLGrRoY
|
||||
* https://www.youtube.com/watch?v=I6X0kER1wDA - You can telekinetic grab the pickled brain jar lol
|
||||
* https://www.youtube.com/watch?v=53_2dKEu0xo - has the letter and the bottom part of the quest dialogue
|
||||
* https://www.youtube.com/watch?v=s57ElnRNhmU - in 2014 but the most thorough
|
||||
|
||||
* 1 - Read Signpost to start the quest
|
||||
* 2 - Talked to Fenkenstrain
|
||||
* 3 - Collected body parts
|
||||
* 4 - Gave needle and thread
|
||||
* 5 - Repaired the lightning conductor and bring the creature to life
|
||||
* 6 - Talked to the creature
|
||||
* 100 - Stoped Fenkenstrain by stealing ring of charos
|
||||
*/
|
||||
@Initializable
|
||||
class CreatureOfFenkenstrain : Quest("Creature of Fenkenstrain", 41, 40, 1, 399, 0, 1, 9) {
|
||||
|
||||
companion object {
|
||||
const val questName = "Creature of Fenkenstrain"
|
||||
const val attributeArms = "/save:quest:creatureoffenkenstrain-arms"
|
||||
const val attributeLegs = "/save:quest:creatureoffenkenstrain-legs"
|
||||
const val attributeTorso = "/save:quest:creatureoffenkenstrain-torso"
|
||||
const val attributeHead = "/save:quest:creatureoffenkenstrain-decaphead"
|
||||
const val attributeUnlockedMemorial = "/save:quest:creatureoffenkenstrain-amuletonmemorial"
|
||||
const val attributeUnlockedShed = "/save:quest:creatureoffenkenstrain-keyonsheddoor"
|
||||
const val attributeNeedle = "/save:quest:creatureoffenkenstrain-needle"
|
||||
const val attributeThread = "/save:quest:creatureoffenkenstrain-thread"
|
||||
const val fenkenstrainVarp = 399
|
||||
}
|
||||
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 reading the signpost in the", line++, false)
|
||||
line(player, "centre of !!Canifis??.", line++, false)
|
||||
line(player, "I must be able to defeat a !!level 51 monster??, and need the", line++, false)
|
||||
line(player, "following skill levels:", line++, false)
|
||||
line(player, "Level 20 Crafting", line++, hasLevelStat(player, Skills.CRAFTING, 20))
|
||||
line(player, "Level 25 Theiving", line++, hasLevelStat(player, Skills.THIEVING, 25))
|
||||
line(player, "I also need to have completed the following quests:", line++, false)
|
||||
line(player, "Priest in Peril", line++, isQuestComplete(player, "Priest in Peril"))
|
||||
line(player, "Restless Ghost", line++, isQuestComplete(player, "The Restless Ghost"))
|
||||
} else {
|
||||
line(player, "I read the signpost in Canifis, which tells of a butler", line++, true)
|
||||
line(player, "position that is available at the castle to the northeast.", line++, true)
|
||||
if (stage >= 2) {
|
||||
line(player, "I spoke to Fenkenstrain, who wanted me to find him some", line++, true)
|
||||
line(player, "body parts so that he could build a creature.", line++, true)
|
||||
} else if (stage >= 1) {
|
||||
line(player, "I should go up to the castle and speak to !!Dr Fenkenstrain??", line++, false)
|
||||
}
|
||||
line++
|
||||
if (stage >= 3) {
|
||||
line(player, "I gave a torso, some arms and legs, and a head to Fenkenstrain,", line++, true)
|
||||
line(player, "who then wanted a needle and 5 lots of thread, so that he could", line++, true)
|
||||
line(player, "sew the bodyparts together and create his creature.", line++, true)
|
||||
} else if (stage >= 2) {
|
||||
line(player, "I need to find these body parts for !!Fenkenstrain??:", line++, false)
|
||||
line(player, "a pair of !!arms??", line++, false)
|
||||
line(player, "a pair legs !!legs??", line++, false)
|
||||
line(player, "a !!torso??", line++, false)
|
||||
line(player, "a !!head??", line++, false)
|
||||
line++
|
||||
line(player, "Apparently the soil of !!Morytania?? has a unique quality", line++, false)
|
||||
line(player, "which preserves the bodies of the dead better than", line++, false)
|
||||
line(player, "elsewhere, so perhaps I should look at the graves in the", line++, false)
|
||||
line(player, "local area", line++, false)
|
||||
}
|
||||
line++
|
||||
if (stage >= 4) {
|
||||
line(player, "I brought Fenkenstrain a needle and 5 quantities of", line++, true)
|
||||
line(player, "thread.", line++, true)
|
||||
} else if (stage >= 3) {
|
||||
line(player, "I need to bring !!Fenkenstrain?? a !!needle?? and !!5 quantities??", line++, false)
|
||||
line(player, "!!of thread??.", line++, false)
|
||||
}
|
||||
line++
|
||||
if (stage >= 5) {
|
||||
line(player, "I repaired the lightning conductor, and Fenkenstrain", line++, true)
|
||||
line(player, "brought the Creature to life.", line++, true)
|
||||
} else if (stage >= 4) {
|
||||
line(player, "I need to repair the !!lightning conductor?? on the", line++, false)
|
||||
line(player, "!!balcony?? above.", line++, false)
|
||||
}
|
||||
line++
|
||||
if (stage == 5) {
|
||||
line(player, "!!Fenkenstrain?? wants to talk to me.", line++, false)
|
||||
line++
|
||||
}
|
||||
if (stage >= 7) {
|
||||
line(player, "The Creature went on a rampage, and Fenkenstrain sent", line++, true)
|
||||
line(player, "me up to the Tower to destroy it.", line++, true)
|
||||
line(player, "The Creature convinced me to stop Fenkenstrain's", line++, true)
|
||||
line(player, "experiments once and for all, and has told me the true", line++, true)
|
||||
line(player, "history of Fenkenstrain's treachery.", line++, true)
|
||||
} else if (stage >= 6) {
|
||||
line(player, "The !!Creature?? went on a rampage, and !!Fenkenstrain?? wants", line++, false)
|
||||
line(player, "me to go up the !!Tower?? to destroy it.", line++, false)
|
||||
}
|
||||
line++
|
||||
if (stage >= 8) {
|
||||
line(player, "I stole Fenkenstrain's Ring of Charos, and he released me from", line++, true)
|
||||
line(player, "his service.", line++, true)
|
||||
} else if (stage >= 7) {
|
||||
line(player, "I must find a way to stop Fenkenstrain's experiments.", line++, false)
|
||||
}
|
||||
if (stage >= 100) {
|
||||
line++
|
||||
line(player,"<col=FF0000>QUEST COMPLETE!</col>", line)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun hasRequirements(player: Player): Boolean {
|
||||
return arrayOf(
|
||||
hasLevelStat(player, Skills.CRAFTING, 20),
|
||||
hasLevelStat(player, Skills.THIEVING, 25),
|
||||
isQuestComplete(player, "Priest in Peril"),
|
||||
isQuestComplete(player, "The Restless Ghost"),
|
||||
).all { it }
|
||||
}
|
||||
|
||||
override fun reset(player: Player) {
|
||||
setVarp(player, fenkenstrainVarp, 0, true)
|
||||
removeAttribute(player, attributeArms)
|
||||
removeAttribute(player, attributeLegs)
|
||||
removeAttribute(player, attributeTorso)
|
||||
removeAttribute(player, attributeHead)
|
||||
removeAttribute(player, attributeUnlockedMemorial)
|
||||
removeAttribute(player, attributeUnlockedShed)
|
||||
removeAttribute(player, attributeNeedle)
|
||||
removeAttribute(player, attributeThread)
|
||||
}
|
||||
|
||||
override fun finish(player: Player) {
|
||||
var ln = 10
|
||||
super.finish(player)
|
||||
player.packetDispatch.sendString("You have completed Creature of Fenkenstrain!", 277, 4)
|
||||
player.packetDispatch.sendItemZoomOnInterface(Items.RING_OF_CHAROS_4202,230,277,5)
|
||||
|
||||
drawReward(player, "2 quest points", ln++)
|
||||
drawReward(player, "Ring of Charos", ln++)
|
||||
drawReward(player, "1,000 Theiving XP", ln++)
|
||||
|
||||
addItemOrDrop(player, Items.RING_OF_CHAROS_4202, 1)
|
||||
rewardXP(player, Skills.THIEVING, 1000.0)
|
||||
}
|
||||
|
||||
override fun setStage(player: Player, stage: Int) {
|
||||
super.setStage(player, stage)
|
||||
this.updateVarps(player)
|
||||
}
|
||||
|
||||
override fun updateVarps(player: Player) {
|
||||
// This is a bit of a hack. I didn't manage to align the quest with the varp,
|
||||
// so I had to include both stage 3 and 4 to varp value 3 to show the creature.
|
||||
if(getQuestStage(player, questName) == 4) {
|
||||
setVarp(player, fenkenstrainVarp, 3, true)
|
||||
}
|
||||
if(getQuestStage(player, questName) >= 8) {
|
||||
setVarp(player, fenkenstrainVarp, 8, true)
|
||||
}
|
||||
}
|
||||
|
||||
override fun newInstance(`object`: Any?): Quest {
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,393 @@
|
|||
package content.region.morytania.quest.creatureoffenkenstrain
|
||||
|
||||
import content.global.travel.canoe.CanoeListener
|
||||
import core.api.*
|
||||
import core.game.dialogue.FacialExpression
|
||||
import core.game.global.action.DoorActionHandler
|
||||
import core.game.global.action.PickupHandler
|
||||
import core.game.interaction.InteractionListener
|
||||
import core.game.node.Node
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.item.GroundItem
|
||||
import core.game.node.item.Item
|
||||
import core.game.system.task.Pulse
|
||||
import core.game.world.map.Location
|
||||
import core.game.world.update.flag.context.Animation
|
||||
import org.rs09.consts.Scenery
|
||||
import org.rs09.consts.Items
|
||||
import org.rs09.consts.NPCs
|
||||
|
||||
class CreatureOfFenkenstrainListeners : InteractionListener {
|
||||
companion object {
|
||||
private val itemToAttribute = hashMapOf(
|
||||
Items.ARMS_4195 to CreatureOfFenkenstrain.attributeArms,
|
||||
Items.LEGS_4196 to CreatureOfFenkenstrain.attributeArms,
|
||||
Items.TORSO_4194 to CreatureOfFenkenstrain.attributeArms,
|
||||
Items.DECAPITATED_HEAD_4197 to CreatureOfFenkenstrain.attributeArms
|
||||
)
|
||||
|
||||
enum class Graves(val location: Location, val graveName: String, val unearthText: String, val unearthItem: Int?) {
|
||||
GRAVE1(Location(3541, 3541), "Anton Hayes", "...but the grave is empty.", null),
|
||||
GRAVE2(Location(3542, 3486), "Callum Elding", "...but the grave is empty.", null),
|
||||
GRAVE3(Location(3585, 3497), "Domin O'Raleigh", "...but the grave is empty.", null),
|
||||
GRAVE4(Location(3608, 3491), "Ed Lestwit", "...and you unearth a decapitated head.", Items.DECAPITATED_HEAD_4197),
|
||||
GRAVE5(Location(3588, 3472), "Elena Frey", "...but the grave is empty.", null),
|
||||
GRAVE6(Location(3593, 3509), "Eryn Treforest", "...but the grave is empty.", null),
|
||||
GRAVE7(Location(3594, 3491), "Isla Skye", "...but the grave is empty.", null),
|
||||
GRAVE8(Location(3596, 3479), "Jayna Harrow", "...but the grave is empty.", null),
|
||||
GRAVE9(Location(3604, 3466), "Jayne Corbo", "...but the grave is empty.", null),
|
||||
GRAVE10(Location(3608, 3466), "Kandik Kludge", "...but the grave is empty.", null),
|
||||
GRAVE11(Location(3616, 3478), "Korvic Frey", "...but the grave is empty.", null),
|
||||
GRAVE12(Location(3619, 3469), "Marabella Kludge", "...but the grave is empty.", null),
|
||||
GRAVE13(Location(3626, 3495), "Marcus Harrow", "...but the grave is empty.", null),
|
||||
GRAVE14(Location(3629, 3483), "Petrik Corbo", "...but the grave is empty.", null),
|
||||
GRAVE15(Location(3631, 3476), "Serra Alcanthric", "...but the grave is empty.", null),
|
||||
GRAVE16(Location(3631, 3500), "Toran Alcanthric", "...but the grave is empty.", null),
|
||||
GRAVE17(Location(3572, 3527), "Unknown", "...but the grave is empty.", null),
|
||||
GRAVE18(Location(3576, 3526), "Unknown", "...but the grave is empty.", null),
|
||||
GRAVE19(Location(3639, 3470), "Lord Rologray", "...but the grave is empty.", null),
|
||||
GRAVE20(Location(3634, 3503), "Lord Rologarth", "...but the grave is empty.", null),
|
||||
GRAVE21(Location(3502, 3576), "Lady Rolobrae", "...and you unearth a torso.", Items.TORSO_4194),
|
||||
GRAVE22(Location(3504, 3577), "Lord Rolomere", "...and you unearth a pair of arms.", Items.ARMS_4195),
|
||||
GRAVE23(Location(3506, 3576), "Lord Rolovanne", "...and you unearth a pair of legs.", Items.LEGS_4196);
|
||||
|
||||
companion object {
|
||||
@JvmField
|
||||
val locationMap = Graves.values().associateBy { it.location }
|
||||
}
|
||||
}
|
||||
}
|
||||
override fun defineListeners() {
|
||||
|
||||
// Climbing ladders
|
||||
addClimbDest(Location.create(3504, 9970, 0), Location.create(3504, 3571, 0))
|
||||
|
||||
// 1: Reading Signpost to start the quest
|
||||
on(Items.NULL_5164, SCENERY, "read") { player, _ ->
|
||||
if (getQuestStage(player, CreatureOfFenkenstrain.questName) < 7 ) {
|
||||
sendDialogueLines(
|
||||
player,
|
||||
"The signpost has a note pinned onto it. The note says:",
|
||||
"'---- Braindead Butler Wanted ----",
|
||||
" Gravedigging skills essential - Hunchback advantageous",
|
||||
"See Dr Fenkenstrain at the castle NE of Canifis'",
|
||||
)
|
||||
} else {
|
||||
sendDialogueLines(
|
||||
player,
|
||||
"The signpost has a note pinned onto it. The note says:",
|
||||
"'AAARRGGGHHHHH!!!!!'",
|
||||
)
|
||||
}
|
||||
if(getQuest(player, CreatureOfFenkenstrain.questName).hasRequirements(player) && getQuestStage(player, CreatureOfFenkenstrain.questName) == 0) {
|
||||
setQuestStage(player, CreatureOfFenkenstrain.questName, 1)
|
||||
}
|
||||
return@on true
|
||||
}
|
||||
|
||||
// 2 Reading a grave
|
||||
on(intArrayOf(Scenery.GRAVE_5168, Scenery.GRAVE_5169), SCENERY, "read") { player, node ->
|
||||
val grave = Graves.locationMap[node.location]
|
||||
sendMessage(player, "The grave says:")
|
||||
sendMessage(player, " 'Here lies ${grave?.graveName ?: "Unknown"} - REST IN PEACE'")
|
||||
return@on true
|
||||
}
|
||||
|
||||
// 2: Digging a grave
|
||||
on(intArrayOf(Scenery.GRAVE_5168, Scenery.GRAVE_5169), SCENERY, "dig") { player, node ->
|
||||
if(!inInventory(player, Items.SPADE_952)) {
|
||||
sendMessage(player, "You need a spade to do that.")
|
||||
return@on true
|
||||
}
|
||||
val grave = Graves.locationMap[node.location]
|
||||
sendMessage(player, "You start digging...")
|
||||
animate(player, Animation(831))
|
||||
player.pulseManager.run(object : Pulse(5) {
|
||||
override fun pulse(): Boolean {
|
||||
player.animate(Animation(-1))
|
||||
if(grave?.unearthItem != null &&
|
||||
!hasAnItem(player, grave.unearthItem).exists() &&
|
||||
!getAttribute(player, itemToAttribute[grave.unearthItem] ?: "", false)) {
|
||||
sendItemDialogue(player, grave.unearthItem, grave.unearthText)
|
||||
addItemOrDrop(player, grave.unearthItem)
|
||||
} else {
|
||||
sendMessage(player, "...but the grave is empty.")
|
||||
}
|
||||
return true
|
||||
}
|
||||
})
|
||||
return@on true
|
||||
}
|
||||
|
||||
// 2: Find amulets in bookcases. Note: even after the quest, these can still be accessed, but nothing falls out.
|
||||
on(Scenery.BOOKCASE_5166, SCENERY, "search") { player, node ->
|
||||
if (node.location.equals(Location(3555, 3558, 1))) {
|
||||
openDialogue(player, BookcaseEastDialogueFile())
|
||||
return@on true
|
||||
} else if(node.location.equals(Location(3542, 3558, 1))) {
|
||||
openDialogue(player, BookcaseWestDialogueFile())
|
||||
return@on true
|
||||
} else {
|
||||
sendMessage(player, "It is a bookcase full of books")
|
||||
return@on true
|
||||
}
|
||||
}
|
||||
|
||||
// 2: Snap together Amulets
|
||||
onUseWith(ITEM, Items.MARBLE_AMULET_4187, Items.OBSIDIAN_AMULET_4188) { player, used, with ->
|
||||
if(removeItem(player, Items.MARBLE_AMULET_4187) && removeItem(player, Items.OBSIDIAN_AMULET_4188)) {
|
||||
sendItemDialogue(player, Items.STAR_AMULET_4183, "The marble and obsidian amulets snap together tightly to form a six-pointed amulet.")
|
||||
addItemOrDrop(player, Items.STAR_AMULET_4183)
|
||||
}
|
||||
return@onUseWith true
|
||||
}
|
||||
|
||||
// 2: Fit Star Amulet on Memorial
|
||||
onUseWith(SCENERY, Items.STAR_AMULET_4183, Items.NULL_5167) { player, used, with ->
|
||||
if (removeItem(player, Items.STAR_AMULET_4183)) {
|
||||
sendItemDialogue(player, Items.STAR_AMULET_4183, "The star amulet fits exactly into the depression on the coffin lid.")
|
||||
setAttribute(player, CreatureOfFenkenstrain.attributeUnlockedMemorial, true)
|
||||
}
|
||||
return@onUseWith true
|
||||
}
|
||||
|
||||
// 2: Opening Cavern Entrance
|
||||
on(Scenery.ENTRANCE_5170, SCENERY, "open") { player, node ->
|
||||
if (inInventory(player, Items.CAVERN_KEY_4184) && removeItem(player, Items.CAVERN_KEY_4184)) {
|
||||
DoorActionHandler.handleAutowalkDoor(player, node.asScenery())
|
||||
} else {
|
||||
sendMessage(player, "The door is locked.")
|
||||
}
|
||||
return@on true
|
||||
}
|
||||
// 2: Using Cavern Key on the Cavern Entrance
|
||||
onUseWith(SCENERY, Items.CAVERN_KEY_4184, Scenery.ENTRANCE_5170) { player, used, with ->
|
||||
if (removeItem(player, used)) {
|
||||
DoorActionHandler.handleAutowalkDoor(player, with.asScenery())
|
||||
}
|
||||
return@onUseWith true
|
||||
}
|
||||
|
||||
|
||||
// 2: Searching for Cavern Key out of the Chest
|
||||
on(Scenery.CHEST_5163, SCENERY, "search") { player, node ->
|
||||
sendItemDialogue(player, Items.CAVERN_KEY_4184, "You take a key out of the chest.")
|
||||
addItemOrDrop(player, Items.CAVERN_KEY_4184)
|
||||
return@on true
|
||||
}
|
||||
|
||||
// 2: Taking the brain from the table (telekinetic grab is allowed)
|
||||
on(Items.PICKLED_BRAIN_4199, GROUNDITEM, "take") { player, node ->
|
||||
if(node.location.equals(3492, 3474, 0)) {
|
||||
openDialogue(player, RoavarDialogueFile(2), findLocalNPC(player, NPCs.ROAVAR_1042)!!)
|
||||
} else {
|
||||
PickupHandler.take(player, node as GroundItem)
|
||||
}
|
||||
return@on true
|
||||
}
|
||||
|
||||
// 2: Fit Brain into Decapitated Head
|
||||
onUseWith(ITEM, Items.PICKLED_BRAIN_4199, Items.DECAPITATED_HEAD_4197) { player, used, with ->
|
||||
if(removeItem(player, used) && removeItem(player, with)) {
|
||||
sendItemDialogue(player, Items.DECAPITATED_HEAD_4198, "You squeeze the pickled brain into the decapitated head.")
|
||||
addItemOrDrop(player, Items.DECAPITATED_HEAD_4198)
|
||||
}
|
||||
return@onUseWith true
|
||||
}
|
||||
|
||||
// 2: Searching the Memorial (Scenery.MEMORIAL_5167)
|
||||
on(Items.NULL_5167, SCENERY, "search") { player, node ->
|
||||
val scenery = node.asScenery()
|
||||
if(getAttribute(player, CreatureOfFenkenstrain.attributeUnlockedMemorial, false) ||
|
||||
getQuestStage(player, CreatureOfFenkenstrain.questName) > 2) {
|
||||
animateScenery(player, scenery, 1620)
|
||||
var dest: Location? = null
|
||||
if(scenery.location.equals(Location(3505, 3571))) {
|
||||
dest = Location(3504, 9969)
|
||||
} else if(scenery.location.equals(Location(3578, 3527))) {
|
||||
dest = Location(3577, 9927)
|
||||
}
|
||||
if(dest != null) {
|
||||
player.pulseManager.run(object : Pulse(3) {
|
||||
override fun pulse(): Boolean {
|
||||
resetAnimator(player)
|
||||
teleport(player, dest!!)
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if (scenery.location.equals(Location(3505, 3571))) {
|
||||
sendMessage(player, "You find a depression in the memorial stone in the shape of a six-pointed star.")
|
||||
} else {
|
||||
sendMessage(player, "You find nothing remarkable about the memorial stone.")
|
||||
}
|
||||
}
|
||||
return@on true
|
||||
}
|
||||
|
||||
// 2: Pushing open the Memorial (Scenery.MEMORIAL_5167)
|
||||
on(Items.NULL_5167, SCENERY, "push") { player, node ->
|
||||
val scenery = node.asScenery()
|
||||
if (getAttribute(player, CreatureOfFenkenstrain.attributeUnlockedMemorial, false) ||
|
||||
getQuestStage(player, CreatureOfFenkenstrain.questName) > 2) {
|
||||
animateScenery(player, scenery, 1620)
|
||||
var dest: Location? = null
|
||||
if(scenery.location.equals(Location(3505, 3571))) {
|
||||
dest = Location(3504, 9969)
|
||||
} else if(scenery.location.equals(Location(3578, 3527))) {
|
||||
dest = Location(3577, 9927)
|
||||
}
|
||||
if(dest != null) {
|
||||
player.pulseManager.run(object : Pulse(3) {
|
||||
override fun pulse(): Boolean {
|
||||
resetAnimator(player)
|
||||
teleport(player, dest!!)
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
player.sendMessage("The coffin is incredibly heavy, and does not budge.")
|
||||
}
|
||||
return@on true
|
||||
}
|
||||
|
||||
// 5: Garden Shed Door
|
||||
on(Scenery.DOOR_5174, SCENERY, "open") { player, node ->
|
||||
if (getAttribute(player, CreatureOfFenkenstrain.attributeUnlockedShed, false) ||
|
||||
getQuestStage(player, CreatureOfFenkenstrain.questName) >= 5) {
|
||||
DoorActionHandler.handleAutowalkDoor(player, node.asScenery())
|
||||
} else if (inInventory(player, Items.SHED_KEY_4186)) {
|
||||
if (removeItem(player, Items.SHED_KEY_4186)) {
|
||||
DoorActionHandler.handleAutowalkDoor(player, node.asScenery())
|
||||
setAttribute(player, CreatureOfFenkenstrain.attributeUnlockedShed, true)
|
||||
}
|
||||
} else {
|
||||
sendMessage(player, "The door is locked.")
|
||||
}
|
||||
return@on true
|
||||
}
|
||||
|
||||
// 5: Garden Shed Door
|
||||
onUseWith(SCENERY, Items.SHED_KEY_4186, Scenery.DOOR_5174) { player, used, with ->
|
||||
if (getAttribute(player, CreatureOfFenkenstrain.attributeUnlockedShed, false) ||
|
||||
getQuestStage(player, CreatureOfFenkenstrain.questName) >= 5) {
|
||||
DoorActionHandler.handleAutowalkDoor(player, with.asScenery())
|
||||
} else if (removeItem(player, used)) {
|
||||
DoorActionHandler.handleAutowalkDoor(player, with.asScenery())
|
||||
setAttribute(player, CreatureOfFenkenstrain.attributeUnlockedShed, true)
|
||||
}
|
||||
return@onUseWith true
|
||||
}
|
||||
|
||||
// 5: Pile of Canes
|
||||
on(Items.NULL_5158, SCENERY, "take-from") { player, node ->
|
||||
sendMessage(player, "You take a garden cane from the pile.")
|
||||
addItemOrDrop(player, Items.GARDEN_CANE_4189)
|
||||
return@on true
|
||||
}
|
||||
|
||||
// 5: Cupboard Garden Brush
|
||||
on(Items.NULL_5157, SCENERY, "search") { player, node ->
|
||||
sendItemDialogue(player, Items.GARDEN_BRUSH_4190, "You find a garden brush in the cupboard.")
|
||||
// player.packetDispatch.sendAngleOnInterface(241, 1, 1000, 510, 0)
|
||||
addItemOrDrop(player, Items.GARDEN_BRUSH_4190)
|
||||
return@on true
|
||||
}
|
||||
|
||||
// 5: Extended Brush up to 3 Fused
|
||||
onUseWith(ITEM, intArrayOf(Items.GARDEN_BRUSH_4190, Items.EXTENDED_BRUSH_4191, Items.EXTENDED_BRUSH_4192), Items.GARDEN_CANE_4189) { player, used, with ->
|
||||
if (!inInventory(player, Items.BRONZE_WIRE_1794)) {
|
||||
sendMessage(player, "You need some bronze wire to tie them together.")
|
||||
return@onUseWith true
|
||||
}
|
||||
if (removeItem(player, Items.BRONZE_WIRE_1794) && removeItem(player, used) && removeItem(player, with)){
|
||||
sendMessage(player, "You attach the cane to the brush.")
|
||||
when (used.id) {
|
||||
Items.GARDEN_BRUSH_4190 -> {
|
||||
addItemOrDrop(player, Items.EXTENDED_BRUSH_4191)
|
||||
}
|
||||
Items.EXTENDED_BRUSH_4191 -> {
|
||||
addItemOrDrop(player, Items.EXTENDED_BRUSH_4192)
|
||||
}
|
||||
Items.EXTENDED_BRUSH_4192 -> {
|
||||
addItemOrDrop(player, Items.EXTENDED_BRUSH_4193)
|
||||
}
|
||||
}
|
||||
}
|
||||
return@onUseWith true
|
||||
}
|
||||
|
||||
// 5: Cupboard Garden Brush
|
||||
on(Scenery.FIREPLACE_5165, SCENERY, "examine") { player, node ->
|
||||
if (!inInventory(player, Items.CONDUCTOR_4201)) {
|
||||
sendMessage(player, "You give the chimney a jolly good clean out.")
|
||||
sendItemDialogue(player, Items.CONDUCTOR_MOULD_4200, "A lightning conductor mould falls down out of the chimney.")
|
||||
addItemOrDrop(player, Items.CONDUCTOR_MOULD_4200)
|
||||
return@on true
|
||||
}
|
||||
sendMessage(player, "It looks like it needs a good sweep out.")
|
||||
return@on true
|
||||
}
|
||||
|
||||
// 5: Brush Fireplace
|
||||
onUseWith(SCENERY, Items.EXTENDED_BRUSH_4193, Scenery.FIREPLACE_5165) { player, used, with ->
|
||||
sendMessage(player, "You give the chimney a jolly good clean out.")
|
||||
sendItemDialogue(player, Items.CONDUCTOR_MOULD_4200, "A lightning conductor mould falls down out of the chimney.")
|
||||
addItemOrDrop(player, Items.CONDUCTOR_MOULD_4200)
|
||||
return@onUseWith true
|
||||
}
|
||||
|
||||
// 5: Repair the lightning rod
|
||||
on(Scenery.LIGHTNING_CONDUCTOR_5176, SCENERY, "repair") { player, node ->
|
||||
if (!inInventory(player, Items.CONDUCTOR_4201)) {
|
||||
sendMessage(player, "You need to repair it with a conductor.")
|
||||
return@on true
|
||||
}
|
||||
if (removeItem(player, Items.CONDUCTOR_4201)) {
|
||||
if(getQuestStage(player, CreatureOfFenkenstrain.questName) == 4) {
|
||||
setQuestStage(player, CreatureOfFenkenstrain.questName, 5)
|
||||
}
|
||||
sendDialogue(player, "You repair the lightning conductor not one moment too soon - a tremendous bold of lightning melts the new lightning conductor, and power blazes throughout the castle, if only briefly.")
|
||||
val scenery = node.asScenery()
|
||||
replaceScenery(scenery, Items.NULL_5177, 3, node.location)
|
||||
animateScenery(player, scenery, 1632)
|
||||
}
|
||||
return@on true
|
||||
}
|
||||
|
||||
// 6: Enter jail above
|
||||
on(Scenery.DOOR_5172, SCENERY, "open") { player, node ->
|
||||
if (inInventory(player, Items.TOWER_KEY_4185) || getQuestStage(player, CreatureOfFenkenstrain.questName) > 7) {
|
||||
DoorActionHandler.handleAutowalkDoor(player, node.asScenery())
|
||||
} else {
|
||||
sendMessage(player, "The door is locked.")
|
||||
}
|
||||
return@on true
|
||||
}
|
||||
// 2: Using Cavern Key on the Cavern Entrance
|
||||
onUseWith(SCENERY, Items.TOWER_KEY_4185, Scenery.DOOR_5172) { player, used, with ->
|
||||
if (inInventory(player, Items.TOWER_KEY_4185)) {
|
||||
DoorActionHandler.handleAutowalkDoor(player, with.asScenery())
|
||||
}
|
||||
return@onUseWith true
|
||||
}
|
||||
|
||||
|
||||
// 7: Pickpocket Ring of Charos from Fenkenstrain
|
||||
on(NPCs.DR_FENKENSTRAIN_1670, NPC, "pickpocket") { player, node ->
|
||||
if (getQuestStage(player, CreatureOfFenkenstrain.questName) == 7) {
|
||||
sendMessage(player, "You steal the Ring of Charos from Fenkenstrain.")
|
||||
finishQuest(player, CreatureOfFenkenstrain.questName)
|
||||
} else if (getQuestStage(player, CreatureOfFenkenstrain.questName) > 7 && hasAnItem(player, Items.RING_OF_CHAROS_4202).container == null) {
|
||||
// Allow Fenkenstrain to be pickpocketed beyond the quest if the ring is lost.
|
||||
addItemOrDrop(player, Items.RING_OF_CHAROS_4202, 1)
|
||||
sendMessage(player, "You steal the Ring of Charos from Fenkenstrain.")
|
||||
} else {
|
||||
player.dialogueInterpreter.sendDialogues(NPCs.DR_FENKENSTRAIN_1670, FacialExpression.NEUTRAL ,"What do you think you're doing???")
|
||||
}
|
||||
return@on true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,272 @@
|
|||
package content.region.morytania.quest.creatureoffenkenstrain
|
||||
|
||||
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 DrFenkenstrainDialogue (player: Player? = null) : DialoguePlugin(player) {
|
||||
override fun handle(interfaceId: Int, buttonId: Int): Boolean {
|
||||
openDialogue(player, DrFenkenstrainDialogueFile(), npc)
|
||||
return true
|
||||
}
|
||||
override fun newInstance(player: Player): DialoguePlugin {
|
||||
return DrFenkenstrainDialogue(player)
|
||||
}
|
||||
override fun getIds(): IntArray {
|
||||
// THE IDs ARE WRONG. 1668 and 1669 are varp controlled Dr Fenkenstrain
|
||||
return intArrayOf(NPCs.WEREWOLF_1668, NPCs.WEREWOLF_1669, NPCs.DR_FENKENSTRAIN_1670)
|
||||
}
|
||||
}
|
||||
|
||||
class DrFenkenstrainDialogueFile : DialogueBuilderFile() {
|
||||
|
||||
companion object {
|
||||
private fun reqArms(player: Player): Boolean {
|
||||
return !getAttribute(player, CreatureOfFenkenstrain.attributeArms, false) && inInventory(player, Items.ARMS_4195)
|
||||
}
|
||||
private fun reqLegs(player: Player): Boolean {
|
||||
return !getAttribute(player, CreatureOfFenkenstrain.attributeLegs, false) && inInventory(player, Items.LEGS_4196)
|
||||
}
|
||||
private fun reqTorso(player: Player): Boolean {
|
||||
return !getAttribute(player, CreatureOfFenkenstrain.attributeTorso, false) && inInventory(player, Items.TORSO_4194)
|
||||
}
|
||||
private fun reqHead(player: Player): Boolean {
|
||||
return !getAttribute(player, CreatureOfFenkenstrain.attributeHead, false) && inInventory(player, Items.DECAPITATED_HEAD_4198)
|
||||
}
|
||||
private fun hasPart(b: DialogueBuilder, item: Item, attributeToSet: String, successMsg: String): DialogueBuilder {
|
||||
return b.branch { player ->
|
||||
return@branch if (!getAttribute(player, attributeToSet, false) && inInventory(player, item.id, item.amount)) { 1 } else { 0 }
|
||||
}.let{ branch ->
|
||||
val returnJoin = b.placeholder()
|
||||
branch.onValue(0)
|
||||
.goto(returnJoin)
|
||||
branch.onValue(1)
|
||||
.betweenStage { _, player, _, _ ->
|
||||
if (removeItem(player, item)) {
|
||||
setAttribute(player, attributeToSet, true)
|
||||
}
|
||||
}
|
||||
.npcl(successMsg)
|
||||
.goto(returnJoin)
|
||||
return@let returnJoin.builder()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun create(b: DialogueBuilder) {
|
||||
|
||||
b.onQuestStages(CREATURE_OF_FENKENSTRAIN, 0)
|
||||
.npcl("Have you come to apply for the job?")
|
||||
.playerl(FacialExpression.THINKING, "What job?")
|
||||
.npcl("I've posted a note on the signpost in Canifis about it. Go take a look at it first.")
|
||||
.end()
|
||||
|
||||
b.onQuestStages(CREATURE_OF_FENKENSTRAIN, 1)
|
||||
.npcl("Have you come to apply for the job?")
|
||||
.options().let { optionBuilder ->
|
||||
val continuePath = b.placeholder()
|
||||
optionBuilder.option ("Yes")
|
||||
.playerl("Yes, if it pays well.")
|
||||
.goto(continuePath) // Continue down below.
|
||||
optionBuilder.option_playerl("No.")
|
||||
.end()
|
||||
return@let continuePath.builder()
|
||||
}
|
||||
.npcl("I'll have to ask you some questions first.")
|
||||
.playerl("Okay...")
|
||||
.npcl("How would you describe yourself in one word?")
|
||||
.options().let { optionBuilder ->
|
||||
val continuePath = b.placeholder()
|
||||
optionBuilder.recordAttribute("creature-of-fenkenstrain:first-question")
|
||||
optionBuilder.option_playerl("Stunning.").goto(continuePath)
|
||||
optionBuilder.option_playerl("Awe-inspiring.").goto(continuePath)
|
||||
optionBuilder.option_playerl("Breathtaking.").goto(continuePath)
|
||||
optionBuilder.option_playerl("Braindead.").goto(continuePath)
|
||||
return@let continuePath.builder()
|
||||
}
|
||||
.npcl("Mmmm, I see.")
|
||||
.npcl("Just one more question. What would you say is your greatest skill?")
|
||||
.options().let { optionBuilder ->
|
||||
val continuePath = b.placeholder()
|
||||
optionBuilder.recordAttribute("creature-of-fenkenstrain:second-question")
|
||||
optionBuilder.option_playerl("Combat.").goto(continuePath)
|
||||
optionBuilder.option_playerl("Magic.").goto(continuePath)
|
||||
optionBuilder.option_playerl("Cooking.").goto(continuePath)
|
||||
optionBuilder.option_playerl("Grave-digging.").goto(continuePath)
|
||||
return@let continuePath.builder()
|
||||
}
|
||||
.npcl("Mmmm, I see.")
|
||||
.branch { player -> if(player.getAttribute("creature-of-fenkenstrain:first-question", -1) == 3 && player.getAttribute("creature-of-fenkenstrain:second-question", -1) == 3) { 1 } else { 0 } }
|
||||
.let{ branch ->
|
||||
// Failure branch
|
||||
branch.onValue(0)
|
||||
.npcl("Looks like you're not the @g[man,woman] for the job.")
|
||||
.end()
|
||||
return@let branch // Return DialogueBranchBuilder instead of DialogueBuilder to forward the success branch.
|
||||
}.onValue(1) // Success branch
|
||||
.npcl("Looks like you're just the @g[man,woman] for the job! Welcome aboard!")
|
||||
.playerl("Is there anything you'd like me to do for you, sir?")
|
||||
.npcl("Yes, there is. You're highly skilled at grave-digging, yes?")
|
||||
.playerl("Err...yes, that's what I said.")
|
||||
.npcl("Excellent. Now listen carefully. I need you to find some...stuff...for me.")
|
||||
.playerl("Stuff?")
|
||||
.npcl("That's what I said...stuff.")
|
||||
.playerl("What kind of stuff?")
|
||||
.npcl("Well...dead stuff.")
|
||||
.playerl("Go on...")
|
||||
.npcl("I need you to get me enough dead body parts for me to stitch together a complete body, which I plan to bring to life.")
|
||||
.playerl("Right...okay...if you insist.")
|
||||
.endWith { _, player ->
|
||||
if(getQuestStage(player, CreatureOfFenkenstrain.questName) == 1) {
|
||||
setQuestStage(player, CreatureOfFenkenstrain.questName, 2)
|
||||
}
|
||||
}
|
||||
|
||||
b.onQuestStages(CreatureOfFenkenstrain.questName, 2)
|
||||
.options().let { optionBuilder ->
|
||||
val continuePath = b.placeholder()
|
||||
|
||||
optionBuilder.optionIf("I have some body parts for you.") { player -> return@optionIf reqArms(player) || reqLegs(player) || reqTorso(player) || reqHead(player) }
|
||||
.playerl("I have some body parts for you.")
|
||||
.goto(continuePath) // Continue down below.
|
||||
optionBuilder.optionIf("Do you know where I could find body parts?") { player -> return@optionIf !(reqArms(player) || reqLegs(player) || reqTorso(player) || reqHead(player)) }
|
||||
.playerl("Do you know where I could find body parts?")
|
||||
.npcl("The soil of Morytania is unique in its ability to preserve the bodies of the dead, which is one reason why I have chosen to carry out my experiments here.")
|
||||
.npcl("I recommend digging up some graves in the local area. To the south-east you will find the Haunted Woods; I believe there are many graves there.")
|
||||
.npcl( "There is also a mausoleum on an island west of this castle. I expect the bodies that are buried there to be extremely well preserved, as they were wealthy in life.")
|
||||
.end()
|
||||
optionBuilder.option_playerl("Remind me what you want me to do.")
|
||||
.npcl("I need you to get me enough dead body parts for me to stitch together a complete body, which I plan to bring to life.")
|
||||
.playerl("Right...okay...if you insist.")
|
||||
.end()
|
||||
optionBuilder.option_playerl("Why are you trying to make this creature?")
|
||||
.npcl("I came to the land of Morytania many years ago, to find a safe sanctuary for my experiments. This abandoned castle suited my purposes exactly.")
|
||||
.playerl("What were you experimenting in?")
|
||||
.npcl("Oh, perfectly innocent experiments - for the good of mankind.")
|
||||
.playerl("Then why did you need to come to Morytania?")
|
||||
.npcl("Enough questions, now. Get back to your work.")
|
||||
.end()
|
||||
optionBuilder.option_playerl("Will this creature put me out of a job?")
|
||||
.npcl("No, my friend. I have a very special purpose in mind for this creature.")
|
||||
.end()
|
||||
optionBuilder.option_playerl("I must get back to work, sir.")
|
||||
.end()
|
||||
|
||||
return@let continuePath.builder()
|
||||
}
|
||||
// Dialogue path to look for arms.
|
||||
.let{ builder -> return@let hasPart(builder, Item(Items.ARMS_4195, 1), CreatureOfFenkenstrain.attributeArms, "Great, you've brought me some arms.") }
|
||||
// Dialogue path to look for legs.
|
||||
.let{ builder -> return@let hasPart(builder, Item(Items.LEGS_4196, 1), CreatureOfFenkenstrain.attributeLegs, "Excellent, you've brought me some legs.") }
|
||||
// Dialogue path to look for torso.
|
||||
.let{ builder -> return@let hasPart(builder, Item(Items.TORSO_4194, 1), CreatureOfFenkenstrain.attributeTorso, "Splendid, you've brought me a torso.") }
|
||||
// Dialogue path to look for head.
|
||||
.let{ builder -> return@let hasPart(builder, Item(Items.DECAPITATED_HEAD_4198, 1), CreatureOfFenkenstrain.attributeHead, "Fantastic, you've brought me a head.") }
|
||||
.branch { player ->
|
||||
return@branch if (getAttribute(player, CreatureOfFenkenstrain.attributeHead, false) &&
|
||||
getAttribute(player, CreatureOfFenkenstrain.attributeLegs, false) &&
|
||||
getAttribute(player, CreatureOfFenkenstrain.attributeTorso, false) &&
|
||||
getAttribute(player, CreatureOfFenkenstrain.attributeHead, false)) { 1 } else { 0 }
|
||||
}.let{ branch ->
|
||||
// Failure branch
|
||||
branch.onValue(0)
|
||||
.end()
|
||||
return@let branch // Return DialogueBranchBuilder instead of DialogueBuilder to forward the success branch.
|
||||
}.onValue(1) // Success branch
|
||||
.npcl("Superb! Those are all the parts I need. Now to sew them together...")
|
||||
.npcl("Oh bother! I haven't got a needle or thread!")
|
||||
.npcl("Go and get me a needle, and I'll need 5 lots of thread.")
|
||||
.endWith { _, player ->
|
||||
if(getQuestStage(player, CreatureOfFenkenstrain.questName) == 2) {
|
||||
setQuestStage(player, CreatureOfFenkenstrain.questName, 3)
|
||||
}
|
||||
}
|
||||
|
||||
b.onQuestStages(CreatureOfFenkenstrain.questName, 3)
|
||||
.npcl("Where are my needle and thread, @name?")
|
||||
// Dialogue path to look for 1 needle.
|
||||
.let{ builder -> return@let hasPart(builder, Item(Items.NEEDLE_1733, 1), CreatureOfFenkenstrain.attributeNeedle, "Ah, a needle. Wonderful.") }
|
||||
// Dialogue path to look for 5 threads.
|
||||
.let{ builder -> return@let hasPart(builder, Item(Items.THREAD_1734, 5), CreatureOfFenkenstrain.attributeThread, "Some thread. Excellent.") }
|
||||
// Final branch to only continue conversation when both needle and threads are submitted.
|
||||
.branch { player ->
|
||||
return@branch if (getAttribute(player, CreatureOfFenkenstrain.attributeNeedle, false) &&
|
||||
getAttribute(player, CreatureOfFenkenstrain.attributeThread, false)) { 1 } else { 0 }
|
||||
}.let{ branch ->
|
||||
// Failure branch
|
||||
branch.onValue(0)
|
||||
.end()
|
||||
return@let branch // Return DialogueBranchBuilder instead of DialogueBuilder to forward the success branch.
|
||||
}.onValue(1) // Success branch
|
||||
.betweenStage { df, player, _, _ ->
|
||||
setVarp(player, CreatureOfFenkenstrain.fenkenstrainVarp, 3, true)
|
||||
}
|
||||
.line("Fenkenstrain uses the needle and thread to sew the body parts", "together. Soon, a hideous creature lies inanimate on the ritual table.")
|
||||
.npcl("Perfect. But I need one more thing from you - flesh and bones by themselves do not make life.")
|
||||
.playerl("Really?")
|
||||
.npcl("I have honed to perfection an ancient ritual that will give life to this creature, but for this I must harness the very power of Nature.")
|
||||
.playerl("And what power is this?")
|
||||
.npcl("The power of lightning.")
|
||||
.playerl("Sorry, can't make lightning, you've got the wrong @g[man,woman]-")
|
||||
.npcl("Silence your insolent tongue! The storm that brews overhead will create the lightning. What I need you to do is to repair the lightning conductor on the balcony above.")
|
||||
.playerl("Repair the lightning conductor, right. Can I have a break, soon? By law I'm entitled to 15 minutes every-")
|
||||
.npcl("Repair the conductor and BEGONE!!")
|
||||
.endWith { _, player ->
|
||||
if(getQuestStage(player, CreatureOfFenkenstrain.questName) == 3) {
|
||||
setQuestStage(player, CreatureOfFenkenstrain.questName, 4)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
b.onQuestStages(CreatureOfFenkenstrain.questName, 4)
|
||||
.playerl(FacialExpression.THINKING, "How do I repair the lighting conductor?")
|
||||
.npcl("Oh, it would be easier to do it myself! If you find a conductor mould you should be able to cast a new one.")
|
||||
.npcl("Remember this, @name, my experiment will only work with a conductor made from silver.")
|
||||
.end()
|
||||
|
||||
b.onQuestStages(CreatureOfFenkenstrain.questName, 5)
|
||||
.playerl("So did it work, then?")
|
||||
.npcl("Yes, I'm afraid it did, @name - all too well.")
|
||||
.playerl(FacialExpression.SUSPICIOUS, "I can't see it anywhere.")
|
||||
.npcl("I tricked it into going up to the Tower, and there it remains, imprisoned.")
|
||||
.playerl(FacialExpression.SUSPICIOUS, "So the creature wasn't all you'd hoped then?")
|
||||
.npcl("...oh, what have I done...")
|
||||
.playerl(FacialExpression.SUSPICIOUS, "Oh, I see, we're developing a sense of right and wrong now are we?")
|
||||
.playerl(FacialExpression.SUSPICIOUS, "Bit late for that, I'd say.")
|
||||
.npcl("I have no control over it! It's coming to get me!")
|
||||
.playerl(FacialExpression.SUSPICIOUS, "What do you want me to do about it?")
|
||||
.npcl("Destroy it!!! Take the key to the Tower and take back the life I never should have granted!!!")
|
||||
.endWith() { df, player ->
|
||||
if(getQuestStage(player, CreatureOfFenkenstrain.questName) == 5) {
|
||||
setQuestStage(player, CreatureOfFenkenstrain.questName, 6)
|
||||
}
|
||||
addItemOrDrop(player, Items.TOWER_KEY_4185)
|
||||
}
|
||||
|
||||
b.onQuestStages(CreatureOfFenkenstrain.questName, 6)
|
||||
.npcl("So have you destroyed it?!!?")
|
||||
.playerl("Not yet.")
|
||||
.npcl("Please, hurry - save me!!!!")
|
||||
.end()
|
||||
|
||||
b.onQuestStages(CreatureOfFenkenstrain.questName, 7)
|
||||
.npcl("So have you destroyed it?!!?")
|
||||
.playerl("Never, now that he has told me the truth!")
|
||||
.npcl("Oh my, oh my, this is exactly what I feared!")
|
||||
.npcl("Why did you have to pick Rologarth's brain of all brains?!?")
|
||||
.playerl("I'm through working for you.")
|
||||
.npcl("No! I refuse to release you! You must help me build another creature to destroy this dreadful mistake!!")
|
||||
.end()
|
||||
|
||||
b.onQuestStages(CreatureOfFenkenstrain.questName, 8, 100)
|
||||
.npcl("theyrecomingtogetme theyrecomingtogetme...")
|
||||
.playerl("It is all you deserve. Lord Rologarth is master of this castle once more. Let him protect you - if he wants to.")
|
||||
.npcl("theyrecomingtogetme theyrecomingtogetme...")
|
||||
.end()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
package content.region.morytania.quest.creatureoffenkenstrain
|
||||
|
||||
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 core.tools.END_DIALOGUE
|
||||
import core.tools.START_DIALOGUE
|
||||
import org.rs09.consts.Items
|
||||
import org.rs09.consts.NPCs
|
||||
import java.util.*
|
||||
|
||||
@Initializable
|
||||
class GardenerGhostDialogue (player: Player? = null) : DialoguePlugin(player) {
|
||||
override fun handle(interfaceId: Int, buttonId: Int): Boolean {
|
||||
val gardenerGhost = npc as GardenerGhostNPC
|
||||
if (gardenerGhost.target == player) {
|
||||
// This is the special case where the gardener follows you.
|
||||
if (gardenerGhost.location.withinDistance(gardenerGhost.graveLocation, 5)) {
|
||||
sendChat(gardenerGhost, "Here is the place where I met me' maker.")
|
||||
} else {
|
||||
sendChat(gardenerGhost, "Go " + gardenerGhost.location.deriveDirection(gardenerGhost.graveLocation).name.lowercase(Locale.getDefault()).replace('_', '-') + ", mate")
|
||||
gardenerGhost.continueFollowing(player)
|
||||
}
|
||||
} else {
|
||||
openDialogue(player, GardenerGhostDialogueFile(), npc)
|
||||
}
|
||||
return true
|
||||
}
|
||||
override fun newInstance(player: Player): DialoguePlugin {
|
||||
return GardenerGhostDialogue(player)
|
||||
}
|
||||
override fun getIds(): IntArray {
|
||||
return intArrayOf(NPCs.GARDENER_GHOST_1675)
|
||||
}
|
||||
}
|
||||
|
||||
class GardenerGhostDialogueFile : DialogueBuilderFile() {
|
||||
override fun create(b: DialogueBuilder) {
|
||||
b.onPredicate { player -> !player.equipment.containsAtLeastOneItem(Items.GHOSTSPEAK_AMULET_552) }
|
||||
.npcl("Wooo wooo wooooo.")
|
||||
.end()
|
||||
b.onQuestStages(CreatureOfFenkenstrain.questName, *(0 .. 8).toIntArray(), 100)
|
||||
.options().let { optionBuilder ->
|
||||
optionBuilder.option("Tell me about Fenkenstrain.")
|
||||
.playerl("Can you tell me anything about Fenkenstrain?")
|
||||
.item(Items.GHOSTSPEAK_AMULET_552, "You feel power emanate from the Amulet of Ghostspeak", "and the air around you vibrates with the ghostly voice", "of the headless gardener.")
|
||||
.npcl("Oi could tell you a few things about old Fenky, yeah.")
|
||||
.playerl("Go on.")
|
||||
.npcl("Once, this castle were full o' good folk - my friends. Fenky was just the castle doctor, you know, to the lord and the castle folk. I don't know what happened to them all, but one by one they all disappeared.")
|
||||
.npcl("When they were gone a while, I went an dug graves for 'em in the forest. After a while there weren't no one left, but the lord, Fenkenstrain and meself.")
|
||||
.npcl("Old Fenky sent me into the forest to dig 'im a pit - never said what for - then would you believe it, someone chops me 'ead off.")
|
||||
.playerl("Did you see who did it...before...?")
|
||||
.npcl("Before oi kicked the bucket, you mean?")
|
||||
.playerl("Umm...")
|
||||
.npcl("Don't worry yerself. I'm not worried about bein' dead. Worse things could happen, I suppose.")
|
||||
.npcl("One thing I do know is, there ain't no lord of the castle anymore, 'cept for old Fenky. Makes ya think a bit, don't it?")
|
||||
.end()
|
||||
optionBuilder.optionIf("Do you know where the key to the shed is?") { player -> return@optionIf getQuestStage(player, CreatureOfFenkenstrain.questName) == 4 }
|
||||
.item(Items.GHOSTSPEAK_AMULET_552, "You feel power emanate from the Amulet of Ghostspeak", "and the air around you vibrates with the ghostly voice", "of the headless gardener.")
|
||||
.npcl("Got it right 'ere in my pocket. Here you go.")
|
||||
.iteml(4186, "The headless gardener hands you a rusty key.")
|
||||
.endWith { _, player ->
|
||||
addItemOrDrop(player, Items.SHED_KEY_4186)
|
||||
}
|
||||
optionBuilder.optionIf("Do you know where I can find a lightning conductor mould is?") { player -> return@optionIf getQuestStage(player, CreatureOfFenkenstrain.questName) == 4 }
|
||||
.item(Items.GHOSTSPEAK_AMULET_552, "You feel power emanate from the Amulet of Ghostspeak", "and the air around you vibrates with the ghostly voice", "of the headless gardener.")
|
||||
.npcl("A conductor mould, you say? Let me see...")
|
||||
.npcl("There used to be a bloke 'ere, sort of an 'andyman 'e was. Did everything 'round the place - fixed what was broke, swept the chimneys and the like. He would 'ave had a mould, I imagine.")
|
||||
.playerl("Where is he now?")
|
||||
.npcl("E's dead, just like everyone else round 'ere... 'cept for me.")
|
||||
.end()
|
||||
optionBuilder.option("What happened to your head?")
|
||||
.playerl("What happened to your head?")
|
||||
.item(Items.GHOSTSPEAK_AMULET_552, "You feel power emanate from the Amulet of Ghostspeak", "and the air around you vibrates with the ghostly voice", "of the headless gardener.")
|
||||
.npcl("Oi was in the old 'aunted Forest to the south, diggin' a pit for moi old master, old Fenkenstrain, when would you believe it, someone chops me head off. Awful bad luck weren't it?")
|
||||
.playerl("Oh yes, dreadful bad luck.")
|
||||
.npcl("So oi thinks to meself, I don't needs any 'ead to be getting on with me gardenin', long as I got me hands and me spade.")
|
||||
.playerl("Would you show me where the place was?")
|
||||
.npcl("Well, oi s'pose oi've got ten minutes to spare.")
|
||||
.endWith { df, player -> (df.npc!! as GardenerGhostNPC).startFollowing(player) }
|
||||
optionBuilder.optionIf("What's your name?") { player -> return@optionIf getQuestStage(player, CreatureOfFenkenstrain.questName) < 4 }
|
||||
.playerl("What's your name?")
|
||||
.item(Items.GHOSTSPEAK_AMULET_552, "You feel power emanate from the Amulet of Ghostspeak", "and the air around you vibrates with the ghostly voice", "of the headless gardener.")
|
||||
.npcl("Me name? It's been a moivellous long while, mate, since I had any use for such a thing as a name.")
|
||||
.playerl("Don't worry, I was just trying to make conversation.")
|
||||
.npcl("No, no, I can't be havin' that. I'll remember in a minute...")
|
||||
.playerl("Please, don't worry.")
|
||||
.npcl("Lestwit, that's it! Ed Lestwit.")
|
||||
.end()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
package content.region.morytania.quest.creatureoffenkenstrain
|
||||
|
||||
import core.api.teleport
|
||||
import core.game.interaction.MovementPulse
|
||||
import core.game.node.entity.impl.PulseType
|
||||
import core.game.node.entity.npc.AbstractNPC
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.world.map.Location
|
||||
import core.game.world.map.path.Pathfinder
|
||||
import core.plugin.Initializable
|
||||
import core.tools.secondsToTicks
|
||||
import org.rs09.consts.NPCs
|
||||
|
||||
@Initializable
|
||||
class GardenerGhostNPC : AbstractNPC {
|
||||
var target: Player? = null
|
||||
var ticksLeft = secondsToTicks(0)
|
||||
val graveLocation = Location(3608, 3490)
|
||||
|
||||
constructor() : super(NPCs.GARDENER_GHOST_1675, null, true) {}
|
||||
private constructor(id: Int, location: Location) : super(id, location) {}
|
||||
|
||||
override fun construct(id: Int, location: Location, vararg objects: Any?): AbstractNPC {
|
||||
return GardenerGhostNPC(id, location)
|
||||
}
|
||||
|
||||
override fun getIds(): IntArray {
|
||||
return intArrayOf(NPCs.GARDENER_GHOST_1675)
|
||||
}
|
||||
|
||||
override fun handleTickActions() {
|
||||
if (target == null) {
|
||||
super.handleTickActions()
|
||||
} else {
|
||||
val distance = location.getDistance(target!!.location)
|
||||
if (distance > 10) {
|
||||
teleport(this, target!!.location)
|
||||
}
|
||||
if (ticksLeft > 0) {
|
||||
ticksLeft--
|
||||
}
|
||||
|
||||
if (this.location.withinDistance(graveLocation, 5) && ticksLeft > secondsToTicks(5)) {
|
||||
// If gardener is about to reach his grave.
|
||||
ticksLeft = secondsToTicks(4)
|
||||
sendChat("Here is the place where I met me' maker.")
|
||||
}
|
||||
if (!properties.spawnLocation.withinDistance(target!!.location, 160) && ticksLeft > secondsToTicks(5)) {
|
||||
// If person is about to walk outside of the range
|
||||
ticksLeft = secondsToTicks(4)
|
||||
sendChat("Fare thee well - oi must return to me' garden.")
|
||||
}
|
||||
if (ticksLeft == secondsToTicks(5)) {
|
||||
// https://www.youtube.com/watch?v=RoR6zLGrRoY 2:07
|
||||
sendChat("Fare thee well - oi must return to me' garden.")
|
||||
}
|
||||
if (ticksLeft == 0) {
|
||||
// No time left, reset him back.
|
||||
target = null
|
||||
pulseManager.clear(PulseType.STANDARD)
|
||||
walkRadius = 11
|
||||
teleport(properties.spawnLocation)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun startFollowing(player: Player) {
|
||||
ticksLeft = secondsToTicks(120) // You have two minutes to let him show you where his grave is.
|
||||
target = player
|
||||
walkRadius = 200
|
||||
|
||||
pulseManager.run((object : MovementPulse(this, player, Pathfinder.DUMB) {
|
||||
override fun pulse(): Boolean {
|
||||
face(player)
|
||||
return false
|
||||
}
|
||||
}), PulseType.STANDARD)
|
||||
}
|
||||
|
||||
fun continueFollowing(player: Player) {
|
||||
pulseManager.run((object : MovementPulse(this, player, Pathfinder.DUMB) {
|
||||
override fun pulse(): Boolean {
|
||||
face(player)
|
||||
return false
|
||||
}
|
||||
}), PulseType.STANDARD)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
package content.region.morytania.quest.creatureoffenkenstrain
|
||||
|
||||
import core.api.*
|
||||
import core.game.dialogue.DialogueBuilder
|
||||
import core.game.dialogue.DialogueBuilderFile
|
||||
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.plugin.Initializable
|
||||
import org.rs09.consts.NPCs
|
||||
|
||||
@Initializable
|
||||
class LordRologarthDialogue (player: Player? = null) : DialoguePlugin(player) {
|
||||
override fun newInstance(player: Player): DialoguePlugin {
|
||||
return LordRologarthDialogue(player)
|
||||
}
|
||||
override fun handle(interfaceId: Int, buttonId: Int): Boolean {
|
||||
openDialogue(player, LordRologarthDialogueFile(), npc)
|
||||
return false
|
||||
}
|
||||
override fun getIds(): IntArray {
|
||||
// THE IDs ARE WRONG. 1671 and 1672 are varp controlled Lord Rologarth
|
||||
return intArrayOf(NPCs.DR_FENKENSTRAIN_1671, NPCs.DR_FENKENSTRAIN_1672, NPCs.FENKENSTRAINS_MONSTER_1673)
|
||||
}
|
||||
}
|
||||
|
||||
class LordRologarthDialogueFile : DialogueBuilderFile() {
|
||||
override fun create(b: DialogueBuilder) {
|
||||
b.onQuestStages(CreatureOfFenkenstrain.questName, 6)
|
||||
.playerl("I am commanded to destroy you, creature!")
|
||||
.npcl("Oh that's *hic* not very *hic* nice ...")
|
||||
.playerl("Are you feeling ok?")
|
||||
.npcl("Abso *hic* lutely. Never *buuurrp* better.")
|
||||
.playerl("You don't look very dangerous.")
|
||||
.npcl("How *hic* do I look?")
|
||||
.playerl("You really don't know, do you? Have a look for yourself.")
|
||||
.line("The creature stumbles over towards the mirror, focuses upon his", "reflection and...")
|
||||
.npcl("AAAAARRGGGGHHHH!")
|
||||
.line("The creature becomes instantly sober, horror all too evident in his", "undead eyes.")
|
||||
.playerl("I'm sorry. I suppose I'm partly to blame for this.")
|
||||
.npcl("No - it was him I wager - Fenkenstrain - wasn't it? He's brought me back to life!")
|
||||
.playerl("Who are - were - you?")
|
||||
.npcl("I was Rologarth, Lord of the North Coast - this castle was once mine. Fenkenstrain was the castle doctor.")
|
||||
.playerl("So the castle wasn't really abandoned when he found it?")
|
||||
.npcl("Is that what he told you? No, no, this castle was once full of people and life. Fenkenstrain advised me to sell them to the vampyres, which - I am sad to say - I did.")
|
||||
.playerl("I found your brain in a jar in Canifis, so he must have sold you too.")
|
||||
.npcl("Of that I will not speak. There lie memories that should rest with the dead, the living unable to bear them.")
|
||||
.playerl("That's it - I'm leaving this dreadful place, whether I get paid or not. Is there anything I can do for you before I leave?")
|
||||
.npcl("Only one - please stop Fenkenstrain from carrying on his experiments, once and for all, so that no other poor soul has to endure suffering such as that of my people and I.")
|
||||
.endWith() { df, player ->
|
||||
if(getQuestStage(player, CreatureOfFenkenstrain.questName) == 6) {
|
||||
setQuestStage(player, CreatureOfFenkenstrain.questName, 7)
|
||||
}
|
||||
}
|
||||
b.onQuestStages(CreatureOfFenkenstrain.questName, 7)
|
||||
.playerl(FacialExpression.THINKING, "Do you know how I can stop Fenkenstrain's experiments?")
|
||||
.npcl("Take the Ring of Charos from him.")
|
||||
.playerl(FacialExpression.THINKING, "What is this ring?")
|
||||
.npcl("It was my birthright, passed down to me through the ages, its origin forgotten.")
|
||||
.npcl("The Ring of Charos has many powers, but Fenkenstrain has bent them to his down evil purposes. Without the power of the ring, he will not be able to raise the dead from their sleep.")
|
||||
.npcl("It has one other, extremely important use - it confuses the werewolves' senses, making them believe that they smell one of their own kind. Without the ring, Fenkenstrain will be at their mercy.")
|
||||
.end()
|
||||
b.onQuestStages(CreatureOfFenkenstrain.questName, 8, 100)
|
||||
.npcl("How goes it, friend?")
|
||||
.playerl("I stole the Ring of Charos from Fenkenstrain.")
|
||||
.npcl("I saw him climb up into the Tower to hide. It doesn't matter - soon the werewolves will come for him, and his experiments will be forever ceased.")
|
||||
.playerl(FacialExpression.THINKING, "Do you want the ring back? It is yours after all.")
|
||||
.npcl("No, you keep it, my friend. Werewolves hunger for the scent of live flesh - I have no need for the ring. I have my castle back, if not my soul.")
|
||||
.end()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
package content.region.morytania.quest.creatureoffenkenstrain
|
||||
|
||||
import core.api.*
|
||||
import core.game.dialogue.DialogueFile
|
||||
import core.game.dialogue.FacialExpression
|
||||
import core.game.dialogue.Topic
|
||||
import core.game.node.item.Item
|
||||
import core.tools.END_DIALOGUE
|
||||
import core.tools.START_DIALOGUE
|
||||
import org.rs09.consts.Items
|
||||
|
||||
class RoavarDialogueFile(private val dialogueNum: Int = 0) : DialogueFile() {
|
||||
override fun handle(componentID: Int, buttonID: Int) {
|
||||
when (stage) {
|
||||
START_DIALOGUE -> {
|
||||
if (dialogueNum == 1) {
|
||||
npcl("If you've got the money, I've got a real treat for you.")
|
||||
stage = 1
|
||||
} else if (dialogueNum == 2) {
|
||||
npcl("You're interested in our speciality, I see. Would you like to buy some?")
|
||||
stage = 2
|
||||
} else if (dialogueNum == 3) {
|
||||
npcl("You can leave that alone, my friend. I've already sold you one of your own - eat that. I can't afford to give away freebies in this business!")
|
||||
stage = END_DIALOGUE
|
||||
} else {
|
||||
npcl("Hey! Don't touch that.")
|
||||
stage = END_DIALOGUE
|
||||
}
|
||||
}
|
||||
1 -> playerl("What have you got?").also { stage = 3 }
|
||||
2 -> playerl("What exactly is in the jar?").also { stage = 3 }
|
||||
3 -> npcl("Pickled brain, my friend. Only 50 gold to you.").also { stage++ }
|
||||
4 -> playerl("Err... pickled brain from what animal?").also { stage++ }
|
||||
5 -> npcl("Animal? Don't be disgusting, man! No, this a human brain - only the best for my customers.").also { stage++ }
|
||||
6 -> showTopics(
|
||||
Topic(FacialExpression.HALF_GUILTY, "I'll buy one.", 10, true),
|
||||
Topic(FacialExpression.HALF_GUILTY, "I'm not hungry.", 20, true),
|
||||
)
|
||||
10 -> playerl("I'll buy one, please.").also { stage++ }
|
||||
11 -> {
|
||||
if (amountInInventory(player!!, Items.COINS_995) >= 50) {
|
||||
if(removeItem(player!!, Item(Items.COINS_995, 50))) {
|
||||
addItemOrDrop(player!!, Items.PICKLED_BRAIN_4199, 1)
|
||||
npcl("A very wise choice. Don't eat it all at once, savour every morsel - that's my advice to you.")
|
||||
}
|
||||
} else {
|
||||
sendDialogue(player!!, "You do not have enough coins.")
|
||||
}
|
||||
stage = END_DIALOGUE
|
||||
}
|
||||
20 -> playerl("I'm afraid I'm not really hungry at the moment.").also { stage = END_DIALOGUE }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -61,12 +61,12 @@ public class ItemDefinition extends Definition<Item> {
|
|||
/**
|
||||
* The model rotation.
|
||||
*/
|
||||
private int modelRotationY;
|
||||
private int modelRotationX;
|
||||
|
||||
/**
|
||||
* The model rotation.
|
||||
*/
|
||||
private int modelRotationX;
|
||||
private int modelRotationY;
|
||||
|
||||
/**
|
||||
* The model offset.
|
||||
|
|
@ -320,9 +320,9 @@ public class ItemDefinition extends Definition<Item> {
|
|||
} else if (opcode == 4) {
|
||||
def.modelZoom = buffer.getShort() & 0xFFFF;
|
||||
} else if (opcode == 5) {
|
||||
def.modelRotationY = buffer.getShort() & 0xFFFF;
|
||||
} else if (opcode == 6) {
|
||||
def.modelRotationX = buffer.getShort() & 0xFFFF;
|
||||
} else if (opcode == 6) {
|
||||
def.modelRotationY = buffer.getShort() & 0xFFFF;
|
||||
} else if (opcode == 7) {
|
||||
def.modelOffsetX = buffer.getShort() & 0xFFFF;
|
||||
if (def.modelOffsetX > 32767)
|
||||
|
|
@ -495,11 +495,11 @@ public class ItemDefinition extends Definition<Item> {
|
|||
modelOffsetY = templateReference.modelOffsetY;
|
||||
textureColour1 = templateReference.textureColour1;
|
||||
value = reference.value;
|
||||
modelRotationX = templateReference.modelRotationX;
|
||||
modelRotationY = templateReference.modelRotationY;
|
||||
stackable = true;
|
||||
unnoted = false;
|
||||
modifiedModelColors = templateReference.modifiedModelColors;
|
||||
modelRotationY = templateReference.modelRotationY;
|
||||
modelRotationX = templateReference.modelRotationX;
|
||||
modelZoom = templateReference.modelZoom;
|
||||
textureColour1 = templateReference.textureColour1;
|
||||
handlers.put(ItemConfigParser.TRADEABLE, true);
|
||||
|
|
@ -518,8 +518,8 @@ public class ItemDefinition extends Definition<Item> {
|
|||
textureColour2 = reference.textureColour2;
|
||||
groundActions = reference.groundActions;
|
||||
unknownArray1 = reference.unknownArray1;
|
||||
modelRotationY = templateReference.modelRotationY;
|
||||
modelRotationX = templateReference.modelRotationX;
|
||||
modelRotationY = templateReference.modelRotationY;
|
||||
originalModelColors = reference.originalModelColors;
|
||||
name = reference.name;
|
||||
maleWornModelId1 = reference.maleWornModelId1;
|
||||
|
|
@ -548,7 +548,7 @@ public class ItemDefinition extends Definition<Item> {
|
|||
public void transferRecolourDefinition(ItemDefinition reference, ItemDefinition templateReference) {
|
||||
femaleWornModelId2 = reference.femaleWornModelId2;
|
||||
options = new String[5];
|
||||
modelRotationX = templateReference.modelRotationX;
|
||||
modelRotationY = templateReference.modelRotationY;
|
||||
name = reference.name;
|
||||
maleWornModelId1 = reference.maleWornModelId1;
|
||||
modelOffsetY = templateReference.modelOffsetY;
|
||||
|
|
@ -557,7 +557,7 @@ public class ItemDefinition extends Definition<Item> {
|
|||
modelOffsetX = templateReference.modelOffsetX;
|
||||
unknownArray1 = reference.unknownArray1;
|
||||
stackable = reference.stackable;
|
||||
modelRotationY = templateReference.modelRotationY;
|
||||
modelRotationX = templateReference.modelRotationX;
|
||||
textureColour1 = reference.textureColour1;
|
||||
colourEquip1 = reference.colourEquip1;
|
||||
textureColour2 = reference.textureColour2;
|
||||
|
|
@ -803,32 +803,32 @@ public class ItemDefinition extends Definition<Item> {
|
|||
* Gets the modelRotation1.
|
||||
* @return The modelRotation1.
|
||||
*/
|
||||
public int getModelRotation1() {
|
||||
return modelRotationY;
|
||||
public int getModelRotationX() {
|
||||
return modelRotationX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the modelRotation1.
|
||||
* @param modelRotation1 The modelRotation1 to set.
|
||||
*/
|
||||
public void setModelRotation1(int modelRotation1) {
|
||||
this.modelRotationY = modelRotation1;
|
||||
public void setModelRotationX(int modelRotation1) {
|
||||
this.modelRotationX = modelRotation1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the modelRotation2.
|
||||
* @return The modelRotation2.
|
||||
*/
|
||||
public int getModelRotation2() {
|
||||
return modelRotationX;
|
||||
public int getModelRotationY() {
|
||||
return modelRotationY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the modelRotation2.
|
||||
* @param modelRotation2 The modelRotation2 to set.
|
||||
*/
|
||||
public void setModelRotation2(int modelRotation2) {
|
||||
this.modelRotationX = modelRotation2;
|
||||
public void setModelRotationY(int modelRotation2) {
|
||||
this.modelRotationY = modelRotation2;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,6 +1,13 @@
|
|||
package core.game.dialogue
|
||||
|
||||
import core.api.splitLines
|
||||
import core.game.node.entity.player.Player
|
||||
import core.tools.END_DIALOGUE
|
||||
import java.util.regex.Pattern
|
||||
|
||||
val DEBUG_DIALOGUE = true
|
||||
val NUMBER_PATTERN1 = Pattern.compile("^(\\d+) \\[label", Pattern.MULTILINE)
|
||||
val NUMBER_PATTERN2 = Pattern.compile("(\\d+) -> (\\d+)")
|
||||
|
||||
abstract class DialogueBuilderFile : DialogueFile() {
|
||||
var data: ArrayList<DialogueClause> = ArrayList()
|
||||
|
|
@ -8,6 +15,37 @@ abstract class DialogueBuilderFile : DialogueFile() {
|
|||
abstract fun create(b: DialogueBuilder)
|
||||
init {
|
||||
create(DialogueBuilder(this))
|
||||
if(DEBUG_DIALOGUE) {
|
||||
var graphSb = StringBuilder("digraph {\n")
|
||||
var nodeIndex = 0
|
||||
for((i, clause) in data.iterator().withIndex()) {
|
||||
var clauseSb = StringBuilder()
|
||||
for(j in 0 until clause.nodes.size) {
|
||||
if(!(clause.nodes[j] is OptionsDispatchNode)) {
|
||||
clauseSb.append("${j} ${clause.nodes[j].toString().replace("@indexPlus", "${j+1}").replace("@index", "${j}")}\n")
|
||||
}
|
||||
}
|
||||
graphSb.append("subgraph cluster_${i} {\n")
|
||||
var tmpSb = StringBuilder()
|
||||
var m = NUMBER_PATTERN1.matcher(clauseSb)
|
||||
while(m.find()) {
|
||||
var x = Integer.valueOf(m.group(1))
|
||||
m.appendReplacement(tmpSb, "${x+nodeIndex} [label")
|
||||
}
|
||||
m.appendTail(tmpSb)
|
||||
m = NUMBER_PATTERN2.matcher(tmpSb)
|
||||
while(m.find()) {
|
||||
var x = Integer.valueOf(m.group(1))
|
||||
var y = Integer.valueOf(m.group(2))
|
||||
m.appendReplacement(graphSb, "${x+nodeIndex} -> ${y+nodeIndex}")
|
||||
}
|
||||
m.appendTail(graphSb)
|
||||
graphSb.append("}\n")
|
||||
nodeIndex += clause.nodes.size
|
||||
}
|
||||
graphSb.append("}\n")
|
||||
System.out.println(graphSb.toString())
|
||||
}
|
||||
}
|
||||
override fun handle(componentID: Int, buttonID: Int) {
|
||||
for((i, clause) in data.iterator().withIndex()) {
|
||||
|
|
@ -26,43 +64,137 @@ interface DialogueNode {
|
|||
fun handle(df: DialogueFile, componentID: Int, buttonID: Int, stage: Int): Int
|
||||
}
|
||||
|
||||
class NpcLNode(val value: String): DialogueNode {
|
||||
class NpcLNode(val expression: FacialExpression, val value: String): DialogueNode {
|
||||
override fun toString(): String {
|
||||
return "[label=\"npcl(FacialExpression.${expression.name}, \\\"${value}\\\")\"]; @index -> @indexPlus;"
|
||||
}
|
||||
override fun handle(df: DialogueFile, componentID: Int, buttonID: Int, stage: Int): Int {
|
||||
df.npcl(value)
|
||||
df.npcl(expression, value)
|
||||
return stage + 1
|
||||
}
|
||||
}
|
||||
class NpcNode(val values: Array<String>): DialogueNode {
|
||||
class NpcNode(val expression: FacialExpression, val values: Array<String>): DialogueNode {
|
||||
override fun toString(): String {
|
||||
return "[label=\"npc(FacialExpression.${expression.name}, \\\"${values.contentDeepToString()}\\\")\"]; @index -> @indexPlus;"
|
||||
}
|
||||
override fun handle(df: DialogueFile, componentID: Int, buttonID: Int, stage: Int): Int {
|
||||
df.npc(*values)
|
||||
df.npc(expression, *values)
|
||||
return stage + 1
|
||||
}
|
||||
}
|
||||
class PlayerLNode(val value: String): DialogueNode {
|
||||
class ItemNode(val item: Int, val values: Array<String>): DialogueNode {
|
||||
override fun toString(): String {
|
||||
return "[label=\"item(${item}, \\\"${values.contentDeepToString()}\\\")\"]; @index -> @indexPlus;"
|
||||
}
|
||||
override fun handle(df: DialogueFile, componentID: Int, buttonID: Int, stage: Int): Int {
|
||||
df.playerl(value)
|
||||
df.interpreter!!.sendItemMessage(item, *values)
|
||||
return stage + 1
|
||||
}
|
||||
}
|
||||
class ClosureNode(val f: (Player) -> Int): DialogueNode {
|
||||
class PlayerLNode(val expression: FacialExpression, val value: String): DialogueNode {
|
||||
override fun toString(): String {
|
||||
return "[label=\"playerl(FacialExpression.${expression.name}, \\\"${value}\\\")\"]; @index -> @indexPlus;"
|
||||
}
|
||||
override fun handle(df: DialogueFile, componentID: Int, buttonID: Int, stage: Int): Int {
|
||||
return f(df.player!!)
|
||||
df.playerl(expression, value)
|
||||
return stage + 1
|
||||
}
|
||||
}
|
||||
class PlayerNode(val expression: FacialExpression, val values: Array<String>): DialogueNode {
|
||||
override fun toString(): String {
|
||||
return "[label=\"player(FacialExpression.${expression.name}, \\\"${values.contentDeepToString()}\\\")\"]; @index -> @indexPlus;"
|
||||
}
|
||||
override fun handle(df: DialogueFile, componentID: Int, buttonID: Int, stage: Int): Int {
|
||||
df.player(expression, *values)
|
||||
return stage + 1
|
||||
}
|
||||
}
|
||||
class BetweenStageNode(val f: (DialogueFile, Player, Int, Int) -> Unit): DialogueNode {
|
||||
override fun toString(): String {
|
||||
return "[label=\"betweenstage(${(f as java.lang.Object).getClass().getName()})\"]; @index -> @indexPlus"
|
||||
}
|
||||
override fun handle(df: DialogueFile, componentID: Int, buttonID: Int, stage: Int): Int {
|
||||
df.stage = stage + 1 // Simulates moving to this stage.
|
||||
f(df, df.player!!, componentID, buttonID) // Calls callback function.
|
||||
df.handle(componentID, buttonID) // Handles the current stage
|
||||
return stage + 2 // Skips over the current stage.
|
||||
}
|
||||
}
|
||||
class ManualStageNode(val f: (DialogueFile, Player, Int, Int) -> Unit): DialogueNode {
|
||||
override fun toString(): String {
|
||||
return "[label=\"manualstage(${(f as java.lang.Object).getClass().getName()})\"]; @index -> @indexPlus"
|
||||
}
|
||||
override fun handle(df: DialogueFile, componentID: Int, buttonID: Int, stage: Int): Int {
|
||||
f(df, df.player!!, componentID, buttonID)
|
||||
return stage + 1
|
||||
}
|
||||
}
|
||||
class ManualStageWithGotoNode(val f: (DialogueFile, Player, Int, Int, Int) -> Int, g: (Int) -> Int): DialogueNode {
|
||||
override fun toString(): String {
|
||||
return "[label=\"manualstagewithgoto(${(f as java.lang.Object).getClass().getName()})\"]; @index -> @indexPlus"
|
||||
}
|
||||
override fun handle(df: DialogueFile, componentID: Int, buttonID: Int, stage: Int): Int {
|
||||
return f(df, df.player!!, componentID, buttonID, stage)
|
||||
}
|
||||
}
|
||||
|
||||
class PlaceholderNode(val dbf: DialogueBuilderFile, val clauseIndex: Int, var targetStage: Int): DialogueNode {
|
||||
override fun toString(): String {
|
||||
return "[label=\"placeholder(${targetStage})\"]; @index -> ${targetStage}"
|
||||
}
|
||||
override fun handle(df: DialogueFile, componentID: Int, buttonID: Int, stage: Int): Int {
|
||||
df.stage = targetStage
|
||||
df.handle(componentID, buttonID)
|
||||
return df.stage
|
||||
}
|
||||
|
||||
fun builder(): DialogueBuilder {
|
||||
targetStage = dbf.data[clauseIndex].nodes.size
|
||||
return DialogueBuilder(dbf, clauseIndex)
|
||||
}
|
||||
}
|
||||
|
||||
class GotoNode(val node: PlaceholderNode): DialogueNode {
|
||||
override fun toString(): String {
|
||||
return "[label=\"goto(${node.targetStage})\"]; @index -> ${node.targetStage}"
|
||||
}
|
||||
override fun handle(df: DialogueFile, componentID: Int, buttonID: Int, stage: Int): Int {
|
||||
df.stage = node.targetStage
|
||||
df.handle(componentID, buttonID)
|
||||
return df.stage
|
||||
}
|
||||
}
|
||||
|
||||
class OptionEntry(val text: String, val nextStage: Int, val predicate: (Player) -> Boolean = { _ -> true }) {}
|
||||
class OptionsData(val header: String, val entries: ArrayList<OptionEntry>, var attr: String? = null) {}
|
||||
|
||||
class OptionsNode(var options: ArrayList<OptionEntry>): DialogueNode {
|
||||
class OptionsNode(var options: OptionsData): DialogueNode {
|
||||
override fun toString(): String {
|
||||
var ret = "[label=\"options(\\\"${options.header}\\\")\"]; "
|
||||
for(entry in options.entries) {
|
||||
ret += "@index -> ${entry.nextStage} [label=\"\\\"${entry.text}\\\"\"]; "
|
||||
}
|
||||
return ret
|
||||
}
|
||||
fun optionNames(player: Player): Array<String?> {
|
||||
return options.asSequence().filter({ it.predicate(player) }).map({ it.text }).toList().toTypedArray()
|
||||
return options.entries.asSequence().filter({ it.predicate(player) }).map({ it.text }).toList().toTypedArray()
|
||||
}
|
||||
override fun handle(df: DialogueFile, componentID: Int, buttonID: Int, stage: Int): Int {
|
||||
val tmp: Array<String?> = optionNames(df.player!!)
|
||||
if(tmp.size > 1) {
|
||||
df.options(*tmp)
|
||||
if(DEBUG_DIALOGUE) {
|
||||
System.out.println("OptionsNode: ${tmp.size}")
|
||||
for(i in 0 until tmp.size) {
|
||||
System.out.println("tmp[${i}]: ${tmp[i]}")
|
||||
}
|
||||
}
|
||||
if(tmp.size > 5) {
|
||||
System.out.printf("Only a maximum of 5 options are supported by the interface")
|
||||
return END_DIALOGUE
|
||||
} else if(tmp.size > 1) {
|
||||
df.interpreter!!.sendOptions(options.header, *tmp)
|
||||
return stage + 1
|
||||
} else if(tmp.size == 1) {
|
||||
val tmp: List<OptionEntry> = options.asSequence().filter({ it.predicate(df.player!!) }).toList()
|
||||
val tmp: List<OptionEntry> = options.entries.asSequence().filter({ it.predicate(df.player!!) }).toList()
|
||||
df.stage = tmp[0].nextStage
|
||||
df.handle(componentID, 0)
|
||||
return df.stage
|
||||
|
|
@ -71,9 +203,18 @@ class OptionsNode(var options: ArrayList<OptionEntry>): DialogueNode {
|
|||
}
|
||||
}
|
||||
}
|
||||
class OptionsDispatchNode(var options: ArrayList<OptionEntry>): DialogueNode {
|
||||
class OptionsDispatchNode(var options: OptionsData): DialogueNode {
|
||||
override fun toString(): String {
|
||||
return "[label=\"OptionsDispatchNode\"]"
|
||||
}
|
||||
override fun handle(df: DialogueFile, componentID: Int, buttonID: Int, stage: Int): Int {
|
||||
val tmp: List<OptionEntry> = options.asSequence().filter({ it.predicate(df.player!!) }).toList()
|
||||
val tmp: List<OptionEntry> = options.entries.asSequence().filter({ it.predicate(df.player!!) }).toList()
|
||||
if(DEBUG_DIALOGUE) {
|
||||
System.out.println("OptionsDispatchNode: ${tmp.size}")
|
||||
}
|
||||
if(options.attr != null) {
|
||||
df.player!!.setAttribute(options.attr, buttonID-1)
|
||||
}
|
||||
df.stage = tmp[buttonID-1].nextStage
|
||||
df.handle(componentID, 0)
|
||||
return df.stage
|
||||
|
|
@ -90,19 +231,70 @@ class DialogueClause(val predicate: (player: Player) -> Boolean, val nodes: Arra
|
|||
}
|
||||
}
|
||||
|
||||
class DialogueOptionsBuilder(var target: DialogueBuilderFile, val clauseIndex: Int, var options: ArrayList<OptionEntry>) {
|
||||
class DialogueOptionsBuilder(var target: DialogueBuilderFile, val clauseIndex: Int, var options: OptionsData) {
|
||||
fun option(value: String): DialogueBuilder {
|
||||
options.add(OptionEntry(value, target.data[clauseIndex].nodes.size))
|
||||
options.entries.add(OptionEntry(value, target.data[clauseIndex].nodes.size))
|
||||
return DialogueBuilder(target, clauseIndex)
|
||||
}
|
||||
|
||||
fun optionIf(value: String, predicate: (Player) -> Boolean): DialogueBuilder {
|
||||
options.add(OptionEntry(value, target.data[clauseIndex].nodes.size, predicate))
|
||||
options.entries.add(OptionEntry(value, target.data[clauseIndex].nodes.size, predicate))
|
||||
return DialogueBuilder(target, clauseIndex)
|
||||
}
|
||||
|
||||
fun option_playerl(value: String): DialogueBuilder {
|
||||
return option(value).playerl(value)
|
||||
}
|
||||
|
||||
fun recordAttribute(name: String): DialogueOptionsBuilder {
|
||||
options.attr = name
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
class BranchesData(val branches: HashMap<Int, Int>, val f: (Player) -> Int) {}
|
||||
class BranchNode(val branches: BranchesData): DialogueNode {
|
||||
override fun toString(): String {
|
||||
var ret = "[label=\"branch(\\\"${(branches.f as Object).getClass().getName()}\\\")\"]; "
|
||||
for(entry in branches.branches) {
|
||||
ret += "@index -> ${entry.value} [label=\"\\\"${entry.key}\\\"\"]; "
|
||||
}
|
||||
return ret
|
||||
}
|
||||
override fun handle(df: DialogueFile, componentID: Int, buttonID: Int, stage: Int): Int {
|
||||
df.stage = branches.branches[branches.f(df.player!!)] ?: END_DIALOGUE
|
||||
df.handle(componentID, buttonID)
|
||||
return df.stage
|
||||
}
|
||||
}
|
||||
class DialogueBranchBuilder(var target: DialogueBuilderFile, val clauseIndex: Int, var branches: BranchesData) {
|
||||
fun onValue(value: Boolean): DialogueBuilder {
|
||||
return onValue(if(value) { 1 } else { 0 })
|
||||
}
|
||||
fun onValue(value: Int): DialogueBuilder {
|
||||
val index = target.data[clauseIndex].nodes.size
|
||||
branches.branches.put(value, index)
|
||||
return DialogueBuilder(target, clauseIndex)
|
||||
}
|
||||
}
|
||||
|
||||
class EndWithNode(val f: (DialogueFile, Player) -> Unit): DialogueNode {
|
||||
override fun toString(): String {
|
||||
return "[label=\"endWith(${(f as java.lang.Object).getClass().getName()})\"]";
|
||||
}
|
||||
override fun handle(df: DialogueFile, componentID: Int, buttonID: Int, stage: Int): Int {
|
||||
f(df, df.player!!)
|
||||
return END_DIALOGUE
|
||||
}
|
||||
}
|
||||
|
||||
class DialogueBuilder(var target: DialogueBuilderFile, var clauseIndex: Int = -1) {
|
||||
companion object DialogueBuilderStatic {
|
||||
@JvmStatic
|
||||
fun endWithNoop(df: DialogueFile, player: Player) {
|
||||
}
|
||||
}
|
||||
|
||||
fun onPredicate(predicate: (player: Player) -> Boolean): DialogueBuilder {
|
||||
target.data.add(DialogueClause(predicate, ArrayList()))
|
||||
clauseIndex = target.data.size - 1
|
||||
|
|
@ -115,34 +307,104 @@ class DialogueBuilder(var target: DialogueBuilderFile, var clauseIndex: Int = -1
|
|||
}
|
||||
}
|
||||
fun playerl(value: String): DialogueBuilder {
|
||||
target.data[clauseIndex].nodes.add(PlayerLNode(value))
|
||||
target.data[clauseIndex].nodes.add(PlayerLNode(FacialExpression.NEUTRAL, value))
|
||||
return this
|
||||
}
|
||||
fun playerl(expression: FacialExpression, value: String): DialogueBuilder {
|
||||
target.data[clauseIndex].nodes.add(PlayerLNode(expression, value))
|
||||
return this
|
||||
}
|
||||
fun player(vararg values: String): DialogueBuilder {
|
||||
target.data[clauseIndex].nodes.add(PlayerNode(FacialExpression.NEUTRAL, values as Array<String>))
|
||||
return this
|
||||
}
|
||||
fun player(expression: FacialExpression, vararg values: String): DialogueBuilder {
|
||||
target.data[clauseIndex].nodes.add(PlayerNode(expression, values as Array<String>))
|
||||
return this
|
||||
}
|
||||
fun npcl(value: String): DialogueBuilder {
|
||||
target.data[clauseIndex].nodes.add(NpcLNode(value))
|
||||
target.data[clauseIndex].nodes.add(NpcLNode(FacialExpression.NEUTRAL, value))
|
||||
return this
|
||||
}
|
||||
fun npcl(expression: FacialExpression, value: String): DialogueBuilder {
|
||||
target.data[clauseIndex].nodes.add(NpcLNode(expression, value))
|
||||
return this
|
||||
}
|
||||
fun npc(vararg values: String): DialogueBuilder {
|
||||
target.data[clauseIndex].nodes.add(NpcNode(values as Array<String>))
|
||||
target.data[clauseIndex].nodes.add(NpcNode(FacialExpression.NEUTRAL, values as Array<String>))
|
||||
return this
|
||||
}
|
||||
fun endWith(f: (Player) -> Unit) {
|
||||
target.data[clauseIndex].nodes.add(ClosureNode({ player ->
|
||||
f(player)
|
||||
return@ClosureNode END_DIALOGUE
|
||||
}))
|
||||
fun npc(expression: FacialExpression, vararg values: String): DialogueBuilder {
|
||||
target.data[clauseIndex].nodes.add(NpcNode(expression, values as Array<String>))
|
||||
return this
|
||||
}
|
||||
fun item(item: Int, vararg values: String): DialogueBuilder {
|
||||
target.data[clauseIndex].nodes.add(ItemNode(item, values as Array<String>))
|
||||
return this
|
||||
}
|
||||
fun iteml(item: Int, value: String): DialogueBuilder {
|
||||
return item(item, *splitLines(value))
|
||||
}
|
||||
fun line(vararg values: String): DialogueBuilder {
|
||||
return manualStage() { df, player, _, _ ->
|
||||
df.interpreter!!.sendDialogue(*(values as Array<String>))
|
||||
}
|
||||
}
|
||||
fun linel(value: String): DialogueBuilder {
|
||||
return manualStage() { df, player, _, _ ->
|
||||
df.interpreter!!.sendDialogue(*splitLines(value))
|
||||
}
|
||||
}
|
||||
fun endWith(f: (DialogueFile, Player) -> Unit) {
|
||||
target.data[clauseIndex].nodes.add(EndWithNode(f))
|
||||
}
|
||||
fun end() {
|
||||
target.data[clauseIndex].nodes.add(ClosureNode({ _ ->
|
||||
return@ClosureNode END_DIALOGUE
|
||||
}))
|
||||
target.data[clauseIndex].nodes.add(EndWithNode(::endWithNoop))
|
||||
}
|
||||
/** Runs block without the need for a DialogueFile df.something call. */
|
||||
fun betweenStage(f: (DialogueFile, Player, Int, Int) -> Unit): DialogueBuilder {
|
||||
target.data[clauseIndex].nodes.add(BetweenStageNode(f))
|
||||
return this
|
||||
}
|
||||
fun manualStage(f: (DialogueFile, Player, Int, Int) -> Unit): DialogueBuilder {
|
||||
target.data[clauseIndex].nodes.add(ManualStageNode(f))
|
||||
return this
|
||||
}
|
||||
fun manualStageWithGoto(f: (DialogueFile, Player, Int, Int, Int) -> Int, g: (Int) -> Int): DialogueBuilder {
|
||||
target.data[clauseIndex].nodes.add(ManualStageWithGotoNode(f, g))
|
||||
return this
|
||||
}
|
||||
fun options(): DialogueOptionsBuilder {
|
||||
var options: ArrayList<OptionEntry> = ArrayList()
|
||||
val node = OptionsNode(options)
|
||||
val dispatchNode = OptionsDispatchNode(options)
|
||||
return options("Select an Option")
|
||||
}
|
||||
fun options(header: String): DialogueOptionsBuilder {
|
||||
val options: ArrayList<OptionEntry> = ArrayList()
|
||||
val optionsData = OptionsData(header, options)
|
||||
val node = OptionsNode(optionsData)
|
||||
val dispatchNode = OptionsDispatchNode(optionsData)
|
||||
target.data[clauseIndex].nodes.add(node)
|
||||
target.data[clauseIndex].nodes.add(dispatchNode)
|
||||
return DialogueOptionsBuilder(target, clauseIndex, options)
|
||||
return DialogueOptionsBuilder(target, clauseIndex, optionsData)
|
||||
}
|
||||
|
||||
fun placeholder(): PlaceholderNode {
|
||||
val node = PlaceholderNode(target, clauseIndex, END_DIALOGUE)
|
||||
target.data[clauseIndex].nodes.add(node)
|
||||
return node
|
||||
}
|
||||
|
||||
fun goto(node: PlaceholderNode): DialogueBuilder {
|
||||
target.data[clauseIndex].nodes.add(GotoNode(node))
|
||||
return this
|
||||
}
|
||||
|
||||
fun branch(f: (Player) -> Int): DialogueBranchBuilder {
|
||||
var branches = BranchesData(HashMap(), f)
|
||||
target.data[clauseIndex].nodes.add(BranchNode(branches))
|
||||
return DialogueBranchBuilder(target, clauseIndex, branches)
|
||||
}
|
||||
|
||||
fun branchBoolAttribute(attrName: String, defaultVal: Boolean): DialogueBranchBuilder {
|
||||
return branch({ player -> return@branch if(player.getAttribute(attrName, defaultVal)) { 1 } else { 0 } })
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ import java.util.ArrayList;
|
|||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static core.tools.DialogueConstKt.END_DIALOGUE;
|
||||
|
||||
|
|
@ -323,13 +325,61 @@ public final class DialogueInterpreter {
|
|||
* @param itemId The item id.
|
||||
*/
|
||||
public Component sendItemMessage(int itemId, String... messages) {
|
||||
player.getInterfaceManager().openChatbox(131);
|
||||
if(1 <= messages.length && messages.length < 4) {
|
||||
ArrayList<String> packedMessages = new ArrayList();
|
||||
for(int i = 0; i < messages.length/2; i++) {
|
||||
packedMessages.add(messages[i] + "<br>" + messages[i+1]);
|
||||
}
|
||||
if(messages.length % 2 == 1) {
|
||||
packedMessages.add(messages[messages.length-1]);
|
||||
}
|
||||
|
||||
int interfaceId = 241 + (packedMessages.size() - 1);
|
||||
player.getInterfaceManager().openChatbox(interfaceId);
|
||||
player.getPacketDispatch().sendItemOnInterface(itemId, 1, interfaceId, 1);
|
||||
ItemDefinition itemDef = ItemDefinition.forId(itemId);
|
||||
player.getPacketDispatch().sendAngleOnInterface(interfaceId, 1, itemDef.getModelZoom() / 2, itemDef.getModelRotationX(), itemDef.getModelRotationY());
|
||||
player.getPacketDispatch().sendString("", interfaceId, 3);
|
||||
for(int i = 0; i < packedMessages.size(); i++) {
|
||||
//System.out.printf("sendItemMessage[%d]: %s\n", i, packedMessages[i]);
|
||||
player.getPacketDispatch().sendString(packedMessages.get(i), interfaceId, 4+i);
|
||||
}
|
||||
} else {
|
||||
int interfaceId = 131;
|
||||
//int interfaceId = 173;
|
||||
//int interfaceId = 519;
|
||||
//int interfaceId = 757;
|
||||
//int interfaceId = 760;
|
||||
player.getInterfaceManager().openChatbox(interfaceId);
|
||||
String message = messages[0];
|
||||
for (int i = 1; i < messages.length; i++) {
|
||||
message += "<br>" + messages[i];
|
||||
}
|
||||
switch(interfaceId) {
|
||||
case 131:
|
||||
player.getPacketDispatch().sendString(message, 131, 1);
|
||||
player.getPacketDispatch().sendItemOnInterface(itemId, 1, 131, 2);
|
||||
break;
|
||||
case 173:
|
||||
player.getPacketDispatch().sendString(message, 173, 4);
|
||||
player.getPacketDispatch().sendString("", 173, 3);
|
||||
player.getPacketDispatch().sendItemOnInterface(itemId, 1, 173, 1);
|
||||
break;
|
||||
case 519:
|
||||
player.getPacketDispatch().sendString(message, 519, 1);
|
||||
player.getPacketDispatch().sendItemOnInterface(itemId, 1, 519, 0);
|
||||
break;
|
||||
case 757:
|
||||
player.getPacketDispatch().sendString(message, 757, 2);
|
||||
player.getPacketDispatch().sendString("", 757, 1);
|
||||
player.getPacketDispatch().sendItemOnInterface(itemId, 1, 757, 0);
|
||||
break;
|
||||
case 760:
|
||||
player.getPacketDispatch().sendString(message, 760, 0);
|
||||
player.getPacketDispatch().sendItemOnInterface(itemId, 1, 760, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return player.getInterfaceManager().getChatbox();
|
||||
}
|
||||
|
||||
|
|
@ -337,14 +387,7 @@ public final class DialogueInterpreter {
|
|||
* Send a message with an item next to it.
|
||||
*/
|
||||
public Component sendItemMessage(final Item item, String... messages) {
|
||||
player.getInterfaceManager().openChatbox(131);
|
||||
String message = messages[0];
|
||||
for (int i = 1; i < messages.length; i++) {
|
||||
message += "<br>" + messages[i];
|
||||
}
|
||||
player.getPacketDispatch().sendString(message, 131, 1);
|
||||
player.getPacketDispatch().sendItemOnInterface(item.getId(), item.getAmount(), 131, 2);
|
||||
return player.getInterfaceManager().getChatbox();
|
||||
return sendItemMessage(item.getId(), messages);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -455,6 +498,19 @@ public final class DialogueInterpreter {
|
|||
return sendDialogues(npcId, expression == null ? -1 : expression.getAnimationId(), messages);
|
||||
}
|
||||
|
||||
static Pattern GENDERED_SUBSTITUTION = Pattern.compile("@g\\[([^,]*),([^\\]]*)\\]");
|
||||
public static String doSubstitutions(Player player, String msg) {
|
||||
msg = msg.replace("@name", player.getUsername());
|
||||
StringBuilder sb = new StringBuilder();
|
||||
Matcher m = GENDERED_SUBSTITUTION.matcher(msg);
|
||||
int index = player.isMale() ? 1 : 2;
|
||||
while(m.find()) {
|
||||
m.appendReplacement(sb, m.group(index));
|
||||
}
|
||||
m.appendTail(sb);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send dialogues based on the amount of specified messages.
|
||||
* @param npcId The npc id.
|
||||
|
|
@ -474,6 +530,7 @@ public final class DialogueInterpreter {
|
|||
}
|
||||
player.getPacketDispatch().sendAnimationInterface(expression, interfaceId, 2);
|
||||
if (npc) {
|
||||
player.getPacketDispatch().sendItemOnInterface(-1, 1, interfaceId, 1);
|
||||
player.getPacketDispatch().sendNpcOnInterface(npcId, interfaceId, 2);
|
||||
player.getPacketDispatch().sendString(NPCDefinition.forId(npcId).getName(), interfaceId, 3);
|
||||
} else {
|
||||
|
|
@ -481,7 +538,7 @@ public final class DialogueInterpreter {
|
|||
player.getPacketDispatch().sendString(player.getUsername(), interfaceId, 3);
|
||||
}
|
||||
for (int i = 0; i < messages.length; i++) {
|
||||
player.getPacketDispatch().sendString(messages[i].toString().replace("@name", player.getUsername()), interfaceId, (i + 4));
|
||||
player.getPacketDispatch().sendString(doSubstitutions(player, messages[i].toString()), interfaceId, (i + 4));
|
||||
}
|
||||
player.getInterfaceManager().openChatbox(interfaceId);
|
||||
player.getPacketDispatch().sendInterfaceConfig(player.getInterfaceManager().getChatbox().getId(), 3, false);
|
||||
|
|
|
|||
|
|
@ -4,9 +4,11 @@ package core.game.dialogue
|
|||
* Topic/IfTopic system backported from my personal project
|
||||
* @author Ceikry
|
||||
*/
|
||||
open class Topic<T>(val expr: FacialExpression, val text: String, val toStage: T, val skipPlayer: Boolean = false) {
|
||||
open class Topic<T> @JvmOverloads constructor(val expr: FacialExpression, val text: String, val toStage: T, val skipPlayer: Boolean = false) {
|
||||
@JvmOverloads
|
||||
constructor(text: String, toStage: T, skipPlayer: Boolean = false) : this(FacialExpression.ASKING, text, toStage, skipPlayer)
|
||||
}
|
||||
class IfTopic<T>(expr: FacialExpression, text: String, toStage: T, val showCondition: Boolean, skipPlayer: Boolean = false) : Topic<T>(expr, text, toStage, skipPlayer) {
|
||||
constructor(text: String, toStage: T, showCondition: Boolean, skipPlayer: Boolean = false) : this(FacialExpression.ASKING, text, toStage, showCondition, skipPlayer)
|
||||
class IfTopic<T> @JvmOverloads constructor(expr: FacialExpression, text: String, toStage: T, val showCondition: Boolean, skipPlayer: Boolean = false) : Topic<T>(expr, text, toStage, skipPlayer) {
|
||||
@JvmOverloads
|
||||
constructor(text: String, toStage: T, showCondition: Boolean, skipPlayer: Boolean = false) : this(core.game.dialogue.FacialExpression.ASKING, text, toStage, showCondition, skipPlayer)
|
||||
}
|
||||
|
|
@ -145,6 +145,12 @@ public abstract class Quest implements Plugin<Object> {
|
|||
playJingle(player, questJingles[new Random().nextInt(3)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the quest. This is called when ::setQuestStage is set to 0.
|
||||
* Useful to override and reset quest player attributes.
|
||||
*/
|
||||
public void reset(Player player) {}
|
||||
|
||||
/**
|
||||
* Draws a line on the journal component.
|
||||
* @param player The player.
|
||||
|
|
|
|||
|
|
@ -35,8 +35,13 @@ class QuestCommandSet : CommandSet(Privilege.ADMIN){
|
|||
}
|
||||
val quest = args[1].toIntOrNull() ?: reject(player,"INVALID QUEST")
|
||||
val stage = args[2].toIntOrNull() ?: reject(player,"INVALID STAGE")
|
||||
player.questRepository.setStageNonmonotonic(player.questRepository.forIndex(quest as Int), stage as Int)
|
||||
notify(player, "<col=209dff>Setting " + player.questRepository.forIndex(quest).name + " to stage $stage</col>")
|
||||
val questObject = player.questRepository.forIndex(quest as Int)
|
||||
player.questRepository.setStageNonmonotonic(questObject, stage as Int)
|
||||
if (stage == 0){
|
||||
questObject.reset(player)
|
||||
}
|
||||
questObject.updateVarps(player)
|
||||
notify(player, "<col=209dff>Setting " + questObject.name + " to stage $stage</col>")
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue