From 9ab4edc276087d73d92c31aa25a18c46e2602695 Mon Sep 17 00:00:00 2001 From: Bishop Date: Sun, 26 Jul 2026 08:56:19 -0500 Subject: [PATCH] Fixed #2221 --- .../handlers/iface/AutocastSelectPlugin.java | 2 +- .../iface/tabs/CombatTabInterface.java | 2 +- .../global/skill/magic/MagicAltarListener.kt | 2 +- .../skill/skillcapeperks/SkillcapePerks.kt | 2 +- .../core/game/global/action/EquipHandler.kt | 2 +- .../node/entity/combat/MagicSwingHandler.kt | 2 +- .../combat/equipment/WeaponInterface.java | 56 +++++++++++++++++-- .../player/info/login/LoginConfiguration.java | 18 ++++-- .../entity/player/link/InterfaceManager.java | 2 +- .../game/system/timer/impl/SpellbookSwap.kt | 2 +- 10 files changed, 74 insertions(+), 16 deletions(-) diff --git a/Server/src/main/content/global/handlers/iface/AutocastSelectPlugin.java b/Server/src/main/content/global/handlers/iface/AutocastSelectPlugin.java index 9213cf0f6..9254f2597 100644 --- a/Server/src/main/content/global/handlers/iface/AutocastSelectPlugin.java +++ b/Server/src/main/content/global/handlers/iface/AutocastSelectPlugin.java @@ -44,7 +44,7 @@ public final class AutocastSelectPlugin extends ComponentPlugin { player.removeAttribute("autocast_select"); final WeaponInterface w = player.getExtension(WeaponInterface.class); if (w != null) { - w.selectAutoSpell(button, true); + w.selectAutoSpell(button, true, false); player.getInterfaceManager().openTab(w); } return true; diff --git a/Server/src/main/content/global/handlers/iface/tabs/CombatTabInterface.java b/Server/src/main/content/global/handlers/iface/tabs/CombatTabInterface.java index 0ca513102..9d4c330fe 100644 --- a/Server/src/main/content/global/handlers/iface/tabs/CombatTabInterface.java +++ b/Server/src/main/content/global/handlers/iface/tabs/CombatTabInterface.java @@ -82,7 +82,7 @@ public class CombatTabInterface extends ComponentPlugin { if (button == 4 || button == 5) { inter.openAutocastSelect(); } else if (p.getProperties().getAutocastSpell() != null) { - inter.selectAutoSpell(-1, false); + inter.selectAutoSpell(-1, false, true); } return true; } diff --git a/Server/src/main/content/global/skill/magic/MagicAltarListener.kt b/Server/src/main/content/global/skill/magic/MagicAltarListener.kt index 70ec07e25..704190b33 100644 --- a/Server/src/main/content/global/skill/magic/MagicAltarListener.kt +++ b/Server/src/main/content/global/skill/magic/MagicAltarListener.kt @@ -54,7 +54,7 @@ class MagicAltarListener : InteractionListener { } val weaponInterface = player.getExtension(WeaponInterface::class.java) if (weaponInterface != null && player.properties.autocastSpell != null) { - weaponInterface.selectAutoSpell(-1, true) + weaponInterface.selectAutoSpell(-1, true, true) } if (SpellBook.forInterface(player.spellBookManager.spellBook) == if (altar.id == ANCIENT_ALTAR) SpellBook.ANCIENT else SpellBook.LUNAR) { player.dispatch(SpellbookChangeEvent( diff --git a/Server/src/main/content/global/skill/skillcapeperks/SkillcapePerks.kt b/Server/src/main/content/global/skill/skillcapeperks/SkillcapePerks.kt index 148696543..e10a8d372 100644 --- a/Server/src/main/content/global/skill/skillcapeperks/SkillcapePerks.kt +++ b/Server/src/main/content/global/skill/skillcapeperks/SkillcapePerks.kt @@ -215,7 +215,7 @@ enum class SkillcapePerks(val attribute: String, val effect: ((Player) -> Unit)? } val weaponInterface = player.getExtension(WeaponInterface::class.java) if (weaponInterface != null && player.properties.autocastSpell != null) { - weaponInterface.selectAutoSpell(-1, true) + weaponInterface.selectAutoSpell(-1, true, true) } player.dispatch(SpellbookChangeEvent( SpellBookManager.SpellBook.forInterface(player.spellBookManager.spellBook), diff --git a/Server/src/main/core/game/global/action/EquipHandler.kt b/Server/src/main/core/game/global/action/EquipHandler.kt index 29299e238..48cc0be93 100644 --- a/Server/src/main/core/game/global/action/EquipHandler.kt +++ b/Server/src/main/core/game/global/action/EquipHandler.kt @@ -82,7 +82,7 @@ class EquipHandler : InteractionListener { if (itemEquipmentSlot == EquipmentContainer.SLOT_WEAPON) { player.properties.autocastSpell = null val wif = player.getExtension(WeaponInterface::class.java) - wif.selectAutoSpell(-1, true) + wif.selectAutoSpell(-1, true, true) wif.openAutocastSelect() } } diff --git a/Server/src/main/core/game/node/entity/combat/MagicSwingHandler.kt b/Server/src/main/core/game/node/entity/combat/MagicSwingHandler.kt index 0062c20d9..dbcc956f0 100644 --- a/Server/src/main/core/game/node/entity/combat/MagicSwingHandler.kt +++ b/Server/src/main/core/game/node/entity/combat/MagicSwingHandler.kt @@ -52,7 +52,7 @@ open class MagicSwingHandler (vararg flags: SwingHandlerFlag) entity.properties.spell = null val inter = entity.getExtension(WeaponInterface::class.java) if (inter != null) { - inter.selectAutoSpell(-1, true) + inter.selectAutoSpell(-1, true, true) entity.properties.combatPulse.updateStyle() } return -1 diff --git a/Server/src/main/core/game/node/entity/combat/equipment/WeaponInterface.java b/Server/src/main/core/game/node/entity/combat/equipment/WeaponInterface.java index cb74f3fa6..7c00c5145 100644 --- a/Server/src/main/core/game/node/entity/combat/equipment/WeaponInterface.java +++ b/Server/src/main/core/game/node/entity/combat/equipment/WeaponInterface.java @@ -125,6 +125,16 @@ public final class WeaponInterface extends Component { */ public static final int STYLE_CAST = 8; + /** + * Attribute storing the player's autocast spell for reload upon relog. + */ + public static final String attributeAutocastButtonId = "/save:autocast:buttonid"; + + /** + * Attribute storing the player's autocast combat style for reload upon relog. + */ + public static final String attributeAutocastStyle = "/save:autocast:style"; + /** * The player. */ @@ -259,7 +269,7 @@ public final class WeaponInterface extends Component { player.getProperties().setAttackAnimation(attackAnimations[player.getSettings().getAttackStyleIndex()]); } if (current != WeaponInterfaces.STAFF) { - selectAutoSpell(-1, false); + selectAutoSpell(-1, false, false); PacketRepository.send(InterfaceConfig.class, new InterfaceConfigContext(player, id, getConfig(current.getAttackStyles().length, current.getInterfaceId()), !specialBar)); } else { //if staff PacketRepository.send(InterfaceConfig.class, new InterfaceConfigContext(player, id, 87, !specialBar)); @@ -268,7 +278,7 @@ public final class WeaponInterface extends Component { if (current == WeaponInterfaces.STAFF && player.getAttribute("autocast_select", false)) { open(); } - selectAutoSpell(-1, true); + selectAutoSpell(-1, true, false); } PacketRepository.send(StringPacket.class, new StringContext(player, name, id, 0)); if (player.getSettings().isSpecialToggled()) { @@ -317,7 +327,7 @@ public final class WeaponInterface extends Component { */ private void checkStaffConfigs(int slot) { if (current != WeaponInterfaces.STAFF) { - selectAutoSpell(-1, false); + selectAutoSpell(-1, false, false); return; } boolean defensive = slot == 3; @@ -354,7 +364,7 @@ public final class WeaponInterface extends Component { * @param buttonId The button id. * @param adjustAttackStyle If the attack style should be adjusted. */ - public void selectAutoSpell(int buttonId, boolean adjustAttackStyle) { + public void selectAutoSpell(int buttonId, boolean adjustAttackStyle, boolean forgetAutocast) { boolean modern = player.getSpellBookManager().getSpellBook() == Components.MAGIC_192; int[] data = modern ? MODERN_SPELL_IDS : ANCIENT_SPELL_IDS; if (modern && player.getEquipment().getNew(3).getName().equalsIgnoreCase("Slayer's staff")) { @@ -376,8 +386,13 @@ public final class WeaponInterface extends Component { } } } + if (forgetAutocast) { + removeAttributes(player, attributeAutocastButtonId, attributeAutocastStyle); + } if (buttonId < 0) { player.getProperties().setAutocastSpell(null); + setComponentVisibility(player, Components.WEAPON_STAFF_SEL_90, 83, false); + setComponentVisibility(player, Components.WEAPON_STAFF_SEL_90, 183, false); if (adjustAttackStyle && current != null) { setAttackStyle(3); player.getProperties().getCombatPulse().updateStyle(); @@ -389,6 +404,8 @@ public final class WeaponInterface extends Component { player.getPacketDispatch().sendInterfaceConfig(Components.WEAPON_STAFF_SEL_90, 83, !defensive); current = (CombatSpell) (modern ? SpellBookManager.SpellBook.MODERN.getSpell(data[buttonId]) : SpellBookManager.SpellBook.ANCIENT.getSpell(data[buttonId])); player.getProperties().setAutocastSpell(current); + setAttribute(player, attributeAutocastButtonId, buttonId); + setAttribute(player, attributeAutocastStyle, player.getSettings().getAttackStyleIndex()); int configId = configStart + (2 * buttonId); if (modern && player.getEquipment().getNew(3).getName().equalsIgnoreCase("Slayer's staff") || modern && player.getEquipment().getNew(3).getName().equalsIgnoreCase("Void knight mace")) { boolean slayer = player.getEquipment().getNew(3).getName().equalsIgnoreCase("Slayer's staff"); @@ -508,6 +525,37 @@ public final class WeaponInterface extends Component { return WeaponInterfaces.values()[slot]; } + /** + * Restores the player's autocast settings to what they were in the prior login session. + * Returns false if the player has no autocast to restore, true otherwise. + */ + public boolean restoreAutocast() { + int buttonID = getAttribute(player, attributeAutocastButtonId, -1); + int style = getAttribute(player, attributeAutocastStyle, -1); + if (buttonID < 0 || style < 0 || current != WeaponInterfaces.STAFF || !canAutocast(false)) { + removeAttributes(player, attributeAutocastButtonId, attributeAutocastStyle); + return false; + } + restoreAttackStyle(style); + checkStaffConfigs(style); + selectAutoSpell(buttonID, false, false); + player.getProperties().getCombatPulse().updateStyle(); + return true; + } + + /** + * Restores the player's attack style settings to what they were in the prior login session. + */ + public void restoreAttackStyle(int index) { + if (index < 0 || index >= current.getAttackStyles().length) return; + player.getSettings().toggleAttackStyleIndex(index); + player.getProperties().setAttackStyle(current.getAttackStyles()[index]); + if (index < attackAnimations.length && !player.getAppearance().isNpc()) { + player.getProperties().setAttackAnimation(attackAnimations[index]); + } + player.getProperties().getCombatPulse().updateStyle(); + } + /** * Represents an attack style. * @author Emperor diff --git a/Server/src/main/core/game/node/entity/player/info/login/LoginConfiguration.java b/Server/src/main/core/game/node/entity/player/info/login/LoginConfiguration.java index b7af34638..a4a0ebb66 100644 --- a/Server/src/main/core/game/node/entity/player/info/login/LoginConfiguration.java +++ b/Server/src/main/core/game/node/entity/player/info/login/LoginConfiguration.java @@ -1,5 +1,6 @@ package core.game.node.entity.player.info.login; +import core.api.EquipmentSlot; import core.game.component.Component; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.emote.Emotes; @@ -11,6 +12,7 @@ import core.net.packet.out.Interface; import core.plugin.Plugin; import core.ServerConstants; import core.game.interaction.InteractionListeners; +import core.game.node.entity.combat.equipment.WeaponInterface; import content.global.handlers.iface.RulesAndInfo; import core.tools.Log; import core.game.world.GameWorld; @@ -138,15 +140,23 @@ public final class LoginConfiguration { player.getEmoteManager().unlock(Emotes.SAFETY_FIRST); } + int savedAttackStyle = player.getSettings().getAttackStyleIndex(); for (Item item : player.getEquipment().toArray()) { //Run equip hooks for all items equipped on login. //We should have already been doing this. //Frankly, I don't even want to imagine the number of bugs us *not* doing this has caused. if (item == null) continue; - player.getEquipment().remove(item); - if (!InteractionListeners.run(item.getId(), player, item, true) || !player.getEquipment().add(item, true, false)) { - player.sendMessage(colorize("%RAs you can no longer wear " + item.getName() + ", it has been unequipped.")); - addItemOrBank(player, item.getId(), item.getAmount()); + if (player.getEquipment().remove(item)) { + if (!InteractionListeners.run(item.getId(), player, item, true) || !player.getEquipment().add(item, true, false)) { + player.sendMessage(colorize("%RAs you can no longer wear " + item.getName() + ", it has been unequipped.")); + addItemOrBank(player, item.getId(), item.getAmount()); + } else if (equipSlot(item.getId()) == EquipmentSlot.WEAPON) { + // Put player's weapon style back to how it was before it was re-equipped. + WeaponInterface inter = player.getExtension(WeaponInterface.class); + if (!inter.restoreAutocast()) { + inter.restoreAttackStyle(savedAttackStyle); + } + } } } diff --git a/Server/src/main/core/game/node/entity/player/link/InterfaceManager.java b/Server/src/main/core/game/node/entity/player/link/InterfaceManager.java index 859b0aed6..dde450c7a 100644 --- a/Server/src/main/core/game/node/entity/player/link/InterfaceManager.java +++ b/Server/src/main/core/game/node/entity/player/link/InterfaceManager.java @@ -410,7 +410,7 @@ public final class InterfaceManager { } } if (player.getProperties().getAutocastSpell() != null) { - inter.selectAutoSpell(inter.getAutospellId(player.getProperties().getAutocastSpell().getSpellId()), true); + inter.selectAutoSpell(inter.getAutospellId(player.getProperties().getAutocastSpell().getSpellId()), true, false); } } diff --git a/Server/src/main/core/game/system/timer/impl/SpellbookSwap.kt b/Server/src/main/core/game/system/timer/impl/SpellbookSwap.kt index b689e3a0a..3fe48b5f5 100644 --- a/Server/src/main/core/game/system/timer/impl/SpellbookSwap.kt +++ b/Server/src/main/core/game/system/timer/impl/SpellbookSwap.kt @@ -79,7 +79,7 @@ class SpellbookSwap : PersistTimer(runInterval = minutesToTicks(2), identifier = // Clear autocast val weaponInterface = entity.getExtension(WeaponInterface::class.java) if (weaponInterface != null && entity.properties.autocastSpell != null) { - weaponInterface.selectAutoSpell(-1, true) + weaponInterface.selectAutoSpell(-1, true, true) } // Close autocast selection interface if it's still open if (entity.getAttribute("autocast_select", false)) {