package net.shadowfacts.phycon.block.terminal import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerInventory import net.minecraft.inventory.CraftingInventory import net.minecraft.inventory.CraftingResultInventory import net.minecraft.inventory.Inventory import net.minecraft.item.ItemStack import net.minecraft.network.PacketByteBuf import net.minecraft.network.packet.s2c.play.ScreenHandlerSlotUpdateS2CPacket import net.minecraft.recipe.RecipeMatcher import net.minecraft.recipe.RecipeType import net.minecraft.screen.slot.CraftingResultSlot import net.minecraft.screen.slot.Slot import net.minecraft.server.network.ServerPlayerEntity import net.shadowfacts.phycon.init.PhyBlocks import net.shadowfacts.phycon.init.PhyScreens /** * @author shadowfacts */ class CraftingTerminalScreenHandler( syncId: Int, playerInv: PlayerInventory, terminal: CraftingTerminalBlockEntity, ): AbstractTerminalScreenHandler(PhyScreens.CRAFTING_TERMINAL, syncId, playerInv, terminal) { val craftingInv = CraftingInv(this) val result = CraftingResultInventory() val resultSlot: CraftingResultSlot val craftingSlotsStart: Int val craftingSlotsEnd: Int get() = craftingSlotsStart + 9 override val xOffset: Int get() = 5 constructor(syncId: Int, playerInv: PlayerInventory, buf: PacketByteBuf): this( syncId, playerInv, PhyBlocks.CRAFTING_TERMINAL.getBlockEntity(playerInv.player.world, buf.readBlockPos())!! ) init { craftingSlotsStart = slots.size for (y in 0 until 3) { for (x in 0 until 3) { this.addSlot(Slot(craftingInv, x + y * 3, 13 + x * 18, 140 + y * 18)) } } resultSlot = CraftingResultSlot(playerInv.player, craftingInv, result, 0, 31, 224) addSlot(resultSlot) updateCraftingResult() } override fun onContentChanged(inventory: Inventory?) { updateCraftingResult() } private fun updateCraftingResult() { val world = playerInv.player.world if (!world.isClient) { val player = playerInv.player as ServerPlayerEntity val recipe = world.server!!.recipeManager.getFirstMatch(RecipeType.CRAFTING, craftingInv, world) val resultStack = if (recipe.isPresent && result.shouldCraftRecipe(world, player, recipe.get())) { recipe.get().craft(craftingInv) } else { ItemStack.EMPTY } result.setStack(0, resultStack) player.networkHandler.sendPacket(ScreenHandlerSlotUpdateS2CPacket(syncId, nextRevision(), resultSlot.id, resultStack)) } } fun clearCraftingGrid() { assert(!playerInv.player.world.isClient) for (i in 0 until terminal.craftingInv.size()) { val craftingInvStack = terminal.craftingInv.getStack(i) if (craftingInvStack.isEmpty) continue val remainder = terminal.internalBuffer.insert(craftingInvStack, TerminalBufferInventory.Mode.TO_NETWORK) terminal.craftingInv.setStack(i, remainder) } updateCraftingResult() sendContentUpdates() } fun requestMoreCraftingIngredients(maxAmount: Int) { assert(!playerInv.player.world.isClient) terminal.requestItemsForCrafting(maxAmount) } override fun transferSlot(player: PlayerEntity, slotId: Int): ItemStack { if (slotId == resultSlot.id && resultSlot.hasStack()) { val craftingResult = resultSlot.stack val originalResult = craftingResult.copy() // todo: CraftingScreenHandler calls onCraft, but I don't think that's necessary because onStackChanged should handle it craftingResult.item.onCraft(craftingResult, player.world, player) if (!insertItem(craftingResult, playerSlotsStart, playerSlotsEnd, true)) { return ItemStack.EMPTY } resultSlot.onQuickTransfer(craftingResult, originalResult) if (craftingResult.isEmpty) { resultSlot.stack = ItemStack.EMPTY } if (craftingResult.count == originalResult.count) { return ItemStack.EMPTY } resultSlot.onTakeItem(player, craftingResult) player.dropItem(craftingResult, false) return originalResult } else { return super.transferSlot(player, slotId) } } // RecipeType.CRAFTING wants a CraftingInventory, but we can't store a CraftingInventory on the BE without a screen handler, so... class CraftingInv(val handler: CraftingTerminalScreenHandler): CraftingInventory(handler, 3, 3) { private val backing = handler.terminal.craftingInv override fun isEmpty(): Boolean { return backing.isEmpty } override fun getStack(i: Int): ItemStack { return backing.getStack(i) } override fun removeStack(i: Int): ItemStack { return backing.removeStack(i) } override fun removeStack(i: Int, j: Int): ItemStack { val res = backing.removeStack(i, j) if (!res.isEmpty) { handler.onContentChanged(this) } return res } override fun setStack(i: Int, itemStack: ItemStack?) { backing.setStack(i, itemStack) handler.onContentChanged(this) } override fun clear() { backing.clear() } override fun provideRecipeInputs(finder: RecipeMatcher) { TODO() } } }