Exosuit effect list, clearDepth(), functional EditablePanel#remove

Fixes #78
This commit is contained in:
DBotThePony 2022-09-10 13:37:31 +07:00
parent c306694483
commit 72dca1457b
Signed by: DBot
GPG Key ID: DCC23B5715498507
15 changed files with 460 additions and 38 deletions

View File

@ -1,15 +1,20 @@
package ru.dbotthepony.mc.otm.client.render package ru.dbotthepony.mc.otm.client.render
import com.mojang.blaze3d.platform.GlStateManager
import com.mojang.blaze3d.systems.RenderSystem import com.mojang.blaze3d.systems.RenderSystem
import com.mojang.blaze3d.vertex.* import com.mojang.blaze3d.vertex.*
import com.mojang.math.Matrix4f import com.mojang.math.Matrix4f
import net.minecraft.client.renderer.GameRenderer import net.minecraft.client.renderer.GameRenderer
import net.minecraft.client.renderer.texture.TextureAtlasSprite
import org.lwjgl.opengl.GL11 import org.lwjgl.opengl.GL11
import org.lwjgl.opengl.GL11.GL_ALWAYS
import org.lwjgl.opengl.GL11.GL_LESS
import ru.dbotthepony.mc.otm.client.minecraft import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.core.RGBAColor import ru.dbotthepony.mc.otm.core.RGBAColor
import kotlin.math.acos import kotlin.math.acos
import kotlin.math.cos import kotlin.math.cos
import kotlin.math.pow import kotlin.math.pow
import kotlin.math.roundToInt
import kotlin.math.sin import kotlin.math.sin
private val identity = Matrix4f().also { it.setIdentity() } private val identity = Matrix4f().also { it.setIdentity() }
@ -404,7 +409,40 @@ fun drawLine(
width: Float width: Float
) = drawLine(identity, startX, startY, endX, endY, width) ) = drawLine(identity, startX, startY, endX, endY, width)
private data class ScissorRect(val x: Int, val y: Int, val width: Int, val height: Int) data class ScissorRect(val x: Int, val y: Int, val width: Int, val height: Int) {
fun withinBounds(x: Int, y: Int): Boolean {
return (x in this.x .. this.x + width) && (y in this.y .. this.y + height)
}
fun cross(x: Int, y: Int, width: Int, height: Int): Boolean {
if (withinBounds(x, y) || withinBounds(x + width, y) || withinBounds(x, y + height) || withinBounds(x + width, y + height)) {
return true
}
if (y in this.y .. this.y + height) {
if (x < this.x && x + width > this.x + this.width) {
return true
}
} else if (x in this.x .. this.x + width) {
if (y < this.y && y + height > this.y + this.height) {
return true
}
}
return false
}
fun crossScaled(x: Float, y: Float, width: Float, height: Float): Boolean {
val scale = minecraft.window.guiScale
return cross((x * scale).roundToInt(), (y * scale).roundToInt(), (width * scale).roundToInt(), (height * scale).roundToInt())
}
fun crossScaled(x: Double, y: Double, width: Double, height: Double): Boolean {
val scale = minecraft.window.guiScale
return cross((x * scale).roundToInt(), (y * scale).roundToInt(), (width * scale).roundToInt(), (height * scale).roundToInt())
}
}
private val scissorStack = ArrayDeque<ScissorRect>() private val scissorStack = ArrayDeque<ScissorRect>()
@Suppress("NAME_SHADOWING") @Suppress("NAME_SHADOWING")
@ -447,3 +485,78 @@ fun popScissorRect() {
RenderSystem.enableScissor(peek.x, y, peek.width, peek.height) RenderSystem.enableScissor(peek.x, y, peek.width, peek.height)
} }
val currentScissorRect get() = scissorStack.lastOrNull()
fun TextureAtlasSprite.render(
stack: PoseStack,
x: Float = 0f,
y: Float = 0f,
width: Float = this.width.toFloat(),
height: Float = this.height.toFloat(),
winding: UVWindingOrder = UVWindingOrder.NORMAL
) {
RenderSystem.setShaderTexture(0, atlas().location())
if (winding.isIdentity) {
drawTexturedRect(stack.last().pose(), x, y, width, height, u0, v0, u1, v1)
} else {
drawTexturedRect(stack.last().pose(), x, y, width, height, winding.translate(u0, v0, u1, v1))
}
}
fun determineTooltipPosition(x: Float, y: Float, width: Float, height: Float): Pair<Float, Float> {
val windowWidth = minecraft.window.guiScaledWidth.toFloat()
val windowHeight = minecraft.window.guiScaledHeight.toFloat()
if (x + width <= windowWidth) {
if (y + height <= windowHeight) {
return x to y
}
return x to (windowHeight - height)
} else {
if (y + height <= windowHeight) {
return (x - width - 4f) to y
}
return (x - width - 4f) to (windowHeight - height)
}
}
/**
* Draws literally nothing to color buffer, but draws to depth buffer.
*
* Allows to fix a fuckton of issues inside GUI.
*
* thanks Mojang
*/
fun clearDepth(stack: PoseStack, x: Float, y: Float, width: Float, height: Float, depth: Float = -600f) {
val oldShader = RenderSystem.getShader()
RenderSystem.setShader(GameRenderer::getPositionShader)
RenderSystem.blendFuncSeparate(GlStateManager.SourceFactor.ZERO, GlStateManager.DestFactor.ONE, GlStateManager.SourceFactor.ZERO, GlStateManager.DestFactor.ONE)
RenderSystem.enableBlend()
RenderSystem.enableDepthTest()
RenderSystem.depthFunc(GL_ALWAYS)
val builder = tesselator.builder
val matrix = stack.last().pose()
builder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION)
builder.vertex(matrix, x, y + height, depth).endVertex()
builder.vertex(matrix, x + width, y + height, depth).endVertex()
builder.vertex(matrix, x + width, y, depth).endVertex()
builder.vertex(matrix, x, y, depth).endVertex()
BufferUploader.drawWithShader(builder.end())
RenderSystem.defaultBlendFunc()
RenderSystem.depthFunc(GL_LESS)
RenderSystem.disableDepthTest()
if (oldShader != null) {
RenderSystem.setShader { oldShader }
}
}

View File

@ -111,6 +111,17 @@ data class SkinElement @JvmOverloads constructor(
require(imageHeight > 0f) { "Invalid image height $imageHeight" } require(imageHeight > 0f) { "Invalid image height $imageHeight" }
} }
/**
* See [ru.dbotthepony.mc.otm.client.render.clearDepth]
*/
fun clearDepth(
stack: PoseStack,
x: Float = 0f,
y: Float = 0f,
width: Float = this.width,
height: Float = this.height,
) = ru.dbotthepony.mc.otm.client.render.clearDepth(stack, x, y, width, height)
@JvmOverloads @JvmOverloads
fun render( fun render(
stack: PoseStack, stack: PoseStack,
@ -312,5 +323,38 @@ data class StretchingRectangleElement(
bottomLeft.render(stack, x, y + height - bottomLeft.height) bottomLeft.render(stack, x, y + height - bottomLeft.height)
bottomRight.render(stack, x + width - bottomRight.width, y + height - bottomLeft.height) bottomRight.render(stack, x + width - bottomRight.width, y + height - bottomLeft.height)
} }
companion object {
fun square(
texture: ResourceLocation,
x: Float,
y: Float,
cornerWidth: Float,
cornerHeight: Float,
width: Float,
height: Float,
sideThickness: Float,
imageWidth: Float = 256f,
imageHeight: Float = 256f,
): StretchingRectangleElement {
val innerThickness = sideThickness.coerceAtLeast(cornerWidth).coerceAtLeast(cornerHeight)
val padding = sideThickness - innerThickness
return StretchingRectangleElement(
topLeft = texture.element(x, y, cornerWidth, cornerHeight, imageWidth, imageHeight),
topRight = texture.element(x + width - cornerWidth, y, cornerWidth, cornerHeight, imageWidth, imageHeight),
bottomLeft = texture.element(x, y + height - cornerHeight, cornerWidth, cornerHeight, imageWidth, imageHeight),
bottomRight = texture.element(x + width - cornerWidth, y + height - cornerHeight, cornerWidth, cornerHeight, imageWidth, imageHeight),
top = texture.element(x + cornerWidth, y, width - cornerWidth * 2f, sideThickness, imageWidth, imageHeight),
bottom = texture.element(x + cornerWidth, y + height - sideThickness, width - cornerWidth * 2f, sideThickness, imageWidth, imageHeight),
left = texture.element(x, y, sideThickness, height - cornerHeight * 2f, imageWidth, imageHeight),
right = texture.element(x, y + width - sideThickness, sideThickness, height - cornerHeight * 2f, imageWidth, imageHeight),
middle = texture.element(x + innerThickness, y + innerThickness, width - innerThickness * 2f, height - innerThickness * 2f, imageWidth, imageHeight),
padding = DockProperty(padding, padding, padding, padding)
)
}
}
} }

View File

@ -80,6 +80,8 @@ enum class UVWindingOrder(
U1_V1_U0_V0(2, 3, 0, 1), // mirror both U1_V1_U0_V0(2, 3, 0, 1), // mirror both
FLIP_FLOP(2, 3, 0, 1); // mirror both FLIP_FLOP(2, 3, 0, 1); // mirror both
val isIdentity: Boolean = u0 == 0 && v0 == 1 && u1 == 2 && v1 == 3
val u0Picker = pickers[u0] val u0Picker = pickers[u0]
val v0Picker = pickers[v0] val v0Picker = pickers[v0]
val u1Picker = pickers[u1] val u1Picker = pickers[u1]

View File

@ -387,7 +387,7 @@ private class AndroidResearchButton(
return true return true
} }
override fun innerRenderTooltips(stack: PoseStack, mouse_x: Float, mouse_y: Float, flag: Float): Boolean { override fun innerRenderTooltips(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float): Boolean {
if (isHovered) { if (isHovered) {
val list = ArrayList<Component>().also { it.addAll(node.screenTooltipLines) } val list = ArrayList<Component>().also { it.addAll(node.screenTooltipLines) }
@ -409,7 +409,7 @@ private class AndroidResearchButton(
} }
} }
screen.renderComponentTooltip(stack, list, mouse_x.toInt(), mouse_y.toInt()) screen.renderComponentTooltip(stack, list, mouseX.toInt(), mouseY.toInt())
} }
return isHovered return isHovered

View File

@ -169,6 +169,8 @@ class ExoSuitInventoryScreen(menu: ExoSuitInventoryMenu) : MatteryScreen<ExoSuit
moveMousePosScaled(y = movePixels) moveMousePosScaled(y = movePixels)
} }
EffectListPanel(this, frame, menu.ply, -56f, gridHeight = 6).tick()
return frame return frame
} }
@ -179,7 +181,6 @@ class ExoSuitInventoryScreen(menu: ExoSuitInventoryMenu) : MatteryScreen<ExoSuit
companion object { companion object {
val ENTITY_RECTANGLE = INVENTORY_LOCATION.element(25f, 7f, 51f, 72f) val ENTITY_RECTANGLE = INVENTORY_LOCATION.element(25f, 7f, 51f, 72f)
val STATUS_EFFECT_BG = INVENTORY_LOCATION.element(0f, 166f, 120f, 32f)
val CRAFT_ARROW = INVENTORY_LOCATION.element(135f, 29f, 16f, 13f) val CRAFT_ARROW = INVENTORY_LOCATION.element(135f, 29f, 16f, 13f)
const val FRAME_BASE_HEIGHT = 126f const val FRAME_BASE_HEIGHT = 126f

View File

@ -2,8 +2,6 @@ package ru.dbotthepony.mc.otm.client.screen.panels
import com.mojang.blaze3d.systems.RenderSystem import com.mojang.blaze3d.systems.RenderSystem
import com.mojang.blaze3d.vertex.PoseStack import com.mojang.blaze3d.vertex.PoseStack
import net.minecraft.client.gui.screens.Screen
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen
import net.minecraft.client.renderer.GameRenderer import net.minecraft.client.renderer.GameRenderer
import net.minecraft.network.chat.Component import net.minecraft.network.chat.Component
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
@ -91,7 +89,7 @@ abstract class AbstractSlotPanel<out S : MatteryScreen<*>> @JvmOverloads constru
} }
} }
override fun innerRenderTooltips(stack: PoseStack, mouse_x: Float, mouse_y: Float, flag: Float): Boolean { override fun innerRenderTooltips(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float): Boolean {
if (isHovered) { if (isHovered) {
val itemstack = getItemStack() val itemstack = getItemStack()
@ -104,8 +102,8 @@ abstract class AbstractSlotPanel<out S : MatteryScreen<*>> @JvmOverloads constru
screen.renderComponentTooltip( screen.renderComponentTooltip(
stack, stack,
getItemStackTooltip(itemstack), getItemStackTooltip(itemstack),
mouse_x.toInt(), mouseX.toInt(),
mouse_y.toInt(), mouseY.toInt(),
font ?: screen.font, font ?: screen.font,
itemstack itemstack
) )

View File

@ -318,13 +318,13 @@ abstract class EnumRectangleButtonPanel<out S : Screen, T : Enum<T>>(
} }
} }
override fun innerRenderTooltips(stack: PoseStack, mouse_x: Float, mouse_y: Float, flag: Float): Boolean { override fun innerRenderTooltips(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float): Boolean {
if (!isHovered && !isTrappingMouseInput()) { if (!isHovered && !isTrappingMouseInput()) {
return super.innerRenderTooltips(stack, mouse_x, mouse_y, flag) return super.innerRenderTooltips(stack, mouseX, mouseY, partialTick)
} }
if (mainTooltip == null && tooltipMapping.size == 0) { if (mainTooltip == null && tooltipMapping.size == 0) {
return super.innerRenderTooltips(stack, mouse_x, mouse_y, flag) return super.innerRenderTooltips(stack, mouseX, mouseY, partialTick)
} }
val listing = ArrayList<Component>() val listing = ArrayList<Component>()
@ -345,8 +345,8 @@ abstract class EnumRectangleButtonPanel<out S : Screen, T : Enum<T>>(
screen.renderComponentTooltip( screen.renderComponentTooltip(
stack, stack,
listing, listing,
mouse_x.toInt(), mouseX.toInt(),
mouse_y.toInt(), mouseY.toInt(),
font font
) )

View File

@ -3,7 +3,6 @@ package ru.dbotthepony.mc.otm.client.screen.panels
import com.google.common.collect.ImmutableList import com.google.common.collect.ImmutableList
import com.mojang.blaze3d.systems.RenderSystem import com.mojang.blaze3d.systems.RenderSystem
import com.mojang.blaze3d.vertex.PoseStack import com.mojang.blaze3d.vertex.PoseStack
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.Font import net.minecraft.client.gui.Font
import net.minecraft.client.gui.components.events.GuiEventListener import net.minecraft.client.gui.components.events.GuiEventListener
import net.minecraft.client.gui.screens.Screen import net.minecraft.client.gui.screens.Screen
@ -12,13 +11,13 @@ import net.minecraft.world.inventory.Slot
import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.LogManager
import ru.dbotthepony.mc.otm.client.minecraft import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.client.moveMousePosScaled import ru.dbotthepony.mc.otm.client.moveMousePosScaled
import ru.dbotthepony.mc.otm.client.render.currentScissorRect
import ru.dbotthepony.mc.otm.client.render.popScissorRect import ru.dbotthepony.mc.otm.client.render.popScissorRect
import ru.dbotthepony.mc.otm.client.render.pushScissorRect import ru.dbotthepony.mc.otm.client.render.pushScissorRect
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
import java.util.* import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
import kotlin.math.max import kotlin.math.max
import kotlin.math.roundToInt
@JvmRecord @JvmRecord
data class ScreenPos(val x: Float, val y: Float) data class ScreenPos(val x: Float, val y: Float)
@ -367,12 +366,26 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
} }
protected open fun innerRender(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float) {} protected open fun innerRender(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float) {}
protected open fun innerRenderTooltips(stack: PoseStack, mouse_x: Float, mouse_y: Float, flag: Float): Boolean { protected open fun innerRenderTooltips(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float): Boolean {
return false return false
} }
fun isVisible(): Boolean { fun isVisible(): Boolean {
return visible && visibleAsChildren return visible && visibleAsChildren && !isRemoved
}
fun isScissored(): Boolean {
var parent = parent
while (parent != null) {
if (parent.scissor) {
return true
}
parent = parent.parent
}
return false
} }
fun render(poseStack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float): Float { fun render(poseStack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float): Float {
@ -408,7 +421,7 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
val scissor = this.scissor val scissor = this.scissor
if (scissor) { if (scissor) {
val scale = Minecraft.getInstance().window.guiScale val scale = minecraft.window.guiScale
pushScissorRect( pushScissorRect(
(absoluteX * scale).toInt(), (absoluteX * scale).toInt(),
@ -418,10 +431,16 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
) )
} }
val currentScissorRect = currentScissorRect
if (currentScissorRect == null || currentScissorRect.crossScaled(absoluteX, absoluteY, width, height)) {
// do not render if we are getting cut off by screen scissor
poseStack.pushPose() poseStack.pushPose()
poseStack.translate(absoluteX.toDouble(), absoluteY.toDouble(), accumulatedDepth.toDouble()) poseStack.translate(absoluteX.toDouble(), absoluteY.toDouble(), accumulatedDepth.toDouble())
RenderSystem.setShaderColor(1f, 1f, 1f, 1f)
innerRender(poseStack, mouseX, mouseY, partialTick) innerRender(poseStack, mouseX, mouseY, partialTick)
poseStack.popPose() poseStack.popPose()
}
for (child in children) { for (child in children) {
if (child.isVisible()) { if (child.isVisible()) {
@ -429,7 +448,6 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
child.absoluteX = absoluteX + child.x + xOffset child.absoluteX = absoluteX + child.x + xOffset
child.absoluteY = absoluteY + child.y + yOffset child.absoluteY = absoluteY + child.y + yOffset
RenderSystem.setShaderColor(1f, 1f, 1f, 1f)
depth = max(depth, child.render(poseStack, mouseX, mouseY, partialTick)) depth = max(depth, child.render(poseStack, mouseX, mouseY, partialTick))
} }
} }
@ -442,6 +460,10 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
} }
open fun tickHover(mouseX: Float, mouseY: Float): Boolean { open fun tickHover(mouseX: Float, mouseY: Float): Boolean {
if (isRemoved) {
return false
}
if ((boundingHeight > height || boundingWidth > width || boundingX < 0 || boundingY < 0) && parent == null) { if ((boundingHeight > height || boundingWidth > width || boundingX < 0 || boundingY < 0) && parent == null) {
var hit = false var hit = false
@ -469,6 +491,10 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
} }
fun findSlot(mouseX: Float, mouseY: Float): Pair<Boolean, Slot?> { fun findSlot(mouseX: Float, mouseY: Float): Pair<Boolean, Slot?> {
if (!isVisible()) {
return false to null
}
if (!acceptMouseInput) { if (!acceptMouseInput) {
return (mouseX >= absoluteX && return (mouseX >= absoluteX &&
mouseX <= absoluteX + width && mouseX <= absoluteX + width &&
@ -509,6 +535,12 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
return false return false
} }
val currentScissorRect = currentScissorRect
if (currentScissorRect != null && !currentScissorRect.crossScaled(absoluteX, absoluteY, width, height)) {
return false
}
for (child in children) { for (child in children) {
if (child.isVisible() && child.renderTooltips(stack, mouseX, mouseY, partialTick)) { if (child.isVisible() && child.renderTooltips(stack, mouseX, mouseY, partialTick)) {
return true return true
@ -1164,34 +1196,42 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
open fun tick() { open fun tick() {
tick++ tick++
for (child in children) { for (child in Array(children.size) { children[it] }) {
child.tick() child.tick()
} }
} }
var isRemoved = false var isRemoved = false
private set private set
protected open fun onRemoved() {} protected open fun onRemoved() {}
fun remove() { fun remove() {
if (isRemoved || screen !is MatteryScreen<*>) { if (isRemoved) {
return return
} }
killFocus() killFocus()
screen.removePanel(this as EditablePanel<MatteryScreen<*>>)
for (child in children) { if (screen is MatteryScreen<*>) {
screen.removePanel(this as EditablePanel<MatteryScreen<*>>)
}
for (child in Array(children.size) { children[it] }) {
child.remove() child.remove()
} }
parent = null
onRemoved() onRemoved()
isRemoved = true isRemoved = true
} }
fun popup() { fun popup() {
if (isRemoved) {
return
}
if (screen is MatteryScreen<*>) { if (screen is MatteryScreen<*>) {
screen.popup(this as EditablePanel<MatteryScreen<*>>) screen.popup(this as EditablePanel<MatteryScreen<*>>)
} }
@ -1212,4 +1252,9 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
fun mouseToCenter() { fun mouseToCenter() {
moveMousePosScaled(absoluteX + width / 2f, absoluteY + height / 2f) moveMousePosScaled(absoluteX + width / 2f, absoluteY + height / 2f)
} }
/**
* See [ru.dbotthepony.mc.otm.client.render.clearDepth]
*/
fun clearDepth(stack: PoseStack, x: Float = 0f, y: Float = 0f, width: Float = this.width, height: Float = this.height) = ru.dbotthepony.mc.otm.client.render.clearDepth(stack, x, y, width, height)
} }

View File

@ -0,0 +1,216 @@
package ru.dbotthepony.mc.otm.client.screen.panels
import com.mojang.blaze3d.systems.RenderSystem
import com.mojang.blaze3d.vertex.PoseStack
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
import net.minecraft.client.gui.screens.Screen
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen
import net.minecraft.world.effect.MobEffect
import net.minecraft.world.effect.MobEffectInstance
import net.minecraft.world.effect.MobEffectUtil
import net.minecraft.world.entity.LivingEntity
import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.client.render.TextAlign
import ru.dbotthepony.mc.otm.client.render.determineTooltipPosition
import ru.dbotthepony.mc.otm.client.render.drawAligned
import ru.dbotthepony.mc.otm.client.render.element
import ru.dbotthepony.mc.otm.client.render.render
import ru.dbotthepony.mc.otm.core.RGBAColor
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.maxScrollDivision
import java.util.stream.Collectors
open class EffectListPanel<out S : Screen> @JvmOverloads constructor(
screen: S,
parent: EditablePanel<*>?,
val entity: LivingEntity,
x: Float = 0f,
y: Float = 0f,
gridWidth: Int = 2,
gridHeight: Int = 2,
) : EditablePanel<S>(screen, parent, x, y, gridWidth * (SQUARE_THIN.width + 2f), gridHeight * (SQUARE_THIN.height + 2f)) {
val scroll: DiscreteScrollBarPanel<out S> = DiscreteScrollBarPanel(screen, this, this::calculateMaxScroll, this::onScrolled, height = height)
var gridWidth: Int = gridWidth
set(value) {
if (field == value)
return
field = value
width = value * (SQUARE_THIN.width + 2f)
canvas.invalidateLayout()
}
var gridHeight: Int = gridHeight
set(value) {
if (field == value)
return
field = value
height = value * (SQUARE_THIN.height + 2f)
canvas.invalidateLayout()
}
var autoAlignOnScrollbarShow = true
init {
scroll.visible = false
scissor = true
}
open inner class EffectSquare(
val effect: MobEffectInstance,
x: Float = 0f,
y: Float = 0f,
width: Float = SQUARE_THIN.width,
height: Float = SQUARE_THIN.height,
) : EditablePanel<S>(screen, this@EffectListPanel.canvas, x, y, width, height) {
override fun mouseScrolledInner(x: Double, y: Double, scroll: Double): Boolean {
if (this@EffectListPanel.scroll.visible) {
this@EffectListPanel.scroll.mouseScrolledInner(x, y, scroll)
}
return true
}
override fun tick() {
super.tick()
if (effect.duration <= 0) {
remove()
}
}
override fun innerRender(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float) {
clearDepth(stack)
SQUARE_THIN.render(stack, width = width, height = height)
RenderSystem.setShaderColor(1f, 1f, 1f, 0.5f)
minecraft.mobEffectTextures.get(effect.effect).render(stack, x = 3f, y = 3f, width = width - 6f, height = height - 6f)
RenderSystem.setShaderColor(1f, 1f, 1f, 1f)
font.drawAligned(stack, MobEffectUtil.formatDuration(effect, 1.0f), TextAlign.CENTER_CENTER, width / 2f + 0.5f, height / 2f, RGBAColor.WHITE)
}
override fun innerRenderTooltips(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float): Boolean {
if (isHovered) {
val (x, y) = determineTooltipPosition(mouseX + 4f, mouseY - 12f, BAR.width, BAR.height)
BAR.clearDepth(stack, x, y)
BAR.render(stack, x, y)
val renderWidth = 18f
val renderHeight = 18f
minecraft.mobEffectTextures.get(effect.effect).render(
stack,
x = x + 8f,
y = y + BAR.height / 2f - renderHeight / 2f,
width = renderWidth,
height = renderHeight)
val name = effect.effect.displayName.copy()
if (effect.amplifier in 1 .. 9) {
name.append(" ").append(TranslatableComponent("enchantment.level.${effect.amplifier + 1}"))
} else if (effect.amplifier > 9) {
name.append(" ${effect.amplifier + 1}")
}
font.drawAligned(stack, name, TextAlign.TOP_LEFT, x + renderWidth + 12f, y + 8f, RGBAColor.WHITE)
font.drawAligned(stack, MobEffectUtil.formatDuration(effect, 1.0f), TextAlign.TOP_LEFT, x + renderWidth + 12f, y + 8f + font.lineHeight + 2f, 8355711)
}
return isHovered
}
}
protected val effectButtons: MutableMap<MobEffect, EffectSquare> = Object2ObjectArrayMap()
fun calculateMaxScroll(scrollBarPanel: DiscreteScrollBarPanel<*>): Int {
return maxScrollDivision(entity.activeEffects.size, gridWidth)
}
fun onScrolled(scrollBarPanel: DiscreteScrollBarPanel<*>, oldScroll: Int, newScroll: Int) {
canvas.yOffset = newScroll * -26f
}
override fun tick() {
for (effect in entity.activeEffects) {
effectButtons.computeIfAbsent(effect.effect, Object2ObjectFunction {
EffectSquare(effect)
})
}
super.tick()
}
val canvas = object : EditablePanel<S>(screen, this@EffectListPanel, 0f, 0f, width, height) {
override fun performLayout() {
super.performLayout()
val sorted = children.stream().filter { it is EffectSquare }.collect(Collectors.toList()) as MutableList<EffectListPanel<S>.EffectSquare>
sorted.sortWith { a, b ->
a.effect.duration.compareTo(b.effect.duration)
}
@Suppress("name_shadowing")
var y = 0
@Suppress("name_shadowing")
var x = 0
for (panel in sorted) {
panel.setPos(x * 26f, y * 26f)
x++
if (x >= gridWidth) {
x = 0
y++
}
}
onSortedEffectPanels(sorted)
}
override fun mouseScrolledInner(x: Double, y: Double, scroll: Double): Boolean {
if (this@EffectListPanel.scroll.visible) {
this@EffectListPanel.scroll.mouseScrolledInner(x, y, scroll)
}
return true
}
}
fun onSortedEffectPanels(sorted: List<EffectSquare>) {
if (sorted.size > (gridHeight * gridWidth) && !scroll.visible) {
scroll.visible = true
if (autoAlignOnScrollbarShow) {
this.x -= scroll.width + 2f
canvas.x = scroll.width + 2f
this.width += scroll.width + 2f
}
scroll.scroll = scroll.scroll
} else if (sorted.size <= (gridHeight * gridWidth) && scroll.visible) {
scroll.visible = false
scroll.scroll = 0
if (autoAlignOnScrollbarShow) {
this.x += scroll.width + 2f
canvas.x = 0f
this.width -= scroll.width + 2f
}
}
}
companion object {
val BAR = AbstractContainerScreen.INVENTORY_LOCATION.element(0f, 166f, 120f, 32f)
val SQUARE = AbstractContainerScreen.INVENTORY_LOCATION.element(0f, 198f, 32f, 32f)
val SQUARE_THIN = AbstractContainerScreen.INVENTORY_LOCATION.element(141f, 166f, 24f, 24f)
val SQUARE_THIN_HIGHLIGHT = AbstractContainerScreen.INVENTORY_LOCATION.element(165f, 166f, 24f, 24f)
}
}

View File

@ -3,6 +3,7 @@ package ru.dbotthepony.mc.otm.client.screen.panels
import com.mojang.blaze3d.vertex.PoseStack import com.mojang.blaze3d.vertex.PoseStack
import net.minecraft.client.gui.screens.Screen import net.minecraft.client.gui.screens.Screen
import net.minecraft.network.chat.Component import net.minecraft.network.chat.Component
import ru.dbotthepony.mc.otm.client.render.clearDepth
import ru.dbotthepony.mc.otm.core.TextComponent import ru.dbotthepony.mc.otm.core.TextComponent
import ru.dbotthepony.mc.otm.core.RGBAColor import ru.dbotthepony.mc.otm.core.RGBAColor
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
@ -30,6 +31,8 @@ open class Label<out S : Screen> @JvmOverloads constructor(
var color = RGBAColor.SLATE_GRAY var color = RGBAColor.SLATE_GRAY
override fun innerRender(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float) { override fun innerRender(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float) {
clearDepth(stack)
if (shadow) { if (shadow) {
font.draw(stack, text, shadowX, shadowY, shadowColor.toInt()) font.draw(stack, text, shadowX, shadowY, shadowColor.toInt())
} }

View File

@ -109,7 +109,7 @@ open class SlotPanel<out S : MatteryScreen<*>, T : Slot> @JvmOverloads construct
} }
} }
override fun innerRenderTooltips(@Nonnull stack: PoseStack, mouse_x: Float, mouse_y: Float, flag: Float): Boolean { override fun innerRenderTooltips(@Nonnull stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float): Boolean {
// no op, screen does it for us (completely) // no op, screen does it for us (completely)
return false return false
} }

View File

@ -80,9 +80,9 @@ open class MatterGaugePanel<S : Screen> @JvmOverloads constructor(
BufferUploader.drawWithShader(builder.end()) BufferUploader.drawWithShader(builder.end())
} }
override fun innerRenderTooltips(stack: PoseStack, mouse_x: Float, mouse_y: Float, flag: Float): Boolean { override fun innerRenderTooltips(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float): Boolean {
if (isHovered) { if (isHovered) {
screen.renderComponentTooltip(stack, makeTooltip(), mouse_x.toInt(), mouse_y.toInt()) screen.renderComponentTooltip(stack, makeTooltip(), mouseX.toInt(), mouseY.toInt())
return true return true
} }

View File

@ -37,9 +37,9 @@ open class PatternGaugePanel<S : Screen> @JvmOverloads constructor(
GAUGE_FOREGROUND.renderPartial(stack, y = this.height - height, height = height, winding = UVWindingOrder.U0_V1_U1_V0) GAUGE_FOREGROUND.renderPartial(stack, y = this.height - height, height = height, winding = UVWindingOrder.U0_V1_U1_V0)
} }
override fun innerRenderTooltips(stack: PoseStack, mouse_x: Float, mouse_y: Float, flag: Float): Boolean { override fun innerRenderTooltips(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float): Boolean {
if (isHovered) { if (isHovered) {
screen.renderComponentTooltip(stack, makeTooltip(), mouse_x.toInt(), mouse_y.toInt()) screen.renderComponentTooltip(stack, makeTooltip(), mouseX.toInt(), mouseY.toInt())
return true return true
} }

View File

@ -51,9 +51,9 @@ open class PowerGaugePanel<out S : Screen> @JvmOverloads constructor(
} }
} }
override fun innerRenderTooltips(stack: PoseStack, mouse_x: Float, mouse_y: Float, flag: Float): Boolean { override fun innerRenderTooltips(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float): Boolean {
if (isHovered) { if (isHovered) {
screen.renderComponentTooltip(stack, makeTooltip(), mouse_x.toInt(), mouse_y.toInt()) screen.renderComponentTooltip(stack, makeTooltip(), mouseX.toInt(), mouseY.toInt())
return true return true
} }

View File

@ -68,9 +68,9 @@ open class ProgressGaugePanel<S : Screen> @JvmOverloads constructor(
} }
} }
override fun innerRenderTooltips(stack: PoseStack, mouse_x: Float, mouse_y: Float, flag: Float): Boolean { override fun innerRenderTooltips(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float): Boolean {
if (isHovered) { if (isHovered) {
screen.renderComponentTooltip(stack, makeTooltip(), mouse_x.toInt(), mouse_y.toInt()) screen.renderComponentTooltip(stack, makeTooltip(), mouseX.toInt(), mouseY.toInt())
return true return true
} }