From aaf6734bd99146c17f208e63a10c0605fde29336 Mon Sep 17 00:00:00 2001 From: KAMKEEL <28842281+KAMKEEL@users.noreply.github.com> Date: Tue, 11 Aug 2026 03:22:56 -0400 Subject: [PATCH] Take deleted abilities away from players properly Deleting a custom ability only cleared the unlocked list. An active toggle kept a direct reference and went on ticking, stale hotbar slots survived, and the client was never resynced so it still showed and could select the ability. Selecting a key the player no longer has now deselects instead of firing whatever was previously selected. --- .../npcs/controllers/AbilityController.java | 18 ++++++++++++++++++ .../ability/AbilityHotbarSelectPacket.java | 7 +++---- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/main/java/kamkeel/npcs/controllers/AbilityController.java b/src/main/java/kamkeel/npcs/controllers/AbilityController.java index c352119dd..6c5810188 100644 --- a/src/main/java/kamkeel/npcs/controllers/AbilityController.java +++ b/src/main/java/kamkeel/npcs/controllers/AbilityController.java @@ -45,7 +45,9 @@ import net.minecraftforge.common.util.Constants; import noppes.npcs.controllers.data.AbilityScript; import noppes.npcs.controllers.data.ChainedAbilityScript; import net.minecraft.entity.EntityLivingBase; +import kamkeel.npcs.controllers.sync.handlers.PlayerAbilitySyncHelper; import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.nbt.NBTTagCompound; import noppes.npcs.CustomNpcs; import noppes.npcs.LogWriter; @@ -470,9 +472,25 @@ public class AbilityController implements IAbilityHandler { pData.abilityData.lockAbility(name); changed = true; } + + // An active toggle holds the resolved Ability directly and keeps ticking it, + // so drop it without firing onToggle against an unregistered ability. + if (uuid != null && !uuid.isEmpty()) { + pData.abilityData.setToggleEntryDirect(uuid, 0); + } + pData.abilityData.setToggleEntryDirect(name, 0); + + if (pData.hotbarData != null) { + pData.hotbarData.validateSlots(); + } + if (changed) { pData.save(); } + + if (pData.player instanceof EntityPlayerMP) { + PlayerAbilitySyncHelper.syncAbilities((EntityPlayerMP) pData.player); + } } } } diff --git a/src/main/java/kamkeel/npcs/network/packets/player/ability/AbilityHotbarSelectPacket.java b/src/main/java/kamkeel/npcs/network/packets/player/ability/AbilityHotbarSelectPacket.java index ba97769a0..87affdc1b 100644 --- a/src/main/java/kamkeel/npcs/network/packets/player/ability/AbilityHotbarSelectPacket.java +++ b/src/main/java/kamkeel/npcs/network/packets/player/ability/AbilityHotbarSelectPacket.java @@ -50,10 +50,9 @@ public final class AbilityHotbarSelectPacket extends AbstractPacket { // Deselect data.abilityData.setSelectedIndex(-1); } else { - int index = data.abilityData.getUnlockedAbilityList().indexOf(key); - if (index >= 0) { - data.abilityData.setSelectedIndex(index); - } + // A key the player no longer has deselects rather than leaving the previous + // selection in place, which would fire a different ability than the one shown. + data.abilityData.setSelectedIndex(data.abilityData.getUnlockedAbilityList().indexOf(key)); } data.save(); }