diff options
7 files changed, 97 insertions, 20 deletions
diff --git a/src/main/java/moe/nea/firmament/mixins/MixinClientPlayerEntity.java b/src/main/java/moe/nea/firmament/mixins/MixinClientPlayerEntity.java index 21e3308..16e8fc3 100644 --- a/src/main/java/moe/nea/firmament/mixins/MixinClientPlayerEntity.java +++ b/src/main/java/moe/nea/firmament/mixins/MixinClientPlayerEntity.java @@ -10,6 +10,7 @@ import moe.nea.firmament.events.IsSlotProtectedEvent; import net.minecraft.client.network.ClientPlayerEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.screen.slot.Slot; +import net.minecraft.screen.slot.SlotActionType; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -24,7 +25,7 @@ public abstract class MixinClientPlayerEntity extends PlayerEntity { @Inject(method = "dropSelectedItem", at = @At("HEAD"), cancellable = true) public void onDropSelectedItem(boolean entireStack, CallbackInfoReturnable<Boolean> cir) { Slot fakeSlot = new Slot(getInventory(), getInventory().selectedSlot, 0, 0); - if (IsSlotProtectedEvent.shouldBlockInteraction(fakeSlot)) { + if (IsSlotProtectedEvent.shouldBlockInteraction(fakeSlot, SlotActionType.THROW)) { cir.setReturnValue(false); } } diff --git a/src/main/java/moe/nea/firmament/mixins/MixinHandledScreen.java b/src/main/java/moe/nea/firmament/mixins/MixinHandledScreen.java index 84c47cb..3972ffc 100644 --- a/src/main/java/moe/nea/firmament/mixins/MixinHandledScreen.java +++ b/src/main/java/moe/nea/firmament/mixins/MixinHandledScreen.java @@ -10,6 +10,7 @@ import moe.nea.firmament.events.*; import net.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.screen.ingame.HandledScreen; import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.item.ItemStack; import net.minecraft.screen.ScreenHandler; import net.minecraft.screen.slot.Slot; import net.minecraft.screen.slot.SlotActionType; @@ -63,11 +64,19 @@ public abstract class MixinHandledScreen<T extends ScreenHandler> { @Inject(method = "onMouseClick(Lnet/minecraft/screen/slot/Slot;IILnet/minecraft/screen/slot/SlotActionType;)V", at = @At("HEAD"), cancellable = true) public void onMouseClickedSlot(Slot slot, int slotId, int button, SlotActionType actionType, CallbackInfo ci) { - if (IsSlotProtectedEvent.shouldBlockInteraction(slot)) { + if (slotId == -999 && getScreenHandler() != null && actionType == SlotActionType.PICKUP) { // -999 is code for "clicked outside the main window" + ItemStack cursorStack = getScreenHandler().getCursorStack(); + if (cursorStack != null && IsSlotProtectedEvent.shouldBlockInteraction(slot, SlotActionType.THROW, cursorStack)) { + ci.cancel(); + return; + } + } + if (IsSlotProtectedEvent.shouldBlockInteraction(slot, actionType)) { ci.cancel(); + return; } if (actionType == SlotActionType.SWAP && 0 <= button && button < 9) { - if (IsSlotProtectedEvent.shouldBlockInteraction(new Slot(playerInventory, button, 0, 0))) { + if (IsSlotProtectedEvent.shouldBlockInteraction(new Slot(playerInventory, button, 0, 0), actionType)) { ci.cancel(); } } diff --git a/src/main/kotlin/moe/nea/firmament/events/IsSlotProtectedEvent.kt b/src/main/kotlin/moe/nea/firmament/events/IsSlotProtectedEvent.kt index f1a861e..02557ab 100644 --- a/src/main/kotlin/moe/nea/firmament/events/IsSlotProtectedEvent.kt +++ b/src/main/kotlin/moe/nea/firmament/events/IsSlotProtectedEvent.kt @@ -6,28 +6,37 @@ package moe.nea.firmament.events +import net.minecraft.item.ItemStack import net.minecraft.screen.slot.Slot +import net.minecraft.screen.slot.SlotActionType import net.minecraft.text.Text import moe.nea.firmament.util.CommonSoundEffects import moe.nea.firmament.util.MC data class IsSlotProtectedEvent( - val slot: Slot, var isProtected: Boolean = false + val slot: Slot?, + val actionType: SlotActionType, + var isProtected: Boolean, + val itemStackOverride: ItemStack?, ) : FirmamentEvent() { + val itemStack get() = itemStackOverride ?: slot!!.stack + + fun protect() { + isProtected = true + } + companion object : FirmamentEventBus<IsSlotProtectedEvent>() { @JvmStatic - fun shouldBlockInteraction(slot: Slot?): Boolean { - if (slot == null) return false - return publish(IsSlotProtectedEvent(slot)).isProtected.also { + @JvmOverloads + fun shouldBlockInteraction(slot: Slot?, action: SlotActionType, itemStackOverride: ItemStack? = null): Boolean { + if (slot == null && itemStackOverride == null) return false + val event = IsSlotProtectedEvent(slot, action, false, itemStackOverride) + return publish(event).isProtected.also { if (it) { - MC.player?.sendMessage(Text.translatable("firmament.protectitem").append(slot.stack.name)) + MC.player?.sendMessage(Text.translatable("firmament.protectitem").append(event.itemStack.name)) CommonSoundEffects.playFailure() } } } } - - fun protect() { - isProtected = true - } } diff --git a/src/main/kotlin/moe/nea/firmament/features/inventory/SlotLocking.kt b/src/main/kotlin/moe/nea/firmament/features/inventory/SlotLocking.kt index 76a719a..943f7dd 100644 --- a/src/main/kotlin/moe/nea/firmament/features/inventory/SlotLocking.kt +++ b/src/main/kotlin/moe/nea/firmament/features/inventory/SlotLocking.kt @@ -3,23 +3,30 @@ * * SPDX-License-Identifier: GPL-3.0-or-later */ +@file:UseSerializers(DashlessUUIDSerializer::class) package moe.nea.firmament.features.inventory +import java.util.* import org.lwjgl.glfw.GLFW import kotlinx.serialization.Serializable +import kotlinx.serialization.UseSerializers import kotlinx.serialization.serializer import net.minecraft.entity.player.PlayerInventory +import net.minecraft.screen.slot.SlotActionType import moe.nea.firmament.events.HandledScreenKeyPressedEvent import moe.nea.firmament.events.IsSlotProtectedEvent import moe.nea.firmament.events.SlotRenderEvents import moe.nea.firmament.features.FirmamentFeature import moe.nea.firmament.gui.config.ManagedConfig +import moe.nea.firmament.keybindings.SavedKeyBinding import moe.nea.firmament.mixins.accessor.AccessorHandledScreen import moe.nea.firmament.util.CommonSoundEffects import moe.nea.firmament.util.MC import moe.nea.firmament.util.SBData import moe.nea.firmament.util.data.ProfileSpecificDataHolder +import moe.nea.firmament.util.json.DashlessUUIDSerializer +import moe.nea.firmament.util.skyblockUUID object SlotLocking : FirmamentFeature { override val identifier: String @@ -29,10 +36,15 @@ object SlotLocking : FirmamentFeature { data class Data( val lockedSlots: MutableSet<Int> = mutableSetOf(), val lockedSlotsRift: MutableSet<Int> = mutableSetOf(), + + val lockedUUIDs: MutableSet<UUID> = mutableSetOf(), ) object TConfig : ManagedConfig(identifier) { - val lock by keyBinding("lock") { GLFW.GLFW_KEY_L } + val lockSlot by keyBinding("lock") { GLFW.GLFW_KEY_L } + val lockUUID by keyBindingWithOutDefaultModifiers("lock-uuid") { + SavedKeyBinding(GLFW.GLFW_KEY_L, shift = true) + } } override val config: TConfig @@ -40,6 +52,8 @@ object SlotLocking : FirmamentFeature { object DConfig : ProfileSpecificDataHolder<Data>(serializer(), "locked-slots", ::Data) + val lockedUUIDs get() = DConfig.data?.lockedUUIDs + val lockedSlots get() = when (SBData.skyblockLocation) { "rift" -> DConfig.data?.lockedSlotsRift @@ -49,7 +63,7 @@ object SlotLocking : FirmamentFeature { override fun onLoad() { HandledScreenKeyPressedEvent.subscribe { - if (!it.matches(TConfig.lock)) return@subscribe + if (!it.matches(TConfig.lockSlot)) return@subscribe val inventory = MC.handledScreen ?: return@subscribe inventory as AccessorHandledScreen @@ -65,19 +79,56 @@ object SlotLocking : FirmamentFeature { CommonSoundEffects.playSuccess() } } + HandledScreenKeyPressedEvent.subscribe { + if (!it.matches(TConfig.lockUUID)) return@subscribe + val inventory = MC.handledScreen ?: return@subscribe + inventory as AccessorHandledScreen + + val slot = inventory.focusedSlot_Firmament ?: return@subscribe + val stack = slot.stack ?: return@subscribe + val uuid = stack.skyblockUUID ?: return@subscribe + val lockedUUIDs = lockedUUIDs ?: return@subscribe + if (uuid in lockedUUIDs) { + lockedUUIDs.remove(uuid) + } else { + lockedUUIDs.add(uuid) + } + DConfig.markDirty() + CommonSoundEffects.playSuccess() + } + IsSlotProtectedEvent.subscribe { + if (it.slot != null && it.slot.inventory is PlayerInventory && it.slot.index in (lockedSlots ?: setOf())) { + it.protect() + } + } IsSlotProtectedEvent.subscribe { - if (it.slot.inventory is PlayerInventory && it.slot.index in (lockedSlots ?: setOf())) { + if (it.actionType == SlotActionType.SWAP + || it.actionType == SlotActionType.PICKUP + || it.actionType == SlotActionType.QUICK_MOVE + || it.actionType == SlotActionType.QUICK_CRAFT + || it.actionType == SlotActionType.CLONE + || it.actionType == SlotActionType.PICKUP_ALL + ) return@subscribe + val stack = it.itemStack ?: return@subscribe + val uuid = stack.skyblockUUID ?: return@subscribe + if (uuid in (lockedUUIDs ?: return@subscribe)) { it.protect() } } SlotRenderEvents.Before.subscribe { - if (it.slot.inventory is PlayerInventory && it.slot.index in (lockedSlots ?: setOf())) { + val isSlotLocked = it.slot.inventory is PlayerInventory && it.slot.index in (lockedSlots ?: setOf()) + val isUUIDLocked = (it.slot.stack?.skyblockUUID ?: return@subscribe) in (lockedUUIDs ?: setOf()) + if (isSlotLocked || isUUIDLocked) { it.context.fill( it.slot.x, it.slot.y, it.slot.x + 16, it.slot.y + 16, - 0xFFFF0000.toInt() + when { + isSlotLocked -> 0xFFFF0000.toInt() + isUUIDLocked -> 0xFF00FF00.toInt() + else -> error("Slot is locked, but not by slot or uuid") + } ) } } diff --git a/src/main/kotlin/moe/nea/firmament/util/MC.kt b/src/main/kotlin/moe/nea/firmament/util/MC.kt index 0c09306..e8611a6 100644 --- a/src/main/kotlin/moe/nea/firmament/util/MC.kt +++ b/src/main/kotlin/moe/nea/firmament/util/MC.kt @@ -13,6 +13,7 @@ import net.minecraft.client.gui.screen.ingame.HandledScreen import net.minecraft.text.Text import net.minecraft.util.math.BlockPos import moe.nea.firmament.events.TickEvent +import moe.nea.firmament.mixins.accessor.AccessorHandledScreen object MC { diff --git a/src/main/kotlin/moe/nea/firmament/util/SkyblockId.kt b/src/main/kotlin/moe/nea/firmament/util/SkyblockId.kt index fe2b4e5..a071107 100644 --- a/src/main/kotlin/moe/nea/firmament/util/SkyblockId.kt +++ b/src/main/kotlin/moe/nea/firmament/util/SkyblockId.kt @@ -5,11 +5,12 @@ */ @file:UseSerializers(DashlessUUIDSerializer::class) + package moe.nea.firmament.util import io.github.moulberry.repo.data.NEUItem import io.github.moulberry.repo.data.Rarity -import java.util.UUID +import java.util.* import kotlinx.serialization.Serializable import kotlinx.serialization.UseSerializers import kotlinx.serialization.decodeFromString @@ -73,6 +74,12 @@ private val jsonparser = Json { ignoreUnknownKeys = true } val ItemStack.extraAttributes: NbtCompound get() = getOrCreateSubNbt("ExtraAttributes") +val ItemStack.skyblockUUIDString: String? + get() = extraAttributes.getString("uuid")?.takeIf { it.isNotBlank() } + +val ItemStack.skyblockUUID: UUID? + get() = skyblockUUIDString?.let { UUID.fromString(it) } + val ItemStack.petData: HypixelPetInfo? get() { val jsonString = extraAttributes.getString("petInfo") diff --git a/src/main/resources/assets/firmament/lang/en_us.json b/src/main/resources/assets/firmament/lang/en_us.json index fb25a39..82bfb13 100644 --- a/src/main/resources/assets/firmament/lang/en_us.json +++ b/src/main/resources/assets/firmament/lang/en_us.json @@ -57,8 +57,6 @@ "firmament.config.fishing-warning": "Fishing Warning", "firmament.config.fishing-warning.display-warning": "Display a warning when you are about to hook a fish", "firmament.config.fishing-warning.highlight-wake-chain": "Highlight fishing particles", - "firmament.key.slotlocking": "Lock Slot / Slot Binding", - "firmament.key.category": "Firmament", "firmament.protectitem": "Firmament protected your item: ", "firmament.recipe.forge.time": "Forging Time: %s", "firmament.pv.skills": "Skills", @@ -97,6 +95,7 @@ "firmament.keybinding.external": "External", "firmament.config.slot-locking": "Slot Locking", "firmament.config.slot-locking.lock": "Lock Slot", + "firmament.config.slot-locking.lock-uuid": "Lock UUID (Lock Item)", "firmament.config.fixes.auto-sprint": "Auto Sprint", "firmament.config.fixes.auto-sprint-keybinding": "Auto Sprint KeyBinding", "firmament.config.fixes.auto-sprint-hud": "Sprint State Hud", |