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.nbt.NbtOps
import net.minecraft.network.chat.Component import net.minecraft.network.chat.Component
import net.minecraft.network.chat.ComponentSerialization import net.minecraft.network.chat.ComponentSerialization
import net.minecraft.server.level.ServerLevel
import net.minecraft.util.RandomSource import net.minecraft.util.RandomSource
import net.minecraft.world.Containers import net.minecraft.world.Containers
import net.minecraft.world.InteractionResult import net.minecraft.world.InteractionResult
@ -259,15 +260,11 @@ open class MatteryBlock(properties: Properties = DEFAULT_PROPERTIES) : Block(pro
newBlockState: BlockState, newBlockState: BlockState,
movedByPiston: Boolean movedByPiston: Boolean
) { ) {
if (!oldBlockState.`is`(newBlockState.block) && !level.isClientSide) { if (!oldBlockState.`is`(newBlockState.block) && level is ServerLevel) {
val blockentity = level.getBlockEntity(blockPos) val blockentity = level.getBlockEntity(blockPos)
if (blockentity is MatteryBlockEntity) { if (blockentity is MatteryBlockEntity) {
blockentity.beforeDroppingItems(oldBlockState, level, blockPos, newBlockState, movedByPiston) blockentity.dropItems(oldBlockState, level, blockPos, newBlockState, movedByPiston)
for (container in blockentity.droppableContainers)
Containers.dropContents(level, blockPos, container)
level.updateNeighbourForOutputSignal(blockPos, this) 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.ServerLevel
import net.minecraft.server.level.ServerPlayer import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.Container import net.minecraft.world.Container
import net.minecraft.world.Containers
import net.minecraft.world.entity.player.Player import net.minecraft.world.entity.player.Player
import net.minecraft.world.level.ChunkPos import net.minecraft.world.level.ChunkPos
import net.minecraft.world.level.Level 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 * 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 sidelessCaps = Reference2ObjectOpenHashMap<BlockCapability<*, *>, Any>()
private val sidedCaps = Array(RelativeSide.entries.size) { private val sidedCaps = Array(RelativeSide.entries.size) {
Reference2ObjectOpenHashMap<BlockCapability<*, *>, ControllableCapability<*>>() Reference2ObjectOpenHashMap<BlockCapability<*, *>, ControllableCapability<*>>()
@ -121,7 +122,11 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc
_neighbourChangeListeners.add(listener) _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 beforeDestroyedByPlayer(level: Level, blockPos: BlockPos, blockState: BlockState, player: Player) {}
open fun tick() { 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 package ru.dbotthepony.mc.otm.block.entity.decorative
import net.minecraft.advancements.CriteriaTriggers
import net.minecraft.core.BlockPos import net.minecraft.core.BlockPos
import net.minecraft.core.HolderLookup import net.minecraft.core.HolderLookup
import net.minecraft.core.registries.Registries
import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.Tag
import net.minecraft.network.chat.Component 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.ServerLevel
import net.minecraft.server.level.ServerPlayer
import net.minecraft.sounds.SoundSource import net.minecraft.sounds.SoundSource
import net.minecraft.world.Containers
import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu 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.Block
import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.gameevent.GameEvent 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 net.neoforged.neoforge.capabilities.Capabilities
import ru.dbotthepony.mc.otm.block.decorative.CargoCrateBlock import ru.dbotthepony.mc.otm.block.decorative.CargoCrateBlock
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity 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.HandlerFilter
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.otmRandom 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.menu.decorative.CargoCrateMenu
import ru.dbotthepony.mc.otm.registry.game.MBlockEntities import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
import ru.dbotthepony.mc.otm.registry.game.MSoundEvents import ru.dbotthepony.mc.otm.registry.game.MSoundEvents
@ -47,24 +36,30 @@ class CargoCrateBlockEntity(
val handler = container.handler(object : HandlerFilter { val handler = container.handler(object : HandlerFilter {
override fun canInsert(slot: Int, stack: ItemStack): Boolean { 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 { 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() unpackLootTable()
super.dropItems(oldBlockState, level, blockPos, newBlockState, movedByPiston)
} }
override fun beforeDestroyedByPlayer(level: Level, blockPos: BlockPos, blockState: BlockState, player: Player) { override fun beforeDestroyedByPlayer(level: Level, blockPos: BlockPos, blockState: BlockState, player: Player) {
unpackLootTable(player) unpackLootTable(player)
} }
var lootTable: ResourceKey<LootTable>? = null val loot = BlockLootTableHolder(::markDirtyFast)
var lootTableSeed: Long? = null
fun onPlayerOpen() { fun onPlayerOpen() {
val level = level val level = level
@ -92,52 +87,24 @@ class CargoCrateBlockEntity(
override fun saveLevel(nbt: CompoundTag, registry: HolderLookup.Provider) { override fun saveLevel(nbt: CompoundTag, registry: HolderLookup.Provider) {
super.saveLevel(nbt, registry) super.saveLevel(nbt, registry)
loot.save(nbt, registry)
if (lootTable != null) {
nbt.putString(LOOT_TABLE_KEY, lootTable!!.location().toString())
nbt.putLong(LOOT_TABLE_SEED_KEY, lootTableSeed ?: 0L)
}
} }
override fun loadAdditional(nbt: CompoundTag, registry: HolderLookup.Provider) { override fun loadAdditional(nbt: CompoundTag, registry: HolderLookup.Provider) {
super.loadAdditional(nbt, registry) super.loadAdditional(nbt, registry)
loot.load(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
}
} }
fun unpackLootTable(ply: Player? = null) { private fun unpackLootTable(ply: Player? = null) {
val lootTable = lootTable ?: return loot.fill(level as? ServerLevel ?: return, blockPos, container, ply = ply, blockEntity = this)
val lootTableSeed = lootTableSeed ?: 0L loot.clear()
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)
} }
override val defaultDisplayName: Component override val defaultDisplayName: Component
get() = MACHINE_NAME get() = MACHINE_NAME
override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu? { override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu? {
if (lootTable != null && ply.isSpectator) if (loot.lootTable != null && ply.isSpectator)
return null return null
unpackLootTable(ply) unpackLootTable(ply)

View File

@ -117,7 +117,7 @@ class MatterRecyclerBlockEntity(blockPos: BlockPos, blockState: BlockState)
stack.shrink(1) stack.shrink(1)
container.setChanged(0) container.setChanged(0)
val actualMatter = dustMatter.matter * (0.4 + level!!.otmRandom.nextDouble() * 0.6) val actualMatter = dustMatter.matter
return JobContainer.success( return JobContainer.success(
RecyclerJob( RecyclerJob(
@ -134,10 +134,11 @@ class MatterRecyclerBlockEntity(blockPos: BlockPos, blockState: BlockState)
if (toReceive.isZero) if (toReceive.isZero)
return status.success() return status.success()
else if (matter.receiveMatter(toReceive, true) != toReceive)
return status.noMatter()
val received = matter.receiveMatter(toReceive, false) matter.receiveMatter(toReceive * 0.4 + level!!.otmRandom.nextDouble() * 0.6, false)
status.scale(received / toReceive) job.totalMatter -= toReceive
job.totalMatter -= received
} }
override fun tick() { override fun tick() {

View File

@ -14,6 +14,8 @@ import net.minecraft.world.inventory.tooltip.TooltipComponent
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import ru.dbotthepony.mc.otm.client.minecraft import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.kommons.math.RGBAColor 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.PI
import kotlin.math.roundToInt import kotlin.math.roundToInt
@ -45,6 +47,73 @@ class MGUIGraphics(val parent: GuiGraphics) {
drawLine(pose.last().pose(), startX, startY, endX, endY, width, z, color) 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( fun renderRect(
x: Float, x: Float,
y: Float, y: Float,

View File

@ -294,6 +294,65 @@ fun renderColoredSphere(pose: PoseStack, radius: Float, color: RGBAColor = RGBAC
BufferUploader.drawWithShader(builder.buildOrThrow()) 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( fun drawLine(
matrix: Matrix4f, matrix: Matrix4f,
startX: Float, startX: Float,
@ -312,46 +371,46 @@ fun drawLine(
RenderSystem.depthFunc(GL_ALWAYS) RenderSystem.depthFunc(GL_ALWAYS)
val builder = tesselator.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR) 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) fun drawLine(
val angle = acos((endX - startX) / length) matrix: Matrix4f,
points: Iterable<LinePoint>,
width: Float,
color: RGBAColor = RGBAColor.WHITE
) {
val itr = points.iterator()
val cos = cos(angle) if (!itr.hasNext()) {
val sin = sin(angle) 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 builder = tesselator.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR)
val y2 = width
val x3 = length for ((startX, startY, endX, endY, z) in itr)
val y3 = -width uploadLineSegment(builder, matrix, startX, startY, endX, endY, width, z, color)
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)
BufferUploader.drawWithShader(builder.buildOrThrow()) 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) { data class ScissorRect(val xStart: Int, val yStart: Int, val xEnd: Int, val yEnd: Int, val lock: Boolean = false) {
val width: Int val width: Int
get() = (xEnd - xStart).coerceAtLeast(0) get() = (xEnd - xStart).coerceAtLeast(0)

View File

@ -30,6 +30,7 @@ object Widgets18 {
val RESTOCK_FROM_STORAGE = storageGrid.next() val RESTOCK_FROM_STORAGE = storageGrid.next()
val RESTOCK_WITH_MOVE_TO_STORAGE = storageGrid.next() val RESTOCK_WITH_MOVE_TO_STORAGE = storageGrid.next()
val RESTOCK_WITH_MOVE_FROM_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) 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.math.clusterize
import ru.dbotthepony.mc.otm.core.util.formatPower import ru.dbotthepony.mc.otm.core.util.formatPower
import ru.dbotthepony.mc.otm.core.math.times import ru.dbotthepony.mc.otm.core.math.times
import ru.dbotthepony.mc.otm.core.util.GJRAND64RandomSource
import java.util.Random import java.util.Random
import kotlin.math.PI import kotlin.math.PI
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
class EnergyCounterRenderer(private val context: BlockEntityRendererProvider.Context) : BlockEntityRenderer<EnergyCounterBlockEntity> { class EnergyCounterRenderer(private val context: BlockEntityRendererProvider.Context) : BlockEntityRenderer<EnergyCounterBlockEntity> {
private val random = Random() private val random = GJRAND64RandomSource()
override fun render( override fun render(
tile: EnergyCounterBlockEntity, tile: EnergyCounterBlockEntity,

View File

@ -17,9 +17,11 @@ import net.neoforged.neoforge.common.NeoForge
import org.lwjgl.opengl.GL11 import org.lwjgl.opengl.GL11
import ru.dbotthepony.kommons.util.getValue import ru.dbotthepony.kommons.util.getValue
import ru.dbotthepony.kommons.util.setValue import ru.dbotthepony.kommons.util.setValue
import ru.dbotthepony.kommons.math.RGBAColor
import ru.dbotthepony.mc.otm.player.matteryPlayer import ru.dbotthepony.mc.otm.player.matteryPlayer
import ru.dbotthepony.mc.otm.client.render.MGUIGraphics import ru.dbotthepony.mc.otm.client.render.MGUIGraphics
import ru.dbotthepony.mc.otm.client.moveMousePosScaled 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.WidgetLocation
import ru.dbotthepony.mc.otm.client.render.Widgets18 import ru.dbotthepony.mc.otm.client.render.Widgets18
import ru.dbotthepony.mc.otm.client.render.translation 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 ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget
import java.util.* import java.util.*
import java.util.concurrent.CopyOnWriteArrayList import java.util.concurrent.CopyOnWriteArrayList
import kotlin.collections.ArrayList
import kotlin.collections.List import kotlin.collections.List
import kotlin.collections.MutableSet import kotlin.collections.MutableSet
import kotlin.collections.isNotEmpty import kotlin.collections.isNotEmpty
@ -690,7 +693,13 @@ abstract class MatteryScreen<T : MatteryMenu>(menu: T, inventory: Inventory, tit
for (panel in panelsReversed) { for (panel in panelsReversed) {
RenderSystem.depthFunc(GL11.GL_ALWAYS) RenderSystem.depthFunc(GL11.GL_ALWAYS)
RenderSystem.setShaderColor(1f, 1f, 1f, 1f) 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() }) if (!panelsReversed.any { it.updateCursor0() })

View File

@ -52,7 +52,7 @@ open class DecimalHistoryChartPanel<out S : MatteryScreen<*>>(
map[1f] = formatText(maximum) map[1f] = formatText(maximum)
for (cluster in chart.asIterable().clusterize(randomGenerator)) { for (cluster in chart.asIterable().clusterize(random)) {
val perc = (cluster.center / maximum).toFloat() val perc = (cluster.center / maximum).toFloat()
if (map.keys.none { (it - perc).absoluteValue < 0.08f }) { 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.FocusNavigationEvent
import net.minecraft.client.gui.navigation.ScreenRectangle import net.minecraft.client.gui.navigation.ScreenRectangle
import net.minecraft.client.gui.screens.Screen import net.minecraft.client.gui.screens.Screen
import net.minecraft.client.renderer.Rect2i
import net.minecraft.network.chat.Component import net.minecraft.network.chat.Component
import net.minecraft.util.RandomSource
import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.LogManager
import ru.dbotthepony.kommons.math.RGBAColor
import ru.dbotthepony.mc.otm.SystemTime import ru.dbotthepony.mc.otm.SystemTime
import ru.dbotthepony.mc.otm.client.CursorType import ru.dbotthepony.mc.otm.client.CursorType
import ru.dbotthepony.mc.otm.client.render.MGUIGraphics import ru.dbotthepony.mc.otm.client.render.MGUIGraphics
import ru.dbotthepony.mc.otm.client.minecraft import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.client.moveMousePosScaled import ru.dbotthepony.mc.otm.client.moveMousePosScaled
import ru.dbotthepony.mc.otm.client.render.LinePoint
import ru.dbotthepony.mc.otm.client.render.RenderGravity import ru.dbotthepony.mc.otm.client.render.RenderGravity
import ru.dbotthepony.mc.otm.client.render.currentScissorRect import ru.dbotthepony.mc.otm.client.render.currentScissorRect
import ru.dbotthepony.mc.otm.client.render.popScissorRect import ru.dbotthepony.mc.otm.client.render.popScissorRect
import ru.dbotthepony.mc.otm.client.render.pushScissorRect import ru.dbotthepony.mc.otm.client.render.pushScissorRect
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
import ru.dbotthepony.mc.otm.client.screen.panels.input.QueryUserPanel 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.concatIterators
import ru.dbotthepony.mc.otm.core.collect.flatMap import ru.dbotthepony.mc.otm.core.collect.flatMap
import ru.dbotthepony.mc.otm.core.util.GJRAND64RandomSource
import java.util.* import java.util.*
import java.util.concurrent.CopyOnWriteArrayList import java.util.concurrent.CopyOnWriteArrayList
import java.util.function.Predicate import java.util.function.Predicate
import java.util.random.RandomGenerator
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
import kotlin.math.max import kotlin.math.max
import kotlin.math.roundToInt 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>( open class EditablePanel<out S : Screen>(
val screen: S, val screen: S,
parent: EditablePanel<*>?, 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<*>) { if (screen is MatteryScreen<*>) {
screen.menu.random screen.menu.random
} else { } else {
RandomSource.create() GJRAND64RandomSource()
} }
} }
val randomGenerator: RandomGenerator by lazy {
RandomSource2Generator(random)
}
/** /**
* Bigger values means lesser priority while docking, rendering and processing inputs. * 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) { if (visibleChildrenParent?.contains(this) == false) {
visibleChildrenParent.add(this) visibleChildrenParent.add(this)
parent?.invalidateChildrenSorting()
parent?.layoutInvalidated = true parent?.layoutInvalidated = true
} }
} else { } else {
if (visibleChildrenParent?.contains(this) == true) { if (visibleChildrenParent?.contains(this) == true) {
visibleChildrenParent.remove(this) visibleChildrenParent.remove(this)
parent?.invalidateChildrenSorting()
parent?.layoutInvalidated = true parent?.layoutInvalidated = true
} }
} }
@ -843,6 +778,12 @@ open class EditablePanel<out S : Screen>(
childrenSortingInvalidated = true 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<*>) { private fun onParent(child: EditablePanel<*>) {
if (childrenInternal.contains(child)) throw IllegalStateException("Already containing $child") if (childrenInternal.contains(child)) throw IllegalStateException("Already containing $child")
childrenInternal.add(child) childrenInternal.add(child)
@ -860,11 +801,15 @@ open class EditablePanel<out S : Screen>(
else else
updateVisibility = true updateVisibility = true
} }
onChildrenAdded(child)
child.onParented(this)
} }
private fun onUnParent(child: EditablePanel<*>) { private fun onUnParent(child: EditablePanel<*>) {
val indexOf = childrenInternal.indexOf(child) val indexOf = childrenInternal.indexOf(child)
if (indexOf == -1) throw IllegalStateException("Already not containing $child") if (indexOf == -1) throw IllegalStateException("Already not containing $child")
child.onUnParented(this)
childrenInternal.removeAt(indexOf) childrenInternal.removeAt(indexOf)
invalidateChildrenSorting() invalidateChildrenSorting()
@ -876,6 +821,8 @@ open class EditablePanel<out S : Screen>(
if (child.isVisible() != isVisible()) { if (child.isVisible() != isVisible()) {
updateVisible() updateVisible()
} }
onChildrenRemoved(child)
} }
private fun sortChildren() { 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 once = true
if (!isVisible()) { if (!isVisible())
return return
}
val poseStack = graphics.pose val poseStack = graphics.pose
@ -997,12 +943,19 @@ open class EditablePanel<out S : Screen>(
child.absoluteX = absoluteX + child.x + xOffset child.absoluteX = absoluteX + child.x + xOffset
child.absoluteY = absoluteY + child.y + yOffset child.absoluteY = absoluteY + child.y + yOffset
child.render(graphics, mouseX, mouseY, partialTick) child.render(graphics, mouseX, mouseY, partialTick, debugSegments)
} }
if (scissor) { if (scissor) {
popScissorRect() 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 { fun updateCursor0(): Boolean {
@ -1508,6 +1461,7 @@ open class EditablePanel<out S : Screen>(
if (old != new) { if (old != new) {
child.visibilityChanges(new, old) child.visibilityChanges(new, old)
child.invalidateChildrenSorting()
} }
child.updateVisible() 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.Renderable
import net.minecraft.client.gui.components.events.GuiEventListener import net.minecraft.client.gui.components.events.GuiEventListener
import net.minecraft.client.gui.screens.Screen import net.minecraft.client.gui.screens.Screen
import ru.dbotthepony.kommons.math.RGBAColor
import ru.dbotthepony.mc.otm.client.render.LinePoint
import ru.dbotthepony.mc.otm.client.render.MGUIGraphics import ru.dbotthepony.mc.otm.client.render.MGUIGraphics
// before 1.19.3 Renderable was Widget // 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 yFloat = mouseY.toFloat()
val wrap = MGUIGraphics(graphics) val wrap = MGUIGraphics(graphics)
val segments = ArrayList<LinePoint>()
panel.tickHovered0(xFloat, yFloat, false) panel.tickHovered0(xFloat, yFloat, false)
panel.tickHovered1() panel.tickHovered1()
panel.tickHovered2() 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) 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.client.screen.panels.slot.AbstractSlotPanel
import ru.dbotthepony.mc.otm.core.collect.filter import ru.dbotthepony.mc.otm.core.collect.filter
import ru.dbotthepony.mc.otm.core.collect.maybe 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>( open class GridPanel<out S : Screen>(
screen: S, screen: S,
@ -20,25 +23,150 @@ open class GridPanel<out S : Screen>(
) : EditablePanel<S>(screen, parent, x, y, width, height) { ) : EditablePanel<S>(screen, parent, x, y, width, height) {
var columns: Int = columns var columns: Int = columns
set(value) { set(value) {
field = value if (field != value) {
invalidateLayout() field = value
invalidateLayout()
}
} }
var rows: Int = rows var rows: Int = rows
set(value) { set(value) {
field = value if (field != value) {
invalidateLayout() field = value
invalidateLayout()
}
} }
var gravity: RenderGravity = RenderGravity.CENTER_CENTER var gravity: RenderGravity = RenderGravity.CENTER_CENTER
set(value) { set(value) {
field = value if (field != value) {
invalidateLayout() 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() { override fun performLayout() {
super.performLayout() super.performLayout()
var children = visibleChildren.iterator().filter { it.dock == Dock.NONE } val children = visibleChildren.filter { it.dock == Dock.NONE }
var totalWidth = 0f var totalWidth = 0f
var totalHeight = 0f var totalHeight = 0f
@ -48,7 +176,7 @@ open class GridPanel<out S : Screen>(
var width = 0f var width = 0f
for (column in 0 until columns) { 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 width += child.dockedWidth
maxHeight = maxHeight.coerceAtLeast(child.dockedHeight) maxHeight = maxHeight.coerceAtLeast(child.dockedHeight)
} }
@ -59,7 +187,6 @@ open class GridPanel<out S : Screen>(
val alignX = gravity.repositionX(width, totalWidth) val alignX = gravity.repositionX(width, totalWidth)
val alignY = gravity.repositionY(height, totalHeight) val alignY = gravity.repositionY(height, totalHeight)
children = visibleChildren.iterator().filter { it.dock == Dock.NONE }
totalWidth = 0f totalWidth = 0f
totalHeight = 0f totalHeight = 0f
@ -69,7 +196,7 @@ open class GridPanel<out S : Screen>(
var width = 0f var width = 0f
for (column in 0 until columns) { 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.x = alignX + width
child.y = alignY + totalHeight child.y = alignY + totalHeight
width += child.dockedWidth width += child.dockedWidth

View File

@ -7,6 +7,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
import net.minecraft.ChatFormatting import net.minecraft.ChatFormatting
import net.minecraft.client.Minecraft import net.minecraft.client.Minecraft
import net.minecraft.network.chat.Component import net.minecraft.network.chat.Component
import net.minecraft.util.RandomSource
import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import net.neoforged.neoforge.network.PacketDistributor 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.kommons.math.RGBAColor
import ru.dbotthepony.mc.otm.player.matteryPlayer import ru.dbotthepony.mc.otm.player.matteryPlayer
import ru.dbotthepony.mc.otm.client.screen.panels.button.BooleanButtonPanel 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.menu.tech.AndroidStationMenu
import ru.dbotthepony.mc.otm.network.AndroidResearchRequestPacket import ru.dbotthepony.mc.otm.network.AndroidResearchRequestPacket
import java.util.* import java.util.*
@ -540,14 +542,14 @@ class AndroidStationScreen(p_97741_: AndroidStationMenu, p_97742_: Inventory, p_
if (isPreview && !layoutInvalidated) { if (isPreview && !layoutInvalidated) {
if (firstTick) { 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) { if (!status) {
scroller = PreviewScrollers.entries.let { it[random.nextInt(it.size)] } 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.JsonElement
import com.google.gson.JsonObject import com.google.gson.JsonObject
import com.google.gson.JsonPrimitive import com.google.gson.JsonPrimitive
import it.unimi.dsi.fastutil.ints.IntList
import it.unimi.dsi.fastutil.objects.ObjectComparators import it.unimi.dsi.fastutil.objects.ObjectComparators
import net.minecraft.Util import net.minecraft.Util
import net.minecraft.core.BlockPos import net.minecraft.core.BlockPos
@ -199,6 +200,15 @@ fun IntArray.shuffle(random: RandomSource): IntArray {
return this 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 { fun <T, L : MutableList<T>> L.shuffle(random: RandomSource): L {
Util.shuffle(this, random) Util.shuffle(this, random)
return this return this
@ -662,3 +672,21 @@ fun RandomSource.nextNormalDoubles(stddev: Double, mean: Double): DoublePair {
fun RandomSource.nextNormalDouble(stddev: Double, mean: Double): Double { fun RandomSource.nextNormalDouble(stddev: Double, mean: Double): Double {
return nextGaussian() * stddev + mean 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 package ru.dbotthepony.mc.otm.core.math
import it.unimi.dsi.fastutil.objects.ObjectArrayList import it.unimi.dsi.fastutil.objects.ObjectArrayList
import net.minecraft.util.RandomSource
import ru.dbotthepony.mc.otm.core.random import ru.dbotthepony.mc.otm.core.random
import java.util.random.RandomGenerator import java.util.random.RandomGenerator
import kotlin.math.min 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( private fun <V : Comparable<V>> Iterable<V>.clusterize(
random: RandomGenerator, random: RandomSource,
initialClusters: Int = 1, initialClusters: Int = 1,
zeroBound: Boolean = false, zeroBound: Boolean = false,
identity: V, identity: V,
@ -202,7 +203,7 @@ private fun <V : Comparable<V>> Iterable<V>.clusterize(
// TODO: could use some tweaking // TODO: could use some tweaking
private val DECIMAL_ERROR_TOLERANCE = Decimal("0.02") 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 -> return clusterize(random, clusters ?: 1, zeroBound, Decimal.ZERO, Decimal::plus, Decimal::minus, Decimal::div, Decimal::absoluteValue) { min, max, error ->
if (clusters != null) if (clusters != null)
false 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.network.chat.MutableComponent
import net.minecraft.world.inventory.tooltip.TooltipComponent import net.minecraft.world.inventory.tooltip.TooltipComponent
import ru.dbotthepony.kommons.math.RGBAColor 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.ChartLevelLabels
import ru.dbotthepony.mc.otm.client.render.ChartTooltipElement import ru.dbotthepony.mc.otm.client.render.ChartTooltipElement
import ru.dbotthepony.mc.otm.config.ClientConfig 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[0f] = (-maxTransferred).formatSiComponent(suffix, decimals, formatAsReadable = verbose, bias = bias)
labelNames[1f] = maxReceived.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) { 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 val perc = (cluster.center / maxTransferred).toFloat() * transferredMult
if (labelNames.keys.none { (it - perc).absoluteValue < 0.08f }) if (labelNames.keys.none { (it - perc).absoluteValue < 0.08f })
@ -465,7 +464,7 @@ private fun formatHistoryChart(
} }
if (maxReceived.isNotZero && receivedMult > 0.2f) { 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 val perc = zero + (cluster.center / maxReceived).toFloat() * receivedMult
if (labelNames.keys.none { (it - perc).absoluteValue < 0.08f }) 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) { for (cluster in clusters) {
val perc: Float 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.Mth
import net.minecraft.util.RandomSource 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.PositionalRandomFactory
import net.minecraft.world.level.levelgen.RandomSupport import net.minecraft.world.level.levelgen.RandomSupport
import ru.dbotthepony.kommons.random.GJRAND64Random import ru.dbotthepony.kommons.random.GJRAND64Random
import java.lang.StringBuilder import java.lang.StringBuilder
class GJRAND64RandomSource : GJRAND64Random, IRandomSourceGenerator { class GJRAND64RandomSource : GJRAND64Random, IRandomSourceGenerator {
private val gaussian = MarsagliaPolarGaussian(this)
constructor() : super(RandomSupport.generateUniqueSeed(), RandomSupport.generateUniqueSeed()) constructor() : super(RandomSupport.generateUniqueSeed(), RandomSupport.generateUniqueSeed())
constructor(seed: Long) : super(seed) constructor(seed: Long) : super(seed)
constructor(seed0: Long, seed1: Long) : super(seed0, seed1) constructor(seed0: Long, seed1: Long) : super(seed0, seed1)
@ -19,10 +16,6 @@ class GJRAND64RandomSource : GJRAND64Random, IRandomSourceGenerator {
return nextLong().ushr(32).toInt() return nextLong().ushr(32).toInt()
} }
override fun nextGaussian(): Double {
return gaussian.nextGaussian()
}
override fun fork(): RandomSource { override fun fork(): RandomSource {
return GJRAND64RandomSource(nextLong(), nextLong()) return GJRAND64RandomSource(nextLong(), nextLong())
} }
@ -33,7 +26,6 @@ class GJRAND64RandomSource : GJRAND64Random, IRandomSourceGenerator {
override fun setSeed(seed: Long) { override fun setSeed(seed: Long) {
reinitialize(seed) reinitialize(seed)
gaussian.reset()
} }
class Positional(private val seed0: Long, private val seed1: Long) : PositionalRandomFactory { class Positional(private val seed0: Long, private val seed1: Long) : PositionalRandomFactory {

View File

@ -26,5 +26,7 @@ interface IRandomSourceGenerator : RandomSource, RandomGenerator {
return super.nextDouble() 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.TranslatableComponent
import ru.dbotthepony.mc.otm.core.isExplosion import ru.dbotthepony.mc.otm.core.isExplosion
import ru.dbotthepony.mc.otm.core.isFire 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.registry.game.MItems
import ru.dbotthepony.mc.otm.runIfClient import ru.dbotthepony.mc.otm.runIfClient
import ru.dbotthepony.mc.otm.triggers.ExopackSlotsExpandedTrigger 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 { override fun getUseDuration(p_41454_: ItemStack, p_344979_: LivingEntity): Int {
return 30 return 20
} }
override fun appendHoverText( 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> { 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)) val uuid = uuid(player.getItemInHand(hand))
@ -98,25 +101,32 @@ abstract class AbstractExopackSlotUpgradeItem(properties: Properties = defaultPr
return super.finishUsingItem(itemStack, level, player) return super.finishUsingItem(itemStack, level, player)
} }
if (!player.abilities.instabuild) var allowedUses = itemStack.count
itemStack.shrink(1)
if (player is ServerPlayer) { while (allowedUses > 0) {
if (uuid != null) { if (!player.abilities.instabuild)
if (ServerConfig.INFINITE_EXOSUIT_UPGRADES && uuid in matteryPlayer.exopackSlotModifier) { itemStack.shrink(1)
matteryPlayer.exopackSlotModifier[UUID.randomUUID()] = slotCount
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 { } else {
matteryPlayer.exopackSlotModifier[uuid] = slotCount matteryPlayer.exopackSlotModifier[level.otmRandom.nextUUID()] = slotCount
} }
} else {
matteryPlayer.exopackSlotModifier[UUID.randomUUID()] = slotCount
}
ExopackSlotsExpandedTrigger.trigger(player, slotCount, matteryPlayer.exopackContainer.containerSize) ExopackSlotsExpandedTrigger.trigger(player, slotCount, matteryPlayer.exopackContainer.containerSize)
player.displayClientMessage(TranslatableComponent("otm.exopack_upgrades.slots_upgraded", slotCount).withStyle(ChatFormatting.DARK_GREEN), false) player.displayClientMessage(TranslatableComponent("otm.exopack_upgrades.slots_upgraded", slotCount).withStyle(ChatFormatting.DARK_GREEN), false)
if (this === MItems.ExopackUpgrades.INVENTORY_UPGRADE_ENDER_DRAGON) { if (this === MItems.ExopackUpgrades.INVENTORY_UPGRADE_ENDER_DRAGON) {
MItems.ExopackUpgrades.ENDER_UPGRADE.finishUsingItem(ItemStack(MItems.ExopackUpgrades.INVENTORY_UPGRADE_ENDER_DRAGON), level, player) 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> { 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) player.startUsingItem(hand)
return InteractionResultHolder.consume(player.getItemInHand(hand)) return InteractionResultHolder.consume(player.getItemInHand(hand))
} }
@ -70,7 +70,7 @@ class ExopackUpgradeItem(
override fun finishUsingItem(itemStack: ItemStack, level: Level, player: LivingEntity): ItemStack { override fun finishUsingItem(itemStack: ItemStack, level: Level, player: LivingEntity): ItemStack {
if (player !is Player) return super.finishUsingItem(itemStack, level, player) 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)) { if (!mattery.hasExopack || type.prop.get(mattery)) {
return super.finishUsingItem(itemStack, level, player) 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.ResourceLocation
import ru.dbotthepony.mc.otm.core.collect.ConditionalSet import ru.dbotthepony.mc.otm.core.collect.ConditionalSet
import ru.dbotthepony.mc.otm.core.math.Decimal 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.input.BooleanInputWithFeedback
import ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget import ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget
import ru.dbotthepony.mc.otm.network.MatteryStreamCodec import ru.dbotthepony.mc.otm.network.MatteryStreamCodec
@ -81,7 +82,7 @@ abstract class MatteryMenu(
val mSynchronizer = SynchableGroup() val mSynchronizer = SynchableGroup()
val synchronizerRemote = mSynchronizer.Remote() val synchronizerRemote = mSynchronizer.Remote()
val player: Player get() = inventory.player val player: Player get() = inventory.player
val random: RandomSource = RandomSource.create() val random = GJRAND64RandomSource()
private val _playerInventorySlots = ArrayList<InventorySlot>() private val _playerInventorySlots = ArrayList<InventorySlot>()
private val _playerHotbarSlots = ArrayList<InventorySlot>() private val _playerHotbarSlots = ArrayList<InventorySlot>()

View File

@ -355,6 +355,8 @@ object MNames {
const val TRITANIUM_DOOR = "tritanium_door" const val TRITANIUM_DOOR = "tritanium_door"
const val TRITANIUM_TRAPDOOR = "tritanium_trapdoor" const val TRITANIUM_TRAPDOOR = "tritanium_trapdoor"
const val TRITANIUM_PRESSURE_PLATE = "tritanium_pressure_plate" const val TRITANIUM_PRESSURE_PLATE = "tritanium_pressure_plate"
const val SMALL_CAPSULE = "small_capsule"
} }
object StatNames { 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.BlackHoleBlockEntity
import ru.dbotthepony.mc.otm.block.entity.blackhole.BlackHoleGeneratorBlockEntity 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.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.CargoCrateBlockEntity
import ru.dbotthepony.mc.otm.block.entity.decorative.DevChestBlockEntity import ru.dbotthepony.mc.otm.block.entity.decorative.DevChestBlockEntity
import ru.dbotthepony.mc.otm.block.entity.decorative.FluidTankBlockEntity 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 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) { internal fun register(bus: IEventBus) {
registry.register(bus) registry.register(bus)
bus.addListener(this::registerClient) 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.RotatableMatteryBlock
import ru.dbotthepony.mc.otm.block.StorageCableBlock import ru.dbotthepony.mc.otm.block.StorageCableBlock
import ru.dbotthepony.mc.otm.block.addSimpleDescription 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.DevChestBlock
import ru.dbotthepony.mc.otm.block.decorative.EngineBlock import ru.dbotthepony.mc.otm.block.decorative.EngineBlock
import ru.dbotthepony.mc.otm.block.decorative.FluidTankBlock import ru.dbotthepony.mc.otm.block.decorative.FluidTankBlock
@ -455,6 +456,15 @@ object MBlocks {
val MULTIBLOCK_TEST by registry.register("multiblock_test") { MultiblockTestBlock() } 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 { init {
MRegistry.registerBlocks(registry) MRegistry.registerBlocks(registry)
} }

View File

@ -692,6 +692,8 @@ object MItems {
val CONFIGURATOR: Item by registry.register(MNames.CONFIGURATOR) { ConfiguratorItem() } 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 { init {
MRegistry.registerItems(registry) MRegistry.registerItems(registry)
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB