diff --git a/src/main/java/net/shadowfacts/phycon/mixin/client/MixinHandledScreen.java b/src/main/java/net/shadowfacts/phycon/mixin/client/MixinHandledScreen.java new file mode 100644 index 0000000..8adf475 --- /dev/null +++ b/src/main/java/net/shadowfacts/phycon/mixin/client/MixinHandledScreen.java @@ -0,0 +1,26 @@ +package net.shadowfacts.phycon.mixin.client; + +import net.minecraft.client.gui.screen.ingame.HandledScreen; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.screen.slot.Slot; +import net.shadowfacts.phycon.network.block.terminal.TerminalScreen; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +/** + * @author shadowfacts + */ +@Mixin(HandledScreen.class) +public class MixinHandledScreen { + + @Inject(method = "drawSlot(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/screen/slot/Slot;)V", at = @At(value = "INVOKE", target = "enableDepthTest()V")) + private void drawSlot(MatrixStack matrixStack, Slot slot, CallbackInfo ci) { + if ((Object)this instanceof TerminalScreen) { + TerminalScreen self = (TerminalScreen)(Object)this; + self.drawSlotUnderlay(matrixStack, slot); + } + } + +} diff --git a/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalBlockEntity.kt b/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalBlockEntity.kt index 8f5318f..25db5e8 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalBlockEntity.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalBlockEntity.kt @@ -238,7 +238,7 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), Invento override fun fromTag(state: BlockState, tag: CompoundTag) { super.fromTag(state, tag) - internalBuffer.fromTag(tag.getList("InternalBuffer", 10)) + internalBuffer.fromTag(tag.getCompound("InternalBuffer")) } override fun toClientTag(tag: CompoundTag): CompoundTag { @@ -254,14 +254,7 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), Invento } override fun fromClientTag(tag: CompoundTag) { - tag.getList("InternalBuffer", 10)?.also { list -> - if (list.isNotEmpty()) { - internalBuffer.fromTag(list) - } else { - // todo: should this clear or just do nothing? - internalBuffer.clear() - } - } + internalBuffer.fromTag(tag.getCompound("InternalBuffer")) val list = tag.getList("CachedNetItems", 10) cachedNetItems.clear() for (entryTag in list) { diff --git a/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalBufferInventory.kt b/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalBufferInventory.kt index 5d0e673..145d31f 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalBufferInventory.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalBufferInventory.kt @@ -3,6 +3,10 @@ package net.shadowfacts.phycon.network.block.terminal import alexiil.mc.lib.attributes.item.ItemStackUtil import net.minecraft.inventory.SimpleInventory import net.minecraft.item.ItemStack +import net.minecraft.nbt.CompoundTag +import net.minecraft.nbt.IntArrayTag +import net.shadowfacts.phycon.util.fromTag +import net.shadowfacts.phycon.util.toTag import kotlin.math.min /** @@ -14,8 +18,21 @@ class TerminalBufferInventory(size: Int): SimpleInventory(size) { TO_NETWORK, FROM_NETWORK, UNASSIGNED } - // todo: modes should be saved to NBT - private val modes = Array(size) { Mode.UNASSIGNED } + var modes = Array(size) { Mode.UNASSIGNED } + private set + + fun toTag(): CompoundTag { + val compound = CompoundTag() + compound.put("Inventory", (this as SimpleInventory).toTag()) + compound.put("Modes", IntArrayTag(modes.map(Mode::ordinal))) + return compound + } + + fun fromTag(tag: CompoundTag) { + val inventory = tag.getList("Inventory", 10) + (this as SimpleInventory).fromTag(inventory) + tag.getIntArray("Modes").forEachIndexed { i, it -> modes[i] = Mode.values()[it] } + } fun insertFromNetwork(stack: ItemStack): ItemStack { var remaining = stack.copy() @@ -57,6 +74,11 @@ class TerminalBufferInventory(size: Int): SimpleInventory(size) { super.setStack(slot, stack) } + override fun clear() { + super.clear() + this.modes = Array(size()) { Mode.UNASSIGNED } + } + fun getMode(slot: Int): Mode { return modes[slot] } diff --git a/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalScreen.kt b/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalScreen.kt index 95bd1f4..6482505 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalScreen.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalScreen.kt @@ -1,9 +1,11 @@ package net.shadowfacts.phycon.network.block.terminal import com.mojang.blaze3d.platform.GlStateManager +import net.minecraft.client.gui.DrawableHelper import net.minecraft.client.gui.screen.ingame.HandledScreen import net.minecraft.client.util.math.MatrixStack import net.minecraft.entity.player.PlayerInventory +import net.minecraft.screen.slot.Slot import net.minecraft.text.LiteralText import net.minecraft.text.Text import net.minecraft.util.Identifier @@ -38,4 +40,19 @@ class TerminalScreen(handler: TerminalScreenHandler, playerInv: PlayerInventory, drawTexture(matrixStack, x, y, 0, 0, backgroundWidth, backgroundHeight) } + @ExperimentalUnsignedTypes + fun drawSlotUnderlay(matrixStack: MatrixStack, slot: Slot) { + if (!handler.isBufferSlot(slot.id)) { + return + } + + val mode = handler.terminal.internalBuffer.getMode(slot.id - handler.bufferSlotsStart) + val color: UInt = when (mode) { + TerminalBufferInventory.Mode.TO_NETWORK -> 0xFFFF0000u + TerminalBufferInventory.Mode.FROM_NETWORK -> 0xFF00FF00u + else -> return + } + DrawableHelper.fill(matrixStack, slot.x, slot.y, slot.x + 16, slot.y + 16, color.toInt()) + } + } diff --git a/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalScreenHandler.kt b/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalScreenHandler.kt index dc357b7..76af6ee 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalScreenHandler.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalScreenHandler.kt @@ -64,7 +64,7 @@ class TerminalScreenHandler(syncId: Int, playerInv: PlayerInventory, val termina } override fun onSlotClick(slotId: Int, clickData: Int, actionType: SlotActionType, player: PlayerEntity): ItemStack { - if (slotId in 0 until 54) { + if (isNetworkSlot(slotId)) { // the slot clicked was one of the network stacks if (actionType == SlotActionType.QUICK_MOVE) { val stack = slots[slotId].stack @@ -83,14 +83,79 @@ class TerminalScreenHandler(syncId: Int, playerInv: PlayerInventory, val termina } } return ItemStack.EMPTY - } else if (slotId in 54 until 72) { + } else if (isBufferSlot(slotId)) { // internal buffer - if (actionType == SlotActionType.PICKUP && !player.inventory.cursorStack.isEmpty) { + // todo: why does this think it's quick_craft sometimes? + if ((actionType == SlotActionType.PICKUP || actionType == SlotActionType.QUICK_CRAFT) && !player.inventory.cursorStack.isEmpty) { // placing cursor stack into buffer - val bufferSlot = slotId - 54 // subtract 54 to convert the handler slot ID to a valid buffer index + val bufferSlot = slotId - bufferSlotsStart // subtract 54 to convert the handler slot ID to a valid buffer index terminal.internalBuffer.markSlot(bufferSlot, TerminalBufferInventory.Mode.TO_NETWORK) } } return super.onSlotClick(slotId, clickData, actionType, player) } + + override fun transferSlot(player: PlayerEntity, slotId: Int): ItemStack { + if (isNetworkSlot(slotId)) { + return ItemStack.EMPTY; + } + + val slot = slots[slotId] + if (!slot.hasStack()) { + return ItemStack.EMPTY + } + + val result = slot.stack.copy() + + if (isBufferSlot(slotId)) { + if (!insertItem(slot.stack, playerSlotsStart, playerSlotsEnd, false)) { + return ItemStack.EMPTY + } + if (slot.stack.isEmpty) { + terminal.internalBuffer.markSlot(slotId - bufferSlotsStart, TerminalBufferInventory.Mode.UNASSIGNED) + } + } else if (isPlayerSlot(slotId)) { + val slotsInsertedInto = tryInsertItem(slot.stack, bufferSlotsStart until playerSlotsStart) { terminal.internalBuffer.getMode(it - bufferSlotsStart) != TerminalBufferInventory.Mode.FROM_NETWORK } + slotsInsertedInto.forEach { terminal.internalBuffer.markSlot(it - bufferSlotsStart, TerminalBufferInventory.Mode.TO_NETWORK) } + if (slot.stack.isEmpty) { + return ItemStack.EMPTY + } + } + + return result + } + + private fun tryInsertItem(stack: ItemStack, slots: IntRange, slotPredicate: (Int) -> Boolean): Collection { + val slotsInsertedInto = mutableListOf() + for (index in slots) { + if (stack.isEmpty) break + if (!slotPredicate(index)) continue + + val slot = this.slots[index] + val slotStack = slot.stack + if (slotStack.isEmpty) { + slot.stack = stack.copy() + stack.count = 0 + + slot.markDirty() + slotsInsertedInto.add(index) + } else if (canStacksCombine(slotStack, stack) && slotStack.count < slotStack.maxCount) { + val maxToMove = slotStack.maxCount - slotStack.count + val toMove = min(maxToMove, stack.count) + slotStack.increment(toMove) + stack.decrement(toMove) + + slot.markDirty() + slotsInsertedInto.add(index) + } + } + return slotsInsertedInto + } + + val bufferSlotsStart = 54 + val playerSlotsStart = 72 + val playerSlotsEnd = 72 + 36 + fun isNetworkSlot(id: Int) = id in 0 until bufferSlotsStart + fun isBufferSlot(id: Int) = id in bufferSlotsStart until playerSlotsStart + fun isPlayerSlot(id: Int) = id >= playerSlotsStart } diff --git a/src/main/kotlin/net/shadowfacts/phycon/util/BasicInventoryExtensions.kt b/src/main/kotlin/net/shadowfacts/phycon/util/BasicInventoryExtensions.kt index 6b70339..f84225d 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/util/BasicInventoryExtensions.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/util/BasicInventoryExtensions.kt @@ -25,6 +25,10 @@ fun SimpleInventory.toTag(): ListTag { } fun SimpleInventory.fromTag(list: ListTag) { + if (list.isEmpty()) { + this.clear() + return + } if (list.elementType != 10.toByte()) throw RuntimeException("Can't decode BasicInventory from list tag that does not contain compound tags") this.clear() for (tag in list) { diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 793fb9d..fdae134 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -28,7 +28,11 @@ ] }, "mixins": [ - "phycon.mixins.json" + "phycon.mixins.json", + { + "config": "phycon-client.mixins.json", + "environment": "client" + } ], "depends": { "fabricloader": ">=0.4.0", diff --git a/src/main/resources/phycon-client.mixins.json b/src/main/resources/phycon-client.mixins.json new file mode 100644 index 0000000..5b87372 --- /dev/null +++ b/src/main/resources/phycon-client.mixins.json @@ -0,0 +1,11 @@ +{ + "required": true, + "package": "net.shadowfacts.phycon.mixin.client", + "compatibilityLevel": "JAVA_8", + "mixins": [ + "MixinHandledScreen" + ], + "injectors": { + "defaultRequire": 1 + } +} \ No newline at end of file