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 c802a88f6c..a54eab77af 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..7063f22c9e
--- /dev/null
+++ b/game/src/main/kotlin/content/area/morytania/canifis/VanstromKlause.kt
@@ -0,0 +1,238 @@
+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..7f3cfac13a
--- /dev/null
+++ b/game/src/main/kotlin/content/area/morytania/mort_myre_swamp/CurpileFyod.kt
@@ -0,0 +1,271 @@
+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..7c166f02ea
--- /dev/null
+++ b/game/src/main/kotlin/content/area/morytania/mort_myre_swamp/HaroldEvans.kt
@@ -0,0 +1,112 @@
+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..a7be5ad861
--- /dev/null
+++ b/game/src/main/kotlin/content/area/morytania/mort_myre_swamp/IvanStrom.kt
@@ -0,0 +1,114 @@
+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..6a183256df
--- /dev/null
+++ b/game/src/main/kotlin/content/area/morytania/mort_myre_swamp/PolmafiFerdygris.kt
@@ -0,0 +1,123 @@
+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..21e40f45e7
--- /dev/null
+++ b/game/src/main/kotlin/content/area/morytania/mort_myre_swamp/RadigadPonfit.kt
@@ -0,0 +1,115 @@
+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.",
+ )
+ }
+
+ private fun getMyrequeMember(player: Player, index: Int): Boolean = player["myreque_member_$index", false]
+
+ private fun setMyrequeMember(player: Player, index: Int, value: Boolean) {
+ player["myreque_member_$index"] = value
+ }
+}
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..394d005777
--- /dev/null
+++ b/game/src/main/kotlin/content/area/morytania/mort_myre_swamp/SaniPiliu.kt
@@ -0,0 +1,133 @@
+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..5cc8c7fcf3
--- /dev/null
+++ b/game/src/main/kotlin/content/area/morytania/mort_myre_swamp/VeliafHurtz.kt
@@ -0,0 +1,447 @@
+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.Evil
+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.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.get
+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 {
+ 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..35c9659baf 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,404 @@
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.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