diff --git a/data/area/morytania/canifis/canifis.teles.toml b/data/area/morytania/canifis/canifis.teles.toml
index 62f0600b1b..e5dfc25d02 100644
--- a/data/area/morytania/canifis/canifis.teles.toml
+++ b/data/area/morytania/canifis/canifis.teles.toml
@@ -23,12 +23,3 @@ option = "Climb-down"
tile = { x = 3422, y = 3484 }
to = { x = 3440, y = 9888 }
-[canifis_tavern_trapdoor]
-option = "Open"
-tile = { x = 3495, y = 3465 }
-to = { x = 3477, y = 9845 }
-
-[canifis_tavern_ladder]
-option = "Climb-up"
-tile = { x = 3477, y = 9846 }
-to = { x = 3495, y = 3466 }
diff --git a/data/area/morytania/meiyerditch/tunnels/meiyerditch_tunnels.npcs.toml b/data/area/morytania/meiyerditch/tunnels/meiyerditch_tunnels.npcs.toml
index f77a436c22..19d16f2afd 100644
--- a/data/area/morytania/meiyerditch/tunnels/meiyerditch_tunnels.npcs.toml
+++ b/data/area/morytania/meiyerditch/tunnels/meiyerditch_tunnels.npcs.toml
@@ -4,10 +4,12 @@ examine = "Looks like the leader of the Myreque."
[sani_piliu_meiyerditch_tunnels]
id = 1570
+hitpoints = 300
examine = "A pretty young rogue."
[harold_evans_meiyerditch_tunnels]
id = 1571
+hitpoints = 300
examine = "Hot headed, big built youth, who trained in the militia."
[ivan_strom_meiyerditch_tunnels]
diff --git a/data/area/morytania/mort_myre_swamp/mort_myre_swamp.areas.toml b/data/area/morytania/mort_myre_swamp/mort_myre_swamp.areas.toml
index 8a6ad20879..3843efe87c 100644
--- a/data/area/morytania/mort_myre_swamp/mort_myre_swamp.areas.toml
+++ b/data/area/morytania/mort_myre_swamp/mort_myre_swamp.areas.toml
@@ -23,3 +23,8 @@ tags = ["no_cannon", "no_cannon_damp"]
[filliman_grotto]
x = [3433, 3447]
y = [3331, 3343]
+
+# The rope bridge over the Hollows, from the tile the southern tree lands on up to the top rung
+[mort_myre_rope_bridge]
+x = [3502, 3502]
+y = [3427, 3430]
diff --git a/data/quest/members/in_search_of_the_myreque/in_search_of_the_myreque.anims.toml b/data/quest/members/in_search_of_the_myreque/in_search_of_the_myreque.anims.toml
new file mode 100644
index 0000000000..010a9db128
--- /dev/null
+++ b/data/quest/members/in_search_of_the_myreque/in_search_of_the_myreque.anims.toml
@@ -0,0 +1,33 @@
+[squeeze_past_stalagmite]
+id = 3278
+
+# Curpile's warning jab, and the punch that puts the player out cold
+[curpile_punch]
+id = 422
+
+[curpile_knockout_punch]
+id = 390
+
+# Also used for Sani and Harold going down in Vanstrom's ambush
+[myreque_knockout]
+id = 837
+
+# Vanstrom's ambush in the hideout
+[vanstrom_transform]
+id = 8594
+
+[vanstrom_hover] #9855 in 667
+id = 1500
+
+[vanstrom_summon]
+id = 4707
+
+# Vanstrom's pet shares the hellhound skeleton's attack, block and death
+[skeleton_hellhound_attack]
+id = 6562
+
+[skeleton_hellhound_defend]
+id = 6563
+
+[skeleton_hellhound_death]
+id = 6564
diff --git a/data/quest/members/in_search_of_the_myreque/in_search_of_the_myreque.combat.toml b/data/quest/members/in_search_of_the_myreque/in_search_of_the_myreque.combat.toml
new file mode 100644
index 0000000000..eab162cd62
--- /dev/null
+++ b/data/quest/members/in_search_of_the_myreque/in_search_of_the_myreque.combat.toml
@@ -0,0 +1,11 @@
+[skeleton_hellhound]
+defend_anim = "skeleton_hellhound_defend"
+death_anim = "skeleton_hellhound_death"
+death_sound = "skeleton_hellhound_death"
+
+[skeleton_hellhound.melee]
+range = 1
+anim = "skeleton_hellhound_attack"
+sound = "skeleton_hellhound_attack"
+target_sound = "skeleton_hellhound_hit"
+target_hit = { offense = "crush", max = 120 }
diff --git a/data/quest/members/in_search_of_the_myreque/in_search_of_the_myreque.drops.toml b/data/quest/members/in_search_of_the_myreque/in_search_of_the_myreque.drops.toml
new file mode 100644
index 0000000000..a7d0cb5ebe
--- /dev/null
+++ b/data/quest/members/in_search_of_the_myreque/in_search_of_the_myreque.drops.toml
@@ -0,0 +1,6 @@
+[skeleton_hellhound_drop_table]
+type = "all"
+drops = [
+ { id = "big_bones", amount = 4 },
+ { id = "uncut_ruby", amount = 2 },
+]
diff --git a/data/quest/members/in_search_of_the_myreque/in_search_of_the_myreque.gfx.toml b/data/quest/members/in_search_of_the_myreque/in_search_of_the_myreque.gfx.toml
new file mode 100644
index 0000000000..b2f81a166a
--- /dev/null
+++ b/data/quest/members/in_search_of_the_myreque/in_search_of_the_myreque.gfx.toml
@@ -0,0 +1,11 @@
+[vanstrom_transform_graphic]
+id = 785
+
+[vanstrom_summon_graphic]
+id = 731
+
+[curpile_knockout_stars]
+id = 245
+
+[skeleton_hellhound_summon]
+id = 86
diff --git a/data/quest/members/in_search_of_the_myreque/in_search_of_the_myreque.npcs.toml b/data/quest/members/in_search_of_the_myreque/in_search_of_the_myreque.npcs.toml
index 82ecf7a37a..609ecbcd50 100644
--- a/data/quest/members/in_search_of_the_myreque/in_search_of_the_myreque.npcs.toml
+++ b/data/quest/members/in_search_of_the_myreque/in_search_of_the_myreque.npcs.toml
@@ -33,7 +33,26 @@ str = 110
def = 100
style = "crush"
max_hit_melee = 120
+combat_def = "skeleton_hellhound"
+attack_speed = 4
hunt_mode = "cowardly"
slayer_xp = 55.0
categories = ["hellhounds"]
+drop_table = "skeleton_hellhound"
examine = "A creature summoned by Vanstrom to kill the remaining Myreque."
+
+# Vanstrom's forms during the Myreque hideout cutscene
+[route_vanstrom_vampire_misty]
+id = 1578
+dialogue = "old"
+
+[route_vanstrom]
+id = 1579
+
+[route_vanstrom_vampire]
+id = 1580
+dialogue = "old"
+
+[route_vanstrom_vampire_flying]
+id = 1581
+dialogue = "old"
diff --git a/data/quest/members/in_search_of_the_myreque/in_search_of_the_myreque.objs.toml b/data/quest/members/in_search_of_the_myreque/in_search_of_the_myreque.objs.toml
new file mode 100644
index 0000000000..558f505638
--- /dev/null
+++ b/data/quest/members/in_search_of_the_myreque/in_search_of_the_myreque.objs.toml
@@ -0,0 +1,17 @@
+[swamp_bridge1]
+id = 5002
+
+[spooky_tree_base_forbridge]
+id = 5003
+
+[route_cavewalltunnel]
+id = 5046
+
+[route_stalagmite_cave_entrace]
+id = 5050
+
+[freedomfighterundergrounddooropenl]
+id = 30263
+
+[freedomfighterundergrounddooropenr]
+id = 30264
diff --git a/data/quest/members/in_search_of_the_myreque/in_search_of_the_myreque.sounds.toml b/data/quest/members/in_search_of_the_myreque/in_search_of_the_myreque.sounds.toml
new file mode 100644
index 0000000000..afc880f1b8
--- /dev/null
+++ b/data/quest/members/in_search_of_the_myreque/in_search_of_the_myreque.sounds.toml
@@ -0,0 +1,17 @@
+[vanstrom_mist]
+id = 1186
+
+[vanstrom_appear]
+id = 1190
+
+[vanstrom_vampire]
+id = 1194
+
+[skeleton_hellhound_attack]
+id = 1187
+
+[skeleton_hellhound_hit]
+id = 1185
+
+[skeleton_hellhound_death]
+id = 1188
diff --git a/data/quest/members/in_search_of_the_myreque/in_search_of_the_myreque.varbits.toml b/data/quest/members/in_search_of_the_myreque/in_search_of_the_myreque.varbits.toml
new file mode 100644
index 0000000000..1aa70a2dae
--- /dev/null
+++ b/data/quest/members/in_search_of_the_myreque/in_search_of_the_myreque.varbits.toml
@@ -0,0 +1,46 @@
+[routequestionscorrect]
+id = 173
+persist = true
+format = "int"
+
+[routequestionsanswered]
+id = 174
+persist = true
+format = "int"
+
+[roomnum]
+id = 175
+persist = true
+format = "boolean"
+
+# The three rungs of the rope bridge over the Hollows, south to north
+[bridgerung1]
+id = 176
+persist = true
+format = "boolean"
+
+[bridgerung2]
+id = 177
+persist = true
+format = "boolean"
+
+[bridgerung3]
+id = 178
+persist = true
+format = "boolean"
+
+# Swaps the stranger in the 'Hair of the Dog' between Vanstrom Klause and a plain stranger
+[thsfm_vanstrom_hide]
+id = 396
+persist = true
+format = "boolean"
+
+[thsfm_spoken_allmyreque]
+id = 2496
+persist = true
+format = "boolean"
+
+[route_bridgecomplete]
+id = 3928
+persist = true
+format = "boolean"
diff --git a/data/quest/members/in_search_of_the_myreque/in_search_of_the_myreque.varps.toml b/data/quest/members/in_search_of_the_myreque/in_search_of_the_myreque.varps.toml
index 941693bdce..623d216ad4 100644
--- a/data/quest/members/in_search_of_the_myreque/in_search_of_the_myreque.varps.toml
+++ b/data/quest/members/in_search_of_the_myreque/in_search_of_the_myreque.varps.toml
@@ -3,4 +3,4 @@ id = 387
persist = true
format = "map"
default = "unstarted"
-values = { unstarted = 0, started = 1 }
+values = { unstarted = 0, agreed_to_help = 5, refused_delivery = 10, persuaded_boatman = 15, gave_planks = 20, reached_hollows = 25, questioned_by_curpile = 52, answered_questions = 55, entered_hideout = 60, met_veliaf = 65, weapons_accepted = 67, delivered_weapons = 70, hellhound_summoned = 80, killed_hellhound = 85, shown_way_out = 90, in_inn_basement = 95, escaped_to_canifis = 97, completed = 105 }
diff --git a/data/quest/members/in_search_of_the_myreque/in_search_of_the_myreque.vars.toml b/data/quest/members/in_search_of_the_myreque/in_search_of_the_myreque.vars.toml
new file mode 100644
index 0000000000..3a77c48ff4
--- /dev/null
+++ b/data/quest/members/in_search_of_the_myreque/in_search_of_the_myreque.vars.toml
@@ -0,0 +1,20 @@
+# Which members of the Myreque the player has introduced themselves to
+[met_sani]
+persist = true
+format = "boolean"
+
+[met_harold]
+persist = true
+format = "boolean"
+
+[met_ivan]
+persist = true
+format = "boolean"
+
+[met_radigad]
+persist = true
+format = "boolean"
+
+[met_polmafi]
+persist = true
+format = "boolean"
diff --git a/data/quest/quests.toml b/data/quest/quests.toml
index 026c80bf55..43e6d35430 100644
--- a/data/quest/quests.toml
+++ b/data/quest/quests.toml
@@ -1958,7 +1958,7 @@ req_items = "Steel longsword, 2 steel short swords, steel mace, steel warhammer,
req_combat = "You will need to defeat a level 44 enemy."
points = 2
reward = "Access to the Hair of the Dog's cellar
Swamp boat shortcut from Mort'ton to The Hollows
Access to 2 uncut rubies and 4 big bones"
-xp = "600 Attack XP lamp
600 Strength XP lamp
600 Crafting XP lamp"
+xp = "600 Attack XP
600 Defence XP
600 Strength XP
600 Constitution XP
600 Crafting XP"
[jungle_potion]
id = 66
diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/map/zone/DynamicZones.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/map/zone/DynamicZones.kt
index c29dabaf16..57f3c7f4db 100644
--- a/engine/src/main/kotlin/world/gregs/voidps/engine/map/zone/DynamicZones.kt
+++ b/engine/src/main/kotlin/world/gregs/voidps/engine/map/zone/DynamicZones.kt
@@ -51,6 +51,25 @@ class DynamicZones(
update = true
}
+ /**
+ * Copies a block of zones, for content that only needs part of a region - a room to stage a
+ * cutscene in, say - rather than all sixty four zones of one.
+ * @param from The south west zone of the block to copy
+ * @param to The south west zone the block is copied to
+ * @param width How many zones wide the block is
+ * @param height How many zones high the block is
+ * @param levels How many levels of it to copy, counting up from the level of [from] and [to]
+ */
+ fun copy(from: Zone, to: Zone, width: Int, height: Int, levels: Int = 1) {
+ for (x in 0 until width) {
+ for (y in 0 until height) {
+ for (level in 0 until levels) {
+ copy(Zone(from.x + x, from.y + y, from.level + level), Zone(to.x + x, to.y + y, to.level + level))
+ }
+ }
+ }
+ }
+
/**
* @param from The region to be copied
* @param to The region to be replaced
diff --git a/engine/src/test/kotlin/world/gregs/voidps/engine/map/zone/DynamicZonesTest.kt b/engine/src/test/kotlin/world/gregs/voidps/engine/map/zone/DynamicZonesTest.kt
index 7b8196ca5a..3a7abfb5df 100644
--- a/engine/src/test/kotlin/world/gregs/voidps/engine/map/zone/DynamicZonesTest.kt
+++ b/engine/src/test/kotlin/world/gregs/voidps/engine/map/zone/DynamicZonesTest.kt
@@ -37,6 +37,27 @@ internal class DynamicZonesTest {
assertEquals(131140, zones.dynamicZone(zone))
}
+ @Test
+ fun `Copy a block of zones to another`() {
+ val from = Zone(4, 4)
+ val to = Zone(8, 8)
+ zones.copy(from, to, width = 2, height = 3, levels = 2)
+
+ assertTrue(zones.dynamic(to.region))
+ for (x in 0 until 2) {
+ for (y in 0 until 3) {
+ for (level in 0 until 2) {
+ assertNotNull(
+ zones.dynamicZone(Zone(to.x + x, to.y + y, level)),
+ "zone $x, $y, $level of the block",
+ )
+ }
+ }
+ }
+ assertNull(zones.dynamicZone(Zone(to.x + 2, to.y, 0)), "nothing outside the block is copied")
+ assertNull(zones.dynamicZone(Zone(to.x, to.y, 2)), "and no levels above it")
+ }
+
@Test
fun `Copy one region to another`() {
val from = Region(8, 8)
diff --git a/game/src/main/kotlin/content/area/morytania/canifis/CanifisTavern.kt b/game/src/main/kotlin/content/area/morytania/canifis/CanifisTavern.kt
index fe0fa85b72..ae4e89c794 100644
--- a/game/src/main/kotlin/content/area/morytania/canifis/CanifisTavern.kt
+++ b/game/src/main/kotlin/content/area/morytania/canifis/CanifisTavern.kt
@@ -1,7 +1,6 @@
package content.area.morytania.canifis
import content.entity.obj.door.DoubleDoor
-import content.entity.obj.door.enterDoor
import world.gregs.voidps.engine.Script
import world.gregs.voidps.engine.entity.character.move.tele
import world.gregs.voidps.engine.entity.character.sound
@@ -9,10 +8,6 @@ import world.gregs.voidps.engine.entity.obj.replace
class CanifisTavern : Script {
init {
- objectOperate("Search", "canifis_fake_wall_closed") { (target) ->
- enterDoor(target)
- }
-
objectOperate("Open", "canifis_wooden_door_left_closed,canifis_wooden_door_right_closed") { (target) ->
anim("take")
sound("locked")
diff --git a/game/src/main/kotlin/content/area/morytania/canifis/VanstromKlause.kt b/game/src/main/kotlin/content/area/morytania/canifis/VanstromKlause.kt
new file mode 100644
index 0000000000..5402d77d87
--- /dev/null
+++ b/game/src/main/kotlin/content/area/morytania/canifis/VanstromKlause.kt
@@ -0,0 +1,174 @@
+package content.area.morytania.canifis
+
+import content.entity.player.dialogue.Happy
+import content.entity.player.dialogue.Neutral
+import content.entity.player.dialogue.Quiz
+import content.entity.player.dialogue.Sad
+import content.entity.player.dialogue.type.ChoiceOption
+import content.entity.player.dialogue.type.choice
+import content.entity.player.dialogue.type.npc
+import content.entity.player.dialogue.type.player
+import content.quest.member.myreque.myrequeStage
+import content.quest.questStage
+import world.gregs.voidps.engine.Script
+import world.gregs.voidps.engine.entity.character.player.Player
+
+class VanstromKlause : Script {
+ init {
+ npcOperate("Talk-to", "stranger_2") {
+ if (questStage("in_search_of_the_myreque") == myrequeStage("unstarted")) {
+ npc("Hello there, how goes it stranger?")
+ firstMeetingChoices()
+ } else {
+ npc("Hello again, how's the quest going?")
+ returningChoices()
+ }
+ }
+ }
+
+ private suspend fun Player.firstMeetingChoices() {
+ player("Quite well thanks for asking, how about you?")
+ npc("Hmm, well, I am a little concerned about some friends of mine, they're in dire need of some assistance, but I'm at a loss of how I can help them.")
+ initialQuestionChoice()
+ }
+
+ suspend fun Player.initialQuestionChoice() {
+ choice {
+ whatFriends()
+ whyHelp()
+ tooBusy()
+ option("Ok, thanks.")
+ }
+ }
+
+ suspend fun Player.extendedQuestionChoice() {
+ choice {
+ whatFriends(extended = true)
+ whyHelp()
+ tooBusy()
+ offerToHelp()
+ option("Ok, thanks.")
+ }
+ }
+
+ fun ChoiceOption.whatFriends(extended: Boolean = false): Unit = option("What friends are these?") {
+ npc("It's a personal tragedy that I have yet to meet them in the flesh. But their exploits make mouth watering hero stories...the real meat and drink of high adventure and daring...so they say.")
+ player("What does that mean exactly? I mean, I have some stories. I'm quite a hero myself, you may actually be talking about me?")
+ npc("They're regarded as heroes in Morytania, though some people see them as vigilantes. The local villagers call them the 'Myreque'. Some people call them terrorists while others call them freedom fighters!")
+
+ if (extended) {
+ extendedQuestionChoice()
+ } else {
+ initialQuestionChoice()
+ }
+ }
+
+ fun ChoiceOption.whyHelp(): Unit = option("Why do they need help? Are they in trouble?") {
+ npc("I should imagine that heroes of such high calibre are almost always in some sort of trouble, wouldn't you? There's always some evil heel ready to grind the face of humanity into the dirt?")
+ npc("However, the Myreque are almost certainly able to handle themselves...given the tools! I hear they're short of weapons, I was hoping to do it myself but I find that I'm rather short of time and ability.")
+ player("What help do you hope to give them?")
+ npc("I'd have taken some weapons to them!")
+ player("What kind of weapons do they need?")
+ npc("Steel I believe. All six of them require steel weapons. I would have suggested a longsword, two shortswords, a dagger, a mace and a warhammer.")
+ extendedQuestionChoice()
+ }
+
+ fun ChoiceOption.tooBusy(): Unit = option("I wish I could help, but I'm busy at the moment.") {
+ npc("Hmm, the same as me I fear! Oh well, I'm sure they'll get on well by themselves, the little dears. Thanks for even considering it though.")
+ }
+
+ fun ChoiceOption.offerToHelp() {
+ option("Perhaps I could help you out here.") {
+ player("Perhaps I could help you out here. Maybe I could take these weapons to the Myreque.")
+ npc("Oh yes, well that would be very nice of you! Are you sure you want to help out?")
+ helpDetailsChoice()
+ }
+ }
+
+ suspend fun Player.helpDetailsChoice() {
+ choice {
+ whatToDo()
+ whatWeapons()
+ whereToTake()
+ agreeToHelp()
+ declineHelp()
+ }
+ }
+
+ fun ChoiceOption.whatToDo(): Unit = option("What would I have to do?") {
+ npc("Well, some freedom fighter, called the Myreque, require some weapons. You'd need to get the weapons they require and give them to the freedom fighters.")
+ helpDetailsChoice()
+ }
+
+ fun ChoiceOption.whatWeapons(): Unit = option("What weapons do I need to get?") {
+ npc("Well, I'm sure the following will be fine. A longsword, two shortswords, a dagger, a mace and a warhammer, all the items should be made of steel.")
+ npc("You would also have to fund the costs of this yourself and once you've found them, I'll be sure to give you a nice surprise in return for your help.")
+ helpDetailsChoice()
+ }
+
+ fun ChoiceOption.whereToTake(): Unit = option("Where do I need to take the weapons?") {
+ npc("I believe there's a boatman in Mort'ton who can show you the way, I'm sure if you use your powers of persuasion you can get some information out of him! The Myreque have a lot of sympathisers.")
+ helpDetailsChoice()
+ }
+
+ fun ChoiceOption.agreeToHelp(): Unit = option("Yes, I'll do it!") {
+ set("in_search_of_the_myreque", "agreed_to_help")
+ npc("That's great news my friend, really great news! Perhaps the many peoples of Morytania now have an additional hero that they can come to rely upon?")
+ }
+
+ fun ChoiceOption.declineHelp(): Unit = option("Sorry, I can't do it!") {
+ npc("Oh well, that is a shame, I'm sorry to hear it. But if you change your mind, please visit me again.")
+ }
+
+ private suspend fun Player.returningChoices() {
+ choice {
+ needHelp()
+ farewell()
+ forgotTask()
+ }
+ }
+
+ fun ChoiceOption.needHelp(): Unit = option("Not very well, I need some help.") {
+ npc("Oh dear... what do you need help with?")
+ helpQuestionsChoice()
+ }
+
+ suspend fun Player.helpQuestionsChoice() {
+ choice {
+ whereMyreque()
+ whatWeaponsAgain()
+ howKnowMyreque()
+ anyoneElse()
+ farewell()
+ }
+ }
+
+ fun ChoiceOption.whereMyreque(): Unit = option("Where are the Myreque?") {
+ npc("You know what? I'm not exactly sure. The only thing I've heard, and it is a rumour, but the boatman in Mort'ton might be able to help. But again, it is only a rumour.")
+ helpQuestionsChoice()
+ }
+
+ fun ChoiceOption.whatWeaponsAgain(): Unit = option("What weapons am I supposed to get again?") {
+ npc("Well, I'm sure the following will be fine. A longsword, two shortswords, a dagger, a mace and a warhammer, all the items should be made of steel.")
+ helpQuestionsChoice()
+ }
+
+ fun ChoiceOption.howKnowMyreque(): Unit = option("How do you know 'The Myreque'?") {
+ npc("Actually you know, I don't know them at all! I've never actually met them, but would love to at some point! However, I'm sure that you'll be much more able than I at tracking them down.")
+ helpQuestionsChoice()
+ }
+
+ fun ChoiceOption.anyoneElse(): Unit = option("Is there anyone else who can help me?") {
+ npc("Hmm. Apart from the boatman at Mor'ton, I have no clue my friend.")
+ helpQuestionsChoice()
+ }
+
+ fun ChoiceOption.forgotTask(): Unit = option("What am I supposed to do again?") {
+ npc("You've forgotten already? Ha! That's funny! But I do understand, my memory isn't what it was!")
+ npc("It would be great if you could get some steel weapons and take them to the Myreque, the boatman in Mort'ton should be able to help you find them. But you may need to be a bit persuasive!")
+ }
+
+ fun ChoiceOption.farewell(): Unit = option("Ok, thanks.") {
+ npc("Well, if you have any problems please feel free to come and chat with me.")
+ }
+}
diff --git a/game/src/main/kotlin/content/area/morytania/mort_myre_swamp/CurpileFyod.kt b/game/src/main/kotlin/content/area/morytania/mort_myre_swamp/CurpileFyod.kt
new file mode 100644
index 0000000000..79c296af74
--- /dev/null
+++ b/game/src/main/kotlin/content/area/morytania/mort_myre_swamp/CurpileFyod.kt
@@ -0,0 +1,250 @@
+package content.area.morytania.mort_myre_swamp
+
+import content.entity.combat.hit.damage
+import content.entity.player.dialogue.Angry
+import content.entity.player.dialogue.Happy
+import content.entity.player.dialogue.Neutral
+import content.entity.player.dialogue.Quiz
+import content.entity.player.dialogue.type.ChoiceOption
+import content.entity.player.dialogue.type.choice
+import content.entity.player.dialogue.type.npc
+import content.entity.player.dialogue.type.player
+import content.entity.player.dialogue.type.statement
+import content.quest.member.myreque.hasAllWeapons
+import content.quest.member.myreque.myrequeStage
+import content.quest.questStage
+import world.gregs.voidps.engine.Script
+import world.gregs.voidps.engine.client.message
+import world.gregs.voidps.engine.client.ui.close
+import world.gregs.voidps.engine.client.ui.open
+import world.gregs.voidps.engine.entity.character.move.tele
+import world.gregs.voidps.engine.entity.character.npc.NPC
+import world.gregs.voidps.engine.entity.character.player.Player
+import world.gregs.voidps.type.random
+
+class CurpileFyod : Script {
+ init {
+ npcOperate("Talk-to", "curpile_fyod_mort_myre_swamp") { (curpile) ->
+ val progress = questStage("in_search_of_the_myreque")
+ when {
+ progress < myrequeStage("reached_hollows") -> {
+ npc("Hey...you's tresspassin' be off wiv ya.")
+ }
+ progress == myrequeStage("reached_hollows") || progress == myrequeStage("questioned_by_curpile") -> {
+ npc("Hey...what're you doin' here?")
+ initialChoice(curpile)
+ }
+ progress > myrequeStage("questioned_by_curpile") -> {
+ npc("Hey...leave me be...I'm guardin' this place.")
+ }
+ }
+ }
+ }
+
+ suspend fun Player.initialChoice(curpile: NPC) {
+ choice {
+ comeToHelp(curpile)
+ dontHaveToAnswer(curpile)
+ noReason(curpile)
+ askForWayOut(curpile)
+ haveToGo()
+ }
+ }
+
+ fun ChoiceOption.comeToHelp(curpile: NPC): Unit = option("I've come to help the Myreque, I've brought weapons.") {
+ if (!hasAllWeapons()) {
+ npc("It doesn't look as if you have the right weapons to me. Get out of here you fake!")
+ return@option
+ }
+ npc("Ok, I see ya got da weapons...but how'd I know you're not gonna use em against my friends?")
+ player("But I just want to help deliver these weapons.")
+ npc("Well, dat's as maybe, and I'm not doubting your sincerity here, you seems all sincered up to me...it's choking me up right here, you're making me cry...but hey, I's godda do my job or da kids don't get fed! Ok,")
+ if (questStage("in_search_of_the_myreque") == myrequeStage("reached_hollows")) {
+ set("in_search_of_the_myreque", "questioned_by_curpile")
+ }
+ npc("so say I asks you a few questions and you were to answer them all correct and so on, well that'd make me believe you's...I'd get to feeling that you was the real deal an all. How's dat sound?")
+ player("Sounds fine to me...go ahead, shoot!")
+ npc("Hey...don't tempt me! You's dicing wiv death here my friend!")
+ runQuiz(curpile)
+ }
+
+ fun ChoiceOption.dontHaveToAnswer(curpile: NPC): Unit = option("I don't have to answer to you!") {
+ npc("You do if you know what's good for you!")
+ sendPunch(curpile)
+ }
+
+ fun ChoiceOption.noReason(curpile: NPC): Unit = option("No reason!") {
+ npc("Come on...speak up! I may be able to help you. However, yous gotta know that your tresspassing and that by rights I should slap your around a bit, you know, just like as a warning.")
+ initialChoice(curpile)
+ }
+
+ fun ChoiceOption.askForWayOut(curpile: NPC): Unit = option("I'm lost, can you show me the way out?") {
+ npc("You got yourself in here... now get yourself out.. and quickly if I were you.")
+ initialChoice(curpile)
+ }
+
+ fun ChoiceOption.haveToGo(): Unit = option("I have to go.") {
+ npc("Sure...be on your way stranger.")
+ }
+
+ private suspend fun Player.runQuiz(curpile: NPC) {
+ val asked = (0..QUESTIONS).shuffled(random).take(3)
+ var correct = 0
+
+ for ((index, q) in asked.withIndex()) {
+ val prefix = when (index) {
+ 0 -> "Ok, first question."
+ 1 -> "Ok, second question."
+ else -> "Ok, third and final question."
+ }
+ if (askQuestion(q, prefix)) {
+ correct++
+ }
+ val ack = when (index) {
+ 0 -> "Ok, interesting answer. First question answered."
+ 1 -> "An interesting response. Second question answered."
+ else -> "Hmm, a calculated retort. Third question answered."
+ }
+ npc(ack)
+ }
+
+ if (correct == 3) {
+ set("in_search_of_the_myreque", "answered_questions")
+ npc("Ok, I believes ya...you can go on.")
+ player("What's the combination to the door?")
+ npc("Oh, there isn't one. I'll unlock it for you.")
+ } else {
+ val word = if (correct == 1) "question" else "questions"
+ npc("I'm sorry but you got $correct $word correct! Go on get outta here...")
+ knockOutPlayer(curpile, correct)
+ }
+ }
+
+ private suspend fun Player.askQuestion(questionIndex: Int, prefix: String): Boolean = when (questionIndex) {
+ 0 -> {
+ npc("$prefix What is the boatman's name?")
+ askBoatmanName()
+ }
+ 1 -> {
+ npc("$prefix Who is the leader of the Myreque?")
+ askMyrequeLeader()
+ }
+ 2 -> {
+ npc("$prefix Name the only female member of the Myreque.")
+ askFemaleMember()
+ }
+ 3 -> {
+ npc("$prefix Who is the youngest member of the Myreque?")
+ askYoungestMember()
+ }
+ 4 -> {
+ npc("$prefix What family is rumoured to rule Morytania?")
+ askRulingFamily()
+ }
+ else -> {
+ npc("$prefix Which member of the 'Myreque' originally had a profession as a scholar?")
+ askScholar()
+ }
+ }
+
+ private suspend fun Player.askBoatmanName(): Boolean {
+ var correct = false
+ choice("What is the boatman's name?") {
+ option("Geof Paddleman") {}
+ option("Cyreg Paddlebone") {}
+ option("Gyrec Paddlehorn") {}
+ option("Cyreg Paddlehorn") { correct = true }
+ option("I don't know!") {}
+ }
+ return correct
+ }
+
+ private suspend fun Player.askMyrequeLeader(): Boolean {
+ var correct = false
+ choice("Who is the leader of the Myreque?") {
+ option("Sani Piliu") {}
+ option("Harold Evans") {}
+ option("Veliaf Hurtz") { correct = true }
+ option("Radigad Ponfit") {}
+ option("Don't know!") {}
+ }
+ return correct
+ }
+
+ private suspend fun Player.askFemaleMember(): Boolean {
+ var correct = false
+ choice("Name the only female member of the Myreque?") {
+ option("Sani Piliu") { correct = true }
+ option("Santi Peliou") {}
+ option("Sani Peridou") {}
+ option("Sandi Pherimou") {}
+ option("Don't know!") {}
+ }
+ return correct
+ }
+
+ private suspend fun Player.askYoungestMember(): Boolean {
+ var correct = false
+ choice("Who is the youngest member of the Myreque?") {
+ option("Sani Piliu") {}
+ option("Ivan Strom") { correct = true }
+ option("Veliaf Hurtz") {}
+ option("Radigad Ponfit") {}
+ option("Don't know!") {}
+ }
+ return correct
+ }
+
+ private suspend fun Player.askRulingFamily(): Boolean {
+ var correct = false
+ choice("What family is rumoured to rule Morytania?") {
+ option("Drunken") {}
+ option("Drakan") { correct = true }
+ option("Draynor") {}
+ option("Praymore") {}
+ option("I don't know!") {}
+ }
+ return correct
+ }
+
+ private suspend fun Player.askScholar(): Boolean {
+ var correct = false
+ choice("Who was previously a scholar?") {
+ option("Sani Piliu") {}
+ option("Harold Evans") {}
+ option("Radogad Ponfit") {}
+ option("Polmafi Ferdygris") { correct = true }
+ option("Don't know!") {}
+ }
+ return correct
+ }
+
+ private suspend fun Player.sendPunch(curpile: NPC) {
+ curpile.anim("curpile_punch")
+ delay(1)
+ damage(30, source = curpile)
+ }
+
+ private suspend fun Player.knockOutPlayer(curpile: NPC, correct: Int) {
+ val word = if (correct == 1) "question" else "questions"
+ message("Curpile Fyod: I'm sorry but you got $correct $word correct! Go on get outta here...")
+ curpile.anim("curpile_knockout_punch")
+ gfx("curpile_knockout_stars")
+ delay(2)
+ anim("myreque_knockout")
+ delay(1)
+ open("fade_out")
+ delay(3)
+ clearAnim()
+ tele(3522, 3285)
+ delay(2)
+ close("fade_out")
+ open("fade_in")
+ delay(3)
+ statement("Curpile knocked you out...You wake up back in Mort'ton...")
+ }
+
+ private companion object {
+ private const val QUESTIONS = 5
+ }
+}
diff --git a/game/src/main/kotlin/content/area/morytania/mort_myre_swamp/HaroldEvans.kt b/game/src/main/kotlin/content/area/morytania/mort_myre_swamp/HaroldEvans.kt
new file mode 100644
index 0000000000..24c73f9129
--- /dev/null
+++ b/game/src/main/kotlin/content/area/morytania/mort_myre_swamp/HaroldEvans.kt
@@ -0,0 +1,82 @@
+package content.area.morytania.mort_myre_swamp
+
+import content.entity.player.dialogue.Confused
+import content.entity.player.dialogue.Happy
+import content.entity.player.dialogue.Neutral
+import content.entity.player.dialogue.Quiz
+import content.entity.player.dialogue.type.ChoiceOption
+import content.entity.player.dialogue.type.choice
+import content.entity.player.dialogue.type.npc
+import content.entity.player.dialogue.type.player
+import content.quest.member.myreque.myrequeStage
+import content.quest.questStage
+import world.gregs.voidps.engine.Script
+import world.gregs.voidps.engine.client.message
+import world.gregs.voidps.engine.entity.character.player.Player
+import world.gregs.voidps.engine.entity.character.player.name
+
+class HaroldEvans : Script {
+ init {
+ npcOperate("Talk-to", "harold_evans_meiyerditch_tunnels") {
+ when (questStage("in_search_of_the_myreque")) {
+ in myrequeStage("delivered_weapons")..myrequeStage("hellhound_summoned") -> {
+ message("This person isn't able to talk to you at this time.")
+ }
+ myrequeStage("entered_hideout") -> {
+ npc("Hail and well met friend! You'd better go and introduce yourself to Veliaf. But after come and have a chat if you've a mind to.")
+ }
+ myrequeStage("met_veliaf"), myrequeStage("weapons_accepted") -> {
+ if (!get("met_harold", false)) {
+ set("met_harold", true)
+ npc("Well met friend! My name's Harold...nice to meet a fellow soldier. Just joined us have you?")
+ player("Not exactly! I'd like to ask a few questions if that's Ok?")
+ soldierResponse()
+ } else {
+ npc("Well met $name! Here's to your strong sword arm!")
+ player("Do you mind if I ask a few questions?")
+ soldierResponse()
+ }
+ }
+ }
+ }
+ }
+
+ private suspend fun Player.soldierResponse() {
+ npc("Sure go ahead, I'll answer as best I can.")
+ questionsChoice()
+ }
+
+ suspend fun Player.questionsChoice() {
+ choice {
+ whatsYourJob()
+ tellMeAboutSquad()
+ graspOfSituation()
+ heardAnyRumors()
+ option("Ok, thanks.")
+ }
+ }
+
+ fun ChoiceOption.whatsYourJob(): Unit = option("What's your job here?") {
+ npc("I generally do what Veliaf orders...taking a bridge defending a tactical retreat...you name it, I'm usually in the thick of it. It's funny, I seem to cheat death most of the time.")
+ questionsChoice()
+ }
+
+ fun ChoiceOption.tellMeAboutSquad(): Unit = option("What can you tell me about your squad?") {
+ npc("They're all good soldiers, though they're pretty much a bunch of irregulars. I consider them a good squad, ready to leap into the fray if needed, but we're seriously outnumbered by the Drakans and their blood.")
+ npc("Most fights we have to improvise but this bunch are very good at that. That young lad over there, Ivan Strom, he saved our bacon in the last encounter by his faith in Saradomin, he weakened the Juves and we ran for it!")
+ player("Juves? What are they?")
+ npc("It's short for 'juvenile', that's the stage that those vampires were at in the forest. They've only recently become vampiric and they're pretty inexperienced.")
+ npc("However, they're stronger and faster than many well trained militia men, and twice as blood thirsty.")
+ questionsChoice()
+ }
+
+ fun ChoiceOption.graspOfSituation(): Unit = option("What's your grasp of the situation soldier?") {
+ npc("The situation is grave, it would be dangerous to think differently. We're really up against it here, but we have no choice, this is a battle for our right to exist and we need to employ any tactics to achieve our goal.")
+ questionsChoice()
+ }
+
+ fun ChoiceOption.heardAnyRumors(): Unit = option("Have you heard any rumors?") {
+ npc("Yes, I hear them all the time, thankfully I don't pay any attention to them, though.")
+ questionsChoice()
+ }
+}
diff --git a/game/src/main/kotlin/content/area/morytania/mort_myre_swamp/IvanStrom.kt b/game/src/main/kotlin/content/area/morytania/mort_myre_swamp/IvanStrom.kt
new file mode 100644
index 0000000000..82a520e7b1
--- /dev/null
+++ b/game/src/main/kotlin/content/area/morytania/mort_myre_swamp/IvanStrom.kt
@@ -0,0 +1,85 @@
+package content.area.morytania.mort_myre_swamp
+
+import content.entity.player.dialogue.Angry
+import content.entity.player.dialogue.Happy
+import content.entity.player.dialogue.Neutral
+import content.entity.player.dialogue.Quiz
+import content.entity.player.dialogue.Sad
+import content.entity.player.dialogue.type.ChoiceOption
+import content.entity.player.dialogue.type.choice
+import content.entity.player.dialogue.type.npc
+import content.entity.player.dialogue.type.player
+import content.quest.member.myreque.myrequeStage
+import content.quest.questStage
+import world.gregs.voidps.engine.Script
+import world.gregs.voidps.engine.client.message
+import world.gregs.voidps.engine.entity.character.player.Player
+
+class IvanStrom : Script {
+ init {
+ npcOperate("Talk-to", "ivan_strom_meiyerditch_tunnels") {
+ when (questStage("in_search_of_the_myreque")) {
+ in myrequeStage("delivered_weapons")..myrequeStage("hellhound_summoned") -> {
+ message("This person isn't able to talk to you at this time.")
+ }
+ myrequeStage("entered_hideout") -> {
+ npc("Oh...er... hello...greetings that is...sorry... you need to speak to Veliaf first. I'm not sure if I'm supposed to be talking to you.")
+ }
+ myrequeStage("met_veliaf"), myrequeStage("weapons_accepted") -> {
+ if (get("met_ivan", false)) {
+ npc("Oh, hello again. How are you?")
+ questionsChoice()
+ } else {
+ firstMeeting()
+ }
+ }
+ else -> aftermath()
+ }
+ }
+ }
+
+ private suspend fun Player.firstMeeting() {
+ player("Hi, Veliaf said that I should introduce myself to all members of the Myreque.")
+ set("met_ivan", true)
+ npc("Ok, Veliaf says it's ok for me to talk to you. My name is Ivan, nice to meet you.")
+ player("Would you mind if I ask you a few questions?")
+ npc("Yeah, I suppose so, but I don't know how much I can help.")
+ questionsChoice()
+ }
+
+ suspend fun Player.questionsChoice() {
+ choice {
+ whatAreYouDoing()
+ tellMeAboutMyreque()
+ aboutMorytania()
+ heardRumors()
+ option("Ok, thanks.")
+ }
+ }
+
+ fun ChoiceOption.whatAreYouDoing(): Unit = option("What are you doing here?") {
+ npc("I'm not sure really, I should really be at the temple to Saradomin, but Veliaf says that the trip's too dangerous. I am a trainee cleric of Saradomin. I hope one day to serve him as faithfully as all his priests.")
+ questionsChoice()
+ }
+
+ fun ChoiceOption.tellMeAboutMyreque(): Unit = option("What can you tell me about the Myreque?") {
+ npc("Well, they're certainly brave and they're prepared to put up a fight against the evil of the Drakan clan. I'm proud to be associated with them, but with my poor skills I worry that I will let them down.")
+ questionsChoice()
+ }
+
+ fun ChoiceOption.aboutMorytania(): Unit = option("What do you know about Morytania?") {
+ npc("It's a lost land ruled by the evil Drakans. People cling to each other in fear every week when the blood tithes are to be paid. With Saradomin's help we'll bring light back to this dark land.")
+ questionsChoice()
+ }
+
+ fun ChoiceOption.heardRumors(): Unit = option("Have you heard any rumors?") {
+ npc("I'm sorry my friend, but I spend most of my free time in prayer to Saradomin. I prefer to cleanse my soul in case he should wish that I serve him closer.")
+ questionsChoice()
+ }
+
+ private suspend fun Player.aftermath() {
+ npc("Oh, thank Saradomin you managed to kill that awful beast! Oh my, I'm in so much shock after seeing Sani and Harold killed right in front of my own eyes.")
+ player("I'm so sorry for your loss. If there's anything I can do, please let me know.")
+ npc("I think you've done enough already, bringing that monster Vanstrom here!")
+ }
+}
diff --git a/game/src/main/kotlin/content/area/morytania/mort_myre_swamp/MortMyreSwamp.kt b/game/src/main/kotlin/content/area/morytania/mort_myre_swamp/MortMyreSwamp.kt
deleted file mode 100644
index 94c7fc17b1..0000000000
--- a/game/src/main/kotlin/content/area/morytania/mort_myre_swamp/MortMyreSwamp.kt
+++ /dev/null
@@ -1,37 +0,0 @@
-package content.area.morytania.mort_myre_swamp
-
-import world.gregs.voidps.engine.Script
-import world.gregs.voidps.engine.client.message
-import world.gregs.voidps.engine.entity.character.move.tele
-import world.gregs.voidps.engine.entity.character.sound
-import world.gregs.voidps.engine.entity.obj.replace
-import world.gregs.voidps.type.Direction
-import world.gregs.voidps.type.Tile
-
-class MortMyreSwamp : Script {
- init {
- objectOperate("Open", "swamp_wooden_doors_closed") { (target) ->
- anim("take")
- sound("locked")
- delay(1)
- target.replace("swamp_wooden_doors_opened", ticks = 3)
- delay(2)
- tele(3500, 9811)
- }
-
- objectOperate("Climb", "swamp_bridge_tree") { (target) ->
- val direction = if (target.tile.y == 3431) Direction.SOUTH else Direction.NORTH
- anim("climb_up")
- delay(2)
- tele(target.tile.add(direction))
- message("You climb up the tree.")
- face(direction)
- delay(1)
- walkOverDelay(target.tile.add(direction).add(direction).add(direction).add(direction))
- message("You climb down the tree.")
- anim("climb_down")
- delay(1)
- tele(if (direction == Direction.SOUTH) Tile(3502, 3425) else Tile(3503, 3431))
- }
- }
-}
diff --git a/game/src/main/kotlin/content/area/morytania/mort_myre_swamp/MyrequeStranger.kt b/game/src/main/kotlin/content/area/morytania/mort_myre_swamp/MyrequeStranger.kt
new file mode 100644
index 0000000000..25a393bfe1
--- /dev/null
+++ b/game/src/main/kotlin/content/area/morytania/mort_myre_swamp/MyrequeStranger.kt
@@ -0,0 +1,64 @@
+package content.area.morytania.mort_myre_swamp
+
+import content.entity.player.dialogue.Angry
+import content.entity.player.dialogue.Confused
+import content.entity.player.dialogue.Neutral
+import content.entity.player.dialogue.Quiz
+import content.entity.player.dialogue.Shock
+import content.entity.player.dialogue.type.npc
+import content.entity.player.dialogue.type.player
+import content.quest.member.myreque.myrequeStage
+import content.quest.questComplete
+import content.quest.questStage
+import content.quest.refreshQuestJournal
+import world.gregs.voidps.engine.Script
+import world.gregs.voidps.engine.entity.character.jingle
+import world.gregs.voidps.engine.entity.character.player.Player
+import world.gregs.voidps.engine.entity.character.player.skill.Skill
+import world.gregs.voidps.engine.entity.character.player.skill.exp.exp
+import world.gregs.voidps.engine.event.AuditLog
+
+class MyrequeStranger : Script {
+ init {
+ npcOperate("Talk-to", "stranger_3") {
+ val progress = questStage("in_search_of_the_myreque")
+ if (progress !in myrequeStage("delivered_weapons")..myrequeStage("escaped_to_canifis")) {
+ return@npcOperate npc("I think we've been through this before haven't we?")
+ }
+ npc("Hello friend? What can I do for you?")
+ player("Oh sorry, I thought you were someone else.")
+ if (progress != myrequeStage("escaped_to_canifis")) {
+ return@npcOperate
+ }
+ npc("Who did you think I was?")
+ player("I thought you were that dirty murderer Vanstrom.")
+ npc("Woah, it sounds like you've got a score to settle!")
+ player("I definitely have!")
+ completeQuest()
+ }
+ }
+
+ private fun Player.completeQuest() {
+ jingle("quest_complete_1")
+ exp(Skill.Attack, 600.0)
+ exp(Skill.Defence, 600.0)
+ exp(Skill.Strength, 600.0)
+ exp(Skill.Constitution, 600.0)
+ exp(Skill.Crafting, 600.0)
+ inc("quest_points", 2)
+ AuditLog.event(this, "quest_completed", "in_search_of_the_myreque")
+ set("in_search_of_the_myreque", "completed")
+ refreshQuestJournal()
+ questComplete(
+ "In Search of the Myreque",
+ "2 Quest Points",
+ "600 Attack XP",
+ "600 Defence XP",
+ "600 Strength XP",
+ "600 Constitution XP",
+ "600 Crafting XP",
+ "A quick route to Mort'ton",
+ item = "steel_sword",
+ )
+ }
+}
diff --git a/game/src/main/kotlin/content/area/morytania/mort_myre_swamp/PolmafiFerdygris.kt b/game/src/main/kotlin/content/area/morytania/mort_myre_swamp/PolmafiFerdygris.kt
new file mode 100644
index 0000000000..9d444db6d1
--- /dev/null
+++ b/game/src/main/kotlin/content/area/morytania/mort_myre_swamp/PolmafiFerdygris.kt
@@ -0,0 +1,82 @@
+package content.area.morytania.mort_myre_swamp
+
+import content.entity.player.dialogue.Happy
+import content.entity.player.dialogue.Neutral
+import content.entity.player.dialogue.Quiz
+import content.entity.player.dialogue.Sad
+import content.entity.player.dialogue.type.ChoiceOption
+import content.entity.player.dialogue.type.choice
+import content.entity.player.dialogue.type.npc
+import content.entity.player.dialogue.type.player
+import content.quest.member.myreque.myrequeStage
+import content.quest.questStage
+import world.gregs.voidps.engine.Script
+import world.gregs.voidps.engine.client.message
+import world.gregs.voidps.engine.entity.character.player.Player
+
+class PolmafiFerdygris : Script {
+ init {
+ npcOperate("Talk-to", "polmafi_ferdygris") {
+ when (questStage("in_search_of_the_myreque")) {
+ in myrequeStage("delivered_weapons")..myrequeStage("hellhound_summoned") -> {
+ message("This person isn't able to talk to you at this time.")
+ }
+ myrequeStage("entered_hideout") -> {
+ npc("Good day. You should quickly introduce yourself to Veliaf and after that I'll be happy to have a chat with you.")
+ }
+ myrequeStage("met_veliaf"), myrequeStage("weapons_accepted") -> {
+ if (!get("met_polmafi", false)) {
+ set("met_polmafi", true)
+ npc("Good! Now that you've introduced yourself to Veliaf and observed proper protocol, I'll be happy to chat with you. Now, please tell me, what can I do for you?")
+ player("Do you mind if I ask a few questions?")
+ } else {
+ npc("Warmest salutations of the day my friend! How are you?")
+ player("Fine thanks. I'd like to ask a few questions if that's Ok?")
+ }
+ npc("Not at all, I'm sure I'll be able to help and if I can't I'll tell you so.")
+ questionsChoice()
+ }
+ else -> aftermath()
+ }
+ }
+ }
+
+ suspend fun Player.questionsChoice() {
+ choice {
+ whatAreYouDoing()
+ tellMeAboutMyreque()
+ aboutMorytania()
+ heardRumors()
+ option("Ok, thanks.")
+ }
+ }
+
+ fun ChoiceOption.whatAreYouDoing(): Unit = option("What are you doing here?") {
+ npc("I am the key advisor to Veliaf, he seeks my council on many issues before we engage the enemy. Not that we would be doing that any time soon, the situation is somewhat difficult and terrifying at the moment.")
+ npc("However, we must always remember, 'All that is required for evil to survive is for good people to do nothing.'")
+ questionsChoice()
+ }
+
+ fun ChoiceOption.tellMeAboutMyreque(): Unit = option("What can you tell me about the Myreque?") {
+ npc("Well, we have a hierarchical chain of command. Veliaf reports to Calsidiu who is considered the head of the armed resistance against the Drakans.")
+ npc("I've also heard people talking about a spiritual man, who inspires many people to join the resistance. He's said to be very influential with supposed contacts within the Drakan clan.")
+ questionsChoice()
+ }
+
+ fun ChoiceOption.aboutMorytania(): Unit = option("What do you know about Morytania?") {
+ npc("It has a rugged landscape,, situated to the east of the River Salve. Primary occupants seem to be human in apperance, but are in fact large vampiric predators.")
+ npc("They maintain a stock of human villages in the same way that humans keep cattle. Weekly blood tithes are paid to the Vampyric overlord Drakan in return for moderate safety.")
+ questionsChoice()
+ }
+
+ fun ChoiceOption.heardRumors(): Unit = option("Have you heard any rumors?") {
+ npc("Yes, I hear them all the time, they're neither correct or even all that interesting, I suspect that we will be looking to make a strike against the human sympathisers some time soon.")
+ questionsChoice()
+ }
+
+ private suspend fun Player.aftermath() {
+ npc("I'm so sad since Sani and Harold were killed. The Myreque will be lonelier without them. Still, they didn't suffer at least.")
+ player("My commiserations for your loss. I wish that there was something I could do to help.")
+ npc("Don't worry. No one blames you. It could have been anyone that was tricked by him. Whilst I'm sad at the loss of my comrades, it serves no useful purpose to blame anyone for it.")
+ }
+}
diff --git a/game/src/main/kotlin/content/area/morytania/mort_myre_swamp/RadigadPonfit.kt b/game/src/main/kotlin/content/area/morytania/mort_myre_swamp/RadigadPonfit.kt
new file mode 100644
index 0000000000..093a18e36d
--- /dev/null
+++ b/game/src/main/kotlin/content/area/morytania/mort_myre_swamp/RadigadPonfit.kt
@@ -0,0 +1,79 @@
+package content.area.morytania.mort_myre_swamp
+
+import content.entity.player.dialogue.Happy
+import content.entity.player.dialogue.Neutral
+import content.entity.player.dialogue.Quiz
+import content.entity.player.dialogue.Sad
+import content.entity.player.dialogue.type.ChoiceOption
+import content.entity.player.dialogue.type.choice
+import content.entity.player.dialogue.type.npc
+import content.entity.player.dialogue.type.player
+import content.quest.member.myreque.myrequeStage
+import content.quest.questStage
+import world.gregs.voidps.engine.Script
+import world.gregs.voidps.engine.client.message
+import world.gregs.voidps.engine.entity.character.player.Player
+import world.gregs.voidps.engine.entity.character.player.name
+
+class RadigadPonfit : Script {
+ init {
+ npcOperate("Talk-to", "radigad_ponfit") {
+ when (questStage("in_search_of_the_myreque")) {
+ in myrequeStage("delivered_weapons")..myrequeStage("hellhound_summoned") -> {
+ message("This person isn't able to talk to you at this time.")
+ }
+ myrequeStage("entered_hideout") -> {
+ npc("Greetings friend! Once you've introduced yourself to Veliaf please feel free to chat with me...")
+ }
+ myrequeStage("met_veliaf"), myrequeStage("weapons_accepted") -> {
+ if (!get("met_radigad", false)) {
+ set("met_radigad", true)
+ npc("Ah it's $name. I see you've introduced yourself to Veliaf, good! Now, how can I be of service to you?")
+ player("Do you mind if I ask a few questions?")
+ } else {
+ npc("Hello again! Radigad at your service. Are you planning to join the fray against the Morytania monster and the rest of the Drakans?")
+ player("Maybe! I'd like to ask a few questions if that's Ok?")
+ }
+ npc("Sure, go ahead, I'll answer as best I can.")
+ questionsChoice()
+ }
+ else -> aftermath()
+ }
+ }
+ }
+
+ suspend fun Player.questionsChoice() {
+ choice {
+ whatsYourJob()
+ tellMeAboutMyreque()
+ futurePlans()
+ heardRumors()
+ option("Ok, thanks.")
+ }
+ }
+
+ fun ChoiceOption.whatsYourJob(): Unit = option("What's your job here?") {
+ npc("I help where I can. I'm a bit of an all rounder. I try to help Veliaf as much as I can. I've relied on his sword arm many a time, I like to think that he can rely on mine.")
+ questionsChoice()
+ }
+
+ fun ChoiceOption.tellMeAboutMyreque(): Unit = option("What can you tell me about the Myreque?") {
+ npc("We're just developing the resistance against the Drakans and the rest of the Vampiric entourage. We're seriously out numbered, Veliaf doesn't like to hear us saying 'hopeless' but that's our situation at the minute.")
+ questionsChoice()
+ }
+
+ fun ChoiceOption.futurePlans(): Unit = option("What are your plans for the future?") {
+ npc("I'm not really sure, I guess Veliaf might know that, however I hope that more people will join the ranks and take the fight to the dead hearts of our Vampiric foe so that we might one day reclaim our right to exist.")
+ questionsChoice()
+ }
+
+ fun ChoiceOption.heardRumors(): Unit = option("Have you heard any rumors?") {
+ npc("I have. Something you might find interesting is that our enigmatic leader 'Safalaan' has connections inside the Drakan Clan. I'm not sure how true that is but it would be great!")
+ questionsChoice()
+ }
+
+ private suspend fun Player.aftermath() {
+ player("Hello, are you ok?")
+ npc("Yeah, I'm Ok thanks. You certainly proved yourself against that unearthly monster that Vanstrom conjured. I'm sorry I can't be more upbeat, but I'm very sad about Harold and Sani. It's terrible to see your comrades cut down in combat, especially those two. They were still so very young.")
+ }
+}
diff --git a/game/src/main/kotlin/content/area/morytania/mort_myre_swamp/SaniPiliu.kt b/game/src/main/kotlin/content/area/morytania/mort_myre_swamp/SaniPiliu.kt
new file mode 100644
index 0000000000..e3821f3ce0
--- /dev/null
+++ b/game/src/main/kotlin/content/area/morytania/mort_myre_swamp/SaniPiliu.kt
@@ -0,0 +1,87 @@
+package content.area.morytania.mort_myre_swamp
+
+import content.entity.player.dialogue.Happy
+import content.entity.player.dialogue.Neutral
+import content.entity.player.dialogue.Quiz
+import content.entity.player.dialogue.Sad
+import content.entity.player.dialogue.type.ChoiceOption
+import content.entity.player.dialogue.type.choice
+import content.entity.player.dialogue.type.npc
+import content.entity.player.dialogue.type.player
+import content.quest.member.myreque.myrequeStage
+import content.quest.questStage
+import world.gregs.voidps.engine.Script
+import world.gregs.voidps.engine.client.message
+import world.gregs.voidps.engine.entity.character.player.Player
+import world.gregs.voidps.engine.entity.character.player.name
+
+class SaniPiliu : Script {
+ init {
+ npcOperate("Talk-to", "sani_piliu_meiyerditch_tunnels") {
+ when (questStage("in_search_of_the_myreque")) {
+ in myrequeStage("delivered_weapons")..myrequeStage("hellhound_summoned") -> {
+ message("This person isn't able to talk to you at this time.")
+ }
+ myrequeStage("entered_hideout") -> {
+ npc("Hi there...you're new here aren't you? You'd best go and talk with Veliaf. But afterwards, I'd love to introduce myself properly.")
+ }
+ myrequeStage("met_veliaf"), myrequeStage("weapons_accepted") -> {
+ if (!get("met_sani", false)) {
+ firstMeeting()
+ } else {
+ npc("Oh, hello $name nice to see you again! How's things?")
+ player("Fine thanks, do you mind if I ask a few questions?")
+ npc("Sure, go ahead, I'll answer as best I can.")
+ questionsChoice()
+ }
+ }
+ }
+ }
+ }
+
+ private suspend fun Player.firstMeeting() {
+ npc("Hello...very nice to meet you! My name is Sani Piliu, what's yours?")
+ player("I am known as $name nice to meet you Sani.")
+ set("met_sani", true)
+ npc("The pleasure's all mine! Excuse me for being forward, but you somehow seem different to other people, somehow more determined. I sense a powerful aura. Sorry, that must seem really strange?")
+ player("Well, perhaps a little but it's nice of you to say so in any case. I'd like to ask a few questions if that's ok?")
+ npc("Sure, go ahead, I'll answer as best I can.")
+ questionsChoice()
+ }
+
+ suspend fun Player.questionsChoice() {
+ choice {
+ tellAboutYourself()
+ tellMeAboutMyreque()
+ whatsGoingOn()
+ mainCamp()
+ option("Ok, thanks.")
+ }
+ }
+
+ fun ChoiceOption.tellAboutYourself(): Unit = option("Tell me a bit about yourself.") {
+ npc("There's not much to tell really. I was about nine years old when my family was ripped to pieces by a vampire attack, one day a happy member of a loving family, the next day an orphan living a hand to mouth existence.")
+ npc("Somehow I managed to get by, eking out a living where ever I could. People took pity on me, I earnt food by helping people out, before too long I was quite adept at helping myself, other people appreciated my skills.")
+ npc("I bumped into Veliaf in Canifis. He saw that I would be an asset to the resistance so I joined. I want to fight back against those killers who took my family. One day I may escape to the west across the Salve! Who knows?")
+ questionsChoice()
+ }
+
+ fun ChoiceOption.tellMeAboutMyreque(): Unit = option("What can you tell me about the Myreque?") {
+ npc("As far as I know Veliaf is one of two Lieutenants who report to another soldier called Calsidiu. While we don't advertise the fact, there are quite a lot of people in Morytania who want to put up a fight.")
+ npc("Our current operations are merely trying to find a weakness in the Drakans' hierarchy amd perhaps get a spy in there. We need to find out how to kill them before we then take the war to them.")
+ player("You make it sound easy!")
+ npc("With determination you can achieve great things. However, Veliaf always tells us not to underestimate the enemy. After those juvenile vampires discovered us in the forest, it's a mistake I won't be making again.")
+ questionsChoice()
+ }
+
+ fun ChoiceOption.whatsGoingOn(): Unit = option("What's going on in Morytania?") {
+ npc("Lowernial Drakan and his siblings collect blood tithes from the locals as a way of controlling the population and keeping his brood happy. Sanguinesti and Castle Drakan hold sway over all the inhabitants of Morytania.")
+ npc("We're nothing more than an ill ogranised rabble, but we're determined to fight against these evil monsters. We won't just offer our family members to the Drakans for a weekly snack, we're going to fight back!")
+ questionsChoice()
+ }
+
+ fun ChoiceOption.mainCamp(): Unit = option("Is this your main camp?") {
+ npc("It's our only camp! At least, I don't know of any others.")
+ questionsChoice()
+ }
+}
diff --git a/game/src/main/kotlin/content/area/morytania/mort_myre_swamp/SwampyBoat.kt b/game/src/main/kotlin/content/area/morytania/mort_myre_swamp/SwampyBoat.kt
index 47eec3f063..c613d246e2 100644
--- a/game/src/main/kotlin/content/area/morytania/mort_myre_swamp/SwampyBoat.kt
+++ b/game/src/main/kotlin/content/area/morytania/mort_myre_swamp/SwampyBoat.kt
@@ -1,5 +1,7 @@
package content.area.morytania.mort_myre_swamp
+import content.area.morytania.mort_ton.earlyCyregOptions
+import content.entity.player.dialogue.Angry
import content.entity.player.dialogue.Neutral
import content.entity.player.dialogue.Quiz
import content.entity.player.dialogue.type.choice
@@ -7,13 +9,17 @@ import content.entity.player.dialogue.type.npc
import content.entity.player.dialogue.type.statement
import content.entity.player.modal.Tab
import content.quest.closeTabs
+import content.quest.member.myreque.myrequeStage
import content.quest.openTabs
-import content.quest.questCompleted
+import content.quest.questStage
import net.pearx.kasechange.toSnakeCase
+import world.gregs.voidps.cache.definition.data.InterfaceDefinition
import world.gregs.voidps.engine.Script
import world.gregs.voidps.engine.client.Minimap
import world.gregs.voidps.engine.client.clearMinimap
import world.gregs.voidps.engine.client.minimap
+import world.gregs.voidps.engine.client.sendScript
+import world.gregs.voidps.engine.client.ui.close
import world.gregs.voidps.engine.client.ui.dialogue.talkWith
import world.gregs.voidps.engine.client.ui.open
import world.gregs.voidps.engine.entity.character.jingle
@@ -36,11 +42,12 @@ class SwampyBoat : Script {
objectOperate("Board", "swamp_boat_mort_ton") {
talkWith(cyreg())
- if (!questCompleted("in_search_of_the_myreque")) {
- npc("Hey, hands off my boat!")
+ if (questStage("in_search_of_the_myreque") <= myrequeStage("persuaded_boatman")) {
+ npc("Hey, hands off my boat!")
+ earlyCyregOptions()
return@objectOperate
}
- npc("I'll have to charge you 10 gold to cover the loan of the boat. Is that okay?")
+ npc("It costs 10 gold to cover the loan of the boat. Will you pay?")
choice {
option("Yes. I'll pay the ten gold.") {
if (!inventory.remove("coins", 10)) {
@@ -61,8 +68,9 @@ class SwampyBoat : Script {
objectOperate("Board ( Pay 10 )", "swamp_boat_mort_ton") {
talkWith(cyreg())
- if (!questCompleted("in_search_of_the_myreque")) {
- npc("Hey, hands off my boat!")
+ if (questStage("in_search_of_the_myreque") <= myrequeStage("persuaded_boatman")) {
+ npc("Hey, hands off my boat!")
+ earlyCyregOptions()
return@objectOperate
}
if (!inventory.remove("coins", 10)) {
@@ -76,22 +84,30 @@ class SwampyBoat : Script {
private fun cyreg(): NPC = NPCs.findOrNull(Region(14131).toLevel(0), "cyreg_paddlehorn") ?: NPCs.find(Region(13875).toLevel(0), "cyreg_paddlehorn")
private suspend fun Player.hollows() {
- travel("the Hollows", Tile(3498, 3380))
+ travel("the Hollows", Tile(3498, 3380)) {
+ if (questStage("in_search_of_the_myreque") == myrequeStage("gave_planks")) {
+ set("in_search_of_the_myreque", "reached_hollows")
+ }
+ }
}
- private suspend fun Player.travel(name: String, tile: Tile) {
+ private suspend fun Player.travel(name: String, tile: Tile, onArrival: Player.() -> Unit = {}) {
open("fade_out")
delay(1)
closeTabs(Tab.Options, Tab.MusicPlayer)
jingle("morytania_boatride")
open("swamp_boat_journey")
+ open("total_blackness")
+ sendScript("text_colour_swapper", InterfaceDefinition.pack(333, 0), 0x433621)
minimap(Minimap.HideMap)
interfaces.sendAnimation("swamp_boat_journey", "boat", "boat_journey_${name.replace("'", "_").toSnakeCase()}")
statement("You board the boat and journey to $name.", clickToContinue = false)
delay(14)
tele(tile)
+ onArrival()
delay(1)
clearMinimap()
+ close("total_blackness")
openTabs(Tab.Options, Tab.MusicPlayer)
open("fade_in")
statement("You arrive in $name.")
diff --git a/game/src/main/kotlin/content/area/morytania/mort_myre_swamp/VeliafHurtz.kt b/game/src/main/kotlin/content/area/morytania/mort_myre_swamp/VeliafHurtz.kt
new file mode 100644
index 0000000000..30cded02a0
--- /dev/null
+++ b/game/src/main/kotlin/content/area/morytania/mort_myre_swamp/VeliafHurtz.kt
@@ -0,0 +1,385 @@
+package content.area.morytania.mort_myre_swamp
+
+import content.entity.combat.hit.damage
+import content.entity.effect.transform
+import content.entity.player.dialogue.Angry
+import content.entity.player.dialogue.EvilLaugh
+import content.entity.player.dialogue.Happy
+import content.entity.player.dialogue.Neutral
+import content.entity.player.dialogue.Quiz
+import content.entity.player.dialogue.Sad
+import content.entity.player.dialogue.type.ChoiceOption
+import content.entity.player.dialogue.type.choice
+import content.entity.player.dialogue.type.npc
+import content.entity.player.dialogue.type.player
+import content.entity.player.dialogue.type.statement
+import content.entity.proj.shoot
+import content.quest.member.myreque.checkMembers
+import content.quest.member.myreque.hasAllWeapons
+import content.quest.member.myreque.myrequeStage
+import content.quest.member.myreque.spawnHellHound
+import content.quest.quest
+import content.quest.questStage
+import content.quest.setInstanceLogout
+import content.quest.startCutscene
+import world.gregs.voidps.engine.Script
+import world.gregs.voidps.engine.client.clearCamera
+import world.gregs.voidps.engine.client.message
+import world.gregs.voidps.engine.client.moveCamera
+import world.gregs.voidps.engine.client.turnCamera
+import world.gregs.voidps.engine.client.ui.open
+import world.gregs.voidps.engine.entity.character.mode.PauseMode
+import world.gregs.voidps.engine.entity.character.move.tele
+import world.gregs.voidps.engine.entity.character.npc.NPC
+import world.gregs.voidps.engine.entity.character.npc.NPCs
+import world.gregs.voidps.engine.entity.character.player.Player
+import world.gregs.voidps.engine.entity.character.sound
+import world.gregs.voidps.engine.inv.inventory
+import world.gregs.voidps.engine.inv.remove
+import world.gregs.voidps.type.Direction
+import world.gregs.voidps.type.Tile
+
+class VeliafHurtz : Script {
+ init {
+ npcOperate("Talk-to", "veliaf_hurtz_meiyerditch_tunnels") {
+ when (questStage("in_search_of_the_myreque")) {
+ myrequeStage("delivered_weapons") -> playMyrequeCutscene()
+ myrequeStage("entered_hideout") -> firstMeeting()
+ myrequeStage("met_veliaf") -> {
+ if (!checkMembers()) {
+ npc("Have you introduced yourself to the rest of the gang?")
+ introducedYourselfChoice()
+ } else {
+ npc("Hello again...so, you've introduced yourself to the team have you? Good. Now, let's have a look at those weapons you brought us.")
+ if (hasAllWeapons()) {
+ acceptWeapons()
+ } else {
+ wrongWeapons()
+ }
+ }
+ }
+ myrequeStage("weapons_accepted") -> {
+ if (hasAllWeapons()) {
+ deliverWeaponsAndStartCutscene()
+ } else {
+ wrongWeapons()
+ }
+ }
+ myrequeStage("hellhound_summoned") -> {
+ message("Veliaf can't talk right now, he seems to be formulating a plan.")
+ }
+ else -> postCutsceneIntro()
+ }
+ }
+ }
+
+ private suspend fun Player.firstMeeting() {
+ player("Hello there...")
+ npc("Hello there...how did you get in here? Who are you?")
+ player("I was asked to bring you some weapons...apparently you need them? Don't worry, I'm a friend.")
+ set("in_search_of_the_myreque", "met_veliaf")
+ npc("Hmm, well, it's true we could do with some supplies. It's very good of you to bring them to us. I'm sorry but I'm a bit busy at the moment, please introduce yourself to the others, then we can talk about those weapons.")
+ firstMeetingChoice()
+ }
+
+ suspend fun Player.firstMeetingChoice() {
+ choice {
+ areYouLeader()
+ tellMeAboutOrganization()
+ tellMeAboutMorytania()
+ futurePlans()
+ option("Ok, thanks.")
+ }
+ }
+
+ fun ChoiceOption.areYouLeader(): Unit = option("So are you the leader?") {
+ npc("Well, as much as anyone is, it's not official or anything, I guess the others just look up to me or something. I feel a certain responsibility for them so I guess that means I'm a leader of sorts.")
+ firstMeetingChoice()
+ }
+
+ fun ChoiceOption.tellMeAboutOrganization(): Unit = option("Can you tell me about your organization?") {
+ npc("Huh, organization? That's rich! We're really not that organized, however, the Myreque are dedicated to mounting a resistance against the Drakans and the evil that they've brough to Morytania.")
+ firstMeetingChoice()
+ }
+
+ fun ChoiceOption.tellMeAboutMorytania(): Unit = option("What can you tell me about Morytania?") {
+ npc("I can tell you that it used to be a beautiful country before Lowerniel and his quarelling siblings arrived.")
+ player("Lowerniel? Who's that?")
+ npc("Lowerniel Vergidiyad Drakan - he's the head of the Drakan family and along with his brother Ranis and his sister Vanescula they play out their petty backstabbing games using the peoples of Morytania as their pawns.")
+ player("You sound very bitter.")
+ npc("So would you if you lost everyone dear to you at the hands of such monsters.")
+ firstMeetingChoice()
+ }
+
+ fun ChoiceOption.futurePlans(): Unit = option("What are your plans for the future?") {
+ npc("At the moment we're maintaining a low profile after we were spotted scouting out the Drakans' castle. The forest where we camped is infested with vampire, and even though they're only juveniles in terms of experience, we only just managed to survive.")
+ npc("After we've regrouped, we will try to recruit more people to our cause, at the minute we're hopelessly outnumberd.")
+ firstMeetingChoice()
+ }
+
+ suspend fun Player.introducedYourselfChoice() {
+ choice {
+ askQuestionsFirst()
+ goIntroduceNow()
+ whoElseToMeet()
+ }
+ }
+
+ fun ChoiceOption.askQuestionsFirst(): Unit = option("I wanted to ask you some questions first.") {
+ npc("Well, ok, but only briefly, I have things to do.")
+ firstMeetingChoice()
+ }
+
+ fun ChoiceOption.goIntroduceNow(): Unit = option("Not yet, I'll go do that now.") {
+ npc("Ok, great.")
+ }
+
+ fun ChoiceOption.whoElseToMeet(): Unit = option("Who else do I have to introduce myself to?") {
+ val sani = if (get("met_sani", false)) "Sani" else "Sani"
+ val harold = if (get("met_harold", false)) "Harold" else "Harold"
+ val radigad = if (get("met_radigad", false)) "Radigad" else "Radigad"
+ val polmafi = if (get("met_polmafi", false)) "Polmafi" else "Polmafi"
+ val ivan = if (get("met_ivan", false)) "Ivan" else "Ivan"
+ statement("$sani
$harold
$radigad
$polmafi
$ivan")
+ }
+
+ private suspend fun Player.wrongWeapons() {
+ npc("Well, those weapons aren't that useful. We really need specific ones, we have our own specialism.")
+ weaponChoice()
+ }
+
+ suspend fun Player.weaponChoice() {
+ choice {
+ goGetWeapons()
+ whichWeapons()
+ }
+ }
+
+ fun ChoiceOption.goGetWeapons(): Unit = option("Ok, I'll go get them.") {
+ npc("Ok, I'd appreciate that.")
+ }
+
+ fun ChoiceOption.whichWeapons(): Unit = option("Which weapons do you need?") {
+ npc("Here, I've compiled a list.")
+ showWeaponList()
+ }
+
+ private suspend fun Player.acceptWeapons() {
+ set("in_search_of_the_myreque", "weapons_accepted")
+ npc("These weapons look great! Many thanks.")
+ deliverWeaponsAndStartCutscene()
+ }
+
+ private suspend fun Player.deliverWeaponsAndStartCutscene() {
+ inventory.remove("steel_longsword", 1)
+ inventory.remove("steel_dagger", 1)
+ inventory.remove("steel_mace", 1)
+ inventory.remove("steel_warhammer", 1)
+ inventory.remove("steel_sword", 2)
+ set("thsfm_vanstrom_hide", true)
+ set("in_search_of_the_myreque", "delivered_weapons")
+ playMyrequeCutscene()
+ }
+
+ private suspend fun Player.showWeaponList() {
+ val longsword = weapon(inventory.contains("steel_longsword"), "1 Steel ", "Longsword")
+ val shortSword = weapon(inventory.contains("steel_sword", 2), "2 Steel ", "Short Swords")
+ val dagger = weapon(inventory.contains("steel_dagger"), "1 Steel ", "Dagger")
+ val mace = weapon(inventory.contains("steel_mace"), "1 Steel ", "Mace")
+ val warhammer = weapon(inventory.contains("steel_warhammer"), "1 Steel ", "Warhammer")
+ statement("$longsword $shortSword $dagger $mace $warhammer")
+ }
+
+ private fun weapon(has: Boolean, prefix: String, name: String): String = if (has) {
+ "$prefix$name"
+ } else {
+ "$prefix$name"
+ }
+
+ private suspend fun Player.playMyrequeCutscene() {
+ open("fade_out")
+ val cutscene = startCutscene("myreque_ambush", AMBUSH_BASE, width = 6, height = 3)
+ setInstanceLogout(VELIAF_SPOT)
+ cutscene.onEnd {
+ if (quest("in_search_of_the_myreque") == "hellhound_summoned") {
+ tele(HOUND_SPOT)
+ } else {
+ tele(VELIAF_SPOT)
+ }
+ clearCamera()
+ }
+ delay(4)
+
+ val veliaf = NPCs.add("veliaf_hurtz_meiyerditch_tunnels", cutscene.convert(VELIAF_SPOT), Direction.SOUTH)
+ val harold = NPCs.add("harold_evans_meiyerditch_tunnels", cutscene.convert(HAROLD_SPOT), Direction.SOUTH)
+ val sani = NPCs.add("sani_piliu_meiyerditch_tunnels", cutscene.convert(SANI_SPOT), Direction.SOUTH)
+ val radigad = NPCs.add("radigad_ponfit", cutscene.convert(RADIGAD_SPOT), Direction.EAST)
+ for (member in listOf(veliaf, harold, sani, radigad)) {
+ member.mode = PauseMode
+ }
+ tele(cutscene.convert(PLAYER_SPOT), clearInterfaces = false)
+ delay(1)
+ veliaf.face(cutscene.tile(3506, 9837, 2))
+ harold.face(cutscene.tile(3504, 9836, 2))
+ sani.face(cutscene.tile(3508, 9836, 2))
+ radigad.face(cutscene.tile(3511, 9831, 2))
+
+ moveCamera(cutscene.tile(3506, 9846, 2), 750)
+ turnCamera(cutscene.tile(3506, 9834, 2), 125)
+ open("fade_in")
+ delay(3)
+ face(cutscene.convert(VANSTROM_SPOT))
+ moveCamera(cutscene.tile(3508, 9836, 2), 325)
+ turnCamera(cutscene.tile(3502, 9842, 2), 125)
+ npc(
+ "veliaf_hurtz_meiyerditch_tunnels",
+ """
+ Hey, what's that mist coming through the door!
+ WE'VE A VAMPIRE IN THE ROOM!
+ """,
+ )
+
+ moveCamera(cutscene.tile(3506, 9846, 2), 750)
+ turnCamera(cutscene.tile(3506, 9834, 2), 125)
+ message("You see a shifting mist enter the room!")
+ sound("vanstrom_mist")
+ val mist = NPCs.add("route_vanstrom_vampire_misty", cutscene.tile(3504, 9832, 2), Direction.SOUTH)
+ delay(1)
+ mist.walkTo(cutscene.tile(3505, 9835, 2))
+ delay(3)
+ NPCs.remove(mist)
+ sound("vanstrom_appear")
+
+ val vanstrom = NPCs.add("route_vanstrom", cutscene.convert(VANSTROM_SPOT), Direction.SOUTH)
+ vanstrom.mode = PauseMode
+ vanstrom.face(cutscene.convert(PLAYER_SPOT))
+ face(cutscene.convert(VANSTROM_SPOT))
+ vanstrom.say("Ha...ha...ha... you took me straight to them!")
+ message("Vanstrom Klause: Ha...ha...ha...you took me straight to them!")
+ npc("stranger_2", "Ha...ha...ha... you took me straight to them!")
+ vanstrom.say("The little dears are going to wonder which side you're on!")
+ message("Vanstrom Klause: The little dears are going to wonder which side you're on!")
+ npc("stranger_2", "The little dears are going to wonder which side you're on!")
+
+ moveCamera(cutscene.tile(3511, 9839, 2), 325)
+ turnCamera(cutscene.tile(3506, 9837, 2), 125)
+ face(cutscene.convert(SANI_SPOT))
+ vanstrom.face(cutscene.convert(SANI_SPOT))
+ sani.face(cutscene.convert(VANSTROM_SPOT))
+ sani.say("It's Vanstrom...we're dead...")
+ message("Sani Piliu: It's Vanstrom...we're dead...")
+ npc("sani_piliu_meiyerditch_tunnels", "It's Vanstrom...we're dead...")
+
+ face(cutscene.convert(VANSTROM_SPOT))
+ vanstrom.strike(sani, cutscene.convert(VANSTROM_SPOT))
+ sani.say("Ahhhhhh!")
+ vanstrom.say("Quite right...")
+ message("Vanstrom Klause: Quite right....")
+ npc("stranger_2", "Quite right...")
+
+ turnCamera(cutscene.tile(3506, 9838, 2), 125, speed = 3, acceleration = 3)
+ delay(1)
+ turnCamera(cutscene.tile(3506, 9837, 2), 125, speed = 3, acceleration = 3)
+ vanstrom.face(cutscene.convert(HAROLD_SPOT))
+ vanstrom.strike(harold, cutscene.convert(VANSTROM_SPOT).add(-1, 0))
+ harold.say("Auugh!")
+ vanstrom.anim("vanstrom_transform")
+ vanstrom.transform("route_vanstrom_vampire")
+ vanstrom.say("Sorry, Harold, you too!")
+ message("Vanstrom Klause: Sorry Harold, you too!")
+ npc("stranger_2", "Sorry, Harold, you too!")
+
+ delay(2)
+ vanstrom.transform("route_vanstrom_vampire_flying")
+ vanstrom.anim("vanstrom_hover")
+ sound("vanstrom_vampire")
+ vanstrom.say("And now I'm going to finish the rest of them off.")
+ message("Vanstrom Klause: And... now I'm going to finish the rest of them off...")
+ npc("route_vanstrom_vampire", "And now I'm going to finish the rest of them off.")
+
+ vanstrom.face(cutscene.convert(PLAYER_SPOT))
+ vanstrom.say("With my little pet!")
+ message("Vanstrom Klause: With my little pet!")
+ npc("route_vanstrom_vampire", "With my little pet!")
+ vanstrom.transform("route_vanstrom_vampire_misty")
+ vanstrom.say("Ha ha ha ha haaaaaa!")
+ sound("vanstrom_mist")
+ delay(2)
+ vanstrom.walkOverDelay(cutscene.tile(3504, 9832, 2))
+ NPCs.remove(vanstrom)
+ message("Vanstrom Klause: Ha ha ha ha haaaaaa!")
+ npc("route_vanstrom_vampire", "Ha ha ha ha haaaaaa!")
+
+ open("fade_out")
+ delay(2)
+ set("in_search_of_the_myreque", "hellhound_summoned")
+ tele(HOUND_SPOT)
+ cutscene.end()
+ spawnHellHound(this)
+ }
+
+ private fun NPC.strike(target: NPC, from: Tile) {
+ anim("thrown_accurate")
+ gfx("iron_knife_throw")
+ from.shoot("iron_knife", target)
+ target.anim("myreque_knockout")
+ target["absorb_damage"] = 0
+ target.damage(300, source = this)
+ }
+
+ private suspend fun Player.postCutsceneIntro() {
+ npc("That murderer Vanstrom has killed Sani and Harold! He came in and killed them right in front of our eyes. And that beast he summoned probably would have killed the rest of us if it wasn't for you!")
+ player("It was the least I could do! After all, I was the one he followed here, I'm sort of responsible.")
+ npc("That is for your conscience to bear, but now perhaps you can understand why we fight for our freedom?")
+ player("Yes, I think I understand now.")
+ aftermathChoice()
+ }
+
+ suspend fun Player.aftermathChoice() {
+ choice {
+ joinOrganisation()
+ whatNext()
+ howToGetOut()
+ getRevenge()
+ option("Ok, thanks.")
+ }
+ }
+
+ fun ChoiceOption.joinOrganisation(): Unit = option("I want to join your organisation.") {
+ npc("Hmm, well thanks for your support. But I'll need to talk to my superior first.")
+ aftermathChoice()
+ }
+
+ fun ChoiceOption.whatNext(): Unit = option("What do you plan to do now?") {
+ npc("Well, I guess Vanstrom will expect that we're dead. He's so arrogant that he doesn't really consider us a threat. I can understand that, if I was that powerful I would probably feel the same.")
+ npc("I guess we'll regroup and then try to recruit more people. We first need to know how we can fight against these monsters, then we'll take the war to them!")
+ aftermathChoice()
+ }
+
+ fun ChoiceOption.howToGetOut(): Unit = option("How do I get out of here?") {
+ npc("If you go back into the main corridor which leads into this room, it goes down towards the basement of an Inn.")
+ if (questStage("in_search_of_the_myreque") == myrequeStage("killed_hellhound")) {
+ set("in_search_of_the_myreque", "shown_way_out")
+ }
+ npc("If you search the wall, you'll find that it is in fact moveable. You should be able to get into the room, take the ladder to come out to the south of the 'Hair of the Dog Tavern' in Canifis.")
+ aftermathChoice()
+ }
+
+ fun ChoiceOption.getRevenge(): Unit = option("Let's get revenge on Vanstrom!") {
+ npc("Yes, of course, we'll certainly get our revenge on him and his superiors! Then they'll taste the bitter sorrow that we've had to endure so long!")
+ aftermathChoice()
+ }
+
+ private companion object {
+ private val AMBUSH_BASE = Tile(3496, 9824)
+
+ private val PLAYER_SPOT = Tile(3507, 9838, 2)
+ private val VELIAF_SPOT = Tile(3506, 9838, 2)
+ private val SANI_SPOT = Tile(3508, 9837, 2)
+ private val HAROLD_SPOT = Tile(3504, 9837, 2)
+ private val RADIGAD_SPOT = Tile(3510, 9831, 2)
+ private val VANSTROM_SPOT = Tile(3506, 9836, 2)
+
+ private val HOUND_SPOT = Tile(3507, 9838, 0)
+ }
+}
diff --git a/game/src/main/kotlin/content/area/morytania/mort_ton/CyregPaddlehorn.kt b/game/src/main/kotlin/content/area/morytania/mort_ton/CyregPaddlehorn.kt
index 6c30c1ee36..9ea5b67143 100644
--- a/game/src/main/kotlin/content/area/morytania/mort_ton/CyregPaddlehorn.kt
+++ b/game/src/main/kotlin/content/area/morytania/mort_ton/CyregPaddlehorn.kt
@@ -1,22 +1,297 @@
package content.area.morytania.mort_ton
+import content.entity.player.dialogue.Angry
+import content.entity.player.dialogue.Confused
+import content.entity.player.dialogue.Happy
import content.entity.player.dialogue.Neutral
+import content.entity.player.dialogue.Quiz
import content.entity.player.dialogue.Sad
+import content.entity.player.dialogue.Scared
+import content.entity.player.dialogue.type.ChoiceOption
+import content.entity.player.dialogue.type.choice
+import content.entity.player.dialogue.type.item
import content.entity.player.dialogue.type.npc
import content.entity.player.dialogue.type.player
+import content.quest.member.myreque.hasAllWeapons
+import content.quest.member.myreque.myrequeStage
import content.quest.questCompleted
+import content.quest.questStage
import world.gregs.voidps.engine.Script
+import world.gregs.voidps.engine.entity.character.player.Player
+import world.gregs.voidps.engine.inv.carriesItem
+import world.gregs.voidps.engine.inv.inventory
+import world.gregs.voidps.engine.inv.remove
class CyregPaddlehorn : Script {
init {
npcOperate("Talk-to", "cyreg_paddlehorn") {
- if (questCompleted("in_search_of_the_myreque")) {
- player("Thanks for your help in finding the Myreque.")
- npc("Well, I'd like to say that you're welcome. But I heard that you led Vanstrom straight to their hideout and he killed Sani and Harold. I feel so guilty.")
- player("Hmm, yeah, I know how you feel.")
- } else {
- npc("Hello there friend. I'm afraid all boat rides are out of service due to the high ghast activity in Mort Myre.")
+ when {
+ questCompleted("in_search_of_the_myreque") -> {
+ player