Rewrote spellbook swap
Corrected spellbook swap spell selection timeout
This commit is contained in:
@@ -30,6 +30,17 @@ public final class AutocastSelectPlugin extends ComponentPlugin {
|
|||||||
if (!player.getAttribute("autocast_select", false)) {
|
if (!player.getAttribute("autocast_select", false)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (player.timers.getTimer("spellbook:swap") != null) {
|
||||||
|
player.getPacketDispatch().sendMessage("You cannot autocast while using Spellbook Swap.");
|
||||||
|
player.removeAttribute("autocast_select");
|
||||||
|
player.removeAttribute("autocast_component");
|
||||||
|
final WeaponInterface w = player.getExtension(WeaponInterface.class);
|
||||||
|
if (w != null) {
|
||||||
|
w.setAttackStyle(3);
|
||||||
|
player.getInterfaceManager().openTab(w);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
player.removeAttribute("autocast_select");
|
player.removeAttribute("autocast_select");
|
||||||
final WeaponInterface w = player.getExtension(WeaponInterface.class);
|
final WeaponInterface w = player.getExtension(WeaponInterface.class);
|
||||||
if (w != null) {
|
if (w != null) {
|
||||||
|
|||||||
@@ -1,15 +1,23 @@
|
|||||||
package content.global.skill.magic
|
package content.global.skill.magic
|
||||||
|
|
||||||
import core.api.*
|
import content.data.Quests
|
||||||
|
import core.api.animate
|
||||||
|
import core.api.hasLevelStat
|
||||||
|
import core.api.hasRequirement
|
||||||
|
import core.api.lock
|
||||||
|
import core.api.playAudio
|
||||||
|
import core.api.sendMessage
|
||||||
|
import core.game.event.SpellbookChangeEvent
|
||||||
import core.game.interaction.IntType
|
import core.game.interaction.IntType
|
||||||
import core.game.interaction.InteractionListener
|
import core.game.interaction.InteractionListener
|
||||||
import core.game.node.Node
|
import core.game.node.Node
|
||||||
|
import core.game.node.entity.combat.equipment.WeaponInterface
|
||||||
import core.game.node.entity.player.Player
|
import core.game.node.entity.player.Player
|
||||||
|
import core.game.node.entity.player.link.SpellBookManager
|
||||||
import core.game.node.entity.player.link.SpellBookManager.SpellBook
|
import core.game.node.entity.player.link.SpellBookManager.SpellBook
|
||||||
import core.game.node.entity.skill.Skills
|
import core.game.node.entity.skill.Skills
|
||||||
import org.rs09.consts.Scenery
|
import org.rs09.consts.Scenery
|
||||||
import org.rs09.consts.Sounds
|
import org.rs09.consts.Sounds
|
||||||
import content.data.Quests
|
|
||||||
|
|
||||||
class MagicAltarListener : InteractionListener {
|
class MagicAltarListener : InteractionListener {
|
||||||
override fun defineListeners() {
|
override fun defineListeners() {
|
||||||
@@ -41,16 +49,26 @@ class MagicAltarListener : InteractionListener {
|
|||||||
lock(player, 3)
|
lock(player, 3)
|
||||||
playAudio(player, Sounds.PRAYER_RECHARGE_2674)
|
playAudio(player, Sounds.PRAYER_RECHARGE_2674)
|
||||||
animate(player, 645)
|
animate(player, 645)
|
||||||
|
|
||||||
if (altar.id == ANCIENT_ALTAR) {
|
if (altar.id == ANCIENT_ALTAR) {
|
||||||
player.skills.decrementPrayerPoints(player.skills.prayerPoints)
|
player.skills.decrementPrayerPoints(player.skills.prayerPoints)
|
||||||
}
|
}
|
||||||
|
val weaponInterface = player.getExtension<WeaponInterface>(WeaponInterface::class.java)
|
||||||
|
if (weaponInterface != null && player.properties.autocastSpell != null) {
|
||||||
|
weaponInterface.selectAutoSpell(-1, true)
|
||||||
|
}
|
||||||
if (SpellBook.forInterface(player.spellBookManager.spellBook) == if (altar.id == ANCIENT_ALTAR) SpellBook.ANCIENT else SpellBook.LUNAR) {
|
if (SpellBook.forInterface(player.spellBookManager.spellBook) == if (altar.id == ANCIENT_ALTAR) SpellBook.ANCIENT else SpellBook.LUNAR) {
|
||||||
|
player.dispatch(SpellbookChangeEvent(
|
||||||
|
SpellBook.forInterface(player.spellBookManager.spellBook),
|
||||||
|
SpellBook.MODERN,
|
||||||
|
SpellBookManager.SpellbookChangeSource.ALTAR))
|
||||||
sendMessage(player, if (altar.id == ANCIENT_ALTAR) "You feel a strange drain upon your memory..." else "Modern spells activated!")
|
sendMessage(player, if (altar.id == ANCIENT_ALTAR) "You feel a strange drain upon your memory..." else "Modern spells activated!")
|
||||||
player.spellBookManager.setSpellBook(SpellBook.MODERN)
|
player.spellBookManager.setSpellBook(SpellBook.MODERN)
|
||||||
player.spellBookManager.update(player)
|
player.spellBookManager.update(player)
|
||||||
} else {
|
} else {
|
||||||
|
player.dispatch(SpellbookChangeEvent(
|
||||||
|
SpellBook.forInterface(player.spellBookManager.spellBook),
|
||||||
|
if (altar.id == ANCIENT_ALTAR) SpellBook.ANCIENT else SpellBook.LUNAR,
|
||||||
|
SpellBookManager.SpellbookChangeSource.ALTAR))
|
||||||
sendMessage(player, if (altar.id == ANCIENT_ALTAR) "You feel a strange wisdom fill your mind..." else "Lunar spells activated!")
|
sendMessage(player, if (altar.id == ANCIENT_ALTAR) "You feel a strange wisdom fill your mind..." else "Lunar spells activated!")
|
||||||
player.spellBookManager.setSpellBook(if (altar.id == ANCIENT_ALTAR) SpellBook.ANCIENT else SpellBook.LUNAR)
|
player.spellBookManager.setSpellBook(if (altar.id == ANCIENT_ALTAR) SpellBook.ANCIENT else SpellBook.LUNAR)
|
||||||
player.spellBookManager.update(player)
|
player.spellBookManager.update(player)
|
||||||
|
|||||||
@@ -1,13 +1,25 @@
|
|||||||
package content.global.skill.magic
|
package content.global.skill.magic
|
||||||
|
|
||||||
import core.game.node.entity.npc.NPC
|
|
||||||
import core.game.node.entity.player.Player
|
|
||||||
import core.game.node.entity.combat.spell.CombinationRune
|
import core.game.node.entity.combat.spell.CombinationRune
|
||||||
import core.game.node.entity.combat.spell.MagicStaff
|
import core.game.node.entity.combat.spell.MagicStaff
|
||||||
import core.game.node.entity.combat.spell.Runes
|
import core.game.node.entity.combat.spell.Runes
|
||||||
|
import core.game.node.entity.npc.NPC
|
||||||
|
import core.game.node.entity.player.Player
|
||||||
import core.game.node.item.Item
|
import core.game.node.item.Item
|
||||||
|
|
||||||
object SpellUtils {
|
object SpellUtils {
|
||||||
|
/**
|
||||||
|
* Validates that the spell packet's interface matches the player's actual server-side spellbook.
|
||||||
|
* This prevents exploits where the client has a stale spellbook interface (e.g., after Spellbook Swap reverts).
|
||||||
|
* @param player The player casting the spell
|
||||||
|
* @param packetInterfaceId The interface ID from the client's spell packet
|
||||||
|
* @return true if the interfaces match (valid), false if there's a client/server desync
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun validateSpellbookInterface(player : Player, packetInterfaceId : Int) : Boolean {
|
||||||
|
val actualSpellbook = player.spellBookManager.spellBook
|
||||||
|
return packetInterfaceId == actualSpellbook
|
||||||
|
}
|
||||||
fun usingStaff(p: Player, rune: Int): Boolean {
|
fun usingStaff(p: Player, rune: Int): Boolean {
|
||||||
val weapon = p.equipment[3] ?: return false
|
val weapon = p.equipment[3] ?: return false
|
||||||
val staff = MagicStaff.forId(rune) ?: return false
|
val staff = MagicStaff.forId(rune) ?: return false
|
||||||
|
|||||||
@@ -1,10 +1,21 @@
|
|||||||
package content.global.skill.magic;
|
package content.global.skill.magic;
|
||||||
|
|
||||||
|
import content.data.Quests;
|
||||||
import core.game.component.Component;
|
import core.game.component.Component;
|
||||||
import core.game.dialogue.DialoguePlugin;
|
import core.game.dialogue.DialoguePlugin;
|
||||||
|
import core.game.event.SpellbookChangeEvent;
|
||||||
|
import core.game.interaction.Listener;
|
||||||
import core.game.node.entity.player.Player;
|
import core.game.node.entity.player.Player;
|
||||||
import core.plugin.Initializable;
|
|
||||||
import core.game.node.entity.player.link.SpellBookManager.SpellBook;
|
import core.game.node.entity.player.link.SpellBookManager.SpellBook;
|
||||||
|
import core.game.node.entity.player.link.SpellBookManager.SpellbookChangeSource;
|
||||||
|
import core.game.node.entity.skill.Skills;
|
||||||
|
import core.game.node.item.Item;
|
||||||
|
import core.plugin.Initializable;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import static core.api.ContentAPIKt.hasRequirement;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the SpellbookSwapDialogue dialogue.
|
* Handles the SpellbookSwapDialogue dialogue.
|
||||||
@@ -73,8 +84,27 @@ public class SpellbookSwapDialogue extends DialoguePlugin {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
final SpellBook book = type == 1 ? SpellBook.ANCIENT : SpellBook.MODERN;
|
final SpellBook book = type == 1 ? SpellBook.ANCIENT : SpellBook.MODERN;
|
||||||
|
if (book == SpellBook.ANCIENT && !hasRequirement(player, Quests.DESERT_TREASURE)) {
|
||||||
|
player.getPacketDispatch().sendMessage("You need to complete Desert Treasure to use Ancient Magicks.");
|
||||||
|
end();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Remove runes for Spellbook Swap
|
||||||
|
ArrayList<Item> runes = player.getAttribute("spell:runes", new ArrayList<>());
|
||||||
|
if (!runes.isEmpty() && player.getInventory().remove(runes.toArray(new Item[0]))) {
|
||||||
|
player.removeAttribute("spell:runes");
|
||||||
|
player.removeAttribute("tablet-spell");
|
||||||
|
}
|
||||||
|
// Award XP
|
||||||
|
player.skills.addExperience(Skills.MAGIC, 130);
|
||||||
|
// Change book
|
||||||
|
player.dispatch(new SpellbookChangeEvent(
|
||||||
|
SpellBook.LUNAR,
|
||||||
|
book,
|
||||||
|
SpellbookChangeSource.SPELLBOOK_SWAP_CAST));
|
||||||
player.getSpellBookManager().setSpellBook(book);
|
player.getSpellBookManager().setSpellBook(book);
|
||||||
player.getInterfaceManager().openTab(new Component(book.getInterfaceId()));
|
player.getInterfaceManager().openTab(new Component(book.getInterfaceId()));
|
||||||
|
player.getPacketDispatch().sendMessage("You have 2 minutes before your spellbook changes back to the Lunar Spellbook!");
|
||||||
end();
|
end();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
package content.global.skill.magic.ancient;
|
package content.global.skill.magic.ancient;
|
||||||
|
|
||||||
import core.game.node.entity.player.Player;
|
import core.game.event.SpellCastEvent;
|
||||||
import core.game.node.entity.player.link.diary.DiaryType;
|
|
||||||
import core.game.node.entity.combat.spell.MagicSpell;
|
|
||||||
import core.game.node.entity.combat.spell.Runes;
|
|
||||||
import core.game.node.Node;
|
import core.game.node.Node;
|
||||||
import core.game.node.entity.Entity;
|
import core.game.node.entity.Entity;
|
||||||
|
import core.game.node.entity.combat.spell.MagicSpell;
|
||||||
|
import core.game.node.entity.combat.spell.Runes;
|
||||||
import core.game.node.entity.combat.spell.SpellType;
|
import core.game.node.entity.combat.spell.SpellType;
|
||||||
|
import core.game.node.entity.player.Player;
|
||||||
import core.game.node.entity.player.link.SpellBookManager.SpellBook;
|
import core.game.node.entity.player.link.SpellBookManager.SpellBook;
|
||||||
import core.game.node.entity.player.link.TeleportManager.TeleportType;
|
import core.game.node.entity.player.link.TeleportManager.TeleportType;
|
||||||
|
import core.game.node.entity.player.link.diary.DiaryType;
|
||||||
import core.game.node.item.Item;
|
import core.game.node.item.Item;
|
||||||
import core.game.world.GameWorld;
|
import core.game.world.GameWorld;
|
||||||
import core.game.world.map.Location;
|
import core.game.world.map.Location;
|
||||||
@@ -67,6 +68,7 @@ public final class AncientTeleportPlugin extends MagicSpell {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
homeTeleport((Player) entity, Location.create(3087, 3495, 0));
|
homeTeleport((Player) entity, Location.create(3087, 3495, 0));
|
||||||
|
entity.dispatch(new SpellCastEvent(SpellBook.ANCIENT, getSpellId(), target));
|
||||||
} else if (entity.getTeleporter().send(location.transform(0, RandomFunction.random(3), 0), TeleportType.ANCIENT)) {
|
} else if (entity.getTeleporter().send(location.transform(0, RandomFunction.random(3), 0), TeleportType.ANCIENT)) {
|
||||||
if (!super.meetsRequirements(entity, true, true)) {
|
if (!super.meetsRequirements(entity, true, true)) {
|
||||||
entity.getTeleporter().getCurrentTeleport().stop();
|
entity.getTeleporter().getCurrentTeleport().stop();
|
||||||
|
|||||||
@@ -24,12 +24,17 @@ import core.game.system.config.NPCConfigParser
|
|||||||
import core.game.system.task.Pulse
|
import core.game.system.task.Pulse
|
||||||
import core.game.system.timer.impl.PoisonImmunity
|
import core.game.system.timer.impl.PoisonImmunity
|
||||||
import core.game.system.timer.impl.SkillRestore
|
import core.game.system.timer.impl.SkillRestore
|
||||||
|
import core.game.system.timer.impl.SpellbookSwap
|
||||||
import core.game.world.map.Location
|
import core.game.world.map.Location
|
||||||
import core.game.world.map.RegionManager
|
import core.game.world.map.RegionManager
|
||||||
import core.game.world.repository.Repository
|
import core.game.world.repository.Repository
|
||||||
import core.game.world.update.flag.context.Animation
|
import core.game.world.update.flag.context.Animation
|
||||||
import core.tools.RandomFunction
|
import core.tools.RandomFunction
|
||||||
import org.rs09.consts.*
|
import org.rs09.consts.Animations
|
||||||
|
import org.rs09.consts.Components
|
||||||
|
import org.rs09.consts.Graphics
|
||||||
|
import org.rs09.consts.Items
|
||||||
|
import org.rs09.consts.Sounds
|
||||||
import kotlin.math.floor
|
import kotlin.math.floor
|
||||||
|
|
||||||
class LunarListeners : SpellListener("lunar"), Commands {
|
class LunarListeners : SpellListener("lunar"), Commands {
|
||||||
@@ -280,9 +285,14 @@ class LunarListeners : SpellListener("lunar"), Commands {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// Level 96
|
// Level 96
|
||||||
/**
|
onCast(Lunar.SPELLBOOK_SWAP, NONE) { player, _ ->
|
||||||
* Spellbook Swap
|
requires(player, 96, arrayOf(
|
||||||
*/
|
Item(Items.LAW_RUNE_563, 1),
|
||||||
|
Item(Items.COSMIC_RUNE_564, 2),
|
||||||
|
Item(Items.ASTRAL_RUNE_9075, 3)
|
||||||
|
))
|
||||||
|
spellbookSwap(player)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Spell handlers
|
// Spell handlers
|
||||||
@@ -785,9 +795,18 @@ class LunarListeners : SpellListener("lunar"), Commands {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// Level 96
|
// Level 96
|
||||||
/**
|
private fun spellbookSwap(player : Player) {
|
||||||
* Spellbook Swap
|
// Runes are removed on successful swap to another spellbook in SpellbookSwapDialogue.java
|
||||||
*/
|
// removeRunes(player, true)
|
||||||
|
lock(player, 4)
|
||||||
|
visualizeSpell(player, 6299, 1062)
|
||||||
|
player.scripts.removeWeakScripts()
|
||||||
|
player.dialogueInterpreter.open(3264731)
|
||||||
|
registerTimer(player, SpellbookSwap())
|
||||||
|
// XP is awarded in SpellbookSwapDialogue.java similarly to rune removal handling
|
||||||
|
// addXP(player, 130.0)
|
||||||
|
setDelay(player, false)
|
||||||
|
}
|
||||||
|
|
||||||
// Other/Multi spell use-case
|
// Other/Multi spell use-case
|
||||||
private fun sendTeleport(player: Player, xp: Double, loc: Location){
|
private fun sendTeleport(player: Player, xp: Double, loc: Location){
|
||||||
|
|||||||
@@ -1,84 +0,0 @@
|
|||||||
package content.global.skill.magic.lunar;
|
|
||||||
|
|
||||||
import core.game.component.Component;
|
|
||||||
import core.game.node.entity.combat.spell.MagicSpell;
|
|
||||||
import core.game.node.entity.combat.spell.Runes;
|
|
||||||
import core.game.node.Node;
|
|
||||||
import core.game.node.entity.Entity;
|
|
||||||
import core.game.node.entity.combat.spell.SpellType;
|
|
||||||
import core.game.node.entity.player.Player;
|
|
||||||
import core.game.node.entity.player.link.SpellBookManager.SpellBook;
|
|
||||||
import core.game.node.item.Item;
|
|
||||||
import core.game.system.task.Pulse;
|
|
||||||
import core.game.world.GameWorld;
|
|
||||||
import core.game.world.update.flag.context.Animation;
|
|
||||||
import core.game.world.update.flag.context.Graphics;
|
|
||||||
import core.plugin.Initializable;
|
|
||||||
import core.plugin.Plugin;
|
|
||||||
import core.tools.RandomFunction;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The spellbook swap spell.
|
|
||||||
* @author 'Vexia
|
|
||||||
*/
|
|
||||||
@Initializable
|
|
||||||
public class SpellbookSwapSpell extends MagicSpell {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents the animation of this spell.
|
|
||||||
*/
|
|
||||||
private final Animation ANIMATION = new Animation(6299);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents the graphics of this spell.
|
|
||||||
*/
|
|
||||||
private final Graphics GRAPHIC = new Graphics(1062);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a new {@code SpellbookSwapSpell} {@code Object}.
|
|
||||||
*/
|
|
||||||
public SpellbookSwapSpell() {
|
|
||||||
super(SpellBook.LUNAR, 96, 130, null, null, null, new Item[] { new Item(Runes.LAW_RUNE.getId(), 1), new Item(Runes.COSMIC_RUNE.getId(), 2), new Item(Runes.ASTRAL_RUNE.getId(), 3) });
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Plugin<SpellType> newInstance(SpellType arg) throws Throwable {
|
|
||||||
SpellBook.LUNAR.register(12, this);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean cast(Entity entity, Node target) {
|
|
||||||
final Player player = (Player) entity;
|
|
||||||
if (!super.meetsRequirements(player, true, true)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
player.lock(9);
|
|
||||||
player.animate(ANIMATION);
|
|
||||||
player.graphics(GRAPHIC);
|
|
||||||
player.getDialogueInterpreter().open(3264731);
|
|
||||||
final int id = RandomFunction.random(1, 500000);
|
|
||||||
player.setAttribute("spell:swap", id);
|
|
||||||
GameWorld.getPulser().submit(new Pulse(20, player) {
|
|
||||||
@Override
|
|
||||||
public boolean pulse() {
|
|
||||||
if (player.getAttribute("spell:swap", 0) == id) {
|
|
||||||
removeTemporarySpell(player);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method used to remove the temp spell swap.
|
|
||||||
* @param player the player.
|
|
||||||
*/
|
|
||||||
public static void removeTemporarySpell(final Player player) {
|
|
||||||
player.removeAttribute("spell:swap");
|
|
||||||
player.getSpellBookManager().setSpellBook(SpellBook.LUNAR);
|
|
||||||
player.getInterfaceManager().openTab(new Component(SpellBook.LUNAR.getInterfaceId()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +1,29 @@
|
|||||||
package content.global.skill.skillcapeperks
|
package content.global.skill.skillcapeperks
|
||||||
|
|
||||||
import core.game.component.Component
|
import content.data.Quests
|
||||||
import core.game.node.entity.player.Player
|
import content.global.skill.farming.PatchType
|
||||||
import core.game.node.entity.player.link.SpellBookManager
|
import content.global.skill.farming.Plantable
|
||||||
import core.game.node.entity.player.link.TeleportManager
|
|
||||||
import content.global.skill.runecrafting.Altar
|
import content.global.skill.runecrafting.Altar
|
||||||
import core.game.world.map.Location
|
|
||||||
import core.game.world.map.zone.impl.DarkZone
|
|
||||||
import core.plugin.Initializable
|
|
||||||
import core.game.world.GameWorld
|
|
||||||
import content.global.skill.farming.*
|
|
||||||
import core.ServerStore
|
import core.ServerStore
|
||||||
import core.ServerStore.Companion.getBoolean
|
import core.ServerStore.Companion.getBoolean
|
||||||
import core.ServerStore.Companion.getInt
|
import core.ServerStore.Companion.getInt
|
||||||
import core.api.*
|
import core.api.closeDialogue
|
||||||
|
import core.api.getAttribute
|
||||||
|
import core.api.hasRequirement
|
||||||
|
import core.api.sendDialogue
|
||||||
|
import core.api.sendMessage
|
||||||
|
import core.api.teleport
|
||||||
import core.cache.def.impl.ItemDefinition
|
import core.cache.def.impl.ItemDefinition
|
||||||
import core.tools.END_DIALOGUE
|
import core.game.component.Component
|
||||||
import org.rs09.consts.Items
|
import core.game.event.SpellbookChangeEvent
|
||||||
import content.data.Quests
|
import core.game.node.entity.combat.equipment.WeaponInterface
|
||||||
|
import core.game.node.entity.player.Player
|
||||||
|
import core.game.node.entity.player.link.SpellBookManager
|
||||||
|
import core.game.node.entity.player.link.TeleportManager
|
||||||
|
import core.game.world.GameWorld
|
||||||
|
import core.game.world.map.Location
|
||||||
|
import core.game.world.map.zone.impl.DarkZone
|
||||||
|
import core.plugin.Initializable
|
||||||
|
|
||||||
enum class SkillcapePerks(val attribute: String, val effect: ((Player) -> Unit)? = null) {
|
enum class SkillcapePerks(val attribute: String, val effect: ((Player) -> Unit)? = null) {
|
||||||
BAREFISTED_SMITHING("cape_perks:barefisted-smithing"),
|
BAREFISTED_SMITHING("cape_perks:barefisted-smithing"),
|
||||||
@@ -203,13 +209,18 @@ enum class SkillcapePerks(val attribute: String, val effect: ((Player) -> Unit)?
|
|||||||
end()
|
end()
|
||||||
if (spellbook != null) {
|
if (spellbook != null) {
|
||||||
if (spellbook == SpellBookManager.SpellBook.ANCIENT) {
|
if (spellbook == SpellBookManager.SpellBook.ANCIENT) {
|
||||||
if (!hasRequirement(player, Quests.DESERT_TREASURE))
|
if (!hasRequirement(player, Quests.DESERT_TREASURE)) return true
|
||||||
return true
|
} else if (spellbook == SpellBookManager.SpellBook.LUNAR) {
|
||||||
|
if (!hasRequirement(player, Quests.LUNAR_DIPLOMACY)) return true
|
||||||
}
|
}
|
||||||
else if (spellbook == SpellBookManager.SpellBook.LUNAR) {
|
val weaponInterface = player.getExtension<WeaponInterface>(WeaponInterface::class.java)
|
||||||
if (!hasRequirement(player, Quests.LUNAR_DIPLOMACY))
|
if (weaponInterface != null && player.properties.autocastSpell != null) {
|
||||||
return true
|
weaponInterface.selectAutoSpell(-1, true)
|
||||||
}
|
}
|
||||||
|
player.dispatch(SpellbookChangeEvent(
|
||||||
|
SpellBookManager.SpellBook.forInterface(player.spellBookManager.spellBook),
|
||||||
|
spellbook,
|
||||||
|
SpellBookManager.SpellbookChangeSource.MAGIC_CAPE_PERK))
|
||||||
player.spellBookManager.setSpellBook(spellbook)
|
player.spellBookManager.setSpellBook(spellbook)
|
||||||
player.interfaceManager.openTab(Component(spellbook.interfaceId))
|
player.interfaceManager.openTab(Component(spellbook.interfaceId))
|
||||||
player.incrementAttribute("/save:cape_perks:librarian-magus-charges", -1)
|
player.incrementAttribute("/save:cape_perks:librarian-magus-charges", -1)
|
||||||
@@ -220,7 +231,6 @@ enum class SkillcapePerks(val attribute: String, val effect: ((Player) -> Unit)?
|
|||||||
override fun getIds() : IntArray {
|
override fun getIds() : IntArray {
|
||||||
return intArrayOf(509871234)
|
return intArrayOf(509871234)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Initializable
|
@Initializable
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ object Event {
|
|||||||
@JvmStatic val AttributeSet = AttributeSetEvent::class.java
|
@JvmStatic val AttributeSet = AttributeSetEvent::class.java
|
||||||
@JvmStatic val AttributeRemoved = AttributeRemoveEvent::class.java
|
@JvmStatic val AttributeRemoved = AttributeRemoveEvent::class.java
|
||||||
@JvmStatic val SpellCast = SpellCastEvent::class.java
|
@JvmStatic val SpellCast = SpellCastEvent::class.java
|
||||||
|
@JvmStatic val SpellbookChanged = SpellbookChangeEvent::class.java
|
||||||
@JvmStatic val ItemAlchemized = ItemAlchemizationEvent::class.java
|
@JvmStatic val ItemAlchemized = ItemAlchemizationEvent::class.java
|
||||||
@JvmStatic val ItemEquipped = ItemEquipEvent::class.java
|
@JvmStatic val ItemEquipped = ItemEquipEvent::class.java
|
||||||
@JvmStatic val ItemUnequipped = ItemUnequipEvent::class.java
|
@JvmStatic val ItemUnequipped = ItemUnequipEvent::class.java
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import core.game.node.Node
|
|||||||
import core.game.node.entity.Entity
|
import core.game.node.entity.Entity
|
||||||
import core.game.node.entity.npc.NPC
|
import core.game.node.entity.npc.NPC
|
||||||
import core.game.node.entity.player.link.SpellBookManager.SpellBook
|
import core.game.node.entity.player.link.SpellBookManager.SpellBook
|
||||||
|
import core.game.node.entity.player.link.SpellBookManager.SpellbookChangeSource
|
||||||
import core.game.node.entity.player.link.TeleportManager.TeleportType
|
import core.game.node.entity.player.link.TeleportManager.TeleportType
|
||||||
import core.game.node.entity.player.link.prayer.PrayerType
|
import core.game.node.entity.player.link.prayer.PrayerType
|
||||||
import core.game.node.item.Item
|
import core.game.node.item.Item
|
||||||
@@ -34,6 +35,7 @@ data class InterfaceCloseEvent(val component: Component) : Event
|
|||||||
data class AttributeSetEvent(val entity: Entity, val attribute: String, val value: Any) : Event
|
data class AttributeSetEvent(val entity: Entity, val attribute: String, val value: Any) : Event
|
||||||
data class AttributeRemoveEvent(val entity: Entity, val attribute: String) : Event
|
data class AttributeRemoveEvent(val entity: Entity, val attribute: String) : Event
|
||||||
data class SpellCastEvent(val spellBook: SpellBook, val spellId: Int, val target: Node? = null) : Event
|
data class SpellCastEvent(val spellBook: SpellBook, val spellId: Int, val target: Node? = null) : Event
|
||||||
|
data class SpellbookChangeEvent(val oldSpellBook: SpellBook, val newSpellBook: SpellBook, val source: SpellbookChangeSource) : Event
|
||||||
data class ItemAlchemizationEvent(val itemId: Int, val isHigh: Boolean) : Event
|
data class ItemAlchemizationEvent(val itemId: Int, val isHigh: Boolean) : Event
|
||||||
data class ItemEquipEvent(val itemId: Int, val slotId: Int) : Event
|
data class ItemEquipEvent(val itemId: Int, val slotId: Int) : Event
|
||||||
data class ItemUnequipEvent(val itemId: Int, val slotId: Int) : Event
|
data class ItemUnequipEvent(val itemId: Int, val slotId: Int) : Event
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
package core.game.node.entity.combat.spell;
|
package core.game.node.entity.combat.spell;
|
||||||
|
|
||||||
import core.game.component.Component;
|
|
||||||
import core.game.event.SpellCastEvent;
|
import core.game.event.SpellCastEvent;
|
||||||
import core.game.node.entity.skill.Skills;
|
|
||||||
import core.game.node.Node;
|
import core.game.node.Node;
|
||||||
import core.game.node.entity.Entity;
|
import core.game.node.entity.Entity;
|
||||||
import core.game.node.entity.combat.CombatSwingHandler;
|
import core.game.node.entity.combat.CombatSwingHandler;
|
||||||
@@ -10,6 +8,7 @@ import core.game.node.entity.combat.equipment.WeaponInterface;
|
|||||||
import core.game.node.entity.player.Player;
|
import core.game.node.entity.player.Player;
|
||||||
import core.game.node.entity.player.link.SpellBookManager.SpellBook;
|
import core.game.node.entity.player.link.SpellBookManager.SpellBook;
|
||||||
import core.game.node.entity.player.link.audio.Audio;
|
import core.game.node.entity.player.link.audio.Audio;
|
||||||
|
import core.game.node.entity.skill.Skills;
|
||||||
import core.game.node.item.Item;
|
import core.game.node.item.Item;
|
||||||
import core.game.world.GameWorld;
|
import core.game.world.GameWorld;
|
||||||
import core.game.world.update.flag.context.Animation;
|
import core.game.world.update.flag.context.Animation;
|
||||||
@@ -121,11 +120,6 @@ public abstract class MagicSpell implements Plugin<SpellType> {
|
|||||||
p.faceTemporary((Entity) target, 1);
|
p.faceTemporary((Entity) target, 1);
|
||||||
}
|
}
|
||||||
if (spell.cast(p, target)) {
|
if (spell.cast(p, target)) {
|
||||||
if (book != SpellBook.LUNAR && p.getAttribute("spell:swap", 0) != 0) {
|
|
||||||
p.removeAttribute("spell:swap");
|
|
||||||
p.getSpellBookManager().setSpellBook(SpellBook.LUNAR);
|
|
||||||
p.getInterfaceManager().openTab(new Component(SpellBook.LUNAR.getInterfaceId()));
|
|
||||||
}
|
|
||||||
if (!combatSpell) {
|
if (!combatSpell) {
|
||||||
p.getSkills().addExperience(Skills.MAGIC, spell.getExperience(p), true);
|
p.getSkills().addExperience(Skills.MAGIC, spell.getExperience(p), true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,6 +55,16 @@ public final class SpellBookManager {
|
|||||||
return spellBook;
|
return spellBook;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All the possible ways a SpellBook can get changed.
|
||||||
|
*/
|
||||||
|
public enum SpellbookChangeSource {
|
||||||
|
ALTAR,
|
||||||
|
MAGIC_CAPE_PERK,
|
||||||
|
SPELLBOOK_SWAP_CAST,
|
||||||
|
SPELLBOOK_SWAP_RESTORE
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a characters spell book.
|
* Represents a characters spell book.
|
||||||
* @author 'Vexia
|
* @author 'Vexia
|
||||||
@@ -134,5 +144,4 @@ public final class SpellBookManager {
|
|||||||
return spells.get(buttonId);
|
return spells.get(buttonId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
package core.game.system.timer.impl
|
||||||
|
|
||||||
|
import core.api.Event.DialogueClosed
|
||||||
|
import core.api.Event.SpellCast
|
||||||
|
import core.api.Event.SpellbookChanged
|
||||||
|
import core.api.clearLogoutListener
|
||||||
|
import core.api.registerLogoutListener
|
||||||
|
import core.api.removeTimer
|
||||||
|
import core.game.event.*
|
||||||
|
import core.game.node.entity.Entity
|
||||||
|
import core.game.node.entity.combat.equipment.WeaponInterface
|
||||||
|
import core.game.node.entity.player.Player
|
||||||
|
import core.game.node.entity.player.link.SpellBookManager.SpellBook
|
||||||
|
import core.game.node.entity.player.link.SpellBookManager.SpellbookChangeSource
|
||||||
|
import core.game.system.timer.PersistTimer
|
||||||
|
import core.tools.minutesToTicks
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A timer dedicated to the handling of the Lunar spell Spellbook Swap.
|
||||||
|
*/
|
||||||
|
class SpellbookSwap : PersistTimer(runInterval = minutesToTicks(2), identifier = "spellbook:swap") {
|
||||||
|
|
||||||
|
private val spellCastHook = object : EventHook<SpellCastEvent> {
|
||||||
|
override fun process(entity : Entity, event : SpellCastEvent) {
|
||||||
|
if (event.spellBook == SpellBook.LUNAR && event.spellId == 12) return // Ignore spellbook swap itself
|
||||||
|
revertSpellbook(entity)
|
||||||
|
removeTimer<SpellbookSwap>(entity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val spellBookChangeHook = object : EventHook<SpellbookChangeEvent> {
|
||||||
|
override fun process(entity : Entity, event : SpellbookChangeEvent) {
|
||||||
|
if (event.source != SpellbookChangeSource.SPELLBOOK_SWAP_CAST
|
||||||
|
&& event.source != SpellbookChangeSource.SPELLBOOK_SWAP_RESTORE) {
|
||||||
|
removeTimer<SpellbookSwap>(entity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val dialogueCloseHook = object : EventHook<DialogueCloseEvent> {
|
||||||
|
override fun process(entity : Entity, event : DialogueCloseEvent) {
|
||||||
|
if (event.dialogue?.getIds()?.contains(3264731) != true) return // Spellbook swap selection dialogue
|
||||||
|
if (entity !is Player) return
|
||||||
|
if (SpellBook.forInterface(entity.spellBookManager.spellBook) == SpellBook.LUNAR) {
|
||||||
|
entity.removeAttribute("spell:runes")
|
||||||
|
removeTimer<SpellbookSwap>(entity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun run(entity : Entity) : Boolean {
|
||||||
|
revertSpellbook(entity)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onRegister(entity : Entity) {
|
||||||
|
entity.hook(SpellCast, spellCastHook)
|
||||||
|
entity.hook(SpellbookChanged, spellBookChangeHook)
|
||||||
|
entity.hook(DialogueClosed, dialogueCloseHook)
|
||||||
|
if (entity is Player) {
|
||||||
|
registerLogoutListener(entity, "spellbook:swap") { player ->
|
||||||
|
revertSpellbook(player)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onRemoval(entity : Entity) {
|
||||||
|
entity.unhook(spellCastHook)
|
||||||
|
entity.unhook(spellBookChangeHook)
|
||||||
|
entity.unhook(dialogueCloseHook)
|
||||||
|
if (entity is Player) {
|
||||||
|
clearLogoutListener(entity, "spellbook:swap")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun revertSpellbook(entity : Entity) {
|
||||||
|
if (entity !is Player) return
|
||||||
|
if (SpellBook.forInterface(entity.spellBookManager.spellBook) == SpellBook.LUNAR) return
|
||||||
|
// Clear autocast
|
||||||
|
val weaponInterface = entity.getExtension<WeaponInterface>(WeaponInterface::class.java)
|
||||||
|
if (weaponInterface != null && entity.properties.autocastSpell != null) {
|
||||||
|
weaponInterface.selectAutoSpell(-1, true)
|
||||||
|
}
|
||||||
|
// Close autocast selection interface if it's still open
|
||||||
|
if (entity.getAttribute("autocast_select", false)) {
|
||||||
|
entity.removeAttribute("autocast_select")
|
||||||
|
entity.removeAttribute("autocast_component")
|
||||||
|
if (weaponInterface != null) {
|
||||||
|
entity.interfaceManager.openTab(weaponInterface)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
entity.dispatch(SpellbookChangeEvent(
|
||||||
|
SpellBook.forInterface(entity.spellBookManager.spellBook),
|
||||||
|
SpellBook.LUNAR,
|
||||||
|
SpellbookChangeSource.SPELLBOOK_SWAP_RESTORE))
|
||||||
|
entity.spellBookManager.setSpellBook(SpellBook.LUNAR)
|
||||||
|
entity.spellBookManager.update(entity)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,62 +1,61 @@
|
|||||||
package core.net.packet
|
package core.net.packet
|
||||||
|
|
||||||
import core.game.event.ButtonClickEvent
|
import content.global.ame.events.maze.MazeInterface
|
||||||
|
import content.global.handlers.iface.ge.StockMarket
|
||||||
|
import content.global.skill.magic.SpellListener
|
||||||
|
import content.global.skill.magic.SpellListeners
|
||||||
|
import content.global.skill.magic.SpellUtils
|
||||||
|
import content.global.skill.summoning.familiar.FamiliarSpecial
|
||||||
|
import core.ServerConstants
|
||||||
import core.api.getAttribute
|
import core.api.getAttribute
|
||||||
|
import core.api.log
|
||||||
import core.api.sendMessage
|
import core.api.sendMessage
|
||||||
import core.api.tryPop
|
import core.api.tryPop
|
||||||
|
import core.api.utils.Vector
|
||||||
import core.cache.def.impl.ItemDefinition
|
import core.cache.def.impl.ItemDefinition
|
||||||
import core.cache.def.impl.NPCDefinition
|
import core.cache.def.impl.NPCDefinition
|
||||||
import core.cache.def.impl.SceneryDefinition
|
import core.cache.def.impl.SceneryDefinition
|
||||||
import core.game.container.Container
|
import core.game.container.Container
|
||||||
import core.game.container.impl.BankContainer
|
import core.game.container.impl.BankContainer
|
||||||
import core.game.node.Node
|
import core.game.event.ButtonClickEvent
|
||||||
import core.game.node.entity.player.Player
|
|
||||||
import core.game.node.entity.player.info.Rights
|
|
||||||
import core.game.node.entity.player.info.login.LoginConfiguration
|
|
||||||
import core.game.node.entity.player.link.SpellBookManager
|
|
||||||
import core.game.node.entity.combat.spell.MagicSpell
|
|
||||||
import content.global.ame.events.maze.MazeInterface
|
|
||||||
import content.global.skill.summoning.familiar.FamiliarSpecial
|
|
||||||
import core.game.node.item.GroundItemManager
|
|
||||||
import core.game.node.item.Item
|
|
||||||
import core.game.node.scenery.Scenery
|
|
||||||
import core.game.system.communication.ClanRank
|
|
||||||
import core.game.system.communication.CommunicationInfo
|
|
||||||
import core.game.system.task.Pulse
|
|
||||||
import core.game.world.map.Location
|
|
||||||
import core.game.world.map.RegionManager
|
|
||||||
import core.game.world.update.flag.context.ChatMessage
|
|
||||||
import core.game.world.update.flag.*
|
|
||||||
import core.net.amsc.MSPacketRepository
|
|
||||||
import core.net.packet.context.PlayerContext
|
|
||||||
import core.net.packet.out.ClearMinimapFlag
|
|
||||||
import org.rs09.consts.Components
|
|
||||||
import proto.management.ClanMessage
|
|
||||||
import proto.management.JoinClanRequest
|
|
||||||
import proto.management.LeaveClanRequest
|
|
||||||
import core.ServerConstants
|
|
||||||
import core.game.ge.GrandExchange.Companion.getOfferStats
|
import core.game.ge.GrandExchange.Companion.getOfferStats
|
||||||
import core.game.ge.GrandExchange.Companion.getRecommendedPrice
|
import core.game.ge.GrandExchange.Companion.getRecommendedPrice
|
||||||
import core.game.ge.GrandExchangeOffer
|
import core.game.ge.GrandExchangeOffer
|
||||||
import core.game.ge.PriceIndex
|
import core.game.ge.PriceIndex
|
||||||
import content.global.handlers.iface.ge.StockMarket
|
|
||||||
import content.global.skill.magic.SpellListener
|
|
||||||
import content.global.skill.magic.SpellListeners
|
|
||||||
import content.global.skill.magic.SpellUtils
|
|
||||||
import core.api.log
|
|
||||||
import core.game.interaction.*
|
import core.game.interaction.*
|
||||||
|
import core.game.node.Node
|
||||||
|
import core.game.node.entity.combat.spell.MagicSpell
|
||||||
|
import core.game.node.entity.player.Player
|
||||||
import core.game.node.entity.player.info.LogType
|
import core.game.node.entity.player.info.LogType
|
||||||
import core.game.node.entity.player.info.PlayerMonitor
|
import core.game.node.entity.player.info.PlayerMonitor
|
||||||
import core.tools.SystemLogger
|
import core.game.node.entity.player.info.Rights
|
||||||
|
import core.game.node.entity.player.info.login.LoginConfiguration
|
||||||
|
import core.game.node.entity.player.link.SpellBookManager
|
||||||
|
import core.game.node.item.GroundItemManager
|
||||||
|
import core.game.node.item.Item
|
||||||
|
import core.game.node.scenery.Scenery
|
||||||
import core.game.system.command.CommandSystem
|
import core.game.system.command.CommandSystem
|
||||||
|
import core.game.system.communication.ClanRank
|
||||||
|
import core.game.system.communication.CommunicationInfo
|
||||||
import core.game.system.communication.GlobalChat
|
import core.game.system.communication.GlobalChat
|
||||||
|
import core.game.system.task.Pulse
|
||||||
import core.game.world.GameWorld
|
import core.game.world.GameWorld
|
||||||
|
import core.game.world.map.Location
|
||||||
|
import core.game.world.map.RegionManager
|
||||||
import core.game.world.repository.Repository
|
import core.game.world.repository.Repository
|
||||||
|
import core.game.world.update.flag.EntityFlag
|
||||||
|
import core.game.world.update.flag.context.ChatMessage
|
||||||
|
import core.net.amsc.MSPacketRepository
|
||||||
|
import core.net.packet.context.PlayerContext
|
||||||
import core.net.packet.`in`.Packet
|
import core.net.packet.`in`.Packet
|
||||||
import core.net.packet.`in`.RunScript
|
import core.net.packet.`in`.RunScript
|
||||||
|
import core.net.packet.out.ClearMinimapFlag
|
||||||
import core.tools.Log
|
import core.tools.Log
|
||||||
import core.worker.ManagementEvents
|
import core.worker.ManagementEvents
|
||||||
import core.api.utils.Vector
|
import org.rs09.consts.Components
|
||||||
|
import proto.management.ClanMessage
|
||||||
|
import proto.management.JoinClanRequest
|
||||||
|
import proto.management.LeaveClanRequest
|
||||||
import java.io.PrintWriter
|
import java.io.PrintWriter
|
||||||
import java.io.StringWriter
|
import java.io.StringWriter
|
||||||
import java.lang.Math.min
|
import java.lang.Math.min
|
||||||
@@ -423,6 +422,11 @@ object PacketProcessor {
|
|||||||
return
|
return
|
||||||
if (player.getAttribute("magic:delay", -1) > GameWorld.ticks)
|
if (player.getAttribute("magic:delay", -1) > GameWorld.ticks)
|
||||||
return
|
return
|
||||||
|
// Validate spell packet interface matches player's actual spellbook
|
||||||
|
// This is to prevent player being able to cast spells when client has stale interface (e.g. after Spellbook swap reverts)
|
||||||
|
if (iface in intArrayOf(192, 193, 430)) {
|
||||||
|
if (!SpellUtils.validateSpellbookInterface(player, iface)) return
|
||||||
|
}
|
||||||
val book = SpellUtils.getBookFromInterface(iface)
|
val book = SpellUtils.getBookFromInterface(iface)
|
||||||
if (book != "none")
|
if (book != "none")
|
||||||
SpellListeners.run(child, type, book, player, target)
|
SpellListeners.run(child, type, book, player, target)
|
||||||
|
|||||||
Reference in New Issue
Block a user