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/core/Ext.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt index 47b8a1c39..e1e0b3427 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt @@ -10,6 +10,7 @@ import com.google.common.collect.ImmutableSet import com.google.gson.JsonElement import com.google.gson.JsonObject import com.google.gson.JsonPrimitive +import it.unimi.dsi.fastutil.ints.IntList import it.unimi.dsi.fastutil.objects.ObjectComparators import net.minecraft.Util import net.minecraft.core.BlockPos @@ -199,6 +200,15 @@ fun IntArray.shuffle(random: RandomSource): IntArray { return this } +fun L.shuffle(random: RandomSource): L { + for (i in lastIndex downTo 1) { + val j = random.nextInt(i + 1) + set(j, set(i, getInt(j))) + } + + return this +} + fun > L.shuffle(random: RandomSource): L { Util.shuffle(this, random) return this diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/BlockLootTableHolder.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/BlockLootTableHolder.kt 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/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/registry/MNames.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MNames.kt index 03e4d3d46..c3ebab17d 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MNames.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MNames.kt @@ -341,6 +341,8 @@ object MNames { const val TRITANIUM_DOOR = "tritanium_door" const val TRITANIUM_TRAPDOOR = "tritanium_trapdoor" const val TRITANIUM_PRESSURE_PLATE = "tritanium_pressure_plate" + + const val SMALL_CAPSULE = "small_capsule" } object StatNames { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/game/MBlockEntities.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/game/MBlockEntities.kt index 021700c52..b5f8e80df 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/game/MBlockEntities.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/game/MBlockEntities.kt @@ -12,6 +12,7 @@ import ru.dbotthepony.mc.otm.block.entity.tech.* import ru.dbotthepony.mc.otm.block.entity.blackhole.BlackHoleBlockEntity import ru.dbotthepony.mc.otm.block.entity.blackhole.BlackHoleGeneratorBlockEntity import ru.dbotthepony.mc.otm.block.entity.cable.SimpleEnergyCableBlockEntity +import ru.dbotthepony.mc.otm.block.entity.decorative.BreakableContainerBlockEntity import ru.dbotthepony.mc.otm.block.entity.decorative.CargoCrateBlockEntity import ru.dbotthepony.mc.otm.block.entity.decorative.DevChestBlockEntity import ru.dbotthepony.mc.otm.block.entity.decorative.FluidTankBlockEntity @@ -118,6 +119,12 @@ object MBlockEntities { val HOLO_SIGN: BlockEntityType by registry.register(MNames.HOLO_SIGN) { BlockEntityType.Builder.of(::HoloSignBlockEntity, MBlocks.HOLO_SIGN).build(null) } + val BREAKABLE: BlockEntityType by registry.register("breakable") { + val blocks = ArrayList() + blocks.add(MBlocks.SMALL_CAPSULE) + BlockEntityType.Builder.of(::BreakableContainerBlockEntity, *blocks.toTypedArray()).build(null) + } + internal fun register(bus: IEventBus) { registry.register(bus) bus.addListener(this::registerClient) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/game/MBlocks.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/game/MBlocks.kt index 33d27ca12..8a4558eae 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/game/MBlocks.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/game/MBlocks.kt @@ -37,6 +37,7 @@ import ru.dbotthepony.mc.otm.block.MultiblockTestBlock import ru.dbotthepony.mc.otm.block.RotatableMatteryBlock import ru.dbotthepony.mc.otm.block.StorageCableBlock import ru.dbotthepony.mc.otm.block.addSimpleDescription +import ru.dbotthepony.mc.otm.block.decorative.BreakableContainerBlock import ru.dbotthepony.mc.otm.block.decorative.DevChestBlock import ru.dbotthepony.mc.otm.block.decorative.EngineBlock import ru.dbotthepony.mc.otm.block.decorative.FluidTankBlock @@ -434,6 +435,15 @@ object MBlocks { val MULTIBLOCK_TEST by registry.register("multiblock_test") { MultiblockTestBlock() } + val SMALL_CAPSULE by registry.register(MNames.SMALL_CAPSULE) { + BreakableContainerBlock( + BlockBehaviour.Properties.of() + .mapColor(MapColor.COLOR_GRAY) + .destroyTime(0f) + .explosionResistance(1.5f) + ) + } + init { MRegistry.registerBlocks(registry) } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/game/MItems.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/game/MItems.kt index 622cf8a9e..e287a6d0d 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/game/MItems.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/game/MItems.kt @@ -680,6 +680,8 @@ object MItems { val CONFIGURATOR: Item by registry.register(MNames.CONFIGURATOR) { ConfiguratorItem() } + val SMALL_CAPSULE by registry.register(MNames.SMALL_CAPSULE) { BlockItem(MBlocks.SMALL_CAPSULE, DEFAULT_PROPERTIES) } + init { MRegistry.registerItems(registry) }