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 372a17e..077eff2 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,11 +1,15 @@ package net.shadowfacts.phycon.network.block.terminal -import com.mojang.blaze3d.platform.GlStateManager +import com.mojang.blaze3d.systems.RenderSystem +import net.minecraft.client.MinecraftClient import net.minecraft.client.gui.DrawableHelper import net.minecraft.client.gui.screen.ingame.HandledScreen +import net.minecraft.client.gui.widget.AbstractButtonWidget +import net.minecraft.client.gui.widget.ButtonWidget import net.minecraft.client.gui.widget.TextFieldWidget 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 @@ -13,6 +17,9 @@ import net.minecraft.text.Text import net.minecraft.util.Identifier import net.shadowfacts.phycon.PhysicalConnectivity import org.lwjgl.glfw.GLFW +import java.lang.NumberFormatException +import kotlin.math.ceil +import kotlin.math.floor /** * @author shadowfacts @@ -20,10 +27,31 @@ import org.lwjgl.glfw.GLFW // todo: translate title class TerminalScreen(handler: TerminalScreenHandler, playerInv: PlayerInventory, title: Text): HandledScreen(handler, playerInv, title) { companion object { - val BACKGROUND = Identifier(PhysicalConnectivity.MODID, "textures/gui/terminal.png") + private val BACKGROUND = Identifier(PhysicalConnectivity.MODID, "textures/gui/terminal.png") + private val DIALOG = Identifier(PhysicalConnectivity.MODID, "textures/gui/terminal_amount.png") } private lateinit var searchBox: TextFieldWidget + private lateinit var amountBox: TextFieldWidget + private var dialogStack = ItemStack.EMPTY + private var showingAmountDialog = false + set(value) { + val oldValue = field + field = value + for (e in dialogChildren) { + e.visible = value + } + amountBox.isVisible + searchBox.setSelected(!value) + amountBox.setSelected(value) + if (value && !oldValue) { + amountBox.text = "1" + } + } + private var dialogChildren = mutableListOf() + + private val dialogWidth = 158 + private val dialogHeight = 62 init { backgroundWidth = 252 @@ -33,6 +61,9 @@ class TerminalScreen(handler: TerminalScreenHandler, playerInv: PlayerInventory, override fun init() { super.init() + children.clear() + dialogChildren.clear() + client!!.keyboard.setRepeatEvents(true) searchBox = TextFieldWidget(textRenderer, x + 138, y + 6, 80, 9, LiteralText("Search")) @@ -43,11 +74,68 @@ class TerminalScreen(handler: TerminalScreenHandler, playerInv: PlayerInventory, searchBox.setSelected(true) searchBox.setEditableColor(0xffffff) children.add(searchBox) + + val dialogMinX = width / 2 - dialogWidth / 2 + val dialogMinY = height / 2 - dialogHeight / 2 + amountBox = TextFieldWidget(textRenderer, dialogMinX + 8, dialogMinY + 27, 80, 9, LiteralText("Amount")) + amountBox.setHasBorder(false) + amountBox.isVisible = false + amountBox.setSelected(false) + amountBox.setEditableColor(0xffffff) + amountBox.setTextPredicate { + if (it.isEmpty()) { + true + } else { + try { + Integer.parseInt(it) > 0 + } catch (e: NumberFormatException) { + false + } + } + } + dialogChildren.add(amountBox) + + val plusOne = SmallButton(dialogMinX + 7, dialogMinY + 7, 28, LiteralText("+1")) { + amountBox.intValue += 1 + } + dialogChildren.add(plusOne) + + val plusTen = SmallButton(dialogMinX + 7 + 28 + 3, dialogMinY + 7, 28, LiteralText("+10")) { + amountBox.intValue = ceil((amountBox.intValue + 1) / 10.0).toInt() * 10 + } + dialogChildren.add(plusTen) + + val plusHundred = SmallButton(dialogMinX + 7 + (28 + 3) * 2, dialogMinY + 7, 28, LiteralText("+100")) { + amountBox.intValue = ceil((amountBox.intValue + 1) / 100.0).toInt() * 100 + } + dialogChildren.add(plusHundred) + + val minusOne = SmallButton(dialogMinX + 7, dialogMinY + 39, 28, LiteralText("-1")) { + amountBox.intValue -= 1 + } + dialogChildren.add(minusOne) + + val minusTen = SmallButton(dialogMinX + 7 + 28 + 3, dialogMinY + 39, 28, LiteralText("-10")) { + amountBox.intValue = floor((amountBox.intValue - 1) / 10.0).toInt() * 10 + } + dialogChildren.add(minusTen) + + val minusHundred = SmallButton(dialogMinX + 7 + (28 + 3) * 2, dialogMinY + 39, 28, LiteralText("-100")) { + amountBox.intValue = floor((amountBox.intValue - 1) / 100.0).toInt() * 100 + } + dialogChildren.add(minusHundred) + + // 101,25 + val request = ButtonWidget(dialogMinX + 101, dialogMinY + 21, 50, 20, LiteralText("Request")) { + doDialogRequest() + } + dialogChildren.add(request) } override fun tick() { super.tick() searchBox.tick() + amountBox.tick() } override fun drawForeground(matrixStack: MatrixStack, mouseX: Int, mouseY: Int) { @@ -58,21 +146,50 @@ class TerminalScreen(handler: TerminalScreenHandler, playerInv: PlayerInventory, } override fun drawBackground(matrixStack: MatrixStack, delta: Float, mouseX: Int, mouseY: Int) { - renderBackground(matrixStack) + // if the dialog is open, the background gradient will be drawn in front of the main terminal gui + if (!showingAmountDialog) { + renderBackground(matrixStack) + } - GlStateManager.color4f(1f, 1f, 1f, 1f) + 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) } + @ExperimentalUnsignedTypes override fun render(matrixStack: MatrixStack, mouseX: Int, mouseY: Int, delta: Float) { - super.render(matrixStack, mouseX, mouseY, delta) + if (showingAmountDialog) { + RenderSystem.pushMatrix() + // items are rendered at some stupidly high z offset. item amounts at an even higher one + RenderSystem.translatef(0f, 0f, -350f) + + // fake the mouse x/y while showing a dialog so slot mouseover highlights aren't drawn + super.render(matrixStack, -1, -1, delta) + + RenderSystem.popMatrix() + } else { + super.render(matrixStack, mouseX, mouseY, delta) + } searchBox.render(matrixStack, mouseX, mouseY, delta) - drawMouseoverTooltip(matrixStack, mouseX, mouseY) + if (showingAmountDialog) { + renderBackground(matrixStack) + + RenderSystem.color4f(1f, 1f, 1f, 1f) + client!!.textureManager.bindTexture(DIALOG) + val dialogMinX = width / 2 - dialogWidth / 2 + val dialogMinY = height / 2 - dialogHeight / 2 + drawTexture(matrixStack, dialogMinX, dialogMinY, 0, 0, dialogWidth, dialogHeight) + + for (e in dialogChildren) { + e.render(matrixStack, mouseX, mouseY, delta) + } + } else { + drawMouseoverTooltip(matrixStack, mouseX, mouseY) + } } @ExperimentalUnsignedTypes @@ -90,37 +207,107 @@ class TerminalScreen(handler: TerminalScreenHandler, playerInv: PlayerInventory, DrawableHelper.fill(matrixStack, slot.x, slot.y, slot.x + 16, slot.y + 16, color.toInt()) } - override fun onMouseClick(slot: Slot?, i: Int, j: Int, slotActionType: SlotActionType?) { - super.onMouseClick(slot, i, j, slotActionType) + override fun onMouseClick(slot: Slot?, invSlot: Int, clickData: Int, type: SlotActionType?) { + super.onMouseClick(slot, invSlot, clickData, type) // don't unfocus the search box on mouse click searchBox.setSelected(true) + + if (type == SlotActionType.PICKUP && clickData == 0 && slot != null && handler.isNetworkSlot(slot.id) && !slot.stack.isEmpty) { + dialogStack = slot.stack + showingAmountDialog = true + searchBox.setSelected(false) + } + } + + override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean { + if (showingAmountDialog) { + for (e in dialogChildren) { + if (e.mouseClicked(mouseX, mouseY, button)) { + return true + } + } + return false + } else { + return super.mouseClicked(mouseX, mouseY, button) + } + } + + override fun mouseDragged(d: Double, e: Double, i: Int, f: Double, g: Double): Boolean { + if (showingAmountDialog) { + return false + } else { + return super.mouseDragged(d, e, i, f, g) + } + } + + override fun mouseMoved(d: Double, e: Double) { + if (showingAmountDialog) { + } else { + super.mouseMoved(d, e) + } + } + + override fun mouseReleased(d: Double, e: Double, i: Int): Boolean { + if (showingAmountDialog) { + return false + } else { + return super.mouseReleased(d, e, i) + } + } + + override fun mouseScrolled(d: Double, e: Double, f: Double): Boolean { + if (showingAmountDialog) { + return false + } else { + return super.mouseScrolled(d, e, f) + } } override fun charTyped(c: Char, i: Int): Boolean { - val oldText = searchBox.text - if (searchBox.charTyped(c, i)) { - if (searchBox.text != oldText) { - search() + if (showingAmountDialog) { + return amountBox.charTyped(c, i) + } else { + val oldText = searchBox.text + if (searchBox.charTyped(c, i)) { + if (searchBox.text != oldText) { + search() + } + return true } - return true - } - return super.charTyped(c, i) + return super.charTyped(c, i) + } } override fun keyPressed(key: Int, j: Int, k: Int): Boolean { - val oldText = searchBox.text - if (searchBox.keyPressed(key, j, k)) { - if (searchBox.text != oldText) { - search() + if (showingAmountDialog) { + return when (key) { + GLFW.GLFW_KEY_ESCAPE -> { + showingAmountDialog = false + true + } + GLFW.GLFW_KEY_ENTER -> { + doDialogRequest() + true + } + else -> { + amountBox.keyPressed(key, j, k) + } } - return true - } - return if (searchBox.isFocused && searchBox.isVisible && key != GLFW.GLFW_KEY_ESCAPE) { - true } else { - super.keyPressed(key, j, k) + val oldText = searchBox.text + if (searchBox.keyPressed(key, j, k)) { + if (searchBox.text != oldText) { + search() + } + return true + } + return if (searchBox.isFocused && searchBox.isVisible && key != GLFW.GLFW_KEY_ESCAPE) { + true + } else { + super.keyPressed(key, j, k) + } } } @@ -128,4 +315,32 @@ class TerminalScreen(handler: TerminalScreenHandler, playerInv: PlayerInventory, screenHandler.search(searchBox.text) } + private fun doDialogRequest() { + showingAmountDialog = false + handler.requestItem(client!!.player!!, dialogStack, amountBox.intValue) + } + + private var TextFieldWidget.intValue: Int + get() = if (text.isEmpty()) 0 else Integer.parseInt(text) + set(value) { + text = value.toString() + setSelected(true) + } + + class SmallButton(x: Int, y: Int, width: Int, title: Text, action: PressAction): ButtonWidget(x, y, width, 14, title, action) { + @ExperimentalUnsignedTypes + override fun renderButton(matrixStack: MatrixStack, mouseX: Int, mouseY: Int, delta: Float) { + val client = MinecraftClient.getInstance() + client.textureManager.bindTexture(DIALOG) + RenderSystem.color4f(1f, 1f, 1f, 1f) + val v = if (isHovered) 142 else 128 + RenderSystem.enableBlend() + RenderSystem.defaultBlendFunc() + RenderSystem.enableDepthTest() + drawTexture(matrixStack, x, y, 0, v, width / 2, height) + drawTexture(matrixStack, x + width / 2, y, 200 - width / 2, v, width / 2, height) + drawCenteredText(matrixStack, client.textRenderer, message, x + width / 2, y + (height - 8) / 2, 0xffffffffu.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 e648f07..bd5b3ff 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 @@ -134,7 +134,7 @@ class TerminalScreenHandler(syncId: Int, playerInv: PlayerInventory, val termina return super.onSlotClick(slotId, clickData, actionType, player) } - private fun requestItem(player: PlayerEntity, stack: ItemStack, amount: Int) { + fun requestItem(player: PlayerEntity, stack: ItemStack, amount: Int) { if (!player.world.isClient) return val handler = (player as ClientPlayerEntity).networkHandler val packet = C2STerminalRequestItem(terminal, stack, amount) diff --git a/src/main/resources/assets/phycon/textures/gui/terminal_amount.png b/src/main/resources/assets/phycon/textures/gui/terminal_amount.png new file mode 100644 index 0000000..3999831 Binary files /dev/null and b/src/main/resources/assets/phycon/textures/gui/terminal_amount.png differ