diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/MatteryBlock.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/MatteryBlock.kt index 7a702d9f2..cc5bd8684 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/MatteryBlock.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/MatteryBlock.kt @@ -14,6 +14,7 @@ import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.NbtOps import net.minecraft.network.chat.Component import net.minecraft.network.chat.ComponentSerialization +import net.minecraft.server.level.ServerLevel import net.minecraft.util.RandomSource import net.minecraft.world.Containers import net.minecraft.world.InteractionResult @@ -259,15 +260,11 @@ open class MatteryBlock(properties: Properties = DEFAULT_PROPERTIES) : Block(pro newBlockState: BlockState, movedByPiston: Boolean ) { - if (!oldBlockState.`is`(newBlockState.block) && !level.isClientSide) { + if (!oldBlockState.`is`(newBlockState.block) && level is ServerLevel) { val blockentity = level.getBlockEntity(blockPos) if (blockentity is MatteryBlockEntity) { - blockentity.beforeDroppingItems(oldBlockState, level, blockPos, newBlockState, movedByPiston) - - for (container in blockentity.droppableContainers) - Containers.dropContents(level, blockPos, container) - + blockentity.dropItems(oldBlockState, level, blockPos, newBlockState, movedByPiston) level.updateNeighbourForOutputSignal(blockPos, this) } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/decorative/BreakableContainerBlock.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/decorative/BreakableContainerBlock.kt new file mode 100644 index 000000000..dd68686cf --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/decorative/BreakableContainerBlock.kt @@ -0,0 +1,26 @@ +package ru.dbotthepony.mc.otm.block.decorative + +import net.minecraft.core.BlockPos +import net.minecraft.world.item.ItemStack +import net.minecraft.world.level.block.EntityBlock +import net.minecraft.world.level.block.entity.BlockEntity +import net.minecraft.world.level.block.state.BlockState +import net.minecraft.world.level.storage.loot.LootParams +import net.minecraft.world.level.storage.loot.parameters.LootContextParams +import ru.dbotthepony.mc.otm.block.RotatableMatteryBlock +import ru.dbotthepony.mc.otm.block.entity.decorative.BreakableContainerBlockEntity + +class BreakableContainerBlock(properties: Properties) : RotatableMatteryBlock(properties), EntityBlock { + override fun newBlockEntity(pos: BlockPos, state: BlockState): BlockEntity { + return BreakableContainerBlockEntity(pos, state) + } + + override fun getDrops(state: BlockState, params: LootParams.Builder): MutableList { + val entity = params.getOptionalParameter(LootContextParams.BLOCK_ENTITY) + + if (entity is BreakableContainerBlockEntity) + return entity.loot.getItems(params, state) + + return super.getDrops(state, params) + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MatteryBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MatteryBlockEntity.kt index 49fb486f3..4a4d90f37 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MatteryBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MatteryBlockEntity.kt @@ -17,6 +17,7 @@ import net.minecraft.nbt.CompoundTag import net.minecraft.server.level.ServerLevel import net.minecraft.server.level.ServerPlayer import net.minecraft.world.Container +import net.minecraft.world.Containers import net.minecraft.world.entity.player.Player import net.minecraft.world.level.ChunkPos import net.minecraft.world.level.Level @@ -70,7 +71,7 @@ import kotlin.collections.ArrayList /** * Absolute barebone (lol) block entity class in Overdrive that Matters, providing bare minimum (lulmao, minecraft engine) functionality */ -abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: BlockPos, p_155230_: BlockState) : BlockEntity(p_155228_, p_155229_, p_155230_), INeighbourChangeListener { +abstract class MatteryBlockEntity(type: BlockEntityType<*>, pos: BlockPos, state: BlockState) : BlockEntity(type, pos, state), INeighbourChangeListener { private val sidelessCaps = Reference2ObjectOpenHashMap, Any>() private val sidedCaps = Array(RelativeSide.entries.size) { Reference2ObjectOpenHashMap, ControllableCapability<*>>() @@ -121,7 +122,11 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc _neighbourChangeListeners.add(listener) } - open fun beforeDroppingItems(oldBlockState: BlockState, level: Level, blockPos: BlockPos, newBlockState: BlockState, movedByPiston: Boolean) {} + open fun dropItems(oldBlockState: BlockState, level: ServerLevel, blockPos: BlockPos, newBlockState: BlockState, movedByPiston: Boolean) { + for (container in droppableContainers) + Containers.dropContents(level, blockPos, container) + } + open fun beforeDestroyedByPlayer(level: Level, blockPos: BlockPos, blockState: BlockState, player: Player) {} open fun tick() { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/decorative/BreakableContainerBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/decorative/BreakableContainerBlockEntity.kt new file mode 100644 index 000000000..d5dfd0d08 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/decorative/BreakableContainerBlockEntity.kt @@ -0,0 +1,23 @@ +package ru.dbotthepony.mc.otm.block.entity.decorative + +import net.minecraft.core.BlockPos +import net.minecraft.core.HolderLookup +import net.minecraft.nbt.CompoundTag +import net.minecraft.world.level.block.state.BlockState +import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity +import ru.dbotthepony.mc.otm.core.util.BlockLootTableHolder +import ru.dbotthepony.mc.otm.registry.game.MBlockEntities + +class BreakableContainerBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryBlockEntity(MBlockEntities.BREAKABLE, blockPos, blockState) { + val loot = BlockLootTableHolder(::markDirtyFast) + + override fun saveLevel(nbt: CompoundTag, registry: HolderLookup.Provider) { + super.saveLevel(nbt, registry) + loot.save(nbt, registry) + } + + override fun loadAdditional(nbt: CompoundTag, registry: HolderLookup.Provider) { + super.loadAdditional(nbt, registry) + loot.load(nbt, registry) + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/decorative/CargoCrateBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/decorative/CargoCrateBlockEntity.kt index e960ffe5c..f1dc3a7d2 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/decorative/CargoCrateBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/decorative/CargoCrateBlockEntity.kt @@ -1,18 +1,11 @@ package ru.dbotthepony.mc.otm.block.entity.decorative -import net.minecraft.advancements.CriteriaTriggers import net.minecraft.core.BlockPos import net.minecraft.core.HolderLookup -import net.minecraft.core.registries.Registries import net.minecraft.nbt.CompoundTag -import net.minecraft.nbt.Tag import net.minecraft.network.chat.Component -import net.minecraft.resources.ResourceKey -import net.minecraft.resources.ResourceLocation import net.minecraft.server.level.ServerLevel -import net.minecraft.server.level.ServerPlayer import net.minecraft.sounds.SoundSource -import net.minecraft.world.Containers import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Player import net.minecraft.world.inventory.AbstractContainerMenu @@ -21,18 +14,14 @@ import net.minecraft.world.level.Level import net.minecraft.world.level.block.Block import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.level.gameevent.GameEvent -import net.minecraft.world.level.storage.loot.LootParams -import net.minecraft.world.level.storage.loot.LootTable -import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets -import net.minecraft.world.level.storage.loot.parameters.LootContextParams -import net.minecraft.world.phys.Vec3 import net.neoforged.neoforge.capabilities.Capabilities import ru.dbotthepony.mc.otm.block.decorative.CargoCrateBlock import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity -import ru.dbotthepony.mc.otm.container.MatteryContainer import ru.dbotthepony.mc.otm.container.HandlerFilter +import ru.dbotthepony.mc.otm.container.MatteryContainer import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.core.otmRandom +import ru.dbotthepony.mc.otm.core.util.BlockLootTableHolder import ru.dbotthepony.mc.otm.menu.decorative.CargoCrateMenu import ru.dbotthepony.mc.otm.registry.game.MBlockEntities import ru.dbotthepony.mc.otm.registry.game.MSoundEvents @@ -47,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? = 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) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/MatterRecyclerBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/MatterRecyclerBlockEntity.kt index d8fe21afe..ce4f69727 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/MatterRecyclerBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/MatterRecyclerBlockEntity.kt @@ -117,7 +117,7 @@ class MatterRecyclerBlockEntity(blockPos: BlockPos, blockState: BlockState) stack.shrink(1) container.setChanged(0) - val actualMatter = dustMatter.matter * (0.4 + level!!.otmRandom.nextDouble() * 0.6) + val actualMatter = dustMatter.matter return JobContainer.success( RecyclerJob( @@ -134,10 +134,11 @@ class MatterRecyclerBlockEntity(blockPos: BlockPos, blockState: BlockState) if (toReceive.isZero) return status.success() + else if (matter.receiveMatter(toReceive, true) != toReceive) + return status.noMatter() - val received = matter.receiveMatter(toReceive, false) - status.scale(received / toReceive) - job.totalMatter -= received + matter.receiveMatter(toReceive * 0.4 + level!!.otmRandom.nextDouble() * 0.6, false) + job.totalMatter -= toReceive } override fun tick() { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/MGUIGraphics.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/MGUIGraphics.kt index ee79d0a17..c7354cff0 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/MGUIGraphics.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/MGUIGraphics.kt @@ -14,6 +14,8 @@ import net.minecraft.world.inventory.tooltip.TooltipComponent import net.minecraft.world.item.ItemStack import ru.dbotthepony.mc.otm.client.minecraft import ru.dbotthepony.kommons.math.RGBAColor +import ru.dbotthepony.mc.otm.client.screen.panels.ScreenPos +import java.util.Arrays import kotlin.math.PI import kotlin.math.roundToInt @@ -45,6 +47,73 @@ class MGUIGraphics(val parent: GuiGraphics) { drawLine(pose.last().pose(), startX, startY, endX, endY, width, z, color) } + fun drawLine( + points: Iterable, + width: Float, + color: RGBAColor = RGBAColor.WHITE + ) { + drawLine(pose.last().pose(), points, width, color) + } + + fun drawLine( + points: Array, + width: Float, + color: RGBAColor = RGBAColor.WHITE + ) { + drawLine(pose.last().pose(), points, width, color) + } + + fun drawLine( + width: Float, + color: RGBAColor, + vararg points: LinePoint, + ) { + drawLine(pose.last().pose(), points, width, color) + } + + fun drawLine( + width: Float, + color: RGBAColor, + points: Iterable, + ) { + drawLine(pose.last().pose(), points, width, color) + } + + fun drawLine( + width: Float, + color: RGBAColor, + z: Float, + points: List, + ) { + require(points.size >= 2) { "Degenerate point list: only ${points.size} defined" } + + val result = ArrayList(points.size) + + for (i in 1 until points.size) { + val (x0, y0) = points[i - 1] + val (x1, y1) = points[i] + + result.add( + LinePoint( + x0, y0, + x1, y1, + z + ) + ) + } + + drawLine(pose.last().pose(), result, width, color) + } + + fun drawLine( + width: Float, + color: RGBAColor, + z: Float, + vararg points: ScreenPos, + ) { + drawLine(width, color, z, Arrays.asList(*points)) + } + fun renderRect( x: Float, y: Float, diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/RenderHelper.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/RenderHelper.kt index 2d54cc06b..c4d3a331a 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/RenderHelper.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/RenderHelper.kt @@ -294,6 +294,65 @@ fun renderColoredSphere(pose: PoseStack, radius: Float, color: RGBAColor = RGBAC BufferUploader.drawWithShader(builder.buildOrThrow()) } +private fun uploadLineSegment( + builder: BufferBuilder, + matrix: Matrix4f, + startX: Float, + startY: Float, + endX: Float, + endY: Float, + width: Float, + z: Float, + color: RGBAColor = RGBAColor.WHITE +) { + val length = ((startX - endX).pow(2f) + (startY - endY).pow(2f)).pow(0.5f) + var angle = acos((endX - startX) / length) + + if (startY > endY) + angle *= -1f + + val cos = cos(angle) + val sin = sin(angle) + + val y0 = -width + + val y1 = width + + val x2 = length + val y2 = width + + val x3 = length + val y3 = -width + + builder.vertex(matrix, + startX - y0 * sin, + startY + y0 * cos, + z).color(color) + + builder.vertex(matrix, + startX - y1 * sin, + startY + y1 * cos, + z).color(color) + + builder.vertex(matrix, + startX + x2 * cos - y2 * sin, + startY + x2 * sin + y2 * cos, + z).color(color) + + builder.vertex(matrix, + startX + x3 * cos - y3 * sin, + startY + x3 * sin + y3 * cos, + z).color(color) +} + +data class LinePoint( + val startX: Float, + val startY: Float, + val endX: Float, + val endY: Float, + val z: Float = 0f, +) + fun drawLine( matrix: Matrix4f, startX: Float, @@ -312,46 +371,46 @@ fun drawLine( RenderSystem.depthFunc(GL_ALWAYS) val builder = tesselator.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR) + uploadLineSegment(builder, matrix, startX, startY, endX, endY, width, z, color) + BufferUploader.drawWithShader(builder.buildOrThrow()) +} - val length = ((startX - endX).pow(2f) + (startY - endY).pow(2f)).pow(0.5f) - val angle = acos((endX - startX) / length) +fun drawLine( + matrix: Matrix4f, + points: Iterable, + width: Float, + color: RGBAColor = RGBAColor.WHITE +) { + val itr = points.iterator() - val cos = cos(angle) - val sin = sin(angle) + if (!itr.hasNext()) { + throw IllegalArgumentException("No line points were provided") + } - val y0 = -width + RenderSystem.setShader(GameRenderer::getPositionColorShader) + RenderSystem.enableBlend() + RenderSystem.defaultBlendFunc() - val y1 = width + if (!is3DContext) + RenderSystem.depthFunc(GL_ALWAYS) - val x2 = length - val y2 = width + val builder = tesselator.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR) - val x3 = length - val y3 = -width - - builder.vertex(matrix, - startX - y0 * sin, - startY + y0 * cos, - z).color(color) - - builder.vertex(matrix, - startX - y1 * sin, - startY + y1 * cos, - z).color(color) - - builder.vertex(matrix, - startX + x2 * cos - y2 * sin, - startY + x2 * sin + y2 * cos, - z).color(color) - - builder.vertex(matrix, - startX + x3 * cos - y3 * sin, - startY + x3 * sin + y3 * cos, - z).color(color) + for ((startX, startY, endX, endY, z) in itr) + uploadLineSegment(builder, matrix, startX, startY, endX, endY, width, z, color) BufferUploader.drawWithShader(builder.buildOrThrow()) } +fun drawLine( + matrix: Matrix4f, + points: Array, + width: Float, + color: RGBAColor = RGBAColor.WHITE +) { + return drawLine(matrix, points.asIterable(), width, color) +} + data class ScissorRect(val xStart: Int, val yStart: Int, val xEnd: Int, val yEnd: Int, val lock: Boolean = false) { val width: Int get() = (xEnd - xStart).coerceAtLeast(0) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/Widgets18.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/Widgets18.kt index 21aa5a69c..ec3f41496 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/Widgets18.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/Widgets18.kt @@ -30,6 +30,7 @@ object Widgets18 { val RESTOCK_FROM_STORAGE = storageGrid.next() val RESTOCK_WITH_MOVE_TO_STORAGE = storageGrid.next() val RESTOCK_WITH_MOVE_FROM_STORAGE = storageGrid.next() + val SMART_STORAGE_EXCHANGE = storageGrid.next() private val miscGrid = WidgetLocation.WIDGET_18.grid(4, 4) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/blockentity/EnergyCounterRenderer.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/blockentity/EnergyCounterRenderer.kt index 2c1b70a67..741f3f925 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/blockentity/EnergyCounterRenderer.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/blockentity/EnergyCounterRenderer.kt @@ -20,12 +20,13 @@ import ru.dbotthepony.mc.otm.core.math.asAngle import ru.dbotthepony.mc.otm.core.math.clusterize import ru.dbotthepony.mc.otm.core.util.formatPower import ru.dbotthepony.mc.otm.core.math.times +import ru.dbotthepony.mc.otm.core.util.GJRAND64RandomSource import java.util.Random import kotlin.math.PI import kotlin.math.absoluteValue class EnergyCounterRenderer(private val context: BlockEntityRendererProvider.Context) : BlockEntityRenderer { - private val random = Random() + private val random = GJRAND64RandomSource() override fun render( tile: EnergyCounterBlockEntity, diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/MatteryScreen.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/MatteryScreen.kt index 23e70a863..fad46c6d4 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/MatteryScreen.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/MatteryScreen.kt @@ -17,9 +17,11 @@ import net.neoforged.neoforge.common.NeoForge import org.lwjgl.opengl.GL11 import ru.dbotthepony.kommons.util.getValue import ru.dbotthepony.kommons.util.setValue +import ru.dbotthepony.kommons.math.RGBAColor import ru.dbotthepony.mc.otm.player.matteryPlayer import ru.dbotthepony.mc.otm.client.render.MGUIGraphics import ru.dbotthepony.mc.otm.client.moveMousePosScaled +import ru.dbotthepony.mc.otm.client.render.LinePoint import ru.dbotthepony.mc.otm.client.render.WidgetLocation import ru.dbotthepony.mc.otm.client.render.Widgets18 import ru.dbotthepony.mc.otm.client.render.translation @@ -55,6 +57,7 @@ import ru.dbotthepony.mc.otm.menu.widget.LevelGaugeWidget import ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget import java.util.* import java.util.concurrent.CopyOnWriteArrayList +import kotlin.collections.ArrayList import kotlin.collections.List import kotlin.collections.MutableSet import kotlin.collections.isNotEmpty @@ -690,7 +693,13 @@ abstract class MatteryScreen(menu: T, inventory: Inventory, tit for (panel in panelsReversed) { RenderSystem.depthFunc(GL11.GL_ALWAYS) RenderSystem.setShaderColor(1f, 1f, 1f, 1f) - panel.render(wrap, mouseXf, mouseYf, partialTick) + val segments = ArrayList() + + panel.render(wrap, mouseXf, mouseYf, partialTick, segments) + + if (segments.isNotEmpty()) { + wrap.drawLine(0.5f, RGBAColor.GOLD, segments) + } } if (!panelsReversed.any { it.updateCursor0() }) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/DecimalHistoryChartPanel.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/DecimalHistoryChartPanel.kt index 1b262ce32..dfc9287ab 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/DecimalHistoryChartPanel.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/DecimalHistoryChartPanel.kt @@ -52,7 +52,7 @@ open class DecimalHistoryChartPanel>( map[1f] = formatText(maximum) - for (cluster in chart.asIterable().clusterize(randomGenerator)) { + for (cluster in chart.asIterable().clusterize(random)) { val perc = (cluster.center / maximum).toFloat() if (map.keys.none { (it - perc).absoluteValue < 0.08f }) { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/EditablePanel.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/EditablePanel.kt index bd1493163..0228f172b 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/EditablePanel.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/EditablePanel.kt @@ -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( val screen: S, parent: EditablePanel<*>?, @@ -175,18 +112,14 @@ open class EditablePanel( } } - 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( 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( 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( 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( if (child.isVisible() != isVisible()) { updateVisible() } + + onChildrenRemoved(child) } private fun sortChildren() { @@ -944,12 +891,11 @@ open class EditablePanel( } } - fun render(graphics: MGUIGraphics, mouseX: Float, mouseY: Float, partialTick: Float) { + fun render(graphics: MGUIGraphics, mouseX: Float, mouseY: Float, partialTick: Float, debugSegments: MutableList) { once = true - if (!isVisible()) { + if (!isVisible()) return - } val poseStack = graphics.pose @@ -997,12 +943,19 @@ open class EditablePanel( child.absoluteX = absoluteX + child.x + xOffset child.absoluteY = absoluteY + child.y + yOffset - child.render(graphics, mouseX, mouseY, partialTick) + child.render(graphics, mouseX, mouseY, partialTick, debugSegments) } if (scissor) { popScissorRect() } + + if (minecraft.entityRenderDispatcher.shouldRenderHitBoxes()) { + debugSegments.add(LinePoint(absoluteX, absoluteY, absoluteX + width, absoluteY)) + debugSegments.add(LinePoint(absoluteX + width, absoluteY, absoluteX + width, absoluteY + height)) + debugSegments.add(LinePoint(absoluteX + width, absoluteY + height, absoluteX, absoluteY + height)) + debugSegments.add(LinePoint(absoluteX, absoluteY + height, absoluteX, absoluteY)) + } } fun updateCursor0(): Boolean { @@ -1508,6 +1461,7 @@ open class EditablePanel( if (old != new) { child.visibilityChanges(new, old) + child.invalidateChildrenSorting() } child.updateVisible() diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/Panel2Widget.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/Panel2Widget.kt index 6c06bc038..bd5bdaee8 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/Panel2Widget.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/Panel2Widget.kt @@ -4,6 +4,8 @@ import net.minecraft.client.gui.GuiGraphics import net.minecraft.client.gui.components.Renderable import net.minecraft.client.gui.components.events.GuiEventListener import net.minecraft.client.gui.screens.Screen +import ru.dbotthepony.kommons.math.RGBAColor +import ru.dbotthepony.mc.otm.client.render.LinePoint import ru.dbotthepony.mc.otm.client.render.MGUIGraphics // before 1.19.3 Renderable was Widget @@ -21,11 +23,17 @@ class Panel2Widget>( val yFloat = mouseY.toFloat() val wrap = MGUIGraphics(graphics) + val segments = ArrayList() panel.tickHovered0(xFloat, yFloat, false) panel.tickHovered1() panel.tickHovered2() - panel.render(wrap, xFloat, yFloat, partialTick) + panel.render(wrap, xFloat, yFloat, partialTick, segments) + + if (segments.isNotEmpty()) { + wrap.drawLine(0.5f, RGBAColor.GOLD, segments) + } + panel.renderTooltips(wrap, xFloat, yFloat, partialTick) } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/Types.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/Types.kt new file mode 100644 index 000000000..7d498d8f6 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/Types.kt @@ -0,0 +1,115 @@ +package ru.dbotthepony.mc.otm.client.screen.panels + +import net.minecraft.client.renderer.Rect2i +import kotlin.math.roundToInt + +data class ScreenPos(val x: Float, val y: Float) + +data class DockProperty(val left: Float = 0f, val top: Float = 0f, val right: Float = 0f, val bottom: Float = 0f) { + val isEmpty get() = left == 0f && right == 0f && top == 0f && bottom == 0f + val horizontal get() = left + right + val vertical get() = top + bottom + + operator fun plus(other: DockProperty): DockProperty { + return DockProperty( + left + other.left, + top + other.top, + right + other.right, + bottom + other.bottom + ) + } + + operator fun minus(other: DockProperty): DockProperty { + return DockProperty( + left - other.left, + top - other.top, + right - other.right, + bottom - other.bottom + ) + } + + operator fun plus(other: Float): DockProperty { + return DockProperty( + left + other, + top + other, + right + other, + bottom + other + ) + } + + operator fun minus(other: Float): DockProperty { + return DockProperty( + left - other, + top - other, + right - other, + bottom - other + ) + } + + companion object { + val EMPTY = DockProperty() + + @JvmStatic + fun all(value: Float): DockProperty { + return DockProperty(value, value, value, value) + } + + @JvmStatic + @JvmOverloads + fun topLeft(top: Float, left: Float = top): DockProperty { + return DockProperty(left = left, top = top) + } + + @JvmStatic + fun left(value: Float): DockProperty { + return DockProperty(left = value) + } + + @JvmStatic + @JvmOverloads + fun topRight(top: Float, right: Float = top): DockProperty { + return DockProperty(right = right, top = top) + } + + @JvmStatic + fun right(value: Float): DockProperty { + return DockProperty(right = value) + } + + @JvmStatic + fun top(value: Float): DockProperty { + return DockProperty(top = value) + } + + @JvmStatic + @JvmOverloads + fun bottomLeft(bottom: Float, left: Float = bottom): DockProperty { + return DockProperty(left = left, bottom = bottom) + } + + @JvmStatic + @JvmOverloads + fun bottomRight(bottom: Float, right: Float = bottom): DockProperty { + return DockProperty(right = right, bottom = bottom) + } + + @JvmStatic + fun bottom(value: Float): DockProperty { + return DockProperty(bottom = value) + } + } +} + +enum class Dock { + NONE, LEFT, RIGHT, TOP, BOTTOM, FILL +} + +enum class DockResizeMode(val changeWidth: Boolean, val changeHeight: Boolean) { + ALL(true, true), NONE(false, false), WIDTH(true, false), HEIGHT(false, true) +} + +data class Rect2f(val x: Float, val y: Float, val width: Float, val height: Float) { + fun toIntRect(): Rect2i { + return Rect2i(x.roundToInt(), y.roundToInt(), width.roundToInt(), height.roundToInt()) + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/util/GridPanel.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/util/GridPanel.kt index 55a550420..28b32a5fd 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/util/GridPanel.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/util/GridPanel.kt @@ -7,6 +7,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( screen: S, @@ -20,25 +23,150 @@ open class GridPanel( ) : EditablePanel(screen, parent, x, y, width, height) { var columns: Int = columns set(value) { - field = value - invalidateLayout() + if (field != value) { + field = value + invalidateLayout() + } } var rows: Int = rows set(value) { - field = value - invalidateLayout() + if (field != value) { + field = value + invalidateLayout() + } } var gravity: RenderGravity = RenderGravity.CENTER_CENTER set(value) { - field = value - invalidateLayout() + if (field != value) { + field = value + invalidateLayout() + } } + var layout: Layout = Layout.TOP_LEFT + set(value) { + if (field != value) { + field = value + invalidateLayout() + } + } + + var columnMajorOrder = false + set(value) { + if (field != value) { + field = value + invalidateLayout() + } + } + + enum class Layout { + TOP_LEFT { + override fun get( + list: List>, + 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>, + 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>, + 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>, + 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>, 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( 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( 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( 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 diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/tech/AndroidStationScreen.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/tech/AndroidStationScreen.kt index f6edd683a..09ef63314 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/tech/AndroidStationScreen.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/tech/AndroidStationScreen.kt @@ -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) } } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt index 6203217b1..e1e0b3427 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt @@ -10,6 +10,7 @@ import com.google.common.collect.ImmutableSet import com.google.gson.JsonElement import com.google.gson.JsonObject import com.google.gson.JsonPrimitive +import it.unimi.dsi.fastutil.ints.IntList import it.unimi.dsi.fastutil.objects.ObjectComparators import net.minecraft.Util import net.minecraft.core.BlockPos @@ -199,6 +200,15 @@ fun IntArray.shuffle(random: RandomSource): IntArray { return this } +fun L.shuffle(random: RandomSource): L { + for (i in lastIndex downTo 1) { + val j = random.nextInt(i + 1) + set(j, set(i, getInt(j))) + } + + return this +} + fun > L.shuffle(random: RandomSource): L { Util.shuffle(this, random) return this @@ -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) +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/math/Clustering.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/math/Clustering.kt index a13d846c0..267d40356 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/math/Clustering.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/math/Clustering.kt @@ -1,6 +1,7 @@ package ru.dbotthepony.mc.otm.core.math import it.unimi.dsi.fastutil.objects.ObjectArrayList +import net.minecraft.util.RandomSource import ru.dbotthepony.mc.otm.core.random import java.util.random.RandomGenerator import kotlin.math.min @@ -88,7 +89,7 @@ private class MutableCluster>(var center: V) { } private fun > Iterable.clusterize( - random: RandomGenerator, + random: RandomSource, initialClusters: Int = 1, zeroBound: Boolean = false, identity: V, @@ -202,7 +203,7 @@ private fun > Iterable.clusterize( // TODO: could use some tweaking private val DECIMAL_ERROR_TOLERANCE = Decimal("0.02") -fun Iterable.clusterize(random: RandomGenerator, clusters: Int? = null, zeroBound: Boolean = false): List> { +fun Iterable.clusterize(random: RandomSource, clusters: Int? = null, zeroBound: Boolean = false): List> { return clusterize(random, clusters ?: 1, zeroBound, Decimal.ZERO, Decimal::plus, Decimal::minus, Decimal::div, Decimal::absoluteValue) { min, max, error -> if (clusters != null) false diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/BlockLootTableHolder.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/BlockLootTableHolder.kt new file mode 100644 index 000000000..af2fca337 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/BlockLootTableHolder.kt @@ -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? = 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 { + 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 { + 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 + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/Formatting.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/Formatting.kt index 961540d14..411b081c6 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/Formatting.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/Formatting.kt @@ -9,6 +9,7 @@ import net.minecraft.network.chat.FormattedText import net.minecraft.network.chat.MutableComponent import net.minecraft.world.inventory.tooltip.TooltipComponent import ru.dbotthepony.kommons.math.RGBAColor +import ru.dbotthepony.mc.otm.THREAD_LOCAL_RANDOM import ru.dbotthepony.mc.otm.client.render.ChartLevelLabels import ru.dbotthepony.mc.otm.client.render.ChartTooltipElement import ru.dbotthepony.mc.otm.config.ClientConfig @@ -453,10 +454,8 @@ private fun formatHistoryChart( labelNames[0f] = (-maxTransferred).formatSiComponent(suffix, decimals, formatAsReadable = verbose, bias = bias) labelNames[1f] = maxReceived.formatSiComponent(suffix, decimals, formatAsReadable = verbose, bias = bias) - val rand = java.util.Random() - if (maxTransferred.isNotZero && transferredMult > 0.2f) { - for (cluster in widget.transferred.clusterize(rand, zeroBound = true)) { + for (cluster in widget.transferred.clusterize(THREAD_LOCAL_RANDOM, zeroBound = true)) { val perc = (cluster.center / maxTransferred).toFloat() * transferredMult if (labelNames.keys.none { (it - perc).absoluteValue < 0.08f }) @@ -465,7 +464,7 @@ private fun formatHistoryChart( } if (maxReceived.isNotZero && receivedMult > 0.2f) { - for (cluster in widget.received.clusterize(rand, zeroBound = true)) { + for (cluster in widget.received.clusterize(THREAD_LOCAL_RANDOM, zeroBound = true)) { val perc = zero + (cluster.center / maxReceived).toFloat() * receivedMult if (labelNames.keys.none { (it - perc).absoluteValue < 0.08f }) @@ -473,7 +472,7 @@ private fun formatHistoryChart( } } - val clusters = diff.asIterable().clusterize(rand, zeroBound = true) + val clusters = diff.asIterable().clusterize(THREAD_LOCAL_RANDOM, zeroBound = true) for (cluster in clusters) { val perc: Float diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/GJRAND64RandomSource.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/GJRAND64RandomSource.kt index c8adbfa13..f26a4aaae 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/GJRAND64RandomSource.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/GJRAND64RandomSource.kt @@ -2,15 +2,12 @@ package ru.dbotthepony.mc.otm.core.util import net.minecraft.util.Mth import net.minecraft.util.RandomSource -import net.minecraft.world.level.levelgen.MarsagliaPolarGaussian import net.minecraft.world.level.levelgen.PositionalRandomFactory import net.minecraft.world.level.levelgen.RandomSupport import ru.dbotthepony.kommons.random.GJRAND64Random import java.lang.StringBuilder class GJRAND64RandomSource : GJRAND64Random, IRandomSourceGenerator { - private val gaussian = MarsagliaPolarGaussian(this) - constructor() : super(RandomSupport.generateUniqueSeed(), RandomSupport.generateUniqueSeed()) constructor(seed: Long) : super(seed) constructor(seed0: Long, seed1: Long) : super(seed0, seed1) @@ -19,10 +16,6 @@ class GJRAND64RandomSource : GJRAND64Random, IRandomSourceGenerator { return nextLong().ushr(32).toInt() } - override fun nextGaussian(): Double { - return gaussian.nextGaussian() - } - override fun fork(): RandomSource { return GJRAND64RandomSource(nextLong(), nextLong()) } @@ -33,7 +26,6 @@ class GJRAND64RandomSource : GJRAND64Random, IRandomSourceGenerator { override fun setSeed(seed: Long) { reinitialize(seed) - gaussian.reset() } class Positional(private val seed0: Long, private val seed1: Long) : PositionalRandomFactory { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/IRandomSourceGenerator.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/IRandomSourceGenerator.kt index 999e39cfd..cd0fa1e47 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/IRandomSourceGenerator.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/IRandomSourceGenerator.kt @@ -26,5 +26,7 @@ interface IRandomSourceGenerator : RandomSource, RandomGenerator { return super.nextDouble() } - override fun nextGaussian(): Double + override fun nextGaussian(): Double { + return super.nextGaussian() + } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/LootTableUtils.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/LootTableUtils.kt new file mode 100644 index 000000000..ae0a0a1be --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/LootTableUtils.kt @@ -0,0 +1,132 @@ +package ru.dbotthepony.mc.otm.core.util + +import it.unimi.dsi.fastutil.ints.IntArrayList +import net.minecraft.util.Mth +import net.minecraft.util.RandomSource +import net.minecraft.world.Container +import net.minecraft.world.item.Item +import net.minecraft.world.item.ItemStack +import net.minecraft.world.level.storage.loot.LootParams +import net.minecraft.world.level.storage.loot.LootTable +import org.apache.logging.log4j.LogManager +import ru.dbotthepony.mc.otm.container.get +import ru.dbotthepony.mc.otm.container.set +import ru.dbotthepony.mc.otm.core.isNotEmpty +import ru.dbotthepony.mc.otm.core.shuffle +import java.util.stream.Collectors +import java.util.stream.IntStream +import kotlin.math.min + +private val LOGGER = LogManager.getLogger() + +private class Bucket { + val items = ArrayList() + var last = 0 + + fun add(item: ItemStack) { + if (item.isEmpty) return + + for (i in last until items.size) { + val existing = items[i] + var available = existing.maxStackSize - existing.count + + if (available > 0 && ItemStack.isSameItemSameComponents(existing, item)) { + val diff = min(available, item.count) + existing.grow(diff) + item.shrink(diff) + available -= diff + + if (available == 0 && i == last) + last++ + + if (item.isEmpty) + return + } + } + + if (item.isNotEmpty) { + items.add(item) + } + } +} + +private fun recombine(lists: Collection>): MutableList { + val result = HashMap() + + for (list in lists) + for (item in list) + result.computeIfAbsent(item.item) { Bucket() }.add(item) + + return result.values + .stream() + .flatMap { it.items.stream() } + .collect(Collectors.toCollection(::ArrayList)) +} + +fun LootTable.getRandomItems(params: LootParams, random: RandomSource, rounds: Int = 1): MutableList { + if (rounds == 0) { + return ArrayList() + } else if (rounds == 1) { + return getRandomItems(params, random) + } else { + return recombine(IntStream.range(0, rounds).mapToObj { getRandomItems(params, random) }.toList()) + } +} + +private fun shuffle(items: MutableList, emptySlotCount: Int, random: RandomSource) { + val pool = ArrayList(items) + items.clear() + + while (items.size + pool.size < emptySlotCount && pool.isNotEmpty()) { + val select = pool.removeAt(random.nextInt(pool.size)) + val maxStackSize = select.maxStackSize + + if (maxStackSize == 1 || select.count == 1) { + items.add(select) + } else { + val split = select.split(Mth.nextInt(random, 1, select.count / 2)) + + if (split.count > 1 && random.nextBoolean()) + pool.add(split) + else + items.add(split) + + if (select.count > 1 && random.nextBoolean()) + pool.add(select) + else + items.add(select) + } + } + + items.addAll(pool) + items.shuffle(random) +} + +fun LootTable.fill(params: LootParams, random: RandomSource, container: Container, rounds: Int = 1) { + val emptySlots = IntArrayList() + + for (i in 0 until container.containerSize) + if (container[i].isEmpty) + emptySlots.add(i) + + emptySlots.shuffle(random) + + if (emptySlots.isEmpty) { + LOGGER.warn("Tried to fill container with no empty slots") + return + } + + val items = getRandomItems(params, random, rounds) + shuffle(items, emptySlots.size, random) + + val slotItr = emptySlots.iterator() + val itemItr = items.iterator() + + while (slotItr.hasNext() && itemItr.hasNext()) { + container[slotItr.nextInt()] = itemItr.next() + } + + if (itemItr.hasNext()) { + LOGGER.warn("Tried to overfill a container") + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/item/exopack/AbstractExopackSlotUpgradeItem.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/item/exopack/AbstractExopackSlotUpgradeItem.kt index e9750cab0..dd15524c2 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/item/exopack/AbstractExopackSlotUpgradeItem.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/item/exopack/AbstractExopackSlotUpgradeItem.kt @@ -20,6 +20,9 @@ import ru.dbotthepony.mc.otm.client.minecraft import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.core.isExplosion import ru.dbotthepony.mc.otm.core.isFire +import ru.dbotthepony.mc.otm.core.isNotEmpty +import ru.dbotthepony.mc.otm.core.nextUUID +import ru.dbotthepony.mc.otm.core.otmRandom import ru.dbotthepony.mc.otm.registry.game.MItems import ru.dbotthepony.mc.otm.runIfClient import ru.dbotthepony.mc.otm.triggers.ExopackSlotsExpandedTrigger @@ -35,7 +38,7 @@ abstract class AbstractExopackSlotUpgradeItem(properties: Properties = defaultPr } override fun getUseDuration(p_41454_: ItemStack, p_344979_: LivingEntity): Int { - return 30 + return 20 } override fun appendHoverText( @@ -76,7 +79,7 @@ abstract class AbstractExopackSlotUpgradeItem(properties: Properties = defaultPr } override fun use(p_41432_: Level, player: Player, hand: InteractionHand): InteractionResultHolder { - val matteryPlayer = player.matteryPlayer ?: return super.use(p_41432_, player, hand) + val matteryPlayer = player.matteryPlayer val uuid = uuid(player.getItemInHand(hand)) @@ -98,25 +101,32 @@ abstract class AbstractExopackSlotUpgradeItem(properties: Properties = defaultPr return super.finishUsingItem(itemStack, level, player) } - if (!player.abilities.instabuild) - itemStack.shrink(1) + var allowedUses = itemStack.count - if (player is ServerPlayer) { - if (uuid != null) { - if (ServerConfig.INFINITE_EXOSUIT_UPGRADES && uuid in matteryPlayer.exopackSlotModifier) { - matteryPlayer.exopackSlotModifier[UUID.randomUUID()] = slotCount + while (allowedUses > 0) { + if (!player.abilities.instabuild) + itemStack.shrink(1) + + allowedUses-- + + if (player is ServerPlayer) { + if (uuid != null) { + if (ServerConfig.INFINITE_EXOSUIT_UPGRADES && uuid in matteryPlayer.exopackSlotModifier) { + matteryPlayer.exopackSlotModifier[level.otmRandom.nextUUID()] = slotCount + } else { + matteryPlayer.exopackSlotModifier[uuid] = slotCount + allowedUses = 0 + } } else { - matteryPlayer.exopackSlotModifier[uuid] = slotCount + matteryPlayer.exopackSlotModifier[level.otmRandom.nextUUID()] = slotCount } - } else { - matteryPlayer.exopackSlotModifier[UUID.randomUUID()] = slotCount - } - ExopackSlotsExpandedTrigger.trigger(player, slotCount, matteryPlayer.exopackContainer.containerSize) - player.displayClientMessage(TranslatableComponent("otm.exopack_upgrades.slots_upgraded", slotCount).withStyle(ChatFormatting.DARK_GREEN), false) + ExopackSlotsExpandedTrigger.trigger(player, slotCount, matteryPlayer.exopackContainer.containerSize) + player.displayClientMessage(TranslatableComponent("otm.exopack_upgrades.slots_upgraded", slotCount).withStyle(ChatFormatting.DARK_GREEN), false) - if (this === MItems.ExopackUpgrades.INVENTORY_UPGRADE_ENDER_DRAGON) { - MItems.ExopackUpgrades.ENDER_UPGRADE.finishUsingItem(ItemStack(MItems.ExopackUpgrades.INVENTORY_UPGRADE_ENDER_DRAGON), level, player) + if (this === MItems.ExopackUpgrades.INVENTORY_UPGRADE_ENDER_DRAGON) { + MItems.ExopackUpgrades.ENDER_UPGRADE.finishUsingItem(ItemStack(MItems.ExopackUpgrades.INVENTORY_UPGRADE_ENDER_DRAGON), level, player) + } } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/item/exopack/ExopackUpgradeItem.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/item/exopack/ExopackUpgradeItem.kt index faf2b52ee..1db813729 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/item/exopack/ExopackUpgradeItem.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/item/exopack/ExopackUpgradeItem.kt @@ -60,7 +60,7 @@ class ExopackUpgradeItem( } override fun use(p_41432_: Level, player: Player, hand: InteractionHand): InteractionResultHolder { - if (player.matteryPlayer?.hasExopack == true && !type.prop.get(player.matteryPlayer!!)) { + if (player.matteryPlayer.hasExopack && !type.prop.get(player.matteryPlayer)) { player.startUsingItem(hand) return InteractionResultHolder.consume(player.getItemInHand(hand)) } @@ -70,7 +70,7 @@ class ExopackUpgradeItem( override fun finishUsingItem(itemStack: ItemStack, level: Level, player: LivingEntity): ItemStack { if (player !is Player) return super.finishUsingItem(itemStack, level, player) - val mattery = player.matteryPlayer ?: return super.finishUsingItem(itemStack, level, player) + val mattery = player.matteryPlayer if (!mattery.hasExopack || type.prop.get(mattery)) { return super.finishUsingItem(itemStack, level, player) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/MatteryMenu.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/MatteryMenu.kt index 8a3711faa..3568f9ecf 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/MatteryMenu.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/MatteryMenu.kt @@ -44,6 +44,7 @@ import ru.dbotthepony.mc.otm.container.sortWithIndices import ru.dbotthepony.mc.otm.core.ResourceLocation import ru.dbotthepony.mc.otm.core.collect.ConditionalSet import ru.dbotthepony.mc.otm.core.math.Decimal +import ru.dbotthepony.mc.otm.core.util.GJRAND64RandomSource import ru.dbotthepony.mc.otm.menu.input.BooleanInputWithFeedback import ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget import ru.dbotthepony.mc.otm.network.MatteryStreamCodec @@ -81,7 +82,7 @@ abstract class MatteryMenu( val mSynchronizer = SynchableGroup() val synchronizerRemote = mSynchronizer.Remote() val player: Player get() = inventory.player - val random: RandomSource = RandomSource.create() + val random = GJRAND64RandomSource() private val _playerInventorySlots = ArrayList() private val _playerHotbarSlots = ArrayList() diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MNames.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MNames.kt index e8c9787cb..408948d2f 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MNames.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MNames.kt @@ -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 { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/game/MBlockEntities.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/game/MBlockEntities.kt index 021700c52..b5f8e80df 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/game/MBlockEntities.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/game/MBlockEntities.kt @@ -12,6 +12,7 @@ import ru.dbotthepony.mc.otm.block.entity.tech.* import ru.dbotthepony.mc.otm.block.entity.blackhole.BlackHoleBlockEntity import ru.dbotthepony.mc.otm.block.entity.blackhole.BlackHoleGeneratorBlockEntity import ru.dbotthepony.mc.otm.block.entity.cable.SimpleEnergyCableBlockEntity +import ru.dbotthepony.mc.otm.block.entity.decorative.BreakableContainerBlockEntity import ru.dbotthepony.mc.otm.block.entity.decorative.CargoCrateBlockEntity import ru.dbotthepony.mc.otm.block.entity.decorative.DevChestBlockEntity import ru.dbotthepony.mc.otm.block.entity.decorative.FluidTankBlockEntity @@ -118,6 +119,12 @@ object MBlockEntities { val HOLO_SIGN: BlockEntityType by registry.register(MNames.HOLO_SIGN) { BlockEntityType.Builder.of(::HoloSignBlockEntity, MBlocks.HOLO_SIGN).build(null) } + val BREAKABLE: BlockEntityType by registry.register("breakable") { + val blocks = ArrayList() + blocks.add(MBlocks.SMALL_CAPSULE) + BlockEntityType.Builder.of(::BreakableContainerBlockEntity, *blocks.toTypedArray()).build(null) + } + internal fun register(bus: IEventBus) { registry.register(bus) bus.addListener(this::registerClient) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/game/MBlocks.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/game/MBlocks.kt index 0fc0ccd15..d2706b4d6 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/game/MBlocks.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/game/MBlocks.kt @@ -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) } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/game/MItems.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/game/MItems.kt index 8453dc6dc..183ef7c27 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/game/MItems.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/game/MItems.kt @@ -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) } diff --git a/src/main/resources/assets/overdrive_that_matters/textures/gui/widgets/storage_controls.png b/src/main/resources/assets/overdrive_that_matters/textures/gui/widgets/storage_controls.png index d46201f5e..7c39f8ee4 100644 Binary files a/src/main/resources/assets/overdrive_that_matters/textures/gui/widgets/storage_controls.png and b/src/main/resources/assets/overdrive_that_matters/textures/gui/widgets/storage_controls.png differ diff --git a/src/main/resources/assets/overdrive_that_matters/textures/gui/widgets/storage_controls.xcf b/src/main/resources/assets/overdrive_that_matters/textures/gui/widgets/storage_controls.xcf index 051859e7b..0fa82a2e4 100644 Binary files a/src/main/resources/assets/overdrive_that_matters/textures/gui/widgets/storage_controls.xcf and b/src/main/resources/assets/overdrive_that_matters/textures/gui/widgets/storage_controls.xcf differ