From f8e38949b565a0cc0769b1510913ac0dff3a50bc Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Sat, 10 Sep 2022 18:10:33 +0700 Subject: [PATCH] Ah yes, gui bound rectangles Fixes #73 --- .../otm/client/screen/AndroidStationScreen.kt | 16 +-- .../mc/otm/client/screen/DriveViewerScreen.kt | 4 +- .../mc/otm/client/screen/ItemMonitorScreen.kt | 4 +- .../mc/otm/client/screen/MatterPanelScreen.kt | 6 +- .../mc/otm/client/screen/MatteryScreen.kt | 2 + .../client/screen/panels/AbstractSlotPanel.kt | 8 +- .../otm/client/screen/panels/EditablePanel.kt | 136 +++++++++++++++--- .../client/screen/panels/FilterSlotPanel.kt | 2 +- .../mc/otm/client/screen/panels/SlotPanel.kt | 5 +- .../mc/otm/compat/jei/JEIPlugin.kt | 32 ++++- .../compat/jei/PlatePressRecipeCategory.kt | 2 +- 11 files changed, 171 insertions(+), 46 deletions(-) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/AndroidStationScreen.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/AndroidStationScreen.kt index b3dd16177..1d0f07b64 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/AndroidStationScreen.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/AndroidStationScreen.kt @@ -423,19 +423,19 @@ private enum class PreviewScrollers( LEFT_TO_RIGHT( init = { it, random -> it.xOffset = it.width - it.yOffset = random.nextFloat(-it.boundingHeight + 18f, 0f) + it.yOffset = random.nextFloat(-it.childrenRectHeight + 18f, 0f) }, scroll = { it, _ -> it.xOffset -= 1f - it.xOffset >= -it.boundingWidth + it.xOffset >= -it.childrenRectWidth } ), RIGHT_TO_LEFT( init = { it, random -> - it.xOffset = -it.boundingWidth - it.yOffset = random.nextFloat(-it.boundingHeight + 18f, 0f) + it.xOffset = -it.childrenRectWidth + it.yOffset = random.nextFloat(-it.childrenRectHeight + 18f, 0f) }, scroll = { it, _ -> @@ -446,8 +446,8 @@ private enum class PreviewScrollers( BOTTOM_TO_TOP( init = { it, random -> - it.yOffset = -it.boundingHeight - it.xOffset = random.nextFloat(-it.boundingWidth + 18f, 0f) + it.yOffset = -it.childrenRectHeight + it.xOffset = random.nextFloat(-it.childrenRectWidth + 18f, 0f) }, scroll = { it, _ -> @@ -459,12 +459,12 @@ private enum class PreviewScrollers( TOP_TO_BOTTOM( init = { it, random -> it.yOffset = it.height - it.xOffset = random.nextFloat(-it.boundingWidth + 18f, 0f) + it.xOffset = random.nextFloat(-it.childrenRectWidth + 18f, 0f) }, scroll = { it, _ -> it.yOffset -= 1f - it.yOffset >= it.boundingHeight + it.yOffset >= it.childrenRectHeight } ) } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/DriveViewerScreen.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/DriveViewerScreen.kt index de9944200..e768e895e 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/DriveViewerScreen.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/DriveViewerScreen.kt @@ -55,7 +55,7 @@ class DriveViewerScreen(menu: DriveViewerMenu, inventory: Inventory, title: Comp for (i in 0 until GRID_WIDTH * GRID_HEIGHT) { object : AbstractSlotPanel(this@DriveViewerScreen, grid, 0f, 0f) { - override fun getItemStack(): ItemStack { + override val itemStack: ItemStack get() { val index = i + scrollBar.scroll * GRID_WIDTH return menu.networkedItemView.sortedView.getOrNull(index)?.stack?.item ?: ItemStack.EMPTY } @@ -72,7 +72,7 @@ class DriveViewerScreen(menu: DriveViewerMenu, inventory: Inventory, title: Comp override fun innerRender(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float) { renderSlotBackground(stack, mouseX, mouseY, partialTick) - renderRegular(stack, getItemStack(), "") + renderRegular(stack, itemStack, "") } override fun getItemStackTooltip(stack: ItemStack): List { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/ItemMonitorScreen.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/ItemMonitorScreen.kt index 55bc3cebf..a8b2ed4af 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/ItemMonitorScreen.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/ItemMonitorScreen.kt @@ -56,7 +56,7 @@ class ItemMonitorScreen(menu: ItemMonitorMenu, inventory: Inventory, title: Comp object : AbstractSlotPanel(this@ItemMonitorScreen, gridPanel) { private val index get() = i + viewScrollBar.scroll * ITEM_GRID_WIDTH - override fun getItemStack(): ItemStack { + override val itemStack: ItemStack get() { return menu.networkedItemView.sortedView.getOrNull(index)?.stack?.item ?: ItemStack.EMPTY } @@ -179,7 +179,7 @@ class ItemMonitorScreen(menu: ItemMonitorMenu, inventory: Inventory, title: Comp for (i in 0 until 9) { object : AbstractSlotPanel(this@ItemMonitorScreen, craftingHistory) { - override fun getItemStack(): ItemStack { + override val itemStack: ItemStack get() { return ItemStack(Items.ARROW, 42) } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/MatterPanelScreen.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/MatterPanelScreen.kt index 36d774d29..7b1fc9518 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/MatterPanelScreen.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/MatterPanelScreen.kt @@ -75,7 +75,7 @@ class MatterPanelScreen( private val index: Int get() = (scrollBar.scroll + row) * GRID_WIDTH + i - override fun getItemStack(): ItemStack { + override val itemStack: ItemStack get() { if (isPatternView) { return menu.patterns.getOrNull(index)?.stack() ?: ItemStack.EMPTY } else { @@ -132,7 +132,7 @@ class MatterPanelScreen( dock = Dock.LEFT } - override fun getItemStack(): ItemStack { + override val itemStack: ItemStack get() { return menu.tasks.firstOrNull { it.id == task.id }?.let { it.stack((it.required + it.inProgress).coerceAtLeast(1)) } ?: task.stack((task.required + task.inProgress).coerceAtLeast(1)) } @@ -187,7 +187,7 @@ class MatterPanelScreen( dockRight = 2f } - override fun getItemStack(): ItemStack { + override val itemStack: ItemStack get() { return pattern.stack() } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/MatteryScreen.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/MatteryScreen.kt index 82e06a3f1..5e6a7df61 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/MatteryScreen.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/MatteryScreen.kt @@ -20,6 +20,7 @@ import ru.dbotthepony.mc.otm.client.moveMousePosScaled import ru.dbotthepony.mc.otm.client.screen.panels.* import ru.dbotthepony.mc.otm.core.maxScrollDivision import ru.dbotthepony.mc.otm.menu.MatteryMenu +import java.util.Collections /** * This class encapsulate most of logic for handling EditablePanel and it's children. @@ -34,6 +35,7 @@ abstract class MatteryScreen(menu: T, inventory: Inventory, tit constructor(menu: T, title: Component) : this(menu, menu.inventory, title) protected val panels = ArrayDeque>() + val panelsView: List> = Collections.unmodifiableList(panels) var inventoryFrame: FramePanel>? = null var mainFrame: FramePanel>? = null diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/AbstractSlotPanel.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/AbstractSlotPanel.kt index 5f854368e..29281604b 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/AbstractSlotPanel.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/AbstractSlotPanel.kt @@ -20,7 +20,7 @@ abstract class AbstractSlotPanel> @JvmOverloads constru width: Float = SIZE, height: Float = SIZE, open val noItemIcon: SkinElement? = null -) : EditablePanel(screen, parent, x, y, width, height) { +) : EditablePanel(screen, parent, x, y, width, height), IItemStackPanel { protected open fun renderSlotBackground(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float) { SLOT_BACKGROUND.render(stack, width = width, height = height) } @@ -68,15 +68,13 @@ abstract class AbstractSlotPanel> @JvmOverloads constru } } - protected abstract fun getItemStack(): ItemStack - protected open fun getItemStackTooltip(stack: ItemStack): List { return screen.getTooltipFromItem(stack) } override fun innerRender(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float) { renderSlotBackground(stack, mouseX, mouseY, partialTick) - val itemStack = getItemStack() + val itemStack = itemStack renderRegular(stack, itemStack) if (itemStack.isEmpty) { @@ -86,7 +84,7 @@ abstract class AbstractSlotPanel> @JvmOverloads constru override fun innerRenderTooltips(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float): Boolean { if (isHovered) { - val itemstack = getItemStack() + val itemstack = itemStack if (!itemstack.isEmpty) { // val font = RenderProperties.get(itemstack).getFont(itemstack) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/EditablePanel.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/EditablePanel.kt index 332a1ed6b..4221356df 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/EditablePanel.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/EditablePanel.kt @@ -6,19 +6,20 @@ import com.mojang.blaze3d.vertex.PoseStack import net.minecraft.client.gui.Font import net.minecraft.client.gui.components.events.GuiEventListener import net.minecraft.client.gui.screens.Screen +import net.minecraft.client.renderer.Rect2i import net.minecraft.network.chat.Component import net.minecraft.world.inventory.Slot +import net.minecraft.world.item.ItemStack import org.apache.logging.log4j.LogManager import ru.dbotthepony.mc.otm.client.minecraft import ru.dbotthepony.mc.otm.client.moveMousePosScaled -import ru.dbotthepony.mc.otm.client.render.ScissorRect import ru.dbotthepony.mc.otm.client.render.currentScissorRect import ru.dbotthepony.mc.otm.client.render.popScissorRect import ru.dbotthepony.mc.otm.client.render.pushScissorRect import ru.dbotthepony.mc.otm.client.screen.MatteryScreen import java.util.* import kotlin.collections.ArrayList -import kotlin.math.max +import kotlin.math.roundToInt @JvmRecord data class ScreenPos(val x: Float, val y: Float) @@ -44,6 +45,16 @@ interface ISlotPanel { val slot: S } +interface IItemStackPanel { + val itemStack: ItemStack +} + +data class Rect2f(val x: Float, val y: Float, val width: Float, val height: Float) { + fun toIntRect(): Rect2i { + return Rect2i(x.roundToInt(), y.roundToInt(), width.roundToInt(), height.roundToInt()) + } +} + open class EditablePanel @JvmOverloads constructor( val screen: S, parent: EditablePanel<*>?, @@ -262,17 +273,61 @@ open class EditablePanel @JvmOverloads constructor( dockPadding = DockProperty(left, top, right, bottom) } - // абсолютное начало координат этой панели и потомков - // негативно если потомок выходит за границы - var boundingX = 0f + /** + * Rectangle which hold all children in panel-local (to this panel) coordinates + */ + var childrenRectX = 0f private set - var boundingY = 0f + + /** + * Rectangle which hold all children in panel-local (to this panel) coordinates + */ + var childrenRectY = 0f private set - var boundingWidth = 0f + + /** + * Rectangle which hold all children in panel-local (to this panel) coordinates + */ + var childrenRectWidth = 0f private set - var boundingHeight = 0f + + /** + * Rectangle which hold all children in panel-local (to this panel) coordinates + */ + var childrenRectHeight = 0f private set + val isOutsideOfParent: Boolean get() { + val parent = parent ?: return false + return x < 0 || y < 0 || x + width > parent.width || y + height > parent.height + } + + fun calculateAbsoluteRectangle(): Rect2f { + val x = if (childrenRectX < 0) childrenRectX + absoluteX else absoluteX + val y = if (childrenRectY < 0) childrenRectY + absoluteY else absoluteY + val width = if (childrenRectWidth > width) childrenRectWidth else width + val height = if (childrenRectHeight > height) childrenRectHeight else height + + return Rect2f(x, y, width, height) + } + + fun calculateAbsoluteRectangles(): List { + if (childrenRectX >= 0 && childrenRectY >= 0 && childrenRectWidth <= width && childrenRectHeight <= height) { + return listOf(Rect2f(absoluteX, absoluteY, width, height)) + } + + val result = ArrayList() + result.add(Rect2f(absoluteX, absoluteY, width, height)) + + for (children in children) { + if (children.isOutsideOfParent) { + result.addAll(children.calculateAbsoluteRectangles()) + } + } + + return result + } + // Абсолютная координата, обновляется на отрисовке var absoluteX = 0f private set @@ -445,7 +500,7 @@ open class EditablePanel @JvmOverloads constructor( return false } - if ((boundingHeight > height || boundingWidth > width || boundingX < 0 || boundingY < 0) && parent == null) { + if ((childrenRectHeight > height || childrenRectWidth > width || childrenRectX < 0 || childrenRectY < 0) && parent == null) { var hit = false for (child in children) { @@ -511,6 +566,46 @@ open class EditablePanel @JvmOverloads constructor( return false to null } + fun findItemStack(mouseX: Float, mouseY: Float, ignoreMouseInputLock: Boolean = false): Pair { + if (!isVisible()) { + return false to ItemStack.EMPTY + } + + if (!acceptMouseInput && !ignoreMouseInputLock) { + return (mouseX >= absoluteX && + mouseX <= absoluteX + width && + mouseY >= absoluteY && + mouseY <= absoluteY + height) to ItemStack.EMPTY + } + + if (trapMouseInput && this is IItemStackPanel) { + return true to this.itemStack + } + + for (child in children) { + val (status, itemStack) = child.findItemStack(mouseX, mouseY, ignoreMouseInputLock) + + if (status) { + return true to itemStack + } + } + + if ( + mouseX >= absoluteX && + mouseX <= absoluteX + width && + mouseY >= absoluteY && + mouseY <= absoluteY + height + ) { + if (this is IItemStackPanel) { + return true to this.itemStack + } + + return true to ItemStack.EMPTY + } + + return false to ItemStack.EMPTY + } + fun renderTooltips(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float): Boolean { if (!isVisible()) { return false @@ -717,19 +812,22 @@ open class EditablePanel @JvmOverloads constructor( fun updateBounds() { boundsInvalidated = false - boundingX = 0f - boundingY = 0f - boundingWidth = 0f - boundingHeight = 0f + childrenRectX = width + childrenRectY = height + childrenRectWidth = 0f + childrenRectHeight = 0f for (child in children) { if (child.isVisible()) { - boundingX = boundingX.coerceAtMost(child.x) - boundingY = boundingY.coerceAtMost(child.y) - boundingWidth = boundingWidth.coerceAtLeast(child.x + child.width) - boundingHeight = boundingHeight.coerceAtLeast(child.y + child.height) + childrenRectX = childrenRectX.coerceAtMost(child.x) + childrenRectY = childrenRectY.coerceAtMost(child.y) + childrenRectWidth = childrenRectWidth.coerceAtLeast(child.x + child.width) + childrenRectHeight = childrenRectHeight.coerceAtLeast(child.y + child.height) } } + + childrenRectX = childrenRectX.coerceAtMost(childrenRectWidth) + childrenRectY = childrenRectY.coerceAtMost(childrenRectHeight) } /** @@ -941,8 +1039,8 @@ open class EditablePanel @JvmOverloads constructor( } fun withinExtendedBounds(x: Double, y: Double): Boolean { - val pos: ScreenPos = localToScreen(boundingX, boundingY) - return pos.x <= x && pos.x + boundingWidth > x && pos.y <= y && pos.y + boundingHeight > y + val pos: ScreenPos = localToScreen(childrenRectX, childrenRectY) + return pos.x <= x && pos.x + childrenRectWidth > x && pos.y <= y && pos.y + childrenRectHeight > y } fun killFocusForEverythingExcept(except: EditablePanel<*>) { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/FilterSlotPanel.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/FilterSlotPanel.kt index 22e4560fc..fa7b56c15 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/FilterSlotPanel.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/FilterSlotPanel.kt @@ -16,7 +16,7 @@ open class FilterSlotPanel> @JvmOverloads constructor( width: Float = SIZE, height: Float = SIZE ) : AbstractSlotPanel(screen, parent, x, y, width, height) { - override fun getItemStack(): ItemStack { + override val itemStack: ItemStack get() { return slot.get() } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/SlotPanel.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/SlotPanel.kt index 665797ef5..24f709207 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/SlotPanel.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/SlotPanel.kt @@ -44,9 +44,8 @@ open class SlotPanel, T : Slot> @JvmOverloads construct return true } - override fun getItemStack(): ItemStack { - return slot.item - } + override val itemStack: ItemStack + get() = slot.item override fun innerRender(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float) { renderSlotBackground(stack, mouseX, mouseY, partialTick) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jei/JEIPlugin.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jei/JEIPlugin.kt index 900707ffe..91083d5a2 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jei/JEIPlugin.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jei/JEIPlugin.kt @@ -3,20 +3,25 @@ package ru.dbotthepony.mc.otm.compat.jei import mezz.jei.api.IModPlugin import mezz.jei.api.JeiPlugin import mezz.jei.api.constants.RecipeTypes +import mezz.jei.api.gui.handlers.IGuiContainerHandler import mezz.jei.api.helpers.IJeiHelpers +import mezz.jei.api.registration.IGuiHandlerRegistration import mezz.jei.api.registration.IRecipeCatalystRegistration import mezz.jei.api.registration.IRecipeCategoryRegistration import mezz.jei.api.registration.IRecipeRegistration +import net.minecraft.client.renderer.Rect2i import net.minecraft.resources.ResourceLocation import net.minecraft.world.item.ItemStack import ru.dbotthepony.mc.otm.OverdriveThatMatters import ru.dbotthepony.mc.otm.client.minecraft -import ru.dbotthepony.mc.otm.registry.MBlocks +import ru.dbotthepony.mc.otm.client.screen.MatteryScreen import ru.dbotthepony.mc.otm.registry.MItems import ru.dbotthepony.mc.otm.registry.MRecipes import ru.dbotthepony.mc.otm.registry.WriteOnce +import java.util.stream.Collectors +import kotlin.math.roundToInt -var helpers: IJeiHelpers by WriteOnce() +var isJeiLoaded = false private set @JeiPlugin @@ -24,6 +29,13 @@ var helpers: IJeiHelpers by WriteOnce() class JEIPlugin : IModPlugin { companion object { private val LOCATION = ResourceLocation(OverdriveThatMatters.MOD_ID, "jei_plugin") + + var helpers: IJeiHelpers by WriteOnce() + private set + } + + init { + isJeiLoaded = true } override fun getPluginUid(): ResourceLocation { @@ -45,4 +57,20 @@ class JEIPlugin : IModPlugin { registration.addRecipes(PlatePressRecipeCategory.recipeType, level.recipeManager.getAllRecipesFor(MRecipes.PLATE_PRESS).filter { !it.isIncomplete }) } + + override fun registerGuiHandlers(registration: IGuiHandlerRegistration) { + registration.addGenericGuiContainerHandler(MatteryScreen::class.java, object : IGuiContainerHandler> { + override fun getGuiExtraAreas(containerScreen: MatteryScreen<*>): MutableList { + return containerScreen.panelsView.stream().map { it.calculateAbsoluteRectangles() }.flatMap { it.stream() }.map { it.toIntRect() }.collect(Collectors.toList()) + } + + override fun getIngredientUnderMouse( + containerScreen: MatteryScreen<*>, + mouseX: Double, + mouseY: Double + ): Any? { + return containerScreen.panelsView.stream().map { it.findItemStack(mouseX.toFloat(), mouseY.toFloat(), ignoreMouseInputLock = true) }.filter { it.first }.findAny().orElse(null)?.second + } + }) + } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jei/PlatePressRecipeCategory.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jei/PlatePressRecipeCategory.kt index 11f12c496..abcbd4448 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jei/PlatePressRecipeCategory.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jei/PlatePressRecipeCategory.kt @@ -83,7 +83,7 @@ object PlatePressRecipeCategory : IRecipeCategory, IDrawable { } private val iconField by lazy { - helpers.guiHelper.createDrawableItemStack(ItemStack(MItems.PLATE_PRESS)) + JEIPlugin.helpers.guiHelper.createDrawableItemStack(ItemStack(MItems.PLATE_PRESS)) } override fun getIcon(): IDrawable {