From fecb24365024b8c2e63554172ca40d049d5cc1e3 Mon Sep 17 00:00:00 2001 From: oftheshire Date: Sun, 21 Jun 2026 08:03:39 +0000 Subject: [PATCH] Implemented swamp decay --- .../morytania/handlers/MorytaniaListeners.kt | 27 +++- .../handlers/MoyrtniaSwampPlugin.java | 30 ---- .../morytania/handlers/SwampDecayHandler.kt | 148 ++++++++++++++++++ 3 files changed, 174 insertions(+), 31 deletions(-) delete mode 100644 Server/src/main/content/region/morytania/handlers/MoyrtniaSwampPlugin.java create mode 100644 Server/src/main/content/region/morytania/handlers/SwampDecayHandler.kt diff --git a/Server/src/main/content/region/morytania/handlers/MorytaniaListeners.kt b/Server/src/main/content/region/morytania/handlers/MorytaniaListeners.kt index 0cd82feda..254bbd4a0 100644 --- a/Server/src/main/content/region/morytania/handlers/MorytaniaListeners.kt +++ b/Server/src/main/content/region/morytania/handlers/MorytaniaListeners.kt @@ -14,6 +14,8 @@ import core.game.interaction.InteractionListener import core.game.interaction.IntType import kotlin.random.Random import content.data.Quests +import core.game.interaction.InterfaceListener +import org.rs09.consts.Components /** * File to be used for anything Morytania related. @@ -40,12 +42,15 @@ class MorytaniaListeners : InteractionListener { on(SWAMP_GATES, IntType.SCENERY, "open"){ player, node -> if(player.location.y == 3457){ core.game.global.action.DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + sendMessage(player, "You skip gladly out of murky Mort Myre.") + removeTimer(player) GlobalScope.launch { findLocalNPC(player, NPCs.ULIZIUS_1054)?.sendChat("Oh my! You're still alive!", 2) } } else { if (player.questRepository.hasStarted(Quests.NATURE_SPIRIT)) { - core.game.global.action.DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + setAttribute(player, "swampgate", node) + openInterface(player, Components.CWS_WARNING_20_580) } else { sendNPCDialogue( player, @@ -97,4 +102,24 @@ class MorytaniaListeners : InteractionListener { return@on true } } +} + +class warningInterface : InterfaceListener { + override fun defineInterfaceListeners() { + on(Components.CWS_WARNING_20_580) { player, _, _, buttonID, _, _ -> + when(buttonID) { + 17 -> { + val gate = getAttribute(player, "swampgate", null) as? core.game.node.scenery.Scenery + if (gate != null) { + core.game.global.action.DoorActionHandler.handleAutowalkDoor(player, gate) + sendMessage(player, "You walk into the gloomy atmosphere of Mort Myre.") + } + closeInterface(player) + } + 18 -> closeInterface(player) + } + removeAttribute(player, "swampgate") + return@on true + } + } } \ No newline at end of file diff --git a/Server/src/main/content/region/morytania/handlers/MoyrtniaSwampPlugin.java b/Server/src/main/content/region/morytania/handlers/MoyrtniaSwampPlugin.java deleted file mode 100644 index 69e663f47..000000000 --- a/Server/src/main/content/region/morytania/handlers/MoyrtniaSwampPlugin.java +++ /dev/null @@ -1,30 +0,0 @@ -package content.region.morytania.handlers; - -import core.cache.def.impl.SceneryDefinition; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.player.Player; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the mortynia swamp plugin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class MoyrtniaSwampPlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.forId(3506).getHandlers().put("option:open", this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - player.getDialogueInterpreter().sendDialogue("There's a message attached to this gate, it reads:~", "~ Mort Myre is a dangerous Ghast infested swamp. ~", "~ Do not enter if you value your life. ~", "~ All persons wishing to enter must see Drezel. ~"); - return true; - } - -} diff --git a/Server/src/main/content/region/morytania/handlers/SwampDecayHandler.kt b/Server/src/main/content/region/morytania/handlers/SwampDecayHandler.kt new file mode 100644 index 000000000..995294730 --- /dev/null +++ b/Server/src/main/content/region/morytania/handlers/SwampDecayHandler.kt @@ -0,0 +1,148 @@ +package content.region.morytania.handlers + +import core.api.* +import core.game.node.entity.Entity +import core.game.node.entity.player.Player +import core.game.system.timer.PersistTimer +import core.game.system.timer.RSTimer +import core.game.system.timer.TimerFlag +import core.game.world.map.zone.ZoneBorders +import org.rs09.consts.Items +import kotlin.random.Random + + +/** + * The area for swamp decay + */ +class SwampDecayArea : MapArea { + override fun defineAreaBorders(): Array { + return arrayOf( + ZoneBorders(3392, 3328, 3519, 3455) + ) + } + + /** + * Sources: + * General mechanic: https://runescape.wiki/w/Swamp_decay + * Historic damage: https://runescape.wiki/w/Mort_Myre_Swamp?oldid=1961846 + * Precise area: https://oldschool.runescape.wiki/w/Swamp_decay + * Video: https://www.youtube.com/watch?v=gkw959x8Q6s + * Video: https://www.youtube.com/watch?v=N86RywmEyEU + */ + + override fun areaEnter(entity: Entity) { + if (entity is Player) { + getOrStartTimer(entity) + } + super.areaEnter(entity) + } + +} + +/** + * The timer for swamp decay + */ +class SwampDecayTimer : PersistTimer (200, "swampdecay", isSoft = true, flags = arrayOf(TimerFlag.ClearOnDeath)) { + companion object { + // Silver sickle, Rod of Ivandis, or Ivandis Flail in equip or inv will protect the player. + private val PROTECTIVE_ITEMS = intArrayOf( + Items.SILVER_SICKLEB_2963, + Items.ROD_OF_IVANDIS10_7639, + Items.ROD_OF_IVANDIS9_7640, + Items.ROD_OF_IVANDIS8_7641, + Items.ROD_OF_IVANDIS7_7642, + Items.ROD_OF_IVANDIS6_7643, + Items.ROD_OF_IVANDIS5_7644, + Items.ROD_OF_IVANDIS4_7645, + Items.ROD_OF_IVANDIS3_7646, + Items.ROD_OF_IVANDIS2_7647, + Items.ROD_OF_IVANDIS1_7648, + Items.IVANDIS_FLAIL_30_13117, + Items.IVANDIS_FLAIL_29_13118, + Items.IVANDIS_FLAIL28_13119, + Items.IVANDIS_FLAIL_27_13120, + Items.IVANDIS_FLAIL_26_13121, + Items.IVANDIS_FLAIL_25_13122, + Items.IVANDIS_FLAIL_24_13123, + Items.IVANDIS_FLAIL_23_13124, + Items.IVANDIS_FLAIL_22_13125, + Items.IVANDIS_FLAIL_21_13126, + Items.IVANDIS_FLAIL_20_13127, + Items.IVANDIS_FLAIL_19_13128, + Items.IVANDIS_FLAIL_18_13129, + Items.IVANDIS_FLAIL_17_13130, + Items.IVANDIS_FLAIL_16_13131, + Items.IVANDIS_FLAIL_15_13132, + Items.IVANDIS_FLAIL_14_13133, + Items.IVANDIS_FLAIL_13_13134, + Items.IVANDIS_FLAIL_12_13135, + Items.IVANDIS_FLAIL_11_13136, + Items.IVANDIS_FLAIL_10_13137, + Items.IVANDIS_FLAIL_9_13138, + Items.IVANDIS_FLAIL_8_13139, + Items.IVANDIS_FLAIL_7_13140, + Items.IVANDIS_FLAIL_6_13141, + Items.IVANDIS_FLAIL_5_13142, + Items.IVANDIS_FLAIL_4_13143, + Items.IVANDIS_FLAIL_3_13144, + Items.IVANDIS_FLAIL_2_13145, + Items.IVANDIS_FLAIL_1_13146 + ) + } + + // returning false stops the timer. returning true keeps it running. + override fun run(entity: Entity): Boolean { + if (entity !is Player) return false + + // player is outside the zone + if (entity.location.x !in 3392..3519 || entity.location.y !in 3328..3455) { + sendMessage(entity, "The swamp decay effect is now over.") + return false + } + + // the nature spirit camp zone is safe + if (entity.location.x in 3435..3445 && entity.location.y in 3331..3442) { + sendMessage(entity, "The aura of Filliman's camp protects you from the swamp.") + return true + } + + // protected + if (hasProtection(entity)) { + return true + } + + // decayed (2-4 random damage) + sendMessage(entity, "The swamp decays you!") + impact(entity, Random.nextInt(2, 5)) + visualize(entity, -1, 255) + return true + } + + override fun onRegister(entity: Entity) { + if (entity !is Player) return + } + + override fun getTimer(vararg args: Any): RSTimer { + val t = SwampDecayTimer() + t.runInterval = args.getOrNull(0) as? Int ?: 200 + return t + } + + // always remember your protection + private fun hasProtection(player: Player): Boolean { + for (itemId in PROTECTIVE_ITEMS) { + if (inEquipmentOrInventory(player, itemId)) { + val prot = when(itemId) { + 2963 -> "blessed silver sickle" + in 7639..7648 -> "rod of Ivandis" + in 13117..13146 -> "Ivandis flail" + else -> "item" // fallback + } + sendMessage(player, "The power of the $prot prevents the swamp decay.") + visualize(player, -1, 259) + return true + } + } + return false + } +} \ No newline at end of file