Merge branch 2009scape:master into escapeTags

This commit is contained in:
Tooze 2026-07-26 17:32:39 +00:00
commit 46a61d4881
11 changed files with 141 additions and 39 deletions

View file

@ -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;

View file

@ -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())
{

View file

@ -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;

View file

@ -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())
{

View file

@ -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())
{

View file

@ -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())
{

View file

@ -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));
}
}
}

View file

@ -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<SkillRestore>(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 {
}

View file

@ -82,6 +82,8 @@ public abstract class Familiar extends NPC implements Plugin<Object> {
* 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<Object> {
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;

View file

@ -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) {

View file

@ -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<String>, 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 <true|false>", "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, _ ->