From 9b384f22133c5d01e3e4a7ba78fa58fafe2bd09d Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Fri, 21 Mar 2025 07:18:42 +0700 Subject: [PATCH 01/23] Implement sizeToContents() for GridPanel --- .../client/screen/panels/util/GridPanel.kt | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/util/GridPanel.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/util/GridPanel.kt index 55a550420..9d17db40e 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/util/GridPanel.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/util/GridPanel.kt @@ -7,6 +7,7 @@ import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel import ru.dbotthepony.mc.otm.client.screen.panels.slot.AbstractSlotPanel import ru.dbotthepony.mc.otm.core.collect.filter import ru.dbotthepony.mc.otm.core.collect.maybe +import kotlin.math.max open class GridPanel( screen: S, @@ -36,6 +37,37 @@ open class GridPanel( invalidateLayout() } + override fun sizeToContents() { + if (visibleChildren.isEmpty()) { + // nothing to size against + return + } + + visibleChildren.forEach { it.sizeToContents() } + + var width = 0f + var height = 0f + + val children = visibleChildren.iterator().filter { it.dock == Dock.NONE } + + for (row in 0 until rows) { + var maxHeight = 0f + var rowWidth = 0f + + for (column in 0 until columns) { + val nextChild = children.maybe() ?: break + rowWidth += nextChild.width + nextChild.dockMargin.horizontal + maxHeight = max(maxHeight, nextChild.height + nextChild.dockMargin.vertical) + } + + height += maxHeight + width = max(width, rowWidth) + } + + this.width = width + this.height = height + } + override fun performLayout() { super.performLayout() var children = visibleChildren.iterator().filter { it.dock == Dock.NONE } From a48aaf52ae4fcd3e398a2410770007ee67e23a95 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Fri, 21 Mar 2025 07:19:03 +0700 Subject: [PATCH 02/23] Add DockProperty#all variant --- .../dbotthepony/mc/otm/client/screen/panels/EditablePanel.kt | 4 ++++ 1 file changed, 4 insertions(+) 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 bd1493163..eb2c0aa02 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 @@ -82,6 +82,10 @@ data class DockProperty(val left: Float = 0f, val top: Float = 0f, val right: Fl companion object { val EMPTY = DockProperty() + + fun all(value: Float): DockProperty { + return DockProperty(value, value, value, value) + } } } From e6c970865258d18fd575ebf6b38081bfc8e56113 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Fri, 21 Mar 2025 08:07:43 +0700 Subject: [PATCH 03/23] Add layout modes to grid panel --- .../client/screen/panels/util/GridPanel.kt | 82 ++++++++++++++++--- 1 file changed, 70 insertions(+), 12 deletions(-) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/util/GridPanel.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/util/GridPanel.kt index 9d17db40e..4e7dd5800 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/util/GridPanel.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/util/GridPanel.kt @@ -21,22 +21,80 @@ open class GridPanel( ) : EditablePanel(screen, parent, x, y, width, height) { var columns: Int = columns set(value) { - field = value - invalidateLayout() + if (field != value) { + field = value + invalidateLayout() + } } var rows: Int = rows set(value) { - field = value - invalidateLayout() + if (field != value) { + field = value + invalidateLayout() + } } var gravity: RenderGravity = RenderGravity.CENTER_CENTER set(value) { - field = value - invalidateLayout() + if (field != value) { + field = value + invalidateLayout() + } } + var layout: Layout = Layout.TOP_LEFT + set(value) { + if (field != value) { + field = value + invalidateLayout() + } + } + + enum class Layout { + TOP_LEFT { + override fun rows(last: Int): IntIterator { + return (0 until last).iterator() + } + + override fun columns(last: Int): IntIterator { + return (0 until last).iterator() + } + }, + + TOP_RIGHT { + override fun rows(last: Int): IntIterator { + return (0 until last).iterator() + } + + override fun columns(last: Int): IntIterator { + return (last - 1 downTo 1).iterator() + } + }, + BOTTOM_LEFT { + override fun rows(last: Int): IntIterator { + return (last - 1 downTo 1).iterator() + } + + override fun columns(last: Int): IntIterator { + return (0 until last).iterator() + } + }, + + BOTTOM_RIGHT { + override fun rows(last: Int): IntIterator { + return (last - 1 downTo 1).iterator() + } + + override fun columns(last: Int): IntIterator { + return (last - 1 downTo 1).iterator() + } + }; + + abstract fun rows(last: Int): IntIterator + abstract fun columns(last: Int): IntIterator + } + override fun sizeToContents() { if (visibleChildren.isEmpty()) { // nothing to size against @@ -50,11 +108,11 @@ open class GridPanel( val children = visibleChildren.iterator().filter { it.dock == Dock.NONE } - for (row in 0 until rows) { + for (row in layout.rows(rows)) { var maxHeight = 0f var rowWidth = 0f - for (column in 0 until columns) { + for (column in layout.columns(columns)) { val nextChild = children.maybe() ?: break rowWidth += nextChild.width + nextChild.dockMargin.horizontal maxHeight = max(maxHeight, nextChild.height + nextChild.dockMargin.vertical) @@ -75,11 +133,11 @@ open class GridPanel( var totalWidth = 0f var totalHeight = 0f - for (row in 0 until rows) { + for (row in layout.rows(rows)) { var maxHeight = 0f var width = 0f - for (column in 0 until columns) { + for (column in layout.columns(columns)) { val child = children.maybe() ?: break width += child.dockedWidth maxHeight = maxHeight.coerceAtLeast(child.dockedHeight) @@ -96,11 +154,11 @@ open class GridPanel( totalWidth = 0f totalHeight = 0f - for (row in 0 until rows) { + for (row in layout.rows(rows)) { var maxHeight = 0f var width = 0f - for (column in 0 until columns) { + for (column in layout.columns(columns)) { val child = children.maybe() ?: break child.x = alignX + width child.y = alignY + totalHeight From c343de60314ed00d1924075654ac28606c0c9f3e Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Fri, 21 Mar 2025 10:45:07 +0700 Subject: [PATCH 04/23] Parenting callbacks in panels --- .../mc/otm/client/screen/panels/EditablePanel.kt | 12 ++++++++++++ 1 file changed, 12 insertions(+) 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 eb2c0aa02..530c92222 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 @@ -847,6 +847,12 @@ open class EditablePanel( childrenSortingInvalidated = true } + protected open fun onChildrenAdded(child: EditablePanel<*>) {} + protected open fun onChildrenRemoved(child: EditablePanel<*>) {} + + protected open fun onParented(parent: EditablePanel<*>) {} + protected open fun onUnParented(parent: EditablePanel<*>) {} + private fun onParent(child: EditablePanel<*>) { if (childrenInternal.contains(child)) throw IllegalStateException("Already containing $child") childrenInternal.add(child) @@ -864,11 +870,15 @@ open class EditablePanel( else updateVisibility = true } + + onChildrenAdded(child) + child.onParented(this) } private fun onUnParent(child: EditablePanel<*>) { val indexOf = childrenInternal.indexOf(child) if (indexOf == -1) throw IllegalStateException("Already not containing $child") + child.onUnParented(this) childrenInternal.removeAt(indexOf) invalidateChildrenSorting() @@ -880,6 +890,8 @@ open class EditablePanel( if (child.isVisible() != isVisible()) { updateVisible() } + + onChildrenRemoved(child) } private fun sortChildren() { From 27834cc5951f5133fe51705aeeef1a255bd7ad4e Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Fri, 21 Mar 2025 12:23:08 +0700 Subject: [PATCH 05/23] Fix wrong layout final iteration value --- .../mc/otm/client/screen/panels/util/GridPanel.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/util/GridPanel.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/util/GridPanel.kt index 4e7dd5800..3b655f3de 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/util/GridPanel.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/util/GridPanel.kt @@ -68,12 +68,12 @@ open class GridPanel( } override fun columns(last: Int): IntIterator { - return (last - 1 downTo 1).iterator() + return (last - 1 downTo 0).iterator() } }, BOTTOM_LEFT { override fun rows(last: Int): IntIterator { - return (last - 1 downTo 1).iterator() + return (last - 1 downTo 0).iterator() } override fun columns(last: Int): IntIterator { @@ -83,11 +83,11 @@ open class GridPanel( BOTTOM_RIGHT { override fun rows(last: Int): IntIterator { - return (last - 1 downTo 1).iterator() + return (last - 1 downTo 0).iterator() } override fun columns(last: Int): IntIterator { - return (last - 1 downTo 1).iterator() + return (last - 1 downTo 0).iterator() } }; From 269227f6cf7ed3f9a0c0a879010bc42bb89d1c00 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Fri, 21 Mar 2025 12:58:13 +0700 Subject: [PATCH 06/23] Add helper methods for fast DockProperty creation --- .../otm/client/screen/panels/EditablePanel.kt | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) 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 530c92222..501315db1 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 @@ -83,9 +83,54 @@ data class DockProperty(val left: Float = 0f, val top: Float = 0f, val right: Fl companion object { val EMPTY = DockProperty() + @JvmStatic fun all(value: Float): DockProperty { return DockProperty(value, value, value, value) } + + @JvmStatic + @JvmOverloads + fun topLeft(top: Float, left: Float = top): DockProperty { + return DockProperty(left = left, top = top) + } + + @JvmStatic + fun left(value: Float): DockProperty { + return DockProperty(left = value) + } + + @JvmStatic + @JvmOverloads + fun topRight(top: Float, right: Float = top): DockProperty { + return DockProperty(right = right, top = top) + } + + @JvmStatic + fun right(value: Float): DockProperty { + return DockProperty(right = value) + } + + @JvmStatic + fun top(value: Float): DockProperty { + return DockProperty(top = value) + } + + @JvmStatic + @JvmOverloads + fun bottomLeft(bottom: Float, left: Float = bottom): DockProperty { + return DockProperty(left = left, bottom = bottom) + } + + @JvmStatic + @JvmOverloads + fun bottomRight(bottom: Float, right: Float = bottom): DockProperty { + return DockProperty(right = right, bottom = bottom) + } + + @JvmStatic + fun bottom(value: Float): DockProperty { + return DockProperty(bottom = value) + } } } From 9b27ed7fb6a156f32967000c38efea881ea75759 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Fri, 21 Mar 2025 13:33:53 +0700 Subject: [PATCH 07/23] Add netFloat and nextDouble helper methods to RandomSource --- .../kotlin/ru/dbotthepony/mc/otm/core/Ext.kt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt index 6203217b1..47b8a1c39 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt @@ -662,3 +662,21 @@ fun RandomSource.nextNormalDoubles(stddev: Double, mean: Double): DoublePair { fun RandomSource.nextNormalDouble(stddev: Double, mean: Double): Double { return nextGaussian() * stddev + mean } + +fun RandomSource.nextFloat(min: Float, max: Float): Float { + if (this is RandomGenerator) + return nextFloat(min, max) + + require(max >= min) { "Min is bigger than max: $min vs $max" } + if (min == max) return min + return min + nextFloat() * (max - min) +} + +fun RandomSource.nextDouble(min: Double, max: Double): Double { + if (this is RandomGenerator) + return nextDouble(min, max) + + require(max >= min) { "Min is bigger than max: $min vs $max" } + if (min == max) return min + return min + nextDouble() * (max - min) +} From b921658eb2f7118b59c0f292766b17f5b5ee6ee3 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Fri, 21 Mar 2025 13:34:53 +0700 Subject: [PATCH 08/23] Remove `randomGenerator` from editable panel --- .../client/screen/panels/DecimalHistoryChartPanel.kt | 2 +- .../mc/otm/client/screen/panels/EditablePanel.kt | 4 ---- .../otm/client/screen/tech/AndroidStationScreen.kt | 12 +++++++----- .../ru/dbotthepony/mc/otm/core/math/Clustering.kt | 5 +++-- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/DecimalHistoryChartPanel.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/DecimalHistoryChartPanel.kt index 1b262ce32..dfc9287ab 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/DecimalHistoryChartPanel.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/DecimalHistoryChartPanel.kt @@ -52,7 +52,7 @@ open class DecimalHistoryChartPanel>( map[1f] = formatText(maximum) - for (cluster in chart.asIterable().clusterize(randomGenerator)) { + for (cluster in chart.asIterable().clusterize(random)) { val perc = (cluster.center / maximum).toFloat() if (map.keys.none { (it - perc).absoluteValue < 0.08f }) { 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 501315db1..f828af807 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 @@ -232,10 +232,6 @@ open class EditablePanel( } } - val randomGenerator: RandomGenerator by lazy { - RandomSource2Generator(random) - } - /** * Bigger values means lesser priority while docking, rendering and processing inputs. */ diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/tech/AndroidStationScreen.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/tech/AndroidStationScreen.kt index f6edd683a..0b52df472 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/tech/AndroidStationScreen.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/tech/AndroidStationScreen.kt @@ -7,6 +7,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap import net.minecraft.ChatFormatting import net.minecraft.client.Minecraft import net.minecraft.network.chat.Component +import net.minecraft.util.RandomSource import net.minecraft.world.entity.player.Inventory import net.minecraft.world.item.ItemStack import net.neoforged.neoforge.network.PacketDistributor @@ -34,6 +35,7 @@ import ru.dbotthepony.mc.otm.config.MachinesConfig import ru.dbotthepony.kommons.math.RGBAColor import ru.dbotthepony.mc.otm.player.matteryPlayer import ru.dbotthepony.mc.otm.client.screen.panels.button.BooleanButtonPanel +import ru.dbotthepony.mc.otm.core.nextFloat import ru.dbotthepony.mc.otm.menu.tech.AndroidStationMenu import ru.dbotthepony.mc.otm.network.AndroidResearchRequestPacket import java.util.* @@ -443,8 +445,8 @@ private class AndroidResearchButton( } private enum class PreviewScrollers( - val init: (EditablePanel<*>, RandomGenerator) -> Unit, - val scroll: (EditablePanel<*>, RandomGenerator) -> Boolean + val init: (EditablePanel<*>, RandomSource) -> Unit, + val scroll: (EditablePanel<*>, RandomSource) -> Boolean ) { LEFT_TO_RIGHT( init = { it, random -> @@ -540,14 +542,14 @@ class AndroidStationScreen(p_97741_: AndroidStationMenu, p_97742_: Inventory, p_ if (isPreview && !layoutInvalidated) { if (firstTick) { - scroller.init.invoke(this, randomGenerator) + scroller.init.invoke(this, random) } - val status = scroller.scroll.invoke(this, randomGenerator) + val status = scroller.scroll.invoke(this, random) if (!status) { scroller = PreviewScrollers.entries.let { it[random.nextInt(it.size)] } - scroller.init.invoke(this, randomGenerator) + scroller.init.invoke(this, random) } } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/math/Clustering.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/math/Clustering.kt index a13d846c0..267d40356 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/math/Clustering.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/math/Clustering.kt @@ -1,6 +1,7 @@ package ru.dbotthepony.mc.otm.core.math import it.unimi.dsi.fastutil.objects.ObjectArrayList +import net.minecraft.util.RandomSource import ru.dbotthepony.mc.otm.core.random import java.util.random.RandomGenerator import kotlin.math.min @@ -88,7 +89,7 @@ private class MutableCluster>(var center: V) { } private fun > Iterable.clusterize( - random: RandomGenerator, + random: RandomSource, initialClusters: Int = 1, zeroBound: Boolean = false, identity: V, @@ -202,7 +203,7 @@ private fun > Iterable.clusterize( // TODO: could use some tweaking private val DECIMAL_ERROR_TOLERANCE = Decimal("0.02") -fun Iterable.clusterize(random: RandomGenerator, clusters: Int? = null, zeroBound: Boolean = false): List> { +fun Iterable.clusterize(random: RandomSource, clusters: Int? = null, zeroBound: Boolean = false): List> { return clusterize(random, clusters ?: 1, zeroBound, Decimal.ZERO, Decimal::plus, Decimal::minus, Decimal::div, Decimal::absoluteValue) { min, max, error -> if (clusters != null) false From f9821aa5520407d11390d58a18dc5bad9d904a56 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Fri, 21 Mar 2025 13:36:46 +0700 Subject: [PATCH 09/23] Use GJRAND64 in menus --- .../dbotthepony/mc/otm/client/screen/panels/EditablePanel.kt | 5 +++-- .../mc/otm/client/screen/tech/AndroidStationScreen.kt | 4 ++-- src/main/kotlin/ru/dbotthepony/mc/otm/menu/MatteryMenu.kt | 3 ++- 3 files changed, 7 insertions(+), 5 deletions(-) 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 f828af807..1b3c0da7f 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 @@ -29,6 +29,7 @@ import ru.dbotthepony.mc.otm.client.screen.panels.input.QueryUserPanel import ru.dbotthepony.mc.otm.core.RandomSource2Generator import ru.dbotthepony.mc.otm.core.collect.concatIterators import ru.dbotthepony.mc.otm.core.collect.flatMap +import ru.dbotthepony.mc.otm.core.util.GJRAND64RandomSource import java.util.* import java.util.concurrent.CopyOnWriteArrayList import java.util.function.Predicate @@ -224,11 +225,11 @@ open class EditablePanel( } } - val random: RandomSource by lazy { + val random: GJRAND64RandomSource by lazy { if (screen is MatteryScreen<*>) { screen.menu.random } else { - RandomSource.create() + GJRAND64RandomSource() } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/tech/AndroidStationScreen.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/tech/AndroidStationScreen.kt index 0b52df472..09ef63314 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/tech/AndroidStationScreen.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/tech/AndroidStationScreen.kt @@ -445,8 +445,8 @@ private class AndroidResearchButton( } private enum class PreviewScrollers( - val init: (EditablePanel<*>, RandomSource) -> Unit, - val scroll: (EditablePanel<*>, RandomSource) -> Boolean + val init: (EditablePanel<*>, RandomGenerator) -> Unit, + val scroll: (EditablePanel<*>, RandomGenerator) -> Boolean ) { LEFT_TO_RIGHT( init = { it, random -> diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/MatteryMenu.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/MatteryMenu.kt index 8a3711faa..3568f9ecf 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/MatteryMenu.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/MatteryMenu.kt @@ -44,6 +44,7 @@ import ru.dbotthepony.mc.otm.container.sortWithIndices import ru.dbotthepony.mc.otm.core.ResourceLocation import ru.dbotthepony.mc.otm.core.collect.ConditionalSet import ru.dbotthepony.mc.otm.core.math.Decimal +import ru.dbotthepony.mc.otm.core.util.GJRAND64RandomSource import ru.dbotthepony.mc.otm.menu.input.BooleanInputWithFeedback import ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget import ru.dbotthepony.mc.otm.network.MatteryStreamCodec @@ -81,7 +82,7 @@ abstract class MatteryMenu( val mSynchronizer = SynchableGroup() val synchronizerRemote = mSynchronizer.Remote() val player: Player get() = inventory.player - val random: RandomSource = RandomSource.create() + val random = GJRAND64RandomSource() private val _playerInventorySlots = ArrayList() private val _playerHotbarSlots = ArrayList() From 3e92c5272dd85f0826bfe29a90a1f900beb51316 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Fri, 21 Mar 2025 13:52:00 +0700 Subject: [PATCH 10/23] Move panels additional types to separate file --- .../otm/client/screen/panels/EditablePanel.kt | 115 ------------------ .../mc/otm/client/screen/panels/Types.kt | 115 ++++++++++++++++++ 2 files changed, 115 insertions(+), 115 deletions(-) create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/Types.kt 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 1b3c0da7f..e2be32ce9 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 @@ -11,9 +11,7 @@ import net.minecraft.client.gui.components.events.GuiEventListener import net.minecraft.client.gui.navigation.FocusNavigationEvent import net.minecraft.client.gui.navigation.ScreenRectangle import net.minecraft.client.gui.screens.Screen -import net.minecraft.client.renderer.Rect2i import net.minecraft.network.chat.Component -import net.minecraft.util.RandomSource import org.apache.logging.log4j.LogManager import ru.dbotthepony.mc.otm.SystemTime import ru.dbotthepony.mc.otm.client.CursorType @@ -26,129 +24,16 @@ 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 ru.dbotthepony.mc.otm.client.screen.panels.input.QueryUserPanel -import ru.dbotthepony.mc.otm.core.RandomSource2Generator import ru.dbotthepony.mc.otm.core.collect.concatIterators import ru.dbotthepony.mc.otm.core.collect.flatMap import ru.dbotthepony.mc.otm.core.util.GJRAND64RandomSource import java.util.* import java.util.concurrent.CopyOnWriteArrayList import java.util.function.Predicate -import java.util.random.RandomGenerator import kotlin.collections.ArrayList import kotlin.math.max import kotlin.math.roundToInt -data class ScreenPos(val x: Float, val y: Float) - -data class DockProperty(val left: Float = 0f, val top: Float = 0f, val right: Float = 0f, val bottom: Float = 0f) { - val isEmpty get() = left == 0f && right == 0f && top == 0f && bottom == 0f - val horizontal get() = left + right - val vertical get() = top + bottom - - operator fun plus(other: DockProperty): DockProperty { - return DockProperty( - left + other.left, - top + other.top, - right + other.right, - bottom + other.bottom - ) - } - - operator fun minus(other: DockProperty): DockProperty { - return DockProperty( - left - other.left, - top - other.top, - right - other.right, - bottom - other.bottom - ) - } - - operator fun plus(other: Float): DockProperty { - return DockProperty( - left + other, - top + other, - right + other, - bottom + other - ) - } - - operator fun minus(other: Float): DockProperty { - return DockProperty( - left - other, - top - other, - right - other, - bottom - other - ) - } - - companion object { - val EMPTY = DockProperty() - - @JvmStatic - fun all(value: Float): DockProperty { - return DockProperty(value, value, value, value) - } - - @JvmStatic - @JvmOverloads - fun topLeft(top: Float, left: Float = top): DockProperty { - return DockProperty(left = left, top = top) - } - - @JvmStatic - fun left(value: Float): DockProperty { - return DockProperty(left = value) - } - - @JvmStatic - @JvmOverloads - fun topRight(top: Float, right: Float = top): DockProperty { - return DockProperty(right = right, top = top) - } - - @JvmStatic - fun right(value: Float): DockProperty { - return DockProperty(right = value) - } - - @JvmStatic - fun top(value: Float): DockProperty { - return DockProperty(top = value) - } - - @JvmStatic - @JvmOverloads - fun bottomLeft(bottom: Float, left: Float = bottom): DockProperty { - return DockProperty(left = left, bottom = bottom) - } - - @JvmStatic - @JvmOverloads - fun bottomRight(bottom: Float, right: Float = bottom): DockProperty { - return DockProperty(right = right, bottom = bottom) - } - - @JvmStatic - fun bottom(value: Float): DockProperty { - return DockProperty(bottom = value) - } - } -} - -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) -} - -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( val screen: S, parent: EditablePanel<*>?, diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/Types.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/Types.kt new file mode 100644 index 000000000..7d498d8f6 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/Types.kt @@ -0,0 +1,115 @@ +package ru.dbotthepony.mc.otm.client.screen.panels + +import net.minecraft.client.renderer.Rect2i +import kotlin.math.roundToInt + +data class ScreenPos(val x: Float, val y: Float) + +data class DockProperty(val left: Float = 0f, val top: Float = 0f, val right: Float = 0f, val bottom: Float = 0f) { + val isEmpty get() = left == 0f && right == 0f && top == 0f && bottom == 0f + val horizontal get() = left + right + val vertical get() = top + bottom + + operator fun plus(other: DockProperty): DockProperty { + return DockProperty( + left + other.left, + top + other.top, + right + other.right, + bottom + other.bottom + ) + } + + operator fun minus(other: DockProperty): DockProperty { + return DockProperty( + left - other.left, + top - other.top, + right - other.right, + bottom - other.bottom + ) + } + + operator fun plus(other: Float): DockProperty { + return DockProperty( + left + other, + top + other, + right + other, + bottom + other + ) + } + + operator fun minus(other: Float): DockProperty { + return DockProperty( + left - other, + top - other, + right - other, + bottom - other + ) + } + + companion object { + val EMPTY = DockProperty() + + @JvmStatic + fun all(value: Float): DockProperty { + return DockProperty(value, value, value, value) + } + + @JvmStatic + @JvmOverloads + fun topLeft(top: Float, left: Float = top): DockProperty { + return DockProperty(left = left, top = top) + } + + @JvmStatic + fun left(value: Float): DockProperty { + return DockProperty(left = value) + } + + @JvmStatic + @JvmOverloads + fun topRight(top: Float, right: Float = top): DockProperty { + return DockProperty(right = right, top = top) + } + + @JvmStatic + fun right(value: Float): DockProperty { + return DockProperty(right = value) + } + + @JvmStatic + fun top(value: Float): DockProperty { + return DockProperty(top = value) + } + + @JvmStatic + @JvmOverloads + fun bottomLeft(bottom: Float, left: Float = bottom): DockProperty { + return DockProperty(left = left, bottom = bottom) + } + + @JvmStatic + @JvmOverloads + fun bottomRight(bottom: Float, right: Float = bottom): DockProperty { + return DockProperty(right = right, bottom = bottom) + } + + @JvmStatic + fun bottom(value: Float): DockProperty { + return DockProperty(bottom = value) + } + } +} + +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) +} + +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()) + } +} From 57c6bbb795d030fd0840772ff8ff1be9c752f688 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Fri, 21 Mar 2025 14:00:19 +0700 Subject: [PATCH 11/23] Update cases where wrong random being used --- .../client/render/blockentity/EnergyCounterRenderer.kt | 3 ++- .../kotlin/ru/dbotthepony/mc/otm/core/util/Formatting.kt | 9 ++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/blockentity/EnergyCounterRenderer.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/blockentity/EnergyCounterRenderer.kt index 2c1b70a67..741f3f925 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/blockentity/EnergyCounterRenderer.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/blockentity/EnergyCounterRenderer.kt @@ -20,12 +20,13 @@ import ru.dbotthepony.mc.otm.core.math.asAngle import ru.dbotthepony.mc.otm.core.math.clusterize import ru.dbotthepony.mc.otm.core.util.formatPower import ru.dbotthepony.mc.otm.core.math.times +import ru.dbotthepony.mc.otm.core.util.GJRAND64RandomSource import java.util.Random import kotlin.math.PI import kotlin.math.absoluteValue class EnergyCounterRenderer(private val context: BlockEntityRendererProvider.Context) : BlockEntityRenderer { - private val random = Random() + private val random = GJRAND64RandomSource() override fun render( tile: EnergyCounterBlockEntity, diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/Formatting.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/Formatting.kt index 961540d14..411b081c6 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/Formatting.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/Formatting.kt @@ -9,6 +9,7 @@ import net.minecraft.network.chat.FormattedText import net.minecraft.network.chat.MutableComponent import net.minecraft.world.inventory.tooltip.TooltipComponent import ru.dbotthepony.kommons.math.RGBAColor +import ru.dbotthepony.mc.otm.THREAD_LOCAL_RANDOM import ru.dbotthepony.mc.otm.client.render.ChartLevelLabels import ru.dbotthepony.mc.otm.client.render.ChartTooltipElement import ru.dbotthepony.mc.otm.config.ClientConfig @@ -453,10 +454,8 @@ private fun formatHistoryChart( labelNames[0f] = (-maxTransferred).formatSiComponent(suffix, decimals, formatAsReadable = verbose, bias = bias) labelNames[1f] = maxReceived.formatSiComponent(suffix, decimals, formatAsReadable = verbose, bias = bias) - val rand = java.util.Random() - if (maxTransferred.isNotZero && transferredMult > 0.2f) { - for (cluster in widget.transferred.clusterize(rand, zeroBound = true)) { + for (cluster in widget.transferred.clusterize(THREAD_LOCAL_RANDOM, zeroBound = true)) { val perc = (cluster.center / maxTransferred).toFloat() * transferredMult if (labelNames.keys.none { (it - perc).absoluteValue < 0.08f }) @@ -465,7 +464,7 @@ private fun formatHistoryChart( } if (maxReceived.isNotZero && receivedMult > 0.2f) { - for (cluster in widget.received.clusterize(rand, zeroBound = true)) { + for (cluster in widget.received.clusterize(THREAD_LOCAL_RANDOM, zeroBound = true)) { val perc = zero + (cluster.center / maxReceived).toFloat() * receivedMult if (labelNames.keys.none { (it - perc).absoluteValue < 0.08f }) @@ -473,7 +472,7 @@ private fun formatHistoryChart( } } - val clusters = diff.asIterable().clusterize(rand, zeroBound = true) + val clusters = diff.asIterable().clusterize(THREAD_LOCAL_RANDOM, zeroBound = true) for (cluster in clusters) { val perc: Float From 7c028b1fa637486c86ab875d13f96ae4050b108c Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Fri, 21 Mar 2025 15:11:09 +0700 Subject: [PATCH 12/23] Im with stupid --- .../client/screen/panels/util/GridPanel.kt | 79 ++++++++++--------- 1 file changed, 43 insertions(+), 36 deletions(-) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/util/GridPanel.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/util/GridPanel.kt index 3b655f3de..008634fac 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/util/GridPanel.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/util/GridPanel.kt @@ -53,46 +53,54 @@ open class GridPanel( enum class Layout { TOP_LEFT { - override fun rows(last: Int): IntIterator { - return (0 until last).iterator() - } - - override fun columns(last: Int): IntIterator { - return (0 until last).iterator() + override fun get( + list: List>, + row: Int, + column: Int, + rows: Int, + columns: Int + ): EditablePanel<*>? { + return list.getOrNull(column + row * columns) } }, TOP_RIGHT { - override fun rows(last: Int): IntIterator { - return (0 until last).iterator() - } - - override fun columns(last: Int): IntIterator { - return (last - 1 downTo 0).iterator() + override fun get( + list: List>, + row: Int, + column: Int, + rows: Int, + columns: Int + ): EditablePanel<*>? { + return list.getOrNull((columns - column - 1) + row * columns) } }, - BOTTOM_LEFT { - override fun rows(last: Int): IntIterator { - return (last - 1 downTo 0).iterator() - } - override fun columns(last: Int): IntIterator { - return (0 until last).iterator() + BOTTOM_LEFT { + override fun get( + list: List>, + row: Int, + column: Int, + rows: Int, + columns: Int + ): EditablePanel<*>? { + return list.getOrNull(column + (rows - row - 1) * columns) } }, BOTTOM_RIGHT { - override fun rows(last: Int): IntIterator { - return (last - 1 downTo 0).iterator() - } - - override fun columns(last: Int): IntIterator { - return (last - 1 downTo 0).iterator() + override fun get( + list: List>, + row: Int, + column: Int, + rows: Int, + columns: Int + ): EditablePanel<*>? { + return list.getOrNull((columns - column - 1) + (rows - row - 1) * columns) } }; - abstract fun rows(last: Int): IntIterator - abstract fun columns(last: Int): IntIterator + abstract fun get(list: List>, row: Int, column: Int, rows: Int, columns: Int): EditablePanel<*>? } override fun sizeToContents() { @@ -108,11 +116,11 @@ open class GridPanel( val children = visibleChildren.iterator().filter { it.dock == Dock.NONE } - for (row in layout.rows(rows)) { + for (row in 0 until rows) { var maxHeight = 0f var rowWidth = 0f - for (column in layout.columns(columns)) { + for (column in 0 until columns) { val nextChild = children.maybe() ?: break rowWidth += nextChild.width + nextChild.dockMargin.horizontal maxHeight = max(maxHeight, nextChild.height + nextChild.dockMargin.vertical) @@ -128,17 +136,17 @@ open class GridPanel( override fun performLayout() { super.performLayout() - var children = visibleChildren.iterator().filter { it.dock == Dock.NONE } + val children = visibleChildren.stream().filter { it.dock == Dock.NONE }.toList() var totalWidth = 0f var totalHeight = 0f - for (row in layout.rows(rows)) { + for (row in 0 until rows) { var maxHeight = 0f var width = 0f - for (column in layout.columns(columns)) { - val child = children.maybe() ?: break + for (column in 0 until columns) { + val child = layout.get(children, row, column, rows, columns) ?: continue width += child.dockedWidth maxHeight = maxHeight.coerceAtLeast(child.dockedHeight) } @@ -149,17 +157,16 @@ open class GridPanel( val alignX = gravity.repositionX(width, totalWidth) val alignY = gravity.repositionY(height, totalHeight) - children = visibleChildren.iterator().filter { it.dock == Dock.NONE } totalWidth = 0f totalHeight = 0f - for (row in layout.rows(rows)) { + for (row in 0 until rows) { var maxHeight = 0f var width = 0f - for (column in layout.columns(columns)) { - val child = children.maybe() ?: break + for (column in 0 until columns) { + val child = layout.get(children, row, column, rows, columns) ?: continue child.x = alignX + width child.y = alignY + totalHeight width += child.dockedWidth From a2d9f43a2e8f5a030264425b0062ce56361cb322 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Fri, 21 Mar 2025 18:17:50 +0700 Subject: [PATCH 13/23] Fix grid panel layouts, introduce column major order --- .../client/screen/panels/util/GridPanel.kt | 58 ++++++++++++++----- 1 file changed, 44 insertions(+), 14 deletions(-) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/util/GridPanel.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/util/GridPanel.kt index 008634fac..28b32a5fd 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/util/GridPanel.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/util/GridPanel.kt @@ -7,6 +7,8 @@ import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel import ru.dbotthepony.mc.otm.client.screen.panels.slot.AbstractSlotPanel import ru.dbotthepony.mc.otm.core.collect.filter import ru.dbotthepony.mc.otm.core.collect.maybe +import java.util.stream.IntStream +import java.util.stream.Stream import kotlin.math.max open class GridPanel( @@ -51,6 +53,14 @@ open class GridPanel( } } + var columnMajorOrder = false + set(value) { + if (field != value) { + field = value + invalidateLayout() + } + } + enum class Layout { TOP_LEFT { override fun get( @@ -58,9 +68,14 @@ open class GridPanel( row: Int, column: Int, rows: Int, - columns: Int + columns: Int, + columnMajorOrder: Boolean ): EditablePanel<*>? { - return list.getOrNull(column + row * columns) + if (columnMajorOrder) { + return list.getOrNull(row + column * rows) + } else { + return list.getOrNull(column + row * columns) + } } }, @@ -70,9 +85,14 @@ open class GridPanel( row: Int, column: Int, rows: Int, - columns: Int + columns: Int, + columnMajorOrder: Boolean ): EditablePanel<*>? { - return list.getOrNull((columns - column - 1) + row * columns) + if (columnMajorOrder) { + return list.getOrNull(row + (columns - column - 1) * rows) + } else { + return list.getOrNull((columns - column - 1) + row * columns) + } } }, @@ -82,9 +102,14 @@ open class GridPanel( row: Int, column: Int, rows: Int, - columns: Int + columns: Int, + columnMajorOrder: Boolean ): EditablePanel<*>? { - return list.getOrNull(column + (rows - row - 1) * columns) + if (columnMajorOrder) { + return list.getOrNull((rows - row - 1) + column * rows) + } else { + return list.getOrNull(column + (rows - row - 1) * columns) + } } }, @@ -94,13 +119,18 @@ open class GridPanel( row: Int, column: Int, rows: Int, - columns: Int + columns: Int, + columnMajorOrder: Boolean ): EditablePanel<*>? { - return list.getOrNull((columns - column - 1) + (rows - row - 1) * columns) + if (columnMajorOrder) { + return list.getOrNull((rows - row - 1) + (columns - column - 1) * rows) + } else { + return list.getOrNull((columns - column - 1) + (rows - row - 1) * columns) + } } }; - abstract fun get(list: List>, row: Int, column: Int, rows: Int, columns: Int): EditablePanel<*>? + abstract fun get(list: List>, row: Int, column: Int, rows: Int, columns: Int, columnMajorOrder: Boolean): EditablePanel<*>? } override fun sizeToContents() { @@ -114,14 +144,14 @@ open class GridPanel( var width = 0f var height = 0f - val children = visibleChildren.iterator().filter { it.dock == Dock.NONE } + val children = visibleChildren.filter { it.dock == Dock.NONE } for (row in 0 until rows) { var maxHeight = 0f var rowWidth = 0f for (column in 0 until columns) { - val nextChild = children.maybe() ?: break + val nextChild = layout.get(children, row, column, rows, columns, columnMajorOrder) ?: continue rowWidth += nextChild.width + nextChild.dockMargin.horizontal maxHeight = max(maxHeight, nextChild.height + nextChild.dockMargin.vertical) } @@ -136,7 +166,7 @@ open class GridPanel( override fun performLayout() { super.performLayout() - val children = visibleChildren.stream().filter { it.dock == Dock.NONE }.toList() + val children = visibleChildren.filter { it.dock == Dock.NONE } var totalWidth = 0f var totalHeight = 0f @@ -146,7 +176,7 @@ open class GridPanel( var width = 0f for (column in 0 until columns) { - val child = layout.get(children, row, column, rows, columns) ?: continue + val child = layout.get(children, row, column, rows, columns, columnMajorOrder) ?: continue width += child.dockedWidth maxHeight = maxHeight.coerceAtLeast(child.dockedHeight) } @@ -166,7 +196,7 @@ open class GridPanel( var width = 0f for (column in 0 until columns) { - val child = layout.get(children, row, column, rows, columns) ?: continue + val child = layout.get(children, row, column, rows, columns, columnMajorOrder) ?: continue child.x = alignX + width child.y = alignY + totalHeight width += child.dockedWidth From 06f109575d10bc29e6fda32daacbfee550c13b1a Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Fri, 21 Mar 2025 18:18:07 +0700 Subject: [PATCH 14/23] Fix children not getting re-sorted upon visibility changes --- .../dbotthepony/mc/otm/client/screen/panels/EditablePanel.kt | 3 +++ 1 file changed, 3 insertions(+) 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 e2be32ce9..247d22b28 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 @@ -281,11 +281,13 @@ open class EditablePanel( if (visibleChildrenParent?.contains(this) == false) { visibleChildrenParent.add(this) + parent?.invalidateChildrenSorting() parent?.layoutInvalidated = true } } else { if (visibleChildrenParent?.contains(this) == true) { visibleChildrenParent.remove(this) + parent?.invalidateChildrenSorting() parent?.layoutInvalidated = true } } @@ -1451,6 +1453,7 @@ open class EditablePanel( if (old != new) { child.visibilityChanges(new, old) + child.invalidateChildrenSorting() } child.updateVisible() From bfb8f0380a3d020d8a376af060fb20ffcdbb2b82 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Fri, 21 Mar 2025 21:10:46 +0700 Subject: [PATCH 15/23] Panel debug rendering --- .../mc/otm/client/render/MGUIGraphics.kt | 69 ++++++++++ .../mc/otm/client/render/RenderHelper.kt | 119 +++++++++++++----- .../mc/otm/client/screen/MatteryScreen.kt | 11 +- .../otm/client/screen/panels/EditablePanel.kt | 16 ++- .../otm/client/screen/panels/Panel2Widget.kt | 10 +- 5 files changed, 189 insertions(+), 36 deletions(-) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/MGUIGraphics.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/MGUIGraphics.kt index ee79d0a17..c7354cff0 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/MGUIGraphics.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/MGUIGraphics.kt @@ -14,6 +14,8 @@ import net.minecraft.world.inventory.tooltip.TooltipComponent import net.minecraft.world.item.ItemStack import ru.dbotthepony.mc.otm.client.minecraft import ru.dbotthepony.kommons.math.RGBAColor +import ru.dbotthepony.mc.otm.client.screen.panels.ScreenPos +import java.util.Arrays import kotlin.math.PI import kotlin.math.roundToInt @@ -45,6 +47,73 @@ class MGUIGraphics(val parent: GuiGraphics) { drawLine(pose.last().pose(), startX, startY, endX, endY, width, z, color) } + fun drawLine( + points: Iterable, + width: Float, + color: RGBAColor = RGBAColor.WHITE + ) { + drawLine(pose.last().pose(), points, width, color) + } + + fun drawLine( + points: Array, + width: Float, + color: RGBAColor = RGBAColor.WHITE + ) { + drawLine(pose.last().pose(), points, width, color) + } + + fun drawLine( + width: Float, + color: RGBAColor, + vararg points: LinePoint, + ) { + drawLine(pose.last().pose(), points, width, color) + } + + fun drawLine( + width: Float, + color: RGBAColor, + points: Iterable, + ) { + drawLine(pose.last().pose(), points, width, color) + } + + fun drawLine( + width: Float, + color: RGBAColor, + z: Float, + points: List, + ) { + require(points.size >= 2) { "Degenerate point list: only ${points.size} defined" } + + val result = ArrayList(points.size) + + for (i in 1 until points.size) { + val (x0, y0) = points[i - 1] + val (x1, y1) = points[i] + + result.add( + LinePoint( + x0, y0, + x1, y1, + z + ) + ) + } + + drawLine(pose.last().pose(), result, width, color) + } + + fun drawLine( + width: Float, + color: RGBAColor, + z: Float, + vararg points: ScreenPos, + ) { + drawLine(width, color, z, Arrays.asList(*points)) + } + fun renderRect( x: Float, y: Float, diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/RenderHelper.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/RenderHelper.kt index 2d54cc06b..c4d3a331a 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/RenderHelper.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/RenderHelper.kt @@ -294,6 +294,65 @@ fun renderColoredSphere(pose: PoseStack, radius: Float, color: RGBAColor = RGBAC BufferUploader.drawWithShader(builder.buildOrThrow()) } +private fun uploadLineSegment( + builder: BufferBuilder, + matrix: Matrix4f, + startX: Float, + startY: Float, + endX: Float, + endY: Float, + width: Float, + z: Float, + color: RGBAColor = RGBAColor.WHITE +) { + val length = ((startX - endX).pow(2f) + (startY - endY).pow(2f)).pow(0.5f) + var angle = acos((endX - startX) / length) + + if (startY > endY) + angle *= -1f + + val cos = cos(angle) + val sin = sin(angle) + + val y0 = -width + + val y1 = width + + val x2 = length + val y2 = width + + val x3 = length + val y3 = -width + + builder.vertex(matrix, + startX - y0 * sin, + startY + y0 * cos, + z).color(color) + + builder.vertex(matrix, + startX - y1 * sin, + startY + y1 * cos, + z).color(color) + + builder.vertex(matrix, + startX + x2 * cos - y2 * sin, + startY + x2 * sin + y2 * cos, + z).color(color) + + builder.vertex(matrix, + startX + x3 * cos - y3 * sin, + startY + x3 * sin + y3 * cos, + z).color(color) +} + +data class LinePoint( + val startX: Float, + val startY: Float, + val endX: Float, + val endY: Float, + val z: Float = 0f, +) + fun drawLine( matrix: Matrix4f, startX: Float, @@ -312,46 +371,46 @@ fun drawLine( RenderSystem.depthFunc(GL_ALWAYS) val builder = tesselator.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR) + uploadLineSegment(builder, matrix, startX, startY, endX, endY, width, z, color) + BufferUploader.drawWithShader(builder.buildOrThrow()) +} - val length = ((startX - endX).pow(2f) + (startY - endY).pow(2f)).pow(0.5f) - val angle = acos((endX - startX) / length) +fun drawLine( + matrix: Matrix4f, + points: Iterable, + width: Float, + color: RGBAColor = RGBAColor.WHITE +) { + val itr = points.iterator() - val cos = cos(angle) - val sin = sin(angle) + if (!itr.hasNext()) { + throw IllegalArgumentException("No line points were provided") + } - val y0 = -width + RenderSystem.setShader(GameRenderer::getPositionColorShader) + RenderSystem.enableBlend() + RenderSystem.defaultBlendFunc() - val y1 = width + if (!is3DContext) + RenderSystem.depthFunc(GL_ALWAYS) - val x2 = length - val y2 = width + val builder = tesselator.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR) - val x3 = length - val y3 = -width - - builder.vertex(matrix, - startX - y0 * sin, - startY + y0 * cos, - z).color(color) - - builder.vertex(matrix, - startX - y1 * sin, - startY + y1 * cos, - z).color(color) - - builder.vertex(matrix, - startX + x2 * cos - y2 * sin, - startY + x2 * sin + y2 * cos, - z).color(color) - - builder.vertex(matrix, - startX + x3 * cos - y3 * sin, - startY + x3 * sin + y3 * cos, - z).color(color) + for ((startX, startY, endX, endY, z) in itr) + uploadLineSegment(builder, matrix, startX, startY, endX, endY, width, z, color) BufferUploader.drawWithShader(builder.buildOrThrow()) } +fun drawLine( + matrix: Matrix4f, + points: Array, + width: Float, + color: RGBAColor = RGBAColor.WHITE +) { + return drawLine(matrix, points.asIterable(), width, color) +} + data class ScissorRect(val xStart: Int, val yStart: Int, val xEnd: Int, val yEnd: Int, val lock: Boolean = false) { val width: Int get() = (xEnd - xStart).coerceAtLeast(0) 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 23e70a863..fad46c6d4 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 @@ -17,9 +17,11 @@ import net.neoforged.neoforge.common.NeoForge import org.lwjgl.opengl.GL11 import ru.dbotthepony.kommons.util.getValue import ru.dbotthepony.kommons.util.setValue +import ru.dbotthepony.kommons.math.RGBAColor import ru.dbotthepony.mc.otm.player.matteryPlayer import ru.dbotthepony.mc.otm.client.render.MGUIGraphics import ru.dbotthepony.mc.otm.client.moveMousePosScaled +import ru.dbotthepony.mc.otm.client.render.LinePoint import ru.dbotthepony.mc.otm.client.render.WidgetLocation import ru.dbotthepony.mc.otm.client.render.Widgets18 import ru.dbotthepony.mc.otm.client.render.translation @@ -55,6 +57,7 @@ import ru.dbotthepony.mc.otm.menu.widget.LevelGaugeWidget import ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget import java.util.* import java.util.concurrent.CopyOnWriteArrayList +import kotlin.collections.ArrayList import kotlin.collections.List import kotlin.collections.MutableSet import kotlin.collections.isNotEmpty @@ -690,7 +693,13 @@ abstract class MatteryScreen(menu: T, inventory: Inventory, tit for (panel in panelsReversed) { RenderSystem.depthFunc(GL11.GL_ALWAYS) RenderSystem.setShaderColor(1f, 1f, 1f, 1f) - panel.render(wrap, mouseXf, mouseYf, partialTick) + val segments = ArrayList() + + panel.render(wrap, mouseXf, mouseYf, partialTick, segments) + + if (segments.isNotEmpty()) { + wrap.drawLine(0.5f, RGBAColor.GOLD, segments) + } } if (!panelsReversed.any { it.updateCursor0() }) 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 247d22b28..0228f172b 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 @@ -13,11 +13,13 @@ import net.minecraft.client.gui.navigation.ScreenRectangle import net.minecraft.client.gui.screens.Screen import net.minecraft.network.chat.Component import org.apache.logging.log4j.LogManager +import ru.dbotthepony.kommons.math.RGBAColor import ru.dbotthepony.mc.otm.SystemTime import ru.dbotthepony.mc.otm.client.CursorType import ru.dbotthepony.mc.otm.client.render.MGUIGraphics import ru.dbotthepony.mc.otm.client.minecraft import ru.dbotthepony.mc.otm.client.moveMousePosScaled +import ru.dbotthepony.mc.otm.client.render.LinePoint import ru.dbotthepony.mc.otm.client.render.RenderGravity import ru.dbotthepony.mc.otm.client.render.currentScissorRect import ru.dbotthepony.mc.otm.client.render.popScissorRect @@ -889,12 +891,11 @@ open class EditablePanel( } } - fun render(graphics: MGUIGraphics, mouseX: Float, mouseY: Float, partialTick: Float) { + fun render(graphics: MGUIGraphics, mouseX: Float, mouseY: Float, partialTick: Float, debugSegments: MutableList) { once = true - if (!isVisible()) { + if (!isVisible()) return - } val poseStack = graphics.pose @@ -942,12 +943,19 @@ open class EditablePanel( child.absoluteX = absoluteX + child.x + xOffset child.absoluteY = absoluteY + child.y + yOffset - child.render(graphics, mouseX, mouseY, partialTick) + child.render(graphics, mouseX, mouseY, partialTick, debugSegments) } if (scissor) { popScissorRect() } + + if (minecraft.entityRenderDispatcher.shouldRenderHitBoxes()) { + debugSegments.add(LinePoint(absoluteX, absoluteY, absoluteX + width, absoluteY)) + debugSegments.add(LinePoint(absoluteX + width, absoluteY, absoluteX + width, absoluteY + height)) + debugSegments.add(LinePoint(absoluteX + width, absoluteY + height, absoluteX, absoluteY + height)) + debugSegments.add(LinePoint(absoluteX, absoluteY + height, absoluteX, absoluteY)) + } } fun updateCursor0(): Boolean { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/Panel2Widget.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/Panel2Widget.kt index 6c06bc038..bd5bdaee8 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/Panel2Widget.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/Panel2Widget.kt @@ -4,6 +4,8 @@ import net.minecraft.client.gui.GuiGraphics import net.minecraft.client.gui.components.Renderable import net.minecraft.client.gui.components.events.GuiEventListener import net.minecraft.client.gui.screens.Screen +import ru.dbotthepony.kommons.math.RGBAColor +import ru.dbotthepony.mc.otm.client.render.LinePoint import ru.dbotthepony.mc.otm.client.render.MGUIGraphics // before 1.19.3 Renderable was Widget @@ -21,11 +23,17 @@ class Panel2Widget>( val yFloat = mouseY.toFloat() val wrap = MGUIGraphics(graphics) + val segments = ArrayList() panel.tickHovered0(xFloat, yFloat, false) panel.tickHovered1() panel.tickHovered2() - panel.render(wrap, xFloat, yFloat, partialTick) + panel.render(wrap, xFloat, yFloat, partialTick, segments) + + if (segments.isNotEmpty()) { + wrap.drawLine(0.5f, RGBAColor.GOLD, segments) + } + panel.renderTooltips(wrap, xFloat, yFloat, partialTick) } From ab2cc33b7a330f1ec0164748e263e6f9d0822c31 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Fri, 21 Mar 2025 21:24:16 +0700 Subject: [PATCH 16/23] "Smart storage exchange icon" --- .../mc/otm/client/render/Widgets18.kt | 1 + .../textures/gui/widgets/storage_controls.png | Bin 1303 -> 1329 bytes .../textures/gui/widgets/storage_controls.xcf | Bin 16168 -> 16856 bytes 3 files changed, 1 insertion(+) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/Widgets18.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/Widgets18.kt index 21aa5a69c..ec3f41496 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/Widgets18.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/Widgets18.kt @@ -30,6 +30,7 @@ object Widgets18 { val RESTOCK_FROM_STORAGE = storageGrid.next() val RESTOCK_WITH_MOVE_TO_STORAGE = storageGrid.next() val RESTOCK_WITH_MOVE_FROM_STORAGE = storageGrid.next() + val SMART_STORAGE_EXCHANGE = storageGrid.next() private val miscGrid = WidgetLocation.WIDGET_18.grid(4, 4) diff --git a/src/main/resources/assets/overdrive_that_matters/textures/gui/widgets/storage_controls.png b/src/main/resources/assets/overdrive_that_matters/textures/gui/widgets/storage_controls.png index d46201f5e4b8bb78105dec2c9e8c3a39eb5432a1..7c39f8ee48dd4cb598345aed4376cad31105b975 100644 GIT binary patch delta 1139 zcmV-(1dRKa3b6{1J%2zP#a~lJk*b5Wh&W`ZPIgfd$5O>2SSW3URvk<({emV9Ns5c3 z;979-W3lSs;;gHKs~`w|fVc}ziY`*(|B^zB7!Qv7@$TN^?j0aBDoi!IqJXMdMk*c? zv$<8V=M{boV*nw9BxdS~^kN2{JD8OWm3W3Ytf(603t5*H z&Rd+-N{zMd$zK@AYb$B4(;Pz_OGqFI5i)8hqXG*NS~XHkq-a0p;U9JUNpi{LDua<@ z0ad7w96$IU{D1D&EKEK2h+1tNoTK)Y1jYo2^w|A!J00006VoOIv0F$}_DGU`37a|9R)$afR11^*A z0V^aTvq%Fw6&I3;+NC8+Lthyly)3h-0}Gt?cFk%tyWSrI;ZWDSw?BkG96SeqxSUM z%I6Xkn=RG*%$YjW&QPZy)uyKGD~F_1I|<1Wp8RZPf@3Nq#8G5QOb0n&L2yEwabnZ4 z*liBcT7eG&761SM0001>gD*{SXW`z#k3JX;(Ipj*k_w45?_5jH(3ATDDt}$t=v0!Q zO9V$wX{82>p|V@Yc>z@y!#$ChGJo4PsoCILbU0St*Rp}zEe9HMs8yCOTNREP0aAbW zpipIU^o|%?DMGV-9V2SCv`>B7 zRR38^PA&hv_#2jzibk$7Q=@Vtbv?VLNlW}=39&87?)7||>yY%2r+?heOzQoVADp{S zC0y!PbD!@tHKTUq7Avjw3P!lkcN!09F&GK)z56FxO=H*at%SJ_$&q9^-0%133pt<9 zEDe}lPBeR`Jq>@~p3TeCt1-ry&z}!-djC3{9N%K0JH3CIZ}Z=Y(?R9|G@af%3(!$?LZyURB$ zlv89l&zGJ`(-M}debV!Wv@0Q{@`f}W8dFre64JY}(u8P@gQGM^>0Ma?28QLt^2W?x z8R>mUbW`^Zr~p(n002ni4`W`y`4Bw(T9p6*002ovPDHLk FV1jZO7()O6 delta 1112 zcmV-e1gHD43YQ9yJ%3OXg-=tZB9$U`5Ov5E^lTmwqMWPgB9B#trNu!uK^XE!aK z^FDEyl_iDvoOsfp28kcJF1!53x#Y0GGs8wUJx?4a7E4_$cQGp)D)9_)L{T-$7jiBu zoVPfu)jDhU$zK>OXe$}6Q;i{kB_xr82pM%$P=$pk?HVa2(sUm8@Q*nD6uD$_Rlvxx zfErXtjvxFFet*x_EKX0kNx?YK`(oQ4<3L~+Xf5?HilAlT_7J>IO`ldWEbPM#axpQlup|V@Yc>z@y!y}QHGJo4PsoCK7=y0sOuVn+bTMjhjP^&CWwkpgT0aAbW zpip{g z0HgHLNga7k#o^IFT1Sj~DZ+01I(pP>X`dwR{E?%-o?iSgr7Y=b*%8+a0002sAjtX0 zRR38^PA&hv_zX))MI%?4sZqI+x}Htbq$U2bgxEdFZuNYd>yY##Pk*_cnbi9!KR9=v zO1RXo=04wPYDVqIEmm4<6^wA7?=&8;$6zGHx9*>4HH}@vw-V+$B%dV9;eNkIU< zW@*6aa-!Kg?P>V?_H16BUX3xveExix)BD$Sa{L|(-Rb?ye4GDHoF~ zZ>Y2qGKJ~TnDVVFAwv?a@!=?h_dpvP-CIRtbUfU)+gtwkutfKk|Aj_FqoL75x_3YY epb`N9KpKCAH*eLfyuZr;00000MgHQS^ljY*p{W@&!f*wnPa*mP~uq-krsSjFa0@ZeuTtOo;n(u<}C z#R?UvjHmjKiV9LK(^9IbtscB+VHLq3B1jPg4~nP<#jd;GB;c`o*k`_H-gjr0y;~@q zBI9Ss?1j-UBXI$*HOri4D`)k0&YBdb!@*hS=d9-tybt-JA;}pCaW>uOY%cF_xWVVP zBb*zDINRHEe9>`&v$K&iQ7)!{&)o&io@pwB-{)3uszr7F)tEXLP@kw%`Y@x;8xbQ< zr@Z4NMAwQvju~F7+{JBlNR-Jzsz%+L#FG^r7C_SkNizvnO%!Z8jk&5!Nt19k(Z7BH*!O#M6oqPnM0j z@*>DnXyqle@+THucL2cGkJdMg)>mN9W&L%7VYI=lVJ*Sk5PIYnbl1y*eA2M8}>Ap9O59{$17>*oL>570(lpp7iE)|Lr?=m^^ACEeB& z#{NWR0b+TSv9lPBU1y)8lK}A?+ISvq{4{$R^8hGU=}|={k5DSFnKwQNkZ8eZVyj^- zi9MB9e5!QQqT*&NN;QGxGYlm^U^V%RWjdWe(v-yOsFpI5W-O;OmGZ~rJ+`@Pnn30j b7Bh=j%#>Il{hL7V8??RO(Ds>WE>rpkobe4z delta 831 zcmYk2PiPZC6o)f!lhCz`(QQ+bE(sdDCZ>tT#+bxzqm}BR3W^Ad_=n;_4}nyXDkv3H z6j7wjc#tB}gQ8N2!YdU~=uJVT^&l7&!IPItRM3kEb>~e6+{^cv@Auy9oBBoeV07#< zn;yTiaK76^pYNb#%T~$OYm!TcB$rnt+jmQbBs`s0Wf3_gx#F*6SJSWlDT_6;lBp+> z>n0_8*1wg-hLe&TB|L>ave+~y+2?Hv5AWC7c*DER3+@RYij47D-!9OOyXO|WM0Qy7 zvfDV|{|(^h?nx*39}D4gJN&P(qTdJ5he_zymC*0vP9vy+@tlP5K?&nKujna0A85fh zfkOa+3ls?4QGvh=9yUJ$m{k&HO$qbmf1L)yTE{*H!6TFko>i$J$KFs`gD@izjwlh% z;b-e8fHgtFno+`sMxX1aF>ftHD3uASMFfd*gYIvb&Nr*m2$DT%EdM} zbNq`vie-Bcz+OffJ0&vWq3i+t7Jmj1UnG&Rlt^UpQ*r_zIZqr9^zMLnnCX`$~fWysM$&2 UyV4&9r7jYsElQO3SNrS#0AS^*Q2+n{ From 0126d4d9767f5a0fccf339dd1dbc700d5788abf2 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Sat, 22 Mar 2025 00:30:03 +0700 Subject: [PATCH 17/23] Don't override nextGaussian, because JVM implementation should be generally faster --- .../dbotthepony/mc/otm/core/util/GJRAND64RandomSource.kt | 8 -------- .../mc/otm/core/util/IRandomSourceGenerator.kt | 4 +++- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/GJRAND64RandomSource.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/GJRAND64RandomSource.kt index c8adbfa13..f26a4aaae 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/GJRAND64RandomSource.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/GJRAND64RandomSource.kt @@ -2,15 +2,12 @@ package ru.dbotthepony.mc.otm.core.util import net.minecraft.util.Mth import net.minecraft.util.RandomSource -import net.minecraft.world.level.levelgen.MarsagliaPolarGaussian import net.minecraft.world.level.levelgen.PositionalRandomFactory import net.minecraft.world.level.levelgen.RandomSupport import ru.dbotthepony.kommons.random.GJRAND64Random import java.lang.StringBuilder class GJRAND64RandomSource : GJRAND64Random, IRandomSourceGenerator { - private val gaussian = MarsagliaPolarGaussian(this) - constructor() : super(RandomSupport.generateUniqueSeed(), RandomSupport.generateUniqueSeed()) constructor(seed: Long) : super(seed) constructor(seed0: Long, seed1: Long) : super(seed0, seed1) @@ -19,10 +16,6 @@ class GJRAND64RandomSource : GJRAND64Random, IRandomSourceGenerator { return nextLong().ushr(32).toInt() } - override fun nextGaussian(): Double { - return gaussian.nextGaussian() - } - override fun fork(): RandomSource { return GJRAND64RandomSource(nextLong(), nextLong()) } @@ -33,7 +26,6 @@ class GJRAND64RandomSource : GJRAND64Random, IRandomSourceGenerator { override fun setSeed(seed: Long) { reinitialize(seed) - gaussian.reset() } class Positional(private val seed0: Long, private val seed1: Long) : PositionalRandomFactory { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/IRandomSourceGenerator.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/IRandomSourceGenerator.kt index 999e39cfd..cd0fa1e47 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/IRandomSourceGenerator.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/IRandomSourceGenerator.kt @@ -26,5 +26,7 @@ interface IRandomSourceGenerator : RandomSource, RandomGenerator { return super.nextDouble() } - override fun nextGaussian(): Double + override fun nextGaussian(): Double { + return super.nextGaussian() + } } From 0a9e90bec6cbed96d87965cdd2fd9c2521cf4aa7 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Sat, 22 Mar 2025 00:49:42 +0700 Subject: [PATCH 18/23] Make matter recycler receive random amount of matter on tick instead of determining total matter received upon job start --- .../otm/block/entity/matter/MatterRecyclerBlockEntity.kt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/MatterRecyclerBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/MatterRecyclerBlockEntity.kt index d8fe21afe..ce4f69727 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/MatterRecyclerBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/MatterRecyclerBlockEntity.kt @@ -117,7 +117,7 @@ class MatterRecyclerBlockEntity(blockPos: BlockPos, blockState: BlockState) stack.shrink(1) container.setChanged(0) - val actualMatter = dustMatter.matter * (0.4 + level!!.otmRandom.nextDouble() * 0.6) + val actualMatter = dustMatter.matter return JobContainer.success( RecyclerJob( @@ -134,10 +134,11 @@ class MatterRecyclerBlockEntity(blockPos: BlockPos, blockState: BlockState) if (toReceive.isZero) return status.success() + else if (matter.receiveMatter(toReceive, true) != toReceive) + return status.noMatter() - val received = matter.receiveMatter(toReceive, false) - status.scale(received / toReceive) - job.totalMatter -= received + matter.receiveMatter(toReceive * 0.4 + level!!.otmRandom.nextDouble() * 0.6, false) + job.totalMatter -= toReceive } override fun tick() { From 22ebdbb1eb9ec9c2cbdd6db7f2f3c6be53b4a212 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Sat, 22 Mar 2025 19:57:42 +0700 Subject: [PATCH 19/23] Rename beforeDroppingItems to dropItems, and change logic --- .../kotlin/ru/dbotthepony/mc/otm/block/MatteryBlock.kt | 9 +++------ .../mc/otm/block/entity/MatteryBlockEntity.kt | 9 +++++++-- .../otm/block/entity/decorative/CargoCrateBlockEntity.kt | 9 ++++++++- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/MatteryBlock.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/MatteryBlock.kt index 7a702d9f2..cc5bd8684 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/MatteryBlock.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/MatteryBlock.kt @@ -14,6 +14,7 @@ import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.NbtOps import net.minecraft.network.chat.Component import net.minecraft.network.chat.ComponentSerialization +import net.minecraft.server.level.ServerLevel import net.minecraft.util.RandomSource import net.minecraft.world.Containers import net.minecraft.world.InteractionResult @@ -259,15 +260,11 @@ open class MatteryBlock(properties: Properties = DEFAULT_PROPERTIES) : Block(pro newBlockState: BlockState, movedByPiston: Boolean ) { - if (!oldBlockState.`is`(newBlockState.block) && !level.isClientSide) { + if (!oldBlockState.`is`(newBlockState.block) && level is ServerLevel) { val blockentity = level.getBlockEntity(blockPos) if (blockentity is MatteryBlockEntity) { - blockentity.beforeDroppingItems(oldBlockState, level, blockPos, newBlockState, movedByPiston) - - for (container in blockentity.droppableContainers) - Containers.dropContents(level, blockPos, container) - + blockentity.dropItems(oldBlockState, level, blockPos, newBlockState, movedByPiston) level.updateNeighbourForOutputSignal(blockPos, this) } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MatteryBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MatteryBlockEntity.kt index 49fb486f3..4a4d90f37 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MatteryBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MatteryBlockEntity.kt @@ -17,6 +17,7 @@ import net.minecraft.nbt.CompoundTag import net.minecraft.server.level.ServerLevel import net.minecraft.server.level.ServerPlayer import net.minecraft.world.Container +import net.minecraft.world.Containers import net.minecraft.world.entity.player.Player import net.minecraft.world.level.ChunkPos import net.minecraft.world.level.Level @@ -70,7 +71,7 @@ import kotlin.collections.ArrayList /** * Absolute barebone (lol) block entity class in Overdrive that Matters, providing bare minimum (lulmao, minecraft engine) functionality */ -abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: BlockPos, p_155230_: BlockState) : BlockEntity(p_155228_, p_155229_, p_155230_), INeighbourChangeListener { +abstract class MatteryBlockEntity(type: BlockEntityType<*>, pos: BlockPos, state: BlockState) : BlockEntity(type, pos, state), INeighbourChangeListener { private val sidelessCaps = Reference2ObjectOpenHashMap, Any>() private val sidedCaps = Array(RelativeSide.entries.size) { Reference2ObjectOpenHashMap, ControllableCapability<*>>() @@ -121,7 +122,11 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc _neighbourChangeListeners.add(listener) } - open fun beforeDroppingItems(oldBlockState: BlockState, level: Level, blockPos: BlockPos, newBlockState: BlockState, movedByPiston: Boolean) {} + open fun dropItems(oldBlockState: BlockState, level: ServerLevel, blockPos: BlockPos, newBlockState: BlockState, movedByPiston: Boolean) { + for (container in droppableContainers) + Containers.dropContents(level, blockPos, container) + } + open fun beforeDestroyedByPlayer(level: Level, blockPos: BlockPos, blockState: BlockState, player: Player) {} open fun tick() { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/decorative/CargoCrateBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/decorative/CargoCrateBlockEntity.kt index e960ffe5c..47e7d4ca6 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/decorative/CargoCrateBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/decorative/CargoCrateBlockEntity.kt @@ -55,8 +55,15 @@ class CargoCrateBlockEntity( } }) - override fun beforeDroppingItems(oldBlockState: BlockState, level: Level, blockPos: BlockPos, newBlockState: BlockState, movedByPiston: Boolean) { + override fun dropItems( + oldBlockState: BlockState, + level: ServerLevel, + blockPos: BlockPos, + newBlockState: BlockState, + movedByPiston: Boolean + ) { unpackLootTable() + super.dropItems(oldBlockState, level, blockPos, newBlockState, movedByPiston) } override fun beforeDestroyedByPlayer(level: Level, blockPos: BlockPos, blockState: BlockState, player: Player) { From c778f192b2c3e28d2ce2a474a8a12bd8cf8411dc Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Sat, 22 Mar 2025 23:33:28 +0700 Subject: [PATCH 20/23] Proof-of-concept "Small capsule" loot block --- .../decorative/BreakableContainerBlock.kt | 26 ++++++ .../BreakableContainerBlockEntity.kt | 23 +++++ .../mc/otm/core/util/BlockLootTableHolder.kt | 87 +++++++++++++++++++ .../ru/dbotthepony/mc/otm/registry/MNames.kt | 2 + .../mc/otm/registry/game/MBlockEntities.kt | 7 ++ .../mc/otm/registry/game/MBlocks.kt | 10 +++ .../mc/otm/registry/game/MItems.kt | 2 + 7 files changed, 157 insertions(+) create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/block/decorative/BreakableContainerBlock.kt create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/decorative/BreakableContainerBlockEntity.kt create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/core/util/BlockLootTableHolder.kt diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/decorative/BreakableContainerBlock.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/decorative/BreakableContainerBlock.kt new file mode 100644 index 000000000..dd68686cf --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/decorative/BreakableContainerBlock.kt @@ -0,0 +1,26 @@ +package ru.dbotthepony.mc.otm.block.decorative + +import net.minecraft.core.BlockPos +import net.minecraft.world.item.ItemStack +import net.minecraft.world.level.block.EntityBlock +import net.minecraft.world.level.block.entity.BlockEntity +import net.minecraft.world.level.block.state.BlockState +import net.minecraft.world.level.storage.loot.LootParams +import net.minecraft.world.level.storage.loot.parameters.LootContextParams +import ru.dbotthepony.mc.otm.block.RotatableMatteryBlock +import ru.dbotthepony.mc.otm.block.entity.decorative.BreakableContainerBlockEntity + +class BreakableContainerBlock(properties: Properties) : RotatableMatteryBlock(properties), EntityBlock { + override fun newBlockEntity(pos: BlockPos, state: BlockState): BlockEntity { + return BreakableContainerBlockEntity(pos, state) + } + + override fun getDrops(state: BlockState, params: LootParams.Builder): MutableList { + val entity = params.getOptionalParameter(LootContextParams.BLOCK_ENTITY) + + if (entity is BreakableContainerBlockEntity) + return entity.loot.getItems(params, state) + + return super.getDrops(state, params) + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/decorative/BreakableContainerBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/decorative/BreakableContainerBlockEntity.kt new file mode 100644 index 000000000..d5dfd0d08 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/decorative/BreakableContainerBlockEntity.kt @@ -0,0 +1,23 @@ +package ru.dbotthepony.mc.otm.block.entity.decorative + +import net.minecraft.core.BlockPos +import net.minecraft.core.HolderLookup +import net.minecraft.nbt.CompoundTag +import net.minecraft.world.level.block.state.BlockState +import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity +import ru.dbotthepony.mc.otm.core.util.BlockLootTableHolder +import ru.dbotthepony.mc.otm.registry.game.MBlockEntities + +class BreakableContainerBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryBlockEntity(MBlockEntities.BREAKABLE, blockPos, blockState) { + val loot = BlockLootTableHolder(::markDirtyFast) + + override fun saveLevel(nbt: CompoundTag, registry: HolderLookup.Provider) { + super.saveLevel(nbt, registry) + loot.save(nbt, registry) + } + + override fun loadAdditional(nbt: CompoundTag, registry: HolderLookup.Provider) { + super.loadAdditional(nbt, registry) + loot.load(nbt, registry) + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/BlockLootTableHolder.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/BlockLootTableHolder.kt new file mode 100644 index 000000000..eda7f3e01 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/BlockLootTableHolder.kt @@ -0,0 +1,87 @@ +package ru.dbotthepony.mc.otm.core.util + +import net.minecraft.core.HolderLookup +import net.minecraft.core.registries.Registries +import net.minecraft.nbt.CompoundTag +import net.minecraft.nbt.Tag +import net.minecraft.resources.ResourceKey +import net.minecraft.resources.ResourceLocation +import net.minecraft.server.level.ServerLevel +import net.minecraft.util.RandomSource +import net.minecraft.world.item.ItemStack +import net.minecraft.world.level.block.state.BlockState +import net.minecraft.world.level.storage.loot.LootParams +import net.minecraft.world.level.storage.loot.LootTable +import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets +import net.minecraft.world.level.storage.loot.parameters.LootContextParams +import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity.Companion.LOOT_TABLE_KEY +import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity.Companion.LOOT_TABLE_SEED_KEY +import ru.dbotthepony.mc.otm.core.otmRandom + +class BlockLootTableHolder(private val listener: Runnable = Runnable { }) { + private var ignoreListener = false + + var lootTable: ResourceKey? = null + set(value) { + if (field != value) { + field = value + if (!ignoreListener) listener.run() + } + } + + var lootTableSeed: Long? = null + set(value) { + if (field != value) { + field = value + if (!ignoreListener) listener.run() + } + } + + fun save(nbt: CompoundTag, registry: HolderLookup.Provider) { + try { + ignoreListener = true + + if (lootTable != null) { + nbt.putString(LOOT_TABLE_KEY, lootTable!!.location().toString()) + + if (lootTableSeed != null) + nbt.putLong(LOOT_TABLE_SEED_KEY, lootTableSeed!!) + } + } finally { + ignoreListener = false + } + } + + fun load(nbt: CompoundTag, registry: HolderLookup.Provider) { + try { + ignoreListener = true + + if (nbt.contains(LOOT_TABLE_KEY, Tag.TAG_STRING.toInt())) { + lootTable = ResourceLocation.tryParse(nbt.getString(LOOT_TABLE_KEY))?.let { ResourceKey.create(Registries.LOOT_TABLE, it) } + lootTableSeed = if (nbt.contains(LOOT_TABLE_SEED_KEY, Tag.TAG_LONG.toInt())) nbt.getLong(LOOT_TABLE_SEED_KEY) else null + } + } finally { + ignoreListener = false + } + } + + fun lookup(level: ServerLevel): LootTable { + val lootTable = lootTable ?: return LootTable.EMPTY + return level.server.reloadableRegistries().getLootTable(lootTable) + } + + private fun selectRandom(overrideSeed: Long?, fallback: RandomSource): RandomSource { + val lootTableSeed = overrideSeed ?: lootTableSeed + return if (lootTableSeed == null) fallback else GJRAND64RandomSource(lootTableSeed) + } + + fun getItems(params: LootParams, level: ServerLevel, overrideSeed: Long? = null): MutableList { + return lookup(level).getRandomItems(params, selectRandom(overrideSeed, level.otmRandom)) + } + + fun getItems(params: LootParams.Builder, blockState: BlockState, overrideSeed: Long? = null): MutableList { + val level = params.level + val create = params.withParameter(LootContextParams.BLOCK_STATE, blockState).create(LootContextParamSets.BLOCK) + return getItems(create, level, overrideSeed) + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MNames.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MNames.kt index 03e4d3d46..c3ebab17d 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MNames.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MNames.kt @@ -341,6 +341,8 @@ object MNames { const val TRITANIUM_DOOR = "tritanium_door" const val TRITANIUM_TRAPDOOR = "tritanium_trapdoor" const val TRITANIUM_PRESSURE_PLATE = "tritanium_pressure_plate" + + const val SMALL_CAPSULE = "small_capsule" } object StatNames { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/game/MBlockEntities.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/game/MBlockEntities.kt index 021700c52..b5f8e80df 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/game/MBlockEntities.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/game/MBlockEntities.kt @@ -12,6 +12,7 @@ import ru.dbotthepony.mc.otm.block.entity.tech.* import ru.dbotthepony.mc.otm.block.entity.blackhole.BlackHoleBlockEntity import ru.dbotthepony.mc.otm.block.entity.blackhole.BlackHoleGeneratorBlockEntity import ru.dbotthepony.mc.otm.block.entity.cable.SimpleEnergyCableBlockEntity +import ru.dbotthepony.mc.otm.block.entity.decorative.BreakableContainerBlockEntity import ru.dbotthepony.mc.otm.block.entity.decorative.CargoCrateBlockEntity import ru.dbotthepony.mc.otm.block.entity.decorative.DevChestBlockEntity import ru.dbotthepony.mc.otm.block.entity.decorative.FluidTankBlockEntity @@ -118,6 +119,12 @@ object MBlockEntities { val HOLO_SIGN: BlockEntityType by registry.register(MNames.HOLO_SIGN) { BlockEntityType.Builder.of(::HoloSignBlockEntity, MBlocks.HOLO_SIGN).build(null) } + val BREAKABLE: BlockEntityType by registry.register("breakable") { + val blocks = ArrayList() + blocks.add(MBlocks.SMALL_CAPSULE) + BlockEntityType.Builder.of(::BreakableContainerBlockEntity, *blocks.toTypedArray()).build(null) + } + internal fun register(bus: IEventBus) { registry.register(bus) bus.addListener(this::registerClient) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/game/MBlocks.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/game/MBlocks.kt index 33d27ca12..8a4558eae 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/game/MBlocks.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/game/MBlocks.kt @@ -37,6 +37,7 @@ import ru.dbotthepony.mc.otm.block.MultiblockTestBlock import ru.dbotthepony.mc.otm.block.RotatableMatteryBlock import ru.dbotthepony.mc.otm.block.StorageCableBlock import ru.dbotthepony.mc.otm.block.addSimpleDescription +import ru.dbotthepony.mc.otm.block.decorative.BreakableContainerBlock import ru.dbotthepony.mc.otm.block.decorative.DevChestBlock import ru.dbotthepony.mc.otm.block.decorative.EngineBlock import ru.dbotthepony.mc.otm.block.decorative.FluidTankBlock @@ -434,6 +435,15 @@ object MBlocks { val MULTIBLOCK_TEST by registry.register("multiblock_test") { MultiblockTestBlock() } + val SMALL_CAPSULE by registry.register(MNames.SMALL_CAPSULE) { + BreakableContainerBlock( + BlockBehaviour.Properties.of() + .mapColor(MapColor.COLOR_GRAY) + .destroyTime(0f) + .explosionResistance(1.5f) + ) + } + init { MRegistry.registerBlocks(registry) } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/game/MItems.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/game/MItems.kt index 622cf8a9e..e287a6d0d 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/game/MItems.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/game/MItems.kt @@ -680,6 +680,8 @@ object MItems { val CONFIGURATOR: Item by registry.register(MNames.CONFIGURATOR) { ConfiguratorItem() } + val SMALL_CAPSULE by registry.register(MNames.SMALL_CAPSULE) { BlockItem(MBlocks.SMALL_CAPSULE, DEFAULT_PROPERTIES) } + init { MRegistry.registerItems(registry) } From 3902e604243cbb9fe4970e07f06b31186b093fe9 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Sun, 23 Mar 2025 01:12:35 +0700 Subject: [PATCH 21/23] BlockLootTableHolder#fill --- .../decorative/CargoCrateBlockEntity.kt | 62 ++------ .../kotlin/ru/dbotthepony/mc/otm/core/Ext.kt | 10 ++ .../mc/otm/core/util/BlockLootTableHolder.kt | 53 ++++++- .../mc/otm/core/util/LootTableUtils.kt | 132 ++++++++++++++++++ 4 files changed, 202 insertions(+), 55 deletions(-) create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/core/util/LootTableUtils.kt diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/decorative/CargoCrateBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/decorative/CargoCrateBlockEntity.kt index 47e7d4ca6..f1dc3a7d2 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/decorative/CargoCrateBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/decorative/CargoCrateBlockEntity.kt @@ -1,18 +1,11 @@ package ru.dbotthepony.mc.otm.block.entity.decorative -import net.minecraft.advancements.CriteriaTriggers import net.minecraft.core.BlockPos import net.minecraft.core.HolderLookup -import net.minecraft.core.registries.Registries import net.minecraft.nbt.CompoundTag -import net.minecraft.nbt.Tag import net.minecraft.network.chat.Component -import net.minecraft.resources.ResourceKey -import net.minecraft.resources.ResourceLocation import net.minecraft.server.level.ServerLevel -import net.minecraft.server.level.ServerPlayer import net.minecraft.sounds.SoundSource -import net.minecraft.world.Containers import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Player import net.minecraft.world.inventory.AbstractContainerMenu @@ -21,18 +14,14 @@ import net.minecraft.world.level.Level import net.minecraft.world.level.block.Block import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.level.gameevent.GameEvent -import net.minecraft.world.level.storage.loot.LootParams -import net.minecraft.world.level.storage.loot.LootTable -import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets -import net.minecraft.world.level.storage.loot.parameters.LootContextParams -import net.minecraft.world.phys.Vec3 import net.neoforged.neoforge.capabilities.Capabilities import ru.dbotthepony.mc.otm.block.decorative.CargoCrateBlock import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity -import ru.dbotthepony.mc.otm.container.MatteryContainer import ru.dbotthepony.mc.otm.container.HandlerFilter +import ru.dbotthepony.mc.otm.container.MatteryContainer import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.core.otmRandom +import ru.dbotthepony.mc.otm.core.util.BlockLootTableHolder import ru.dbotthepony.mc.otm.menu.decorative.CargoCrateMenu import ru.dbotthepony.mc.otm.registry.game.MBlockEntities import ru.dbotthepony.mc.otm.registry.game.MSoundEvents @@ -47,11 +36,11 @@ class CargoCrateBlockEntity( val handler = container.handler(object : HandlerFilter { override fun canInsert(slot: Int, stack: ItemStack): Boolean { - return lootTable == null + return loot.lootTable == null } override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean { - return lootTable == null + return loot.lootTable == null } }) @@ -70,8 +59,7 @@ class CargoCrateBlockEntity( unpackLootTable(player) } - var lootTable: ResourceKey? = null - var lootTableSeed: Long? = null + val loot = BlockLootTableHolder(::markDirtyFast) fun onPlayerOpen() { val level = level @@ -99,52 +87,24 @@ class CargoCrateBlockEntity( override fun saveLevel(nbt: CompoundTag, registry: HolderLookup.Provider) { super.saveLevel(nbt, registry) - - if (lootTable != null) { - nbt.putString(LOOT_TABLE_KEY, lootTable!!.location().toString()) - nbt.putLong(LOOT_TABLE_SEED_KEY, lootTableSeed ?: 0L) - } + loot.save(nbt, registry) } override fun loadAdditional(nbt: CompoundTag, registry: HolderLookup.Provider) { super.loadAdditional(nbt, registry) - - if (nbt.contains(LOOT_TABLE_KEY, Tag.TAG_STRING.toInt())) { - lootTable = ResourceLocation.tryParse(nbt.getString(LOOT_TABLE_KEY))?.let { ResourceKey.create(Registries.LOOT_TABLE, it) } - lootTableSeed = if (nbt.contains(LOOT_TABLE_SEED_KEY, Tag.TAG_LONG.toInt())) nbt.getLong(LOOT_TABLE_SEED_KEY) else 0L - } + loot.load(nbt, registry) } - fun unpackLootTable(ply: Player? = null) { - val lootTable = lootTable ?: return - val lootTableSeed = lootTableSeed ?: 0L - val server = level?.server ?: return - - val loot = server.reloadableRegistries().getLootTable(lootTable) - - if (ply is ServerPlayer) { - CriteriaTriggers.GENERATE_LOOT.trigger(ply, lootTable) - } - - this.lootTable = null - this.lootTableSeed = null - - val params = LootParams.Builder(level as ServerLevel) - .withParameter(LootContextParams.ORIGIN, Vec3.atCenterOf(this.worldPosition)) - - if (ply != null) { - params.withLuck(ply.luck).withParameter(LootContextParams.THIS_ENTITY, ply) - } - - Containers.dropContents(level as ServerLevel, blockPos, container) - loot.fill(container, params.create(LootContextParamSets.CHEST), lootTableSeed) + private fun unpackLootTable(ply: Player? = null) { + loot.fill(level as? ServerLevel ?: return, blockPos, container, ply = ply, blockEntity = this) + loot.clear() } override val defaultDisplayName: Component get() = MACHINE_NAME override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu? { - if (lootTable != null && ply.isSpectator) + if (loot.lootTable != null && ply.isSpectator) return null unpackLootTable(ply) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt index 47b8a1c39..e1e0b3427 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt @@ -10,6 +10,7 @@ import com.google.common.collect.ImmutableSet import com.google.gson.JsonElement import com.google.gson.JsonObject import com.google.gson.JsonPrimitive +import it.unimi.dsi.fastutil.ints.IntList import it.unimi.dsi.fastutil.objects.ObjectComparators import net.minecraft.Util import net.minecraft.core.BlockPos @@ -199,6 +200,15 @@ fun IntArray.shuffle(random: RandomSource): IntArray { return this } +fun L.shuffle(random: RandomSource): L { + for (i in lastIndex downTo 1) { + val j = random.nextInt(i + 1) + set(j, set(i, getInt(j))) + } + + return this +} + fun > L.shuffle(random: RandomSource): L { Util.shuffle(this, random) return this diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/BlockLootTableHolder.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/BlockLootTableHolder.kt index eda7f3e01..4c8958029 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/BlockLootTableHolder.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/BlockLootTableHolder.kt @@ -1,5 +1,7 @@ package ru.dbotthepony.mc.otm.core.util +import net.minecraft.advancements.CriteriaTriggers +import net.minecraft.core.BlockPos import net.minecraft.core.HolderLookup import net.minecraft.core.registries.Registries import net.minecraft.nbt.CompoundTag @@ -7,15 +9,22 @@ import net.minecraft.nbt.Tag import net.minecraft.resources.ResourceKey import net.minecraft.resources.ResourceLocation import net.minecraft.server.level.ServerLevel +import net.minecraft.server.level.ServerPlayer import net.minecraft.util.RandomSource +import net.minecraft.world.Container +import net.minecraft.world.Containers +import net.minecraft.world.entity.player.Player import net.minecraft.world.item.ItemStack +import net.minecraft.world.level.block.entity.BlockEntity import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.level.storage.loot.LootParams import net.minecraft.world.level.storage.loot.LootTable import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets import net.minecraft.world.level.storage.loot.parameters.LootContextParams +import net.minecraft.world.phys.Vec3 import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity.Companion.LOOT_TABLE_KEY import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity.Companion.LOOT_TABLE_SEED_KEY +import ru.dbotthepony.mc.otm.core.getBlockEntityNow import ru.dbotthepony.mc.otm.core.otmRandom class BlockLootTableHolder(private val listener: Runnable = Runnable { }) { @@ -75,13 +84,49 @@ class BlockLootTableHolder(private val listener: Runnable = Runnable { }) { return if (lootTableSeed == null) fallback else GJRAND64RandomSource(lootTableSeed) } - fun getItems(params: LootParams, level: ServerLevel, overrideSeed: Long? = null): MutableList { - return lookup(level).getRandomItems(params, selectRandom(overrideSeed, level.otmRandom)) + fun getItems(params: LootParams, level: ServerLevel, overrideSeed: Long? = null, rounds: Int = 1): MutableList { + return lookup(level).getRandomItems(params, selectRandom(overrideSeed, level.otmRandom), rounds) } - fun getItems(params: LootParams.Builder, blockState: BlockState, overrideSeed: Long? = null): MutableList { + fun getItems(params: LootParams.Builder, blockState: BlockState, overrideSeed: Long? = null, rounds: Int = 1): MutableList { val level = params.level val create = params.withParameter(LootContextParams.BLOCK_STATE, blockState).create(LootContextParamSets.BLOCK) - return getItems(create, level, overrideSeed) + return getItems(create, level, overrideSeed, rounds) + } + + fun fill(level: ServerLevel, blockPos: BlockPos, container: Container, ply: Player? = null, blockEntity: BlockEntity? = level.getBlockEntityNow(blockPos), overrideSeed: Long? = null, dropContents: Boolean = true, rounds: Int = 1) { + if (rounds == 0) + return + else if (rounds < 0) + throw IllegalArgumentException("Negative amount of rounds: $rounds") + + val lootTable = lootTable ?: return + val server = level.server + val loot = server.reloadableRegistries().getLootTable(lootTable) + + if (ply is ServerPlayer) + CriteriaTriggers.GENERATE_LOOT.trigger(ply, lootTable) + + val params = LootParams.Builder(level) + .withParameter(LootContextParams.ORIGIN, Vec3.atCenterOf(blockPos)) + + if (ply != null) { + params + .withLuck(ply.luck) + .withParameter(LootContextParams.THIS_ENTITY, ply) + } + + if (blockEntity != null) + params.withParameter(LootContextParams.BLOCK_ENTITY, blockEntity) + + if (dropContents) + Containers.dropContents(level, blockPos, container) + + loot.fill(params.create(LootContextParamSets.CHEST), selectRandom(overrideSeed, level.otmRandom), container, rounds) + } + + fun clear() { + lootTable = null + lootTableSeed = null } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/LootTableUtils.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/LootTableUtils.kt new file mode 100644 index 000000000..ae0a0a1be --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/LootTableUtils.kt @@ -0,0 +1,132 @@ +package ru.dbotthepony.mc.otm.core.util + +import it.unimi.dsi.fastutil.ints.IntArrayList +import net.minecraft.util.Mth +import net.minecraft.util.RandomSource +import net.minecraft.world.Container +import net.minecraft.world.item.Item +import net.minecraft.world.item.ItemStack +import net.minecraft.world.level.storage.loot.LootParams +import net.minecraft.world.level.storage.loot.LootTable +import org.apache.logging.log4j.LogManager +import ru.dbotthepony.mc.otm.container.get +import ru.dbotthepony.mc.otm.container.set +import ru.dbotthepony.mc.otm.core.isNotEmpty +import ru.dbotthepony.mc.otm.core.shuffle +import java.util.stream.Collectors +import java.util.stream.IntStream +import kotlin.math.min + +private val LOGGER = LogManager.getLogger() + +private class Bucket { + val items = ArrayList() + var last = 0 + + fun add(item: ItemStack) { + if (item.isEmpty) return + + for (i in last until items.size) { + val existing = items[i] + var available = existing.maxStackSize - existing.count + + if (available > 0 && ItemStack.isSameItemSameComponents(existing, item)) { + val diff = min(available, item.count) + existing.grow(diff) + item.shrink(diff) + available -= diff + + if (available == 0 && i == last) + last++ + + if (item.isEmpty) + return + } + } + + if (item.isNotEmpty) { + items.add(item) + } + } +} + +private fun recombine(lists: Collection>): MutableList { + val result = HashMap() + + for (list in lists) + for (item in list) + result.computeIfAbsent(item.item) { Bucket() }.add(item) + + return result.values + .stream() + .flatMap { it.items.stream() } + .collect(Collectors.toCollection(::ArrayList)) +} + +fun LootTable.getRandomItems(params: LootParams, random: RandomSource, rounds: Int = 1): MutableList { + if (rounds == 0) { + return ArrayList() + } else if (rounds == 1) { + return getRandomItems(params, random) + } else { + return recombine(IntStream.range(0, rounds).mapToObj { getRandomItems(params, random) }.toList()) + } +} + +private fun shuffle(items: MutableList, emptySlotCount: Int, random: RandomSource) { + val pool = ArrayList(items) + items.clear() + + while (items.size + pool.size < emptySlotCount && pool.isNotEmpty()) { + val select = pool.removeAt(random.nextInt(pool.size)) + val maxStackSize = select.maxStackSize + + if (maxStackSize == 1 || select.count == 1) { + items.add(select) + } else { + val split = select.split(Mth.nextInt(random, 1, select.count / 2)) + + if (split.count > 1 && random.nextBoolean()) + pool.add(split) + else + items.add(split) + + if (select.count > 1 && random.nextBoolean()) + pool.add(select) + else + items.add(select) + } + } + + items.addAll(pool) + items.shuffle(random) +} + +fun LootTable.fill(params: LootParams, random: RandomSource, container: Container, rounds: Int = 1) { + val emptySlots = IntArrayList() + + for (i in 0 until container.containerSize) + if (container[i].isEmpty) + emptySlots.add(i) + + emptySlots.shuffle(random) + + if (emptySlots.isEmpty) { + LOGGER.warn("Tried to fill container with no empty slots") + return + } + + val items = getRandomItems(params, random, rounds) + shuffle(items, emptySlots.size, random) + + val slotItr = emptySlots.iterator() + val itemItr = items.iterator() + + while (slotItr.hasNext() && itemItr.hasNext()) { + container[slotItr.nextInt()] = itemItr.next() + } + + if (itemItr.hasNext()) { + LOGGER.warn("Tried to overfill a container") + } +} From 16f91343d82995246cd4c707cd0f82a12b77936f Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Sun, 23 Mar 2025 01:23:52 +0700 Subject: [PATCH 22/23] Allow to specify loot table rounds through nbt --- .../mc/otm/core/util/BlockLootTableHolder.kt | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/BlockLootTableHolder.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/BlockLootTableHolder.kt index 4c8958029..af2fca337 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/BlockLootTableHolder.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/BlockLootTableHolder.kt @@ -46,6 +46,14 @@ class BlockLootTableHolder(private val listener: Runnable = Runnable { }) { } } + var lootTableRounds: Int? = null + set(value) { + if (field != value) { + field = value + if (!ignoreListener) listener.run() + } + } + fun save(nbt: CompoundTag, registry: HolderLookup.Provider) { try { ignoreListener = true @@ -55,6 +63,9 @@ class BlockLootTableHolder(private val listener: Runnable = Runnable { }) { if (lootTableSeed != null) nbt.putLong(LOOT_TABLE_SEED_KEY, lootTableSeed!!) + + if (lootTableRounds != null) + nbt.putInt("LootRounds", lootTableRounds!!) } } finally { ignoreListener = false @@ -68,6 +79,10 @@ class BlockLootTableHolder(private val listener: Runnable = Runnable { }) { if (nbt.contains(LOOT_TABLE_KEY, Tag.TAG_STRING.toInt())) { lootTable = ResourceLocation.tryParse(nbt.getString(LOOT_TABLE_KEY))?.let { ResourceKey.create(Registries.LOOT_TABLE, it) } lootTableSeed = if (nbt.contains(LOOT_TABLE_SEED_KEY, Tag.TAG_LONG.toInt())) nbt.getLong(LOOT_TABLE_SEED_KEY) else null + lootTableRounds = if (nbt.contains("LootRounds", Tag.TAG_INT.toInt())) nbt.getInt("LootRounds") else null + + if (lootTableRounds != null && lootTableRounds!! < 0) + lootTableRounds = null } } finally { ignoreListener = false @@ -84,20 +99,20 @@ class BlockLootTableHolder(private val listener: Runnable = Runnable { }) { return if (lootTableSeed == null) fallback else GJRAND64RandomSource(lootTableSeed) } - fun getItems(params: LootParams, level: ServerLevel, overrideSeed: Long? = null, rounds: Int = 1): MutableList { - return lookup(level).getRandomItems(params, selectRandom(overrideSeed, level.otmRandom), rounds) + fun getItems(params: LootParams, level: ServerLevel, overrideSeed: Long? = null, rounds: Int? = null): MutableList { + return lookup(level).getRandomItems(params, selectRandom(overrideSeed, level.otmRandom), rounds ?: lootTableRounds ?: 1) } - fun getItems(params: LootParams.Builder, blockState: BlockState, overrideSeed: Long? = null, rounds: Int = 1): MutableList { + fun getItems(params: LootParams.Builder, blockState: BlockState, overrideSeed: Long? = null, rounds: Int? = null): MutableList { val level = params.level val create = params.withParameter(LootContextParams.BLOCK_STATE, blockState).create(LootContextParamSets.BLOCK) return getItems(create, level, overrideSeed, rounds) } - fun fill(level: ServerLevel, blockPos: BlockPos, container: Container, ply: Player? = null, blockEntity: BlockEntity? = level.getBlockEntityNow(blockPos), overrideSeed: Long? = null, dropContents: Boolean = true, rounds: Int = 1) { + fun fill(level: ServerLevel, blockPos: BlockPos, container: Container, ply: Player? = null, blockEntity: BlockEntity? = level.getBlockEntityNow(blockPos), overrideSeed: Long? = null, dropContents: Boolean = true, rounds: Int? = null) { if (rounds == 0) return - else if (rounds < 0) + else if (rounds != null && rounds < 0) throw IllegalArgumentException("Negative amount of rounds: $rounds") val lootTable = lootTable ?: return @@ -122,7 +137,7 @@ class BlockLootTableHolder(private val listener: Runnable = Runnable { }) { if (dropContents) Containers.dropContents(level, blockPos, container) - loot.fill(params.create(LootContextParamSets.CHEST), selectRandom(overrideSeed, level.otmRandom), container, rounds) + loot.fill(params.create(LootContextParamSets.CHEST), selectRandom(overrideSeed, level.otmRandom), container, rounds ?: lootTableRounds ?: 1) } fun clear() { From fdfc406ca67290bdce2d2412911ab1218029cc77 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Sun, 23 Mar 2025 01:48:33 +0700 Subject: [PATCH 23/23] Make exopack slot upgrades be consumed in stacks, to make bulk use way less annoying --- .../exopack/AbstractExopackSlotUpgradeItem.kt | 42 ++++++++++++------- .../mc/otm/item/exopack/ExopackUpgradeItem.kt | 4 +- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/item/exopack/AbstractExopackSlotUpgradeItem.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/item/exopack/AbstractExopackSlotUpgradeItem.kt index e9750cab0..dd15524c2 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/item/exopack/AbstractExopackSlotUpgradeItem.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/item/exopack/AbstractExopackSlotUpgradeItem.kt @@ -20,6 +20,9 @@ import ru.dbotthepony.mc.otm.client.minecraft import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.core.isExplosion import ru.dbotthepony.mc.otm.core.isFire +import ru.dbotthepony.mc.otm.core.isNotEmpty +import ru.dbotthepony.mc.otm.core.nextUUID +import ru.dbotthepony.mc.otm.core.otmRandom import ru.dbotthepony.mc.otm.registry.game.MItems import ru.dbotthepony.mc.otm.runIfClient import ru.dbotthepony.mc.otm.triggers.ExopackSlotsExpandedTrigger @@ -35,7 +38,7 @@ abstract class AbstractExopackSlotUpgradeItem(properties: Properties = defaultPr } override fun getUseDuration(p_41454_: ItemStack, p_344979_: LivingEntity): Int { - return 30 + return 20 } override fun appendHoverText( @@ -76,7 +79,7 @@ abstract class AbstractExopackSlotUpgradeItem(properties: Properties = defaultPr } override fun use(p_41432_: Level, player: Player, hand: InteractionHand): InteractionResultHolder { - val matteryPlayer = player.matteryPlayer ?: return super.use(p_41432_, player, hand) + val matteryPlayer = player.matteryPlayer val uuid = uuid(player.getItemInHand(hand)) @@ -98,25 +101,32 @@ abstract class AbstractExopackSlotUpgradeItem(properties: Properties = defaultPr return super.finishUsingItem(itemStack, level, player) } - if (!player.abilities.instabuild) - itemStack.shrink(1) + var allowedUses = itemStack.count - if (player is ServerPlayer) { - if (uuid != null) { - if (ServerConfig.INFINITE_EXOSUIT_UPGRADES && uuid in matteryPlayer.exopackSlotModifier) { - matteryPlayer.exopackSlotModifier[UUID.randomUUID()] = slotCount + while (allowedUses > 0) { + if (!player.abilities.instabuild) + itemStack.shrink(1) + + allowedUses-- + + if (player is ServerPlayer) { + if (uuid != null) { + if (ServerConfig.INFINITE_EXOSUIT_UPGRADES && uuid in matteryPlayer.exopackSlotModifier) { + matteryPlayer.exopackSlotModifier[level.otmRandom.nextUUID()] = slotCount + } else { + matteryPlayer.exopackSlotModifier[uuid] = slotCount + allowedUses = 0 + } } else { - matteryPlayer.exopackSlotModifier[uuid] = slotCount + matteryPlayer.exopackSlotModifier[level.otmRandom.nextUUID()] = slotCount } - } else { - matteryPlayer.exopackSlotModifier[UUID.randomUUID()] = slotCount - } - ExopackSlotsExpandedTrigger.trigger(player, slotCount, matteryPlayer.exopackContainer.containerSize) - player.displayClientMessage(TranslatableComponent("otm.exopack_upgrades.slots_upgraded", slotCount).withStyle(ChatFormatting.DARK_GREEN), false) + ExopackSlotsExpandedTrigger.trigger(player, slotCount, matteryPlayer.exopackContainer.containerSize) + player.displayClientMessage(TranslatableComponent("otm.exopack_upgrades.slots_upgraded", slotCount).withStyle(ChatFormatting.DARK_GREEN), false) - if (this === MItems.ExopackUpgrades.INVENTORY_UPGRADE_ENDER_DRAGON) { - MItems.ExopackUpgrades.ENDER_UPGRADE.finishUsingItem(ItemStack(MItems.ExopackUpgrades.INVENTORY_UPGRADE_ENDER_DRAGON), level, player) + if (this === MItems.ExopackUpgrades.INVENTORY_UPGRADE_ENDER_DRAGON) { + MItems.ExopackUpgrades.ENDER_UPGRADE.finishUsingItem(ItemStack(MItems.ExopackUpgrades.INVENTORY_UPGRADE_ENDER_DRAGON), level, player) + } } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/item/exopack/ExopackUpgradeItem.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/item/exopack/ExopackUpgradeItem.kt index faf2b52ee..1db813729 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/item/exopack/ExopackUpgradeItem.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/item/exopack/ExopackUpgradeItem.kt @@ -60,7 +60,7 @@ class ExopackUpgradeItem( } override fun use(p_41432_: Level, player: Player, hand: InteractionHand): InteractionResultHolder { - if (player.matteryPlayer?.hasExopack == true && !type.prop.get(player.matteryPlayer!!)) { + if (player.matteryPlayer.hasExopack && !type.prop.get(player.matteryPlayer)) { player.startUsingItem(hand) return InteractionResultHolder.consume(player.getItemInHand(hand)) } @@ -70,7 +70,7 @@ class ExopackUpgradeItem( override fun finishUsingItem(itemStack: ItemStack, level: Level, player: LivingEntity): ItemStack { if (player !is Player) return super.finishUsingItem(itemStack, level, player) - val mattery = player.matteryPlayer ?: return super.finishUsingItem(itemStack, level, player) + val mattery = player.matteryPlayer if (!mattery.hasExopack || type.prop.get(mattery)) { return super.finishUsingItem(itemStack, level, player)