new rack UI
This commit is contained in:
parent
766a9e10ee
commit
1a5a31aafe
16 changed files with 443 additions and 47 deletions
Binary file not shown.
|
|
@ -51,6 +51,22 @@ class DeviceBlockEntity(
|
||||||
*/
|
*/
|
||||||
var shelf: UUID? = null
|
var shelf: UUID? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Order of shelves when the UI is open.
|
||||||
|
*
|
||||||
|
* This is propagated and merged between devices upon the UI being opened.
|
||||||
|
*/
|
||||||
|
val shelfOrder = mutableListOf<UUID>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* At which position the device should be sorted in the rack. Lower priorities mean earlier
|
||||||
|
* positions.
|
||||||
|
*
|
||||||
|
* Devices start out with the maximum possible priority and no shelf, which means they'll be
|
||||||
|
* appended to the end of the sidebar in an undefined order.
|
||||||
|
*/
|
||||||
|
var sortPriority = Int.MAX_VALUE
|
||||||
|
|
||||||
/** NBT compound keys. */
|
/** NBT compound keys. */
|
||||||
private object Nbt {
|
private object Nbt {
|
||||||
const val controls = "controls"
|
const val controls = "controls"
|
||||||
|
|
@ -72,6 +88,8 @@ class DeviceBlockEntity(
|
||||||
}
|
}
|
||||||
|
|
||||||
const val shelf = "shelf"
|
const val shelf = "shelf"
|
||||||
|
const val shelfOrder = "shelfOrder"
|
||||||
|
const val sortPriority = "sortPriority"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun readNbt(nbt: NbtCompound) {
|
override fun readNbt(nbt: NbtCompound) {
|
||||||
|
|
@ -131,9 +149,23 @@ class DeviceBlockEntity(
|
||||||
outputConnections[port] = blockPosition
|
outputConnections[port] = blockPosition
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nbt.containsUuid(Nbt.shelf)) {
|
shelf = if (nbt.containsUuid(Nbt.shelf)) {
|
||||||
shelf = nbt.getUuid(Nbt.shelf)
|
nbt.getUuid(Nbt.shelf)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
}
|
}
|
||||||
|
val shelfOrderNbt = nbt.getList(Nbt.shelfOrder, NbtElement.INT_ARRAY_TYPE.toInt())
|
||||||
|
shelfOrder.clear()
|
||||||
|
for (i in 0 until shelfOrderNbt.size) {
|
||||||
|
try {
|
||||||
|
val uuid = NbtHelper.toUuid(shelfOrderNbt[i])
|
||||||
|
shelfOrder.add(uuid)
|
||||||
|
} catch (_: IllegalArgumentException) {
|
||||||
|
// toUuid may throw an IllegalArgumentException if the UUID doesn't follow the
|
||||||
|
// expected format. In that case we just ignore the error and move on.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sortPriority = nbt.getInt(Nbt.sortPriority)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun writeNbt(nbt: NbtCompound) {
|
override fun writeNbt(nbt: NbtCompound) {
|
||||||
|
|
@ -170,6 +202,18 @@ class DeviceBlockEntity(
|
||||||
outputConnectionsNbt.add(connectionNbt)
|
outputConnectionsNbt.add(connectionNbt)
|
||||||
}
|
}
|
||||||
nbt.put(Nbt.outputConnections, outputConnectionsNbt)
|
nbt.put(Nbt.outputConnections, outputConnectionsNbt)
|
||||||
|
|
||||||
|
if (shelf != null) {
|
||||||
|
nbt.putUuid(Nbt.shelf, shelf)
|
||||||
|
} else {
|
||||||
|
nbt.remove(Nbt.shelf)
|
||||||
|
}
|
||||||
|
val shelfOrderNbt = NbtList()
|
||||||
|
for (shelfUuid in shelfOrder) {
|
||||||
|
shelfOrderNbt.add(NbtHelper.fromUuid(shelfUuid))
|
||||||
|
}
|
||||||
|
nbt.put(Nbt.shelfOrder, shelfOrderNbt)
|
||||||
|
nbt.putInt(Nbt.sortPriority, sortPriority)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onClientLoad(world: ClientWorld) {
|
override fun onClientLoad(world: ClientWorld) {
|
||||||
|
|
|
||||||
15
src/main/kotlin/net/liquidev/dawd3/common/lists.kt
Normal file
15
src/main/kotlin/net/liquidev/dawd3/common/lists.kt
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
package net.liquidev.dawd3.common
|
||||||
|
|
||||||
|
fun <T> moveElement(from: MutableList<T>, fromIndex: Int, to: MutableList<T>, toIndex: Int) {
|
||||||
|
if (from == to && fromIndex != toIndex) {
|
||||||
|
val element = from.removeAt(fromIndex)
|
||||||
|
to.add(
|
||||||
|
if (fromIndex < toIndex) toIndex - 1
|
||||||
|
else toIndex,
|
||||||
|
element,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
val element = from.removeAt(fromIndex)
|
||||||
|
to.add(toIndex, element)
|
||||||
|
}
|
||||||
|
}
|
||||||
104
src/main/kotlin/net/liquidev/dawd3/net/EditRack.kt
Normal file
104
src/main/kotlin/net/liquidev/dawd3/net/EditRack.kt
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
package net.liquidev.dawd3.net
|
||||||
|
|
||||||
|
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs
|
||||||
|
import net.fabricmc.fabric.api.networking.v1.PlayerLookup
|
||||||
|
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking
|
||||||
|
import net.liquidev.dawd3.Mod
|
||||||
|
import net.liquidev.dawd3.block.device.DeviceBlockEntity
|
||||||
|
import net.liquidev.dawd3.net.serialization.readOptionalUuid
|
||||||
|
import net.liquidev.dawd3.net.serialization.writeOptionalUuid
|
||||||
|
import net.liquidev.dawd3.ui.widget.rack.shelves.ShelfEditing
|
||||||
|
import net.minecraft.block.Block
|
||||||
|
import net.minecraft.network.PacketByteBuf
|
||||||
|
import net.minecraft.util.Identifier
|
||||||
|
import net.minecraft.util.math.BlockPos
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assign a shelf and shelf order to a block at the given position.
|
||||||
|
*
|
||||||
|
* When sent C2S, the data of the device block entity at the given position is modified to write
|
||||||
|
* back its shelf UUID and shelf order. The server then propagates the shelf order to every block
|
||||||
|
* entity in the rack and sends this packet S2C to all witnesses of the propagation.
|
||||||
|
*/
|
||||||
|
data class EditRack(
|
||||||
|
val blockPosition: BlockPos,
|
||||||
|
val shelf: UUID?,
|
||||||
|
val shelfOrder: Array<UUID>,
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
val id = Identifier(Mod.id, "edit_rack")
|
||||||
|
|
||||||
|
fun registerServerReceiver() {
|
||||||
|
ServerPlayNetworking.registerGlobalReceiver(id) { server, player, _, buffer, _ ->
|
||||||
|
val packet = deserialize(buffer)
|
||||||
|
server.execute {
|
||||||
|
val blockState = player.world.getBlockState(packet.blockPosition)
|
||||||
|
val blockEntity =
|
||||||
|
player.world.getBlockEntity(packet.blockPosition) as? DeviceBlockEntity
|
||||||
|
?: return@execute
|
||||||
|
blockEntity.shelf = packet.shelf
|
||||||
|
ShelfEditing.propagateShelfOrderData(
|
||||||
|
player.world,
|
||||||
|
packet.blockPosition,
|
||||||
|
packet.shelfOrder,
|
||||||
|
)
|
||||||
|
blockEntity.markDirty()
|
||||||
|
|
||||||
|
player.world.updateListeners(
|
||||||
|
packet.blockPosition,
|
||||||
|
blockState,
|
||||||
|
blockState,
|
||||||
|
Block.NOTIFY_LISTENERS,
|
||||||
|
)
|
||||||
|
for (witness in PlayerLookup.tracking(blockEntity)) {
|
||||||
|
if (witness != player) {
|
||||||
|
ServerPlayNetworking.send(witness, id, buffer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun registerClientReceiver() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun deserialize(buffer: PacketByteBuf) =
|
||||||
|
EditRack(
|
||||||
|
blockPosition = buffer.readBlockPos(),
|
||||||
|
shelf = buffer.readOptionalUuid(),
|
||||||
|
shelfOrder = Array(buffer.readVarInt()) { buffer.readUuid() }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun serialize(): PacketByteBuf =
|
||||||
|
PacketByteBufs.create()
|
||||||
|
.writeBlockPos(blockPosition)
|
||||||
|
.writeOptionalUuid(shelf)
|
||||||
|
.writeVarInt(shelfOrder.size)
|
||||||
|
.apply {
|
||||||
|
for (shelfUuid in shelfOrder) {
|
||||||
|
writeUuid(shelfUuid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (javaClass != other?.javaClass) return false
|
||||||
|
|
||||||
|
other as EditRack
|
||||||
|
|
||||||
|
if (blockPosition != other.blockPosition) return false
|
||||||
|
if (shelf != other.shelf) return false
|
||||||
|
return shelfOrder.contentEquals(other.shelfOrder)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = blockPosition.hashCode()
|
||||||
|
result = 31 * result + (shelf?.hashCode() ?: 0)
|
||||||
|
result = 31 * result + shelfOrder.contentHashCode()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -11,5 +11,7 @@ object Packets {
|
||||||
fun registerServerReceivers() {
|
fun registerServerReceivers() {
|
||||||
TweakControl.registerServerReceiver()
|
TweakControl.registerServerReceiver()
|
||||||
ControlTweaked.registerServerReceiver()
|
ControlTweaked.registerServerReceiver()
|
||||||
|
EditRack.registerServerReceiver()
|
||||||
|
ReorderRack.registerServerReceiver()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
67
src/main/kotlin/net/liquidev/dawd3/net/ReorderRack.kt
Normal file
67
src/main/kotlin/net/liquidev/dawd3/net/ReorderRack.kt
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
package net.liquidev.dawd3.net
|
||||||
|
|
||||||
|
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs
|
||||||
|
import net.fabricmc.fabric.api.networking.v1.PlayerLookup
|
||||||
|
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking
|
||||||
|
import net.liquidev.dawd3.Mod
|
||||||
|
import net.liquidev.dawd3.block.device.DeviceBlockEntity
|
||||||
|
import net.minecraft.block.Block
|
||||||
|
import net.minecraft.network.PacketByteBuf
|
||||||
|
import net.minecraft.util.Identifier
|
||||||
|
import net.minecraft.util.math.BlockPos
|
||||||
|
|
||||||
|
data class ReorderRack(val entries: List<Entry>) {
|
||||||
|
data class Entry(val blockPosition: BlockPos, val sortPriority: Int)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val id = Identifier(Mod.id, "reorder_rack")
|
||||||
|
|
||||||
|
fun registerServerReceiver() {
|
||||||
|
ServerPlayNetworking.registerGlobalReceiver(id) { server, player, _, buffer, _ ->
|
||||||
|
val packet = deserialize(buffer)
|
||||||
|
server.execute {
|
||||||
|
for (entry in packet.entries) {
|
||||||
|
val blockEntity =
|
||||||
|
player.world.getBlockEntity(entry.blockPosition) as? DeviceBlockEntity
|
||||||
|
?: continue
|
||||||
|
blockEntity.sortPriority = entry.sortPriority
|
||||||
|
blockEntity.markDirty()
|
||||||
|
|
||||||
|
val blockState = player.world.getBlockState(entry.blockPosition)
|
||||||
|
player.world.updateListeners(
|
||||||
|
entry.blockPosition,
|
||||||
|
blockState,
|
||||||
|
blockState,
|
||||||
|
Block.NOTIFY_LISTENERS
|
||||||
|
)
|
||||||
|
for (witness in PlayerLookup.tracking(blockEntity)) {
|
||||||
|
if (witness != player) {
|
||||||
|
ServerPlayNetworking.send(witness, id, buffer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun deserialize(buffer: PacketByteBuf) =
|
||||||
|
ReorderRack(
|
||||||
|
entries = MutableList(buffer.readVarInt()) {
|
||||||
|
Entry(
|
||||||
|
blockPosition = buffer.readBlockPos(),
|
||||||
|
sortPriority = buffer.readVarInt(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun serialize(): PacketByteBuf {
|
||||||
|
val buffer = PacketByteBufs.create()
|
||||||
|
buffer.writeVarInt(entries.size)
|
||||||
|
for (entry in entries) {
|
||||||
|
buffer.writeBlockPos(entry.blockPosition)
|
||||||
|
buffer.writeVarInt(entry.sortPriority)
|
||||||
|
}
|
||||||
|
return buffer
|
||||||
|
}
|
||||||
|
}
|
||||||
24
src/main/kotlin/net/liquidev/dawd3/net/serialization/bin.kt
Normal file
24
src/main/kotlin/net/liquidev/dawd3/net/serialization/bin.kt
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
package net.liquidev.dawd3.net.serialization
|
||||||
|
|
||||||
|
import net.minecraft.network.PacketByteBuf
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
val zeroUuid = UUID(0L, 0L)
|
||||||
|
|
||||||
|
fun PacketByteBuf.readOptionalUuid(): UUID? {
|
||||||
|
val uuid = readUuid()
|
||||||
|
return if (uuid.leastSignificantBits == 0L && uuid.mostSignificantBits == 0L) {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
uuid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun PacketByteBuf.writeOptionalUuid(uuid: UUID?): PacketByteBuf {
|
||||||
|
if (uuid != null) {
|
||||||
|
writeUuid(uuid)
|
||||||
|
} else {
|
||||||
|
writeUuid(zeroUuid)
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
@ -27,6 +27,7 @@ object Render {
|
||||||
sprite: Sprite,
|
sprite: Sprite,
|
||||||
) {
|
) {
|
||||||
RenderSystem.setShaderTexture(0, atlas.asset)
|
RenderSystem.setShaderTexture(0, atlas.asset)
|
||||||
|
RenderSystem.enableBlend()
|
||||||
DrawableHelper.drawTexture(
|
DrawableHelper.drawTexture(
|
||||||
matrices,
|
matrices,
|
||||||
x.toInt(),
|
x.toInt(),
|
||||||
|
|
@ -166,6 +167,7 @@ object Render {
|
||||||
) {
|
) {
|
||||||
// Temporarily defined to just defer to DrawableHelper, later on we should introduce support
|
// Temporarily defined to just defer to DrawableHelper, later on we should introduce support
|
||||||
// for rendering at floating-point coordinates.
|
// for rendering at floating-point coordinates.
|
||||||
|
RenderSystem.enableBlend()
|
||||||
DrawableHelper.drawTexture(
|
DrawableHelper.drawTexture(
|
||||||
matrices,
|
matrices,
|
||||||
x.toInt(),
|
x.toInt(),
|
||||||
|
|
@ -365,7 +367,7 @@ object Render {
|
||||||
tooltip(matrices, x, y, text, atlas, tooltip)
|
tooltip(matrices, x, y, text, atlas, tooltip)
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun colorized(r: Float, g: Float, b: Float, a: Float, then: () -> Unit) {
|
inline fun colorized(r: Float, g: Float, b: Float, a: Float, crossinline then: () -> Unit) {
|
||||||
val (oldR, oldG, oldB, oldA) = RenderSystem.getShaderColor()
|
val (oldR, oldG, oldB, oldA) = RenderSystem.getShaderColor()
|
||||||
RenderSystem.setShaderColor(r, g, b, a)
|
RenderSystem.setShaderColor(r, g, b, a)
|
||||||
RenderSystem.enableBlend() // sigh
|
RenderSystem.enableBlend() // sigh
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,8 @@ class RackScreen(
|
||||||
shownDevices: Iterable<BlockPos>,
|
shownDevices: Iterable<BlockPos>,
|
||||||
) : Screen(Text.translatable("screen.dawd3.rack.title")) {
|
) : Screen(Text.translatable("screen.dawd3.rack.title")) {
|
||||||
|
|
||||||
private val rootWidget = Rack(width.toFloat(), height.toFloat(), world, shownDevices)
|
private val rootWidget =
|
||||||
|
Rack(width.toFloat(), height.toFloat(), world, shownDevices)
|
||||||
|
|
||||||
override fun init() {
|
override fun init() {
|
||||||
super.init()
|
super.init()
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ abstract class Widget<in C, out M : Message>(var x: Float, var y: Float) {
|
||||||
|
|
||||||
abstract fun event(context: C, event: Event): M
|
abstract fun event(context: C, event: Event): M
|
||||||
|
|
||||||
inline fun drawInside(matrices: MatrixStack, draw: () -> Unit) {
|
inline fun drawInside(matrices: MatrixStack, crossinline draw: () -> Unit) {
|
||||||
matrices.push()
|
matrices.push()
|
||||||
matrices.translate(x.toDouble(), y.toDouble(), 0.0)
|
matrices.translate(x.toDouble(), y.toDouble(), 0.0)
|
||||||
draw()
|
draw()
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
package net.liquidev.dawd3.ui.widget.rack
|
package net.liquidev.dawd3.ui.widget.rack
|
||||||
|
|
||||||
|
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking
|
||||||
import net.liquidev.dawd3.block.device.DeviceBlockEntity
|
import net.liquidev.dawd3.block.device.DeviceBlockEntity
|
||||||
|
import net.liquidev.dawd3.common.moveElement
|
||||||
|
import net.liquidev.dawd3.net.EditRack
|
||||||
|
import net.liquidev.dawd3.net.ReorderRack
|
||||||
import net.liquidev.dawd3.render.NinePatch
|
import net.liquidev.dawd3.render.NinePatch
|
||||||
import net.liquidev.dawd3.render.Render
|
import net.liquidev.dawd3.render.Render
|
||||||
import net.liquidev.dawd3.render.TextureStrip
|
import net.liquidev.dawd3.render.TextureStrip
|
||||||
|
|
@ -12,6 +16,7 @@ import net.minecraft.client.util.math.MatrixStack
|
||||||
import net.minecraft.client.world.ClientWorld
|
import net.minecraft.client.world.ClientWorld
|
||||||
import net.minecraft.util.math.BlockPos
|
import net.minecraft.util.math.BlockPos
|
||||||
import org.lwjgl.glfw.GLFW
|
import org.lwjgl.glfw.GLFW
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
class Rack(
|
class Rack(
|
||||||
override var width: Float,
|
override var width: Float,
|
||||||
|
|
@ -20,10 +25,10 @@ class Rack(
|
||||||
shownDevices: Iterable<BlockPos>,
|
shownDevices: Iterable<BlockPos>,
|
||||||
) : Widget<Unit, Message>(0f, 0f) {
|
) : Widget<Unit, Message>(0f, 0f) {
|
||||||
|
|
||||||
private val sidebar = Sidebar(0f, 0f, 160f, 0f)
|
private val sidebar = Sidebar(0f, 0f, 0f, 0f)
|
||||||
private val blockPositionsByWidget = hashMapOf<DeviceWidget, BlockPos>()
|
private val blockPositionsByWidget = hashMapOf<DeviceWidget, BlockPos>()
|
||||||
|
|
||||||
private val shelves = MutableList(10) { Shelf(0f, 0f, width) }
|
private val shelves = mutableListOf<Shelf>()
|
||||||
|
|
||||||
enum class DragPlace {
|
enum class DragPlace {
|
||||||
Sidebar,
|
Sidebar,
|
||||||
|
|
@ -33,6 +38,7 @@ class Rack(
|
||||||
data class DraggedWindow(
|
data class DraggedWindow(
|
||||||
val window: Window,
|
val window: Window,
|
||||||
val source: DragPlace,
|
val source: DragPlace,
|
||||||
|
val indexInSource: Int,
|
||||||
val byX: Float,
|
val byX: Float,
|
||||||
val byY: Float,
|
val byY: Float,
|
||||||
|
|
||||||
|
|
@ -42,14 +48,45 @@ class Rack(
|
||||||
private var draggedWindow: DraggedWindow? = null
|
private var draggedWindow: DraggedWindow? = null
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
val shelvesByUuid = hashMapOf<UUID, Shelf>()
|
||||||
|
|
||||||
for (blockPosition in shownDevices) {
|
for (blockPosition in shownDevices) {
|
||||||
val blockEntity = world.getBlockEntity(blockPosition) as? DeviceBlockEntity ?: continue
|
val blockEntity = world.getBlockEntity(blockPosition) as? DeviceBlockEntity ?: continue
|
||||||
val widget = blockEntity.descriptor.ui?.open(blockEntity.controls, x = 0f, y = 0f)
|
for (shelfUuid in blockEntity.shelfOrder) {
|
||||||
if (widget != null) {
|
if (shelfUuid !in shelvesByUuid) {
|
||||||
sidebar.windows.add(widget)
|
val shelf = Shelf(0f, 0f, width, shelfUuid)
|
||||||
blockPositionsByWidget[widget] = blockPosition
|
shelves.add(shelf)
|
||||||
|
shelvesByUuid[shelfUuid] = shelf
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (blockPosition in shownDevices) {
|
||||||
|
val blockEntity = world.getBlockEntity(blockPosition) as? DeviceBlockEntity ?: continue
|
||||||
|
val window = blockEntity.descriptor.ui?.open(blockEntity.controls, x = 0f, y = 0f)
|
||||||
|
if (window != null) {
|
||||||
|
val shelfUuid = blockEntity.shelf
|
||||||
|
if (shelfUuid != null) {
|
||||||
|
shelvesByUuid[shelfUuid]?.windows?.add(window)
|
||||||
|
} else {
|
||||||
|
sidebar.windows.add(window)
|
||||||
|
}
|
||||||
|
blockPositionsByWidget[window] = blockPosition
|
||||||
|
}
|
||||||
|
}
|
||||||
|
removeEmptyShelvesFromEnd()
|
||||||
|
addEmptyShelfIfNotPresent()
|
||||||
|
|
||||||
|
sortWindowsInContainer(sidebar.windows)
|
||||||
|
shelves.forEach { sortWindowsInContainer(it.windows) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <W : DeviceWidget> sortWindowsInContainer(container: MutableList<W>) {
|
||||||
|
container.sortBy { window ->
|
||||||
|
val blockPosition = blockPositionsByWidget[window]
|
||||||
|
val blockEntity = world.getBlockEntity(blockPosition) as? DeviceBlockEntity
|
||||||
|
blockEntity?.sortPriority ?: Int.MAX_VALUE
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <W : DeviceWidget> removeStaleWidgetsFromContainer(container: MutableList<W>) {
|
private fun <W : DeviceWidget> removeStaleWidgetsFromContainer(container: MutableList<W>) {
|
||||||
|
|
@ -67,6 +104,9 @@ class Rack(
|
||||||
for (shelf in shelves) {
|
for (shelf in shelves) {
|
||||||
shelf.draw(matrices, mouseX, mouseY, deltaTime)
|
shelf.draw(matrices, mouseX, mouseY, deltaTime)
|
||||||
}
|
}
|
||||||
|
if (shelves.size == 1 && shelves.last().windows.isEmpty()) {
|
||||||
|
shelves.last().drawUsageHint(matrices)
|
||||||
|
}
|
||||||
|
|
||||||
sidebar.draw(matrices, mouseX, mouseY, deltaTime)
|
sidebar.draw(matrices, mouseX, mouseY, deltaTime)
|
||||||
|
|
||||||
|
|
@ -83,7 +123,6 @@ class Rack(
|
||||||
)
|
)
|
||||||
|
|
||||||
val destination = findDragDestination(mouseX, mouseY)
|
val destination = findDragDestination(mouseX, mouseY)
|
||||||
println(destination)
|
|
||||||
when (destination?.place) {
|
when (destination?.place) {
|
||||||
DragPlace.Sidebar -> {
|
DragPlace.Sidebar -> {
|
||||||
sidebar.drawInside(matrices) {
|
sidebar.drawInside(matrices) {
|
||||||
|
|
@ -91,7 +130,7 @@ class Rack(
|
||||||
Sidebar.padding
|
Sidebar.padding
|
||||||
} else if (destination.indexInPlace == sidebar.windows.size) {
|
} else if (destination.indexInPlace == sidebar.windows.size) {
|
||||||
val window = sidebar.windows.last()
|
val window = sidebar.windows.last()
|
||||||
window.y + window.height - Sidebar.spacingBetweenWindows / 2f
|
window.y + window.height + Sidebar.spacingBetweenWindows / 2f
|
||||||
} else {
|
} else {
|
||||||
val window = sidebar.windows[destination.indexInPlace]
|
val window = sidebar.windows[destination.indexInPlace]
|
||||||
window.y - Sidebar.spacingBetweenWindows / 2f
|
window.y - Sidebar.spacingBetweenWindows / 2f
|
||||||
|
|
@ -143,7 +182,7 @@ class Rack(
|
||||||
source: DragPlace,
|
source: DragPlace,
|
||||||
sourceShelfIndex: Int = 0,
|
sourceShelfIndex: Int = 0,
|
||||||
): Message {
|
): Message {
|
||||||
for (window in windows) {
|
windows.forEachIndexed { index, window ->
|
||||||
val blockPosition = blockPositionsByWidget[window]!!
|
val blockPosition = blockPositionsByWidget[window]!!
|
||||||
val windowRelativeEvent = event.relativeTo(window.x, window.y)
|
val windowRelativeEvent = event.relativeTo(window.x, window.y)
|
||||||
val windowMessage =
|
val windowMessage =
|
||||||
|
|
@ -153,6 +192,7 @@ class Rack(
|
||||||
DraggedWindow(
|
DraggedWindow(
|
||||||
window,
|
window,
|
||||||
source,
|
source,
|
||||||
|
index,
|
||||||
windowMessage.x,
|
windowMessage.x,
|
||||||
windowMessage.y,
|
windowMessage.y,
|
||||||
sourceShelfIndex
|
sourceShelfIndex
|
||||||
|
|
@ -192,21 +232,38 @@ class Rack(
|
||||||
if (draggedWindow != null) {
|
if (draggedWindow != null) {
|
||||||
val destination = findDragDestination(event.mouseX, event.mouseY)
|
val destination = findDragDestination(event.mouseX, event.mouseY)
|
||||||
if (destination != null) {
|
if (destination != null) {
|
||||||
removeWindowFromSource(
|
val fromList: MutableList<Window> = when (draggedWindow.source) {
|
||||||
draggedWindow.source,
|
DragPlace.Sidebar -> sidebar.windows
|
||||||
draggedWindow.sourceShelfIndex,
|
DragPlace.Shelf -> shelves[draggedWindow.sourceShelfIndex].windows
|
||||||
draggedWindow.window,
|
|
||||||
)
|
|
||||||
when (destination.place) {
|
|
||||||
DragPlace.Sidebar -> sidebar.windows.add(
|
|
||||||
destination.indexInPlace,
|
|
||||||
draggedWindow.window
|
|
||||||
)
|
|
||||||
DragPlace.Shelf -> shelves[destination.shelfIndex].windows.add(
|
|
||||||
destination.indexInPlace,
|
|
||||||
draggedWindow.window
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
val toList: MutableList<Window> = when (destination.place) {
|
||||||
|
DragPlace.Sidebar -> sidebar.windows
|
||||||
|
DragPlace.Shelf -> shelves[destination.shelfIndex].windows
|
||||||
|
}
|
||||||
|
moveElement(
|
||||||
|
fromList,
|
||||||
|
fromIndex = draggedWindow.indexInSource,
|
||||||
|
toList,
|
||||||
|
toIndex = destination.indexInPlace
|
||||||
|
)
|
||||||
|
|
||||||
|
val blockPosition = blockPositionsByWidget[draggedWindow.window]
|
||||||
|
if (blockPosition != null) {
|
||||||
|
when (destination.place) {
|
||||||
|
DragPlace.Shelf -> {
|
||||||
|
val shelf = shelves[destination.shelfIndex]
|
||||||
|
updateBlockData(blockPosition, shelf.uuid)
|
||||||
|
}
|
||||||
|
DragPlace.Sidebar -> {
|
||||||
|
updateBlockData(blockPosition, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
removeEmptyShelvesFromEnd()
|
||||||
|
addEmptyShelfIfNotPresent()
|
||||||
|
updateBlockPriorities(fromList)
|
||||||
|
updateBlockPriorities(toList)
|
||||||
reflow()
|
reflow()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -216,6 +273,27 @@ class Rack(
|
||||||
return Message.eventIgnored
|
return Message.eventIgnored
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun removeEmptyShelvesFromEnd() {
|
||||||
|
while (shelves.size > 1 && shelves.last().windows.isEmpty()) {
|
||||||
|
shelves.removeLast()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addEmptyShelfIfNotPresent() {
|
||||||
|
if (shelves.size == 0 || shelves.last().windows.isNotEmpty()) {
|
||||||
|
shelves.add(Shelf(0f, 0f, width, UUID.randomUUID()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <W : DeviceWidget> updateBlockPriorities(windows: MutableList<W>) {
|
||||||
|
val packetEntries = mutableListOf<ReorderRack.Entry>()
|
||||||
|
windows.forEachIndexed { index, window ->
|
||||||
|
val blockPosition = blockPositionsByWidget[window] ?: return@forEachIndexed
|
||||||
|
packetEntries.add(ReorderRack.Entry(blockPosition, index))
|
||||||
|
}
|
||||||
|
ClientPlayNetworking.send(ReorderRack.id, ReorderRack(packetEntries).serialize())
|
||||||
|
}
|
||||||
|
|
||||||
internal data class DragDestination(
|
internal data class DragDestination(
|
||||||
val place: DragPlace,
|
val place: DragPlace,
|
||||||
val indexInPlace: Int,
|
val indexInPlace: Int,
|
||||||
|
|
@ -238,22 +316,8 @@ class Rack(
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun removeWindowFromSource(
|
|
||||||
source: DragPlace,
|
|
||||||
indexOfSource: Int,
|
|
||||||
window: Window,
|
|
||||||
) {
|
|
||||||
when (source) {
|
|
||||||
DragPlace.Sidebar -> {
|
|
||||||
sidebar.windows.remove(window)
|
|
||||||
}
|
|
||||||
DragPlace.Shelf -> {
|
|
||||||
shelves[indexOfSource].windows.remove(window)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun reflow() {
|
override fun reflow() {
|
||||||
|
sidebar.width = if (sidebar.windows.isNotEmpty()) 160f else 16f
|
||||||
sidebar.x = width - sidebar.width
|
sidebar.x = width - sidebar.width
|
||||||
sidebar.height = height
|
sidebar.height = height
|
||||||
sidebar.reflow()
|
sidebar.reflow()
|
||||||
|
|
@ -262,11 +326,21 @@ class Rack(
|
||||||
for (shelf in shelves) {
|
for (shelf in shelves) {
|
||||||
shelf.reflow()
|
shelf.reflow()
|
||||||
shelf.y = dy
|
shelf.y = dy
|
||||||
shelf.width = width
|
shelf.width = width - sidebar.width
|
||||||
dy += shelf.height
|
dy += shelf.height
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun updateBlockData(editedDevicePosition: BlockPos, newShelfUuid: UUID?) {
|
||||||
|
ClientPlayNetworking.send(
|
||||||
|
EditRack.id, EditRack(
|
||||||
|
editedDevicePosition,
|
||||||
|
shelf = newShelfUuid,
|
||||||
|
shelfOrder = shelves.map { it.uuid }.toTypedArray()
|
||||||
|
).serialize()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val windowDraggingPreview =
|
private val windowDraggingPreview =
|
||||||
NinePatch(u = 48f, v = 0f, width = 8f, height = 8f, border = 2f)
|
NinePatch(u = 48f, v = 0f, width = 8f, height = 8f, border = 2f)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package net.liquidev.dawd3.ui.widget.rack
|
package net.liquidev.dawd3.ui.widget.rack
|
||||||
|
|
||||||
|
import net.liquidev.dawd3.common.rgba
|
||||||
import net.liquidev.dawd3.render.Render
|
import net.liquidev.dawd3.render.Render
|
||||||
import net.liquidev.dawd3.render.TextureStrip
|
import net.liquidev.dawd3.render.TextureStrip
|
||||||
import net.liquidev.dawd3.ui.Event
|
import net.liquidev.dawd3.ui.Event
|
||||||
|
|
@ -8,9 +9,16 @@ import net.liquidev.dawd3.ui.RackScreen
|
||||||
import net.liquidev.dawd3.ui.widget.Widget
|
import net.liquidev.dawd3.ui.widget.Widget
|
||||||
import net.liquidev.dawd3.ui.widget.Window
|
import net.liquidev.dawd3.ui.widget.Window
|
||||||
import net.minecraft.client.util.math.MatrixStack
|
import net.minecraft.client.util.math.MatrixStack
|
||||||
|
import net.minecraft.text.Text
|
||||||
|
import java.util.*
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
|
|
||||||
class Shelf(x: Float, y: Float, override var width: Float) : Widget<Nothing, Message>(x, y) {
|
class Shelf(
|
||||||
|
x: Float,
|
||||||
|
y: Float,
|
||||||
|
override var width: Float,
|
||||||
|
val uuid: UUID,
|
||||||
|
) : Widget<Nothing, Message>(x, y) {
|
||||||
override var height = 128f
|
override var height = 128f
|
||||||
private set
|
private set
|
||||||
|
|
||||||
|
|
@ -39,6 +47,20 @@ class Shelf(x: Float, y: Float, override var width: Float) : Widget<Nothing, Mes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun drawUsageHint(matrices: MatrixStack) {
|
||||||
|
drawInside(matrices) {
|
||||||
|
Render.colorized(1f, 1f, 1f, 0.6f) {
|
||||||
|
Render.textCentered(
|
||||||
|
matrices,
|
||||||
|
width / 2f,
|
||||||
|
height / 2f - 4,
|
||||||
|
Text.translatable("screen.dawd3.rack.shelf_hint"),
|
||||||
|
rgba(255, 255, 255, 127)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// NOTE: Events here are not handled directly by the Shelf, but rather by the parent rack
|
// NOTE: Events here are not handled directly by the Shelf, but rather by the parent rack
|
||||||
// screen which has extra context that needs to be passed down to windows. Hence this cannot
|
// screen which has extra context that needs to be passed down to windows. Hence this cannot
|
||||||
// be called (context is Nothing.)
|
// be called (context is Nothing.)
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import net.minecraft.client.util.math.MatrixStack
|
||||||
class Sidebar(
|
class Sidebar(
|
||||||
x: Float,
|
x: Float,
|
||||||
y: Float,
|
y: Float,
|
||||||
override val width: Float,
|
override var width: Float,
|
||||||
override var height: Float,
|
override var height: Float,
|
||||||
) : Widget<Nothing, Message>(x, y) {
|
) : Widget<Nothing, Message>(x, y) {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
package net.liquidev.dawd3.ui.widget.rack.shelves
|
||||||
|
|
||||||
|
import net.liquidev.dawd3.block.device.DeviceBlockEntity
|
||||||
|
import net.minecraft.block.Block
|
||||||
|
import net.minecraft.util.math.BlockPos
|
||||||
|
import net.minecraft.world.World
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
object ShelfEditing {
|
||||||
|
fun propagateShelfOrderData(world: World, from: BlockPos, shelfOrder: Array<UUID>) {
|
||||||
|
propagateShelfOrderDataRec(world, from, HashSet(), shelfOrder)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun propagateShelfOrderDataRec(
|
||||||
|
world: World,
|
||||||
|
position: BlockPos,
|
||||||
|
traversed: HashSet<BlockPos>,
|
||||||
|
sourceShelfOrder: Array<UUID>,
|
||||||
|
) {
|
||||||
|
val thisBlockEntity = world.getBlockEntity(position) as? DeviceBlockEntity ?: return
|
||||||
|
if (position !in traversed) {
|
||||||
|
traversed.add(position)
|
||||||
|
|
||||||
|
// Maybe not the fastest thing... but I don't want to share a single piece of mutable
|
||||||
|
// data between all the block entities, as that can cause weird bugs later down the line.
|
||||||
|
thisBlockEntity.shelfOrder.clear()
|
||||||
|
thisBlockEntity.shelfOrder.addAll(sourceShelfOrder)
|
||||||
|
thisBlockEntity.markDirty()
|
||||||
|
|
||||||
|
val blockState = world.getBlockState(position)
|
||||||
|
world.updateListeners(position, blockState, blockState, Block.NOTIFY_LISTENERS)
|
||||||
|
|
||||||
|
propagateShelfOrderDataRec(world, position.up(), traversed, sourceShelfOrder)
|
||||||
|
propagateShelfOrderDataRec(world, position.down(), traversed, sourceShelfOrder)
|
||||||
|
propagateShelfOrderDataRec(world, position.north(), traversed, sourceShelfOrder)
|
||||||
|
propagateShelfOrderDataRec(world, position.south(), traversed, sourceShelfOrder)
|
||||||
|
propagateShelfOrderDataRec(world, position.east(), traversed, sourceShelfOrder)
|
||||||
|
propagateShelfOrderDataRec(world, position.west(), traversed, sourceShelfOrder)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -32,7 +32,7 @@
|
||||||
"block.dawd3.saw_oscillator": "Saw Oscillator",
|
"block.dawd3.saw_oscillator": "Saw Oscillator",
|
||||||
"block.dawd3.sine_oscillator": "Sine Oscillator",
|
"block.dawd3.sine_oscillator": "Sine Oscillator",
|
||||||
"block.dawd3.triangle_oscillator": "Triangle Oscillator",
|
"block.dawd3.triangle_oscillator": "Triangle Oscillator",
|
||||||
"screen.dawd3.rack.title": "Rack",
|
"screen.dawd3.rack.shelf_hint": "Drag devices here to organize them",
|
||||||
"dawd3.control.dawd3.adsr.attack": "ATT",
|
"dawd3.control.dawd3.adsr.attack": "ATT",
|
||||||
"dawd3.control.dawd3.adsr.decay": "DEC",
|
"dawd3.control.dawd3.adsr.decay": "DEC",
|
||||||
"dawd3.control.dawd3.adsr.sustain": "SUS",
|
"dawd3.control.dawd3.adsr.sustain": "SUS",
|
||||||
|
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 551 B After Width: | Height: | Size: 548 B |
Reference in a new issue