diff --git a/data/area/kharidian_desert/pollnivneach/dungeon/pollninveach_dungeon.npc-spawns.toml b/data/area/kharidian_desert/pollnivneach/dungeon/pollninveach_dungeon.npc-spawns.toml index 0f51dd68c2..d55aeceb00 100644 --- a/data/area/kharidian_desert/pollnivneach/dungeon/pollninveach_dungeon.npc-spawns.toml +++ b/data/area/kharidian_desert/pollnivneach/dungeon/pollninveach_dungeon.npc-spawns.toml @@ -28,13 +28,13 @@ spawns = [ { id = "basilisk", x = 3319, y = 4295 }, { id = "basilisk", x = 3322, y = 4292 }, { id = "basilisk", x = 3324, y = 4302 }, - { id = "basilisk_pollninveach_dungeon", x = 3302, y = 4318 }, - { id = "basilisk_pollninveach_dungeon", x = 3302, y = 4325 }, - { id = "basilisk_pollninveach_dungeon", x = 3306, y = 4329 }, - { id = "basilisk_pollninveach_dungeon", x = 3308, y = 4324 }, - { id = "basilisk_pollninveach_dungeon", x = 3311, y = 4303 }, - { id = "basilisk_pollninveach_dungeon", x = 3311, y = 4314 }, - { id = "basilisk_pollninveach_dungeon", x = 3317, y = 4320 }, + { id = "basilisk_pollninveach", x = 3302, y = 4318 }, + { id = "basilisk_pollninveach", x = 3302, y = 4325 }, + { id = "basilisk_pollninveach", x = 3306, y = 4329 }, + { id = "basilisk_pollninveach", x = 3308, y = 4324 }, + { id = "basilisk_pollninveach", x = 3311, y = 4303 }, + { id = "basilisk_pollninveach", x = 3311, y = 4314 }, + { id = "basilisk_pollninveach", x = 3317, y = 4320 }, { id = "turoth", x = 3271, y = 4295 }, { id = "turoth", x = 3280, y = 4308 }, { id = "turoth", x = 3282, y = 4313 }, @@ -86,19 +86,19 @@ spawns = [ { id = "cave_crawler_pollnivneach", x = 3311, y = 4407, members = true }, { id = "cave_crawler_pollnivneach", x = 3319, y = 4407, members = true }, { id = "cave_crawler_pollnivneach", x = 3323, y = 4402, members = true }, - { id = "aberrant_spectre_pollninveach_dungeon", x = 3283, y = 4346, members = true }, - { id = "aberrant_spectre_pollninveach_dungeon", x = 3308, y = 4349, members = true }, - { id = "aberrant_spectre_pollninveach_dungeon", x = 3293, y = 4375, members = true }, - { id = "aberrant_spectre_pollninveach_dungeon", x = 3303, y = 4363, members = true }, - { id = "aberrant_spectre_pollninveach_dungeon_2", x = 3288, y = 4350, members = true }, - { id = "aberrant_spectre_pollninveach_dungeon_2", x = 3296, y = 4340, members = true }, - { id = "aberrant_spectre_pollninveach_dungeon_2", x = 3288, y = 4361, members = true }, - { id = "aberrant_spectre_pollninveach_dungeon_2", x = 3310, y = 4355, members = true }, - { id = "aberrant_spectre_pollninveach_dungeon_3", x = 3297, y = 4347, members = true }, - { id = "aberrant_spectre_pollninveach_dungeon_3", x = 3315, y = 4346, members = true }, - { id = "aberrant_spectre_pollninveach_dungeon_3", x = 3282, y = 4357, members = true }, - { id = "aberrant_spectre_pollninveach_dungeon_3", x = 3303, y = 4369, members = true }, - { id = "aberrant_spectre_pollninveach_dungeon_4", x = 3279, y = 4350, members = true }, - { id = "aberrant_spectre_pollninveach_dungeon_4", x = 3294, y = 4353, members = true }, - { id = "aberrant_spectre_pollninveach_dungeon_4", x = 3294, y = 4366, members = true }, + { id = "aberrant_spectre_pollninveach", x = 3283, y = 4346, members = true }, + { id = "aberrant_spectre_pollninveach", x = 3308, y = 4349, members = true }, + { id = "aberrant_spectre_pollninveach", x = 3293, y = 4375, members = true }, + { id = "aberrant_spectre_pollninveach", x = 3303, y = 4363, members = true }, + { id = "aberrant_spectre_pollninveach_2", x = 3288, y = 4350, members = true }, + { id = "aberrant_spectre_pollninveach_2", x = 3296, y = 4340, members = true }, + { id = "aberrant_spectre_pollninveach_2", x = 3288, y = 4361, members = true }, + { id = "aberrant_spectre_pollninveach_2", x = 3310, y = 4355, members = true }, + { id = "aberrant_spectre_pollninveach_3", x = 3297, y = 4347, members = true }, + { id = "aberrant_spectre_pollninveach_3", x = 3315, y = 4346, members = true }, + { id = "aberrant_spectre_pollninveach_3", x = 3282, y = 4357, members = true }, + { id = "aberrant_spectre_pollninveach_3", x = 3303, y = 4369, members = true }, + { id = "aberrant_spectre_pollninveach_4", x = 3279, y = 4350, members = true }, + { id = "aberrant_spectre_pollninveach_4", x = 3294, y = 4353, members = true }, + { id = "aberrant_spectre_pollninveach_4", x = 3294, y = 4366, members = true }, ] diff --git a/data/area/kharidian_desert/pollnivneach/dungeon/pollninveach_dungeon.npcs.toml b/data/area/kharidian_desert/pollnivneach/dungeon/pollninveach_dungeon.npcs.toml index 74bad03152..c97a100f54 100644 --- a/data/area/kharidian_desert/pollnivneach/dungeon/pollninveach_dungeon.npcs.toml +++ b/data/area/kharidian_desert/pollnivneach/dungeon/pollninveach_dungeon.npcs.toml @@ -4,7 +4,7 @@ id = 13214 [iwazaru_pollninveach_dungeon] id = 13225 -[basilisk_pollninveach_dungeon] +[basilisk_pollninveach] id = 1617 hitpoints = 750 att = 30 @@ -20,17 +20,32 @@ examine = "The eyes of evil." [catolax_pollninveach_dungeon] id = 7783 -[aberrant_spectre_pollninveach_dungeon] +[aberrant_spectre_pollninveach] id = 7801 +str = 70 +def = 90 +mage = 115 +combat_def = "aberrant_spectre" +hunt_mode = "aggressive" +wander_range = 4 +respawn_delay = 30 +slayer_xp = 110.0 +slayer_level = 60 +drop_table = "aberrant_spectre" +categories = ["aberrant_spectre"] +examine = "A very smelly ghost." -[aberrant_spectre_pollninveach_dungeon_2] +[aberrant_spectre_pollninveach_2] id = 7802 +clone = "aberrant_spectre_pollninveach" -[aberrant_spectre_pollninveach_dungeon_3] +[aberrant_spectre_pollninveach_3] id = 7803 +clone = "aberrant_spectre_pollninveach" -[aberrant_spectre_pollninveach_dungeon_4] +[aberrant_spectre_pollninveach_4] id = 7804 +clone = "aberrant_spectre_pollninveach" [catolax_pollninveach_dungeon_2] id = 7782 @@ -40,3 +55,92 @@ id = 13215 [iwazaru_pollninveach_dungeon_2] id = 13226 + +[monstrous_cave_crawler] +id = 7798 +hitpoints = 2650 +slayer_level = 10 +slayer_xp = 1000.0 +# Made up stats +range = 200 +categories = ["cave_crawler"] +hunt_mode = "aggressive" +combat_def = "monstrous_cave_crawler" +examine = "It oozes venom from every pore." + +[kurask_overlord] +id = 7797 +hitpoints = 2500 +slayer_level = 70 +slayer_xp = 1000.0 +# Made up stats +att = 220 +str = 220 +def = 200 +categories = ["kurask"] +hunt_mode = "aggressive" +combat_def = "kurask" +max_hit_crush = 300 +examine = "A bulky Kurask." + +[kurask_minion] +id = 7805 +hitpoints = 950 +slayer_level = 70 +slayer_xp = 95.0 +# Made up stats +att = 150 +str = 150 +def = 100 +categories = ["kurask"] +hunt_mode = "aggressive" +combat_def = "kurask_minion" +drop_table = "bones" +examine = "Large, heavy, with sharp things attached to its head." + +[basilisk_boss] +id = 7799 +hitpoints = 2700 +slayer_level = 40 +slayer_xp = 1000.0 +# Made up stats +att = 200 +str = 200 +def = 175 +magic = 210 +height = 10 +categories = ["basilisk"] +hunt_mode = "aggressive" +combat_def = "basilisk_boss" +drop_table = "bones" +examine = "It radiates an unhealthy aura." + +[turoth_mightiest] +id = 7800 +hitpoints = 1750 +slayer_level = 55 +slayer_xp = 391.0 +# Made up stats +att = 160 +str = 180 +def = 150 +categories = ["turoth"] +immune_poison = true +hunt_mode = "aggressive" +combat_def = "mightiest_turoth" +examine = "Something tells you it has many minions" + +[turoth_swarming] +id = 1611 +hitpoints = 100 +slayer_level = 55 +slayer_xp = 10.0 +# Made up stats +att = 100 +str = 100 +def = 80 +drop_table = "bones" +hunt_mode = "aggressive" +combat_def = "turoth_small" +max_hit_stab = 110 +examine = "A vicious bite on a feeble body." diff --git a/data/area/kharidian_desert/pollnivneach/dungeon/pollnivneach_dungeon.anims.toml b/data/area/kharidian_desert/pollnivneach/dungeon/pollnivneach_dungeon.anims.toml new file mode 100644 index 0000000000..db66a9399d --- /dev/null +++ b/data/area/kharidian_desert/pollnivneach/dungeon/pollnivneach_dungeon.anims.toml @@ -0,0 +1,17 @@ +[monstrous_cave_crawler_attack] +id = 9414 + +[monstrous_cave_crawler_defend] +id = 9416 + +[monstrous_cave_crawler_death] +id = 9418 + +[monstrous_cave_crawler_fling] +id = 9419 + +[basilisk_boss_attack] +id = 9533 + +[mightest_turoth_attack] +id = 9413 \ No newline at end of file diff --git a/data/area/kharidian_desert/pollnivneach/dungeon/pollnivneach_dungeon.combat.toml b/data/area/kharidian_desert/pollnivneach/dungeon/pollnivneach_dungeon.combat.toml new file mode 100644 index 0000000000..d8ec199e02 --- /dev/null +++ b/data/area/kharidian_desert/pollnivneach/dungeon/pollnivneach_dungeon.combat.toml @@ -0,0 +1,68 @@ +[monstrous_cave_crawler] +attack_speed = 4 +retreat_range = 8 +defend_anim = "monstrous_cave_crawler_defend" +defend_sound = "cave_crawler_defend" +death_anim = "monstrous_cave_crawler_death" +death_sound = "cave_crawler_death" + +[monstrous_cave_crawler.melee] +range = 1 +anim = "monstrous_cave_crawler_attack" +target_sound = "cave_crawler_attack" +target_hit = { offense = "stab", defence = "range", max = 240 } + +[monstrous_cave_crawler.poison] +range = 8 +anim = "monstrous_cave_crawler_fling" +gfx = "monstrous_cave_crawler_cast" +target_sound = "cave_crawler_attack" +projectile = "monstrous_cave_crawler_travel" +impact_gfx = "monstrous_cave_crawler_impact" +target_hit = { offense = "range", max = 240 } +impact_poison = 80 + +[kurask_minion] +attack_speed = 6 +clone = "kurask" + +[kurask_minion.attack] +clone = "kurask.attack" +target_hit = { offense = "crush", max = 110 } + +[basilisk_boss] +attack_speed = 4 +retreat_range = 8 +defend_anim = "basilisk_defend" +defend_sound = "basilisk_defend" +death_anim = "basilisk_death" +death_sound = "basilisk_death" + +[basilisk_boss.attack] +condition = "mirror_shield" +anim = "basilisk_attack" +target_sound = "basilisk_attack" +target_hit = { offense = "slash", max = 300 } + +[basilisk_boss.piercing_gaze] +clone = "basilisk.piercing_gaze" + +[basilisk_boss.gaze] +range = 8 +condition = "mirror_shield" +anim = "basilisk_boss_attack" +gfx = "basilisk_boss_cast" +projectile = "basilisk_boss_travel" +target_hit = { offense = "magic", max = 300 } +impact_gfx = "basilisk_boss_impact" +impact_drains = [ + { skill = "random", min = 1, max = 3 }, +] + +[mightiest_turoth] +clone = "turoth_large" + +[mightiest_turoth.attack] +anim = "mightest_turoth_attack" +target_sound = "turoth_attack" +target_hit = { offense = "stab", max = 165 } diff --git a/data/area/kharidian_desert/pollnivneach/dungeon/pollnivneach_dungeon.gfx.toml b/data/area/kharidian_desert/pollnivneach/dungeon/pollnivneach_dungeon.gfx.toml new file mode 100644 index 0000000000..41f84d58e0 --- /dev/null +++ b/data/area/kharidian_desert/pollnivneach/dungeon/pollnivneach_dungeon.gfx.toml @@ -0,0 +1,28 @@ +[pass_barrier_red] +id = 1659 + +[basilisk_boss_cast] +id = 1662 + +[basilisk_boss_travel] +id = 1663 +delay = 45 + +[basilisk_boss_impact] +id = 1664 + +[monstrous_cave_crawler_cast] +id = 1653 + +[monstrous_cave_crawler_travel] +id = 1654 +delay = 30 + +[monstrous_cave_crawler_impact] +id = 1655 + +[mightiest_turoth_spawn] +id = 1638 + +[turoth_minion_spawn] +id = 1639 diff --git a/data/area/kharidian_desert/pollnivneach/dungeon/pollnivneach_dungeon.objs.toml b/data/area/kharidian_desert/pollnivneach/dungeon/pollnivneach_dungeon.objs.toml new file mode 100644 index 0000000000..f77c325869 --- /dev/null +++ b/data/area/kharidian_desert/pollnivneach/dungeon/pollnivneach_dungeon.objs.toml @@ -0,0 +1,17 @@ +[pollnivneach_bucket_rope] +id = 31316 + +[pollnivneach_dungeon_barrier] +id = 31435 + +[pollnivneach_dungeon_barrier_north] +id = 31436 + +[pollnivneach_stairs_down] +id = 31412 + +[pollnivneach_stairs_up] +id = 31417 + +[pollnivneach_markings] +id = 31363 \ No newline at end of file diff --git a/data/area/kharidian_desert/pollnivneach/dungeon/pollnivneach_dungeon.tables.toml b/data/area/kharidian_desert/pollnivneach/dungeon/pollnivneach_dungeon.tables.toml new file mode 100644 index 0000000000..9f84e3d277 --- /dev/null +++ b/data/area/kharidian_desert/pollnivneach/dungeon/pollnivneach_dungeon.tables.toml @@ -0,0 +1,60 @@ +[desert_dungeon_boss] +row_id = "npc" +region = "int" +player_spawn = "tile" +npc_spawn = "tile" +exit = "tile" +type = "string" + +[.monstrous_cave_crawler] +type = "cave_crawler" +region = 12356 +player_spawn = { x = 3123, y = 4381 } +npc_spawn = { x = 3121, y = 4384 } +exit = { x = 3319, y = 4370 } + +[.turoth_mightiest] +type = "turoth" +region = 12355 +player_spawn = { x = 3089, y = 4318 } +npc_spawn = { x = 3088, y = 4324 } +exit = { x = 3275, y = 4335 } + +[.basilisk_boss] +type = "basilisk" +region = 12355 +player_spawn = { x = 3123, y = 4333 } +npc_spawn = { x = 3115, y = 4322 } +exit = { x = 3315, y = 4335 } + +[.kurask_overlord] +type = "kurask" +region = 12356 +player_spawn = { x = 3082, y = 4380 } +npc_spawn = { x = 3084, y = 4381 } +exit = { x = 3277, y = 4371 } + +[dessert_dungeon_barriers] +tile = "tile" +inside = "tile" +boss = "npc" + +[.turoth] +tile = { x = 3274, y = 4334 } +inside = { x = 3082, y = 4334 } +boss = "turoth_mightiest" + +[.basilisk] +tile = { x = 3314, y = 4334 } +inside = { x = 3122, y = 4334 } +boss = "basilisk_boss" + +[.cave_crawler] +tile = { x = 3318, y = 4371 } +inside = { x = 3126, y = 4371 } +boss = "monstrous_cave_crawler" + +[.kurask] +tile = { x = 3276, y = 4372 } +inside = { x = 3084, y = 4372 } +boss = "kurask_overlord" diff --git a/data/area/kharidian_desert/pollnivneach/dungeon/pollnivneach_dungeon.teles.toml b/data/area/kharidian_desert/pollnivneach/dungeon/pollnivneach_dungeon.teles.toml new file mode 100644 index 0000000000..b8501c3333 --- /dev/null +++ b/data/area/kharidian_desert/pollnivneach/dungeon/pollnivneach_dungeon.teles.toml @@ -0,0 +1,59 @@ +[pollnivneach_stairs_down] +option = "Climb-down" +tile = { x = 3377, y = 9367 } +to = { x = 3322, y = 4340 } + +[pollnivneach_stairs_up] +option = "Climb-up" +tile = { x = 3318, y = 4339 } +to = { x = 3376, y = 9368 } + +[pollnivneach_stairs_down] +option = "Climb-down" +tile = { x = 3338, y = 9368 } +to = { x = 3270, y = 4340 } + +[pollnivneach_stairs_up] +option = "Climb-up" +tile = { x = 3271, y = 4339 } +to = { x = 3342, y = 9369 } + +[pollnivneach_stairs_down] +option = "Climb-down" +tile = { x = 3340, y = 9426 } +to = { x = 3276, y = 4368 } + +[pollnivneach_stairs_up] +option = "Climb-up" +tile = { x = 3277, y = 4367 } +to = { x = 3344, y = 9427 } + +[pollnivneach_stairs_down] +option = "Climb-down" +tile = { x = 3374, y = 9426 } +to = { x = 3321, y = 4365 } + +[pollnivneach_stairs_up] +option = "Climb-up" +tile = { x = 3317, y = 4364 } +to = { x = 3373, y = 9427 } + +[pollnivneach_markings] +option = "Enter" +tile = { x = 3294, y = 4293 } +to = { x = 2741, y = 10008 } + +[pollnivneach_markings] +option = "Enter" +tile = { x = 3325, y = 4391 } +to = { x = 2800, y = 9998 } + +[pollnivneach_markings] +option = "Enter" +tile = { x = 3295, y = 4407 } +to = { x = 2703, y = 9995 } + +[pollnivneach_markings] +option = "Enter" +tile = { x = 3288, y = 4293 } +to = { x = 2714, y = 10012 } diff --git a/data/area/kharidian_desert/pollnivneach/dungeon/pollnivneach_dungeon.vars.toml b/data/area/kharidian_desert/pollnivneach/dungeon/pollnivneach_dungeon.vars.toml new file mode 100644 index 0000000000..2fe98e46ac --- /dev/null +++ b/data/area/kharidian_desert/pollnivneach/dungeon/pollnivneach_dungeon.vars.toml @@ -0,0 +1,15 @@ +[killed_monstrous_cave_crawler] +format = "boolean" +persist = true + +[killed_turoth_mightiest] +format = "boolean" +persist = true + +[killed_basilisk_boss] +format = "boolean" +persist = true + +[killed_kurask_overlord] +format = "boolean" +persist = true diff --git a/data/area/kharidian_desert/pollnivneach/pollnivneach.areas.toml b/data/area/kharidian_desert/pollnivneach/pollnivneach.areas.toml index bb0c249f39..25b1fee091 100644 --- a/data/area/kharidian_desert/pollnivneach/pollnivneach.areas.toml +++ b/data/area/kharidian_desert/pollnivneach/pollnivneach.areas.toml @@ -10,4 +10,23 @@ tags = ["teleport"] [smoke_dungeon] x = [3200, 3327] -y = [9344, 9407] \ No newline at end of file +y = [9344, 9407] + +[pollnivneach_dungeon_boss] +x = [3072, 3135] +y = [4288, 4415] +tags = ["multi_combat"] + +[mightiest_turoth_boss] +x = [3086, 3096] +y = [4318, 4326] + +[pollnivneach_dungeon_lower] +x = [3264, 3327] +y = [4288, 4415] +tags = ["multi_combat"] + +[pollnivneach_dungeon_upper] +x = [3328, 3391] +y = [9344, 9471] +tags = ["multi_combat"] \ No newline at end of file diff --git a/data/area/kharidian_desert/pollnivneach/pollnivneach.objs.toml b/data/area/kharidian_desert/pollnivneach/pollnivneach.objs.toml index b810c29063..323154c6f9 100644 --- a/data/area/kharidian_desert/pollnivneach/pollnivneach.objs.toml +++ b/data/area/kharidian_desert/pollnivneach/pollnivneach.objs.toml @@ -3,3 +3,12 @@ id = 36002 [smoke_dungeon_rope] id = 6439 + +[pollnivneach_well] +id = 31359 + +[dark_stairs] +id = 31390 + +[smoke_dungeon_dark_hole] +id = 31367 \ No newline at end of file diff --git a/data/area/kharidian_desert/pollnivneach/pollnivneach.teles.toml b/data/area/kharidian_desert/pollnivneach/pollnivneach.teles.toml index 0df242809d..9edd990362 100644 --- a/data/area/kharidian_desert/pollnivneach/pollnivneach.teles.toml +++ b/data/area/kharidian_desert/pollnivneach/pollnivneach.teles.toml @@ -7,3 +7,23 @@ to = { x = 3206, y = 9379 } option = "Climb-up" tile = { x = 3205, y = 9379 } to = { x = 3310, y = 2961 } + +[pollnivneach_bucket_rope] +option = "Climb-up" +tile = { x = 3358, y = 9352 } +to = { x = 3359, y = 2970 } + +[pollnivneach_well] +option = "Climb-down" +tile = { x = 3358, y = 2971 } +to = { x = 3358, y = 9354 } + +[dark_stairs] +option = "Enter" +tile = { x = 3337, y = 9350 } +to = { x = 3318, y = 9355 } + +[smoke_dungeon_dark_hole] +option = "Enter" +tile = { x = 3318, y = 9356 } +to = { x = 3338, y = 9350 } diff --git a/data/area/wilderness/chaos_tunnels/chaos_tunnels.npcs.toml b/data/area/wilderness/chaos_tunnels/chaos_tunnels.npcs.toml index 176e9327bb..c195b869f9 100644 --- a/data/area/wilderness/chaos_tunnels/chaos_tunnels.npcs.toml +++ b/data/area/wilderness/chaos_tunnels/chaos_tunnels.npcs.toml @@ -28,8 +28,7 @@ hitpoints = 800 att = 57 str = 87 def = 87 -style = "stab" -max_hit_melee = 100 +combat_def = "turoth_large" slayer_xp = 80.0 categories = ["turoth"] immune_cannon = true diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/data/config/RowDefinition.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/data/config/RowDefinition.kt index 4430c22b50..17c1e83920 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/data/config/RowDefinition.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/data/config/RowDefinition.kt @@ -23,6 +23,14 @@ data class RowDefinition( fun skillPairOrNull(column: String) = Tables.skillPairOrNull("${stringId}.$column") + fun tile(column: String) = Tables.tile("${stringId}.$column") + + fun tileOrNull(column: String) = Tables.tileOrNull("${stringId}.$column") + + fun npc(column: String) = Tables.npc("${stringId}.$column") + + fun npcOrNull(column: String) = Tables.npcOrNull("${stringId}.$column") + fun intList(column: String) = Tables.intList("${stringId}.$column") fun intListOrNull(column: String) = Tables.intListOrNull("${stringId}.$column") diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/ColumnReader.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/ColumnReader.kt index 49eca21a55..1b562087a0 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/ColumnReader.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/ColumnReader.kt @@ -2,6 +2,7 @@ package world.gregs.voidps.engine.data.definition import world.gregs.config.ConfigReader import world.gregs.voidps.engine.entity.character.player.skill.Skill +import world.gregs.voidps.type.Tile sealed interface ColumnReader { val type: ColumnType @@ -20,6 +21,24 @@ sealed interface ColumnReader { override fun read(reader: ConfigReader) = reader.int() } + object ReaderTile : ColumnReader { + override val type = ColumnType.ColumnInt + override fun list() = mutableListOf() + override fun read(reader: ConfigReader): Int { + var x = 0 + var y = 0 + var level = 0 + while (reader.nextEntry()) { + when (reader.key()) { + "x" -> x = reader.int() + "y" -> y = reader.int() + "level" -> level = reader.int() + } + } + return Tile.id(x, y, level) + } + } + object ReaderIntRange : ColumnReader { override val type = ColumnType.ColumnIntRange override fun list() = mutableListOf() @@ -90,6 +109,7 @@ sealed interface ColumnReader { "boolean" -> ReaderBoolean "int" -> ReaderInt "range" -> ReaderIntRange + "tile" -> ReaderTile "string" -> ReaderString "skill" -> ReaderEntity(Skill.map) "npc" -> ReaderEntity(NPCDefinitions.ids) diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/Tables.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/Tables.kt index 8a528bd50a..2f508b779e 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/Tables.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/Tables.kt @@ -7,6 +7,7 @@ import world.gregs.voidps.engine.data.config.RowDefinition import world.gregs.voidps.engine.data.config.TableDefinition import world.gregs.voidps.engine.entity.character.player.skill.Skill import world.gregs.voidps.engine.timedLoad +import world.gregs.voidps.type.Tile import kotlin.collections.set import kotlin.math.exp @@ -65,6 +66,13 @@ object Tables { return item.stringId } + fun tile(path: String): Tile = Tile(get(path, ColumnType.ColumnInt)) + + fun tileOrNull(path: String): Tile? { + val id = getOrNull(path, ColumnType.ColumnInt) ?: return null + return Tile(id) + } + fun obj(path: String): String = ObjectDefinitions.get(get(path, ColumnType.ColumnEntity)).stringId fun objOrNull(path: String): String? { diff --git a/engine/src/test/kotlin/world/gregs/voidps/engine/data/definition/TablesTest.kt b/engine/src/test/kotlin/world/gregs/voidps/engine/data/definition/TablesTest.kt index 8b1144f437..2f504be255 100644 --- a/engine/src/test/kotlin/world/gregs/voidps/engine/data/definition/TablesTest.kt +++ b/engine/src/test/kotlin/world/gregs/voidps/engine/data/definition/TablesTest.kt @@ -6,6 +6,7 @@ import world.gregs.voidps.cache.definition.data.ItemDefinition import world.gregs.voidps.cache.definition.data.NPCDefinition import world.gregs.voidps.cache.definition.data.ObjectDefinition import world.gregs.voidps.engine.data.config.TableDefinition +import world.gregs.voidps.type.Tile import kotlin.test.assertContentEquals import kotlin.test.assertEquals import kotlin.test.assertTrue @@ -32,22 +33,23 @@ class TablesTest { assertNotNull(definition) assertColumns( listOf( - Field("int_field", ColumnType.ColumnInt, 0), // int_field - Field("string_field", ColumnType.ColumnString, ""), // string_field - Field("item_field", ColumnType.ColumnEntity, -1), // item_field - Field("obj_field", ColumnType.ColumnEntity, -1), // obj_field - Field("npc_field", ColumnType.ColumnEntity, -1), // npc_field - Field("int_list", ColumnType.ColumnList(ColumnType.ColumnInt), emptyList()), // int_list - Field("str_list", ColumnType.ColumnList(ColumnType.ColumnString), emptyList()), // str_list - Field("item_list", ColumnType.ColumnList(ColumnType.ColumnEntity), emptyList()), // item_list - Field("obj_list", ColumnType.ColumnList(ColumnType.ColumnEntity), emptyList()), // obj_list - Field("npc_list", ColumnType.ColumnList(ColumnType.ColumnEntity), emptyList()), // npc_list - Field("int_int", ColumnType.ColumnPair(ColumnType.ColumnInt, ColumnType.ColumnInt), Pair(0, 0)), // int_int - Field("str_int", ColumnType.ColumnPair(ColumnType.ColumnString, ColumnType.ColumnInt), Pair("", 0)), // str_int - Field("int_str", ColumnType.ColumnPair(ColumnType.ColumnInt, ColumnType.ColumnString), Pair(0, "")), // int_str - Field("int_int_list", ColumnType.ColumnList(ColumnType.ColumnPair(ColumnType.ColumnInt, ColumnType.ColumnInt)), emptyList>()), // int_int_list - Field("str_int_list", ColumnType.ColumnList(ColumnType.ColumnPair(ColumnType.ColumnString, ColumnType.ColumnInt)), emptyList>()), // str_int_list - Field("int_str_list", ColumnType.ColumnList(ColumnType.ColumnPair(ColumnType.ColumnInt, ColumnType.ColumnString)), emptyList>()), // int_str_list + Field("int_field", ColumnType.ColumnInt, 0), + Field("string_field", ColumnType.ColumnString, ""), + Field("item_field", ColumnType.ColumnEntity, -1), + Field("obj_field", ColumnType.ColumnEntity, -1), + Field("npc_field", ColumnType.ColumnEntity, -1), + Field("tile_field", ColumnType.ColumnInt, 0), + Field("int_list", ColumnType.ColumnList(ColumnType.ColumnInt), emptyList()), + Field("str_list", ColumnType.ColumnList(ColumnType.ColumnString), emptyList()), + Field("item_list", ColumnType.ColumnList(ColumnType.ColumnEntity), emptyList()), + Field("obj_list", ColumnType.ColumnList(ColumnType.ColumnEntity), emptyList()), + Field("npc_list", ColumnType.ColumnList(ColumnType.ColumnEntity), emptyList()), + Field("int_int", ColumnType.ColumnPair(ColumnType.ColumnInt, ColumnType.ColumnInt), Pair(0, 0)), + Field("str_int", ColumnType.ColumnPair(ColumnType.ColumnString, ColumnType.ColumnInt), Pair("", 0)), + Field("int_str", ColumnType.ColumnPair(ColumnType.ColumnInt, ColumnType.ColumnString), Pair(0, "")), + Field("int_int_list", ColumnType.ColumnList(ColumnType.ColumnPair(ColumnType.ColumnInt, ColumnType.ColumnInt)), emptyList>()), + Field("str_int_list", ColumnType.ColumnList(ColumnType.ColumnPair(ColumnType.ColumnString, ColumnType.ColumnInt)), emptyList>()), + Field("int_str_list", ColumnType.ColumnList(ColumnType.ColumnPair(ColumnType.ColumnInt, ColumnType.ColumnString)), emptyList>()), ), definition ) assertContentEquals(intArrayOf(0, 1), definition.rows) @@ -58,9 +60,10 @@ class TablesTest { val expected = arrayOf( 1, "text", - 0, - 0, - 0, + 0, // item_field + 0, // obj_field + 0, // npc_field + Tile.id(1, 2, 3), // tile_field listOf(1, 2, 3), // int list listOf("one", "two"), // str list listOf(0), // item list @@ -83,6 +86,8 @@ class TablesTest { assertEquals("text", Tables.string("header.row.string_field")) assertEquals("item_id", Tables.item("header.row.item_field")) assertEquals("obj_id", Tables.obj("header.row.obj_field")) + assertEquals("npc_id", Tables.npc("header.row.npc_field")) + assertEquals(Tile(1, 2, 3), Tables.tile("header.row.tile_field")) assertEquals(listOf(1, 2, 3), Tables.intList("header.row.int_list")) assertEquals(listOf("one", "two"), Tables.stringList("header.row.str_list")) assertEquals(listOf("item_id"), Tables.itemList("header.row.item_list")) @@ -97,6 +102,9 @@ class TablesTest { assertEquals(0, Tables.int("header.row_two.int_field")) assertEquals("", Tables.string("header.row_two.string_field")) assertEquals("", Tables.item("header.row_two.item_field")) + assertEquals("", Tables.obj("header.row_two.obj_field")) + assertEquals("", Tables.npc("header.row_two.npc_field")) + assertEquals(Tile.EMPTY, Tables.tile("header.row_two.tile_field")) assertEquals(emptyList(), Tables.intList("header.row_two.int_list")) assertEquals(emptyList(), Tables.stringList("header.row_two.str_list")) assertEquals(emptyList(), Tables.itemList("header.row_two.item_list")) diff --git a/engine/src/test/resources/world/gregs/voidps/engine/data/definition/test-table.toml b/engine/src/test/resources/world/gregs/voidps/engine/data/definition/test-table.toml index 7a65fa97c3..4551ea88e9 100644 --- a/engine/src/test/resources/world/gregs/voidps/engine/data/definition/test-table.toml +++ b/engine/src/test/resources/world/gregs/voidps/engine/data/definition/test-table.toml @@ -4,6 +4,7 @@ string_field = "string" item_field = "item" obj_field = "obj" npc_field = "npc" +tile_field = "tile" int_list = "list" str_list = "list" item_list = "list" @@ -22,6 +23,7 @@ int_field = 1 item_field = "item_id" obj_field = "obj_id" npc_field = "npc_id" +tile_field = { x = 1, y = 2, level = 3 } int_list = [1, 2, 3] str_list = ["one", "two"] item_list = ["item_id"] diff --git a/game/src/main/kotlin/content/area/kharidian_desert/pollnivneach/dungeon/DesertSlayerBosses.kt b/game/src/main/kotlin/content/area/kharidian_desert/pollnivneach/dungeon/DesertSlayerBosses.kt new file mode 100644 index 0000000000..46b21b9e1d --- /dev/null +++ b/game/src/main/kotlin/content/area/kharidian_desert/pollnivneach/dungeon/DesertSlayerBosses.kt @@ -0,0 +1,25 @@ +package content.area.kharidian_desert.pollnivneach.dungeon + +import content.entity.combat.killer +import content.entity.player.dialogue.type.statement +import world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.data.definition.Tables +import world.gregs.voidps.engine.entity.character.move.tele +import world.gregs.voidps.engine.entity.character.player.Player +import world.gregs.voidps.engine.entity.character.player.skill.Skill +import world.gregs.voidps.engine.entity.character.player.skill.exp.exp +import world.gregs.voidps.engine.queue.queue + +class DesertSlayerBosses : Script { + init { + npcDespawn("monstrous_cave_crawler,turoth_mightiest,basilisk_boss,kurask_overlord") { + val killer = killer as? Player ?: return@npcDespawn + killer["killed_$id"] = true + killer.exp(Skill.Slayer, 1000.0) + killer.tele(Tables.tile("desert_dungeon_boss.$id.exit")) + killer.queue("shift_back") { + killer.statement("You shift back to reality, having defeated this boss. You may now pass this barrier freely.") + } + } + } +} diff --git a/game/src/main/kotlin/content/area/kharidian_desert/pollnivneach/dungeon/MightiestTuroth.kt b/game/src/main/kotlin/content/area/kharidian_desert/pollnivneach/dungeon/MightiestTuroth.kt new file mode 100644 index 0000000000..b2dd543f78 --- /dev/null +++ b/game/src/main/kotlin/content/area/kharidian_desert/pollnivneach/dungeon/MightiestTuroth.kt @@ -0,0 +1,32 @@ +package content.area.kharidian_desert.pollnivneach.dungeon + +import content.entity.gfx.areaGfx +import world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.client.instruction.handle.interactPlayer +import world.gregs.voidps.engine.data.definition.Areas +import world.gregs.voidps.engine.entity.character.npc.NPCs +import world.gregs.voidps.engine.entity.character.player.Player +import world.gregs.voidps.engine.map.collision.random +import world.gregs.voidps.type.random + +class MightiestTuroth : Script { + init { + npcCombatDamage("turoth_mightiest") { + if (random.nextInt(5) == 0) { + return@npcCombatDamage + } + val target = it.source as? Player ?: return@npcCombatDamage + for (i in 0 until 3) { + if (contains("swarming_$i")) { + continue + } + val tile = Areas["mightiest_turoth_boss"].random(this) ?: return@npcCombatDamage + areaGfx("turoth_minion_spawn", tile) + val npc = NPCs.add("turoth_swarming", tile) + npc.interactPlayer(target, "Attack") + set("swarming_$i", true) + break + } + } + } +} diff --git a/game/src/main/kotlin/content/area/kharidian_desert/pollnivneach/dungeon/MonstrousCaveCrawler.kt b/game/src/main/kotlin/content/area/kharidian_desert/pollnivneach/dungeon/MonstrousCaveCrawler.kt new file mode 100644 index 0000000000..b384e43755 --- /dev/null +++ b/game/src/main/kotlin/content/area/kharidian_desert/pollnivneach/dungeon/MonstrousCaveCrawler.kt @@ -0,0 +1,22 @@ +package content.area.kharidian_desert.pollnivneach.dungeon + +import content.entity.combat.target +import content.entity.effect.toxin.poisonDamage +import world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.entity.character.player.Player + +class MonstrousCaveCrawler : Script { + init { + npcAttack("monstrous_cave_crawler", "melee") { + if (inc("hit_count") != 2) { + return@npcAttack + } + clear("hit_count") + val target = target as? Player ?: return@npcAttack + // Reduce anti-poison resistance + target.poisonDamage = (target.poisonDamage + 80).coerceAtMost(80) + target.timers.startIfAbsent("poison") + target["poison_source"] = this + } + } +} diff --git a/game/src/main/kotlin/content/area/kharidian_desert/pollnivneach/dungeon/PollnivneachDungeon.kt b/game/src/main/kotlin/content/area/kharidian_desert/pollnivneach/dungeon/PollnivneachDungeon.kt new file mode 100644 index 0000000000..01463f4bc5 --- /dev/null +++ b/game/src/main/kotlin/content/area/kharidian_desert/pollnivneach/dungeon/PollnivneachDungeon.kt @@ -0,0 +1,87 @@ +package content.area.kharidian_desert.pollnivneach.dungeon + +import content.entity.player.dialogue.type.choice +import content.entity.player.dialogue.type.statement +import content.quest.clearInstance +import content.quest.instanceOffset +import content.quest.setInstanceLogout +import content.quest.smallInstance +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.data.definition.Rows +import world.gregs.voidps.engine.data.definition.Tables +import world.gregs.voidps.engine.entity.character.move.tele +import world.gregs.voidps.engine.entity.character.npc.NPCs +import world.gregs.voidps.type.Region +import world.gregs.voidps.type.Tile + +class PollnivneachDungeon : Script { + init { + objectOperate("Pass", "pollnivneach_dungeon_barrier_north") { (target) -> + val offset = instanceOffset() + val boss = boss(target.tile.minus(offset), "tile") ?: return@objectOperate + if (!get("killed_$boss", false)) { + statement("This portal leads to the lair of a ferocious creature. Are you sure you want to do battle?") + choice("Do you want to head into the fray?") { + option("Yes, I feel brave.") { + val row = Rows.get("desert_dungeon_boss.$boss") + smallInstance(Region(row.int("region"))) + setInstanceLogout(row.tile("exit")) + delay(3) + val offset = instanceOffset() + tele(offset.tile(row.tile("player_spawn"))) + val boss = NPCs.add(boss, offset.tile(row.tile("npc_spawn"))) + message("Your surroundings shift to become familiar, as you face a powerful ${row.string("type")}.") + boss.interactPlayer(this, "Attack") + } + option("No way!") + } + return@objectOperate + } + face(target) + val x = tile.x.coerceIn(target.tile.x, target.tile.x + 2) + val y = if (tile.y <= target.tile.y) target.tile.y + 1 else target.tile.y - 1 + walkOverDelay(tile.copy(x = x)) + anim("pass_through_barrier") + gfx("pass_barrier_red") + exactMoveDelay(Tile(x, y), delay = 30) + } + + exited("pollnivneach_dungeon_boss") { + clearInstance() + } + + objectOperate("Pass", "pollnivneach_dungeon_barrier") { (target) -> + val offset = instanceOffset() + val boss = boss(target.tile.minus(offset), "inside") + if (boss != null) { + clearInstance() + tele(Tables.tile("desert_dungeon_boss.$boss.exit")) + return@objectOperate + } + face(target) + val x = if (tile.x <= target.tile.x) target.tile.x + 1 else target.tile.x - 1 + val y = tile.y.coerceIn(target.tile.y, target.tile.y + 2) + walkOverDelay(tile.copy(y = y)) + message("You pass through the mystic barrier, which feels odd.") + anim("pass_through_barrier") + gfx("pass_barrier_red") + exactMoveDelay(Tile(x, y), delay = 30) + } + + objTeleportLand("Climb-down", "pollnivneach_well") { _, _ -> + message("You descend into the somewhat smoky depths of the well, to the accompaniment of eery wails.") + // TODO quest req, smoke interface + damage + } + } + + private fun boss(tile: Tile, key: String): String? { + for (row in Tables.get("dessert_dungeon_barriers").rows()) { + if (row.tile(key) == tile) { + return row.npc("boss") + } + } + return null + } +} diff --git a/game/src/main/kotlin/content/area/misthalin/lumbridge/PlayerDeathSignpost.kt b/game/src/main/kotlin/content/area/misthalin/lumbridge/PlayerDeathSignpost.kt index 41af32e998..7bce78b269 100644 --- a/game/src/main/kotlin/content/area/misthalin/lumbridge/PlayerDeathSignpost.kt +++ b/game/src/main/kotlin/content/area/misthalin/lumbridge/PlayerDeathSignpost.kt @@ -11,6 +11,7 @@ class PlayerDeathSignpost : Script { playerDeath { playerDeathsToday += 1 } + objectOperate("Read", "lumbridge_signpost_death") { if (playerDeathsToday > 0) { statement("So far today, $playerDeathsToday unlucky adventurers have died on ${Settings["server.name"]} and been sent to their respawn location. Be careful out there.") diff --git a/game/src/main/kotlin/content/entity/death/CharacterDeath.kt b/game/src/main/kotlin/content/entity/death/CharacterDeath.kt deleted file mode 100644 index 629e255281..0000000000 --- a/game/src/main/kotlin/content/entity/death/CharacterDeath.kt +++ /dev/null @@ -1,22 +0,0 @@ -package content.entity.death - -import world.gregs.voidps.engine.Script -import world.gregs.voidps.engine.entity.character.Death -import world.gregs.voidps.engine.entity.character.player.skill.Skill - -class CharacterDeath : Script { - - init { - levelChanged(Skill.Constitution) { _, _, to -> - if (to <= 0 && !queue.contains("death")) { - Death.killed(this) - } - } - - npcLevelChanged(Skill.Constitution) { _, _, to -> - if (to <= 0 && !queue.contains("death") && Death.canDie(this)) { - Death.killed(this) - } - } - } -} diff --git a/game/src/main/kotlin/content/entity/death/NPCDeath.kt b/game/src/main/kotlin/content/entity/death/NPCDeath.kt index d7c23e0348..444533c3d4 100644 --- a/game/src/main/kotlin/content/entity/death/NPCDeath.kt +++ b/game/src/main/kotlin/content/entity/death/NPCDeath.kt @@ -17,6 +17,7 @@ import world.gregs.voidps.engine.data.definition.CombatDefinitions import world.gregs.voidps.engine.entity.Spawn import world.gregs.voidps.engine.entity.World import world.gregs.voidps.engine.entity.character.Character +import world.gregs.voidps.engine.entity.character.Death 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.move.tele @@ -50,11 +51,15 @@ class NPCDeath( val logger = InlineLogger() init { - npcDeath { + npcLevelChanged(Skill.Constitution) { _, _, to -> + if (to > 0 || queue.contains("death") || !Death.canDie(this)) { + return@npcLevelChanged + } mode = PauseMode dead = true steps.clear() val npc = this + Death.killed(npc) strongQueue(name = "death", 1) { val killer = killer val tile = if (transformId == "wall_beast") tile.addY(-1) else tile diff --git a/game/src/main/kotlin/content/entity/death/PlayerDeath.kt b/game/src/main/kotlin/content/entity/death/PlayerDeath.kt index 6cfcc96cd0..9858bf1382 100644 --- a/game/src/main/kotlin/content/entity/death/PlayerDeath.kt +++ b/game/src/main/kotlin/content/entity/death/PlayerDeath.kt @@ -18,6 +18,7 @@ import world.gregs.voidps.engine.Script import world.gregs.voidps.engine.client.message import world.gregs.voidps.engine.data.Settings import world.gregs.voidps.engine.entity.character.Character +import world.gregs.voidps.engine.entity.character.Death import world.gregs.voidps.engine.entity.character.jingle import world.gregs.voidps.engine.entity.character.move.tele import world.gregs.voidps.engine.entity.character.npc.NPCs @@ -41,8 +42,12 @@ class PlayerDeath : Script { get() = Tile(Settings["world.home.x", 0], Settings["world.home.y", 0], Settings["world.home.level", 0]) init { - playerDeath { onDeath -> + levelChanged(Skill.Constitution) { _, _, to -> + if (to > 0 || queue.contains("death")) { + return@levelChanged + } dead = true + val onDeath = Death.killed(this@levelChanged) strongQueue("death") { steps.clear() val dealer = damageDealers.maxByOrNull { it.value } diff --git a/game/src/main/kotlin/content/entity/player/command/ServerCommands.kt b/game/src/main/kotlin/content/entity/player/command/ServerCommands.kt index c498cdf87c..d96b4f651a 100644 --- a/game/src/main/kotlin/content/entity/player/command/ServerCommands.kt +++ b/game/src/main/kotlin/content/entity/player/command/ServerCommands.kt @@ -36,6 +36,7 @@ import world.gregs.voidps.engine.data.definition.QuestDefinitions import world.gregs.voidps.engine.data.definition.RenderEmoteDefinitions import world.gregs.voidps.engine.data.definition.SoundDefinitions import world.gregs.voidps.engine.data.definition.SpellDefinitions +import world.gregs.voidps.engine.data.definition.Tables import world.gregs.voidps.engine.data.definition.VariableDefinitions import world.gregs.voidps.engine.entity.World import world.gregs.voidps.engine.entity.character.npc.loadNpcSpawns @@ -143,6 +144,7 @@ class ServerCommands(val accountLoader: PlayerAccountLoader) : Script { SettingsReload.now() } "bots" -> get().load(files) + "tables", "rows", "dbs" -> Tables.load(files.list(Settings["definitions.tables"])) } } diff --git a/game/src/main/kotlin/content/entity/world/RegionLoading.kt b/game/src/main/kotlin/content/entity/world/RegionLoading.kt index befadd112b..c8918cade3 100644 --- a/game/src/main/kotlin/content/entity/world/RegionLoading.kt +++ b/game/src/main/kotlin/content/entity/world/RegionLoading.kt @@ -166,22 +166,27 @@ class RegionLoading(val dynamicZones: DynamicZones) : Script { val view = player.tile.zone.minus(viewport.zoneRadius, viewport.zoneRadius) val zoneSize = viewport.zoneArea var append = 0 - for (origin in view.toCuboid(zoneSize, zoneSize).copy(minLevel = 0, maxLevel = 3).toZones()) { - val target = dynamicZones.dynamicZone(origin) - if (target == null) { - zones.add(null) - continue - } - zones.add(target) - val xtea = blankXtea - if (!xteaList.contains(xtea)) { - xteaList.add(xtea) - } else { - append++ + val xtea = blankXtea + for (lvl in 0..3) { + for (x in 0 until zoneSize) { + for (y in 0 until zoneSize) { + val zone = Zone(view.x + x, view.y + y, lvl) + val target = dynamicZones.dynamicZone(zone) + if (target == null) { + zones.add(null) + continue + } + zones.add(target) + if (!xteaList.contains(xtea)) { + xteaList.add(xtea) + } else { + append++ + } + } } } for (i in 0..append) { - xteaList.add(blankXtea) + xteaList.add(xtea) } viewport.dynamic = true player.client?.dynamicMapRegion( diff --git a/game/src/test/kotlin/content/area/kharidian_desert/pollnivneach/dungeon/PollnivneachDungeonTest.kt b/game/src/test/kotlin/content/area/kharidian_desert/pollnivneach/dungeon/PollnivneachDungeonTest.kt new file mode 100644 index 0000000000..03de78c3e6 --- /dev/null +++ b/game/src/test/kotlin/content/area/kharidian_desert/pollnivneach/dungeon/PollnivneachDungeonTest.kt @@ -0,0 +1,113 @@ +package content.area.kharidian_desert.pollnivneach.dungeon + +import WorldTest +import dialogueContinue +import dialogueOption +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import world.gregs.voidps.engine.client.instruction.handle.interactObject +import world.gregs.voidps.engine.entity.obj.GameObjects +import world.gregs.voidps.engine.inv.equipment +import world.gregs.voidps.network.login.protocol.visual.update.player.EquipSlot +import world.gregs.voidps.type.Tile + +class PollnivneachDungeonTest : WorldTest() { + + @Test + fun `Kill monstrous cave crawler`() { + val player = createPlayer(Tile(3319, 4370, 0)) + player["auto_retaliate"] = true + player["god_mode"] = true + player["insta_kill"] = true + + val barrier = GameObjects.find(Tile(3318, 4371), "pollnivneach_dungeon_barrier_north") + player.interactObject(barrier, "Pass") + tick(2) + + player.dialogueContinue(1) + player.dialogueOption("line1") + + tick(15) + + assertEquals(Tile(3319, 4370), player.tile) + assertEquals(true, player["killed_monstrous_cave_crawler", false]) + + player.interactObject(barrier, "Pass") + tick(2) + assertEquals(Tile(3319, 4372), player.tile) + } + + @Test + fun `Kill kurask overlord`() { + val player = createPlayer(Tile(3277, 4371, 0)) + player["auto_retaliate"] = true + player["god_mode"] = true + player["insta_kill"] = true + + val barrier = GameObjects.find(Tile(3276, 4372), "pollnivneach_dungeon_barrier_north") + player.interactObject(barrier, "Pass") + tick(2) + + player.dialogueContinue(1) + player.dialogueOption("line1") + + tick(15) + + assertEquals(Tile(3277, 4371), player.tile) + assertEquals(true, player["killed_kurask_overlord", false]) + + player.interactObject(barrier, "Pass") + tick(2) + assertEquals(Tile(3277, 4373), player.tile) + } + + @Test + fun `Kill mightiest turoth`() { + val player = createPlayer(Tile(3275, 4335, 0)) + player.equipment.set(EquipSlot.Weapon.index, "leaf_bladed_sword") + player["auto_retaliate"] = true + player["god_mode"] = true + player["insta_kill"] = true + + val barrier = GameObjects.find(Tile(3274, 4334), "pollnivneach_dungeon_barrier_north") + player.interactObject(barrier, "Pass") + tick(2) + + player.dialogueContinue(1) + player.dialogueOption("line1") + + tick(20) + + assertEquals(Tile(3275, 4335), player.tile) + assertEquals(true, player["killed_turoth_mightiest", false]) + + player.interactObject(barrier, "Pass") + tick(2) + assertEquals(Tile(3275, 4333), player.tile) + } + + @Test + fun `Kill basilisk boss`() { + val player = createPlayer(Tile(3315, 4335, 0)) + player.equipment.set(EquipSlot.Shield.index, "mirror_shield") + player["auto_retaliate"] = true + player["god_mode"] = true + player["insta_kill"] = true + + val barrier = GameObjects.find(Tile(3314, 4334), "pollnivneach_dungeon_barrier_north") + player.interactObject(barrier, "Pass") + tick(2) + + player.dialogueContinue(1) + player.dialogueOption("line1") + + tick(20) + + assertEquals(Tile(3315, 4335), player.tile) + assertEquals(true, player["killed_basilisk_boss", false]) + + player.interactObject(barrier, "Pass") + tick(2) + assertEquals(Tile(3315, 4333), player.tile) + } +} diff --git a/types/src/main/kotlin/world/gregs/voidps/type/Delta.kt b/types/src/main/kotlin/world/gregs/voidps/type/Delta.kt index 2fec2157e7..5e4ca38cfb 100644 --- a/types/src/main/kotlin/world/gregs/voidps/type/Delta.kt +++ b/types/src/main/kotlin/world/gregs/voidps/type/Delta.kt @@ -54,6 +54,7 @@ value class Delta(val id: Long) { fun delta(value: Delta) = delta(value.x, value.y, value.level) fun tile(x: Int = 0, y: Int = 0, level: Int = 0) = Tile(this.x + x, this.y + y, this.level + level) + fun tile(tile: Tile) = Tile(this.x + tile.x, this.y + tile.y, this.level + tile.level) fun add(direction: Direction) = add(direction.delta) fun minus(direction: Direction) = minus(direction.delta)