aboutsummaryrefslogtreecommitdiff
path: root/src/main/kotlin/features/inventory/SlotLocking.kt
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/kotlin/features/inventory/SlotLocking.kt')
-rw-r--r--src/main/kotlin/features/inventory/SlotLocking.kt178
1 files changed, 144 insertions, 34 deletions
diff --git a/src/main/kotlin/features/inventory/SlotLocking.kt b/src/main/kotlin/features/inventory/SlotLocking.kt
index fc09476..d3348a2 100644
--- a/src/main/kotlin/features/inventory/SlotLocking.kt
+++ b/src/main/kotlin/features/inventory/SlotLocking.kt
@@ -2,19 +2,33 @@
package moe.nea.firmament.features.inventory
-import com.mojang.blaze3d.systems.RenderSystem
import java.util.UUID
import org.lwjgl.glfw.GLFW
+import util.render.CustomRenderLayers
+import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers
+import kotlinx.serialization.descriptors.SerialDescriptor
+import kotlinx.serialization.encoding.Decoder
+import kotlinx.serialization.encoding.Encoder
+import kotlinx.serialization.json.JsonDecoder
+import kotlinx.serialization.json.JsonElement
+import kotlinx.serialization.json.JsonObject
+import kotlinx.serialization.json.JsonPrimitive
+import kotlinx.serialization.json.int
import kotlinx.serialization.serializer
import net.minecraft.client.gui.screen.ingame.HandledScreen
+import net.minecraft.client.render.RenderLayer
+import net.minecraft.client.render.RenderLayers
+import net.minecraft.client.render.TexturedRenderLayers
import net.minecraft.entity.player.PlayerInventory
import net.minecraft.screen.GenericContainerScreenHandler
import net.minecraft.screen.slot.Slot
import net.minecraft.screen.slot.SlotActionType
import net.minecraft.util.Identifier
+import net.minecraft.util.StringIdentifiable
import moe.nea.firmament.annotations.Subscribe
+import moe.nea.firmament.events.FeaturesInitializedEvent
import moe.nea.firmament.events.HandledScreenForegroundEvent
import moe.nea.firmament.events.HandledScreenKeyPressedEvent
import moe.nea.firmament.events.HandledScreenKeyReleasedEvent
@@ -35,8 +49,8 @@ import moe.nea.firmament.util.mc.ScreenUtil.getSlotByIndex
import moe.nea.firmament.util.mc.SlotUtils.swapWithHotBar
import moe.nea.firmament.util.mc.displayNameAccordingToNbt
import moe.nea.firmament.util.mc.loreAccordingToNbt
-import moe.nea.firmament.util.render.GuiRenderLayers
import moe.nea.firmament.util.render.drawLine
+import moe.nea.firmament.util.skyblock.DungeonUtil
import moe.nea.firmament.util.skyblockUUID
import moe.nea.firmament.util.unformattedString
@@ -49,9 +63,66 @@ object SlotLocking : FirmamentFeature {
val lockedSlots: MutableSet<Int> = mutableSetOf(),
val lockedSlotsRift: MutableSet<Int> = mutableSetOf(),
val lockedUUIDs: MutableSet<UUID> = mutableSetOf(),
- val boundSlots: MutableMap<Int, Int> = mutableMapOf()
+ val boundSlots: BoundSlots = BoundSlots()
)
+ @Serializable
+ data class BoundSlot(
+ val hotbar: Int,
+ val inventory: Int,
+ )
+
+ @Serializable(with = BoundSlots.Serializer::class)
+ data class BoundSlots(
+ val pairs: MutableSet<BoundSlot> = mutableSetOf()
+ ) {
+ fun findMatchingSlots(index: Int): List<BoundSlot> {
+ return pairs.filter { it.hotbar == index || it.inventory == index }
+ }
+
+ fun removeDuplicateForInventory(index: Int) {
+ pairs.removeIf { it.inventory == index }
+ }
+
+ fun removeAllInvolving(index: Int): Boolean {
+ return pairs.removeIf { it.inventory == index || it.hotbar == index }
+ }
+
+ fun insert(hotbar: Int, inventory: Int) {
+ if (!TConfig.allowMultiBinding) {
+ removeAllInvolving(hotbar)
+ removeAllInvolving(inventory)
+ }
+ pairs.add(BoundSlot(hotbar, inventory))
+ }
+
+ object Serializer : KSerializer<BoundSlots> {
+ override val descriptor: SerialDescriptor
+ get() = serializer<JsonElement>().descriptor
+
+ override fun serialize(
+ encoder: Encoder,
+ value: BoundSlots
+ ) {
+ serializer<MutableSet<BoundSlot>>()
+ .serialize(encoder, value.pairs)
+ }
+
+ override fun deserialize(decoder: Decoder): BoundSlots {
+ decoder as JsonDecoder
+ val json = decoder.decodeJsonElement()
+ if (json is JsonObject) {
+ return BoundSlots(json.entries.map {
+ BoundSlot(it.key.toInt(), (it.value as JsonPrimitive).int)
+ }.toMutableSet())
+ }
+ return BoundSlots(decoder.json.decodeFromJsonElement(serializer<MutableSet<BoundSlot>>(), json))
+
+ }
+ }
+ }
+
+
object TConfig : ManagedConfig(identifier, Category.INVENTORY) {
val lockSlot by keyBinding("lock") { GLFW.GLFW_KEY_L }
val lockUUID by keyBindingWithOutDefaultModifiers("lock-uuid") {
@@ -59,6 +130,19 @@ object SlotLocking : FirmamentFeature {
}
val slotBind by keyBinding("bind") { GLFW.GLFW_KEY_L }
val slotBindRequireShift by toggle("require-quick-move") { true }
+ val slotRenderLines by choice("bind-render") { SlotRenderLinesMode.ONLY_BOXES }
+ val allowMultiBinding by toggle("multi-bind") { true } // TODO: filter based on this option
+ val allowDroppingInDungeons by toggle("drop-in-dungeons") { true }
+ }
+
+ enum class SlotRenderLinesMode : StringIdentifiable {
+ EVERYTHING,
+ ONLY_BOXES,
+ NOTHING;
+
+ override fun asString(): String {
+ return name
+ }
}
override val config: TConfig
@@ -95,7 +179,7 @@ object SlotLocking : FirmamentFeature {
if (handler.inventory.size() < 9) return false
val sellItem = handler.inventory.getStack(handler.inventory.size() - 5)
if (sellItem == null) return false
- if (sellItem.displayNameAccordingToNbt?.unformattedString == "Sell Item") return true
+ if (sellItem.displayNameAccordingToNbt.unformattedString == "Sell Item") return true
val lore = sellItem.loreAccordingToNbt
return (lore.lastOrNull() ?: return false).unformattedString == "Click to buyback!"
}
@@ -104,12 +188,16 @@ object SlotLocking : FirmamentFeature {
fun onSalvageProtect(event: IsSlotProtectedEvent) {
if (event.slot == null) return
if (!event.slot.hasStack()) return
- if (event.slot.stack.displayNameAccordingToNbt?.unformattedString != "Salvage Items") return
+ if (event.slot.stack.displayNameAccordingToNbt.unformattedString != "Salvage Items") return
val inv = event.slot.inventory
var anyBlocked = false
for (i in 0 until event.slot.index) {
val stack = inv.getStack(i)
- if (IsSlotProtectedEvent.shouldBlockInteraction(null, SlotActionType.THROW, stack))
+ if (IsSlotProtectedEvent.shouldBlockInteraction(null,
+ SlotActionType.THROW,
+ IsSlotProtectedEvent.MoveOrigin.SALVAGE,
+ stack)
+ )
anyBlocked = true
}
if (anyBlocked) {
@@ -145,20 +233,33 @@ object SlotLocking : FirmamentFeature {
}
@Subscribe
+ fun onEvent(event: FeaturesInitializedEvent) {
+ IsSlotProtectedEvent.subscribe(receivesCancelled = true, "SlotLocking:unlockInDungeons") {
+ if (it.isProtected
+ && it.origin == IsSlotProtectedEvent.MoveOrigin.DROP_FROM_HOTBAR
+ && DungeonUtil.isInActiveDungeon
+ && TConfig.allowDroppingInDungeons
+ ) {
+ it.isProtected = false
+ }
+ }
+ }
+
+ @Subscribe
fun onQuickMoveBoundSlot(it: IsSlotProtectedEvent) {
- val boundSlots = DConfig.data?.boundSlots ?: mapOf()
+ val boundSlots = DConfig.data?.boundSlots ?: BoundSlots()
val isValidAction =
it.actionType == SlotActionType.QUICK_MOVE || (it.actionType == SlotActionType.PICKUP && !TConfig.slotBindRequireShift)
if (!isValidAction) return
val handler = MC.handledScreen?.screenHandler ?: return
val slot = it.slot
if (slot != null && it.slot.inventory is PlayerInventory) {
- val boundSlot = boundSlots.entries.find {
- it.value == slot.index || it.key == slot.index
- } ?: return
+ val matchingSlots = boundSlots.findMatchingSlots(slot.index)
+ if (matchingSlots.isEmpty()) return
it.protectSilent()
- val inventorySlot = MC.handledScreen?.getSlotByIndex(boundSlot.value, true)
- inventorySlot?.swapWithHotBar(handler, boundSlot.key)
+ val boundSlot = matchingSlots.singleOrNull() ?: return
+ val inventorySlot = MC.handledScreen?.getSlotByIndex(boundSlot.inventory, true)
+ inventorySlot?.swapWithHotBar(handler, boundSlot.hotbar)
}
}
@@ -197,10 +298,8 @@ object SlotLocking : FirmamentFeature {
val boundSlots = DConfig.data?.boundSlots ?: return
lockedSlots?.remove(hotBarSlot.index)
lockedSlots?.remove(invSlot.index)
- boundSlots.entries.removeIf {
- it.value == invSlot.index
- }
- boundSlots[hotBarSlot.index] = invSlot.index
+ boundSlots.removeDuplicateForInventory(invSlot.index)
+ boundSlots.insert(hotBarSlot.index, invSlot.index)
DConfig.markDirty()
CommonSoundEffects.playSuccess()
return
@@ -214,9 +313,7 @@ object SlotLocking : FirmamentFeature {
storedLockingSlot = null
val boundSlots = DConfig.data?.boundSlots ?: return
if (slot != null)
- boundSlots.entries.removeIf {
- it.value == slot.index || it.key == slot.index
- }
+ boundSlots.removeAllInvolving(slot.index)
}
}
@@ -227,23 +324,38 @@ object SlotLocking : FirmamentFeature {
val accScreen = event.screen as AccessorHandledScreen
val sx = accScreen.x_Firmament
val sy = accScreen.y_Firmament
- boundSlots.entries.forEach {
- val hotbarSlot = findByIndex(it.key) ?: return@forEach
- val inventorySlot = findByIndex(it.value) ?: return@forEach
+ val highlitSlots = mutableSetOf<Slot>()
+ for (it in boundSlots.pairs) {
+ val hotbarSlot = findByIndex(it.hotbar) ?: continue
+ val inventorySlot = findByIndex(it.inventory) ?: continue
val (hotX, hotY) = hotbarSlot.lineCenter()
val (invX, invY) = inventorySlot.lineCenter()
- event.context.drawLine(
- invX + sx, invY + sy,
- hotX + sx, hotY + sy,
- me.shedaniel.math.Color.ofOpaque(0x00FF00)
- )
+ val anyHovered = accScreen.focusedSlot_Firmament === hotbarSlot
+ || accScreen.focusedSlot_Firmament === inventorySlot
+ if (!anyHovered && TConfig.slotRenderLines == SlotRenderLinesMode.NOTHING)
+ continue
+ if (anyHovered) {
+ highlitSlots.add(hotbarSlot)
+ highlitSlots.add(inventorySlot)
+ }
+ fun color(highlit: Boolean) =
+ if (highlit)
+ me.shedaniel.math.Color.ofOpaque(0x00FF00)
+ else
+ me.shedaniel.math.Color.ofTransparent(0xc0a0f000.toInt())
+ if (TConfig.slotRenderLines == SlotRenderLinesMode.EVERYTHING || anyHovered)
+ event.context.drawLine(
+ invX + sx, invY + sy,
+ hotX + sx, hotY + sy,
+ color(anyHovered)
+ )
event.context.drawBorder(hotbarSlot.x + sx,
hotbarSlot.y + sy,
- 16, 16, 0xFF00FF00u.toInt())
+ 16, 16, color(hotbarSlot in highlitSlots).color)
event.context.drawBorder(inventorySlot.x + sx,
inventorySlot.y + sy,
- 16, 16, 0xFF00FF00u.toInt())
+ 16, 16, color(inventorySlot in highlitSlots).color)
}
}
@@ -299,11 +411,9 @@ object SlotLocking : FirmamentFeature {
fun toggleSlotLock(slot: Slot) {
val lockedSlots = lockedSlots ?: return
- val boundSlots = DConfig.data?.boundSlots ?: mutableMapOf()
+ val boundSlots = DConfig.data?.boundSlots ?: BoundSlots()
if (slot.inventory is PlayerInventory) {
- if (boundSlots.entries.removeIf {
- it.value == slot.index || it.key == slot.index
- }) {
+ if (boundSlots.removeAllInvolving(slot.index)) {
// intentionally do nothing
} else if (slot.index in lockedSlots) {
lockedSlots.remove(slot.index)
@@ -338,7 +448,7 @@ object SlotLocking : FirmamentFeature {
val isUUIDLocked = (it.slot.stack?.skyblockUUID) in (lockedUUIDs ?: setOf())
if (isSlotLocked || isUUIDLocked) {
it.context.drawGuiTexture(
- GuiRenderLayers.GUI_TEXTURED_NO_DEPTH,
+ RenderLayer::getGuiTexturedOverlay,
when {
isSlotLocked ->
(Identifier.of("firmament:slot_locked"))