diff --git a/data/area/asgarnia/entrana/entrana.npc-spawns.toml b/data/area/asgarnia/entrana/entrana.npc-spawns.toml index 6b6e0d3690..0a01759cea 100644 --- a/data/area/asgarnia/entrana/entrana.npc-spawns.toml +++ b/data/area/asgarnia/entrana/entrana.npc-spawns.toml @@ -14,7 +14,7 @@ spawns = [ { id = "entrana_monk_grey", x = 2835, y = 3336, members = true }, { id = "entrana_monk_white", x = 2839, y = 3335 }, { id = "entrana_monk_ginger", x = 2830, y = 3335 }, - { id = "mazion_entrana", x = 2818, y = 3342, members = true }, + { id = "mazion", x = 2818, y = 3342, members = true }, { id = "fishing_spot_entrana", x = 2840, y = 3356, members = true }, { id = "fishing_spot_entrana", x = 2842, y = 3359, members = true }, { id = "fishing_spot_entrana", x = 2845, y = 3356, members = true }, diff --git a/data/area/asgarnia/entrana/entrana.npcs.toml b/data/area/asgarnia/entrana/entrana.npcs.toml index 92c881ef0d..968868058c 100644 --- a/data/area/asgarnia/entrana/entrana.npcs.toml +++ b/data/area/asgarnia/entrana/entrana.npcs.toml @@ -32,7 +32,7 @@ examine = "Holy looking." id = 658 examine = "Holy looking." -[mazion_entrana] +[mazion] id = 3114 examine = "Mazion seems to be covered in sand." diff --git a/data/area/asgarnia/entrana/entrana.objs.toml b/data/area/asgarnia/entrana/entrana.objs.toml index 1d79c30794..8f3f46a59c 100644 --- a/data/area/asgarnia/entrana/entrana.objs.toml +++ b/data/area/asgarnia/entrana/entrana.objs.toml @@ -9,3 +9,18 @@ examine = "Handy for boarding the ship." [entrana_gangplank_enter] id = 2414 examine = "Handy for boarding the ship." + +[entrana_ladder] +id = 2408 + +[dramen_tree] +id = 1292 +examine = "An ancient magical tree." + +[magic_door_opened] +id = 31812 +examine = "A large single door." + +[magic_door_closed] +id = 2407 +examine = "The door is closed." diff --git a/data/area/kandarin/ancient_cavern/ancient_cavern.combat.toml b/data/area/kandarin/ancient_cavern/ancient_cavern.combat.toml index 1f82904323..1f01d3285e 100644 --- a/data/area/kandarin/ancient_cavern/ancient_cavern.combat.toml +++ b/data/area/kandarin/ancient_cavern/ancient_cavern.combat.toml @@ -36,8 +36,8 @@ attack_speed = 4 retreat_range = 8 defend_anim = "barbarian_unarmed_defend" defend_sound = "barbarian_grunt_defend" -death_anim = "confused_barbarian_death" -death_sound = "confused_barbarian_death" +death_anim = "human_death" +death_sound = "barbarian_grunt_death" [skeleton_barbarian_axe.melee] range = 1 diff --git a/data/area/kandarin/ancient_cavern/ancient_cavern.sounds.toml b/data/area/kandarin/ancient_cavern/ancient_cavern.sounds.toml index 87aed71d81..33ced1a4fe 100644 --- a/data/area/kandarin/ancient_cavern/ancient_cavern.sounds.toml +++ b/data/area/kandarin/ancient_cavern/ancient_cavern.sounds.toml @@ -9,12 +9,3 @@ id = 3382 [dharok_axe_crush] id = 1316 - -[ghoul_attack] -id = 442 - -[ghoul_defend] -id = 444 - -[ghoul_death] -id = 443 \ No newline at end of file diff --git a/data/area/kandarin/ardougne/east_ardougne.objs.toml b/data/area/kandarin/ardougne/east_ardougne.objs.toml index 6d0035c051..19b461cdd1 100644 --- a/data/area/kandarin/ardougne/east_ardougne.objs.toml +++ b/data/area/kandarin/ardougne/east_ardougne.objs.toml @@ -175,4 +175,16 @@ examine = "Used for fashioning clay items." id = 8783 [ladder_mourner_tunnels] -id = 8785 \ No newline at end of file +id = 8785 + +[flour_bin_ardougne] +id = 1782 + +[hopper_ardougne] +id = 2717 + +[hopper_controls_ardougne] +id = 2721 + +[flour_bin_ardougne_empty] +id = 5792 diff --git a/data/area/misthalin/lumbridge/swamp/lumbridge_swamp.objs.toml b/data/area/misthalin/lumbridge/swamp/lumbridge_swamp.objs.toml index b71308c17b..2ba8b0ce7e 100644 --- a/data/area/misthalin/lumbridge/swamp/lumbridge_swamp.objs.toml +++ b/data/area/misthalin/lumbridge/swamp/lumbridge_swamp.objs.toml @@ -4,4 +4,19 @@ examine = "Noxious gas hazard! Naked flames may cause an explosion!" [goblin_cave_entrance] id = 5947 -examine = "An entrance to Lumbridge Swamp Caves." \ No newline at end of file +examine = "An entrance to Lumbridge Swamp Caves." + +[lost_city_tree] +clone = "tree" +id = 2409 +examine = "A gnarly swamp tree." + +[tools] +id = 10375 + +[zanaris_door_opened] +id = 10363 + +[zanaris_door_closed] +id = 2406 +examine = "A pretty door." \ No newline at end of file diff --git a/data/area/misthalin/varrock/cooks_guild/cooks_guild.objs.toml b/data/area/misthalin/varrock/cooks_guild/cooks_guild.objs.toml new file mode 100644 index 0000000000..c97cbc96b6 --- /dev/null +++ b/data/area/misthalin/varrock/cooks_guild/cooks_guild.objs.toml @@ -0,0 +1,11 @@ +[flour_bin_cooks_guild] +id = 24070 + +[hopper_cooks_guild] +id = 24071 + +[hopper_controls_cooks_guild] +id = 24072 + +[flour_bin_cooks_guild_empty] +id = 24069 diff --git a/data/area/misthalin/zanaris/zanaris.anims.toml b/data/area/misthalin/zanaris/zanaris.anims.toml index f952e58490..5e041ff89c 100644 --- a/data/area/misthalin/zanaris/zanaris.anims.toml +++ b/data/area/misthalin/zanaris/zanaris.anims.toml @@ -6,3 +6,55 @@ id = 2776 [zygomite_death] id = 2777 + +[teleport_puro_puro] +id = 6601 +ticks = 9 + +[otherworldly_being_attack] +id = 14094 + +[otherworldly_being_defend] +id = 14095 + +[otherworldly_being_death] +id = 14096 + +[pick_zygomite] +id = 3335 + +[zygomite_grow] +id = 2983 + +[spear_trap_side_caught] +id = 3279 + +[spear_trap_caught_right] +id = 3278 + +[spear_trap_caught_left] +id = 3279 + +[spear_trap_jump_left] +id = 3275 + +[spear_trap_jump_right] +id = 3274 + +[side_hurt_right] +id = 2592 + +[side_hurt_left] +id = 2593 + +[spear_trap_release] +id = 459 + +[spear_trap_walk_left] +id = 3277 + +[spear_trap_walk_right] +id = 3276 + +[dive_player] +id = 1115 diff --git a/data/area/misthalin/zanaris/zanaris.combat.toml b/data/area/misthalin/zanaris/zanaris.combat.toml index ca6507a2fe..751f62fcb0 100644 --- a/data/area/misthalin/zanaris/zanaris.combat.toml +++ b/data/area/misthalin/zanaris/zanaris.combat.toml @@ -10,10 +10,47 @@ death_sound = "zygomite_death" range = 1 anim = "zygomite_attack" target_sound = "zygomite_attack" -target_hit = { offense = "magic", defence = "melee", max = 70 } +target_hit = { offense = "magic", defence = "melee", max = 80 } [zygomite.ranged] range = 1 anim = "zygomite_attack" target_sound = "zygomite_attack" +projectile = "zygomite_spores" +target_hit = { offense = "magic", defence = "range", max = 100 } + +[adolescent_zygomite] +attack_speed = 4 +retreat_range = 8 +defend_anim = "zygomite_defend" +defend_sound = "adolescent_zygomite_defend" +death_anim = "zygomite_death" +death_sound = "adolescent_zygomite_death" + +[adolescent_zygomite.melee] +range = 1 +anim = "zygomite_attack" +target_sound = "zygomite_attack" +target_hit = { offense = "magic", defence = "melee", max = 70 } + +[adolescent_zygomite.ranged] +range = 1 +anim = "zygomite_attack" +target_sound = "adolescent_zygomite_attack" +projectile = "adolescent_zygomite_spores" +impact_sound = "adolescent_zygomite_impact" target_hit = { offense = "magic", defence = "range", max = 60 } + +[otherworldly_being] +attack_speed = 4 +retreat_range = 8 +defend_anim = "otherworldly_being_defend" +defend_sound = "otherworldly_being_defend" +death_anim = "otherworldly_being_death" +death_sound = "otherworldly_being_death" + +[otherworldly_being.melee] +range = 1 +anim = "otherworldly_being_attack" +target_sound = "otherworldly_being_attack" +target_hit = { offense = "crush", max = 70 } \ No newline at end of file diff --git a/data/area/misthalin/zanaris/zanaris.gfx.toml b/data/area/misthalin/zanaris/zanaris.gfx.toml new file mode 100644 index 0000000000..00d4d5a04b --- /dev/null +++ b/data/area/misthalin/zanaris/zanaris.gfx.toml @@ -0,0 +1,7 @@ +[teleport_puro_puro] +id = 1118 + +[zygomite_spores] +id = 576 +delay = 35 +time_offset = 5 \ No newline at end of file diff --git a/data/area/misthalin/zanaris/zanaris.npc-spawns.toml b/data/area/misthalin/zanaris/zanaris.npc-spawns.toml index 4ec12f0a14..7d9de488b0 100644 --- a/data/area/misthalin/zanaris/zanaris.npc-spawns.toml +++ b/data/area/misthalin/zanaris/zanaris.npc-spawns.toml @@ -82,11 +82,11 @@ spawns = [ { id = "fungi", x = 2415, y = 4475, members = true }, { id = "fungi", x = 2419, y = 4467, members = true }, { id = "fungi", x = 2419, y = 4474, members = true }, - { id = "fungi_2", x = 2412, y = 4370, members = true }, - { id = "fungi_2", x = 2416, y = 4376, members = true }, - { id = "fungi_2", x = 2419, y = 4374, members = true }, - { id = "fungi_2", x = 2420, y = 4377, members = true }, - { id = "fungi_2", x = 2421, y = 4370, members = true }, + { id = "fungi_large", x = 2412, y = 4370, members = true }, + { id = "fungi_large", x = 2416, y = 4376, members = true }, + { id = "fungi_large", x = 2419, y = 4374, members = true }, + { id = "fungi_large", x = 2420, y = 4377, members = true }, + { id = "fungi_large", x = 2421, y = 4370, members = true }, { id = "fairy_9", x = 2377, y = 4450, members = true }, { id = "fairy_9", x = 2381, y = 4459, members = true }, { id = "fairy_9", x = 2388, y = 4425, members = true }, @@ -114,7 +114,7 @@ spawns = [ { id = "jukat", x = 2484, y = 4452 }, { id = "lunderwin", x = 2475, y = 4465 }, { id = "irksol", x = 2479, y = 4469 }, - { id = "fairy_11", x = 2486, y = 4470 }, + { id = "fairy_attendant", x = 2486, y = 4470 }, { id = "fairy_aeryka", x = 2426, y = 4442 }, { id = "wandering_impling", x = 2424, y = 4436, members = true }, { id = "banker_zanaris_2", x = 2379, y = 4459, members = true }, diff --git a/data/area/misthalin/zanaris/zanaris.npcs.toml b/data/area/misthalin/zanaris/zanaris.npcs.toml index f4bd8d59f8..c60c3dcd5f 100644 --- a/data/area/misthalin/zanaris/zanaris.npcs.toml +++ b/data/area/misthalin/zanaris/zanaris.npcs.toml @@ -46,6 +46,7 @@ examine = "Guardian of the market gate." [sheep_zanaris] id = 3310 +large_head = true [fairy_queen] id = 3314 @@ -68,10 +69,35 @@ examine = "Likes to cook with mushrooms." [fungi] id = 3344 +hitpoints = 650 +att = 65 +str = 65 +def = 65 +mage = 65 +range = 65 +attack_bonus = 30 +slayer_level = 57 +slayer_xp = 65.0 +categories = ["mutated_zygomites"] +respawn_delay = 30 +drop_table = "zygomite_level_74" examine = "A fun guy. No wait, that's awful. Plus it doesn't even make sense." -[fungi_2] +[fungi_large] id = 3345 +hitpoints = 750 +att = 75 +str = 75 +def = 75 +mage = 75 +range = 75 +attack_bonus = 30 +slayer_level = 57 +slayer_xp = 75.0 +categories = ["mutated_zygomites"] +respawn_delay = 30 +drop_table = "zygomite_level_86" +examine = "A fun guy. No wait, that's awful. Plus it doesn't even make sense." [fairy_9] id = 4422 @@ -94,6 +120,7 @@ examine = "Fairy ring maintenance division." [sheep_zanaris_2] id = 5164 +large_head = true [fairy_shopkeeper] id = 534 @@ -119,7 +146,7 @@ id = 566 shop = "irksols_ruby_rings" examine = "Is he invisible or just floating clothing?" -[fairy_11] +[fairy_attendant] id = 567 examine = "Looks strange and mysterious." @@ -137,8 +164,7 @@ hitpoints = 660 att = 56 str = 56 def = 46 -style = "crush" -max_hit_melee = 70 +combat_def = "otherworldly_being" slayer_xp = 66.0 categories = ["otherworldly_beings"] respawn_delay = 20 @@ -219,21 +245,11 @@ id = 11434 [zygomite] id = 3346 -hitpoints = 650 -att = 65 -str = 65 -def = 65 -mage = 65 -range = 65 -combat_def = "zygomite" -attack_bonus = 30 -slayer_xp = 65.0 -categories = ["mutated_zygomites"] -respawn_delay = 30 -drop_table = "zygomite_level_74" +combat_def = "adolescent_zygomite" examine = "A bouncy fungus." -[zygomite_2] +[zygomite_large] id = 3347 clone = "zygomite" -drop_table = "zygomite_level_86" +combat_def = "zygomite" +examine = "A fun guy. No wait, that's awful.." diff --git a/data/area/misthalin/zanaris/zanaris.objs.toml b/data/area/misthalin/zanaris/zanaris.objs.toml index f901a8e6bd..f5f1bb5112 100644 --- a/data/area/misthalin/zanaris/zanaris.objs.toml +++ b/data/area/misthalin/zanaris/zanaris.objs.toml @@ -9,3 +9,55 @@ examine = "I could cook up a storm on this." [furnace_zanaris] id = 52574 examine = "Very hot!" + +[zanaris_crop_circle] +id = 24991 + +[zanaris_al_kharid_fairy_ring] +id = 12003 + +[puro_puro_exit] +id = 25014 + +[magic_door_zanaris_opened] +id = 12046 +examine = "A magic door." + +[zanaris_magic_door_opened] +id = 12046 + +[magic_door_zanaris_closed] +id = 12045 +examine = "A magic door." + +[zanaris_magic_door_closed] +id = 12047 +examine = "A magic door." + +[cow_wheel] +id = 52530 +examine = "She likes the exercise." + +[hopper_controls_zanaris] +id = 52552 + +[hopper_zanaris] +id = 52551 + +[flour_bin_zanaris] +id = 52550 + +[flour_bin_zanaris_empty] +id = 52548 + +[zanaris_mill_ladder] +id = 52546 + +[zanaris_mill_ladder_down] +id = 52547 + +[zanaris_damaged_wall] +id = 12126 + +[zanaris_jutting_wall] +id = 12127 diff --git a/data/area/misthalin/zanaris/zanaris.recipes.toml b/data/area/misthalin/zanaris/zanaris.recipes.toml new file mode 100644 index 0000000000..377830ee3c --- /dev/null +++ b/data/area/misthalin/zanaris/zanaris.recipes.toml @@ -0,0 +1,5 @@ +[fungicide] +remove = ["fungicide", "fungicide_spray_0"] +add = ["fungicide_spray_10"] +message = "You reload the fungicide pump." +sound = "zygo_reload" \ No newline at end of file diff --git a/data/area/misthalin/zanaris/zanaris.sounds.toml b/data/area/misthalin/zanaris/zanaris.sounds.toml index 920cc5944a..e94f75ad63 100644 --- a/data/area/misthalin/zanaris/zanaris.sounds.toml +++ b/data/area/misthalin/zanaris/zanaris.sounds.toml @@ -6,3 +6,51 @@ id = 1969 [zygomite_defend] id = 1970 + +[adolescent_zygomite_death] +id = 1961 + +[adolescent_zygomite_defend] +id = 1962 + +[adolescent_zygomite_attack] +id = 1965 + +[adolescent_zygomite_impact] +id = 1966 + +[zygomite_spray] +id = 1967 + +[teleport_puro_puro] +id = 3727 + +[fill_grain] +id = 2576 + +[zygo_grow] +id = 1963 + +[zygo_reload] +id = 1964 + +[otherworldly_being_defend] +id = 665 + +[otherworldly_being_attack] +id = 662 + +[otherworldly_being_death] +id = 663 + +[spear_trap] +id = 1394 + +[human_hit] +id = 514 + +[spear_trap_jump] +id = 2487 + +[squeeze_out] +id = 2490 \ No newline at end of file diff --git a/data/area/misthalin/zanaris/zanaris.teles.toml b/data/area/misthalin/zanaris/zanaris.teles.toml index cca166218b..a310b35ae3 100644 --- a/data/area/misthalin/zanaris/zanaris.teles.toml +++ b/data/area/misthalin/zanaris/zanaris.teles.toml @@ -4,3 +4,12 @@ option = "Climb-down" tile = { x = 4316, y = 5475, level = 1 } delta = { x = 5, level = -1 } +[zanaris_mill_ladder] +option= "Climb-up" +tile = { x = 2403, y = 4458 } +to = { x = 2403, y = 4457, level = 1} + +[zanaris_mill_ladder_down] +option= "Climb-down" +tile = { x = 2403, y = 4458, level = 1 } +to = { x = 2403, y = 4459 } \ No newline at end of file diff --git a/data/area/misthalin/zanaris/zanaris.varbits.toml b/data/area/misthalin/zanaris/zanaris.varbits.toml new file mode 100644 index 0000000000..3e6b3a8f8d --- /dev/null +++ b/data/area/misthalin/zanaris/zanaris.varbits.toml @@ -0,0 +1,4 @@ +[met_fairy_aeryka] +id = 3728 # osrs +format = "boolean" +persist = true diff --git a/data/area/morytania/canifis/canifis.anims.toml b/data/area/morytania/canifis/canifis.anims.toml new file mode 100644 index 0000000000..4f4a22ee73 --- /dev/null +++ b/data/area/morytania/canifis/canifis.anims.toml @@ -0,0 +1,11 @@ +[ghoul_attack] +id = 442 + +[ghoul_defend] +id = 3238 + +[ghoul_death] +id = 443 + +[werewolf_transform] +id = 6554 \ No newline at end of file diff --git a/data/area/morytania/canifis/canifis.combat.toml b/data/area/morytania/canifis/canifis.combat.toml new file mode 100644 index 0000000000..04b736591c --- /dev/null +++ b/data/area/morytania/canifis/canifis.combat.toml @@ -0,0 +1,33 @@ +[ghoul] +attack_speed = 4 +retreat_range = 8 +defend_anim = "unarmed_block" +defend_sound = "ghoul_defend" +death_anim = "human_death" +death_sound = "ghoul_death" + +[ghoul.melee] +range = 1 +anim = "unarmed_punch" +target_sound = "ghoul_attack" +target_hit = { offense = "crush", max = 50 } + +[canifis_citizen] +clone = "human" +attack_speed = 4 +retreat_range = 8 +defend_anim = "werewolf_transform" + +[werewolf] +attack_speed = 4 +retreat_range = 8 +defend_anim = "werewolf_defend" +defend_sound = "wolves_defend" +death_anim = "werewolf_death" +death_sound = "wolves_death" + +[werewolf.melee] +range = 1 +anim = "werewolf_attack" +target_sound = "wolves_attack" +target_hit = { offense = "slash", max = 80 } diff --git a/data/area/morytania/canifis/canifis.npc-spawns.toml b/data/area/morytania/canifis/canifis.npc-spawns.toml index fccff7d2d1..4b019b137a 100644 --- a/data/area/morytania/canifis/canifis.npc-spawns.toml +++ b/data/area/morytania/canifis/canifis.npc-spawns.toml @@ -32,20 +32,29 @@ spawns = [ { id = "liliya", x = 3479, y = 3498, level = 1 }, { id = "achtryn", x = 3511, y = 3509, members = true }, { id = "mazchna", x = 3512, y = 3509, members = true }, - { id = "ghoul_canifis", x = 3483, y = 3533 }, - { id = "ghoul_canifis", x = 3484, y = 3537 }, - { id = "ghoul_canifis", x = 3484, y = 3545 }, - { id = "ghoul_canifis", x = 3485, y = 3528 }, - { id = "ghoul_canifis", x = 3486, y = 3540 }, - { id = "ghoul_canifis", x = 3488, y = 3531 }, - { id = "ghoul_canifis", x = 3489, y = 3535 }, - { id = "ghoul_canifis", x = 3490, y = 3529 }, - { id = "ghoul_canifis", x = 3490, y = 3549 }, - { id = "ghoul_canifis", x = 3492, y = 3533 }, - { id = "ghoul_canifis", x = 3494, y = 3540 }, - { id = "ghoul_canifis", x = 3495, y = 3537 }, - { id = "ghoul_canifis", x = 3496, y = 3535 }, - { id = "ghoul_canifis", x = 3496, y = 3546 }, - { id = "ghoul_canifis", x = 3497, y = 3530 }, + # 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 index ac81de79da..8beb0d42cd 100644 --- a/data/area/morytania/canifis/canifis.npcs.toml +++ b/data/area/morytania/canifis/canifis.npcs.toml @@ -34,13 +34,14 @@ examine = "She smells unpleasantly of chemicals." [boris] id = 6026 -hitpoints = 600 -att = 10 -str = 10 -def = 10 -style = "crush" -max_hit_melee = 20 +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?" @@ -135,27 +136,21 @@ id = 6044 clone = "boris" examine = "A healthy villager." -[werewolf_boris] +[werewolf] id = 6006 hitpoints = 1000 att = 70 str = 70 def = 70 -style = "slash" -max_hit_melee = 80 +combat_def = "werewolf" slayer_xp = 100.0 categories = ["werewolves"] +drop_table = "canifis_werewolf" examine = "Eek! A werewolf!" [liliya] id = 6045 -hitpoints = 600 -att = 10 -str = 10 -def = 10 -style = "crush" -max_hit_melee = 20 -drop_table = "canifis_werewolf" +clone = "werewolf" examine = "She's looking for flowers." [achtryn] @@ -164,14 +159,13 @@ id = 8481 [mazchna_base] id = 8482 -[ghoul_canifis] +[ghoul] id = 1218 hitpoints = 500 att = 30 str = 40 def = 30 -style = "crush" -max_hit_melee = 50 +combat_def = "ghoul" hunt_mode = "cowardly" slayer_xp = 50.0 categories = ["ghouls"] diff --git a/data/area/morytania/canifis/canifis.objs.toml b/data/area/morytania/canifis/canifis.objs.toml index 16a19984e3..e6158ddcab 100644 --- a/data/area/morytania/canifis/canifis.objs.toml +++ b/data/area/morytania/canifis/canifis.objs.toml @@ -1,3 +1,22 @@ [bank_booth_canifis] id = 24914 examine = "The bank teller will serve you from here." + + +[gate_mort_myre_opened] +id = 2040 + +[gate_mort_myre_closed] +id = 3506 +gate = false +material = "iron" +examine = "A wrought iron gate." + +[gate_mort_myre_swamp_opened] +id = 2042 + +[gate_mort_myre_swamp_closed] +id = 3507 +gate = false +material = "iron" +examine = "A wrought iron gate." \ No newline at end of file diff --git a/data/area/morytania/canifis/canifis.sounds.toml b/data/area/morytania/canifis/canifis.sounds.toml new file mode 100644 index 0000000000..d9ef01c27d --- /dev/null +++ b/data/area/morytania/canifis/canifis.sounds.toml @@ -0,0 +1,11 @@ +[ghoul_attack] +id = 442 + +[ghoul_defend] +id = 444 + +[ghoul_death] +id = 443 + +[lycanthropy] +id = 1736 \ No newline at end of file diff --git a/data/area/morytania/paterdomus/temple/paterdomus_temple.npc-spawns.toml b/data/area/morytania/paterdomus/temple/paterdomus_temple.npc-spawns.toml index e33c904a14..e71d75505c 100644 --- a/data/area/morytania/paterdomus/temple/paterdomus_temple.npc-spawns.toml +++ b/data/area/morytania/paterdomus/temple/paterdomus_temple.npc-spawns.toml @@ -11,7 +11,7 @@ spawns = [ { id = "monk_of_zamorak_paterdomus_temple_3", x = 3413, y = 3488, level = 1, members = true }, { id = "drezel_paterdomus_temple", x = 3414, y = 3486, level = 2, members = true }, { id = "mercenary_adventurer", x = 3439, y = 3486, members = true }, - { id = "ulizius_paterdomus_temple", x = 3444, y = 3459, members = true }, + { id = "ulizius", x = 3444, y = 3459, members = true }, { id = "hiylik_myna", x = 3440, y = 3486 }, { id = "monk_paterdomus_temple", x = 3402, y = 3492, members = true }, { id = "dead_monk_paterdomus_temple", x = 3401, y = 3492, members = true }, diff --git a/data/area/morytania/paterdomus/temple/paterdomus_temple.npcs.toml b/data/area/morytania/paterdomus/temple/paterdomus_temple.npcs.toml index b9ce3b54fb..a2e3f6b594 100644 --- a/data/area/morytania/paterdomus/temple/paterdomus_temple.npcs.toml +++ b/data/area/morytania/paterdomus/temple/paterdomus_temple.npcs.toml @@ -52,7 +52,7 @@ id = 1048 [mercenary_adventurer_normal] id = 7664 -[ulizius_paterdomus_temple] +[ulizius] id = 1054 examine = "A slightly nervous guard." diff --git a/data/entity/npc/animal/cow/cow.npcs.toml b/data/entity/npc/animal/cow/cow.npcs.toml index 5bdd2ad791..7709f022ac 100644 --- a/data/entity/npc/animal/cow/cow.npcs.toml +++ b/data/entity/npc/animal/cow/cow.npcs.toml @@ -35,6 +35,7 @@ examine = "Young, but still beefy." [cow_zanaris] clone = "cow_default" +large_head = true id = 3309 [cow_2] diff --git a/data/entity/obj/all.objs.toml b/data/entity/obj/all.objs.toml index 341a9af9ee..3fbabad3db 100644 --- a/data/entity/obj/all.objs.toml +++ b/data/entity/obj/all.objs.toml @@ -668,7 +668,7 @@ examine = "Controls the dangling claw." id = 15165 examine = "A stand for hats." -[wheat_2] +[wheat_zanaris] clone = "wheat" id = 15506 diff --git a/data/entity/obj/door/door.objs.toml b/data/entity/obj/door/door.objs.toml index 67238e7532..59d85f7f7d 100644 --- a/data/entity/obj/door/door.objs.toml +++ b/data/entity/obj/door/door.objs.toml @@ -590,21 +590,6 @@ id = 15502 id = 2399 examine = "A solid looking door." -[door_49_opened] -id = 10363 - -[door_49_closed] -id = 2406 -examine = "A pretty door." - -[magic_door_opened] -id = 31812 -examine = "A large single door." - -[magic_door_closed] -id = 2407 -examine = "The door is closed." - [large_door_17_opened] id = 2417 examine = "A sturdy wooden door." @@ -1751,14 +1736,6 @@ examine = "The door is open." id = 11993 examine = "The door is closed." -[magic_door_2_opened] -id = 12046 -examine = "A magic door." - -[magic_door_2_closed] -id = 12045 -examine = "A magic door." - [door_273_opened] id = 34233 diff --git a/data/entity/obj/gates.objs.toml b/data/entity/obj/gates.objs.toml index fd82e42a39..c8240b3c33 100644 --- a/data/entity/obj/gates.objs.toml +++ b/data/entity/obj/gates.objs.toml @@ -215,13 +215,6 @@ id = 2040 id = 2673 examine = "A solid looking gate." -[gate_80_opened] -id = 2040 - -[gate_80_closed] -id = 3506 -examine = "A wrought iron gate." - [gate_35_opened] id = 2042 @@ -229,13 +222,6 @@ id = 2042 id = 2041 examine = "It's closed." -[gate_81_opened] -id = 2042 - -[gate_81_closed] -id = 3507 -examine = "A wrought iron gate." - [gate_54_opened] id = 2042 diff --git a/data/entity/obj/picking.enums.toml b/data/entity/obj/picking.enums.toml index bb84700434..fa64fada40 100644 --- a/data/entity/obj/picking.enums.toml +++ b/data/entity/obj/picking.enums.toml @@ -6,6 +6,7 @@ values = { cabbage_draynor_manor = "cabbage", potato = "raw_potato", wheat = "grain", + wheat_zanaris = "grain", cabbage = "cabbage", flax = "flax", onion = "onion", @@ -19,6 +20,7 @@ values = { cabbage_draynor_manor = "You pick a cabbage.", potato = "You pick a potato.", wheat = "You pick some wheat.", + wheat_zanaris = "You pick some wheat.", cabbage = "You pick a cabbage.", flax = "You pick some flax.", onion = "You pick an onion.", @@ -40,6 +42,7 @@ values = { cabbage_draynor_manor = 45, potato = 30, wheat = 30, + wheat_zanaris = 30, cabbage = 45, flax = 5, onion = 30, diff --git a/data/entity/obj/trees.objs.toml b/data/entity/obj/trees.objs.toml index 2f212dc4db..7dbdf6384a 100644 --- a/data/entity/obj/trees.objs.toml +++ b/data/entity/obj/trees.objs.toml @@ -351,11 +351,6 @@ examine = "It's hollow..." id = 2310 examine = "This tree has been cut down." -[tree_13] -clone = "tree" -id = 2409 -examine = "A gnarly swamp tree." - [tree_13_stump] id = 40355 examine = "This tree has been cut down." diff --git a/data/entity/obj/windmill/windmill.varps.toml b/data/entity/obj/windmill/windmill.varps.toml index 25723e3914..a8c9dc2ab5 100644 --- a/data/entity/obj/windmill/windmill.varps.toml +++ b/data/entity/obj/windmill/windmill.varps.toml @@ -1,4 +1,4 @@ [flour_bin] id = 695 persist = true -default = 0 +default = 0 \ No newline at end of file diff --git a/data/minigame/puro_puro/puro_puro.npc-spawns.toml b/data/minigame/puro_puro/puro_puro.npc-spawns.toml index a47ce22ae1..d3c4e6b47a 100644 --- a/data/minigame/puro_puro/puro_puro.npc-spawns.toml +++ b/data/minigame/puro_puro/puro_puro.npc-spawns.toml @@ -60,8 +60,8 @@ spawns = [ { id = "eclectic_impling_puro_puro", x = 2591, y = 4295, members = true }, { id = "eclectic_impling_puro_puro", x = 2591, y = 4340, members = true }, { id = "eclectic_impling_puro_puro", x = 2615, y = 4326, members = true }, - { id = "elnock_inquisitor_puro_puro", x = 2586, y = 4314, members = true }, - { id = "immenizz_puro_puro", x = 2592, y = 4320, members = true }, + { id = "elnock_inquisitor", x = 2586, y = 4314, members = true }, + { id = "immenizz", x = 2592, y = 4320, members = true }, { id = "imp_defender_puro_puro", x = 2560, y = 4288, members = true }, { id = "imp_defender_puro_puro", x = 2560, y = 4351, members = true }, { id = "imp_defender_puro_puro", x = 2563, y = 4292, members = true }, diff --git a/data/minigame/puro_puro/puro_puro.npcs.toml b/data/minigame/puro_puro/puro_puro.npcs.toml index 54e255949c..8e946a2734 100644 --- a/data/minigame/puro_puro/puro_puro.npcs.toml +++ b/data/minigame/puro_puro/puro_puro.npcs.toml @@ -68,11 +68,11 @@ id = 6063 [dragon_impling_puro_puro] id = 6064 -[elnock_inquisitor_puro_puro] +[elnock_inquisitor] id = 6070 examine = "Insurance investigator extraordinaire." -[immenizz_puro_puro] +[immenizz] id = 6071 examine = "An impling." diff --git a/data/quest/members/lost_city/lost_city.npcs.toml b/data/quest/members/lost_city/lost_city.npcs.toml index 1dcac616e0..693ca2db16 100644 --- a/data/quest/members/lost_city/lost_city.npcs.toml +++ b/data/quest/members/lost_city/lost_city.npcs.toml @@ -4,9 +4,13 @@ hitpoints = 850 att = 90 str = 95 def = 80 -style = "crush" -max_hit_melee = 70 +combat_def = "ghost" +max_hit_crush = 70 hunt_mode = "aggressive" -categories = ["ghosts"] -drop_table = "tree_spirit" -examine = "Guardian of the dramen tree." \ No newline at end of file +categories = ["ghosts", "undead"] +examine = "Guardian of the dramen tree." + +[shamus] +id = 654 +wander_range = 4 +examine = "A funny little man who lives in a tree." \ No newline at end of file diff --git a/data/quest/members/lost_city/lost_city.recipes.toml b/data/quest/members/lost_city/lost_city.recipes.toml new file mode 100644 index 0000000000..44a6118289 --- /dev/null +++ b/data/quest/members/lost_city/lost_city.recipes.toml @@ -0,0 +1,7 @@ +[dramen_staff] +skill = "crafting" +level = 31 +requires = ["knife"] +remove = ["dramen_branch"] +add = ["dramen_staff"] +message = "You carve a branch into a staff." diff --git a/data/quest/members/lost_city/lost_city.varps.toml b/data/quest/members/lost_city/lost_city.varps.toml new file mode 100644 index 0000000000..379e194e04 --- /dev/null +++ b/data/quest/members/lost_city/lost_city.varps.toml @@ -0,0 +1,6 @@ +[lost_city] +id = 147 +persist = true +format = "map" +default = "unstarted" +values = { unstarted = 0, started = 1, find_staff = 2, tree_spirit = 3, spirit_killed = 4, enter_shed = 5, completed = 6 } diff --git a/data/skill/crafting/tanning.enums.toml b/data/skill/crafting/tanning.enums.toml index 60a20db7f5..228dc08bf4 100644 --- a/data/skill/crafting/tanning.enums.toml +++ b/data/skill/crafting/tanning.enums.toml @@ -1,4 +1,3 @@ - [tanning_product] keyType = "item" valueType = "string" @@ -13,7 +12,7 @@ values = { green_dragonhide = "green_dragon_leather", } -[tanning_price] +[ellis_tanning_price] keyType = "item" valueType = "int" defaultInt = 0 @@ -27,6 +26,20 @@ values = { green_dragonhide = 20, } +[sbott_tanning_price] +keyType = "item" +valueType = "int" +defaultInt = 0 +values = { + snake_hide = 25, + snake_hide_swamp = 25, + cowhide = 2, + black_dragonhide = 45, + red_dragonhide = 45, + blue_dragonhide = 45, + green_dragonhide = 45, +} + [tanning_secondary_product] keyType = "item" valueType = "string" @@ -35,10 +48,18 @@ values = { cowhide = "hard_leather", } -[tanning_secondary_price] +[ellis_tanning_secondary_price] keyType = "item" valueType = "int" defaultInt = 0 values = { cowhide = 3, } + +[sbott_tanning_secondary_price] +keyType = "item" +valueType = "int" +defaultInt = 0 +values = { + cowhide = 5, +} diff --git a/data/skill/magic/teleport/teleport.anims.toml b/data/skill/magic/teleport/teleport.anims.toml index eb4f3912c2..f8f3bc0209 100644 --- a/data/skill/magic/teleport/teleport.anims.toml +++ b/data/skill/magic/teleport/teleport.anims.toml @@ -117,6 +117,14 @@ id = 2140 id = 9603 ticks = 4 +[teleport_fairy] +id = 3265 +ticks = 3 + +[teleport_land_fairy] +id = 3266 +ticks = 2 + [teleport_pharaohs_sceptre] id = 9596 ticks = 3 diff --git a/data/skill/magic/teleport/teleport.gfx.toml b/data/skill/magic/teleport/teleport.gfx.toml index 56ed9e9ba0..18fc7bc557 100644 --- a/data/skill/magic/teleport/teleport.gfx.toml +++ b/data/skill/magic/teleport/teleport.gfx.toml @@ -76,6 +76,12 @@ id = 1688 [teleport_jewellery] id = 1684 +[teleport_fairy] +id = 2670 + +[teleport_land_fairy] +id = 2671 + [teleport_pharaohs_sceptre] id = 715 diff --git a/data/skill/magic/teleport/teleport.sounds.toml b/data/skill/magic/teleport/teleport.sounds.toml index 3708d92544..dfa11da126 100644 --- a/data/skill/magic/teleport/teleport.sounds.toml +++ b/data/skill/magic/teleport/teleport.sounds.toml @@ -1,8 +1,35 @@ [teleport] id = 200 +[teleport_ectophial] +id = 200 + +[teleport_jewellery] +id = 200 + +[teleport_skull_sceptre] +id = 200 + +[teleport_wilderness] +id = 200 + +[teleport_fairy] +id = 1098 + [teleport_land] id = 201 +[teleport_land_ectophial] +id = 201 + +[teleport_land_jewellery] +id = 201 + +[teleport_land_skull_sceptre] +id = 201 + +[teleport_land_wilderness] +id = 201 + [teleport_tablet] id = 965 diff --git a/data/skill/slayer/chaeldar.enums.toml b/data/skill/slayer/chaeldar.enums.toml index 06f4330dba..bb62797999 100644 --- a/data/skill/slayer/chaeldar.enums.toml +++ b/data/skill/slayer/chaeldar.enums.toml @@ -66,7 +66,7 @@ values = { kalphite_worker = 11, kurask = 12, lesser_demon = 9, -# zygomite = 7, + zygomite = 7, nechryael = 12, shadow_warrior = 8, skeletal_wyvern = 7, diff --git a/data/skill/slayer/mazchna.enums.toml b/data/skill/slayer/mazchna.enums.toml index 2c4b2e88f3..615daca3fe 100644 --- a/data/skill/slayer/mazchna.enums.toml +++ b/data/skill/slayer/mazchna.enums.toml @@ -15,7 +15,7 @@ values = { guard_dog = "30-50", flesh_crawler = "15-25", ghost = "30-50", - ghoul_canifis = "10-20", + ghoul = "10-20", hill_giant = "30-50", hobgoblin_unarmed = "30-50", ice_warrior = "40-50", @@ -51,7 +51,7 @@ values = { guard_dog = 7, flesh_crawler = 7, ghost = 7, - ghoul_canifis = 7, + ghoul = 7, hill_giant = 7, hobgoblin_unarmed = 7, ice_warrior = 7, diff --git a/data/skill/slayer/slayer.enums.toml b/data/skill/slayer/slayer.enums.toml index 9866a8308b..c35042ea1e 100644 --- a/data/skill/slayer/slayer.enums.toml +++ b/data/skill/slayer/slayer.enums.toml @@ -45,7 +45,7 @@ values = { frost_dragon = "frost_dragons", ghost = "ghosts", gargoyle = "gargoyles", - ghoul_canifis = "ghouls", + ghoul = "ghouls", goblin_mohawk_blue = "goblin", gorak = "gorak", greater_demon = "greater_demons", @@ -99,7 +99,7 @@ values = { tzhaar_mej = "tzhaar", vampyre_juvenile = "vampyres", waterfiend = "waterfiends", - werewolf_boris = "werewolves", + werewolf = "werewolves", wolf = "wolves", wall_beast = "wall_beasts", warped_terrorbird = "warped_creatures", @@ -153,7 +153,7 @@ values = { frost_dragon = "Frost dragons reside beneath the island of Grimstone. They have thick scales and powerful icy breath. Dragonfire protection is a must.", ghost = "Ghosts are undead so magic is your best bet against them, there is even a spell specially for fighting the undead.", gargoyle = "Gargoyles are winged creatures of stone. You'll need to fight them to near death before breaking them apart with a Rock Hammer.", - ghoul_canifis = "Ghouls aren't undead but they are stronger and tougher than they look. However they're also very cowardly and will run if they're losing a fight.", + ghoul = "Ghouls aren't undead but they are stronger and tougher than they look. However they're also very cowardly and will run if they're losing a fight.", goblin_mohawk_blue = "Goblins are mostly just annoying, but they can be vicious. Watch out for the spears they sometimes carry.", gorak = "Goraks are extremely aggressive creatures. They have been imprisoned on an alternative plane, which is only accesible by using the fairyrings. Be extremely careful, their touch drains health as well as skills! ", greater_demon = "Demons are particularly weak to water spells. Though Greater Demons are not the strongest type of demon they are still dangerous.", @@ -207,7 +207,7 @@ values = { tzhaar_mej = "Tzhaar reside in their cave under the Karamja volcano. Beware as they can call to their fellows for aid.", vampyre_juvenile = "Vampyres are extremely powerful beings. An underground group known as the Myreque have been searching for tools to defeat them, which they may be willing to share if you can find them.", waterfiend = "Waterfiends are creatures of water, which live under the Baxtorian Lake. Their watery form is well defended against slashing and piercing weapons, so use something blunt.", - werewolf_boris = "Werewolves are feral creatures, they are strong and tough with sharp claws and teeth.", + werewolf = "Werewolves are feral creatures, they are strong and tough with sharp claws and teeth.", wolf = "Wolves are pack animals, so you'll always find them in groups. Watch out for their bite, it can be nasty.", wall_beast = "Wall Beasts are really much larger creatures but you'll only see their arms. You'll want something sharp on your head to stop them grabbing you.", warped_terrorbird = "Warped Creatures can supposedly be found within a mysterious dungeon on the eastern edge of the Poison Waste. Be aware that to defeat them, you'll need to purify them in some way.", @@ -235,7 +235,7 @@ values = { flesh_crawler = 15, guard_dog = 15, ghost = 13, - ghoul_canifis = 25, + ghoul = 25, hill_giant = 25, hobgoblin_unarmed = 20, icefiend = 20, @@ -275,7 +275,7 @@ values = { banshee = "priest_in_peril", # TODO aviansie_god_wars & spiritual_mage_zamorak req god wars dungeon knight varbit crawling_hand = "priest_in_peril", - ghoul_canifis = "priest_in_peril", + ghoul = "priest_in_peril", gargoyle = "priest_in_peril", killerwatt = "ernest_the_chicken", mogre = "skippy_and_the_mogres", diff --git a/data/skill/slayer/vannaka.enums.toml b/data/skill/slayer/vannaka.enums.toml index a2684929a3..ef4d71a7fd 100644 --- a/data/skill/slayer/vannaka.enums.toml +++ b/data/skill/slayer/vannaka.enums.toml @@ -18,7 +18,7 @@ values = { fever_spider = "30-90", fire_giant = "40-90", gargoyle = "40-90", - ghoul_canifis = "10-40", + ghoul = "10-40", harpie_bug_swarm = "40-90", hellhound = "30-60", hill_giant = "40-90", @@ -44,7 +44,7 @@ values = { ice_troll_troll_country = "40-90", turoth = "30-90", vampyre_juvenile = "10-20", - werewolf_boris = "30-60", + werewolf = "30-60", } [vannaka_task_weight] @@ -67,7 +67,7 @@ values = { # fever_spider = 7, fire_giant = 7, gargoyle = 5, -# ghoul_canifis = 7, +# ghoul = 7, # harpie_bug_swarm = 8, hellhound = 7, hill_giant = 7, @@ -84,7 +84,7 @@ values = { moss_giant = 7, nechryael = 5, ogre_chieftain = 7, -# otherworldly_being = 8, + otherworldly_being = 8, pyrefiend_large = 8, # sea_snake_young = 6, # asyn_shade = 8, @@ -93,5 +93,5 @@ values = { ice_troll_troll_country = 7, turoth = 8, # vampyre_juvenile = 7, -# werewolf_boris = 7, + werewolf = 7, } diff --git a/data/skill/woodcutting/woodcutting.enums.toml b/data/skill/woodcutting/woodcutting.enums.toml index b8771d948b..4b3e3e3035 100644 --- a/data/skill/woodcutting/woodcutting.enums.toml +++ b/data/skill/woodcutting/woodcutting.enums.toml @@ -95,7 +95,6 @@ values = { tree_10 = "logs", tree_11 = "logs", tree_12 = "logs", - tree_13 = "logs", tree_14 = "logs", tree_15 = "logs", tree_18 = "logs", diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/client/ui/chat/Text.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/client/ui/chat/Text.kt index 1b99417105..57b14a7d5d 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/client/ui/chat/Text.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/client/ui/chat/Text.kt @@ -162,10 +162,9 @@ fun String.splitSafe(delimiter: Char): List { val parts = mutableListOf() val sb = StringBuilder() while (i < length) { - val c = this[i++] - when { - c == '"' -> inQuotes = !inQuotes - c == delimiter && !inQuotes -> { + when (val c = this[i++]) { + '"' -> inQuotes = !inQuotes + delimiter if !inQuotes -> { parts.add(sb.toString()) sb.clear() } diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/player/Teleport.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/player/Teleport.kt index 48b81ee9f6..69d7940ecb 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/player/Teleport.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/player/Teleport.kt @@ -88,14 +88,14 @@ interface Teleport { } player.steps.clear() if (sound) { - player.sound("teleport") + player.sound("teleport_${type}") } player.gfx("teleport_$type") player.animDelay("teleport_$type") player.tele(tile) player.delay(1) if (sound) { - player.sound("teleport_land") + player.sound("teleport_land_${type}") } player.gfx("teleport_land_$type") player.animDelay("teleport_land_$type") diff --git a/game/src/main/kotlin/content/area/asgarnia/entrana/CaveMonk.kt b/game/src/main/kotlin/content/area/asgarnia/entrana/CaveMonk.kt new file mode 100644 index 0000000000..ee083e9392 --- /dev/null +++ b/game/src/main/kotlin/content/area/asgarnia/entrana/CaveMonk.kt @@ -0,0 +1,40 @@ +package content.area.asgarnia.entrana + +import content.entity.player.dialogue.Confused +import content.entity.player.dialogue.Neutral +import content.entity.player.dialogue.Sad +import content.entity.player.dialogue.type.choice +import content.entity.player.dialogue.type.npc +import world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.client.message +import world.gregs.voidps.engine.client.ui.dialogue.talkWith +import world.gregs.voidps.engine.entity.character.move.tele +import world.gregs.voidps.engine.entity.character.npc.NPCs +import world.gregs.voidps.engine.entity.character.player.Player + +class CaveMonk : Script { + init { + npcOperate("Talk-to", "cave_monk") { (target) -> + careful() + } + + objectOperate("Climb-down", "entrana_ladder") { + val monk = NPCs.find(tile.regionLevel, "cave_monk") + talkWith(monk) + careful() + } + } + + private suspend fun Player.careful() { + npc("Be careful going in there! You are unarmed, and there is much evilness lurking down there! The evilness seems to block off our contact with our gods,") + npc("so our prayers seem to have less effect down there. Oh, also, you won't be able to come back this way - This ladder only goes one way!") + npc("The only exit from the caves below is a portal which leads only to the deepest wilderness!") + choice { + option("I don't think I'm strong enough to enter then.") + option("Well that is a risk I will have to take.") { + message("You climb down the ladder.") + tele(2822, 9774) + } + } + } +} diff --git a/game/src/main/kotlin/content/area/asgarnia/entrana/DramenTree.kt b/game/src/main/kotlin/content/area/asgarnia/entrana/DramenTree.kt new file mode 100644 index 0000000000..199e6f5eb2 --- /dev/null +++ b/game/src/main/kotlin/content/area/asgarnia/entrana/DramenTree.kt @@ -0,0 +1,57 @@ +package content.area.asgarnia.entrana + +import content.entity.combat.killer +import content.quest.quest +import content.skill.woodcutting.Hatchet +import world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.client.instruction.handle.interactPlayer +import world.gregs.voidps.engine.client.message +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.skill.Skill +import world.gregs.voidps.engine.entity.character.player.skill.level.Level.has +import world.gregs.voidps.engine.inv.add +import world.gregs.voidps.engine.inv.inventory +import world.gregs.voidps.type.Tile + +class DramenTree : Script { + init { + objectOperate("Chop down", "dramen_tree") { (target) -> + val hatchet = Hatchet.best(this) + if (hatchet == null) { + message("You do not have a hatchet which you have the woodcutting level to use.") + return@objectOperate + } + if (!has(Skill.Woodcutting, 36, true)) { + return@objectOperate + } + when (quest("lost_city")) { + "unstarted", "started" -> { + message("The tree seems to have a ominous aura to it. You do not feel like chopping it down.") + return@objectOperate + } + "find_staff", "tree_spirit" -> { + set("lost_city", "tree_spirit") + val spirit = NPCs.findOrNull(tile.regionLevel, "tree_spirit_lost_city") + ?: NPCs.add("tree_spirit_lost_city", Tile(2859, 9734)) + spirit.say("You must defeat me before touching the tree!") + spirit.interactPlayer(this, "Attack") + return@objectOperate + } + else -> { + anim("${hatchet.id}_chop") + delay(4) + inventory.add("dramen_branch") + message("You cut a branch from the Dramen tree.") + } + } + } + + npcDeath("tree_spirit_lost_city") { + val killer = killer as? Player ?: return@npcDeath + if (killer.quest("lost_city") == "tree_spirit") { + killer["lost_city"] = "spirit_killed" + } + } + } +} diff --git a/game/src/main/kotlin/content/area/asgarnia/entrana/Entrana.kt b/game/src/main/kotlin/content/area/asgarnia/entrana/Entrana.kt index d01104034e..12daa1da42 100644 --- a/game/src/main/kotlin/content/area/asgarnia/entrana/Entrana.kt +++ b/game/src/main/kotlin/content/area/asgarnia/entrana/Entrana.kt @@ -44,5 +44,11 @@ class Entrana : Script { anim("take") message("You steal a candle.", ChatType.Filter) } + + objectOperate("Open", "magic_door_closed") { + message("You feel the world around you dissolve...") + tele(3250, 3772) + sound("door_open") + } } } diff --git a/game/src/main/kotlin/content/area/asgarnia/entrana/EntranaMonk.kt b/game/src/main/kotlin/content/area/asgarnia/entrana/EntranaMonk.kt new file mode 100644 index 0000000000..41dfb0d322 --- /dev/null +++ b/game/src/main/kotlin/content/area/asgarnia/entrana/EntranaMonk.kt @@ -0,0 +1,32 @@ +package content.area.asgarnia.entrana + +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.message +import world.gregs.voidps.engine.entity.character.areaSound +import world.gregs.voidps.engine.entity.character.player.skill.Skill + +class EntranaMonk : Script { + init { + npcOperate("Talk-to", "monk_entrana") { (target) -> + npc("Greetings traveller.") + choice { + option("Can you heal me? I'm injured.") { + npc("Ok.") + message("You feel a little better.") + gfx("heal") + areaSound("heal", tile, radius = 10) + levels.restore(Skill.Constitution, -levels.getOffset(Skill.Constitution)) + } + option("Isn't this place built a bit out the way?") { + player("Isn't this place built a bit out of the way?") + npc("We like it that way actually! We get disturbed less. We still get rather a large amount of travellers looking for sanctuary and healing here as it is!") + } + } + } + } +} diff --git a/game/src/main/kotlin/content/area/asgarnia/entrana/Frincos.kt b/game/src/main/kotlin/content/area/asgarnia/entrana/Frincos.kt new file mode 100644 index 0000000000..5ed4df2419 --- /dev/null +++ b/game/src/main/kotlin/content/area/asgarnia/entrana/Frincos.kt @@ -0,0 +1,22 @@ +package content.area.asgarnia.entrana + +import content.entity.npc.shop.openShop +import content.entity.player.dialogue.Neutral +import content.entity.player.dialogue.type.choice +import content.entity.player.dialogue.type.npc +import world.gregs.voidps.engine.Script + +class Frincos : Script { + init { + npcOperate("Talk-to", "mazion") { (target) -> + npc("Hello, how can I help you?") + choice { + option("What are you selling?") { + openShop(target.def["shop"]) + } + option("You can't; I'm beyond help.") + option("I'm okay, thank you.") + } + } + } +} diff --git a/game/src/main/kotlin/content/area/asgarnia/entrana/FritzTheGlassblower.kt b/game/src/main/kotlin/content/area/asgarnia/entrana/FritzTheGlassblower.kt new file mode 100644 index 0000000000..fff64934d0 --- /dev/null +++ b/game/src/main/kotlin/content/area/asgarnia/entrana/FritzTheGlassblower.kt @@ -0,0 +1,40 @@ +package content.area.asgarnia.entrana + +import content.entity.player.dialogue.Happy +import content.entity.player.dialogue.Neutral +import content.entity.player.dialogue.Quiz +import content.entity.player.dialogue.Sad +import content.entity.player.dialogue.type.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 + +class FritzTheGlassblower : Script { + init { + npcOperate("Talk-to", "fritz_the_glassblower") { (target) -> + npc("Hello adventurer, welcome to the Entrana furnace.") + npc("Would you like me to explain my craft to you?") + choice { + option("Yes please. I'd be fascinated to hear what you do.") { + npc("I'm extremely pleased to hear that! I've always wanted an apprentice. Let me talk you through the secrets of glassblowing.") + npc("Glass is made from soda ash and silica. We get our soda ash by collecting seaweed from the rocks - the prevailing currents make the north-west corner of the island the best place to find it, it can also be found in") + npc("your nets sometimes when you're fishing, on Karamja island or at the Piscatoris Fishing Colony in the nets there. To turn seaweed into soda ash, all you need to do is burn it on a fire. Feel free to use the range in") + npc("my house for that; it's the one directly west of here. Next we collect sand from the sandpit that you'll also find just west of here, there are others located in Yanille and Shilo Village.") + npc("You'll need a bucket to carry it in. Tell you what, you can have this old one of mine.") + addOrDrop("bucket") + npc("Bring the sand and the soda ash back here and melt them together in the furnace, and there you have it - molten glass!") + npc("There are many things you can use the molten glass for once you have made it. Depending on how talented you are, you could try turning it into something, like a fishbowl, for example. If you'd like to try your hand at") + npc("the fine art of glassblowing you can use my spare glassblowing pipe. I think I left it on the chest of drawers in my house this morning.") + npc("Alternatively I am always happy to buy the molten glass from you, saves me running about making it for myself.") + player("That sounds good. How much will you pay me?") + npc("Tell you what, because you've been interested in my art, I'll pay you the premium price of 20 gold pieces for each piece of molten glass you bring me.") + } + option("No thanks, I doubt I'll ever turn my hand to glassblowing.") { + player("No thanks, I doubt I'll ever turn my hand to glassblowing.") + npc("Ok, suit yourself. Nobody seems to be interested in the skilled crafts these days.") + } + } + } + } +} diff --git a/game/src/main/kotlin/content/area/asgarnia/entrana/Mazion.kt b/game/src/main/kotlin/content/area/asgarnia/entrana/Mazion.kt new file mode 100644 index 0000000000..6c883b12a8 --- /dev/null +++ b/game/src/main/kotlin/content/area/asgarnia/entrana/Mazion.kt @@ -0,0 +1,20 @@ +package content.area.asgarnia.entrana + +import content.entity.player.dialogue.Happy +import content.entity.player.dialogue.Sad +import content.entity.player.dialogue.type.npc +import world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.entity.character.player.name +import world.gregs.voidps.type.random + +class Mazion : Script { + init { + npcOperate("Talk-to", "mazion") { + when (random.nextInt(3)) { + 0 -> npc("Hello $name, fine day today!") + 1 -> npc("Nice weather we're having today!") + else -> npc("Please leave me alone, a parrot stole my banana.") + } + } + } +} diff --git a/game/src/main/kotlin/content/area/kharidian_desert/al_kharid/Ellis.kt b/game/src/main/kotlin/content/area/kharidian_desert/al_kharid/Ellis.kt index f14962f046..2c9a8f69d6 100644 --- a/game/src/main/kotlin/content/area/kharidian_desert/al_kharid/Ellis.kt +++ b/game/src/main/kotlin/content/area/kharidian_desert/al_kharid/Ellis.kt @@ -89,9 +89,10 @@ class Ellis : Script { player.message("You don't have any ${item.toLowerSpaceCase()} to tan.") return } + val tanner = player["tanner", "ellis"] val primary = if (type.endsWith("_1")) "_secondary" else "" val leather = EnumDefinitions.string("tanning${primary}_product", item) - val cost = EnumDefinitions.int("tanning${primary}_price", item) + val cost = EnumDefinitions.int("${tanner}_tanning${primary}_price", item) var tanned = 0 var noHides = false for (i in 0 until amount) { diff --git a/game/src/main/kotlin/content/area/misthalin/edgeville/monastery/MonasteryMonk.kt b/game/src/main/kotlin/content/area/misthalin/edgeville/monastery/MonasteryMonk.kt index de47f23a9d..a8597eb952 100644 --- a/game/src/main/kotlin/content/area/misthalin/edgeville/monastery/MonasteryMonk.kt +++ b/game/src/main/kotlin/content/area/misthalin/edgeville/monastery/MonasteryMonk.kt @@ -19,7 +19,7 @@ class MonasteryMonk : Script { message("You feel a little better.") gfx("heal") areaSound("heal", tile, radius = 10) - levels.restore(Skill.Constitution, levels.getOffset(Skill.Constitution)) + levels.restore(Skill.Constitution, -levels.getOffset(Skill.Constitution)) } option("Isn't this place built a bit out of the way?") { npc("We like it that way actually! We get disturbed less. We still get rather a large amount of travellers looking for sanctuary and healing here as it is!") diff --git a/game/src/main/kotlin/content/area/misthalin/lumbridge/swamp/LostCityAdventurers.kt b/game/src/main/kotlin/content/area/misthalin/lumbridge/swamp/LostCityAdventurers.kt index 9077ecf0e9..8f79a862e3 100644 --- a/game/src/main/kotlin/content/area/misthalin/lumbridge/swamp/LostCityAdventurers.kt +++ b/game/src/main/kotlin/content/area/misthalin/lumbridge/swamp/LostCityAdventurers.kt @@ -5,52 +5,90 @@ import content.entity.player.dialogue.Confused import content.entity.player.dialogue.Happy import content.entity.player.dialogue.Neutral import content.entity.player.dialogue.Quiz +import content.entity.player.dialogue.Sad import content.entity.player.dialogue.type.ChoiceOption import content.entity.player.dialogue.type.choice import content.entity.player.dialogue.type.npc import content.entity.player.dialogue.type.player +import content.quest.quest import world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.entity.World import world.gregs.voidps.engine.entity.character.player.Player class LostCityAdventurers : Script { init { npcOperate("Talk-to", "archer_lumbridge") { - player("Why are you guys hanging around here?") - npc("(ahem)...'Guys'?") - player("Uh... yeah, sorry about that. Why are you all standing around out here?") - npc("Well, that's really none of your business.") + when (quest("lost_city")) { + "unstarted", "started" -> { + player("Why are you guys hanging around here?") + npc("(ahem)...'Guys'?") + player("Uh... yeah, sorry about that. Why are you all standing around out here?") + npc("Well, that's really none of your business.") + } + else -> { + player("So you didn't find the entrance to Zanaris yet, huh?") + npc("Don't tell me a novice like YOU has found it!") + player("Yep. Found it REALLY easily too.") + npc("...I cannot believe that someone like YOU could find the portal where experienced adventurers such as ourselves could not.") + player("Believe what you want. Enjoy your little camp fire.") + } + } } npcOperate("Talk-to", "wizard_lumbridge") { - player("Why are all of you standing around here?") - npc("Hahaha you dare talk to a mighty wizard such as myself? I bet you can't even cast windstrike yet amateur!") - player("...You're an idiot.") + when (quest("lost_city")) { + "unstarted", "started" -> { + player("Why are all of you standing around here?") + npc("Hahaha you dare talk to a mighty wizard such as myself? I bet you can't even cast windstrike yet amateur!") + player("...You're an idiot.") + } + else -> { + npc("Hahaha you're such an amateur!") + npc("Go away and play with some cabbage amateur!") + player("...right.") + } + } } npcOperate("Talk-to", "monk_lumbridge") { - player("Why are all of you standing around here?") - npc("None of your business. Get lost.") + when (quest("lost_city")) { + "unstarted", "started" -> { + player("Why are all of you standing around here?") + npc("None of your business. Get lost.") + } + else -> npc("I already told you. I'm not talking to you anymore.") + } } npcOperate("Talk-to", "warrior_lumbridge") { - npc("Hello there traveller.") - choice { - option("What are you camped out here for?") { - player("What are you camped here for?") - lookingForZanaris() - } - option("Do you know any good adventures I can go on?") { - npc("Well we're on an adventure right now. Mind you, this is OUR adventure and we don't want to share it - find your own!") + when (quest("lost_city")) { + "unstarted" -> { + npc("Hello there traveller.") choice { - pleaseTell() - option("I don't think you've found a good adventure at all!") { - npc("Hah! Adventurers of our calibre don't just hang around in forests for fun, whelp!") - player("Oh really?") + option("What are you camped out here for?") { player("What are you camped here for?") lookingForZanaris() } + option("Do you know any good adventures I can go on?") { + npc("Well we're on an adventure right now. Mind you, this is OUR adventure and we don't want to share it - find your own!") + choice { + pleaseTell() + option("I don't think you've found a good adventure at all!") { + npc("Hah! Adventurers of our calibre don't just hang around in forests for fun, whelp!") + player("Oh really?") + player("What are you camped here for?") + lookingForZanaris() + } + } + } } } + "started" -> straight() + "find_staff" -> { + player("Have you found anything yet?") + npc("We're still searching for Zanaris...GAH! I mean we're not doing anything here at all.") + npc("I haven't found it yet either.") + } } } } @@ -81,12 +119,29 @@ class LostCityAdventurers : Script { pleaseTell() option("Looks like you don't know either.") { player("Well, it looks to me like YOU don't know EITHER seeing as you're all just sat around here.") - npc("Of course we know! We will find Zanaris, just you wait. Now go away!") + if (!World.members) { + npc("Of course we know! We will find Zanaris, just you wait. Now go away!") + return@option + } + set("lost_city", "started") + npc("Of course we know! We just haven't found which tree the stupid leprechaun's hiding in yet!") + player("Leprechaun?") + npc("GAH! I didn't mean to tell you that! Look, just forget I said anything okay?") + player("So a leprechaun knows where Zanaris is eh?") + npc("Ye.. uh, no. No, not at all. And even if he did - which he doesn't - he DEFINITELY ISN'T hiding in some tree around here. Nope, definitely not. Honestly.") + player("Thanks for the help!") + npc("Help? What help? I didn't help! Please don't say I did, I'll get in trouble!") + straight() } } } } + private suspend fun Player.straight() { + player("So let me get this straight: I need to search the trees around here for a leprechaun; and then when I find him, he will tell me where this 'Zanaris' is?") + npc("What? How did you know that? Uh... I mean, no, no you're very wrong. Very wrong, and not right at all, and I definitely didn't tell you about that at all.") + } + private fun ChoiceOption.noSuchThing() { option("There's no such thing.") { player("There's no such thing!") diff --git a/game/src/main/kotlin/content/area/misthalin/lumbridge/swamp/LumbridgeSwamp.kt b/game/src/main/kotlin/content/area/misthalin/lumbridge/swamp/LumbridgeSwamp.kt index d2fdf99093..14e04cbed8 100644 --- a/game/src/main/kotlin/content/area/misthalin/lumbridge/swamp/LumbridgeSwamp.kt +++ b/game/src/main/kotlin/content/area/misthalin/lumbridge/swamp/LumbridgeSwamp.kt @@ -1,26 +1,16 @@ package content.area.misthalin.lumbridge.swamp -import content.entity.combat.hit.hit import content.entity.player.bank.ownsItem -import content.entity.player.dialogue.type.choice -import content.entity.player.dialogue.type.item import content.entity.player.dialogue.type.statement -import content.entity.player.dialogue.type.warning import content.quest.messageScroll import content.quest.quest -import content.skill.firemaking.Light import world.gregs.voidps.engine.Script 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.open import world.gregs.voidps.engine.entity.World -import world.gregs.voidps.engine.entity.character.move.tele import world.gregs.voidps.engine.entity.character.npc.NPCs -import world.gregs.voidps.engine.entity.character.sound 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.softQueue import world.gregs.voidps.engine.timer.toTicks import world.gregs.voidps.type.Direction @@ -103,67 +93,5 @@ class LumbridgeSwamp : Script { ), ) } - - objectOperate("Climb-down", "goblin_cave_entrance") { - if (get("cave_goblin_rope", false)) { - val light = Light.hasLightSource(this) - if (!light && !warning("lumbridge_cellar")) { - message("You should find a light source and a tinderbox before going down there.") - return@objectOperate - } - if (!inventory.contains("tinderbox") && !warning("lumbridge_swamp_cave_rope")) { - message("You should find a tinderbox before going down there.") - return@objectOperate - } - anim("climb_down") - delay(2) - tele(3167, 9573) - } else if (inventory.contains("rope")) { - choice("Attach a rope to the top of the hole?") { - option("Yes.") { - if (!inventory.remove("rope")) { - return@option - } - anim("climb_down") - set("cave_goblin_rope", true) - sound("attach_rope") - item("rope", 400, "You tie the rope to the top of the hole and throw it down.") - } - option("No.") - } - } else { - statement("There is a sheer drop below the hole. You will need a rope.") - } - } - - entered("lumbridge_swamp_caves") { - if (Light.hasLightSource(this)) { - open("level_one_darkness") - } else { - open("level_three_darkness") - timers.start("insect_swarm") - } - } - - timerStart("insect_swarm") { - message("Tiny biting insects swarm all over you!") - sound("insect_swarm") - 10 - } - - timerTick("insect_swarm") { - hit(this, damage = 10) - sound("insect_bites") - 1 - } - - interfaceClosed("level_three_darkness") { - timers.stop("insect_swarm") - } - - exited("lumbridge_swamp_caves") { - close("level_one_darkness") - close("level_three_darkness") - } } } diff --git a/game/src/main/kotlin/content/area/misthalin/lumbridge/swamp/LumbridgeSwampCave.kt b/game/src/main/kotlin/content/area/misthalin/lumbridge/swamp/LumbridgeSwampCave.kt new file mode 100644 index 0000000000..82091ec0b1 --- /dev/null +++ b/game/src/main/kotlin/content/area/misthalin/lumbridge/swamp/LumbridgeSwampCave.kt @@ -0,0 +1,82 @@ +package content.area.misthalin.lumbridge.swamp + +import content.entity.combat.hit.hit +import content.entity.player.dialogue.type.choice +import content.entity.player.dialogue.type.item +import content.entity.player.dialogue.type.statement +import content.entity.player.dialogue.type.warning +import content.skill.firemaking.Light +import world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.client.message +import world.gregs.voidps.engine.client.ui.close +import world.gregs.voidps.engine.client.ui.open +import world.gregs.voidps.engine.entity.character.move.tele +import world.gregs.voidps.engine.entity.character.sound +import world.gregs.voidps.engine.inv.inventory +import world.gregs.voidps.engine.inv.remove + +class LumbridgeSwampCave : Script { + init { + objectOperate("Climb-down", "goblin_cave_entrance") { + if (get("cave_goblin_rope", false)) { + val light = Light.hasLightSource(this) + if (!light && !warning("lumbridge_cellar")) { + message("You should find a light source and a tinderbox before going down there.") + return@objectOperate + } + if (!inventory.contains("tinderbox") && !warning("lumbridge_swamp_cave_rope")) { + message("You should find a tinderbox before going down there.") + return@objectOperate + } + anim("climb_down") + delay(2) + tele(3167, 9573) + } else if (inventory.contains("rope")) { + choice("Attach a rope to the top of the hole?") { + option("Yes.") { + if (!inventory.remove("rope")) { + return@option + } + anim("climb_down") + set("cave_goblin_rope", true) + sound("attach_rope") + item("rope", 400, "You tie the rope to the top of the hole and throw it down.") + } + option("No.") + } + } else { + statement("There is a sheer drop below the hole. You will need a rope.") + } + } + + entered("lumbridge_swamp_caves") { + if (Light.hasLightSource(this)) { + open("level_one_darkness") + } else { + open("level_three_darkness") + timers.start("insect_swarm") + } + } + + timerStart("insect_swarm") { + message("Tiny biting insects swarm all over you!") + sound("insect_swarm") + 10 + } + + timerTick("insect_swarm") { + hit(this, damage = 10) + sound("insect_bites") + 1 + } + + interfaceClosed("level_three_darkness") { + timers.stop("insect_swarm") + } + + exited("lumbridge_swamp_caves") { + close("level_one_darkness") + close("level_three_darkness") + } + } +} diff --git a/game/src/main/kotlin/content/area/misthalin/lumbridge/swamp/LumbridgeSwampShed.kt b/game/src/main/kotlin/content/area/misthalin/lumbridge/swamp/LumbridgeSwampShed.kt new file mode 100644 index 0000000000..7dbcc63496 --- /dev/null +++ b/game/src/main/kotlin/content/area/misthalin/lumbridge/swamp/LumbridgeSwampShed.kt @@ -0,0 +1,94 @@ +package content.area.misthalin.lumbridge.swamp + +import content.entity.obj.door.enterDoor +import content.entity.player.dialogue.type.choice +import content.quest.quest +import content.quest.questComplete +import content.quest.refreshQuestJournal +import world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.client.message +import world.gregs.voidps.engine.entity.character.jingle +import world.gregs.voidps.engine.entity.character.player.Player +import world.gregs.voidps.engine.entity.character.player.Teleport +import world.gregs.voidps.engine.entity.character.player.chat.ChatType +import world.gregs.voidps.engine.entity.character.player.equip.equipped +import world.gregs.voidps.engine.event.AuditLog +import world.gregs.voidps.engine.inv.add +import world.gregs.voidps.engine.inv.carriesItem +import world.gregs.voidps.engine.inv.inventory +import world.gregs.voidps.engine.queue.softQueue +import world.gregs.voidps.network.login.protocol.visual.update.player.EquipSlot +import world.gregs.voidps.type.Tile + +class LumbridgeSwampShed : Script { + init { + objectOperate("Open", "zanaris_door_closed") { (target) -> + val stage = quest("lost_city") + val enter = equipped(EquipSlot.Weapon).id == "dramen_staff" && (stage == "spirit_killed" || stage == "enter_shed" || stage == "completed") + if (enter) { + message("The world starts to shimmer...", type = ChatType.Game) + } + enterDoor(target) + if (enter) { + Teleport.teleport(this, Tile(2452, 4473), "fairy") + } + } + + objectOperate("Take", "tools") { + if (inventory.isFull()) { + message("You haven't got room to hold them.") + return@objectOperate + } + val hasRake = carriesItem("rake") + val hasSpade = carriesItem("spade") + if (hasRake && hasSpade) { + message("You've already got a spade and a rake.") + return@objectOperate + } + choice("Which would you like to take?") { + if (!hasRake) { + option("Rake") { + inventory.add("rake") + message("You 'borrow' a rake.", type = ChatType.Filter) + } + } + if (!hasSpade) { + option("Spade") { + inventory.add("spade") + message("You 'borrow' a spade.", type = ChatType.Filter) + } + } + if (inventory.spaces >= 2 && !hasRake && !hasSpade) { + option("Both") { + inventory.add("rake", "spade") + message("You 'borrow' a rake and a spade.", type = ChatType.Filter) + } + } + } + } + + teleportLand("fairy") { + val stage = quest("lost_city") + if (stage == "spirit_killed" || stage == "enter_shed") { + questComplete() + } + } + } + + fun Player.questComplete() { + AuditLog.event(this, "quest_completed", "lost_city") + set("lost_city", "completed") + jingle("quest_complete_1") + refreshQuestJournal() + inc("quest_points", 3) + softQueue("quest_complete", 1) { + message("Congratulations, Quest complete!") + questComplete( + "Lost City", + "3 Quest Points", + "Access to Zanaris", + item = "dramen_staff", + ) + } + } +} diff --git a/game/src/main/kotlin/content/area/misthalin/lumbridge/swamp/Shamus.kt b/game/src/main/kotlin/content/area/misthalin/lumbridge/swamp/Shamus.kt new file mode 100644 index 0000000000..05b8324bad --- /dev/null +++ b/game/src/main/kotlin/content/area/misthalin/lumbridge/swamp/Shamus.kt @@ -0,0 +1,102 @@ +package content.area.misthalin.lumbridge.swamp + +import content.entity.player.dialogue.Angry +import content.entity.player.dialogue.Confused +import content.entity.player.dialogue.Neutral +import content.entity.player.dialogue.Quiz +import content.entity.player.dialogue.type.choice +import content.entity.player.dialogue.type.npc +import content.entity.player.dialogue.type.player +import content.entity.player.dialogue.type.statement +import content.quest.quest +import content.skill.woodcutting.Hatchet +import world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.client.instruction.handle.interactNpc +import world.gregs.voidps.engine.client.message +import world.gregs.voidps.engine.client.ui.dialogue.talkWith +import world.gregs.voidps.engine.entity.character.npc.NPC +import world.gregs.voidps.engine.entity.character.npc.NPCs +import world.gregs.voidps.engine.entity.character.player.Player +import world.gregs.voidps.engine.queue.softQueue +import world.gregs.voidps.engine.timer.toTicks +import world.gregs.voidps.type.Tile +import java.util.concurrent.TimeUnit + +class Shamus : Script { + init { + npcOperate("Talk-to", "shamus") { (shamus) -> + npc("Ay yer big elephant! Yer've caught me, to be sure! What would an elephant like yer be wanting wid ol' Shamus then?") + when (quest("lost_city")) { + "unstarted" -> { + player("I'm not sure.") + npc("Well you'll have to be catchin' me again when yer are, elephant!") + NPCs.remove(shamus) + statement("The leprechaun magically disappears.") + } + "started" -> { + player("I want to find Zanaris.") + npc("Zanaris is it now? Well well well... Yer'll be needing to be going to that funny little shed out there in the swamp, so you will.") + player("...but... I thought... Zanaris was a city...?") + npc("Aye that it is!") + choice { + option("How does it fit in a shed then?") { + player("...How does it fit in a shed then?") + npc("Ah yer stupid elephant! The city isn't IN the shed! The doorway to the shed is being a portal to Zanaris, so it is.") + player("So I just walk into the shed and end up in Zanaris then?") + dramenStaff(shamus) + } + option("I've been in that shed, I didn't see a city.") { + dramenStaff(shamus) + } + } + } + else -> { + choice { + option("I'm not sure.") { + npc("Ha! Look at yer! Look at the stupid elephant who tries to go catching a leprechaun when he don't even be knowing what he wants!") + NPCs.remove(shamus) + statement("The leprechaun magically disappears.") + } + option("How do I get to Zanaris again?") { + npc("Yer stupid elephant! I'll tell yer again! Yer need to be entering the shed in the middle of the swamp while holding a dramenwood staff! Yer can make the Dramen staff") + npc("from a dramen tree branch, and there's a Dramen tree on Entrana! Now leave me alone yer great elephant!") + NPCs.remove(shamus) + statement("The leprechaun magically disappears.") + } + } + } + } + } + + objectOperate("Chop", "lost_city_tree") { + val hatchet = Hatchet.best(this) + if (hatchet == null) { + message("You do not have a hatchet which you have the woodcutting level to use.") + return@objectOperate + } + var shamus = NPCs.findOrNull(tile.regionLevel, "shamus") + if (shamus != null) { + talkWith(shamus) + npc("Hey! Yer big elephant! Don't go choppin' down me house, now!") + return@objectOperate + } + shamus = NPCs.add("shamus", Tile(3139, 3211)) + shamus.softQueue("shamus_despawn", TimeUnit.SECONDS.toTicks(60)) { + NPCs.remove(shamus) + } + talkWith(shamus) + interactNpc(shamus, "Talk-to") + } + } + + private suspend fun Player.dramenStaff(shamus: NPC) { + npc("Oh, was I fergetting to say? Yer need to be carrying a Dramenwood staff to be getting there! Otherwise Yer'll just be ending up in the shed.") + player("So where would I get a staff?") + npc("Dramenwood staffs are crafted from branches of the Dramen tree, so they are. I hear there's a Dramen tree over on the island of Entrana in a cave") + npc("or some such. There would probably be a good place for an elephant like yer to be starting looking I reckon.") + set("lost_city", "find_staff") + npc("The monks are running a ship from Port Sarim to Entrana, I hear too. Now leave me alone yer elephant!") + NPCs.remove(shamus) + statement("The leprechaun magically disappears.") + } +} diff --git a/game/src/main/kotlin/content/area/misthalin/zanaris/Blaec.kt b/game/src/main/kotlin/content/area/misthalin/zanaris/Blaec.kt new file mode 100644 index 0000000000..c5cf2472e1 --- /dev/null +++ b/game/src/main/kotlin/content/area/misthalin/zanaris/Blaec.kt @@ -0,0 +1,21 @@ +package content.area.misthalin.zanaris + +import content.entity.player.dialogue.Happy +import content.entity.player.dialogue.Neutral +import content.entity.player.dialogue.Sad +import content.entity.player.dialogue.type.npc +import world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.entity.character.player.name +import world.gregs.voidps.type.random + +class Blaec : Script { + init { + npcOperate("Talk-to", "blaec") { + when (random.nextInt(2)) { + 0 -> npc("Wunnerful weather we're having today!") + 1 -> npc("Please leave me alone, I'm busy trapping the pygmy shrews.") + else -> npc("Greetin's $name, fine day today!") + } + } + } +} diff --git a/game/src/main/kotlin/content/area/misthalin/zanaris/CoOrdinator.kt b/game/src/main/kotlin/content/area/misthalin/zanaris/CoOrdinator.kt new file mode 100644 index 0000000000..68d33e498e --- /dev/null +++ b/game/src/main/kotlin/content/area/misthalin/zanaris/CoOrdinator.kt @@ -0,0 +1,22 @@ +package content.area.misthalin.zanaris + +import content.entity.player.dialogue.Confused +import content.entity.player.dialogue.Quiz +import content.entity.player.dialogue.type.npc +import content.entity.player.dialogue.type.player +import world.gregs.voidps.engine.Script +import world.gregs.voidps.type.random + +class CoOrdinator : Script { + init { + npcOperate("Talk-to", "co_ordinator") { + player("Hello, what are you doing?") + when (random.nextInt(4)) { + 0 -> npc("Sorry, I don't have time to stop, I need to send a Weather Fairy off to Etceteria!") + 1 -> npc("Sorry, I don't have time for idle chit-chat, I need to send an Autumn Fairy off to Burthorpe!") + 2 -> npc("Sorry, I don't have time for idle chit-chat, I need to find a Winter Fairy to send to Trollheim!") + else -> npc("Sorry, I don't have time for idle chit-chat, I need to send a fairy to get little Freddies tooth!") + } + } + } +} diff --git a/game/src/main/kotlin/content/area/misthalin/zanaris/FairyAeryka.kt b/game/src/main/kotlin/content/area/misthalin/zanaris/FairyAeryka.kt new file mode 100644 index 0000000000..ce76e42ce0 --- /dev/null +++ b/game/src/main/kotlin/content/area/misthalin/zanaris/FairyAeryka.kt @@ -0,0 +1,121 @@ +package content.area.misthalin.zanaris + +import content.entity.player.dialogue.Happy +import content.entity.player.dialogue.Idle +import content.entity.player.dialogue.Laugh +import content.entity.player.dialogue.Neutral +import content.entity.player.dialogue.Quiz +import content.entity.player.dialogue.Sad +import content.entity.player.dialogue.Shock +import content.entity.player.dialogue.type.ChoiceOption +import content.entity.player.dialogue.type.choice +import content.entity.player.dialogue.type.npc +import content.entity.player.dialogue.type.player +import world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.entity.character.player.Player +import world.gregs.voidps.engine.entity.character.player.name + +class FairyAeryka : Script { + init { + npcOperate("Talk-to", "fairy_aeryka") { + if (get("met_fairy_aeryka", false)) { + npc("It's still here.") + player("Pardon?") + npc("It's still here. The crop circle is still here.") + player("Oh yes, thanks Aery. It didn't go anywhere in the meantime, then?") + npc("Nope. It just sat there.") + player("Jolly good. I can come back and visit Puro-Puro whenever I want then. Brilliant!") + anythingElse() + } else { + player("Hello, my name is $name. Who are you?") + npc("Oh hello $name. I'm Aeryka. Aery for short.") + player("Airy Fairy?") + npc("That's right. What can I do for you?") + player("What's that crop circle thing doing here?") + npc("Crop circle? Oh, you mean the Puromantic portal?") + player("The pyromanic what?") + npc("The Puromantic portal. At least that's what we call them. It's the way the implings travel from Puro-Puro to other planes.") + player("Puro-Puro?") + npc("The impling home. We call it Puro-Puro. The implings just call it home, I think.") + set("met_fairy_aeryka", true) + anythingElse() + } + } + } + + private suspend fun Player.anythingElse() { + choice("Is there anything else you want to ask?") { + puroPuro() + implings() + dragonEquipment() + bye() + } + } + + private fun ChoiceOption.dragonEquipment() { + option("I've heard I may find dragon equipment in Puro-Puro.") { + npc("Really? You humans like that stuff a lot, don't you? I don't like really old stuff myself.") + player("Old?") + npc("Yes, dragon stuff feels really old.") + player("How can you tell that?") + npc("From its magical aura, obviously. Oh, I forget you humans can't feel auras.") + player("How old is old?") + npc("Really, really, old.") + player("Can you be any more precise?") + npc("Not really. Time doesn't really mean a lot round here. Hundreds, maybe thousands of your human lifespans, I suppose. Anyway, it would have to be old since it all comes from the Necrosyrtes.") + player("Necrosyrtes? Who are they?") + npc("Old and powerful creatures. I don't think there have been any around here for aeons. I haven't seen one. I don't think they are very nice. Not like me. Anyway, this is all ancient history. Boooring!") + player("While I'm on the subject do you have any dragon stuff I can have?") + npc("Oh, I did have loads, but I threw it away.") + player("What?") + npc("Only joking. ") + npc("No, sorry, I can't help you there. You'll have to look for it yourself. Although, maybe if you find Necrosyrtes then they'll give you some. I heard they give dragon stuff away to people they like. Not fairies. We're too") + npc("nice.") + choice("Is there anything else you want to ask?") { + puroPuro() + implings() + bye() + } + } + } + + private fun ChoiceOption.implings() { + option("So what are these implings then?") { + npc("Well, no-one knows for sure. The mischievous little creatures are probably related to imps. And they fly as well.") + npc("Also, like imps, they love collecting things. I'm not sure why, though. They also seem to like being chased.") + player("So how would I get hold of what they are carrying, then?") + npc("Catch them, I suppose. I don't know really. Why would you want to?") + player("Well, if they were carrying something useful. Maybe I could catch them with a big net - like butterflies.") + npc("Sounds a bit cruel to me, but I suppose that's possible.") + choice("Is there anything else you want to ask?") { + puroPuro() + dragonEquipment() + bye() + } + } + } + + private fun ChoiceOption.puroPuro() { + option("What's in Puro-Puro?") { + npc("Implings...and wheat.") + player("Erm, anything else?") + npc("Not really. Though I have noticed quite a lot of you humans travelling through the portal recently. I suppose you must like wheat.") + player("Well, most of us prefer lobsters to be perfectly honest, but there must be something interesting there.") + npc("Oh, I did notice a very serious-looking gnome go into the portal. Maybe he knows what's going on.") + player("Do you remember what his name was?") + npc("Errm, Egg-nog, or something like that.") + player("Right, thanks! I'll have a chat with him when I go. Maybe he'll know what's going on.") + choice("Is there anything else you want to ask?") { + implings() + dragonEquipment() + bye() + } + } + } + + private fun ChoiceOption.bye() { + option("No, bye!") { + npc("See you around!") + } + } +} diff --git a/game/src/main/kotlin/content/area/misthalin/zanaris/FairyAttendant.kt b/game/src/main/kotlin/content/area/misthalin/zanaris/FairyAttendant.kt new file mode 100644 index 0000000000..77d7975056 --- /dev/null +++ b/game/src/main/kotlin/content/area/misthalin/zanaris/FairyAttendant.kt @@ -0,0 +1,38 @@ +package content.area.misthalin.zanaris + +import content.entity.player.dialogue.Neutral +import content.entity.player.dialogue.type.choice +import content.entity.player.dialogue.type.npc +import world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.client.ui.dialogue.talkWith +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.type.Tile + +class FairyAttendant : Script { + init { + objectOperate("Use", "zanaris_al_kharid_fairy_ring") { + val attendant = NPCs.find(tile.regionLevel, "fairy_attendant") + talkWith(attendant) + leave() + } + npcOperate("Talk-to", "fairy_attendant") { + leave() + } + } + + private suspend fun Player.leave() { + npc("This fairy ring will take you out of Zanaris. It leads to the place known as Al Kharid in your realm. Once passed you can not return this way.") + npc("Before leaving make sure that you have fully sampled the delights of our marketplace.") + choice { + option("I think I'll stay down here a bit longer.") { + npc("As you wish.") + } + option("Yes, I'm ready to leave.") { + walkToDelay(Tile(2486, 4471)) + Teleport.teleport(this, Tile(3260, 3171), "fairy") + } + } + } +} diff --git a/game/src/main/kotlin/content/area/misthalin/zanaris/FairyChef.kt b/game/src/main/kotlin/content/area/misthalin/zanaris/FairyChef.kt new file mode 100644 index 0000000000..4eebc655b6 --- /dev/null +++ b/game/src/main/kotlin/content/area/misthalin/zanaris/FairyChef.kt @@ -0,0 +1,13 @@ +package content.area.misthalin.zanaris + +import content.entity.player.dialogue.Happy +import content.entity.player.dialogue.type.npc +import world.gregs.voidps.engine.Script + +class FairyChef : Script { + init { + npcOperate("Talk-to", "fairy_chef") { + npc("'Ello, sugar. I'm afraid I can't gossip right now, I've got a cake in the oven.") + } + } +} diff --git a/game/src/main/kotlin/content/area/misthalin/zanaris/FairyShopkeeper.kt b/game/src/main/kotlin/content/area/misthalin/zanaris/FairyShopkeeper.kt new file mode 100644 index 0000000000..f60f114067 --- /dev/null +++ b/game/src/main/kotlin/content/area/misthalin/zanaris/FairyShopkeeper.kt @@ -0,0 +1,22 @@ +package content.area.misthalin.zanaris + +import content.entity.npc.shop.openShop +import content.entity.player.dialogue.Happy +import content.entity.player.dialogue.Neutral +import content.entity.player.dialogue.type.choice +import content.entity.player.dialogue.type.npc +import world.gregs.voidps.engine.Script + +class FairyShopkeeper : Script { + init { + npcOperate("Talk-to", "fairy_shopkeeper,fairy_shop_assistant") { (target) -> + npc("Can I help you at all?") + choice { + option("Yes please. What are you selling?") { + openShop(target.def["shop"]) + } + option("No thanks.") + } + } + } +} diff --git a/game/src/main/kotlin/content/area/misthalin/zanaris/Gatekeeper.kt b/game/src/main/kotlin/content/area/misthalin/zanaris/Gatekeeper.kt new file mode 100644 index 0000000000..ef8756a8c7 --- /dev/null +++ b/game/src/main/kotlin/content/area/misthalin/zanaris/Gatekeeper.kt @@ -0,0 +1,73 @@ +package content.area.misthalin.zanaris + +import content.entity.obj.door.enterDoor +import content.entity.player.dialogue.Confused +import content.entity.player.dialogue.Happy +import content.entity.player.dialogue.Neutral +import content.entity.player.dialogue.Quiz +import content.entity.player.dialogue.Sad +import content.entity.player.dialogue.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.message +import world.gregs.voidps.engine.client.ui.dialogue.talkWith +import world.gregs.voidps.engine.entity.character.npc.NPCs +import world.gregs.voidps.engine.entity.character.player.Player +import world.gregs.voidps.engine.entity.obj.GameObjects +import world.gregs.voidps.engine.inv.inventory +import world.gregs.voidps.engine.inv.remove +import world.gregs.voidps.type.Tile + +class Gatekeeper : Script { + init { + npcOperate("Talk-to", "gatekeeper") { + player("What happened to the old man who used to be the doorman?") + npc("You mean my father? He went into retirement, I've taken over the family business instead.") + tax() + } + + objectOperate("Open", "magic_door_zanaris_closed") { (target) -> + if (tile.y <= 4433 || tile.x >= 2470) { + enterDoor(target) + return@objectOperate + } + val gate = NPCs.find(tile.regionLevel, "gatekeeper") + talkWith(gate) + tax() + } + } + + private suspend fun Player.tax() { + npc("You may not pass through this door without paying the trading tax.") + player("So how much is the tax?") + npc("The cost is one diamond.") + choice { + option("Okay...") { + if (inventory.remove("diamond")) { + message("You give the doorman a diamond.") + val nearest = if (tile.y <= 4436) Tile(2465, 4433) else Tile(2469, 4437) + val tile = if (tile.y <= 4436) Tile(2465, 4433) else Tile(2470, 4437) + val door = GameObjects.find(tile, "magic_door_zanaris_closed") + walkToDelay(nearest) + enterDoor(door) + } else { + player("...but...") + player("I haven't brought my diamonds with me.") + npc("No tax, no entry.") + } + } + option("A diamond? Are you crazy?") { + npc("Not at all. Those are the rules.") + } + option("I haven't brought my diamonds with me.") { + npc("No tax, no entry.") + } + option("What do you do with all the diamonds you get?") { + npc("Ever heard of fairylights? Well how do you think we make 'em? First we collect a pile of gems and then we get a spider to spin 'em into a long web, we light the jewels by imbuing each one with a little bit of magic.") + player("So you're telling me fairylights are made out of gems?") + npc("That's right, how else could we make 'em twinkle so beautifully?") + } + } + } +} diff --git a/game/src/main/kotlin/content/area/misthalin/zanaris/Irksol.kt b/game/src/main/kotlin/content/area/misthalin/zanaris/Irksol.kt new file mode 100644 index 0000000000..7a0558505e --- /dev/null +++ b/game/src/main/kotlin/content/area/misthalin/zanaris/Irksol.kt @@ -0,0 +1,24 @@ +package content.area.misthalin.zanaris + +import content.entity.npc.shop.openShop +import content.entity.player.dialogue.Neutral +import content.entity.player.dialogue.type.choice +import content.entity.player.dialogue.type.npc +import world.gregs.voidps.engine.Script + +class Irksol : Script { + init { + npcOperate("Talk-to", "irksol") { (target) -> + npc("Selling ruby rings! The best deals on rings in over twenty four hundred planes of existence!") + choice { + option("I'm interested in these deals.") { + npc("Aha! A connoisseur! Check out these beauties!") + openShop(target.def["shop"]) + } + option("No thanks, just browsing.") { + npc("Fair enough.") + } + } + } + } +} diff --git a/game/src/main/kotlin/content/area/misthalin/zanaris/Jukat.kt b/game/src/main/kotlin/content/area/misthalin/zanaris/Jukat.kt new file mode 100644 index 0000000000..0b28869514 --- /dev/null +++ b/game/src/main/kotlin/content/area/misthalin/zanaris/Jukat.kt @@ -0,0 +1,21 @@ +package content.area.misthalin.zanaris + +import content.entity.npc.shop.openShop +import content.entity.player.dialogue.Neutral +import content.entity.player.dialogue.type.choice +import content.entity.player.dialogue.type.npc +import world.gregs.voidps.engine.Script + +class Jukat : Script { + init { + npcOperate("Talk-to", "jukat") { (target) -> + npc("Dragon swords! Here, Dragon swords! Straight from Frenaskrae!") + choice { + option("Yes please.") { + openShop(target.def["shop"]) + } + option("No thanks, I'm just browsing.") + } + } + } +} diff --git a/game/src/main/kotlin/content/area/misthalin/zanaris/Lunderwin.kt b/game/src/main/kotlin/content/area/misthalin/zanaris/Lunderwin.kt new file mode 100644 index 0000000000..a669b6e1b9 --- /dev/null +++ b/game/src/main/kotlin/content/area/misthalin/zanaris/Lunderwin.kt @@ -0,0 +1,40 @@ +package content.area.misthalin.zanaris + +import content.entity.player.dialogue.Neutral +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 world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.inv.carriesItem +import world.gregs.voidps.engine.inv.inventory +import world.gregs.voidps.engine.inv.transact.TransactionError +import world.gregs.voidps.engine.inv.transact.operation.AddItem.add +import world.gregs.voidps.engine.inv.transact.operation.RemoveItemLimit.removeToLimit + +class Lunderwin : Script { + init { + npcOperate("Talk-to", "lunderwin") { (target) -> + npc("Buying cabbage am I, not have such thing where I from. Will pay money much handsome for wondrous object, cabbage you called.") + npc("Say I 100 gold coins each fair price to be giving yes?") + if (!carriesItem("cabbage")) { + player("Alas, I have no cabbages either...") + npc("Pity be that, I want badly do.") + return@npcOperate + } + choice { + option("Yes, I will sell you all my cabbages.") { + inventory.transaction { + val removed = removeToLimit("cabbage", 28) + add("coins", removed * 100) + } + when (inventory.transaction.error) { + TransactionError.None -> npc("Business good doing with you! Please, again come, buying always.") + else -> {} + } + } + option("No, I will keep my cabbbages.") + } + } + } +} diff --git a/game/src/main/kotlin/content/area/misthalin/zanaris/Zanaris.kt b/game/src/main/kotlin/content/area/misthalin/zanaris/Zanaris.kt new file mode 100644 index 0000000000..daefd2f2fb --- /dev/null +++ b/game/src/main/kotlin/content/area/misthalin/zanaris/Zanaris.kt @@ -0,0 +1,87 @@ +package content.area.misthalin.zanaris + +import content.entity.combat.hit.directHit +import world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.client.message +import world.gregs.voidps.engine.entity.character.move.tele +import world.gregs.voidps.engine.entity.character.player.Teleport +import world.gregs.voidps.engine.entity.character.player.chat.ChatType +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 +import world.gregs.voidps.engine.entity.character.player.skill.level.Level.has +import world.gregs.voidps.engine.entity.character.sound +import world.gregs.voidps.engine.entity.obj.GameObjects +import world.gregs.voidps.type.Direction +import world.gregs.voidps.type.Tile + +class Zanaris : Script { + init { + objectOperate("Use", "fairy_ring_zanaris") { (target) -> + walkOverDelay(target.tile) + Teleport.teleport(this, Tile(3201, 3169), "fairy") + } + + objectOperate("Enter", "zanaris_crop_circle") { + walkToDelay(Tile(2427, 4446)) + Teleport.teleport(this, Tile(2591, 4319), "puro_puro") + } + + objectOperate("Exit", "puro_puro_exit") { + walkToDelay(Tile(2592, 4320)) + Teleport.teleport(this, Tile(2591, 4319), "puro_puro") + } + + objectOperate("Quick-leave", "puro_puro_exit") { + tele(2426, 4445) // TODO check how this works + } + + objectOperate("Squeeze-past", "zanaris_jutting_wall") { (target) -> + val level = if (target.tile.x == 2400) 46 else 66 + if (!has(Skill.Agility, level, message = true)) { + return@objectOperate + } + message("You try to squeeze past.", type = ChatType.Filter) + if (Level.success(levels.get(Skill.Agility), 50..254)) { + val direction = if (tile.y < target.tile.y) { + Direction.NORTH + } else { + Direction.SOUTH + } + walkToDelay(target.tile.add(direction.inverse())) + face(direction) + anim("spear_trap_walk_${if (direction == Direction.SOUTH) "right" else "left"}") + exactMoveDelay(target.tile.add(direction), startDelay = 28, delay = 124, direction = direction) + sound("squeeze_out") + exp(Skill.Agility, 10.0) + } else { + val direction = if (tile.y < target.tile.y) { + Direction.NORTH + } else { + Direction.SOUTH + } + walkToDelay(target.tile.add(direction.inverse())) + anim("spear_trap_caught_${if (direction == Direction.SOUTH) "right" else "left"}") + face(direction) + exactMoveDelay(target.tile, startDelay = 28, delay = 48, direction = direction) + val trap = GameObjects.find(target.tile.addX(-1), "zanaris_damaged_wall") + // FIXME: Directional anim broken on the right side + for (ouch in listOf("Ahhh...", "Owww...", "Arrgghhhh!")) { + anim("side_hurt_${if (direction == Direction.SOUTH) "left" else "left"}") + trap.anim("spear_trap_release") + say(ouch) + delay(1) + } + anim("dive_player") + sound("human_hit") + sound("spear_trap_jump") + sound("male_defend_1") + sound("male_defend_2") + directHit(20) + directHit(20) + exp(Skill.Agility, 6.0) + exactMoveDelay(target.tile.add(direction), startDelay = 10, delay = 20, direction = direction) + } + } + } +} diff --git a/game/src/main/kotlin/content/area/misthalin/zanaris/ZanarisCow.kt b/game/src/main/kotlin/content/area/misthalin/zanaris/ZanarisCow.kt new file mode 100644 index 0000000000..9a66d88d39 --- /dev/null +++ b/game/src/main/kotlin/content/area/misthalin/zanaris/ZanarisCow.kt @@ -0,0 +1,13 @@ +package content.area.misthalin.zanaris + +import content.entity.player.dialogue.Neutral +import content.entity.player.dialogue.type.npc +import world.gregs.voidps.engine.Script + +class ZanarisCow : Script { + init { + npcOperate("Talk-to", "cow_zanaris") { + npc("Hmm, interesting, there appears to be a human trying to talk to me. Is it aware that cows can't talk I wonder? Maybe if I just ignore it, it might go away.") + } + } +} diff --git a/game/src/main/kotlin/content/area/misthalin/zanaris/ZanarisSheep.kt b/game/src/main/kotlin/content/area/misthalin/zanaris/ZanarisSheep.kt new file mode 100644 index 0000000000..9933b549bb --- /dev/null +++ b/game/src/main/kotlin/content/area/misthalin/zanaris/ZanarisSheep.kt @@ -0,0 +1,26 @@ +package content.area.misthalin.zanaris + +import content.entity.player.dialogue.Angry +import content.entity.player.dialogue.Happy +import content.entity.player.dialogue.Neutral +import content.entity.player.dialogue.Quiz +import content.entity.player.dialogue.type.choice +import content.entity.player.dialogue.type.npc +import world.gregs.voidps.engine.Script + +class ZanarisSheep : Script { + init { + npcOperate("Talk-to", "sheep_zanaris,sheep_zanaris_2") { + npc("Hello, I've just finished composing my latest poem. Would you like to hear it?") + choice { + option("Yes, please.") { + npc("Excellent! The humans an aficionado! Make yourself comfortable and I'll begin...") + npc("Twinkle, twinkle little egg Wouldn't you like to grow some legs? And run around, on grass so green arguing with the milk and cream.") + } + option("No thanks.") { + npc("Huh, the cow's right. You humans have absolutely no understanding of art!") + } + } + } + } +} diff --git a/game/src/main/kotlin/content/area/misthalin/zanaris/Zygomite.kt b/game/src/main/kotlin/content/area/misthalin/zanaris/Zygomite.kt new file mode 100644 index 0000000000..b14a93f49a --- /dev/null +++ b/game/src/main/kotlin/content/area/misthalin/zanaris/Zygomite.kt @@ -0,0 +1,81 @@ +package content.area.misthalin.zanaris + +import content.entity.combat.attacker +import content.entity.combat.attackers +import content.entity.combat.hit.damage +import content.entity.combat.underAttack +import content.entity.effect.transform +import world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.client.instruction.handle.interactPlayer +import world.gregs.voidps.engine.client.message +import world.gregs.voidps.engine.entity.character.mode.EmptyMode +import world.gregs.voidps.engine.entity.character.mode.PauseMode +import world.gregs.voidps.engine.entity.character.npc.NPC +import world.gregs.voidps.engine.entity.character.player.Player +import world.gregs.voidps.engine.entity.character.player.chat.ChatType +import world.gregs.voidps.engine.entity.character.player.skill.Skill +import world.gregs.voidps.engine.entity.character.sound +import world.gregs.voidps.engine.inv.discharge +import world.gregs.voidps.engine.inv.inventory + +class Zygomite : Script { + init { + npcOperate("Pick", "fungi,fungi_large") { (target) -> + anim("pick_zygomite") + sound("pick") + delay(1) + target.anim("zygomite_grow") + sound("zygo_grow") + delay(2) + target.transform(if (target.id == "fungi") "zygomite" else "zygomite_large") + target.interactPlayer(this, "Attack") + } + + npcLevelChanged(Skill.Constitution, "fungi*", ::killingBlow) + + itemOnNPCOperate("fungicide_spray_*", "zygomite*") { (target, item, index) -> + arriveDelay() + if (item.id == "fungicide_spay_0") { + message("The spray pump is empty! Reload it with another fungicide canister!") + return@itemOnNPCOperate + } + if (target.underAttack && target.attacker != this) { + message("Someone else is fighting that.") + return@itemOnNPCOperate + } + fungicide(this, target, index) + } + } + + fun killingBlow(npc: NPC, skill: Skill, from: Int, to: Int) { + if (to > 80) { + return + } + for (attacker in npc.attackers) { + attacker.mode = EmptyMode + attacker.message("The zygomite is on its last legs! Finish it quickly!") + if (attacker is Player && attacker["killing_blow", false]) { + val index = attacker.inventory.items.indexOfFirst { it.id.startsWith("fungicide_spray_") } + fungicide(attacker, npc, index) + break + } + } + npc.mode = PauseMode + } + + fun fungicide(player: Player, target: NPC, index: Int) { + if (!player.inventory.discharge(player, index)) { + player.message("You need a fungicide to kill the zygomite.") // TODO proper message + return + } + val hitpoints = target.levels.get(Skill.Constitution) + player.face(target) + player.sound("zygomite_spray") + if (hitpoints >= 80) { + player.message("The zygomite is not weak enough to succumb to the effects of the fungicide!") + return + } + target.damage(hitpoints, source = player) + player.message("The zygomite is covered in fungicide. It bubbles away to nothing!", type = ChatType.Filter) + } +} diff --git a/game/src/main/kotlin/content/area/misthalin/zanaris/puro_puro/Elnock.kt b/game/src/main/kotlin/content/area/misthalin/zanaris/puro_puro/Elnock.kt new file mode 100644 index 0000000000..9e283afe56 --- /dev/null +++ b/game/src/main/kotlin/content/area/misthalin/zanaris/puro_puro/Elnock.kt @@ -0,0 +1,221 @@ +package content.area.misthalin.zanaris.puro_puro + +import content.entity.player.dialogue.Confused +import content.entity.player.dialogue.Happy +import content.entity.player.dialogue.Neutral +import content.entity.player.dialogue.Quiz +import content.entity.player.dialogue.type.ChoiceOption +import content.entity.player.dialogue.type.choice +import content.entity.player.dialogue.type.intEntry +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.ui.chat.plural +import world.gregs.voidps.engine.client.ui.chat.toDigitGroupString +import world.gregs.voidps.engine.entity.character.player.Player +import world.gregs.voidps.engine.inv.add +import world.gregs.voidps.engine.inv.carriesItem +import world.gregs.voidps.engine.inv.inventory +import world.gregs.voidps.engine.inv.transact.TransactionError +import world.gregs.voidps.engine.inv.transact.operation.AddItem.add +import world.gregs.voidps.engine.inv.transact.operation.RemoveItem.remove + +class Elnock : Script { + init { + npcOperate("Talk-to", "elnock_inquisitor") { + // TODO intro + npc("Ah, good day, it's you again. Would you like to trade, or get an impling collector's scroll? Or can I help you some other way?") + menu() + } + } + + private suspend fun Player.menu() { + choice("What would you like to say?") { + catchImplings() + tradeJars() + option("Could you store some equipment for me?") { + npc("I suppose I could keep hold of a net and a few empty jars for you. Just give me what you want me to hold.") + } + spareEquipment() + option("More...") { + more() + } + } + } + + private fun ChoiceOption.tradeJars() { + option("Can I trade some jarred implings please?") { + // TODO interface + } + } + + private fun ChoiceOption.spareEquipment() { + option("Do you have some spare equipment I can use?") { + npc("I have already given you some equipment.") + npc("If you are ready to start hunting implings, then enter the main part of the maze.") + npc("Just push through the wheat that surrounds the centre of the maze and get catching!") + choice("What would you like to say?") { + wheat() + thanks() + } + } + } + + private suspend fun Player.more() { + choice("What would you like to say?") { + option("Can I have an impling collector's scroll, please?") { + if (carriesItem("impling_scroll")) { + npc("You've already got one with you - carrying more won't help! You can always get replacements from me if you lose it.") + return@option + } + if (inventory.add("impling_scroll")) { + item("impling_scroll", 400, "Elnock gives you a scroll. If you check it whilst in the maze, you will see how many of each impling you have captured.") // 11273 + } else { + // TODO + } + menu() + } + wheat() + buyJars() + thanks() + option("Back...") { + menu() + } + } + } + + private fun ChoiceOption.wheat() { + option("Can you tell me about the wheat?") { + npc("The wheat here can be hard to push through and pushing through it can make you stronger. I found it easier to push through it for about half an hour just after coming through a temporary crop circle. I call it") + npc("the Farmer's Affinity.") + player("I want to become stronger pushing through the wheat.") + choice("Turn wheat Strength XP ON?") { + option("Yes") { + // TODO + } + option("No") { + // TODO + } + } + } + } + + private fun ChoiceOption.catchImplings() { + option("Can you remind me how to catch implings again?") { + npc("Certainly.") + npc("Firstly you will need a butterfly net in which to catch them and at least one special impling jar to store an impling.") + npc("You will also require some experience as a Hunter since these creatures are elusive. The more immature implings require less experience, but some of the rarer implings are extraordinarily hard to find and catch.") + npc("Once you have caught one, you may break the jar open and obtain the object the impling is carrying. Alternatively, you may exchange certain combinations of jars with me. I will return the jars to my clients. In") + npc("exchange I will be able to provide you with some equipment that may help you hunt butterflies more effectively.") + choice("What would you like to say?") { + jars() + thievingImps() + option("So what's this equipment you can give me then?") + spareEquipment() + buyJars() + } + } + } + + private fun ChoiceOption.thievingImps() { + option("Tell me more about these thieving imps.") { + npc("Imps and implings appear to be related, and the imps here are quite protective of their smaller relations. If you allow them to get too close then they will attempt to steal jarred implings from your pack, if you have them.") + npc("They will then set them free, dropping your jar on the floor. So, if you're quick, you may be able to catch it again.") + npc("I have some impling deterrent which I may trade if you prove that you can catch implings well.") + choice("What would you like to say?") { + jars() + thievingImps() + equipment() + spareEquipment() + buyJars() + } + } + } + + private fun ChoiceOption.jars() { + option("Tell me more about these jars.") { + npc("You cannot use an ordinary butterfly jar as a container as the implings will escape from them with ease. However, I have done some investigation and have come up with a solution - if a butterfly jar is coated") + npc("with a thin layer of a substance noxious to them they become incapable of escape.") + player("What substance is that, then?") + npc("I have tried a few experiments with the help of a friend back home, and it turns out that a combination of anchovy oil and flowers - marigolds, rosemary or nasturtiums - will work.") + player("How do you make anchovy oil then?") + npc("I'd grind up some cooked anchovies and pass them through a sieve.") + player("Where do I make these jars?") + npc("Well, I believe there is a chemist in Rimmington that has a small still that you could use.") + player("Is there anywhere I can buy these jars?") + npc("Well I may be able to let you have a few - if it means you will start hunting these implings - although I do not have an infinite supply.") + npc("Would you like to buy some?") + choice("Would you like to buy some impling jars?") { + option("Yes") { + trade() + } + option("No") { + choice("What would you like to say?") { + thievingImps() + equipment() + spareEquipment() + buyJars() + thanks() + } + } + } + } + } + + private fun ChoiceOption.buyJars() { + option("Can I buy a few impling jars?") { + trade() + } + } + + private suspend fun Player.trade() { + npc("I usually prefer trading these jars for implings, but if you desperately need them I'm willing to sell you up to 10 jars per day.") + npc("I will sell these jars for 2,000 coins each. How many would you like to purchase?") + val jars = intEntry("How many jars would you like to purchase? (1 - 10)") + val cost = jars * 2000 + npc("So you would like to purchase $jars ${"jar".plural(jars)}, costing you a total of ${cost.toDigitGroupString()} coins. Is that correct?") + choice("Purchase $jars impling ${"jar".plural(jars)} for ${cost.toDigitGroupString()} coins?") { + option("Yes") { + inventory.transaction { + remove("coins", cost) + add("impling_jar", jars) + } + when (inventory.transaction.error) { + is TransactionError.Deficient -> npc("You don't seem to have brought enough coins, please come back with ${cost.toDigitGroupString()} coins if you'd like to buy the jars.") + is TransactionError.Full -> TODO() + TransactionError.None -> { + // TODO track bought per day + npc("Here you go. You can come back if you need more and tomorrow I will have another 10 jars ready for purchase.") + } + else -> {} + } + } + option("No") { + npc("My time is valuable, please give me a serious offer next time.") + } + } + } + + private fun ChoiceOption.equipment() { + option("So what's this equipment you can give me, then?") { + npc("I have been given permission by my clients to give three pieces of equipment to able hunters.") + npc("Firstly, I have some imp deterrent. If you bring me three baby implings, two young implings and one gourmet impling already jarred, I will give you a vial. Imps don't like the smell, so they will be less likely to") + npc("steal jarred implings from you.") + npc("Secondly, I have magical butterfly nets. If you bring me three gourmet implings, two earth implings and one essence impling I will give you a new net. It will help you catch both implings and butterflies.") + npc("Lastly, I have magical jar generators. If you bring me three essence implings, two eclectic implings and one nature impling I will give you a jar generator. This object will create either butterfly or impling jars (up to") + npc("a limited number of charges) without having to carry a pack full of them.") + choice("What would you like to say?") { + jars() + thievingImps() + spareEquipment() + buyJars() + thanks() + } + } + } + + private fun ChoiceOption.thanks() { + option("Thanks, I'll get going.") + } +} diff --git a/game/src/main/kotlin/content/area/misthalin/zanaris/puro_puro/Immenizz.kt b/game/src/main/kotlin/content/area/misthalin/zanaris/puro_puro/Immenizz.kt new file mode 100644 index 0000000000..5b9c2a4654 --- /dev/null +++ b/game/src/main/kotlin/content/area/misthalin/zanaris/puro_puro/Immenizz.kt @@ -0,0 +1,69 @@ +package content.area.misthalin.zanaris.puro_puro + +import content.entity.player.dialogue.Angry +import content.entity.player.dialogue.Bored +import content.entity.player.dialogue.Happy +import content.entity.player.dialogue.Neutral +import content.entity.player.dialogue.Quiz +import content.entity.player.dialogue.type.ChoiceOption +import content.entity.player.dialogue.type.choice +import content.entity.player.dialogue.type.npc +import content.entity.player.dialogue.type.player +import world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.entity.character.move.tele +import world.gregs.voidps.engine.entity.character.player.Teleport +import world.gregs.voidps.type.Tile + +class Immenizz : Script { + init { + npcOperate("Talk-to", "immenizz") { + choice { + place() + portal() + option("I'll leave you alone.") + } + } + npcOperate("Quick-leave", "immenizz") { + // TODO check how this works + tele(2426, 4445) + } + } + + private fun ChoiceOption.place() { + option("What is this place?") { + npc("This is my home, mundane human! What do you have in your pockets? Something tasty?") + player("Stay out of my pockets! I don't have anything that you want.") + npc("Ah, but do you have anything that *you* want?") + player("Of course I do!") + npc("Then you have something that implings want.") + player("Eh?") + npc("We want things you people want. They are tasty to us! The more you want them, the tastier they are!") + player("So, you collect things that humans want? Interesting... So, what would happen if I caught an impling in a butterfly net?") + npc("Don't do that! That would be cruel. But chase us, yes! That is good. Implings are not easy to catch. Especially ones with really tasty food.") + player("So, some of these implings have things that I will really want? Hmm, maybe it would be worth my while trying to catch some.") + choice { + portal() + option("I'll leave you alone.") + } + } + } + + private fun ChoiceOption.portal() { + option("Tell me about this portal.") { + npc("You want to know about the portal? It is for leaving this place, of course!") + player("Where will I go if I leave?") + npc("Back where you came from, of course!") + player("So not back to where the crop circle is now?") + npc("The aetheric thread connecting you to your own plane leads back to the wheat field where you came from. Obviously. So you will always go back to where you came from! Don't you humans know anything?") + player("Evidently not.") + npc("You wanna leave?") + choice { + option("Yes, get me out of here!") { + walkToDelay(Tile(2592, 4319)) + Teleport.teleport(this, Tile(2426, 4445), "puro_puro") + } + option("Ah, no I'll hang around a bit longer.") + } + } + } +} diff --git a/game/src/main/kotlin/content/area/morytania/canifis/Barker.kt b/game/src/main/kotlin/content/area/morytania/canifis/Barker.kt new file mode 100644 index 0000000000..c846bf80b2 --- /dev/null +++ b/game/src/main/kotlin/content/area/morytania/canifis/Barker.kt @@ -0,0 +1,23 @@ +package content.area.morytania.canifis + +import content.entity.npc.shop.openShop +import content.entity.player.dialogue.* +import content.entity.player.dialogue.type.choice +import content.entity.player.dialogue.type.npc +import world.gregs.voidps.engine.Script + +class Barker : Script { + init { + npcOperate("Talk-to", "barker") { (target) -> + npc("You are looking for clothes, yes? You look at my products! I have very many nice clothes, yes?") + choice { + option("Yes, please.") { + openShop(target.def["shop"]) + } + option("No thanks.") { + npc("Unfortunate for you, yes? Many bargains, won't find elsewhere!") + } + } + } + } +} diff --git a/game/src/main/kotlin/content/area/morytania/canifis/Fidelio.kt b/game/src/main/kotlin/content/area/morytania/canifis/Fidelio.kt new file mode 100644 index 0000000000..7c61b5006f --- /dev/null +++ b/game/src/main/kotlin/content/area/morytania/canifis/Fidelio.kt @@ -0,0 +1,31 @@ +package content.area.morytania.canifis + +import content.entity.npc.shop.openShop +import content.entity.player.dialogue.Angry +import content.entity.player.dialogue.Happy +import content.entity.player.dialogue.Neutral +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 world.gregs.voidps.engine.Script + +class Fidelio : Script { + init { + npcOperate("Talk-to", "fidelio") { (target) -> + player("Hello there.") + npc("H-hello. You l-look like a s-stranger to these p-parts. Would you l-like to buy something? I h-have some s- special offers at the m-minute...some s-sample bottles for s-storing s-snail slime.") + choice { + option("Yes, please.") { + openShop(target.def["shop"]) + } + option("No thanks.") { + npc("(sigh) Th-that's okay. Nobody ever w-wants to buy my wares. Oh, s-sure, if it was food or c-clothes they would though!") + } + option("Why are your prices so high?") { + npc("As you p-probably know, the h-humans hate our kind and k-keep us trapped here. To get my s-stocks I have to sneak into the human lands in s-secret!") + } + } + } + } +} diff --git a/game/src/main/kotlin/content/area/morytania/canifis/Malak.kt b/game/src/main/kotlin/content/area/morytania/canifis/Malak.kt new file mode 100644 index 0000000000..4fd8bf66b7 --- /dev/null +++ b/game/src/main/kotlin/content/area/morytania/canifis/Malak.kt @@ -0,0 +1,13 @@ +package content.area.morytania.canifis + +import content.entity.player.dialogue.* +import content.entity.player.dialogue.type.npc +import world.gregs.voidps.engine.Script + +class Malak : Script { + init { + npcOperate("Talk-to", "malak") { + npc("Be lucky I have let you live, meat. Our deal is done, I wish no further dealing with you.") + } + } +} diff --git a/game/src/main/kotlin/content/area/morytania/canifis/Roavar.kt b/game/src/main/kotlin/content/area/morytania/canifis/Roavar.kt new file mode 100644 index 0000000000..c419e31f24 --- /dev/null +++ b/game/src/main/kotlin/content/area/morytania/canifis/Roavar.kt @@ -0,0 +1,168 @@ +package content.area.morytania.canifis + +import content.entity.player.dialogue.* +import content.entity.player.dialogue.type.ChoiceOption +import content.entity.player.dialogue.type.choice +import content.entity.player.dialogue.type.npc +import content.entity.player.dialogue.type.player +import content.entity.player.inv.item.addOrDrop +import world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.inv.inventory +import world.gregs.voidps.engine.inv.remove +import world.gregs.voidps.engine.inv.transact.TransactionError +import world.gregs.voidps.engine.inv.transact.operation.AddItem.add +import world.gregs.voidps.engine.inv.transact.operation.RemoveItem.remove + +class Roavar : Script { + init { + npcOperate("Talk-to", "roavar") { + player("Hello there!") + npc("Greetings traveller. Welcome to 'The Hair Of The Dog' Tavern. What can I do you for?") + choice { + option("Can I buy a beer?") { + npc("Well that's my speciality! The local brew's named 'Moonlight Mead' and will set you back 5 gold. Whaddya say? Fancy a pint?") + choice { + option("Yes please.") { + inventory.transaction { + add("moonlight_mead") + remove("coins", 5) + } + when (inventory.transaction.error) { + is TransactionError.Deficient -> { + player("I don't have the money on me right now though... can I start a tab?") + npc("You see that sign behind me there?") + player("The one that says; 'Please Do Not Ask For Credit As Being Attacked By A Large Angry Werewolf Inn Keeper Often Offends'?") + npc("Bingo.") + } + is TransactionError.Full -> if (inventory.remove("coins", 5) && addOrDrop("moonlight_mead")) { + npc("Here ya go pal. Enjoy!") + } + TransactionError.None -> npc("Here ya go pal. Enjoy!") + else -> {} + } + } + option("Actually, no thanks.") { + npc("Eh, suit yourself. You're missing out on a genuine taste experience.") + } + } + } + option("Can I hear some gossip?") { + npc("Well, I dunno... The village is kind of on the fringe out here, I dunno how up to date the stuff I hear about is...") + choice { + village() + morytania() + shopkeepers() + temple() + } + } + story() + option("Nothing thanks.") { + npc("...I don't know why you talked to me if you don't want anything then...") + } + } + } + } + + private fun ChoiceOption.village() { + option("Tell me about this village.") { + npc("You want to know about Canifis? I dunno why, not a lot happens here. We're just your typical everyday down at earth werewolf folk, after all...") + player("So... everyone here is a werewolf?") + npc("Yep. We are as Zamorak made us!") + player("You mentioned Zamorak...") + npc("Yeah, he's great isn't he? Every year we hold a big festival to give thanks to Zamorak for keeping us well fed and happy here.") + npc("I hear over in the West they worship Saradomin, but I dunno why! What's he ever done to help out us werewolves, huh?") + npc("I hear he has all these crazy followers who say things like we shouldn't kill people and eat them! What's up with that?") + player("...So when is this festival?") + npc("Aaaah, not for a good few months yet. Come and ask me again nearer to the time, I'll keep some extra meat and mead back from the rest for ya pal.") + } + } + + private fun ChoiceOption.story() { + option("Can I hear a story?") { + npc("A story??? Heh, well the only one I can think of right now is one my dear old mammy told me as a pup...") + npc("Now how did it go... Ah yes!") + npc("Once upon a time a brave young wolf was walking through a forest, when he came upon a human dressed all in red. 'Aha!' he thought to himself, 'Here's a nice easy meal!' But the human talked to him, and as he was") + npc("always taught to be a polite wolf, he spoke back to it.") + npc("Well, this cunning human told him that there was a better meal that would not run away in a house in the woods. As the brave young wolf could see that this human was not fully grown, he figured maybe he'd") + npc("better get a better meal at this house, so he ran as fast as his paws could carry him to the house the human told him about... inside he found an old human lying in bed, and although the meat was a little tough, as the") + npc("human was older than he thought, he had a good meal and decided to sleep it off in the house for awhile. He had not been asleep long though, when he was woken by a knocking at the door. The human in red had followed") + npc("him to the house! Suspecting a human trap, the brave young wolf put on the old humans clothes and jumped into the bed, so that he could pretend to be human and escape from this terrible trap! Well now, this human") + npc("dressed in red came into the house, and pretended to believe the brave wolf was really a human, and began to talk to him. But then the human started asking strange questions of the wolf, because the human knew that it") + npc("was not a human at all! The brave young wolf decided to try and escape, for it was only a young human, and not very strong, so the brave young wolf said he would eat the human if they did not let him escape! As he said") + npc("this however, the human in red shouted out at him: 'Aha! You are a wolf!' and as the human shouted this another bigger human ran into the room from outside! This bigger human was much stronger and was") + npc("carrying an axe, and the poor young wolf died in this terrible trap made for him by the humans.") + npc("And do you know what the moral of this story is?") + player("Um... no, not really.") + npc("It's 'Never trust humans' of course! My dear old mammy told me that story when I was a pup, and I'm still alive and well to tell it to you today!") + npc("Pretty good story huh?") + player("Um... yeah. It was great. Really.") + } + } + + private fun ChoiceOption.temple() { + option("Tell me about the temple to the West.") { + player("Tell me about the temple to the west.") + npc("Well, I'm not old enough to remember the full story behind it, but it was a terrible day for our kingdom when it was built there.") + npc("Apparently Morytania had a once strong kingdom, with lands spreading far further west than they do today, and south into the desert, until the day when a sneak attack by the hated human forces who worship") + npc("Saradomin burned our villages and slaughtered our peoples in mass.") + npc("They then cursed the river, so that it would burn our kind should we touch it! Can you imagine??? To make an entire river poisonous to us???") + npc("Luckily they seem to have calmed down somewhat recently, and tend to stay on their side of the river... but the wrong they have done my people will never be forgotten, and will never be forgiven. What is worse is") + npc("that they then had the nerve to build that gigantic statue on the river mouth to mock the slaughter of our people and the poisoning of our river! I can understand Lord Drakan's hatred for them even if I do not share") + npc("it to his extent.") + player("So you hate humans too?") + npc("Absolutely! If I ever met one I would gobble him up where he stands! And then chew the bones for dessert!") + player("...Um, okay, thanks, bye.") + } + } + + private fun ChoiceOption.shopkeepers() { + option("Tell me about the shopkeepers here.") { + npc("Hmmm? Why, who did you want to hear the gossip about?") + choice { + option("Sbott the Tanner.") { + player("Tell me what you know about Sbott the Tanner.") + npc("Hey, I won't hear a word said bad about that guy! He's an honest and hard-working wolf if I ever met one! He charges a lot for his job, but he's one of the best tanners I've ever seen - and I'm over four hundred") + npc("years old! You need stuff tanning, I recommend him!") + } + option("Rufus the food seller.") { + player("Know anything interesting about Rufus the food seller?") + npc("Ah yeah... good old Rufus... He's kind of getting on in years and doesn't like to come out of his wolf form so often, but let me tell you this: that guy is a hunter through and through. You seen how much food he") + npc("catches a day? That takes real dedication! Some of the young pups think just because a wolf has a bit of grey in his fur then he's past it, but I've seen him put those pups to shame in a hunt! He's a real inspiration to us") + npc("all!") + } + option("Barker the clothes seller.") { + player("Got anything to share about Barker the clothes seller?") + npc("Eh, I don't like the guy much, but you can't knock the quality of his stock. They're some fine quality threads you can get yourself there.") + player("What's wrong with him?") + npc("Eh... I can't really tell you to be honest. Something about the guy just gets my hackles up everytime he opens his yap, know what I mean?") + player("No, not really.") + npc("Lucky for you. I can't really explain it, I just don't like the guy. Does great clothes though, you got to give him that.") + } + option("Fidelio the general store owner.") { + player("I bet you have some juicy gossip about Fidelio the general store owner.") + npc("That nut job? Oh sure, we all know about him. He was a real firebrand daredevil when he was younger, always taking risks to make himself a quick buck... he got himself caught up in the smuggling trade, sneaking over") + } + } + } + } + + private fun ChoiceOption.morytania() { + option("Tell me about the land of Morytania.") { + npc("Well, I don't know what to tell you really... This village is called Canifis and lies on the border between Morytania and Misthalin... so we're kind of on the front line if those Saradominists to the west ever decide to") + npc("attack us... South East of here is the castle of Lord Drakan, our master.") + player("Lord Drakan? Who is that?") + npc("Ahhh... you must be new to these parts if you haven't heard of Lord Drakan! He's the lord of this land, and we all pledge allegiance to him.") + npc("In return for our allegiance, and the tithe of course, he keeps our land safe from any invaders and the Saradominists who want to kill us all.") + player("Tithe? What do you mean?") + npc("Ah, well, in return for his protection, we have to give Lord Drakan a share of blood every week. If we don't have any to spare from our hunts, then we need to pick a member of the village to give their life in return") + npc("for the blood so that the tithe is fulfilled.") + player("You mean you kill someone you know in order to meet the tithe???") + npc("That's right, but only if we haven't managed to get enough spare blood from our hunts. It's kind of severe if you look at it that way, but frankly, I think the price is fair in return for his protection and his tolerance of") + npc("our village. He could probably kill us all if he wanted us gone, so keeping on his good side is worth the sacrifice to us. Lucky we're not human really!") + player("Why's that?") + npc("He hates humans! Apparently long ago his brother Draynor was trapped in Misthalin and lost much of his powers, and was recently killed by some human!") + npc("Man, I'd hate to be a human around here, that's for sure. Drakan would really enjoy hunting them down and killing them!") + player("Uh... thanks for the info...") + } + } +} diff --git a/game/src/main/kotlin/content/area/morytania/canifis/Rufus.kt b/game/src/main/kotlin/content/area/morytania/canifis/Rufus.kt new file mode 100644 index 0000000000..be8a301803 --- /dev/null +++ b/game/src/main/kotlin/content/area/morytania/canifis/Rufus.kt @@ -0,0 +1,32 @@ +package content.area.morytania.canifis + +import content.entity.npc.shop.openShop +import content.entity.player.dialogue.* +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 + +class Rufus : Script { + init { + npcOperate("Talk-to", "rufus") { (target) -> + player("Hi!") + npc("Grrreetings frrriend! Welcome to my worrrld famous food emporrrium! All my meats are so frrresh you'd swear you killed them yourrrself!") + choice { + option("Why do you only sell meats?") { + npc("What? Why, what else would you want to eat? What kind of lycanthrrrope are you anyway?") + player("...A vegetarian one?") + npc("Vegetarrrian...?") + player("Never mind.") + } + option("Do you sell cooked food?") { + npc("Cooked food? Who would want that? You lose all the flavourrr of the meat when you can't taste the blood!") + } + option("Can I buy some food?") { + npc("Cerrrtainly!") + openShop(target.def["shop"]) + } + } + } + } +} diff --git a/game/src/main/kotlin/content/area/morytania/canifis/Sbott.kt b/game/src/main/kotlin/content/area/morytania/canifis/Sbott.kt new file mode 100644 index 0000000000..312dd80b1c --- /dev/null +++ b/game/src/main/kotlin/content/area/morytania/canifis/Sbott.kt @@ -0,0 +1,51 @@ +package content.area.morytania.canifis + +import content.entity.player.dialogue.Happy +import content.entity.player.dialogue.Neutral +import content.entity.player.dialogue.Quiz +import content.entity.player.dialogue.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.inv.inventory + +class Sbott : Script { + init { + npcOperate("Talk-to", "sbott") { (target) -> + npc("Hello stranger. Would you like me to tan any hides for you?") + npc("Soft leather - 2 gp per hide
Hard leather - 5 gp per hide
Snakeskins - 25 gp per hide
Dragon leather - 45 gp per hide.") + choice("What would you like to say?") { + val hides = inventory.items.any { it.id == "cowhide" || it.id.startsWith("snake_hide") || it.id.endsWith("dragonhide") } + if (hides) { + option("Yes please.") { + set("tanner", "sbott") + open("tanner") + } + } + option("Why are you so expensive?") { + player("Why are you so expensive? The tanner in Al Kharid is almost half the price!") + npc("Hey, I charge more because I'm worth it! I deal in bulk, and I work extremely quickly. You'll see for yourself!") + npc("You got a lot of hides you want tanning quickly? I'm your guy!") + npc("So you got hides for me to tan, or are you just gonna bust my chops about prices all day?") + player("No thanks, I haven't any hides.") + npc("Fair enough. I can't tan what you don't bring me.") + } + if (hides) { + option("No thanks, I'm not interested.") { + npc("Okay; you change your mind, you come see me. I'm your guy!") + } + } else { + option("No thanks, I haven't any hides.") { + npc("Fair enough. I can't tan what you don't bring me.") + } + } + } + } + + npcOperate("Trade", "sbott") { + set("tanner", "sbott") + open("tanner") + } + } +} diff --git a/game/src/main/kotlin/content/area/morytania/canifis/Taxidermist.kt b/game/src/main/kotlin/content/area/morytania/canifis/Taxidermist.kt new file mode 100644 index 0000000000..a6ae3f458b --- /dev/null +++ b/game/src/main/kotlin/content/area/morytania/canifis/Taxidermist.kt @@ -0,0 +1,122 @@ +package content.area.morytania.canifis + +import content.entity.player.dialogue.Confused +import content.entity.player.dialogue.Happy +import content.entity.player.dialogue.Laugh +import content.entity.player.dialogue.Neutral +import content.entity.player.dialogue.Quiz +import content.entity.player.dialogue.Sad +import content.entity.player.dialogue.Shock +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.chat.toDigitGroupString +import world.gregs.voidps.engine.entity.Spawn.Companion.player +import world.gregs.voidps.engine.inv.inventory +import world.gregs.voidps.engine.inv.transact.TransactionError +import world.gregs.voidps.engine.inv.transact.operation.RemoveItem.remove +import world.gregs.voidps.engine.inv.transact.operation.ReplaceItem.replace + +class Taxidermist : Script { + init { + npcOperate("Talk-to", "taxidermist") { (target) -> + npc("Oh, hello. Have you got something you want preserving?") + choice { + option("Yes please") { + player("Yes please.") + npc("Give it to me to look at then.") + } + option("Not right now") { + player("Not right now.") + npc("Well, you go kill things so I can stuff them, eh?") + } + option("What?") { + npc("If you bring me a monster head or a very big fish, I can preserve it for you so you can mount it in your house.") + npc("I hear there are all sorts of exotic creatures in the Slayer Tower -- I'd like a chance to stuff one of them!") + } + } + } + + itemOnNPCOperate(npc = "taxidermist") { + var price = 0 + when (it.item.id) { + "raw_chicken" -> { + npc("Killing a chicken is hardly worth boasting about!") + return@itemOnNPCOperate + } + "raw_cavefish", "raw_rocktail", "raw_monkfish", "raw_karambwanji", "leaping_trout", "leaping_salmon", "leaping_sturgeon", "raw_shrimps", "raw_anchovies", "raw_sardine", "raw_salmon", "raw_trout", "raw_cod", "raw_herring", "raw_pike", "raw_mackerel", "raw_tuna", "raw_bass", "raw_swordfish", "raw_lobster", "raw_shark", "raw_manta_ray", "raw_sea_turtle", "raw_karambwan", "raw_rainbow_fish", "raw_crayfish" -> { + npc("That's a pretty ordinary fish, isn't it? Not really worth preserving.") + player("You should have seen the one that got away!") + return@itemOnNPCOperate + } + "big_bass" -> { + npc("That's a mighty fine sea bass you've caught there.") + price = 1_000 + } + "big_swordfish" -> { + npc("Don't point that thing at me!") + price = 2_500 + } + "big_shark" -> { + npc("That's quite a fearsome shark! You've done everyone a service by removing it from the sea!") + price = 5_000 + } + "crawling_hand" -> { + npc("That's a very fine crawling hand.") + price = 1_000 + } + "cockatrice_head" -> { + npc("A cockatrice! Beautiful, isn't it? Look at the plumage!") + price = 2_000 + } + "basilisk_head" -> { + npc("My, he's a scary-looking fellow, isn't he? He'll look good on your wall!") + price = 4_000 + } + "kurask_head" -> { + npc("A kurask? Splendid! Look at those horns!") + price = 6_000 + } + "abyssal_head" -> { + npc("Goodness, an abyssal demon!") + npc("See how it's still glowing? I'll have to use some magic to preserve that.") + price = 12_000 + } + "king_black_dragon_head" -> { + npc("Three?! This must be a King Black Dragon!") + npc("I'll have to get out my heavy duty tools -- this skin's as tough as iron!") + price = 50_000 + } + "kalphite_queen_head" -> { + npc("That must be the biggest kalphite I've ever seen!") + npc("Preserving insects is always tricky. I'll have to be careful...") + price = 50_000 + } + else -> { + npc("Don't be silly, I can't preserve that!") + return@itemOnNPCOperate + } + } + npc("I can preserve that for you for ${price.toDigitGroupString()} coins.") + if (inventory.count("coins") < price) { + player("Maybe another time.") + return@itemOnNPCOperate + } + choice { + option("Yes please.") { + inventory.transaction { + remove("coins", price) + replace(it.item.id, "${it.item.id}_stuffed") + } + if (inventory.transaction.error != TransactionError.None) { + npc("There you go!") + } + } + option("No thanks.") { + npc("All right, come back if you change your mind, eh?") + } + } + } + } +} diff --git a/game/src/main/kotlin/content/area/morytania/canifis/Ulizius.kt b/game/src/main/kotlin/content/area/morytania/canifis/Ulizius.kt new file mode 100644 index 0000000000..3ac6a0c9cb --- /dev/null +++ b/game/src/main/kotlin/content/area/morytania/canifis/Ulizius.kt @@ -0,0 +1,56 @@ +package content.area.morytania.canifis + +import content.entity.obj.door.enterDoor +import content.entity.player.dialogue.Happy +import content.entity.player.dialogue.Neutral +import content.entity.player.dialogue.Quiz +import content.entity.player.dialogue.Scared +import content.entity.player.dialogue.type.npc +import content.entity.player.dialogue.type.player +import content.quest.quest +import content.quest.questCompleted +import world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.client.ui.dialogue.talkWith +import world.gregs.voidps.engine.entity.character.npc.NPCs +import world.gregs.voidps.engine.entity.character.player.Player +import world.gregs.voidps.type.Region + +class Ulizius : Script { + init { + npcOperate("Talk-to", "ulizius") { + player("Hello there.") + npc("What... Oh, don't creep up on me like that... I thought you were a Ghast!") + canIGoThrough() + } + + objectOperate("Open", "gate_mort_myre*_closed") { (target) -> + val ulizius = NPCs.findOrNull(Region(13622).toLevel(0), "ulizius") ?: return@objectOperate + val enter = tile.y >= target.tile.y + if (enter && !questCompleted("nature_spirit")) { + talkWith(ulizius) + canIGoThrough() + return@objectOperate + } + enterDoor(target) + if (!enter) { + ulizius.say("Oh my! You're still alive!") + } + } + } + + private suspend fun Player.canIGoThrough() { + player("Can I go through the gate please?") + if (questCompleted("nature_spirit")) { + npc("Yes of course my friend, you seem to be able to handle yourself with those ghasts really well.") + } else { + npc("Absolutely not! I've been given strict instructions not to let anyone through. It's just too dangerous. No one gets in without Drezels say so!") + if (quest("nature_spirit") == "unstarted") { + player("Where is Drezel?") + npc("Oh, he's in the temple, just go back over the bridge, down the ladder and along the hallway, you can't miss him.") + } else { + player("But I'm doing a quest for Drezel!") + npc("Ok, on your way then!") + } + } + } +} diff --git a/game/src/main/kotlin/content/area/morytania/canifis/Werewolf.kt b/game/src/main/kotlin/content/area/morytania/canifis/Werewolf.kt new file mode 100644 index 0000000000..8df53d99a3 --- /dev/null +++ b/game/src/main/kotlin/content/area/morytania/canifis/Werewolf.kt @@ -0,0 +1,74 @@ +package content.area.morytania.canifis + +import content.entity.effect.transform +import content.entity.player.dialogue.Angry +import content.entity.player.dialogue.Neutral +import content.entity.player.dialogue.Quiz +import content.entity.player.dialogue.Shock +import content.entity.player.dialogue.type.npc +import content.entity.player.dialogue.type.player +import world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.client.instruction.handle.interactPlayer +import world.gregs.voidps.engine.entity.character.areaSound +import world.gregs.voidps.engine.entity.character.mode.EmptyMode +import world.gregs.voidps.engine.entity.character.mode.PauseMode +import world.gregs.voidps.engine.entity.character.player.Player +import world.gregs.voidps.engine.entity.character.player.equip.equipped +import world.gregs.voidps.engine.queue.softQueue +import world.gregs.voidps.network.login.protocol.visual.update.player.EquipSlot +import world.gregs.voidps.type.random + +class Werewolf : Script { + init { + val werewolves = "alexis,boris,eduard,galina,georgy,imre,irina,joseph,ksenia,lev,liliya,milla,nikita,nikolai,sofiya,svetlana,vera,yadviga,yuri,zoja" + npcOperate("Talk-to", werewolves) { + if (equipped(EquipSlot.Ring).id.startsWith("ring_of_charos")) { + when (random.nextInt(10)) { + 0 -> npc("Fancy going up to the castle for a bit of a snack?") + 1 -> npc("Seen any humans around here? I'm v-e-r-y hungry.") + 2 -> npc("I bet you have wonderful paws.") + 3 -> npc("A very miserable day, altogether... enjoy it while it lasts.") + 4 -> npc("You look to me like someone with a healthy taste for blood.") + 5 -> npc("If you catch anyone promise me you'll share.") + 6 -> npc("Give me a moment, I have a bit of someone stuck in my teeth...") + 7 -> npc("I haven't smelt you around here before...") + 8 -> npc("Good day to you, my friend.") + else -> npc("You smell familiar...") + } + return@npcOperate + } + when (random.nextInt(10)) { + 0 -> npc("Leave me alone.") + 1 -> npc("If I were as ugly as you I would not dare to show my face in public!") + 2 -> npc("I have no interest in talking to a pathetic meat bag like yourself.") + 3 -> npc("Don't talk to me again if you value your life!") + 4 -> npc("I don't have anything to give you so leave me alone, mendicant.") + 5 -> npc("Out of my way, punk.") + 6 -> npc("Get lost!") + 7 -> npc("Have you no manners?") + 8 -> npc("I don't have time for this right now.") + else -> { + npc("Hmm... you smell strange...") + player("Strange how?") + npc("Like a human!") + player("Oh! Er... I just ate one is why!") + } + } + } + + npcCombatDamage(werewolves) { (source) -> + if (transform != "") { + return@npcCombatDamage + } + areaSound("lycanthropy", tile, radius = 5) + source.mode = EmptyMode + mode = PauseMode + softQueue("werewolf_transform", 1) { + transform("werewolf") + if (source is Player) { + interactPlayer(source, "Attack") + } + } + } + } +} diff --git a/game/src/main/kotlin/content/entity/obj/Mill.kt b/game/src/main/kotlin/content/entity/obj/Windmill.kt similarity index 57% rename from game/src/main/kotlin/content/entity/obj/Mill.kt rename to game/src/main/kotlin/content/entity/obj/Windmill.kt index 248d108f88..457de33bc1 100644 --- a/game/src/main/kotlin/content/entity/obj/Mill.kt +++ b/game/src/main/kotlin/content/entity/obj/Windmill.kt @@ -6,18 +6,21 @@ import content.entity.player.dialogue.type.player import content.quest.quest import world.gregs.voidps.engine.Script import world.gregs.voidps.engine.client.message +import world.gregs.voidps.engine.entity.character.player.Player import world.gregs.voidps.engine.entity.character.sound import world.gregs.voidps.engine.entity.obj.replace import world.gregs.voidps.engine.inv.* -class Mill : Script { +class Windmill : Script { init { playerSpawn { sendVariable("flour_bin") } - objectOperate("Operate", "hopper_controls") { (target) -> + // TODO: Fix Zanaris Flour Bin object definition which makes it invisible if it contains more than 1 flour. + + objectOperate("Operate", "hopper_controls*") { (target) -> if (get("flour_bin", 0) == 30) { message("The flour bin downstairs is full, I should empty it first.") return@objectOperate @@ -39,42 +42,38 @@ class Mill : Script { } } - itemOnObjectOperate("grain", "hopper") { - if (quest("cooks_assistant") != "started") { - anim("fill_hopper") - inventory.remove("grain") - set("hopper_bin", 1) - message("You put the grain in the hopper. You should now pull the lever nearby to operate the hopper.") - return@itemOnObjectOperate - } - if (get("cooks_assistant_talked_to_millie", 0) == 0) { - player("Hmm. I should probably ask that lady downstairs how I can make extra fine flour.") - return@itemOnObjectOperate - } - if (carriesItem("extra_fine_flour")) { - message("It'd be best to take the extra fine flour you already have to the cook first.") - return@itemOnObjectOperate - } - if (bank.contains("extra_fine_flour")) { - message("It'd be best to take the extra fine flour you already have in your bank to the cook first.") - return@itemOnObjectOperate + itemOnObjectOperate("grain", "hopper*") { (target) -> + if (target.id == "hopper") { + if (quest("cooks_assistant") != "started") { + fillGrain() + return@itemOnObjectOperate + } + if (get("cooks_assistant_talked_to_millie", 0) == 0) { + player("Hmm. I should probably ask that lady downstairs how I can make extra fine flour.") + return@itemOnObjectOperate + } + if (carriesItem("extra_fine_flour")) { + message("It'd be best to take the extra fine flour you already have to the cook first.") + return@itemOnObjectOperate + } + if (bank.contains("extra_fine_flour")) { + message("It'd be best to take the extra fine flour you already have in your bank to the cook first.") + return@itemOnObjectOperate + } } if (get("hopper_bin", 0) == 1) { message("There is already grain in the hopper.") } else { - anim("fill_hopper") - inventory.remove("grain") - set("hopper_bin", 1) - message("You put the grain in the hopper. You should now pull the lever nearby to operate the hopper.") + fillGrain() } } - objectOperate("Take-flour", "flour_bin") { + objectOperate("Take-flour", "flour_bin*") { (target) -> if (!carriesItem("empty_pot")) { message("You need an empty pot to hold the flour in.") return@objectOperate } - if (quest("cooks_assistant") == "started" && get("cooks_assistant_talked_to_millie", 0) == 1) { + if (target.id == "flour_bin_3" && quest("cooks_assistant") == "started" && get("cooks_assistant_talked_to_millie", 0) == 1) { inventory.remove("empty_pot") if (carriesItem("extra_fine_flour") || bank.contains("extra_fine_flour")) { inventory.add("pot_of_flour") @@ -91,4 +90,12 @@ class Mill : Script { } } } + + private fun Player.fillGrain() { + anim("fill_hopper") + inventory.remove("grain") + set("hopper_bin", 1) + sound("fill_grain") + message("You put the grain in the hopper. You should now pull the lever nearby to operate the hopper.") + } } diff --git a/game/src/main/kotlin/content/quest/Quest.kt b/game/src/main/kotlin/content/quest/Quest.kt index a4b8dc2c9e..df39b3433e 100644 --- a/game/src/main/kotlin/content/quest/Quest.kt +++ b/game/src/main/kotlin/content/quest/Quest.kt @@ -21,6 +21,7 @@ val quests = setOf( // members "druidic_ritual", "plague_city", + "lost_city", // mini-quests "enter_the_abyss", ) diff --git a/game/src/main/kotlin/content/quest/member/fairy_tale_part_2/fairy_ring/FairyRing.kt b/game/src/main/kotlin/content/quest/member/fairy_tale_part_2/fairy_ring/FairyRing.kt index 939b590263..ccdc977bb0 100644 --- a/game/src/main/kotlin/content/quest/member/fairy_tale_part_2/fairy_ring/FairyRing.kt +++ b/game/src/main/kotlin/content/quest/member/fairy_tale_part_2/fairy_ring/FairyRing.kt @@ -23,6 +23,9 @@ class FairyRing(val fairyRing: FairyRingCodes, val variableDefinitions: Variable init { objectOperate("Use", "fairy_ring_*") { (target) -> + if (target.id == "fairy_ring_zanaris") { + return@objectOperate + } if (quest("fairy_tale_ii") == "unstarted") { message("You don't have permission to use that fairy ring.") return@objectOperate diff --git a/game/src/main/kotlin/content/skill/magic/jewellery/JewelleryTeleport.kt b/game/src/main/kotlin/content/skill/magic/jewellery/JewelleryTeleport.kt index 9cf0c180c1..86512eaf64 100644 --- a/game/src/main/kotlin/content/skill/magic/jewellery/JewelleryTeleport.kt +++ b/game/src/main/kotlin/content/skill/magic/jewellery/JewelleryTeleport.kt @@ -35,7 +35,7 @@ fun itemTeleport(player: Player, tile: Tile, type: String, force: Boolean = fals } player.closeInterfaces() player.queue("teleport_$type", onCancel = null) { - player.sound("teleport") + player.sound("teleport_$type") player.gfx("teleport_$type") player.animDelay("teleport_$type") player.tele(tile) diff --git a/game/src/test/kotlin/content/area/misthalin/zanaris/ZanarisShortcutTest.kt b/game/src/test/kotlin/content/area/misthalin/zanaris/ZanarisShortcutTest.kt new file mode 100644 index 0000000000..ef0c8680fd --- /dev/null +++ b/game/src/test/kotlin/content/area/misthalin/zanaris/ZanarisShortcutTest.kt @@ -0,0 +1,73 @@ +package content.area.misthalin.zanaris + +import FakeRandom +import WorldTest +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.DynamicTest.dynamicTest +import org.junit.jupiter.api.TestFactory +import world.gregs.voidps.engine.client.instruction.handle.interactObject +import world.gregs.voidps.engine.entity.character.player.skill.Skill +import world.gregs.voidps.engine.entity.obj.GameObjects +import world.gregs.voidps.type.Direction +import world.gregs.voidps.type.Tile +import world.gregs.voidps.type.setRandom + +class ZanarisShortcutTest : WorldTest() { + + private val data = listOf( + Pair(Tile(2400, 4404), Direction.SOUTH), + Pair(Tile(2400, 4402), Direction.NORTH), + Pair(Tile(2415, 4403), Direction.SOUTH), + Pair(Tile(2415, 4401), Direction.NORTH), + Pair(Tile(2408, 4396), Direction.SOUTH), + Pair(Tile(2408, 4394), Direction.NORTH), + ) + + @TestFactory + fun `Shortcut success`() = data.map { (tile, dir) -> + dynamicTest("$tile shortcut success $dir") { + setRandom(object : FakeRandom() { + override fun nextInt(until: Int) = 0 + }) + val player = createPlayer(tile) + player.levels.set(Skill.Agility, 66) + val wall = GameObjects.find(tile.add(dir), "zanaris_jutting_wall") + player.interactObject(wall, "Squeeze-past") + tick(5) + assertEquals(100, player.levels.get(Skill.Constitution)) + assertEquals(10.0, player.experience.get(Skill.Agility)) + assertEquals(tile.add(dir).add(dir), player.tile) + } + } + + @TestFactory + fun `Shortcut failure`() = data.map { (tile, dir) -> + dynamicTest("$tile shortcut failure $dir") { + setRandom(object : FakeRandom() { + override fun nextInt(until: Int) = until + }) + val player = createPlayer(tile) + player.levels.set(Skill.Agility, 66) + val wall = GameObjects.find(tile.add(dir), "zanaris_jutting_wall") + player.interactObject(wall, "Squeeze-past") + tick(6) + assertEquals(60, player.levels.get(Skill.Constitution)) + assertEquals(6.0, player.experience.get(Skill.Agility)) + assertEquals(tile.add(dir).add(dir), player.tile) + } + } + + @TestFactory + fun `Shortcut without level`() = data.map { (tile, dir) -> + dynamicTest("$tile shortcut without level $dir") { + val player = createPlayer(tile) + player.levels.set(Skill.Agility, 45) + val wall = GameObjects.find(tile.add(dir), "zanaris_jutting_wall") + player.interactObject(wall, "Squeeze-past") + tick(5) + assertEquals(100, player.levels.get(Skill.Constitution)) + assertEquals(0.0, player.experience.get(Skill.Agility)) + assertEquals(tile, player.tile) + } + } +} diff --git a/game/src/test/kotlin/content/quest/member/lost_city/LostCityTest.kt b/game/src/test/kotlin/content/quest/member/lost_city/LostCityTest.kt new file mode 100644 index 0000000000..94a9ba8dd5 --- /dev/null +++ b/game/src/test/kotlin/content/quest/member/lost_city/LostCityTest.kt @@ -0,0 +1,92 @@ +package content.quest.member.lost_city + +import WorldTest +import dialogueContinue +import dialogueOption +import equipItem +import itemOnItem +import npcOption +import org.junit.jupiter.api.Test +import world.gregs.voidps.engine.client.instruction.handle.interactObject +import world.gregs.voidps.engine.entity.character.move.tele +import world.gregs.voidps.engine.entity.character.npc.NPCs +import world.gregs.voidps.engine.entity.character.player.skill.Skill +import world.gregs.voidps.engine.entity.obj.GameObjects +import world.gregs.voidps.engine.inv.add +import world.gregs.voidps.engine.inv.inventory +import world.gregs.voidps.type.Tile +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +class LostCityTest : WorldTest() { + override var loadNpcs = true + + @Test + fun `Complete lost city quest`() { + val player = createPlayer(Tile(3150, 3207)) + player.levels.set(Skill.Woodcutting, 36) + player.levels.set(Skill.Crafting, 35) + player.inventory.add("bronze_hatchet") + player.inventory.add("knife") + player["auto_retaliate"] = true + player["god_mode"] = true + player["insta_kill"] = true + + val warrior = NPCs.find(player.tile.regionLevel, "warrior_lumbridge") + player.npcOption(warrior, "Talk-to") + tick(2) + player.dialogueContinue(1) + player.dialogueOption("line1") + player.dialogueContinue(2) + player.dialogueOption("line1") // Who's zanaris? + player.dialogueContinue(2) + player.dialogueOption("line1") // How find? + player.dialogueContinue(2) + player.dialogueOption("line2") // You don't know + player.dialogueContinue(3) + assertEquals("started", player["lost_city", "unstarted"]) + + player.tele(3138, 3211) + var tree = GameObjects.find(Tile(3138, 3212), "lost_city_tree") + player.interactObject(tree, "Chop") + tick(2) + player.dialogueContinue(5) + player.dialogueOption("line2") // Been in that shed + player.dialogueContinue(7) + assertEquals("find_staff", player["lost_city", "unstarted"]) + + player.tele(2821, 3374) + val monk = NPCs.find(Tile(2822, 3374), "cave_monk") + player.npcOption(monk, "Talk-to") + tick(1) + player.dialogueContinue(3) + player.dialogueOption("line2") // Risk it + player.dialogueContinue(1) + assertEquals(Tile(2822, 9774), player.tile) + + player.tele(2859, 9735) + tree = GameObjects.find(Tile(2860, 9734), "dramen_tree") + player.interactObject(tree, "Chop down") + tick(2) + assertEquals("tree_spirit", player["lost_city", "unstarted"]) + // Fight tree spirit + tick(4) + assertEquals("spirit_killed", player["lost_city", "unstarted"]) + player.interactObject(tree, "Chop down") + tick(5) + + assertTrue(player.inventory.contains("dramen_branch")) + player.itemOnItem(1, 2) + tick(2) + assertTrue(player.inventory.contains("dramen_staff")) + + player.tele(3201, 3169) + player.equipItem("dramen_staff") + val door = GameObjects.find(Tile(3201, 3169), "zanaris_door_closed") + player.interactObject(door, "Open") + tick(9) + assertEquals(Tile(2452, 4473), player.tile) + assertEquals(3, player["quest_points", 0]) + assertEquals("completed", player["lost_city", "unstarted"]) + } +}