From d5527c268fe9c30a9b1e6a81b2196ed63a78d6d2 Mon Sep 17 00:00:00 2001 From: Bishop Date: Sun, 26 Jul 2026 11:59:12 +0000 Subject: [PATCH 1/4] Removed inauthentic fight caves heal --- .../karamja/tzhaar/handlers/TzhaarFightCavesPlugin.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Server/src/main/content/region/karamja/tzhaar/handlers/TzhaarFightCavesPlugin.java b/Server/src/main/content/region/karamja/tzhaar/handlers/TzhaarFightCavesPlugin.java index 89a8d5e38..5a4cdfd11 100644 --- a/Server/src/main/content/region/karamja/tzhaar/handlers/TzhaarFightCavesPlugin.java +++ b/Server/src/main/content/region/karamja/tzhaar/handlers/TzhaarFightCavesPlugin.java @@ -145,11 +145,6 @@ public final class TzhaarFightCavesPlugin extends ActivityPlugin { public void leave(Player player, int wave) { activeNPCs.clear(); player.getProperties().setTeleportLocation(getSpawnLocation()); - player.getSkills().restore(); - player.timers.removeTimer("poison"); - player.timers.removeTimer("disease"); - player.timers.removeTimer("teleblock"); - player.getSettings().updateRunEnergy(-100); boolean practice = player.getAttribute("fc_practice_jad", false); if (wave == 63) { if (!practice) { From e611b1780bdf0ec3949d8ffca28b9613f887e580 Mon Sep 17 00:00:00 2001 From: dam <27978131-real_damighty@users.noreply.gitlab.com> Date: Sun, 26 Jul 2026 15:18:14 +0300 Subject: [PATCH 2/4] Fixed dream spell animation loop --- .../skill/magic/lunar/LunarListeners.kt | 33 ++++++++++++++----- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/Server/src/main/content/global/skill/magic/lunar/LunarListeners.kt b/Server/src/main/content/global/skill/magic/lunar/LunarListeners.kt index 8d70f99cb..fd8dc5ccd 100644 --- a/Server/src/main/content/global/skill/magic/lunar/LunarListeners.kt +++ b/Server/src/main/content/global/skill/magic/lunar/LunarListeners.kt @@ -37,6 +37,10 @@ import org.rs09.consts.Items import org.rs09.consts.Sounds import kotlin.math.floor +private const val DREAM_HEAL_INTERVAL_TICKS = 50 +private const val DREAM_HARD_RESET_INTERVAL_TICKS = 90 +private const val DREAM_HARD_RESET_DELAY_TICKS = 0 + class LunarListeners : SpellListener("lunar"), Commands { override fun defineListeners() { @@ -592,32 +596,46 @@ class LunarListeners : SpellListener("lunar"), Commands { // Level 79 private fun dream(player: Player) { - if(player.skills.lifepoints >= getStatLevel(player, Skills.HITPOINTS)) { + if (player.skills.lifepoints >= getStatLevel(player, Skills.HITPOINTS)) { sendMessage(player, "You have no need to cast this spell since your hitpoints are already full.") throw IllegalStateException() } - // https://runescape.wiki/w/Dream?oldid=880976 claims Dream makes you heal 1 hp every 20 seconds // https://oldschool.runescape.wiki/w/Dream claims that Dream has its own timer, so the Dream heals don't need // to align with the natural heals val timer = getOrStartTimer(player) + val dreamStartAnimationTicks = animationDuration(Animation(Animations.LUNAR_SPELLBOOK_DREAM_START_6295)) + var nextHealTick = getWorldTicks() + dreamStartAnimationTicks + DREAM_HEAL_INTERVAL_TICKS + var nextHardResetTick = -1 + var waitingForHardReset = false animate(player, Animations.LUNAR_SPELLBOOK_DREAM_START_6295) removeRunes(player, true) addXP(player, 82.0) - delayEntity(player, 4) - queueScript(player, 4, QueueStrength.WEAK) { stage: Int -> + delayEntity(player, dreamStartAnimationTicks) + queueScript(player, dreamStartAnimationTicks, QueueStrength.WEAK) { stage: Int -> if (stage == 0) { sendGraphics(Graphics.LUNAR_SPELLBOOK_DREAM_1056, player.location) playAudio(player, Sounds.LUNAR_SLEEP_3619) return@queueScript delayScript(player, 5) } + if (waitingForHardReset) { + waitingForHardReset = false + nextHardResetTick = getWorldTicks() + DREAM_HARD_RESET_INTERVAL_TICKS + } else if (nextHardResetTick != -1 && getWorldTicks() >= nextHardResetTick) { + // The 530 client eventually drops Dream back to idle if it only sees repeated 6296 updates + // Sending an explicit reset makes the next 6296 a full restart instead of a same-sequence refresh + resetAnimator(player) + waitingForHardReset = true + return@queueScript delayScript(player, DREAM_HARD_RESET_DELAY_TICKS) + } else if (nextHardResetTick == -1) { + nextHardResetTick = getWorldTicks() + DREAM_HARD_RESET_INTERVAL_TICKS + } animate(player, Animations.LUNAR_SPELLBOOK_DREAM_MID_6296) sendGraphics(Graphics.LUNAR_SPELLBOOK_DREAM_1056, player.location) - // This heals 2 HP every min. Naturally you heal 1 for a total of 3 - // The script steps every 5 ticks and we want 50 ticks before a heal - if (stage.mod(10) == 0) { + if (getWorldTicks() >= nextHealTick) { val amt = timer.getHealAmount(player) //accounts for regen brace heal(player, amt) + nextHealTick += DREAM_HEAL_INTERVAL_TICKS if (player.skills.lifepoints >= getStatLevel(player, Skills.HITPOINTS)) { animate(player, Animations.LUNAR_SPELLBOOK_DREAM_END_6297) return@queueScript stopExecuting(player) @@ -874,4 +892,3 @@ class LunarListeners : SpellListener("lunar"), Commands { } - From a643299b0c2a87e70a1c9bcd64289c1cb5d71a90 Mon Sep 17 00:00:00 2001 From: dam <27978131-real_damighty@users.noreply.gitlab.com> Date: Sun, 26 Jul 2026 15:29:51 +0300 Subject: [PATCH 3/4] Added admin command ::infinitespecialmove [true|false] for infinite summoning familiar special move energy Providing no argument to admin commands ::infinitespecial and ::allowaggro now toggles the value instead of rejecting --- .../skill/summoning/familiar/Familiar.java | 6 +- .../system/command/sets/MiscCommandSet.kt | 82 ++++++++++++++----- 2 files changed, 67 insertions(+), 21 deletions(-) diff --git a/Server/src/main/content/global/skill/summoning/familiar/Familiar.java b/Server/src/main/content/global/skill/summoning/familiar/Familiar.java index b561a5b30..089db9352 100644 --- a/Server/src/main/content/global/skill/summoning/familiar/Familiar.java +++ b/Server/src/main/content/global/skill/summoning/familiar/Familiar.java @@ -82,6 +82,8 @@ public abstract class Familiar extends NPC implements Plugin { * The amount of special points left. */ protected int specialPoints = 60; + + public static final String INFINITE_SPECIAL_MOVE_ATTRIBUTE = "infinite-special-move"; /** * The pouch id. @@ -494,7 +496,9 @@ public abstract class Familiar extends NPC implements Plugin { owner.getInventory().remove(new Item(scroll.getItemId())); playAudio(owner, Sounds.SPELL_4161); visualizeSpecialMove(); - updateSpecialPoints(specialCost); + if (!owner.getAttribute(INFINITE_SPECIAL_MOVE_ATTRIBUTE, false)) { + updateSpecialPoints(specialCost); + } owner.getSkills().addExperience(Skills.SUMMONING, scroll.getExperience(), true); } return true; diff --git a/Server/src/main/core/game/system/command/sets/MiscCommandSet.kt b/Server/src/main/core/game/system/command/sets/MiscCommandSet.kt index 281491ae2..0727f65db 100644 --- a/Server/src/main/core/game/system/command/sets/MiscCommandSet.kt +++ b/Server/src/main/core/game/system/command/sets/MiscCommandSet.kt @@ -1,6 +1,7 @@ package core.game.system.command.sets import content.global.handlers.iface.* +import content.global.skill.summoning.familiar.Familiar import content.global.skill.cooking.fermenting.BrewGrowth import content.global.skill.farming.timers.* import content.minigame.fishingtrawler.TrawlerLoot @@ -40,6 +41,27 @@ import java.awt.HeadlessException import java.awt.Toolkit import java.awt.datatransfer.StringSelection +private const val INFINITE_SPECIAL_ATTRIBUTE = "infinite-special" +private const val ALLOW_ADMIN_AGGRESSION_ATTRIBUTE = "/save:allow_admin_aggression" + +private fun resolveBooleanToggle(player: Player, args: Array, attribute: String): Boolean? { + return when { + args.size < 2 -> !player.getAttribute(attribute, false) + args[1].equals("true", ignoreCase = true) -> true + args[1].equals("false", ignoreCase = true) -> false + else -> null + } +} + +private fun setBooleanAttribute(player: Player, attribute: String, enabled: Boolean, label: String) { + if (enabled) { + setAttribute(player, attribute, true) + } else { + removeAttribute(player, attribute) + } + sendMessage(player, "$label is now ${if (enabled) "enabled" else "disabled"}.") +} + @Initializable class MiscCommandSet : CommandSet(Privilege.ADMIN){ override fun defineCommands() { @@ -662,29 +684,49 @@ class MiscCommandSet : CommandSet(Privilege.ADMIN){ notify(player,"No parent NPC found.") } } - define("infinitespecial", Privilege.ADMIN, "::infinitespecial ", "Turns the infinite special-attack flag on or off for your player."){ player, args -> - val usageStr = "Usage: ::infinitespecial true|false" - if(args.size < 2){ - reject(player, usageStr) - } - when(args[1]){ - "true" -> player.setAttribute("infinite-special", true) - "false" -> player.removeAttribute("infinite-special") - else -> reject(player, usageStr) - } - } - define("allowaggro", Privilege.ADMIN, "allowaggro true | false", "Toggle NPCs aggroing on you") { player, args -> - val usageStr = "Usage: ::allowaggro true | false" - if(args.size < 2) { - notify(player, "Allow admin aggression is currently ${player.getAttribute("/save:allow_admin_aggression", false)}") + define( + "infinitespecial", + Privilege.ADMIN, + "::infinitespecial [true|false]", + "Toggles infinite special attack energy for your player (special-attack flag)." + ) { player, args -> + val enabled = resolveBooleanToggle(player, args, INFINITE_SPECIAL_ATTRIBUTE) + if (enabled == null) { + reject(player, "Usage: ::infinitespecial [true|false]") return@define } - when(args[1]) { - "true" -> player.setAttribute("/save:allow_admin_aggression", true) - "false" -> player.setAttribute("/save:allow_admin_aggression", false) - else -> reject(player, usageStr) + setBooleanAttribute(player, INFINITE_SPECIAL_ATTRIBUTE, enabled, "Infinite special attack") + } + define( + "infinitespecialmove", + Privilege.ADMIN, + "::infinitespecialmove [true|false]", + "Toggles infinite Summoning familiar special move points for your player." + ) { player, args -> + val enabled = resolveBooleanToggle(player, args, Familiar.INFINITE_SPECIAL_MOVE_ATTRIBUTE) + if (enabled == null) { + reject(player, "Usage: ::infinitespecialmove [true|false]") + return@define } - notify(player, "Setting aggro ${args[1]}") + setBooleanAttribute( + player, + Familiar.INFINITE_SPECIAL_MOVE_ATTRIBUTE, + enabled, + "Infinite familiar special move energy" + ) + } + define( + "allowaggro", + Privilege.ADMIN, + "::allowaggro [true|false]", + "Toggles NPCs aggroing on you as an admin." + ) { player, args -> + val enabled = resolveBooleanToggle(player, args, ALLOW_ADMIN_AGGRESSION_ATTRIBUTE) + if (enabled == null) { + reject(player, "Usage: ::allowaggro [true|false]") + return@define + } + setBooleanAttribute(player, ALLOW_ADMIN_AGGRESSION_ATTRIBUTE, enabled, "Admin NPC aggression") } define("rules", Privilege.STANDARD, "", "Shows the rules."){ player, _ -> From a6699243dbd6222a84b97c4da651d4d2fb20772e Mon Sep 17 00:00:00 2001 From: Tooze Date: Sun, 26 Jul 2026 12:37:41 +0000 Subject: [PATCH 4/4] Agility arena fixes Fixed looping when a player attempted dart trap, spinning blades, floor spikes and pressure pads Fixed direction player faces when traversing hand holds Fixed animation for walking across planks --- .../content/global/skill/agility/AgilityHandler.java | 10 ++++++++++ .../global/skill/agility/brimhaven/BladeTrap.java | 7 +++++++ .../skill/agility/brimhaven/BrimhavenCourse.java | 11 +++++++---- .../global/skill/agility/brimhaven/DartTrap.java | 5 +++++ .../global/skill/agility/brimhaven/FloorSpikes.java | 6 ++++++ .../global/skill/agility/brimhaven/PressurePad.java | 7 +++++++ .../skill/agility/brimhaven/SpinningBlades.java | 8 +++++++- 7 files changed, 49 insertions(+), 5 deletions(-) diff --git a/Server/src/main/content/global/skill/agility/AgilityHandler.java b/Server/src/main/content/global/skill/agility/AgilityHandler.java index 722de4afe..c12b7d4fc 100644 --- a/Server/src/main/content/global/skill/agility/AgilityHandler.java +++ b/Server/src/main/content/global/skill/agility/AgilityHandler.java @@ -139,6 +139,13 @@ public final class AgilityHandler { * @return The force movement instance, if force movement is used. */ public static ForceMovement forceWalk(final Player player, final int courseIndex, Location start, Location end, Animation animation, int speed, final double experience, final String message) { + return forceWalk(player, courseIndex, start, end, animation, speed, experience, message, null); + } + + /** + * Walks across an obstacle while using an explicit force-movement facing direction. + */ + public static ForceMovement forceWalk(final Player player, final int courseIndex, Location start, Location end, Animation animation, int speed, final double experience, final String message, Direction direction) { player.logoutListeners.put("forcewalk", p -> { p.setLocation(player.getLocation().transform(0,0,0)); return Unit.INSTANCE; @@ -158,6 +165,9 @@ public final class AgilityHandler { player.logoutListeners.remove("forcewalk"); } }; + if (direction != null) { + movement.setDirection(direction); + } movement.start(); GameWorld.getPulser().submit(movement); return movement; diff --git a/Server/src/main/content/global/skill/agility/brimhaven/BladeTrap.java b/Server/src/main/content/global/skill/agility/brimhaven/BladeTrap.java index 773cd9907..5303bde7d 100644 --- a/Server/src/main/content/global/skill/agility/brimhaven/BladeTrap.java +++ b/Server/src/main/content/global/skill/agility/brimhaven/BladeTrap.java @@ -5,6 +5,9 @@ import core.game.node.entity.Entity; import core.game.node.entity.player.Player; import core.game.system.task.MovementHook; import core.game.system.task.Pulse; +import core.net.packet.PacketRepository; +import core.net.packet.context.PlayerContext; +import core.net.packet.out.ClearMinimapFlag; import kotlin.Unit; import core.game.world.GameWorld; import core.game.world.map.Direction; @@ -24,6 +27,10 @@ public final class BladeTrap implements MovementHook { final Direction dir = e.getDirection(); final Player player = (Player) e; final Location start = l.transform(-dir.getStepX(), -dir.getStepY(), 0); + player.getPulseManager().clear(); + player.getWalkingQueue().reset(); + PacketRepository.send(ClearMinimapFlag.class, new PlayerContext(player)); + e.lock(5); if(e.isPlayer()) { diff --git a/Server/src/main/content/global/skill/agility/brimhaven/BrimhavenCourse.java b/Server/src/main/content/global/skill/agility/brimhaven/BrimhavenCourse.java index 4fb8bdb52..adb8f14fd 100644 --- a/Server/src/main/content/global/skill/agility/brimhaven/BrimhavenCourse.java +++ b/Server/src/main/content/global/skill/agility/brimhaven/BrimhavenCourse.java @@ -119,6 +119,8 @@ public final class BrimhavenCourse extends OptionHandler { final Direction dir = direction; final Direction faceDirection = Direction.get(object.getDirection().toInteger() - 1 & 3); final Location start = player.getLocation(); + player.setDirection(faceDirection); + player.faceLocation(start.transform(faceDirection)); player.getAppearance().setAnimations(Animation.create(1118 + m)); player.getAppearance().sync(); AgilityHandler.climb(player, -1, new Animation(1117 + m), start.transform(dir), 0.0, null); @@ -143,12 +145,12 @@ public final class BrimhavenCourse extends OptionHandler { } else if (count == 6) { player.getAppearance().setAnimations(); player.getAppearance().sync(); - AgilityHandler.forceWalk(player, -1, last, last.transform(dir), Animation.create(1120 + m), 5, getExp(player, 22.0), null).setDirection(faceDirection); + AgilityHandler.forceWalk(player, -1, last, last.transform(dir), Animation.create(1120 + m), 5, getExp(player, 22.0), null, faceDirection); player.logoutListeners.remove("brimcourse"); return true; } player.logoutListeners.remove("brimcourse"); - AgilityHandler.forceWalk(player, -1, last, last = last.transform(dir), Animation.create(1118 + m), 5, 0.0, null).setDirection(faceDirection); + AgilityHandler.forceWalk(player, -1, last, last = last.transform(dir), Animation.create(1118 + m), 5, 0.0, null, faceDirection); return false; } }); @@ -272,7 +274,7 @@ public final class BrimhavenCourse extends OptionHandler { public boolean pulse() { if (!finish) { setDelay(2); - AgilityHandler.fail(player, 1, player.getLocation().transform(0, 0, -3), Animation.create(189), getHitAmount(player), "You stepped on a broken piece of plank!"); + AgilityHandler.fail(player, 1, player.getLocation().transform(0, 0, -3), Animation.create(768), getHitAmount(player), "You stepped on a broken piece of plank!"); finish = true; return false; } @@ -282,7 +284,7 @@ public final class BrimhavenCourse extends OptionHandler { }); return; } - AgilityHandler.walk(player, -1, start, end, ForceMovement.WALK_ANIMATION, getExp(player, 6.0), null); + AgilityHandler.walk(player, -1, start, end, Animation.create(1426), getExp(player, 6.0), null); } /** @@ -389,6 +391,7 @@ public final class BrimhavenCourse extends OptionHandler { } break; case 3551: + case 3583: return n.getLocation(); } return null; diff --git a/Server/src/main/content/global/skill/agility/brimhaven/DartTrap.java b/Server/src/main/content/global/skill/agility/brimhaven/DartTrap.java index 086a59522..3ac7da4a3 100644 --- a/Server/src/main/content/global/skill/agility/brimhaven/DartTrap.java +++ b/Server/src/main/content/global/skill/agility/brimhaven/DartTrap.java @@ -7,6 +7,8 @@ import core.game.node.entity.impl.Projectile; import core.game.node.entity.player.Player; import core.game.system.task.MovementHook; import core.game.system.task.Pulse; +import core.net.packet.context.PlayerContext; +import core.net.packet.out.ClearMinimapFlag; import kotlin.Unit; import core.game.world.GameWorld; import core.game.world.map.Direction; @@ -29,6 +31,9 @@ public final class DartTrap implements MovementHook { final Direction dir = e.getDirection(); final Player player = (Player) e; final Location start = l.transform(-dir.getStepX(), -dir.getStepY(), 0); + player.getPulseManager().clear(); + player.getWalkingQueue().reset(); + PacketRepository.send(ClearMinimapFlag.class, new PlayerContext(player)); e.lock(6); if(e.isPlayer()) { diff --git a/Server/src/main/content/global/skill/agility/brimhaven/FloorSpikes.java b/Server/src/main/content/global/skill/agility/brimhaven/FloorSpikes.java index 69111c6bf..2885a7f6a 100644 --- a/Server/src/main/content/global/skill/agility/brimhaven/FloorSpikes.java +++ b/Server/src/main/content/global/skill/agility/brimhaven/FloorSpikes.java @@ -6,6 +6,9 @@ import core.game.node.entity.Entity; import core.game.node.entity.player.Player; import core.game.system.task.MovementHook; import core.game.system.task.Pulse; +import core.net.packet.PacketRepository; +import core.net.packet.context.PlayerContext; +import core.net.packet.out.ClearMinimapFlag; import kotlin.Unit; import core.game.world.GameWorld; import core.game.world.map.Direction; @@ -24,6 +27,9 @@ public final class FloorSpikes implements MovementHook { final Direction dir = e.getDirection(); final Player player = (Player) e; final Location start = l.transform(-dir.getStepX(), -dir.getStepY(), 0); + player.getPulseManager().clear(); + player.getWalkingQueue().reset(); + PacketRepository.send(ClearMinimapFlag.class, new PlayerContext(player)); e.lock(5); if(e.isPlayer()) { diff --git a/Server/src/main/content/global/skill/agility/brimhaven/PressurePad.java b/Server/src/main/content/global/skill/agility/brimhaven/PressurePad.java index a2bc07cb2..d27a76bcf 100644 --- a/Server/src/main/content/global/skill/agility/brimhaven/PressurePad.java +++ b/Server/src/main/content/global/skill/agility/brimhaven/PressurePad.java @@ -6,6 +6,9 @@ import core.game.node.entity.Entity; import core.game.node.entity.player.Player; import core.game.system.task.MovementHook; import core.game.system.task.Pulse; +import core.net.packet.PacketRepository; +import core.net.packet.context.PlayerContext; +import core.net.packet.out.ClearMinimapFlag; import kotlin.Unit; import core.game.world.GameWorld; import core.game.world.map.Direction; @@ -24,6 +27,10 @@ public final class PressurePad implements MovementHook { final Direction dir = e.getDirection(); final Player player = (Player) e; final Location start = dest.transform(-dir.getStepX(), -dir.getStepY(), 0); + player.getPulseManager().clear(); + player.getWalkingQueue().reset(); + PacketRepository.send(ClearMinimapFlag.class, new PlayerContext(player)); + e.lock(5); if(e.isPlayer()) { diff --git a/Server/src/main/content/global/skill/agility/brimhaven/SpinningBlades.java b/Server/src/main/content/global/skill/agility/brimhaven/SpinningBlades.java index a13815767..3d2a9c1e2 100644 --- a/Server/src/main/content/global/skill/agility/brimhaven/SpinningBlades.java +++ b/Server/src/main/content/global/skill/agility/brimhaven/SpinningBlades.java @@ -6,6 +6,9 @@ import core.game.node.entity.Entity; import core.game.node.entity.player.Player; import core.game.system.task.MovementHook; import core.game.system.task.Pulse; +import core.net.packet.PacketRepository; +import core.net.packet.context.PlayerContext; +import core.net.packet.out.ClearMinimapFlag; import kotlin.Unit; import core.game.world.GameWorld; import core.game.world.map.Direction; @@ -24,6 +27,9 @@ public final class SpinningBlades implements MovementHook { final Direction dir = e.getDirection(); final Player player = (Player) e; final Location start = l.transform(-dir.getStepX(), -dir.getStepY(), 0); + player.getPulseManager().clear(); + player.getWalkingQueue().reset(); + PacketRepository.send(ClearMinimapFlag.class, new PlayerContext(player)); e.lock(5); if(e.isPlayer()) { @@ -70,4 +76,4 @@ public final class SpinningBlades implements MovementHook { } player.getPacketDispatch().sendSceneryAnimation(RegionManager.getObject(l), Animation.create(1107)); } -} \ No newline at end of file +}