diff --git a/data/area/morytania/canifis/canifis.npc-spawns.toml b/data/area/morytania/canifis/canifis.npc-spawns.toml deleted file mode 100644 index 4b019b137a..0000000000 --- a/data/area/morytania/canifis/canifis.npc-spawns.toml +++ /dev/null @@ -1,60 +0,0 @@ -spawns = [ -# 13878 - { id = "banker_canifis", x = 3514, y = 3481, direction = "WEST", members = true }, - { id = "banker_canifis", x = 3514, y = 3479, direction = "WEST", members = true }, - { id = "rufus", x = 3507, y = 3496 }, - { id = "barker", x = 3499, y = 3506 }, - { id = "fidelio", x = 3475, y = 3497 }, - { id = "sbott", x = 3490, y = 3503 }, - { id = "roavar", x = 3493, y = 3471 }, - { id = "malak", x = 3497, y = 3478 }, - { id = "stranger", x = 3503, y = 3477, members = true }, - { id = "taxidermist", x = 3480, y = 3484, members = true }, - { id = "boris", x = 3489, y = 3490 }, - { id = "imre", x = 3490, y = 3474 }, - { id = "yuri", x = 3497, y = 3497 }, - { id = "joseph", x = 3474, y = 3474 }, - { id = "nikolai", x = 3496, y = 3476 }, - { id = "eduard", x = 3511, y = 3482 }, - { id = "lev", x = 3502, y = 3487 }, - { id = "georgy", x = 3483, y = 3478 }, - { id = "svetlana", x = 3501, y = 3493 }, - { id = "irina", x = 3485, y = 3489 }, - { id = "alexis", x = 3499, y = 3474, level = 1 }, - { id = "milla", x = 3497, y = 3475 }, - { id = "galina", x = 3490, y = 3472, level = 1 }, - { id = "sofiya", x = 3483, y = 3495 }, - { id = "ksenia", x = 3498, y = 3472, level = 1 }, - { id = "yadviga", x = 3479, y = 3492 }, - { id = "nikita", x = 3479, y = 3498 }, - { id = "vera", x = 3490, y = 3473 }, - { id = "zoja", x = 3505, y = 3491 }, - { id = "liliya", x = 3479, y = 3498, level = 1 }, - { id = "achtryn", x = 3511, y = 3509, members = true }, - { id = "mazchna", x = 3512, y = 3509, members = true }, - # North of Mort Myre Swamp - { id = "ghoul", x = 3423, y = 3461, members = true }, - { id = "ghoul", x = 3426, y = 3465, members = true }, - { id = "ghoul", x = 3427, y = 3463, members = true }, - { id = "ghoul", x = 3428, y = 3458, members = true }, - { id = "ghoul", x = 3428, y = 3465, members = true }, - { id = "ghoul", x = 3430, y = 3462, members = true }, - { id = "ghoul", x = 3430, y = 3467, members = true }, - { id = "ghoul", x = 3433, y = 3458, members = true }, - { id = "ghoul", x = 3433, y = 3468, members = true }, - { id = "ghoul", x = 3434, y = 3464, members = true }, - # South of the Slayer Tower - { id = "ghoul", x = 3412, y = 3512, members = true }, - { id = "ghoul", x = 3412, y = 3515, members = true }, - { id = "ghoul", x = 3413, y = 3514, members = true }, - { id = "ghoul", x = 3414, y = 3512, members = true }, - { id = "ghoul", x = 3415, y = 3518, members = true }, - { id = "ghoul", x = 3416, y = 3509, members = true }, - { id = "ghoul", x = 3416, y = 3511, members = true }, - { id = "ghoul", x = 3417, y = 3518, members = true }, - { id = "ghoul", x = 3418, y = 3509, members = true }, - { id = "ghoul", x = 3419, y = 3512, members = true }, - { id = "ghoul", x = 3420, y = 3517, members = true }, - { id = "ghoul", x = 3420, y = 3518, members = true }, - { id = "old_crone", x = 3461, y = 3558, members = true }, -] \ No newline at end of file diff --git a/data/area/morytania/canifis/canifis.npcs.toml b/data/area/morytania/canifis/canifis.npcs.toml deleted file mode 100644 index 8beb0d42cd..0000000000 --- a/data/area/morytania/canifis/canifis.npcs.toml +++ /dev/null @@ -1,189 +0,0 @@ -[rufus] -id = 1038 -shop = "rufuss_meat_emporium" -examine = "There's something strange about him..." - -[barker] -id = 1039 -shop = "barkers_haberdashery" -examine = "Has a distinguished air about him." - -[fidelio] -id = 1040 -shop = "canifis_general_store" -examine = "A lycanthrope shopkeeper." - -[sbott] -id = 1041 -examine = "Has a faint smell of chemicals about him." - -[roavar] -id = 1042 -examine = "Seems a jolly chap." - -[malak] -id = 1920 -examine = "One of Morytania's vampyric nobility." - -[stranger] -id = 2020 - -[taxidermist] -id = 4246 -examine = "She smells unpleasantly of chemicals." - -[boris] -id = 6026 -hitpoints = 1000 -att = 70 -str = 70 -def = 70 -combat_def = "canifis_citizen" -slayer_xp = 100.0 -respawn_delay = 25 -categories = ["werewolves"] -drop_table = "canifis_werewolf" -examine = "A traveling man. Are those fleas?" - -[imre] -id = 6027 -clone = "boris" -examine = "A well organised shop keeper." - -[yuri] -id = 6028 -clone = "boris" -examine = "He's staring at the moon." - -[joseph] -id = 6029 -clone = "boris" -examine = "He keeps calling me comrade." - -[nikolai] -id = 6030 -clone = "boris" -examine = "There's a set of brows that mean business." - -[eduard] -id = 6031 -clone = "boris" -examine = "This is one feral looking innkeeper." - -[lev] -id = 6032 -clone = "boris" -examine = "A tough looking villager." - -[georgy] -id = 6033 -clone = "boris" -examine = "A simple villager." - -[svetlana] -id = 6034 -clone = "boris" -examine = "Seems a bit of a drama queen." - -[irina] -id = 6035 -clone = "boris" -examine = "I bet she works out." - -[alexis] -id = 6036 -clone = "boris" -drop_table = "werewolf" -examine = "She doesn't say much." - -[milla] -id = 6037 -clone = "boris" -examine = "She looks like she can handle herself." - -[galina] -id = 6038 -clone = "boris" -examine = "She seems light on her feet." - -[sofiya] -id = 6039 -clone = "boris" -examine = "A wise villager." - -[ksenia] -id = 6040 -clone = "boris" -examine = "Not exactly a warrior princess." - -[yadviga] -id = 6041 -clone = "boris" -examine = "A well off villager." - -[nikita] -id = 6042 -clone = "boris" -examine = "The woman." - -[vera] -id = 6043 -clone = "boris" -examine = "She seems sure of herself." - -[zoja] -id = 6044 -clone = "boris" -examine = "A healthy villager." - -[werewolf] -id = 6006 -hitpoints = 1000 -att = 70 -str = 70 -def = 70 -combat_def = "werewolf" -slayer_xp = 100.0 -categories = ["werewolves"] -drop_table = "canifis_werewolf" -examine = "Eek! A werewolf!" - -[liliya] -id = 6045 -clone = "werewolf" -examine = "She's looking for flowers." - -[achtryn] -id = 8481 - -[mazchna_base] -id = 8482 - -[ghoul] -id = 1218 -hitpoints = 500 -att = 30 -str = 40 -def = 30 -combat_def = "ghoul" -hunt_mode = "cowardly" -slayer_xp = 50.0 -categories = ["ghouls"] -respawn_delay = 35 -drop_table = "ghoul" -examine = "It's totally savage." - -[old_crone] -id = 1695 - -[stranger_2] -id = 1577 - -[stranger_3] -id = 1576 - -[mazchna] -id = 8464 - -[achtryn_3] -id = 8465 diff --git a/data/area/morytania/dragontooth_island/dragontooth_island.npc-spawns.toml b/data/area/morytania/dragontooth_island/dragontooth_island.npc-spawns.toml deleted file mode 100644 index a97f72d2b4..0000000000 --- a/data/area/morytania/dragontooth_island/dragontooth_island.npc-spawns.toml +++ /dev/null @@ -1,3 +0,0 @@ -spawns = [ - { id = "ghost_captain", x = 3792, y = 3560, members = true }, -] diff --git a/data/area/morytania/port_phasmatys/port_phasmatys.areas.toml b/data/area/morytania/port_phasmatys/port_phasmatys.areas.toml deleted file mode 100644 index 0c7fd30f0f..0000000000 --- a/data/area/morytania/port_phasmatys/port_phasmatys.areas.toml +++ /dev/null @@ -1,14 +0,0 @@ -[ectophial_teleport] -x = [3654, 3665] -y = [3521, 3524] -tags = ["teleport"] - -[port_phasmatys] -x = [3650, 3650, 3653, 3653, 3655, 3668, 3677, 3688, 3688, 3718, 3718] -y = [3456, 3471, 3474, 3505, 3507, 3507, 3516, 3516, 3532, 3532, 3456] - -[greater_phasmatys] -x = [3648, 3711] -y = [3456, 3583] -tags = ["penguin_area"] -hint = "near the city of ghosts." diff --git a/data/area/morytania/port_phasmatys/port_phasmatys.drops.toml b/data/area/morytania/port_phasmatys/port_phasmatys.drops.toml deleted file mode 100644 index 696acb84e8..0000000000 --- a/data/area/morytania/port_phasmatys/port_phasmatys.drops.toml +++ /dev/null @@ -1,51 +0,0 @@ -[tortured_soul_drop_table] -roll = 128 -drops = [ - { id = "body_rune", amount = 7, chance = 7 }, - { id = "water_rune", amount = 6, chance = 6 }, - { id = "law_rune", amount = 2, chance = 3 }, - { id = "grimy_harralander", chance = 5 }, - { id = "grimy_tarromin", chance = 3 }, - { id = "grimy_marrentill", chance = 2 }, - { id = "grimy_guam" }, - { id = "grimy_ranarr" }, - { id = "coins", amount = 12 }, - { id = "coins", amount = 2, chance = 2 }, - { id = "coins", amount = 6, chance = 3 }, - { id = "coins", amount = 5, chance = 3 }, - { id = "coins", amount = 3, chance = 30 }, - { id = "coins", amount = 2, chance = 21 }, - { id = "nothing", amount = 0, chance = 16 }, - { id = "limpwurt_root", chance = 3 }, - { id = "vial_of_water" }, -] - -[undead_chicken_drop_table] -type = "all" -drops = [ - { id = "bones" }, - { id = "raw_undead_chicken" }, - { table = "undead_chicken_secondary" }, -] - -[undead_chicken_secondary] -roll = 128 -drops = [ - { id = "feather", amount = 5, chance = 64 }, - { id = "feather", amount = 15, chance = 32 }, -] - -[undead_cow_drop_table] -type = "all" -drops = [ - { id = "cowhide" }, - { id = "raw_undead_beef" }, - { id = "bones" }, - { table = "undead_cow_tertiary" }, -] - -[undead_cow_tertiary] -roll = 4 -drops = [ - { id = "undead_cow_ribs", variable = "rag_and_bone_man_wish_list", equals = "started", default = "unstarted" }, -] diff --git a/data/area/morytania/port_phasmatys/port_phasmatys.npc-spawns.toml b/data/area/morytania/port_phasmatys/port_phasmatys.npc-spawns.toml deleted file mode 100644 index cdb40517d3..0000000000 --- a/data/area/morytania/port_phasmatys/port_phasmatys.npc-spawns.toml +++ /dev/null @@ -1,61 +0,0 @@ -spawns = [ -# 14902 - { id = "rat", x = 3715, y = 3502, level = 1, members = true }, - { id = "rat", x = 3714, y = 3498, members = true }, - { id = "rat", x = 3713, y = 3492, members = true }, - { id = "trader_crewmember_purple", x = 3701, y = 3502, members = true }, - { id = "trader_crewmember_grey", x = 3701, y = 3503, members = true }, - { id = "velorina_port_phasmatys", x = 3678, y = 3510, members = true }, - { id = "necrovarus_port_phasmatys", x = 3660, y = 3516, members = true }, - { id = "ghost_disciple_port_phasmatys", x = 3655, y = 3516 }, - { id = "ghost_disciple_port_phasmatys", x = 3662, y = 3516 }, - { id = "ghost_disciple_port_phasmatys", x = 3653, y = 3518, level = 1 }, - { id = "ak_haranu", x = 3689, y = 3495, members = true }, - { id = "robin_port_phasmatys", x = 3672, y = 3491, members = true }, - { id = "ghost_villager_port_phasmatys", x = 3658, y = 3465, members = true }, - { id = "ghost_villager_port_phasmatys", x = 3661, y = 3494, members = true }, - { id = "ghost_villager_port_phasmatys", x = 3661, y = 3497, members = true }, - { id = "ghost_villager_port_phasmatys", x = 3662, y = 3463, members = true }, - { id = "ghost_villager_port_phasmatys", x = 3662, y = 3502, members = true }, - { id = "ghost_villager_port_phasmatys", x = 3665, y = 3493, members = true }, - { id = "ghost_villager_port_phasmatys", x = 3668, y = 3474, members = true }, - { id = "ghost_villager_port_phasmatys", x = 3672, y = 3503, members = true }, - { id = "ghost_villager_port_phasmatys", x = 3673, y = 3479, members = true }, - { id = "ghost_villager_port_phasmatys", x = 3675, y = 3496, members = true }, - { id = "ghost_villager_port_phasmatys", x = 3679, y = 3474, members = true }, - { id = "ghost_villager_port_phasmatys", x = 3684, y = 3485, members = true }, - { id = "tortured_soul", x = 3648, y = 3472, members = true }, - { id = "tortured_soul", x = 3649, y = 3503, members = true }, - { id = "tortured_soul", x = 3654, y = 3536, members = true }, - { id = "tortured_soul", x = 3657, y = 3534, members = true }, - { id = "tortured_soul", x = 3666, y = 3529, members = true }, - { id = "tortured_soul", x = 3668, y = 3537, members = true }, - { id = "tortured_soul", x = 3672, y = 3520, members = true }, - { id = "tortured_soul", x = 3673, y = 3528, members = true }, - { id = "ghost_shopkeeper_port_phasmatys", x = 3658, y = 3477, members = true }, - { id = "ghost_innkeeper_port_phasmatys", x = 3681, y = 3496, members = true }, - { id = "ghost_banker_port_phasmatys", x = 3687, y = 3464 }, - { id = "ghost_banker_port_phasmatys", x = 3688, y = 3464 }, - { id = "ghost_banker_port_phasmatys", x = 3690, y = 3464 }, - { id = "ghost_banker_port_phasmatys", x = 3691, y = 3464 }, - { id = "ghost_sailor_port_phasmatys", x = 3686, y = 3503, members = true }, - { id = "ghost_sailor_port_phasmatys", x = 3689, y = 3508, members = true }, - { id = "ghost_sailor_port_phasmatys", x = 3690, y = 3514, members = true }, - { id = "ghost_sailor_port_phasmatys", x = 3691, y = 3512, members = true }, - { id = "ghost_sailor_port_phasmatys", x = 3694, y = 3518, level = 1, members = true }, - { id = "ghost_sailor_port_phasmatys", x = 3698, y = 3497, members = true }, - { id = "ghost_sailor_port_phasmatys", x = 3698, y = 3514, level = 1, members = true }, - { id = "ghost_sailor_port_phasmatys", x = 3704, y = 3496, members = true }, - { id = "ghost_captain", x = 3703, y = 3487, members = true }, - { id = "ghost_captain_2", x = 3691, y = 3513, members = true }, - { id = "ghost_guard_port_phasmatys", x = 3652, y = 3484, direction = "WEST", members = true }, - { id = "ghost_guard_port_phasmatys", x = 3652, y = 3487, direction = "WEST", members = true }, - { id = "ghost_guard_port_phasmatys", x = 3658, y = 3508, direction = "NORTH", members = true }, - { id = "ghost_guard_port_phasmatys", x = 3661, y = 3508, direction = "NORTH", members = true }, - { id = "pirate_pete", x = 3680, y = 3537, members = true }, - { id = "melina_port_phasmatys", x = 3674, y = 3484, members = true }, - { id = "droalak_port_phasmatys", x = 3657, y = 3469, members = true }, - { id = "bill_teach_port_phasmatys", x = 3713, y = 3497, level = 1, members = true }, - { id = "gravingas_port_phasmatys", x = 3660, y = 3499, members = true }, - { id = "gravingas_port_phasmatys_2", x = 3660, y = 3499, members = true }, -] \ No newline at end of file diff --git a/data/area/morytania/port_phasmatys/port_phasmatys.npcs.toml b/data/area/morytania/port_phasmatys/port_phasmatys.npcs.toml deleted file mode 100644 index f41322d5f5..0000000000 --- a/data/area/morytania/port_phasmatys/port_phasmatys.npcs.toml +++ /dev/null @@ -1,102 +0,0 @@ -[velorina_port_phasmatys] -id = 1683 -examine = "A ghost lady." - -[necrovarus_port_phasmatys] -id = 1684 -examine = "An evil priest." - -[ghost_disciple_port_phasmatys] -id = 1686 -examine = "A ghost disciple." - -[ak_haranu] -id = 1687 -shop = "ak_haranus_exotic_shop" -examine = "A trader from across the eastern sea." - -[ak_haranu_2] -id = 1688 - -[robin_port_phasmatys] -id = 1694 -examine = "A master bowman." - -[ghost_villager_port_phasmatys] -id = 1697 -examine = "A spooky ghost villager." - -[tortured_soul] -id = 1698 -hitpoints = 510 -att = 52 -str = 62 -def = 38 -combat_def = "tortured_soul" -slayer_xp = 51.0 -categories = ["ghosts"] -respawn_delay = 50 -drop_table = "tortured_soul" -examine = "This poor soul cannot understand why it has not passed to the next world." - -[ghost_shopkeeper_port_phasmatys] -id = 1699 -shop = "port_phasmatys_general_store" -examine = "Beware the ghostly shopkeeper's wares!" - -[ghost_innkeeper_port_phasmatys] -id = 1700 -examine = "Doesn't look like the bar's open anymore." - -[ghost_banker_port_phasmatys] -id = 1702 -examine = "A ghost banker." - -[ghost_sailor_port_phasmatys] -id = 1703 -examine = "A ghost sailor." - -[ghost_captain] -id = 1704 -examine = "A ghostship captain." - -[ghost_captain_2] -id = 1705 - -[ghost_guard_port_phasmatys] -id = 1706 -examine = "This ghost guards the gates of Port Phasmatys." - -[pirate_pete] -id = 2825 -examine = "A shifty-looking character." - -[melina_port_phasmatys] -id = 2933 - -[droalak_port_phasmatys] -id = 2936 - -[bill_teach_port_phasmatys] -id = 3157 - -[gravingas_port_phasmatys] -id = 6075 - -[gravingas_port_phasmatys_2] -id = 6076 - -[melina_port_phasmatys_2] -id = 2935 - -[droalak_port_phasmatys_2] -id = 2938 - -[bill_teach_port_phasmatys_2] -id = 3160 - -[bill_teach_port_phasmatys_3] -id = 3156 - -[gravingas_port_phasmatys_3] -id = 1685 diff --git a/data/area/morytania/port_phasmatys/port_phasmatys.objs.toml b/data/area/morytania/port_phasmatys/port_phasmatys.objs.toml deleted file mode 100644 index 410a841135..0000000000 --- a/data/area/morytania/port_phasmatys/port_phasmatys.objs.toml +++ /dev/null @@ -1,55 +0,0 @@ -[bank_booth_port_phasmatys] -id = 5276 -examine = "The bank teller will serve you from here." - -[wrecked_ghost_ship_ladder] -id = 5265 -examine = "I can climb up here." - -[wrecked_ghost_ship_ladder_down] -id = 5266 -examine = "I can go below decks with this ladder." - -[wrecked_ghost_ship_gangplank] -id = 5285 -examine = "The tatty gangplank of a tatty ship." - -[wrecked_ghost_ship_gangplank_end] -id = 5286 -examine = "The tatty gangplank of a tatty ship." - -[wrecked_ghost_ship_rock] -id = 5269 -examine = " I can't see a rock!" - -[trapdoor_ahoy_opened] -id = 5268 -examine = "I wonder what's down there." - -[trapdoor_ahoy_closed] -id = 5267 -examine = "I wonder what's under it." - -[ectopool_shortcut_rail] -id = 9308 -examine = "A well weathered wall." - -[ectopool_shortcut_wall] -id = 9307 -examine = "A well weathered wall." - -[ectopool_staircase_down] -id = 5263 -examine = "These stairs were carved out of the rock of the cavern." - -[ectopool_staircase_up] -id = 5262 -examine = "These stairs were carved out of the rock of the cavern." - -[phasmatys_barrier] -id = 52760 -examine = "A ghostly barrier." - -[cooking_range_port_phasmatys] -id = 5275 -examine = "An appliance for cooking with." diff --git a/data/area/morytania/port_phasmatys/port_phasmatys.teles.toml b/data/area/morytania/port_phasmatys/port_phasmatys.teles.toml deleted file mode 100644 index 4716bb7f56..0000000000 --- a/data/area/morytania/port_phasmatys/port_phasmatys.teles.toml +++ /dev/null @@ -1,69 +0,0 @@ -[wrecked_ghost_ship_ladder] -option = "Climb-up" -tile = { x = 3609, y = 3543 } -to = { x = 3610, y = 3543, level = 1 } - -[wrecked_ghost_ship_ladder] -option = "Climb-up" -tile = { x = 3613, y = 3543 } -to = { x = 3614, y = 3543, level = 1 } - -[wrecked_ghost_ship_ladder_down] -option = "Climb-down" -tile = { x = 3609, y = 3543, level = 1 } -to = { x = 3612, y = 3543 } - -[wrecked_ghost_ship_ladder_down] -option = "Climb-down" -tile = { x = 3613, y = 3543, level = 1 } -to = { x = 3614, y = 3543 } - -[wrecked_ghost_ship_ladder_down] -option = "Climb-down" -tile = { x = 3613, y = 3543, level = 1 } -to = { x = 3614, y = 3543 } - -[trapdoor_ahoy_down] -option = "Climb-down" -tile = { x = 3653, y = 3519 } -to = { x = 3699, y = 9888, level = 3 } - -[ectopool_shortcut_rail] -option = "Jump-down" -tile = { x = 3670, y = 9888, level = 3 } -to = { x = 3671, y = 9888, level = 2 } - -[ectopool_shortcut_wall] -option = "Jump-up" -tile = { x = 3670, y = 9888, level = 2 } -to = { x = 3670, y = 9888, level = 3 } - -[ectopool_staircase_down] -option = "Climb-down" -tile = { x = 3689, y = 9887, level = 3 } -near = { x = 3688, y = 9887, level = 2, height = 3 } - -[ectopool_staircase_up] -option = "Climb-up" -tile = { x = 3689, y = 9887, level = 2 } -near = { x = 3692, y = 9887, level = 3, height = 3 } - -[ectopool_staircase_down] -option = "Climb-down" -tile = { x = 3672, y = 9887, level = 2 } -near = { x = 3675, y = 9887, level = 1, height = 3 } - -[ectopool_staircase_up] -option = "Climb-up" -tile = { x = 3672, y = 9887, level = 1 } -near = { x = 3671, y = 9887, level = 2, height = 3 } - -[ectopool_staircase_down] -option = "Climb-down" -tile = { x = 3684, y = 9887, level = 1 } -near = { x = 3683, y = 9887, level = 0, height = 3 } - -[ectopool_staircase_up] -option = "Climb-up" -tile = { x = 3684, y = 9887 } -near = { x = 3687, y = 9887, level = 1, height = 3 } diff --git a/data/quest/members/ghosts_ahoy/ghosts_ahoy.drops.toml b/data/quest/members/ghosts_ahoy/ghosts_ahoy.drops.toml deleted file mode 100644 index 7a2660514a..0000000000 --- a/data/quest/members/ghosts_ahoy/ghosts_ahoy.drops.toml +++ /dev/null @@ -1,5 +0,0 @@ -[giant_lobster_ghosts_ahoy_drop_table] -type = "all" -drops = [ - { id = "seaweed" }, -] diff --git a/data/quest/members/ghosts_ahoy/ghosts_ahoy.items.toml b/data/quest/members/ghosts_ahoy/ghosts_ahoy.items.toml deleted file mode 100644 index 5b9a2a0aeb..0000000000 --- a/data/quest/members/ghosts_ahoy/ghosts_ahoy.items.toml +++ /dev/null @@ -1,279 +0,0 @@ -[signed_oak_bow] -id = 4236 -tradeable = false -weight = 2.267 -slot = "Weapon" -type = "TwoHanded" -examine = "This bow has been signed by Robin, Master Bowman." -kept = "Wilderness" - -[nettle_water] -id = 4237 -heals = 10 -excess = "bowl" -tradeable = false -weight = 0.907 -empty = "bowl" -examine = "It's a bowl of water, with some nettles in it." -kept = "Reclaim" - -[puddle_of_slime] # Unreleased -id = 4238 -tradeable = false -weight = 0.453 -examine = "It's a puddle of slime." - -[nettle_tea] -id = 4239 -heals = 30 -excess = "bowl" -tradeable = false -weight = 0.907 -examine = "It's a bowl of nettle tea." -kept = "Reclaim" - -[milky_nettle_tea] -id = 4240 -heals = 30 -excess = "bowl" -tradeable = false -weight = 0.907 -empty = "bowl" -examine = "It's a bowl of milky nettle tea." -kept = "Reclaim" - -[nettles] -id = 4241 -tradeable = false -weight = 0.007 -examine = "A handful of nettles." -kept = "Reclaim" - -[cup_of_tea_nettle_tea] -id = 4242 -heals = 30 -excess = "empty_cup" -tradeable = false -weight = 0.15 -empty = "empty_cup" -examine = "A nice cup of nettle tea." -kept = "Reclaim" - -[cup_of_milky_tea] -id = 4243 -limit = 1000 -heals = 0 -excess = "empty_cup" -tradeable = false -weight = 0.15 -empty = "empty_cup" -examine = "A milky cup of nettle tea." -kept = "Reclaim" - -[porcelain_cup] -id = 4244 -tradeable = false -weight = 0.05 -examine = "A porcelain cup." -kept = "Reclaim" - -[cup_of_tea_ghosts_ahoy] -id = 4245 -tradeable = false -weight = 0.15 -empty = "porcelain_cup" -examine = "Some nettle tea in a porcelain cup." -kept = "Reclaim" - -[cup_of_milky_tea_ghosts_ahoy] -id = 4246 -limit = 1000 -heals = 0 -tradeable = false -weight = 0.15 -empty = "porcelain_cup" -examine = "Some milky nettle tea in a porcelain cup." -kept = "Reclaim" - -[mystical_robes] -id = 4247 -tradeable = false -weight = 0.907 -examine = "The Robes of Necrovarus." -kept = "Wilderness" - -[book_of_haricanto] -id = 4248 -tradeable = false -weight = 1.814 -examine = "The Book of Haricanto." -kept = "Wilderness" - -[translation_manual] -id = 4249 -tradeable = false -weight = 0.51 -examine = "A translation manual." -kept = "Wilderness" - -[ghostspeak_amulet_enchanted] -id = 4250 -tradeable = false -weight = 0.01 -slot = "Amulet" -examine = "The amulet of ghostspeak glows green from the crone's enchantment." -kept = "Wilderness" - -[ectophial] -id = 4251 -tradeable = false -weight = 0.035 -charges = 1 -aka = ["ecto", "ectophile"] -degrade = "ectophial_empty" -destroy = "The Ectophial will smash if you drop it. Velorina can replace it. You can also reclaim it from the quest storage chest." -examine = "The Ectophial." -kept = "Wilderness" - -[ectophial_empty] -id = 4252 -tradeable = false -weight = 0.035 -destroy = "The Ectophial will smash if you drop it. Velorina can replace it." -examine = "The Ectophial." -kept = "Wilderness" - -[model_ship] -id = 4253 -tradeable = false -weight = 0.01 -examine = "A small wooden ship." -kept = "Wilderness" - -[model_ship_silk] -id = 4254 -tradeable = false -weight = 0.01 -examine = "A small wooden ship with a silk flag." -kept = "Wilderness" - -[bone_key_ghosts_ahoy] -id = 4272 -tradeable = false -weight = 0.01 -examine = "A key dropped by Necrovarus." -kept = "Wilderness" - -[chest_key_ghosts_ahoy] -id = 4273 -tradeable = false -weight = 0.01 -examine = "A key to a chest." -kept = "Wilderness" - -[map_scrap_1] -id = 4274 -tradeable = false -weight = 0.01 -examine = "A section from some kind of map." -kept = "Wilderness" - -[map_scrap_3] -clone = "map_scrap_1" -id = 4275 - -[map_scrap_2] -clone = "map_scrap_1" -id = 4276 - -[treasure_map] -id = 4277 -tradeable = false -weight = 0.01 -examine = "A complete treasure map." -kept = "Wilderness" - -[ecto_token] -id = 4278 -tradeable = false -examine = "A token with ectoplasm on it." -kept = "Reclaim" - -[petition_form] -id = 4283 -tradeable = false -weight = 0.001 -examine = "A scroll of paper containing signatures." -kept = "Wilderness" - -[bedsheet] -id = 4284 -tradeable = false -weight = 1.0 -slot = "Hat" -type = "FullFace" -examine = "It's a bedsheet." -kept = "Wilderness" - -[bedsheet_ectoplasm] -id = 4285 -tradeable = false -weight = 1.0 -slot = "Hat" -type = "FullFace" -examine = "It's an ectoplasm-covered bedsheet." -kept = "Wilderness" - -[bucket_of_slime] -id = 4286 -tradeable = false -weight = 3.0 -empty = "bucket" -examine = "It's a bucket of ectoplasm." -kept = "Wilderness" - -[raw_undead_beef] -id = 4287 -price = 403 -limit = 1000 -weight = 0.283 -examine = "This raw beef is rancid." -kept = "Wilderness" - -[raw_undead_beef_noted] -id = 4288 - -[raw_undead_chicken] -id = 4289 -price = 307 -limit = 1000 -weight = 0.17 -examine = "This raw chicken is rancid." -kept = "Wilderness" - -[raw_undead_chicken_noted] -id = 4290 - -[cooked_undead_chicken] -id = 4291 -price = 57 -limit = 1000 -heals = 10 -weight = 0.141 -examine = "This cooked chicken looks disgusting." -kept = "Wilderness" - -[cooked_undead_chicken_noted] -id = 4292 - -[cooked_undead_meat] -id = 4293 -price = 69 -limit = 1000 -heals = 10 -weight = 0.283 -examine = "I wouldn't eat that if I were you." -kept = "Wilderness" - -[cooked_undead_meat_noted] -id = 4294 diff --git a/data/quest/members/ghosts_ahoy/ghosts_ahoy.npcs.toml b/data/quest/members/ghosts_ahoy/ghosts_ahoy.npcs.toml deleted file mode 100644 index f86b455380..0000000000 --- a/data/quest/members/ghosts_ahoy/ghosts_ahoy.npcs.toml +++ /dev/null @@ -1,13 +0,0 @@ -[giant_lobster_ghosts_ahoy] -id = 1693 -hitpoints = 320 -att = 18 -str = 26 -def = 40 -style = "stab" -max_hit_melee = 40 -hunt_mode = "cowardly" -slayer_xp = 32.0 -categories = ["scorpions"] -drop_table = "giant_lobster_ghosts_ahoy" -examine = "An extremely vicious lobster." diff --git a/data/quest/members/ghosts_ahoy/ghosts_ahoy.objs.toml b/data/quest/members/ghosts_ahoy/ghosts_ahoy.objs.toml deleted file mode 100644 index 3635b33193..0000000000 --- a/data/quest/members/ghosts_ahoy/ghosts_ahoy.objs.toml +++ /dev/null @@ -1,3 +0,0 @@ -[ectofuntus] -id = 5282 -examine = "A ghastly fountain filled with slime and bones, the source of Necrovarus' power." diff --git a/data/skill/cooking/cooking.recipes.toml b/data/skill/cooking/cooking.recipes.toml deleted file mode 100644 index 0f54baf733..0000000000 --- a/data/skill/cooking/cooking.recipes.toml +++ /dev/null @@ -1,499 +0,0 @@ -[uncooked_cake] -skill = "cooking" -level = 40 -remove = ["pot_of_flour", "egg", "bucket_of_milk", "cake_tin"] -add = ["uncooked_cake", "empty_pot", "bucket"] -ticks = 2 -message = "You mix the milk, flour and egg together to make a raw cake mix." - -[chocolate_cake_dust] -skill = "cooking" -level = 50 -xp = 30.0 -remove = ["cake", "chocolate_dust"] -add = ["chocolate_cake"] -ticks = 2 -message = "You make a chocolate cake." - -[chocolate_cake_bar] -skill = "cooking" -level = 50 -xp = 30.0 -remove = ["cake", "chocolate_bar"] -add = ["chocolate_cake"] -ticks = 2 -message = "You make a chocolate cake." - -[incomplete_stew_potato] -skill = "cooking" -level = 25 -remove = ["bowl_of_water", "raw_potato"] -add = ["incomplete_stew_potato"] -ticks = 2 -message = "You cut up the potato and put it into the bowl." - -[incomplete_stew_meat] -skill = "cooking" -level = 25 -remove = ["bowl_of_water", "cooked_meat"] -add = ["incomplete_stew_meat"] -ticks = 2 -message = "You cut up the meat and put it into the bowl." - -[incomplete_stew_meat_chicken] -skill = "cooking" -level = 25 -remove = ["bowl_of_water", "cooked_chicken"] -add = ["incomplete_stew_meat"] -ticks = 2 -message = "You cut up the meat and put it into the bowl." - -[uncooked_stew] -skill = "cooking" -level = 25 -remove = ["incomplete_stew_potato", "cooked_meat"] -add = ["uncooked_stew"] -ticks = 2 -message = "You cut up the meat and put it into the stew." - -[uncooked_chicken_stew] -skill = "cooking" -level = 25 -remove = ["incomplete_stew_potato", "cooked_chicken"] -add = ["uncooked_stew"] -ticks = 2 -message = "You cut up the chicken and put it into the stew." - -[uncooked_curry] -skill = "cooking" -level = 60 -remove = ["uncooked_stew", "spice"] -add = ["uncooked_curry"] -ticks = 2 - -[uncooked_curry_leaf] -skill = "cooking" -level = 60 -remove = [{ id = "uncooked_stew" }, { id = "curry_leaf", amount = 3 }] -add = ["uncooked_curry"] -ticks = 2 - -[incomplete_pizza] -skill = "cooking" -level = 35 -remove = ["pizza_base", "tomato"] -add = ["incomplete_pizza"] -ticks = 2 -message = "You add the tomato to the pizza." - -[uncooked_pizza] -skill = "cooking" -level = 35 -remove = ["incomplete_pizza", "cheese"] -add = ["uncooked_pizza"] -ticks = 2 -message = "You add the cheese to the pizza." - -[meat_pizza] -skill = "cooking" -level = 45 -xp = 26.0 -remove = ["plain_pizza", "cooked_meat"] -add = ["meat_pizza"] -ticks = 2 -message = "You add the meat to the pizza." - -[chicken_meat_pizza] -skill = "cooking" -level = 45 -xp = 26.0 -remove = ["plain_pizza", "cooked_chicken"] -add = ["meat_pizza"] -ticks = 2 -message = "You add the meat to the pizza." - -[anchovy_pizza] -skill = "cooking" -level = 55 -xp = 39.0 -remove = ["plain_pizza", "anchovies"] -add = ["anchovy_pizza"] -ticks = 2 -message = "You add the anchovies to the pizza." - -[pineapple_ring_pizza] -skill = "cooking" -level = 65 -xp = 45.0 -remove = ["plain_pizza", "pineapple_ring"] -add = ["pineapple_pizza"] -ticks = 2 -message = "You add the pineapple to the pizza. Why?" - -[pineapple_chunk_pizza] -skill = "cooking" -level = 65 -xp = 45.0 -remove = ["plain_pizza", "pineapple_chunks"] -add = ["pineapple_pizza"] -ticks = 2 -message = "You add the pineapple to the pizza. Why?" - -[bread_dough_bucket] -skill = "cooking" -remove = ["bucket_of_water", "pot_of_flour"] -add = ["bread_dough", "bucket", "empty_pot"] -ticks = 2 -message = "You mix the water and flour to make some bread dough." - -[bread_dough_bowl] -skill = "cooking" -remove = ["bowl_of_water", "pot_of_flour"] -add = ["bread_dough", "bowl", "empty_pot"] -ticks = 2 -message = "You mix the water and flour to make some bread dough." - -[bread_dough_jug] -skill = "cooking" -remove = ["jug_of_water", "pot_of_flour"] -add = ["bread_dough", "jug", "empty_pot"] -ticks = 2 -message = "You mix the water and flour to make some bread dough." - -[pastry_dough_bucket] -skill = "cooking" -xp = 1.0 -remove = ["bucket_of_water", "pot_of_flour"] -add = ["pastry_dough", "bucket", "empty_pot"] -ticks = 2 -message = "You mix the water and flour to make some pastry dough." - -[pastry_dough_bowl] -skill = "cooking" -xp = 1.0 -remove = ["bowl_of_water", "pot_of_flour"] -add = ["pastry_dough", "bowl", "empty_pot"] -ticks = 2 -message = "You mix the water and flour to make some pastry dough." - -[pastry_dough_jug] -skill = "cooking" -xp = 1.0 -remove = ["jug_of_water", "pot_of_flour"] -add = ["pastry_dough", "jug", "empty_pot"] -ticks = 2 -message = "You mix the water and flour to make some pastry dough." - -[pizza_base_bucket] -skill = "cooking" -level = 35 -remove = ["bucket_of_water", "pot_of_flour"] -add = ["pizza_base", "bucket", "empty_pot"] -ticks = 2 -message = "You mix the water and flour to make a pizza base." - -[pizza_base_bowl] -skill = "cooking" -level = 35 -remove = ["bowl_of_water", "pot_of_flour"] -add = ["pizza_base", "bowl", "empty_pot"] -ticks = 2 -message = "You mix the water and flour to make a pizza base." - -[pizza_base_jug] -skill = "cooking" -level = 35 -remove = ["jug_of_water", "pot_of_flour"] -add = ["pizza_base", "jug", "empty_pot"] -ticks = 2 -message = "You mix the water and flour to make a pizza base." - -[pitta_dough_bucket] -skill = "cooking" -level = 35 -xp = 1.0 -remove = ["bucket_of_water", "pot_of_flour"] -add = ["pitta_dough", "bucket", "empty_pot"] -ticks = 1 -message = "You mix the water and flour to make a pizza base." - -[pitta_dough_bowl] -skill = "cooking" -level = 35 -xp = 1.0 -remove = ["bowl_of_water", "pot_of_flour"] -add = ["pitta_dough", "bowl", "empty_pot"] -ticks = 1 -message = "You mix the water and flour to make a pizza base." - -[pitta_dough_jug] -skill = "cooking" -level = 35 -xp = 1.0 -remove = ["jug_of_water", "pot_of_flour"] -add = ["pitta_dough", "jug", "empty_pot"] -ticks = 1 -message = "You mix the water and flour to make a pizza base." - -[pie_shell] -skill = "cooking" -remove = ["pastry_dough", "pie_dish"] -add = ["pie_shell"] -ticks = 2 -message = "You put the pastry dough into the pie dish to make a pie shell." - -[uncooked_apple_pie] -skill = "cooking" -level = 30 -remove = ["cooking_apple", "pie_shell"] -add = ["uncooked_apple_pie"] -ticks = 2 -message = "You fill the pie with apple." - -[uncooked_meat_pie] -skill = "cooking" -level = 20 -remove = ["redberries", "pie_shell"] -add = ["uncooked_meat_pie"] -ticks = 2 -message = "You fill the pie with meat." - -[nettle_water] -skill = "cooking" -remove = ["bowl_of_water", "nettles"] -add = ["nettle_water"] -ticks = 2 -message = "You place the nettles into the bowl of water." - -[milky_nettle_tea] -skill = "cooking" -remove = ["nettle_tea", "bucket_of_milk"] -add = ["milky_nettle_tea", "bucket"] -ticks = 2 -sound = "drink" - -[cup_of_tea_nettle_tea] -skill = "cooking" -remove = ["empty_cup", "nettle_tea"] -add = ["cup_of_tea_nettle_tea", "bowl"] -ticks = 2 -sound = "drink" - -[cup_of_milky_nettle_tea] -skill = "cooking" -remove = ["empty_cup", "milky_nettle_tea"] -add = ["cup_of_milky_tea", "bowl"] -ticks = 2 -sound = "drink" - -[cup_of_milky_tea] -skill = "cooking" -remove = ["cup_of_tea_nettle_tea", "bucket_of_milk"] -add = ["cup_of_milky_tea", "bucket"] -ticks = 2 -sound = "drink" - -[ugthanki_kebab] -skill = "cooking" -level = 58 -xp = 40.0 -remove = ["kebab_mix", "pitta_bread"] -add = ["ugthanki_kebab", "bowl"] -ticks = 2 - -[kebab_mix_ugthanki] -skill = "cooking" -level = 58 -remove = ["onion_and_tomato", "ugthanki_meat"] -add = ["kebab_mix"] -ticks = 2 - -[kebab_mix_onion] -skill = "cooking" -level = 58 -remove = ["onion_and_tomato", "onion"] -add = ["kebab_mix"] -ticks = 2 - -[kebab_mix_tomato] -skill = "cooking" -level = 58 -remove = ["onion_and_tomato", "tomato"] -add = ["kebab_mix"] -ticks = 2 - -[super_kebab] -remove = ["kebab", "red_hot_sauce"] -add = ["super_kebab"] -ticks = 2 - -[super_ugthanki_kebab] -remove = ["ugthanki_kebab", "red_hot_sauce"] -add = ["super_kebab"] -ticks = 2 - -[super_kebab_smelling] -remove = ["ugthanki_kebab_smelling", "red_hot_sauce"] -add = ["super_kebab"] -ticks = 2 - -[coconut_milk] -remove = ["vial", "coconut_open"] -add = ["coconut_milk", "coconut_shell"] -ticks = 4 -message = "You fill the vial with coconut milk." - -[coconut_open] -requires = ["hammer"] -remove = ["coconut"] -add = ["coconut_open"] -ticks = 4 -message = "You break the coconut with the hammer." - -[potato_with_butter] -skill = "cooking" -level = 39 -xp = 40.0 -remove = ["baked_potato", "pat_of_butter"] -add = ["potato_with_butter"] -ticks = 2 -message = "You add the butter to the potato." - -[potato_with_cheese] -skill = "cooking" -level = 47 -xp = 40.0 -remove = ["cheese", "potato_with_butter"] -add = ["potato_with_cheese"] -ticks = 2 -message = "You add the cheese to the potato." - -[chilli_potato] -skill = "cooking" -level = 41 -xp = 15.0 -remove = ["chilli_con_carne", "potato_with_butter"] -add = ["chilli_potato", "bowl"] -ticks = 2 -message = "You add the topping to the potato." - -[egg_potato] -skill = "cooking" -level = 51 -xp = 45.0 -remove = ["egg_and_tomato", "potato_with_butter"] -add = ["egg_potato", "bowl"] -ticks = 2 -message = "You add the topping to the potato." - -[mushroom_potato] -skill = "cooking" -level = 64 -xp = 55.0 -remove = ["mushroom_and_onion", "potato_with_butter"] -add = ["mushroom_potato", "bowl"] -ticks = 2 -message = "You add the topping to the potato." - -[tuna_potato] -skill = "cooking" -level = 68 -xp = 10.0 -remove = ["tuna_and_corn", "potato_with_butter"] -add = ["tuna_potato", "bowl"] -ticks = 2 -message = "You add the topping to the potato." - -[chilli_con_carne] -skill = "cooking" -xp = 25.0 -requires = ["knife"] -remove = ["cooked_meat", "spicy_sauce"] -add = ["chilli_con_carne"] -ticks = 2 -message = "You chop up the meat and add it to the sauce in the bowl." - -[egg_and_tomato] -skill = "cooking" -level = 23 -remove = ["scrambled_egg", "tomato"] -add = ["egg_and_tomato"] -ticks = 2 -message = "You mix the ingredients to make the topping." - -[mushroom_and_onion] -skill = "cooking" -level = 57 -remove = ["fried_mushrooms", "fried_onions"] -add = ["mushroom_and_onion", "bowl"] -ticks = 2 -message = "You mix the ingredients to make the topping." - -[tuna_and_corn] -skill = "cooking" -level = 67 -requires = ["knife"] -remove = ["chopped_tuna", "cooked_sweetcorn"] -add = ["tuna_and_corn"] -ticks = 2 -message = "You mix the ingredients to make the topping." - -[sweetcorn_and_tuna] -skill = "cooking" -level = 67 -requires = ["knife"] -remove = ["sweetcorn_bowl", "tuna"] -add = ["tuna_and_corn"] -ticks = 2 -message = "You mix the ingredients to make the topping." - -[spicy_sauce] -skill = "cooking" -level = 9 -xp = 25.0 -remove = ["chopped_garlic", "gnome_spice"] -add = ["spicy_sauce"] -ticks = 2 -message = "You add the spices to the garlic to make a spicy sauce." - -[chopped_onion] -skill = "cooking" -type = "cut" -requires = ["knife"] -remove = ["bowl", "onion"] -add = ["chopped_onion"] -ticks = 2 -message = "You cut the onion into the bowl." - -[chopped_garlic] -skill = "cooking" -type = "cut" -requires = ["knife"] -remove = ["bowl", "garlic"] -add = ["chopped_garlic"] -ticks = 2 -message = "You chop the onion into the bowl." - -[uncooked_egg] -skill = "cooking" -remove = ["bowl", "egg"] -add = ["uncooked_egg"] -ticks = 2 -message = "You carefully break the egg into the bowl." - -[sliced_mushrooms] -skill = "cooking" -type = "cut" -requires = ["knife"] -remove = ["bowl", "bittercap_mushroom"] -add = ["sliced_mushrooms"] -ticks = 2 -message = "You slice the mushroom into the bowl." - -[sweetcorn_bowl] -skill = "cooking" -type = "cut" -remove = ["bowl", "cooked_sweetcorn"] -add = ["sweetcorn_bowl"] -ticks = 2 -message = "You put the cooked sweetcorn into the bowl." diff --git a/data/skill/cooking/cooking.tables.toml b/data/skill/cooking/cooking.tables.toml deleted file mode 100644 index cdf1f61139..0000000000 --- a/data/skill/cooking/cooking.tables.toml +++ /dev/null @@ -1,829 +0,0 @@ -[cooking] -level = "int" -xp = "int" -cooked = "item" -cooked_message = "string" -leftover = "item" -burnt = "item" -burnt_message = "string" -start_ticks = "int" -ticks = "int" -type = "string" -type_default = "cook" -range_only = "boolean" -chance_fire = "range" -chance_range = "range" -chance_cook_o_matic = "range" -chance_gauntlet = "range" - -[.bread_dough] -level = 1 -xp = 400 -range_only = true -chance_range = [11, 491] -chance_cook_o_matic = [128, 511] -chance_gauntlet = [11, 491] -cooked = "bread" -cooked_message = "You successfully bake some bread." -burnt = "burnt_bread" -burnt_message = "You accidentally burn the bread dough." - -[.chopped_onion] -level = 42 -xp = 600 -range_only = true -chance_range = [35, 321] -chance_cook_o_matic = [35, 321] -chance_gauntlet = [35, 321] -cooked = "fried_onions" -cooked_message = "You successfully fry the onion." -burnt = "burnt_onion" -burnt_message = "You accidentally burn the onion." - -[.crab_meat] -level = 21 -xp = 1000 -chance_fire = [57, 376] -chance_range = [57, 376] -chance_cook_o_matic = [57, 376] -chance_gauntlet = [57, 376] -cooked = "cooked_crab_meat_5" -burnt = "burnt_crab_meat" - -[.fat_snail] -level = 22 -xp = 950 -chance_fire = [73, 401] -chance_range = [73, 401] -chance_cook_o_matic = [83, 421] -chance_gauntlet = [73, 401] -cooked = "fat_snail_meat" -cooked_message = "You cook the fat snail." -burnt = "burnt_snail" -burnt_message = "You accidentally burn the fat snail." - -[.lean_snail] -level = 17 -xp = 800 -chance_fire = [93, 443] -chance_range = [85, 427] -chance_cook_o_matic = [95, 447] -chance_gauntlet = [85, 427] -cooked = "lean_snail_meat" -cooked_message = "You cook the lean snail." -burnt = "burnt_snail" -burnt_message = "You accidentally burn the lean snail." - -[.mixed_dragon_garnished] -level = 32 -xp = 1300 -range_only = true -chance_range = [255, 254] -chance_cook_o_matic = [255, 254] -chance_gauntlet = [255, 254] -cooked = "drunk_dragon" - -[.nettle_water] -level = 20 -xp = 520 -type = "heat" -chance_range = [255, 255] -chance_cook_o_matic = [255, 255] -chance_gauntlet = [255, 255] -cooked = "nettle_tea" -cooked_message = "You boil the water and make nettle tea." - -[.pitta_dough] -level = 58 -xp = 400 -range_only = true -chance_range = [118, 491] -chance_cook_o_matic = [118, 491] -chance_gauntlet = [118, 491] -cooked = "pitta_bread" -cooked_message = "You successfully bake some pitta bread." -burnt = "burnt_pitta_bread" -burnt_message = "You accidentally burn the pitta dough." - -[.raw_admiral_pie] -level = 70 -xp = 2100 -type = "bake" -range_only = true -chance_range = [15, 269] -chance_cook_o_matic = [15, 269] -chance_gauntlet = [15, 269] -cooked = "admiral_pie" -cooked_message = "You successfully bake a tasty fish pie." -burnt = "burnt_pie" -burnt_message = "You accidentally burn the admiral pie." - -[.raw_anchovies] -level = 1 -xp = 300 -chance_fire = [128, 511] -chance_range = [128, 511] -chance_cook_o_matic = [138, 531] -chance_gauntlet = [128, 511] -cooked = "anchovies" -cooked_message = "You successfully cook some anchovies." -burnt = "burnt_anchovies" -burnt_message = "You accidentally burn the anchovies." - -[.raw_bass] -level = 43 -xp = 1300 -chance_fire = [33, 311] -chance_range = [33, 311] -chance_cook_o_matic = [33, 311] -chance_gauntlet = [33, 311] -cooked = "bass" -cooked_message = "You cook a bass." -burnt = "burnt_tuna" -burnt_message = "You accidentally burn the bass." - -[.raw_bear_meat] -level = 1 -xp = 300 -chance_fire = [128, 511] -chance_range = [128, 511] -chance_cook_o_matic = [138, 531] -chance_gauntlet = [128, 511] -cooked = "cooked_meat" -cooked_message = "You cook a piece of meat." -burnt = "burnt_meat" -burnt_message = "You accidentally burn the bear meat." - -[.raw_beast_meat] -level = 21 -xp = 620 -type = "roast" -chance_fire = [180, 254] -chance_range = [180, 254] -chance_cook_o_matic = [180, 254] -chance_gauntlet = [180, 254] -cooked = "roast_beast_meat" -burnt = "burnt_beast_meat" - -[.raw_beef] -level = 1 -xp = 300 -chance_fire = [128, 511] -chance_range = [128, 511] -chance_cook_o_matic = [138, 531] -chance_gauntlet = [128, 511] -cooked = "cooked_meat" -cooked_message = "You cook a piece of meat." -burnt = "burnt_meat" -burnt_message = "You accidentally burn the beef." - -[.raw_bird_meat] -level = 11 -xp = 625 -type = "roast" -chance_fire = [155, 254] -chance_range = [155, 254] -chance_cook_o_matic = [155, 254] -chance_gauntlet = [155, 254] -cooked = "roast_bird_meat" -burnt = "burnt_bird_meat" - -[.raw_cave_eel] -level = 38 -xp = 1150 -chance_fire = [38, 331] -chance_range = [38, 331] -chance_cook_o_matic = [38, 331] -chance_gauntlet = [38, 331] -cooked = "cave_eel" -cooked_message = "You successfully cook a slimy cave eel." -burnt = "burnt_cave_eel" -burnt_message = "You accidentally burn the cave eel." - -[.raw_cavefish] -level = 88 -xp = 2140 -chance_fire = [4, 229] -chance_range = [4, 229] -chance_cook_o_matic = [4, 229] -chance_gauntlet = [12, 268] -cooked = "cavefish" -burnt = "burnt_cavefish" - -[.raw_chicken] -level = 1 -xp = 300 -chance_fire = [128, 511] -chance_range = [128, 511] -chance_cook_o_matic = [138, 531] -chance_gauntlet = [128, 511] -cooked_message = "You cook some chicken." -burnt = "burnt_chicken" -burnt_message = "You accidentally burn the chicken." - -[.raw_cod] -level = 18 -xp = 750 -chance_fire = [83, 421] -chance_range = [88, 431] -chance_cook_o_matic = [93, 441] -chance_gauntlet = [88, 431] -cooked = "cod" -cooked_message = "You successfully cook a cod." -burnt = "burnt_trout" -burnt_message = "You accidentally burn the cod." - -[.raw_crayfish] -level = 1 -xp = 300 -chance_fire = [128, 511] -chance_range = [128, 511] -chance_cook_o_matic = [128, 511] -chance_gauntlet = [128, 511] -cooked = "crayfish" -cooked_message = "You successfully cook some crayfish." -burnt = "burnt_crayfish" -burnt_message = "You accidentally burn the crayfish." - -[.raw_fish_pie] -level = 47 -xp = 1640 -type = "bake" -range_only = true -chance_range = [38, 331] -chance_cook_o_matic = [38, 331] -chance_gauntlet = [38, 331] -cooked = "fish_pie" -cooked_message = "You successfully bake a tasty fish pie." -burnt = "burnt_pie" -burnt_message = "You accidentally burn the fish pie." - -[.raw_fishcake] -level = 31 -xp = 1000 -chance_fire = [57, 376] -chance_range = [57, 376] -chance_cook_o_matic = [57, 376] -chance_gauntlet = [57, 376] -cooked_message = "You manage to cook a fishcake." -burnt = "burnt_fishcake" -burnt_message = "You accidentally burn the uncooked cake." - -[.raw_garden_pie] -level = 34 -xp = 1380 -type = "bake" -range_only = true -chance_range = [48, 351] -chance_cook_o_matic = [48, 351] -chance_gauntlet = [48, 351] -cooked = "garden_pie" -cooked_message = "You successfully bake a tasty garden pie." -burnt = "burnt_pie" -burnt_message = "You accidentally burn the garden pie." - -[.raw_guide_cake] -level = 40 -xp = 600 -type = "bake" -range_only = true -chance_range = [255, 254] -chance_cook_o_matic = [255, 254] -chance_gauntlet = [255, 254] -cooked = "cake_of_guidance" -cooked_message = "You successfully bake a Cake of Guidance." -leftover = "cake_tin" - -[.raw_herring] -level = 5 -xp = 500 -chance_fire = [108, 471] -chance_range = [108, 471] -chance_cook_o_matic = [118, 491] -chance_gauntlet = [108, 471] -cooked = "herring" -cooked_message = "You successfully cook a herring." -burnt = "burnt_herring" -burnt_message = "You accidentally burn the herring." - -[.raw_jubbly] -level = 41 -xp = 1600 -ticks = 6 -chance_fire = [50, 259] -chance_range = [50, 259] -chance_cook_o_matic = [50, 259] -chance_gauntlet = [50, 259] -cooked = "cooked_jubbly" -burnt = "burnt_jubbly" - -[.raw_karambwan] -level = 30 -xp = 1900 -chance_fire = [70, 254] -chance_range = [70, 254] -chance_cook_o_matic = [70, 254] -chance_gauntlet = [70, 254] -cooked_message = "You cook the karambwan. It looks delicious." -cooked = "cooked_karambwan" -burnt_message = "You accidentally burn the karambwan." -burnt = "burnt_karambwan" - -[.raw_lava_eel] -level = 53 -xp = 300 -chance_fire = [0, -1] -chance_range = [0, -1] -chance_cook_o_matic = [0, -1] -chance_gauntlet = [0, -1] -cooked = "lava_eel" - -[.raw_lobster] -level = 40 -xp = 1200 -type = "roast" -chance_fire = [38, 331] -chance_range = [38, 331] -chance_cook_o_matic = [38, 331] -chance_gauntlet = [55, 367] -cooked = "lobster" -cooked_message = "You roast a lobster." -burnt = "burnt_lobster" -burnt_message = "You accidentally burn the lobster." - -[.raw_mackerel] -level = 10 -xp = 600 -chance_fire = [98, 451] -chance_range = [98, 451] -chance_cook_o_matic = [108, 471] -chance_gauntlet = [98, 451] -cooked = "mackerel" -cooked_message = "You successfully cook a mackerel." -burnt = "burnt_herring" -burnt_message = "You accidentally burn the mackerel." - -[.raw_manta_ray] -level = 91 -xp = 2163 -chance_fire = [1, 201] -chance_range = [1, 221] -chance_cook_o_matic = [1, 221] -chance_gauntlet = [1, 221] -cooked = "manta_ray" -cooked_message = "You successfully cook a manta ray." -burnt = "burnt_manta_ray" -burnt_message = "You accidentally burn the manta ray." - -[.raw_monkfish] -level = 62 -xp = 1500 -chance_fire = [11, 274] -chance_range = [13, 279] -chance_cook_o_matic = [13, 279] -chance_gauntlet = [24, 289] -cooked = "monkfish" -cooked_message = "You successfully cook a monkfish." -burnt = "burnt_monkfish" -burnt_message = "You accidentally burn the monkfish." - -[.raw_mud_pie] -level = 29 -xp = 1280 -type = "bake" -range_only = true -chance_range = [58, 371] -chance_cook_o_matic = [58, 371] -chance_gauntlet = [58, 371] -cooked = "mud_pie" -cooked_message = "You successfully bake a mucky mud pie." -burnt = "burnt_pie" -burnt_message = "You accidentally burn the mud pie." - -[.raw_pawya_meat] -level = 1 -xp = 300 -chance_fire = [128, 511] -chance_range = [128, 511] -chance_cook_o_matic = [138, 531] -chance_gauntlet = [128, 511] -cooked = "cooked_meat" -burnt = "burnt_meat" - -[.raw_pike] -level = 20 -xp = 800 -chance_fire = [78, 411] -chance_range = [78, 411] -chance_cook_o_matic = [88, 431] -chance_gauntlet = [78, 411] -cooked = "pike" -cooked_message = "You successfully cook a pike." -burnt = "burnt_trout" -burnt_message = "You accidentally burn the pike." - -[.raw_potato] -level = 7 -xp = 150 -range_only = true -chance_range = [108, 471] -chance_cook_o_matic = [108, 471] -chance_gauntlet = [108, 471] -cooked = "baked_potato" -cooked_message = "You successfully bake a potato." -burnt = "burnt_potato" -burnt_message = "You accidentally burn the potato." - -[.raw_rabbit] -level = 1 -xp = 300 -chance_fire = [128, 511] -chance_range = [128, 511] -chance_cook_o_matic = [128, 511] -chance_gauntlet = [128, 511] -cooked = "rabbit" -cooked_message = "You cook the rabbit." -burnt = "burnt_rabbit" -burnt_message = "You accidentally burn the rabbit." - -[.raw_rainbow_fish] -level = 35 -xp = 1100 -chance_fire = [56, 369] -chance_range = [56, 369] -chance_cook_o_matic = [56, 369] -chance_gauntlet = [56, 369] -cooked = "rainbow_fish" -cooked_message = "You manage to cook a rainbow fish." -burnt = "burnt_rainbow_fish" -burnt_message = "You accidentally burn the rainbow fish." - -[.raw_rat_meat] -level = 1 -xp = 300 -chance_fire = [128, 511] -chance_range = [128, 511] -chance_cook_o_matic = [138, 531] -chance_gauntlet = [128, 511] -cooked = "cooked_meat" -cooked_message = "You cook a piece of meat." -burnt = "burnt_meat" -burnt_message = "You accidentally burn the rat meat." - -[.raw_rocktail] -level = 92 -xp = 2250 -chance_fire = [1, 179] -chance_range = [1, 179] -chance_cook_o_matic = [1, 179] -chance_gauntlet = [1, 268] -cooked = "rocktail" -cooked_message = "You successfully cook a rocktail." -burnt = "burnt_rocktail" -burnt_message = "You accidentally burn the rocktail." - -[.raw_salmon] -level = 25 -xp = 900 -chance_fire = [68, 391] -chance_range = [68, 391] -chance_cook_o_matic = [78, 401] -chance_gauntlet = [68, 391] -cooked = "salmon" -cooked_message = "You successfully cook a salmon." -burnt = "burnt_trout" -burnt_message = "You accidentally burn the salmon." - -[.raw_sardine] -level = 1 -xp = 400 -chance_fire = [118, 491] -chance_range = [118, 491] -chance_cook_o_matic = [128, 511] -chance_gauntlet = [118, 491] -cooked = "sardine" -cooked_message = "You successfully cook a sardine." -burnt = "burnt_sardine" -burnt_message = "You accidentally burn the sardine." - -[.raw_sea_turtle] -level = 82 -xp = 2113 -chance_fire = [1, 201] -chance_range = [1, 221] -chance_cook_o_matic = [1, 221] -chance_gauntlet = [1, 221] -cooked = "sea_turtle" -cooked_message = "You successfully cook a sea turtle." -burnt = "burnt_sea_turtle" -burnt_message = "You accidentally burn the sea turtle." - -[.raw_shark] -level = 80 -xp = 2100 -chance_fire = [1, 201] -chance_range = [1, 231] -chance_cook_o_matic = [1, 231] -chance_gauntlet = [15, 269] -cooked = "shark" -cooked_message = "You successfully cook a shark." -burnt = "burnt_shark" -burnt_message = "You accidentally burn the shark." - -[.raw_shrimps] -level = 1 -xp = 300 -chance_fire = [128, 511] -chance_range = [128, 511] -chance_cook_o_matic = [138, 531] -chance_gauntlet = [128, 511] -cooked = "shrimps" -cooked_message = "You successfully cook some shrimps." -burnt = "burnt_shrimp" -burnt_message = "You accidentally burn the shrimps." - -[.raw_summer_pie] -level = 95 -xp = 2600 -type = "bake" -range_only = true -chance_range = [1, 211] -chance_cook_o_matic = [1, 211] -chance_gauntlet = [1, 211] -cooked = "summer_pie" -cooked_message = "You successfully bake a tasty summer pie." -burnt = "burnt_pie" -burnt_message = "You accidentally burn the summer pie." - -[.raw_swordfish] -level = 45 -xp = 1400 -chance_fire = [18, 291] -chance_range = [30, 309] -chance_cook_o_matic = [30, 309] -chance_gauntlet = [30, 309] -cooked = "swordfish" -cooked_message = "You successfully cook a swordfish." -burnt = "burnt_swordfish" -burnt_message = "You accidentally burn the swordfish." - -[.raw_trout] -level = 15 -xp = 700 -chance_fire = [88, 431] -chance_range = [88, 431] -chance_cook_o_matic = [98, 451] -chance_gauntlet = [88, 431] -cooked = "trout" -cooked_message = "You successfully cook a trout." -burnt = "burnt_trout" -burnt_message = "You accidentally burn the trout." - -[.raw_tuna] -level = 30 -xp = 1000 -chance_fire = [58, 371] -chance_range = [58, 371] -chance_cook_o_matic = [58, 371] -chance_gauntlet = [58, 371] -cooked = "tuna" -cooked_message = "You manage to cook a tuna." -burnt = "burnt_tuna" -burnt_message = "You accidentally burn the tuna." - -[.raw_ugthanki_meat] -level = 1 -xp = 400 -chance_fire = [40, 251] -chance_range = [30, 252] -chance_cook_o_matic = [30, 252] -chance_gauntlet = [30, 252] -cooked = "ugthanki_meat" -burnt = "burnt_meat" - -[.raw_wild_pie] -level = 85 -xp = 2400 -type = "bake" -range_only = true -chance_range = [1, 221] -chance_cook_o_matic = [1, 221] -chance_gauntlet = [1, 221] -cooked = "wild_pie" -cooked_message = "You successfully bake a tasty wild pie." -burnt = "burnt_pie" -burnt_message = "You accidentally burn the wild pie." - -[.raw_yak_meat] -level = 1 -xp = 400 -chance_fire = [128, 511] -chance_range = [128, 511] -chance_cook_o_matic = [138, 531] -chance_gauntlet = [128, 511] -cooked = "cooked_meat" -burnt = "burnt_meat" - -[.sinew] -level = 1 -xp = 30 -type = "heat" -range_only = true -chance_range = [255, 254] -chance_cook_o_matic = [255, 254] -chance_gauntlet = [255, 254] -cooked = "sinew" -cooked_message = "You dry a piece of beef and extract the sinew." - -[.skewered_chompy] -level = 30 -xp = 1000 -type = "roast" -chance_fire = [200, 254] -chance_range = [200, 254] -chance_cook_o_matic = [200, 254] -chance_gauntlet = [200, 254] -cooked = "cooked_chompy" -burnt = "burnt_chompy" - -[.skewered_rabbit] -level = 16 -xp = 720 -type = "roast" -ticks = 3 -chance_fire = [160, 254] -chance_range = [160, 254] -chance_cook_o_matic = [160, 254] -chance_gauntlet = [160, 254] -cooked = "roast_rabbit" -cooked_message = "You roast the skewered rabbit." -burnt = "burnt_rabbit" -burnt_message = "You accidentally burn the skewered rabbit." - -[.sliced_mushrooms] -level = 46 -xp = 600 -range_only = true -chance_range = [28, 299] -chance_cook_o_matic = [28, 299] -chance_gauntlet = [28, 299] -cooked = "fried_mushrooms" -cooked_message = "You successfully fry the mushroom." -burnt = "burnt_mushroom" -burnt_message = "You accidentally burn the mushroom." - -[.slimy_eel] -level = 28 -xp = 950 -chance_fire = [63, 381] -chance_range = [63, 381] -chance_cook_o_matic = [63, 381] -chance_gauntlet = [63, 381] -cooked = "cooked_slimy_eel" -cooked_message = "You cook an eel." -burnt = "burnt_eel" -burnt_message = "You accidentally burn the slimy eel." - -[.spider_on_shaft_raw] -level = 16 -xp = 800 -type = "roast" -chance_fire = [91, 437] -chance_range = [91, 437] -chance_cook_o_matic = [91, 437] -chance_gauntlet = [91, 437] -cooked = "spider_on_shaft" -burnt = "spider_on_shaft_burnt" - -[.spider_on_stick_raw] -level = 16 -xp = 800 -type = "roast" -chance_fire = [91, 437] -chance_range = [91, 437] -chance_cook_o_matic = [91, 437] -chance_gauntlet = [91, 437] -cooked = "spider_on_stick" -burnt = "burnt_spider" - -[.sweetcorn] -level = 28 -xp = 1040 -range_only = true -chance_range = [65, 381] -chance_cook_o_matic = [65, 381] -chance_gauntlet = [65, 381] -cooked = "cooked_sweetcorn" -cooked_message = "You cook some sweetcorn." -burnt = "burnt_sweetcorn" -burnt_message = "You accidentally burn the sweetcorn." - -[.thin_snail] -level = 12 -xp = 700 -chance_fire = [93, 443] -chance_range = [93, 443] -chance_cook_o_matic = [103, 463] -chance_gauntlet = [93, 443] -cooked = "thin_snail_meat" -cooked_message = "You cook the thin snail." -burnt = "burnt_snail" -burnt_message = "You accidentally burn the thin snail." - -[.uncooked_apple_pie] -level = 30 -xp = 1300 -range_only = true -chance_range = [58, 371] -chance_cook_o_matic = [58, 371] -chance_gauntlet = [58, 371] -cooked = "apple_pie" -cooked_message = "You successfully bake a traditional apple pie." -burnt = "burnt_pie" -burnt_message = "You accidentally burn the uncooked apple pie." - -[.uncooked_berry_pie] -level = 10 -xp = 780 -range_only = true -chance_range = [98, 451] -chance_cook_o_matic = [108, 461] -chance_gauntlet = [98, 451] -cooked = "redberry_pie" -cooked_message = "You successfully bake a delicious redberry pie." -burnt = "burnt_pie" -burnt_message = "You accidentally burn the uncooked berry pie." - -[.uncooked_cake] -level = 40 -xp = 1800 -range_only = true -chance_range = [38, 331] -chance_cook_o_matic = [38, 331] -chance_gauntlet = [38, 331] -cooked = "cake" -cooked_message = "You successfully bake a cake." -leftover = "cake_tin" -burnt = "burnt_cake" -burnt_message = "You accidentally burn the uncooked cake." - -[.uncooked_curry] -level = 60 -xp = 2800 -chance_fire = [38, 331] -chance_range = [38, 331] -chance_cook_o_matic = [38, 331] -chance_gauntlet = [38, 331] -cooked = "curry" -cooked_message = "You cook some curry." -burnt = "burnt_curry" -burnt_message = "You accidentally burn the curry." - -[.uncooked_egg] -level = 13 -xp = 500 -start_ticks = 0 -range_only = true -chance_range = [90, 437] -chance_cook_o_matic = [90, 437] -chance_gauntlet = [90, 437] -cooked = "scrambled_egg" -cooked_message = "You successfully scramble the egg." -burnt = "burnt_egg" -burnt_message = "You accidentally burn the egg." - -[.uncooked_meat_pie] -level = 20 -xp = 1100 -range_only = true -chance_range = [78, 411] -chance_cook_o_matic = [88, 431] -chance_gauntlet = [78, 411] -cooked = "meat_pie" -cooked_message = "You successfully bake a tasty meat pie." -burnt = "burnt_pie" -burnt_message = "You accidentally burn the uncooked meat pie." - -[.uncooked_pizza] -level = 35 -xp = 1430 -range_only = true -chance_range = [48, 351] -chance_cook_o_matic = [48, 351] -chance_gauntlet = [48, 351] -cooked = "plain_pizza" -cooked_message = "You successfully bake a pizza." -burnt = "burnt_pizza" -burnt_message = "You accidentally burn the uncooked pizza." - -[.uncooked_stew] -level = 25 -xp = 1170 -chance_fire = [68, 391] -chance_range = [68, 391] -chance_cook_o_matic = [78, 411] -chance_gauntlet = [68, 391] -cooked = "stew" -cooked_message = "You cook some stew." -burnt = "burnt_stew" -burnt_message = "You accidentally burn the stew." diff --git a/game/src/main/kotlin/content/quest/member/ghosts_ahoy/AkHaranu.kt b/game/src/main/kotlin/content/quest/member/ghosts_ahoy/AkHaranu.kt new file mode 100644 index 0000000000..562b18bd22 --- /dev/null +++ b/game/src/main/kotlin/content/quest/member/ghosts_ahoy/AkHaranu.kt @@ -0,0 +1,110 @@ +package content.quest.member.ghosts_ahoy + +import content.entity.npc.shop.openShop +import content.entity.player.bank.ownsItem +import content.entity.player.dialogue.Neutral +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 world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.entity.character.player.Player +import world.gregs.voidps.engine.entity.character.player.male +import world.gregs.voidps.engine.entity.character.player.name +import world.gregs.voidps.engine.inv.add +import world.gregs.voidps.engine.inv.inventory +import world.gregs.voidps.engine.inv.remove + +class AkHaranu : Script { + init { + npcOperate("Talk-To", "ahoy_akharanu") { + val stage = get("ahoy_subquest_bow", 0) + when { + stage == 8 -> postExchange() + stage >= 1 -> exchangePhase() + else -> introPhase() + } + } + + npcOperate("Trade", "ahoy_akharanu") { + openShop("ak_haranus_exotic_shop") + } + } + + private suspend fun Player.introPhase() { + player("It's nice to see a human face around here.") + npc("My name Ak-Haranu. I am trader, come from many far across sea in east.") + player( + "You come from the lands of the East? Do you have anything that can help me " + + "translate a book that is scribed in your language?", + ) + npc( + "Ak-Haranu may help you. A translation manual I have, much good for reading " + + "Eastern language.", + ) + player("How much do you want for it?") + npc( + "Ak-Haranu not want money for this book, as is such small thing. But there may " + + "be something you could do for me. I am big admirer of Robin, Master Bowman. " + + "He staying in village inn.", + ) + player("What would you like me to do?") + npc( + "Please get Master Bowman sign an oak longbow for me, so Ak-Haranu can show " + + "family and friends when returning home and become much admired. Then I give " + + "you book in exchange.", + ) + choice { + option("Okay, wait here - I'll get you your bow.") { + player("Okay, wait here - I'll get you your bow.") + set("ahoy_subquest_bow", 1) + } + option("Sorry, I have too much to do at the moment.") + } + } + + private suspend fun Player.exchangePhase() { + when { + inventory.contains("signed_oak_bow") -> { + player("I have your signed longbow for you.") + npc("Ah, can it be true? You have obtained bow from Master Bowman?") + player("He was more than happy to oblige (cough). Here you are.") + inventory.remove("signed_oak_bow") + inventory.add("translation_manual") + set("ahoy_subquest_bow", 8) + item( + item = "signed_oak_bow", + text = "Ak-Haranu gives you a translation manual in return for the signed oak longbow.", + ) + npc("May honour be bestowed upon you and your family!") + } + inventory.contains("oak_longbow") -> { + npc("How is ${if (male) "Master" else "Mistress"} $name getting on with longbow?") + player("I'll let you know.") + } + else -> { + player("Have you got an oak longbow that I can get Robin to sign for you?") + npc( + "No, Ak-Haranu afraid that no longbow in supply at moment. You must make " + + "one or buy one.", + ) + } + } + } + + private suspend fun Player.postExchange() { + if (ownsItem("translation_manual") || get("ahoy_given_manual", false)) { + player( + "Thank you for the translation manual, Ak-Haranu - it may save many souls before long.", + ) + npc("And Ak-Haranu thanks you for kind gift of longbow.") + } else { + player("I'm sorry, I seem to have lost the translation manual you gave me.") + npc( + "Ah, no worry, my friend. For the gift of longbow Ak-Haranu would give a thousand books.", + ) + inventory.add("translation_manual") + item(item = "translation_manual", text = "Ak-Haranu gives you another translation manual.") + } + } +} diff --git a/game/src/main/kotlin/content/quest/member/ghosts_ahoy/GhostCaptain.kt b/game/src/main/kotlin/content/quest/member/ghosts_ahoy/GhostCaptain.kt new file mode 100644 index 0000000000..8a037cb5a3 --- /dev/null +++ b/game/src/main/kotlin/content/quest/member/ghosts_ahoy/GhostCaptain.kt @@ -0,0 +1,117 @@ +package content.quest.member.ghosts_ahoy + +import content.entity.player.dialogue.Bored +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.choice +import content.entity.player.dialogue.type.npc +import content.entity.player.dialogue.type.player +import world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.client.ui.open +import world.gregs.voidps.engine.entity.character.move.tele +import world.gregs.voidps.engine.entity.character.player.Player +import world.gregs.voidps.engine.inv.equipment +import world.gregs.voidps.engine.inv.inventory +import world.gregs.voidps.engine.inv.remove +import world.gregs.voidps.type.Tile + +class GhostCaptain : Script { + init { + npcOperate("Talk-To", "ahoy_ghost_captain_*") { (target) -> + if (!checkGhostspeak()) return@npcOperate + if (equipment.contains("bedsheet") || equipment.contains("bedsheet_ectoplasm")) { + if (equipment.contains("bedsheet_ectoplasm")) { + npc("Is that your bedsheet covered in filthy slime?") + player("I can say in all honesty that it is not.") + npc("Well, whoever it belongs to, it's not coming on this ship!") + } else { + npc( + "Would you like to visit Dragontooth Island? If so, I recommend you " + + "take off that sheet first. The winds are very strong out at sea.", + ) + } + return@npcOperate + } + if (target.id == "ahoy_ghost_captain_1") { + npc( + "Would you like to visit Dragontooth Island? It is a most pleasant island " + + "in the sea between Morytania and the lands of the east. It'll cost " + + "you 25 ectotokens for the return trip.", + ) + askToSail(toDragontooth = true) + } else { + offerReturn() + } + } + + npcOperate("Travel", "ahoy_ghost_captain_1") { + if (!checkGhostspeak()) return@npcOperate + sailIfAble(toDragontooth = true) + } + + npcOperate("Travel", "ahoy_ghost_captain_2") { + if (!checkGhostspeak()) return@npcOperate + sail(toDragontooth = false) + } + } + + private suspend fun Player.askToSail(toDragontooth: Boolean) { + if (!inventory.contains("ecto_token", 25) && toDragontooth) { + player("Not now, thanks.") + return + } + choice { + option("Okay, here's 25 ectotokens. Let's go.") { + sailIfAble(toDragontooth) + } + option("Not now, thanks.") + } + } + + private suspend fun Player.offerReturn() { + choice { + option("Can you take me back to Phasmatys, now?") { + npc("Yes, climb in.") + sail(toDragontooth = false) + } + option("There isn't much on this island, is there?") { + npc("Beautiful isn't it?") + player("But it cost me a lot of ectotokens to get here!") + npc("Worth every token, if you ask me.") + offerReturn() + } + option("Actually, I don't want anything.") + } + } + + private suspend fun Player.sailIfAble(toDragontooth: Boolean) { + if (toDragontooth && !inventory.contains("ecto_token", 25)) { + npc("A return trip to Dragontooth Island costs 25 ectotokens.") + return + } + sail(toDragontooth) + } + + private suspend fun Player.sail(toDragontooth: Boolean) { + if (toDragontooth) { + inventory.remove("ecto_token", 25) + } + val destination = if (toDragontooth) Tile(3792, 3559, 0) else Tile(3702, 3487, 0) + open("fade_out") + interfaces.sendText( + "fade_out", + "text", + "After a long boat trip you ${if (toDragontooth) { + "arrive at Dragontooth Island" + } else { + "return to Port Phasmatys" + }}.", + ) + delay(5) + tele(destination) + open("fade_in") + delay(3) + } +} diff --git a/game/src/main/kotlin/content/quest/member/ghosts_ahoy/GhostInnKeeper.kt b/game/src/main/kotlin/content/quest/member/ghosts_ahoy/GhostInnKeeper.kt new file mode 100644 index 0000000000..7ae1eaffbd --- /dev/null +++ b/game/src/main/kotlin/content/quest/member/ghosts_ahoy/GhostInnKeeper.kt @@ -0,0 +1,71 @@ +package content.quest.member.ghosts_ahoy + +import content.entity.player.bank.ownsItem +import content.entity.player.dialogue.Happy +import content.entity.player.dialogue.Neutral +import content.entity.player.dialogue.Quiz +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.inv.item.addOrDrop +import content.quest.quest +import world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.entity.character.player.Player + +class GhostInnKeeper : Script { + init { + npcOperate("Talk-To", "ahoy_ghost_innkeeper") { + if (!checkGhostspeak()) return@npcOperate + player("Hello there!") + npc("Greetings, traveller. Welcome to 'The Green Ghost' Tavern. What can I do you for?") + menu() + } + } + + private suspend fun Player.menu() { + choice { + option("Can I buy a beer?") { + npc( + "Sorry, but our pumps dried up over half a century ago. We of this village " + + "do not have much of a thirst these days.", + ) + } + option("Can I hear some gossip?") { + npc("I suppose, as long as you keep it to yourself...") + npc( + "You see Gravingas out there in the marketplace? He speaks for the silent " + + "majority of Port Phasmatys, for those of us who would prefer to pass " + + "over into the next world.", + ) + if (ghosts_ahoy == 0) { + npc( + "But old Gravingas is far too obvious in his methods. Now Velorina, " + + "she's a ghost of a different colour altogether. If you feel like " + + "helping our cause at all, go speak to Velorina.", + ) + } + } + option("Do you have any jobs I can do?") { + if (ownsItem("bedsheet")) { + npc("Well, you could take that bedsheet through to Robin like I asked.") + return@option + } + npc( + "Yes, actually, I do. We have a very famous Master Bowman named Robin staying " + + "with us at the moment. Could you take him some clean bed linen for me?", + ) + choice { + option("Yes, I'd be delighted.") { + addOrDrop("bedsheet") + npc( + "Oh, thank you. Be careful with that Robin, though - he's far too " + + "full of himself, that one.", + ) + } + option("No, I didn't mean a job like that.") + } + } + option("Nothing, thanks.") + } + } +} diff --git a/game/src/main/kotlin/content/quest/member/ghosts_ahoy/GhostVillager.kt b/game/src/main/kotlin/content/quest/member/ghosts_ahoy/GhostVillager.kt new file mode 100644 index 0000000000..9ec80d953b --- /dev/null +++ b/game/src/main/kotlin/content/quest/member/ghosts_ahoy/GhostVillager.kt @@ -0,0 +1,131 @@ +package content.quest.member.ghosts_ahoy + +import content.entity.player.dialogue.Angry +import content.entity.player.dialogue.Bored +import content.entity.player.dialogue.Neutral +import content.entity.player.dialogue.Quiz +import content.entity.player.dialogue.Shock +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 world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.entity.character.player.Player +import world.gregs.voidps.engine.inv.equipment +import world.gregs.voidps.engine.inv.inventory +import world.gregs.voidps.engine.inv.remove + +class GhostVillager : Script { + init { + npcOperate("Talk-To", "ahoy_ghost_villager") { (target) -> + if (!checkGhostspeak()) return@npcOperate + val petition = get("ahoy_signaturecounter", 0) + val hasForm = inventory.contains("petition_form") + val isCollecting = petition in 1..11 && hasForm + + if (!isCollecting) { + randomChat() + return@npcOperate + } + + if (petition == 11) { + player("Thank you for your kind support.") + return@npcOperate + } + + player("Would you sign this petition form, please?") + when { + equipment.contains("bedsheet_ectoplasm") -> bedsheetSignature(target) + equipment.contains("bedsheet") -> { + npc( + "Why are you wearing that bedsheet? If you're trying to pretend to be " + + "one of us, you're not fooling anybody - you're not even green!!", + ) + } + else -> npc( + "I'm sorry, but it's hard to believe that a mortal could be interested in " + + "helping us.", + ) + } + } + } + + private suspend fun Player.randomChat() { + when ((0..3).random()) { + 0 -> npc("This cold wind blows right through you, doesn't it?") + 1 -> npc("Worship the Ectofuntus all you want, but don't bother us, human.") + 2 -> npc("Why did we have to listen to that maniacal priest?") + 3 -> npc("We do not talk to the warm-bloods.") + } + } + + private suspend fun Player.bedsheetSignature(target: world.gregs.voidps.engine.entity.character.npc.NPC) { + if (get("ahoy_last_villager_index", -1) == target.index) { + npc("You only just asked me the same thing! Leave me alone - I've had my say!") + return + } + set("ahoy_last_villager_index", target.index) + when ((0..2).random()) { + 0 -> refuseSignature() + 1 -> happilySign() + 2 -> bribeSignature() + } + } + + private suspend fun Player.refuseSignature() { + when ((0..4).random()) { + 0 -> npc("I don't have time for this nonsense.") + 1 -> npc("How dare you accost me in the street?") + 2 -> npc("Get lost.") + 3 -> npc("My answer is no.") + 4 -> npc("I will have you know that I am a fervent supporter of Necrovarus.") + } + } + + private suspend fun Player.happilySign() { + when ((0..3).random()) { + 0 -> npc("Most certainly, I will.") + 1 -> npc("Yes, of course.") + 2 -> npc("Yes! It's about time somebody did something about Necrovarus.") + 3 -> npc("I'll do anything that annoys Necrovarus.") + } + recordSignature(0) + } + + private suspend fun Player.bribeSignature() { + when ((0..2).random()) { + 0 -> npc("I will if you make it worth my while...") + 1 -> npc("You scratch my back and I'll scratch yours...") + else -> npc("It'll cost you...") + } + player("How much?") + val cost = (1..5).random() + npc("Oh, it'll cost you $cost ecto-tokens.") + if (!inventory.contains("ecto_token", cost)) { + player("I don't have that many on me.") + npc("No tokens, no signature.") + return + } + choice { + option("Okay, if you insist.") { + inventory.remove("ecto_token", cost) + recordSignature(cost) + } + option("There's no way I'm giving in to corruption.") { + npc("Suit yourself.") + } + } + } + + private suspend fun Player.recordSignature(@Suppress("UNUSED_PARAMETER") tokensPaid: Int) { + val newCount = get("ahoy_signaturecounter", 0) + 1 + set("ahoy_signaturecounter", newCount) + val total = newCount - 1 + val text = when (total) { + 10 -> "You have succeeded in obtaining 10 signatures on the petition form!" + 1 -> "The ghost signs your petition.
You have obtained 1 signature so far." + else -> "The ghost signs your petition.
You have obtained $total signatures so far." + } + item(item = "petition_form", text = text) + } +} diff --git a/game/src/main/kotlin/content/quest/member/ghosts_ahoy/GhostsAhoy.kt b/game/src/main/kotlin/content/quest/member/ghosts_ahoy/GhostsAhoy.kt new file mode 100644 index 0000000000..8ba284b39d --- /dev/null +++ b/game/src/main/kotlin/content/quest/member/ghosts_ahoy/GhostsAhoy.kt @@ -0,0 +1,736 @@ +package content.quest.member.ghosts_ahoy + +import content.entity.combat.hit.directHit +import content.entity.combat.killer +import content.entity.effect.clearTransform +import content.entity.effect.transform +import content.entity.obj.door.Door +import content.entity.obj.door.walkThroughDoor +import content.entity.player.bank.ownsItem +import content.entity.player.dialogue.Neutral +import content.entity.player.dialogue.Scared +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.entity.player.dialogue.type.statement +import content.entity.player.inv.item.addOrDrop +import content.quest.questComplete +import content.quest.questCompleted +import content.quest.questJournal +import content.quest.refreshQuestJournal +import world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.client.hint +import world.gregs.voidps.engine.client.instruction.handle.interactPlayer +import world.gregs.voidps.engine.client.message +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 +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.player.Teleport +import world.gregs.voidps.engine.entity.character.player.equip.equipped +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.entity.character.player.skill.level.Level.hasMax +import world.gregs.voidps.engine.entity.character.player.wearingGhostspeak +import world.gregs.voidps.engine.entity.character.sound +import world.gregs.voidps.engine.entity.obj.replace +import world.gregs.voidps.engine.event.AuditLog +import world.gregs.voidps.engine.inv.add +import world.gregs.voidps.engine.inv.equipment +import world.gregs.voidps.engine.inv.inventory +import world.gregs.voidps.engine.inv.remove +import world.gregs.voidps.engine.inv.replace +import world.gregs.voidps.network.login.protocol.visual.update.player.EquipSlot +import world.gregs.voidps.type.Tile + +class GhostsAhoy : Script { + + init { + + questJournalOpen("ghosts_ahoy") { + val progress = ghosts_ahoy + val lines = mutableListOf() + + if (progress == 0) { + val ghost = questCompleted("the_restless_ghost") + val priest = questCompleted("priest_in_peril") + val agility = hasMax(Skill.Agility, 25, false) + val cooking = hasMax(Skill.Cooking, 20, false) + lines += "To start this quest I need to speak to Velorina," + lines += "a ghost of Port Phasmatys." + lines += "I must have at least:" + lines += if (agility) "level 25 agility" else "level 25 agility" + lines += if (cooking) "level 20 cooking." else "level 20 cooking." + lines += "I must also be able to defeat a level 32 monster." + lines += "I must also have completed the following quests:" + lines += if (priest) "Priest in Peril" else "Priest in Peril" + lines += if (ghost) "Restless Ghost" else "Restless Ghost" + questJournal("Ghosts Ahoy", lines) + return@questJournalOpen + } + + lines += "I have spoken with Velorina, who has told me the sad" + lines += "history of the ghosts of Port Phasmatys. She has asked" + lines += "me to plead with Necrovarus in the Phasmatyan Temple to" + lines += "let any ghost who so wishes pass over into the next world." + if (progress == 1) { + lines += "" + lines += "I should speak to Necrovarus in the Temple." + } + if (progress >= 2) { + lines += "I pleaded with Necrovarus, to no avail." + } + if (progress == 2) { + lines += "" + lines += "I should tell Velorina that Necrovarus will not listen to" + lines += "reason." + } + if (progress >= 3) { + lines += "Velorina was crestfallen at Necrovarus' refusal to lift his" + lines += "ban, and she told me of a woman who fled Port Phasmatys" + lines += "before the townsfolk died, and to seek her out, as she" + lines += "may know of a way around Necrovarus' stubbornness." + } + if (progress == 3) { + lines += "" + lines += "I should find the old woman that Velorina speaks of." + } + if (progress >= 4) { + lines += "I found the old woman, who told me of an enchantment she" + lines += "can perform on the Amulet of Ghostspeak, which will then" + lines += "let me command Necrovarus to do my bidding" + } + if (progress == 4) { + lines += "" + lines += "I need to bring the old woman the following items:" + val book = get("ahoy_given_book", false) || ownsItem("book_of_haricanto") + val robes = get("ahoy_given_robes", false) || ownsItem("mystical_robes") + val manual = get("ahoy_given_manual", false) || ownsItem("translation_manual") + lines += if (book) "The Book of Haricanto" else "The Book of Haricanto" + lines += if (robes) "The Robes of Necrovarus" else "The Robes of Necrovarus" + lines += if (manual) "Something to translate the Book of Haricanto" else "Something to translate the Book of Haricanto" + + val toyStage = get("ahoy_subquest_toyboat", 0) + if (toyStage == 3) { + lines += "" + lines += "The old woman gave me a toy boat to give to her son, if I" + lines += "came across him. I found him and gave him the boat." + } else if (toyStage >= 1) { + lines += "" + lines += "The old woman has given me a toy boat which was made by" + lines += "her son when he was a young boy. It is a model of the same" + lines += "pirate-ship that he sailed away in when he later ran away." + } + + val bow = get("ahoy_subquest_bow", 0) + if (bow == 8) { + lines += "" + lines += "I brought Ak-Haranu an oak longbow signed by Robin," + lines += "Master Bowman, and in exchange he gave me a translation" + lines += "manual for the Eastern language." + } else if (bow >= 1) { + lines += "" + lines += "I have agreed to bring Ak-Haranu (a trader from the East)" + lines += "an oak longbow, signed by Robin, Master Bowman. Ak-" + lines += "Haranu will give me a translation manual for the Eastern" + lines += "language in exchange." + lines += "Robin is staying at the inn in Port Phasmatys." + } + + val petition = get("ahoy_signaturecounter", 0) + if (petition == 31) { + lines += "" + lines += "Gravingas, a ghost protestor, asked me to help him with a" + lines += "petition to free the ghosts of Phasmatys. I collected 10" + lines += "signatures and presented the petition to Necrovarus, but" + lines += "he burned it to ashes in my hands." + } else if (petition >= 1) { + lines += "" + lines += "Gravingas, a ghost protestor, has asked me to help with a" + lines += "petition to free the ghosts of Phasmatys, and wants me" + lines += "to collect 10 signatures." + when (petition) { + 1 -> lines += "I haven't got any signatures yet." + 11 -> { + lines += "I have obtained all the signatures." + lines += "I need to present the petition to Necrovarus." + } + + else -> lines += "I have obtained ${petition - 1} signatures." + } + } + } + if (progress >= 5) { + lines += "I brought the old woman the Book of Haricanto, the Robes" + lines += "of Necrovarus, and a translation manual." + } + if (progress == 5) { + lines += "" + lines += "I need to bring the Amulet of Ghostspeak to the old woman" + lines += "so that she can perform the enchantment." + } + if (progress >= 6) { + lines += "The old woman used the items I brought her to perform the" + lines += "enchantment on the Amulet of Ghostspeak." + } + if (progress == 6) { + lines += "" + lines += "I need to return to the Temple of Phasmatys and use the" + lines += "enchanted Amulet of Ghostspeak to command Necrovarus." + } + if (progress >= 7) { + lines += "I have commanded Necrovarus to remove his ban." + } + if (progress == 7) { + lines += "" + lines += "I need to tell Velorina that I have successfully commanded" + lines += "Necrovarus to lift his ban." + } + if (progress >= 8) { + lines += "I have told Velorina that Necrovarus has been commanded" + lines += "to remove his ban, and to allow any ghost who so desires" + lines += "to pass over into the next plane of existence. Velorina" + lines += "gave me the Ectophial in return, which I can use to teleport" + lines += "to the Temple of Phasmatys." + lines += "" + lines += "QUEST COMPLETE!" + } + questJournal("Ghosts Ahoy", lines) + } + + playerSpawn { + if (equipped(EquipSlot.Hat).id == "bedsheet") { + transform("ahoy_ghost_disguise") + } + + if (equipped(EquipSlot.Hat).id == "bedsheet_ectoplasm") { + transform("ahoy_ghost_disguise_green") + } + } + + teleportTakeOff("*") { + if (transform.contains("ahoy_ghost_disguise")) { + message("Please remove your ghost disguise before teleporting.") + false + } else { + true + } + } + + // === Object hooks === + + objectOperate("Pick", "nettles*") { (target) -> + if (inventory.isFull()) { + message("You can't carry any more nettles.") + return@objectOperate + } + anim("human_pickupfloor") + sound("pick") + delay(2) + val gloves = equipment[EquipSlot.Hands.index].def.name.contains("gloves", ignoreCase = true) + if (gloves) { + addOrDrop("nettles") + message("You pick a handful of nettles.") + target.replace(target.id, ticks = 13) + } else { + message("You have been stung by the nettles!") + sound("stung") + directHit(20) + } + } + + objectOperate("Open", "ahoy_pirate_chest_locked") { (target) -> + if (target.tile.x == 3619 && target.tile.y == 3545 && target.tile.level == 1 && get("ahoy_subquest_toyboat", 0) == 3) { + sound("chest_open") + target.replace("ahoy_pirate_chest_unlocked", ticks = 3) + if (!ownsItem("map_scrap_1") && + !ownsItem("book_of_haricanto") && + !ownsItem("treasure_map") && + !get("ahoy_given_book", false) + ) { + addOrDrop("map_scrap_1") + item(item = "map_scrap_1", text = "You find a piece of a map inside the chest.") + } else { + message("You search the chest but find nothing.") + } + } else { + sound("locked") + message("The chest is locked.") + } + } + + objectOperate("Open", "ahoy_pirate_chest_closed") { (target) -> + anim("open_chest") + sound("chest_open") + target.replace("ahoy_pirate_chest_open", ticks = 500) + delay(2) + } + + objectOperate("Close", "ahoy_pirate_chest_open") { (target) -> + anim("close_chest") + sound("chest_close") + target.replace("ahoy_pirate_chest_closed") + delay(2) + } + + objectOperate("Search", "ahoy_pirate_chest_open") { (target) -> + val progress = ghosts_ahoy + if (target.tile.x == 3606 && target.tile.y == 3564 && target.tile.level == 0) { + if (progress != 4 || + get("ahoy_given_book", false) || + ownsItem("book_of_haricanto") || + ownsItem("treasure_map") || + ownsItem("map_scrap_3") + ) { + message("You search the chest but find nothing.") + } else { + addOrDrop("map_scrap_3") + item(item = "map_scrap_3", text = "You find a piece of a map inside the chest.") + } + return@objectOperate + } + if (target.tile.x == 3618 && target.tile.y == 3542 && target.tile.level == 0) { + val killed = get("ahoy_killed_lobster", false) + if (killed || progress != 4) { // TODO or spawned lobster + if (progress != 4 || + get("ahoy_given_book", false) || + ownsItem("book_of_haricanto") || + ownsItem("treasure_map") || + ownsItem("map_scrap_2") + ) { + message("You search the chest but find nothing.") + } else { + addOrDrop("map_scrap_2") + item(item = "map_scrap_2", text = "You find a piece of a map inside the chest.") + } + } else { + message("You are attacked by a giant lobster!!") + val lobster = NPCs.add("giant_lobster", Tile(3616, 3543, 0)) + hint(lobster) + lobster.interactPlayer(this, "Attack") + } + return@objectOperate + } + message("You search the chest but find nothing.") + } + + objectOperate("Open", "ahoy_necrovarus_coffin_closed") { (target) -> + message("The coffin creaks open...") + target.replace("ahoy_necrovarus_coffin_open", ticks = 100) + } + + objectOperate("Search", "ahoy_necrovarus_coffin_open") { + if (get("ahoy_given_robes", false) || ownsItem("mystical_robes")) { + message("Nothing interesting happens.") + return@objectOperate + } + item( + item = "mystical_robes", + text = "You take the Robes of Necrovarus from the remains of his mortal body.", + ) + addOrDrop("mystical_robes") + } + + objectOperate("Close", "ahoy_necrovarus_coffin_open") { (target) -> + message("You close the coffin.") + target.replace("ahoy_necrovarus_coffin_closed", ticks = 3) + } + + objectOperate("Open", "ahoy_harbour_door_closed") { (target) -> + if (target.tile.x == 3656 && target.tile.y == 3514 && target.tile.level == 1) { + if (get("ahoy_templedoor_unlocked", false)) { + walkThroughDoor( + target = target, + enter = tile.x < target.tile.x, + enterOffset = -1, + exitOffset = 0, + openId = "ahoy_harbour_door_opened", + openRotation = 1, + openOffset = -1, + openSound = "creakydoor_open", + ) + return@objectOperate + } + val disciple = NPCs.find(tile.regionLevel) { it.id.startsWith("ghost_disciple_port_phasmatys") } + talkWith(disciple) + npc("What are you doing going in there?") + player("Err, I was just curious...") + npc( + "Inside that room is a coffin, inside which lie the mortal " + + "remains of our most glorious master, Necrovarus. None may enter.", + ) + + return@objectOperate + } + Door.openDoor(this, target) + } + + objectApproach("Search", "ahoy_ship_mast") { + if (get("ahoy_windspeed", false)) { + when ((0..2).random()) { + 0 -> statement( + "You can see a tattered flag blowing in the wind.
" + + "The top half of the flag is coloured ${flagColor(get("ahoy_mast_top", 0))}.", + ) + + 1 -> statement( + "You can see a tattered flag blowing in the wind.
" + + "The bottom half of the flag is coloured ${flagColor(get("ahoy_mast_bottom", 0))}.", + ) + + else -> statement( + "You can see a tattered flag blowing in the wind.
" + + "The skull emblem is coloured ${flagColor(get("ahoy_mast_skull", 0))}.", + ) + } + } else { + statement( + "You can see a tattered flag blowing in the wind.
" + + "The wind is blowing too hard to make out any details.", + ) + } + } + + objectOperate("Talk-to", "ahoy_pirate_captain") { + statement( + "The pirate captain ignores you and continues to stare lifelessly at " + + "nothing, as he has clearly been dead for some time.", + ) + } + + objTeleportTakeOff("Climb-down", "ahoy_ghostship_laddertop") { obj, _ -> + if (obj.tile == Tile(3615, 3545, 2)) { + message("That ladder doesn't go anywhere very safe.") + Teleport.CANCEL + } + + Teleport.CONTINUE + } + + // === Item-on-object hooks === + + itemOnObjectOperate("chest_key_ghosts_ahoy", "ahoy_pirate_chest_locked") { interaction -> + if (interaction.target.tile.x != 3619 || + interaction.target.tile.y != 3545 || + interaction.target.tile.level != 1 + ) { + return@itemOnObjectOperate message("Nothing interesting happens.") + } + if (get("ahoy_subquest_toyboat", 0) == 3) { + return@itemOnObjectOperate message("The chest is already unlocked.") + } + inventory.remove("chest_key_ghosts_ahoy") + sound("unlock") + set("ahoy_subquest_toyboat", 3) + item(item = "chest_key_ghosts_ahoy", text = "You unlock the chest.") + } + + itemOnObjectOperate("bone_key_ghosts_ahoy", "ahoy_harbour_door_closed") { interaction -> + if (interaction.target.tile.x != 3656 || + interaction.target.tile.y != 3514 || + interaction.target.tile.level != 1 + ) { + return@itemOnObjectOperate message("Nothing interesting happens.") + } + if (get("ahoy_templedoor_unlocked", false)) { + return@itemOnObjectOperate message("The door is already unlocked.") + } + sound("unlock") + set("ahoy_templedoor_unlocked", true) + inventory.remove("bone_key_ghosts_ahoy") + message("You unlock the door.") + } + + // === Item-on-item hooks === + + itemOnItem("map_scrap_1", "map_scrap_2") { _, _ -> tryMergeMap() } + itemOnItem("map_scrap_1", "map_scrap_3") { _, _ -> tryMergeMap() } + itemOnItem("map_scrap_2", "map_scrap_3") { _, _ -> tryMergeMap() } + + itemOnItem("bedsheet", "bucket_of_slime") { _, _ -> + inventory.remove("bedsheet") + inventory.remove("bucket_of_slime") + inventory.add("bucket") + inventory.add("bedsheet_ectoplasm") + message("You dip the bedsheet in the bucket of ectoplasm.") + } + + // Dye the toy boat's flag. Java: `dyeOnToyBoat` (GhostsAhoy.java:346). Each dye id maps to + // colour index ((dye_id - 1761) / 2): red=1, yellow=2, blue=3, orange=4, green=5, purple=6. + // Player picks which of the three flag segments (top / skull / bottom) gets the colour. + itemOnItem("model_ship_silk", "red_dye") { _, _ -> dyeToyBoat("red", 1) } + itemOnItem("model_ship_silk", "yellow_dye") { _, _ -> dyeToyBoat("yellow", 2) } + itemOnItem("model_ship_silk", "blue_dye") { _, _ -> dyeToyBoat("blue", 3) } + itemOnItem("model_ship_silk", "orange_dye") { _, _ -> dyeToyBoat("orange", 4) } + itemOnItem("model_ship_silk", "green_dye") { _, _ -> dyeToyBoat("green", 5) } + itemOnItem("model_ship_silk", "purple_dye") { _, _ -> dyeToyBoat("purple", 6) } + + // Pour a brewed nettle tea into the crone's porcelain cup. Java: `fillPorcelainCup` + // (GhostsAhoy.java:375). Source container empties (bowl → bowl; cup → empty_cup) and the + // porcelain cup is filled with the appropriate (plain / milky) tea variant. + itemOnItem("porcelain_cup", "nettle_tea") { _, _ -> + sound("liquid") + inventory.remove("porcelain_cup") + inventory.remove("nettle_tea") + inventory.add("bowl") + inventory.add("cup_of_tea_ghosts_ahoy") + } + itemOnItem("porcelain_cup", "milky_nettle_tea") { _, _ -> + sound("liquid") + inventory.remove("porcelain_cup") + inventory.remove("milky_nettle_tea") + inventory.add("bowl") + inventory.add("cup_of_milky_tea_ghosts_ahoy") + } + itemOnItem("porcelain_cup", "cup_of_tea_nettle_tea") { _, _ -> + sound("liquid") + inventory.remove("porcelain_cup") + inventory.remove("cup_of_tea_nettle_tea") + inventory.add("empty_cup") + inventory.add("cup_of_tea_ghosts_ahoy") + } + itemOnItem("porcelain_cup", "cup_of_milky_tea") { _, _ -> + sound("liquid") + inventory.remove("porcelain_cup") + inventory.remove("cup_of_milky_tea") + inventory.add("empty_cup") + inventory.add("cup_of_milky_tea_ghosts_ahoy") + } + + // Add milk to the porcelain-cup tea. Java: `addMilkPorcelain` (GhostsAhoy.java:399). + itemOnItem("cup_of_tea_ghosts_ahoy", "bucket_of_milk") { _, _ -> + sound("liquid") + inventory.remove("bucket_of_milk") + inventory.add("bucket") + inventory.replace("cup_of_tea_ghosts_ahoy", "cup_of_milky_tea_ghosts_ahoy") + } + + // Add milk to the standard cup-of-tea (non-porcelain). Java: `addMilkTeaCup` + // (GhostsAhoy.java:410). + itemOnItem("cup_of_tea_nettle_tea", "bucket_of_milk") { _, _ -> + sound("liquid") + inventory.remove("bucket_of_milk") + inventory.add("bucket") + inventory.replace("cup_of_tea_nettle_tea", "cup_of_milky_tea") + } + + // === Item options === + + itemOption("Count", "petition_form") { + val signed = (get("ahoy_signaturecounter", 0) - 1).coerceAtLeast(0) + when (signed) { + 0 -> message("You haven't got any signatures yet.") + 1 -> message("You have obtained 1 signature.") + else -> message("You have obtained $signed signatures.") + } + } + + itemOption("Drop", "petition_form") { + inventory.remove("petition_form") + message("You drop the petition form; it blows away in the wind.") + } + + itemOption("Repair", "model_ship") { + if (inventory.contains("silk") && + inventory.contains("needle") && + inventory.contains("thread") && + inventory.contains("knife") + ) { + inventory.remove("silk") + inventory.remove("thread", 1) + inventory.replace("model_ship", "model_ship_silk") + item(item = "model_ship_silk", text = "You replace the toy boat's missing flag.") + } else { + statement( + "You need some silk to replace the flag, something to sew it to the " + + "boat, and something to cut the flag to the right size.", + ) + } + } + + itemOption("Inspect", "model_ship_silk") { + item( + item = "model_ship_silk", + text = "The top of the flag is ${flagColor(get("ahoy_toy_top", 0))}.
" + + "The skull emblem is ${flagColor(get("ahoy_toy_skull", 0))}.
" + + "The bottom of the flag is ${flagColor(get("ahoy_toy_bottom", 0))}.", + ) + } + + // === NPC death === + + npcDeath("giant_lobster") { + val killer = killer + if (killer is Player) { + killer["ahoy_killed_lobster"] = true + } + } + + itemOption("Dig", "spade") { + if (tile.x == 3803 && tile.y == 3530 && inventory.contains("treasure_map") && !inventory.contains("book_of_haricanto") && inventory.spaces > 0) { + anim("human_dig") + delay(3) + addOrDrop("book_of_haricanto") + item(item = "book_of_haricanto", text = "You unearth the Book of Haricanto.") + } + } + + itemAdded("ghostspeak_amulet_enchanted", "worn_equipment", EquipSlot.Amulet) { + set("wearing_ghost_speak_amulet", true) + } + + itemRemoved("ghostspeak_amulet_enchanted", "worn_equipment", EquipSlot.Amulet) { + set("wearing_ghost_speak_amulet", false) + } + + itemAdded("bedsheet", "worn_equipment", EquipSlot.Hat) { + message("I'll only wear the bedsheet inside Port Phasmatys!") + transform("ahoy_ghost_disguise") + } + + itemAdded("bedsheet_ectoplasm", "worn_equipment", EquipSlot.Hat) { + message("I'll only wear the bedsheet inside Port Phasmatys!") + transform("ahoy_ghost_disguise_green") + } + + itemRemoved("bedsheet", "worn_equipment", EquipSlot.Hat) { + if (this.transform == "ahoy_ghost_disguise" || this.transform == "ahoy_ghost_disguise_green") { + clearTransform() + } + } + + itemRemoved("bedsheet_ectoplasm", "worn_equipment", EquipSlot.Hat) { + if (this.transform == "ahoy_ghost_disguise" || this.transform == "ahoy_ghost_disguise_green") { + clearTransform() + } + } + + entered("ahoy_shipwreck_top") { + open("ahoy_windspeed") + set("ahoy_windspeed", false) + timers.start("windspeed") + } + + exited("ahoy_shipwreck_top") { + close("ahoy_windspeed") + timers.stop("windspeed") + } + + timerStart("windspeed") { + (0..16).random() + 2 + } + + timerTick("windspeed") { + val lowWind = get("ahoy_windspeed", false) + set("ahoy_windspeed", !lowWind) + interfaces.sendText("ahoy_windspeed", "content", if (lowWind) "High" else "Low") + (0..16).random() + 2 + } + } + + private suspend fun Player.dyeToyBoat(colourName: String, colourIndex: Int) { + choice("Which part of the flag do you want to dye?") { + option("Top half") { + inventory.remove("${colourName}_dye") + set("ahoy_toy_top", colourIndex) + item(item = "model_ship_silk", text = "You dye the top of the flag $colourName.") + } + option("Bottom half") { + inventory.remove("${colourName}_dye") + set("ahoy_toy_bottom", colourIndex) + item(item = "model_ship_silk", text = "You dye the bottom of the flag $colourName.") + } + option("Skull emblem") { + inventory.remove("${colourName}_dye") + set("ahoy_toy_skull", colourIndex) + item(item = "model_ship_silk", text = "You dye the skull emblem $colourName.") + } + } + } + + private suspend fun Player.tryMergeMap() { + if (inventory.contains("map_scrap_1") && + inventory.contains("map_scrap_2") && + inventory.contains("map_scrap_3") + ) { + inventory.remove("map_scrap_1") + inventory.remove("map_scrap_2") + inventory.remove("map_scrap_3") + inventory.add("treasure_map") + item( + item = "treasure_map", + text = "You piece the three map scraps together to form a complete map.", + ) + } else { + statement("You don't have all the pieces of the map yet.") + } + } + + private fun flagColor(value: Int): String = when (value) { + 0 -> "white" + 1 -> "red" + 2 -> "yellow" + 3 -> "blue" + 4 -> "orange" + 5 -> "green" + 6 -> "purple" + else -> "" + } +} + +var Player.ghosts_ahoy: Int + get() = get("ahoy_questvar", 0) + set(value) = set("ahoy_questvar", value) + +suspend fun Player.sendGhostsAhoyReward() { + jingle("quest_complete_1") + exp(Skill.Prayer, 2400.0) + addOrDrop("ectophial") + inc("quest_points", 2) + AuditLog.event(this, "quest_completed", "ghosts_ahoy") + ghosts_ahoy = 8 + // Java completeQuest() side-effects: lock subquest varbits to their finished values and + // randomise the in-world mast flag colours for inspection. + set("ahoy_given_manual", true) + set("ahoy_given_robes", true) + set("ahoy_given_book", true) + set("ahoy_signaturecounter", 31) + set("ahoy_subquest_nettletea", 3) + set("ahoy_subquest_bow", 8) + set("ahoy_templedoor_unlocked", true) + set("ahoy_subquest_toyboat", 3) + set("ahoy_killed_lobster", true) + clear("ahoy_toy_top") + clear("ahoy_toy_skull") + clear("ahoy_toy_bottom") + set("ahoy_mast_top", (1..6).random()) + set("ahoy_mast_skull", (1..6).random()) + set("ahoy_mast_bottom", (1..6).random()) + refreshQuestJournal() + questComplete( + "Ghosts Ahoy", + "2 Quest Points", + "2400 Prayer XP", + "An Ectophial", + "Free Port Phasmatys entry", + item = "ectophial", + ) +} + +suspend fun Player.checkGhostspeak(): Boolean { + if (!wearingGhostspeak) { + npc("Woooo wooo wooooo woooo") + statement("You cannot understand the ghost.") + return false + } + return true +} + +// TODO lobster spawn time +// TODO bedsheet runs when interacting +// TODO you shouldn't have to run up to the mast with object approach +// TODO
adds an extra line ? +// TODO bill teach with bedsheet diff --git a/game/src/main/kotlin/content/quest/member/ghosts_ahoy/Gravingas.kt b/game/src/main/kotlin/content/quest/member/ghosts_ahoy/Gravingas.kt new file mode 100644 index 0000000000..0687fb9788 --- /dev/null +++ b/game/src/main/kotlin/content/quest/member/ghosts_ahoy/Gravingas.kt @@ -0,0 +1,144 @@ +package content.quest.member.ghosts_ahoy + +import content.entity.player.bank.ownsItem +import content.entity.player.dialogue.Neutral +import content.entity.player.dialogue.Quiz +import content.entity.player.dialogue.Sad +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.inv.item.addOrDrop +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.timer.Timer + +class Gravingas : Script { + + private val noAmulet = listOf( + "Woooo wo woo wooooo!", + "Wooo wooo Wooooooooo!", + "Wo woooo wooooooo!", + "Woo Wooooooooo wooo wooo wo wooo woo!", + "Wooooo wo woooooo - woooooo wo wooo!", + "Wooo wooooooo, Wooooo woooooo o woooo!", + "Wooo wo wo wooooo wooooo, woo wo woooo wo wooooooooo!", + "Woo'o wooo wooooo - woooooo wo woooooo!", + ) + + private val amulet = listOf( + "Power to the Ghosts!!", + "United we conquer - divided we fall!!", + "Rise together, Ghosts without a cause!!", + "Let Necrovarus know that we want out!!", + "Down with Necrovarus!!", + "We shall overcome!!", + "Don't stay silent - victory in numbers!!", + "Rise up my fellow ghosts, and we shall be victorious!!", + ) + + init { + npcOperate("Talk-To", "ahoy_ghost_protestor") { + if (!checkGhostspeak()) return@npcOperate + val petition = get("ahoy_signaturecounter", 0) + + when { + ghosts_ahoy == 7 -> { + player( + "Why are you still protesting? I have commanded Necrovarus to let you " + + "and your friends do as you like! There's no need for this anymore!", + ) + npc("There's always a need for a healthy interest in politics.") + } + ghosts_ahoy < 4 -> { + npc( + "Will you join with me and protest against the evil ban of Necrovarus " + + "and his disciples?", + ) + if (ghosts_ahoy < 1) { + player("I'm sorry, I don't really think I should get involved.") + npc("Ah, the youth of today - so apathetic to politics.") + return@npcOperate + } + askToJoin() + } + petition == 31 || ghosts_ahoy > 4 -> { + npc("So have you presented the petition to Necrovarus?") + player("Yes. He burned it.") + npc("That's exactly what I thought he would do.") + player( + "Well, if you knew that he would do that, why have I been wasting my " + + "time running around after ghosts for signatures?", + ) + npc("It never hurts to get involved in politics.") + } + petition > 0 -> petitionStatus(petition) + else -> { + npc( + "Will you join with me and protest against the evil desires of " + + "Necrovarus and his disciples?", + ) + askToJoin() + } + } + } + + npcSpawn("protester_standardspeak_multi,protester_ghostspeak_multi") { + softTimers.start("protester_standardspeak_multi,protester_ghostspeak_multi") + } + + npcTimerStart("protester_standardspeak_multi,protester_ghostspeak_multi") { + 14 + } + + npcTimerTick("protester_standardspeak_multi,protester_ghostspeak_multi") { + say(if (id == "protester_standardspeak_multi") amulet.random() else noAmulet.random()) + Timer.CONTINUE + } + } + + private suspend fun Player.askToJoin() { + choice { + option("After hearing Velorina's story I will be happy to help out.") { + addOrDrop("petition_form") + set("ahoy_signaturecounter", 1) + npc( + "Excellent, excellent!! Here - take this petition form, and try and get " + + "10 signatures from the townsfolk.", + ) + } + option("I'm sorry, I don't really think I should get involved.") + } + } + + private suspend fun Player.petitionStatus(petition: Int) { + if (!inventory.contains("petition_form")) { + if (ownsItem("petition_form")) { + player("I'm sorry, I seem to have lost my petition form.") + npc("Are you sure? Have another look in the bank - you might be mistaken.") + } else { + player("I'm sorry, I seem to have lost my petition form.") + if (inventory.isFull()) { + npc( + "Blown away in the sea breeze, hey? Oh well, can't be helped. I'd " + + "give you another one, but you look like you're carrying too much already.", + ) + } else { + set("ahoy_signaturecounter", 1) + addOrDrop("petition_form") + npc( + "Blown away in the sea breeze, hey? Oh well, can't be helped. Here's " + + "another one, but you'll have to start from scratch again.", + ) + } + } + return + } + when (petition) { + 11 -> npc("You've got them all! Now go and present it to Necrovarus!!") + 10 -> npc("Very nearly there!! Only 1 more signature to go.") + 1 -> npc("Come on - you haven't even started yet! You need 10 more signatures.") + else -> npc("Not doing bad I see! You need ${11 - petition} more signatures.") + } + } +} diff --git a/game/src/main/kotlin/content/quest/member/ghosts_ahoy/Necrovarus.kt b/game/src/main/kotlin/content/quest/member/ghosts_ahoy/Necrovarus.kt new file mode 100644 index 0000000000..723ca7d23f --- /dev/null +++ b/game/src/main/kotlin/content/quest/member/ghosts_ahoy/Necrovarus.kt @@ -0,0 +1,246 @@ +package content.quest.member.ghosts_ahoy + +import content.entity.player.bank.ownsItem +import content.entity.player.dialogue.Neutral +import content.entity.player.dialogue.Quiz +import content.entity.player.dialogue.Scared +import content.entity.player.dialogue.Shifty +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.entity.player.dialogue.type.statement +import content.entity.player.inv.item.addOrDrop +import world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.entity.character.player.Player +import world.gregs.voidps.engine.entity.item.floor.FloorItems +import world.gregs.voidps.engine.inv.add +import world.gregs.voidps.engine.inv.equipment +import world.gregs.voidps.engine.inv.inventory +import world.gregs.voidps.engine.inv.remove +import world.gregs.voidps.engine.inv.replace +import world.gregs.voidps.network.login.protocol.visual.update.player.EquipSlot + +class Necrovarus : Script { + init { + npcOperate("Talk-To", "ahoy_necrovarus") { (target) -> + if (!checkGhostspeak()) return@npcOperate + when (ghosts_ahoy) { + 0 -> chitchatOptions() + 1 -> pleaForVelorina() + 2, 3 -> ignored() + 4 -> petitionFlow(target) + 6 -> commandPhase() + 7, 8 -> postQuestTaunt() + } + } + } + + private suspend fun Player.chitchatOptions() { + choice { + option("What is this place?") { + randomRebuff() + } + option("What happened to everyone here?") { + randomRebuff() + } + option("How do I get into the town?") { + randomRebuff() + } + } + } + + private suspend fun Player.randomRebuff() { + when ((0..3).random()) { + 0 -> npc("Speak to me again and I will rend the soul from your flesh.") + 1 -> npc("I will answer questions when you are dead!!") + 2 -> npc("You dare to speak to me??? Have you lost your wits????") + 3 -> npc("I do not answer questions, mortal fool!!") + } + } + + private suspend fun Player.pleaForVelorina() { + player("I must speak with you on behalf of Velorina.") + npc("You dare to speak that name in this place?????") + player("She wants to pass-") + npc("Silence!! Or I will incinerate the flesh from your bones!!!") + player("But she-") + ghosts_ahoy = 2 + npc( + "Get out of my sight!! Or I promise you that you will regret your insolence for " + + "the rest of eternity!!!", + ) + } + + private suspend fun Player.ignored() { + player("Please, listen to me-") + npc("No - listen to me. Go from this place and do not return, or I will remove your head.") + } + + private suspend fun Player.petitionFlow(necrovarus: world.gregs.voidps.engine.entity.character.npc.NPC) { + val petition = get("ahoy_signaturecounter", 0) + val doorUnlocked = get("ahoy_templedoor_unlocked", false) + when { + petition in 1..11 && !inventory.contains("petition_form") || doorUnlocked -> { + player( + "It matters not that you ignore your citizens' wishes, Necrovarus. Wheels " + + "have been set in motion, wheels that will set them free.", + ) + npc( + "I have almost completely lost patience with you, mortal. Another word, " + + "and every threat I have uttered will be made real for you.", + ) + } + + petition == 31 -> { + player("So, have you changed your mind yet?") + npc("NEVER!!!!!") + if (ownsItem("bone_key_ghosts_ahoy")) { + npc("WHICH IS A VERY LONG TIME!!!!!!") + } else { + statement("In his rage, Necrovarus drops a key on the floor.") + FloorItems.add(necrovarus.tile, "bone_key_ghosts_ahoy", revealTicks = 0, disappearTicks = 300, owner = this) + } + } + + petition == 11 -> presentPetition(necrovarus) + petition >= 1 -> { + player( + "I must let you know, Necrovarus, that I am collecting signatures from " + + "the citizens of Port Phasmatys.", + ) + npc( + "Oh, I do admire an activist. Do let me know when you're finished, and " + + "I'll give it my fullest consideration.", + ) + } + + else -> { + player( + "Wheels have been set in motion, Necrovarus; wheels that will set the " + + "citizens of Port Phasmatys free.", + ) + npc("Oh goody goody. I just can't wait.") + } + } + } + + private suspend fun Player.presentPetition(necrovarus: world.gregs.voidps.engine.entity.character.npc.NPC) { + player( + "Necrovarus, I am presenting you with a petition form that has been signed by 10 " + + "citizens of Port Phasmatys.", + ) + npc("A petition you say? Continue, mortal.") + player( + "It says that the citizens of Port Phasmatys should have the right to choose " + + "whether they pass over into the next world or not, and not have this " + + "decided by the powers that be on their behalf.", + ) + npc("I see.") + player("So you will let them pass over if they wish?") + npc("Oh yes.") + player("Really?") + npc( + "NO!!!!! Get out of my sight before I burn every ounce of flesh from your bones!!!!!", + ) + set("ahoy_signaturecounter", 31) + inventory.remove("petition_form") + addOrDrop("ashes") + statement("The petition form turns to ashes in your hand.") + statement("In his rage, Necrovarus drops a key on the floor.") + FloorItems.add(necrovarus.tile, "bone_key_ghosts_ahoy", revealTicks = 0, disappearTicks = 300, owner = this) + } + + private suspend fun Player.commandPhase() { + npc( + "You dare to face me again - you must be truly insane!!!!", + ) + if (!equipment.contains("ghostspeak_amulet_enchanted")) { + return + } + player( + "No, Necrovarus, I am not insane. With this enchanted amulet of ghostspeak I have " + + "the power to command you to do my will!", + ) + commandChoice() + } + + private suspend fun Player.commandChoice() { + choice("What do you want to command Necrovarus to do?") { + option("Let any ghost who so wishes pass on into the next world.") { + releaseBan() + } + option("Tell me a joke.") { + joke() + } + option("Do a chicken impression.") { + chicken() + } + } + } + + private suspend fun Player.releaseBan() { + item( + item = "ghostspeak_amulet_enchanted", + text = "A beam of intense green light radiates out from the amulet of ghostspeak, " + + "enveloping Necrovarus in its power. His eyes become softer, and appear to " + + "stare into nothingness.", + ) + ghosts_ahoy = 7 + equipment.replace(EquipSlot.Weapon.index, "ghostspeak_amulet_enchanted", "ghostspeak_amulet") + set("ahoy_templedoor_unlocked", true) + npc("I - will - let ...") + player("Carry on...") + npc(" ... any ... ") + player("Yes?") + npc("... ghost who so wishes ... ") + player("I think we're almost getting there...") + npc("... pass into the next world.") + } + + private suspend fun Player.joke() { + item( + item = "ghostspeak_amulet_enchanted", + text = "A beam of intense green light radiates out from the amulet of ghostspeak, " + + "enveloping Necrovarus in its power. His eyes become softer, and appear to " + + "stare into nothingness.", + ) + npc("Knock knock") + player("Who's there?") + npc("Egbert.") + player("Egbert who?") + npc("Egbert no bacon.") + item( + item = "ghostspeak_amulet_enchanted", + text = "Luckily the amulet of ghostspeak does not seem to have fully discharged.", + ) + } + + private suspend fun Player.chicken() { + item( + item = "ghostspeak_amulet_enchanted", + text = "A beam of intense green light radiates out from the amulet of ghostspeak, " + + "enveloping Necrovarus in its power. His eyes become softer, and appear to " + + "stare into nothingness.", + ) + npc("Cluck cluck squuuaaaakkkk cluck cluck") + npc("I think I've laid an egg...") + item( + item = "ghostspeak_amulet_enchanted", + text = "Luckily the amulet of ghostspeak does not seem to have fully discharged.", + ) + } + + private suspend fun Player.postQuestTaunt() { + player( + "Told you I'd defeat you, Necrovarus. My advice to you is to pass over to the " + + "next world yourself with everybody else.", + ) + npc("I should fry you for what you have done...") + player( + "Quiet, evil priest!! If you try anything I will command you again, but this time " + + "it will be to throw yourself into the Endless Void for the rest of eternity.", + ) + npc("Please no! I will do whatever you say!!") + } +} diff --git a/game/src/main/kotlin/content/quest/member/ghosts_ahoy/OldCrone.kt b/game/src/main/kotlin/content/quest/member/ghosts_ahoy/OldCrone.kt new file mode 100644 index 0000000000..9a81449ae6 --- /dev/null +++ b/game/src/main/kotlin/content/quest/member/ghosts_ahoy/OldCrone.kt @@ -0,0 +1,360 @@ +package content.quest.member.ghosts_ahoy + +import content.entity.player.bank.ownsItem +import content.entity.player.dialogue.Neutral +import content.entity.player.dialogue.Quiz +import content.entity.player.dialogue.Sad +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.entity.player.dialogue.type.statement +import content.entity.player.inv.item.addOrDrop +import world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.entity.character.npc.NPC +import world.gregs.voidps.engine.entity.character.player.Player +import world.gregs.voidps.engine.entity.item.Item +import world.gregs.voidps.engine.inv.add +import world.gregs.voidps.engine.inv.equipment +import world.gregs.voidps.engine.inv.inventory +import world.gregs.voidps.engine.inv.remove +import world.gregs.voidps.engine.inv.replace +import world.gregs.voidps.network.login.protocol.visual.update.player.EquipSlot + +class OldCrone : Script { + + lateinit var crone: NPC + + init { + npcOperate("Talk-To", "ahoy_crone") { (target) -> + // TODO: Animal Magnetism branch (Java OldCroneDialogue cases 2-28) — wire up once + // that quest is ported. For now we only handle the Ghost's Ahoy flow. + crone = target + when { + ghosts_ahoy == 0 -> idleSmallTalk() + ghosts_ahoy > 2 -> ahoyBranch() + else -> { + choice { + option("I'm here about Necrovarus.") { + ahoyBranch() + } + option("I'm here about the farmers east of here.") { + npc("Come back to me about that after you've made some progress with Velorina.") + } + } + } + } + } + + itemOnNPCOperate("cup_of_milky_tea_ghosts_ahoy,cup_of_tea_ghosts_ahoy,cup_of_milky_tea,cup_of_tea_nettle_tea,nettle_tea,milky_nettle_tea,translation_manual,mystical_robes,book_of_haricanto", "ahoy_crone") { interaction -> + when { + TEA_PRIORITY.contains(interaction.item.id) -> offerTea(interaction.item) + ENCHANTMENT_PRIORITY.contains(interaction.item.id) -> stageFourOptions(interaction.item) + } + } + } + + private suspend fun Player.idleSmallTalk() { + player("Hello, old woman.") + when ((0..2).random()) { + 0 -> npc("When 100 years old you reach, look like a prune you will.") + 1 -> npc("I lived here when this was all just fields, you know.") + 2 -> npc("I'm over 100 years old, you know.") + } + } + + private suspend fun Player.ahoyBranch() { + player("I'm here about Necrovarus.") + when { + ghosts_ahoy >= 7 -> postQuestThanks() + ghosts_ahoy == 6 -> enchantmentFollowUp() + ghosts_ahoy == 5 -> performEnchantment() + ghosts_ahoy == 4 -> stageFourOptions(Item(firstEnchantmentItem())) + ghosts_ahoy == 3 -> wereYouADisciple() + } + } + + private suspend fun Player.wereYouADisciple() { + player("Were you once a disciple of Necrovarus in the Temple of Phasmatys, old woman?") + npc("I don't remember; I am so old and my memory goes back only so far...") + player("Is there anything that can help to refresh your memory?") + if (get("ahoy_subquest_nettletea", 0) < 1) { + set("ahoy_subquest_nettletea", 1) + } + npc("Yes, I would love a nice hot cup of nettle tea.") + val tea = firstNettleTea() + if (tea != null) { + offerTea(Item(tea)) + } else { + player("Do you know where I can find nettles around here?") + npc("I believe they grow wild in the Haunted Forest.") + } + } + + private suspend fun Player.offerTea(itemUsed: Item) { + if (ghosts_ahoy >= 4) { + npc("Oh, no thanks; I'm not thirsty anymore.") + return + } + + when (itemUsed.id) { + "cup_of_milky_tea_ghosts_ahoy" -> { + set("ahoy_subquest_nettletea", 3) + ghosts_ahoy = 4 + inventory.remove("cup_of_milky_tea_ghosts_ahoy") + player("Here's a lovely cup of milky tea for you, in your own special cup.") + } + "cup_of_tea_ghosts_ahoy" -> { + player("Here's a lovely cup of tea for you, in your own special cup.") + npc("Oh no, it hasn't got milk in it. I only drink tea with milk, I'm afraid.") + return + } + else -> { + player("Here's some tea for you, like you asked.") + npc("Yes, but it's not in my special cup! I only ever drink from my special cup!!") + player("I see. Can I have this special cup, then?") + if (inventory.contains("porcelain_cup") || ownsItem("porcelain_cup")) { + npc("I already gave you a cup - what did you do with it?") + } else { + set("ahoy_subquest_nettletea", 2) + addOrDrop("porcelain_cup") + item(item = "porcelain_cup", text = "The old crone gives you her special cup.") + } + return + } + } + + statement("As the old woman drinks the cup of milky tea, enlightenment glows from within her eyes.") + npc("Ah, that's better. Now, let me see... Yes, I was once a disciple of Necrovarus.") + player("I have come from Velorina. She needs your help.") + npc("Velorina? That name sounds familiar...") + npc( + "Yes, Velorina was once a very good friend! It has been many years since we spoke " + + "last. How is she?", + ) + player("She's a ghost, I'm afraid.") + npc( + "They are all dead, then. Even Velorina. I should have done something to stop what " + + "was happening, instead of running away.", + ) + player( + "She and many others want to pass on into the next world, but Necrovarus will not " + + "let them. Do you know of any way to help them?", + ) + npc("There might be one way...") + npc("Do you have such a thing as a ghostspeak amulet?") + if (equipment.contains("ghostspeak_amulet") || inventory.contains("ghostspeak_amulet")) { + if (equipment.contains("ghostspeak_amulet")) { + player("Yes, I'm wearing one right now.") + } else { + player("Yes, I'm carrying one in my bag.") + } + npc("Well, that's a stroke of luck.") + } else { + player("No I don't, I'm afraid.") + npc("Well, you'll need to find one.") + } + explainEnchantmentNeeds() + } + + private suspend fun Player.explainEnchantmentNeeds() { + npc( + "There is an enchantment that I can perform on such an amulet that will give it the " + + "power of command over ghosts. It will work only once, but it will enable you " + + "to command Necrovarus to let the ghosts pass on.", + ) + player("What do you need to perform the enchantment?") + npc("You have already given me robes of Necrovarus.") + npc( + "Necrovarus had a magical robe for which he must have no more use. Only these robes " + + "hold the power needed to perform the enchantment.", + ) + npc("You have given me the Book of Haricanto.") + npc( + "All his rituals came from a book written by an ancient sorcerer from the East " + + "called Haricanto. Bring me this strange book.", + ) + npc( + "I cannot read the strange letters of the eastern lands. I will need something to " + + "help me translate the book.", + ) + sendStageFourMenu() + } + + private suspend fun Player.stageFourOptions(itemUsed: Item) { + val hasManual = itemUsed.id == "translation_manual" && !get("ahoy_given_manual", false) + val hasRobes = itemUsed.id == "mystical_robes" && !get("ahoy_given_robes", false) + val hasBook = itemUsed.id == "book_of_haricanto" && !get("ahoy_given_book", false) + + if (hasManual || hasRobes || hasBook) { + when (itemUsed.id) { + "translation_manual" -> acceptQuestItem("translation_manual", "ahoy_given_manual", "A translation manual - yes, this should do the job.") + "mystical_robes" -> acceptQuestItem("mystical_robes", "ahoy_given_robes", "Good - the robes of Necrovarus.") + "book_of_haricanto" -> acceptQuestItem("book_of_haricanto", "ahoy_given_book", "The Book of Haricanto! I have no idea how you came by this, but well done!!") + } + + if (get("ahoy_given_manual", false) && + get("ahoy_given_robes", false) && + get("ahoy_given_book", false) + ) { + npc("Wonderful; that's everything I need.") + ghosts_ahoy = 5 + npc("I will now perform the ritual of enchantment.") + performEnchantment() + } + return + } + sendStageFourMenu() + } + + private suspend fun Player.acceptQuestItem(itemId: String, flag: String, response: String) { + inventory.remove(itemId) + set(flag, true) + player("I have something for you.") + npc(response) + } + + private suspend fun Player.sendStageFourMenu() { + val toyStage = get("ahoy_subquest_toyboat", 0) + choice { + if (toyStage >= 2) { + option("Good news! I have found your son!") { + foundSon() + } + } else if (toyStage == 1) { + if (inventory.contains("model_ship") || inventory.contains("model_ship_silk")) { + option("I am afraid I have not found your son yet.") { + npc( + "I never expected that you would find him - although if you do, " + + "please let me know.", + ) + } + } else { + option("I am afraid I have lost the boat you gave to me.") { + npc("No, I've found it - here you go.") + addOrDrop("model_ship") + item(item = "model_ship", text = "The old woman gives you a toy boat.") + } + } + } else { + option("You are doing so much for me - is there anything I can do for you?") { + sonStory() + } + } + option("Remind me - what can I do about Necrovarus?") { + explainEnchantmentNeeds() + } + option("What did you want me to get for you?") { + npc( + "Necrovarus had a magical robe for which he must have no more use. Only " + + "these robes hold the power needed to perform the enchantment.", + ) + } + option("I'll go and search for those items you need.") + } + } + + private suspend fun Player.sonStory() { + npc( + "I have lived here on my own for many years, but not always. When I left Port " + + "Phasmatys, I took my son with me. He grew up to be a fine boy, always in " + + "love with the sea.", + ) + npc( + "He was about twelve years old when he ran away with some pirates to be a cabin " + + "boy. I never saw him again.", + ) + player("That's the second saddest story I have heard today.") + npc("If you ever see him, please give him this...and tell him that his mother still loves him.") + set("ahoy_subquest_toyboat", 1) + set("ahoy_mast_top", (1..6).random()) + set("ahoy_mast_bottom", (1..6).random()) + set("ahoy_mast_skull", (1..6).random()) + addOrDrop("model_ship") + item(item = "model_ship", text = "The old woman gives you a toy boat.") + player("Was this his boat?") + npc( + "Yes, he made it himself. It is a model of the very ship in which he sailed away. " + + "The paint has peeled off and it has lost its flag, but I could never throw " + + "it away.", + ) + player("If I find him, I will pass it on.") + } + + private suspend fun Player.foundSon() { + npc("Goodness! Where is he?") + player( + "He lives on a shipwreck to the east of here. He remembers you and wishes you well.", + ) + npc("Oh thank you!! I will travel to see him as soon as I am able!!") + } + + private suspend fun Player.performEnchantment() { + if (!ownsItem("ghostspeak_amulet")) { + return npc("You don't have a ghostspeak amulet for me to enchant.") + } + crone.anim("human_dancing") + delay(3) + if (equipment.contains("ghostspeak_amulet")) { + equipment.replace(EquipSlot.Weapon.index, "ghostspeak_amulet_enchanted", "ghostspeak_amulet") + } else if (inventory.contains("ghostspeak_amulet")) { + inventory.remove("ghostspeak_amulet") + inventory.add("ghostspeak_amulet_enchanted") + } + ghosts_ahoy = 6 + item( + item = "ghostspeak_amulet_enchanted", + text = "The ghostspeak amulet emits a green glow from its gem.", + ) + } + + private suspend fun Player.enchantmentFollowUp() { + if (ownsItem("ghostspeak_amulet_enchanted")) { + npc("Did it work?") + player("Actually, I haven't tried it yet.") + return + } + if (inventory.contains("ghostspeak_amulet") || equipment.contains("ghostspeak_amulet")) { + player("Could you enchant the amulet for me again?") + npc("I didn't hear the magic word...") + player("Please?") + npc("Sorry, didn't quite catch that...") + player("Pretty please???") + npc("Oh, alright; come here, then.") + npc("I will now perform the ritual of enchantment.") + performEnchantment() + } else { + player("I seem to have lost my ghostspeak amulet.") + npc("Oh dear, that's unfortunate. You'll have to go and find another one, then.") + } + } + + private suspend fun Player.postQuestThanks() { + player("Good news - the enchantment worked!") + npc( + "So Necrovarus has let the ghosts go free.... You have achieved many wonderful " + + "things, my young friend.", + ) + } + + private fun Player.firstNettleTea(): String? = TEA_PRIORITY.firstOrNull { inventory.contains(it) } + + private fun Player.firstEnchantmentItem(): String = ENCHANTMENT_PRIORITY.first { inventory.contains(it) } + + companion object { + private val TEA_PRIORITY = listOf( + "cup_of_milky_tea_ghosts_ahoy", + "cup_of_tea_ghosts_ahoy", + "cup_of_milky_tea", + "cup_of_tea_nettle_tea", + "milky_nettle_tea", + "nettle_tea", + ) + + private val ENCHANTMENT_PRIORITY = listOf( + "translation_manual", + "mystical_robes", + "book_of_haricanto", + ) + } +} diff --git a/game/src/main/kotlin/content/quest/member/ghosts_ahoy/OldCroneSon.kt b/game/src/main/kotlin/content/quest/member/ghosts_ahoy/OldCroneSon.kt new file mode 100644 index 0000000000..fc0b55d598 --- /dev/null +++ b/game/src/main/kotlin/content/quest/member/ghosts_ahoy/OldCroneSon.kt @@ -0,0 +1,112 @@ +package content.quest.member.ghosts_ahoy + +import content.entity.player.bank.ownsItem +import content.entity.player.dialogue.Neutral +import content.entity.player.dialogue.Quiz +import content.entity.player.dialogue.Sad +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.entity.player.dialogue.type.statement +import content.entity.player.inv.item.addOrDrop +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 OldCroneSon : Script { + init { + npcOperate("Talk-To", "ahoy_oldman") { + when (val stage = get("ahoy_subquest_toyboat", 0)) { + 0 -> shipwreckIntro() + 1 -> handOverBoat() + 2, 3 -> postBoat(stage) + } + } + } + + private suspend fun Player.shipwreckIntro() { + player("What are you doing on this shipwreck?") + npc("Shipwreck?!? Not shipwreck, surely not! Just in port, that's all!") + player("Don't be silly - half the ship's missing!") + npc("No no no - the captain's just waiting for the wind to change, then we're off!") + player("You mean this skeleton sitting here in this chair?") + npc("You must show more respect to the Captain.") + } + + private suspend fun Player.handOverBoat() { + val hasRepairedBoat = inventory.contains("model_ship_silk") + choice { + option("What is your name?") { + npc("I don't remember. Everyone around here just calls me 'boy'.") + player("You're the cabin boy?!?") + npc("Yes, and proud of it.") + } + option("Can I have the key to the chest?") { + npc("Hang on, let me ask the Captain ...") + statement("The old man cocks an ear towards the Pirate Captain's skeleton.") + if (hasCorrectBoat()) { + grantKey() + } else { + npc("The Captain says no.") + } + } + if (hasRepairedBoat) { + option("Is this your toy boat?") { + item(item = "model_ship_silk", text = "The old man inspects the toy boat.") + if (hasCorrectBoat()) { + npc( + "My word - so it is!!! I never thought I would see it again!! Where " + + "did you get it from?", + ) + player("Your mother gave it to me to pass on to you.") + npc("My mother? She still lives?") + player("Yes, in a shack to the west of here.") + npc("After all these years ...") + player("Can I have the key to the chest, then?") + npc("Hang on, let me ask the Captain ...") + statement("The old man cocks an ear towards the Pirate Captain's skeleton.") + grantKey() + } else if (inventory.contains("model_ship")) { + npc("No - I made a toy boat a long while ago, but that one had a flag.") + } else { + npc( + "No - I made a toy boat a long while ago, but the colours on the " + + "flag were different.", + ) + } + } + } + } + } + + private suspend fun Player.grantKey() { + set("ahoy_subquest_toyboat", 2) + inventory.remove("model_ship") + inventory.remove("model_ship_silk") + addOrDrop("chest_key_ghosts_ahoy") + npc("The Captain says yes.") + item(item = "chest_key_ghosts_ahoy", text = "The old man gives you the chest key.") + } + + private suspend fun Player.postBoat(stage: Int) { + if (ownsItem("chest_key_ghosts_ahoy") || stage == 3) { + player("How is it going?") + npc("Wonderful, wonderful! Mother's coming to get me!") + } else { + player("I've lost my key to the chest - do you have another one?") + npc( + "What? But the chest is only just over there! How on RuneScape did you lose it " + + "in that short distance?", + ) + player("Sorry, I won't do it again...") + addOrDrop("chest_key_ghosts_ahoy") + item(item = "chest_key_ghosts_ahoy", text = "The old man grudgingly gives you another chest key.") + } + } + + private fun Player.hasCorrectBoat(): Boolean = get("ahoy_mast_top", 0) == get("ahoy_toy_top", 0) && + get("ahoy_mast_skull", 0) == get("ahoy_toy_skull", 0) && + get("ahoy_mast_bottom", 0) == get("ahoy_toy_bottom", 0) +} diff --git a/game/src/main/kotlin/content/quest/member/ghosts_ahoy/Robin.kt b/game/src/main/kotlin/content/quest/member/ghosts_ahoy/Robin.kt new file mode 100644 index 0000000000..9ad4483c42 --- /dev/null +++ b/game/src/main/kotlin/content/quest/member/ghosts_ahoy/Robin.kt @@ -0,0 +1,337 @@ +package content.quest.member.ghosts_ahoy + +import content.entity.player.bank.ownsItem +import content.entity.player.dialogue.Angry +import content.entity.player.dialogue.Bored +import content.entity.player.dialogue.Neutral +import content.entity.player.dialogue.Quiz +import content.entity.player.dialogue.Sad +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 world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.client.message +import world.gregs.voidps.engine.client.ui.open +import world.gregs.voidps.engine.entity.character.player.Player +import world.gregs.voidps.engine.entity.item.Item +import world.gregs.voidps.engine.inv.add +import world.gregs.voidps.engine.inv.inventory +import world.gregs.voidps.engine.inv.remove +import world.gregs.voidps.engine.queue.longQueue + +class Robin : Script { + init { + + itemOnNPCOperate("*", "ahoy_robin") { + npc("Go away peasant, why would I want that?") + } + + npcOperate("Talk-To", "ahoy_robin") { + val stage = get("ahoy_subquest_bow", 0) + when { + inventory.contains("bedsheet") -> deliverBedsheet() + stage == 0 -> intro() + stage == 1 -> stageOneFlow() + + stage == 7 || stage == 8 -> { + if (stage == 7 && !ownsItem("signed_oak_bow") && inventory.contains("oak_longbow")) { + player("I need you to sign another longbow.") + npc("Oh, okay - just keep your promise not to tell the ghosts anything.") + inventory.remove("oak_longbow") + inventory.add("signed_oak_bow") + item(item = "signed_oak_bow", text = "Robin signs another oak longbow for you.") + } else { + player("So, still here then?") + npc("Do you want another game of Runedraw?") + player("No!") + } + } + + else -> runedrawResult() + } + } + + interfaceOption("Draw", "ahoy_runedraw:runedraw_btn_draw") { + playerTurn() + } + + interfaceOption("Hold", "ahoy_runedraw:runedraw_btn_hold") { + set("ahoy_runedraw_held", 1) + set("ahoy_player_runedraw_turn", get("ahoy_player_runedraw_turn", 0) + 1) + interfaces.sendText("ahoy_runedraw", "runedraw_totalscore_r", "HELD") + interfaces.sendText("ahoy_runedraw", "runedraw_totalscore_l", "Thinking...") + interfaces.sendVisibility("ahoy_runedraw", "runedraw_btn_draw", false) + interfaces.sendVisibility("ahoy_runedraw", "runedraw_btn_hold", false) + robinTurn() + } + } + + private suspend fun Player.intro() { + player("It's nice to see another human face around here.") + npc("Leave me be, peasant - I am relaxing.") + player("Well, that's nice!") + npc("Do you know who I am?") + player("I'm sorry, I haven't had the privilege.") + npc("I, peasant, am Robin, Master Bowman. I am very famous you know.") + player("Oh, Robin, Master Bowman, I see.") + npc("So have you heard of me?") + player("No.") + npc( + "Would you do me a favour? I appear to have run out of bed linen. Can you run along " + + "to the innkeeper and get me a clean bedsheet?", + ) + choice { + option("Anything for a famous person such as yourself.") { + npc("Now that sounds more like it. Run along now, get me my sheet.") + } + option("Go and get it yourself.") { + npc("Oh charming. You just can't get the staff these days.") + } + } + } + + private suspend fun Player.deliverBedsheet() { + player("I've brought you a clean bedsheet, Robin.") + inventory.remove("bedsheet") + npc("Well it's about time. Run along now, I need some Robin time.") + } + + private suspend fun Player.stageOneFlow() { + if (inventory.contains("oak_longbow")) { + player("Would you sign this oak longbow for me?") + npc("I'm sorry, I don't sign autographs.") + npc( + "While you're here, though, why don't you have a game of Runedraw with me? " + + "If you've got 25 gold pieces I've got a bag of runes we can use.", + ) + } else { + npc( + "Hey you, why don't you have a game of Runedraw with me? If you've got 25 gold " + + "pieces I've got a bag of runes we can use.", + ) + } + runedrawOptions(withDebt = false) + } + + private suspend fun Player.askAnotherGame() { + val stage = get("ahoy_subquest_bow", 0) + if (stage == 2) { + npc("How about another game?") + } else { + npc("Err ... how about another game?", "We can settle up after we've finished playing.") + } + runedrawOptions(withDebt = false) + } + + private suspend fun Player.runedrawOptions(withDebt: Boolean) { + choice { + option("Yes, I'll give you a game.") { + if (!inventory.contains("coins", 25)) { + player("Yes, I'll give you a game.") + player("I don't have enough money on me for a game of Runedraw at the moment.") + return@option + } + if (get("ahoy_subquest_bow", 0) == 1) { + set("ahoy_subquest_bow", 2) + } + inventory.remove("coins", 25) + player("Yes, I'll give you a game.") + start() + } + option("How do you play Runedraw?") { + npc( + "Two players take turns to draw a rune from a bag, which contains ten " + + "runes in total. Each rune has a different value: an air rune is worth " + + "one point, up to a Nature rune which is worth nine points.", + ) + npc("If a player draws the Death rune then the game is over, and they have lost.") + npc( + "A player can choose to hold if they wish and not draw any more runes, but " + + "this runs the risk of the other player drawing more runes until they " + + "have a greater points total and win.", + ) + } + if (withDebt) { + option("No, you didn't pay up the last time you lost.") + } else { + option("No, that doesn't sound fun.") + } + } + } + + private suspend fun Player.handleBowSign(extraSign: Boolean) { + if (inventory.contains("oak_longbow")) { + npc("Yes, anything!!!") + inventory.remove("oak_longbow") + inventory.add("signed_oak_bow") + item( + item = "signed_oak_bow", + text = if (extraSign) "Robin signs another oak longbow for you." else "Robin signs the oak longbow for you.", + ) + set("ahoy_subquest_bow", 7) + } else { + npc("I'll certainly sign your longbow, if you bring me one!!") + } + } + + private suspend fun Player.playerTurn() { + val turn = get("ahoy_player_runedraw_turn", 0) + val roll = if (turn == 9) 9 else (0..9).random() + if (roll != 9) { + set("ahoy_player_runedraw_sum", get("ahoy_player_runedraw_sum", 0) + roll + 1) + } + val slot = "player_slot_${turn + 1}" + interfaces.sendItem("ahoy_runedraw", slot, Item(RUNEDRAW_RUNES[roll], 80)) + interfaces.sendVisibility("ahoy_runedraw", slot, true) + set("ahoy_player_runedraw_turn", turn + 1) + interfaces.sendVisibility("ahoy_runedraw", "runedraw_btn_draw", false) + interfaces.sendVisibility("ahoy_runedraw", "runedraw_btn_hold", false) + if (roll == 9) { + sendLoss() + return + } + interfaces.sendText("ahoy_runedraw", "runedraw_score_r", "${get("ahoy_player_runedraw_sum", 0)}") + interfaces.sendText("ahoy_runedraw", "runedraw_totalscore_r", "") + interfaces.sendText("ahoy_runedraw", "runedraw_totalscore_l", "Thinking...") + robinTurn() + } + + private suspend fun Player.robinTurn() { + delay(4) + val turn = get("ahoy_robin_runedraw_turn", 0) + val roll = if (get("ahoy_player_runedraw_turn", 0) == 9) 9 else (0..9).random() + if (roll != 9) { + set("ahoy_robin_runedraw_sum", get("ahoy_robin_runedraw_sum", 0) + roll + 1) + } + val slot = "robin_slot_${turn + 1}" + interfaces.sendItem("ahoy_runedraw", slot, Item(RUNEDRAW_RUNES[roll], 80)) + interfaces.sendVisibility("ahoy_runedraw", slot, true) + set("ahoy_robin_runedraw_turn", turn + 1) + interfaces.sendText("ahoy_runedraw", "runedraw_score_l", "${get("ahoy_robin_runedraw_sum", 0)}") + + val playerSum = get("ahoy_player_runedraw_sum", 0) + val robinSum = get("ahoy_robin_runedraw_sum", 0) + val held = get("ahoy_runedraw_held", 0) == 1 + + when { + turn + 1 == 10 -> if (playerSum > robinSum) sendWin() else sendLoss() + roll == 9 -> sendWin() + held && playerSum < robinSum -> sendLoss() + held -> { + set("ahoy_player_runedraw_turn", get("ahoy_player_runedraw_turn", 0) + 1) + robinTurn() + } + + else -> { + interfaces.sendVisibility("ahoy_runedraw", "runedraw_btn_draw", true) + interfaces.sendVisibility("ahoy_runedraw", "runedraw_btn_hold", true) + interfaces.sendText("ahoy_runedraw", "runedraw_totalscore_l", "") + interfaces.sendText("ahoy_runedraw", "runedraw_totalscore_r", "Your turn") + } + } + } + + private fun Player.sendWin() { + interfaces.sendText("ahoy_runedraw", "runedraw_totalscore_l", "") + interfaces.sendText("ahoy_runedraw", "runedraw_totalscore_r", "You win!") + interfaces.sendVisibility("ahoy_runedraw", "runedraw_btn_draw", false) + interfaces.sendVisibility("ahoy_runedraw", "runedraw_btn_hold", false) + set("ahoy_subquest_bow", get("ahoy_subquest_bow", 0) + 1) + set("ahoy_runedraw_outcome", "win") + message("You've won the game of Runedraw!") + } + + private fun Player.sendLoss() { + interfaces.sendText("ahoy_runedraw", "runedraw_totalscore_l", "") + interfaces.sendText("ahoy_runedraw", "runedraw_totalscore_r", "You lose!") + interfaces.sendVisibility("ahoy_runedraw", "runedraw_btn_draw", false) + interfaces.sendVisibility("ahoy_runedraw", "runedraw_btn_hold", false) + set("ahoy_runedraw_outcome", "loss") + message("You've lost the game of Runedraw.") + } + + private suspend fun Player.runedrawResult() { + val outcome = get("ahoy_runedraw_outcome", "") + val stage = get("ahoy_subquest_bow", 0) + + if (stage == 6) { + player( + "I've had enough of you not paying up - you owe me 100 gold coins. " + + "I'm going to tell the ghosts what you're doing.", + ) + npc("Please don't do that!!! They will suck the life from my bones!!!") + player("How about you signing my longbow then?") + handleBowSign(extraSign = false) + return + } + + when (outcome) { + "" -> { + set("ahoy_runedraw_outcome", "loss") + message("Your resignation from the game of Runedraw has been treated as a loss.") + player("Oh, I give up.") + npc("How about another game?") + runedrawOptions(stage > 2) + } + + "loss" -> { + val text = if (stage <= 2) { + "Well done, you beat me." + } else { + "Well done, you beat me. But you still owe me ${(stage - 2) * 25} gold pieces." + } + player(text) + askAnotherGame() + } + + "win" -> { + player( + "So are you going to pay up then? You owe me ${(stage - 2) * 25} gold coins!", + ) + npc("How about another game? I'll pay you back with the winnings.") + player("What if you lose again?") + npc("Er ... we'll deal with that when we come to it.") + runedrawOptions(stage > 2) + } + } + } + + fun Player.start() { + longQueue("Runedraw result") { + runedrawResult() + } + set("ahoy_runedraw_held", 0) + set("ahoy_player_runedraw_turn", 0) + set("ahoy_player_runedraw_sum", 0) + set("ahoy_robin_runedraw_turn", 0) + set("ahoy_robin_runedraw_sum", 0) + set("ahoy_runedraw_outcome", "") + for (i in 1..10) { + interfaces.sendVisibility("ahoy_runedraw", "player_slot_$i", false) + interfaces.sendVisibility("ahoy_runedraw", "robin_slot_$i", false) + } + interfaces.sendText("ahoy_runedraw", "runedraw_score_r", "") + interfaces.sendText("ahoy_runedraw", "runedraw_totalscore_r", "Your turn") + interfaces.sendText("ahoy_runedraw", "runedraw_score_l", "") + interfaces.sendText("ahoy_runedraw", "runedraw_totalscore_l", "") + interfaces.sendVisibility("ahoy_runedraw", "runedraw_btn_draw", true) + interfaces.sendVisibility("ahoy_runedraw", "runedraw_btn_hold", true) + open("ahoy_runedraw") + } + + companion object { + private val RUNEDRAW_RUNES = listOf( + "air_rune", + "mind_rune", + "water_rune", + "earth_rune", + "fire_rune", + "body_rune", + "cosmic_rune", + "chaos_rune", + "nature_rune", + "death_rune", + ) + } +} diff --git a/game/src/main/kotlin/content/quest/member/ghosts_ahoy/TreasureMap.kt b/game/src/main/kotlin/content/quest/member/ghosts_ahoy/TreasureMap.kt new file mode 100644 index 0000000000..7f029e3766 --- /dev/null +++ b/game/src/main/kotlin/content/quest/member/ghosts_ahoy/TreasureMap.kt @@ -0,0 +1,49 @@ +package content.quest.member.ghosts_ahoy + +import content.entity.player.dialogue.type.choice +import content.entity.player.dialogue.type.statement +import world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.client.ui.open +import world.gregs.voidps.engine.entity.character.player.Player + +class TreasureMap : Script { + init { + itemOption("Read", "treasure_map") { + open("ahoy_islandmap") + } + + itemOption("Follow", "treasure_map") { + if (tile.region.id != 15159) { + statement("The map is for use elsewhere.") + return@itemOption + } + walkSession() + } + } + + private suspend fun Player.walkSession() { + var direction = 0 + choice("Which direction do you wish to walk?") { + option("North") { direction = 1 } + option("East") { direction = 2 } + option("South") { direction = 3 } + option("West") { direction = 4 } + } + if (direction == 0) return + var steps = 0 + choice("How far do you wish to walk?") { + option("1 pace") { steps = 1 } + option("2 paces") { steps = 2 } + option("4 paces") { steps = 4 } + option("10 paces") { steps = 10 } + } + if (steps == 0) return + val targetTile = when (direction) { + 1 -> tile.add(0, steps) + 2 -> tile.add(steps, 0) + 3 -> tile.add(0, -steps) + else -> tile.add(-steps, 0) + } + walkTo(targetTile, forceWalk = false, noCollision = false) + } +} diff --git a/game/src/main/kotlin/content/quest/member/ghosts_ahoy/Velorina.kt b/game/src/main/kotlin/content/quest/member/ghosts_ahoy/Velorina.kt new file mode 100644 index 0000000000..3c0eec8602 --- /dev/null +++ b/game/src/main/kotlin/content/quest/member/ghosts_ahoy/Velorina.kt @@ -0,0 +1,286 @@ +package content.quest.member.ghosts_ahoy + +import content.entity.player.bank.ownsItem +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.Shock +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.entity.player.inv.item.addOrDrop +import world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.entity.character.player.Player +import world.gregs.voidps.engine.entity.character.player.male +import world.gregs.voidps.engine.entity.character.player.name +import world.gregs.voidps.engine.inv.inventory + +class Velorina : Script { + init { + npcOperate("Talk-To", "ahoy_velorina") { + if (!checkGhostspeak()) return@npcOperate + when (ghosts_ahoy) { + 0 -> intro() + 1 -> notSpoken() + 2 -> { + player("I'm sorry, but Necrovarus will not let you go.") + necrovarusRefused() + } + 3 -> oldCroneOptions() + 4 -> itemRequestOptions() + 5 -> { + npc("How is it going?") + player("I found your old friend. She still lives, and has agreed to perform an enchantment for me that will enable me to command Necrovarus to set you free.") + npc("Oh, kind ${if (male) "sir" else "lady"} - you are the answer to all our prayers!") + } + 6 -> { + npc("How is it going?") + if (ownsItem("ghostspeak_amulet_enchanted")) { + player("I have had the Amulet of Ghostspeak enchanted, which I shall use to command Necrovarus to set you free.") + } else { + player( + "I have had the Amulet of Ghostspeak enchanted, but I used it foolishly. I shall return " + + "to the old crone to have it enchanted again - then I shall command Necrovarus to set you free.", + ) + } + npc("Oh, kind ${if (male) "sir" else "lady"} - you are the answer to all our prayers!") + } + 7 -> rewardScene() + 8 -> postQuestOptions() + } + } + } + + private suspend fun Player.intro() { + npc( + "Take pity on me, please - eternity stretches out before me and I am helpless in " + + "its grasp.", + ) + choice { + option("Why, what is the matter?") { + wailExplanation() + explainHistory() + } + option("Sorry, I'm scared of ghosts.") { + player("Leave me alone - I'm scared of ghosts!!!") + } + } + } + + private suspend fun Player.wailExplanation() { + npc("Oh, I'm sorry - I was just wailing out loud. I didn't mean to scare you.") + player( + "No, that's okay - it takes more than a ghost to scare me. What is wrong?", + ) + } + + private suspend fun Player.explainHistory() { + npc("Do you know the history of our town?") + choice { + option("Yes, I do. It is a very sad story.") { + hookOffer() + } + option("No - could you tell me?") { + fullHistory() + hookOffer() + } + } + } + + private suspend fun Player.fullHistory() { + npc("Do you know why ghosts exist?") + player( + "A ghost is a soul left in limbo, unable to pass over to the next world; they " + + "might have something left to do in this world that torments them, or they " + + "might just have died in a state of confusion.", + ) + npc( + "Yes, that is normally the case. But here in Port Phasmatys, we of this town once " + + "chose freely to become ghosts!", + ) + player("Why on earth would you do such a thing?") + npc( + "It is a long story. Many years ago, this was a thriving port, a trading centre " + + "to the eastern lands of Gielinor. We became rich on the profits made from " + + "the traders that came across the eastern seas.", + ) + npc("We were very happy... until Lord Drakan noticed us.") + npc( + "He sent unholy creatures to demand that a blood-tithe be paid to the Lord " + + "Vampyre, as is required from all in the domain of Morytania. We had no " + + "choice but to agree to his demands.", + ) + npc( + "As the years went by, our numbers dwindled and many spoke of abandoning the " + + "town for safer lands. Then, Necrovarus came to us.", + ) + npc( + "He came from the eastern lands, but of more than that, little is known. Some say " + + "he was once a mage, some say a priest. Either way, he was in possession of " + + "knowledge totally unknown to even the most learned among us.", + ) + npc( + "Necrovarus told us that he had been brought by a vision he'd had of an " + + "underground source of power. He inspired us to delve beneath the town, " + + "promising us the power to repel the vampyres.", + ) + npc( + "Deep underneath Phasmatys, we found a pool of green slime that Necrovarus called " + + "ectoplasm. He showed us how to build the Ectofuntus, which would turn the " + + "ectoplasm into the power he had promised us.", + ) + npc( + "Indeed, this Ectopower did repel the vampyres; they would not enter Phasmatys " + + "once the Ectofuntus began working. But little did we know that we had " + + "exchanged one evil for yet another - Ectopower.", + ) + npc( + "Little by little, we began to lose any desire for food or water, and our desire " + + "for the Ectopower grew until it dominated our thoughts entirely. Our bodies " + + "shrivelled and, one by one, we died.", + ) + npc( + "The Ectofuntus and the power it emanates keeps us here as ghosts - some, like " + + "myself, content to remain in this world; some becoming tortured souls who " + + "we do not allow to pass our gates.", + ) + npc( + "We would be able to pass over into the next world but Necrovarus has used his " + + "power to create a psychic barrier, preventing us.", + ) + npc("We must remain here for all eternity, even unto the very end of the world.") + player("That's a very sad story.") + } + + private suspend fun Player.hookOffer() { + npc("Would you help us obtain our release into the next world?") + choice { + option("Yes.") { + ghosts_ahoy = 1 + player("Yes, of course I will. Tell me what you want me to do.") + npc("Oh, thank you!") + explainTask() + } + option("No.") { + player("I'm sorry, but it isn't really my problem.") + npc( + "No, you're right - it's our own fault. If you do change your mind " + + "though, please come back and help us. We will be here ... forever.", + ) + } + } + } + + private suspend fun Player.explainTask() { + npc( + "Necrovarus will not listen to those of us who are already dead. He might rethink " + + "his position if someone with a mortal soul pleaded our cause.", + ) + npc("If he declines, there may yet be another way.") + } + + private suspend fun Player.notSpoken() { + npc("I sense that you have not yet spoken to Necrovarus.") + player("No, I was just getting to that.") + npc("Well, I suppose we do have all the time in the world...") + } + + private suspend fun Player.necrovarusRefused() { + npc("I feared as much. His spirit is a thing of fire and wrath.") + player("You spoke of another way.") + npc( + "It is only a small chance. During the building of the Ectofuntus one of " + + "Necrovarus's disciples spoke out against him. It is such a long time ago I " + + "cannot remember her name, although I knew her as a friend.", + ) + npc( + "She fled before the Ectofuntus took control over us, but being a disciple of " + + "Necrovarus she would have been privy to many of his darkest secrets. She " + + "may know of a way to aid us without Necrovarus.", + ) + ghosts_ahoy = 3 + oldCroneOptions() + } + + private suspend fun Player.oldCroneOptions() { + choice { + option("Do you know where this woman can be found?") { + npc( + "I have a vision of a small wooden shack, the land it was built on the " + + "unholy soil of Morytania. I sense the sea is very close, and that " + + "there looms castles to the west and the east.", + ) + } + option("If it was such a long time ago, won't she be dead already?") { + npc( + "She was a friend of mine. Had she died, I would have felt her spirit " + + "pass over to the next world, though I may not follow.", + ) + } + } + } + + private suspend fun Player.itemRequestOptions() { + choice { + option("Do you know where I can find the Book of Haricanto?") { + npc( + "Nobody knows what has happened to the Book. It was stolen when our port " + + "was raided by pirates many years ago, and never seen since.", + ) + } + option("Do you know where I can find the Robes of Necrovarus?") { + npc( + "I imagine they are still worn by his mortal body, which now lies in a " + + "coffin inside the Temple.", + ) + } + option("I need something to translate the Book of Haricanto.") { + npc( + "I don't really know. You could try asking some of the traders from the " + + "East - they might be able to help you.", + ) + } + } + } + + private suspend fun Player.rewardScene() { + npc( + "You don't need to tell me $name- I sensed the removal of Necrovarus's psychic barrier!", + ) + player("Only happy to help out.") + npc("Here, take this as a thank you for the service that you have given us.") + item(item = "ectophial", text = "Velorina gives you a vial of bright green ectoplasm.") + npc( + "This is an Ectophial. If you ever want to come back to Port Phasmatys, empty " + + "this on the floor beneath your feet, and you will be instantly teleported " + + "to the temple - the source of its power.", + ) + npc("Remember that once the Ectophial has been used you need to refill it from the Ectofuntus.") + sendGhostsAhoyReward() + } + + private suspend fun Player.postQuestOptions() { + choice { + option("I thought you were going to pass over to the next world.") { + npc( + "All in good time, $name. We stand forever in your debt, and will " + + "certainly put in a good word for you when we pass over.", + ) + } + option("Can I have another Ectophial?") { + when { + inventory.isFull() -> npc("No, you've got nowhere to hold it.") + ownsItem("ectophial") || ownsItem("ectophial_empty") -> + npc("Really? You've already got an Ectophial.") + else -> { + npc("Of course you can, you have helped us more than we could ever have hoped.") + addOrDrop("ectophial") + item(item = "ectophial", text = "Velorina gives you a vial of bright green ectoplasm.") + } + } + } + } + } +}