PhysicalConnectivity/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalScreenHandler.kt

162 lines
5.1 KiB
Kotlin

package net.shadowfacts.phycon.network.block.terminal
import net.minecraft.screen.slot.Slot
import net.minecraft.screen.slot.SlotActionType
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.entity.player.PlayerInventory
import net.minecraft.item.ItemStack
import net.minecraft.network.PacketByteBuf
import net.minecraft.screen.ScreenHandler
import net.minecraft.util.Identifier
import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.init.PhyBlocks
import net.shadowfacts.phycon.init.PhyScreens
import kotlin.math.ceil
import kotlin.math.min
/**
* @author shadowfacts
*/
class TerminalScreenHandler(syncId: Int, playerInv: PlayerInventory, val terminal: TerminalBlockEntity): ScreenHandler(PhyScreens.TERMINAL_SCREEN_HANDLER, syncId) {
companion object {
val ID = Identifier(PhysicalConnectivity.MODID, "terminal")
}
constructor(syncId: Int, playerInv: PlayerInventory, buf: PacketByteBuf):
this(syncId, playerInv, PhyBlocks.TERMINAL.getBlockEntity(playerInv.player.world, buf.readBlockPos())!!)
init {
// network
for (y in 0 until 6) {
for (x in 0 until 9) {
addSlot(TerminalFakeSlot(terminal, y * 9 + x, 66 + x * 18, 18 + y * 18))
}
}
// internal buffer
for (y in 0 until 6) {
for (x in 0 until 3) {
addSlot(Slot(terminal.internalBuffer, y * 3 + x, 8 + x * 18, 18 + y * 18))
}
}
// player inv
for (y in 0 until 3) {
for (x in 0 until 9) {
addSlot(Slot(playerInv, x + y * 9 + 9, 66 + x * 18, 140 + y * 18))
}
}
// hotbar
for (x in 0 until 9) {
addSlot(Slot(playerInv, x, 66 + x * 18, 198))
}
}
override fun canUse(player: PlayerEntity): Boolean {
return true
}
override fun close(player: PlayerEntity) {
super.close(player)
terminal.removeObserver()
}
override fun onSlotClick(slotId: Int, clickData: Int, actionType: SlotActionType, player: PlayerEntity): ItemStack {
if (isNetworkSlot(slotId)) {
// the slot clicked was one of the network stacks
if (actionType == SlotActionType.QUICK_MOVE) {
val stack = slots[slotId].stack
if (!stack.isEmpty && !player.world.isClient) {
terminal.requestItem(stack, min(stack.count, stack.maxCount))
}
} else if (actionType == SlotActionType.PICKUP && clickData == 1) {
if (clickData == 1) {
// right click, request half stack
val stack = slots[slotId].stack
if (!stack.isEmpty && !player.world.isClient) {
terminal.requestItem(stack, ceil(min(stack.count, stack.maxCount) / 2f).toInt())
}
} else {
// todo: left click, show amount dialog
}
}
return ItemStack.EMPTY
} else if (isBufferSlot(slotId)) {
// internal buffer
// 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 - 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<Int> {
val slotsInsertedInto = mutableListOf<Int>()
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
}