Compare commits

...

24 Commits

Author SHA1 Message Date
e31817e482
Merge branch '1.21' into world-structures 2025-03-23 10:11:41 +07:00
fdfc406ca6
Make exopack slot upgrades be consumed in stacks, to make bulk use way less annoying 2025-03-23 01:48:33 +07:00
16f91343d8
Allow to specify loot table rounds through nbt 2025-03-23 01:23:52 +07:00
3902e60424
BlockLootTableHolder#fill 2025-03-23 01:17:26 +07:00
c778f192b2
Proof-of-concept "Small capsule" loot block 2025-03-22 23:33:28 +07:00
22ebdbb1eb
Rename beforeDroppingItems to dropItems, and change logic 2025-03-22 19:59:48 +07:00
0a9e90bec6
Make matter recycler receive random amount of matter on tick instead of determining total matter received upon job start 2025-03-22 00:49:42 +07:00
0126d4d976
Don't override nextGaussian, because JVM implementation should be generally faster 2025-03-22 00:30:03 +07:00
ab2cc33b7a
"Smart storage exchange icon" 2025-03-21 21:24:16 +07:00
bfb8f0380a
Panel debug rendering 2025-03-21 21:10:46 +07:00
06f109575d
Fix children not getting re-sorted upon visibility changes 2025-03-21 18:18:07 +07:00
a2d9f43a2e
Fix grid panel layouts, introduce column major order 2025-03-21 18:17:50 +07:00
7c028b1fa6
Im with stupid 2025-03-21 15:11:09 +07:00
57c6bbb795
Update cases where wrong random being used 2025-03-21 14:00:19 +07:00
3e92c5272d
Move panels additional types to separate file 2025-03-21 13:52:00 +07:00
f9821aa552
Use GJRAND64 in menus 2025-03-21 13:36:46 +07:00
b921658eb2
Remove randomGenerator from editable panel 2025-03-21 13:34:53 +07:00
9b27ed7fb6
Add netFloat and nextDouble helper methods to RandomSource 2025-03-21 13:33:53 +07:00
269227f6cf
Add helper methods for fast DockProperty creation 2025-03-21 12:58:13 +07:00
27834cc595
Fix wrong layout final iteration value 2025-03-21 12:23:08 +07:00
c343de6031
Parenting callbacks in panels 2025-03-21 10:45:07 +07:00
e6c9708652
Add layout modes to grid panel 2025-03-21 08:07:43 +07:00
a48aaf52ae
Add DockProperty#all variant 2025-03-21 07:19:03 +07:00
9b384f2213
Implement sizeToContents() for GridPanel 2025-03-21 07:18:42 +07:00
33 changed files with 919 additions and 222 deletions

View File

@ -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)
}
}

View File

@ -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<ItemStack> {
val entity = params.getOptionalParameter(LootContextParams.BLOCK_ENTITY)
if (entity is BreakableContainerBlockEntity)
return entity.loot.getItems(params, state)
return super.getDrops(state, params)
}
}

View File

@ -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<BlockCapability<*, *>, Any>()
private val sidedCaps = Array(RelativeSide.entries.size) {
Reference2ObjectOpenHashMap<BlockCapability<*, *>, 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() {

View File

@ -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)
}
}

View File

@ -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,24 +36,30 @@ 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
}
})
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) {
unpackLootTable(player)
}
var lootTable: ResourceKey<LootTable>? = null
var lootTableSeed: Long? = null
val loot = BlockLootTableHolder(::markDirtyFast)
fun onPlayerOpen() {
val level = level
@ -92,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)

View File

@ -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() {

View File

@ -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<LinePoint>,
width: Float,
color: RGBAColor = RGBAColor.WHITE
) {
drawLine(pose.last().pose(), points, width, color)
}
fun drawLine(
points: Array<out LinePoint>,
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<LinePoint>,
) {
drawLine(pose.last().pose(), points, width, color)
}
fun drawLine(
width: Float,
color: RGBAColor,
z: Float,
points: List<ScreenPos>,
) {
require(points.size >= 2) { "Degenerate point list: only ${points.size} defined" }
val result = ArrayList<LinePoint>(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,

View File

@ -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<LinePoint>,
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<out LinePoint>,
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)

View File

@ -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)

View File

@ -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<EnergyCounterBlockEntity> {
private val random = Random()
private val random = GJRAND64RandomSource()
override fun render(
tile: EnergyCounterBlockEntity,

View File

@ -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<T : MatteryMenu>(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<LinePoint>()
panel.render(wrap, mouseXf, mouseYf, partialTick, segments)
if (segments.isNotEmpty()) {
wrap.drawLine(0.5f, RGBAColor.GOLD, segments)
}
}
if (!panelsReversed.any { it.updateCursor0() })

View File

@ -52,7 +52,7 @@ open class DecimalHistoryChartPanel<out S : MatteryScreen<*>>(
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 }) {

View File

@ -11,94 +11,31 @@ 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.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
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()
}
}
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<out S : Screen>(
val screen: S,
parent: EditablePanel<*>?,
@ -175,18 +112,14 @@ open class EditablePanel<out S : Screen>(
}
}
val random: RandomSource by lazy {
val random: GJRAND64RandomSource by lazy {
if (screen is MatteryScreen<*>) {
screen.menu.random
} else {
RandomSource.create()
GJRAND64RandomSource()
}
}
val randomGenerator: RandomGenerator by lazy {
RandomSource2Generator(random)
}
/**
* Bigger values means lesser priority while docking, rendering and processing inputs.
*/
@ -350,11 +283,13 @@ open class EditablePanel<out S : Screen>(
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
}
}
@ -843,6 +778,12 @@ open class EditablePanel<out S : Screen>(
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)
@ -860,11 +801,15 @@ open class EditablePanel<out S : Screen>(
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()
@ -876,6 +821,8 @@ open class EditablePanel<out S : Screen>(
if (child.isVisible() != isVisible()) {
updateVisible()
}
onChildrenRemoved(child)
}
private fun sortChildren() {
@ -944,12 +891,11 @@ open class EditablePanel<out S : Screen>(
}
}
fun render(graphics: MGUIGraphics, mouseX: Float, mouseY: Float, partialTick: Float) {
fun render(graphics: MGUIGraphics, mouseX: Float, mouseY: Float, partialTick: Float, debugSegments: MutableList<LinePoint>) {
once = true
if (!isVisible()) {
if (!isVisible())
return
}
val poseStack = graphics.pose
@ -997,12 +943,19 @@ open class EditablePanel<out S : Screen>(
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 {
@ -1508,6 +1461,7 @@ open class EditablePanel<out S : Screen>(
if (old != new) {
child.visibilityChanges(new, old)
child.invalidateChildrenSorting()
}
child.updateVisible()

View File

@ -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<out S: Screen, out P : EditablePanel<S>>(
val yFloat = mouseY.toFloat()
val wrap = MGUIGraphics(graphics)
val segments = ArrayList<LinePoint>()
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)
}

View File

@ -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())
}
}

View File

@ -7,6 +7,9 @@ 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<out S : Screen>(
screen: S,
@ -20,25 +23,150 @@ open class GridPanel<out S : Screen>(
) : EditablePanel<S>(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()
}
}
var columnMajorOrder = false
set(value) {
if (field != value) {
field = value
invalidateLayout()
}
}
enum class Layout {
TOP_LEFT {
override fun get(
list: List<EditablePanel<*>>,
row: Int,
column: Int,
rows: Int,
columns: Int,
columnMajorOrder: Boolean
): EditablePanel<*>? {
if (columnMajorOrder) {
return list.getOrNull(row + column * rows)
} else {
return list.getOrNull(column + row * columns)
}
}
},
TOP_RIGHT {
override fun get(
list: List<EditablePanel<*>>,
row: Int,
column: Int,
rows: Int,
columns: Int,
columnMajorOrder: Boolean
): EditablePanel<*>? {
if (columnMajorOrder) {
return list.getOrNull(row + (columns - column - 1) * rows)
} else {
return list.getOrNull((columns - column - 1) + row * columns)
}
}
},
BOTTOM_LEFT {
override fun get(
list: List<EditablePanel<*>>,
row: Int,
column: Int,
rows: Int,
columns: Int,
columnMajorOrder: Boolean
): EditablePanel<*>? {
if (columnMajorOrder) {
return list.getOrNull((rows - row - 1) + column * rows)
} else {
return list.getOrNull(column + (rows - row - 1) * columns)
}
}
},
BOTTOM_RIGHT {
override fun get(
list: List<EditablePanel<*>>,
row: Int,
column: Int,
rows: Int,
columns: Int,
columnMajorOrder: Boolean
): EditablePanel<*>? {
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<EditablePanel<*>>, row: Int, column: Int, rows: Int, columns: Int, columnMajorOrder: Boolean): EditablePanel<*>?
}
override fun sizeToContents() {
if (visibleChildren.isEmpty()) {
// nothing to size against
return
}
visibleChildren.forEach { it.sizeToContents() }
var width = 0f
var height = 0f
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 = layout.get(children, row, column, rows, columns, columnMajorOrder) ?: continue
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 }
val children = visibleChildren.filter { it.dock == Dock.NONE }
var totalWidth = 0f
var totalHeight = 0f
@ -48,7 +176,7 @@ open class GridPanel<out S : Screen>(
var width = 0f
for (column in 0 until columns) {
val child = children.maybe() ?: break
val child = layout.get(children, row, column, rows, columns, columnMajorOrder) ?: continue
width += child.dockedWidth
maxHeight = maxHeight.coerceAtLeast(child.dockedHeight)
}
@ -59,7 +187,6 @@ open class GridPanel<out S : Screen>(
val alignX = gravity.repositionX(width, totalWidth)
val alignY = gravity.repositionY(height, totalHeight)
children = visibleChildren.iterator().filter { it.dock == Dock.NONE }
totalWidth = 0f
totalHeight = 0f
@ -69,7 +196,7 @@ open class GridPanel<out S : Screen>(
var width = 0f
for (column in 0 until columns) {
val child = children.maybe() ?: break
val child = layout.get(children, row, column, rows, columns, columnMajorOrder) ?: continue
child.x = alignX + width
child.y = alignY + totalHeight
width += child.dockedWidth

View File

@ -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.*
@ -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)
}
}
}

View File

@ -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 : IntList> 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 <T, L : MutableList<T>> L.shuffle(random: RandomSource): L {
Util.shuffle(this, random)
return this
@ -662,3 +672,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)
}

View File

@ -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<V : Comparable<V>>(var center: V) {
}
private fun <V : Comparable<V>> Iterable<V>.clusterize(
random: RandomGenerator,
random: RandomSource,
initialClusters: Int = 1,
zeroBound: Boolean = false,
identity: V,
@ -202,7 +203,7 @@ private fun <V : Comparable<V>> Iterable<V>.clusterize(
// TODO: could use some tweaking
private val DECIMAL_ERROR_TOLERANCE = Decimal("0.02")
fun Iterable<Decimal>.clusterize(random: RandomGenerator, clusters: Int? = null, zeroBound: Boolean = false): List<Cluster<Decimal>> {
fun Iterable<Decimal>.clusterize(random: RandomSource, clusters: Int? = null, zeroBound: Boolean = false): List<Cluster<Decimal>> {
return clusterize(random, clusters ?: 1, zeroBound, Decimal.ZERO, Decimal::plus, Decimal::minus, Decimal::div, Decimal::absoluteValue) { min, max, error ->
if (clusters != null)
false

View File

@ -0,0 +1,147 @@
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
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 { }) {
private var ignoreListener = false
var lootTable: ResourceKey<LootTable>? = 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()
}
}
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
if (lootTable != null) {
nbt.putString(LOOT_TABLE_KEY, lootTable!!.location().toString())
if (lootTableSeed != null)
nbt.putLong(LOOT_TABLE_SEED_KEY, lootTableSeed!!)
if (lootTableRounds != null)
nbt.putInt("LootRounds", lootTableRounds!!)
}
} 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
lootTableRounds = if (nbt.contains("LootRounds", Tag.TAG_INT.toInt())) nbt.getInt("LootRounds") else null
if (lootTableRounds != null && lootTableRounds!! < 0)
lootTableRounds = 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, rounds: Int? = null): MutableList<ItemStack> {
return lookup(level).getRandomItems(params, selectRandom(overrideSeed, level.otmRandom), rounds ?: lootTableRounds ?: 1)
}
fun getItems(params: LootParams.Builder, blockState: BlockState, overrideSeed: Long? = null, rounds: Int? = null): MutableList<ItemStack> {
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? = null) {
if (rounds == 0)
return
else if (rounds != null && 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 ?: lootTableRounds ?: 1)
}
fun clear() {
lootTable = null
lootTableSeed = null
}
}

View File

@ -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

View File

@ -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 {

View File

@ -26,5 +26,7 @@ interface IRandomSourceGenerator : RandomSource, RandomGenerator {
return super.nextDouble()
}
override fun nextGaussian(): Double
override fun nextGaussian(): Double {
return super.nextGaussian()
}
}

View File

@ -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<ItemStack>()
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<Collection<ItemStack>>): MutableList<ItemStack> {
val result = HashMap<Item, Bucket>()
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<ItemStack> {
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<ItemStack>, emptySlotCount: Int, random: RandomSource) {
val pool = ArrayList<ItemStack>(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")
}
}

View File

@ -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<ItemStack> {
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)
}
}
}

View File

@ -60,7 +60,7 @@ class ExopackUpgradeItem(
}
override fun use(p_41432_: Level, player: Player, hand: InteractionHand): InteractionResultHolder<ItemStack> {
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)

View File

@ -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<InventorySlot>()
private val _playerHotbarSlots = ArrayList<InventorySlot>()

View File

@ -355,6 +355,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 {

View File

@ -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<HoloSignBlockEntity> by registry.register(MNames.HOLO_SIGN) { BlockEntityType.Builder.of(::HoloSignBlockEntity, MBlocks.HOLO_SIGN).build(null) }
val BREAKABLE: BlockEntityType<BreakableContainerBlockEntity> by registry.register("breakable") {
val blocks = ArrayList<Block>()
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)

View File

@ -38,6 +38,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
@ -455,6 +456,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)
}

View File

@ -692,6 +692,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)
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB