package net.shadowfacts.phycon.block.terminal import com.mojang.blaze3d.systems.RenderSystem import net.minecraft.client.MinecraftClient import net.minecraft.client.gui.DrawableHelper import net.minecraft.client.gui.Element import net.minecraft.client.gui.widget.TextFieldWidget import net.minecraft.client.render.Tessellator import net.minecraft.client.render.VertexConsumerProvider import net.minecraft.client.util.math.MatrixStack import net.minecraft.entity.player.PlayerInventory import net.minecraft.item.ItemStack import net.minecraft.screen.slot.Slot import net.minecraft.screen.slot.SlotActionType import net.minecraft.text.LiteralText import net.minecraft.text.Text import net.minecraft.util.Identifier import net.shadowfacts.cacao.CacaoHandledScreen import net.shadowfacts.cacao.window.ScreenHandlerWindow import net.shadowfacts.cacao.window.Window import net.shadowfacts.phycon.PhysicalConnectivity import net.shadowfacts.phycon.networking.C2STerminalRequestItem import net.shadowfacts.phycon.networking.C2STerminalUpdateDisplayedItems import net.shadowfacts.phycon.util.SortMode import java.math.RoundingMode import java.text.DecimalFormat import java.util.LinkedList import kotlin.math.ceil import kotlin.math.min /** * @author shadowfacts */ class TerminalScreen(handler: TerminalScreenHandler, playerInv: PlayerInventory, title: Text): CacaoHandledScreen(handler, playerInv, title) { companion object { private val BACKGROUND = Identifier(PhysicalConnectivity.MODID, "textures/gui/terminal.png") private val clickHandlers = LinkedList Boolean?>() fun registerClickHandler(handler: TerminalScreen.(Double, Double, Int) -> Boolean?) { clickHandlers.add(handler) } } val backgroundWidth: Int get() = backgroundWidth val backgroundHeight: Int get() = backgroundHeight val terminalVC = TerminalViewController(this, handler, handler.terminal) var amountVC: TerminalRequestAmountViewController? = null var searchQuery = "" var scrollPosition = 0.0 init { backgroundWidth = 252 backgroundHeight = 222 addWindow(ScreenHandlerWindow(handler, terminalVC)) requestUpdatedItems() } fun requestItem(stack: ItemStack, amount: Int) { val netHandler = MinecraftClient.getInstance().player!!.networkHandler val packet = C2STerminalRequestItem(handler.terminal, stack, amount) netHandler.sendPacket(packet) } fun requestUpdatedItems() { val player = MinecraftClient.getInstance().player!! player.networkHandler.sendPacket(C2STerminalUpdateDisplayedItems(handler.terminal, searchQuery, scrollPosition.toFloat())) } private fun showRequestAmountDialog(stack: ItemStack) { val vc = TerminalRequestAmountViewController(this, stack) addWindow(Window(vc)) amountVC = vc } @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()) } private val DECIMAL_FORMAT = DecimalFormat("#.#").apply { roundingMode = RoundingMode.HALF_UP } private val FORMAT = DecimalFormat("##").apply { roundingMode = RoundingMode.HALF_UP } fun drawNetworkSlotAmount(stack: ItemStack, x: Int, y: Int, slot: Slot, matrixStack: MatrixStack) { val amount = stack.count val s = when { amount < 1_000 -> amount.toString() amount < 1_000_000 -> { val format = if (amount < 10_000) DECIMAL_FORMAT else FORMAT format.format(amount / 1_000.0) + "K" } amount < 1_000_000_000 -> { val format = if (amount < 10_000_000) DECIMAL_FORMAT else FORMAT format.format(amount / 1_000_000.0) + "M" } else -> { DECIMAL_FORMAT.format(amount / 1000000000.0).toString() + "B" } } // draw damage bar // empty string for label because vanilla renders the count behind the damage bar itemRenderer.renderGuiItemOverlay(textRenderer, stack, x, y, "") matrixStack.push() matrixStack.translate(x.toDouble(), y.toDouble(), itemRenderer.zOffset + 200.0) val scale = 2 / 3f matrixStack.scale(scale, scale, 1.0f) val immediate = VertexConsumerProvider.immediate(Tessellator.getInstance().buffer) val textX = (1 / scale * 18) - textRenderer.getWidth(s).toFloat() - 3 val textY = (1 / scale * 18) - 11 textRenderer.draw(s, textX, textY, 0xffffff, true, matrixStack.peek().model, immediate, false, 0, 0xF000F0) immediate.draw() matrixStack.pop() } override fun drawBackground(matrixStack: MatrixStack, delta: Float, mouseX: Int, mouseY: Int) { super.drawBackground(matrixStack, delta, mouseX, mouseY) RenderSystem.color4f(1f, 1f, 1f, 1f) client!!.textureManager.bindTexture(BACKGROUND) val x = (width - backgroundWidth) / 2 val y = (height - backgroundHeight) / 2 drawTexture(matrixStack, x, y, 0, 0, backgroundWidth, backgroundHeight) } override fun tick() { super.tick() if (amountVC != null) { amountVC!!.field.tick() } else { terminalVC.searchField.tick() } } override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean { for (handler in clickHandlers) { val res = handler(mouseX, mouseY, button) if (res != null) { return res } } return super.mouseClicked(mouseX, mouseY, button) } override fun onMouseClick(slot: Slot?, invSlot: Int, clickData: Int, type: SlotActionType?) { super.onMouseClick(slot, invSlot, clickData, type) if (slot != null && !slot.stack.isEmpty && handler.isNetworkSlot(slot.id)) { val stack = slot.stack if (type == SlotActionType.QUICK_MOVE) { // shift click, request full stack requestItem(stack, min(stack.count, stack.maxCount)) } else if (type == SlotActionType.PICKUP) { if (clickData == 1) { // right click, request half stack requestItem(stack, ceil(min(stack.count, stack.maxCount) / 2f).toInt()) } else { showRequestAmountDialog(stack) } } } } private val fakeFocusedElement = TextFieldWidget(textRenderer, 0, 0, 0, 0, LiteralText("")) override fun getFocused(): Element? { return if (windows.last().firstResponder != null) { fakeFocusedElement } else { null } } }