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.GameRenderer 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.networking.C2STerminalRequestItem import net.shadowfacts.phycon.networking.C2STerminalUpdateDisplayedItems import java.math.RoundingMode import java.text.DecimalFormat import java.util.LinkedList import kotlin.math.ceil import kotlin.math.min /** * @author shadowfacts */ abstract class AbstractTerminalScreen>( handler: T, playerInv: PlayerInventory, title: Text, val terminalBackgroundWidth: Int, val terminalBackgroundHeight: Int, ): CacaoHandledScreen(handler, playerInv, title) { companion object { private val clickHandlers = LinkedList.(Double, Double, Int) -> Boolean?>() fun registerClickHandler(handler: AbstractTerminalScreen<*, *>.(Double, Double, Int) -> Boolean?) { clickHandlers.add(handler) } } abstract val backgroundTexture: Identifier val terminalVC: AbstractTerminalViewController<*, *, *> var amountVC: TerminalRequestAmountViewController? = null var searchQuery = "" var scrollPosition = 0.0 init { backgroundWidth = terminalBackgroundWidth backgroundHeight = terminalBackgroundHeight terminalVC = createViewController() addWindow(ScreenHandlerWindow(handler, terminalVC)) requestUpdatedItems() } abstract fun createViewController(): AbstractTerminalViewController<*, *, *> 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) { 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, "") // ItemRenderer.renderGuiItemOverlay creates a new MatrixStack specifically for drawing the overlay val matrixStack = MatrixStack() 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().positionMatrix, immediate, false, 0, 0xF000F0) RenderSystem.enableDepthTest() immediate.draw() } override fun drawBackground(matrixStack: MatrixStack, delta: Float, mouseX: Int, mouseY: Int) { super.drawBackground(matrixStack, delta, mouseX, mouseY) drawBackgroundTexture(matrixStack) } open fun drawBackgroundTexture(matrixStack: MatrixStack) { RenderSystem.setShader(GameRenderer::getPositionTexColorShader) RenderSystem.setShaderTexture(0, backgroundTexture) RenderSystem.setShaderColor(1f, 1f, 1f, 1f) val x = (width - backgroundWidth) / 2 val y = (height - backgroundHeight) / 2 drawTexture(matrixStack, x, y, 0, 0, backgroundWidth, backgroundHeight) } override fun handledScreenTick() { super.handledScreenTick() 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) && handler.cursorStack.isEmpty) { 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 } } }