From 6b152bcc8c20e6ebaa4721817b6650c0bb5b61d7 Mon Sep 17 00:00:00 2001 From: Player Name Date: Sat, 13 Jul 2024 05:46:17 +0000 Subject: [PATCH] Wilderness improvements Added an extra option to the deep wilderness gate warning: "I wish to proceed, and don't ask me again" Reused the same warning, with appropriate changes, for the edge/ardy levers due to the pvp mechanics Removed the interface warning when crossing the ditch until doomsayer is implemented Removed the inauthentic threat revenant and restricted brawler/pvp drops to revenants and chaos elemental only Now require 100k risk to obtain the boosted deep-wildy drop rates Hid our inauthentic mechanics behind a new server config option enhanced_deep_wilderness --- .../handlers/WildernessLeverPlugin.java | 15 +- .../wilderness/handlers/DeepWildyThreat.kt | 152 ------------------ .../handlers/WildernessDitchPlugin.java | 5 + .../handlers/WildernessGateHandler.kt | 55 ++++--- Server/src/main/core/ServerConstants.kt | 3 + .../node/entity/player/link/SkullManager.java | 2 +- .../game/system/config/ServerConfigParser.kt | 1 + .../world/map/zone/impl/WildernessZone.java | 89 +++++----- Server/worldprops/default.conf | 4 +- 9 files changed, 98 insertions(+), 228 deletions(-) delete mode 100644 Server/src/main/content/region/wilderness/handlers/DeepWildyThreat.kt diff --git a/Server/src/main/content/region/kandarin/ardougne/handlers/WildernessLeverPlugin.java b/Server/src/main/content/region/kandarin/ardougne/handlers/WildernessLeverPlugin.java index 5b8c59117..117dbf5da 100644 --- a/Server/src/main/content/region/kandarin/ardougne/handlers/WildernessLeverPlugin.java +++ b/Server/src/main/content/region/kandarin/ardougne/handlers/WildernessLeverPlugin.java @@ -1,5 +1,6 @@ package content.region.kandarin.ardougne.handlers; +import core.ServerConstants; import core.cache.def.impl.SceneryDefinition; import core.plugin.Initializable; import core.game.dialogue.DialogueInterpreter; @@ -8,7 +9,6 @@ import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.TeleportManager; -import core.game.node.entity.player.link.audio.Audio; import core.game.node.scenery.Scenery; import core.game.node.scenery.SceneryBuilder; import core.game.system.task.Pulse; @@ -20,10 +20,11 @@ import core.plugin.ClassScanner; import org.rs09.consts.Sounds; import static core.api.ContentAPIKt.playAudio; +import static content.region.wilderness.handlers.WildernessGateHandlerKt.enterDeepWilderness; /** * Handles wilderness levers. - * @author 'Vexia + * @author 'Vexia, Player Name * @version 1.0 */ @Initializable @@ -279,7 +280,6 @@ public final class WildernessLeverPlugin extends OptionHandler { public Location[] getLocations() { return locations; } - } /** @@ -325,7 +325,14 @@ public final class WildernessLeverPlugin extends OptionHandler { public boolean open(Object... args) { lever = (LeverSets) args[0]; id = (int) args[1]; - interpreter.sendDialogue("Warning! Pulling the lever will teleport you deep into the wilderness."); + if (ServerConstants.ENHANCED_DEEP_WILDERNESS) { + enterDeepWilderness(player, (player) -> { + lever.pull(player, lever.getIndex(id), true); + return null; + }, "Pulling the lever will teleport you into the deep wilderness."); + } else { + interpreter.sendDialogue("Warning! Pulling the lever will teleport you deep into the wilderness."); + } return true; } diff --git a/Server/src/main/content/region/wilderness/handlers/DeepWildyThreat.kt b/Server/src/main/content/region/wilderness/handlers/DeepWildyThreat.kt deleted file mode 100644 index 929375a65..000000000 --- a/Server/src/main/content/region/wilderness/handlers/DeepWildyThreat.kt +++ /dev/null @@ -1,152 +0,0 @@ -package content.region.wilderness.handlers - -import content.region.wilderness.handlers.revenants.RevenantController -import content.region.wilderness.handlers.revenants.RevenantNPC -import content.region.wilderness.handlers.revenants.RevenantType -import core.api.* -import core.game.node.entity.Entity -import core.game.node.entity.combat.CombatStyle -import core.game.node.entity.combat.DeathTask -import core.game.node.entity.npc.NPC -import core.game.node.entity.npc.NPCBehavior -import core.game.node.entity.player.Player -import core.game.node.item.Item -import core.game.system.command.Privilege -import core.game.system.timer.PersistTimer -import core.game.system.timer.impl.Disease -import core.game.world.map.path.Pathfinder -import core.game.world.map.zone.impl.WildernessZone -import core.game.world.update.flag.context.Graphics -import core.tools.RandomFunction -import core.tools.colorize -import org.json.simple.JSONObject - -object DeepWildyThreat { - @JvmStatic fun getThreat (player: Player) : Int { - return getOrStartTimer(player).ticksLeft - } - - @JvmStatic fun adjustThreat (player: Player, amount: Int) { - val timer = getOrStartTimer(player) - timer.ticksLeft += amount - if (timer.ticksLeft >= 2500 && timer.lastMessage < 2000) { - sendMessage(player, colorize("%RYou sense a great wrath upon you.")) - timer.lastMessage = 2000 - } else if (timer.ticksLeft >= 1000 && timer.lastMessage < 1000) { - sendMessage(player, colorize("%RYou sense watchful eyes upon you.")) - timer.lastMessage = 1000 - } else if (timer.ticksLeft >= 500 && timer.lastMessage < 500) { - sendMessage(player, colorize("%RYou sense a dark presence.")) - timer.lastMessage = 500 - } - } -} - -class DWThreatTimer : PersistTimer(1, "dw-threat"), Commands { - var ticksLeft = 0 - var lastMessage = 0 - var currentRev: NPC? = null - var forceSpawn = false - - override fun run(entity: Entity): Boolean { - if (ticksLeft-- <= 0) return false - if (ticksLeft > 3000) ticksLeft = 3000 - if (ticksLeft % 5 != 0) return true - if (entity !is Player) return false - if (!entity.skullManager.isWilderness) return true - - val rollchance = - if (ticksLeft >= 2500) 10 - else if (ticksLeft >= 2000) 200 - else if (ticksLeft >= 1500) 400 - else if (ticksLeft >= 1000) 800 - else if (ticksLeft >= 500) 1500 - else 2_000_000 - - if ((currentRev == null || DeathTask.isDead(currentRev) || !currentRev!!.isActive) && (forceSpawn || RandomFunction.roll(rollchance))) { - forceSpawn = false - val type = RevenantType.getClosestHigherOrEqual(entity.properties.currentCombatLevel) - val npc = NPC.create(type.ids.random(), entity.location) - npc.isRespawn = false - npc.behavior = RevGuardianBehavior() - npc.init() - npc.setAttribute("dw-threat-target", entity) - RevenantController.unregisterRevenant(npc as RevenantNPC, false) - currentRev = npc - } else if (currentRev != null && !currentRev!!.location.withinDistance(entity.location, 25) && currentRev!!.properties.teleportLocation == null) { - poofClear(currentRev!!) - currentRev = null - } - - return true - } - - override fun save(root: JSONObject, entity: Entity) { - root["threat-time-remaining"] = ticksLeft.toString() - root["threat-forceSpawn"] = (currentRev != null).toString() - } - - override fun parse(root: JSONObject, entity: Entity) { - ticksLeft = root["threat-time-remaining"]?.toString()?.toIntOrNull() ?: 0 - forceSpawn = root["threat-forceSpawn"]?.toString()?.toBoolean() ?: false - } - - override fun defineCommands() { - define("dwthreat", Privilege.ADMIN, "", "") {player, _ -> - val timer = getOrStartTimer(player) - notify(player, "Current Threat: ${timer.ticksLeft}") - } - } -} - -class RevGuardianBehavior : NPCBehavior() { - val deathMessages = arrayOf("Curses upon thee!", "Rot in blight!", "Suffer my wrath!", "Nevermore!", "May ye be undone!") - var chats = arrayOf("Leave this place!", "Suffer!", "Death to thee!", "Flee, coward!", "Leave my resting place!", "Let me rest in peace!", "Thou belongeth to me!") - - override fun tick(self: NPC): Boolean { - val target = getAttribute(self, "dw-threat-target", null) ?: return true - if (!target.isActive || DeathTask.isDead(target)) { - self.clear() - return true - } - if (target.properties.teleportLocation != null && self.properties.teleportLocation == null) { - if (WildernessZone.isInZone(target.properties.teleportLocation)) - self.properties.teleportLocation = target.properties.teleportLocation - } - self.attack(target) - return true - } - - override fun onCreation(self: NPC) { - Graphics.send(Graphics(86), self.location) - sendChat(self, chats.random()) - } - - override fun canBeAttackedBy(self: NPC, attacker: Entity, style: CombatStyle, shouldSendMessage: Boolean): Boolean { - val target = getAttribute(self, "dw-threat-target", null) - if (attacker != target) { - if (shouldSendMessage && attacker is Player) - sendMessage(attacker, "This revenant is focused on someone else.") - return false - } - return super.canBeAttackedBy(self, attacker, style, shouldSendMessage) - } - - override fun onDeathStarted(self: NPC, killer: Entity) { - val target = getAttribute(self, "dw-threat-target", null) ?: return - sendChat(self, deathMessages.random()) - val disease = getOrStartTimer(target, 25) - disease.hitsLeft = 25 - } - - override fun onDropTableRolled(self: NPC, killer: Entity, drops: ArrayList) { - val target = getAttribute(self, "dw-threat-target", null) ?: return - if (killer != target) drops.clear() - val timer = getOrStartTimer(target) - timer.ticksLeft = 0 - } - - override fun getPathfinderOverride(self: NPC): Pathfinder? { - return Pathfinder.SMART - } -} diff --git a/Server/src/main/content/region/wilderness/handlers/WildernessDitchPlugin.java b/Server/src/main/content/region/wilderness/handlers/WildernessDitchPlugin.java index 4c93fdf39..20b1c8719 100644 --- a/Server/src/main/content/region/wilderness/handlers/WildernessDitchPlugin.java +++ b/Server/src/main/content/region/wilderness/handlers/WildernessDitchPlugin.java @@ -52,6 +52,10 @@ public final class WildernessDitchPlugin extends OptionHandler { player.faceLocation(node.getLocation()); Scenery ditch = (Scenery) node; player.setAttribute("wildy_ditch", ditch); + /* + + Comment out the annoying ditch warning until the doomsayer has been implemented so that players can disable it properly. + if(!player.isArtificial()) { if (ditch.getRotation() % 2 == 0) { if (player.getLocation().getY() <= node.getLocation().getY()) { @@ -65,6 +69,7 @@ public final class WildernessDitchPlugin extends OptionHandler { } } } + */ WildernessInterfacePlugin.handleDitch(player); } diff --git a/Server/src/main/content/region/wilderness/handlers/WildernessGateHandler.kt b/Server/src/main/content/region/wilderness/handlers/WildernessGateHandler.kt index d65982858..2f0ed6a1f 100644 --- a/Server/src/main/content/region/wilderness/handlers/WildernessGateHandler.kt +++ b/Server/src/main/content/region/wilderness/handlers/WildernessGateHandler.kt @@ -1,9 +1,10 @@ package content.region.wilderness.handlers +import content.region.wilderness.handlers.WildernessGateHandler.GateDialogue +import core.ServerConstants import core.api.* import core.game.interaction.* import core.game.node.entity.player.Player -import core.game.node.scenery.Scenery import core.game.node.Node import core.game.global.action.DoorActionHandler import core.game.dialogue.* @@ -16,39 +17,57 @@ class WildernessGateHandler : InteractionListener { on (gates, IntType.SCENERY, "open", handler = ::handleGate) } - private fun handleGate (player: Player, node: Node) : Boolean { - if (player.location.y > 3890) { - val isEntering = !player.skullManager.isDeepWilderness() - - if (isEntering) - openDialogue(player, GateDialogue(node.asScenery())) - else { - if (player.properties.combatPulse.isInCombat) + private fun handleGate(player: Player, node: Node) : Boolean { + if (player.location.y > 3890 && ServerConstants.ENHANCED_DEEP_WILDERNESS) { + val isEntering = !player.skullManager.isDeepWilderness + if (isEntering) { + fun enter(player: Player) { + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + player.skullManager.isDeepWilderness = true + } + enterDeepWilderness(player, ::enter, "Beyond this gate you enter the deep wilderness!") + } else { + if (player.properties.combatPulse.isInCombat) { sendMessage(player, "You cannot leave while you are under attack.") - else { + } else { DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) player.skullManager.isDeepWilderness = false } } - } else DoorActionHandler.handleAutowalkDoor (player, node.asScenery()) + } else DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) return true } - class GateDialogue (val gate: Scenery) : DialogueFile() { + class GateDialogue(val callback: (Player) -> Unit, val firstLine: String) : DialogueFile() { override fun handle (interfaceId: Int, buttonId: Int) { when (stage) { - 0 -> sendDialogueLines(player!!, "WARNING!", "Beyond this gate you enter the deep wilderness!", "Anyone will be able to attack you without consequence!", "You WILL NOT be able to leave during combat!").also { stage++ } - 1 -> showTopics ( + 0 -> sendDialogueLines(player!!, "WARNING!", firstLine, "Anyone will be able to attack you without consequence!", "You WILL NOT be able to leave during combat!").also { stage++ } + 1 -> sendDialogueLines(player!!, "While you are there, you will be skulled:","you will lose all your items if you die!","The skull will go away when you leave the deep wilderness.").also { stage++ } + 2 -> sendDialogueLines(player!!, "If you are risking sufficient value, the skull will become red.","This increases your chances of receiving special loot from killing","revenants and the Chaos Elemental!").also { stage++ } + 3 -> showTopics( Topic(FacialExpression.NEUTRAL, "I wish to proceed.", 10, true), - Topic(FacialExpression.NEUTRAL, "Nevermind.", END_DIALOGUE, true) + Topic(FacialExpression.NEUTRAL, "I wish to proceed, and don't show this warning again.", 11, true), + Topic(FacialExpression.NEUTRAL, "Never mind.", END_DIALOGUE, true) ) - 10 -> { + 10 -> { end() - DoorActionHandler.handleAutowalkDoor (player!!, gate) - player!!.skullManager.isDeepWilderness = true + callback(player!!) + } + 11 -> { + player!!.setAttribute("/save:skip-deep-wilderness-warning", true) + end() + callback(player!!) } } } } } + +fun enterDeepWilderness(player: Player, callback: (Player) -> Unit, firstLine: String) { + if (player.getAttribute("/save:skip-deep-wilderness-warning",false)) { + callback(player) + } else { + openDialogue(player, GateDialogue(callback, firstLine)) + } +} diff --git a/Server/src/main/core/ServerConstants.kt b/Server/src/main/core/ServerConstants.kt index 1dd988d99..b71225235 100644 --- a/Server/src/main/core/ServerConstants.kt +++ b/Server/src/main/core/ServerConstants.kt @@ -333,6 +333,9 @@ class ServerConstants { @JvmField var RUNECRAFTING_FORMULA_REVISION = 581 + @JvmField + var ENHANCED_DEEP_WILDERNESS = false + @JvmField var STARTUP_MOMENT = Calendar.getInstance() } diff --git a/Server/src/main/core/game/node/entity/player/link/SkullManager.java b/Server/src/main/core/game/node/entity/player/link/SkullManager.java index fd50bdeca..7b3f97c33 100644 --- a/Server/src/main/core/game/node/entity/player/link/SkullManager.java +++ b/Server/src/main/core/game/node/entity/player/link/SkullManager.java @@ -236,7 +236,7 @@ public final class SkullManager { } public void setDeepWilderness (boolean deepWildy) { - if(deepWildy) { + if(ServerConstants.ENHANCED_DEEP_WILDERNESS && deepWildy) { updateDWSkullIcon(); } else { removeDWSkullIcon(); diff --git a/Server/src/main/core/game/system/config/ServerConfigParser.kt b/Server/src/main/core/game/system/config/ServerConfigParser.kt index 5996a508f..266970ad8 100644 --- a/Server/src/main/core/game/system/config/ServerConfigParser.kt +++ b/Server/src/main/core/game/system/config/ServerConfigParser.kt @@ -165,6 +165,7 @@ object ServerConfigParser { ServerConstants.FORCE_CHRISTMAS_EVENTS = data.getBoolean("world.force_christmas_randoms", false) ServerConstants.FORCE_EASTER_EVENTS = data.getBoolean("world.force_easter_randoms", false) ServerConstants.RUNECRAFTING_FORMULA_REVISION = data.getLong("world.runecrafting_formula_revision", 581).toInt() + ServerConstants.ENHANCED_DEEP_WILDERNESS = data.getBoolean("world.enhanced_deep_wilderness", false) val logLevel = data.getString("server.log_level", "VERBOSE").uppercase() ServerConstants.LOG_LEVEL = parseEnumEntry(logLevel) ?: LogLevel.VERBOSE diff --git a/Server/src/main/core/game/world/map/zone/impl/WildernessZone.java b/Server/src/main/core/game/world/map/zone/impl/WildernessZone.java index 1eaf60bb1..c06ba08ce 100644 --- a/Server/src/main/core/game/world/map/zone/impl/WildernessZone.java +++ b/Server/src/main/core/game/world/map/zone/impl/WildernessZone.java @@ -2,7 +2,6 @@ package core.game.world.map.zone.impl; import content.global.handlers.item.equipment.brawling_gloves.BrawlingGloves; import content.global.skill.summoning.familiar.Familiar; -import content.region.wilderness.handlers.DeepWildyThreat; import core.game.component.Component; import core.game.interaction.Option; import core.game.node.Node; @@ -23,6 +22,7 @@ import core.game.world.map.zone.ZoneBorders; import core.game.world.map.zone.ZoneRestriction; import core.game.world.repository.Repository; import core.tools.RandomFunction; +import org.rs09.consts.Items; import org.rs09.consts.NPCs; @@ -41,8 +41,8 @@ public final class WildernessZone extends MapZone { */ private static final WildernessZone INSTANCE = new WildernessZone(new ZoneBorders(2944, 3525, 3400, 3975), new ZoneBorders(3070, 9924, 3135, 10002), ZoneBorders.forRegion(12192), ZoneBorders.forRegion(12193), ZoneBorders.forRegion(11937)); - public static final String WILDERNESS_PROT_ATTR = "/save:wilderness-protection-active"; - public static final String WILDERNESS_HIGHER_NEXTFEE = "/save:wilderness-higher-next-fee"; + public static final String WILDERNESS_PROT_ATTR = "/save:wilderness-protection-active"; + public static final String WILDERNESS_HIGHER_NEXTFEE = "/save:wilderness-higher-next-fee"; /** * The zone borders. @@ -92,50 +92,37 @@ public final class WildernessZone extends MapZone { } private void rollWildernessExclusiveLoot(Entity e, Entity killer) { - if (!(killer instanceof Player)) return; + if (!(killer instanceof Player)) return; + if (!(e instanceof NPC)) return; + if (!(e.getId() == NPCs.CHAOS_ELEMENTAL_3200 || e.asNpc().getName().contains("Revenant"))) return; - boolean isDeepWildy = ((Player) killer).getSkullManager().isDeepWilderness(); - boolean isRevOrCele = e.asNpc().getName().contains("Revenant") || e.getId() == NPCs.CHAOS_ELEMENTAL_3200; - boolean isSufficientRisk = ((Player) killer).getAttribute("deepwild-value-risk", 0L) > SkullManager.DEEP_WILD_DROP_RISK_THRESHOLD; - boolean isValidTarget = e instanceof NPC && ((isDeepWildy && isSufficientRisk) || isRevOrCele); + int pvpGearRate = getNewDropRate(e.asNpc().getDefinition().getCombatLevel()); + boolean higherRate = ((Player) killer).getSkullManager().isDeepWilderness() && ((Player) killer).getAttribute("deepwild-value-risk", 0L) > SkullManager.DEEP_WILD_DROP_RISK_THRESHOLD; + if (higherRate) { + pvpGearRate /= 2; + } + int cEleGloveRate = 50; + int normalGloveRate = higherRate ? 100 : (int) ((1.0 / (1.0 - Math.pow(1.0 - (1.0 / (double) pvpGearRate), 16.0))) * 5.0 / 6.0); - if (isDeepWildy) { - DeepWildyThreat.adjustThreat((Player) killer, 50); + if (RandomFunction.roll(e.getId() == NPCs.CHAOS_ELEMENTAL_3200 ? cEleGloveRate : normalGloveRate)) { + byte glove = (byte) RandomFunction.random(1, 14); + Item reward = new Item(BrawlingGloves.forIndicator(glove).getId()); + GroundItemManager.create(reward, e.asNpc().getDropLocation(), killer.asPlayer()); + Repository.sendNews(killer.getUsername() + " has received " + reward.getName().toLowerCase() + " from a " + e.asNpc().getName() + "!"); + } + for (int j : PVP_GEAR) { + boolean chance = RandomFunction.roll(pvpGearRate); + if (chance) { + Item reward; + if (j == Items.MORRIGANS_JAVELIN_13879 || j == Items.MORRIGANS_THROWING_AXE_13883) { + reward = new Item(j, RandomFunction.random(15, 50)); + } else { + reward = new Item(j); } - - if (!isValidTarget) return; - - int pvpGearRate = getNewDropRate(e.asNpc().getDefinition().getCombatLevel()); - if (isDeepWildy && isRevOrCele) - pvpGearRate /= 2; - - int cEleGloveRate = isDeepWildy ? 50 : 150; - int normalGloveRate = isDeepWildy && isRevOrCele ? 100 : (int)((1.0/(1.0-Math.pow(1.0 - (1.0/(double)pvpGearRate), 16.0))) * 5.0 / 6.0); - - if (RandomFunction.roll(e.getId() == NPCs.CHAOS_ELEMENTAL_3200 ? cEleGloveRate : normalGloveRate)) { - byte glove = (byte) RandomFunction.random(1, 14); - Item reward = new Item(BrawlingGloves.forIndicator(glove).getId()); - GroundItemManager.create(reward, e.asNpc().getDropLocation(), killer.asPlayer()); - Repository.sendNews(killer.getUsername() + " has received " + reward.getName().toLowerCase() + " from a " + e.asNpc().getName() + "!"); - if (isDeepWildy) - DeepWildyThreat.adjustThreat((Player) killer, 750); - } - - for (int j : PVP_GEAR) { - boolean chance = RandomFunction.roll(pvpGearRate); - if (chance) { - Item reward; - if (j == 13879 || j == 13883) { // checks if it's a javelin or throwing axe - reward = new Item(j, RandomFunction.random(15, 50)); - } else { - reward = new Item(j); - } - Repository.sendNews(killer.asPlayer().getUsername() + " has received a " + reward.getName() + " from a " + e.asNpc().getName() + "!"); - GroundItemManager.create(reward, ((NPC) e).getDropLocation(), killer.asPlayer()); - if (isDeepWildy) - DeepWildyThreat.adjustThreat((Player) killer, 3000); - } - } + GroundItemManager.create(reward, ((NPC) e).getDropLocation(), killer.asPlayer()); + Repository.sendNews(killer.asPlayer().getUsername() + " has received a " + reward.getName() + " from a " + e.asNpc().getName() + "!"); + } + } } /** @@ -156,8 +143,6 @@ public final class WildernessZone extends MapZone { return super.interact(e, target, option); } - - @Override public boolean enter(Entity e) { if (e instanceof Player) { @@ -324,12 +309,12 @@ public final class WildernessZone extends MapZone { * @return the level. */ public static int getWilderness(Entity e) { - int y = e.getLocation().getY(); - if(6400 < y) { - return ((y - 9920) / 8) + 1; - } else { - return ((y - 3520) / 8) + 1; - } + int y = e.getLocation().getY(); + if(6400 < y) { + return ((y - 9920) / 8) + 1; + } else { + return ((y - 3520) / 8) + 1; + } } /** diff --git a/Server/worldprops/default.conf b/Server/worldprops/default.conf index 3a8310e91..ef20fdf8d 100644 --- a/Server/worldprops/default.conf +++ b/Server/worldprops/default.conf @@ -102,8 +102,10 @@ holiday_event_randoms = true #force holiday randoms (can only force one at a time) force_halloween_randoms = false force_christmas_randoms = false -# runecrafting formula revision (573 introduced probabilistic multiple runes, 581 extrapolated probabilistic runes past 99) +#runecrafting formula revision (573 introduced probabilistic multiple runes, 581 extrapolated probabilistic runes past 99) runecrafting_formula_revision = 581 +#enable the enhanced deep wilderness, where the area past the members' fence applies a red skull that boosts brawler/pvp drop rates +enhanced_deep_wilderness = true [paths] #path to the data folder, which contains the cache subfolder and such