diff --git a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/English.kt b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/English.kt index cb3de77d3..180d0ab7e 100644 --- a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/English.kt +++ b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/English.kt @@ -365,6 +365,8 @@ private fun blocks(provider: MatteryLanguageProvider) { add(MBlocks.COBBLESTONE_GENERATOR, "Cobblestone Generator") add(MBlocks.ESSENCE_STORAGE, "Essence Storage") add(MBlocks.ESSENCE_STORAGE, "desc", "Allows to store and retrieve experience levels") + add(MBlocks.ITEM_REPAIRER, "Matter Reconstructor") + add(MBlocks.ITEM_REPAIRER, "desc", "Repairs tools using matter") add(MBlocks.ENGINE, "Ship Engine") add(MBlocks.HOLO_SIGN, "Holo Sign") diff --git a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/Russian.kt b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/Russian.kt index 41f5fc203..d901d0db2 100644 --- a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/Russian.kt +++ b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/Russian.kt @@ -372,6 +372,8 @@ private fun blocks(provider: MatteryLanguageProvider) { add(MBlocks.COBBLESTONE_GENERATOR, "Генератор булыжника") add(MBlocks.ESSENCE_STORAGE, "Хранилище эссенции") add(MBlocks.ESSENCE_STORAGE, "desc", "Позволяет хранить очки опыта") + add(MBlocks.ITEM_REPAIRER, "Материальный реконструктор") + add(MBlocks.ITEM_REPAIRER, "desc", "Чинит инструменты используя материю") add(MBlocks.ENGINE, "Двигатель корабля") add(MBlocks.HOLO_SIGN, "Голографическая табличка") diff --git a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/loot/LootTablesData.kt b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/loot/LootTablesData.kt index 456b343df..6451c3831 100644 --- a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/loot/LootTablesData.kt +++ b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/loot/LootTablesData.kt @@ -137,6 +137,7 @@ fun addLootTables(lootTables: LootTables) { lootTables.tile(MBlocks.COBBLESTONE_GENERATOR) lootTables.tile(MBlocks.ESSENCE_STORAGE) + lootTables.tile(MBlocks.ITEM_REPAIRER) lootTables.tile(MBlocks.ENERGY_SERVO) lootTables.tile(MBlocks.ENERGY_COUNTER) diff --git a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/tags/Tags.kt b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/tags/Tags.kt index 2b23f8acf..3a5c7f456 100644 --- a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/tags/Tags.kt +++ b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/tags/Tags.kt @@ -172,6 +172,7 @@ fun addTags(tagsProvider: TagsProvider) { MBlocks.HOLO_SIGN, MBlocks.COBBLESTONE_GENERATOR, MBlocks.ESSENCE_STORAGE, + MBlocks.ITEM_REPAIRER, ), Tiers.IRON) tagsProvider.requiresPickaxe(MBlocks.TRITANIUM_ANVIL, Tiers.IRON) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/ItemRepairerBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/ItemRepairerBlockEntity.kt new file mode 100644 index 000000000..9c423d202 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/ItemRepairerBlockEntity.kt @@ -0,0 +1,290 @@ +package ru.dbotthepony.mc.otm.block.entity.matter + +import net.minecraft.core.BlockPos +import net.minecraft.nbt.StringTag +import net.minecraft.resources.ResourceLocation +import net.minecraft.server.level.ServerLevel +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.Item +import net.minecraft.world.item.ItemStack +import net.minecraft.world.level.Level +import net.minecraft.world.level.block.state.BlockState +import net.minecraftforge.common.ForgeConfigSpec +import net.minecraftforge.common.ForgeConfigSpec.ConfigValue +import net.minecraftforge.registries.ForgeRegistries +import ru.dbotthepony.mc.otm.block.entity.MatteryPoweredBlockEntity +import ru.dbotthepony.mc.otm.capability.FlowDirection +import ru.dbotthepony.mc.otm.capability.MatteryCapability +import ru.dbotthepony.mc.otm.capability.energy.BlockEnergyStorageImpl +import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage +import ru.dbotthepony.mc.otm.capability.matter.IMatterStorage +import ru.dbotthepony.mc.otm.capability.matter.MatterStorageImpl +import ru.dbotthepony.mc.otm.config.ConciseBalanceValues +import ru.dbotthepony.mc.otm.container.HandlerFilter +import ru.dbotthepony.mc.otm.container.MatteryContainer +import ru.dbotthepony.mc.otm.core.math.Decimal +import ru.dbotthepony.mc.otm.core.math.DecimalConfigValue +import ru.dbotthepony.mc.otm.core.math.defineDecimal +import ru.dbotthepony.mc.otm.core.registryName +import ru.dbotthepony.mc.otm.core.util.WriteOnce +import ru.dbotthepony.mc.otm.graph.Graph6Node +import ru.dbotthepony.mc.otm.graph.matter.IMatterGraphNode +import ru.dbotthepony.mc.otm.graph.matter.MatterNetworkGraph +import ru.dbotthepony.mc.otm.matter.IMatterValue +import ru.dbotthepony.mc.otm.matter.MatterManager +import ru.dbotthepony.mc.otm.menu.matter.ItemRepairerMenu +import ru.dbotthepony.mc.otm.registry.MBlockEntities +import ru.dbotthepony.mc.otm.registry.MNames + +class ItemRepairerBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryPoweredBlockEntity(MBlockEntities.ITEM_REPAIRER, blockPos, blockState), IMatterGraphNode { + val repairContainer = MatteryContainer(::containerChanged, 1).also(::addDroppableContainer) + + private var matterPerTick = Decimal.ZERO + private var progressPerTick = 0.0 + private var repairProgress = 0.0 + private var lastItem: Item? = null + + var canNotWork = false + + override val matterNode = Graph6Node(this) + val matter = MatterStorageImpl(::setChangedLight, FlowDirection.INPUT, ::CAPACITY) + val energy = WorkerEnergyStorage(::setChangedLight, ENERGY_VALUES) + + init { + exposeGlobally(MatteryCapability.MATTER, matter) + exposeGlobally(MatteryCapability.MATTER_NODE, this) + + savetables.stateful(::repairContainer) + savetables.stateful(::matter) + savetables.stateful(::energy) + + savetables.decimal(::matterPerTick) + savetables.double(::progressPerTick) + savetables.double(::repairProgress) + + savetables.Stateless(::lastItem, type = StringTag::class.java) + .withSerializer { it?.registryName?.toString()?.let(StringTag::valueOf) } + .withDeserializer { ResourceLocation.tryParse(it.asString)?.let { ForgeRegistries.ITEMS.getValue(it) } } + } + + val energyConfig = ConfigurableEnergy(energy) + val itemConfig = ConfigurableItemHandler( + input = repairContainer.handler(object : HandlerFilter { + override fun canInsert(slot: Int, stack: ItemStack): Boolean { + if (!stack.isRepairable || !stack.isDamaged) { + return false + } + + if (!ONLY_ANVIL) { + if (MatterManager.get(stack.item).hasMatterValue) { + return true + } + } + + return (matterNode.graph as MatterNetworkGraph?) + ?.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 false + } + }), + + output = repairContainer.handler(object : HandlerFilter { + override fun canInsert(slot: Int, stack: ItemStack): Boolean { + return false + } + + override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean { + return progressPerTick <= 0.0 || matterPerTick <= Decimal.ZERO + } + }) + ) + + override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu { + return ItemRepairerMenu(containerID, inventory, this) + } + + override fun getMatterHandler(): IMatterStorage { + return matter + } + + override fun setRemoved() { + super.setRemoved() + matterNode.destroy(::MatterNetworkGraph) + } + + override fun setLevel(level: Level) { + super.setLevel(level) + + if (level is ServerLevel) + MatterNetworkGraph.discoverFull(this, matterNode) + } + + private fun containerChanged() { + matterPerTick = Decimal.ZERO + progressPerTick = 0.0 + + val item = repairContainer[0] + + tickList.once { + if (lastItem != item.item) { + lastItem = item.item + repairProgress = 0.0 + } + + if (item.isEmpty || !item.isRepairable || !item.isDamaged) { + matterPerTick = Decimal.ZERO + progressPerTick = 0.0 + } else { + if (!ONLY_ANVIL) { + val matter = MatterManager.get(item.item) + + if (matter.hasMatterValue) { + progressPerTick = (item.maxDamage / matter.complexity) / DIVISOR + matterPerTick = (matter.matter / matter.complexity) / DIVISOR + return@once + } + } + + val found = (matterNode.graph as MatterNetworkGraph?)?.patterns?.filter { item.item.isValidRepairItem(item, ItemStack(it.item, 1)) }?.findFirst()?.orElse(null) + + if (found != null) { + @Suppress("name_shadowing") + val matter = MatterManager.get(found.item) * 2 + + progressPerTick = (item.maxDamage / matter.complexity) / DIVISOR + matterPerTick = (matter.matter / matter.complexity) / DIVISOR + } else { + matterPerTick = Decimal.ZERO + progressPerTick = 0.0 + } + } + } + } + + override fun tick() { + super.tick() + + if (!redstoneControl.isBlockedByRedstone) { + canNotWork = false + + val item = repairContainer[0] + + if (!item.isEmpty && matterPerTick.isPositive && progressPerTick > 0.0 && item.isRepairable && item.isDamaged) { + var progressPerTick = (repairProgress + progressPerTick).coerceAtMost(item.damageValue.toDouble()) - repairProgress + if (progressPerTick <= 0.0) return + + if (ENERGY_CONSUMPTION.isPositive) { + if (!energy.batteryLevel.isPositive) return + val multEnergy = ENERGY_CONSUMPTION * (progressPerTick / this.progressPerTick) + progressPerTick *= (energy.extractEnergy(multEnergy, true) / multEnergy).toDouble() + + if (progressPerTick <= 0.0) { + canNotWork = true + return + } + } + + if (matter.storedMatter < matterPerTick) { + val graph = matterNode.graph as MatterNetworkGraph? + + if (graph != null) { + val toDrain = (matterPerTick * EXTRACT_TICKS.coerceAtMost(item.damageValue)).coerceAtLeast(Decimal.ZERO).coerceAtMost(matter.missingMatter) + matter.receiveMatterInner(graph.extractMatter(toDrain, false), false) + } + } + + val toDrain = matterPerTick * (progressPerTick / this.progressPerTick) + val drain = matter.extractMatterInner(toDrain, true) + if (!drain.isPositive) { + canNotWork = true + return + } + + progressPerTick *= (drain / toDrain).toDouble() + if (progressPerTick <= 0.0) { + canNotWork = true + return + } + + if (FAILURE_CHANCE <= 0.0 || level!!.random.nextDouble() >= FAILURE_CHANCE) + repairProgress += progressPerTick + + energy.extractEnergy(ENERGY_CONSUMPTION * (progressPerTick / this.progressPerTick), false) + matter.extractMatterInner(matterPerTick * (progressPerTick / this.progressPerTick), false) + + if (repairProgress >= 1.0) { + item.damageValue = (item.damageValue - repairProgress.toInt()).coerceAtLeast(0) + repairProgress %= 1.0 + } + } + } + } + + companion object { + val CAPACITY get() = _CAPACITY.get() + val ENERGY_CONSUMPTION get() = _ENERGY_CONSUMPTION.get() + val DIVISOR: Double get() = _DIVISOR.get() + val EXTRACT_TICKS: Int get() = _EXTRACT_TICKS.get() + val FAILURE_CHANCE: Double get() = _FAILURE_CHANCE.get() + val ONLY_ANVIL: Boolean get() = _ONLY_ANVIL.get() + + private var _CAPACITY: DecimalConfigValue by WriteOnce() + private var _ENERGY_CONSUMPTION: DecimalConfigValue by WriteOnce() + private var _DIVISOR: ConfigValue by WriteOnce() + private var _FAILURE_CHANCE: ConfigValue by WriteOnce() + private var _EXTRACT_TICKS: ConfigValue by WriteOnce() + private var _ONLY_ANVIL: ConfigValue by WriteOnce() + + var ENERGY_VALUES: ConciseBalanceValues by WriteOnce() + private set + + fun registerConfig(builder: ForgeConfigSpec.Builder) { + builder.push(MNames.ITEM_REPAIRER) + + ENERGY_VALUES = BlockEnergyStorageImpl.makeConfigEntry(builder, capacity = Decimal(400_000), throughput = Decimal(2_000)) + + _CAPACITY = builder.defineDecimal("matterCapacity", Decimal(4_000), Decimal.ONE_TENTH) + _ENERGY_CONSUMPTION = builder.defineDecimal("energyConsumption", Decimal(600), Decimal.ONE) + + _ONLY_ANVIL = builder + .comment("Force repairing only by matter value of anvil materials") + .comment("Doesn't make logical sense but might be good for balancing") + .define("onlyAnvil", false) + + _EXTRACT_TICKS = builder + .comment("How much ticks of matter work will be attempted to be extracted from matter network") + .comment("on matter starvation") + .comment("---") + .comment("This is a performance tuning value, do not change it if you don't know what you are doing!") + .defineInRange("extractTicks", 200, 1, Int.MAX_VALUE) + + _FAILURE_CHANCE = builder + .comment("Chance that replication will fail at any given tick") + .comment("In event of failure repair tick is wasted, wasting resources") + .defineInRange("failureChance", 0.01, 0.0, 0.99) + + _DIVISOR = builder + .comment("Magnitute of **slowdown** of repairer.") + .comment("If this value is 1, repairer will repair item from 0% to 100% in COMPLEXITY (of tool) ticks") + .comment("(or twice the complexity of anvil repair ingredients if tool itself has no matter value)") + .comment("---") + .comment("If value is smaller than 1, repairer will repair items faster") + .comment("If value is bigger than 1, repairer will repair items slower") + .defineInRange("divisor", 3.0, 0.1, Double.MAX_VALUE) + + builder.pop() + } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/MatterBottlerBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/MatterBottlerBlockEntity.kt index a92b0ed2c..697386f75 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/MatterBottlerBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/MatterBottlerBlockEntity.kt @@ -317,7 +317,5 @@ class MatterBottlerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : builder.pop() } - - const val IS_BOTTLING_KEY = "isBottling" } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/matter/ItemRepairerBlock.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/matter/ItemRepairerBlock.kt new file mode 100644 index 000000000..471b7d7ed --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/matter/ItemRepairerBlock.kt @@ -0,0 +1,24 @@ +package ru.dbotthepony.mc.otm.block.matter + +import net.minecraft.core.BlockPos +import net.minecraft.world.level.Level +import net.minecraft.world.level.block.EntityBlock +import net.minecraft.world.level.block.entity.BlockEntity +import net.minecraft.world.level.block.entity.BlockEntityTicker +import net.minecraft.world.level.block.entity.BlockEntityType +import net.minecraft.world.level.block.state.BlockState +import ru.dbotthepony.mc.otm.block.RotatableMatteryBlock +import ru.dbotthepony.mc.otm.block.entity.matter.ItemRepairerBlockEntity + +class ItemRepairerBlock : RotatableMatteryBlock(), EntityBlock { + override fun newBlockEntity(pPos: BlockPos, pState: BlockState): BlockEntity { + return ItemRepairerBlockEntity(pPos, pState) + } + + override fun getTicker(pLevel: Level, pState: BlockState, pBlockEntityType: BlockEntityType): BlockEntityTicker? { + if (pLevel.isClientSide) + return null + + return BlockEntityTicker { _, _, _, pBlockEntity -> if (pBlockEntity is ItemRepairerBlockEntity) pBlockEntity.tick() } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/MatteryPlayerCapability.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/MatteryPlayerCapability.kt index 276c8d165..fcd6c734b 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/MatteryPlayerCapability.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/MatteryPlayerCapability.kt @@ -1315,11 +1315,13 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial return } - val iterator = inventory.player.matteryPlayer?.exoPackContainer?.iterator()?.nonEmpty() ?: return + val iterator = inventory.player.matteryPlayer?.exoPackContainer?.iterator() ?: return for (item in iterator) { - inventory.player.drop(item, true, false) - iterator.remove() + if (!item.isEmpty) { + inventory.player.drop(item, true, false) + iterator.remove() + } } } @@ -1338,10 +1340,10 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial return } - val iterator = player.matteryPlayer?.exoPackContainer?.iterator()?.nonEmpty() ?: return + val iterator = player.matteryPlayer?.exoPackContainer?.iterator() ?: return for (item in iterator) { - if (hasVanishingCurse(item)) { + if (!item.isEmpty && hasVanishingCurse(item)) { iterator.remove() } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/matter/ItemRepairerScreen.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/matter/ItemRepairerScreen.kt new file mode 100644 index 000000000..f4ceb099c --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/matter/ItemRepairerScreen.kt @@ -0,0 +1,26 @@ +package ru.dbotthepony.mc.otm.client.screen.matter + +import net.minecraft.network.chat.Component +import net.minecraft.world.entity.player.Inventory +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.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.menu.matter.ItemRepairerMenu + +class ItemRepairerScreen(menu: ItemRepairerMenu, inventory: Inventory, title: Component) : MatteryScreen(menu, inventory, title) { + override fun makeMainFrame(): FramePanel> { + val frame = super.makeMainFrame()!! + + val p = PowerGaugePanel(this, frame, menu.powerWidget, LEFT_MARGIN, GAUGE_TOP_WITH_SLOT) + MatterGaugePanel(this, frame, menu.matterWidget, LEFT_MARGIN + p.width, GAUGE_TOP_WITH_SLOT) + + BatterySlotPanel(this, frame, menu.batterySlot, LEFT_MARGIN, SLOT_TOP_UNDER_GAUGE) + + SlotPanel(this, frame, menu.slot, 80f, PROGRESS_SLOT_TOP) + + return frame + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/config/MachinesConfig.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/config/MachinesConfig.kt index c80357cd2..b8b6213ba 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/config/MachinesConfig.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/config/MachinesConfig.kt @@ -1,5 +1,6 @@ package ru.dbotthepony.mc.otm.config +import ru.dbotthepony.mc.otm.block.entity.matter.ItemRepairerBlockEntity import ru.dbotthepony.mc.otm.block.entity.matter.MatterBottlerBlockEntity import ru.dbotthepony.mc.otm.block.entity.matter.MatterDecomposerBlockEntity import ru.dbotthepony.mc.otm.block.entity.matter.MatterRecyclerBlockEntity @@ -20,6 +21,7 @@ object MachinesConfig : AbstractConfig("machines") { MatterReplicatorBlockEntity.registerConfig(builder) MatterScannerBlockEntity.registerConfig(builder) MatterDecomposerBlockEntity.registerConfig(builder) + ItemRepairerBlockEntity.registerConfig(builder) } val PLATE_PRESS = BlockEnergyStorageImpl.makeConfigEntry(builder, MNames.PLATE_PRESS) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/collect/ItemStackIterators.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/collect/ItemStackIterators.kt index f3da0a85d..ea38e5fec 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/collect/ItemStackIterators.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/collect/ItemStackIterators.kt @@ -2,5 +2,4 @@ package ru.dbotthepony.mc.otm.core.collect import net.minecraft.world.item.ItemStack -fun Iterator.nonEmpty() = PredicateIterator(this) { !it.isEmpty } -fun MutableIterator.nonEmpty() = MutablePredicateIterator(this) { !it.isEmpty } +fun Iterator.nonEmpty() = FilteredIterator(this) { !it.isEmpty } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/collect/PredicateIterator.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/collect/PredicateIterator.kt deleted file mode 100644 index 631bd998b..000000000 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/collect/PredicateIterator.kt +++ /dev/null @@ -1,99 +0,0 @@ -package ru.dbotthepony.mc.otm.core.collect - -import java.util.function.Consumer -import java.util.function.Predicate - -class PredicateIterator : Iterator { - private val parent: Iterator - private val predicate: Predicate - private val consumer: Consumer? - - constructor(parent: Iterator, predicate: Predicate) { - this.parent = parent - this.predicate = predicate - this.consumer = null - } - - constructor(parent: Iterator, predicate: Predicate, consumer: Consumer) { - this.parent = parent - this.predicate = predicate - this.consumer = consumer - } - - private var foundValue: Any? = Companion - - var once: Boolean = false - private set - - override fun hasNext(): Boolean { - if (foundValue === Companion) { - while (parent.hasNext()) { - val next = parent.next() - - if (predicate.test(next)) { - foundValue = next - once = true - return true - } - } - - return false - } - - return true - } - - @Suppress("unchecked_cast") - override fun next(): T { - if (!hasNext()) { - throw NoSuchElementException() - } - - val foundValue = foundValue - - if (foundValue === Companion) { - throw ConcurrentModificationException() - } - - this.foundValue = Companion - consumer?.accept(foundValue as T) - return foundValue as T - } - - private companion object -} - -fun Iterator.filter(condition: Predicate) = PredicateIterator(this, condition) - -class MutablePredicateIterator : MutableIterator { - private val parent: MutableIterator - private val predicateParent: PredicateIterator - - constructor(parent: MutableIterator, predicate: Predicate) { - this.parent = parent - this.predicateParent = PredicateIterator(parent, predicate) - } - - constructor(parent: MutableIterator, predicate: Predicate, consumer: Consumer) { - this.parent = parent - this.predicateParent = PredicateIterator(parent, predicate, consumer) - } - - override fun hasNext(): Boolean { - return predicateParent.hasNext() - } - - override fun next(): T { - return predicateParent.next() - } - - override fun remove() { - if (!predicateParent.once) { - throw NoSuchElementException() - } - - return parent.remove() - } -} - -fun MutableIterator.filter(condition: Predicate) = MutablePredicateIterator(this, condition) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/collect/StreamyIterator.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/collect/StreamyIterator.kt new file mode 100644 index 000000000..d0d195ba1 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/collect/StreamyIterator.kt @@ -0,0 +1,210 @@ +package ru.dbotthepony.mc.otm.core.collect + +import java.util.Optional +import java.util.function.BinaryOperator +import java.util.function.Predicate +import java.util.stream.Collector + +class FilteredIterator(private val parent: Iterator, private val predicate: Predicate) : Iterator { + private var foundValue: Any? = Companion + + override fun hasNext(): Boolean { + if (foundValue === Companion) { + while (parent.hasNext()) { + val next = parent.next() + + if (predicate.test(next)) { + foundValue = next + return true + } + } + + return false + } + + return true + } + + @Suppress("unchecked_cast") + override fun next(): T { + var foundValue = foundValue + + if (foundValue === Companion) { + if (!hasNext()) { + throw NoSuchElementException() + } + + foundValue = this.foundValue + + if (foundValue === Companion) { + throw ConcurrentModificationException() + } + } + + this.foundValue = Companion + return foundValue as T + } + + private companion object +} + +class MappingIterator(private val parent: Iterator, private val transform: (T) -> R) : Iterator { + override fun hasNext(): Boolean { + return parent.hasNext() + } + + override fun next(): R { + return transform.invoke(parent.next()) + } +} + +fun Iterator.filter(condition: Predicate): Iterator = FilteredIterator(this, condition) +fun Iterator.map(mapper: (T) -> R): Iterator = MappingIterator(this, mapper) +fun Iterator.map(mapper: java.util.function.Function): Iterator = MappingIterator(this, mapper::apply) + +fun Iterator.reduce(identity: T, reducer: (T, T) -> T): T { + var result = identity + + for (value in this) { + result = reducer.invoke(result, value) + } + + return result +} + +fun Iterator.reduce(identity: T, reducer: BinaryOperator): T = reduce(identity, reducer::apply) +fun Iterator.filterNotNull(): Iterator = filter { it != null } as Iterator + +fun Iterator.anyMatch(predicate: Predicate): Boolean { + for (value in this) { + if (predicate.test(value)) { + return true + } + } + + return false +} + +fun Iterator.allMatch(predicate: Predicate): Boolean { + for (value in this) { + if (!predicate.test(value)) { + return false + } + } + + return true +} + +fun Iterator.noneMatch(predicate: Predicate): Boolean { + for (value in this) { + if (predicate.test(value)) { + return false + } + } + + return true +} + +fun Iterator.collect(collector: Collector): R { + val accumulator = collector.accumulator() + val instance = collector.supplier().get() + + for (value in this) { + accumulator.accept(instance, value) + } + + return collector.finisher().apply(instance) +} + +fun Iterator.findFirst(): Optional { + if (hasNext()) { + return Optional.of(next()) + } + + return Optional.empty() +} + +fun Iterator.findAny() = findFirst() + +fun Iterator.limit(limit: Long): Iterator { + require(limit > 0) { "Invalid limit $limit" } + + return object : Iterator { + var found = 0L + + override fun hasNext(): Boolean { + return found < limit && this@limit.hasNext() + } + + override fun next(): T { + if (found >= limit) { + throw NoSuchElementException() + } + + return this@limit.next() + } + } +} + +fun Iterator.skip(skip: Long): Iterator { + require(skip > 0) { "Invalid skip $skip" } + + return object : Iterator { + var found = skip + + override fun hasNext(): Boolean { + while (this@skip.hasNext() && found > 0L) { + found-- + this@skip.next() + } + + return this@skip.hasNext() + } + + override fun next(): T { + if (!hasNext()) { + throw NoSuchElementException() + } + + return this@skip.next() + } + } +} + +inline fun Iterator.forEach(action: (T) -> Unit) { + for (value in this) { + action.invoke(value) + } +} + +fun Iterator.min(comparator: Comparator): Optional { + if (!hasNext()) { + return Optional.empty() + } + + var min = next() + + for (value in this) { + if (comparator.compare(min, value) > 0) { + min = value + } + } + + return Optional.of(min) +} + +fun Iterator.max(comparator: Comparator): Optional { + if (!hasNext()) { + return Optional.empty() + } + + var max = next() + + for (value in this) { + if (comparator.compare(max, value) < 0) { + max = value + } + } + + return Optional.of(max) +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/graph/matter/MatterNetworkGraph.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/graph/matter/MatterNetworkGraph.kt index 9cc19b1e8..8fea4f865 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/graph/matter/MatterNetworkGraph.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/graph/matter/MatterNetworkGraph.kt @@ -7,6 +7,7 @@ import net.minecraft.world.level.block.entity.BlockEntity import ru.dbotthepony.mc.otm.capability.FlowDirection import ru.dbotthepony.mc.otm.capability.MatteryCapability import ru.dbotthepony.mc.otm.capability.matter.* +import ru.dbotthepony.mc.otm.core.filterNotNull import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.graph.Abstract6Graph import ru.dbotthepony.mc.otm.graph.Graph6Node @@ -206,7 +207,7 @@ class MatterNetworkGraph : Abstract6Graph(), IMatterGraphListe } val patterns: Stream get() { - return Streams.concat(*nodes.mapNotNull { it.value.getPatternHandler()?.patterns }.toTypedArray()) + return nodes.stream().map { it.value.getPatternHandler()?.patterns }.filterNotNull().flatMap { it } } val patternCount: Long get() { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/matter/IMatterValue.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/matter/IMatterValue.kt index ab6fb81d8..17fa4e5c2 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/matter/IMatterValue.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/matter/IMatterValue.kt @@ -50,6 +50,8 @@ interface IMatterValue : Comparable { * ZERO */ companion object : IMatterValue { + val ZERO: IMatterValue get() = this + override val matter: Decimal get() = Decimal.ZERO override val complexity: Double diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/MatteryPoweredMenu.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/MatteryPoweredMenu.kt index 4156116c1..844cd97be 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/MatteryPoweredMenu.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/MatteryPoweredMenu.kt @@ -6,6 +6,7 @@ import ru.dbotthepony.mc.otm.block.entity.MatteryPoweredBlockEntity import ru.dbotthepony.mc.otm.menu.widget.LevelGaugeWidget import net.minecraft.world.SimpleContainer import ru.dbotthepony.mc.otm.block.entity.RedstoneSetting +import ru.dbotthepony.mc.otm.capability.matter.matter import ru.dbotthepony.mc.otm.capability.matteryEnergy import ru.dbotthepony.mc.otm.menu.input.EnumInputWithFeedback @@ -16,14 +17,11 @@ abstract class MatteryPoweredMenu protected constructor( tile: MatteryPoweredBlockEntity? = null ) : MatteryMenu(menuType, containerID, inventory, tile) { val powerWidget = LevelGaugeWidget(this, tile?.matteryEnergy) - val batterySlot: BatterySlot + val batterySlot = BatterySlot(tile?.batteryContainer ?: SimpleContainer(1), 0) val redstone = EnumInputWithFeedback(this, RedstoneSetting::class.java) init { - if (tile == null) { - batterySlot = BatterySlot(SimpleContainer(1), 0) - } else { - batterySlot = BatterySlot(tile.batteryContainer, 0) + if (tile != null) { redstone.with(tile.redstoneControl::redstoneSetting) } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/matter/ItemRepairerMenu.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/matter/ItemRepairerMenu.kt new file mode 100644 index 000000000..22899909e --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/matter/ItemRepairerMenu.kt @@ -0,0 +1,29 @@ +package ru.dbotthepony.mc.otm.menu.matter + +import net.minecraft.world.SimpleContainer +import net.minecraft.world.entity.player.Inventory +import net.minecraft.world.item.ItemStack +import ru.dbotthepony.mc.otm.block.entity.matter.ItemRepairerBlockEntity +import ru.dbotthepony.mc.otm.capability.matter.matter +import ru.dbotthepony.mc.otm.menu.MatteryPoweredMenu +import ru.dbotthepony.mc.otm.menu.MatterySlot +import ru.dbotthepony.mc.otm.menu.widget.LevelGaugeWidget +import ru.dbotthepony.mc.otm.registry.MMenus + +class ItemRepairerMenu( + containerId: Int, + inventory: Inventory, + tile: ItemRepairerBlockEntity? = null +) : MatteryPoweredMenu(MMenus.ITEM_REPAIER, containerId, inventory, tile) { + val matterWidget = LevelGaugeWidget(this, tile?.matter) + val slot = object : MatterySlot(tile?.repairContainer ?: SimpleContainer(1), 0) { + override fun mayPlace(itemStack: ItemStack): Boolean { + return itemStack.isRepairable && itemStack.isDamaged && super.mayPlace(itemStack) + } + } + + init { + addStorageSlot(slot) + addInventorySlots() + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlockEntities.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlockEntities.kt index 68fa57c9c..c59564892 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlockEntities.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlockEntities.kt @@ -57,6 +57,7 @@ object MBlockEntities { val ENERGY_SERVO: BlockEntityType by registry.register(MNames.ENERGY_SERVO) { BlockEntityType.Builder.of(::EnergyServoBlockEntity, MBlocks.ENERGY_SERVO).build(null) } val COBBLESTONE_GENERATOR: BlockEntityType by registry.register(MNames.COBBLESTONE_GENERATOR) { BlockEntityType.Builder.of(::CobblerBlockEntity, MBlocks.COBBLESTONE_GENERATOR).build(null) } val ESSENCE_STORAGE: BlockEntityType by registry.register(MNames.ESSENCE_STORAGE) { BlockEntityType.Builder.of(::EssenceStorageBlockEntity, MBlocks.ESSENCE_STORAGE).build(null) } + val ITEM_REPAIRER: BlockEntityType by registry.register(MNames.ITEM_REPAIRER) { BlockEntityType.Builder.of(::ItemRepairerBlockEntity, MBlocks.ITEM_REPAIRER).build(null) } val STORAGE_BUS: BlockEntityType by registry.register(MNames.STORAGE_BUS) { BlockEntityType.Builder.of(::StorageBusBlockEntity, MBlocks.STORAGE_BUS).build(null) } val STORAGE_IMPORTER: BlockEntityType by registry.register(MNames.STORAGE_IMPORTER) { BlockEntityType.Builder.of(::StorageImporterBlockEntity, MBlocks.STORAGE_IMPORTER).build(null) } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlocks.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlocks.kt index b18d5965b..07e803b92 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlocks.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlocks.kt @@ -48,6 +48,7 @@ import ru.dbotthepony.mc.otm.block.tech.PlatePressBlock import ru.dbotthepony.mc.otm.block.StorageCableBlock import ru.dbotthepony.mc.otm.block.decorative.EngineBlock import ru.dbotthepony.mc.otm.block.decorative.HoloSignBlock +import ru.dbotthepony.mc.otm.block.matter.ItemRepairerBlock import ru.dbotthepony.mc.otm.block.matter.MatterBottlerBlock import ru.dbotthepony.mc.otm.block.matter.MatterCapacitorBankBlock import ru.dbotthepony.mc.otm.block.matter.MatterDecomposerBlock @@ -93,6 +94,7 @@ object MBlocks { val ENERGY_SERVO: Block by registry.register(MNames.ENERGY_SERVO) { EnergyServoBlock() } val COBBLESTONE_GENERATOR: Block by registry.register(MNames.COBBLESTONE_GENERATOR) { CobblerBlock() } val ESSENCE_STORAGE: EssenceStorageBlock by registry.register(MNames.ESSENCE_STORAGE) { EssenceStorageBlock() } + val ITEM_REPAIRER: ItemRepairerBlock by registry.register(MNames.ITEM_REPAIRER) { ItemRepairerBlock() } val STORAGE_BUS: Block by registry.register(MNames.STORAGE_BUS) { StorageBusBlock() } val STORAGE_IMPORTER: Block by registry.register(MNames.STORAGE_IMPORTER) { StorageImporterBlock() } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MItems.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MItems.kt index ed3e6a8bb..11bca8c79 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MItems.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MItems.kt @@ -109,13 +109,22 @@ object MItems { } } + val ITEM_REPAIRER: BlockItem by registry.register(MNames.ITEM_REPAIRER) { + object : BlockItem(MBlocks.ITEM_REPAIRER, DEFAULT_PROPERTIES) { + override fun appendHoverText(p_40572_: ItemStack, p_40573_: Level?, p_40574_: MutableList, p_40575_: TooltipFlag) { + super.appendHoverText(p_40572_, p_40573_, p_40574_, p_40575_) + p_40574_.add(TranslatableComponent("$descriptionId.desc").withStyle(ChatFormatting.GRAY)) + } + } + } + val MACHINES = SupplierList( ::ANDROID_STATION, ::BATTERY_BANK, ::MATTER_DECOMPOSER, ::MATTER_CAPACITOR_BANK, ::MATTER_CABLE, ::PATTERN_STORAGE, ::MATTER_SCANNER, ::MATTER_PANEL, ::MATTER_REPLICATOR, ::MATTER_BOTTLER, ::ENERGY_COUNTER, ::CHEMICAL_GENERATOR, ::PLATE_PRESS, ::MATTER_RECYCLER, ::STORAGE_BUS, ::STORAGE_IMPORTER, ::STORAGE_EXPORTER, ::DRIVE_VIEWER, ::DRIVE_RACK, ::ITEM_MONITOR, ::STORAGE_CABLE, ::STORAGE_POWER_SUPPLIER, ::ENERGY_SERVO, - ::PHANTOM_ATTRACTOR, - ::GRAVITATION_STABILIZER, ::COBBLESTONE_GENERATOR, ::ESSENCE_STORAGE + ::PHANTOM_ATTRACTOR, ::GRAVITATION_STABILIZER, ::COBBLESTONE_GENERATOR, ::ESSENCE_STORAGE, + ::ITEM_REPAIRER ) val DEBUG_EXPLOSION_SMALL: Item by registry.register(MNames.DEBUG_EXPLOSION_SMALL) { BlockItem(MBlocks.DEBUG_EXPLOSION_SMALL, Item.Properties().stacksTo(64)) } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MMenus.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MMenus.kt index 3c3d70b9e..245456f38 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MMenus.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MMenus.kt @@ -10,6 +10,7 @@ import ru.dbotthepony.mc.otm.OverdriveThatMatters import ru.dbotthepony.mc.otm.client.screen.decorative.CargoCrateScreen import ru.dbotthepony.mc.otm.client.screen.decorative.HoloSignScreen import ru.dbotthepony.mc.otm.client.screen.decorative.MinecartCargoCrateScreen +import ru.dbotthepony.mc.otm.client.screen.matter.ItemRepairerScreen import ru.dbotthepony.mc.otm.client.screen.matter.MatterBottlerScreen import ru.dbotthepony.mc.otm.client.screen.matter.MatterCapacitorBankScreen import ru.dbotthepony.mc.otm.client.screen.matter.MatterDecomposerScreen @@ -36,6 +37,7 @@ import ru.dbotthepony.mc.otm.client.screen.tech.PlatePressScreen import ru.dbotthepony.mc.otm.menu.decorative.CargoCrateMenu import ru.dbotthepony.mc.otm.menu.decorative.HoloSignMenu import ru.dbotthepony.mc.otm.menu.decorative.MinecartCargoCrateMenu +import ru.dbotthepony.mc.otm.menu.matter.ItemRepairerMenu import ru.dbotthepony.mc.otm.menu.matter.MatterBottlerMenu import ru.dbotthepony.mc.otm.menu.matter.MatterCapacitorBankMenu import ru.dbotthepony.mc.otm.menu.matter.MatterDecomposerMenu @@ -85,6 +87,7 @@ object MMenus { val HOLO_SIGN: MenuType by registry.register(MNames.HOLO_SIGN) { MenuType(::HoloSignMenu) } val COBBLESTONE_GENERATOR: MenuType by registry.register(MNames.COBBLESTONE_GENERATOR) { MenuType(::CobblerMenu) } val ESSENCE_STORAGE: MenuType by registry.register(MNames.ESSENCE_STORAGE) { MenuType(::EssenceStorageMenu) } + val ITEM_REPAIER: MenuType by registry.register(MNames.ITEM_REPAIRER) { MenuType(::ItemRepairerMenu) } val STORAGE_BUS: MenuType<*> by registry.register(MNames.STORAGE_BUS) { MenuType(::StorageBusMenu) } val STORAGE_EXPORTER: MenuType<*> by registry.register(MNames.STORAGE_EXPORTER) { MenuType(::StorageExporterMenu) } @@ -125,6 +128,7 @@ object MMenus { MenuScreens.register(HOLO_SIGN, ::HoloSignScreen) MenuScreens.register(COBBLESTONE_GENERATOR, ::CobblerScreen) MenuScreens.register(ESSENCE_STORAGE, ::EssenceStorageScreen) + MenuScreens.register(ITEM_REPAIER, ::ItemRepairerScreen) } } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MNames.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MNames.kt index 02bfadbcd..205d773c2 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MNames.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MNames.kt @@ -23,6 +23,7 @@ object MNames { const val MATTER_PANEL = "matter_panel" const val MATTER_REPLICATOR = "matter_replicator" const val MATTER_BOTTLER = "matter_bottler" + const val ITEM_REPAIRER = "item_repairer" const val DRIVE_VIEWER = "drive_viewer" const val DRIVE_RACK = "drive_rack" const val ITEM_MONITOR = "item_monitor"