Improved, but not finished, android station GUI

This commit is contained in:
DBotThePony 2022-09-01 00:20:39 +07:00
parent f136760ace
commit 75fe096059
Signed by: DBot
GPG Key ID: DCC23B5715498507
8 changed files with 336 additions and 103 deletions

View File

@ -31,6 +31,7 @@ import ru.dbotthepony.mc.otm.network.AndroidResearchRequestPacket
import ru.dbotthepony.mc.otm.registry.MRegistry
import java.util.*
import kotlin.collections.ArrayList
import kotlin.properties.Delegates
private fun exploreTree(research: AndroidResearchType<*>, seen: MutableSet<AndroidResearchType<*>>, result: MutableList<AndroidResearchType<*>>) {
if (!seen.add(research)) {
@ -246,7 +247,7 @@ private class Tree(val node: AndroidResearchType<*>) : Iterable<Tree> {
}
}
private open class AndroidResearchButton(
private class AndroidResearchButton(
parent: EditablePanel,
private val node: AndroidResearch,
private val lines: List<LinePos>,
@ -410,22 +411,38 @@ private open class AndroidResearchButton(
class AndroidStationScreen constructor(p_97741_: AndroidStationMenu, p_97742_: Inventory, p_97743_: Component) :
MatteryScreen<AndroidStationMenu>(p_97741_, p_97742_, p_97743_) {
private var canvas: DraggableCanvasPanel? = null
private var research: FramePanel? = null
var hoveredResearch: AndroidResearch? = null
private fun openResearchTree() {
val window = minecraft!!.window
research = FramePanel(this, null, 0f, 0f, window.guiScaledWidth * 0.8f, window.guiScaledHeight * 0.8f, TranslatableComponent("otm.gui.android_research"))
private fun makeCanvas(isPreview: Boolean): DraggableCanvasPanel {
val rows = Int2ObjectAVLTreeMap<EditablePanel>()
canvas = object : DraggableCanvasPanel(this@AndroidStationScreen, research, width = (GRID_WIDTH * 22).toFloat(), height = 0f) {
val canvas = object : DraggableCanvasPanel(this@AndroidStationScreen, null) {
private val random = Random()
override fun innerRender(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float) {
drawColor = RGBAColor.BLACK
drawRect(stack, 0f, 0f, width, height)
}
override fun mouseClickedInner(x: Double, y: Double, button: Int): Boolean {
if (isPreview) {
openResearchTree()
return true
}
return super.mouseClickedInner(x, y, button)
}
override fun tick() {
super.tick()
if (isPreview) {
xOffset -= 1f
if (xOffset < -boundingWidth) {
xOffset = width
yOffset = random.nextFloat(-boundingHeight + 18f, 0f)
}
}
}
}
minecraft?.player?.getCapability(MatteryCapability.MATTERY_PLAYER)?.ifPresentK {
@ -475,7 +492,26 @@ class AndroidStationScreen constructor(p_97741_: AndroidStationMenu, p_97742_: I
}
}
canvas!!.dock = Dock.FILL
for (row in rows.values) {
row.sizeToContents()
row.acceptMouseInput = !isPreview
}
return canvas
}
private var research: FramePanel? = null
private var playerStrip: EditablePanel by Delegates.notNull()
var hoveredResearch: AndroidResearch? = null
private fun openResearchTree() {
val window = minecraft!!.window
research = FramePanel(this, null, 0f, 0f, window.guiScaledWidth * 0.8f, window.guiScaledHeight * 0.8f, TranslatableComponent("otm.gui.android_research"))
val researchCanvas = makeCanvas(false)
researchCanvas.dock = Dock.FILL
researchCanvas.parent = research
val bottom = EditablePanel(this, research, 0f, 0f, 0f, 20f)
val close = ButtonPanel(this, bottom, 0f, 0f, 90f, 20f, TranslatableComponent("otm.container.matter_panel.close"))
@ -486,7 +522,7 @@ class AndroidStationScreen constructor(p_97741_: AndroidStationMenu, p_97742_: I
close.bind { research!!.remove() }
bottom.setDockMargin(0f, 0f, 4f, 4f)
canvas!!.setDockMargin(4f, 4f, 4f, 4f)
researchCanvas.setDockMargin(4f, 4f, 4f, 4f)
research!!.toScreenCenter()
addPanel(research!!)
@ -505,14 +541,48 @@ class AndroidStationScreen constructor(p_97741_: AndroidStationMenu, p_97742_: I
}
override fun makeMainFrame(): FramePanel {
val frame = super.makeMainFrame()!!
val frame = FramePanel(this, 200f, 100f, title)
WidePowerGaugePanel(this, frame, menu.powerWidget, LEFT_MARGIN, GAUGE_TOP_WITH_SLOT)
SlotPanel(this, frame, menu.batterySlot, LEFT_MARGIN, SLOT_TOP_UNDER_GAUGE)
SlotPanel(this, frame, menu.androidBattery, 38f, 17f)
val stripLeft = EditablePanel(this, frame, width = AbstractSlotPanel.SIZE)
val button = ButtonPanel(this, frame, 38f, 69f, 124f, 20f, TranslatableComponent("otm.gui.android_research"))
button.bind(this::openResearchTree)
stripLeft.dock = Dock.LEFT
stripLeft.dockRight = 3f
WidePowerGaugePanel(this, stripLeft, menu.powerWidget).also {
it.dock = Dock.TOP
}
SlotPanel(this, stripLeft, menu.batterySlot).also {
it.dock = Dock.TOP
it.dockTop = 4f
}
val playerStrip = EditablePanel(this, frame, height = 72f)
playerStrip.dock = Dock.TOP
val armorStrip = EditablePanel(this, playerStrip, width = AbstractSlotPanel.SIZE)
armorStrip.dock = Dock.LEFT
for (slot in menu.armorSlots) {
SlotPanel(this, armorStrip, slot).also { it.dock = Dock.TOP }
}
EntityRendererPanel(this, playerStrip, minecraft!!.player!!).also {
it.dock = Dock.LEFT
}
val androidStrip = EditablePanel(this, playerStrip, width = AbstractSlotPanel.SIZE)
androidStrip.dock = Dock.LEFT
SlotPanel(this, androidStrip, menu.androidBattery).also { it.dock = Dock.TOP }
val preview = makeCanvas(true)
preview.parent = playerStrip
preview.height = 72f
preview.dock = Dock.TOP
this.playerStrip = playerStrip
return frame
}

View File

@ -110,23 +110,7 @@ class ExoSuitInventoryScreen(menu: ExoSuitInventoryMenu) : MatteryScreen<ExoSuit
panel.dock = Dock.TOP
}
val playerRectangle = object : EditablePanel(this@ExoSuitInventoryScreen, topLine, width = ENTITY_RECTANGLE.w) {
override fun innerRender(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float) {
ENTITY_RECTANGLE.render(stack)
val renderX = absoluteX.toInt() + width.toInt() / 2
val renderY = absoluteY.toInt() + (height * 0.9f).toInt()
InventoryScreen.renderEntityInInventory(
renderX,
renderY,
30, // scale
renderX - mouseX,
absoluteY + height * 0.15f - mouseY,
minecraft!!.player!!
)
}
}
val playerRectangle = EntityRendererPanel(this, topLine, minecraft!!.player!!)
playerRectangle.dock = Dock.LEFT
playerRectangle.setDockMargin(right = 3f)

View File

@ -10,6 +10,8 @@ import org.apache.logging.log4j.LogManager
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
@JvmRecord
@ -28,6 +30,10 @@ enum class Dock {
NONE, LEFT, RIGHT, TOP, BOTTOM, FILL
}
enum class DockResizeMode(val changeWidth: Boolean, val changeHeight: Boolean) {
ALL(true, true), NONE(false, false), WIDTH(true, false), HEIGHT(false, true)
}
open class EditablePanel @JvmOverloads constructor(
val screen: MatteryScreen<*>,
parent: EditablePanel?,
@ -46,7 +52,7 @@ open class EditablePanel @JvmOverloads constructor(
field?.onUnParent(this)
field = value
needsInvalidation = true
layoutInvalidated = true
value?.onParent(this)
}
@ -56,8 +62,9 @@ open class EditablePanel @JvmOverloads constructor(
val old = field
field = value
needsInvalidation = true
// layoutInvalidated = true
xPosUpdated(value, old)
parent?.boundsInvalidated = true
}
var y: Float = 0f
@ -66,8 +73,9 @@ open class EditablePanel @JvmOverloads constructor(
val old = field
field = value
needsInvalidation = true
// layoutInvalidated = true
yPosUpdated(value, old)
parent?.boundsInvalidated = true
}
var width: Float = 10f
@ -78,8 +86,9 @@ open class EditablePanel @JvmOverloads constructor(
val old = field
field = value
needsInvalidation = true
layoutInvalidated = true
widthUpdated(value, old)
parent?.boundsInvalidated = true
}
var height: Float = 10f
@ -90,13 +99,33 @@ open class EditablePanel @JvmOverloads constructor(
val old = field
field = value
needsInvalidation = true
layoutInvalidated = true
heightUpdated(value, old)
parent?.boundsInvalidated = true
}
/**
* Width to be utilized in docking code.
*
* Can only differ from [width] if [dockResize] is not [DockResizeMode.ALL] and not [DockResizeMode.WIDTH]
*/
var dockedWidth: Float = 0f
private set
/**
* Height to be utilized in docking code.
*
* Can only differ from [height] if [dockResize] is not [DockResizeMode.ALL] and not [DockResizeMode.HEIGHT]
*/
var dockedHeight: Float = 0f
private set
@JvmField
protected val children = ArrayList<EditablePanel>()
private var needsInvalidation = true
val childrenView: List<EditablePanel> = Collections.unmodifiableList(children)
private var layoutInvalidated = true
private var boundsInvalidated = true
protected var visibleAsChildren: Boolean = true
private set
@ -110,8 +139,8 @@ open class EditablePanel @JvmOverloads constructor(
if (old != new) {
visibilityChanges(new, old)
parent?.needsInvalidation = true
needsInvalidation = true
parent?.layoutInvalidated = true
layoutInvalidated = true
if (new) {
killFocus()
@ -130,24 +159,64 @@ open class EditablePanel @JvmOverloads constructor(
var scissor = false
var dock = Dock.NONE
set(value) {
field = value
parent?.needsInvalidation = true
if (field != value) {
field = value
parent?.layoutInvalidated = true
}
}
var dockResize = DockResizeMode.ALL
set(value) {
if (field != value) {
field = value
parent?.layoutInvalidated = true
}
}
var dockMargin = DockProperty.EMPTY
set(value) {
if (field != value) {
parent?.needsInvalidation = true
field = value
parent?.layoutInvalidated = true
}
field = value
}
var dockLeft: Float
get() = dockMargin.left
set(value) {
if (value != dockMargin.left) {
dockMargin = dockMargin.copy(left = value)
}
}
var dockRight: Float
get() = dockMargin.right
set(value) {
if (value != dockMargin.right) {
dockMargin = dockMargin.copy(right = value)
}
}
var dockTop: Float
get() = dockMargin.top
set(value) {
if (value != dockMargin.top) {
dockMargin = dockMargin.copy(top = value)
}
}
var dockBottom: Float
get() = dockMargin.bottom
set(value) {
if (value != dockMargin.bottom) {
dockMargin = dockMargin.copy(bottom = value)
}
}
var dockPadding = DockProperty.EMPTY
set(value) {
if (field != value) {
parent?.needsInvalidation = true
field = value
parent?.layoutInvalidated = true
}
field = value
}
var acceptMouseInput = true
@ -228,7 +297,7 @@ open class EditablePanel @JvmOverloads constructor(
val font: Font get() = screen.font
fun invalidateLayout() {
needsInvalidation = true
layoutInvalidated = true
}
fun most3DHeight(): Float {
@ -252,7 +321,7 @@ open class EditablePanel @JvmOverloads constructor(
private fun onParent(child: EditablePanel) {
if (children.contains(child)) throw IllegalStateException("Already containing $child")
children.add(child)
needsInvalidation = true
layoutInvalidated = true
updateVisible()
}
@ -260,7 +329,7 @@ open class EditablePanel @JvmOverloads constructor(
val indexOf = children.indexOf(child)
if (indexOf == -1) throw IllegalStateException("Already not containing $child")
children.removeAt(indexOf)
needsInvalidation = true
layoutInvalidated = true
updateVisible()
}
@ -280,9 +349,10 @@ open class EditablePanel @JvmOverloads constructor(
var depth = 0f
if (needsInvalidation) {
needsInvalidation = false
if (layoutInvalidated) {
performLayout()
} else if (boundsInvalidated) {
updateBounds()
}
val parent = this.parent
@ -412,6 +482,8 @@ open class EditablePanel @JvmOverloads constructor(
* In almost all cases you want to call [invalidateLayout], and not this.
*/
open fun performLayout() {
layoutInvalidated = false
var left = dockPadding.left
var right = dockPadding.right
var top = dockPadding.top
@ -424,57 +496,59 @@ open class EditablePanel @JvmOverloads constructor(
Dock.FILL -> {}
Dock.LEFT -> {
child.setPos(left + child.dockMargin.left, top + child.dockMargin.top)
child.x = left + child.dockMargin.left
child.y = top + child.dockMargin.top
left += child.width + child.dockMargin.left + child.dockMargin.right
child.height = (
Math.max(
1f,
height - top - bottom - child.dockMargin.top - child.dockMargin.bottom
)
)
child.dockedHeight = 1f.coerceAtLeast(height - top - bottom - child.dockMargin.top - child.dockMargin.bottom)
if (child.dockResize.changeHeight)
child.height = child.dockedHeight
else if (child.height != child.dockedHeight)
child.y += child.dockedHeight / 2f - child.height / 2f
}
Dock.RIGHT -> {
child.setPos(
width - right - child.width - child.dockMargin.right,
top + child.dockMargin.top
)
child.x = width - right - child.width - child.dockMargin.right
child.y = top + child.dockMargin.top
right += child.width + child.dockMargin.left + child.dockMargin.right
child.height = (
Math.max(
1f,
height - top - bottom - child.dockMargin.top - child.dockMargin.bottom
)
)
child.dockedHeight = 1f.coerceAtLeast(height - top - bottom - child.dockMargin.top - child.dockMargin.bottom)
if (child.dockResize.changeHeight)
child.height = child.dockedHeight
else if (child.height != child.dockedHeight)
child.y += child.dockedHeight / 2f - child.height / 2f
}
Dock.TOP -> {
child.setPos(left + child.dockMargin.left, top + child.dockMargin.top)
child.x = left + child.dockMargin.left
child.y = top + child.dockMargin.top
top += child.height + child.dockMargin.top + child.dockMargin.bottom
child.width = (
Math.max(
1f,
width - left - right - child.dockMargin.left - child.dockMargin.right
)
)
child.dockedWidth = 1f.coerceAtLeast(width - left - right - child.dockMargin.left - child.dockMargin.right)
if (child.dockResize.changeWidth)
child.width = child.dockedWidth
else if (child.width != child.dockedWidth)
child.x += child.dockedWidth / 2f - child.width / 2f
}
Dock.BOTTOM -> {
child.setPos(
left + child.dockMargin.left,
height - bottom - child.height - child.dockMargin.bottom
)
child.x = left + child.dockMargin.left
child.y = height - bottom - child.height - child.dockMargin.bottom
bottom += child.height + child.dockMargin.top + child.dockMargin.bottom
child.width = (
Math.max(
1f,
width - left - right - child.dockMargin.left - child.dockMargin.right
)
)
child.dockedWidth = 1f.coerceAtLeast(width - left - right - child.dockMargin.left - child.dockMargin.right)
if (child.dockResize.changeWidth)
child.width = child.dockedWidth
else if (child.width != child.dockedWidth)
child.x += child.dockedWidth / 2f - child.width / 2f
}
}
}
@ -489,15 +563,30 @@ open class EditablePanel @JvmOverloads constructor(
LOGGER.error("Unable to fit $child inside $this (FILL returned dimensions of $w $h)")
}
child.setDimensions(
left + child.dockMargin.left,
top + child.dockMargin.top,
Math.max(1f, w),
Math.max(1f, h)
)
child.x = left + child.dockMargin.left
child.y = top + child.dockMargin.top
child.dockedWidth = w.coerceAtLeast(1f)
child.dockedHeight = h.coerceAtLeast(1f)
if (child.dockResize.changeHeight)
child.height = child.dockedHeight
else if (child.height != child.dockedHeight)
child.y += child.dockedHeight / 2f - child.height / 2f
if (child.dockResize.changeWidth)
child.width = child.dockedWidth
else if (child.width != child.dockedWidth)
child.x += child.dockedWidth / 2f - child.width / 2f
}
}
updateBounds()
}
fun updateBounds() {
boundsInvalidated = false
boundingX = 0f
boundingY = 0f
boundingWidth = 0f
@ -513,6 +602,30 @@ open class EditablePanel @JvmOverloads constructor(
}
}
/**
* Attempts to tightly fit dimensions to all children
*
* Performs layout if required
*/
open fun sizeToContents() {
if (layoutInvalidated) {
performLayout()
}
var width = 1f
var height = 1f
for (child in children) {
if (child.isVisible()) {
width = width.coerceAtLeast(child.x + child.width)
height = height.coerceAtLeast(child.y + child.height)
}
}
this.width = width
this.height = height
}
companion object {
private val LOGGER = LogManager.getLogger()!!
}

View File

@ -0,0 +1,58 @@
package ru.dbotthepony.mc.otm.client.screen.panels
import com.mojang.blaze3d.vertex.PoseStack
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen
import net.minecraft.client.gui.screens.inventory.InventoryScreen
import net.minecraft.world.entity.LivingEntity
import ru.dbotthepony.mc.otm.client.render.element
import ru.dbotthepony.mc.otm.client.screen.ExoSuitInventoryScreen
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
private fun calculateScale(width: Float, height: Float): Int {
val aspectRatio = width / height
if (aspectRatio.isNaN()) {
return 1
}
if (aspectRatio < 0.7083) {
return (width / 51f * 30f).toInt()
} else {
return (height / 72f * 30f).toInt()
}
}
class EntityRendererPanel(
screen: MatteryScreen<*>,
parent: EditablePanel?,
val entity: LivingEntity,
x: Float = 0f,
y: Float = 0f,
width: Float = ENTITY_RECTANGLE.w,
height: Float = ENTITY_RECTANGLE.h,
var renderScale: Int = calculateScale(width, height)
) : EditablePanel(screen, parent, x, y, width, height) {
override fun innerRender(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float) {
ExoSuitInventoryScreen.ENTITY_RECTANGLE.render(stack)
if (entity.isDeadOrDying) {
return
}
val renderX = absoluteX.toInt() + width.toInt() / 2
val renderY = absoluteY.toInt() + (height * 0.9f).toInt()
InventoryScreen.renderEntityInInventory(
renderX,
renderY,
renderScale,
renderX - mouseX,
absoluteY + height * 0.15f - mouseY,
entity
)
}
companion object {
val ENTITY_RECTANGLE = AbstractContainerScreen.INVENTORY_LOCATION.element(25f, 7f, 51f, 72f)
}
}

View File

@ -1,5 +1,6 @@
package ru.dbotthepony.mc.otm.menu
import com.google.common.collect.ImmutableList
import net.minecraft.world.SimpleContainer
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player
@ -38,7 +39,10 @@ class AndroidStationMenu @JvmOverloads constructor(
override val storageSlots = listOf(addSlot(androidBattery))
val armorSlots = makeArmorSlots()
init {
armorSlots.forEach(this::addSlot)
addInventorySlots()
}
}

View File

@ -48,12 +48,7 @@ class ExoSuitInventoryMenu(val capability: MatteryPlayerCapability) : MatteryMen
mainInventorySlots = builder.build()
}
val armorSlots: List<EquipmentSlot> = ImmutableList.of(
EquipmentSlot(net.minecraft.world.entity.EquipmentSlot.HEAD),
EquipmentSlot(net.minecraft.world.entity.EquipmentSlot.CHEST),
EquipmentSlot(net.minecraft.world.entity.EquipmentSlot.LEGS),
EquipmentSlot(net.minecraft.world.entity.EquipmentSlot.FEET),
)
val armorSlots: List<EquipmentSlot> = makeArmorSlots()
val offhandSlot = object : InventorySlot(capability.ply.inventory, 40) {
override fun getNoItemIcon(): Pair<ResourceLocation, ResourceLocation> {

View File

@ -372,6 +372,13 @@ abstract class MatteryMenu @JvmOverloads protected constructor(
super.addDataSlots(p_38885_)
}
fun makeArmorSlots(): List<EquipmentSlot> = ImmutableList.of(
EquipmentSlot(net.minecraft.world.entity.EquipmentSlot.HEAD),
EquipmentSlot(net.minecraft.world.entity.EquipmentSlot.CHEST),
EquipmentSlot(net.minecraft.world.entity.EquipmentSlot.LEGS),
EquipmentSlot(net.minecraft.world.entity.EquipmentSlot.FEET),
)
companion object {
val TEXTURE_EMPTY_SLOTS: List<ResourceLocation> = ImmutableList.of(
InventoryMenu.EMPTY_ARMOR_SLOT_BOOTS,

View File

@ -12,7 +12,9 @@ class LevelGaugeWidget(menu: MatteryMenu) : AbstractWidget(menu) {
var maxLevel = {ImpreciseFraction.ONE }
var levelContainer by menu.mSynchronizer.fraction()
private set
var maxLevelContainer by menu.mSynchronizer.fraction()
private set
constructor(
menu: MatteryMenu,