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/FriendsListInterface.kt b/Server/src/main/content/global/handlers/iface/FriendsListInterface.kt new file mode 100644 index 000000000..6617812a0 --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/FriendsListInterface.kt @@ -0,0 +1,23 @@ +package content.global.handlers.iface + +import core.ServerConstants +import core.api.setInterfaceText +import core.game.component.Component +import core.game.node.entity.player.Player +import core.game.world.GameWorld +import org.rs09.consts.Components + +/** + * Makes sure the server name is always applied to the Friends List interface. + */ + +class FriendsListInterface : Component(Components.FRIENDS2_550) { + companion object { + private const val TITLE_ELEMENT = 3 + } + + override fun open(player: Player) { + super.open(player) + setInterfaceText(player, "Friends List - ${ServerConstants.SERVER_NAME} ${GameWorld.settings!!.worldId}", Components.FRIENDS2_550, TITLE_ELEMENT) + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/iface/bank/BankDepositBoxInterface.kt b/Server/src/main/content/global/handlers/iface/bank/BankDepositBoxInterface.kt index e190a8dde..1d2692aa3 100644 --- a/Server/src/main/content/global/handlers/iface/bank/BankDepositBoxInterface.kt +++ b/Server/src/main/content/global/handlers/iface/bank/BankDepositBoxInterface.kt @@ -1,9 +1,11 @@ package content.global.handlers.iface.bank +import core.ServerConstants import core.api.animate import core.api.dumpBeastOfBurden import core.api.runWorldTask import core.api.sendMessage +import core.api.setInterfaceText import core.game.component.Component import core.game.node.entity.player.Player import org.rs09.consts.Animations @@ -21,6 +23,7 @@ class BankDepositBoxInterface : InterfaceListener { private const val BUTTON_DEPOSIT_BOB = 13 private const val MENU_ELEMENT = 11 + private const val TITLE_ELEMENT = 12 private const val OP_AMOUNT_ONE = 155 private const val OP_AMOUNT_FIVE = 196 private const val OP_AMOUNT_TEN = 124 @@ -65,12 +68,14 @@ class BankDepositBoxInterface : InterfaceListener { } override fun defineInterfaceListeners() { + onOpen(Components.BANK_DEPOSIT_BOX_11) { player, _ -> + setInterfaceText(player, "The Bank of ${ServerConstants.SERVER_NAME} - Deposit Box", Components.BANK_DEPOSIT_BOX_11, TITLE_ELEMENT) + return@onOpen true + } on(Components.BANK_DEPOSIT_BOX_11, ::handleDepositBoxMenu) - on( - Components.BANK_DEPOSIT_BOX_11, - BUTTON_DEPOSIT_BOB - ) { player, _, _, _, _, _ -> - dumpBeastOfBurden(player); true + on(Components.BANK_DEPOSIT_BOX_11, BUTTON_DEPOSIT_BOB) { player, _, _, _, _, _ -> + dumpBeastOfBurden(player) + return@on true } } } \ No newline at end of file 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/content/region/asgarnia/falador/handlers/BankNotices.kt b/Server/src/main/content/region/asgarnia/falador/handlers/BankNotices.kt index 11a124e8b..0e1f4a309 100644 --- a/Server/src/main/content/region/asgarnia/falador/handlers/BankNotices.kt +++ b/Server/src/main/content/region/asgarnia/falador/handlers/BankNotices.kt @@ -2,6 +2,7 @@ package content.region.asgarnia.falador.handlers import content.global.handlers.iface.ScrollInterface import content.global.handlers.iface.ScrollLine +import core.ServerConstants import core.game.interaction.InteractionListener import org.rs09.consts.Components import org.rs09.consts.Scenery @@ -23,7 +24,7 @@ class BankNotices : InteractionListener { ) val CONTENTS_PASSWORD = arrayOf( - ScrollLine("The Bank of RuneScape would like to remind customers that",4), + ScrollLine("The Bank of ${ServerConstants.SERVER_NAME} would like to remind customers that",4), ScrollLine("they should NEVER tell ANYONE their password.",5), ScrollLine("If someone asks you to say your password, please report",7), 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..87832e062 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 ATTRIBUTE_AUTOCAST_BUTTON_ID = "/save:autocast:buttonid"; + + /** + * Attribute storing the player's autocast combat style for reload upon relog. + */ + public static final String ATTRIBUTE_AUTOCAST_STYLE = "/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()) { @@ -298,6 +308,9 @@ public final class WeaponInterface extends Component { slot = 3; } } + if (current == WeaponInterfaces.STAFF && slot <= 2) { + removeAttributes(player, ATTRIBUTE_AUTOCAST_BUTTON_ID, ATTRIBUTE_AUTOCAST_STYLE); + } if (slot < 0 || slot >= current.getAttackStyles().length) { return false; } @@ -317,7 +330,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 +367,9 @@ 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) { + int stdAutocastBookChild = 83; + int defAutocastBookChild = 183; 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 +391,13 @@ public final class WeaponInterface extends Component { } } } + if (forgetAutocast) { + removeAttributes(player, ATTRIBUTE_AUTOCAST_BUTTON_ID, ATTRIBUTE_AUTOCAST_STYLE); + } if (buttonId < 0) { player.getProperties().setAutocastSpell(null); + setComponentVisibility(player, Components.WEAPON_STAFF_SEL_90, defAutocastBookChild, false); + setComponentVisibility(player, Components.WEAPON_STAFF_SEL_90, stdAutocastBookChild, false); if (adjustAttackStyle && current != null) { setAttackStyle(3); player.getProperties().getCombatPulse().updateStyle(); @@ -385,10 +405,12 @@ public final class WeaponInterface extends Component { return; } boolean defensive = player.getSettings().getAttackStyleIndex() == 3; - player.getPacketDispatch().sendInterfaceConfig(Components.WEAPON_STAFF_SEL_90, 183, defensive); - player.getPacketDispatch().sendInterfaceConfig(Components.WEAPON_STAFF_SEL_90, 83, !defensive); + setComponentVisibility(player, Components.WEAPON_STAFF_SEL_90, defAutocastBookChild, defensive); + setComponentVisibility(player, Components.WEAPON_STAFF_SEL_90, stdAutocastBookChild, !defensive); current = (CombatSpell) (modern ? SpellBookManager.SpellBook.MODERN.getSpell(data[buttonId]) : SpellBookManager.SpellBook.ANCIENT.getSpell(data[buttonId])); player.getProperties().setAutocastSpell(current); + setAttribute(player, ATTRIBUTE_AUTOCAST_BUTTON_ID, buttonId); + setAttribute(player, ATTRIBUTE_AUTOCAST_STYLE, 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 +530,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, ATTRIBUTE_AUTOCAST_BUTTON_ID, -1); + int style = getAttribute(player, ATTRIBUTE_AUTOCAST_STYLE, -1); + if (buttonID < 0 || style < 0 || !canAutocast(false)) { + removeAttributes(player, ATTRIBUTE_AUTOCAST_BUTTON_ID, ATTRIBUTE_AUTOCAST_STYLE); + 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 fb50ee051..9fdf421cb 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,7 @@ package core.game.node.entity.player.info.login; +import core.api.Container; +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 +13,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; @@ -103,7 +106,7 @@ public final class LoginConfiguration { setInterfaceText(player, "", 378, 39); setInterfaceText(player, "Discord Invite", 378, 14); setInterfaceText(player, "Discord Invite", 378, 129); - setInterfaceText(player, "You can gain more credits by reporting bugs and various other methods of contribution.", 378, 93); + setInterfaceText(player, "You can gain more credits by testing new features and other methods of contribution.", 378, 93); setInterfaceText(player, player.getDetails().getCredits() + "", 378, 96); setInterfaceText(player, "Credits", 378, 94); setInterfaceText(player, "", 378, 229); @@ -138,15 +141,28 @@ 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 (removeItem(player, item, Container.EQUIPMENT)) { + 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 (equipSlot(item.getId()) == EquipmentSlot.WEAPON) { + // Default to accurate style if weapon frisk failed + WeaponInterface inter = player.getExtension(WeaponInterface.class); + inter.restoreAttackStyle(0); + } + } else if (equipSlot(item.getId()) == EquipmentSlot.WEAPON) { + WeaponInterface inter = player.getExtension(WeaponInterface.class); + // Put player's weapon style back to how it was before it was re-equipped + 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 4a0fb00ae..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 @@ -14,6 +14,7 @@ import core.net.packet.out.Interface; import core.net.packet.out.WindowsPane; import core.tools.Log; import org.rs09.consts.Components; +import content.global.handlers.iface.FriendsListInterface; import content.region.misc.tutisland.handlers.TutorialStage; import static core.api.ContentAPIKt.log; @@ -352,23 +353,25 @@ public final class InterfaceManager { if (inter == null) { player.addExtension(WeaponInterface.class, inter = new WeaponInterface(player)); } - openTab(0, inter); + openTab(i, inter); break; case 6: - openTab(6, new Component(player.getSpellBookManager().getSpellBook())); // Magic + openTab(i, new Component(player.getSpellBookManager().getSpellBook())); // Magic break; case 7: if (player.getFamiliarManager().hasFamiliar()) { - openTab(7, new Component(662)); + openTab(i, new Component(Components.LORE_STATS_SIDE_662)); // Summoning } break; + case 8: + openTab(i, new FriendsListInterface()); // Friends + break; default: openTab(i, new Component(DEFAULT_TABS[i])); } } else if (tab.isHidden()) { int child = (i < 7 ? 38 : 13) + i; -// boolean resize = isResizable(); //TODO: player.getPacketDispatch().sendInterfaceConfig(getWindowPaneId(), child, false); player.getPacketDispatch().sendInterfaceConfig(getWindowPaneId(), child + 7, false); tabs[i].setHidden(false); @@ -380,31 +383,34 @@ public final class InterfaceManager { * Opens the default tabs. */ public void openDefaultTabs() { - // player.getPacketDispatch().sendInterfaceConfig(548, 51, false); WeaponInterface inter = player.getExtension(WeaponInterface.class); if (inter == null) { player.addExtension(WeaponInterface.class, inter = new WeaponInterface(player)); } - //sendTab(16, 747); // Summoning bar - openTab(0, inter); // Attack - openTab(1, new Component(Components.STATS_320)); // Skills - openTab(2, new Component(Components.QUESTJOURNAL_V2_274)); // Quest - openTab(3, new Component(Components.INVENTORY_149)); // inventory - openTab(4, new Component(Components.WORNITEMS_387)); // Equipment - openTab(5, new Component(Components.PRAYER_271)); // Prayer - openTab(6, new Component(player.getSpellBookManager().getSpellBook())); // Magic - if (player.getFamiliarManager().hasFamiliar()) { - openTab(7, new Component(Components.LORE_STATS_SIDE_662)); // summoning. + for (int i = 0; i < tabs.length; i++) { + switch (i) { + case 0: + openTab(i, inter); + break; + case 6: + openTab(i, new Component(player.getSpellBookManager().getSpellBook())); // Magic + break; + case 7: + if (player.getFamiliarManager().hasFamiliar()) { + openTab(i, new Component(Components.LORE_STATS_SIDE_662)); // Summoning + } + break; + case 8: + if (tabs[i] == null) { + openTab(i, new FriendsListInterface()); // Friends + } + break; + default: + openTab(i, new Component(DEFAULT_TABS[i])); + } } - openTab(8, new Component(Components.FRIENDS2_550)); // Friends - openTab(9, new Component(Components.IGNORE2_551)); // Ignores - openTab(10, new Component(Components.CLANJOIN_589)); // Clan chat - openTab(11, new Component(Components.OPTIONS_261)); // Settings - openTab(12, new Component(Components.EMOTES_464)); // Emotes - openTab(13, new Component(Components.MUSIC_V3_187)); // Music - openTab(14, new Component(Components.LOGOUT_182)); // Logout if (player.getProperties().getAutocastSpell() != null) { - inter.selectAutoSpell(inter.getAutospellId(player.getProperties().getAutocastSpell().getSpellId()), true); + inter.selectAutoSpell(inter.getAutospellId(player.getProperties().getAutocastSpell().getSpellId()), true, false); } } @@ -428,25 +434,13 @@ public final class InterfaceManager { * Closes the default tabs. */ public void closeDefaultTabs() { - WeaponInterface inter = player.getExtension(WeaponInterface.class); - if (inter != null) { - close(inter); // Attack + for (int i = 0; i < tabs.length; i++) { + Component tab = tabs[i]; + if (tab != null) { + close(tab); + tabs[i] = null; + } } - close(new Component(Components.STATS_320)); // Skills - close(new Component(Components.QUESTJOURNAL_V2_274)); // Quest - close(new Component(Components.AREA_TASK_259)); // Diary - close(new Component(Components.INVENTORY_149)); // inventory - close(new Component(Components.WORNITEMS_387)); // Equipment - close(new Component(Components.PRAYER_271)); // Prayer - close(new Component(player.getSpellBookManager().getSpellBook())); - close(new Component(Components.LORE_STATS_SIDE_662)); // summoning. - close(new Component(Components.FRIENDS2_550)); // Friends - close(new Component(Components.IGNORE2_551)); // Ignores - close(new Component(Components.CLANJOIN_589)); // Clan chat - close(new Component(Components.OPTIONS_261)); // Settings - close(new Component(Components.EMOTES_464)); // Emotes - close(new Component(Components.MUSIC_V3_187)); // Music - //close(new Component(Components.LOGOUT_182)); // Logout } /** 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)) {