From e443c74069e07e342644bdd522f5b425a3c88ea7 Mon Sep 17 00:00:00 2001 From: GregHib Date: Sun, 15 Mar 2026 23:07:03 +0000 Subject: [PATCH 01/27] Start work on kalphite queen --- .../kalphite_hive/kalphite_hive.combat.toml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 data/area/kharidian_desert/kalphite_hive/kalphite_hive.combat.toml diff --git a/data/area/kharidian_desert/kalphite_hive/kalphite_hive.combat.toml b/data/area/kharidian_desert/kalphite_hive/kalphite_hive.combat.toml new file mode 100644 index 0000000000..e832e14002 --- /dev/null +++ b/data/area/kharidian_desert/kalphite_hive/kalphite_hive.combat.toml @@ -0,0 +1,13 @@ +[kalphite] +attack_speed = 4 +retreat_range = 15 +defend_anim = "human_defend" +defend_sound = "kalphite_defend" +death_anim = "human_death" +death_sound = "kalphite_death" + +[kalphite.melee] +range = 1 +anim = "human_attack" +target_sound = "kalphite_attack" +target_hit = { offense = "crush", max = 80 } From 740d13c811349e54df627d0035752e914ab0633f Mon Sep 17 00:00:00 2001 From: GregHib Date: Mon, 16 Mar 2026 20:53:21 +0000 Subject: [PATCH 02/27] Fix npc combat not using transform id --- game/src/main/kotlin/content/entity/npc/combat/Attack.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/game/src/main/kotlin/content/entity/npc/combat/Attack.kt b/game/src/main/kotlin/content/entity/npc/combat/Attack.kt index ab183c4d7c..0a83d12342 100644 --- a/game/src/main/kotlin/content/entity/npc/combat/Attack.kt +++ b/game/src/main/kotlin/content/entity/npc/combat/Attack.kt @@ -6,6 +6,7 @@ import content.entity.combat.hit.hit import content.entity.death.weightedSample import content.entity.effect.freeze import content.entity.effect.toxin.poison +import content.entity.effect.transform import content.entity.gfx.areaGfx import content.entity.proj.shoot import net.pearx.kasechange.toPascalCase From d59e2f6cf0bc9871aeaa8310f8efcae38f3ea4be Mon Sep 17 00:00:00 2001 From: GregHib Date: Mon, 16 Mar 2026 21:05:52 +0000 Subject: [PATCH 03/27] WIP kalphite queen combat --- .../kalphite_hive/kalphite_hive.combat.toml | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 data/area/kharidian_desert/kalphite_hive/kalphite_hive.combat.toml diff --git a/data/area/kharidian_desert/kalphite_hive/kalphite_hive.combat.toml b/data/area/kharidian_desert/kalphite_hive/kalphite_hive.combat.toml deleted file mode 100644 index e832e14002..0000000000 --- a/data/area/kharidian_desert/kalphite_hive/kalphite_hive.combat.toml +++ /dev/null @@ -1,13 +0,0 @@ -[kalphite] -attack_speed = 4 -retreat_range = 15 -defend_anim = "human_defend" -defend_sound = "kalphite_defend" -death_anim = "human_death" -death_sound = "kalphite_death" - -[kalphite.melee] -range = 1 -anim = "human_attack" -target_sound = "kalphite_attack" -target_hit = { offense = "crush", max = 80 } From 6d689e05e8dc952e0b8257c65f2dca9e0b288f5e Mon Sep 17 00:00:00 2001 From: GregHib Date: Tue, 17 Mar 2026 15:56:17 +0000 Subject: [PATCH 04/27] Finish KQ --- game/src/main/kotlin/content/entity/npc/combat/Attack.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/game/src/main/kotlin/content/entity/npc/combat/Attack.kt b/game/src/main/kotlin/content/entity/npc/combat/Attack.kt index 0a83d12342..ab183c4d7c 100644 --- a/game/src/main/kotlin/content/entity/npc/combat/Attack.kt +++ b/game/src/main/kotlin/content/entity/npc/combat/Attack.kt @@ -6,7 +6,6 @@ import content.entity.combat.hit.hit import content.entity.death.weightedSample import content.entity.effect.freeze import content.entity.effect.toxin.poison -import content.entity.effect.transform import content.entity.gfx.areaGfx import content.entity.proj.shoot import net.pearx.kasechange.toPascalCase From 08e0fc57a90f8722a8bb9e2101f58f9895f60502 Mon Sep 17 00:00:00 2001 From: GregHib Date: Tue, 17 Mar 2026 17:11:19 +0000 Subject: [PATCH 05/27] Fix exiting lumbridge caves not removing darkness Add insects swarming in darkness --- .../caves/lumbridge_swamp_caves.areas.toml | 4 +++ .../lumbridge/swamp/LumbridgeSwamp.kt | 25 ++++++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/data/area/misthalin/lumbridge/swamp/caves/lumbridge_swamp_caves.areas.toml b/data/area/misthalin/lumbridge/swamp/caves/lumbridge_swamp_caves.areas.toml index fdddbb52d3..5607731dab 100644 --- a/data/area/misthalin/lumbridge/swamp/caves/lumbridge_swamp_caves.areas.toml +++ b/data/area/misthalin/lumbridge/swamp/caves/lumbridge_swamp_caves.areas.toml @@ -5,3 +5,7 @@ y = [9542, 9544] [lumbridge_swamp_east_fishing_area] x = [3245, 3249] y = [9568, 9572] + +[lumbridge_swamp_caves] +x = [3136, 3263] +y = [9536, 9602] \ No newline at end of file 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 0ff9b17e06..6dc7ee3abf 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,5 +1,6 @@ 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 @@ -140,7 +141,29 @@ class LumbridgeSwamp : Script { } } - objTeleportLand("Climb", "swamp_cave_climbing_rope") { _, _ -> + entered("lumbridge_swamp_caves") { + if (interfaces.contains("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") } From 9364e6f827b63f154573e8746fd5411c5210197d Mon Sep 17 00:00:00 2001 From: GregHib Date: Tue, 17 Mar 2026 19:03:05 +0000 Subject: [PATCH 06/27] Add wall beast --- .../caves/lumbridge_swamp_caves.anims.toml | 2 + .../caves/lumbridge_swamp_caves.areas.toml | 30 +++++- .../caves/lumbridge_swamp_caves.combat.toml | 4 +- .../lumbridge_swamp_caves.npc-spawns.toml | 14 +-- .../caves/lumbridge_swamp_caves.npcs.toml | 15 +-- data/skill/slayer/mazchna.enums.toml | 4 +- .../entity/character/mode/move/Movement.kt | 4 + .../lumbridge/swamp/LumbridgeSwamp.kt | 10 +- .../misthalin/lumbridge/swamp/WallBeast.kt | 98 +++++++++++++++++++ .../kotlin/content/entity/death/NPCDeath.kt | 4 +- 10 files changed, 153 insertions(+), 32 deletions(-) create mode 100644 game/src/main/kotlin/content/area/misthalin/lumbridge/swamp/WallBeast.kt diff --git a/data/area/misthalin/lumbridge/swamp/caves/lumbridge_swamp_caves.anims.toml b/data/area/misthalin/lumbridge/swamp/caves/lumbridge_swamp_caves.anims.toml index aaae877c86..621348f321 100644 --- a/data/area/misthalin/lumbridge/swamp/caves/lumbridge_swamp_caves.anims.toml +++ b/data/area/misthalin/lumbridge/swamp/caves/lumbridge_swamp_caves.anims.toml @@ -40,3 +40,5 @@ id = 6002 [cave_goblin_death] id = 6003 +[insect_swarm] +id = 1582 diff --git a/data/area/misthalin/lumbridge/swamp/caves/lumbridge_swamp_caves.areas.toml b/data/area/misthalin/lumbridge/swamp/caves/lumbridge_swamp_caves.areas.toml index 5607731dab..02d9411c98 100644 --- a/data/area/misthalin/lumbridge/swamp/caves/lumbridge_swamp_caves.areas.toml +++ b/data/area/misthalin/lumbridge/swamp/caves/lumbridge_swamp_caves.areas.toml @@ -8,4 +8,32 @@ y = [9568, 9572] [lumbridge_swamp_caves] x = [3136, 3263] -y = [9536, 9602] \ No newline at end of file +y = [9536, 9602] + +[wall_beast_spot_1] +x = [3161] +y = [9546] + +[wall_beast_spot_2] +x = [3162] +y = [9573] + +[wall_beast_spot_3] +x = [3164] +y = [9555] + +[wall_beast_spot_4] +x = [3198] +y = [9553] + +[wall_beast_spot_5] +x = [3198] +y = [9571] + +[wall_beast_spot_6] +x = [3215] +y = [9559] + +[wall_beast_spot_7] +x = [3216] +y = [9587] diff --git a/data/area/misthalin/lumbridge/swamp/caves/lumbridge_swamp_caves.combat.toml b/data/area/misthalin/lumbridge/swamp/caves/lumbridge_swamp_caves.combat.toml index 36506221d9..ee87734612 100644 --- a/data/area/misthalin/lumbridge/swamp/caves/lumbridge_swamp_caves.combat.toml +++ b/data/area/misthalin/lumbridge/swamp/caves/lumbridge_swamp_caves.combat.toml @@ -22,6 +22,6 @@ death_sound = "wall_beast_death" [wall_beast.melee] range = 1 -anim = "wall_beast_attack" -target_sound = "wall_beast_attack" +anim = "wall_beast_swipe" +target_sound = "wall_beast_swipe" target_hit = { offense = "crush", max = 40 } diff --git a/data/area/misthalin/lumbridge/swamp/caves/lumbridge_swamp_caves.npc-spawns.toml b/data/area/misthalin/lumbridge/swamp/caves/lumbridge_swamp_caves.npc-spawns.toml index d9e866a496..e1a67c6e3b 100644 --- a/data/area/misthalin/lumbridge/swamp/caves/lumbridge_swamp_caves.npc-spawns.toml +++ b/data/area/misthalin/lumbridge/swamp/caves/lumbridge_swamp_caves.npc-spawns.toml @@ -58,13 +58,13 @@ spawns = [ { id = "cave_bug_larva", x = 3246, y = 9564 }, { id = "cave_bug_larva", x = 3250, y = 9579 }, { id = "cave_bug_larva", x = 3252, y = 9570 }, - { id = "hole_in_the_wall_lumbridge_swamp_caves", x = 3161, y = 9547 }, - { id = "hole_in_the_wall_lumbridge_swamp_caves", x = 3162, y = 9574 }, - { id = "hole_in_the_wall_lumbridge_swamp_caves", x = 3164, y = 9556 }, - { id = "hole_in_the_wall_lumbridge_swamp_caves", x = 3198, y = 9554 }, - { id = "hole_in_the_wall_lumbridge_swamp_caves", x = 3198, y = 9572 }, - { id = "hole_in_the_wall_lumbridge_swamp_caves", x = 3215, y = 9560 }, - { id = "hole_in_the_wall_lumbridge_swamp_caves", x = 3216, y = 9588 }, + { id = "hole_in_the_wall", x = 3161, y = 9547 }, + { id = "hole_in_the_wall", x = 3162, y = 9574 }, + { id = "hole_in_the_wall", x = 3164, y = 9556 }, + { id = "hole_in_the_wall", x = 3198, y = 9554 }, + { id = "hole_in_the_wall", x = 3198, y = 9572 }, + { id = "hole_in_the_wall", x = 3215, y = 9560 }, + { id = "hole_in_the_wall", x = 3216, y = 9588 }, { id = "fishing_spot_lumbridge_swamp_caves", x = 3154, y = 9544 }, { id = "fishing_spot_lumbridge_swamp_caves_2", x = 3245, y = 9570, members = true }, { id = "sergeant_mossfists_lumbridge_swamp_caves", x = 3168, y = 9572, members = true }, diff --git a/data/area/misthalin/lumbridge/swamp/caves/lumbridge_swamp_caves.npcs.toml b/data/area/misthalin/lumbridge/swamp/caves/lumbridge_swamp_caves.npcs.toml index 26bc9cc4be..2611f4493b 100644 --- a/data/area/misthalin/lumbridge/swamp/caves/lumbridge_swamp_caves.npcs.toml +++ b/data/area/misthalin/lumbridge/swamp/caves/lumbridge_swamp_caves.npcs.toml @@ -19,31 +19,22 @@ clone = "cave_goblin_lumbridge_swamp_caves" id = 1825 clone = "cave_goblin_lumbridge_swamp_caves" -[hole_in_the_wall_lumbridge_swamp_caves] +[hole_in_the_wall] id = 2058 hitpoints = 1050 att = 30 str = 30 def = 16 -combat_def = "wall_beast" -hunt_mode = "cowardly" +respawn_delay = 0 slayer_xp = 105.0 categories = ["wall_beasts"] immune_poison = true +drop_table = "wall_beast" examine = "What could be hiding in that crack in the wall?" [wall_beast] id = 7823 -hitpoints = 1050 -att = 30 -str = 30 -def = 16 combat_def = "wall_beast" -hunt_mode = "cowardly" -slayer_xp = 105.0 -categories = ["wall_beasts"] -immune_poison = true -drop_table = "wall_beast" examine = "A big, scary hand!" [fishing_spot_lumbridge_swamp_caves] diff --git a/data/skill/slayer/mazchna.enums.toml b/data/skill/slayer/mazchna.enums.toml index ce24d8de1d..2c4b2e88f3 100644 --- a/data/skill/slayer/mazchna.enums.toml +++ b/data/skill/slayer/mazchna.enums.toml @@ -59,13 +59,13 @@ values = { # killerwatt = 6, lizard = 8, # mogre = 8, - # pyrefiend_large = 8, + pyrefiend_large = 8, rockslug = 8, scorpion = 7, # asyn_shade = 8, skeleton_heavy = 7, # vampyre_juvinate = 6, - # wall_beast = 7, + wall_beast = 7, wolf = 7, zombie = 7, } diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/move/Movement.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/move/Movement.kt index 128918b72d..842a6bd88f 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/move/Movement.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/move/Movement.kt @@ -11,6 +11,7 @@ import world.gregs.voidps.engine.data.definition.Areas import world.gregs.voidps.engine.entity.character.Character import world.gregs.voidps.engine.entity.character.mode.EmptyMode import world.gregs.voidps.engine.entity.character.mode.Mode +import world.gregs.voidps.engine.entity.character.mode.ModeType import world.gregs.voidps.engine.entity.character.mode.move.target.TargetStrategy import world.gregs.voidps.engine.entity.character.move.running import world.gregs.voidps.engine.entity.character.npc.NPC @@ -71,6 +72,9 @@ open class Movement( } private fun canMove(): Boolean { + if (character is NPC && (character as NPC).def.walkMode.toInt() == ModeType.EMPTY) { + return false + } if (!hasDelay() && (character as? Player)?.menu == null) { return true } 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 6dc7ee3abf..d2fdf99093 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 @@ -117,11 +117,6 @@ class LumbridgeSwamp : Script { } anim("climb_down") delay(2) - if (light) { - open("level_one_darkness") - } else { - open("level_three_darkness") - } tele(3167, 9573) } else if (inventory.contains("rope")) { choice("Attach a rope to the top of the hole?") { @@ -142,7 +137,10 @@ class LumbridgeSwamp : Script { } entered("lumbridge_swamp_caves") { - if (interfaces.contains("level_three_darkness")) { + if (Light.hasLightSource(this)) { + open("level_one_darkness") + } else { + open("level_three_darkness") timers.start("insect_swarm") } } diff --git a/game/src/main/kotlin/content/area/misthalin/lumbridge/swamp/WallBeast.kt b/game/src/main/kotlin/content/area/misthalin/lumbridge/swamp/WallBeast.kt new file mode 100644 index 0000000000..b602b42e2a --- /dev/null +++ b/game/src/main/kotlin/content/area/misthalin/lumbridge/swamp/WallBeast.kt @@ -0,0 +1,98 @@ +package content.area.misthalin.lumbridge.swamp + +import content.entity.combat.hit.directHit +import content.entity.combat.target +import content.entity.combat.underAttack +import content.entity.effect.clearTransform +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.data.definition.AreaDefinition +import world.gregs.voidps.engine.entity.character.mode.EmptyMode +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 +import world.gregs.voidps.engine.entity.character.player.equip.equipped +import world.gregs.voidps.engine.entity.character.sound +import world.gregs.voidps.engine.queue.strongQueue +import world.gregs.voidps.engine.timer.Timer +import world.gregs.voidps.engine.timer.toTicks +import world.gregs.voidps.network.login.protocol.visual.update.player.EquipSlot +import world.gregs.voidps.type.Direction +import world.gregs.voidps.type.random +import java.util.concurrent.TimeUnit + +class WallBeast : Script { + init { + entered("wall_beast_spot_1", ::grab) + entered("wall_beast_spot_2", ::grab) + entered("wall_beast_spot_3", ::grab) + entered("wall_beast_spot_4", ::grab) + entered("wall_beast_spot_5", ::grab) + entered("wall_beast_spot_6", ::grab) + entered("wall_beast_spot_7", ::grab) + + npcTimerStart("wall_beast_revert") { + TimeUnit.SECONDS.toTicks(8) // TODO this is triggered on combat stop not a timer + } + + npcTimerTick("wall_beast_revert") { + if (underAttack) { // FIXME should be: attacking but doesn't check if swinging or not + return@npcTimerTick Timer.CONTINUE + } + clearTransform() + mode = EmptyMode + Timer.CANCEL + } + } + + fun grab(player: Player, definition: AreaDefinition) { + if (random.nextInt(4) != 0) { + return + } + val tile = definition.area.random() + val beast = NPCs.find(tile.addY(1), "hole_in_the_wall") + if (beast.transform == "wall_beast") { + return + } + if (beast.softTimers.contains("wall_beast_revert")) { + return + } + if (beast.target != null) { + return + } + if (beast.queue.contains("grab_player")) { + return + } + player.steps.clear() + player.strongQueue("grab_player", 2) { + player.tele(tile) + player.face(Direction.NORTH) + beast["movement_delay"] = Int.MAX_VALUE + if (player.equipped(EquipSlot.Hat).id == "spiny_helmet") { + player.message("Your helmet repels the wall beast!") + beast.anim("wall_beast_repelled_attack") + player.sound("wall_beast_foiled") + beast.transform("wall_beast") + pause(4) + beast.interactPlayer(player, "Attack") + beast.softTimers.start("wall_beast_revert") + return@strongQueue + } + player.message("A giant hand appears and grabs your head!") + player.anim("grabbed_by_wall_beast") + player.sound("wall_beast_attack") + beast.anim("wall_beast_attack") + pause(1) + player.anim("held_by_wall_beast") + beast.anim("wall_beast_hold") + pause(8) + beast.anim("released_by_wall_beast") + player.anim("wall_beast_release") + player.sound("male_defend_0") + player.directHit(160, source = beast) + beast.softTimers.start("wall_beast_revert") + } + } +} diff --git a/game/src/main/kotlin/content/entity/death/NPCDeath.kt b/game/src/main/kotlin/content/entity/death/NPCDeath.kt index 0bf2753eca..408b87cb17 100644 --- a/game/src/main/kotlin/content/entity/death/NPCDeath.kt +++ b/game/src/main/kotlin/content/entity/death/NPCDeath.kt @@ -58,9 +58,9 @@ class NPCDeath( val npc = this strongQueue(name = "death", 1) { val killer = killer - val tile = tile - npc["death_tile"] = tile val id = get("transform_id", npc.id) + val tile = if (id == "wall_beast") tile.addY(-1) else tile + npc["death_tile"] = tile val def = NPCDefinitions.get(id) val combat = combatDefinitions.get(def["combat_def", get("transform_id", npc.id)]) val ticks = anim(combat.deathAnim) From 2af6f5b6d5b38cf316214e56b0924ceb6df9a26c Mon Sep 17 00:00:00 2001 From: GregHib Date: Tue, 17 Mar 2026 20:13:05 +0000 Subject: [PATCH 07/27] Add brine rat cavern --- .../brine_rat_cavern.anims.toml} | 3 + .../brine_rat_cavern.areas.toml | 3 + .../brine_rat_cavern.combat.toml} | 0 .../brine_rat_cavern.drops.toml} | 0 .../brine_rat_cavern.npc-spawns.toml} | 2 +- .../brine_rat_cavern.npcs.toml} | 3 +- .../brine_rat_cavern.objs.toml | 5 ++ .../brine_rat_cavern.sounds.toml | 5 ++ .../brine_rat_cavern.teles.toml | 10 +++ data/skill/slayer/chaeldar.enums.toml | 2 +- data/skill/slayer/vannaka.enums.toml | 2 +- .../brine_rat_cavern/BrineRatCavern.kt | 61 +++++++++++++++++++ 12 files changed, 91 insertions(+), 5 deletions(-) rename data/area/fremennik_province/rellekka/{brine_rat_caverns/brine_rat_caverns.anims.toml => brine_rat_cavern/brine_rat_cavern.anims.toml} (73%) create mode 100644 data/area/fremennik_province/rellekka/brine_rat_cavern/brine_rat_cavern.areas.toml rename data/area/fremennik_province/rellekka/{brine_rat_caverns/brine_rat_caverns.combat.toml => brine_rat_cavern/brine_rat_cavern.combat.toml} (100%) rename data/area/fremennik_province/rellekka/{brine_rat_caverns/brine_rat_caverns.drops.toml => brine_rat_cavern/brine_rat_cavern.drops.toml} (100%) rename data/area/fremennik_province/rellekka/{brine_rat_caverns/brine_rat_caverns.npc-spawns.toml => brine_rat_cavern/brine_rat_cavern.npc-spawns.toml} (96%) rename data/area/fremennik_province/rellekka/{brine_rat_caverns/brine_rat_caverns.npcs.toml => brine_rat_cavern/brine_rat_cavern.npcs.toml} (97%) create mode 100644 data/area/fremennik_province/rellekka/brine_rat_cavern/brine_rat_cavern.objs.toml create mode 100644 data/area/fremennik_province/rellekka/brine_rat_cavern/brine_rat_cavern.sounds.toml create mode 100644 data/area/fremennik_province/rellekka/brine_rat_cavern/brine_rat_cavern.teles.toml create mode 100644 game/src/main/kotlin/content/area/fremennik_province/rellekka/brine_rat_cavern/BrineRatCavern.kt diff --git a/data/area/fremennik_province/rellekka/brine_rat_caverns/brine_rat_caverns.anims.toml b/data/area/fremennik_province/rellekka/brine_rat_cavern/brine_rat_cavern.anims.toml similarity index 73% rename from data/area/fremennik_province/rellekka/brine_rat_caverns/brine_rat_caverns.anims.toml rename to data/area/fremennik_province/rellekka/brine_rat_cavern/brine_rat_cavern.anims.toml index 0131b38685..3cb6e3cc1a 100644 --- a/data/area/fremennik_province/rellekka/brine_rat_caverns/brine_rat_caverns.anims.toml +++ b/data/area/fremennik_province/rellekka/brine_rat_cavern/brine_rat_cavern.anims.toml @@ -6,3 +6,6 @@ id = 6116 [brine_rat_attack] id = 6117 + +[olaf_boulder_push] +id = 6131 diff --git a/data/area/fremennik_province/rellekka/brine_rat_cavern/brine_rat_cavern.areas.toml b/data/area/fremennik_province/rellekka/brine_rat_cavern/brine_rat_cavern.areas.toml new file mode 100644 index 0000000000..6ed0bd6480 --- /dev/null +++ b/data/area/fremennik_province/rellekka/brine_rat_cavern/brine_rat_cavern.areas.toml @@ -0,0 +1,3 @@ +[brine_rat_cavern_entrance] +x = [2747, 2749] +y = [3732, 3733] \ No newline at end of file diff --git a/data/area/fremennik_province/rellekka/brine_rat_caverns/brine_rat_caverns.combat.toml b/data/area/fremennik_province/rellekka/brine_rat_cavern/brine_rat_cavern.combat.toml similarity index 100% rename from data/area/fremennik_province/rellekka/brine_rat_caverns/brine_rat_caverns.combat.toml rename to data/area/fremennik_province/rellekka/brine_rat_cavern/brine_rat_cavern.combat.toml diff --git a/data/area/fremennik_province/rellekka/brine_rat_caverns/brine_rat_caverns.drops.toml b/data/area/fremennik_province/rellekka/brine_rat_cavern/brine_rat_cavern.drops.toml similarity index 100% rename from data/area/fremennik_province/rellekka/brine_rat_caverns/brine_rat_caverns.drops.toml rename to data/area/fremennik_province/rellekka/brine_rat_cavern/brine_rat_cavern.drops.toml diff --git a/data/area/fremennik_province/rellekka/brine_rat_caverns/brine_rat_caverns.npc-spawns.toml b/data/area/fremennik_province/rellekka/brine_rat_cavern/brine_rat_cavern.npc-spawns.toml similarity index 96% rename from data/area/fremennik_province/rellekka/brine_rat_caverns/brine_rat_caverns.npc-spawns.toml rename to data/area/fremennik_province/rellekka/brine_rat_cavern/brine_rat_cavern.npc-spawns.toml index 8e8523b26f..52a64a8a63 100644 --- a/data/area/fremennik_province/rellekka/brine_rat_caverns/brine_rat_caverns.npc-spawns.toml +++ b/data/area/fremennik_province/rellekka/brine_rat_cavern/brine_rat_cavern.npc-spawns.toml @@ -26,5 +26,5 @@ spawns = [ { id = "brine_rat", x = 2708, y = 10131, members = true }, { id = "brine_rat", x = 2710, y = 10134, members = true }, { id = "brine_rat", x = 2712, y = 10132, members = true }, - { id = "boulder_brine_rat_caverns", x = 2691, y = 10124, members = true }, + { id = "olaf_cave_boulder", x = 2691, y = 10124, members = true }, ] diff --git a/data/area/fremennik_province/rellekka/brine_rat_caverns/brine_rat_caverns.npcs.toml b/data/area/fremennik_province/rellekka/brine_rat_cavern/brine_rat_cavern.npcs.toml similarity index 97% rename from data/area/fremennik_province/rellekka/brine_rat_caverns/brine_rat_caverns.npcs.toml rename to data/area/fremennik_province/rellekka/brine_rat_cavern/brine_rat_cavern.npcs.toml index d06d25824c..de56a47aac 100644 --- a/data/area/fremennik_province/rellekka/brine_rat_caverns/brine_rat_caverns.npcs.toml +++ b/data/area/fremennik_province/rellekka/brine_rat_cavern/brine_rat_cavern.npcs.toml @@ -76,6 +76,5 @@ respawn_delay = 30 drop_table = "brine_rat" examine = "Eww, a bald rat!" -[boulder_brine_rat_caverns] +[olaf_cave_boulder] id = 3708 - diff --git a/data/area/fremennik_province/rellekka/brine_rat_cavern/brine_rat_cavern.objs.toml b/data/area/fremennik_province/rellekka/brine_rat_cavern/brine_rat_cavern.objs.toml new file mode 100644 index 0000000000..9bdbf2520b --- /dev/null +++ b/data/area/fremennik_province/rellekka/brine_rat_cavern/brine_rat_cavern.objs.toml @@ -0,0 +1,5 @@ +[brine_rat_cavern_exit] +id = 23157 + +[brine_rat_cavern_exit_cave] +id = 23158 \ No newline at end of file diff --git a/data/area/fremennik_province/rellekka/brine_rat_cavern/brine_rat_cavern.sounds.toml b/data/area/fremennik_province/rellekka/brine_rat_cavern/brine_rat_cavern.sounds.toml new file mode 100644 index 0000000000..4a4ffd225c --- /dev/null +++ b/data/area/fremennik_province/rellekka/brine_rat_cavern/brine_rat_cavern.sounds.toml @@ -0,0 +1,5 @@ +[olaf_fall_into_cave] +id = 3553 + +[olaf_roll_boulder] +id = 3554 diff --git a/data/area/fremennik_province/rellekka/brine_rat_cavern/brine_rat_cavern.teles.toml b/data/area/fremennik_province/rellekka/brine_rat_cavern/brine_rat_cavern.teles.toml new file mode 100644 index 0000000000..966202cf0b --- /dev/null +++ b/data/area/fremennik_province/rellekka/brine_rat_cavern/brine_rat_cavern.teles.toml @@ -0,0 +1,10 @@ +[brine_rat_cavern_exit] +option = "Exit" +tile = { x = 2689, y = 10125 } +to = { x = 2729, y = 3734 } + + +[brine_rat_cavern_exit_cave] +option = "Exit" +tile = { x = 2689, y = 10124 } +to = { x = 2729, y = 3734 } \ No newline at end of file diff --git a/data/skill/slayer/chaeldar.enums.toml b/data/skill/slayer/chaeldar.enums.toml index 6894b09e7c..032fc6d500 100644 --- a/data/skill/slayer/chaeldar.enums.toml +++ b/data/skill/slayer/chaeldar.enums.toml @@ -49,7 +49,7 @@ values = { black_demon = 10, bloodveld = 8, blue_dragon = 8, -# brine_rat = 7, + brine_rat = 7, # cave_horror = 10, rock_crab = 8, dagannoth_lighthouse_range_74 = 11, diff --git a/data/skill/slayer/vannaka.enums.toml b/data/skill/slayer/vannaka.enums.toml index db32faae86..c1a2fde7cd 100644 --- a/data/skill/slayer/vannaka.enums.toml +++ b/data/skill/slayer/vannaka.enums.toml @@ -57,7 +57,7 @@ values = { basilisk = 8, bloodveld = 8, black_dragon = 7, -# brine_rat = 7, + brine_rat = 7, cockatrice = 8, rock_crab = 7, crocodile_kharidian_desert = 6, diff --git a/game/src/main/kotlin/content/area/fremennik_province/rellekka/brine_rat_cavern/BrineRatCavern.kt b/game/src/main/kotlin/content/area/fremennik_province/rellekka/brine_rat_cavern/BrineRatCavern.kt new file mode 100644 index 0000000000..8b6ef6e2a8 --- /dev/null +++ b/game/src/main/kotlin/content/area/fremennik_province/rellekka/brine_rat_cavern/BrineRatCavern.kt @@ -0,0 +1,61 @@ +package content.area.fremennik_province.rellekka.brine_rat_cavern + +import world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.client.message +import world.gregs.voidps.engine.client.ui.open +import world.gregs.voidps.engine.data.definition.Areas +import world.gregs.voidps.engine.entity.character.areaSound +import world.gregs.voidps.engine.entity.character.move.tele +import world.gregs.voidps.engine.entity.character.sound +import world.gregs.voidps.type.Direction +import world.gregs.voidps.type.Tile +import world.gregs.voidps.type.equals + +class BrineRatCavern : Script { + init { + npcOperate("Roll", "olaf_cave_boulder") { (target) -> + if (!target.tile.equals(2691, 10124)) { + return@npcOperate + } + walkToDelay(Tile(2692, 10123)) + message("You push the boulder.") + face(Direction.NORTH) + areaSound("olaf_roll_boulder", tile, radius = 7) + delay(1) + anim("olaf_boulder_push") + delay(1) + target.exactMove(Tile(2691, 10126), startDelay = 20, delay = 75) + exactMoveDelay(Tile(2691, 10125), startDelay = 20, delay = 75, direction = Direction.NORTH) + delay(3) + clearAnim() + face(Direction.NORTH) + delay(5) + tele(2691, 10125) + } + + objTeleportTakeOff("Exit", "brine_rat_cavern_exit*") { _, _ -> + message("You squeeze through the narrow crack in the cliff face.") + 1 + } + + objTeleportLand("Exit", "brine_rat_cavern_exit*") { _, _ -> + message("You exit the cave. From this side you can't even make out how to get into the cave!") + message("You'll likely need to dig to get back inside.") + } + + itemOption("Dig", "spade") { + if (tile !in Areas["brine_rat_cavern_entrance"]) { + return@itemOption + } + message("You dig a hole...") + open("fade_out") + delay(4) + tele(2697, 10120) + delay(1) + message("...and fall into a dark and slimy pit!") + gfx("stun_long", delay = 20) + open("fade_in") + sound("olaf_fall_into_cave") + } + } +} From 9130afb6bf80c92d428d3614398544a5d40bfb67 Mon Sep 17 00:00:00 2001 From: GregHib Date: Tue, 17 Mar 2026 20:24:20 +0000 Subject: [PATCH 08/27] Fix charter ship interface --- .../main/kotlin/content/entity/obj/ship/CharterShip.kt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/game/src/main/kotlin/content/entity/obj/ship/CharterShip.kt b/game/src/main/kotlin/content/entity/obj/ship/CharterShip.kt index e4a61b540a..c6ab30017a 100644 --- a/game/src/main/kotlin/content/entity/obj/ship/CharterShip.kt +++ b/game/src/main/kotlin/content/entity/obj/ship/CharterShip.kt @@ -9,6 +9,7 @@ import net.pearx.kasechange.toTitleCase import world.gregs.voidps.engine.Script import world.gregs.voidps.engine.client.message import world.gregs.voidps.engine.client.ui.chat.toDigitGroupString +import world.gregs.voidps.engine.client.ui.closeMenu import world.gregs.voidps.engine.client.ui.open import world.gregs.voidps.engine.entity.character.jingle import world.gregs.voidps.engine.entity.character.move.tele @@ -17,7 +18,7 @@ import world.gregs.voidps.engine.entity.character.player.Player import world.gregs.voidps.engine.entity.character.player.chat.ChatType import world.gregs.voidps.engine.inv.inventory import world.gregs.voidps.engine.inv.remove -import world.gregs.voidps.engine.queue.strongQueue +import world.gregs.voidps.engine.queue.queue import world.gregs.voidps.type.Tile class CharterShip(val ships: CharterShips, val teles: ObjectTeleports) : Script { @@ -99,12 +100,13 @@ class CharterShip(val ships: CharterShips, val teles: ObjectTeleports) : Script if (component == currentLocation) { return@interfaceOption } + closeMenu() val price = ships.get(currentLocation, component) ?: return@interfaceOption if (!hasQuestRequirements(component)) { return@interfaceOption } val readablePrice = price.toDigitGroupString() - strongQueue("charter_ship") { + queue("charter_ship") { if (!inventory.contains("coins", price)) { choice("Sailing to ${component.toTitleCase()} costs $readablePrice coins.") { option("Choose again") { @@ -112,7 +114,7 @@ class CharterShip(val ships: CharterShips, val teles: ObjectTeleports) : Script } option("No") } - return@strongQueue + return@queue } statement("To sail to ${component.toTitleCase()} from here will cost you $readablePrice gold. Are you sure you want to pay that?") choice { From cd8c46c6bd68b553e2465a42b9af3128f1629110 Mon Sep 17 00:00:00 2001 From: GregHib Date: Wed, 18 Mar 2026 20:48:06 +0000 Subject: [PATCH 09/27] Add cave horrors --- .../caves/mos_le_harmless.combat.toml | 13 --- .../caves/mos_le_harmless_caves.anims.toml | 10 +++ .../caves/mos_le_harmless_caves.combat.toml | 51 ++++++++++++ .../caves/mos_le_harmless_caves.npcs.toml | 2 +- .../caves/mos_le_harmless_caves.sounds.toml | 9 ++ .../mos_le_harmless.areas.toml | 6 +- .../mos_le_harmless/mos_le_harmless.npcs.toml | 4 +- .../mos_le_harmless/mos_le_harmless.objs.toml | 9 ++ .../mos_le_harmless.teles.toml | 9 ++ data/skill/slayer/chaeldar.enums.toml | 2 +- data/skill/slayer/sumona.enums.toml | 2 +- .../morytania/mos_le_harmless/CaveyDavey.kt | 83 +++++++++++++++++++ .../mos_le_harmless/MosLeHarmlessCave.kt | 42 ++++++++++ .../content/entity/player/combat/Attack.kt | 4 + 14 files changed, 226 insertions(+), 20 deletions(-) delete mode 100644 data/area/morytania/mos_le_harmless/caves/mos_le_harmless.combat.toml create mode 100644 data/area/morytania/mos_le_harmless/caves/mos_le_harmless_caves.combat.toml create mode 100644 data/area/morytania/mos_le_harmless/mos_le_harmless.teles.toml create mode 100644 game/src/main/kotlin/content/area/morytania/mos_le_harmless/CaveyDavey.kt create mode 100644 game/src/main/kotlin/content/area/morytania/mos_le_harmless/MosLeHarmlessCave.kt diff --git a/data/area/morytania/mos_le_harmless/caves/mos_le_harmless.combat.toml b/data/area/morytania/mos_le_harmless/caves/mos_le_harmless.combat.toml deleted file mode 100644 index cd3e1c5cd2..0000000000 --- a/data/area/morytania/mos_le_harmless/caves/mos_le_harmless.combat.toml +++ /dev/null @@ -1,13 +0,0 @@ -[cave_horror] -retreat_range = 8 -attack_speed = 5 -defend_anim = "cave_horror_defend" -death_anim = "cave_horror_howl" -defend_sound = "jungle_horror_defend" -death_sound = "jungle_horror_death" - -[cave_horror.attack] -range = 1 -anim = "cave_horror_swing" -target_sound = "jungle_horror_attack" -target_hit = { offense = "crush", defence = "magic", max = 90 } \ No newline at end of file diff --git a/data/area/morytania/mos_le_harmless/caves/mos_le_harmless_caves.anims.toml b/data/area/morytania/mos_le_harmless/caves/mos_le_harmless_caves.anims.toml index 01cf8d7c3f..db22db6dcd 100644 --- a/data/area/morytania/mos_le_harmless/caves/mos_le_harmless_caves.anims.toml +++ b/data/area/morytania/mos_le_harmless/caves/mos_le_harmless_caves.anims.toml @@ -9,3 +9,13 @@ id = 4235 [cave_horror_howl] id = 4237 + +[mosquito_defend] +id = 2399 + +[mosquito_death] +id = 2398 + +[mosquito_attack] +id = 2397 + diff --git a/data/area/morytania/mos_le_harmless/caves/mos_le_harmless_caves.combat.toml b/data/area/morytania/mos_le_harmless/caves/mos_le_harmless_caves.combat.toml new file mode 100644 index 0000000000..7673bc41e5 --- /dev/null +++ b/data/area/morytania/mos_le_harmless/caves/mos_le_harmless_caves.combat.toml @@ -0,0 +1,51 @@ +[cave_horror] +retreat_range = 8 +attack_speed = 5 # TODO support irregular attack speeds/override +defend_anim = "cave_horror_defend" +death_anim = "cave_horror_howl" +defend_sound = "jungle_horror_defend" +death_sound = "jungle_horror_death" + +[cave_horror.attack] +range = 1 +anim = "cave_horror_swing" +target_sound = "jungle_horror_attack" +target_hit = { offense = "crush", defence = "magic", max = 90 } + +[cave_horror.howl] +condition = "witchwood_icon" +range = 1 +anim = "cave_horror_howl" +target_sound = "cave_horror_howl" +target_hit = { offense = "crush", defence = "magic", max = 90 } + +[cave_horror.special] +condition = "no_witchwood_icon" +range = 1 +anim = "cave_horror_howl" +target_sound = "cave_horror_howl" +target_hit = { offense = "crush", defence = "magic", min = 80, max = 90 } +# Copied from banshee's, not sure what actually drains as osrs works differently +impact_drains = [ + { skill = "attack", multiplier = 0.2 }, + { skill = "strength", multiplier = 0.2 }, + { skill = "defence", multiplier = 0.2 }, + { skill = "ranged", multiplier = 0.2 }, + { skill = "magic", multiplier = 0.2 }, + { skill = "prayer", multiplier = 0.1 }, + { skill = "agility", multiplier = 0.1 }, +] + +[giant_mosquito] +retreat_range = 8 +attack_speed = 12 +defend_anim = "mosquito_defend" +defend_sound = "mosquito_defend" +death_anim = "mosquito_death" +death_sound = "mosquito_death" + +[giant_mosquito.attack] +range = 1 +anim = "mosquito_attack" +target_sound = "mosquito_attack" +target_hit = { offense = "crush", max = 10 } diff --git a/data/area/morytania/mos_le_harmless/caves/mos_le_harmless_caves.npcs.toml b/data/area/morytania/mos_le_harmless/caves/mos_le_harmless_caves.npcs.toml index 75327b6d0e..fe2aca05fd 100644 --- a/data/area/morytania/mos_le_harmless/caves/mos_le_harmless_caves.npcs.toml +++ b/data/area/morytania/mos_le_harmless/caves/mos_le_harmless_caves.npcs.toml @@ -6,7 +6,7 @@ str = 57 def = 30 combat_def = "bat" max_hit_stab = 70 -hunt_mode = "cowardly" +hunt_mode = "aggressive" slayer_xp = 33.0 categories = ["bats"] respawn_delay = 35 diff --git a/data/area/morytania/mos_le_harmless/caves/mos_le_harmless_caves.sounds.toml b/data/area/morytania/mos_le_harmless/caves/mos_le_harmless_caves.sounds.toml index 0a09bd5788..4cf0023f2e 100644 --- a/data/area/morytania/mos_le_harmless/caves/mos_le_harmless_caves.sounds.toml +++ b/data/area/morytania/mos_le_harmless/caves/mos_le_harmless_caves.sounds.toml @@ -12,3 +12,12 @@ id = 499 [jungle_horror_defend] id = 500 + +[mosquito_attack] +id = 637 + +[mosquito_defend] +id = 821 + +[mosquito_death] +id = 820 diff --git a/data/area/morytania/mos_le_harmless/mos_le_harmless.areas.toml b/data/area/morytania/mos_le_harmless/mos_le_harmless.areas.toml index 0073c29210..f847f1e3d9 100644 --- a/data/area/morytania/mos_le_harmless/mos_le_harmless.areas.toml +++ b/data/area/morytania/mos_le_harmless/mos_le_harmless.areas.toml @@ -2,4 +2,8 @@ x = [3648, 3853] y = [2880, 3071] tags = ["penguin_area"] -hint = "where pirates feel mostly harmless." \ No newline at end of file +hint = "where pirates feel mostly harmless." + +[mos_le_harmless_cave] +x = [3712, 3839] +y = [9344, 9741] \ No newline at end of file diff --git a/data/area/morytania/mos_le_harmless/mos_le_harmless.npcs.toml b/data/area/morytania/mos_le_harmless/mos_le_harmless.npcs.toml index 09c7167c78..fff72db9a5 100644 --- a/data/area/morytania/mos_le_harmless/mos_le_harmless.npcs.toml +++ b/data/area/morytania/mos_le_harmless/mos_le_harmless.npcs.toml @@ -114,9 +114,7 @@ id = 4347 hitpoints = 30 att = 5 def = 45 -attack_speed = 12 -style = "crush" -max_hit_melee = 10 +combat_def = "giant_mosquito" respawn_delay = 50 examine = "A flying blood sucker." diff --git a/data/area/morytania/mos_le_harmless/mos_le_harmless.objs.toml b/data/area/morytania/mos_le_harmless/mos_le_harmless.objs.toml index c7b8016b8d..571bbc424a 100644 --- a/data/area/morytania/mos_le_harmless/mos_le_harmless.objs.toml +++ b/data/area/morytania/mos_le_harmless/mos_le_harmless.objs.toml @@ -1,3 +1,12 @@ [bank_booth_mos_le_harmless] id = 11338 examine = "The bank teller will serve you from here." + +[cave_entrance_mos_le_harmless] +id = 15767 + +[cave_exit_mos_le_harmless] +id = 15811 + +[cave_exit_2_mos_le_harmless] +id = 15812 diff --git a/data/area/morytania/mos_le_harmless/mos_le_harmless.teles.toml b/data/area/morytania/mos_le_harmless/mos_le_harmless.teles.toml new file mode 100644 index 0000000000..88dd58d83e --- /dev/null +++ b/data/area/morytania/mos_le_harmless/mos_le_harmless.teles.toml @@ -0,0 +1,9 @@ +[cave_exit_mos_le_harmless] +option = "Exit" +tile = { x = 3749, y = 9373 } +to = { x = 3749, y = 2973 } + +[cave_exit_2_mos_le_harmless] +option = "Exit" +tile = { x = 3749, y = 9374 } +to = { x = 3749, y = 2973 } diff --git a/data/skill/slayer/chaeldar.enums.toml b/data/skill/slayer/chaeldar.enums.toml index 032fc6d500..4047526781 100644 --- a/data/skill/slayer/chaeldar.enums.toml +++ b/data/skill/slayer/chaeldar.enums.toml @@ -50,7 +50,7 @@ values = { bloodveld = 8, blue_dragon = 8, brine_rat = 7, -# cave_horror = 10, + cave_horror = 10, rock_crab = 8, dagannoth_lighthouse_range_74 = 11, # dust_devil = 9, diff --git a/data/skill/slayer/sumona.enums.toml b/data/skill/slayer/sumona.enums.toml index ed98b38af6..2ed9d71389 100644 --- a/data/skill/slayer/sumona.enums.toml +++ b/data/skill/slayer/sumona.enums.toml @@ -52,7 +52,7 @@ values = { bloodveld = 10, blue_dragon = 5, cave_crawler = 15, -# cave_horror = 15, + cave_horror = 15, crocodile_kharidian_desert = 4, dagannoth_lighthouse_range_74 = 10, lizard = 4, diff --git a/game/src/main/kotlin/content/area/morytania/mos_le_harmless/CaveyDavey.kt b/game/src/main/kotlin/content/area/morytania/mos_le_harmless/CaveyDavey.kt new file mode 100644 index 0000000000..f2b2ddc558 --- /dev/null +++ b/game/src/main/kotlin/content/area/morytania/mos_le_harmless/CaveyDavey.kt @@ -0,0 +1,83 @@ +package content.area.morytania.mos_le_harmless + +import content.entity.player.dialogue.* +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.equip.equipped +import world.gregs.voidps.engine.inv.inventory +import world.gregs.voidps.network.login.protocol.visual.update.player.EquipSlot +import world.gregs.voidps.type.random + +class CaveyDavey : Script { + init { + npcOperate("Talk-to", "cavey_davey") { + if (equipped(EquipSlot.Amulet).id == "witchwood_icon") { + npc("Be ye here te deal with the Horrors?") + player("I might well give it a shot.") + npc("Aye, well, keep yer Icon with ye if ye wants te walk out alive again.") + player("Don't worry, I will.") + } else if (inventory.contains("witchwood_icon")) { + npc("Be ye some form of simpleton? Do ye not hear the howlin' of the Horrors?") + player("Well, I can hear something now you mention it...") + npc("That be them, howlin', always howlin'!") + npc("If ye value yer limbs ye'll put that Icon round yer neck, and hope they don't come out into the light!") + player("Err, all right, I'll get right on that.") + } else { + npc("Be ye mad? There be Horrors in this cave!") + player("What do you mean?") + npc("Have ye ever heard of the sort of evil, flesh-eatin' horrors that dwell in the darkest pits of the world?") + npc("The sort of dark, sanity-breakin' THINGS that cause the livin' to drop to their knees and weep for the fate of all creation?") + npc("Well, have ye?") + player("Yes, I think I've killed a few of them as well.") + npc("Well, that's ok then.") + npc("But, ye'll need a Witchwood Icon from a slayer master if ye want te go in these caves and live.") + player("Why?") + npc("Well, ye see them Jungle Horrors? Well down in the caves there be Cave Horrors.") + npc("They are bigger, badder, meaner, and have a howl that freezes the blood in yer veins.") + npc("Wearin' earmuffs or a helmet won't work, cos them masks they wear make the sound magical. Only thing that works is wearin' a Witchwood Icon.") + npc("That is, o'course, if ye can see them, cos if ye don't have any light down there then yer likely te be picked te bones by the insects before the Horrors get ye.") + player("I see, thanks for the warning.") + npc("Yer welcome.") + } + } + + npcOperate("Talk-to", "monkey_mos_le_harmless") { + if (equipped(EquipSlot.Amulet).id == "monkeyspeak_amulet") { + npc("Eeekeek ookeek!") + return@npcOperate + } + when (random.nextInt(4)) { + 0 -> { + npc("Arr!") + player("Arr!") + npc("Arr!") + player("Arr!") + npc("Arr!") + player("Arr!") + npc("Arr!") + player("Arr!") + npc("Bored now...") + } + 1 -> { + npc("Let me go, can't ye hear them? Howlin' in the dark...") + player("What do you mean?") + npc("I'm not hangin' around te be killed!") + npc("The Horrors, the Horrors!") + } + 2 -> npc("I'm not goin' back in that brewery, not fer all the Bitternuts I can carry!") + 3 -> { + npc("Arr! Yer messin with me monkey plunder!") + player("What?") + } + else -> { + npc("Are ye here for...the stuff?") + player("What?") + npc("You know...the 'special' bananas?") + player("No...why do you ask?") + npc("No reason. Have a nice day.") + } + } + } + } +} diff --git a/game/src/main/kotlin/content/area/morytania/mos_le_harmless/MosLeHarmlessCave.kt b/game/src/main/kotlin/content/area/morytania/mos_le_harmless/MosLeHarmlessCave.kt new file mode 100644 index 0000000000..8ba8933720 --- /dev/null +++ b/game/src/main/kotlin/content/area/morytania/mos_le_harmless/MosLeHarmlessCave.kt @@ -0,0 +1,42 @@ +package content.area.morytania.mos_le_harmless + +import content.entity.player.dialogue.type.choice +import content.skill.firemaking.Light +import world.gregs.voidps.engine.Script +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.player.Player +import world.gregs.voidps.engine.entity.character.player.equip.equipped +import world.gregs.voidps.network.login.protocol.visual.update.player.EquipSlot + +class MosLeHarmlessCave : Script { + init { + objectOperate("Enter", "cave_entrance_mos_le_harmless") { + choice("Do you still want to proceed?") { + option("Yes, I think I am fully prepared.") { + open("fade_in") + tele(3748, 9373) + } + option("No, I think I need to go get food, weapons and a light source.") + } + } + + npcCondition("witchwood_icon") { target -> target is Player && target.equipped(EquipSlot.Amulet).id == "witchwood_icon" } + npcCondition("no_witchwood_icon") { target -> target is Player && target.equipped(EquipSlot.Amulet).id != "witchwood_icon" } + + entered("mos_le_harmless_cave") { + if (Light.hasLightSource(this)) { + open("level_one_darkness") + } else { + open("level_three_darkness") + timers.start("insect_swarm") + } + } + + exited("mos_le_harmless_cave") { + close("level_one_darkness") + close("level_three_darkness") + } + } +} diff --git a/game/src/main/kotlin/content/entity/player/combat/Attack.kt b/game/src/main/kotlin/content/entity/player/combat/Attack.kt index 90b3797267..30e122eb75 100644 --- a/game/src/main/kotlin/content/entity/player/combat/Attack.kt +++ b/game/src/main/kotlin/content/entity/player/combat/Attack.kt @@ -28,6 +28,10 @@ class Attack : Script { message("You need a higher slayer level to know how to wound this monster.") return@npcApproach } + if (timers.contains("insect_swarm")) { + message("You can't see to attack anything!") + return@npcApproach + } if (equipped(EquipSlot.Weapon).id.endsWith("_greegree")) { statement("You cannot attack as a monkey.") return@npcApproach From b0f39e3715a32cbbf9b8bc15d9b08fe66a762d7f Mon Sep 17 00:00:00 2001 From: GregHib Date: Wed, 18 Mar 2026 21:23:02 +0000 Subject: [PATCH 10/27] Add smoke dungeon entrance --- .../kharidian_desert/pollnivneach/pollnivneach.objs.toml | 5 +++++ .../pollnivneach/pollnivneach.teles.toml | 9 +++++++++ 2 files changed, 14 insertions(+) create mode 100644 data/area/kharidian_desert/pollnivneach/pollnivneach.objs.toml create mode 100644 data/area/kharidian_desert/pollnivneach/pollnivneach.teles.toml diff --git a/data/area/kharidian_desert/pollnivneach/pollnivneach.objs.toml b/data/area/kharidian_desert/pollnivneach/pollnivneach.objs.toml new file mode 100644 index 0000000000..b810c29063 --- /dev/null +++ b/data/area/kharidian_desert/pollnivneach/pollnivneach.objs.toml @@ -0,0 +1,5 @@ +[smoke_dungeon_well] +id = 36002 + +[smoke_dungeon_rope] +id = 6439 diff --git a/data/area/kharidian_desert/pollnivneach/pollnivneach.teles.toml b/data/area/kharidian_desert/pollnivneach/pollnivneach.teles.toml new file mode 100644 index 0000000000..0df242809d --- /dev/null +++ b/data/area/kharidian_desert/pollnivneach/pollnivneach.teles.toml @@ -0,0 +1,9 @@ +[smoke_dungeon_well] +option = "Climb-down" +tile = { x = 3310, y = 2962 } +to = { x = 3206, y = 9379 } + +[smoke_dungeon_rope] +option = "Climb-up" +tile = { x = 3205, y = 9379 } +to = { x = 3310, y = 2961 } From 3a8ece936300aa88ef2cb779c807ab3f2e12096c Mon Sep 17 00:00:00 2001 From: GregHib Date: Thu, 19 Mar 2026 15:46:49 +0000 Subject: [PATCH 11/27] Add desert snake anims --- data/area/kharidian_desert/kharidian_desert.anims.toml | 9 +++++++++ .../area/kharidian_desert/kharidian_desert.combat.toml | 10 ++++++++++ data/area/kharidian_desert/kharidian_desert.npcs.toml | 5 +++++ .../area/kharidian_desert/kharidian_desert.sounds.toml | 9 +++++++++ 4 files changed, 33 insertions(+) diff --git a/data/area/kharidian_desert/kharidian_desert.anims.toml b/data/area/kharidian_desert/kharidian_desert.anims.toml index b31ef144d8..faa31f3c47 100644 --- a/data/area/kharidian_desert/kharidian_desert.anims.toml +++ b/data/area/kharidian_desert/kharidian_desert.anims.toml @@ -15,3 +15,12 @@ id = 3841 [goat_attack] id = 4936 + +[snake_attack] +id = 275 + +[snake_defend] +id = 276 + +[snake_death] +id = 278 \ No newline at end of file diff --git a/data/area/kharidian_desert/kharidian_desert.combat.toml b/data/area/kharidian_desert/kharidian_desert.combat.toml index dd5b8c7ab2..5376180837 100644 --- a/data/area/kharidian_desert/kharidian_desert.combat.toml +++ b/data/area/kharidian_desert/kharidian_desert.combat.toml @@ -29,3 +29,13 @@ clone = "goat_kharidian_desert" [goat_kharidian_desert_2.melee] clone = "goat_kharidian_desert.melee" +[desert_snake] +attack_speed = 4 +retreat_range = 8 +defend_anim = "snake_defend" +death_anim = "snake_death" + +[desert_snake.melee] +range = 1 +anim = "snake_attack" +target_hit = { offense = "stab", max = 10 } \ No newline at end of file diff --git a/data/area/kharidian_desert/kharidian_desert.npcs.toml b/data/area/kharidian_desert/kharidian_desert.npcs.toml index d8f4acc972..64fd82af3c 100644 --- a/data/area/kharidian_desert/kharidian_desert.npcs.toml +++ b/data/area/kharidian_desert/kharidian_desert.npcs.toml @@ -37,6 +37,11 @@ wander_range = 2 [desert_snake] id = 1874 +hitpoints = 60 +att = 4 +str = 5 +def = 3 +combat_def = "desert_snake" examine = "A slithering serpent." [desert_phoenix_kharidian_desert] diff --git a/data/area/kharidian_desert/kharidian_desert.sounds.toml b/data/area/kharidian_desert/kharidian_desert.sounds.toml index 2d92760cf2..5847e8609f 100644 --- a/data/area/kharidian_desert/kharidian_desert.sounds.toml +++ b/data/area/kharidian_desert/kharidian_desert.sounds.toml @@ -6,3 +6,12 @@ id = 387 [crocodile_defend] id = 388 + +[snake_attack] +id = 797 + +[snake_death] +id = 798 + +[snake_defend] +id = 800 From beb4f7ac886ffcea0dc2e325f46a22952d18a45a Mon Sep 17 00:00:00 2001 From: GregHib Date: Thu, 19 Mar 2026 18:47:30 +0000 Subject: [PATCH 12/27] Add desert heat --- .../al_kharid/al_kharid.shops.toml | 8 +- data/area/kharidian_desert/desert.items.toml | 16 +-- data/area/kharidian_desert/desert.objs.toml | 10 +- .../kharidian_desert.anims.toml | 10 +- .../kharidian_desert.areas.toml | 8 +- .../kharidian_desert.combat.toml | 11 -- .../kharidian_desert.enums.toml | 29 +++++ .../kharidian_desert.npcs.toml | 2 +- .../kharidian_desert.sounds.toml | 10 +- .../monster/reptile/snake/snake.anims.toml | 8 ++ .../monster/reptile/snake/snake.combat.toml | 3 + .../data/definition/AnimationDefinitions.kt | 1 + .../area/kharidian_desert/DesertHeat.kt | 103 ++++++++++++++++++ game/src/main/resources/game.properties | 3 + 14 files changed, 179 insertions(+), 43 deletions(-) create mode 100644 data/area/kharidian_desert/kharidian_desert.enums.toml create mode 100644 data/entity/npc/monster/reptile/snake/snake.anims.toml create mode 100644 game/src/main/kotlin/content/area/kharidian_desert/DesertHeat.kt diff --git a/data/area/kharidian_desert/al_kharid/al_kharid.shops.toml b/data/area/kharidian_desert/al_kharid/al_kharid.shops.toml index 7e7663ca58..586747221c 100644 --- a/data/area/kharidian_desert/al_kharid/al_kharid.shops.toml +++ b/data/area/kharidian_desert/al_kharid/al_kharid.shops.toml @@ -115,14 +115,14 @@ defaults = [ [alis_discount_wares_menaphite] id = 311 defaults = [ - { id = "menap_headgear_purple", amount = 30 }, + { id = "menaphite_headgear_purple", amount = 30 }, { id = "menaphite_top_purple", amount = 30 }, { id = "menaphite_robe_purple", amount = 30 }, - { id = "menap_action_kilt_purple", amount = 30 }, - { id = "menap_headgear_red", amount = 30 }, + { id = "menaphite_action_kilt_purple", amount = 30 }, + { id = "menaphite_headgear_red", amount = 30 }, { id = "menaphite_top_red", amount = 30 }, { id = "menaphite_robe_red", amount = 30 }, - { id = "menap_action_kilt_red", amount = 30 }, + { id = "menaphite_action_kilt_red", amount = 30 }, ] [alis_discount_wares_desert] diff --git a/data/area/kharidian_desert/desert.items.toml b/data/area/kharidian_desert/desert.items.toml index 253a80a69b..0b4b93e3c4 100644 --- a/data/area/kharidian_desert/desert.items.toml +++ b/data/area/kharidian_desert/desert.items.toml @@ -100,7 +100,7 @@ examine = "Better than factor 50 sun cream." [desert_legs_noted] id = 6391 -[menap_headgear_purple] +[menaphite_headgear_purple] id = 6392 price = 475 limit = 100 @@ -109,7 +109,7 @@ slot = "Hat" type = "Hair" examine = "Good for keeping the sun off my neck." -[menap_headgear_purple_noted] +[menaphite_headgear_purple_noted] id = 6393 [menaphite_top_purple] @@ -134,7 +134,7 @@ examine = "A cool light Menaphite robe." [menaphite_robe_purple_noted] id = 6397 -[menap_action_kilt_purple] +[menaphite_action_kilt_purple] id = 6398 price = 265 limit = 100 @@ -142,10 +142,10 @@ weight = 0.005 slot = "Legs" examine = "Look at those nobbily knees." -[menap_action_kilt_purple_noted] +[menaphite_action_kilt_purple_noted] id = 6399 -[menap_headgear_red] +[menaphite_headgear_red] id = 6400 price = 443 limit = 100 @@ -154,7 +154,7 @@ slot = "Hat" type = "Hair" examine = "Good for keeping the sun off my neck." -[menap_headgear_red_noted] +[menaphite_headgear_red_noted] id = 6401 [menaphite_top_red] @@ -179,7 +179,7 @@ examine = "A cool light Menaphite robe." [menaphite_robe_red_noted] id = 6405 -[menap_action_kilt_red] +[menaphite_action_kilt_red] id = 6406 price = 249 limit = 100 @@ -187,7 +187,7 @@ weight = 0.005 slot = "Legs" examine = "Look at those nobbily knees." -[menap_action_kilt_red_noted] +[menaphite_action_kilt_red_noted] id = 6407 [oak_blackjack_o] diff --git a/data/area/kharidian_desert/desert.objs.toml b/data/area/kharidian_desert/desert.objs.toml index c427c9140b..9df0447e2e 100644 --- a/data/area/kharidian_desert/desert.objs.toml +++ b/data/area/kharidian_desert/desert.objs.toml @@ -4,4 +4,12 @@ examine = "Where does it go?" [desert_obelisk_ladder_up] id = 28740 -examine = "Going up?" \ No newline at end of file +examine = "Going up?" + +[desert_cactus_full] +id = 2670 +examine = "A succulent cactus." + +[desert_cactus_empty] +id = 2671 +examine = "A less-than-succulent succulent." \ No newline at end of file diff --git a/data/area/kharidian_desert/kharidian_desert.anims.toml b/data/area/kharidian_desert/kharidian_desert.anims.toml index faa31f3c47..5dd582a7b4 100644 --- a/data/area/kharidian_desert/kharidian_desert.anims.toml +++ b/data/area/kharidian_desert/kharidian_desert.anims.toml @@ -16,11 +16,5 @@ id = 3841 [goat_attack] id = 4936 -[snake_attack] -id = 275 - -[snake_defend] -id = 276 - -[snake_death] -id = 278 \ No newline at end of file +[knife_chop] +id = 911 diff --git a/data/area/kharidian_desert/kharidian_desert.areas.toml b/data/area/kharidian_desert/kharidian_desert.areas.toml index f7ed071e14..f20794d0a3 100644 --- a/data/area/kharidian_desert/kharidian_desert.areas.toml +++ b/data/area/kharidian_desert/kharidian_desert.areas.toml @@ -5,7 +5,11 @@ tags = ["penguin_area"] hint = "in the north of the Kharidian desert." [south_kharidian_desert] -x = [3200,3200,3136,3136,3519,3519,3455,3455,3328,3328,3263,3263] -y = [2752,2880,2880,3071,3071,3008,3008,2752,2752,2816,2816,2752] +x = [3200, 3200, 3136, 3136, 3519, 3519, 3455, 3455, 3328, 3328, 3263, 3263] +y = [2752, 2880, 2880, 3071, 3071, 3008, 3008, 2752, 2752, 2816, 2816, 2752] tags = ["penguin_area"] hint = "in the south of the Kharidian desert." + +[kharidian_desert] +x = [3200, 3200, 3136, 3136, 3191, 3191, 3136, 3136, 3200, 3200, 3294, 3294, 3314, 3314, 3327, 3331, 3334, 3334, 3352, 3352, 3379, 3395, 3395, 3412, 3415, 3420, 3479, 3519, 3519, 3519, 3455, 3455, 3391, 3391, 3328, 3328, 3391, 3391, 3455, 3455, 3328, 3328, 3263, 3263] +y = [2752, 2880, 2880, 2960, 2960, 2999, 2999, 3071, 3071, 3135, 3135, 3116, 3116, 3135, 3135, 3139, 3148, 3158, 3158, 3148, 3124, 3148, 3163, 3163, 3173, 3173, 3141, 3136, 3071, 3008, 3008, 2944, 2944, 3007, 3007, 2944, 2944, 2879, 2879, 2752, 2752, 2816, 2816, 2752] diff --git a/data/area/kharidian_desert/kharidian_desert.combat.toml b/data/area/kharidian_desert/kharidian_desert.combat.toml index 5376180837..26945b86bf 100644 --- a/data/area/kharidian_desert/kharidian_desert.combat.toml +++ b/data/area/kharidian_desert/kharidian_desert.combat.toml @@ -28,14 +28,3 @@ clone = "goat_kharidian_desert" [goat_kharidian_desert_2.melee] clone = "goat_kharidian_desert.melee" - -[desert_snake] -attack_speed = 4 -retreat_range = 8 -defend_anim = "snake_defend" -death_anim = "snake_death" - -[desert_snake.melee] -range = 1 -anim = "snake_attack" -target_hit = { offense = "stab", max = 10 } \ No newline at end of file diff --git a/data/area/kharidian_desert/kharidian_desert.enums.toml b/data/area/kharidian_desert/kharidian_desert.enums.toml new file mode 100644 index 0000000000..6f2d429889 --- /dev/null +++ b/data/area/kharidian_desert/kharidian_desert.enums.toml @@ -0,0 +1,29 @@ +[desert_heat_delay] +keyType = "item" +valueType = "int" +values = { + desert_disguise = 20, + desert_shirt = 20, + desert_robe = 20, + desert_boots = 20, + black_desert_shirt = 20, + black_desert_robe = 20, + fez = 20, + desert_top = 20, + desert_legs = 20, + desert_top_overcoat = 20, + desert_robes = 20, + slave_shirt = 10, + slave_robe = 10, + slave_boots = 10, + robe_of_elidinis_top = 20, + robe_of_elidinis_bottom = 20, + menaphite_headgear_purple = 20, + menaphite_top_purple = 20, + menaphite_robe_purple = 20, + menaphite_action_kilt_purple = 20, + menaphite_headgear_red = 20, + menaphite_top_red = 20, + menaphite_robe_red = 20, + menaphite_action_kilt_red = 20, +} \ No newline at end of file diff --git a/data/area/kharidian_desert/kharidian_desert.npcs.toml b/data/area/kharidian_desert/kharidian_desert.npcs.toml index 64fd82af3c..2466929654 100644 --- a/data/area/kharidian_desert/kharidian_desert.npcs.toml +++ b/data/area/kharidian_desert/kharidian_desert.npcs.toml @@ -41,7 +41,7 @@ hitpoints = 60 att = 4 str = 5 def = 3 -combat_def = "desert_snake" +combat_def = "snake" examine = "A slithering serpent." [desert_phoenix_kharidian_desert] diff --git a/data/area/kharidian_desert/kharidian_desert.sounds.toml b/data/area/kharidian_desert/kharidian_desert.sounds.toml index 5847e8609f..44c87255ad 100644 --- a/data/area/kharidian_desert/kharidian_desert.sounds.toml +++ b/data/area/kharidian_desert/kharidian_desert.sounds.toml @@ -7,11 +7,5 @@ id = 387 [crocodile_defend] id = 388 -[snake_attack] -id = 797 - -[snake_death] -id = 798 - -[snake_defend] -id = 800 +[sword_slash] +id = 2500 diff --git a/data/entity/npc/monster/reptile/snake/snake.anims.toml b/data/entity/npc/monster/reptile/snake/snake.anims.toml new file mode 100644 index 0000000000..0fed2a6b13 --- /dev/null +++ b/data/entity/npc/monster/reptile/snake/snake.anims.toml @@ -0,0 +1,8 @@ +[snake_attack] +id = 275 + +[snake_defend] +id = 276 + +[snake_death] +id = 278 diff --git a/data/entity/npc/monster/reptile/snake/snake.combat.toml b/data/entity/npc/monster/reptile/snake/snake.combat.toml index ade6886116..385ef0b374 100644 --- a/data/entity/npc/monster/reptile/snake/snake.combat.toml +++ b/data/entity/npc/monster/reptile/snake/snake.combat.toml @@ -1,10 +1,13 @@ [snake] attack_speed = 4 retreat_range = 11 +defend_anim = "snake_defend" defend_sound = "snake_defend" +death_anim = "snake_death" death_sound = "snake_death" [snake.melee] range = 1 +anim = "snake_attack" target_sound = "snake_attack" target_hit = { offense = "stab", max = 10 } diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/AnimationDefinitions.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/AnimationDefinitions.kt index 7f5affbfa9..7b67ad637a 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/AnimationDefinitions.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/AnimationDefinitions.kt @@ -36,6 +36,7 @@ class AnimationDefinitions( } } require(!ids.containsKey(stringId)) { "Duplicate animation id found '$stringId' at $path." } + require(id != -1) { "Missing id for animation '$stringId' at $path." } ids[stringId] = id definitions[id].stringId = stringId if (params.isNotEmpty()) { diff --git a/game/src/main/kotlin/content/area/kharidian_desert/DesertHeat.kt b/game/src/main/kotlin/content/area/kharidian_desert/DesertHeat.kt new file mode 100644 index 0000000000..5599929757 --- /dev/null +++ b/game/src/main/kotlin/content/area/kharidian_desert/DesertHeat.kt @@ -0,0 +1,103 @@ +package content.area.kharidian_desert + +import content.entity.combat.hit.directHit +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.data.definition.EnumDefinitions +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.equip.equipped +import world.gregs.voidps.engine.entity.character.player.skill.Skill +import world.gregs.voidps.engine.entity.character.player.skill.exp.exp +import world.gregs.voidps.engine.entity.character.sound +import world.gregs.voidps.engine.entity.obj.replace +import world.gregs.voidps.engine.inv.inventory +import world.gregs.voidps.engine.inv.remove +import world.gregs.voidps.engine.inv.replace +import world.gregs.voidps.network.login.protocol.visual.update.player.EquipSlot +import world.gregs.voidps.type.random + +class DesertHeat : Script { + init { + entered("kharidian_desert") { + if (!Settings["world.desertHeat", true]) { + return@entered + } + // Doesn't tick when dialogues are open + timers.start("desert_heat") + } + + exited("kharidian_desert") { + timers.stop("desert_heat") + } + + timerStart("desert_heat") { + heatTime(this) + } + + timerTick("desert_heat") { + val index = inventory.items.indexOfFirst { it.id == "waterskin_1" || it.id == "waterskin_2" || it.id == "waterskin_3" || it.id == "waterskin_4" } + if (index != -1) { + val number = inventory[index].id.substringAfterLast("_").toInt() + if (inventory.replace(index, "waterskin_$number", "waterskin_${number - 1}")) { + anim("eat_drink") + sound("drink") + message("You take a drink of water.") + } + } else if (inventory.remove("choc_ice")) { + anim("eat_drink") + message("You eat a choc ice.") + } else if (inventory.contains("waterskin_0")) { + directHit(random.nextInt(1, 11) * 10) + message("Perhaps you should fill up one of your empty waterskins.") + message("You start dying of thirst while you're in the desert.", type = ChatType.Filter) + } else { + directHit(random.nextInt(1, 11) * 10) + message("You should get a waterskin for any travelling in the desert.") + message("You start dying of thirst while you're in the desert.", type = ChatType.Filter) + } + heatTime(this) + } + + objectOperate("Cut", "desert_cactus_full") { (target) -> + if (!inventory.contains("knife")) { + message("You need a knife to cut the cactus...") + return@objectOperate + } + anim("knife_chop") + sound("sword_slash") + target.replace("desert_cactus_empty", ticks = 100) + if (random.nextInt(10) == 0) { + message("You fail to cut the cactus correctly and it gives no water this time.") + return@objectOperate + } + if (!inventory.replace("waterskin_0", "waterkin_1") && !inventory.replace("waterskin_1", "waterkin_2") && !inventory.replace("waterskin_2", "waterkin_3") && !inventory.replace("waterskin_3", "waterkin_4")) { + message("You fail to cut the cactus correctly and it gives no water this time.") + return@objectOperate + } + sound("drink") + exp(Skill.Woodcutting, 10.0) + message("You top up your skin with water from the cactus.") + } + } + + fun heatTime(player: Player): Int { + var time = 150 + time += delay(player, EquipSlot.Hat, -10) + time += delay(player, EquipSlot.Chest, -40) + time += delay(player, EquipSlot.Hands, -10) + time += delay(player, EquipSlot.Legs, -30) + time += delay(player, EquipSlot.Shield, -10) + time += delay(player, EquipSlot.Feet, -10) + return time + } + + private fun delay(player: Player, slot: EquipSlot, default: Int): Int { + val item = player.equipped(slot).id + if (item == "") { + return 0 + } + return EnumDefinitions.intOrNull("desert_heat_delay", item) ?: default + } +} \ No newline at end of file diff --git a/game/src/main/resources/game.properties b/game/src/main/resources/game.properties index 55be019604..a95d37b416 100644 --- a/game/src/main/resources/game.properties +++ b/game/src/main/resources/game.properties @@ -115,6 +115,9 @@ world.objs.redberry.regrowTicks=200 # Number of times a player can close a door before it gets stuck world.objs.door.stuck.count=5 +# Whether you can get dehydrated and take damage when in the desert +world.desertHeat=true + #=================================== # Gameplay Mechanics From 5598e74374d5e966b69b37925a20300682b2f5f8 Mon Sep 17 00:00:00 2001 From: GregHib Date: Thu, 19 Mar 2026 19:16:22 +0000 Subject: [PATCH 13/27] Add dust devils --- .../pollnivneach/pollnivneach.areas.toml | 4 ++ .../chaos_tunnels/chaos_tunnels.combat.toml | 20 ++++++++ .../chaos_tunnels/chaos_tunnels.drops.toml | 46 +++++++++++++++++++ .../chaos_tunnels/chaos_tunnels.gfx.toml | 3 ++ data/skill/slayer/chaeldar.enums.toml | 2 +- data/skill/slayer/duradel.enums.toml | 2 +- data/skill/slayer/kuradal.enums.toml | 2 +- data/skill/slayer/sumona.enums.toml | 2 +- data/skill/slayer/vannaka.enums.toml | 2 +- .../pollnivneach/smoke_dungeon/DustDevil.kt | 14 ++++++ .../smoke_dungeon/SmokeDungeon.kt | 12 +++++ .../content/entity/player/equip/Equipment.kt | 2 + 12 files changed, 106 insertions(+), 5 deletions(-) create mode 100644 data/area/wilderness/chaos_tunnels/chaos_tunnels.gfx.toml create mode 100644 game/src/main/kotlin/content/area/kharidian_desert/pollnivneach/smoke_dungeon/DustDevil.kt create mode 100644 game/src/main/kotlin/content/area/kharidian_desert/pollnivneach/smoke_dungeon/SmokeDungeon.kt diff --git a/data/area/kharidian_desert/pollnivneach/pollnivneach.areas.toml b/data/area/kharidian_desert/pollnivneach/pollnivneach.areas.toml index 82db98e78a..bb0c249f39 100644 --- a/data/area/kharidian_desert/pollnivneach/pollnivneach.areas.toml +++ b/data/area/kharidian_desert/pollnivneach/pollnivneach.areas.toml @@ -7,3 +7,7 @@ tags = ["teleport", "scroll"] x = [3361, 3362] y = [2992, 2994] tags = ["teleport"] + +[smoke_dungeon] +x = [3200, 3327] +y = [9344, 9407] \ No newline at end of file diff --git a/data/area/wilderness/chaos_tunnels/chaos_tunnels.combat.toml b/data/area/wilderness/chaos_tunnels/chaos_tunnels.combat.toml index f70ce5a973..068d9c23bd 100644 --- a/data/area/wilderness/chaos_tunnels/chaos_tunnels.combat.toml +++ b/data/area/wilderness/chaos_tunnels/chaos_tunnels.combat.toml @@ -22,9 +22,29 @@ death_sound = "dust_devil_death" [dust_devil.attack] range = 1 +condition = "face_mask" anim = "dust_devil_attack" +target_sound = "dust_devil_attack" target_hit = { offense = "melee", defence = "range", max = 80 } +[dust_devil.special] +range = 1 +condition = "no_face_mask" +projectile = "slayer_devil_dust" +anim = "dust_devil_attack" +target_sound = "dust_devil_attack" +target_hit = { offense = "melee", defence = "range", max = 160 } +impact_regardless = true +impact_drains = [ + { skill = "attack", multiplier = 1.0 }, + { skill = "strength", multiplier = 1.0 }, + { skill = "defence", multiplier = 0.5 }, + { skill = "ranged", multiplier = 1.0 }, + { skill = "magic", multiplier = 1.0 }, + { skill = "prayer", multiplier = 0.5 }, + { skill = "agility", multiplier = 0.5 }, +] + [giant_crypt_rat] attack_speed = 4 retreat_range = 8 diff --git a/data/area/wilderness/chaos_tunnels/chaos_tunnels.drops.toml b/data/area/wilderness/chaos_tunnels/chaos_tunnels.drops.toml index 547ddd0db5..66a8b8112b 100644 --- a/data/area/wilderness/chaos_tunnels/chaos_tunnels.drops.toml +++ b/data/area/wilderness/chaos_tunnels/chaos_tunnels.drops.toml @@ -84,3 +84,49 @@ drops = [ { id = "adamant_pickaxe", chance = 4 }, { id = "rune_pickaxe" }, ] +[dust_devil_drop_table] +type = "all" +drops = [ + { id = "bones" }, + { table = "dust_devil_secondary" }, + { table = "dust_devil_charms" }, +] + +[dust_devil_secondary] +roll = 32768 +drops = [ + { id = "adamant_hatchet", chance = 768 }, + { id = "rune_dagger", chance = 512 }, + { id = "red_dragonhide_vambraces", chance = 512 }, + { id = "black_dragonhide_vambraces", chance = 256 }, + { id = "air_battlestaff", chance = 512 }, + { id = "earth_battlestaff", chance = 512 }, + { id = "mystic_air_staff", chance = 256 }, + { id = "mystic_earth_staff", chance = 256 }, + { id = "dragon_dagger", chance = 256 }, + { id = "dragon_chainbody" }, + { id = "dust_rune", amount = 200, chance = 2560 }, + { id = "earth_rune", amount = 300, chance = 2560 }, + { id = "fire_rune", amount = 300, chance = 2560 }, + { id = "fire_rune", amount = 50, chance = 256 }, + { id = "chaos_rune", amount = 65, chance = 1792 }, + { id = "rune_arrow", amount = 12, chance = 1280 }, + { id = "soul_rune", amount = 15, chance = 1024 }, + { id = "soul_rune", amount = 50, chance = 256 }, + { table = "herb_drop_table", chance = 4864 }, + { id = "nothing", amount = 0, chance = 255 }, + { id = "coins", min = 2000, max = 4000, chance = 7168 }, + { id = "ugthanki_kebab", amount = 4, chance = 512 }, + { id = "mithril_bar_noted", amount = 10, chance = 768 }, + { id = "adamant_bar_noted", amount = 4, chance = 256 }, + { table = "gem_drop_table", chance = 2816 }, +] + +[dust_devil_charms] +roll = 1000 +drops = [ + { id = "gold_charm", chance = 121 }, + { id = "green_charm", chance = 60 }, + { id = "crimson_charm", chance = 243 }, + { id = "blue_charm", chance = 8 }, +] \ No newline at end of file diff --git a/data/area/wilderness/chaos_tunnels/chaos_tunnels.gfx.toml b/data/area/wilderness/chaos_tunnels/chaos_tunnels.gfx.toml new file mode 100644 index 0000000000..6a254ba5ba --- /dev/null +++ b/data/area/wilderness/chaos_tunnels/chaos_tunnels.gfx.toml @@ -0,0 +1,3 @@ +[slayer_devil_dust] +id = 73 +height = 50 \ No newline at end of file diff --git a/data/skill/slayer/chaeldar.enums.toml b/data/skill/slayer/chaeldar.enums.toml index 4047526781..d1448893b7 100644 --- a/data/skill/slayer/chaeldar.enums.toml +++ b/data/skill/slayer/chaeldar.enums.toml @@ -53,7 +53,7 @@ values = { cave_horror = 10, rock_crab = 8, dagannoth_lighthouse_range_74 = 11, -# dust_devil = 9, + dust_devil = 9, # elf_warrior = 8, # fever_spider = 7, fire_giant = 12, diff --git a/data/skill/slayer/duradel.enums.toml b/data/skill/slayer/duradel.enums.toml index 4dcb1e6b5f..61a3f96c3d 100644 --- a/data/skill/slayer/duradel.enums.toml +++ b/data/skill/slayer/duradel.enums.toml @@ -47,7 +47,7 @@ values = { dagannoth_lighthouse_range_74 = 10, # dark_beast = 15, # desert_strykewyrm = 11, -# dust_devil = 10, + dust_devil = 10, fire_giant = 10, gargoyle = 10, gorak = 5, diff --git a/data/skill/slayer/kuradal.enums.toml b/data/skill/slayer/kuradal.enums.toml index 0e1eaef0f0..80a914589b 100644 --- a/data/skill/slayer/kuradal.enums.toml +++ b/data/skill/slayer/kuradal.enums.toml @@ -53,7 +53,7 @@ values = { dagannoth_lighthouse_range_74 = 10, # dark_beast = 12, # desert_strykewyrm = 7, -# dust_devil = 10, + dust_devil = 10, # elf_warrior = 12, fire_giant = 10, gargoyle = 12, diff --git a/data/skill/slayer/sumona.enums.toml b/data/skill/slayer/sumona.enums.toml index 2ed9d71389..fbcb04078e 100644 --- a/data/skill/slayer/sumona.enums.toml +++ b/data/skill/slayer/sumona.enums.toml @@ -57,7 +57,7 @@ values = { dagannoth_lighthouse_range_74 = 10, lizard = 4, # desert_strykewyrm = 14, -# dust_devil = 15, + dust_devil = 15, # elf_warrior = 10, fire_giant = 10, # gargoyle = 10, diff --git a/data/skill/slayer/vannaka.enums.toml b/data/skill/slayer/vannaka.enums.toml index c1a2fde7cd..e112535e95 100644 --- a/data/skill/slayer/vannaka.enums.toml +++ b/data/skill/slayer/vannaka.enums.toml @@ -62,7 +62,7 @@ values = { rock_crab = 7, crocodile_kharidian_desert = 6, dagannoth_lighthouse_range_74 = 7, -# dust_devil = 8, + dust_devil = 8, # elf_warrior = 7, # fever_spider = 7, fire_giant = 7, diff --git a/game/src/main/kotlin/content/area/kharidian_desert/pollnivneach/smoke_dungeon/DustDevil.kt b/game/src/main/kotlin/content/area/kharidian_desert/pollnivneach/smoke_dungeon/DustDevil.kt new file mode 100644 index 0000000000..3322570721 --- /dev/null +++ b/game/src/main/kotlin/content/area/kharidian_desert/pollnivneach/smoke_dungeon/DustDevil.kt @@ -0,0 +1,14 @@ +package content.area.kharidian_desert.pollnivneach.smoke_dungeon + +import content.entity.player.equip.Equipment +import world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.entity.character.player.Player +import world.gregs.voidps.engine.entity.character.player.equip.equipped +import world.gregs.voidps.network.login.protocol.visual.update.player.EquipSlot + +class DustDevil : Script { + init { + npcCondition("face_mask") { it is Player && Equipment.isFaceMask(it.equipped(EquipSlot.Hat).id) } + npcCondition("no_face_mask") { it is Player && !Equipment.isFaceMask(it.equipped(EquipSlot.Hat).id) } + } +} \ No newline at end of file diff --git a/game/src/main/kotlin/content/area/kharidian_desert/pollnivneach/smoke_dungeon/SmokeDungeon.kt b/game/src/main/kotlin/content/area/kharidian_desert/pollnivneach/smoke_dungeon/SmokeDungeon.kt new file mode 100644 index 0000000000..95d68f07dd --- /dev/null +++ b/game/src/main/kotlin/content/area/kharidian_desert/pollnivneach/smoke_dungeon/SmokeDungeon.kt @@ -0,0 +1,12 @@ +package content.area.kharidian_desert.pollnivneach.smoke_dungeon + +import world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.client.ui.close + +class SmokeDungeon : Script { + init { + // TODO smoke interface - https://youtu.be/2ARmHyBkO8w?t=108 +// entered("smoke_dungeon") {} +// exited("smoke_dungeon") {} + } +} \ No newline at end of file diff --git a/game/src/main/kotlin/content/entity/player/equip/Equipment.kt b/game/src/main/kotlin/content/entity/player/equip/Equipment.kt index 9ce124be04..dc166623cb 100644 --- a/game/src/main/kotlin/content/entity/player/equip/Equipment.kt +++ b/game/src/main/kotlin/content/entity/player/equip/Equipment.kt @@ -110,6 +110,8 @@ object Equipment { fun isNosePeg(hat: String) = hat == "nose_peg" || hat.startsWith("slayer_helmet") || hat.startsWith("full_slayer_helmet") + fun isFaceMask(hat: String) = hat == "face_mask" || hat.startsWith("slayer_helmet") || hat.startsWith("full_slayer_helmet") + fun dragonFireImmune(target: Character) = target.protectMagic() || antiDragonShield(target) || target.antifire || target.superAntifire fun antiDragonShield(target: Character): Boolean { From ed423dc1517f0a57d65b4c80269f4c5a73caabb4 Mon Sep 17 00:00:00 2001 From: GregHib Date: Thu, 19 Mar 2026 19:56:07 +0000 Subject: [PATCH 14/27] Add jungle horror --- .../caves/mos_le_harmless_caves.anims.toml | 9 +++++++++ .../caves/mos_le_harmless_caves.sounds.toml | 2 +- .../mos_le_harmless/mos_le_harmless.combat.toml | 13 +++++++++++++ .../mos_le_harmless/mos_le_harmless.npcs.toml | 3 +-- data/entity/obj/boat/charter_ship.areas.toml | 2 +- data/skill/slayer/chaeldar.enums.toml | 2 +- data/skill/slayer/vannaka.enums.toml | 2 +- 7 files changed, 27 insertions(+), 6 deletions(-) create mode 100644 data/area/morytania/mos_le_harmless/mos_le_harmless.combat.toml diff --git a/data/area/morytania/mos_le_harmless/caves/mos_le_harmless_caves.anims.toml b/data/area/morytania/mos_le_harmless/caves/mos_le_harmless_caves.anims.toml index db22db6dcd..34948c04fe 100644 --- a/data/area/morytania/mos_le_harmless/caves/mos_le_harmless_caves.anims.toml +++ b/data/area/morytania/mos_le_harmless/caves/mos_le_harmless_caves.anims.toml @@ -19,3 +19,12 @@ id = 2398 [mosquito_attack] id = 2397 +[jungle_horror_death] +id = 4233 + +[jungle_horror_defend] +id = 4232 + +[jungle_horror_attack] +id = 4234 + diff --git a/data/area/morytania/mos_le_harmless/caves/mos_le_harmless_caves.sounds.toml b/data/area/morytania/mos_le_harmless/caves/mos_le_harmless_caves.sounds.toml index 4cf0023f2e..99b22d8251 100644 --- a/data/area/morytania/mos_le_harmless/caves/mos_le_harmless_caves.sounds.toml +++ b/data/area/morytania/mos_le_harmless/caves/mos_le_harmless_caves.sounds.toml @@ -8,7 +8,7 @@ id = 497 id = 498 [jungle_horror_death] -id = 499 +id = 480 [jungle_horror_defend] id = 500 diff --git a/data/area/morytania/mos_le_harmless/mos_le_harmless.combat.toml b/data/area/morytania/mos_le_harmless/mos_le_harmless.combat.toml new file mode 100644 index 0000000000..a1745bbcd1 --- /dev/null +++ b/data/area/morytania/mos_le_harmless/mos_le_harmless.combat.toml @@ -0,0 +1,13 @@ +[jungle_horror] +retreat_range = 8 +attack_speed = 4 +defend_anim = "jungle_horror_defend" +defend_sound = "jungle_horror_defend" +death_anim = "jungle_horror_death" +death_sound = "jungle_horror_death" + +[jungle_horror.attack] +range = 1 +anim = "jungle_horror_attack" +target_sound = "jungle_horror_attack" +target_hit = { offense = "stab", defence = "magic", max = 80 } diff --git a/data/area/morytania/mos_le_harmless/mos_le_harmless.npcs.toml b/data/area/morytania/mos_le_harmless/mos_le_harmless.npcs.toml index fff72db9a5..a872149b96 100644 --- a/data/area/morytania/mos_le_harmless/mos_le_harmless.npcs.toml +++ b/data/area/morytania/mos_le_harmless/mos_le_harmless.npcs.toml @@ -124,8 +124,7 @@ hitpoints = 450 att = 70 str = 70 def = 55 -style = "stab" -max_hit_melee = 80 +combat_def = "jungle_horror" hunt_mode = "cowardly" slayer_xp = 45.0 categories = ["jungle_horrors"] diff --git a/data/entity/obj/boat/charter_ship.areas.toml b/data/entity/obj/boat/charter_ship.areas.toml index a70b99a3ee..179ed60bd8 100644 --- a/data/entity/obj/boat/charter_ship.areas.toml +++ b/data/entity/obj/boat/charter_ship.areas.toml @@ -22,7 +22,7 @@ y = [3409, 3475, 3475, 3464, 3460, 3455, 3450, 3436, 3432, 3409] x = [2515, 2515, 2523, 2537, 2543, 2556, 2629, 2629, 2566, 2558, 2518] y = [2832, 2845, 2856, 2864, 2864, 2870, 2870, 2834, 2835, 2829, 2829] -[mos_le_harmless] +[mos_le_harmless_town] x = [3646, 3646, 3710, 3710, 3711, 3711] y = [2925, 3006, 3006, 2980, 2976, 2925] diff --git a/data/skill/slayer/chaeldar.enums.toml b/data/skill/slayer/chaeldar.enums.toml index d1448893b7..b4198dfd02 100644 --- a/data/skill/slayer/chaeldar.enums.toml +++ b/data/skill/slayer/chaeldar.enums.toml @@ -61,7 +61,7 @@ values = { greater_demon = 9, hellhound = 9, jelly = 10, -# jungle_horror = 10, + jungle_horror = 10, # jungle_strykewyrm = 12, # kalphite_worker = 11, kurask = 12, diff --git a/data/skill/slayer/vannaka.enums.toml b/data/skill/slayer/vannaka.enums.toml index e112535e95..1e140ca795 100644 --- a/data/skill/slayer/vannaka.enums.toml +++ b/data/skill/slayer/vannaka.enums.toml @@ -76,7 +76,7 @@ values = { ice_warrior = 7, infernal_mage = 8, jelly = 8, -# jungle_horror = 8, + jungle_horror = 8, # kalphite_worker = 7, kurask = 7, lesser_demon = 7, From b99d25f21394f3c034c4d8e35c2eeaae25749995 Mon Sep 17 00:00:00 2001 From: GregHib Date: Thu, 19 Mar 2026 19:56:45 +0000 Subject: [PATCH 15/27] Allow kalphite tasks --- data/skill/slayer/chaeldar.enums.toml | 2 +- data/skill/slayer/duradel.enums.toml | 2 +- data/skill/slayer/kuradal.enums.toml | 2 +- data/skill/slayer/sumona.enums.toml | 2 +- data/skill/slayer/turael.enums.toml | 2 +- data/skill/slayer/vannaka.enums.toml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/data/skill/slayer/chaeldar.enums.toml b/data/skill/slayer/chaeldar.enums.toml index b4198dfd02..e90aeec180 100644 --- a/data/skill/slayer/chaeldar.enums.toml +++ b/data/skill/slayer/chaeldar.enums.toml @@ -63,7 +63,7 @@ values = { jelly = 10, jungle_horror = 10, # jungle_strykewyrm = 12, -# kalphite_worker = 11, + kalphite_worker = 11, kurask = 12, lesser_demon = 9, # zygomite = 7, diff --git a/data/skill/slayer/duradel.enums.toml b/data/skill/slayer/duradel.enums.toml index 61a3f96c3d..169a4a24b4 100644 --- a/data/skill/slayer/duradel.enums.toml +++ b/data/skill/slayer/duradel.enums.toml @@ -56,7 +56,7 @@ values = { # ice_strykewyrm = 8, iron_dragon = 9, # jungle_strykewyrm = 10, -# kalphite_worker = 10, + kalphite_worker = 10, mithril_dragon = 7, nechryael = 10, # giant_scarab_normal = 10, diff --git a/data/skill/slayer/kuradal.enums.toml b/data/skill/slayer/kuradal.enums.toml index 80a914589b..974704fa0c 100644 --- a/data/skill/slayer/kuradal.enums.toml +++ b/data/skill/slayer/kuradal.enums.toml @@ -62,7 +62,7 @@ values = { # ice_strykewyrm = 9, iron_dragon = 9, # jungle_strykewyrm = 8, -# kalphite_worker = 5, + kalphite_worker = 5, # living_rock_protector = 10, mithril_dragon = 8, nechryael = 10, diff --git a/data/skill/slayer/sumona.enums.toml b/data/skill/slayer/sumona.enums.toml index fbcb04078e..1f0458934f 100644 --- a/data/skill/slayer/sumona.enums.toml +++ b/data/skill/slayer/sumona.enums.toml @@ -65,7 +65,7 @@ values = { hellhound = 10, iron_dragon = 7, # jungle_strykewyrm = 12, -# kalphite_worker = 10, + kalphite_worker = 10, kurask = 15, nechryael = 10, red_dragon = 5, diff --git a/data/skill/slayer/turael.enums.toml b/data/skill/slayer/turael.enums.toml index ec0a3a2459..c00be636dd 100644 --- a/data/skill/slayer/turael.enums.toml +++ b/data/skill/slayer/turael.enums.toml @@ -37,7 +37,7 @@ values = { ghost = 7, spider = 6, skeleton_heavy = 7, -# kalphite_worker = 6, + kalphite_worker = 6, lizard = 8, guard_dog = 7, icefiend = 8, diff --git a/data/skill/slayer/vannaka.enums.toml b/data/skill/slayer/vannaka.enums.toml index 1e140ca795..d800b5e4a2 100644 --- a/data/skill/slayer/vannaka.enums.toml +++ b/data/skill/slayer/vannaka.enums.toml @@ -77,7 +77,7 @@ values = { infernal_mage = 8, jelly = 8, jungle_horror = 8, -# kalphite_worker = 7, + kalphite_worker = 7, kurask = 7, lesser_demon = 7, # mogre = 7, From bd07b33841aaf2f5b61200000fe5afbeb59ea2e6 Mon Sep 17 00:00:00 2001 From: GregHib Date: Thu, 19 Mar 2026 20:43:36 +0000 Subject: [PATCH 16/27] Start jungle styrkewyrms --- .../feldip_hills/feldip_hills.anims.toml | 17 ++++++ .../feldip_hills/feldip_hills.combat.toml | 30 +++++++++ .../feldip_hills/feldip_hills.gfx.toml | 10 +++ .../feldip_hills/feldip_hills.npcs.toml | 9 +++ .../desert_strykewyrm.npcs.toml | 2 - .../engine/entity/character/mode/Wander.kt | 5 +- .../kandarin/feldip_hills/JungleStrykewyrm.kt | 61 +++++++++++++++++++ 7 files changed, 131 insertions(+), 3 deletions(-) create mode 100644 data/area/kandarin/feldip_hills/feldip_hills.anims.toml create mode 100644 data/area/kandarin/feldip_hills/feldip_hills.combat.toml create mode 100644 data/area/kandarin/feldip_hills/feldip_hills.gfx.toml create mode 100644 game/src/main/kotlin/content/area/kandarin/feldip_hills/JungleStrykewyrm.kt diff --git a/data/area/kandarin/feldip_hills/feldip_hills.anims.toml b/data/area/kandarin/feldip_hills/feldip_hills.anims.toml new file mode 100644 index 0000000000..fa29b3fd3f --- /dev/null +++ b/data/area/kandarin/feldip_hills/feldip_hills.anims.toml @@ -0,0 +1,17 @@ +[styrkewyrm_attack] +id = 12791 + +[styrkewyrm_defend] +id = 12792 + +[styrkewyrm_death] +id = 12792 + +[strykewyrm_shoot] +id = 12793 + +[strykewyrm_surface] +id = 12795 + +[strykewyrm_bury] +id = 12796 diff --git a/data/area/kandarin/feldip_hills/feldip_hills.combat.toml b/data/area/kandarin/feldip_hills/feldip_hills.combat.toml new file mode 100644 index 0000000000..892371cdcc --- /dev/null +++ b/data/area/kandarin/feldip_hills/feldip_hills.combat.toml @@ -0,0 +1,30 @@ +[jungle_strykewyrm] +attack_speed = 4 +retreat_range = 12 +defend_anim = "strykewyrm_defend" +defend_sound = "strykewyrm_defend" +death_anim = "strykewyrm_death" +death_sound = "strykewyrm_death" + +[jungle_strykewyrm.melee] +range = 1 +anim = "strykewyrm_attack" +target_hit = { offense = "stab", max = 90 } + +[jungle_strykewyrm.poison] +clone = "jungle_strykewyrm.melee" +impact_poison = 44 +impact_gfx = "jungle_strykewyrm_poison" + +[jungle_strykewyrm.magic] +range = 8 +anim = "strykewyrm_shoot" +projectile = "jungle_strykewyrm_travel" +impact_gfx = "jungle_strykewyrm_impact" + +[jungle_strykewyrm.cloud] +clone = "jungle_strykewyrm.magic" +impact_poison = 88 + +[jungle_strykewyrm.dig] +range = 8 \ No newline at end of file diff --git a/data/area/kandarin/feldip_hills/feldip_hills.gfx.toml b/data/area/kandarin/feldip_hills/feldip_hills.gfx.toml new file mode 100644 index 0000000000..2dee2f3cf4 --- /dev/null +++ b/data/area/kandarin/feldip_hills/feldip_hills.gfx.toml @@ -0,0 +1,10 @@ +[jungle_strykewyrm_travel] +id = 2313 +curve = 16 +# startHeight = 41, endHeight = 16, delay = 30, speed = 2 + +[jungle_strykewyrm_impact] +id = 2314 + +[jungle_strykewyrm_poison] +id = 2309 diff --git a/data/area/kandarin/feldip_hills/feldip_hills.npcs.toml b/data/area/kandarin/feldip_hills/feldip_hills.npcs.toml index 77b59bcd8a..826caf1aba 100644 --- a/data/area/kandarin/feldip_hills/feldip_hills.npcs.toml +++ b/data/area/kandarin/feldip_hills/feldip_hills.npcs.toml @@ -84,6 +84,15 @@ id = 8659 [mound_feldip_hills] id = 9466 +[jungle_strykewyrm] +id = 9467 +# Unknown stats +hitpoints = 1100 +slayer_level = 73 +slayer_xp = 110.0 +immune_poison = true +examine = "Who said worms were small and harmless?" + [swamp_toad_feldip_hills] id = 1013 examine = "A green skinned croaker, loves the swamp." diff --git a/data/area/kharidian_desert/al_kharid/desert_strykewyrm/desert_strykewyrm.npcs.toml b/data/area/kharidian_desert/al_kharid/desert_strykewyrm/desert_strykewyrm.npcs.toml index f3f2746f29..3ea79d0006 100644 --- a/data/area/kharidian_desert/al_kharid/desert_strykewyrm/desert_strykewyrm.npcs.toml +++ b/data/area/kharidian_desert/al_kharid/desert_strykewyrm/desert_strykewyrm.npcs.toml @@ -7,5 +7,3 @@ id = 9463 [desert_strykewyrm] id = 9465 -[jungle_strykewyrm] -id = 9467 diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/Wander.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/Wander.kt index d26a2c8e01..8b3b38570b 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/Wander.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/Wander.kt @@ -1,6 +1,7 @@ package world.gregs.voidps.engine.entity.character.mode import world.gregs.voidps.engine.data.Settings +import world.gregs.voidps.engine.data.definition.NPCDefinitions import world.gregs.voidps.engine.entity.character.mode.move.Movement import world.gregs.voidps.engine.entity.character.move.tele import world.gregs.voidps.engine.entity.character.npc.NPC @@ -50,7 +51,9 @@ class Wander( if (!Settings["world.npcs.randomWalk", false]) { return false } - return when (npc.def.walkMode.toInt()) { + val id = npc["transform_id", npc.id] + val def = NPCDefinitions.get(id) + return when (def.walkMode.toInt()) { ModeType.WANDER_THROUGH, ModeType.WANDER_SPECIAL, ModeType.WANDER_WATER -> true else -> false } diff --git a/game/src/main/kotlin/content/area/kandarin/feldip_hills/JungleStrykewyrm.kt b/game/src/main/kotlin/content/area/kandarin/feldip_hills/JungleStrykewyrm.kt new file mode 100644 index 0000000000..6b6524ae18 --- /dev/null +++ b/game/src/main/kotlin/content/area/kandarin/feldip_hills/JungleStrykewyrm.kt @@ -0,0 +1,61 @@ +package content.area.kandarin.feldip_hills + +import content.entity.combat.hit.directHit +import content.entity.combat.inCombat +import content.entity.combat.target +import content.entity.effect.clearTransform +import content.entity.effect.toxin.poison +import content.entity.effect.transform +import content.skill.slayer.slayerTask +import world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.client.message +import world.gregs.voidps.engine.entity.character.mode.EmptyMode +import world.gregs.voidps.engine.map.Overlap +import world.gregs.voidps.engine.queue.queue +import world.gregs.voidps.engine.timer.Timer +import world.gregs.voidps.type.random + +class JungleStrykewyrm : Script { + init { + npcOperate("Investigate", "mound_feldip_hills") { (target) -> + if (slayerTask != "jungle_strykewyrm") { + message("You need to have strykewyrm assigned as a task in order to fight them.") + return@npcOperate + } + + anim("emote_stomp") + target.transform("jungle_strykewyrm") + target.anim("strykewyrm_surface") + softTimers.start("strykewyrm_revert") + } + + npcTimerStart("strykewyrm_revert") { + 20 + } + + npcTimerTick("strykewyrm_revert") { + if (inCombat) { + return@npcTimerTick Timer.CONTINUE + } + anim("strykewyrm_bury") + // TODO what if in combat? + clearTransform() + Timer.CANCEL + } + + npcAttack("jungle_strykewyrm", "dig") { target -> + anim("jungle_strykewyrm_bury") + clearTransform() + target.target?.mode = EmptyMode + val distance = tile.distanceTo(target.tile) + walkTo(target.tile) + queue("resurface", distance) { + anim("jungle_strykewyrm_surface") + if (Overlap.isUnder(target.tile, 1, tile, size)) { + directHit(random.nextInt(50, 201)) + poison(target, 88) + } + } + } + } +} \ No newline at end of file From 1771504aaa152aefe1fe6e8c6ddf80f347756d8f Mon Sep 17 00:00:00 2001 From: GregHib Date: Fri, 20 Mar 2026 13:29:50 +0000 Subject: [PATCH 17/27] Fix elite clue drop tables --- data/entity/npc/boss/giant_mole/giant_mole.drops.toml | 2 +- .../npc/boss/king_black_dragon/king_black_dragon.drops.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data/entity/npc/boss/giant_mole/giant_mole.drops.toml b/data/entity/npc/boss/giant_mole/giant_mole.drops.toml index afa9995f2e..c3c330a973 100644 --- a/data/entity/npc/boss/giant_mole/giant_mole.drops.toml +++ b/data/entity/npc/boss/giant_mole/giant_mole.drops.toml @@ -42,6 +42,6 @@ drops = [ roll = 10000 drops = [ { id = "long_bone", chance = 25 }, - { id = "clue_scroll_elite", lacks = "clue_scroll_elite*", chance = 20 }, + { table = "elite_clue_scroll", chance = 20 }, { id = "curved_bone", chance = 2}, ] diff --git a/data/entity/npc/boss/king_black_dragon/king_black_dragon.drops.toml b/data/entity/npc/boss/king_black_dragon/king_black_dragon.drops.toml index dc165b5370..e7bafa82ec 100644 --- a/data/entity/npc/boss/king_black_dragon/king_black_dragon.drops.toml +++ b/data/entity/npc/boss/king_black_dragon/king_black_dragon.drops.toml @@ -52,6 +52,6 @@ drops = [ [king_black_dragon_clues] roll = 10 drops = [ - { id = "clue_scroll_elite", chance = 4, lacks = "clue_scroll_elite*" }, + { table = "elite_clue_scroll", chance = 4 }, { table = "hard_clue_scroll" } ] From 631e119f68b358babca2eaea55ea78ba06309542 Mon Sep 17 00:00:00 2001 From: GregHib Date: Fri, 20 Mar 2026 13:30:51 +0000 Subject: [PATCH 18/27] Take transform into account with attack range and combat def --- .../character/mode/combat/CombatMovement.kt | 11 +++++++++-- .../move/target/NPCCharacterTargetStrategy.kt | 14 ++++++++++++-- .../voidps/engine/entity/character/npc/NPC.kt | 17 +++++++++++++++-- .../main/kotlin/content/entity/combat/Combat.kt | 4 ++-- .../kotlin/content/entity/death/NPCDeath.kt | 7 ++----- .../kotlin/content/skill/melee/weapon/Weapon.kt | 8 +++++++- 6 files changed, 47 insertions(+), 14 deletions(-) diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/combat/CombatMovement.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/combat/CombatMovement.kt index f4d5fc5b54..78fbd6be65 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/combat/CombatMovement.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/combat/CombatMovement.kt @@ -49,7 +49,7 @@ class CombatMovement( } if (character is NPC) { val spawn: Tile = character["spawn_tile"] ?: return - val definition = get().get(character.def["combat_def", character.id]) + val definition = get().get(character.transformDef["combat_def", character.id]) if (!withinAggro(this.target, spawn, definition)) { character.mode = EmptyMode return @@ -101,7 +101,14 @@ class CombatMovement( return false } - private fun attackRange(): Int = character["attack_range", if (character is NPC) character.def["attack_range", get().get(character.def["combat_def", character.id]).attackRange] else 1] + private fun attackRange(): Int { + val default = if (character is NPC) { + val def = character.transformDef + val combatDefinition = get().get(def["combat_def", character.id]) + def["attack_range", combatDefinition.attackRange] + } else 1 + return character["attack_range", default] + } override fun onCompletion() { } diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/move/target/NPCCharacterTargetStrategy.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/move/target/NPCCharacterTargetStrategy.kt index 8949d8f40a..375c165376 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/move/target/NPCCharacterTargetStrategy.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/move/target/NPCCharacterTargetStrategy.kt @@ -1,7 +1,10 @@ package world.gregs.voidps.engine.entity.character.mode.move.target import org.rsmod.game.pathfinder.PathFinder +import world.gregs.voidps.engine.data.definition.NPCDefinitions import world.gregs.voidps.engine.entity.character.Character +import world.gregs.voidps.engine.entity.character.mode.ModeType +import world.gregs.voidps.engine.entity.character.mode.Wander import world.gregs.voidps.engine.entity.character.npc.NPC import world.gregs.voidps.type.Tile @@ -23,8 +26,15 @@ data class NPCCharacterTargetStrategy( get() = character.size override fun destination(source: Character): Tile { - if (source is NPC && source.id == "bed_draynor") { - return Tile.EMPTY + if (source is NPC) { + val def = if (source.contains("transform_id")) { + NPCDefinitions.get(source["transform_id", source.id]) + } else { + source.def + } + if (def.walkMode.toInt() == ModeType.EMPTY) { + return Tile.EMPTY + } } return Tile( PathFinder.naiveDestination( diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/npc/NPC.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/npc/NPC.kt index 72f4e7dcb0..93323731fc 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/npc/NPC.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/npc/NPC.kt @@ -32,8 +32,21 @@ data class NPC( override val visuals: NPCVisuals = NPCVisuals() var hide = false - override var blockMove: Int = if (def["solid", true]) CollisionFlag.BLOCK_PLAYERS or CollisionFlag.BLOCK_NPCS else 0 - override var collisionFlag: Int = CollisionFlag.BLOCK_NPCS or if (def["solid", false]) CollisionFlag.FLOOR else 0 + override val blockMove: Int + get() = if (transformDef["solid", true]) CollisionFlag.BLOCK_PLAYERS or CollisionFlag.BLOCK_NPCS else 0 + override val collisionFlag: Int + get() = CollisionFlag.BLOCK_NPCS or if (transformDef["solid", false]) CollisionFlag.FLOOR else 0 + + val transformId: String + get() = this["transform_id", id] + + val transformDef: NPCDefinition + get() { + if (contains("transform_id")) { + return NPCDefinitions.get(get("transform_id", id)) + } + return def + } init { if (index != -1) { diff --git a/game/src/main/kotlin/content/entity/combat/Combat.kt b/game/src/main/kotlin/content/entity/combat/Combat.kt index bd61085857..382bef5889 100644 --- a/game/src/main/kotlin/content/entity/combat/Combat.kt +++ b/game/src/main/kotlin/content/entity/combat/Combat.kt @@ -115,7 +115,7 @@ class Combat(val combatDefinitions: CombatDefinitions) : * [CombatMovement.combatReached] is emitted by [CombatMovement] every tick the [Character] is within range of the target */ fun retaliates(character: Character) = if (character is NPC) { - character.def["retaliates", true] + character.transformDef["retaliates", true] } else { character["auto_retaliate", false] } @@ -132,7 +132,7 @@ class Combat(val combatDefinitions: CombatDefinitions) : } if (character is NPC) { // Retreat - val definition = combatDefinitions.getOrNull(character.def["combat_def", character.id]) ?: return + val definition = combatDefinitions.getOrNull(character.transformDef["combat_def", character.id]) ?: return val spawn: Tile = character["spawn_tile"]!! if (!CombatMovement.withinAggro(source, spawn, definition)) { if (character.mode !is Retreat || (character.mode as Retreat).target != source) { diff --git a/game/src/main/kotlin/content/entity/death/NPCDeath.kt b/game/src/main/kotlin/content/entity/death/NPCDeath.kt index 408b87cb17..d7c23e0348 100644 --- a/game/src/main/kotlin/content/entity/death/NPCDeath.kt +++ b/game/src/main/kotlin/content/entity/death/NPCDeath.kt @@ -14,7 +14,6 @@ import world.gregs.voidps.engine.Script import world.gregs.voidps.engine.client.message import world.gregs.voidps.engine.client.ui.chat.plural import world.gregs.voidps.engine.data.definition.CombatDefinitions -import world.gregs.voidps.engine.data.definition.NPCDefinitions import world.gregs.voidps.engine.entity.Spawn import world.gregs.voidps.engine.entity.World import world.gregs.voidps.engine.entity.character.Character @@ -58,11 +57,9 @@ class NPCDeath( val npc = this strongQueue(name = "death", 1) { val killer = killer - val id = get("transform_id", npc.id) - val tile = if (id == "wall_beast") tile.addY(-1) else tile + val tile = if (transformId == "wall_beast") tile.addY(-1) else tile npc["death_tile"] = tile - val def = NPCDefinitions.get(id) - val combat = combatDefinitions.get(def["combat_def", get("transform_id", npc.id)]) + val combat = combatDefinitions.get(transformDef["combat_def", transformId]) val ticks = anim(combat.deathAnim) if (combat.deathSound != null) { (killer as? Player)?.sound(combat.deathSound!!.id) diff --git a/game/src/main/kotlin/content/skill/melee/weapon/Weapon.kt b/game/src/main/kotlin/content/skill/melee/weapon/Weapon.kt index 23363d5f56..2f558b7efe 100644 --- a/game/src/main/kotlin/content/skill/melee/weapon/Weapon.kt +++ b/game/src/main/kotlin/content/skill/melee/weapon/Weapon.kt @@ -249,7 +249,13 @@ val Character.attackSpeed: Int } var Character.attackRange: Int - get() = get("attack_range", if (this is NPC) def["attack_range", get().get(def["combat_def", id]).attackRange] else 1) + get() { + val default = if (this is NPC) { + val combatDefinition = get().get(transformDef["combat_def", id]) + transformDef["attack_range", combatDefinition.attackRange] + } else 1 + return get("attack_range", default) + } set(value) = set("attack_range", value) // E.g "accurate" From d81518856c6fccd4e7b131326e4ef2ad0078d038 Mon Sep 17 00:00:00 2001 From: GregHib Date: Fri, 20 Mar 2026 13:40:11 +0000 Subject: [PATCH 19/27] Add jungle strykewyrm --- .../feldip_hills/feldip_hills.anims.toml | 10 +-- .../feldip_hills/feldip_hills.combat.toml | 6 ++ .../feldip_hills/feldip_hills.drops.toml | 86 +++++++++++++++++++ .../feldip_hills/feldip_hills.gfx.toml | 5 +- .../feldip_hills/feldip_hills.npcs.toml | 16 +++- data/skill/slayer/chaeldar.enums.toml | 2 +- data/skill/slayer/duradel.enums.toml | 2 +- data/skill/slayer/kuradal.enums.toml | 2 +- data/skill/slayer/sumona.enums.toml | 2 +- .../kandarin/feldip_hills/JungleStrykewyrm.kt | 46 ++++++---- .../kotlin/content/entity/combat/Target.kt | 5 ++ 11 files changed, 150 insertions(+), 32 deletions(-) create mode 100644 data/area/kandarin/feldip_hills/feldip_hills.drops.toml diff --git a/data/area/kandarin/feldip_hills/feldip_hills.anims.toml b/data/area/kandarin/feldip_hills/feldip_hills.anims.toml index fa29b3fd3f..b1d4006184 100644 --- a/data/area/kandarin/feldip_hills/feldip_hills.anims.toml +++ b/data/area/kandarin/feldip_hills/feldip_hills.anims.toml @@ -1,14 +1,14 @@ -[styrkewyrm_attack] +[strykewyrm_attack] id = 12791 -[styrkewyrm_defend] +[strykewyrm_defend] id = 12792 -[styrkewyrm_death] -id = 12792 +[strykewyrm_death] +id = 12793 [strykewyrm_shoot] -id = 12793 +id = 12794 [strykewyrm_surface] id = 12795 diff --git a/data/area/kandarin/feldip_hills/feldip_hills.combat.toml b/data/area/kandarin/feldip_hills/feldip_hills.combat.toml index 892371cdcc..91f5bf7a92 100644 --- a/data/area/kandarin/feldip_hills/feldip_hills.combat.toml +++ b/data/area/kandarin/feldip_hills/feldip_hills.combat.toml @@ -7,6 +7,7 @@ death_anim = "strykewyrm_death" death_sound = "strykewyrm_death" [jungle_strykewyrm.melee] +chance = 5 range = 1 anim = "strykewyrm_attack" target_hit = { offense = "stab", max = 90 } @@ -17,14 +18,19 @@ impact_poison = 44 impact_gfx = "jungle_strykewyrm_poison" [jungle_strykewyrm.magic] +chance = 5 range = 8 anim = "strykewyrm_shoot" projectile = "jungle_strykewyrm_travel" +projectile_origin_y = 1 +projectile_origin_x = 1 impact_gfx = "jungle_strykewyrm_impact" +target_hit = { offense = "magic", max = 88 } [jungle_strykewyrm.cloud] clone = "jungle_strykewyrm.magic" impact_poison = 88 +target_hit = { offense = "magic", max = 0 } [jungle_strykewyrm.dig] range = 8 \ No newline at end of file diff --git a/data/area/kandarin/feldip_hills/feldip_hills.drops.toml b/data/area/kandarin/feldip_hills/feldip_hills.drops.toml new file mode 100644 index 0000000000..62beb377d7 --- /dev/null +++ b/data/area/kandarin/feldip_hills/feldip_hills.drops.toml @@ -0,0 +1,86 @@ +[jungle_strykewyrm_drop_table] +type = "all" +drops = [ + { table = "jungle_strykewyrm_secondary" }, + { table = "jungle_strykewyrm_tertiary" }, + { table = "jungle_strykewyrm_charms" }, +] + +[jungle_strykewyrm_secondary] +roll = 512 +drops = [ + { id = "nature_rune", amount = 15, chance = 32 }, + { id = "death_rune", min = 5, max = 10, chance = 32 }, + { id = "law_rune", amount = 10, chance = 32 }, + { id = "soul_rune", amount = 20, chance = 8 }, + { id = "water_talisman", chance = 2 }, + { id = "air_talisman", chance = 2 }, + { id = "body_talisman", chance = 2 }, + { id = "fire_talisman", chance = 2 }, + { id = "mind_talisman", chance = 2 }, + { id = "cosmic_talisman", chance = 2 }, + { id = "earth_talisman", chance = 2 }, + { id = "chaos_talisman", chance = 2 }, + { id = "grimy_cadantine", min = 1, max = 4, chance = 32 }, + { id = "grimy_guam_noted", min = 1, max = 4, chance = 8 }, + { id = "grimy_marrentill_noted", min = 1, max = 4, chance = 8 }, + { id = "grimy_tarromin_noted", min = 1, max = 4, chance = 8 }, + { id = "grimy_irit_noted", min = 1, max = 4, chance = 8 }, + { id = "grimy_harralander_noted", min = 1, max = 4, chance = 8 }, + { id = "grimy_dwarf_weed_noted", min = 1, max = 4, chance = 8 }, + { id = "grimy_avantoe_noted", min = 1, max = 4, chance = 2 }, + { id = "grimy_lantadyme_noted", min = 1, max = 4, chance = 2 }, + { id = "grimy_ranarr_noted", min = 1, max = 4, chance = 2 }, + { id = "grimy_kwuarm_noted", min = 1, max = 4, chance = 2 }, + { id = "toadflax_seed", chance = 32 }, + { id = "limpwurt_seed", chance = 32 }, + { id = "marrentill_seed", chance = 8 }, + { id = "tarromin_seed", chance = 8 }, + { id = "whiteberry_seed", chance = 8 }, + { id = "watermelon_seed", chance = 8 }, + { id = "harralander_seed", chance = 8 }, + { id = "wildblood_seed", chance = 8 }, + { id = "bittercap_mushroom_spore", chance = 8 }, + { id = "cactus_seed", chance = 8 }, + { id = "poison_ivy_seed", chance = 8 }, + { id = "yew_seed", chance = 2 }, + { id = "snapdragon_seed", chance = 2 }, + { id = "jangerberry_seed", chance = 2 }, + { id = "ranarr_seed", chance = 2 }, + { id = "spirit_weed_seed", chance = 2 }, + { id = "strawberry_seed", chance = 2 }, + { id = "kwuarm_seed", chance = 2 }, + { id = "dwarf_weed_seed", chance = 2 }, + { id = "avantoe_seed", chance = 2 }, + { id = "maple_seed", chance = 2 }, + { id = "magic_seed" }, + { id = "torstol_seed" }, + { id = "lantadyme_seed" }, + { id = "adamant_hatchet", chance = 8 }, + { id = "mithril_battleaxe", chance = 8 }, + { id = "hexcrest", chance = 2 }, + { id = "coins", min = 200, max = 2465, chance = 32 }, + { id = "papaya_fruit_noted", min = 3, max = 10, chance = 32 }, + { id = "pure_essence_noted", amount = 40, chance = 32 }, + { id = "teak_logs_noted", amount = 20, chance = 32 }, + { id = "lobster", min = 1, max = 2, chance = 32 }, + { id = "super_defence_1", chance = 32 }, + { id = "mithril_bar_noted", amount = 5, chance = 8 }, +] + +[jungle_strykewyrm_tertiary] +roll = 512 +drops = [ + { table = "hard_clue_scroll", chance = 2 }, + { id = "starved_ancient_effigy", chance = 2 }, + { table = "elite_clue_scroll" }, +] + +[jungle_strykewyrm_charms] +roll = 1000 +drops = [ + { id = "gold_charm", chance = 56 }, + { id = "green_charm", chance = 140 }, + { id = "crimson_charm", chance = 22 }, + { id = "blue_charm", chance = 11 }, +] \ No newline at end of file diff --git a/data/area/kandarin/feldip_hills/feldip_hills.gfx.toml b/data/area/kandarin/feldip_hills/feldip_hills.gfx.toml index 2dee2f3cf4..5b15d85d21 100644 --- a/data/area/kandarin/feldip_hills/feldip_hills.gfx.toml +++ b/data/area/kandarin/feldip_hills/feldip_hills.gfx.toml @@ -1,10 +1,11 @@ [jungle_strykewyrm_travel] -id = 2313 +id = 2312 curve = 16 +delay = 30 # startHeight = 41, endHeight = 16, delay = 30, speed = 2 [jungle_strykewyrm_impact] -id = 2314 +id = 2313 [jungle_strykewyrm_poison] id = 2309 diff --git a/data/area/kandarin/feldip_hills/feldip_hills.npcs.toml b/data/area/kandarin/feldip_hills/feldip_hills.npcs.toml index 826caf1aba..091f09cd01 100644 --- a/data/area/kandarin/feldip_hills/feldip_hills.npcs.toml +++ b/data/area/kandarin/feldip_hills/feldip_hills.npcs.toml @@ -83,14 +83,22 @@ id = 8659 [mound_feldip_hills] id = 9466 - -[jungle_strykewyrm] -id = 9467 -# Unknown stats hitpoints = 1100 +solid = false +allowed_under = true +# Made up stats +att = 150 +str = 150 +def = 150 +magic = 250 slayer_level = 73 slayer_xp = 110.0 +drop_table = "jungle_strykewyrm" immune_poison = true + +[jungle_strykewyrm] +id = 9467 +combat_def = "jungle_strykewyrm" examine = "Who said worms were small and harmless?" [swamp_toad_feldip_hills] diff --git a/data/skill/slayer/chaeldar.enums.toml b/data/skill/slayer/chaeldar.enums.toml index e90aeec180..543524b248 100644 --- a/data/skill/slayer/chaeldar.enums.toml +++ b/data/skill/slayer/chaeldar.enums.toml @@ -62,7 +62,7 @@ values = { hellhound = 9, jelly = 10, jungle_horror = 10, -# jungle_strykewyrm = 12, + jungle_strykewyrm = 12, kalphite_worker = 11, kurask = 12, lesser_demon = 9, diff --git a/data/skill/slayer/duradel.enums.toml b/data/skill/slayer/duradel.enums.toml index 169a4a24b4..072cc06195 100644 --- a/data/skill/slayer/duradel.enums.toml +++ b/data/skill/slayer/duradel.enums.toml @@ -55,7 +55,7 @@ values = { hellhound = 9, # ice_strykewyrm = 8, iron_dragon = 9, -# jungle_strykewyrm = 10, + jungle_strykewyrm = 10, kalphite_worker = 10, mithril_dragon = 7, nechryael = 10, diff --git a/data/skill/slayer/kuradal.enums.toml b/data/skill/slayer/kuradal.enums.toml index 974704fa0c..2bb6af8a52 100644 --- a/data/skill/slayer/kuradal.enums.toml +++ b/data/skill/slayer/kuradal.enums.toml @@ -61,7 +61,7 @@ values = { hellhound = 10, # ice_strykewyrm = 9, iron_dragon = 9, -# jungle_strykewyrm = 8, + jungle_strykewyrm = 8, kalphite_worker = 5, # living_rock_protector = 10, mithril_dragon = 8, diff --git a/data/skill/slayer/sumona.enums.toml b/data/skill/slayer/sumona.enums.toml index 1f0458934f..c6c2a605ae 100644 --- a/data/skill/slayer/sumona.enums.toml +++ b/data/skill/slayer/sumona.enums.toml @@ -64,7 +64,7 @@ values = { greater_demon = 10, hellhound = 10, iron_dragon = 7, -# jungle_strykewyrm = 12, + jungle_strykewyrm = 12, kalphite_worker = 10, kurask = 15, nechryael = 10, diff --git a/game/src/main/kotlin/content/area/kandarin/feldip_hills/JungleStrykewyrm.kt b/game/src/main/kotlin/content/area/kandarin/feldip_hills/JungleStrykewyrm.kt index 6b6524ae18..4a7ac774dd 100644 --- a/game/src/main/kotlin/content/area/kandarin/feldip_hills/JungleStrykewyrm.kt +++ b/game/src/main/kotlin/content/area/kandarin/feldip_hills/JungleStrykewyrm.kt @@ -9,9 +9,13 @@ import content.entity.effect.transform import content.skill.slayer.slayerTask import world.gregs.voidps.engine.Script import world.gregs.voidps.engine.client.message +import world.gregs.voidps.engine.client.variable.start import world.gregs.voidps.engine.entity.character.mode.EmptyMode +import world.gregs.voidps.engine.entity.character.mode.PauseMode import world.gregs.voidps.engine.map.Overlap import world.gregs.voidps.engine.queue.queue +import world.gregs.voidps.engine.queue.softQueue +import world.gregs.voidps.engine.queue.strongQueue import world.gregs.voidps.engine.timer.Timer import world.gregs.voidps.type.random @@ -24,35 +28,43 @@ class JungleStrykewyrm : Script { } anim("emote_stomp") - target.transform("jungle_strykewyrm") - target.anim("strykewyrm_surface") - softTimers.start("strykewyrm_revert") + target.start("movement_delay", Int.MAX_VALUE) + target.mode = EmptyMode + target.steps.clear() + target.softTimers.start("strykewyrm_revert") + target.softQueue("styrkyewyrm_transform", 3) { + target.mode = EmptyMode + target.transform("jungle_strykewyrm") + target.anim("strykewyrm_surface") + target.face(this@npcOperate) + } } - npcTimerStart("strykewyrm_revert") { - 20 - } + npcTimerStart("strykewyrm_revert") { 20 } npcTimerTick("strykewyrm_revert") { if (inCombat) { return@npcTimerTick Timer.CONTINUE } anim("strykewyrm_bury") - // TODO what if in combat? - clearTransform() + softQueue("bury", 3) { + clearTransform() + } Timer.CANCEL } npcAttack("jungle_strykewyrm", "dig") { target -> - anim("jungle_strykewyrm_bury") - clearTransform() - target.target?.mode = EmptyMode - val distance = tile.distanceTo(target.tile) - walkTo(target.tile) - queue("resurface", distance) { - anim("jungle_strykewyrm_surface") - if (Overlap.isUnder(target.tile, 1, tile, size)) { - directHit(random.nextInt(50, 201)) + anim("strykewyrm_bury") + val temp = mode + softQueue("resurface", 3) { + clearTransform() + mode = PauseMode + walkToDelay(target.tile) + mode = temp + transform("jungle_strykewyrm") + anim("strykewyrm_surface") + if (tile.toCuboid(size, size).contains(target.tile)) { + target.directHit(random.nextInt(50, 201)) poison(target, 88) } } diff --git a/game/src/main/kotlin/content/entity/combat/Target.kt b/game/src/main/kotlin/content/entity/combat/Target.kt index 84b63bd60d..c19306c8fb 100644 --- a/game/src/main/kotlin/content/entity/combat/Target.kt +++ b/game/src/main/kotlin/content/entity/combat/Target.kt @@ -10,6 +10,7 @@ import content.entity.player.equip.Equipment import content.skill.melee.weapon.fightStyle import content.skill.ranged.ammo import content.skill.slayer.categories +import content.skill.slayer.slayerTask import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap import it.unimi.dsi.fastutil.objects.ObjectArrayList import world.gregs.voidps.engine.client.message @@ -34,6 +35,10 @@ object Target { if (target.id.startsWith("door_support") && NPCDefinitions.get(target.id).options[1] == "Destroy") { return true } + if (target.id == "mound_feldip_hills" && source is Player && source.slayerTask != "jungle_strykewyrm") { + source.message("You need to have strykewyrm assigned as a task in order to fight them.") + return false + } if (target.transform != "") { if (NPCDefinitions.get(target.transform).options[1] != "Attack") { return false From fd09d2a228a98cda06a3ac793f4a309f4d4828ab Mon Sep 17 00:00:00 2001 From: GregHib Date: Fri, 20 Mar 2026 14:19:23 +0000 Subject: [PATCH 20/27] Add desert strykewyrm --- .../feldip_hills/feldip_hills.drops.toml | 1 + .../feldip_hills/feldip_hills.npcs.toml | 1 + .../desert_strykewyrm.combat.toml | 26 ++++++ .../desert_strykewyrm.drops.toml | 83 +++++++++++++++++++ .../desert_strykewyrm.gfx.toml | 8 ++ .../desert_strykewyrm.npcs.toml | 16 ++++ .../skill/agility/shortcut/shortcut.objs.toml | 4 + data/skill/slayer/duradel.enums.toml | 2 +- data/skill/slayer/kuradal.enums.toml | 2 +- data/skill/slayer/sumona.enums.toml | 2 +- .../kandarin/feldip_hills/JungleStrykewyrm.kt | 80 +++++++++++------- .../al_kharid/DesertStrykewyrm.kt | 16 ++++ .../kotlin/content/entity/combat/Target.kt | 4 + .../content/skill/agility/shortcut/Stiles.kt | 4 + 14 files changed, 214 insertions(+), 35 deletions(-) create mode 100644 data/area/kharidian_desert/al_kharid/desert_strykewyrm/desert_strykewyrm.combat.toml create mode 100644 data/area/kharidian_desert/al_kharid/desert_strykewyrm/desert_strykewyrm.drops.toml create mode 100644 data/area/kharidian_desert/al_kharid/desert_strykewyrm/desert_strykewyrm.gfx.toml create mode 100644 game/src/main/kotlin/content/area/kharidian_desert/al_kharid/DesertStrykewyrm.kt diff --git a/data/area/kandarin/feldip_hills/feldip_hills.drops.toml b/data/area/kandarin/feldip_hills/feldip_hills.drops.toml index 62beb377d7..729aa573f1 100644 --- a/data/area/kandarin/feldip_hills/feldip_hills.drops.toml +++ b/data/area/kandarin/feldip_hills/feldip_hills.drops.toml @@ -66,6 +66,7 @@ drops = [ { id = "lobster", min = 1, max = 2, chance = 32 }, { id = "super_defence_1", chance = 32 }, { id = "mithril_bar_noted", amount = 5, chance = 8 }, + { table = "rare_drop_table", chance = 10 }, ] [jungle_strykewyrm_tertiary] diff --git a/data/area/kandarin/feldip_hills/feldip_hills.npcs.toml b/data/area/kandarin/feldip_hills/feldip_hills.npcs.toml index 091f09cd01..407c52ed69 100644 --- a/data/area/kandarin/feldip_hills/feldip_hills.npcs.toml +++ b/data/area/kandarin/feldip_hills/feldip_hills.npcs.toml @@ -94,6 +94,7 @@ magic = 250 slayer_level = 73 slayer_xp = 110.0 drop_table = "jungle_strykewyrm" +categories = ["jungle_strykewyrm", "strykewyrm"] immune_poison = true [jungle_strykewyrm] diff --git a/data/area/kharidian_desert/al_kharid/desert_strykewyrm/desert_strykewyrm.combat.toml b/data/area/kharidian_desert/al_kharid/desert_strykewyrm/desert_strykewyrm.combat.toml new file mode 100644 index 0000000000..c678e63ba5 --- /dev/null +++ b/data/area/kharidian_desert/al_kharid/desert_strykewyrm/desert_strykewyrm.combat.toml @@ -0,0 +1,26 @@ +[desert_strykewyrm] +attack_speed = 4 +retreat_range = 12 +defend_anim = "strykewyrm_defend" +defend_sound = "strykewyrm_defend" +death_anim = "strykewyrm_death" +death_sound = "strykewyrm_death" + +[desert_strykewyrm.melee] +chance = 8 +range = 1 +anim = "strykewyrm_attack" +target_hit = { offense = "stab", max = 120 } + +[desert_strykewyrm.range] +chance = 8 +range = 8 +anim = "strykewyrm_shoot" +projectile = "desert_strykewyrm_travel" +projectile_origin_y = 1 +projectile_origin_x = 1 +impact_gfx = "desert_strykewyrm_impact" +target_hit = { offense = "range", defence = "magic", max = 125 } + +[desert_strykewyrm.dig] +range = 8 \ No newline at end of file diff --git a/data/area/kharidian_desert/al_kharid/desert_strykewyrm/desert_strykewyrm.drops.toml b/data/area/kharidian_desert/al_kharid/desert_strykewyrm/desert_strykewyrm.drops.toml new file mode 100644 index 0000000000..bd524c4ef9 --- /dev/null +++ b/data/area/kharidian_desert/al_kharid/desert_strykewyrm/desert_strykewyrm.drops.toml @@ -0,0 +1,83 @@ +[desert_strykewyrm_drop_table] +type = "all" +drops = [ + { table = "desert_strykewyrm_secondary" }, + { table = "desert_strykewyrm_tertiary" }, + { table = "desert_strykewyrm_charms" }, +] + +[desert_strykewyrm_secondary] +roll = 512 +drops = [ + { id = "death_rune", amount = 15, chance = 8 }, + { id = "water_rune", min = 2, max = 100, chance = 8 }, + { id = "law_rune", amount = 15, chance = 8 }, + { id = "air_talisman", chance = 8 }, + { id = "earth_talisman", chance = 8 }, + { id = "body_talisman", chance = 2 }, + { id = "mind_talisman", chance = 2 }, + { id = "cosmic_talisman", chance = 2 }, + { id = "fire_talisman", chance = 2 }, + { id = "mithril_battleaxe", chance = 32 }, + { id = "rune_hatchet", chance = 8 }, + { id = "focus_sight", chance = 2 }, + { id = "shield_left_half" }, + { id = "cadantine_seed", chance = 32 }, + { id = "avantoe_seed", chance = 8 }, + { id = "kwuarm_seed", chance = 8 }, + { id = "toadflax_seed", chance = 8 }, + { id = "irit_seed", chance = 8 }, + { id = "watermelon_seed", chance = 8 }, + { id = "poison_ivy_seed", chance = 8 }, + { id = "belladonna_seed", chance = 8 }, + { id = "cactus_seed", chance = 8 }, + { id = "lantadyme_seed", chance = 8 }, + { id = "snapdragon_seed", chance = 2 }, + { id = "dwarf_weed_seed", chance = 2 }, + { id = "yew_seed", chance = 2 }, + { id = "magic_seed", chance = 2 }, + { id = "torstol_seed", chance = 2 }, + { id = "maple_seed" }, + { id = "grimy_guam_noted", min = 1, max = 4, chance = 8 }, + { id = "grimy_marrentill_noted", min = 1, max = 4, chance = 8 }, + { id = "grimy_lantadyme_noted", min = 1, max = 4, chance = 8 }, + { id = "grimy_avantoe_noted", min = 1, max = 4, chance = 8 }, + { id = "grimy_cadantine_noted", min = 1, max = 4, chance = 8 }, + { id = "grimy_dwarf_weed_noted", min = 1, max = 4, chance = 8 }, + { id = "grimy_harralander_noted", min = 1, max = 4, chance = 8 }, + { id = "grimy_toadflax_noted", min = 1, max = 4, chance = 8 }, + { id = "grimy_irit_noted", min = 1, max = 4, chance = 8 }, + { id = "grimy_kwuarm_noted", min = 1, max = 4, chance = 8 }, + { id = "grimy_ranarr_noted", min = 1, max = 4, chance = 8 }, + { id = "grimy_tarromin_noted", min = 1, max = 4, chance = 8 }, + { id = "coins", amount = 200, chance = 32 }, + { id = "coins", amount = 240, chance = 32 }, + { id = "coins", amount = 400, chance = 32 }, + { id = "coins", min = 1200, max = 4500, chance = 32 }, + { id = "waterskin_4", amount = 2, chance = 32 }, + { id = "super_defence_2", chance = 32 }, + { id = "potato_cactus_noted", min = 5, max = 10, chance = 32 }, + { id = "yew_logs_noted", amount = 10, chance = 32 }, + { id = "pure_essence_noted", amount = 120, chance = 32 }, + { id = "swordfish", amount = 2, chance = 8 }, + { id = "adamant_bar_noted", amount = 3, chance = 8 }, + { table = "rare_drop_table", chance = 22 }, +] + +[desert_strykewyrm_tertiary] +roll = 512 +drops = [ + { table = "hard_clue_scroll", chance = 2 }, + { id = "starved_ancient_effigy", chance = 2 }, + { id = "court_summons", chance = 2 }, + { table = "elite_clue_scroll" }, +] + +[desert_strykewyrm_charms] +roll = 1000 +drops = [ + { id = "gold_charm", chance = 37 }, + { id = "green_charm", chance = 19 }, + { id = "crimson_charm", chance = 119 }, + { id = "blue_charm", chance = 7 }, +] \ No newline at end of file diff --git a/data/area/kharidian_desert/al_kharid/desert_strykewyrm/desert_strykewyrm.gfx.toml b/data/area/kharidian_desert/al_kharid/desert_strykewyrm/desert_strykewyrm.gfx.toml new file mode 100644 index 0000000000..b0af736c33 --- /dev/null +++ b/data/area/kharidian_desert/al_kharid/desert_strykewyrm/desert_strykewyrm.gfx.toml @@ -0,0 +1,8 @@ +[desert_strykewyrm_travel] +id = 2310 +curve = 16 +delay = 30 +# startHeight = 41, endHeight = 16, delay = 30, speed = 2 + +[desert_strykewyrm_impact] +id = 2311 \ No newline at end of file diff --git a/data/area/kharidian_desert/al_kharid/desert_strykewyrm/desert_strykewyrm.npcs.toml b/data/area/kharidian_desert/al_kharid/desert_strykewyrm/desert_strykewyrm.npcs.toml index 3ea79d0006..ea57642d69 100644 --- a/data/area/kharidian_desert/al_kharid/desert_strykewyrm/desert_strykewyrm.npcs.toml +++ b/data/area/kharidian_desert/al_kharid/desert_strykewyrm/desert_strykewyrm.npcs.toml @@ -1,9 +1,25 @@ [mound_desert_strykewyrm] id = 9464 +hitpoints = 1200 +solid = false +allowed_under = true +# Made up stats +att = 175 +str = 175 +def = 175 +range = 300 +magic = 300 +slayer_level = 77 +slayer_xp = 120.0 +drop_table = "desert_strykewyrm" +categories = ["desert_strykewyrm", "strykewyrm"] +examine = "I'm pretty sure there's something under there." [ice_strykewyrm] id = 9463 [desert_strykewyrm] id = 9465 +combat_def = "desert_strykewyrm" +examine = "I dread to think of how many of these are in the sand." diff --git a/data/skill/agility/shortcut/shortcut.objs.toml b/data/skill/agility/shortcut/shortcut.objs.toml index 8d733a3943..51c0975eef 100644 --- a/data/skill/agility/shortcut/shortcut.objs.toml +++ b/data/skill/agility/shortcut/shortcut.objs.toml @@ -6,6 +6,10 @@ examine = "I can climb over the fence with this." id = 34776 examine = "I can climb over the fence with this." +[al_kharid_stile] +id = 48208 +examine = "I can climb over the fence with this." + [catherby_stile] id = 993 examine = "I can climb over the fence with this." diff --git a/data/skill/slayer/duradel.enums.toml b/data/skill/slayer/duradel.enums.toml index 072cc06195..0108f9782a 100644 --- a/data/skill/slayer/duradel.enums.toml +++ b/data/skill/slayer/duradel.enums.toml @@ -46,7 +46,7 @@ values = { bloodveld = 10, dagannoth_lighthouse_range_74 = 10, # dark_beast = 15, -# desert_strykewyrm = 11, + desert_strykewyrm = 11, dust_devil = 10, fire_giant = 10, gargoyle = 10, diff --git a/data/skill/slayer/kuradal.enums.toml b/data/skill/slayer/kuradal.enums.toml index 2bb6af8a52..b0eaf9659c 100644 --- a/data/skill/slayer/kuradal.enums.toml +++ b/data/skill/slayer/kuradal.enums.toml @@ -52,7 +52,7 @@ values = { blue_dragon = 7, dagannoth_lighthouse_range_74 = 10, # dark_beast = 12, -# desert_strykewyrm = 7, + desert_strykewyrm = 7, dust_devil = 10, # elf_warrior = 12, fire_giant = 10, diff --git a/data/skill/slayer/sumona.enums.toml b/data/skill/slayer/sumona.enums.toml index c6c2a605ae..d03e7bd9ea 100644 --- a/data/skill/slayer/sumona.enums.toml +++ b/data/skill/slayer/sumona.enums.toml @@ -56,7 +56,7 @@ values = { crocodile_kharidian_desert = 4, dagannoth_lighthouse_range_74 = 10, lizard = 4, -# desert_strykewyrm = 14, + desert_strykewyrm = 14, dust_devil = 15, # elf_warrior = 10, fire_giant = 10, diff --git a/game/src/main/kotlin/content/area/kandarin/feldip_hills/JungleStrykewyrm.kt b/game/src/main/kotlin/content/area/kandarin/feldip_hills/JungleStrykewyrm.kt index 4a7ac774dd..b8dac26bc7 100644 --- a/game/src/main/kotlin/content/area/kandarin/feldip_hills/JungleStrykewyrm.kt +++ b/game/src/main/kotlin/content/area/kandarin/feldip_hills/JungleStrykewyrm.kt @@ -2,7 +2,6 @@ package content.area.kandarin.feldip_hills import content.entity.combat.hit.directHit import content.entity.combat.inCombat -import content.entity.combat.target import content.entity.effect.clearTransform import content.entity.effect.toxin.poison import content.entity.effect.transform @@ -10,34 +9,20 @@ import content.skill.slayer.slayerTask import world.gregs.voidps.engine.Script import world.gregs.voidps.engine.client.message import world.gregs.voidps.engine.client.variable.start +import world.gregs.voidps.engine.entity.character.Character import world.gregs.voidps.engine.entity.character.mode.EmptyMode import world.gregs.voidps.engine.entity.character.mode.PauseMode -import world.gregs.voidps.engine.map.Overlap -import world.gregs.voidps.engine.queue.queue +import world.gregs.voidps.engine.entity.character.npc.NPC +import world.gregs.voidps.engine.entity.character.player.Player import world.gregs.voidps.engine.queue.softQueue -import world.gregs.voidps.engine.queue.strongQueue import world.gregs.voidps.engine.timer.Timer import world.gregs.voidps.type.random +import kotlin.random.nextInt class JungleStrykewyrm : Script { init { npcOperate("Investigate", "mound_feldip_hills") { (target) -> - if (slayerTask != "jungle_strykewyrm") { - message("You need to have strykewyrm assigned as a task in order to fight them.") - return@npcOperate - } - - anim("emote_stomp") - target.start("movement_delay", Int.MAX_VALUE) - target.mode = EmptyMode - target.steps.clear() - target.softTimers.start("strykewyrm_revert") - target.softQueue("styrkyewyrm_transform", 3) { - target.mode = EmptyMode - target.transform("jungle_strykewyrm") - target.anim("strykewyrm_surface") - target.face(this@npcOperate) - } + investigate(this, target, "jungle_strykewyrm") } npcTimerStart("strykewyrm_revert") { 20 } @@ -54,20 +39,51 @@ class JungleStrykewyrm : Script { } npcAttack("jungle_strykewyrm", "dig") { target -> - anim("strykewyrm_bury") - val temp = mode - softQueue("resurface", 3) { - clearTransform() - mode = PauseMode - walkToDelay(target.tile) - mode = temp - transform("jungle_strykewyrm") - anim("strykewyrm_surface") - if (tile.toCuboid(size, size).contains(target.tile)) { - target.directHit(random.nextInt(50, 201)) - poison(target, 88) + burrow(this, target) { + poison(target, 88) + } + } + } + companion object { + + fun burrow(source: NPC, target: Character, block: () -> Unit = {}) { + source.anim("strykewyrm_bury") + val temp = source.mode + val type = source.transform + source.softQueue("resurface", 3) { + source.clearTransform() + source.mode = PauseMode + source.walkToDelay(target.tile) + source.mode = temp + source.transform(type) + source.anim("strykewyrm_surface") + if (source.tile.toCuboid(source.size, source.size).contains(target.tile)) { + target.directHit(random.nextInt(50..300)) + block.invoke() } } } + + fun investigate(source: Player, target: NPC, to: String) { + if (source.slayerTask != to) { + source.anim("emote_stomp") + source.softQueue("stomp_mound", 3) { + source.anim("emote_think") + } + source.message("You need to have strykewyrm assigned as a task in order to fight them.") + return + } + source.anim("emote_stomp") + target.start("movement_delay", Int.MAX_VALUE) + target.mode = EmptyMode + target.steps.clear() + target.softTimers.start("strykewyrm_revert") + target.softQueue("styrkyewyrm_transform", 3) { + target.mode = EmptyMode + target.transform(to) + target.anim("strykewyrm_surface") + target.face(source) + } + } } } \ No newline at end of file diff --git a/game/src/main/kotlin/content/area/kharidian_desert/al_kharid/DesertStrykewyrm.kt b/game/src/main/kotlin/content/area/kharidian_desert/al_kharid/DesertStrykewyrm.kt new file mode 100644 index 0000000000..1c4a66de67 --- /dev/null +++ b/game/src/main/kotlin/content/area/kharidian_desert/al_kharid/DesertStrykewyrm.kt @@ -0,0 +1,16 @@ +package content.area.kharidian_desert.al_kharid + +import content.area.kandarin.feldip_hills.JungleStrykewyrm +import world.gregs.voidps.engine.Script + +class DesertStrykewyrm : Script { + init { + npcOperate("Investigate", "mound_desert_strykewyrm") { (target) -> + JungleStrykewyrm.investigate(this, target, "desert_strykewyrm") + } + + npcAttack("desert_strykewyrm", "dig") { target -> + JungleStrykewyrm.burrow(this, target) + } + } +} \ No newline at end of file diff --git a/game/src/main/kotlin/content/entity/combat/Target.kt b/game/src/main/kotlin/content/entity/combat/Target.kt index c19306c8fb..e604945797 100644 --- a/game/src/main/kotlin/content/entity/combat/Target.kt +++ b/game/src/main/kotlin/content/entity/combat/Target.kt @@ -39,6 +39,10 @@ object Target { source.message("You need to have strykewyrm assigned as a task in order to fight them.") return false } + if (target.id == "mound_desert_strykewyrm" && source is Player && source.slayerTask != "desert_strykewyrm") { + source.message("You need to have strykewyrm assigned as a task in order to fight them.") + return false + } if (target.transform != "") { if (NPCDefinitions.get(target.transform).options[1] != "Attack") { return false diff --git a/game/src/main/kotlin/content/skill/agility/shortcut/Stiles.kt b/game/src/main/kotlin/content/skill/agility/shortcut/Stiles.kt index c9cb3c65f4..055fdccf98 100644 --- a/game/src/main/kotlin/content/skill/agility/shortcut/Stiles.kt +++ b/game/src/main/kotlin/content/skill/agility/shortcut/Stiles.kt @@ -33,6 +33,10 @@ class Stiles : Script { climbStile(target, Direction.EAST) } + objectOperate("Climb-over", "al_kharid_stile") { (target) -> + climbStile(target, Direction.EAST) + } + objectOperate("Climb-over", "falador_farm_stile") { (target) -> val rotation = when (target.rotation) { 2 -> Direction.NORTH From 6898c562c8dacc4b4324ce8e946c741e3f49fcc2 Mon Sep 17 00:00:00 2001 From: GregHib Date: Fri, 20 Mar 2026 14:46:50 +0000 Subject: [PATCH 21/27] Add slayer unlock requirements for aquanites --- data/skill/slayer/slayer.enums.toml | 7 +++++++ game/src/main/kotlin/content/skill/slayer/Slayer.kt | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/data/skill/slayer/slayer.enums.toml b/data/skill/slayer/slayer.enums.toml index c1cc4f12e2..9866a8308b 100644 --- a/data/skill/slayer/slayer.enums.toml +++ b/data/skill/slayer/slayer.enums.toml @@ -295,4 +295,11 @@ values = { terror_dog = "haunted_mine", skeletal_wyvern = "elemental_workshop_i", ice_strykewyrm = "the_tale_of_the_muspah", +} + +[slayer_task_variable] +keyType = "npc" +valueType = "string" +values = { + aquanite = "aquanites", } \ No newline at end of file diff --git a/game/src/main/kotlin/content/skill/slayer/Slayer.kt b/game/src/main/kotlin/content/skill/slayer/Slayer.kt index f37dbf62c5..99e0532a84 100644 --- a/game/src/main/kotlin/content/skill/slayer/Slayer.kt +++ b/game/src/main/kotlin/content/skill/slayer/Slayer.kt @@ -105,6 +105,10 @@ private fun hasRequirements(player: Player, index: Int): Boolean { if (player.combatLevel < combatLevel) { return false } + val variable = EnumDefinitions.stringOrNull("slayer_task_variable", index) + if (variable != null && !player.contains(variable)) { + return false + } val quest = EnumDefinitions.stringOrNull("slayer_task_quest", index) ?: return true return player.questCompleted(quest) } From 93ef740f2894cf1089faeb51a3c4bec62f202f87 Mon Sep 17 00:00:00 2001 From: GregHib Date: Fri, 20 Mar 2026 15:58:57 +0000 Subject: [PATCH 22/27] Add ice strykewyrms --- .../rellekka_hunter_area.npc-spawns.toml | 2 +- .../rellekka_hunter_area.npcs.toml | 2 +- .../ice_strykewyrm.combat.toml | 37 ++++++++ .../ice_strykewyrm.drops.toml | 87 +++++++++++++++++++ .../ice_strykewyrm.gfx.toml | 8 ++ .../ice_strykewyrm_cave.npcs.toml | 21 +++++ .../ice_styrkewyrm_cave.npc-spawns.toml | 24 ++--- .../rellekka/rellekka.npcs.toml | 3 +- .../rellekka/rellekka.objs.toml | 17 ++++ .../rellekka/rellekka.teles.toml | 32 ++++++- .../feldip_hills/feldip_hills.npcs.toml | 5 +- .../desert_strykewyrm.npcs.toml | 10 +-- data/area/wilderness/wilderness.npcs.toml | 3 - data/entity/player/modal/icon.items.toml | 2 +- data/skill/slayer/duradel.enums.toml | 2 +- data/skill/slayer/kuradal.enums.toml | 2 +- .../fremennik_province/rellekka/Erjolf.kt | 24 +++++ .../rellekka/IceStrykewyrm.kt | 29 +++++++ .../kandarin/feldip_hills/JungleStrykewyrm.kt | 16 +++- .../content/entity/player/equip/Equipment.kt | 3 +- game/src/main/resources/game.properties | 9 +- 21 files changed, 303 insertions(+), 35 deletions(-) create mode 100644 data/area/fremennik_province/rellekka/ice_strykewyrm_cave/ice_strykewyrm.combat.toml create mode 100644 data/area/fremennik_province/rellekka/ice_strykewyrm_cave/ice_strykewyrm.drops.toml create mode 100644 data/area/fremennik_province/rellekka/ice_strykewyrm_cave/ice_strykewyrm.gfx.toml create mode 100644 game/src/main/kotlin/content/area/fremennik_province/rellekka/Erjolf.kt create mode 100644 game/src/main/kotlin/content/area/fremennik_province/rellekka/IceStrykewyrm.kt diff --git a/data/area/fremennik_province/rellekka/hunter_area/rellekka_hunter_area.npc-spawns.toml b/data/area/fremennik_province/rellekka/hunter_area/rellekka_hunter_area.npc-spawns.toml index 2e632e14ae..87c2b05196 100644 --- a/data/area/fremennik_province/rellekka/hunter_area/rellekka_hunter_area.npc-spawns.toml +++ b/data/area/fremennik_province/rellekka/hunter_area/rellekka_hunter_area.npc-spawns.toml @@ -63,6 +63,6 @@ spawns = [ { id = "gnoeals_2", x = 3423, y = 4377, members = true }, { id = "gnoeals_2", x = 3432, y = 4378, members = true }, { id = "gnoeals_2", x = 3432, y = 4395, members = true }, - { id = "erjolf", x = 2695, y = 3794, members = true }, + { id = "erjolf_base", x = 2695, y = 3794, members = true }, { id = "natural_historian_rellekka", x = 2735, y = 3824, level = 1, members = true }, ] diff --git a/data/area/fremennik_province/rellekka/hunter_area/rellekka_hunter_area.npcs.toml b/data/area/fremennik_province/rellekka/hunter_area/rellekka_hunter_area.npcs.toml index 6f1b396f90..b76e45089a 100644 --- a/data/area/fremennik_province/rellekka/hunter_area/rellekka_hunter_area.npcs.toml +++ b/data/area/fremennik_province/rellekka/hunter_area/rellekka_hunter_area.npcs.toml @@ -29,7 +29,7 @@ examine = "A strange, magical beast." [gnoeals_2] id = 7382 -[erjolf] +[erjolf_base] id = 7405 [natural_historian_rellekka] diff --git a/data/area/fremennik_province/rellekka/ice_strykewyrm_cave/ice_strykewyrm.combat.toml b/data/area/fremennik_province/rellekka/ice_strykewyrm_cave/ice_strykewyrm.combat.toml new file mode 100644 index 0000000000..6f22c25ac7 --- /dev/null +++ b/data/area/fremennik_province/rellekka/ice_strykewyrm_cave/ice_strykewyrm.combat.toml @@ -0,0 +1,37 @@ +[ice_strykewyrm] +attack_speed = 4 +retreat_range = 12 +defend_anim = "strykewyrm_defend" +defend_sound = "strykewyrm_defend" +death_anim = "strykewyrm_death" +death_sound = "strykewyrm_death" + +[ice_strykewyrm.melee] +chance = 8 +range = 1 +anim = "strykewyrm_attack" +target_hit = { offense = "stab", max = 170 } + +[ice_strykewyrm.magic] +chance = 8 +range = 8 +anim = "strykewyrm_shoot" +projectile = "ice_strykewyrm_travel" +projectile_origin_y = 1 +projectile_origin_x = 1 +impact_gfx = "ice_strykewyrm_impact" +target_hit = { offense = "magic", max = 170 } + +[ice_strykewyrm.freeze] +range = 8 +anim = "strykewyrm_shoot" +projectile = "ice_strykewyrm_travel" +projectile_origin_y = 1 +projectile_origin_x = 1 +impact_regardless = true +impact_gfx = "ice_barrage_impact" +target_hit = { offense = "magic", max = 170, accuracy_roll = false } +impact_freeze = 5 + +[ice_strykewyrm.dig] +range = 8 \ No newline at end of file diff --git a/data/area/fremennik_province/rellekka/ice_strykewyrm_cave/ice_strykewyrm.drops.toml b/data/area/fremennik_province/rellekka/ice_strykewyrm_cave/ice_strykewyrm.drops.toml new file mode 100644 index 0000000000..39c7b44475 --- /dev/null +++ b/data/area/fremennik_province/rellekka/ice_strykewyrm_cave/ice_strykewyrm.drops.toml @@ -0,0 +1,87 @@ +[ice_strykewyrm_drop_table] +type = "all" +drops = [ + { table = "ice_strykewyrm_secondary" }, + { table = "ice_strykewyrm_tertiary" }, + { table = "ice_strykewyrm_charms" }, +] + +[ice_strykewyrm_secondary] +roll = 512 +drops = [ + { id = "mithril_battleaxe", chance = 32 }, + { id = "rune_battleaxe", chance = 2 }, + { id = "rune_2h_sword", chance = 2 }, + { id = "staff_of_light", chance = 2 }, + { id = "rune_sq_shield", chance = 2 }, + { id = "dragon_med_helm", chance = 2 }, + { id = "blood_rune", amount = 15, chance = 8 }, + { id = "law_rune", min = 20, max = 45, chance = 8 }, + { id = "death_rune", min = 20, max = 45, chance = 8 }, + { id = "nature_rune", min = 60, max = 79 }, + { id = "water_talisman_noted", min = 1, max = 3, chance = 8 }, + { id = "earth_talisman", chance = 2 }, + { id = "mind_talisman", chance = 2 }, + { id = "fire_talisman", chance = 2 }, + { id = "body_talisman", chance = 2 }, + { id = "air_talisman", chance = 2 }, + { id = "cosmic_talisman", chance = 2 }, + { id = "steel_arrow", amount = 150 }, + { id = "rune_arrow", amount = 42 }, + { id = "adamant_javelin", amount = 5 }, + { id = "rune_bar_noted", chance = 8 }, + { id = "rune_bar", chance = 8 }, + { id = "grimy_guam_noted", min = 1, max = 4, chance = 32 }, + { id = "grimy_marrentill_noted", min = 1, max = 4, chance = 32 }, + { id = "grimy_tarromin_noted", min = 1, max = 4, chance = 32 }, + { id = "grimy_harralander_noted", min = 1, max = 4, chance = 32 }, + { id = "grimy_ranarr_noted", min = 1, max = 4, chance = 32 }, + { id = "grimy_irit_noted", min = 1, max = 4, chance = 32 }, + { id = "grimy_avantoe_noted", min = 1, max = 4, chance = 32 }, + { id = "grimy_kwuarm_noted", min = 1, max = 4, chance = 32 }, + { id = "grimy_cadantine_noted", min = 1, max = 4, chance = 8 }, + { id = "grimy_lantadyme_noted", min = 1, max = 4, chance = 8 }, + { id = "grimy_dwarf_weed_noted", min = 1, max = 4, chance = 8 }, + { id = "watermelon_seed", chance = 32 }, + { id = "cactus_seed", chance = 32 }, + { id = "ranarr_seed", chance = 32 }, + { id = "poison_ivy_seed", chance = 32 }, + { id = "kwuarm_seed", chance = 32 }, + { id = "toadflax_seed", chance = 32 }, + { id = "jangerberry_seed", chance = 32 }, + { id = "wildblood_seed", chance = 32 }, + { id = "belladonna_seed", chance = 32 }, + { id = "whiteberry_seed", chance = 32 }, + { id = "bittercap_mushroom_spore", chance = 32 }, + { id = "limpwurt_seed", chance = 8 }, + { id = "torstol_seed", chance = 2 }, + { id = "maple_seed", chance = 2 }, + { id = "spirit_weed_seed", chance = 2 }, + { id = "snapdragon_seed", chance = 2 }, + { id = "dwarf_weed_seed", chance = 2 }, + { id = "coins", min = 200, max = 8001, chance = 32 }, + { id = "shark", min = 1, max = 6, chance = 32 }, + { id = "crushed_nest_noted", min = 5, max = 9, chance = 32 }, + { id = "super_defence_3", chance = 32 }, + { id = "pure_essence_noted", amount = 200, chance = 32 }, + { id = "magic_logs_noted", amount = 5, chance = 32 }, + { table = "rare_drop_table", chance = 25 }, +] + +[ice_strykewyrm_tertiary] +roll = 512 +drops = [ + { table = "hard_clue_scroll", chance = 2 }, + { table = "elite_clue_scroll", chance = 2 }, + { id = "starved_ancient_effigy", chance = 2 }, + { id = "court_summons" }, +] + +[ice_strykewyrm_charms] +roll = 1000 +drops = [ + { id = "gold_charm", chance = 56 }, + { id = "green_charm", chance = 28 }, + { id = "crimson_charm", chance = 22 }, + { id = "blue_charm", chance = 90 }, +] \ No newline at end of file diff --git a/data/area/fremennik_province/rellekka/ice_strykewyrm_cave/ice_strykewyrm.gfx.toml b/data/area/fremennik_province/rellekka/ice_strykewyrm_cave/ice_strykewyrm.gfx.toml new file mode 100644 index 0000000000..1f27cf33f2 --- /dev/null +++ b/data/area/fremennik_province/rellekka/ice_strykewyrm_cave/ice_strykewyrm.gfx.toml @@ -0,0 +1,8 @@ +[ice_strykewyrm_travel] +id = 2314 +curve = 16 +delay = 30 +# startHeight = 41, endHeight = 16, delay = 30, speed = 2 + +[ice_strykewyrm_impact] +id = 2315 \ No newline at end of file diff --git a/data/area/fremennik_province/rellekka/ice_strykewyrm_cave/ice_strykewyrm_cave.npcs.toml b/data/area/fremennik_province/rellekka/ice_strykewyrm_cave/ice_strykewyrm_cave.npcs.toml index e69de29bb2..fc4c3fb5a5 100644 --- a/data/area/fremennik_province/rellekka/ice_strykewyrm_cave/ice_strykewyrm_cave.npcs.toml +++ b/data/area/fremennik_province/rellekka/ice_strykewyrm_cave/ice_strykewyrm_cave.npcs.toml @@ -0,0 +1,21 @@ +[mound_ice_strykewyrm] +id = 9462 +hitpoints = 3000 +solid = false +allowed_under = true +# Made up stats +att = 225 +str = 225 +def = 175 +magic = 250 +attack_bonus = 100 +slayer_level = 93 +slayer_xp = 300.0 +drop_table = "ice_strykewyrm" +categories = ["ice_strykewyrm", "strykewyrm"] +examine = "I'm pretty sure there's something big under there." + +[ice_strykewyrm] +id = 9463 +combat_def = "ice_strykewyrm" +examine = "Now that's just cold." \ No newline at end of file diff --git a/data/area/fremennik_province/rellekka/ice_strykewyrm_cave/ice_styrkewyrm_cave.npc-spawns.toml b/data/area/fremennik_province/rellekka/ice_strykewyrm_cave/ice_styrkewyrm_cave.npc-spawns.toml index 3029158166..d4f1609e0c 100644 --- a/data/area/fremennik_province/rellekka/ice_strykewyrm_cave/ice_styrkewyrm_cave.npc-spawns.toml +++ b/data/area/fremennik_province/rellekka/ice_strykewyrm_cave/ice_styrkewyrm_cave.npc-spawns.toml @@ -1,14 +1,14 @@ spawns = [ - { id = "mound_wilderness", x = 3412, y = 5675, members = true }, - { id = "mound_wilderness", x = 3413, y = 5667, members = true }, - { id = "mound_wilderness", x = 3415, y = 5654, members = true }, - { id = "mound_wilderness", x = 3419, y = 5661, members = true }, - { id = "mound_wilderness", x = 3421, y = 5674, members = true }, - { id = "mound_wilderness", x = 3423, y = 5654, members = true }, - { id = "mound_wilderness", x = 3423, y = 5667, members = true }, - { id = "mound_wilderness", x = 3426, y = 5679, members = true }, - { id = "mound_wilderness", x = 3430, y = 5652, members = true }, - { id = "mound_wilderness", x = 3430, y = 5660, members = true }, - { id = "mound_wilderness", x = 3432, y = 5673, members = true }, - { id = "mound_wilderness", x = 3437, y = 5665, members = true }, + { id = "mound_ice_strykewyrm", x = 3412, y = 5675, members = true }, + { id = "mound_ice_strykewyrm", x = 3413, y = 5667, members = true }, + { id = "mound_ice_strykewyrm", x = 3415, y = 5654, members = true }, + { id = "mound_ice_strykewyrm", x = 3419, y = 5661, members = true }, + { id = "mound_ice_strykewyrm", x = 3421, y = 5674, members = true }, + { id = "mound_ice_strykewyrm", x = 3423, y = 5654, members = true }, + { id = "mound_ice_strykewyrm", x = 3423, y = 5667, members = true }, + { id = "mound_ice_strykewyrm", x = 3426, y = 5679, members = true }, + { id = "mound_ice_strykewyrm", x = 3430, y = 5652, members = true }, + { id = "mound_ice_strykewyrm", x = 3430, y = 5660, members = true }, + { id = "mound_ice_strykewyrm", x = 3432, y = 5673, members = true }, + { id = "mound_ice_strykewyrm", x = 3437, y = 5665, members = true }, ] diff --git a/data/area/fremennik_province/rellekka/rellekka.npcs.toml b/data/area/fremennik_province/rellekka/rellekka.npcs.toml index 62e24ba8ea..0dea503edb 100644 --- a/data/area/fremennik_province/rellekka/rellekka.npcs.toml +++ b/data/area/fremennik_province/rellekka/rellekka.npcs.toml @@ -436,8 +436,9 @@ id = 2437 [general_khazard_rellekka_2] id = 5566 -[erjolf_rellekka_2] +[erjolf] id = 7462 +dialogue = "child" [larry_rellekka_3] id = 5425 diff --git a/data/area/fremennik_province/rellekka/rellekka.objs.toml b/data/area/fremennik_province/rellekka/rellekka.objs.toml index 80e6995447..685803839c 100644 --- a/data/area/fremennik_province/rellekka/rellekka.objs.toml +++ b/data/area/fremennik_province/rellekka/rellekka.objs.toml @@ -38,3 +38,20 @@ examine = "Used for spinning thread." id = 4310 examine = "Used for fashioning clay items." +[ice_strykewyrm_cave_entrance] +id = 48188 + +[ice_strykewyrm_cave_exit] +id = 48189 + +[erjolf_cave_opening] +id = 42794 + +[erjolf_cave_opening_exit] +id = 42795 + +[erjolf_cave_exit] +id = 42891 + +[erjolf_cave_entrance] +id = 42793 \ No newline at end of file diff --git a/data/area/fremennik_province/rellekka/rellekka.teles.toml b/data/area/fremennik_province/rellekka/rellekka.teles.toml index fbf9a2f766..2bf1aaae76 100644 --- a/data/area/fremennik_province/rellekka/rellekka.teles.toml +++ b/data/area/fremennik_province/rellekka/rellekka.teles.toml @@ -46,4 +46,34 @@ delta = { level = -1 } [4188] option = "Climb-up" tile = { x = 2672, y = 10099, level = 2 } -to = { x = 2666, y = 3694 } \ No newline at end of file +to = { x = 2666, y = 3694 } + +[erjolf_cave_entrance] +option = "Enter" +tile = { x = 2737, y = 3729 } +to = { x = 3485, y = 5511 } + +[erjolf_cave_exit] +option = "Leave-cave" +tile = { x = 3484, y = 5509 } +to = { x = 2736, y = 3731 } + +[ice_strykewyrm_cave_entrance] +option = "Enter" +tile = { x = 3510, y = 5514 } +to = { x = 3435, y = 5646 } + +[ice_strykewyrm_cave_exit] +option = "Enter" +tile = { x = 3435, y = 5645 } +to = { x = 3509, y = 5515 } + +[erjolf_cave_opening] +option = "Enter" +tile = { x = 3470, y = 5524 } +to = { x = 3471, y = 5531 } + +[erjolf_cave_opening_exit] +option = "Enter" +tile = { x = 3470, y = 5529 } +to = { x = 3471, y = 5523 } diff --git a/data/area/kandarin/feldip_hills/feldip_hills.npcs.toml b/data/area/kandarin/feldip_hills/feldip_hills.npcs.toml index 407c52ed69..4f8fabcb2b 100644 --- a/data/area/kandarin/feldip_hills/feldip_hills.npcs.toml +++ b/data/area/kandarin/feldip_hills/feldip_hills.npcs.toml @@ -89,8 +89,9 @@ allowed_under = true # Made up stats att = 150 str = 150 -def = 150 -magic = 250 +def = 100 +magic = 150 +attack_bonus = 60 slayer_level = 73 slayer_xp = 110.0 drop_table = "jungle_strykewyrm" diff --git a/data/area/kharidian_desert/al_kharid/desert_strykewyrm/desert_strykewyrm.npcs.toml b/data/area/kharidian_desert/al_kharid/desert_strykewyrm/desert_strykewyrm.npcs.toml index ea57642d69..04daf74d9c 100644 --- a/data/area/kharidian_desert/al_kharid/desert_strykewyrm/desert_strykewyrm.npcs.toml +++ b/data/area/kharidian_desert/al_kharid/desert_strykewyrm/desert_strykewyrm.npcs.toml @@ -6,18 +6,16 @@ allowed_under = true # Made up stats att = 175 str = 175 -def = 175 -range = 300 -magic = 300 +def = 125 +range = 200 +magic = 150 +attack_bonus = 80 slayer_level = 77 slayer_xp = 120.0 drop_table = "desert_strykewyrm" categories = ["desert_strykewyrm", "strykewyrm"] examine = "I'm pretty sure there's something under there." -[ice_strykewyrm] -id = 9463 - [desert_strykewyrm] id = 9465 combat_def = "desert_strykewyrm" diff --git a/data/area/wilderness/wilderness.npcs.toml b/data/area/wilderness/wilderness.npcs.toml index c5b44e30b1..00e068d756 100644 --- a/data/area/wilderness/wilderness.npcs.toml +++ b/data/area/wilderness/wilderness.npcs.toml @@ -181,9 +181,6 @@ respawn_delay = 150 drop_table = "bones" examine = "He kills in the name of Guthix." -[mound_wilderness] -id = 9462 - [gull_wilderness] id = 9717 diff --git a/data/entity/player/modal/icon.items.toml b/data/entity/player/modal/icon.items.toml index 675b76fa5a..928c32f2b8 100644 --- a/data/entity/player/modal/icon.items.toml +++ b/data/entity/player/modal/icon.items.toml @@ -748,7 +748,7 @@ id = 14670 [erjolf] id = 14741 -examine = " Erjolf." +examine = "Erjolf." [leela] id = 14817 diff --git a/data/skill/slayer/duradel.enums.toml b/data/skill/slayer/duradel.enums.toml index 0108f9782a..1ea3b2c5d0 100644 --- a/data/skill/slayer/duradel.enums.toml +++ b/data/skill/slayer/duradel.enums.toml @@ -53,7 +53,7 @@ values = { gorak = 5, greater_demon = 10, hellhound = 9, -# ice_strykewyrm = 8, + ice_strykewyrm = 8, iron_dragon = 9, jungle_strykewyrm = 10, kalphite_worker = 10, diff --git a/data/skill/slayer/kuradal.enums.toml b/data/skill/slayer/kuradal.enums.toml index b0eaf9659c..ecb7ac8ed4 100644 --- a/data/skill/slayer/kuradal.enums.toml +++ b/data/skill/slayer/kuradal.enums.toml @@ -59,7 +59,7 @@ values = { gargoyle = 12, greater_demon = 11, hellhound = 10, -# ice_strykewyrm = 9, + ice_strykewyrm = 9, iron_dragon = 9, jungle_strykewyrm = 8, kalphite_worker = 5, diff --git a/game/src/main/kotlin/content/area/fremennik_province/rellekka/Erjolf.kt b/game/src/main/kotlin/content/area/fremennik_province/rellekka/Erjolf.kt new file mode 100644 index 0000000000..1e9a20296c --- /dev/null +++ b/game/src/main/kotlin/content/area/fremennik_province/rellekka/Erjolf.kt @@ -0,0 +1,24 @@ +package content.area.fremennik_province.rellekka + +import content.entity.player.dialogue.Angry +import content.entity.player.dialogue.Happy +import content.entity.player.dialogue.Quiz +import content.entity.player.dialogue.Shifty +import content.entity.player.dialogue.type.npc +import content.entity.player.dialogue.type.player +import world.gregs.voidps.engine.Script + +class Erjolf : Script { + init { + npcOperate("Talk-to", "erjolf") { + npc("Incredible! This'll show them!") + player("Hello?") + npc("Oh! Hello there.") + player("You seem rather excited about something.") + npc("Who, me? No, I'm not excited about anything. I certainly haven't found anything amazing inside the caves here.") + player("You won't mind if I have a look inside the cave, then.") + npc("Just you hold on a minute. I know what you adventurers are like, you'll wander in there and decide that anything you find is yours to take.") + // TODO Tale of the Muspah + } + } +} diff --git a/game/src/main/kotlin/content/area/fremennik_province/rellekka/IceStrykewyrm.kt b/game/src/main/kotlin/content/area/fremennik_province/rellekka/IceStrykewyrm.kt new file mode 100644 index 0000000000..7407c838a7 --- /dev/null +++ b/game/src/main/kotlin/content/area/fremennik_province/rellekka/IceStrykewyrm.kt @@ -0,0 +1,29 @@ +package content.area.fremennik_province.rellekka + +import content.area.kandarin.feldip_hills.JungleStrykewyrm +import content.entity.combat.hit.directHit +import world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.client.message + +class IceStrykewyrm : Script { + init { + npcOperate("Investigate", "mound_ice_strykewyrm") { (target) -> + JungleStrykewyrm.investigate(this, target, "ice_strykewyrm") + } + + npcAttack("ice_strykewyrm", "dig") { target -> + JungleStrykewyrm.burrow(this, target) + } + + npcCombatDamage("ice_strykewyrm") { + if (it.spell.startsWith("ice_")) { + directHit(it.damage, "healed") + } + } + + objTeleportTakeOff("Enter", "ice_strykewyrm_cave_entrance") { _, _ -> + message("You follow the cave down deeper.") + 0 + } + } +} \ No newline at end of file diff --git a/game/src/main/kotlin/content/area/kandarin/feldip_hills/JungleStrykewyrm.kt b/game/src/main/kotlin/content/area/kandarin/feldip_hills/JungleStrykewyrm.kt index b8dac26bc7..398ad3c49b 100644 --- a/game/src/main/kotlin/content/area/kandarin/feldip_hills/JungleStrykewyrm.kt +++ b/game/src/main/kotlin/content/area/kandarin/feldip_hills/JungleStrykewyrm.kt @@ -7,8 +7,10 @@ import content.entity.effect.toxin.poison import content.entity.effect.transform import content.skill.slayer.slayerTask 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.variable.start +import world.gregs.voidps.engine.data.Settings import world.gregs.voidps.engine.entity.character.Character import world.gregs.voidps.engine.entity.character.mode.EmptyMode import world.gregs.voidps.engine.entity.character.mode.PauseMode @@ -16,7 +18,9 @@ import world.gregs.voidps.engine.entity.character.npc.NPC import world.gregs.voidps.engine.entity.character.player.Player import world.gregs.voidps.engine.queue.softQueue import world.gregs.voidps.engine.timer.Timer +import world.gregs.voidps.engine.timer.toTicks import world.gregs.voidps.type.random +import java.util.concurrent.TimeUnit import kotlin.random.nextInt class JungleStrykewyrm : Script { @@ -50,10 +54,14 @@ class JungleStrykewyrm : Script { source.anim("strykewyrm_bury") val temp = source.mode val type = source.transform - source.softQueue("resurface", 3) { + source.start("action_delay", 8) + source.softQueue("burrow", 3) { source.clearTransform() source.mode = PauseMode + target.mode = EmptyMode + source.start("action_delay", Int.MAX_VALUE) source.walkToDelay(target.tile) + source.start("action_delay", 4) source.mode = temp source.transform(type) source.anim("strykewyrm_surface") @@ -65,7 +73,7 @@ class JungleStrykewyrm : Script { } fun investigate(source: Player, target: NPC, to: String) { - if (source.slayerTask != to) { + if (Settings["slayer.strykewyrmReqTask", false] && source.slayerTask != to) { source.anim("emote_stomp") source.softQueue("stomp_mound", 3) { source.anim("emote_think") @@ -78,11 +86,13 @@ class JungleStrykewyrm : Script { target.mode = EmptyMode target.steps.clear() target.softTimers.start("strykewyrm_revert") - target.softQueue("styrkyewyrm_transform", 3) { + target.softQueue("strykewyrm_transform", 3) { target.mode = EmptyMode target.transform(to) target.anim("strykewyrm_surface") target.face(source) + target.start("action_delay", TimeUnit.SECONDS.toTicks(3)) + target.interactPlayer(source, "Attack") } } } diff --git a/game/src/main/kotlin/content/entity/player/equip/Equipment.kt b/game/src/main/kotlin/content/entity/player/equip/Equipment.kt index dc166623cb..103120d18a 100644 --- a/game/src/main/kotlin/content/entity/player/equip/Equipment.kt +++ b/game/src/main/kotlin/content/entity/player/equip/Equipment.kt @@ -1,6 +1,7 @@ package content.entity.player.equip import content.entity.combat.hit.Hit +import content.entity.effect.transform import content.entity.player.effect.antifire import content.entity.player.effect.superAntifire import content.skill.magic.spell.spell @@ -92,7 +93,7 @@ object Equipment { if (source.tile in area && target.tile in area) { damage = (damage * 1.04).toInt() } - } else if (type == "magic" && target is NPC && target.id == "ice_strykewyrm") { + } else if (type == "magic" && target is NPC && target.transform == "ice_strykewyrm") { val fireCape = source.equipped(EquipSlot.Cape).id == "fire_cape" if (fireCape) { damage += 40 diff --git a/game/src/main/resources/game.properties b/game/src/main/resources/game.properties index a95d37b416..57207fd37f 100644 --- a/game/src/main/resources/game.properties +++ b/game/src/main/resources/game.properties @@ -166,8 +166,15 @@ farming.weeds.regrow=true # Deposit multiple items into a compost bin in one go farming.compost.all=false +#------- Magic ------- + # Starting value of an item when alchemy starts giving warnings (-1 for never) -magic.alchemy.warningLimit = 25000 +magic.alchemy.warningLimit=25000 + +#------- Slayer ------- + +# Fighting strykewyrms requires a slayer task +slayer.strykewyrmReqTask=true #=================================== # Content & Events From 3a6dbb46d2fae1d871ab7abf7f791d9938229417 Mon Sep 17 00:00:00 2001 From: GregHib Date: Fri, 20 Mar 2026 16:04:56 +0000 Subject: [PATCH 23/27] Add shadow warriors --- .../legends_guild/dungeon/legends_guild_dungeon.npcs.toml | 3 +-- data/skill/slayer/chaeldar.enums.toml | 2 +- data/skill/slayer/vannaka.enums.toml | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/data/area/kandarin/legends_guild/dungeon/legends_guild_dungeon.npcs.toml b/data/area/kandarin/legends_guild/dungeon/legends_guild_dungeon.npcs.toml index 76188c5219..f4dca3dc85 100644 --- a/data/area/kandarin/legends_guild/dungeon/legends_guild_dungeon.npcs.toml +++ b/data/area/kandarin/legends_guild/dungeon/legends_guild_dungeon.npcs.toml @@ -6,8 +6,7 @@ str = 33 def = 36 mage = 0 range = 0 -style = "slash" -max_hit_melee = 60 +combat_def = "ice_warrior" attack_bonus = 20 hunt_mode = "cowardly" slayer_xp = 67.0 diff --git a/data/skill/slayer/chaeldar.enums.toml b/data/skill/slayer/chaeldar.enums.toml index 543524b248..92c2e5af41 100644 --- a/data/skill/slayer/chaeldar.enums.toml +++ b/data/skill/slayer/chaeldar.enums.toml @@ -68,7 +68,7 @@ values = { lesser_demon = 9, # zygomite = 7, nechryael = 12, -# shadow_warrior = 8, + shadow_warrior = 8, skeletal_wyvern = 7, spiritual_mage_zamorak = 12, ice_troll_troll_country = 11, diff --git a/data/skill/slayer/vannaka.enums.toml b/data/skill/slayer/vannaka.enums.toml index d800b5e4a2..a2684929a3 100644 --- a/data/skill/slayer/vannaka.enums.toml +++ b/data/skill/slayer/vannaka.enums.toml @@ -88,7 +88,7 @@ values = { pyrefiend_large = 8, # sea_snake_young = 6, # asyn_shade = 8, -# shadow_warrior = 8, + shadow_warrior = 8, spiritual_mage_zamorak = 8, ice_troll_troll_country = 7, turoth = 8, From 93794a01d7b97492feb55ad9c9be915144067d8a Mon Sep 17 00:00:00 2001 From: GregHib Date: Fri, 20 Mar 2026 16:06:50 +0000 Subject: [PATCH 24/27] Formatting --- .../area/fremennik_province/rellekka/IceStrykewyrm.kt | 2 +- .../content/area/kandarin/feldip_hills/JungleStrykewyrm.kt | 2 +- .../main/kotlin/content/area/kharidian_desert/DesertHeat.kt | 2 +- .../area/kharidian_desert/al_kharid/DesertStrykewyrm.kt | 2 +- .../kharidian_desert/pollnivneach/smoke_dungeon/DustDevil.kt | 2 +- .../pollnivneach/smoke_dungeon/SmokeDungeon.kt | 5 ++--- game/src/main/kotlin/content/skill/melee/weapon/Weapon.kt | 4 +++- 7 files changed, 10 insertions(+), 9 deletions(-) diff --git a/game/src/main/kotlin/content/area/fremennik_province/rellekka/IceStrykewyrm.kt b/game/src/main/kotlin/content/area/fremennik_province/rellekka/IceStrykewyrm.kt index 7407c838a7..874d1fe4a3 100644 --- a/game/src/main/kotlin/content/area/fremennik_province/rellekka/IceStrykewyrm.kt +++ b/game/src/main/kotlin/content/area/fremennik_province/rellekka/IceStrykewyrm.kt @@ -26,4 +26,4 @@ class IceStrykewyrm : Script { 0 } } -} \ No newline at end of file +} diff --git a/game/src/main/kotlin/content/area/kandarin/feldip_hills/JungleStrykewyrm.kt b/game/src/main/kotlin/content/area/kandarin/feldip_hills/JungleStrykewyrm.kt index 398ad3c49b..e75a58e81d 100644 --- a/game/src/main/kotlin/content/area/kandarin/feldip_hills/JungleStrykewyrm.kt +++ b/game/src/main/kotlin/content/area/kandarin/feldip_hills/JungleStrykewyrm.kt @@ -96,4 +96,4 @@ class JungleStrykewyrm : Script { } } } -} \ No newline at end of file +} diff --git a/game/src/main/kotlin/content/area/kharidian_desert/DesertHeat.kt b/game/src/main/kotlin/content/area/kharidian_desert/DesertHeat.kt index 5599929757..07b4384476 100644 --- a/game/src/main/kotlin/content/area/kharidian_desert/DesertHeat.kt +++ b/game/src/main/kotlin/content/area/kharidian_desert/DesertHeat.kt @@ -100,4 +100,4 @@ class DesertHeat : Script { } return EnumDefinitions.intOrNull("desert_heat_delay", item) ?: default } -} \ No newline at end of file +} diff --git a/game/src/main/kotlin/content/area/kharidian_desert/al_kharid/DesertStrykewyrm.kt b/game/src/main/kotlin/content/area/kharidian_desert/al_kharid/DesertStrykewyrm.kt index 1c4a66de67..baefb72a02 100644 --- a/game/src/main/kotlin/content/area/kharidian_desert/al_kharid/DesertStrykewyrm.kt +++ b/game/src/main/kotlin/content/area/kharidian_desert/al_kharid/DesertStrykewyrm.kt @@ -13,4 +13,4 @@ class DesertStrykewyrm : Script { JungleStrykewyrm.burrow(this, target) } } -} \ No newline at end of file +} diff --git a/game/src/main/kotlin/content/area/kharidian_desert/pollnivneach/smoke_dungeon/DustDevil.kt b/game/src/main/kotlin/content/area/kharidian_desert/pollnivneach/smoke_dungeon/DustDevil.kt index 3322570721..c7c215a2af 100644 --- a/game/src/main/kotlin/content/area/kharidian_desert/pollnivneach/smoke_dungeon/DustDevil.kt +++ b/game/src/main/kotlin/content/area/kharidian_desert/pollnivneach/smoke_dungeon/DustDevil.kt @@ -11,4 +11,4 @@ class DustDevil : Script { npcCondition("face_mask") { it is Player && Equipment.isFaceMask(it.equipped(EquipSlot.Hat).id) } npcCondition("no_face_mask") { it is Player && !Equipment.isFaceMask(it.equipped(EquipSlot.Hat).id) } } -} \ No newline at end of file +} diff --git a/game/src/main/kotlin/content/area/kharidian_desert/pollnivneach/smoke_dungeon/SmokeDungeon.kt b/game/src/main/kotlin/content/area/kharidian_desert/pollnivneach/smoke_dungeon/SmokeDungeon.kt index 95d68f07dd..b2aaf5c826 100644 --- a/game/src/main/kotlin/content/area/kharidian_desert/pollnivneach/smoke_dungeon/SmokeDungeon.kt +++ b/game/src/main/kotlin/content/area/kharidian_desert/pollnivneach/smoke_dungeon/SmokeDungeon.kt @@ -1,12 +1,11 @@ package content.area.kharidian_desert.pollnivneach.smoke_dungeon import world.gregs.voidps.engine.Script -import world.gregs.voidps.engine.client.ui.close class SmokeDungeon : Script { init { - // TODO smoke interface - https://youtu.be/2ARmHyBkO8w?t=108 + // TODO smoke interface - https://youtu.be/2ARmHyBkO8w?t=108 // entered("smoke_dungeon") {} // exited("smoke_dungeon") {} } -} \ No newline at end of file +} diff --git a/game/src/main/kotlin/content/skill/melee/weapon/Weapon.kt b/game/src/main/kotlin/content/skill/melee/weapon/Weapon.kt index 2f558b7efe..3dadd30bdb 100644 --- a/game/src/main/kotlin/content/skill/melee/weapon/Weapon.kt +++ b/game/src/main/kotlin/content/skill/melee/weapon/Weapon.kt @@ -253,7 +253,9 @@ var Character.attackRange: Int val default = if (this is NPC) { val combatDefinition = get().get(transformDef["combat_def", id]) transformDef["attack_range", combatDefinition.attackRange] - } else 1 + } else { + 1 + } return get("attack_range", default) } set(value) = set("attack_range", value) From 6f34ad09bc74f8d43bdab3a9029b47161fc00e9b Mon Sep 17 00:00:00 2001 From: GregHib Date: Fri, 20 Mar 2026 16:45:46 +0000 Subject: [PATCH 25/27] Add dark beasts --- .../kuradals_dungeon/kuradals_dungeon.combat.toml | 3 ++- .../kuradals_dungeon/kuradals_dungeon.drops.toml | 10 ++++++++++ .../kuradals_dungeon/kuradals_dungeon.npcs.toml | 2 +- data/area/kandarin/ardougne/east_ardougne.objs.toml | 6 ++++++ data/area/kandarin/ardougne/east_ardougne.teles.toml | 12 +++++++++++- data/skill/slayer/duradel.enums.toml | 2 +- data/skill/slayer/kuradal.enums.toml | 2 +- 7 files changed, 32 insertions(+), 5 deletions(-) diff --git a/data/area/kandarin/ancient_cavern/kuradals_dungeon/kuradals_dungeon.combat.toml b/data/area/kandarin/ancient_cavern/kuradals_dungeon/kuradals_dungeon.combat.toml index c453f3a77a..3ab3d7dbac 100644 --- a/data/area/kandarin/ancient_cavern/kuradals_dungeon/kuradals_dungeon.combat.toml +++ b/data/area/kandarin/ancient_cavern/kuradals_dungeon/kuradals_dungeon.combat.toml @@ -13,7 +13,8 @@ target_sound = "dark_beast_attack" target_hit = { offense = "crush", max = 170 } [dark_beast.magic] -range = 1 +range = 8 +condition = "ranged_only" anim = "dark_beast_attack" target_sound = "dark_beast_attack" target_hit = { offense = "magic", max = 80 } diff --git a/data/area/kandarin/ancient_cavern/kuradals_dungeon/kuradals_dungeon.drops.toml b/data/area/kandarin/ancient_cavern/kuradals_dungeon/kuradals_dungeon.drops.toml index 0c3079df9e..ba4ee8e9c2 100644 --- a/data/area/kandarin/ancient_cavern/kuradals_dungeon/kuradals_dungeon.drops.toml +++ b/data/area/kandarin/ancient_cavern/kuradals_dungeon/kuradals_dungeon.drops.toml @@ -4,6 +4,7 @@ drops = [ { id = "big_bones" }, { table = "dark_beast_secondary" }, { table = "dark_beast_tertiary" }, + { table = "dark_beast_charms" }, ] [dark_beast_secondary] @@ -42,3 +43,12 @@ drops = [ { table = "elite_clue_scroll", chance = 4 }, { id = "curved_bone" }, ] + +[dark_beast_charms] +roll = 1000 +drops = [ + { id = "gold_charm", chance = 84 }, + { id = "green_charm", chance = 42 }, + { id = "crimson_charm", chance = 84 }, + { id = "blue_charm", chance = 134 }, +] diff --git a/data/area/kandarin/ancient_cavern/kuradals_dungeon/kuradals_dungeon.npcs.toml b/data/area/kandarin/ancient_cavern/kuradals_dungeon/kuradals_dungeon.npcs.toml index 4b2fc1729f..b1e9577b0e 100644 --- a/data/area/kandarin/ancient_cavern/kuradals_dungeon/kuradals_dungeon.npcs.toml +++ b/data/area/kandarin/ancient_cavern/kuradals_dungeon/kuradals_dungeon.npcs.toml @@ -7,7 +7,7 @@ def = 120 mage = 160 combat_def = "dark_beast" xp_bonus = 2.5 -hunt_mode = "aggressive" +hunt_mode = "aggressive_intolerant" slayer_xp = 225.4 categories = ["dark_beasts"] respawn_delay = 50 diff --git a/data/area/kandarin/ardougne/east_ardougne.objs.toml b/data/area/kandarin/ardougne/east_ardougne.objs.toml index c462fdaeca..6d0035c051 100644 --- a/data/area/kandarin/ardougne/east_ardougne.objs.toml +++ b/data/area/kandarin/ardougne/east_ardougne.objs.toml @@ -170,3 +170,9 @@ examine = "An appliance used for cooking." [potters_wheel_ardougne] id = 34801 examine = "Used for fashioning clay items." + +[trapdoor_mourner_tunnels] +id = 8783 + +[ladder_mourner_tunnels] +id = 8785 \ No newline at end of file diff --git a/data/area/kandarin/ardougne/east_ardougne.teles.toml b/data/area/kandarin/ardougne/east_ardougne.teles.toml index 8e3670047b..67fe945f9b 100644 --- a/data/area/kandarin/ardougne/east_ardougne.teles.toml +++ b/data/area/kandarin/ardougne/east_ardougne.teles.toml @@ -261,4 +261,14 @@ to = { x = 2652, y = 3280, level = 1 } [34550] option = "Climb-down" tile = { x = 2650, y = 3280, level = 1 } -to = { x = 2649, y = 3280 } \ No newline at end of file +to = { x = 2649, y = 3280 } + +[trapdoor_mourner_tunnels] +option = "Open" +tile = { x = 2542, y = 3327 } +to = { x = 2044, y = 4649 } + +[ladder_mourner_tunnels] +option = "Climb-up" +tile = { x = 2044, y = 4650 } +to = { x = 2543, y = 3327 } diff --git a/data/skill/slayer/duradel.enums.toml b/data/skill/slayer/duradel.enums.toml index 1ea3b2c5d0..f482c8ea95 100644 --- a/data/skill/slayer/duradel.enums.toml +++ b/data/skill/slayer/duradel.enums.toml @@ -45,7 +45,7 @@ values = { black_dragon = 9, bloodveld = 10, dagannoth_lighthouse_range_74 = 10, -# dark_beast = 15, + dark_beast = 15, desert_strykewyrm = 11, dust_devil = 10, fire_giant = 10, diff --git a/data/skill/slayer/kuradal.enums.toml b/data/skill/slayer/kuradal.enums.toml index ecb7ac8ed4..54eec08710 100644 --- a/data/skill/slayer/kuradal.enums.toml +++ b/data/skill/slayer/kuradal.enums.toml @@ -51,7 +51,7 @@ values = { bloodveld = 10, blue_dragon = 7, dagannoth_lighthouse_range_74 = 10, -# dark_beast = 12, + dark_beast = 12, desert_strykewyrm = 7, dust_devil = 10, # elf_warrior = 12, From cb440c369d3e35c9836ea30611318abdd1aeb4d4 Mon Sep 17 00:00:00 2001 From: GregHib Date: Fri, 20 Mar 2026 17:51:00 +0000 Subject: [PATCH 26/27] Add lunar isle access and suqah tasks --- .../lunar_isle/lunar_isle.areas.toml | 4 ++ .../lunar_isle/lunar_isle.combat.toml | 16 ++++-- .../lunar_isle/lunar_isle.npcs.toml | 2 +- .../lunar_isle/lunar_isle.objs.toml | 12 ++++ .../lunar_isle/lunar_isle.teles.toml | 19 +++++++ .../pirates_cove/pirates_cove.areas.toml | 4 ++ .../pirates_cove/pirates_cove.npc-spawns.toml | 2 +- .../pirates_cove/pirates_cove.npcs.toml | 2 +- .../pirates_cove/pirates_cove.teles.toml | 29 ++++++++++ .../runecrafting_altars.objs.toml | 4 ++ data/skill/slayer/duradel.enums.toml | 2 +- data/skill/slayer/kuradal.enums.toml | 2 +- .../lunar_isle/AstralAltar.kt | 20 +++++++ .../lunar_isle/CaptainBentley.kt | 49 ++++++++++++++++ .../lunar_isle/Oneiromancer.kt | 31 ++++++++++ .../fremennik_province/lunar_isle/Suqah.kt | 14 +++++ .../pirates_cove/LokarSearunner.kt | 56 +++++++++++++++++++ .../entity/player/command/PlayerCommands.kt | 1 + .../skill/runecrafting/Runecrafting.kt | 2 +- .../skill/runecrafting/MysteriousRuinsTest.kt | 10 ++-- .../skill/runecrafting/RunecraftingTest.kt | 15 ++--- 21 files changed, 271 insertions(+), 25 deletions(-) create mode 100644 data/area/fremennik_province/lunar_isle/lunar_isle.teles.toml create mode 100644 data/area/fremennik_province/pirates_cove/pirates_cove.teles.toml create mode 100644 game/src/main/kotlin/content/area/fremennik_province/lunar_isle/AstralAltar.kt create mode 100644 game/src/main/kotlin/content/area/fremennik_province/lunar_isle/CaptainBentley.kt create mode 100644 game/src/main/kotlin/content/area/fremennik_province/lunar_isle/Oneiromancer.kt create mode 100644 game/src/main/kotlin/content/area/fremennik_province/lunar_isle/Suqah.kt create mode 100644 game/src/main/kotlin/content/area/fremennik_province/pirates_cove/LokarSearunner.kt diff --git a/data/area/fremennik_province/lunar_isle/lunar_isle.areas.toml b/data/area/fremennik_province/lunar_isle/lunar_isle.areas.toml index f2dbd5ba76..fb42529c4c 100644 --- a/data/area/fremennik_province/lunar_isle/lunar_isle.areas.toml +++ b/data/area/fremennik_province/lunar_isle/lunar_isle.areas.toml @@ -14,3 +14,7 @@ y = [3840, 3967] level = 0 tags = ["multi_combat", "penguin_area"] hint = "on a large crescent island." + +[lunar_isle] +x = [2048, 2175] +y = [3840, 3967] diff --git a/data/area/fremennik_province/lunar_isle/lunar_isle.combat.toml b/data/area/fremennik_province/lunar_isle/lunar_isle.combat.toml index c95e9f0217..939c17a896 100644 --- a/data/area/fremennik_province/lunar_isle/lunar_isle.combat.toml +++ b/data/area/fremennik_province/lunar_isle/lunar_isle.combat.toml @@ -5,29 +5,35 @@ defend_anim = "suqah_defend" death_anim = "suqah_death" [suqah.melee] -chance = 5 # Unknown chances +chance = 10 # Unknown chances range = 1 +condition = "no_protect_melee" anim = "suqah_attack" target_hit = { offense = "stab", max = 100 } [suqah.water] -chance = 1 +chance = 5 range = 8 +condition = "no_protect_magic" anim = "suqah_attack" -gfx = "water_wave" -projectile = "water_spell_cast" +target_sound = "water_wave_cast" +projectile = "water_wave" target_hit = { offense = "magic", max = 100 } impact_gfx = "water_wave_impact" +miss_sound = "spell_splash" [suqah.ice] chance = 1 range = 8 +condition = "no_protect_magic" anim = "suqah_attack" -target_sound = "ice_barrage_cast" +target_sound = "water_wave_cast" projectile = { id = "ice_barrage", end_height = 0 } target_hit = { offense = "magic", min = 100, max = 100 } impact_gfx = "ice_barrage_impact" impact_sound = "ice_barrage_impact" +miss_sound = "spell_splash" +impact_freeze = 10 [suqah_swords] clone = "suqah" diff --git a/data/area/fremennik_province/lunar_isle/lunar_isle.npcs.toml b/data/area/fremennik_province/lunar_isle/lunar_isle.npcs.toml index c8d36e5a26..80d7b1ece2 100644 --- a/data/area/fremennik_province/lunar_isle/lunar_isle.npcs.toml +++ b/data/area/fremennik_province/lunar_isle/lunar_isle.npcs.toml @@ -170,7 +170,7 @@ id = 5899 [cyrisus_2] id = 5900 -[captain_bentley_3] +[captain_bentley_after] id = 408 [cyrisus_3] diff --git a/data/area/fremennik_province/lunar_isle/lunar_isle.objs.toml b/data/area/fremennik_province/lunar_isle/lunar_isle.objs.toml index ad41fce9bf..4eeb0b8399 100644 --- a/data/area/fremennik_province/lunar_isle/lunar_isle.objs.toml +++ b/data/area/fremennik_province/lunar_isle/lunar_isle.objs.toml @@ -1,3 +1,15 @@ [bank_booth_lunar_isle] id = 16700 examine = "The bank teller will serve you from here." + +[lunar_isle_dock_ladder] +id = 16960 + +[lunar_isle_dock_ladder_top] +id = 16962 + +[lunar_isle_boat_ladder] +id = 16959 + +[lunar_isle_boat_ladder_top] +id = 16961 diff --git a/data/area/fremennik_province/lunar_isle/lunar_isle.teles.toml b/data/area/fremennik_province/lunar_isle/lunar_isle.teles.toml new file mode 100644 index 0000000000..f8325e4395 --- /dev/null +++ b/data/area/fremennik_province/lunar_isle/lunar_isle.teles.toml @@ -0,0 +1,19 @@ +[lunar_isle_dock_ladder] +option = "Climb" +tile = { x = 2118, y = 3894 } +to = { x = 2118, y = 3893, level = 1 } + +[lunar_isle_dock_ladder_top] +option = "Climb" +tile = { x = 2118, y = 3894, level = 1 } +to = { x = 2118, y = 3895 } + +[lunar_isle_boat_ladder] +option = "Climb" +tile = { x = 2127, y = 3893, level = 1 } +to = { x = 2128, y = 3893, level = 2 } + +[lunar_isle_boat_ladder_top] +option = "Climb" +tile = { x = 2127, y = 3893, level = 2 } +to = { x = 2126, y = 3893, level = 1 } diff --git a/data/area/fremennik_province/pirates_cove/pirates_cove.areas.toml b/data/area/fremennik_province/pirates_cove/pirates_cove.areas.toml index 9543b3866f..da49e262f4 100644 --- a/data/area/fremennik_province/pirates_cove/pirates_cove.areas.toml +++ b/data/area/fremennik_province/pirates_cove/pirates_cove.areas.toml @@ -3,3 +3,7 @@ x = [2176, 2239] y = [3776, 3839] level = 0 tags = ["multi_combat"] + +[pirates_cove] +x = [2176, 2239] +y = [3776, 3839] diff --git a/data/area/fremennik_province/pirates_cove/pirates_cove.npc-spawns.toml b/data/area/fremennik_province/pirates_cove/pirates_cove.npc-spawns.toml index 67e8d6bb76..1bd9d55ecb 100644 --- a/data/area/fremennik_province/pirates_cove/pirates_cove.npc-spawns.toml +++ b/data/area/fremennik_province/pirates_cove/pirates_cove.npc-spawns.toml @@ -16,7 +16,7 @@ spawns = [ { id = "pirate_pirates_cove", x = 2205, y = 3814 }, { id = "captain_bentley", x = 2222, y = 3796, level = 2, members = true }, { id = "birds_eye_jack", x = 2222, y = 3788, level = 1, members = true }, - { id = "lokar_searunner_pirates_cove", x = 2214, y = 3794 }, + { id = "lokar_searunner", x = 2214, y = 3794 }, { id = "cabin_boy", x = 2223, y = 3788, level = 3, members = true }, { id = "beefy_burns", x = 2222, y = 3789 }, { id = "eagle_eye_shultz", x = 2224, y = 3813, level = 2 }, diff --git a/data/area/fremennik_province/pirates_cove/pirates_cove.npcs.toml b/data/area/fremennik_province/pirates_cove/pirates_cove.npcs.toml index 12330852f1..69bbd3d614 100644 --- a/data/area/fremennik_province/pirates_cove/pirates_cove.npcs.toml +++ b/data/area/fremennik_province/pirates_cove/pirates_cove.npcs.toml @@ -13,7 +13,7 @@ categories = ["pirates"] respawn_delay = 25 examine = "Avast ye scurvy land lubbers!" -[lokar_searunner_pirates_cove] +[lokar_searunner_after] id = 4537 examine = "Either a very fremennikey pirate, or a very piratey fremennik." diff --git a/data/area/fremennik_province/pirates_cove/pirates_cove.teles.toml b/data/area/fremennik_province/pirates_cove/pirates_cove.teles.toml new file mode 100644 index 0000000000..1a20d5a600 --- /dev/null +++ b/data/area/fremennik_province/pirates_cove/pirates_cove.teles.toml @@ -0,0 +1,29 @@ +[lunar_isle_boat_ladder_top] +option = "Climb" +tile = { x = 2214, y = 3801, level = 2 } +to = { x = 2214, y = 3802, level = 1 } + +[lunar_isle_boat_ladder] +option = "Climb" +tile = { x = 2214, y = 3801, level = 1 } +to = { x = 2214, y = 3800, level = 2 } + +[lunar_isle_dock_ladder_top] +option = "Climb" +tile = { x = 2212, y = 3809, level = 1 } +to = { x = 2211, y = 3809 } + +[lunar_isle_dock_ladder] +option = "Climb" +tile = { x = 2212, y = 3809 } +to = { x = 2213, y = 3809, level = 1 } + +[lunar_isle_dock_ladder_top] +option = "Climb" +tile = { x = 2213, y = 3795, level = 1 } +to = { x = 2213, y = 3794 } + +[lunar_isle_dock_ladder] +option = "Climb" +tile = { x = 2213, y = 3795 } +to = { x = 2213, y = 3796, level = 1 } diff --git a/data/skill/runecrafting/runecrafting_altars.objs.toml b/data/skill/runecrafting/runecrafting_altars.objs.toml index 7468f05c9a..36ede3d24e 100644 --- a/data/skill/runecrafting/runecrafting_altars.objs.toml +++ b/data/skill/runecrafting/runecrafting_altars.objs.toml @@ -234,6 +234,10 @@ examine = "A mysterious power emanates from this shrine." id = 2488 examine = "A mysterious power emanates from this shrine." +[astral_altar] +id = 17010 +examine = "A mysterious power emanates from this shrine." + [blood_altar] id = 30624 examine = "A mysterious power emanates from this shrine." diff --git a/data/skill/slayer/duradel.enums.toml b/data/skill/slayer/duradel.enums.toml index f482c8ea95..21b2882060 100644 --- a/data/skill/slayer/duradel.enums.toml +++ b/data/skill/slayer/duradel.enums.toml @@ -63,7 +63,7 @@ values = { skeletal_wyvern = 5, spiritual_mage_zamorak = 10, steel_dragon = 7, -# suqah = 5, + suqah = 5, # warped_terrorbird = 9, # waterfiend = 10, } diff --git a/data/skill/slayer/kuradal.enums.toml b/data/skill/slayer/kuradal.enums.toml index 54eec08710..28f53d5285 100644 --- a/data/skill/slayer/kuradal.enums.toml +++ b/data/skill/slayer/kuradal.enums.toml @@ -70,7 +70,7 @@ values = { skeletal_wyvern = 5, spiritual_mage_zamorak = 10, steel_dragon = 9, -# suqah = 5, + suqah = 5, # terror_dog = 6, # tormented_demon = 8, tzhaar_mej = 7, diff --git a/game/src/main/kotlin/content/area/fremennik_province/lunar_isle/AstralAltar.kt b/game/src/main/kotlin/content/area/fremennik_province/lunar_isle/AstralAltar.kt new file mode 100644 index 0000000000..6e966015d4 --- /dev/null +++ b/game/src/main/kotlin/content/area/fremennik_province/lunar_isle/AstralAltar.kt @@ -0,0 +1,20 @@ +package content.area.fremennik_province.lunar_isle + +import world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.client.message +import world.gregs.voidps.engine.client.ui.open + +class AstralAltar : Script { + init { + objectOperate("Pray", "astral_altar") { + anim("altar_pray") + if (interfaces.contains("lunar_spellbook")) { + open("modern_spellbook") + message("Lunar spells deactivated!") + } else { + open("lunar_spellbook") + message("Lunar spells activated!") + } + } + } +} diff --git a/game/src/main/kotlin/content/area/fremennik_province/lunar_isle/CaptainBentley.kt b/game/src/main/kotlin/content/area/fremennik_province/lunar_isle/CaptainBentley.kt new file mode 100644 index 0000000000..11198d9176 --- /dev/null +++ b/game/src/main/kotlin/content/area/fremennik_province/lunar_isle/CaptainBentley.kt @@ -0,0 +1,49 @@ +package content.area.fremennik_province.lunar_isle + +import content.entity.player.dialogue.Neutral +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.data.definition.Areas +import world.gregs.voidps.engine.entity.character.jingle +import world.gregs.voidps.engine.entity.character.move.tele + +class CaptainBentley : Script { + init { + playerSpawn { + sendVariable("lunar_diplomacy") + } + + npcOperate("Talk-to", "captain_bentley*") { + if (tile in Areas["lunar_isle"]) { + player("Hi.") + npc("And you're wanting what now?") + choice { + option("Can you take me back to Rellekka please?") { + npc("I'll take you as far as Pirates' Cove. You'll have to find the rest of the way back yourself.") + tele(2224, 3796, 2) + } + option("So we're here?") { + npc("Yep. You're free to explore the island. Be careful though, the Moon Clan are very powerful, it wouldn't be wise to wrong them.") + player("Thanks, I'll keep that seal of passage close.") + } + } + } else { + player("Can we head to Lunar Isle?") + npc("Sure matey!") + // TODO interface 431 + jingle("sailing_theme_short") + tele(2137, 3900, 2) + } + } + + npcOperate("Travel", "captain_bentley*") { + if (tile in Areas["lunar_isle"]) { + tele(2224, 3796, 2) + } else { + tele(2137, 3900, 2) + } + } + } +} diff --git a/game/src/main/kotlin/content/area/fremennik_province/lunar_isle/Oneiromancer.kt b/game/src/main/kotlin/content/area/fremennik_province/lunar_isle/Oneiromancer.kt new file mode 100644 index 0000000000..1f9c3f4b6c --- /dev/null +++ b/game/src/main/kotlin/content/area/fremennik_province/lunar_isle/Oneiromancer.kt @@ -0,0 +1,31 @@ +package content.area.fremennik_province.lunar_isle + +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 + +class Oneiromancer : Script { + init { + npcOperate("Talk-to", "oneiromancer") { + npc("Hello there. What do you want to talk about?") + choice { + option("The state of the island.") { + player("Hi, how are things going?") + npc("Well hopefully a lot better now that you've initiated the calm between the Moon Clan and the Fremenniks. Remember, if you want to use our Lunar Spells at any time, pray at the altar beside me and you can modify") + npc("your knowledge!") + } + option("Cyrisus.") { + player("Hi.") + npc("Hello there. I hear that Cyrisus is no longer with us.") + player("He died fighting for a good cause. He'll be remembered as a great hero.") + npc("I suppose the good do die young.") + } + option("Nothing.") + } + } + } +} diff --git a/game/src/main/kotlin/content/area/fremennik_province/lunar_isle/Suqah.kt b/game/src/main/kotlin/content/area/fremennik_province/lunar_isle/Suqah.kt new file mode 100644 index 0000000000..d895ae7c5c --- /dev/null +++ b/game/src/main/kotlin/content/area/fremennik_province/lunar_isle/Suqah.kt @@ -0,0 +1,14 @@ +package content.area.fremennik_province.lunar_isle + +import content.skill.prayer.protectMagic +import content.skill.prayer.protectMelee +import content.skill.prayer.protectRange +import world.gregs.voidps.engine.Script + +class Suqah : Script { + init { + npcCondition("no_protect_melee") { !it.protectMelee() } + npcCondition("no_protect_magic") { !it.protectMagic() } + npcCondition("no_protect_range") { !it.protectRange() } + } +} diff --git a/game/src/main/kotlin/content/area/fremennik_province/pirates_cove/LokarSearunner.kt b/game/src/main/kotlin/content/area/fremennik_province/pirates_cove/LokarSearunner.kt new file mode 100644 index 0000000000..7ae96d3214 --- /dev/null +++ b/game/src/main/kotlin/content/area/fremennik_province/pirates_cove/LokarSearunner.kt @@ -0,0 +1,56 @@ +package content.area.fremennik_province.pirates_cove + +import content.entity.player.dialogue.Confused +import content.entity.player.dialogue.Happy +import content.entity.player.dialogue.Neutral +import content.entity.player.dialogue.Quiz +import content.entity.player.dialogue.type.choice +import content.entity.player.dialogue.type.npc +import content.entity.player.dialogue.type.player +import world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.data.definition.Areas +import world.gregs.voidps.engine.entity.character.move.tele + +class LokarSearunner : Script { + init { + npcOperate("Talk-to", "lokar_searunner_after") { + if (tile in Areas["pirates_cove"]) { + player("Hello again Lokar.") + val title = "Dalkar Drapare" + npc("Hi again $title! What can I do for you?") + choice { + option("Can you take me back to Rellekka?") { + player("Can you take me back to Rellekka please?") + npc("Hey, if you want to go back to loserville with all the losers, who am I to stop you?") + tele(2621, 3686) + } + option("Nothing thanks.") { + player("Nothing thanks! I just saw you here and thought I'd say hello!") + npc("Hey, I knew you seemed cool when I met you $title!") + } + } + } else { + player("Hi Lokar, can you take me back to your ship?") + npc("Sheesh, make your mind up pal, I'm not a taxi service!") + choice { + option("Go now.") { + tele(2213, 3794) + } + option("Don't go.") { + player("Actually, I've changed my mind. Again. I don't want to go.") + npc("You are possibly the most indecisive person I have ever met...") + player("Well, 'bye then.") + } + } + } + } + + npcOperate("Travel", "lokar_searunner_after") { + if (tile in Areas["pirates_cove"]) { + tele(2621, 3686) + } else { + tele(2213, 3794) + } + } + } +} diff --git a/game/src/main/kotlin/content/entity/player/command/PlayerCommands.kt b/game/src/main/kotlin/content/entity/player/command/PlayerCommands.kt index 48b07ef029..28aed4df3e 100644 --- a/game/src/main/kotlin/content/entity/player/command/PlayerCommands.kt +++ b/game/src/main/kotlin/content/entity/player/command/PlayerCommands.kt @@ -322,6 +322,7 @@ class PlayerCommands( target[quest] = "completed" } target["recipe_for_disaster"] = "completed" + target["lunar_diplomacy"] = "completed" target["quest_points"] = target["quest_points_total", 1] target.refreshQuestJournal() target.message("All quests unlocked.") diff --git a/game/src/main/kotlin/content/skill/runecrafting/Runecrafting.kt b/game/src/main/kotlin/content/skill/runecrafting/Runecrafting.kt index 94d20bea92..24f4da9fee 100644 --- a/game/src/main/kotlin/content/skill/runecrafting/Runecrafting.kt +++ b/game/src/main/kotlin/content/skill/runecrafting/Runecrafting.kt @@ -102,7 +102,7 @@ class Runecrafting : Script { } } - fun Runecrafting.bindRunes(player: Player, id: String) { + fun bindRunes(player: Player, id: String) { val xp = EnumDefinitions.intOrNull("runecrafting_xp", id) ?: return val level = EnumDefinitions.int("runecrafting_level", id) if (!player.has(Skill.Runecrafting, level, message = true)) { diff --git a/game/src/test/kotlin/content/skill/runecrafting/MysteriousRuinsTest.kt b/game/src/test/kotlin/content/skill/runecrafting/MysteriousRuinsTest.kt index e0ca792ceb..504700b07a 100644 --- a/game/src/test/kotlin/content/skill/runecrafting/MysteriousRuinsTest.kt +++ b/game/src/test/kotlin/content/skill/runecrafting/MysteriousRuinsTest.kt @@ -20,7 +20,7 @@ import kotlin.test.assertEquals internal class MysteriousRuinsTest : WorldTest() { @TestFactory - fun `Can enter ruins with talisman`() = RunecraftingTest.altars.map { (type, ruinsTile, altarTile) -> + fun `Can enter ruins with talisman`() = RunecraftingTest.altars.filter { it.ruinsTile != Tile.EMPTY }.map { (type, ruinsTile, altarTile) -> dynamicTest("Enter $type ruins with talisman") { val tile = Areas["${type}_altar_teleport"].random() val player = createPlayer(tile) @@ -39,7 +39,7 @@ internal class MysteriousRuinsTest : WorldTest() { } @TestFactory - fun `Can enter ruins with tiara`() = RunecraftingTest.altars.map { (type, ruinsTile, altarTile) -> + fun `Can enter ruins with tiara`() = RunecraftingTest.altars.filter { it.ruinsTile != Tile.EMPTY }.map { (type, ruinsTile, altarTile) -> dynamicTest("Enter $type ruins with tiara") { val tile = Areas["${type}_altar_teleport"].random() val player = createPlayer(tile) @@ -55,7 +55,7 @@ internal class MysteriousRuinsTest : WorldTest() { } @TestFactory - fun `Can enter ruins with omni tiara`() = RunecraftingTest.altars.map { (type, ruinsTile, altarTile) -> + fun `Can enter ruins with omni tiara`() = RunecraftingTest.altars.filter { it.ruinsTile != Tile.EMPTY }.map { (type, ruinsTile, altarTile) -> dynamicTest("Enter $type ruins with omni tiara") { val tile = Areas["${type}_altar_teleport"].random() val player = createPlayer(tile) @@ -70,7 +70,7 @@ internal class MysteriousRuinsTest : WorldTest() { } @TestFactory - fun `Can enter ruins with omni staff`() = RunecraftingTest.altars.map { (type, ruinsTile, altarTile) -> + fun `Can enter ruins with omni staff`() = RunecraftingTest.altars.filter { it.ruinsTile != Tile.EMPTY }.map { (type, ruinsTile, altarTile) -> dynamicTest("Enter $type ruins with omni tiara") { val tile = Areas["${type}_altar_teleport"].random() val player = createPlayer(tile) @@ -96,7 +96,7 @@ internal class MysteriousRuinsTest : WorldTest() { } @TestFactory - fun `Cannot enter ruins with no items`() = RunecraftingTest.altars.map { (type, ruinsTile, altarTile) -> + fun `Cannot enter ruins with no items`() = RunecraftingTest.altars.filter { it.ruinsTile != Tile.EMPTY }.map { (type, ruinsTile, altarTile) -> dynamicTest("Cannot enter $type ruins") { val tile = Areas["${type}_altar_teleport"].random() val player = createPlayer(tile) diff --git a/game/src/test/kotlin/content/skill/runecrafting/RunecraftingTest.kt b/game/src/test/kotlin/content/skill/runecrafting/RunecraftingTest.kt index 2ee49eed1d..6fef1e7b87 100644 --- a/game/src/test/kotlin/content/skill/runecrafting/RunecraftingTest.kt +++ b/game/src/test/kotlin/content/skill/runecrafting/RunecraftingTest.kt @@ -27,8 +27,7 @@ internal class RunecraftingTest : WorldTest() { @TestFactory fun `Craft runes with rune essence`() = altars.filter { !it.pure }.map { (type, _, altarTile) -> dynamicTest("Craft $type runes with rune essence") { - val tile = teleports.get("${type}_altar_ruins_enter", "Enter").first().to - val player = createPlayer(tile) + val player = createPlayer(altarTile.addY(-1)) player.levels.set(Skill.Runecrafting, 99) player.inventory.add("rune_essence") @@ -46,8 +45,7 @@ internal class RunecraftingTest : WorldTest() { @TestFactory fun `Cant craft high level runes with rune essence`() = altars.filter { it.pure }.map { (type, _, altarTile) -> dynamicTest("Can't craft $type runes with rune essence") { - val tile = teleports.get("${type}_altar_ruins_enter", "Enter").first().to - val player = createPlayer(tile) + val player = createPlayer(altarTile.addY(-1)) player.levels.set(Skill.Runecrafting, 99) player.inventory.add("rune_essence") @@ -65,8 +63,7 @@ internal class RunecraftingTest : WorldTest() { @TestFactory fun `Craft runes with pure essence`() = altars.map { (type, _, altarTile) -> dynamicTest("Craft $type runes with pure essence") { - val tile = teleports.get("${type}_altar_ruins_enter", "Enter").first().to - val player = createPlayer(tile) + val player = createPlayer(altarTile.addY(-1)) player.levels.set(Skill.Runecrafting, 99) player.inventory.add("pure_essence") @@ -82,7 +79,7 @@ internal class RunecraftingTest : WorldTest() { } @TestFactory - fun `Can craft multiple runes with one essence`() = altars.filter { it.type != "law" && it.type != "death" && it.type != "blood" }.map { (type, _, altarTile) -> + fun `Can craft multiple runes with one essence`() = altars.filter { it.type != "law" && it.type != "death" && it.type != "blood" && it.type != "astral" }.map { (type, _, altarTile) -> dynamicTest("Craft multiple $type runes with pure essence") { val tile = teleports.get("${type}_altar_ruins_enter", "Enter").first().to val player = createPlayer(tile) @@ -103,8 +100,7 @@ internal class RunecraftingTest : WorldTest() { @TestFactory fun `Cant craft runes without required level`() = altars.map { (type, _, altarTile) -> dynamicTest("Can't craft $type runes") { - val tile = teleports.get("${type}_altar_ruins_enter", "Enter").first().to - val player = createPlayer(tile) + val player = createPlayer(altarTile.addY(-1)) player.levels.set(Skill.Runecrafting, 0) player.inventory.add("pure_essence") @@ -135,6 +131,7 @@ internal class RunecraftingTest : WorldTest() { Altar("chaos", Tile(3059, 3590), Tile(2270, 4841), pure = true), Altar("death", Tile(1860, 4638), Tile(2204, 4835), pure = true), Altar("blood", Tile(3560, 9780), Tile(2461, 4894, 1), pure = true), + Altar("astral", Tile.EMPTY, Tile(2157, 3863), pure = true), ) } } From 0f494fe1cb91018e346c020750d772454167f9b3 Mon Sep 17 00:00:00 2001 From: GregHib Date: Fri, 20 Mar 2026 20:57:30 +0000 Subject: [PATCH 27/27] Fixes --- .../rellekka/ice_strykewyrm_cave/ice_strykewyrm_cave.npcs.toml | 1 + data/area/kandarin/feldip_hills/feldip_hills.npcs.toml | 1 + .../al_kharid/desert_strykewyrm/desert_strykewyrm.npcs.toml | 1 + .../gregs/voidps/engine/entity/character/mode/move/Movement.kt | 3 --- 4 files changed, 3 insertions(+), 3 deletions(-) diff --git a/data/area/fremennik_province/rellekka/ice_strykewyrm_cave/ice_strykewyrm_cave.npcs.toml b/data/area/fremennik_province/rellekka/ice_strykewyrm_cave/ice_strykewyrm_cave.npcs.toml index fc4c3fb5a5..16d3b7ab48 100644 --- a/data/area/fremennik_province/rellekka/ice_strykewyrm_cave/ice_strykewyrm_cave.npcs.toml +++ b/data/area/fremennik_province/rellekka/ice_strykewyrm_cave/ice_strykewyrm_cave.npcs.toml @@ -11,6 +11,7 @@ magic = 250 attack_bonus = 100 slayer_level = 93 slayer_xp = 300.0 +height = 60 drop_table = "ice_strykewyrm" categories = ["ice_strykewyrm", "strykewyrm"] examine = "I'm pretty sure there's something big under there." diff --git a/data/area/kandarin/feldip_hills/feldip_hills.npcs.toml b/data/area/kandarin/feldip_hills/feldip_hills.npcs.toml index 4f8fabcb2b..4e4b2c6679 100644 --- a/data/area/kandarin/feldip_hills/feldip_hills.npcs.toml +++ b/data/area/kandarin/feldip_hills/feldip_hills.npcs.toml @@ -94,6 +94,7 @@ magic = 150 attack_bonus = 60 slayer_level = 73 slayer_xp = 110.0 +height = 60 drop_table = "jungle_strykewyrm" categories = ["jungle_strykewyrm", "strykewyrm"] immune_poison = true diff --git a/data/area/kharidian_desert/al_kharid/desert_strykewyrm/desert_strykewyrm.npcs.toml b/data/area/kharidian_desert/al_kharid/desert_strykewyrm/desert_strykewyrm.npcs.toml index 04daf74d9c..f7d6f65efb 100644 --- a/data/area/kharidian_desert/al_kharid/desert_strykewyrm/desert_strykewyrm.npcs.toml +++ b/data/area/kharidian_desert/al_kharid/desert_strykewyrm/desert_strykewyrm.npcs.toml @@ -12,6 +12,7 @@ magic = 150 attack_bonus = 80 slayer_level = 77 slayer_xp = 120.0 +height = 60 drop_table = "desert_strykewyrm" categories = ["desert_strykewyrm", "strykewyrm"] examine = "I'm pretty sure there's something under there." diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/move/Movement.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/move/Movement.kt index 842a6bd88f..f6e66dc622 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/move/Movement.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/move/Movement.kt @@ -72,9 +72,6 @@ open class Movement( } private fun canMove(): Boolean { - if (character is NPC && (character as NPC).def.walkMode.toInt() == ModeType.EMPTY) { - return false - } if (!hasDelay() && (character as? Player)?.menu == null) { return true }