feat: Allow multi slot binding
This commit is contained in:
@@ -4,8 +4,17 @@ package moe.nea.firmament.features.inventory
|
|||||||
|
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import org.lwjgl.glfw.GLFW
|
import org.lwjgl.glfw.GLFW
|
||||||
|
import kotlinx.serialization.KSerializer
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.UseSerializers
|
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 kotlinx.serialization.serializer
|
||||||
import net.minecraft.client.gui.screen.ingame.HandledScreen
|
import net.minecraft.client.gui.screen.ingame.HandledScreen
|
||||||
import net.minecraft.entity.player.PlayerInventory
|
import net.minecraft.entity.player.PlayerInventory
|
||||||
@@ -51,9 +60,66 @@ object SlotLocking : FirmamentFeature {
|
|||||||
val lockedSlots: MutableSet<Int> = mutableSetOf(),
|
val lockedSlots: MutableSet<Int> = mutableSetOf(),
|
||||||
val lockedSlotsRift: MutableSet<Int> = mutableSetOf(),
|
val lockedSlotsRift: MutableSet<Int> = mutableSetOf(),
|
||||||
val lockedUUIDs: MutableSet<UUID> = 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) {
|
object TConfig : ManagedConfig(identifier, Category.INVENTORY) {
|
||||||
val lockSlot by keyBinding("lock") { GLFW.GLFW_KEY_L }
|
val lockSlot by keyBinding("lock") { GLFW.GLFW_KEY_L }
|
||||||
val lockUUID by keyBindingWithOutDefaultModifiers("lock-uuid") {
|
val lockUUID by keyBindingWithOutDefaultModifiers("lock-uuid") {
|
||||||
@@ -62,6 +128,7 @@ object SlotLocking : FirmamentFeature {
|
|||||||
val slotBind by keyBinding("bind") { GLFW.GLFW_KEY_L }
|
val slotBind by keyBinding("bind") { GLFW.GLFW_KEY_L }
|
||||||
val slotBindRequireShift by toggle("require-quick-move") { true }
|
val slotBindRequireShift by toggle("require-quick-move") { true }
|
||||||
val slotRenderLines by choice("bind-render") { SlotRenderLinesMode.ONLY_BOXES }
|
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 }
|
val allowDroppingInDungeons by toggle("drop-in-dungeons") { true }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,19 +244,19 @@ object SlotLocking : FirmamentFeature {
|
|||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
fun onQuickMoveBoundSlot(it: IsSlotProtectedEvent) {
|
fun onQuickMoveBoundSlot(it: IsSlotProtectedEvent) {
|
||||||
val boundSlots = DConfig.data?.boundSlots ?: mapOf()
|
val boundSlots = DConfig.data?.boundSlots ?: BoundSlots()
|
||||||
val isValidAction =
|
val isValidAction =
|
||||||
it.actionType == SlotActionType.QUICK_MOVE || (it.actionType == SlotActionType.PICKUP && !TConfig.slotBindRequireShift)
|
it.actionType == SlotActionType.QUICK_MOVE || (it.actionType == SlotActionType.PICKUP && !TConfig.slotBindRequireShift)
|
||||||
if (!isValidAction) return
|
if (!isValidAction) return
|
||||||
val handler = MC.handledScreen?.screenHandler ?: return
|
val handler = MC.handledScreen?.screenHandler ?: return
|
||||||
val slot = it.slot
|
val slot = it.slot
|
||||||
if (slot != null && it.slot.inventory is PlayerInventory) {
|
if (slot != null && it.slot.inventory is PlayerInventory) {
|
||||||
val boundSlot = boundSlots.entries.find {
|
val matchingSlots = boundSlots.findMatchingSlots(slot.index)
|
||||||
it.value == slot.index || it.key == slot.index
|
if (matchingSlots.isEmpty()) return
|
||||||
} ?: return
|
|
||||||
it.protectSilent()
|
it.protectSilent()
|
||||||
val inventorySlot = MC.handledScreen?.getSlotByIndex(boundSlot.value, true)
|
val boundSlot = matchingSlots.singleOrNull() ?: return
|
||||||
inventorySlot?.swapWithHotBar(handler, boundSlot.key)
|
val inventorySlot = MC.handledScreen?.getSlotByIndex(boundSlot.inventory, true)
|
||||||
|
inventorySlot?.swapWithHotBar(handler, boundSlot.hotbar)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,10 +295,8 @@ object SlotLocking : FirmamentFeature {
|
|||||||
val boundSlots = DConfig.data?.boundSlots ?: return
|
val boundSlots = DConfig.data?.boundSlots ?: return
|
||||||
lockedSlots?.remove(hotBarSlot.index)
|
lockedSlots?.remove(hotBarSlot.index)
|
||||||
lockedSlots?.remove(invSlot.index)
|
lockedSlots?.remove(invSlot.index)
|
||||||
boundSlots.entries.removeIf {
|
boundSlots.removeDuplicateForInventory(invSlot.index)
|
||||||
it.value == invSlot.index
|
boundSlots.insert(hotBarSlot.index, invSlot.index)
|
||||||
}
|
|
||||||
boundSlots[hotBarSlot.index] = invSlot.index
|
|
||||||
DConfig.markDirty()
|
DConfig.markDirty()
|
||||||
CommonSoundEffects.playSuccess()
|
CommonSoundEffects.playSuccess()
|
||||||
return
|
return
|
||||||
@@ -245,9 +310,7 @@ object SlotLocking : FirmamentFeature {
|
|||||||
storedLockingSlot = null
|
storedLockingSlot = null
|
||||||
val boundSlots = DConfig.data?.boundSlots ?: return
|
val boundSlots = DConfig.data?.boundSlots ?: return
|
||||||
if (slot != null)
|
if (slot != null)
|
||||||
boundSlots.entries.removeIf {
|
boundSlots.removeAllInvolving(slot.index)
|
||||||
it.value == slot.index || it.key == slot.index
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,9 +321,10 @@ object SlotLocking : FirmamentFeature {
|
|||||||
val accScreen = event.screen as AccessorHandledScreen
|
val accScreen = event.screen as AccessorHandledScreen
|
||||||
val sx = accScreen.x_Firmament
|
val sx = accScreen.x_Firmament
|
||||||
val sy = accScreen.y_Firmament
|
val sy = accScreen.y_Firmament
|
||||||
for (it in boundSlots.entries) {
|
val highlitSlots = mutableSetOf<Slot>()
|
||||||
val hotbarSlot = findByIndex(it.key) ?: continue
|
for (it in boundSlots.pairs) {
|
||||||
val inventorySlot = findByIndex(it.value) ?: continue
|
val hotbarSlot = findByIndex(it.hotbar) ?: continue
|
||||||
|
val inventorySlot = findByIndex(it.inventory) ?: continue
|
||||||
|
|
||||||
val (hotX, hotY) = hotbarSlot.lineCenter()
|
val (hotX, hotY) = hotbarSlot.lineCenter()
|
||||||
val (invX, invY) = inventorySlot.lineCenter()
|
val (invX, invY) = inventorySlot.lineCenter()
|
||||||
@@ -268,22 +332,27 @@ object SlotLocking : FirmamentFeature {
|
|||||||
|| accScreen.focusedSlot_Firmament === inventorySlot
|
|| accScreen.focusedSlot_Firmament === inventorySlot
|
||||||
if (!anyHovered && TConfig.slotRenderLines == SlotRenderLinesMode.NOTHING)
|
if (!anyHovered && TConfig.slotRenderLines == SlotRenderLinesMode.NOTHING)
|
||||||
continue
|
continue
|
||||||
val color = if (anyHovered)
|
if (anyHovered) {
|
||||||
me.shedaniel.math.Color.ofOpaque(0x00FF00)
|
highlitSlots.add(hotbarSlot)
|
||||||
else
|
highlitSlots.add(inventorySlot)
|
||||||
me.shedaniel.math.Color.ofTransparent(0xc0a0f000.toInt())
|
}
|
||||||
|
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)
|
if (TConfig.slotRenderLines == SlotRenderLinesMode.EVERYTHING || anyHovered)
|
||||||
event.context.drawLine(
|
event.context.drawLine(
|
||||||
invX + sx, invY + sy,
|
invX + sx, invY + sy,
|
||||||
hotX + sx, hotY + sy,
|
hotX + sx, hotY + sy,
|
||||||
color
|
color(anyHovered)
|
||||||
)
|
)
|
||||||
event.context.drawBorder(hotbarSlot.x + sx,
|
event.context.drawBorder(hotbarSlot.x + sx,
|
||||||
hotbarSlot.y + sy,
|
hotbarSlot.y + sy,
|
||||||
16, 16, color.color)
|
16, 16, color(hotbarSlot in highlitSlots).color)
|
||||||
event.context.drawBorder(inventorySlot.x + sx,
|
event.context.drawBorder(inventorySlot.x + sx,
|
||||||
inventorySlot.y + sy,
|
inventorySlot.y + sy,
|
||||||
16, 16, color.color)
|
16, 16, color(inventorySlot in highlitSlots).color)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -339,11 +408,9 @@ object SlotLocking : FirmamentFeature {
|
|||||||
|
|
||||||
fun toggleSlotLock(slot: Slot) {
|
fun toggleSlotLock(slot: Slot) {
|
||||||
val lockedSlots = lockedSlots ?: return
|
val lockedSlots = lockedSlots ?: return
|
||||||
val boundSlots = DConfig.data?.boundSlots ?: mutableMapOf()
|
val boundSlots = DConfig.data?.boundSlots ?: BoundSlots()
|
||||||
if (slot.inventory is PlayerInventory) {
|
if (slot.inventory is PlayerInventory) {
|
||||||
if (boundSlots.entries.removeIf {
|
if (boundSlots.removeAllInvolving(slot.index)) {
|
||||||
it.value == slot.index || it.key == slot.index
|
|
||||||
}) {
|
|
||||||
// intentionally do nothing
|
// intentionally do nothing
|
||||||
} else if (slot.index in lockedSlots) {
|
} else if (slot.index in lockedSlots) {
|
||||||
lockedSlots.remove(slot.index)
|
lockedSlots.remove(slot.index)
|
||||||
|
|||||||
@@ -242,6 +242,8 @@
|
|||||||
"firmament.config.slot-locking.lock-uuid": "Lock UUID (Lock Item)",
|
"firmament.config.slot-locking.lock-uuid": "Lock UUID (Lock Item)",
|
||||||
"firmament.config.slot-locking.lock-uuid.description": "Lock a SkyBlock item by it's UUID. This blocks a specific item from being dropped/sold, but still allows moving it around.",
|
"firmament.config.slot-locking.lock-uuid.description": "Lock a SkyBlock item by it's UUID. This blocks a specific item from being dropped/sold, but still allows moving it around.",
|
||||||
"firmament.config.slot-locking.lock.description": "Lock a slot, preventing any item from being moved from, dropped from, or placed into this slot.",
|
"firmament.config.slot-locking.lock.description": "Lock a slot, preventing any item from being moved from, dropped from, or placed into this slot.",
|
||||||
|
"firmament.config.slot-locking.multi-bind": "Allow Multi Binding",
|
||||||
|
"firmament.config.slot-locking.multi-bind.description": "Allow binding the same hotbar slot to multiple inventory slots.",
|
||||||
"firmament.config.slot-locking.require-quick-move": "Require Shift-Click for Bound Slots",
|
"firmament.config.slot-locking.require-quick-move": "Require Shift-Click for Bound Slots",
|
||||||
"firmament.config.slot-locking.require-quick-move.description": "If turned off, allows to switch between bound slots without holding shift.",
|
"firmament.config.slot-locking.require-quick-move.description": "If turned off, allows to switch between bound slots without holding shift.",
|
||||||
"firmament.config.storage-overlay": "Storage Overlay",
|
"firmament.config.storage-overlay": "Storage Overlay",
|
||||||
|
|||||||
Reference in New Issue
Block a user