Merge branch '1.21' into worldgen-placement-providers

This commit is contained in:
DBotThePony 2025-03-29 18:20:37 +07:00
commit ddab73bad1
Signed by: DBot
GPG Key ID: DCC23B5715498507
188 changed files with 5800 additions and 3488 deletions

View File

@ -22,7 +22,7 @@ mixin_version=0.8.5
neogradle.subsystems.parchment.minecraftVersion=1.21.1
neogradle.subsystems.parchment.mappingsVersion=2024.11.17
kommons_version=3.5.2
kommons_version=3.6.0
caffeine_cache_version=3.1.5
jei_version=19.16.4.171

View File

@ -631,6 +631,8 @@ private fun blocks(provider: MatteryLanguageProvider) {
add(MBlocks.ENERGY_COUNTER[null]!!, "switch", "Switch input facing")
add(MBlocks.ENERGY_COUNTER[null]!!, "limit", "I/O Limit. -1 means no limit")
add(MBlocks.ENERGY_COUNTER[null]!!, "display_this", "Display this information on block's screen")
add(MBlocks.ENERGY_COUNTER[null]!!, "do_pull", "Pull energy from input side and push to output")
add(MBlocks.ENERGY_COUNTER[null]!!, "dont_pull", "Don't pull energy")
addBlock(MBlocks.CHEMICAL_GENERATOR.values, "Chemical Generator")
addBlock(MBlocks.CHEMICAL_GENERATOR.values, "desc", "Generates power by burning solid fuels")
@ -987,6 +989,18 @@ private fun androidFeatures(provider: MatteryLanguageProvider) {
private fun gui(provider: MatteryLanguageProvider) {
with(provider.english) {
gui("quickmove_from.restock", "Restock from storage")
gui("quickmove_from.restock_with_move", "Quickstack from storage")
gui("quickmove_from.move", "Take all")
gui("quickmove_to.restock", "Restock to storage")
gui("quickmove_to.restock_with_move", "Quickstack to storage")
gui("quickmove_to.move", "Deposit all")
gui("quickmove.exchange", "Smart exchange with storage")
gui("quickmove.exchange.desc", "Filtered slots get restocked, everything else gets quickstacked from Exopack")
gui("quickmove_hint", "Right click to show all variants")
gui("exopack.accept_wireless_charge", "Accept wireless charging")
gui("exopack.dont_accept_wireless_charge", "Do not accept wireless charging")

View File

@ -635,6 +635,8 @@ private fun blocks(provider: MatteryLanguageProvider) {
add(MBlocks.ENERGY_COUNTER[null]!!, "switch", "Сменить сторону входа")
add(MBlocks.ENERGY_COUNTER[null]!!, "limit", "Лимит ввода/вывода. -1 для отключения лимитов")
add(MBlocks.ENERGY_COUNTER[null]!!, "display_this", "Отображать эти данные на экране блока")
add(MBlocks.ENERGY_COUNTER[null]!!, "do_pull", "Забирать энергию на входе и выталкивать её на выходе")
add(MBlocks.ENERGY_COUNTER[null]!!, "dont_pull", "Не забирать энергию на входе")
addBlock(MBlocks.CHEMICAL_GENERATOR.values, "Химический генератор")
addBlock(MBlocks.CHEMICAL_GENERATOR.values, "desc", "Генерирует энергию сжигая твёрдое топливо")
@ -980,6 +982,18 @@ private fun androidFeatures(provider: MatteryLanguageProvider) {
private fun gui(provider: MatteryLanguageProvider) {
with(provider.russian) {
gui("quickmove_from.restock", "Быстрое пополнение из хранилища")
gui("quickmove_from.restock_with_move", "Быстрое складирование из хранилища")
gui("quickmove_from.move", "Взять всё")
gui("quickmove_to.restock", "Быстрое пополнение в хранилище")
gui("quickmove_to.restock_with_move", "Быстрое складирование в хранилище")
gui("quickmove_to.move", "Переместить всё")
gui("quickmove.exchange", "Умный обмен с хранилищем")
gui("quickmove.exchange.desc", "Слоты с фильтрами заполняются до максимума, всё остальное быстро складируется из экзопака")
gui("quickmove_hint", "Правый клик чтоб увидеть все варианты")
gui("exopack.accept_wireless_charge", "Принимать заряд от беспроводных зарядников")
gui("exopack.dont_accept_wireless_charge", "Не принимать заряд от беспроводных зарядников")

View File

@ -15,7 +15,6 @@ import ru.dbotthepony.mc.otm.capability.matter.IReplicationTaskProvider;
import ru.dbotthepony.mc.otm.capability.matter.IPatternStorage;
import ru.dbotthepony.mc.otm.graph.matter.MatterNode;
import ru.dbotthepony.mc.otm.graph.storage.StorageNode;
import ru.dbotthepony.mc.otm.storage.StorageStack;
import javax.annotation.Nonnull;
@ -70,4 +69,8 @@ public class MatteryCapability {
@Nonnull
@NotNull
public static final ItemCapability<IMatteryUpgrade, Void> UPGRADE = ItemCapability.createVoid(ResourceLocation.fromNamespaceAndPath(OverdriveThatMatters.MOD_ID, "machine_upgrade"), IMatteryUpgrade.class);
@Nonnull
@NotNull
public static final BlockCapability<IQuickStackContainer, Void> QUICK_STACK_CONTAINER = BlockCapability.createVoid(ResourceLocation.fromNamespaceAndPath(OverdriveThatMatters.MOD_ID, "quick_stack_container"), IQuickStackContainer.class);
}

View File

@ -0,0 +1,16 @@
package ru.dbotthepony.mc.otm.mixin;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.level.block.entity.ShulkerBoxBlockEntity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import ru.dbotthepony.mc.otm.compat.vanilla.MatteryShulkerBoxMenu;
@Mixin(ShulkerBoxBlockEntity.class)
public abstract class ShulkerBoxBlockEntityMixin {
@Overwrite(remap = false)
public AbstractContainerMenu createMenu(int p_59312_, Inventory p_59313_) {
return new MatteryShulkerBoxMenu(p_59312_, p_59313_, (ShulkerBoxBlockEntity) (Object) this);
}
}

View File

@ -12,6 +12,8 @@ import ru.dbotthepony.mc.otm.player.android.AndroidResearchResults
import ru.dbotthepony.mc.otm.player.android.feature.EnderTeleporterFeature
import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity
import ru.dbotthepony.mc.otm.block.entity.decorative.DevChestBlockEntity
import ru.dbotthepony.mc.otm.block.entity.tech.AbstractPoweredFurnaceBlockEntity
import ru.dbotthepony.mc.otm.block.entity.tech.PlatePressBlockEntity
import ru.dbotthepony.mc.otm.player.MatteryPlayer
import ru.dbotthepony.mc.otm.capability.drive.DrivePool
import ru.dbotthepony.mc.otm.client.AndroidAbilityKeyMapping
@ -41,6 +43,7 @@ import ru.dbotthepony.mc.otm.client.render.blockentity.MatterBatteryBankRenderer
import ru.dbotthepony.mc.otm.compat.curios.isCuriosLoaded
import ru.dbotthepony.mc.otm.compat.curios.onCuriosSlotModifiersUpdated
import ru.dbotthepony.mc.otm.compat.vanilla.MatteryChestMenu
import ru.dbotthepony.mc.otm.compat.vanilla.VanillaMenuTypes
import ru.dbotthepony.mc.otm.config.PlayerConfig
import ru.dbotthepony.mc.otm.config.CablesConfig
import ru.dbotthepony.mc.otm.config.ClientConfig
@ -49,6 +52,7 @@ import ru.dbotthepony.mc.otm.config.ItemsConfig
import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.config.ServerConfig
import ru.dbotthepony.mc.otm.config.ToolsConfig
import ru.dbotthepony.mc.otm.container.ItemFilter
import ru.dbotthepony.mc.otm.data.FlywheelMaterials
import ru.dbotthepony.mc.otm.data.world.BooleanProvider
import ru.dbotthepony.mc.otm.data.world.DecimalProvider
@ -131,7 +135,7 @@ object OverdriveThatMatters {
MLootNumberProviders.register(MOD_BUS)
StorageStack.register(MOD_BUS)
MatteryChestMenu.register(MOD_BUS)
VanillaMenuTypes.register(MOD_BUS)
MCreativeTabs.initialize(MOD_BUS)
@ -146,6 +150,7 @@ object OverdriveThatMatters {
AbstractRegistryAction.register(MOD_BUS)
IMatterFunction.register(MOD_BUS)
ItemFilter.register(MOD_BUS)
MRegistry.initialize(MOD_BUS)
MatterManager.initialize(MOD_BUS)
@ -247,6 +252,9 @@ object OverdriveThatMatters {
FORGE_BUS.addListener(EventPriority.NORMAL, MStructureTags::registerVillagerTrades)
FORGE_BUS.addListener(EventPriority.LOWEST, PlatePressBlockEntity::onReload)
FORGE_BUS.addListener(EventPriority.LOWEST, AbstractPoweredFurnaceBlockEntity.Companion::onReload)
if (isCuriosLoaded) {
FORGE_BUS.addListener(EventPriority.NORMAL, ::onCuriosSlotModifiersUpdated)
}

View File

@ -16,7 +16,6 @@ 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
import net.minecraft.world.MenuProvider
import net.minecraft.world.entity.LivingEntity
@ -36,8 +35,6 @@ import net.minecraft.world.phys.shapes.VoxelShape
import org.apache.logging.log4j.LogManager
import ru.dbotthepony.mc.otm.block.entity.IRedstoneControlled
import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity.Companion
import ru.dbotthepony.mc.otm.block.entity.WorkerState
import ru.dbotthepony.mc.otm.core.TooltipList
import ru.dbotthepony.mc.otm.core.TranslatableComponent
@ -149,6 +146,9 @@ open class MatteryBlock(properties: Properties = DEFAULT_PROPERTIES) : Block(pro
return getShapeForEachState(ArrayList(stateDefinition.properties), mapper)
}
protected open val neverOpensAMenu: Boolean
get() = false
override fun useWithoutItem(
blockState: BlockState,
level: Level,
@ -156,18 +156,17 @@ open class MatteryBlock(properties: Properties = DEFAULT_PROPERTIES) : Block(pro
ply: Player,
blockHitResult: BlockHitResult
): InteractionResult {
if (this is EntityBlock && !level.isClientSide) {
if (!neverOpensAMenu && this is EntityBlock) {
val tile = level.getBlockEntity(blockPos)
if (tile is MenuProvider) {
ply.openMenu(tile)
return InteractionResult.CONSUME
if (!level.isClientSide)
ply.openMenu(tile)
return InteractionResult.sidedSuccess(level.isClientSide)
}
}
if (this is EntityBlock && level.isClientSide)
return InteractionResult.SUCCESS
return super.useWithoutItem(blockState, level, blockPos, ply, blockHitResult)
}

View File

@ -117,6 +117,10 @@ class ExperienceStorage(val maxExperience: DoubleSupplier = DoubleSupplier { Dou
experience = (nbt?.asDouble ?: 0.0).coerceAtLeast(0.0)
}
private val liquidXPMilliBuckets: Int get() {
return (experience * XP_TO_LIQUID_RATIO).toInt()
}
override fun getTanks(): Int {
return 1
}
@ -125,7 +129,7 @@ class ExperienceStorage(val maxExperience: DoubleSupplier = DoubleSupplier { Dou
if (tank != 0)
return FluidStack.EMPTY
return FluidStack(MFluids.LIQUID_XP, (experience * XP_TO_LIQUID_RATIO).toInt())
return FluidStack(MFluids.LIQUID_XP, liquidXPMilliBuckets)
}
override fun getTankCapacity(tank: Int): Int {
@ -148,13 +152,13 @@ class ExperienceStorage(val maxExperience: DoubleSupplier = DoubleSupplier { Dou
}
override fun drain(maxDrain: Int, action: IFluidHandler.FluidAction): FluidStack {
val actualDrain = maxDrain.coerceAtMost((experience * XP_TO_LIQUID_RATIO).toInt()).let { it / XP_TO_LIQUID_RATIO * XP_TO_LIQUID_RATIO }
val actualDrain = maxDrain.coerceAtMost(liquidXPMilliBuckets)
if (actualDrain <= 0)
return FluidStack.EMPTY
if (action.execute())
experience -= actualDrain / XP_TO_LIQUID_RATIO
experience -= actualDrain.toDouble() / XP_TO_LIQUID_RATIO
return FluidStack(MFluids.LIQUID_XP, actualDrain)
}

View File

@ -6,13 +6,12 @@ import net.minecraft.world.level.block.state.BlockState
import ru.dbotthepony.mc.otm.capability.energy
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
import ru.dbotthepony.mc.otm.capability.extractEnergy
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.slotted.AutomationFilters
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.math.Decimal
abstract class MatteryPoweredBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: BlockPos, p_155230_: BlockState) : MatteryDeviceBlockEntity(p_155228_, p_155229_, p_155230_) {
val batteryContainer = MatteryContainer(::markDirtyFast, 1).also(::addDroppableContainer)
val batteryItemHandler = batteryContainer.handler(HandlerFilter.Dischargeable)
val batteryContainer = SlottedContainer.simple(1, AutomationFilters.DISCHARGABLE.filteredProvider, ::markDirtyFast).also(::addDroppableContainer)
open val energy: IMatteryEnergyStorage?
get() = null

View File

@ -17,12 +17,16 @@ import net.minecraft.world.level.gameevent.GameEvent
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.HandlerFilter
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.capability.IQuickStackContainer
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.container.slotted.FilteredContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.otmRandom
import ru.dbotthepony.mc.otm.menu.MatteryMenuSlot
import ru.dbotthepony.mc.otm.core.util.BlockLootTableHolder
import ru.dbotthepony.mc.otm.menu.decorative.CargoCrateMenu
import ru.dbotthepony.mc.otm.menu.makeSlots
import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
import ru.dbotthepony.mc.otm.registry.game.MSoundEvents
@ -30,20 +34,28 @@ class CargoCrateBlockEntity(
p_155229_: BlockPos,
p_155230_: BlockState
) : MatteryDeviceBlockEntity(MBlockEntities.CARGO_CRATE, p_155229_, p_155230_) {
val container = MatteryContainer(this::setChanged, CAPACITY).also(::addDroppableContainer)
private inner class Slot(container: SlottedContainer, slot: Int) : FilteredContainerSlot(container, slot) {
override fun canAutomationPlaceItem(itemStack: ItemStack): Boolean {
return super<FilteredContainerSlot>.canAutomationPlaceItem(itemStack) && loot.lootTable == null
}
override fun canAutomationTakeItem(desired: Int): Boolean {
return super.canAutomationTakeItem(desired) && loot.lootTable == null
}
}
val container = SlottedContainer.Builder()
.add(CAPACITY, ::Slot)
.onChanged(::setChanged)
.build()
.also(::addDroppableContainer)
init {
exposeSideless(MatteryCapability.QUICK_STACK_CONTAINER, IQuickStackContainer.Simple(makeSlots(container, ::MatteryMenuSlot)))
}
private var interactingPlayers = 0
val handler = container.handler(object : HandlerFilter {
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
return loot.lootTable == null
}
override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
return loot.lootTable == null
}
})
override fun dropItems(
oldBlockState: BlockState,
level: ServerLevel,
@ -81,7 +93,7 @@ class CargoCrateBlockEntity(
}
init {
exposeGlobally(Capabilities.ItemHandler.BLOCK, handler)
exposeGlobally(Capabilities.ItemHandler.BLOCK, container)
savetablesLevel.stateful(::container, INVENTORY_KEY)
}

View File

@ -18,9 +18,12 @@ import ru.dbotthepony.mc.otm.capability.item.CombinedItemHandler
import ru.dbotthepony.mc.otm.capability.fluid.BlockMatteryFluidHandler
import ru.dbotthepony.mc.otm.capability.moveFluid
import ru.dbotthepony.mc.otm.config.ItemsConfig
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.get
import ru.dbotthepony.mc.otm.container.slotted.AutomationFilter
import ru.dbotthepony.mc.otm.container.slotted.AutomationFilters
import ru.dbotthepony.mc.otm.container.slotted.ContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.FilteredContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.isNotEmpty
import ru.dbotthepony.mc.otm.core.isNotSameAs
import ru.dbotthepony.mc.otm.menu.decorative.FluidTankMenu
@ -37,28 +40,42 @@ class FluidTankBlockEntity(blockPos: BlockPos, blockState: BlockState) : Mattery
}
}), FluidStack.OPTIONAL_STREAM_CODEC.wrap()))
val fillInput = MatteryContainer(::markDirtyFast, 1).also(::addDroppableContainer)
val drainInput = MatteryContainer(::markDirtyFast, 1).also(::addDroppableContainer)
val output = MatteryContainer(::markDirtyFast, 1).also(::addDroppableContainer)
private inner class FillSlot(container: SlottedContainer, slot: Int) : FilteredContainerSlot(container, slot) {
override fun canAutomationPlaceItem(itemStack: ItemStack): Boolean {
if (!super.canAutomationPlaceItem(itemStack))
return false
if (fluid.isEmpty) {
return itemStack.getCapability(Capabilities.FluidHandler.ITEM)?.let { it.tanks > 0 } ?: false
}
return itemStack.getCapability(Capabilities.FluidHandler.ITEM)?.let { it.fill(fluid[0], IFluidHandler.FluidAction.SIMULATE) > 0 } ?: false
}
override fun canAutomationTakeItem(desired: Int): Boolean {
return super.canAutomationTakeItem(desired) && !canAutomationPlaceItem(item)
}
}
val inputContainer = SlottedContainer.Builder()
.add(DRAIN_TAG, AutomationFilters.DRAINABLE_FLUID_CONTAINERS.filteredProvider)
.add(FILL_TAG, ::FillSlot)
.onChanged(::markDirtyFast)
.build()
.also(::addDroppableContainer)
val outputContainer = SlottedContainer.Builder()
.add(AutomationFilters.ONLY_OUT.simpleProvider)
.onChanged(::markDirtyFast)
.build()
.also(::addDroppableContainer)
private val fillSlot = inputContainer[FILL_TAG]
private val drainSlot = inputContainer[DRAIN_TAG]
val itemConfig = ConfigurableItemHandler(
input = CombinedItemHandler(
drainInput.handler(HandlerFilter.DrainableFluidContainers),
fillInput.handler(object : HandlerFilter {
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
if (fluid.isEmpty) {
return stack.getCapability(Capabilities.FluidHandler.ITEM)?.let { it.tanks > 0 } ?: false
}
return stack.getCapability(Capabilities.FluidHandler.ITEM)?.let { it.fill(fluid[0], IFluidHandler.FluidAction.SIMULATE) > 0 } ?: false
}
override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
return !canInsert(slot, stack)
}
})
),
output = output.handler(HandlerFilter.OnlyOut),
input = inputContainer,
output = outputContainer,
frontDefault = ItemHandlerMode.INPUT_OUTPUT,
backDefault = ItemHandlerMode.INPUT_OUTPUT,
leftDefault = ItemHandlerMode.INPUT_OUTPUT,
@ -71,20 +88,19 @@ class FluidTankBlockEntity(blockPos: BlockPos, blockState: BlockState) : Mattery
init {
savetables.stateful(::fluid, FLUID_KEY)
savetables.stateful(::fillInput)
savetables.stateful(::drainInput)
savetables.stateful(::output)
savetables.stateful(::inputContainer)
savetables.stateful(::outputContainer)
}
private fun drainItem() {
val item = drainInput[0]
val item = drainSlot.item
if (item.isNotEmpty) {
val cap = (if (item.count == 1) item else item.copyWithCount(1)).getCapability(Capabilities.FluidHandler.ITEM)
if (cap == null) {
if (output.consumeItem(item, simulate = false)) {
drainInput.setChanged(0)
if (outputContainer.consumeItem(item, simulate = false)) {
drainSlot.setChanged()
}
return
@ -95,17 +111,17 @@ class FluidTankBlockEntity(blockPos: BlockPos, blockState: BlockState) : Mattery
val moved0 = moveFluid(source = cap, destination = fluid)
if (moved0.isNotEmpty) {
drainInput[0] = cap.container
drainSlot.item = cap.container
if (output.consumeItem(drainInput[0], simulate = false)) {
drainInput.setChanged(0)
if (outputContainer.consumeItem(drainSlot.item, simulate = false)) {
drainSlot.setChanged()
}
}
} else {
val moved0 = moveFluid(source = cap, destination = fluid, actuallyFill = false)
if (moved0.isNotEmpty) {
if (output.consumeItem(cap.container, simulate = true)) {
if (outputContainer.consumeItem(cap.container, simulate = true)) {
val cap1 = item.copyWithCount(1).getCapability(Capabilities.FluidHandler.ITEM) ?: throw ConcurrentModificationException()
val moved1 = moveFluid(source = cap1, destination = fluid)
@ -114,9 +130,9 @@ class FluidTankBlockEntity(blockPos: BlockPos, blockState: BlockState) : Mattery
LOGGER.error("Error moving fluids in Fluid tank at $blockPos: moved $moved0 during simulation from ${cap.container}, moved $moved1 from ${cap1.container} during execution. This is likely a bug in OTM or other mod!")
} else {
item.count--
drainInput.setChanged(0)
drainSlot.setChanged()
if (!output.consumeItem(cap1.container, simulate = false)) {
if (!outputContainer.consumeItem(cap1.container, simulate = false)) {
LOGGER.error("Unable to insert ${cap1.container} into output slot of Fluid tank at $blockPos, popping item in world instead to avoid item loss! This is likely a bug in OTM or other mod!")
(level as? ServerLevel)?.addFreshEntity(ItemEntity(level!!, blockPos.x.toDouble(), blockPos.y.toDouble(), blockPos.z.toDouble(), cap1.container))
}
@ -129,14 +145,14 @@ class FluidTankBlockEntity(blockPos: BlockPos, blockState: BlockState) : Mattery
}
private fun fillItem() {
val item = fillInput[0]
val item = fillSlot.item
if (item.isNotEmpty) {
val cap = (if (item.count == 1) item else item.copyWithCount(1)).getCapability(Capabilities.FluidHandler.ITEM)
if (cap == null) {
if (output.consumeItem(item, simulate = false)) {
fillInput.setChanged(0)
if (outputContainer.consumeItem(item, simulate = false)) {
fillSlot.setChanged()
}
return
@ -147,17 +163,17 @@ class FluidTankBlockEntity(blockPos: BlockPos, blockState: BlockState) : Mattery
val moved0 = moveFluid(source = fluid, destination = cap)
if (moved0.isNotEmpty) {
fillInput[0] = cap.container
fillSlot.item = cap.container
if (output.consumeItem(fillInput[0], simulate = false)) {
fillInput.setChanged(0)
if (outputContainer.consumeItem(fillSlot.item, simulate = false)) {
fillSlot.setChanged()
}
}
} else {
val moved0 = moveFluid(source = fluid, destination = cap, actuallyDrain = false)
if (moved0.isNotEmpty) {
if (output.consumeItem(cap.container, simulate = true)) {
if (outputContainer.consumeItem(cap.container, simulate = true)) {
val cap1 = item.copyWithCount(1).getCapability(Capabilities.FluidHandler.ITEM) ?: throw ConcurrentModificationException()
val moved1 = moveFluid(source = fluid, destination = cap1)
@ -166,9 +182,9 @@ class FluidTankBlockEntity(blockPos: BlockPos, blockState: BlockState) : Mattery
LOGGER.error("Error moving fluids in Fluid tank at $blockPos: moved $moved0 during simulation from ${cap.container}, moved $moved1 from ${cap1.container} during execution. This is likely a bug in OTM or other mod!")
} else {
item.count--
fillInput.setChanged(0)
fillSlot.setChanged()
if (!output.consumeItem(cap1.container, simulate = false)) {
if (!outputContainer.consumeItem(cap1.container, simulate = false)) {
LOGGER.error("Unable to insert ${cap1.container} into output slot of Fluid tank at $blockPos, popping item in world instead to avoid item loss! This is likely a bug in OTM or other mod!")
(level as? ServerLevel)?.addFreshEntity(ItemEntity(level!!, blockPos.x.toDouble(), blockPos.y.toDouble(), blockPos.z.toDouble(), cap1.container))
}
@ -194,5 +210,8 @@ class FluidTankBlockEntity(blockPos: BlockPos, blockState: BlockState) : Mattery
companion object {
const val FLUID_KEY = "fluid"
private val LOGGER = LogManager.getLogger()
private val FILL_TAG = SlottedContainer.tag<FilteredContainerSlot>()
private val DRAIN_TAG = SlottedContainer.tag<FilteredContainerSlot>()
}
}

View File

@ -6,6 +6,7 @@ import net.minecraft.core.BlockPos
import net.minecraft.core.HolderLookup
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerLevel
import net.minecraft.world.Containers
import net.minecraft.world.MenuProvider
@ -19,12 +20,16 @@ import net.minecraft.world.item.crafting.SmokingRecipe
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.state.BlockState
import ru.dbotthepony.kommons.util.Delegate
import ru.dbotthepony.kommons.util.KOptional
import ru.dbotthepony.mc.otm.block.IBlockWithCustomName
import ru.dbotthepony.mc.otm.block.decorative.GrillBlock
import ru.dbotthepony.mc.otm.block.entity.ExperienceStorage
import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity
import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.set
import ru.dbotthepony.mc.otm.container.slotted.AutomationFilters
import ru.dbotthepony.mc.otm.container.slotted.ContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.TextComponent
import ru.dbotthepony.mc.otm.core.get
import ru.dbotthepony.mc.otm.core.isNotEmpty
@ -38,47 +43,45 @@ import ru.dbotthepony.mc.otm.menu.decorative.GrillMenu
import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
class GrillBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryBlockEntity(MBlockEntities.GRILL, blockPos, blockState), MenuProvider, IBlockWithCustomName {
val fuelSlot = object : MatteryContainer(this@GrillBlockEntity::markDirtyFast, 1) {
override fun getMaxStackSize(): Int {
return 4
}
}
val fuelSlot = SlottedContainer.simple(1, FUEL_SLOT_PROVIDER, ::markDirtyFast)
val inputSlots = object : MatteryContainer(this@GrillBlockEntity::markDirtyFast, SLOTS) {
override fun getMaxStackSize(): Int {
return 1
}
private inner class InputSlot(container: SlottedContainer, slot: Int) : ContainerSlot(container, slot) {
override val maxStackSize: Int
get() = 1
private fun clearSlot(slot: Int) {
private fun clearSlot() {
inputProgress[slot] = 0
outputs[slot] = null
outputsRecipeNames[slot] = null
activeSlots.rem(slot)
}
override fun setChanged(slot: Int, new: ItemStack, old: ItemStack) {
super.setChanged(slot, new, old)
override fun notifyChanged(old: ItemStack) {
super.notifyChanged(old)
if (new.isEmpty || new.count > 1) {
clearSlot(slot)
if (item.isEmpty || item.count > 1) {
clearSlot()
} else {
val level = level
if (level == null) {
clearSlot(slot)
clearSlot()
return
}
val input = SingleRecipeInput(new)
val input = SingleRecipeInput(item)
val result = level.recipeManager
.byType(RecipeType.SMOKING)
.firstOrNull { it.value.matches(input, level) }
if (result == null) {
clearSlot(slot)
clearSlot()
} else {
if (outputs[slot] != result.value)
if (outputsRecipeNames[slot] != result.id)
inputProgress[slot] = 0
outputsRecipeNames[slot] = result.id
outputs[slot] = result.value
activeSlots.add(slot)
}
@ -86,16 +89,18 @@ class GrillBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryBloc
}
}
val inputSlots = SlottedContainer.simple(SLOTS, ::InputSlot, ::markDirtyFast)
override var customDisplayName: Component? = null
override fun createMenu(p_39954_: Int, p_39955_: Inventory, p_39956_: Player): AbstractContainerMenu {
return GrillMenu(p_39954_, p_39955_, this)
override fun createMenu(containerID: Int, inventory: Inventory, player: Player): AbstractContainerMenu {
return GrillMenu(containerID, inventory, this)
}
override fun getDisplayName(): Component {
return customDisplayName ?: level?.getBlockState(blockPos)?.block?.name ?: TextComponent("null at $blockPos")
}
private val outputsRecipeNames = arrayOfNulls<ResourceLocation>(SLOTS)
private val outputs = arrayOfNulls<SmokingRecipe>(SLOTS)
private val activeSlots = IntArrayList()
private val inputProgress = IntArray(SLOTS)
@ -132,9 +137,12 @@ class GrillBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryBloc
savetables.stateful(::fuelSlot)
savetables.stateful(::experience)
outputsRecipeNames.fill(null)
// TODO: could have been done as list (and get more space efficient storage), but we use an array, oh well
for (i in inputProgress.indices) {
savetables.codec(Delegate.Of({ inputProgress[i] }, { inputProgress[i] = it }), progressCodec, "inputProgress${i}")
savetables.codecNullable(Delegate.Of({ outputsRecipeNames[i] }, { outputsRecipeNames[i] = it }), ResourceLocation.CODEC, "recipeName${i}")
}
// addDroppableContainer(inputSlots)
@ -207,5 +215,6 @@ class GrillBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryBloc
const val SLOTS = 6
private val progressCodec = Codec.INT.minRange(0)
val FUEL_SLOT_PROVIDER = ContainerSlot.Simple(maxStackSize = 4, filter = AutomationFilters.CHEMICAL_FUEL)
}
}

View File

@ -18,8 +18,8 @@ import net.neoforged.neoforge.capabilities.Capabilities
import net.neoforged.neoforge.fluids.FluidStack
import net.neoforged.neoforge.fluids.capability.IFluidHandler
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.slotted.ContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.immutableList
import ru.dbotthepony.mc.otm.core.immutableMap
import ru.dbotthepony.mc.otm.core.isNotEmpty
@ -34,7 +34,39 @@ class PainterBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryDe
return PainterMenu(containerID, inventory, this)
}
val dyeInput = MatteryContainer(this::markDirtyFast, 1)
private inner class InputSlot(container: SlottedContainer, slot: Int) : ContainerSlot(container, slot) {
override fun canAutomationPlaceItem(itemStack: ItemStack): Boolean {
if (!super.canAutomationPlaceItem(itemStack))
return false
if (waterStored() < MAX_WATER_STORAGE) {
itemStack.getCapability(Capabilities.FluidHandler.ITEM)?.let {
val drain = it.drain(FluidStack(Fluids.WATER, MAX_WATER_STORAGE - waterStored()), IFluidHandler.FluidAction.SIMULATE)
if (drain.isNotEmpty) {
return true
}
}
}
val dye = DyeColor.getColor(itemStack) ?: return false
return dyeStored(dye) + HUE_PER_ITEM <= MAX_STORAGE
}
override fun canAutomationTakeItem(desired: Int): Boolean {
return false
}
override fun modifyAutomationPlaceCount(itemStack: ItemStack): Int {
if (itemStack.getCapability(Capabilities.FluidHandler.ITEM) != null)
return 1
val dye = DyeColor.getColor(itemStack) ?: return 0
return itemStack.count.coerceAtMost((MAX_STORAGE - dyeStored(dye)) / HUE_PER_ITEM - item.count)
}
}
val dyeInput = SlottedContainer.simple(1, ::InputSlot, ::markDirtyFast)
// null - water
val dyeStored = Object2IntArrayMap<DyeColor?>()
@ -108,34 +140,7 @@ class PainterBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryDe
markDirtyFast()
}
val config = ConfigurableItemHandler(input = dyeInput.handler(object : HandlerFilter {
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
if (waterStored() < MAX_WATER_STORAGE) {
stack.getCapability(Capabilities.FluidHandler.ITEM)?.let {
val drain = it.drain(FluidStack(Fluids.WATER, MAX_WATER_STORAGE - waterStored()), IFluidHandler.FluidAction.SIMULATE)
if (drain.isNotEmpty) {
return true
}
}
}
val dye = DyeColor.entries.firstOrNull { stack.`is`(it.tag) } ?: return false
return dyeStored(dye) + HUE_PER_ITEM <= MAX_STORAGE
}
override fun modifyInsertCount(slot: Int, stack: ItemStack, existing: ItemStack, simulate: Boolean): Int {
if (!ItemStack.isSameItemSameComponents(stack, existing))
return super.modifyInsertCount(slot, stack, existing, simulate)
val dye = DyeColor.entries.firstOrNull { stack.`is`(it.tag) } ?: return 0
return stack.count.coerceAtMost((MAX_STORAGE - dyeStored(dye)) / HUE_PER_ITEM - existing.count)
}
override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
return false
}
}))
val config = ConfigurableItemHandler(input = dyeInput)
fun waterStored(): Int {
return dyeStored.getInt(null)

View File

@ -21,39 +21,37 @@ import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.capability.matter.MatterStorageImpl
import ru.dbotthepony.mc.otm.capability.matter.ProfiledMatterStorage
import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.UpgradeContainer
import ru.dbotthepony.mc.otm.container.slotted.FilteredContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.menu.matter.MatterBottlerMenu
import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.util.countingLazy
import ru.dbotthepony.mc.otm.graph.matter.SimpleMatterNode
import java.util.function.BooleanSupplier
class MatterBottlerBlockEntity(blockPos: BlockPos, blockState: BlockState) :
MatteryPoweredBlockEntity(MBlockEntities.MATTER_BOTTLER, blockPos, blockState) {
val upgrades = UpgradeContainer(3, UpgradeType.BASIC_MATTER, BooleanSupplier { blockState.getValue(WorkerState.SEMI_WORKER_STATE) !== WorkerState.IDLE }, ::markDirtyFast)
val upgrades = UpgradeContainer(3, UpgradeType.BASIC_MATTER, BooleanSupplier { this.blockState.getValue(WorkerState.SEMI_WORKER_STATE) !== WorkerState.IDLE }, ::markDirtyFast)
override val energy = ProfiledEnergyStorage(WorkerEnergyStorage(this::markDirtyFast, upgrades.transform(MachinesConfig.MatterBottler.VALUES)))
val energyConfig = ConfigurableEnergy(energy)
private inner class Container : MatteryContainer(3) {
init {
addDroppableContainer(this)
}
override fun getMaxStackSize(slot: Int, itemStack: ItemStack): Int {
return 1
}
override fun setChanged(slot: Int, new: ItemStack, old: ItemStack) {
markDirtyFast()
updateBlockState()
private inner class BottlingSlot(container: SlottedContainer, slot: Int) : FilteredContainerSlot(container, slot) {
override fun canAutomationPlaceItem(itemStack: ItemStack): Boolean {
return super.canAutomationPlaceItem(itemStack) && isBottling && itemStack.getCapability(MatteryCapability.MATTER_ITEM)?.receiveMatterChecked(Decimal.ONE, true)?.isPositive == true
}
}
val unbottling: MatteryContainer = Container()
val bottling: MatteryContainer = Container()
private inner class UnBottlingSlot(container: SlottedContainer, slot: Int) : FilteredContainerSlot(container, slot) {
override fun canAutomationPlaceItem(itemStack: ItemStack): Boolean {
return super.canAutomationPlaceItem(itemStack) && !isBottling && itemStack.getCapability(MatteryCapability.MATTER_ITEM)?.extractMatterChecked(Decimal.ONE, true)?.isPositive == true
}
}
val unbottling = SlottedContainer.simple(3, ::UnBottlingSlot, ::updateBlockState)
val bottling = SlottedContainer.simple(3, ::BottlingSlot, ::updateBlockState)
var isBottling: Boolean = true
set(value) {
@ -63,11 +61,11 @@ class MatterBottlerBlockEntity(blockPos: BlockPos, blockState: BlockState) :
this.markDirtyFast()
if (value) {
inputHandler.parent = bottlingHandler
outputHandler.parent = unbottlingHandler
inputHandler.parent = bottling
outputHandler.parent = unbottling
} else {
inputHandler.parent = unbottlingHandler
outputHandler.parent = bottlingHandler
inputHandler.parent = unbottling
outputHandler.parent = bottling
}
updateBlockState()
@ -79,25 +77,13 @@ class MatterBottlerBlockEntity(blockPos: BlockPos, blockState: BlockState) :
this.markDirtyFast()
}
val bottlingHandler = bottling.handler(object : HandlerFilter {
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
return isBottling && stack.getCapability(MatteryCapability.MATTER_ITEM)?.let { it.matterFlow.input && it.missingMatter.isPositive } ?: false
}
})
val unbottlingHandler = unbottling.handler(object : HandlerFilter {
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
return !isBottling && stack.getCapability(MatteryCapability.MATTER_ITEM)?.let { it.matterFlow.output && it.storedMatter.isPositive } ?: false
}
})
private val inputHandler = ProxiedItemHandler(bottlingHandler)
private val outputHandler = ProxiedItemHandler(unbottlingHandler)
private val inputHandler = ProxiedItemHandler(bottling)
private val outputHandler = ProxiedItemHandler(unbottling)
val itemConfig = ConfigurableItemHandler(
input = inputHandler,
output = outputHandler,
battery = batteryItemHandler
battery = batteryContainer
)
val matter: ProfiledMatterStorage<MatterStorageImpl> = ProfiledMatterStorage(object : MatterStorageImpl(this@MatterBottlerBlockEntity::markDirtyFast, FlowDirection.BI_DIRECTIONAL, upgrades.matterCapacity(MachinesConfig.MatterBottler.VALUES::matterCapacity)) {
@ -141,12 +127,12 @@ class MatterBottlerBlockEntity(blockPos: BlockPos, blockState: BlockState) :
matterNode.isValid = false
}
private fun updateBlockState(container: MatteryContainer) {
private fun updateBlockState(container: SlottedContainer) {
val level = level as? ServerLevel ?: return
var state = blockState
for (i in 0 .. 2) {
val desired = !container.getItem(i).isEmpty && container.getItem(i).getCapability(MatteryCapability.MATTER_ITEM) != null
for ((i, slot) in container.slotIterator().withIndex()) {
val desired = !slot.isEmpty && slot.item.getCapability(MatteryCapability.MATTER_ITEM) != null
if (state.getValue(MatterBottlerBlock.SLOT_PROPERTIES[i]) != desired) {
state = state.setValue(MatterBottlerBlock.SLOT_PROPERTIES[i], desired)
@ -154,26 +140,156 @@ class MatterBottlerBlockEntity(blockPos: BlockPos, blockState: BlockState) :
}
if (state !== blockState) {
level.setBlock(blockPos, state, Block.UPDATE_CLIENTS)
level.setBlock(blockPos, state, Block.UPDATE_ALL)
}
}
private fun updateBlockState() {
markDirtyFast()
if (isBottling)
updateBlockState(bottling)
else
updateBlockState(unbottling)
}
private val workerState by countingLazy(blockStateChangesCounter) {
this.blockState.getValue(WorkerState.SEMI_WORKER_STATE)
}
private fun blockstateToWorking() {
if (blockState.getValue(WorkerState.SEMI_WORKER_STATE) !== WorkerState.WORKING) {
level?.setBlock(blockPos, blockState.setValue(WorkerState.SEMI_WORKER_STATE, WorkerState.WORKING), Block.UPDATE_CLIENTS)
if (workerState !== WorkerState.WORKING) {
level?.setBlock(blockPos, blockState.setValue(WorkerState.SEMI_WORKER_STATE, WorkerState.WORKING), Block.UPDATE_ALL)
}
}
private fun blockstateToIdle() {
if (blockState.getValue(WorkerState.SEMI_WORKER_STATE) !== WorkerState.IDLE) {
level?.setBlock(blockPos, blockState.setValue(WorkerState.SEMI_WORKER_STATE, WorkerState.IDLE), Block.UPDATE_CLIENTS)
if (workerState !== WorkerState.IDLE) {
level?.setBlock(blockPos, blockState.setValue(WorkerState.SEMI_WORKER_STATE, WorkerState.IDLE), Block.UPDATE_ALL)
}
}
private fun tickBottling() {
var isWorking = false
var hasCapacitors = false
for (slot in bottling.slotIterator()) {
val item = slot.item
val capability = item.getCapability(MatteryCapability.MATTER_ITEM) ?: continue
if (!capability.receiveMatterChecked(Decimal.LONG_MAX_VALUE, true).isPositive) {
unbottling.consumeItem(item, false)
slot.setChanged()
continue
}
hasCapacitors = true
initialCapacity = initialCapacity ?: capability.storedMatter
val rate = MachinesConfig.MatterBottler.RATE * (1.0 + upgrades.speedBonus)
val energyRate = MachinesConfig.MatterBottler.VALUES.energyConsumption * (1.0 + upgrades.speedBonus)
if (matter.storedMatter < rate) {
val toExtract = matter.missingMatter
.coerceAtMost(rate * 200)
.coerceAtMost(capability.missingMatter - matter.storedMatter)
matter.receiveMatter(matterNode.graph.extractMatter(toExtract, false), false)
}
if (matter.storedMatter.isPositive) {
val energyRatio = if (energyRate <= Decimal.ZERO) Decimal.ONE else energy.extractEnergy(energyRate, true) / energyRate
val matterRatio = matter.extractMatter(capability.receiveMatter(rate.coerceAtMost(matter.storedMatter), true), true) / rate
val minRatio = minOf(matterRatio, energyRatio)
if (minRatio > Decimal.ZERO) {
isWorking = true
energy.extractEnergy(energyRate * minRatio, false)
matter.extractMatter(capability.receiveMatter(rate * minRatio, false), false)
workProgress = ((capability.storedMatter - initialCapacity!!) / capability.maxStoredMatter).toFloat()
slot.setChanged()
}
} else {
if (spitItemsWhenCantWork) {
unbottling.consumeItem(item, false)
slot.setChanged()
}
}
break
}
if (isWorking) {
blockstateToWorking()
} else {
blockstateToIdle()
}
if (!hasCapacitors) {
matter.extractMatter(matterNode.graph.receiveMatter(matter.storedMatter, false), false)
initialCapacity = null
workProgress = 0f
}
}
private fun tickUnbottling() {
matter.extractMatter(matterNode.graph.receiveMatter(matter.storedMatter, false), false)
if (!matter.missingMatter.isPositive) {
if (spitItemsWhenCantWork) {
for (slot in unbottling.slotIterator()) {
bottling.consumeItem(slot.item, false)
slot.setChanged()
}
}
blockstateToIdle()
return
}
var any = false
var hasCapacitors = false
for (slot in unbottling.slotIterator()) {
val item = slot.item
val it = item.getCapability(MatteryCapability.MATTER_ITEM) ?: continue
if (!it.extractMatterChecked(Decimal.LONG_MAX_VALUE, true).isPositive) {
bottling.consumeItem(item, false)
slot.setChanged()
continue
}
initialCapacity = initialCapacity ?: it.storedMatter
hasCapacitors = true
val rate = MachinesConfig.MatterBottler.RATE * (1.0 + upgrades.speedBonus)
val energyRate = MachinesConfig.MatterBottler.VALUES.energyConsumption * (1.0 + upgrades.speedBonus)
val energyRatio = if (energyRate <= Decimal.ZERO) Decimal.ONE else energy.extractEnergy(energyRate, true) / energyRate
val matterRatio = matter.receiveMatter(it.extractMatterChecked(rate, true), true) / rate
val minRatio = minOf(energyRatio, matterRatio)
if (minRatio > Decimal.ZERO) {
any = true
energy.extractEnergy(energyRate * energyRatio, false)
matter.receiveMatter(it.extractMatterChecked(rate * minRatio, false), false)
workProgress = 1f - (it.storedMatter / initialCapacity!!).toFloat()
}
break
}
if (any) {
blockstateToWorking()
} else {
blockstateToIdle()
}
if (!hasCapacitors) {
initialCapacity = null
workProgress = 0f
}
}
@ -186,105 +302,9 @@ class MatterBottlerBlockEntity(blockPos: BlockPos, blockState: BlockState) :
}
if (isBottling) {
var any = false
var idle = false
for (slot in bottling.slotIterator()) {
val item = slot.item
item.getCapability(MatteryCapability.MATTER_ITEM)?.let {
if (!it.missingMatter.isPositive) {
unbottling.consumeItem(item, false)
slot.setChanged()
} else {
any = true
initialCapacity = initialCapacity ?: it.storedMatter
val rate = MachinesConfig.MatterBottler.RATE * (1.0 + upgrades.speedBonus)
if (matter.storedMatter < rate) {
matter.receiveMatter(matterNode.graph.extractMatter(matter.missingMatter
.coerceAtMost(rate * 200)
.coerceAtMost(it.missingMatter - matter.storedMatter), false), false)
}
if (matter.storedMatter.isPositive) {
matter.extractMatter(it.receiveMatter(rate.coerceAtMost(matter.storedMatter), false), false)
if (!it.missingMatter.isPositive) {
initialCapacity = null
workProgress = 0f
} else {
workProgress = ((it.storedMatter - initialCapacity!!) / it.maxStoredMatter).toFloat()
}
} else {
idle = true
if (spitItemsWhenCantWork) {
unbottling.consumeItem(item, false)
slot.setChanged()
}
}
}
}
if (any) {
break
}
}
if (any && !idle) {
blockstateToWorking()
} else {
matter.extractMatter(matterNode.graph.receiveMatter(matter.storedMatter, false), false)
blockstateToIdle()
}
tickBottling()
} else {
matter.extractMatter(matterNode.graph.receiveMatter(matter.storedMatter, false), false)
if (!matter.missingMatter.isPositive) {
if (spitItemsWhenCantWork) {
for (slot in unbottling.slotIterator()) {
bottling.consumeItem(slot.item, false)
slot.setChanged()
}
}
blockstateToIdle()
return
}
var any = false
for (slot in unbottling.slotIterator()) {
val item = slot.item
item.getCapability(MatteryCapability.MATTER_ITEM)?.let {
if (!it.storedMatter.isPositive) {
bottling.consumeItem(item, false)
slot.setChanged()
} else {
any = true
initialCapacity = initialCapacity ?: it.storedMatter
matter.receiveMatter(it.extractMatter(MachinesConfig.MatterBottler.RATE, false), false)
if (!it.storedMatter.isPositive) {
initialCapacity = null
workProgress = 0f
} else {
workProgress = 1f - (it.storedMatter / initialCapacity!!).toFloat()
}
}
}
if (any) {
break
}
}
if (any) {
blockstateToWorking()
} else {
blockstateToIdle()
}
tickUnbottling()
}
}
}

View File

@ -16,16 +16,15 @@ import ru.dbotthepony.mc.otm.capability.FlowDirection
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.matter.IMatterStorage
import ru.dbotthepony.mc.otm.capability.matter.ProfiledMatterStorage
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.slotted.ContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.immutableList
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.graph.matter.SimpleMatterNode
import ru.dbotthepony.mc.otm.menu.matter.MatterCapacitorBankMenu
import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
class MatterCapacitorBankBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : MatteryDeviceBlockEntity(
MBlockEntities.MATTER_CAPACITOR_BANK, p_155229_, p_155230_), IMatterStorage {
class MatterCapacitorBankBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : MatteryDeviceBlockEntity(MBlockEntities.MATTER_CAPACITOR_BANK, p_155229_, p_155230_), IMatterStorage {
var gaugeLevel by syncher.float()
private set
@ -40,10 +39,9 @@ class MatterCapacitorBankBlockEntity(p_155229_: BlockPos, p_155230_: BlockState)
var summ = Decimal.ZERO
for (stack in container)
if (!stack.isEmpty)
stack.getCapability(MatteryCapability.MATTER_ITEM)?.let {
summ += it.storedMatter
}
stack.getCapability(MatteryCapability.MATTER_ITEM)?.let {
summ += it.storedMatter
}
return summ
}
@ -51,19 +49,35 @@ class MatterCapacitorBankBlockEntity(p_155229_: BlockPos, p_155230_: BlockState)
throw UnsupportedOperationException()
}
override val maxStoredMatter: Decimal
get() {
override val maxStoredMatter: Decimal get() {
var summ = Decimal.ZERO
for (stack in container)
if (!stack.isEmpty)
stack.getCapability(MatteryCapability.MATTER_ITEM)?.let {
summ += it.maxStoredMatter
}
stack.getCapability(MatteryCapability.MATTER_ITEM)?.let {
summ += it.maxStoredMatter
}
return summ
}
private fun updateGaugeLevel() {
var stored = Decimal.ZERO
var maxStored = Decimal.ZERO
for (stack in container) {
val cap = stack.getCapability(MatteryCapability.MATTER_ITEM) ?: continue
stored += cap.storedMatter
maxStored += cap.maxStoredMatter
}
gaugeLevel = stored.percentage(maxStored)
}
override fun tick() {
super.tick()
updateGaugeLevel()
}
override fun receiveMatter(howMuch: Decimal, simulate: Boolean): Decimal {
if (!howMuch.isPositive)
return Decimal.ZERO
@ -73,21 +87,18 @@ class MatterCapacitorBankBlockEntity(p_155229_: BlockPos, p_155230_: BlockState)
var summ = Decimal.ZERO
for (stack in container) {
if (!stack.isEmpty) {
stack.getCapability(MatteryCapability.MATTER_ITEM)?.let {
val diff = it.receiveMatterChecked(howMuch, simulate)
summ += diff
howMuch -= diff
}
val it = stack.getCapability(MatteryCapability.MATTER_ITEM) ?: continue
val diff = it.receiveMatterChecked(howMuch, simulate)
summ += diff
howMuch -= diff
if (howMuch.isZero) {
break
}
if (howMuch.isZero) {
break
}
}
if (summ.isPositive && !simulate) {
gaugeLevel = storedMatter.percentage(maxStoredMatter)
if (!simulate && !summ.isZero) {
markDirtyFast()
}
return summ
@ -102,21 +113,18 @@ class MatterCapacitorBankBlockEntity(p_155229_: BlockPos, p_155230_: BlockState)
var summ = Decimal.ZERO
for (stack in container) {
if (!stack.isEmpty) {
stack.getCapability(MatteryCapability.MATTER_ITEM)?.let {
val diff = it.extractMatterChecked(howMuch, simulate)
summ += diff
howMuch -= diff
}
val it = stack.getCapability(MatteryCapability.MATTER_ITEM) ?: continue
val diff = it.extractMatterChecked(howMuch, simulate)
summ += diff
howMuch -= diff
if (howMuch.isZero) {
break
}
if (howMuch.isZero) {
break
}
}
if (summ.isPositive && !simulate) {
gaugeLevel = storedMatter.percentage(maxStoredMatter)
if (!simulate && !summ.isZero) {
markDirtyFast()
}
return summ
@ -125,31 +133,38 @@ class MatterCapacitorBankBlockEntity(p_155229_: BlockPos, p_155230_: BlockState)
override val matterFlow: FlowDirection
get() = FlowDirection.BI_DIRECTIONAL
val container = object : MatteryContainer(::markDirtyFast, BatteryBankBlockEntity.CAPACITY) {
override fun setChanged(slot: Int, new: ItemStack, old: ItemStack) {
super.setChanged(slot, new, old)
capacitorStatus[slot].value = new.getCapability(MatteryCapability.MATTER_ITEM) != null
gaugeLevel = storedMatter.percentage(maxStoredMatter)
private inner class Slot(container: SlottedContainer, slot: Int) : ContainerSlot(container, slot) {
override fun notifyChanged(old: ItemStack) {
super.notifyChanged(old)
capacitorStatus[slot].value = item.getCapability(MatteryCapability.MATTER_ITEM) != null
}
override fun getMaxStackSize(): Int = 1
}.also(::addDroppableContainer)
val itemConfig = ConfigurableItemHandler(inputOutput = container.handler(object : HandlerFilter {
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
return stack.getCapability(MatteryCapability.MATTER_ITEM) != null
override fun canAutomationPlaceItem(itemStack: ItemStack): Boolean {
return super.canAutomationPlaceItem(itemStack) && itemStack.getCapability(MatteryCapability.MATTER_ITEM) != null
}
override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
stack.getCapability(MatteryCapability.MATTER_ITEM)?.let {
override fun canAutomationTakeItem(desired: Int): Boolean {
item.getCapability(MatteryCapability.MATTER_ITEM)?.let {
if (it.storedMatter.isPositive) {
return false
}
}
return true
return super.canAutomationTakeItem(desired)
}
}))
override val maxStackSize: Int
get() = 1
}
private fun containerUpdated() {
markDirtyFast()
updateGaugeLevel()
}
val container = SlottedContainer.simple(BatteryBankBlockEntity.CAPACITY, ::Slot, ::containerUpdated).also(::addDroppableContainer)
val itemConfig = ConfigurableItemHandler(inputOutput = container)
val capacitorStatus = immutableList(BatteryBankBlockEntity.CAPACITY) {
syncher.boolean(false)

View File

@ -21,8 +21,10 @@ import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.capability.matter.MatterStorageImpl
import ru.dbotthepony.mc.otm.capability.matter.ProfiledMatterStorage
import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.slotted.AutomationFilters
import ru.dbotthepony.mc.otm.container.slotted.ContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.FilteredContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.otmRandom
import ru.dbotthepony.mc.otm.data.codec.DecimalCodec
@ -72,22 +74,23 @@ class MatterDecomposerBlockEntity(pos: BlockPos, state: BlockState)
savetables.stateful(::matter, MATTER_STORAGE_KEY)
}
private inner class InputSlot(container: SlottedContainer, slot: Int) : FilteredContainerSlot(container, slot) {
override fun canAutomationPlaceItem(itemStack: ItemStack): Boolean {
return super.canAutomationPlaceItem(itemStack) && MatterManager.canDecompose(itemStack)
}
override fun canAutomationTakeItem(desired: Int): Boolean {
return false
}
}
// вход, выход
val inputContainer = MatteryContainer(::markDirtyFast, 1).also(::addDroppableContainer)
val outputContainer = MatteryContainer(::markDirtyFast, 2).also(::addDroppableContainer)
val inputContainer = SlottedContainer.simple(1, ::InputSlot, ::markDirtyFast).also(::addDroppableContainer)
val outputContainer = SlottedContainer.simple(2, AutomationFilters.ONLY_OUT.simpleProvider, ::markDirtyFast).also(::addDroppableContainer)
val itemConfig = ConfigurableItemHandler(
input = inputContainer.handler(object : HandlerFilter {
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
return MatterManager.canDecompose(stack)
}
override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
return false
}
}),
output = outputContainer.handler(HandlerFilter.OnlyOut)
input = inputContainer,
output = outputContainer
)
init {

View File

@ -8,6 +8,7 @@ import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.crafting.CraftingInput
import net.minecraft.world.item.crafting.RecipeManager
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.state.BlockState
import net.neoforged.neoforge.capabilities.Capabilities
@ -24,19 +25,28 @@ import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.capability.matter.MatterStorageImpl
import ru.dbotthepony.mc.otm.capability.matter.ProfiledMatterStorage
import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.MatteryCraftingContainer
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.IEnhancedCraftingContainer
import ru.dbotthepony.mc.otm.container.slotted.AutomationFilters
import ru.dbotthepony.mc.otm.container.slotted.FilteredContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.SimpleCache
import ru.dbotthepony.mc.otm.core.collect.filter
import ru.dbotthepony.mc.otm.core.collect.forEach
import ru.dbotthepony.mc.otm.core.collect.map
import ru.dbotthepony.mc.otm.core.collect.toList
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.container.ItemStackKey
import ru.dbotthepony.mc.otm.container.asKey
import ru.dbotthepony.mc.otm.data.codec.DecimalCodec
import ru.dbotthepony.mc.otm.data.codec.minRange
import ru.dbotthepony.mc.otm.graph.matter.MatterNode
import ru.dbotthepony.mc.otm.menu.matter.MatterEntanglerMenu
import ru.dbotthepony.mc.otm.recipe.IMatterEntanglerRecipe
import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
import ru.dbotthepony.mc.otm.registry.game.MRecipes
import java.time.Duration
class MatterEntanglerBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryWorkerBlockEntity<MatterEntanglerBlockEntity.Job>(
MBlockEntities.MATTER_ENTANGLER, blockPos, blockState, Job.CODEC) {
class MatterEntanglerBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryWorkerBlockEntity<MatterEntanglerBlockEntity.Job>(MBlockEntities.MATTER_ENTANGLER, blockPos, blockState, Job.CODEC) {
class Job(itemStack: ItemStack, val matter: Decimal, ticks: Double, experience: Float) : ItemJob(itemStack, ticks, MachinesConfig.MATTER_ENTANGLER.energyConsumption, experience = experience) {
val matterPerTick = matter / ticks
@ -60,36 +70,67 @@ class MatterEntanglerBlockEntity(blockPos: BlockPos, blockState: BlockState) : M
val experience = ExperienceStorage(MachinesConfig.MATTER_ENTANGLER::maxExperienceStored).also(::addNeighbourListener)
val energyConfig = ConfigurableEnergy(energy)
val inputs = object : MatteryCraftingContainer(::itemContainerUpdated, 3, 3) {
override fun getMaxStackSize(): Int {
return 1
private var recipeCache: Collection<IMatterEntanglerRecipe> = listOf()
private var seenRecipeManager: RecipeManager? = null
private fun getRecipes(): Collection<IMatterEntanglerRecipe> {
val level = level!!
val manager = level.recipeManager
if (seenRecipeManager !== manager) {
seenRecipeManager = manager
val input = inputs.asPositionedCraftInput()
recipeCache = manager.byType(MRecipes.MATTER_ENTANGLER)
.iterator()
.map { it.value }
.filter { it.preemptivelyMatches(input, level, 3, 3) }
.toList()
}
return recipeCache
}
val output = object : MatteryContainer(::itemContainerUpdated, 1) {
override fun getMaxStackSize(slot: Int, itemStack: ItemStack): Int {
return Int.MAX_VALUE
private inner class InputSlot(container: SlottedContainer, slot: Int) : FilteredContainerSlot(container, slot) {
// may get stalled on /reload command for up to a minute
// shouldn't cause major issues through, since /reload is not something you frequently be executing
val insertCache = SimpleCache<ItemStackKey, Boolean>(Duration.ofMinutes(1))
override fun canAutomationPlaceItem(itemStack: ItemStack): Boolean {
if (!super.canAutomationPlaceItem(itemStack))
return false
val level = level ?: return false
return insertCache.get(itemStack.asKey()) {
val list = container.toList()
list[slot] = itemStack
val shadow = CraftingInput.ofPositioned(3, 3, list)
getRecipes().any { it.preemptivelyMatches(shadow, level, 3, 3) }
}
}
override fun canAutomationTakeItem(desired: Int): Boolean {
return false
}
override val maxStackSize: Int
get() = 1
}
private fun inputsChanged() {
inputs.slotIterator().forEach { (it as InputSlot).insertCache.invalidateAll() }
seenRecipeManager = null
recipeCache = listOf()
setChanged()
}
val inputs = IEnhancedCraftingContainer.Wrapper(SlottedContainer.simple(3 * 3, ::InputSlot, ::inputsChanged), 3, 3)
val output = SlottedContainer.simple(1, AutomationFilters.ONLY_OUT.unlimitedSimpleProvider, ::markDirtyFast)
val itemConfig = ConfigurableItemHandler(
input = inputs.handler(object : HandlerFilter {
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
val list = inputs.toList()
list[slot] = stack
val shadow = CraftingInput.ofPositioned(3, 3, list)
return (level ?: return false)
.recipeManager
.byType(MRecipes.MATTER_ENTANGLER)
.any { it.value.preemptivelyMatches(shadow, level!!, 3, 3) }
}
override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
return false
}
}),
output = output.handler(HandlerFilter.OnlyOut)
input = inputs.parent,
output = output
)
init {
@ -98,7 +139,7 @@ class MatterEntanglerBlockEntity(blockPos: BlockPos, blockState: BlockState) : M
savetables.stateful(::energy, ENERGY_KEY)
savetables.stateful(::matter, MATTER_STORAGE_KEY)
savetables.stateful(::upgrades)
savetables.stateful(::inputs)
savetables.stateful(inputs::parent, "inputs")
savetables.stateful(::output)
savetables.stateful(::experience)
@ -124,7 +165,7 @@ class MatterEntanglerBlockEntity(blockPos: BlockPos, blockState: BlockState) : M
val required = status.job.matterPerTick * status.ticksAdvanced
if (matter.storedMatter < required) {
matter.receiveMatter(node.graph.extractMatter(status.job.matterPerTick.coerceAtLeast(Decimal.TEN).coerceAtMost(matter.missingMatter), false), false)
matter.receiveMatter(node.graph.extractMatter(status.job.matterPerTick.coerceIn(Decimal.TEN, matter.missingMatter), false), false)
}
status.scale(matter.extractMatter(required, false) / required)
@ -150,7 +191,7 @@ class MatterEntanglerBlockEntity(blockPos: BlockPos, blockState: BlockState) : M
if (!energy.batteryLevel.isPositive)
return JobContainer.noEnergy()
val inputs = CraftingInput.of(3, 3, inputs.toList())
val inputs = this.inputs.asCraftInput()
val recipe = (level ?: return JobContainer.failure())
.recipeManager

View File

@ -24,9 +24,9 @@ import ru.dbotthepony.mc.otm.capability.matter.PatternState
import ru.dbotthepony.mc.otm.capability.matter.MatterStorageImpl
import ru.dbotthepony.mc.otm.capability.matter.ProfiledMatterStorage
import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.UpgradeContainer
import ru.dbotthepony.mc.otm.container.slotted.FilteredContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.otmRandom
import ru.dbotthepony.mc.otm.core.registryName
@ -37,9 +37,35 @@ import ru.dbotthepony.mc.otm.menu.matter.MatterReconstructorMenu
import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
import java.util.function.BooleanSupplier
class MatterReconstructorBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryPoweredBlockEntity(
MBlockEntities.MATTER_RECONSTRUCTOR, blockPos, blockState) {
val repairContainer = MatteryContainer(::containerChanged, 1).also(::addDroppableContainer)
class MatterReconstructorBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryPoweredBlockEntity(MBlockEntities.MATTER_RECONSTRUCTOR, blockPos, blockState) {
private inner class Slot(container: SlottedContainer, slot: Int) : FilteredContainerSlot(container, slot) {
override fun canAutomationPlaceItem(itemStack: ItemStack): Boolean {
if (!super.canAutomationPlaceItem(itemStack) || !itemStack.isRepairable || !itemStack.isDamaged) {
return false
}
if (MachinesConfig.MatterReconstructor.ALLOW_TO_SKIP_ANVIL && !MachinesConfig.MatterReconstructor.ONLY_ANVIL && MatterManager.get(itemStack.item).hasMatterValue) {
return true
}
return matterNode.graph
.patterns
.filter { itemStack.item.isValidRepairItem(itemStack, ItemStack(it.item, 1)) }
.findFirst().orElse(null).let {
if (it == null) {
IMatterValue.ZERO
} else {
MatterManager.get(it.item)
}
}.hasMatterValue
}
override fun canAutomationTakeItem(desired: Int): Boolean {
return super.canAutomationTakeItem(desired) && (progressPerTick <= 0.0 || matterPerTick <= Decimal.ZERO)
}
}
val repairContainer = SlottedContainer.simple(1, ::Slot, ::rescan).also(::addDroppableContainer)
private var matterPerTick = Decimal.ZERO
private var progressPerTick = 0.0
@ -67,15 +93,15 @@ class MatterReconstructorBlockEntity(blockPos: BlockPos, blockState: BlockState)
}
override fun onPatternAdded(state: PatternState) {
containerChanged()
rescan()
}
override fun onPatternRemoved(state: PatternState) {
containerChanged()
rescan()
}
override fun onPatternUpdated(newState: PatternState, oldState: PatternState) {
containerChanged()
rescan()
}
}
@ -99,34 +125,7 @@ class MatterReconstructorBlockEntity(blockPos: BlockPos, blockState: BlockState)
val energyConfig = ConfigurableEnergy(energy)
val itemConfig = ConfigurableItemHandler(
inputOutput = repairContainer.handler(object : HandlerFilter {
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
if (!stack.isRepairable || !stack.isDamaged) {
return false
}
if (MachinesConfig.MatterReconstructor.ALLOW_TO_SKIP_ANVIL && !MachinesConfig.MatterReconstructor.ONLY_ANVIL) {
if (MatterManager.get(stack.item).hasMatterValue) {
return true
}
}
return matterNode.graph
.patterns
.filter { stack.item.isValidRepairItem(stack, ItemStack(it.item, 1)) }
.findFirst().orElse(null).let {
if (it == null) {
IMatterValue.ZERO
} else {
MatterManager.get(it.item)
}
}.hasMatterValue
}
override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
return progressPerTick <= 0.0 || matterPerTick <= Decimal.ZERO
}
})
inputOutput = repairContainer
)
override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu {
@ -145,7 +144,7 @@ class MatterReconstructorBlockEntity(blockPos: BlockPos, blockState: BlockState)
private var changeset = 0
private fun containerChanged() {
private fun rescan() {
matterPerTick = Decimal.ZERO
progressPerTick = 0.0
@ -196,7 +195,6 @@ class MatterReconstructorBlockEntity(blockPos: BlockPos, blockState: BlockState)
}
}
@Suppress("name_shadowing")
val matter = MatterManager.get(found.item) * 2
progressPerTick = (item.maxDamage / matter.complexity) / MachinesConfig.MatterReconstructor.DIVISOR

View File

@ -21,9 +21,10 @@ import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.capability.matter.MatterStorageImpl
import ru.dbotthepony.mc.otm.capability.matter.ProfiledMatterStorage
import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.slotted.ContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.nextDecimal
import ru.dbotthepony.mc.otm.core.otmRandom
import ru.dbotthepony.mc.otm.graph.matter.MatterGraph
import ru.dbotthepony.mc.otm.item.matter.MatterDustItem
@ -33,9 +34,7 @@ import ru.dbotthepony.mc.otm.data.codec.DecimalCodec
import ru.dbotthepony.mc.otm.data.codec.minRange
import ru.dbotthepony.mc.otm.graph.matter.SimpleMatterNode
class MatterRecyclerBlockEntity(blockPos: BlockPos, blockState: BlockState)
: MatteryWorkerBlockEntity<MatterRecyclerBlockEntity.RecyclerJob>(MBlockEntities.MATTER_RECYCLER, blockPos, blockState, RecyclerJob.CODEC) {
class MatterRecyclerBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryWorkerBlockEntity<MatterRecyclerBlockEntity.RecyclerJob>(MBlockEntities.MATTER_RECYCLER, blockPos, blockState, RecyclerJob.CODEC) {
class RecyclerJob(ticks: Double, powerUsage: Decimal, var totalMatter: Decimal) : Job(ticks, powerUsage) {
companion object {
val CODEC: Codec<RecyclerJob> by lazy {
@ -48,20 +47,22 @@ class MatterRecyclerBlockEntity(blockPos: BlockPos, blockState: BlockState)
override val upgrades = makeUpgrades(3, UpgradeType.BASIC_MATTER)
val matter = ProfiledMatterStorage(MatterStorageImpl(::matterLevelUpdated, FlowDirection.OUTPUT, upgrades.matterCapacity(MachinesConfig.MatterRecycler.VALUES::matterCapacity)))
val container = MatteryContainer(::itemContainerUpdated, 1).also(::addDroppableContainer)
private class Slot(container: SlottedContainer, slot: Int) : ContainerSlot(container, slot) {
override fun canAutomationTakeItem(desired: Int): Boolean {
return false
}
override fun canAutomationPlaceItem(itemStack: ItemStack): Boolean {
return super.canAutomationPlaceItem(itemStack) && itemStack.item is MatterDustItem
}
}
val container = SlottedContainer.simple(1, ::Slot, ::itemContainerUpdated).also(::addDroppableContainer)
val matterNode = SimpleMatterNode(matter = matter)
override val energy = ProfiledEnergyStorage(WorkerEnergyStorage(::energyLevelUpdated, upgrades.transform(MachinesConfig.MatterRecycler.VALUES)))
val itemConfig = ConfigurableItemHandler(input = container.handler(object : HandlerFilter {
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
return stack.item is MatterDustItem
}
override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
return false
}
}))
val itemConfig = ConfigurableItemHandler(container)
val energyConfig = ConfigurableEnergy(energy)
@ -137,7 +138,7 @@ class MatterRecyclerBlockEntity(blockPos: BlockPos, blockState: BlockState)
else if (matter.receiveMatter(toReceive, true) != toReceive)
return status.noMatter()
matter.receiveMatter(toReceive * 0.4 + level!!.otmRandom.nextDouble() * 0.6, false)
matter.receiveMatter(toReceive * level!!.otmRandom.nextDecimal(BASE_RECEIVE, Decimal.ONE), false)
job.totalMatter -= toReceive
}
@ -151,4 +152,8 @@ class MatterRecyclerBlockEntity(blockPos: BlockPos, blockState: BlockState)
matter.extractMatter(received, false)
}
}
companion object {
private val BASE_RECEIVE = Decimal("0.4")
}
}

View File

@ -25,8 +25,8 @@ import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.capability.matter.*
import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.slotted.AutomationFilters
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.otmRandom
import ru.dbotthepony.mc.otm.data.codec.DecimalCodec
@ -71,11 +71,11 @@ class MatterReplicatorBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
override val upgrades = makeUpgrades(3, UpgradeType.REPLICATOR)
override val energy = ProfiledEnergyStorage(WorkerEnergyStorage(::energyLevelUpdated, upgrades.transform(MachinesConfig.MATTER_REPLICATOR)))
val matter = ProfiledMatterStorage(MatterStorageImpl(::matterLevelUpdated, FlowDirection.INPUT, upgrades.matterCapacity(MachinesConfig.MATTER_REPLICATOR::matterCapacity)))
val outputContainer = MatteryContainer(::itemContainerUpdated, 3).also(::addDroppableContainer)
val dustContainer = MatteryContainer(::itemContainerUpdated, 2).also(::addDroppableContainer)
val outputContainer = SlottedContainer.simple(3, AutomationFilters.ONLY_OUT.simpleProvider, ::itemContainerUpdated).also(::addDroppableContainer)
val dustContainer = SlottedContainer.simple(2, AutomationFilters.ONLY_OUT.simpleProvider, ::itemContainerUpdated).also(::addDroppableContainer)
val energyConfig = ConfigurableEnergy(energy)
val itemConfig = ConfigurableItemHandler(output = CombinedItemHandler(outputContainer.handler(HandlerFilter.OnlyOut), dustContainer.handler(HandlerFilter.OnlyOut)))
val itemConfig = ConfigurableItemHandler(output = CombinedItemHandler(outputContainer, dustContainer))
val matterNode = object : MatterNode() {
override fun getMatterHandler(): IMatterStorage {

View File

@ -20,8 +20,8 @@ import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.capability.matter.PatternState
import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.slotted.ContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.menu.matter.MatterScannerMenu
import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
import ru.dbotthepony.mc.otm.graph.matter.MatterNode
@ -34,19 +34,20 @@ class MatterScannerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
MatteryWorkerBlockEntity<ItemJob>(MBlockEntities.MATTER_SCANNER, p_155229_, p_155230_, ItemJob.CODEC) {
override val upgrades = makeUpgrades(2, UpgradeType.BASIC)
val container = MatteryContainer(::itemContainerUpdated, 1).also(::addDroppableContainer)
override val energy = ProfiledEnergyStorage(WorkerEnergyStorage(::energyLevelUpdated, upgrades.transform(MachinesConfig.MATTER_SCANNER)))
val itemConfig = ConfigurableItemHandler(inputOutput = container.handler(object : HandlerFilter {
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
return MatterManager.canDecompose(stack)
private inner class Slot(container: SlottedContainer, slot: Int) : ContainerSlot(container, slot) {
override fun canAutomationPlaceItem(itemStack: ItemStack): Boolean {
return super.canAutomationPlaceItem(itemStack) && MatterManager.canDecompose(itemStack)
}
override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
return jobEventLoops[0].isIdling
override fun canAutomationTakeItem(desired: Int): Boolean {
return jobEventLoops[0].isIdling && super.canAutomationTakeItem(desired)
}
}))
}
val container = SlottedContainer.simple(1, ::Slot, ::itemContainerUpdated).also(::addDroppableContainer)
val itemConfig = ConfigurableItemHandler(inputOutput = container)
val energyConfig = ConfigurableEnergy(energy)
val matterNode = object : MatterNode() {

View File

@ -1,8 +1,8 @@
package ru.dbotthepony.mc.otm.block.entity.matter
import net.minecraft.core.BlockPos
import net.minecraft.server.level.ServerLevel
import net.minecraft.world.level.block.state.BlockState
import ru.dbotthepony.mc.otm.container.MatteryContainer
import net.minecraft.world.item.ItemStack
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.block.matter.PatternStorageBlock
@ -14,7 +14,8 @@ import net.minecraft.world.level.Level
import net.minecraft.world.level.block.Block
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.capability.matter.*
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.slotted.ContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.collect.filterNotNull
import ru.dbotthepony.mc.otm.core.collect.map
import ru.dbotthepony.mc.otm.core.filterNotNull
@ -23,53 +24,48 @@ import ru.dbotthepony.mc.otm.graph.matter.SimpleMatterNode
import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
import java.util.stream.Stream
class PatternStorageBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
MatteryDeviceBlockEntity(MBlockEntities.PATTERN_STORAGE, p_155229_, p_155230_), IPatternStorage {
class PatternStorageBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : MatteryDeviceBlockEntity(MBlockEntities.PATTERN_STORAGE, p_155229_, p_155230_), IPatternStorage {
val matterNode = SimpleMatterNode(patterns = this)
val container: MatteryContainer = object : MatteryContainer(::markDirtyFast, 8) {
override fun setChanged(slot: Int, new: ItemStack, old: ItemStack) {
if (!ItemStack.isSameItemSameComponents(new, old)) {
private inner class Slot(container: SlottedContainer, slot: Int) : ContainerSlot(container, slot) {
override val maxStackSize: Int
get() = 1
override fun canAutomationPlaceItem(itemStack: ItemStack): Boolean {
return super.canAutomationPlaceItem(itemStack) && itemStack.getCapability(MatteryCapability.PATTERN_ITEM) != null
}
override fun canAutomationTakeItem(desired: Int): Boolean {
return false
}
override fun notifyChanged(old: ItemStack) {
if (!ItemStack.isSameItemSameComponents(item, old)) {
if (!old.isEmpty) {
old.getCapability(MatteryCapability.PATTERN_ITEM)?.let { cap: IPatternStorage ->
cap.patterns.forEach { matterNode.graph.onPatternRemoved(it) }
}
}
if (!new.isEmpty) {
new.getCapability(MatteryCapability.PATTERN_ITEM)?.let { cap: IPatternStorage ->
if (!item.isEmpty) {
item.getCapability(MatteryCapability.PATTERN_ITEM)?.let { cap: IPatternStorage ->
cap.patterns.forEach { matterNode.graph.onPatternAdded(it) }
}
}
updateBlockstate()
val level = level
if (level is ServerLevel) {
level.setBlock(blockPos, blockState.setValue(PatternStorageBlock.PATTERN_STORAGE_DISKS_PROPS[slot], item.getCapability(MatteryCapability.PATTERN_ITEM) != null), Block.UPDATE_CLIENTS)
}
}
super.setChanged(slot, new, old)
}
override fun getMaxStackSize(): Int = 1
}.also(::addDroppableContainer)
private fun updateBlockstate() {
val level = level ?: return
var state = blockState
for (i in 0..7) {
state = state.setValue(
PatternStorageBlock.PATTERN_STORAGE_DISKS_PROPS[i],
this.container.getItem(i).getCapability(MatteryCapability.PATTERN_ITEM) != null
)
}
if (state !== blockState) {
level.setBlock(blockPos, state, Block.UPDATE_CLIENTS)
super.notifyChanged(old)
}
}
val itemConfig = ConfigurableItemHandler(inputOutput = container.handler(HandlerFilter.IsPattern.and(HandlerFilter.OnlyIn)))
val container = SlottedContainer.simple(2 * 4, ::Slot, ::markDirtyFast).also(::addDroppableContainer)
val itemConfig = ConfigurableItemHandler(inputOutput = container)
override fun setLevel(level: Level) {
super.setLevel(level)
@ -96,20 +92,12 @@ class PatternStorageBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
}
override val patternCapacity: Int get() {
var stored = 0L
for (pattern in this.container.iterator().map { it.getCapability(MatteryCapability.PATTERN_ITEM) }.filterNotNull())
stored += pattern.patternCapacity.toLong()
val stored = container.sumOf { it.getCapability(MatteryCapability.PATTERN_ITEM)?.patternCapacity?.toLong() ?: 0L }
return if (stored > Int.MAX_VALUE) Int.MAX_VALUE else stored.toInt()
}
override val storedPatterns: Int get() {
var stored = 0L
for (pattern in this.container.iterator().map { it.getCapability(MatteryCapability.PATTERN_ITEM) }.filterNotNull())
stored += pattern.storedPatterns.toLong()
val stored = container.sumOf { it.getCapability(MatteryCapability.PATTERN_ITEM)?.storedPatterns?.toLong() ?: 0L }
return if (stored > Int.MAX_VALUE) Int.MAX_VALUE else stored.toInt()
}

View File

@ -14,7 +14,8 @@ import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.slotted.ContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.menu.storage.DriveRackMenu
import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
import ru.dbotthepony.mc.otm.storage.optics.priority
@ -45,19 +46,21 @@ class DriveRackBlockEntity(blockPos: BlockPos, blockState: BlockState) : Mattery
markDirtyFast()
}
val container: MatteryContainer = object : MatteryContainer(::markDirtyFast, 4) {
override fun setChanged(slot: Int, new: ItemStack, old: ItemStack) {
super.setChanged(slot, new, old)
private inner class Slot(container: SlottedContainer, slot: Int) : ContainerSlot(container, slot) {
override fun notifyChanged(old: ItemStack) {
super.notifyChanged(old)
old.getCapability(MatteryCapability.CONDENSATION_DRIVE)?.let {
cell.removeStorageComponent(it.priority(::insertPriority, ::extractPriority).powered(energy).flow(::mode))
}
new.getCapability(MatteryCapability.CONDENSATION_DRIVE)?.let {
item.getCapability(MatteryCapability.CONDENSATION_DRIVE)?.let {
cell.addStorageComponent(it.priority(::insertPriority, ::extractPriority).powered(energy).flow(::mode))
}
}
}.also(::addDroppableContainer)
}
val container = SlottedContainer.simple(4, ::Slot, ::markDirtyFast).also(::addDroppableContainer)
init {
savetables.stateful(::energy, ENERGY_KEY)

View File

@ -17,7 +17,7 @@ import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.EnhancedContainer
import ru.dbotthepony.mc.otm.core.get
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.util.ItemStorageStackSorter
@ -29,15 +29,16 @@ class DriveViewerBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matte
override val energy = ProfiledEnergyStorage(WorkerEnergyStorage(this::energyUpdated, MachinesConfig.DRIVE_VIEWER))
val energyConfig = ConfigurableEnergy(energy)
val container: MatteryContainer = object : MatteryContainer(::markDirtyFast, 1) {
override fun setChanged(slot: Int, new: ItemStack, old: ItemStack) {
super.setChanged(slot, new, old)
val container: EnhancedContainer.Simple = object : EnhancedContainer.Simple(1) {
override fun notifySlotChanged(slot: Int, old: ItemStack) {
super.notifySlotChanged(slot, old)
markDirtyFast()
val level = level
if (level is ServerLevel) {
tickList.once {
val isPresent = new.getCapability(MatteryCapability.CONDENSATION_DRIVE) != null
val isPresent = get(slot).getCapability(MatteryCapability.CONDENSATION_DRIVE) != null
var state = this@DriveViewerBlockEntity.blockState.setValue(DriveViewerBlock.DRIVE_PRESENT, isPresent)
if (!isPresent) {

View File

@ -29,12 +29,14 @@ import ru.dbotthepony.mc.otm.client.render.UVWindingOrder
import ru.dbotthepony.mc.otm.client.render.Widgets8
import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.CombinedContainer
import ru.dbotthepony.mc.otm.container.MatteryCraftingContainer
import ru.dbotthepony.mc.otm.container.EnhancedContainer
import ru.dbotthepony.mc.otm.container.IEnhancedCraftingContainer
import ru.dbotthepony.mc.otm.container.util.slotIterator
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.collect.map
import ru.dbotthepony.mc.otm.core.collect.toList
import ru.dbotthepony.mc.otm.core.isNotEmpty
import ru.dbotthepony.mc.otm.core.nbt.map
import ru.dbotthepony.mc.otm.core.nbt.mapString
import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.core.util.ItemStorageStackSorter
@ -94,42 +96,42 @@ class ItemMonitorPlayerSettings : INBTSerializable<CompoundTag>, IItemMonitorPla
enum class IngredientPriority(override val component: Component, icon: Lazy<IGUIRenderable>, override val winding: UVWindingOrder = UVWindingOrder.NORMAL) : Setting {
// Refill everything from system
SYSTEM(TranslatableComponent("otm.gui.item_monitor.refill_source.system"), lazy { Widgets8.WHITE_ARROW_DOWN }, UVWindingOrder.FLIP) {
override fun takeOne(item: ItemStack, view: IStorageProvider<ItemStorageStack>?, inventory: CombinedContainer?, id: UUID?): Boolean {
override fun takeOne(item: ItemStack, view: IStorageProvider<ItemStorageStack>?, inventory: CombinedContainer<*>?, id: UUID?): Boolean {
return takeOne(id, view)
}
},
// Refill everything from player's inventory
INVENTORY(TranslatableComponent("otm.gui.item_monitor.refill_source.inventory"), lazy { Widgets8.WHITE_ARROW_DOWN }) {
override fun takeOne(item: ItemStack, view: IStorageProvider<ItemStorageStack>?, inventory: CombinedContainer?, id: UUID?): Boolean {
override fun takeOne(item: ItemStack, view: IStorageProvider<ItemStorageStack>?, inventory: CombinedContainer<*>?, id: UUID?): Boolean {
return takeOne(inventory, item)
}
},
// Refill everything from system, if can't refill from player's inventory
SYSTEM_FIRST(TranslatableComponent("otm.gui.item_monitor.refill_source.system_first"), lazy { Widgets8.ARROW_SIDEWAYS }, UVWindingOrder.FLIP) {
override fun takeOne(item: ItemStack, view: IStorageProvider<ItemStorageStack>?, inventory: CombinedContainer?, id: UUID?): Boolean {
override fun takeOne(item: ItemStack, view: IStorageProvider<ItemStorageStack>?, inventory: CombinedContainer<*>?, id: UUID?): Boolean {
return takeOne(id, view) || takeOne(inventory, item)
}
},
// Refill everything from player's inventory, if can't refill from system
INVENTORY_FIRST(TranslatableComponent("otm.gui.item_monitor.refill_source.inventory_first"), lazy { Widgets8.ARROW_SIDEWAYS }) {
override fun takeOne(item: ItemStack, view: IStorageProvider<ItemStorageStack>?, inventory: CombinedContainer?, id: UUID?): Boolean {
override fun takeOne(item: ItemStack, view: IStorageProvider<ItemStorageStack>?, inventory: CombinedContainer<*>?, id: UUID?): Boolean {
return takeOne(inventory, item) || takeOne(id, view)
}
},
// Do not refill (?)
DO_NOT(TranslatableComponent("otm.gui.item_monitor.refill_source.do_not"), lazy { Widgets8.MINUS }) {
override fun takeOne(item: ItemStack, view: IStorageProvider<ItemStorageStack>?, inventory: CombinedContainer?, id: UUID?): Boolean {
override fun takeOne(item: ItemStack, view: IStorageProvider<ItemStorageStack>?, inventory: CombinedContainer<*>?, id: UUID?): Boolean {
return false
}
};
override val icon: IGUIRenderable by icon
abstract fun takeOne(item: ItemStack, view: IStorageProvider<ItemStorageStack>?, inventory: CombinedContainer?, id: UUID?): Boolean
abstract fun takeOne(item: ItemStack, view: IStorageProvider<ItemStorageStack>?, inventory: CombinedContainer<*>?, id: UUID?): Boolean
}
enum class ResultTarget(override val component: Component, icon: Lazy<IGUIRenderable>, override val winding: UVWindingOrder = UVWindingOrder.NORMAL) : Setting {
@ -216,17 +218,13 @@ class ItemMonitorBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matte
fun howMuchPlayerCrafted(ply: Player): Int = craftingAmount.getInt(ply)
fun lastCraftingRecipe(ply: Player) = lastCraftingRecipe[ply]
// a lot of code is hardcoded to take CraftingContainer as it's input
// hence we are forced to work around this by providing proxy container
val craftingGrid = object : MatteryCraftingContainer(::markDirtyFast, 3, 3) {
override fun setChanged(slot: Int, new: ItemStack, old: ItemStack) {
markDirtyFast()
val craftingGrid = IEnhancedCraftingContainer.Wrapper(EnhancedContainer.WithListener(3 * 3) {
markDirtyFast()
if (!inProcessOfCraft) {
scanCraftingGrid(false)
}
if (!inProcessOfCraft) {
scanCraftingGrid(false)
}
}.also(::addDroppableContainer)
}, 3, 3).also(::addDroppableContainer)
private fun scanCraftingGrid(justCheckForRecipeChange: Boolean): Boolean {
val level = level ?: return false
@ -346,8 +344,8 @@ class ItemMonitorBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matte
check(residue.size == craftingGrid.containerSize) { "Container and residue list sizes mismatch: ${residue.size} != ${craftingGrid.containerSize}" }
val combinedInventory = craftingPlayer.matteryPlayer?.inventoryAndExopack
val copy = craftingGrid.iterator(false).map { it.copy() }.toList()
val combinedInventory = craftingPlayer.matteryPlayer.inventoryAndExopack
val copy = craftingGrid.parent.copyToList()
// удаляем по одному предмету из сетки крафта
for (slot in 0 until craftingGrid.containerSize) {
@ -381,7 +379,7 @@ class ItemMonitorBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matte
remaining = poweredView?.insertStack(ItemStorageStack(remaining), false)?.toItemStack() ?: remaining
}
remaining = combinedInventory?.addItem(remaining, false) ?: remaining
remaining = combinedInventory.addItem(remaining, false)
if (remaining.isNotEmpty) {
craftingPlayer.spawnAtLocation(remaining)
@ -425,7 +423,7 @@ class ItemMonitorBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matte
}
})
nbt["crafting_grid"] = craftingGrid.serializeNBT(registry)
nbt["crafting_grid"] = craftingGrid.parent.serializeNBT(registry)
}
override fun loadAdditional(nbt: CompoundTag, registry: HolderLookup.Provider) {
@ -438,7 +436,7 @@ class ItemMonitorBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matte
check(this.settings.put(UUID.fromString(key), ItemMonitorPlayerSettings().also { it.deserializeNBT(registry, settings.getCompound(key)) }) == null)
}
craftingGrid.deserializeNBT(registry, nbt["crafting_grid"])
nbt.map("crafting_grid", craftingGrid.parent::deserializeNBT, registry)
}
fun getSettings(ply: ServerPlayer): ItemMonitorPlayerSettings {

View File

@ -23,7 +23,7 @@ import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.ItemFilter
import ru.dbotthepony.mc.otm.container.ItemFilterSet
import ru.dbotthepony.mc.otm.core.*
import ru.dbotthepony.mc.otm.core.math.RelativeSide
import ru.dbotthepony.mc.otm.core.math.isPositive
@ -123,7 +123,7 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter
})
}
var filter = ItemFilter(MAX_FILTERS)
var filter = ItemFilterSet.EMPTY
set(value) {
field = value
component?.scan()
@ -131,7 +131,7 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter
}
init {
savetablesConfig.codec(::filter, ItemFilter.CODEC, FILTER_KEY, Supplier { ItemFilter(MAX_FILTERS) })
savetablesConfig.codec(::filter, ItemFilterSet.CODEC, FILTER_KEY, Supplier { ItemFilterSet.EMPTY })
}
override fun setLevel(level: Level) {
@ -348,7 +348,7 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter
}
fun scan(slot: Int) {
val current = parent[slot].let { if (it.isEmpty || !filter.match(it)) null else it }
val current = parent[slot].let { if (it.isEmpty || !filter.test(it)) null else it }
val last = slot2itemStack[slot]
if (current == null && last != null) {
@ -374,7 +374,7 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter
}
override fun insertStack(stack: ItemStorageStack, simulate: Boolean): ItemStorageStack {
if (redstoneControl.isBlockedByRedstone || energy.batteryLevel.isZero || !filter.match(stack.toItemStack()) || !mode.input)
if (redstoneControl.isBlockedByRedstone || energy.batteryLevel.isZero || !filter.test(stack.toItemStack()) || !mode.input)
return stack
val required = StorageStack.ITEMS.energyPerInsert(stack)

View File

@ -23,7 +23,7 @@ import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.config.EnergyBalanceValues
import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.ItemFilter
import ru.dbotthepony.mc.otm.container.ItemFilterSet
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.isNotEmpty
import ru.dbotthepony.mc.otm.core.math.RelativeSide
@ -98,7 +98,7 @@ abstract class AbstractStorageImportExport(
protected val target = CapabilityCache(RelativeSide.FRONT, Capabilities.ItemHandler.BLOCK)
var filter: ItemFilter = ItemFilter(MAX_FILTERS)
var filter: ItemFilterSet = ItemFilterSet.EMPTY
set(value) {
if (value != field) {
field = value
@ -112,7 +112,7 @@ abstract class AbstractStorageImportExport(
}
init {
savetablesConfig.codec(::filter, ItemFilter.CODEC, FILTER_KEY, Supplier { ItemFilter(MAX_FILTERS) })
savetablesConfig.codec(::filter, ItemFilterSet.CODEC, FILTER_KEY, Supplier { ItemFilterSet.EMPTY })
}
companion object {
@ -168,7 +168,7 @@ class StorageImporterBlockEntity(
}
override fun insertItem(slot: Int, stack: ItemStack, simulate: Boolean): ItemStack {
if (redstoneControl.isBlockedByRedstone || !filter.match(stack))
if (redstoneControl.isBlockedByRedstone || !filter.test(stack))
return stack
return acceptItem(stack, simulate)
@ -183,7 +183,7 @@ class StorageImporterBlockEntity(
}
override fun isItemValid(slot: Int, stack: ItemStack): Boolean {
return filter.match(stack)
return filter.test(stack)
}
override fun tick() {
@ -205,7 +205,7 @@ class StorageImporterBlockEntity(
val extracted = target.extractItem(lastSlot, MAX_MOVE_PER_OPERATION, true)
if (extracted.isEmpty || !filter.match(extracted)) {
if (extracted.isEmpty || !filter.test(extracted)) {
lastSlot++
} else {
val leftover = acceptItem(extracted, true)
@ -244,7 +244,7 @@ class StorageExporterBlockEntity(blockPos: BlockPos, blockState: BlockState) :
}
override fun onStackAdded(stack: ItemStorageStack, id: UUID, provider: IStorageProvider<ItemStorageStack>) {
if (filter.match(stack.toItemStack())) {
if (filter.test(stack.toItemStack())) {
relevantTuples.add(id)
}
}

View File

@ -16,6 +16,7 @@ import net.minecraft.world.item.crafting.SmokingRecipe
import net.minecraft.world.level.block.entity.BlockEntityType
import net.minecraft.world.level.block.state.BlockState
import net.neoforged.neoforge.capabilities.Capabilities
import net.neoforged.neoforge.event.AddReloadListenerEvent
import ru.dbotthepony.kommons.util.getValue
import ru.dbotthepony.kommons.util.setValue
import ru.dbotthepony.mc.otm.block.entity.ExperienceStorage
@ -29,33 +30,60 @@ import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.config.WorkerBalanceValues
import ru.dbotthepony.mc.otm.container.CombinedContainer
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.balance
import ru.dbotthepony.mc.otm.core.collect.filter
import ru.dbotthepony.mc.otm.core.collect.maybe
import ru.dbotthepony.mc.otm.container.slotted.AutomationFilters
import ru.dbotthepony.mc.otm.container.slotted.FilteredContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.SimpleCache
import ru.dbotthepony.mc.otm.core.immutableList
import ru.dbotthepony.mc.otm.core.otmRandom
import ru.dbotthepony.mc.otm.container.ItemStackKey
import ru.dbotthepony.mc.otm.container.asKey
import ru.dbotthepony.mc.otm.menu.tech.PoweredFurnaceMenu
import ru.dbotthepony.mc.otm.recipe.MatteryCookingRecipe
import ru.dbotthepony.mc.otm.recipe.MicrowaveRecipe
import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
import ru.dbotthepony.mc.otm.registry.game.MRecipes
import java.time.Duration
sealed class AbstractPoweredFurnaceBlockEntity<P : AbstractCookingRecipe, S : MatteryCookingRecipe>(
type: BlockEntityType<*>,
blockPos: BlockPos,
blockState: BlockState,
val recipeType: RecipeType<P>,
val secondaryRecipeType: RecipeType<S>?,
val config: WorkerBalanceValues,
maxJobs: Int = 2
) : MatteryWorkerBlockEntity<ItemJob>(type, blockPos, blockState, ItemJob.CODEC, maxJobs) {
abstract val recipeType: RecipeType<P>
abstract val secondaryRecipeType: RecipeType<S>?
abstract val config: WorkerBalanceValues
final override val upgrades = makeUpgrades(2, UpgradeType.BASIC_PROCESSING)
final override val energy = ProfiledEnergyStorage(WorkerEnergyStorage(this::energyLevelUpdated, upgrades.transform(config)))
val inputs = MatteryContainer(this::itemContainerUpdated, maxJobs)
val outputs = MatteryContainer(this::itemContainerUpdated, maxJobs)
private inner class InputSlot(container: SlottedContainer, slot: Int) : FilteredContainerSlot(container, slot) {
override fun canAutomationPlaceItem(itemStack: ItemStack): Boolean {
if (!super.canAutomationPlaceItem(itemStack))
return false
val level = level ?: return true
return acceptableItems.get(itemStack.asKey()) {
val input = SingleRecipeInput(itemStack)
val secondaryRecipeType = secondaryRecipeType
if (secondaryRecipeType != null && level.recipeManager.byType(secondaryRecipeType).any { it.value.matches(input, level) })
return@get true
return@get level.recipeManager.byType(recipeType).any { it.value.matches(input, level) }
}
}
override fun canAutomationTakeItem(desired: Int): Boolean {
return false
}
}
val inputs = SlottedContainer.simple(maxJobs, ::InputSlot, ::itemContainerUpdated)
val outputs = SlottedContainer.simple(maxJobs, AutomationFilters.ONLY_OUT.simpleProvider, ::itemContainerUpdated)
init {
addDroppableContainer(inputs)
@ -76,9 +104,9 @@ sealed class AbstractPoweredFurnaceBlockEntity<P : AbstractCookingRecipe, S : Ma
val experience = ExperienceStorage(config::maxExperienceStored).also(::addNeighbourListener)
val energyConfig = ConfigurableEnergy(energy)
val itemConfig = ConfigurableItemHandler(
input = inputs.handler(HandlerFilter.OnlyIn),
output = outputs.handler(HandlerFilter.OnlyOut),
battery = batteryItemHandler
input = inputs,
output = outputs,
battery = batteryContainer
)
init {
@ -127,13 +155,13 @@ sealed class AbstractPoweredFurnaceBlockEntity<P : AbstractCookingRecipe, S : Ma
return JobContainer.noItem()
val level = level as? ServerLevel ?: return JobContainer.failure()
val secondaryRecipeType = secondaryRecipeType
if (secondaryRecipeType != null) {
val recipe = level.recipeManager
.byType(secondaryRecipeType)
.iterator()
.filter { it.value.matches(SingleRecipeInput(inputs[id]), level) }
.maybe()?.value
.firstOrNull { it.value.matches(SingleRecipeInput(inputs[id]), level) }
?.value
if (recipe != null) {
val toProcess = inputs[id].count.coerceAtMost(1 + upgrades.processingItems)
@ -172,24 +200,51 @@ sealed class AbstractPoweredFurnaceBlockEntity<P : AbstractCookingRecipe, S : Ma
))
}.orElse(JobContainer.noItem())
}
companion object {
// shared by all furnace instances, so cache should be large enough
private val acceptableItems = SimpleCache<ItemStackKey, Boolean>(Duration.ofMinutes(1))
internal fun onReload(event: AddReloadListenerEvent) {
acceptableItems.invalidateAll()
}
}
}
class PoweredFurnaceBlockEntity(blockPos: BlockPos, blockState: BlockState) : AbstractPoweredFurnaceBlockEntity<SmeltingRecipe, MatteryCookingRecipe>(
MBlockEntities.POWERED_FURNACE, blockPos, blockState, RecipeType.SMELTING, null, MachinesConfig.POWERED_FURNACE) {
class PoweredFurnaceBlockEntity(blockPos: BlockPos, blockState: BlockState) : AbstractPoweredFurnaceBlockEntity<SmeltingRecipe, MatteryCookingRecipe>(MBlockEntities.POWERED_FURNACE, blockPos, blockState) {
override val recipeType: RecipeType<SmeltingRecipe>
get() = RecipeType.SMELTING
override val secondaryRecipeType: RecipeType<MatteryCookingRecipe>?
get() = null
override val config: WorkerBalanceValues
get() = MachinesConfig.POWERED_FURNACE
override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu {
return PoweredFurnaceMenu.furnace(containerID, inventory, this)
}
}
class PoweredBlastFurnaceBlockEntity(blockPos: BlockPos, blockState: BlockState) : AbstractPoweredFurnaceBlockEntity<BlastingRecipe, MatteryCookingRecipe>(
MBlockEntities.POWERED_BLAST_FURNACE, blockPos, blockState, RecipeType.BLASTING, null, MachinesConfig.POWERED_FURNACE) {
class PoweredBlastFurnaceBlockEntity(blockPos: BlockPos, blockState: BlockState) : AbstractPoweredFurnaceBlockEntity<BlastingRecipe, MatteryCookingRecipe>(MBlockEntities.POWERED_BLAST_FURNACE, blockPos, blockState) {
override val recipeType: RecipeType<BlastingRecipe>
get() = RecipeType.BLASTING
override val secondaryRecipeType: RecipeType<MatteryCookingRecipe>?
get() = null
override val config: WorkerBalanceValues
get() = MachinesConfig.POWERED_FURNACE
override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu {
return PoweredFurnaceMenu.blasting(containerID, inventory, this)
}
}
class PoweredSmokerBlockEntity(blockPos: BlockPos, blockState: BlockState) : AbstractPoweredFurnaceBlockEntity<SmokingRecipe, MicrowaveRecipe>(
MBlockEntities.POWERED_SMOKER, blockPos, blockState, RecipeType.SMOKING, MRecipes.MICROWAVE, MachinesConfig.POWERED_FURNACE) {
class PoweredSmokerBlockEntity(blockPos: BlockPos, blockState: BlockState) : AbstractPoweredFurnaceBlockEntity<SmokingRecipe, MicrowaveRecipe>(MBlockEntities.POWERED_SMOKER, blockPos, blockState) {
override val recipeType: RecipeType<SmokingRecipe>
get() = RecipeType.SMOKING
override val secondaryRecipeType: RecipeType<MicrowaveRecipe>
get() = MRecipes.MICROWAVE
override val config: WorkerBalanceValues
get() = MachinesConfig.POWERED_FURNACE
override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu {
return PoweredFurnaceMenu.smoking(containerID, inventory, this)
}

View File

@ -1,5 +1,6 @@
package ru.dbotthepony.mc.otm.block.entity.tech
import it.unimi.dsi.fastutil.ints.IntArrayList
import net.minecraft.core.BlockPos
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player
@ -12,6 +13,7 @@ import ru.dbotthepony.kommons.util.setValue
import ru.dbotthepony.kommons.util.value
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.capability.FlowDirection
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.energy
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
@ -19,9 +21,12 @@ import ru.dbotthepony.mc.otm.capability.energyStoredMattery
import ru.dbotthepony.mc.otm.capability.matteryEnergy
import ru.dbotthepony.mc.otm.capability.maxEnergyStoredMattery
import ru.dbotthepony.mc.otm.capability.transcieveEnergy
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.slotRange
import ru.dbotthepony.mc.otm.container.slotted.ContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.FilteredContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.immutableList
import ru.dbotthepony.mc.otm.core.isNotEmpty
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.otmRandom
import ru.dbotthepony.mc.otm.core.shuffle
@ -33,31 +38,73 @@ class BatteryBankBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Matte
var gaugeLevel by syncher.float()
private set
// 6 на 2
val container: MatteryContainer = object : MatteryContainer(::setChanged, CAPACITY) {
override fun setChanged(slot: Int, new: ItemStack, old: ItemStack) {
super.setChanged(slot, new, old)
batteryStatus[slot].value = new.getCapability(Capabilities.EnergyStorage.ITEM) != null
gaugeLevel = batteryLevel.percentage(maxBatteryLevel)
private var containerSlotIndices = IntArray(0)
private inner class Slot(container: SlottedContainer, slot: Int) : FilteredContainerSlot(container, slot) {
override fun canAutomationPlaceItem(itemStack: ItemStack): Boolean {
return super.canAutomationPlaceItem(itemStack) && itemStack.getCapability(Capabilities.EnergyStorage.ITEM)?.let { it.canExtract() && it.extractEnergy(Int.MAX_VALUE, true) > 0 } == true
}
override fun getMaxStackSize(): Int = 1
}.also(::addDroppableContainer)
override fun canAutomationTakeItem(desired: Int): Boolean {
return super.canAutomationTakeItem(desired) && item.getCapability(Capabilities.EnergyStorage.ITEM)?.let { !it.canExtract() || it.extractEnergy(Int.MAX_VALUE, true) <= 0 } != false
}
val batteryStatus = immutableList(CAPACITY) {
syncher.boolean(false)
override fun notifyChanged(old: ItemStack) {
super.notifyChanged(old)
batteryStatus[slot].value = item.getCapability(Capabilities.EnergyStorage.ITEM) != null
updateGaugeLevel()
}
override val maxStackSize: Int
get() = 1
}
val itemConfig = ConfigurableItemHandler(inputOutput = container.handler(HandlerFilter.Dischargeable))
private fun containerUpdated() {
markDirtyFast()
val newSlots = IntArrayList()
for (i in 0 until container.containerSize) {
val stack = container[i]
if (stack.isNotEmpty && stack.energy != null)
newSlots.add(i)
}
containerSlotIndices = newSlots.toIntArray()
}
// 6 на 2
val container = SlottedContainer.simple(CAPACITY, ::Slot, ::containerUpdated).also(::addDroppableContainer)
val batteryStatus = immutableList(CAPACITY) { syncher.boolean(false) }
val itemConfig = ConfigurableItemHandler(inputOutput = container)
init {
savetables.stateful(::container, INVENTORY_KEY)
}
private val containerSlotIndices = IntArray(CAPACITY) { it }
private fun updateGaugeLevel() {
var stored = Decimal.ZERO
var maxStored = Decimal.ZERO
for (stack in container) {
val cap = stack.energy ?: continue
stored += cap.energyStoredMattery
maxStored += cap.maxEnergyStoredMattery
}
gaugeLevel = stored.percentage(maxStored)
}
override fun tick() {
super.tick()
updateGaugeLevel()
}
private fun distributeEnergy(isReceiving: Boolean, howMuch: Decimal, simulate: Boolean): Decimal {
if (!howMuch.isPositive)
if (!howMuch.isPositive || containerSlotIndices.isEmpty())
return Decimal.ZERO
containerSlotIndices.shuffle(level!!.otmRandom)
@ -84,7 +131,6 @@ class BatteryBankBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Matte
if (!simulate && !summ.isZero) {
markDirtyFast()
gaugeLevel = batteryLevel.percentage(maxBatteryLevel)
}
return summ
@ -108,13 +154,9 @@ class BatteryBankBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Matte
get() {
var result = Decimal.ZERO
for (i in 0 until container.containerSize) {
val stack = container.getItem(i)
if (!stack.isEmpty) {
stack.energy?.let {
result += it.energyStoredMattery
}
for (stack in container) {
stack.energy?.let {
result += it.energyStoredMattery
}
}
@ -128,13 +170,9 @@ class BatteryBankBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Matte
get() {
var result = Decimal.ZERO
for (i in 0 until container.containerSize) {
val stack = container.getItem(i)
if (!stack.isEmpty) {
stack.energy?.let {
result += it.maxEnergyStoredMattery
}
for (stack in container) {
stack.energy?.let {
result += it.maxEnergyStoredMattery
}
}

View File

@ -12,8 +12,8 @@ import ru.dbotthepony.mc.otm.capability.*
import ru.dbotthepony.mc.otm.capability.energy.GeneratorEnergyStorage
import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.slotted.AutomationFilters
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.menu.tech.ChemicalGeneratorMenu
import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
import ru.dbotthepony.mc.otm.core.math.Decimal
@ -23,20 +23,16 @@ class ChemicalGeneratorBlockEntity(pos: BlockPos, state: BlockState) : MatteryDe
return ChemicalGeneratorMenu(containerID, inventory, this)
}
val batteryContainer = MatteryContainer(::markDirtyFast, 1).also(::addDroppableContainer)
val residueContainer = MatteryContainer(::markDirtyFast, 1).also(::addDroppableContainer)
val fuelContainer = MatteryContainer(::markDirtyFast, 1).also(::addDroppableContainer)
val batteryItemHandler = batteryContainer.handler(HandlerFilter.Chargeable)
val residueItemHandler = residueContainer.handler(HandlerFilter.OnlyOut)
val fuelItemHandler = fuelContainer.handler(HandlerFilter.ChemicalFuel)
val batteryContainer = SlottedContainer.simple(1, AutomationFilters.DISCHARGABLE.filteredProvider, ::markDirtyFast).also(::addDroppableContainer)
val residueContainer = SlottedContainer.simple(1, AutomationFilters.ONLY_OUT.simpleProvider, ::markDirtyFast).also(::addDroppableContainer)
val fuelContainer = SlottedContainer.simple(1, AutomationFilters.CHEMICAL_FUEL.filteredProvider, ::markDirtyFast).also(::addDroppableContainer)
val energy = ProfiledEnergyStorage(GeneratorEnergyStorage(::markDirtyFast, MachinesConfig.ChemicalGenerator.VALUES::energyCapacity, MachinesConfig.ChemicalGenerator.VALUES::energyThroughput))
val itemConfig = ConfigurableItemHandler(
input = fuelItemHandler,
output = residueItemHandler,
battery = batteryItemHandler,
input = fuelContainer,
output = residueContainer,
battery = batteryContainer,
backDefault = ItemHandlerMode.BATTERY)
val energyConfig = ConfigurableEnergy(energy)

View File

@ -11,8 +11,8 @@ import ru.dbotthepony.mc.otm.block.entity.ItemJob
import ru.dbotthepony.mc.otm.block.entity.JobContainer
import ru.dbotthepony.mc.otm.block.entity.JobStatus
import ru.dbotthepony.mc.otm.block.entity.MatteryWorkerBlockEntity
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.slotted.AutomationFilters
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.menu.tech.CobblerMenu
import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
@ -22,8 +22,8 @@ class CobblerBlockEntity(blockPos: BlockPos, blockState: BlockState)
return CobblerMenu(containerID, inventory, this)
}
val container = MatteryContainer(this::itemContainerUpdated, CONTAINER_SIZE).also(::addDroppableContainer)
val itemConfig = ConfigurableItemHandler(output = container.handler(HandlerFilter.OnlyOut))
val container = SlottedContainer.simple(CONTAINER_SIZE, AutomationFilters.ONLY_OUT.simpleProvider, ::itemContainerUpdated).also(::addDroppableContainer)
val itemConfig = ConfigurableItemHandler(output = container)
init {
savetables.stateful(::container, INVENTORY_KEY)

View File

@ -22,7 +22,7 @@ import ru.dbotthepony.mc.otm.core.chart.DecimalHistoryChart
import ru.dbotthepony.mc.otm.core.math.BlockRotation
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.RelativeSide
import ru.dbotthepony.mc.otm.core.math.getDecimal
import ru.dbotthepony.mc.otm.core.nbt.getDecimal
import ru.dbotthepony.mc.otm.core.nbt.mapPresent
import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.core.util.countingLazy
@ -31,6 +31,7 @@ import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : MatteryDeviceBlockEntity(MBlockEntities.ENERGY_COUNTER, p_155229_, p_155230_) {
var passed by syncher.decimal()
var pullEnergyFromInput by syncher.boolean()
override val blockRotation: BlockRotation by countingLazy(blockStateChangesCounter) {
BlockRotation.of(blockState[EnergyCounterBlock.INPUT_DIRECTION])
@ -86,10 +87,8 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat
savetables.stateful(::history1h)
savetables.stateful(::history6h)
savetables.stateful(::history24h)
}
override fun saveLevel(nbt: CompoundTag, registry: HolderLookup.Provider) {
super.saveLevel(nbt, registry)
savetablesConfig.bool(::pullEnergyFromInput)
}
override fun loadAdditional(nbt: CompoundTag, registry: HolderLookup.Provider) {
@ -267,6 +266,14 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat
override fun tick() {
super.tick()
if (pullEnergyFromInput) {
val input = inputCapability
val output = outputCapability
if (input != null && output != null)
thisTick += moveEnergy(source = input, destination = output, simulate = false)
}
lastTick = thisTick
charts.forEach { it.add(thisTick) }
thisTick = Decimal.ZERO

View File

@ -16,8 +16,8 @@ import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
import ru.dbotthepony.mc.otm.capability.moveEnergy
import ru.dbotthepony.mc.otm.config.EnergyBalanceValues
import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.slotted.AutomationFilters
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.math.RelativeSide
import ru.dbotthepony.mc.otm.core.multiblock.BlockEntityTag
import ru.dbotthepony.mc.otm.menu.tech.EnergyHatchMenu
@ -32,13 +32,12 @@ class EnergyHatchBlockEntity(
) : MatteryDeviceBlockEntity(type, blockPos, blockState) {
val energy = ProfiledEnergyStorage(BlockEnergyStorageImpl(this::markDirtyFast, FlowDirection.input(isInput), capacity))
val container = object : MatteryContainer(::markDirtyFast, CAPACITY) {
override fun getMaxStackSize(slot: Int, itemStack: ItemStack): Int {
return 1
}
}.also(::addDroppableContainer)
val container = SlottedContainer.simple(
CAPACITY,
if (isInput) AutomationFilters.DISCHARGABLE.limitedFilteredProvider else AutomationFilters.CHARGEABLE.limitedFilteredProvider,
::markDirtyFast
).also(::addDroppableContainer)
val itemHandler = container.handler(if (isInput) HandlerFilter.Dischargeable else HandlerFilter.Chargeable)
private val neighbours = ArrayList<CapabilityCache<IEnergyStorage>>()
init {
@ -47,7 +46,7 @@ class EnergyHatchBlockEntity(
// it would cause a lot of frustration if hatches accept stuff only though one face
exposeEnergyGlobally(energy)
exposeGlobally(Capabilities.ItemHandler.BLOCK, itemHandler)
exposeGlobally(Capabilities.ItemHandler.BLOCK, container)
if (!isInput) {
for (side in RelativeSide.entries) {

View File

@ -18,8 +18,8 @@ import ru.dbotthepony.mc.otm.capability.FlowDirection
import ru.dbotthepony.mc.otm.capability.energy.CombinedProfiledEnergyStorage
import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
import ru.dbotthepony.mc.otm.capability.moveEnergy
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.slotted.AutomationFilters
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.math.RelativeSide
import ru.dbotthepony.mc.otm.core.multiblock.BlockEntityTag
import ru.dbotthepony.mc.otm.core.multiblock.IMultiblockAccess
@ -56,13 +56,11 @@ class EnergyInterfaceBlockEntity(
targets.invalidate()
}
val container = object : MatteryContainer(::markDirtyFast, CAPACITY) {
override fun getMaxStackSize(slot: Int, itemStack: ItemStack): Int {
return 1
}
}.also(::addDroppableContainer)
val container = SlottedContainer.simple(
CAPACITY,
if (isInput) AutomationFilters.DISCHARGABLE.limitedFilteredProvider else AutomationFilters.CHARGEABLE.limitedFilteredProvider,
::markDirtyFast).also(::addDroppableContainer)
val itemHandler = container.handler(if (isInput) HandlerFilter.Dischargeable else HandlerFilter.Chargeable)
private val neighbours = ArrayList<CapabilityCache<IEnergyStorage>>()
override fun setRemoved() {
@ -75,7 +73,7 @@ class EnergyInterfaceBlockEntity(
// it would cause a lot of frustration if hatches accept stuff only though one face
exposeEnergyGlobally(energy)
exposeGlobally(Capabilities.ItemHandler.BLOCK, itemHandler)
exposeGlobally(Capabilities.ItemHandler.BLOCK, container)
if (!isInput) {
for (side in RelativeSide.entries) {

View File

@ -14,16 +14,19 @@ import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
import ru.dbotthepony.mc.otm.capability.energyStoredMattery
import ru.dbotthepony.mc.otm.capability.extractEnergy
import ru.dbotthepony.mc.otm.capability.maxEnergyStoredMattery
import ru.dbotthepony.mc.otm.capability.moveEnergy
import ru.dbotthepony.mc.otm.capability.receiveEnergy
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.slotted.AutomationFilters
import ru.dbotthepony.mc.otm.container.slotted.FilteredContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.container.slotted.and
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.menu.tech.EnergyServoMenu
import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
class EnergyServoBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryDeviceBlockEntity(MBlockEntities.ENERGY_SERVO, blockPos, blockState) {
val discharge = MatteryContainer(::markDirtyFast, 1).also(::addDroppableContainer)
val charge = MatteryContainer(::markDirtyFast, 1).also(::addDroppableContainer)
val discharge = SlottedContainer.simple(1, AutomationFilters.DISCHARGABLE.filteredProvider, ::markDirtyFast).also(::addDroppableContainer)
val charge = SlottedContainer.simple(1, AutomationFilters.CHARGEABLE.filteredProvider, ::markDirtyFast).also(::addDroppableContainer)
val energy: ProfiledEnergyStorage<IMatteryEnergyStorage> = ProfiledEnergyStorage(object : IMatteryEnergyStorage {
override val energyFlow: FlowDirection get() {
@ -61,11 +64,7 @@ class EnergyServoBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matte
})
val energyConfig = ConfigurableEnergy(energy, possibleModes = FlowDirection.BI_DIRECTIONAL)
val itemConfig = ConfigurableItemHandler(
input = charge.handler(HandlerFilter.OnlyIn.and(HandlerFilter.Chargeable)),
output = discharge.handler(HandlerFilter.OnlyOut.and(HandlerFilter.Dischargeable))
)
val itemConfig = ConfigurableItemHandler(input = charge, output = discharge)
init {
savetables.stateful(::charge)
@ -89,19 +88,7 @@ class EnergyServoBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matte
val chargeEnergy = charge.energy ?: return
val dischargeEnergy = discharge.energy ?: return
val extracted = dischargeEnergy.extractEnergy(Decimal.LONG_MAX_VALUE, true)
if (extracted.isPositive) {
val received = chargeEnergy.receiveEnergy(extracted, true)
if (received.isPositive) {
val extracted2 = dischargeEnergy.extractEnergy(received, false)
if (extracted2 == received) {
chargeEnergy.receiveEnergy(dischargeEnergy.extractEnergy(received, false), false)
}
}
}
moveEnergy(source = dischargeEnergy, destination = chargeEnergy, amount = Decimal.LONG_MAX_VALUE, simulate = false)
}
}
}

View File

@ -23,8 +23,10 @@ import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.block.tech.EssenceStorageBlock
import ru.dbotthepony.mc.otm.capability.item.CombinedItemHandler
import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.EnhancedContainer
import ru.dbotthepony.mc.otm.container.slotted.ContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.FilteredContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.getEntitiesInEllipsoid
import ru.dbotthepony.mc.otm.core.lookupOrThrow
import ru.dbotthepony.mc.otm.core.math.Vector
@ -36,6 +38,7 @@ import ru.dbotthepony.mc.otm.menu.tech.EssenceStorageMenu
import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
import ru.dbotthepony.mc.otm.registry.game.MDataComponentTypes
import ru.dbotthepony.mc.otm.registry.game.MFluids
import kotlin.math.max
class EssenceStorageBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryDeviceBlockEntity(MBlockEntities.ESSENCE_STORAGE, blockPos, blockState), IFluidHandler {
var experienceStored = 0L
@ -45,9 +48,29 @@ class EssenceStorageBlockEntity(blockPos: BlockPos, blockState: BlockState) : Ma
markDirtyFast()
}
val capsuleContainer = MatteryContainer(::markDirtyFast, 1)
val servoContainer = MatteryContainer(::markDirtyFast, 1)
val mendingContainer = MatteryContainer(::markDirtyFast, 1).also(::addDroppableContainer)
private class CapsuleSlot(container: SlottedContainer, slot: Int) : ContainerSlot(container, slot) {
override fun canAutomationPlaceItem(itemStack: ItemStack): Boolean {
return super.canAutomationPlaceItem(itemStack) && itemStack.item is EssenceCapsuleItem
}
override fun canAutomationTakeItem(desired: Int): Boolean {
return false
}
}
private inner class MendingSlot(container: SlottedContainer, slot: Int) : FilteredContainerSlot(container, slot) {
override fun canAutomationPlaceItem(itemStack: ItemStack): Boolean {
return super.canAutomationPlaceItem(itemStack) && itemStack.isDamaged && itemStack.getEnchantmentLevel(mending!!) > 0
}
override fun canAutomationTakeItem(desired: Int): Boolean {
return super.canAutomationTakeItem(desired) && (!item.isDamaged || experienceStored <= 0)
}
}
val capsuleContainer = SlottedContainer.simple(1, ::CapsuleSlot, ::markDirtyFast)
val servoContainer = EnhancedContainer.WithListener(1, ::markDirtyFast)
val mendingContainer = SlottedContainer.simple(1, ::MendingSlot, ::markDirtyFast).also(::addDroppableContainer)
private var mending: Holder<Enchantment>? = null
@ -63,22 +86,7 @@ class EssenceStorageBlockEntity(blockPos: BlockPos, blockState: BlockState) : Ma
}
val itemConfig = ConfigurableItemHandler(
inputOutput = CombinedItemHandler(
capsuleContainer.handler(HandlerFilter.OnlyIn.and(object : HandlerFilter {
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
return stack.item is EssenceCapsuleItem
}
})),
mendingContainer.handler(object : HandlerFilter {
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
return stack.isDamaged && stack.getEnchantmentLevel(mending!!) > 0
}
override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
return !stack.isDamaged || experienceStored <= 0
}
})
)
inputOutput = CombinedItemHandler(capsuleContainer, mendingContainer)
)
override fun loadAdditional(nbt: CompoundTag, registry: HolderLookup.Provider) {
@ -160,7 +168,7 @@ class EssenceStorageBlockEntity(blockPos: BlockPos, blockState: BlockState) : Ma
val capsule = capsuleContainer[0]
if (!capsule.isEmpty && capsule.has(MDataComponentTypes.EXPERIENCE)) {
experienceStored += capsule.get(MDataComponentTypes.EXPERIENCE)!! * capsule.count
experienceStored += max(capsule.get(MDataComponentTypes.EXPERIENCE)!! * capsule.count, 0L)
capsuleContainer.clearContent()
}

View File

@ -9,8 +9,8 @@ import net.minecraft.world.level.block.state.BlockState
import net.neoforged.neoforge.capabilities.Capabilities
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.block.entity.decorative.CargoCrateBlockEntity
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.slotted.AutomationFilters
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.multiblock.BlockEntityTag
import ru.dbotthepony.mc.otm.menu.tech.ItemHatchMenu
import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
@ -21,12 +21,11 @@ class ItemHatchBlockEntity(
blockPos: BlockPos,
blockState: BlockState
) : MatteryDeviceBlockEntity(type, blockPos, blockState) {
val container = MatteryContainer(this::markDirtyFast, CAPACITY).also(::addDroppableContainer)
val itemHandler = container.handler(if (isInput) HandlerFilter.OnlyIn else HandlerFilter.OnlyOut)
val container = SlottedContainer.simple(CAPACITY, if (isInput) AutomationFilters.ONLY_IN.filteredProvider else AutomationFilters.ONLY_OUT.filteredProvider, this::markDirtyFast).also(::addDroppableContainer)
init {
savetables.stateful(::container, INVENTORY_KEY)
exposeGlobally(Capabilities.ItemHandler.BLOCK, itemHandler)
exposeGlobally(Capabilities.ItemHandler.BLOCK, container)
}
override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu {

View File

@ -15,8 +15,8 @@ import ru.dbotthepony.mc.otm.capability.matter.MatterStorageImpl
import ru.dbotthepony.mc.otm.capability.matter.ProfiledMatterStorage
import ru.dbotthepony.mc.otm.capability.moveMatter
import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.slotted.AutomationFilters
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.multiblock.BlockEntityTag
import ru.dbotthepony.mc.otm.menu.tech.MatterHatchMenu
@ -28,26 +28,15 @@ class MatterHatchBlockEntity(
blockPos: BlockPos,
blockState: BlockState
) : MatteryDeviceBlockEntity(type, blockPos, blockState) {
val container = object : MatteryContainer(::markDirtyFast, CAPACITY) {
override fun getMaxStackSize(slot: Int, itemStack: ItemStack): Int {
return 1
}
}.also(::addDroppableContainer)
val container = SlottedContainer.simple(CAPACITY, if (isInput) AutomationFilters.MATTER_PROVIDERS.limitedFilteredProvider else AutomationFilters.MATTER_CONSUMERS.limitedFilteredProvider, ::markDirtyFast).also(::addDroppableContainer)
val matter = ProfiledMatterStorage(MatterStorageImpl(this::markDirtyFast, FlowDirection.input(isInput), MachinesConfig::MATTER_HATCH))
val itemHandler = if (isInput) {
container.handler(HandlerFilter.MatterProviders)
} else {
container.handler(HandlerFilter.MatterConsumers)
}
init {
savetables.stateful(::container, INVENTORY_KEY)
savetables.stateful(::matter, MATTER_STORAGE_KEY)
// it would cause a lot of frustration if hatches accept stuff only though one face
exposeGlobally(Capabilities.ItemHandler.BLOCK, itemHandler)
exposeGlobally(Capabilities.ItemHandler.BLOCK, container)
exposeGlobally(MatteryCapability.MATTER_BLOCK, matter)
}

View File

@ -5,8 +5,11 @@ import net.minecraft.core.BlockPos
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.crafting.SingleRecipeInput
import net.minecraft.world.level.block.state.BlockState
import net.neoforged.neoforge.capabilities.Capabilities
import net.neoforged.neoforge.event.AddReloadListenerEvent
import ru.dbotthepony.mc.otm.block.entity.ExperienceStorage
import ru.dbotthepony.mc.otm.block.entity.JobContainer
import ru.dbotthepony.mc.otm.block.entity.JobStatus
@ -16,15 +19,18 @@ import ru.dbotthepony.mc.otm.capability.UpgradeType
import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.balance
import ru.dbotthepony.mc.otm.core.collect.filter
import ru.dbotthepony.mc.otm.core.collect.maybe
import ru.dbotthepony.mc.otm.container.slotted.AutomationFilters
import ru.dbotthepony.mc.otm.container.slotted.FilteredContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.SimpleCache
import ru.dbotthepony.mc.otm.core.otmRandom
import ru.dbotthepony.mc.otm.container.ItemStackKey
import ru.dbotthepony.mc.otm.container.asKey
import ru.dbotthepony.mc.otm.menu.tech.PlatePressMenu
import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
import ru.dbotthepony.mc.otm.registry.game.MRecipes
import java.time.Duration
class PlatePressBlockEntity(
blockPos: BlockPos,
@ -33,14 +39,36 @@ class PlatePressBlockEntity(
) : MatteryWorkerBlockEntity<ItemJob>(if (isTwin) MBlockEntities.TWIN_PLATE_PRESS else MBlockEntities.PLATE_PRESS, blockPos, blockState, ItemJob.CODEC, if (isTwin) 2 else 1) {
override val upgrades = makeUpgrades(if (isTwin) 4 else 3, UpgradeType.BASIC_PROCESSING)
override val energy = ProfiledEnergyStorage(WorkerEnergyStorage(this::energyLevelUpdated, upgrades.transform(MachinesConfig.PLATE_PRESS)))
val inputContainer = MatteryContainer(this::itemContainerUpdated, if (isTwin) 2 else 1).also(::addDroppableContainer)
val outputContainer = MatteryContainer(this::itemContainerUpdated, if (isTwin) 2 else 1).also(::addDroppableContainer)
private inner class InputSlot(container: SlottedContainer, slot: Int) : FilteredContainerSlot(container, slot) {
override fun canAutomationPlaceItem(itemStack: ItemStack): Boolean {
if (!super.canAutomationPlaceItem(itemStack))
return false
val level = level ?: return true
val input = SingleRecipeInput(itemStack)
return cache.get(itemStack.asKey()) {
return@get level.recipeManager
.byType(MRecipes.PLATE_PRESS)
.any { it.value.matches(input, level) }
}
}
override fun canAutomationTakeItem(desired: Int): Boolean {
return false
}
}
val inputContainer = SlottedContainer.simple(if (isTwin) 2 else 1, ::InputSlot, this::itemContainerUpdated).also(::addDroppableContainer)
val outputContainer = SlottedContainer.simple(if (isTwin) 2 else 1, AutomationFilters.ONLY_OUT.simpleProvider, this::itemContainerUpdated).also(::addDroppableContainer)
val experience = ExperienceStorage(MachinesConfig.PLATE_PRESS::maxExperienceStored).also(::addNeighbourListener)
val energyConfig = ConfigurableEnergy(energy)
val itemConfig = ConfigurableItemHandler(
input = inputContainer.handler(HandlerFilter.OnlyIn),
output = outputContainer.handler(HandlerFilter.OnlyOut),
input = inputContainer,
output = outputContainer,
)
init {
@ -76,9 +104,7 @@ class PlatePressBlockEntity(
val recipe = level.recipeManager
.byType(MRecipes.PLATE_PRESS)
.iterator()
.filter { it.value.matches(inputContainer, id) }
.maybe()?.value ?: return JobContainer.noItem()
.firstOrNull { it.value.matches(inputContainer, id) }?.value ?: return JobContainer.noItem()
val toProcess = inputContainer[id].count.coerceAtMost(1 + upgrades.processingItems)
@ -100,4 +126,12 @@ class PlatePressBlockEntity(
super.tick()
}
companion object {
private val cache = SimpleCache<ItemStackKey, Boolean>(Duration.ofMinutes(1L))
internal fun onReload(event: AddReloadListenerEvent) {
cache.invalidateAll()
}
}
}

View File

@ -146,13 +146,13 @@ val ItemStack.matteryEnergy: IMatteryEnergyStorage? get() {
}
fun Player.items(includeCosmetics: Boolean = true): Iterator<ItemStack> {
val matteryPlayer = matteryPlayer ?: return emptyIterator()
val matteryPlayer = matteryPlayer
val iterators = ArrayList<Iterator<ItemStack>>()
iterators.add(matteryPlayer.wrappedInventory.slotIterator().filter { !it.isForbiddenForAutomation }.map { it.item })
iterators.add(matteryPlayer.wrappedInventory.slotIterator().filter { !it.filter.denyAll }.map { it.item })
if (matteryPlayer.hasExopack) {
iterators.add(matteryPlayer.exopackContainer.slotIterator().filter { !it.isForbiddenForAutomation }.map { it.item })
iterators.add(matteryPlayer.exopackContainer.slotIterator().filter { !it.filter.denyAll }.map { it.item })
iterators.add(matteryPlayer.exopackEnergy.parent.iterator())
iterators.add(matteryPlayer.exopackChargeSlots.iterator())
}
@ -185,10 +185,8 @@ fun Player.awareItemsStream(includeCosmetics: Boolean = false): Stream<out Aware
val streams = ArrayList<Stream<out AwareItemStack>>()
streams.add(inventory.awareStream())
matteryPlayer?.let {
if (it.hasExopack) {
streams.add(it.exopackContainer.awareStream())
}
if (matteryPlayer.hasExopack) {
streams.add(matteryPlayer.exopackContainer.awareStream())
}
if (isCuriosLoaded) {

View File

@ -136,7 +136,7 @@ enum class FlowDirection(val input: Boolean, val output: Boolean, val translatio
*/
@JvmStatic
fun input(flag: Boolean): FlowDirection {
return of(flag, !flag)
return if (flag) INPUT else OUTPUT
}
/**

View File

@ -0,0 +1,14 @@
package ru.dbotthepony.mc.otm.capability
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.inventory.Slot
interface IQuickStackContainer {
fun getSlotsFor(player: ServerPlayer): Collection<Slot>
class Simple(private val slots: Collection<Slot>) : IQuickStackContainer {
override fun getSlotsFor(player: ServerPlayer): Collection<Slot> {
return slots
}
}
}

View File

@ -15,7 +15,7 @@ import ru.dbotthepony.mc.otm.capability.FlowDirection
import ru.dbotthepony.mc.otm.capability.extractEnergy
import ru.dbotthepony.mc.otm.capability.receiveEnergy
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.getDecimal
import ru.dbotthepony.mc.otm.core.nbt.getDecimal
import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.registry.StatNames
import ru.dbotthepony.mc.otm.triggers.AndroidBatteryTrigger

View File

@ -3,7 +3,7 @@ package ru.dbotthepony.mc.otm.capability.item
import com.google.common.collect.ImmutableList
import net.minecraft.world.item.ItemStack
import net.neoforged.neoforge.items.IItemHandler
import ru.dbotthepony.mc.otm.container.ContainerHandler
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import java.util.stream.Stream
class CombinedItemHandler(val handlers: ImmutableList<IItemHandler>) : IItemHandler {
@ -11,7 +11,7 @@ class CombinedItemHandler(val handlers: ImmutableList<IItemHandler>) : IItemHand
constructor(handlers: Collection<IItemHandler>) : this(ImmutableList.copyOf(handlers))
constructor(vararg handlers: IItemHandler) : this(ImmutableList.copyOf(handlers))
private val needsChecking = handlers.any { it !is ContainerHandler }
private val needsChecking = handlers.any { it !is SlottedContainer }
private val lastSizes = IntArray(this.handlers.size)
private var totalSize = 0
private val mappings = ArrayList<Mapping>()

View File

@ -25,8 +25,11 @@ import ru.dbotthepony.mc.otm.client.setMousePos
import ru.dbotthepony.mc.otm.client.shouldOpenVanillaInventory
import ru.dbotthepony.mc.otm.core.math.integerDivisionDown
import ru.dbotthepony.mc.otm.menu.ExopackInventoryMenu
import ru.dbotthepony.mc.otm.menu.QuickMoveInput
import ru.dbotthepony.mc.otm.network.ExopackMenuOpen
import ru.dbotthepony.mc.otm.network.QuickStackPacket
import yalter.mousetweaks.api.MouseTweaksDisableWheelTweak
import java.util.function.IntConsumer
@MouseTweaksDisableWheelTweak
class ExopackInventoryScreen(menu: ExopackInventoryMenu) : MatteryScreen<ExopackInventoryMenu>(menu, TranslatableComponent("otm.gui.exopack")) {
@ -281,7 +284,9 @@ class ExopackInventoryScreen(menu: ExopackInventoryMenu) : MatteryScreen<Exopack
curios.x = x
}
EffectListPanel(this, frame, menu.player, x - 56f, gridHeight = 6).tick()
EffectListPanel(this, frame, menu.player, x - 56f, gridHeight = 4).tick()
QuickStackControlsPanel(this, frame, x = -19f, y = 96f + 18f)
return frame
}

View File

@ -10,7 +10,6 @@ import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen
import net.minecraft.network.chat.Component
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.inventory.Slot
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.neoforged.neoforge.client.event.ContainerScreenEvent
import net.neoforged.neoforge.common.NeoForge
@ -52,7 +51,7 @@ import ru.dbotthepony.mc.otm.core.math.component1
import ru.dbotthepony.mc.otm.core.math.component2
import ru.dbotthepony.mc.otm.core.math.integerDivisionDown
import ru.dbotthepony.mc.otm.menu.MatteryMenu
import ru.dbotthepony.mc.otm.menu.MatterySlot
import ru.dbotthepony.mc.otm.menu.MatteryMenuSlot
import ru.dbotthepony.mc.otm.menu.widget.LevelGaugeWidget
import ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget
import java.util.*
@ -364,8 +363,6 @@ abstract class MatteryScreen<T : MatteryMenu>(menu: T, inventory: Inventory, tit
override fun mouseScrolledInner(x: Double, y: Double, scroll: Double): Boolean {
return false
}
override var slotFilter: Item? by slot.filter!!
}
}
@ -425,7 +422,7 @@ abstract class MatteryScreen<T : MatteryMenu>(menu: T, inventory: Inventory, tit
matter: LevelGaugeWidget? = null,
profiledMatter: ProfiledLevelGaugeWidget<*>? = null,
patterns: LevelGaugeWidget? = null,
batterySlot: MatterySlot? = null,
batterySlot: MatteryMenuSlot? = null,
) {
var bars = 0
if (energy != null) bars++

View File

@ -2,11 +2,13 @@ package ru.dbotthepony.mc.otm.client.screen.decorative
import net.minecraft.network.chat.Component
import net.minecraft.world.entity.player.Inventory
import ru.dbotthepony.kommons.util.Either
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
import ru.dbotthepony.mc.otm.client.screen.panels.FramePanel
import ru.dbotthepony.mc.otm.client.screen.panels.button.DeviceControls
import ru.dbotthepony.mc.otm.client.screen.panels.util.GridPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.UserFilteredSlotPanel
import ru.dbotthepony.mc.otm.menu.QuickMoveInput
import ru.dbotthepony.mc.otm.menu.decorative.CargoCrateMenu
class CargoCrateScreen(menu: CargoCrateMenu, inventory: Inventory, title: Component) : MatteryScreen<CargoCrateMenu>(menu, inventory, title) {
@ -20,12 +22,19 @@ class CargoCrateScreen(menu: CargoCrateMenu, inventory: Inventory, title: Compon
val grid = GridPanel(this, frame, 8f, 18f, 9f * 18f, 6f * 18f, 9, 6)
for (slot in menu.storageSlots)
UserFilteredSlotPanel.of(this, grid, slot)
UserFilteredSlotPanel(this, grid, slot)
val controls = DeviceControls(this, frame)
controls.sortingButtons(menu.sort)
val leftControls = DeviceControls(this, frame)
leftControls.dockOnLeft = true
leftControls.quickMoveButtons(menu.quickMoveFromStorage)
val leftInventoryControls = DeviceControls(this, inventoryFrame!!)
leftInventoryControls.dockOnLeft = true
leftInventoryControls.quickMoveButtons(menu.quickMoveToStorage, menu.quickMoveFromStorage)
return frame
}
}

View File

@ -12,6 +12,7 @@ import ru.dbotthepony.mc.otm.client.screen.panels.button.makeDeviceControls
import ru.dbotthepony.mc.otm.client.screen.panels.makeCuriosPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.SpritePanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.UserFilteredSlotPanel
import ru.dbotthepony.mc.otm.client.screen.widget.FluidGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.ProgressGaugePanel
import ru.dbotthepony.mc.otm.menu.decorative.FluidTankMenu
@ -25,8 +26,8 @@ class FluidTankScreen(menu: FluidTankMenu, inventory: Inventory, title: Componen
val s = SpritePanel(this, frame, ProgressGaugePanel.GAUGE_BACKGROUND, x = 30f, y = 30f)
SpritePanel(this, frame, ProgressGaugePanel.GAUGE_BACKGROUND, x = 30f, y = 55f, winding = UVWindingOrder.FLOP)
SlotPanel(this, frame, menu.fillInput, x = 30f + s.width + 4f, y = 28f)
SlotPanel(this, frame, menu.drainInput, x = 30f + s.width + 4f, y = 53f)
UserFilteredSlotPanel(this, frame, menu.fillInput, x = 30f + s.width + 4f, y = 28f)
UserFilteredSlotPanel(this, frame, menu.drainInput, x = 30f + s.width + 4f, y = 53f)
SlotPanel(this, frame, menu.output, x = 30f + s.width + 4f + 20f, y = 53f)

View File

@ -8,6 +8,7 @@ import ru.dbotthepony.mc.otm.client.screen.panels.slot.BatterySlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.FramePanel
import ru.dbotthepony.mc.otm.client.screen.panels.button.makeDeviceControls
import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.UserFilteredSlotPanel
import ru.dbotthepony.mc.otm.client.screen.widget.MatterGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.PowerGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.ProfiledMatterGaugePanel
@ -25,7 +26,7 @@ class MatterDecomposerScreen(p_97741_: MatterDecomposerMenu, p_97742_: Inventory
BatterySlotPanel(this, frame, menu.batterySlot, LEFT_MARGIN, SLOT_TOP_UNDER_GAUGE)
SlotPanel(this, frame, menu.input, 122f, PROGRESS_SLOT_TOP)
UserFilteredSlotPanel(this, frame, menu.input, 122f, PROGRESS_SLOT_TOP)
ProgressGaugePanel(this, frame, menu.progressWidget, 96f, PROGRESS_ARROW_TOP).also { it.flop = true }
SlotPanel(this, frame, menu.outputMain, 74f, PROGRESS_SLOT_TOP)
SlotPanel(this, frame, menu.outputStacking, 56f, PROGRESS_SLOT_TOP)

View File

@ -11,6 +11,7 @@ import ru.dbotthepony.mc.otm.client.screen.panels.SpritePanel
import ru.dbotthepony.mc.otm.client.screen.panels.button.DeviceControls
import ru.dbotthepony.mc.otm.client.screen.panels.slot.AbstractSlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.UserFilteredSlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.util.GridPanel
import ru.dbotthepony.mc.otm.client.screen.widget.ProgressGaugePanel
import ru.dbotthepony.mc.otm.compat.jei.MatterEntanglerRecipeCategory
@ -30,7 +31,7 @@ class MatterEntanglerScreen(menu: MatterEntanglerMenu, inventory: Inventory, tit
it.dockResize = DockResizeMode.NONE
for (slot in menu.inputs)
SlotPanel(this, it, slot)
UserFilteredSlotPanel(this, it, slot)
}
ProgressGaugePanel(this, frame, menu.progress).also {

View File

@ -10,9 +10,7 @@ import ru.dbotthepony.mc.otm.client.screen.panels.PlayerEquipmentPanel
import ru.dbotthepony.mc.otm.client.screen.panels.button.makeDeviceControls
import ru.dbotthepony.mc.otm.client.screen.panels.makeCuriosPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.BatterySlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel
import ru.dbotthepony.mc.otm.client.screen.widget.MatterGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.PowerGaugePanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.UserFilteredSlotPanel
import ru.dbotthepony.mc.otm.client.screen.widget.ProfiledMatterGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.ProfiledPowerGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.ProgressGaugePanel
@ -27,7 +25,7 @@ class MatterReconstructorScreen(menu: MatterReconstructorMenu, inventory: Invent
BatterySlotPanel(this, frame, menu.batterySlot, LEFT_MARGIN, SLOT_TOP_UNDER_GAUGE)
SlotPanel(this, frame, menu.slot, 66f, PROGRESS_SLOT_TOP)
UserFilteredSlotPanel(this, frame, menu.slot, 66f, PROGRESS_SLOT_TOP)
ProgressGaugePanel(this, frame, menu.progress, 37f, PROGRESS_ARROW_TOP)
makeDeviceControls(this, frame, redstoneConfig = menu.redstoneConfig, itemConfig = menu.itemConfig, energyConfig = menu.energyConfig, upgrades = menu.upgrades)

View File

@ -62,7 +62,7 @@ open class EffectListPanel<out S : Screen> @JvmOverloads constructor(
init {
scroll.visible = false
//scissor = true
scissor = true
}
open inner class EffectSquare(

View File

@ -60,7 +60,7 @@ open class PlayerEquipmentPanel<S : MatteryScreen<*>>(
parent: EditablePanel<*>?,
x: Float = 0f,
y: Float = 0f,
val armorSlots: List<PlayerSlot<MatteryMenu.EquipmentSlot, Slot>>
val armorSlots: List<PlayerSlot<MatteryMenu.EquipmentMenuSlot, Slot>>
) : EditablePanel<S>(screen, parent, x, y, height = HEIGHT, width = WIDTH) {
val armorSlotsStrip = EditablePanel(screen, this, width = AbstractSlotPanel.SIZE)
val entityPanel = EntityRendererPanel(screen, this, minecraft.player!!)

View File

@ -0,0 +1,120 @@
package ru.dbotthepony.mc.otm.client.screen.panels
import com.mojang.blaze3d.platform.InputConstants
import net.minecraft.ChatFormatting
import net.minecraft.client.gui.screens.Screen
import net.neoforged.neoforge.network.PacketDistributor
import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.client.render.IGUIRenderable
import ru.dbotthepony.mc.otm.client.render.Widgets18
import ru.dbotthepony.mc.otm.client.screen.panels.button.ButtonPanel
import ru.dbotthepony.mc.otm.client.screen.panels.util.GridPanel
import ru.dbotthepony.mc.otm.core.TextComponent
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.menu.QuickMoveInput
import ru.dbotthepony.mc.otm.network.QuickStackPacket
import java.util.function.IntConsumer
class QuickStackControlsPanel<out S : Screen>(
screen: S,
parent: EditablePanel<*>? = null,
x: Float = 0f,
y: Float = 0f
) : EditablePanel<S>(screen, parent, x, y, 18f, 18f) {
private val grid = GridPanel(screen, this, columns = 1, rows = 1)
private val buttons = ArrayList<EditablePanel<S>>()
init {
grid.dock = Dock.FILL
grid.layout = GridPanel.Layout.TOP_RIGHT
grid.columnMajorOrder = true
// so row with main button contains only that button (visibly)
val fill = EditablePanel(screen, grid, width = 18f, height = 18f)
fill.childrenOrder = 999
fill.visible = false
fill.dockMargin = DockProperty.bottomRight(1f)
buttons.add(fill)
}
private inner class MainButton : ButtonPanel<S>(screen, grid, width = 18f, height = 18f) {
init {
dockMargin = DockProperty.bottom(1f)
childrenOrder = -100000
tooltips.add(TranslatableComponent("otm.gui.quickmove.exchange"))
tooltips.add(TranslatableComponent("otm.gui.quickmove.exchange.desc").withStyle(ChatFormatting.GRAY))
tooltips.add(TextComponent(""))
tooltips.add(TranslatableComponent("otm.gui.quickmove_hint").withStyle(ChatFormatting.GRAY))
}
override var isDisabled: Boolean
get() = minecraft.player!!.isSpectator
set(value) {}
override val icon: IGUIRenderable
get() = Widgets18.SMART_STORAGE_EXCHANGE
override fun test(value: Int): Boolean {
return value == InputConstants.MOUSE_BUTTON_LEFT || value == InputConstants.MOUSE_BUTTON_RIGHT
}
override fun onClick(mouseButton: Int) {
if (mouseButton == InputConstants.MOUSE_BUTTON_LEFT) {
PacketDistributor.sendToServer(QuickStackPacket(QuickMoveInput.Mode.RESTOCK_WITH_MOVE, true))
PacketDistributor.sendToServer(QuickStackPacket(QuickMoveInput.Mode.RESTOCK, false))
} else {
buttons.forEach { it.visible = !it.visible }
if (buttons[0].visible) {
grid.columns = 2
grid.rows = 4
} else {
grid.columns = 1
grid.rows = 1
}
this@QuickStackControlsPanel.sizeToContents()
}
}
}
override fun mouseClickedInner(x: Double, y: Double, button: Int): Boolean {
return false
}
override fun sizeToContents() {
val oldWidth = width
super.sizeToContents()
x -= width - oldWidth
}
init {
for ((i, mode) in QuickMoveInput.Mode.entries.withIndex()) {
val button = ButtonPanel.square18(
screen, grid,
mode.iconFromStorage,
onPress = IntConsumer { PacketDistributor.sendToServer(QuickStackPacket(mode, false)) })
button.childrenOrder = 1000 + i
button.dockMargin = DockProperty.bottomRight(1f)
button.visible = false
button.tooltips.add(mode.textFromStorage)
buttons.add(button)
}
for ((i, mode) in QuickMoveInput.Mode.entries.withIndex()) {
val button = ButtonPanel.square18(
screen, grid,
mode.iconToStorage,
onPress = IntConsumer { PacketDistributor.sendToServer(QuickStackPacket(mode, true)) })
button.childrenOrder = i
button.dockMargin = DockProperty.bottom(1f)
button.visible = false
button.tooltips.add(mode.textToStorage)
buttons.add(button)
}
MainButton()
}
}

View File

@ -2,12 +2,11 @@ package ru.dbotthepony.mc.otm.client.screen.panels.button
import com.mojang.blaze3d.platform.InputConstants
import net.minecraft.ChatFormatting
import net.minecraft.client.gui.screens.Screen
import net.minecraft.network.chat.Component
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import ru.dbotthepony.kommons.math.RGBAColor
import ru.dbotthepony.kommons.util.Delegate
import ru.dbotthepony.kommons.util.Either
import ru.dbotthepony.kommons.util.value
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.block.entity.RedstoneSetting
@ -21,7 +20,6 @@ import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.client.render.IGUIRenderable
import ru.dbotthepony.mc.otm.client.render.sprites.AbstractMatterySprite
import ru.dbotthepony.mc.otm.client.render.ItemStackIcon
import ru.dbotthepony.mc.otm.client.render.UVWindingOrder
import ru.dbotthepony.mc.otm.client.render.Widgets18
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
import ru.dbotthepony.mc.otm.client.screen.panels.Dock
@ -38,8 +36,9 @@ import ru.dbotthepony.mc.otm.core.immutableList
import ru.dbotthepony.mc.otm.core.math.RelativeSide
import ru.dbotthepony.mc.otm.core.util.ItemStackSorter
import ru.dbotthepony.mc.otm.core.util.getLevelFromXp
import ru.dbotthepony.mc.otm.menu.MatteryMenu
import ru.dbotthepony.mc.otm.menu.MatterySlot
import ru.dbotthepony.mc.otm.menu.MatteryMenuSlot
import ru.dbotthepony.mc.otm.menu.QuickMoveInput
import ru.dbotthepony.mc.otm.menu.SortInput
import ru.dbotthepony.mc.otm.menu.UpgradeSlots
import ru.dbotthepony.mc.otm.menu.input.BooleanInputWithFeedback
import ru.dbotthepony.mc.otm.menu.input.EnergyConfigPlayerInput
@ -388,13 +387,17 @@ class DeviceControls<out S : MatteryScreen<*>>(
}
fun <P : EditablePanel<@UnsafeVariance S>> addButton(button: P): P {
buttons.add(button)
button.parent = this
alignButtons()
if (button !in buttons) {
buttons.add(button)
button.parent = this
alignButtons()
}
return button
}
fun <P : EditablePanel<@UnsafeVariance S>> addButton(button: P, after: EditablePanel<*>): P {
buttons.remove(button)
val index = buttons.indexOf(after)
if (index == -1) throw NoSuchElementException("Unknown panel to add button after: $after")
buttons.add(index + 1, button)
@ -404,9 +407,12 @@ class DeviceControls<out S : MatteryScreen<*>>(
}
fun <P : EditablePanel<@UnsafeVariance S>> prependButton(button: P): P {
buttons.add(0, button)
button.parent = this
alignButtons()
if (button !in buttons) {
buttons.add(0, button)
button.parent = this
alignButtons()
}
return button
}
@ -452,9 +458,74 @@ class DeviceControls<out S : MatteryScreen<*>>(
return result
}
fun sortingButtons(input: MatteryMenu.SortInput, unfoldableSettings: Boolean = true) {
object : ButtonPanel<S>(screen, this@DeviceControls, width = 18f, height = 18f) {
var buttons: List<EditablePanel<*>>? = null
abstract inner class FoldableButtonPanel() : ButtonPanel<S>(screen, this@DeviceControls, width = 18f, height = 18f) {
init {
addButton(this)
}
private var buttons: List<EditablePanel<S>>? = null
fun makeButtons() {
if (buttons == null) {
buttons = doMakeButtons().also {
it.forEach { addButton(it, this) }
}
}
}
fun removeButtons() {
if (buttons != null) {
buttons!!.forEach { it.remove() }
buttons = null
}
}
override fun test(value: Int): Boolean {
return value == InputConstants.MOUSE_BUTTON_LEFT || hasExtraButtons && value == InputConstants.MOUSE_BUTTON_RIGHT
}
fun switchButtons() {
if (buttons == null)
makeButtons()
else
removeButtons()
}
protected abstract val hasExtraButtons: Boolean
protected abstract fun performAction()
protected abstract fun doMakeButtons(): List<EditablePanel<S>>
final override fun onClick(mouseButton: Int) {
if (mouseButton == InputConstants.MOUSE_BUTTON_LEFT) {
performAction()
} else {
switchButtons()
}
}
}
fun sortingButtons(input: SortInput, unfoldableSettings: Boolean = true) {
object : FoldableButtonPanel() {
override fun performAction() {
input.clientInput()
}
override var isDisabled: Boolean
get() { return !input.input.test(minecraft.player ?: return false) }
set(value) {}
override val hasExtraButtons: Boolean
get() = unfoldableSettings
override fun doMakeButtons(): List<EditablePanel<S>> {
return sortingButtons(Delegate.Of(input.settings::isAscending), Delegate.Of(input.settings::sorting), ItemStackSorter.DEFAULT) {
for (v in ItemStackSorter.entries) {
add(v, v.icon, v.title)
}
finish()
}
}
init {
tooltips.add(TranslatableComponent("otm.gui.sorting.sort_now"))
@ -462,50 +533,75 @@ class DeviceControls<out S : MatteryScreen<*>>(
if (unfoldableSettings) {
tooltips.add(TextComponent(""))
tooltips.add(TranslatableComponent("otm.gui.sorting.sort_settings").withStyle(ChatFormatting.GRAY))
}
addButton(this)
if (!unfoldableSettings) {
} else {
makeButtons()
}
}
override val icon: IGUIRenderable
get() = Widgets18.SORT_NOW
}
}
override fun test(value: Int): Boolean {
return value == InputConstants.MOUSE_BUTTON_LEFT || unfoldableSettings && value == InputConstants.MOUSE_BUTTON_RIGHT
fun quickMoveButtons(
buttons: Map<QuickMoveInput.Mode, QuickMoveInput>,
fromStorage: Map<QuickMoveInput.Mode, QuickMoveInput>? = null
) {
object : FoldableButtonPanel() {
override fun performAction() {
if (fromStorage == null) {
buttons[QuickMoveInput.Mode.MOVE]!!.clientInput()
} else {
buttons[QuickMoveInput.Mode.RESTOCK_WITH_MOVE]!!.clientInput()
fromStorage[QuickMoveInput.Mode.RESTOCK]!!.clientInput()
}
}
override var isDisabled: Boolean
get() { return !input.input.test(minecraft.player ?: return false) }
get() { return !buttons.values.first().input.test(minecraft.player ?: return false) }
set(value) {}
private fun makeButtons() {
buttons = sortingButtons(Delegate.Of(input.settings::isAscending), Delegate.Of(input.settings::sorting), ItemStackSorter.DEFAULT) {
for (v in ItemStackSorter.entries) {
add(v, v.icon, v.title)
override val hasExtraButtons: Boolean
get() = true
override fun doMakeButtons(): List<EditablePanel<S>> {
var stream = buttons.entries
.stream()
if (fromStorage == null)
stream = stream.filter { (m) -> m != QuickMoveInput.Mode.MOVE }
return stream
.map { (m, b) ->
square18(
screen,
this,
icon = if (fromStorage == null) m.iconFromStorage else m.iconToStorage,
onPress = { b.clientInput() }
).also {
if (fromStorage == null)
it.tooltips.add(m.textFromStorage)
else
it.tooltips.add(m.textToStorage)
}
}
finish()
}
buttons!!.forEach { removeButton(it) }
buttons!!.forEach { addButton(it as EditablePanel<S>, this) }
.toList()
}
override fun onClick(mouseButton: Int) {
if (mouseButton == InputConstants.MOUSE_BUTTON_LEFT) {
input.clientInput()
} else {
if (buttons == null) {
makeButtons()
} else {
buttons!!.forEach { it.remove() }
buttons = null
}
init {
if (fromStorage == null)
tooltips.add(QuickMoveInput.Mode.MOVE.textFromStorage)
else {
tooltips.add(TranslatableComponent("otm.gui.quickmove.exchange"))
tooltips.add(TranslatableComponent("otm.gui.quickmove.exchange.desc").withStyle(ChatFormatting.GRAY))
}
tooltips.add(TextComponent(""))
tooltips.add(TranslatableComponent("otm.gui.quickmove_hint").withStyle(ChatFormatting.GRAY))
}
override val icon: IGUIRenderable
get() = if (fromStorage == null) QuickMoveInput.Mode.MOVE.iconFromStorage else Widgets18.SMART_STORAGE_EXCHANGE
}
}
@ -571,7 +667,7 @@ class DeviceControls<out S : MatteryScreen<*>>(
val grid = GridPanel(screen, frame, columns = columns, rows = rows)
for (slot in upgrades.slots) {
object : SlotPanel<S, MatterySlot>(screen, grid, slot) {
object : SlotPanel<S, MatteryMenuSlot>(screen, grid, slot) {
override val cursorType: CursorType
get() = if (upgrades.areLocked.get()) CursorType.NOT_ALLOWED else super.cursorType
}

View File

@ -4,22 +4,36 @@ import net.minecraft.world.item.ItemStack
import ru.dbotthepony.kommons.util.Delegate
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
import ru.dbotthepony.mc.otm.container.ItemFilter
open class FilterSlotPanel<out S : MatteryScreen<*>> @JvmOverloads constructor(
open class FilterSlotPanel<out S : MatteryScreen<*>>(
screen: S,
parent: EditablePanel<*>?,
val slot: Delegate<ItemStack>,
val slot: Delegate<ItemFilter>,
x: Float = 0f,
y: Float = 0f,
width: Float = SIZE,
height: Float = SIZE
) : AbstractSlotPanel<S>(screen, parent, x, y, width, height) {
private var lastFilteredItemDisplayUpdate = System.nanoTime()
private var filteredItemDisplayIndex = 0
override val itemStack: ItemStack get() {
return slot.get()
val items = slot.get().displayItems
if (items.isEmpty())
return ItemStack.EMPTY
if (System.nanoTime() - lastFilteredItemDisplayUpdate >= 1_000_000_000L || filteredItemDisplayIndex !in items.indices) {
lastFilteredItemDisplayUpdate = System.nanoTime()
filteredItemDisplayIndex = random.nextInt(items.size)
}
return items.asList()[filteredItemDisplayIndex].asItemStack()
}
override fun mouseClickedInner(x: Double, y: Double, button: Int): Boolean {
slot.accept(screen.menu.carried)
slot.accept(ItemFilter.item(screen.menu.carried))
return true
}
}

View File

@ -18,10 +18,6 @@ open class InventorySlotPanel<out S : MatteryScreen<*>, out T : MatteryMenu.Inve
x: Float = 0f,
y: Float = 0f,
) : UserFilteredSlotPanel<S, T>(screen, parent, slot, x, y, SIZE, SIZE) {
override var slotFilter: Item?
get() = slot.filter?.get()
set(value) { slot.filter?.accept(value) }
override fun renderBackgroundBeforeFilter(graphics: MGUIGraphics, mouseX: Float, mouseY: Float, partialTick: Float) {
if (slot.chargeFlag?.get() == true) {
Widgets18.CHARGE_SLOT_BACKGROUND.render(graphics, 0f, 0f, width, height)

View File

@ -5,9 +5,13 @@ package ru.dbotthepony.mc.otm.client.screen.panels.slot
import com.mojang.blaze3d.systems.RenderSystem
import net.minecraft.ChatFormatting
import net.minecraft.client.renderer.GameRenderer
import net.minecraft.network.chat.Component
import net.minecraft.world.inventory.AbstractContainerMenu
import net.minecraft.world.inventory.Slot
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import net.neoforged.neoforge.client.extensions.common.IClientItemExtensions
import ru.dbotthepony.kommons.math.RGBAColor
import ru.dbotthepony.mc.otm.client.render.MGUIGraphics
import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.client.render.Widgets18
@ -15,6 +19,14 @@ import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
import ru.dbotthepony.mc.otm.compat.itemborders.isItemBordersLoaded
import ru.dbotthepony.mc.otm.compat.itemborders.renderSlotBorder
import ru.dbotthepony.mc.otm.container.IFilteredContainerSlot
import ru.dbotthepony.mc.otm.container.ItemFilter
import ru.dbotthepony.mc.otm.container.ItemStackKey
import ru.dbotthepony.mc.otm.container.util.containerSlotOrNull
import ru.dbotthepony.mc.otm.core.TextComponent
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.isNotEmpty
import ru.dbotthepony.mc.otm.menu.UserFilteredMenuSlot
import javax.annotation.Nonnull
import kotlin.math.roundToInt
@ -52,6 +64,49 @@ open class SlotPanel<out S : MatteryScreen<*>, out T : Slot>(
}
}
protected open fun renderBackgroundBeforeFilter(graphics: MGUIGraphics, mouseX: Float, mouseY: Float, partialTick: Float) {}
private var lastFilteredItemDisplayUpdate = System.nanoTime()
private var filteredItemDisplayIndex = 0
private fun selectRandomItemFromFilter(filter: ItemFilter): ItemStack {
val items = filter.displayItems
if (items.isEmpty()) {
return ItemStack.EMPTY
} else {
if (System.nanoTime() - lastFilteredItemDisplayUpdate >= 1_000_000_000L || filteredItemDisplayIndex !in items.indices) {
lastFilteredItemDisplayUpdate = System.nanoTime()
filteredItemDisplayIndex = random.nextInt(items.size)
}
return items.asList()[filteredItemDisplayIndex].asItemStack()
}
}
override fun renderSlotBackground(graphics: MGUIGraphics, mouseX: Float, mouseY: Float, partialTick: Float) {
super.renderSlotBackground(graphics, mouseX, mouseY, partialTick)
val containerSlot = slot.container.containerSlotOrNull(slot.slotIndex)
if (containerSlot is IFilteredContainerSlot) {
renderBackgroundBeforeFilter(graphics, mouseX, mouseY, partialTick)
if (containerSlot.filter.denyAll) {
graphics.renderRect(0f, 0f, width, height, color = SLOT_BLOCK_COLOR)
} else if (!containerSlot.filter.allowAll) {
val itemStack = selectRandomItemFromFilter(containerSlot.filter)
if (itemStack.isNotEmpty) {
screen.renderItemStack(graphics, itemStack, null)
clearDepth(graphics)
graphics.renderRect(0f, 0f, width, height, color = SLOT_FILTER_COLOR)
}
}
}
}
override fun innerRender(graphics: MGUIGraphics, mouseX: Float, mouseY: Float, partialTick: Float) {
slot.x = absoluteX.roundToInt() - screen.guiLeft
slot.y = absoluteY.roundToInt() - screen.guiTop
@ -120,10 +175,58 @@ open class SlotPanel<out S : MatteryScreen<*>, out T : Slot>(
}
}
override fun innerRenderTooltips(@Nonnull graphics: MGUIGraphics, mouseX: Float, mouseY: Float, partialTick: Float): Boolean {
override fun innerRenderTooltips(graphics: MGUIGraphics, mouseX: Float, mouseY: Float, partialTick: Float): Boolean {
val slot = slot.container.containerSlotOrNull(slot.containerSlot) as? IFilteredContainerSlot
if (isHovered && slot?.filter != null && slot.filter.hasRules && itemStack.isEmpty) {
val itemstack = selectRandomItemFromFilter(slot.filter)
val text: List<Component>
if (itemstack.isEmpty) {
text = listOf(
TranslatableComponent("otm.gui.slot_filter.filtered").withStyle(ChatFormatting.GRAY),
TranslatableComponent("otm.gui.slot_filter.hint").withStyle(ChatFormatting.GRAY)
)
} else {
text = getItemStackTooltip(itemstack).toMutableList().also {
it.add(0, TranslatableComponent("otm.gui.slot_filter.filtered").withStyle(ChatFormatting.GRAY))
it.add(1, TranslatableComponent("otm.gui.slot_filter.hint").withStyle(ChatFormatting.GRAY))
it.add(2, TextComponent(""))
}
}
graphics.renderComponentTooltip(
IClientItemExtensions.of(itemstack).getFont(itemstack, IClientItemExtensions.FontContext.TOOLTIP) ?: font,
text,
mouseX.toInt(),
mouseY.toInt(),
itemstack
)
return true
} else if (isHovered && slot?.filter?.denyAll == true && itemStack.isEmpty) {
graphics.renderComponentTooltip(
font,
ArrayList<Component>().also {
it.add(TranslatableComponent("otm.gui.slot_filter.forbidden").withStyle(ChatFormatting.GRAY))
it.add(TranslatableComponent("otm.gui.slot_filter.hint").withStyle(ChatFormatting.GRAY))
},
mouseX.toInt(),
mouseY.toInt()
)
return true
}
// no op, screen does it for us (completely)
return false
}
companion object {
val SLOT_FILTER_COLOR = RGBAColor(85, 113, 216, 150)
val SLOT_BLOCK_COLOR = RGBAColor(219, 113, 113, 150)
}
}
fun <S : MatteryScreen<*>, T : Slot> BatterySlotPanel(
@ -134,7 +237,7 @@ fun <S : MatteryScreen<*>, T : Slot> BatterySlotPanel(
y: Float = 0f,
width: Float = AbstractSlotPanel.SIZE,
height: Float = AbstractSlotPanel.SIZE,
) = SlotPanel(screen, parent, slot, x, y, width, height).also { it.slotBackgroundEmpty = Widgets18.BATTERY_SLOT_BACKGROUND }
) = (if (slot is UserFilteredMenuSlot) UserFilteredSlotPanel(screen, parent, slot, x, y, width, height) else SlotPanel(screen, parent, slot, x, y, width, height)).also { it.slotBackgroundEmpty = Widgets18.BATTERY_SLOT_BACKGROUND }
fun <S : MatteryScreen<*>, T : Slot> EquipmentBatterySlotPanel(
screen: S,
@ -144,7 +247,7 @@ fun <S : MatteryScreen<*>, T : Slot> EquipmentBatterySlotPanel(
y: Float = 0f,
width: Float = AbstractSlotPanel.SIZE,
height: Float = AbstractSlotPanel.SIZE,
) = SlotPanel(screen, parent, slot, x, y, width, height).also { it.slotBackgroundEmpty = Widgets18.EQUIPMENT_BATTERY_SLOT_BACKGROUND }
) = (if (slot is UserFilteredMenuSlot) UserFilteredSlotPanel(screen, parent, slot, x, y, width, height) else SlotPanel(screen, parent, slot, x, y, width, height)).also { it.slotBackgroundEmpty = Widgets18.EQUIPMENT_BATTERY_SLOT_BACKGROUND }
fun <S : MatteryScreen<*>, T : Slot> PatternSlotPanel(
screen: S,
@ -154,7 +257,7 @@ fun <S : MatteryScreen<*>, T : Slot> PatternSlotPanel(
y: Float = 0f,
width: Float = AbstractSlotPanel.SIZE,
height: Float = AbstractSlotPanel.SIZE,
) = SlotPanel(screen, parent, slot, x, y, width, height).also { it.slotBackgroundEmpty = Widgets18.PATTERN_SLOT_BACKGROUND }
) = (if (slot is UserFilteredMenuSlot) UserFilteredSlotPanel(screen, parent, slot, x, y, width, height) else SlotPanel(screen, parent, slot, x, y, width, height)).also { it.slotBackgroundEmpty = Widgets18.PATTERN_SLOT_BACKGROUND }
fun <S : MatteryScreen<*>, T : Slot> MatterCapacitorSlotPanel(
screen: S,
@ -164,4 +267,4 @@ fun <S : MatteryScreen<*>, T : Slot> MatterCapacitorSlotPanel(
y: Float = 0f,
width: Float = AbstractSlotPanel.SIZE,
height: Float = AbstractSlotPanel.SIZE,
) = SlotPanel(screen, parent, slot, x, y, width, height).also { it.slotBackgroundEmpty = Widgets18.MATTER_CAPACITOR_SLOT_BACKGROUND }
) = (if (slot is UserFilteredMenuSlot) UserFilteredSlotPanel(screen, parent, slot, x, y, width, height) else SlotPanel(screen, parent, slot, x, y, width, height)).also { it.slotBackgroundEmpty = Widgets18.MATTER_CAPACITOR_SLOT_BACKGROUND }

View File

@ -1,28 +1,17 @@
package ru.dbotthepony.mc.otm.client.screen.panels.slot
import com.mojang.blaze3d.platform.InputConstants
import net.minecraft.ChatFormatting
import net.minecraft.network.chat.Component
import net.minecraft.world.inventory.Slot
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import net.neoforged.neoforge.client.extensions.common.IClientItemExtensions
import ru.dbotthepony.mc.otm.client.render.MGUIGraphics
import ru.dbotthepony.mc.otm.client.isCtrlDown
import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.client.playGuiClickSound
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
import ru.dbotthepony.mc.otm.core.TextComponent
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.kommons.math.RGBAColor
import ru.dbotthepony.kommons.util.Delegate
import ru.dbotthepony.kommons.util.getValue
import ru.dbotthepony.kommons.util.setValue
import ru.dbotthepony.mc.otm.menu.UserFilteredSlot
import ru.dbotthepony.mc.otm.container.IFilteredContainerSlot
import ru.dbotthepony.mc.otm.container.ItemFilter
import ru.dbotthepony.mc.otm.container.util.containerSlot
import ru.dbotthepony.mc.otm.menu.UserFilteredMenuSlot
abstract class UserFilteredSlotPanel<out S : MatteryScreen<*>, out T : Slot>(
open class UserFilteredSlotPanel<out S : MatteryScreen<*>, out T : UserFilteredMenuSlot>(
screen: S,
parent: EditablePanel<*>?,
slot: T,
@ -31,77 +20,22 @@ abstract class UserFilteredSlotPanel<out S : MatteryScreen<*>, out T : Slot>(
width: Float = SIZE,
height: Float = SIZE,
) : SlotPanel<S, T>(screen, parent, slot, x, y, width, height) {
abstract var slotFilter: Item?
protected open fun renderBackgroundBeforeFilter(graphics: MGUIGraphics, mouseX: Float, mouseY: Float, partialTick: Float) {}
override fun renderSlotBackground(graphics: MGUIGraphics, mouseX: Float, mouseY: Float, partialTick: Float) {
super.renderSlotBackground(graphics, mouseX, mouseY, partialTick)
renderBackgroundBeforeFilter(graphics, mouseX, mouseY, partialTick)
if (slotFilter != null) {
if (slotFilter !== Items.AIR) {
val itemStack = ItemStack(slotFilter!!, 1)
screen.renderItemStack(graphics, itemStack, null)
clearDepth(graphics)
graphics.renderRect(0f, 0f, width, height, color = SLOT_FILTER_COLOR)
} else {
graphics.renderRect(0f, 0f, width, height, color = SLOT_BLOCK_COLOR)
}
}
}
override fun innerRenderTooltips(graphics: MGUIGraphics, mouseX: Float, mouseY: Float, partialTick: Float): Boolean {
if (isHovered && slotFilter != null && slotFilter !== Items.AIR && itemStack.isEmpty) {
val itemstack = ItemStack(slotFilter!!, 1)
graphics.renderComponentTooltip(
IClientItemExtensions.of(itemstack).getFont(itemstack, IClientItemExtensions.FontContext.TOOLTIP) ?: font,
getItemStackTooltip(itemstack).toMutableList().also {
it.add(0, TranslatableComponent("otm.gui.slot_filter.filtered").withStyle(ChatFormatting.GRAY))
it.add(1, TranslatableComponent("otm.gui.slot_filter.hint").withStyle(ChatFormatting.GRAY))
it.add(2, TextComponent(""))
},
mouseX.toInt(),
mouseY.toInt(),
itemstack
)
return true
} else if (isHovered && slotFilter === Items.AIR && itemStack.isEmpty) {
graphics.renderComponentTooltip(
font,
ArrayList<Component>().also {
it.add(TranslatableComponent("otm.gui.slot_filter.forbidden").withStyle(ChatFormatting.GRAY))
it.add(TranslatableComponent("otm.gui.slot_filter.hint").withStyle(ChatFormatting.GRAY))
},
mouseX.toInt(),
mouseY.toInt()
)
return true
}
return super.innerRenderTooltips(graphics, mouseX, mouseY, partialTick)
}
override fun mouseClickedInner(x: Double, y: Double, button: Int): Boolean {
val filterInput = slot.filterInput ?: return super.mouseClickedInner(x, y, button)
val containerSlot = slot.containerSlot() as IFilteredContainerSlot
if (button == InputConstants.MOUSE_BUTTON_LEFT && minecraft.window.isCtrlDown) {
if (slotFilter === null) {
if (containerSlot.filter.allowAll) {
if (screen.menu.carried.isEmpty) {
slotFilter = slot.item.item
filterInput.accept(ItemFilter.item(slot.item.item))
} else {
slotFilter = screen.menu.carried.item
filterInput.accept(ItemFilter.item(screen.menu.carried.item))
}
} else {
slotFilter = null
filterInput.accept(ItemFilter.EMPTY)
}
playGuiClickSound()
return true
} else {
return super.mouseClickedInner(x, y, button)
@ -115,40 +49,4 @@ abstract class UserFilteredSlotPanel<out S : MatteryScreen<*>, out T : Slot>(
return super.mouseReleasedInner(x, y, button)
}
companion object {
val SLOT_FILTER_COLOR = RGBAColor(85, 113, 216, 150)
val SLOT_BLOCK_COLOR = RGBAColor(219, 113, 113, 150)
fun <S : MatteryScreen<*>, T : Slot> of(
screen: S,
parent: EditablePanel<*>?,
slot: T,
x: Float = 0f,
y: Float = 0f,
width: Float = SIZE,
height: Float = SIZE,
filter: Delegate<Item?>
): UserFilteredSlotPanel<S, T> {
return object : UserFilteredSlotPanel<S, T>(screen, parent, slot, x, y, width, height) {
override var slotFilter: Item? by filter
}
}
fun <S : MatteryScreen<*>, T : UserFilteredSlot> of(
screen: S,
parent: EditablePanel<*>?,
slot: T,
x: Float = 0f,
y: Float = 0f,
width: Float = SIZE,
height: Float = SIZE,
): UserFilteredSlotPanel<S, T> {
return object : UserFilteredSlotPanel<S, T>(screen, parent, slot, x, y, width, height) {
override var slotFilter: Item?
get() = slot.filter?.get()
set(value) { slot.filter?.accept(value) }
}
}
}
}

View File

@ -66,14 +66,12 @@ class DriveViewerScreen(menu: DriveViewerMenu, inventory: Inventory, title: Comp
settings.add(filterGrid)
for (i in 0 until PortableCondensationDriveItem.MAX_FILTERS) {
FilterSlotPanel(this, filterGrid, menu.driveFilterSlots[i], 0f, 0f)
FilterSlotPanel(this, filterGrid, menu.driveFilter.slots[i], 0f, 0f)
}
settings.add(EditablePanel(this, frame, width = 90f).also {
it.dock = Dock.LEFT
BooleanButtonPanel.Checkbox(this, it, menu.isWhitelist, TranslatableComponent("otm.gui.filter.is_whitelist")).also { it.dockTop = 20f; it.dock = Dock.TOP }
BooleanButtonPanel.Checkbox(this, it, menu.matchTag, TranslatableComponent("otm.gui.filter.match_tag")).also { it.dockTop = 4f; it.dock = Dock.TOP }
BooleanButtonPanel.Checkbox(this, it, menu.matchComponents, TranslatableComponent("otm.gui.filter.match_nbt")).also { it.dockTop = 4f; it.dock = Dock.TOP }
BooleanButtonPanel.Checkbox(this, it, menu.driveFilter.isWhitelist, TranslatableComponent("otm.gui.filter.is_whitelist")).also { it.dockTop = 20f; it.dock = Dock.TOP }
})
frame.CustomTab(view, activeIcon = ItemStackIcon(ItemStack(MItems.PORTABLE_CONDENSATION_DRIVE)))

View File

@ -34,18 +34,6 @@ class StorageImporterExporterScreen(menu: StorageImporterExporterMenu, inventory
it.childrenOrder = -1
}
BooleanButtonPanel.Checkbox(this, right, menu.filter.matchComponents, TranslatableComponent("otm.gui.filter.match_nbt")).also {
it.dock = Dock.BOTTOM
it.dockTop = 2f
it.childrenOrder = -2
}
BooleanButtonPanel.Checkbox(this, right, menu.filter.matchTag, TranslatableComponent("otm.gui.filter.match_tag")).also {
it.dock = Dock.BOTTOM
it.dockTop = 2f
it.childrenOrder = -3
}
makeDeviceControls(this, frame, redstoneConfig = menu.redstoneConfig, energyConfig = menu.energyConfig)
return frame

View File

@ -12,9 +12,11 @@ import ru.dbotthepony.mc.otm.client.screen.panels.button.DeviceControls
import ru.dbotthepony.mc.otm.client.screen.panels.slot.AbstractSlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.BatterySlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.UserFilteredSlotPanel
import ru.dbotthepony.mc.otm.client.screen.widget.ProgressGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.WideProfiledPowerGaugePanel
import ru.dbotthepony.mc.otm.compat.jei.isJeiLoaded
import ru.dbotthepony.mc.otm.menu.UserFilteredMenuSlot
import ru.dbotthepony.mc.otm.menu.tech.AbstractProcessingMachineMenu
import kotlin.math.roundToInt
@ -50,9 +52,14 @@ open class AbstractProcessingMachineScreen<M : AbstractProcessingMachineMenu>(me
row.dock = Dock.TOP
row.dockBottom = 2f
SlotPanel(this, row, input).also {
it.dock = Dock.LEFT
}
if (input is UserFilteredMenuSlot)
UserFilteredSlotPanel(this, row, input).also {
it.dock = Dock.LEFT
}
else
SlotPanel(this, row, input).also {
it.dock = Dock.LEFT
}
progressPanels.add(ProgressGaugePanel(this, row, progress).also {
it.dock = Dock.LEFT

View File

@ -9,6 +9,7 @@ import ru.dbotthepony.mc.otm.client.screen.panels.button.makeDeviceControls
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.client.screen.panels.slot.BatterySlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.UserFilteredSlotPanel
import ru.dbotthepony.mc.otm.client.screen.widget.ProgressGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.WideProfiledPowerGaugePanel
import ru.dbotthepony.mc.otm.menu.tech.ChemicalGeneratorMenu
@ -35,7 +36,7 @@ class ChemicalGeneratorScreen(menu: ChemicalGeneratorMenu, inventory: Inventory,
progress.setRecipeType { listOf(RecipeTypes.FUELING) }
SlotPanel(this, frame, menu.residueSlot, 56f, PROGRESS_SLOT_TOP)
SlotPanel(this, frame, menu.fuelSlot, 104f, PROGRESS_SLOT_TOP)
UserFilteredSlotPanel(this, frame, menu.fuelSlot, 104f, PROGRESS_SLOT_TOP)
makeDeviceControls(this, frame, redstoneConfig = menu.redstoneConfig, itemConfig = menu.itemConfig, energyConfig = menu.energyConfig)

View File

@ -111,7 +111,19 @@ class EnergyCounterScreen(menu: EnergyCounterMenu, inventory: Inventory, title:
}
}
makeDeviceControls(this, frame, redstoneConfig = menu.redstone)
val controls = makeDeviceControls(this, frame, redstoneConfig = menu.redstone)
controls.addButton(
BooleanButtonPanel.square18(
this,
controls,
menu.pullEnergyFromInput,
iconActive = Widgets18.LEFT_CONTROLS.push,
iconInactive = Widgets18.LEFT_CONTROLS.output,
tooltipActive = TranslatableComponent("block.overdrive_that_matters.energy_counter.do_pull"),
tooltipInactive = TranslatableComponent("block.overdrive_that_matters.energy_counter.dont_pull")
)
)
return frame
}

View File

@ -21,6 +21,7 @@ import ru.dbotthepony.mc.otm.client.screen.panels.button.ButtonPanel
import ru.dbotthepony.mc.otm.client.screen.panels.button.makeDeviceControls
import ru.dbotthepony.mc.otm.client.screen.panels.input.TextInputPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.UserFilteredSlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.util.HorizontalStripPanel
import ru.dbotthepony.mc.otm.core.ResourceLocation
import ru.dbotthepony.mc.otm.core.TextComponent
@ -279,7 +280,7 @@ class EssenceStorageScreen(menu: EssenceStorageMenu, inventory: Inventory, title
get() = SET_EXACT
override fun onClick(mouseButton: Int) {
val player = minecraft!!.player!! ?: return
val player = minecraft!!.player!!
if (player.experienceLevel == customDispense) {
if (player.experienceProgress > 0f) {
@ -297,7 +298,7 @@ class EssenceStorageScreen(menu: EssenceStorageMenu, inventory: Inventory, title
set(_) {}
}
SlotPanel(this, inputs, menu.mendingSlot).also {
UserFilteredSlotPanel(this, inputs, menu.mendingSlot).also {
it.dock = Dock.LEFT
it.tooltips.add(TranslatableComponent("enchantment.minecraft.mending").withStyle(ChatFormatting.GRAY))
}

View File

@ -20,7 +20,7 @@ class ItemHatchScreen(menu: ItemHatchMenu, inventory: Inventory, title: Componen
val grid = GridPanel(this, frame, 8f, 18f, 9f * 18f, 6f * 18f, 9, 6)
for (slot in menu.storageSlots)
UserFilteredSlotPanel.of(this, grid, slot)
UserFilteredSlotPanel(this, grid, slot)
if (menu.isInput) {
val controls = DeviceControls(this, frame)

View File

@ -6,7 +6,6 @@ import lain.mods.cos.impl.client.PlayerRenderHandler
import lain.mods.cos.impl.client.gui.GuiCosArmorInventory
import lain.mods.cos.impl.network.payload.PayloadSetSkinArmor
import net.minecraft.client.gui.screens.Screen
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.Container
@ -20,9 +19,6 @@ import net.neoforged.neoforge.network.PacketDistributor
import ru.dbotthepony.kommons.math.RGBAColor
import ru.dbotthepony.mc.otm.client.render.MGUIGraphics
import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.client.render.IGUIRenderable
import ru.dbotthepony.mc.otm.client.render.UVWindingOrder
import ru.dbotthepony.mc.otm.client.render.sprites.MatterySprite
import ru.dbotthepony.mc.otm.client.render.sprites.sprite
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
import ru.dbotthepony.mc.otm.client.screen.panels.button.ButtonPanel
@ -32,7 +28,7 @@ import ru.dbotthepony.mc.otm.container.util.awareStream
import ru.dbotthepony.mc.otm.container.util.iterator
import ru.dbotthepony.mc.otm.core.collect.AwareItemStack
import ru.dbotthepony.mc.otm.core.collect.emptyIterator
import ru.dbotthepony.mc.otm.menu.MatterySlot
import ru.dbotthepony.mc.otm.menu.MatteryMenuSlot
import java.util.stream.Stream
val isCosmeticArmorLoaded by lazy {
@ -47,7 +43,7 @@ val Player.cosmeticArmorSlots: Map<EquipmentSlot, Slot>? get() {
return cosmeticArmorSlotsImpl
}
private class CosmeticSlot(container: Container, private val slot: EquipmentSlot, private val player: Player) : MatterySlot(container, when (slot) {
private class CosmeticMenuSlot(container: Container, private val slot: EquipmentSlot, private val player: Player) : MatteryMenuSlot(container, when (slot) {
EquipmentSlot.FEET -> 0
EquipmentSlot.LEGS -> 1
EquipmentSlot.CHEST -> 2
@ -127,10 +123,10 @@ private val Player.cosmeticArmorSlotsImpl: Map<EquipmentSlot, Slot>? get() {
}
return mapOf(
EquipmentSlot.HEAD to CosmeticSlot(container, EquipmentSlot.HEAD, this),
EquipmentSlot.CHEST to CosmeticSlot(container, EquipmentSlot.CHEST, this),
EquipmentSlot.LEGS to CosmeticSlot(container, EquipmentSlot.LEGS, this),
EquipmentSlot.FEET to CosmeticSlot(container, EquipmentSlot.FEET, this),
EquipmentSlot.HEAD to CosmeticMenuSlot(container, EquipmentSlot.HEAD, this),
EquipmentSlot.CHEST to CosmeticMenuSlot(container, EquipmentSlot.CHEST, this),
EquipmentSlot.LEGS to CosmeticMenuSlot(container, EquipmentSlot.LEGS, this),
EquipmentSlot.FEET to CosmeticMenuSlot(container, EquipmentSlot.FEET, this),
)
}

View File

@ -13,8 +13,8 @@ import ru.dbotthepony.mc.otm.compat.jade.JadeUids
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.getCapability
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.getDecimal
import ru.dbotthepony.mc.otm.core.math.putDecimal
import ru.dbotthepony.mc.otm.core.nbt.getDecimal
import ru.dbotthepony.mc.otm.core.nbt.putDecimal
import ru.dbotthepony.mc.otm.core.util.formatMatter
import snownee.jade.api.BlockAccessor
import snownee.jade.api.IBlockComponentProvider

View File

@ -13,8 +13,8 @@ import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.kommons.math.RGBAColor
import ru.dbotthepony.mc.otm.capability.IProfiledStorage
import ru.dbotthepony.mc.otm.core.getCapability
import ru.dbotthepony.mc.otm.core.math.getDecimal
import ru.dbotthepony.mc.otm.core.math.putDecimal
import ru.dbotthepony.mc.otm.core.nbt.getDecimal
import ru.dbotthepony.mc.otm.core.nbt.putDecimal
import ru.dbotthepony.mc.otm.core.util.formatPower
import snownee.jade.api.*
import snownee.jade.api.config.IPluginConfig

View File

@ -0,0 +1,34 @@
package ru.dbotthepony.mc.otm.compat.vanilla
import net.minecraft.world.Container
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.MenuType
import ru.dbotthepony.mc.otm.menu.MatteryMenu
import ru.dbotthepony.mc.otm.menu.MatteryMenuSlot
import ru.dbotthepony.mc.otm.menu.QuickMoveInput
import ru.dbotthepony.mc.otm.menu.SortInput
abstract class AbstractVanillaChestMenu(
type: MenuType<*>,
containerId: Int,
inventory: Inventory,
val container: Container
) : MatteryMenu(type, containerId, inventory) {
abstract val rows: Int
abstract val columns: Int
abstract val containerSlots: List<MatteryMenuSlot>
abstract val quickMoveToStorage: Map<QuickMoveInput.Mode, QuickMoveInput>
abstract val quickMoveFromStorage: Map<QuickMoveInput.Mode, QuickMoveInput>
val sort = SortInput(this, container, playerSortSettings)
override fun stillValid(player: Player): Boolean {
return container.stillValid(player)
}
override fun removed(player: Player) {
super.removed(player)
container.stopOpen(player)
}
}

View File

@ -22,7 +22,7 @@ import ru.dbotthepony.mc.otm.player.MatteryPlayer
import ru.dbotthepony.mc.otm.player.matteryPlayer
import ru.dbotthepony.mc.otm.core.ResourceLocation
import ru.dbotthepony.mc.otm.menu.MatteryMenu
import ru.dbotthepony.mc.otm.menu.MatterySlot
import ru.dbotthepony.mc.otm.menu.MatteryMenuSlot
import java.util.*
private val menuConfigurations = WeakHashMap<AbstractContainerMenu, MenuConfiguration>()
@ -47,9 +47,9 @@ private class MenuConfiguration(
for (i in 0 .. 8) {
if (matteryPlayer.exopackContainer.containerSize > i + offset) {
row.add(MatterySlot(matteryPlayer.exopackContainer, i + offset))
row.add(MatteryMenuSlot(matteryPlayer.exopackContainer, i + offset))
} else {
row.add(FakeSlot())
row.add(FakeMenuSlot())
}
}
@ -150,7 +150,7 @@ private class MenuConfiguration(
}
}
private class FakeSlot : MatterySlot(SimpleContainer(1), 0, 0, 0) {
private class FakeMenuSlot : MatteryMenuSlot(SimpleContainer(1), 0, 0, 0) {
override fun mayPickup(player: Player): Boolean {
return false
}

View File

@ -1,118 +1,78 @@
package ru.dbotthepony.mc.otm.compat.vanilla
import net.minecraft.core.registries.Registries
import net.minecraft.world.Container
import net.minecraft.world.SimpleContainer
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player
import net.minecraft.world.flag.FeatureFlags
import net.minecraft.world.inventory.MenuType
import net.neoforged.bus.api.IEventBus
import net.neoforged.neoforge.client.event.RegisterMenuScreensEvent
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.menu.MatteryMenu
import ru.dbotthepony.mc.otm.menu.MatterySlot
import ru.dbotthepony.mc.otm.container.EnhancedContainer
import ru.dbotthepony.mc.otm.menu.MatteryMenuSlot
import ru.dbotthepony.mc.otm.menu.QuickMoveInput
import ru.dbotthepony.mc.otm.menu.makeSlots
import ru.dbotthepony.mc.otm.registry.MDeferredRegister
class MatteryChestMenu(
type: MenuType<*>, containerId: Int,
inventory: Inventory, val rows: Int, val columns: Int,
val container: Container = SimpleContainer(rows * columns)
) : MatteryMenu(type, containerId, inventory) {
val chestSlots = makeSlots(container, ::MatterySlot)
val sort = SortInput(container, playerSortSettings)
inventory: Inventory, override val rows: Int, override val columns: Int,
container: Container = EnhancedContainer.Simple(rows * columns)
) : AbstractVanillaChestMenu(type, containerId, inventory, container) {
override val containerSlots = makeSlots(container, ::MatteryMenuSlot)
init {
require(rows * columns == container.containerSize) { "Provided container $container has different dimensions than specified rows x columns: ${container.containerSize} vs $rows x $columns (${rows * columns})" }
container.startOpen(player)
addStorageSlot(chestSlots)
addStorageSlot(containerSlots)
addInventorySlots()
}
override fun stillValid(player: Player): Boolean {
return container.stillValid(player)
}
override fun removed(player: Player) {
super.removed(player)
container.stopOpen(player)
}
override val quickMoveToStorage = QuickMoveInput.create(this, playerCombinedInventorySlots, containerSlots)
override val quickMoveFromStorage = QuickMoveInput.create(this, containerSlots, playerInventorySlots, false)
companion object {
private val registrar = MDeferredRegister(Registries.MENU, OverdriveThatMatters.MOD_ID)
private val GENERIC_9x1 by registrar.register("generic_9x1") { MenuType(::c9x1, FeatureFlags.VANILLA_SET) }
private val GENERIC_9x2 by registrar.register("generic_9x2") { MenuType(::c9x2, FeatureFlags.VANILLA_SET) }
private val GENERIC_9x3 by registrar.register("generic_9x3") { MenuType(::c9x3, FeatureFlags.VANILLA_SET) }
private val GENERIC_9x4 by registrar.register("generic_9x4") { MenuType(::c9x4, FeatureFlags.VANILLA_SET) }
private val GENERIC_9x5 by registrar.register("generic_9x5") { MenuType(::c9x5, FeatureFlags.VANILLA_SET) }
private val GENERIC_9x6 by registrar.register("generic_9x6") { MenuType(::c9x6, FeatureFlags.VANILLA_SET) }
private val GENERIC_3x3 by registrar.register("generic_3x3") { MenuType(::c3x3, FeatureFlags.VANILLA_SET) }
private val HOPPER by registrar.register("hopper") { MenuType(::hopper, FeatureFlags.VANILLA_SET) }
@JvmStatic
@JvmOverloads
fun c9x1(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(9)): MatteryChestMenu {
return MatteryChestMenu(GENERIC_9x1, containerId, inventory, 1, 9, container)
return MatteryChestMenu(VanillaMenuTypes.GENERIC_9x1, containerId, inventory, 1, 9, container)
}
@JvmStatic
@JvmOverloads
fun c9x2(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(9 * 2)): MatteryChestMenu {
return MatteryChestMenu(GENERIC_9x2, containerId, inventory, 2, 9, container)
return MatteryChestMenu(VanillaMenuTypes.GENERIC_9x2, containerId, inventory, 2, 9, container)
}
@JvmStatic
@JvmOverloads
fun c9x3(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(9 * 3)): MatteryChestMenu {
return MatteryChestMenu(GENERIC_9x3, containerId, inventory, 3, 9, container)
return MatteryChestMenu(VanillaMenuTypes.GENERIC_9x3, containerId, inventory, 3, 9, container)
}
@JvmStatic
@JvmOverloads
fun c9x4(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(9 * 4)): MatteryChestMenu {
return MatteryChestMenu(GENERIC_9x4, containerId, inventory, 4, 9, container)
return MatteryChestMenu(VanillaMenuTypes.GENERIC_9x4, containerId, inventory, 4, 9, container)
}
@JvmStatic
@JvmOverloads
fun c9x5(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(9 * 5)): MatteryChestMenu {
return MatteryChestMenu(GENERIC_9x5, containerId, inventory, 5, 9, container)
return MatteryChestMenu(VanillaMenuTypes.GENERIC_9x5, containerId, inventory, 5, 9, container)
}
@JvmStatic
@JvmOverloads
fun c9x6(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(9 * 6)): MatteryChestMenu {
return MatteryChestMenu(GENERIC_9x6, containerId, inventory, 6, 9, container)
return MatteryChestMenu(VanillaMenuTypes.GENERIC_9x6, containerId, inventory, 6, 9, container)
}
@JvmStatic
@JvmOverloads
fun c3x3(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(3 * 3)): MatteryChestMenu {
return MatteryChestMenu(GENERIC_3x3, containerId, inventory, 3, 3, container)
return MatteryChestMenu(VanillaMenuTypes.GENERIC_3x3, containerId, inventory, 3, 3, container)
}
@JvmStatic
@JvmOverloads
fun hopper(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(5)): MatteryChestMenu {
return MatteryChestMenu(HOPPER, containerId, inventory, 1, 5, container)
}
internal fun register(bus: IEventBus) {
registrar.register(bus)
bus.addListener(this::registerScreens)
}
private fun registerScreens(event: RegisterMenuScreensEvent) {
event.register(GENERIC_9x1, ::MatteryChestScreen)
event.register(GENERIC_9x2, ::MatteryChestScreen)
event.register(GENERIC_9x3, ::MatteryChestScreen)
event.register(GENERIC_9x4, ::MatteryChestScreen)
event.register(GENERIC_9x5, ::MatteryChestScreen)
event.register(GENERIC_9x6, ::MatteryChestScreen)
event.register(GENERIC_3x3, ::MatteryChestScreen)
event.register(HOPPER, ::MatteryChestScreen)
return MatteryChestMenu(VanillaMenuTypes.HOPPER, containerId, inventory, 1, 5, container)
}
}
}

View File

@ -0,0 +1,36 @@
package ru.dbotthepony.mc.otm.compat.vanilla
import net.minecraft.world.Container
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.item.ItemStack
import ru.dbotthepony.mc.otm.container.EnhancedContainer
import ru.dbotthepony.mc.otm.menu.MatteryMenuSlot
import ru.dbotthepony.mc.otm.menu.QuickMoveInput
import ru.dbotthepony.mc.otm.menu.makeSlots
class MatteryShulkerBoxMenu(
containerId: Int,
inventory: Inventory,
container: Container = EnhancedContainer.Simple(27)
) : AbstractVanillaChestMenu(VanillaMenuTypes.SHULKER_BOX, containerId, inventory, container) {
override val containerSlots = makeSlots(container, ::Slot)
override val rows: Int
get() = 3
override val columns: Int
get() = 9
init {
container.startOpen(player)
addStorageSlot(containerSlots)
addInventorySlots()
}
override val quickMoveToStorage = QuickMoveInput.create(this, playerCombinedInventorySlots, containerSlots)
override val quickMoveFromStorage = QuickMoveInput.create(this, containerSlots, playerInventorySlots, false)
class Slot(container: Container, slot: Int) : MatteryMenuSlot(container, slot) {
override fun mayPlace(stack: ItemStack): Boolean {
return super.mayPlace(stack) && stack.item.canFitInsideContainerItems()
}
}
}

View File

@ -2,6 +2,7 @@ package ru.dbotthepony.mc.otm.compat.vanilla
import net.minecraft.network.chat.Component
import net.minecraft.world.entity.player.Inventory
import ru.dbotthepony.kommons.util.Either
import ru.dbotthepony.mc.otm.client.render.RenderGravity
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
import ru.dbotthepony.mc.otm.client.screen.panels.Dock
@ -10,8 +11,9 @@ import ru.dbotthepony.mc.otm.client.screen.panels.button.DeviceControls
import ru.dbotthepony.mc.otm.client.screen.panels.slot.AbstractSlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.util.GridPanel
import ru.dbotthepony.mc.otm.menu.QuickMoveInput
class MatteryChestScreen(menu: MatteryChestMenu, inventory: Inventory, title: Component) : MatteryScreen<MatteryChestMenu>(menu, inventory, title) {
class VanillaChestScreen(menu: AbstractVanillaChestMenu, inventory: Inventory, title: Component) : MatteryScreen<AbstractVanillaChestMenu>(menu, inventory, title) {
override fun makeMainFrame(): FramePanel<MatteryScreen<*>> {
val frame = FramePanel.padded(this, AbstractSlotPanel.SIZE * menu.columns.coerceAtLeast(9), AbstractSlotPanel.SIZE * menu.rows + 4f, title)
@ -22,12 +24,20 @@ class MatteryChestScreen(menu: MatteryChestMenu, inventory: Inventory, title: Co
grid.dock = Dock.FILL
grid.gravity = RenderGravity.BOTTOM_CENTER
for (slot in menu.chestSlots)
for (slot in menu.containerSlots)
SlotPanel(this, grid, slot)
val controls = DeviceControls(this, frame)
controls.sortingButtons(menu.sort)
val leftControls = DeviceControls(this, frame)
leftControls.dockOnLeft = true
leftControls.quickMoveButtons(menu.quickMoveFromStorage)
val leftInventoryControls = DeviceControls(this, inventoryFrame!!)
leftInventoryControls.dockOnLeft = true
leftInventoryControls.quickMoveButtons(menu.quickMoveToStorage, menu.quickMoveFromStorage)
return frame
}
}

View File

@ -0,0 +1,71 @@
package ru.dbotthepony.mc.otm.compat.vanilla
import net.minecraft.core.BlockPos
import net.minecraft.core.registries.Registries
import net.minecraft.world.Container
import net.minecraft.world.flag.FeatureFlags
import net.minecraft.world.inventory.MenuType
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.Blocks
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.entity.ChestBlockEntity
import net.minecraft.world.level.block.state.BlockState
import net.neoforged.bus.api.IEventBus
import net.neoforged.neoforge.capabilities.IBlockCapabilityProvider
import net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent
import net.neoforged.neoforge.client.event.RegisterMenuScreensEvent
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.capability.IQuickStackContainer
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.menu.MatteryMenuSlot
import ru.dbotthepony.mc.otm.menu.makeSlots
import ru.dbotthepony.mc.otm.registry.MDeferredRegister
object VanillaMenuTypes {
private val registrar = MDeferredRegister(Registries.MENU, OverdriveThatMatters.MOD_ID)
val GENERIC_9x1 by registrar.register("generic_9x1") { MenuType(MatteryChestMenu::c9x1, FeatureFlags.VANILLA_SET) }
val GENERIC_9x2 by registrar.register("generic_9x2") { MenuType(MatteryChestMenu::c9x2, FeatureFlags.VANILLA_SET) }
val GENERIC_9x3 by registrar.register("generic_9x3") { MenuType(MatteryChestMenu::c9x3, FeatureFlags.VANILLA_SET) }
val GENERIC_9x4 by registrar.register("generic_9x4") { MenuType(MatteryChestMenu::c9x4, FeatureFlags.VANILLA_SET) }
val GENERIC_9x5 by registrar.register("generic_9x5") { MenuType(MatteryChestMenu::c9x5, FeatureFlags.VANILLA_SET) }
val GENERIC_9x6 by registrar.register("generic_9x6") { MenuType(MatteryChestMenu::c9x6, FeatureFlags.VANILLA_SET) }
val GENERIC_3x3 by registrar.register("generic_3x3") { MenuType(MatteryChestMenu::c3x3, FeatureFlags.VANILLA_SET) }
val HOPPER by registrar.register("hopper") { MenuType(MatteryChestMenu::hopper, FeatureFlags.VANILLA_SET) }
val SHULKER_BOX by registrar.register("shulker_box") { MenuType(::MatteryShulkerBoxMenu, FeatureFlags.VANILLA_SET) }
private fun provider(level: Level, pos: BlockPos, state: BlockState, blockEntity: BlockEntity?, context: Void?): IQuickStackContainer? {
val container = blockEntity as? Container ?: return null
return IQuickStackContainer.Simple(makeSlots(container, ::MatteryMenuSlot))
}
fun registerCapabilities(event: RegisterCapabilitiesEvent) {
event.registerBlock(
MatteryCapability.QUICK_STACK_CONTAINER,
::provider,
Blocks.CHEST,
Blocks.TRAPPED_CHEST,
Blocks.SHULKER_BOX,
Blocks.BARREL,
)
}
internal fun register(bus: IEventBus) {
registrar.register(bus)
bus.addListener(this::registerScreens)
bus.addListener(this::registerCapabilities)
}
private fun registerScreens(event: RegisterMenuScreensEvent) {
event.register(GENERIC_9x1, ::VanillaChestScreen)
event.register(GENERIC_9x2, ::VanillaChestScreen)
event.register(GENERIC_9x3, ::VanillaChestScreen)
event.register(GENERIC_9x4, ::VanillaChestScreen)
event.register(GENERIC_9x5, ::VanillaChestScreen)
event.register(GENERIC_9x6, ::VanillaChestScreen)
event.register(GENERIC_3x3, ::VanillaChestScreen)
event.register(HOPPER, ::VanillaChestScreen)
event.register(SHULKER_BOX, ::VanillaChestScreen)
}
}

View File

@ -7,7 +7,6 @@ import ru.dbotthepony.kommons.util.Delegate
import ru.dbotthepony.kommons.util.getValue
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.defineDecimal
import ru.dbotthepony.mc.otm.core.util.WriteOnce
abstract class AbstractConfig(private val configName: String, private val type: ModConfig.Type = ModConfig.Type.SERVER) {

View File

@ -3,7 +3,6 @@ package ru.dbotthepony.mc.otm.config
import ru.dbotthepony.kommons.util.getValue
import ru.dbotthepony.kommons.util.setValue
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.defineDecimal
object CablesConfig : AbstractConfig("cables") {
enum class E(throughput: Decimal) {

View File

@ -0,0 +1,58 @@
package ru.dbotthepony.mc.otm.config
import net.neoforged.neoforge.common.ModConfigSpec
import ru.dbotthepony.mc.otm.core.math.Decimal
class DecimalConfigValue(
parent: ModConfigSpec.ConfigValue<String>,
val minimum: Decimal? = null,
val maximum: Decimal? = null,
) : ObservedConfigValue<Decimal>(parent) {
override fun fromString(value: String): Decimal? {
try {
val parsed = Decimal(value)
if (minimum != null && minimum > parsed) {
return minimum
} else if (maximum != null && maximum < parsed) {
return maximum
}
return parsed
} catch (err: java.lang.NumberFormatException) {
return null
}
}
override fun toString(value: Decimal): Pair<String, Decimal> {
if (minimum != null && minimum > value) {
return minimum.toString() to minimum
} else if (maximum != null && maximum < value) {
return maximum.toString() to maximum
}
return value.toString() to value
}
}
private fun ModConfigSpec.Builder.commentRange(minimum: Decimal?, maximum: Decimal?) {
if (minimum != null && maximum != null) {
comment("Range: $minimum ~ $maximum")
} else if (minimum != null) {
comment("Range: >= $minimum")
} else if (maximum != null) {
comment("Range: <= $maximum")
}
}
fun ModConfigSpec.Builder.defineDecimal(path: String, defaultValue: Decimal, minimum: Decimal? = null, maximum: Decimal? = null): DecimalConfigValue {
commentRange(minimum, maximum)
comment("Default: $defaultValue")
return DecimalConfigValue(define(path, defaultValue.toString()), minimum, maximum)
}
fun ModConfigSpec.Builder.defineDecimal(path: List<String>, defaultValue: Decimal, minimum: Decimal? = null, maximum: Decimal? = null): DecimalConfigValue {
commentRange(minimum, maximum)
comment("Default: $defaultValue")
return DecimalConfigValue(define(path, defaultValue.toString()), minimum, maximum)
}

View File

@ -2,7 +2,6 @@ package ru.dbotthepony.mc.otm.config
import ru.dbotthepony.kommons.util.getValue
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.defineDecimal
object ExopackConfig : AbstractConfig("exopack") {
val ENERGY_CAPACITY by builder

View File

@ -2,7 +2,6 @@ package ru.dbotthepony.mc.otm.config
import ru.dbotthepony.kommons.util.getValue
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.defineDecimal
import ru.dbotthepony.mc.otm.registry.MNames
object ItemsConfig : AbstractConfig("items") {

View File

@ -2,7 +2,6 @@ package ru.dbotthepony.mc.otm.config
import ru.dbotthepony.kommons.util.getValue
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.defineDecimal
import ru.dbotthepony.mc.otm.registry.MNames
object MachinesConfig : AbstractConfig("machines") {
@ -60,7 +59,7 @@ object MachinesConfig : AbstractConfig("machines") {
private val MATTER_BOTTLER = workerValues(
MNames.MATTER_BOTTLER,
energyStorage = Decimal(40_000),
energyConsumption = Decimal(10),
energyConsumption = Decimal(40),
energyThroughput = Decimal(200),
matterCapacity = Decimal(400),
workTimeMultiplier = null
@ -85,7 +84,7 @@ object MachinesConfig : AbstractConfig("machines") {
object MatterBottler {
val VALUES by ::MATTER_BOTTLER
val RATE by builder.comment("Matter transferred per tick").defineDecimal("RATE", Decimal("2.0"), Decimal.ONE_TENTH)
val RATE by builder.comment("Matter transferred per tick").defineDecimal("RATE", Decimal("10.0"), Decimal.ONE_TENTH)
}
object MatterRecycler {
@ -111,8 +110,8 @@ object MachinesConfig : AbstractConfig("machines") {
private val MATTER_RECONSTRUCTOR = workerValues(
MNames.MATTER_RECONSTRUCTOR,
energyStorage = Decimal(100_000),
energyThroughput = Decimal(1000),
energyStorage = Decimal(200_000),
energyThroughput = Decimal(4_000),
energyConsumption = Decimal(400),
matterCapacity = Decimal(200),
workTimeMultiplier = null,

View File

@ -2,7 +2,6 @@ package ru.dbotthepony.mc.otm.config
import ru.dbotthepony.kommons.util.getValue
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.defineDecimal
object PlayerConfig : AbstractConfig("player") {
init {

View File

@ -2,7 +2,6 @@ package ru.dbotthepony.mc.otm.config
import ru.dbotthepony.kommons.util.getValue
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.defineDecimal
object ServerConfig : AbstractConfig("misc") {
val LABORATORY_LAMP_LIGHT_LENGTH: Int by builder.comment("In blocks").defineInRange("LABORATORY_LAMP_LIGHT_LENGTH", 6, 1, 128)

View File

@ -0,0 +1,61 @@
package ru.dbotthepony.mc.otm.container
import it.unimi.dsi.fastutil.ints.IntCollection
import net.minecraft.world.item.ItemStack
import ru.dbotthepony.kommons.collect.iterateClearBits
import ru.dbotthepony.kommons.collect.iterateSetBits
import ru.dbotthepony.mc.otm.core.collect.IntRange2Set
import ru.dbotthepony.mc.otm.core.collect.map
import java.util.*
abstract class BitmapTrackingContainer<out S : IContainerSlot> : IEnhancedContainer<S> {
protected val bitmap = BitSet()
final override fun isEmpty(): Boolean {
return bitmap.isEmpty
}
final override fun nextEmptySlot(startIndex: Int): Int {
if (startIndex >= containerSize)
return -1
else if (startIndex < 0)
return bitmap.nextClearBit(0)
else
return bitmap.nextClearBit(startIndex)
}
final override fun nextNonEmptySlot(startIndex: Int): Int {
if (startIndex >= containerSize)
return -1
else if (startIndex < 0)
return bitmap.nextSetBit(0)
else
return bitmap.nextSetBit(startIndex)
}
final override fun iterator(): Iterator<ItemStack> {
return bitmap.iterateSetBits(containerSize).map { this[it] }
}
final override fun nonEmptySlotIndexIterator(): IntIterator {
return bitmap.iterateSetBits(containerSize)
}
final override fun emptySlotIndexIterator(): IntIterator {
return bitmap.iterateClearBits(containerSize)
}
final override fun emptySlotIndexIterator(allowedSlots: IntCollection): IntIterator {
if (allowedSlots is IntRange2Set && allowedSlots.isNotEmpty())
return bitmap.iterateClearBits(allowedSlots.firstInt(), allowedSlots.lastInt() + 1)
return super.emptySlotIndexIterator(allowedSlots)
}
final override fun nonEmptySlotIndexIterator(allowedSlots: IntCollection): IntIterator {
if (allowedSlots is IntRange2Set && allowedSlots.isNotEmpty())
return bitmap.iterateSetBits(allowedSlots.firstInt(), allowedSlots.lastInt() + 1)
return super.nonEmptySlotIndexIterator(allowedSlots)
}
}

View File

@ -5,6 +5,7 @@ import com.google.common.collect.ImmutableMap
import com.google.common.collect.ImmutableSet
import it.unimi.dsi.fastutil.ints.IntAVLTreeSet
import it.unimi.dsi.fastutil.ints.IntArrayList
import it.unimi.dsi.fastutil.ints.IntOpenHashSet
import it.unimi.dsi.fastutil.ints.IntSet
import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap
@ -22,39 +23,36 @@ import ru.dbotthepony.mc.otm.core.isNotEmpty
import ru.dbotthepony.mc.otm.core.stream
import java.util.stream.Stream
class CombinedContainer(containers: Stream<Pair<Container, Iterable<Int>>>) : IMatteryContainer {
constructor(vararg containers: Container) : this(containers.stream().map { it to (0 until it.containerSize) })
constructor(containers: Collection<Container>) : this(containers.stream().map { it to (0 until it.containerSize) })
class CombinedContainer<S : IContainerSlot>(containers: Stream<Pair<IEnhancedContainer<S>, Iterable<Int>>>) : ISlottedContainer<S> {
constructor(vararg containers: IEnhancedContainer<S>) : this(containers.stream().map { it to (0 until it.containerSize) })
constructor(containers: Collection<IEnhancedContainer<S>>) : this(containers.stream().map { it to (0 until it.containerSize) })
private inner class Slot(override val slot: Int, val outer: IContainerSlot) : IContainerSlot by outer {
override val container: Container
get() = this@CombinedContainer
}
private val slots: ImmutableList<Slot>
private val slotsMap: ImmutableMap<Container, List<IContainerSlot>>
private val slots: ImmutableList<S>
private val slotsMap: ImmutableMap<Container, List<S>>
private val containers: ImmutableSet<Container>
private val fullCoverage: ImmutableList<Container>
private val notFullCoverage: ImmutableMap<Container, List<IContainerSlot>>
private val notFullCoverage: ImmutableMap<Container, List<S>>
init {
val list = ImmutableList.Builder<Slot>()
var i = 0
val list = ImmutableList.Builder<S>()
val validationMap = Reference2ObjectOpenHashMap<Container, IntSet>()
val slotsMap = Reference2ObjectOpenHashMap<Container, ArrayList<IContainerSlot>>()
val slotsMap = Reference2ObjectOpenHashMap<Container, ArrayList<S>>()
var i = 0
for ((container, slots) in containers) {
val validator = validationMap.computeIfAbsent(container, Object2ObjectFunction { IntAVLTreeSet() })
val validator = validationMap.computeIfAbsent(container, Object2ObjectFunction { IntOpenHashSet() })
val slotList = slotsMap.computeIfAbsent(container, Object2ObjectFunction { ArrayList() })
for (slot in slots) {
if (validator.add(slot)) {
val slotObj = container.containerSlot(slot)
list.add(Slot(i++, slotObj))
list.add(slotObj)
slotList.add(slotObj)
} else {
throw IllegalArgumentException("Duplicate mapping for $container at $i for slot $slot")
}
i++
}
}
@ -78,6 +76,8 @@ class CombinedContainer(containers: Stream<Pair<Container, Iterable<Int>>>) : IM
.collect(ImmutableMap.toImmutableMap({ it.key }, { it.value }))
}
override val hasFilterableSlots: Boolean = super.hasFilterableSlots
override fun clearContent() {
for (container in fullCoverage) {
container.clearContent()
@ -85,20 +85,7 @@ class CombinedContainer(containers: Stream<Pair<Container, Iterable<Int>>>) : IM
for (slots in notFullCoverage.values) {
for (slot in slots) {
slot.item = ItemStack.EMPTY
}
}
}
override fun clearSlotFilters() {
for (container in fullCoverage) {
if (container is IMatteryContainer)
container.clearSlotFilters()
}
for (slots in notFullCoverage.values) {
for (slot in slots) {
slot.setFilter()
slot.remove()
}
}
}
@ -108,37 +95,7 @@ class CombinedContainer(containers: Stream<Pair<Container, Iterable<Int>>>) : IM
}
override fun isEmpty(): Boolean {
if (fullCoverage.isNotEmpty())
for (container in fullCoverage)
if (!container.isEmpty)
return false
if (notFullCoverage.isNotEmpty())
for (slots in notFullCoverage.values)
for (slot in slots)
if (!slot.isEmpty)
return false
return true
}
override fun getItem(slot: Int): ItemStack {
// do not violate contract of getItem not throwing exceptions when index is invalid
return slots.getOrNull(slot)?.item ?: ItemStack.EMPTY
}
override fun removeItem(slot: Int, amount: Int): ItemStack {
val data = slots.getOrNull(slot) ?: return ItemStack.EMPTY
return data.outer.container.removeItem(data.outer.slot, amount)
}
override fun removeItemNoUpdate(slot: Int): ItemStack {
val data = slots.getOrNull(slot) ?: return ItemStack.EMPTY
return data.outer.container.removeItemNoUpdate(data.outer.slot)
}
override fun setItem(slot: Int, itemStack: ItemStack) {
slots.getOrNull(slot)?.item = itemStack
return fullCoverage.all { it.isEmpty } && notFullCoverage.values.all { it.all { it.isEmpty } }
}
override fun setChanged() {
@ -148,82 +105,87 @@ class CombinedContainer(containers: Stream<Pair<Container, Iterable<Int>>>) : IM
}
override fun stillValid(player: Player): Boolean {
for (container in containers)
if (!container.stillValid(player))
return false
return true
return containers.all { it.stillValid(player) }
}
override fun iterator(nonEmpty: Boolean): Iterator<ItemStack> {
override fun iterator(): Iterator<ItemStack> {
if (notFullCoverage.isEmpty())
return fullCoverage.iterator().flatMap { it.iterator(nonEmpty) }
return fullCoverage.iterator().flatMap { it.iterator() }
return concatIterators(
fullCoverage.iterator().flatMap { it.iterator(nonEmpty) },
notFullCoverage.values.iterator().flatMap { it.iterator() }.map { it.item }.let { if (nonEmpty) it.filter { it.isNotEmpty } else it }
fullCoverage.iterator().flatMap { it.iterator() },
notFullCoverage.values.iterator().flatMap { it.iterator() }.map { it.item }.filter { it.isNotEmpty }
)
}
override fun slotIterator(nonEmpty: Boolean): Iterator<IContainerSlot> {
if (nonEmpty)
return slots.iterator().filter { it.isNotEmpty }
override fun slotIterator(): Iterator<S> {
return slots.iterator()
}
override fun containerSlot(slot: Int): IContainerSlot {
override fun containerSlot(slot: Int): S {
return slots[slot]
}
override fun getSlotFilter(slot: Int): Item? {
return slots[slot].getFilter()
}
override fun setChanged(slot: Int) {
slots[slot].setChanged()
}
override fun setSlotFilter(slot: Int, filter: Item?): Boolean {
return slots[slot].setFilter(filter)
}
class Builder<S : IContainerSlot> {
private val values = ArrayList<Pair<IEnhancedContainer<S>, Iterable<Int>>>()
class Builder {
private val values = ArrayList<Pair<Container, Iterable<Int>>>()
fun add(container: Container): Builder<S> {
return add(IEnhancedContainer.wrap(container))
}
fun add(container: Container): Builder {
fun add(container: Container, slots: Iterator<Int>): Builder<S> {
return add(IEnhancedContainer.wrap(container), slots)
}
fun add(container: Container, slot: Int): Builder<S> {
return add(IEnhancedContainer.wrap(container), slot)
}
fun add(container: Container, from: Int, to: Int): Builder<S> {
return add(IEnhancedContainer.wrap(container), from, to)
}
fun add(container: Container, slots: Iterable<Int>): Builder<S> {
return add(IEnhancedContainer.wrap(container), slots)
}
fun add(container: IEnhancedContainer<S>): Builder<S> {
values.add(container to container.slotRange)
return this
}
fun add(container: Container, slots: Iterator<Int>): Builder {
fun add(container: IEnhancedContainer<S>, slots: Iterator<Int>): Builder<S> {
values.add(container to IntArrayList(slots))
return this
}
fun add(container: Container, slot: Int): Builder {
fun add(container: IEnhancedContainer<S>, slot: Int): Builder<S> {
values.add(container to intArrayOf(slot).asIterable())
return this
}
fun add(container: Container, from: Int, to: Int): Builder {
fun add(container: IEnhancedContainer<S>, from: Int, to: Int): Builder<S> {
values.add(container to (from .. to))
return this
}
fun add(container: Container, slots: Iterable<Int>): Builder {
fun add(container: IEnhancedContainer<S>, slots: Iterable<Int>): Builder<S> {
values.add(container to slots)
return this
}
fun build(): CombinedContainer {
fun build(): CombinedContainer<S> {
return CombinedContainer(values.stream())
}
}
companion object {
fun fromMenuSlots(slots: Iterator<net.minecraft.world.inventory.Slot>): CombinedContainer {
val builder = Builder()
fun fromMenuSlots(slots: Iterator<net.minecraft.world.inventory.Slot>): CombinedContainer<IContainerSlot> {
val builder = Builder<IContainerSlot>()
for (slot in slots) {
builder.add(slot.container, slot.slotIndex)

View File

@ -1,89 +0,0 @@
package ru.dbotthepony.mc.otm.container
import net.minecraft.world.item.ItemStack
import net.neoforged.neoforge.items.IItemHandler
class ContainerHandler(
private val container: IMatteryContainer,
private val filter: HandlerFilter = HandlerFilter.Both,
) : IItemHandler {
override fun getSlots() = container.containerSize
override fun getStackInSlot(slot: Int) = container[slot]
override fun insertItem(slot: Int, stack: ItemStack, simulate: Boolean): ItemStack {
if (!container.testSlotFilter(slot, stack) || !filter.canInsert(slot, stack))
return stack
filter.preInsert(slot, stack, simulate)
val localStack = container[slot]
var amount = filter.modifyInsertCount(slot, stack, localStack, simulate)
if (amount <= 0)
return stack
if (localStack.isEmpty) {
amount = stack.count.coerceAtMost(container.getMaxStackSize(slot, stack)).coerceAtMost(amount)
if (!simulate) {
container.setItem(slot, stack.copyWithCount(amount))
}
if (stack.count <= amount) {
return ItemStack.EMPTY
} else {
return stack.copyWithCount(stack.count - amount)
}
} else if (localStack.isStackable && container.getMaxStackSize(slot, localStack) > localStack.count && ItemStack.isSameItemSameComponents(localStack, stack)) {
val newCount = container.getMaxStackSize(slot, localStack).coerceAtMost(localStack.count + stack.count.coerceAtMost(amount))
val diff = newCount - localStack.count
if (diff != 0) {
if (!simulate) {
localStack.grow(diff)
container.setChanged(slot)
}
val copy = stack.copy()
copy.shrink(diff)
return copy
}
}
return stack
}
override fun extractItem(slot: Int, amount: Int, simulate: Boolean): ItemStack {
if (amount <= 0 || container.isSlotForbiddenForAutomation(slot))
return ItemStack.EMPTY
val localStack = container.getItem(slot)
if (localStack.isEmpty) return ItemStack.EMPTY
@Suppress("name_shadowing")
val amount = filter.modifyExtractCount(slot, amount, simulate)
if (amount <= 0) return ItemStack.EMPTY
if (!filter.canExtract(slot, amount, localStack)) return ItemStack.EMPTY
filter.preExtract(slot, amount, simulate)
val minimal = amount.coerceAtMost(localStack.count)
val copy = localStack.copy()
copy.count = minimal
if (!simulate) {
localStack.shrink(minimal)
container.setChanged(slot)
}
return copy
}
override fun getSlotLimit(slot: Int): Int {
return container.maxStackSize
}
override fun isItemValid(slot: Int, stack: ItemStack): Boolean {
return container.testSlotFilter(slot, stack) && filter.canInsert(slot, stack)
}
}

View File

@ -9,6 +9,7 @@ import it.unimi.dsi.fastutil.ints.IntIterator
import it.unimi.dsi.fastutil.ints.IntList
import it.unimi.dsi.fastutil.ints.IntOpenHashSet
import it.unimi.dsi.fastutil.ints.IntSet
import it.unimi.dsi.fastutil.ints.IntSortedSet
import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap
import it.unimi.dsi.fastutil.objects.ObjectArrayList
@ -22,6 +23,7 @@ import ru.dbotthepony.mc.otm.container.util.ItemStackHashStrategy
import ru.dbotthepony.mc.otm.container.util.containerSlot
import ru.dbotthepony.mc.otm.container.util.slotIterator
import ru.dbotthepony.mc.otm.core.addAll
import ru.dbotthepony.mc.otm.core.collect.IntRange2Set
import ru.dbotthepony.mc.otm.core.collect.filter
import ru.dbotthepony.mc.otm.core.collect.toList
import ru.dbotthepony.mc.otm.core.isNotEmpty
@ -38,89 +40,12 @@ inline operator fun Container.get(index: Int): ItemStack = getItem(index)
@Suppress("nothing_to_inline")
inline operator fun IFluidHandler.get(index: Int) = getFluidInTank(index)
val Container.slotRange: IntIterable get() {
return IntIterable {
val i = (0 until containerSize).iterator()
object : IntIterator {
override fun hasNext(): Boolean {
return i.hasNext()
}
override fun remove() {
throw UnsupportedOperationException()
}
override fun nextInt(): Int {
return i.nextInt()
}
}
}
val Container.slotRange: IntRange2Set get() {
return IntRange2Set.openEnded(0, containerSize)
}
fun Container.addItem(stack: ItemStack, simulate: Boolean, slots: IntIterable = slotRange): ItemStack {
if (this is IMatteryContainer) {
return this.addItem(stack, simulate, slots)
}
if (stack.isEmpty)
return stack
val copy = stack.copy()
// двигаем в одинаковые слоты
var i = slots.intIterator()
while (i.hasNext()) {
val slot = i.nextInt()
if (ItemStack.isSameItemSameComponents(this[slot], copy)) {
val slotStack = this[slot]
val slotLimit = maxStackSize.coerceAtMost(slotStack.maxStackSize)
if (slotStack.count < slotLimit) {
val newCount = (slotStack.count + copy.count).coerceAtMost(slotLimit)
val diff = newCount - slotStack.count
if (!simulate) {
slotStack.count = newCount
setChanged()
}
copy.shrink(diff)
if (copy.isEmpty) {
return copy
}
}
}
}
// двигаем в пустые слоты
i = slots.intIterator()
while (i.hasNext()) {
val slot = i.nextInt()
if (this[slot].isEmpty) {
val diff = copy.count.coerceAtMost(maxStackSize.coerceAtMost(copy.maxStackSize))
if (!simulate) {
val copyToPut = copy.copy()
copyToPut.count = diff
this[slot] = copyToPut
setChanged()
}
copy.shrink(diff)
if (copy.isEmpty) {
return copy
}
}
}
return copy
fun Container.addItem(stack: ItemStack, simulate: Boolean, slots: IntSet = slotRange): ItemStack {
return IEnhancedContainer.wrap(this).addItem(stack, simulate, slots)
}
fun Container.vanishCursedItems() {
@ -131,7 +56,7 @@ fun Container.vanishCursedItems() {
}
}
fun Container.balance(slots: IntSet, checkForEmpty: Boolean = true) {
fun Container.balance(slots: IntCollection, checkForEmpty: Boolean = true) {
if (slots.isEmpty() || checkForEmpty && !slots.any { getItem(it).isNotEmpty }) return
val empty = IntArrayList()
@ -312,12 +237,18 @@ fun Container.sortWithIndices(sortedSlots: IntCollection) {
if (value in 0 until containerSize && seen.add(value)) {
val slot = containerSlot(value)
if (
slot.isNotEmpty &&
!slot.isForbiddenForAutomation &&
slot.item.count <= slot.getMaxStackSize() &&
(!slot.hasFilter || slot.getFilter() != slot.item.item || slot.getMaxStackSize() > 1)
) {
val condition: Boolean
if (slot is IFilteredContainerSlot) {
condition = slot.isNotEmpty &&
!slot.filter.denyAll &&
slot.item.count <= slot.maxStackSize(slot.item) &&
(slot.filter.allowAll || !slot.filter.test(slot.item) || slot.maxStackSize(slot.item) > 1)
} else {
condition = slot.isNotEmpty && slot.item.count <= slot.maxStackSize(slot.item)
}
if (condition) {
valid.add(slot)
}
}
@ -335,21 +266,24 @@ fun Container.computeSortedIndices(comparator: Comparator<ItemStack> = ItemStack
if (isEmpty)
return IntList.of()
val slots = slotIterator().filter { !it.isForbiddenForAutomation && it.getMaxStackSize() >= it.item.count }.toList()
val slots = slotIterator()
.withIndex()
.filter { (_, it) -> (it !is IFilteredContainerSlot || !it.filter.denyAll) && it.maxStackSize(it.item) >= it.item.count }
.toList()
if (slots.isEmpty())
return IntList.of()
val items = Object2ObjectOpenCustomHashMap<ItemStack, Pair<ItemStack, IntList>>(ItemStackHashStrategy)
slots.forEach {
slots.forEach { (index, it) ->
val get = items[it.item]
if (get == null) {
items[it.item] = it.item.copy() to IntArrayList().also { s -> s.add(it.slot) }
items[it.item] = it.item.copy() to IntArrayList().also { s -> s.add(index) }
} else {
get.first.count += it.item.count
get.second.add(it.slot)
get.second.add(index)
}
}

View File

@ -1,81 +0,0 @@
package ru.dbotthepony.mc.otm.container
import net.minecraft.world.Container
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import java.util.function.Predicate
import java.util.function.Supplier
/**
* because mods tend to do crazy shit
*/
class DynamicallyProxiedContainer(private val toProxy: Supplier<Container>) : IContainer {
override fun clearContent() {
return toProxy.get().clearContent()
}
override fun getContainerSize(): Int {
return toProxy.get().containerSize
}
override fun isEmpty(): Boolean {
return toProxy.get().isEmpty
}
override fun getItem(slot: Int): ItemStack {
return toProxy.get().getItem(slot)
}
override fun removeItem(slot: Int, amount: Int): ItemStack {
return toProxy.get().removeItem(slot, amount)
}
override fun removeItemNoUpdate(slot: Int): ItemStack {
return toProxy.get().removeItemNoUpdate(slot)
}
override fun setItem(slot: Int, itemStack: ItemStack) {
return toProxy.get().setItem(slot, itemStack)
}
override fun setChanged() {
return toProxy.get().setChanged()
}
override fun stillValid(player: Player): Boolean {
return toProxy.get().stillValid(player)
}
override fun getMaxStackSize(): Int {
return toProxy.get().getMaxStackSize()
}
override fun startOpen(player: Player) {
toProxy.get().startOpen(player)
}
override fun stopOpen(player: Player) {
toProxy.get().stopOpen(player)
}
override fun canPlaceItem(slot: Int, itemStack: ItemStack): Boolean {
return toProxy.get().canPlaceItem(slot, itemStack)
}
override fun canTakeItem(container: Container, slot: Int, itemStack: ItemStack): Boolean {
return toProxy.get().canTakeItem(container, slot, itemStack)
}
override fun countItem(item: Item): Int {
return toProxy.get().countItem(item)
}
override fun hasAnyOf(items: Set<Item>): Boolean {
return toProxy.get().hasAnyOf(items)
}
override fun hasAnyMatching(predicate: Predicate<ItemStack>): Boolean {
return toProxy.get().hasAnyMatching(predicate)
}
}

View File

@ -0,0 +1,212 @@
package ru.dbotthepony.mc.otm.container
import it.unimi.dsi.fastutil.ints.IntCollection
import it.unimi.dsi.fastutil.ints.IntOpenHashSet
import net.minecraft.core.HolderLookup.Provider
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.ListTag
import net.minecraft.nbt.NbtOps
import net.minecraft.nbt.Tag
import net.minecraft.resources.RegistryOps
import net.minecraft.world.SimpleContainer
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack
import net.neoforged.neoforge.common.util.INBTSerializable
import org.apache.logging.log4j.LogManager
import ru.dbotthepony.kommons.collect.iterateClearBits
import ru.dbotthepony.kommons.collect.iterateSetBits
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.collect.IntRange2Set
import ru.dbotthepony.mc.otm.core.collect.map
import ru.dbotthepony.mc.otm.core.isNotEmpty
import ru.dbotthepony.mc.otm.core.nbt.set
import java.util.BitSet
/**
* Flexible base implementation of [IEnhancedContainer], designed to be inherited, or used as-is
* if no specific logic is required (this implementation is more efficient than one provided by [SlottedContainer.simple]).
*
* This is supposed to be counterpart to [SimpleContainer] of Minecraft itself, with more features
* and improved performance (inside [IEnhancedContainer] defined methods).
*/
abstract class EnhancedContainer<out S : IContainerSlot>(private val size: Int) : BitmapTrackingContainer<S>(), INBTSerializable<CompoundTag> {
private val items = Array(size) { ItemStack.EMPTY }
private val observedItems = Array(size) { ItemStack.EMPTY }
protected open fun notifySlotChanged(slot: Int, old: ItemStack) {}
private fun observeSlotChanges(slot: Int) {
if (items[slot].count != observedItems[slot].count || !ItemStack.isSameItemSameComponents(items[slot], observedItems[slot])) {
notifySlotChanged(slot, observedItems[slot])
if (items[slot].isEmpty) {
items[slot] = ItemStack.EMPTY
observedItems[slot] = ItemStack.EMPTY
bitmap[slot] = false
} else {
notifySlotChanged(slot, observedItems[slot])
observedItems[slot] = items[slot].copy()
bitmap[slot] = true
}
}
}
override fun setChanged(slot: Int) {
observeSlotChanges(slot)
}
override fun clearContent() {
items.fill(ItemStack.EMPTY)
observedItems.fill(ItemStack.EMPTY)
bitmap.clear()
}
override fun setChanged() {
for (slot in 0 until size)
observeSlotChanges(slot)
}
final override fun getContainerSize(): Int {
return size
}
final override fun getItem(slot: Int): ItemStack {
return items[slot]
}
final override fun removeItem(slot: Int, amount: Int): ItemStack {
val item = items[slot]
if (item.isEmpty)
return ItemStack.EMPTY
else if (item.count >= amount)
return removeItemNoUpdate(slot)
else {
val split = item.split(amount)
notifySlotChanged(slot, observedItems[slot])
observedItems[slot] = item.copy()
return split
}
}
final override fun removeItemNoUpdate(slot: Int): ItemStack {
val item = items[slot]
if (item.isEmpty)
return ItemStack.EMPTY
items[slot] = ItemStack.EMPTY
notifySlotChanged(slot, observedItems[slot])
observedItems[slot] = ItemStack.EMPTY
bitmap[slot] = false
return item
}
final override fun setItem(slot: Int, itemStack: ItemStack) {
if (itemStack.count != items[slot].count || !ItemStack.isSameItemSameComponents(itemStack, items[slot])) {
items[slot] = itemStack
notifySlotChanged(slot, observedItems[slot])
observedItems[slot] = itemStack.copy()
bitmap[slot] = itemStack.isNotEmpty
}
}
override fun stillValid(player: Player): Boolean {
return true
}
/**
* Called from inside [serializeNBT] per slot, return true if you have written something into [nbt].
* If returning false, and slot does not contain an item, tag entry is discarded
*/
protected open fun attachSlotData(provider: Provider, slot: Int, nbt: CompoundTag, ops: RegistryOps<Tag>): Boolean {
return false
}
protected open fun loadSlotData(provider: Provider, slot: Int, nbt: CompoundTag, ops: RegistryOps<Tag>) {
}
override fun serializeNBT(provider: Provider): CompoundTag {
val ops = provider.createSerializationContext(NbtOps.INSTANCE)
return CompoundTag().also {
it["items"] = ListTag().also {
for (i in 0 until size) {
val tag = CompoundTag()
var attached = attachSlotData(provider, i, tag, ops)
if (items[i].isNotEmpty) {
attached = true
tag["item"] = ItemStack.OPTIONAL_CODEC.encodeStart(ops, items[i])
.getOrThrow { RuntimeException("Unable to serialize item ${items[i]} at slot $i: $it") }
}
tag["slot"] = i
if (attached) {
it.add(tag)
}
}
}
}
}
override fun deserializeNBT(provider: Provider, nbt: CompoundTag) {
val copy = observedItems.copyOf()
items.fill(ItemStack.EMPTY)
observedItems.fill(ItemStack.EMPTY)
bitmap.clear()
val seenSlots = IntOpenHashSet()
val ops = provider.createSerializationContext(NbtOps.INSTANCE)
if ("items" in nbt) {
for (element in nbt.getList("items", Tag.TAG_COMPOUND.toInt())) {
element as CompoundTag
if ("slot" !in element) continue
val slot = element.getInt("slot")
if (!seenSlots.add(slot)) continue
if ("item" in element) {
ItemStack.OPTIONAL_CODEC.decode(ops, element["item"])
.map { it.first }
.ifError { LOGGER.error("Failed to deserialize item stack in slot $slot: ${it.message()}") }
.ifSuccess {
if (it.isNotEmpty) {
items[slot] = it
observedItems[slot] = it.copy()
bitmap[slot] = true
if (it.count != copy[slot].count || !ItemStack.isSameItemSameComponents(it, copy[slot]))
notifySlotChanged(slot, copy[slot])
}
}
}
loadSlotData(provider, slot, element, ops)
}
}
}
open class Simple(size: Int) : EnhancedContainer<IContainerSlot>(size) {
private val slots by lazy(LazyThreadSafetyMode.PUBLICATION) { Array(size) { IContainerSlot.Simple(it, this) } }
override fun containerSlot(slot: Int): IContainerSlot {
return slots[slot]
}
}
open class WithListener(size: Int, private val listener: Runnable) : Simple(size) {
override fun notifySlotChanged(slot: Int, old: ItemStack) {
super.notifySlotChanged(slot, old)
listener.run()
}
}
companion object {
private val LOGGER = LogManager.getLogger()
}
}

View File

@ -1,153 +0,0 @@
package ru.dbotthepony.mc.otm.container
import net.minecraft.world.item.ItemStack
import net.neoforged.neoforge.capabilities.Capabilities
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.fluid.stream
import ru.dbotthepony.mc.otm.core.isNotEmpty
import ru.dbotthepony.mc.otm.core.math.Decimal
interface HandlerFilter {
fun canInsert(slot: Int, stack: ItemStack): Boolean {
return true
}
fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
return true
}
fun preInsert(slot: Int, stack: ItemStack, simulate: Boolean) {}
fun preExtract(slot: Int, amount: Int, simulate: Boolean) {}
fun modifyInsertCount(slot: Int, stack: ItemStack, existing: ItemStack, simulate: Boolean): Int {
return stack.count
}
fun modifyExtractCount(slot: Int, amount: Int, simulate: Boolean): Int {
return amount
}
fun and(other: HandlerFilter): HandlerFilter {
return object : HandlerFilter {
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
return this@HandlerFilter.canInsert(slot, stack) && other.canInsert(slot, stack)
}
override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
return this@HandlerFilter.canExtract(slot, amount, stack) && other.canExtract(slot, amount, stack)
}
override fun preInsert(slot: Int, stack: ItemStack, simulate: Boolean) {
this@HandlerFilter.preInsert(slot, stack, simulate)
other.preInsert(slot, stack, simulate)
}
override fun preExtract(slot: Int, amount: Int, simulate: Boolean) {
this@HandlerFilter.preExtract(slot, amount, simulate)
other.preExtract(slot, amount, simulate)
}
}
}
object FluidContainers : HandlerFilter {
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
return stack.getCapability(Capabilities.FluidHandler.ITEM)?.let { it.tanks > 0 } ?: false
}
}
object DrainableFluidContainers : HandlerFilter {
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
return stack.getCapability(Capabilities.FluidHandler.ITEM)?.let { it.stream().anyMatch { it.isNotEmpty } } ?: false
}
override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
return !canInsert(slot, stack)
}
}
object OnlyIn : HandlerFilter {
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
return true
}
override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
return false
}
}
object OnlyOut : HandlerFilter {
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
return false
}
override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
return true
}
}
object Both : HandlerFilter
object Dischargeable : HandlerFilter {
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
return stack.getCapability(Capabilities.EnergyStorage.ITEM)?.let { it.canExtract() && it.extractEnergy(Int.MAX_VALUE, true) > 0 } ?: false
}
override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
return stack.getCapability(Capabilities.EnergyStorage.ITEM)?.let { !it.canExtract() || it.extractEnergy(Int.MAX_VALUE, true) <= 0 } ?: false
}
}
object Chargeable : HandlerFilter {
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
return stack.getCapability(Capabilities.EnergyStorage.ITEM)?.let { it.canReceive() && it.receiveEnergy(Int.MAX_VALUE, true) > 0 } ?: false
}
override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
return stack.getCapability(Capabilities.EnergyStorage.ITEM)?.let { !it.canReceive() || it.receiveEnergy(Int.MAX_VALUE, true) <= 0 } ?: false
}
}
object ChemicalFuel : HandlerFilter {
override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
return stack.getBurnTime(null) <= 0
}
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
return stack.getBurnTime(null) > 0
}
}
object IsPattern : HandlerFilter {
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
return stack.getCapability(MatteryCapability.PATTERN_ITEM) != null
}
}
object MatterProviders : HandlerFilter {
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
return stack.getCapability(MatteryCapability.MATTER_ITEM)
?.let { it.matterFlow.output && it.extractMatterChecked(Decimal.POSITIVE_INFINITY, true) > Decimal.ZERO }
?: false
}
override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
return stack.getCapability(MatteryCapability.MATTER_ITEM)
?.let { !it.matterFlow.output || it.extractMatterChecked(Decimal.POSITIVE_INFINITY, true) <= Decimal.ZERO }
?: false
}
}
object MatterConsumers : HandlerFilter {
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
return stack.getCapability(MatteryCapability.MATTER_ITEM)
?.let { it.matterFlow.input && it.receiveMatterChecked(Decimal.POSITIVE_INFINITY, true) > Decimal.ZERO }
?: false
}
override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
return stack.getCapability(MatteryCapability.MATTER_ITEM)
?.let { !it.matterFlow.input || it.receiveMatterChecked(Decimal.POSITIVE_INFINITY, true) <= Decimal.ZERO }
?: false
}
}
}

View File

@ -0,0 +1,44 @@
package ru.dbotthepony.mc.otm.container
import net.minecraft.world.Container
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack
import net.neoforged.neoforge.items.IItemHandler
import net.neoforged.neoforge.items.IItemHandlerModifiable
/**
* Reinforced [ISlottedContainer] which slots are [IAutomatedContainerSlot]s, which
* subsequently allow this container to implement [IItemHandler]
*/
interface IAutomatedContainer<out S : IAutomatedContainerSlot> : ISlottedContainer<S>, IItemHandlerModifiable {
override fun canPlaceItem(slot: Int, itemStack: ItemStack): Boolean {
return containerSlot(slot).canAutomationPlaceItem(itemStack)
}
override fun canTakeItem(container: Container, slot: Int, itemStack: ItemStack): Boolean {
return containerSlot(slot).canAutomationTakeItem()
}
override fun getSlots() = containerSize
override fun getStackInSlot(slot: Int) = containerSlot(slot).item
override fun insertItem(slot: Int, stack: ItemStack, simulate: Boolean): ItemStack {
return containerSlot(slot).insertItem(stack, simulate)
}
override fun extractItem(slot: Int, amount: Int, simulate: Boolean): ItemStack {
return containerSlot(slot).extractItem(amount, simulate)
}
override fun getSlotLimit(slot: Int): Int {
return containerSlot(slot).maxStackSize
}
override fun setStackInSlot(slot: Int, stack: ItemStack) {
setItem(slot, stack)
}
override fun isItemValid(slot: Int, stack: ItemStack): Boolean {
return canPlaceItem(slot, stack)
}
}

View File

@ -0,0 +1,93 @@
package ru.dbotthepony.mc.otm.container
import net.minecraft.world.item.ItemStack
import net.neoforged.neoforge.items.IItemHandler
/**
* Slot of [IAutomatedContainer], with additional methods to implement interaction behavior for both for players and mechanisms
*/
interface IAutomatedContainerSlot : IContainerSlot {
fun canAutomationPlaceItem(itemStack: ItemStack): Boolean {
return true
}
fun canAutomationTakeItem(desired: Int = item.count): Boolean {
return true
}
fun modifyAutomationPlaceCount(itemStack: ItemStack): Int {
return itemStack.count
}
fun modifyAutomationExtractionCount(desired: Int): Int {
return desired
}
/**
* Slot-specific implementation for [IItemHandler.insertItem]
*/
fun insertItem(stack: ItemStack, simulate: Boolean): ItemStack {
if (!canAutomationPlaceItem(stack))
return stack
var amount = modifyAutomationPlaceCount(stack)
if (amount <= 0)
return stack
if (item.isEmpty) {
amount = stack.count.coerceAtMost(maxStackSize(stack)).coerceAtMost(amount)
if (!simulate) {
item = stack.copyWithCount(amount)
}
if (stack.count <= amount) {
return ItemStack.EMPTY
} else {
return stack.copyWithCount(stack.count - amount)
}
} else if (item.isStackable && maxStackSize(item) > item.count && ItemStack.isSameItemSameComponents(item, stack)) {
val newCount = maxStackSize(item).coerceAtMost(item.count + stack.count.coerceAtMost(amount))
val diff = newCount - item.count
if (diff != 0) {
if (!simulate) {
item.grow(diff)
setChanged()
}
val copy = stack.copy()
copy.shrink(diff)
return copy
}
}
return stack
}
fun extractItem(amount: Int, simulate: Boolean): ItemStack {
if (amount <= 0 || !canAutomationTakeItem(amount) || item.isEmpty)
return ItemStack.EMPTY
@Suppress("name_shadowing")
val amount = modifyAutomationExtractionCount(amount)
if (amount <= 0 || !canAutomationTakeItem(amount)) return ItemStack.EMPTY
val minimal = amount.coerceAtMost(item.count)
val copy = item.copy()
copy.count = minimal
if (!simulate) {
if (item.count == minimal) {
item = ItemStack.EMPTY
} else {
item.shrink(minimal)
}
setChanged()
}
return copy
}
}

View File

@ -1,95 +0,0 @@
package ru.dbotthepony.mc.otm.container
import net.minecraft.world.Container
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import java.util.function.Predicate
// passthrough all default methods to fix Kotlin bug related to implementation delegation not properly working on Java interfaces
// and also to give params proper names
// https://youtrack.jetbrains.com/issue/KT-55080/Change-the-behavior-of-inheritance-delegation-to-delegates-implementing-Java-interfaces-with-default-methods
interface IContainer : Container {
override fun getMaxStackSize(): Int {
return super.getMaxStackSize()
}
override fun startOpen(player: Player) {
super.startOpen(player)
}
override fun stopOpen(player: Player) {
super.stopOpen(player)
}
override fun canPlaceItem(slot: Int, itemStack: ItemStack): Boolean {
return super.canPlaceItem(slot, itemStack)
}
override fun canTakeItem(container: Container, slot: Int, itemStack: ItemStack): Boolean {
return super.canTakeItem(container, slot, itemStack)
}
override fun countItem(item: Item): Int {
return super.countItem(item)
}
override fun hasAnyOf(items: Set<Item>): Boolean {
return super.hasAnyOf(items)
}
override fun hasAnyMatching(predicate: Predicate<ItemStack>): Boolean {
return super.hasAnyMatching(predicate)
}
override fun clearContent()
override fun getContainerSize(): Int
override fun isEmpty(): Boolean
override fun getItem(slot: Int): ItemStack
override fun removeItem(slot: Int, amount: Int): ItemStack
override fun removeItemNoUpdate(slot: Int): ItemStack
override fun setItem(slot: Int, itemStack: ItemStack)
override fun setChanged()
override fun stillValid(player: Player): Boolean
companion object {
fun wrap(container: Container): IContainer {
if (container is IContainer)
return container
else
return object : IContainer, Container by container {
override fun getMaxStackSize(): Int {
return container.getMaxStackSize()
}
override fun startOpen(player: Player) {
container.startOpen(player)
}
override fun stopOpen(player: Player) {
container.stopOpen(player)
}
override fun canPlaceItem(slot: Int, itemStack: ItemStack): Boolean {
return container.canPlaceItem(slot, itemStack)
}
override fun canTakeItem(container: Container, slot: Int, itemStack: ItemStack): Boolean {
return container.canTakeItem(container, slot, itemStack)
}
override fun countItem(item: Item): Int {
return container.countItem(item)
}
override fun hasAnyOf(items: Set<Item>): Boolean {
return container.hasAnyOf(items)
}
override fun hasAnyMatching(predicate: Predicate<ItemStack>): Boolean {
return container.hasAnyMatching(predicate)
}
}
}
}
}

View File

@ -1,9 +1,7 @@
package ru.dbotthepony.mc.otm.container
import net.minecraft.world.Container
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import ru.dbotthepony.kommons.util.Delegate
import ru.dbotthepony.mc.otm.core.isNotEmpty
@ -12,57 +10,64 @@ import ru.dbotthepony.mc.otm.core.isNotEmpty
* for Player interaction.
*/
interface IContainerSlot : Delegate<ItemStack> {
val slot: Int
val container: Container
operator fun component1() = slot
operator fun component2() = item
fun getMaxStackSize(item: ItemStack = this.item): Int {
return container.maxStackSize
}
val isForbiddenForAutomation: Boolean get() {
return getFilter() === Items.AIR
}
fun getFilter(): Item?
fun setChanged()
var item: ItemStack
/**
* @return whenever the filter was set. Returns false only if container can't be filtered.
* Max stack size regardless of item
*
* Prefer to use ItemStack version instead
*/
fun setFilter(filter: Item? = null): Boolean
val maxStackSize: Int
val hasFilter: Boolean
get() = getFilter() != null
fun remove() {
container[slot] = ItemStack.EMPTY
/**
* Max amount of [item] that can be stored in this slot.
*
* This may be larger or smaller than value returned [ItemStack.getMaxStackSize],
* and as such returned value by this method should be ground truth
*/
fun maxStackSize(item: ItemStack): Int {
return maxStackSize.coerceAtMost(item.maxStackSize)
}
fun remove(count: Int): ItemStack {
return container.removeItem(slot, count)
}
fun remove(): ItemStack
fun remove(count: Int): ItemStack
override fun get(): ItemStack {
return container[slot]
return item
}
override fun accept(t: ItemStack) {
container[slot] = t
item = t
}
fun setChanged() {
container.setChanged()
}
var item: ItemStack
get() = container[slot]
set(value) { container[slot] = value }
val isEmpty: Boolean
get() = container[slot].isEmpty
get() = item.isEmpty
val isNotEmpty: Boolean
get() = container[slot].isNotEmpty
get() = item.isNotEmpty
open class Simple(protected val slot: Int, protected val container: Container) : IContainerSlot {
override fun setChanged() {
container.setChanged()
}
override var item: ItemStack
get() = container[slot]
set(value) { container[slot] = value }
override val maxStackSize: Int
get() = container.maxStackSize
override fun remove(count: Int): ItemStack {
return container.removeItem(slot, count)
}
override fun remove(): ItemStack {
return container.removeItemNoUpdate(slot)
}
override fun maxStackSize(item: ItemStack): Int {
return maxStackSize.coerceAtMost(item.maxStackSize)
}
}
}

View File

@ -0,0 +1,477 @@
package ru.dbotthepony.mc.otm.container
import it.unimi.dsi.fastutil.ints.IntCollection
import net.minecraft.world.Container
import net.minecraft.world.entity.player.Player
import net.minecraft.world.entity.player.StackedContents
import net.minecraft.world.inventory.StackedContentsCompatible
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import net.minecraft.world.item.crafting.RecipeInput
import ru.dbotthepony.mc.otm.core.collect.any
import ru.dbotthepony.mc.otm.core.collect.filter
import ru.dbotthepony.mc.otm.core.collect.map
import ru.dbotthepony.mc.otm.core.isNotEmpty
import java.util.function.Predicate
import java.util.stream.Stream
import java.util.stream.StreamSupport
/**
* "Backward-compatible" enhanced container interface, where all methods can be derived/emulated from existing [Container] code
*
* This is useful because it allows to interact with actually enhanced and regular containers through unified interface,
* and actual implementations of this interface are likely to provide efficient method implementations in place of derived/emulated ones.
*/
interface IEnhancedContainer<out S : IContainerSlot> : Container, RecipeInput, Iterable<ItemStack>, StackedContentsCompatible {
// https://youtrack.jetbrains.com/issue/KT-55080/Change-the-behavior-of-inheritance-delegation-to-delegates-implementing-Java-interfaces-with-default-methods
override fun getMaxStackSize(): Int {
return super.getMaxStackSize()
}
override fun startOpen(player: Player) {
super.startOpen(player)
}
override fun stopOpen(player: Player) {
super.stopOpen(player)
}
override fun canPlaceItem(slot: Int, itemStack: ItemStack): Boolean {
return super.canPlaceItem(slot, itemStack)
}
override fun canTakeItem(container: Container, slot: Int, itemStack: ItemStack): Boolean {
return super.canTakeItem(container, slot, itemStack)
}
override fun clearContent()
override fun getContainerSize(): Int
override fun getItem(slot: Int): ItemStack
override fun removeItem(slot: Int, amount: Int): ItemStack
override fun removeItemNoUpdate(slot: Int): ItemStack
override fun setItem(slot: Int, itemStack: ItemStack)
override fun setChanged()
// provide non-ambiguous get and set operators
operator fun get(slot: Int): ItemStack {
return getItem(slot)
}
operator fun set(slot: Int, value: ItemStack) {
setItem(slot, value)
}
fun containerSlot(slot: Int): S
override fun fillStackedContents(contents: StackedContents) {
forEach { contents.accountStack(it) }
}
/**
* Returns iterator over **all** slots this container has
*/
fun slotIterator(): Iterator<S> {
return (0 until containerSize).iterator().map { containerSlot(it) }
}
fun nonEmptySlotIterator(): Iterator<S> {
return nonEmptySlotIndexIterator().map { containerSlot(it) }
}
fun emptySlotIterator(): Iterator<S> {
return emptySlotIndexIterator().map { containerSlot(it) }
}
fun nonEmptySlotIterator(allowedSlots: IntCollection): Iterator<S> {
return nonEmptySlotIndexIterator(allowedSlots).map { containerSlot(it) }
}
fun emptySlotIterator(allowedSlots: IntCollection): Iterator<S> {
return emptySlotIndexIterator(allowedSlots).map { containerSlot(it) }
}
private fun slotIndexWalker(allowedSlots: IntCollection, predicate: Predicate<ItemStack>): IntIterator {
return object : IntIterator() {
private val parent = allowedSlots.intIterator()
private var foundNext = false
private var next = -1
private fun findNext() {
if (!foundNext) {
foundNext = true
next = -1
while (parent.hasNext()) {
val i = parent.nextInt()
if (predicate.test(this@IEnhancedContainer[i])) {
next = i
break
}
}
}
}
override fun nextInt(): Int {
findNext()
if (next == -1)
throw NoSuchElementException()
foundNext = false
val next = next
this.next = -1
return next
}
override fun hasNext(): Boolean {
findNext()
return next != -1
}
}
}
fun emptySlotIndexIterator(allowedSlots: IntCollection): IntIterator {
return slotIndexWalker(allowedSlots) { it.isEmpty }
}
fun nonEmptySlotIndexIterator(allowedSlots: IntCollection): IntIterator {
return slotIndexWalker(allowedSlots) { it.isNotEmpty }
}
fun emptySlotIndexIterator(): IntIterator {
return emptySlotIndexIterator(slotRange)
}
fun nonEmptySlotIndexIterator(): IntIterator {
return nonEmptySlotIndexIterator(slotRange)
}
fun slotIndexWithItemIterator(item: Item): IntIterator {
return slotIndexWithItemIterator(item, slotRange)
}
fun slotIndexWithItemIterator(item: Item, allowedSlots: IntCollection): IntIterator {
val parent = nonEmptySlotIndexIterator(allowedSlots).filter { this[it].isNotEmpty && this[it].item === item }
return object : IntIterator() {
override fun nextInt(): Int {
return parent.next()
}
override fun hasNext(): Boolean {
return parent.hasNext()
}
}
}
fun slotWithItemIterator(item: Item): Iterator<S> {
return slotWithItemIterator(item, slotRange)
}
fun slotWithItemIterator(item: Item, allowedSlots: IntCollection): Iterator<S> {
return slotIndexWithItemIterator(item, allowedSlots).map { containerSlot(it) }
}
fun nextEmptySlot(startIndex: Int): Int {
for (i in startIndex until containerSize) {
if (this[i].isEmpty) {
return i
}
}
return -1
}
fun nextNonEmptySlot(startIndex: Int): Int {
for (i in startIndex until containerSize) {
if (this[i].isNotEmpty) {
return i
}
}
return -1
}
fun nextSlotWithItem(startIndex: Int, item: Item): Int {
var find = nextNonEmptySlot(startIndex)
while (find != -1 && this[find].item !== item)
find = nextNonEmptySlot(find + 1)
return find
}
fun setChanged(slot: Int) {
return setChanged()
}
fun getMaxStackSize(slot: Int, itemStack: ItemStack): Int {
return maxStackSize
}
/**
* Returns iterator over **non-empty** [ItemStack]s inside this container
*/
override fun iterator(): Iterator<ItemStack> {
return (0 until containerSize).iterator().map { this[it] }.filter { it.isNotEmpty }
}
override fun isEmpty(): Boolean {
return nextNonEmptySlot(0) != -1
}
val hasEmptySlots: Boolean get() {
return nextEmptySlot(0) != -1
}
override fun size(): Int {
return containerSize
}
override fun countItem(item: Item): Int {
var count = 0
for (slot in slotWithItemIterator(item))
count += slot.item.count
return count
}
override fun hasAnyOf(items: Set<Item>): Boolean {
if (Items.AIR in items && hasEmptySlots)
return true
return iterator().any { it.item in items }
}
override fun hasAnyMatching(predicate: Predicate<ItemStack>): Boolean {
if (predicate.test(ItemStack.EMPTY) && hasEmptySlots)
return true
return iterator().any(predicate)
}
fun toList(): MutableList<ItemStack> {
val list = ArrayList<ItemStack>(containerSize)
for (i in 0 until containerSize) {
list.add(this[i])
}
return list
}
fun copyToList(): MutableList<ItemStack> {
val list = ArrayList<ItemStack>(containerSize)
for (i in 0 until containerSize) {
list.add(this[i].copy())
}
return list
}
private fun addItem(stack: ItemStack, simulate: Boolean, filterPass: Boolean, slots: IntCollection, onlyIntoExisting: Boolean, popTime: Int?, ignoreFilters: Boolean): ItemStack {
if (stack.isEmpty || slots.isEmpty())
return stack
// двигаем в одинаковые слоты
for (slot in slotWithItemIterator(stack.item, slots)) {
val condition: Boolean
if (slot is IFilteredContainerSlot) {
condition = (ignoreFilters || !slot.filter.denyAll) &&
ItemStack.isSameItemSameComponents(slot.item, stack) &&
(ignoreFilters || !filterPass && slot.filter.allowAll || filterPass && !slot.filter.allowAll && slot.filter.test(stack))
} else {
condition = (ignoreFilters || !filterPass) && ItemStack.isSameItemSameComponents(slot.item, stack)
}
if (condition) {
val slotLimit = slot.maxStackSize(slot.item)
if (slot.item.count < slotLimit) {
val newCount = (slot.item.count + stack.count).coerceAtMost(slotLimit)
val diff = newCount - slot.item.count
if (!simulate) {
slot.item.count = newCount
slot.setChanged()
if (popTime != null) {
slot.item.popTime = popTime
}
}
stack.shrink(diff)
if (stack.isEmpty)
return ItemStack.EMPTY
}
}
}
if (!onlyIntoExisting) {
for (slot in emptySlotIterator(slots)) {
val condition: Boolean
if (slot is IFilteredContainerSlot) {
condition = (ignoreFilters || !slot.filter.denyAll) &&
(ignoreFilters || !filterPass && slot.filter.allowAll || filterPass && !slot.filter.allowAll && slot.filter.test(stack))
} else {
condition = ignoreFilters || !filterPass
}
if (condition) {
val diff = stack.count.coerceAtMost(slot.maxStackSize(stack))
if (!simulate) {
val copyToPut = stack.copy()
copyToPut.count = diff
slot.item = copyToPut
if (popTime != null) {
copyToPut.popTime = popTime
}
}
stack.shrink(diff)
if (stack.isEmpty)
return ItemStack.EMPTY
}
}
}
return stack
}
/**
* Hint used internally by [IEnhancedContainer] to potentially speed up default method implementations
*/
val hasFilterableSlots: Boolean
get() = false
fun addItem(stack: ItemStack, simulate: Boolean, slots: IntCollection = slotRange, onlyIntoExisting: Boolean = false, popTime: Int? = null, ignoreFilters: Boolean = false): ItemStack {
if (stack.isEmpty || slots.isEmpty())
return stack
if (ignoreFilters || !hasFilterableSlots) {
return addItem(stack.copy(), simulate, filterPass = true, slots, onlyIntoExisting = onlyIntoExisting, popTime = popTime, ignoreFilters = true)
} else {
var copy = addItem(stack.copy(), simulate, filterPass = true, slots, onlyIntoExisting = onlyIntoExisting, popTime = popTime, ignoreFilters = false)
copy = addItem(copy, simulate, filterPass = false, slots, onlyIntoExisting = onlyIntoExisting, popTime = popTime, ignoreFilters = false)
return copy
}
}
/**
* Unlike [addItem], modifies original [stack]
*
* @return Whenever [stack] was modified
*/
fun consumeItem(stack: ItemStack, simulate: Boolean, slots: IntCollection = slotRange, onlyIntoExisting: Boolean = false, popTime: Int? = null, ignoreFilters: Boolean = false): Boolean {
if (stack.isEmpty)
return false
val result = addItem(stack, simulate = simulate, slots = slots, onlyIntoExisting = onlyIntoExisting, popTime = popTime, ignoreFilters = ignoreFilters)
if (!simulate) stack.count = result.count
return result.count != stack.count
}
fun fullyAddItem(stack: ItemStack, slots: IntCollection = slotRange, onlyIntoExisting: Boolean = false, popTime: Int? = null, ignoreFilters: Boolean = false): Boolean {
if (!addItem(stack, simulate = true, slots = slots, popTime = popTime, onlyIntoExisting = onlyIntoExisting, ignoreFilters = ignoreFilters).isEmpty)
return false
return addItem(stack, simulate = false, slots = slots, popTime = popTime, onlyIntoExisting = onlyIntoExisting, ignoreFilters = ignoreFilters).isEmpty
}
fun stream(): Stream<ItemStack> {
return StreamSupport.stream(spliterator(), false)
}
private class Wrapper(private val parent: Container) : IEnhancedContainer<IContainerSlot> {
override fun containerSlot(slot: Int): IContainerSlot {
return IContainerSlot.Simple(slot, parent)
}
override fun clearContent() {
return parent.clearContent()
}
override fun setChanged() {
return parent.setChanged()
}
override fun getContainerSize(): Int {
return parent.containerSize
}
override fun getItem(slot: Int): ItemStack {
return parent.getItem(slot)
}
override fun removeItem(slot: Int, amount: Int): ItemStack {
return parent.removeItem(slot, amount)
}
override fun removeItemNoUpdate(slot: Int): ItemStack {
return parent.removeItemNoUpdate(slot)
}
override fun setItem(slot: Int, itemStack: ItemStack) {
return parent.setItem(slot, itemStack)
}
override fun stillValid(player: Player): Boolean {
return parent.stillValid(player)
}
override fun getMaxStackSize(): Int {
return parent.maxStackSize
}
override fun startOpen(player: Player) {
parent.startOpen(player)
}
override fun stopOpen(player: Player) {
parent.stopOpen(player)
}
override fun canPlaceItem(slot: Int, itemStack: ItemStack): Boolean {
return parent.canPlaceItem(slot, itemStack)
}
override fun canTakeItem(container: Container, slot: Int, itemStack: ItemStack): Boolean {
return parent.canTakeItem(container, slot, itemStack)
}
override fun isEmpty(): Boolean {
return parent.isEmpty
}
override fun hasAnyMatching(predicate: Predicate<ItemStack>): Boolean {
return parent.hasAnyMatching(predicate)
}
override fun hasAnyOf(items: Set<Item>): Boolean {
return parent.hasAnyOf(items)
}
override fun countItem(item: Item): Int {
return parent.countItem(item)
}
}
companion object {
fun wrap(other: Container): IEnhancedContainer<*> {
if (other is IEnhancedContainer<*>)
return other
return Wrapper(other)
}
}
}

View File

@ -0,0 +1,33 @@
package ru.dbotthepony.mc.otm.container
import net.minecraft.world.entity.player.StackedContents
import net.minecraft.world.inventory.CraftingContainer
import net.minecraft.world.item.ItemStack
interface IEnhancedCraftingContainer<out S : IContainerSlot> : IEnhancedContainer<S>, CraftingContainer {
override fun getItems(): MutableList<ItemStack> {
return toList()
}
override fun fillStackedContents(contents: StackedContents) {
forEach { contents.accountSimpleStack(it) }
}
class Wrapper<out C : IEnhancedContainer<S>, out S : IContainerSlot>(val parent: C, private val width: Int, private val height: Int) : IEnhancedCraftingContainer<S>, IEnhancedContainer<S> by parent {
init {
require(width * height == parent.containerSize) { "Crafting container dimensions ($width x $height) do not match container size provided (${parent.containerSize})" }
}
override fun getWidth(): Int {
return width
}
override fun getHeight(): Int {
return height
}
override fun fillStackedContents(contents: StackedContents) {
super<IEnhancedCraftingContainer>.fillStackedContents(contents)
}
}
}

View File

@ -0,0 +1,14 @@
package ru.dbotthepony.mc.otm.container
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
interface IFilteredAutomatedContainerSlot : IFilteredContainerSlot, IAutomatedContainerSlot {
override fun canAutomationPlaceItem(itemStack: ItemStack): Boolean {
return super.canAutomationPlaceItem(itemStack) && filter.test(itemStack)
}
override fun canAutomationTakeItem(desired: Int): Boolean {
return super.canAutomationTakeItem(desired) && !filter.denyAll
}
}

View File

@ -0,0 +1,5 @@
package ru.dbotthepony.mc.otm.container
interface IFilteredContainerSlot : IContainerSlot {
var filter: ItemFilter
}

View File

@ -1,255 +0,0 @@
package ru.dbotthepony.mc.otm.container
import it.unimi.dsi.fastutil.ints.IntIterable
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import net.minecraft.world.item.crafting.RecipeInput
import ru.dbotthepony.mc.otm.core.collect.filter
import ru.dbotthepony.mc.otm.core.collect.map
import ru.dbotthepony.mc.otm.core.isNotEmpty
interface IMatteryContainer : IContainer, RecipeInput, Iterable<ItemStack> {
fun getSlotFilter(slot: Int): Item?
/**
* @return whenever the filter was set. Returns false only if container can't be filtered.
*/
fun setSlotFilter(slot: Int, filter: Item? = null): Boolean {
return false
}
fun clearSlotFilters()
override fun isEmpty(): Boolean
fun setChanged(slot: Int)
override fun size(): Int {
return containerSize
}
/**
* Iterates over non-empty itemstacks of this container
*/
override fun iterator(): Iterator<ItemStack> {
return iterator(true)
}
/**
* Iterates non-empty slots of this container
*/
fun slotIterator(): Iterator<IContainerSlot> {
return slotIterator(true)
}
fun iterator(nonEmpty: Boolean): Iterator<ItemStack> {
if (nonEmpty) {
return (0 until containerSize).iterator().map { this[it] }.filter { it.isNotEmpty }
} else {
return (0 until containerSize).iterator().map { this[it] }
}
}
open class ContainerSlot(override val slot: Int, override val container: IMatteryContainer) : IContainerSlot {
override val isForbiddenForAutomation: Boolean
get() = container.isSlotForbiddenForAutomation(slot)
override fun getFilter(): Item? {
return container.getSlotFilter(slot)
}
override fun setFilter(filter: Item?): Boolean {
return container.setSlotFilter(slot, filter)
}
override fun getMaxStackSize(item: ItemStack): Int {
return container.getMaxStackSize(slot, item)
}
override fun setChanged() {
container.setChanged(slot)
}
}
/**
* Iterates either non-empty slots of container or all slots of container
*/
fun slotIterator(nonEmpty: Boolean): Iterator<IContainerSlot> {
if (nonEmpty) {
return (0 until containerSize).iterator().filter { this[it].isNotEmpty }.map { containerSlot(it) }
} else {
return (0 until containerSize).iterator().map { containerSlot(it) }
}
}
fun containerSlot(slot: Int): IContainerSlot {
return ContainerSlot(slot, this)
}
fun hasSlotFilter(slot: Int) = getSlotFilter(slot) !== null
fun isSlotForbiddenForAutomation(slot: Int) = getSlotFilter(slot) === Items.AIR
fun testSlotFilter(slot: Int, itemStack: ItemStack): Boolean {
return testSlotFilter(slot, itemStack.item)
}
fun testSlotFilter(slot: Int, item: Item): Boolean {
if (getSlotFilter(slot) == null) {
return true
} else {
return getSlotFilter(slot) === item
}
}
fun getMaxStackSize(slot: Int, itemStack: ItemStack) = maxStackSize.coerceAtMost(itemStack.maxStackSize)
private fun addItem(stack: ItemStack, simulate: Boolean, filterPass: Boolean, slots: IntIterable, onlyIntoExisting: Boolean, popTime: Int?, ignoreFilters: Boolean): ItemStack {
if (stack.isEmpty)
return stack
// двигаем в одинаковые слоты
var i = slots.intIterator()
while (i.hasNext()) {
val slot = i.nextInt()
if (
(ignoreFilters || !isSlotForbiddenForAutomation(slot)) &&
ItemStack.isSameItemSameComponents(getItem(slot), stack) &&
(ignoreFilters || !filterPass && !hasSlotFilter(slot) || filterPass && hasSlotFilter(slot) && testSlotFilter(slot, stack))
) {
val slotStack = getItem(slot)
val slotLimit = getMaxStackSize(slot, slotStack)
if (slotStack.count < slotLimit) {
val newCount = (slotStack.count + stack.count).coerceAtMost(slotLimit)
val diff = newCount - slotStack.count
if (!simulate) {
slotStack.count = newCount
setChanged(slot)
if (popTime != null) {
slotStack.popTime = popTime
}
}
stack.shrink(diff)
if (stack.isEmpty) {
return stack
}
}
}
}
if (!onlyIntoExisting) {
i = slots.intIterator()
// двигаем в пустые слоты
while (i.hasNext()) {
val slot = i.nextInt()
if (
getItem(slot).isEmpty &&
(ignoreFilters || !isSlotForbiddenForAutomation(slot)) &&
(ignoreFilters || !filterPass && !hasSlotFilter(slot) || filterPass && hasSlotFilter(slot) && testSlotFilter(slot, stack))
) {
val diff = stack.count.coerceAtMost(getMaxStackSize(slot, stack))
if (!simulate) {
val copyToPut = stack.copy()
copyToPut.count = diff
setItem(slot, copyToPut)
if (popTime != null) {
copyToPut.popTime = popTime
}
}
stack.shrink(diff)
if (stack.isEmpty) {
return stack
}
}
}
}
return stack
}
fun addItem(stack: ItemStack, simulate: Boolean, slots: IntIterable = slotRange, onlyIntoExisting: Boolean = false, popTime: Int? = null, ignoreFilters: Boolean = false): ItemStack {
if (stack.isEmpty)
return stack
if (ignoreFilters) {
return addItem(stack.copy(), simulate, true, slots, onlyIntoExisting, popTime, true)
} else {
var copy = addItem(stack.copy(), simulate, true, slots, onlyIntoExisting, popTime, false)
copy = addItem(copy, simulate, false, slots, onlyIntoExisting, popTime, false)
return copy
}
}
fun handler(filter: HandlerFilter = HandlerFilter.Both): ContainerHandler {
return ContainerHandler(this, filter)
}
/**
* Unlike [addItem], modifies original [stack]
*
* @return Whenever [stack] was modified
*/
fun consumeItem(stack: ItemStack, simulate: Boolean, slots: IntIterable = slotRange, onlyIntoExisting: Boolean = false, popTime: Int? = null, ignoreFilters: Boolean = false): Boolean {
if (stack.isEmpty)
return false
val result = addItem(stack, simulate, slots, onlyIntoExisting, popTime, ignoreFilters)
if (result.count != stack.count) {
if (!simulate) {
stack.count = result.count
}
return true
}
return false
}
fun fullyAddItem(stack: ItemStack, slots: IntIterable = slotRange, ignoreFilters: Boolean = false): Boolean {
if (!addItem(stack, true, slots, ignoreFilters).isEmpty)
return false
return addItem(stack, false, slots, ignoreFilters).isEmpty
}
fun toList(): MutableList<ItemStack> {
val list = ArrayList<ItemStack>(size())
for (i in 0 until size()) {
list.add(this[i])
}
return list
}
fun shrink(slot: Int, amount: Int): Boolean {
if (slot < 0 || slot > size())
return false
val item = this[slot]
if (item.isEmpty)
return false
if (item.count <= amount) {
this[slot] = ItemStack.EMPTY
} else {
item.shrink(amount)
setChanged(slot)
}
return true
}
}

View File

@ -0,0 +1,36 @@
package ru.dbotthepony.mc.otm.container
import net.minecraft.world.item.ItemStack
import ru.dbotthepony.kommons.collect.any
/**
* Skeletal implementation for containers which revolve around [IContainerSlot]
*/
interface ISlottedContainer<out S : IContainerSlot> : IEnhancedContainer<S> {
override fun setChanged(slot: Int) {
containerSlot(slot).setChanged()
}
override fun getMaxStackSize(slot: Int, itemStack: ItemStack): Int {
return containerSlot(slot).maxStackSize(itemStack)
}
override fun getItem(slot: Int): ItemStack {
return containerSlot(slot).item
}
override fun removeItem(slot: Int, amount: Int): ItemStack {
return containerSlot(slot).remove(amount)
}
override fun removeItemNoUpdate(slot: Int): ItemStack {
return containerSlot(slot).remove()
}
override fun setItem(slot: Int, itemStack: ItemStack) {
containerSlot(slot).item = itemStack
}
override val hasFilterableSlots: Boolean
get() = slotIterator().any { it is IFilteredContainerSlot }
}

Some files were not shown because too many files have changed in this diff Show More