From e6758f1e271081a39edc6084677654ffc25b997f Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Thu, 17 Aug 2023 22:26:43 +0700 Subject: [PATCH] Matter entangler, ingredient matrix, shadow containers, quantum battery recipes, more JEI compat --- .../ru/dbotthepony/mc/otm/datagen/DataGen.kt | 2 + .../mc/otm/datagen/lang/English.kt | 3 + .../mc/otm/datagen/lang/Russian.kt | 1 + .../mc/otm/datagen/loot/LootTablesData.kt | 1 + .../datagen/recipes/MatterEntanglerRecipes.kt | 43 +++++ .../dbotthepony/mc/otm/datagen/tags/Tags.kt | 1 + .../dbotthepony/mc/otm/block/entity/Jobs.kt | 9 +- .../entity/decorative/PainterBlockEntity.kt | 2 +- .../matter/MatterEntanglerBlockEntity.kt | 157 ++++++++++++++++ .../otm/block/matter/MatterEntanglerBlock.kt | 22 +++ .../ru/dbotthepony/mc/otm/capability/Ext.kt | 2 + .../client/screen/ExopackInventoryScreen.kt | 3 +- .../mc/otm/client/screen/MatteryScreen.kt | 10 +- .../{tech => decorative}/PainterScreen.kt | 4 +- .../screen/matter/MatterEntanglerScreen.kt | 75 ++++++++ .../otm/client/screen/panels/EditablePanel.kt | 4 + .../client/screen/tech/EnergyCounterScreen.kt | 2 +- .../client/screen/widget/MatterGaugePanel.kt | 42 +++-- .../dbotthepony/mc/otm/compat/jei/Gauges.kt | 50 ++++++ .../mc/otm/compat/jei/JEIPlugin.kt | 4 + .../jei/MatterEntanglerRecipeCategory.kt | 91 ++++++++++ .../otm/compat/jei/PainterRecipeCategory.kt | 4 +- .../mc/otm/config/MachinesConfig.kt | 8 + .../ru/dbotthepony/mc/otm/container/Ext.kt | 12 ++ .../mc/otm/container/MatteryContainer.kt | 68 ++++++- .../otm/container/MatteryCraftingContainer.kt | 22 +++ .../mc/otm/container/ShadowContainer.kt | 48 +++++ .../otm/container/ShadowCraftingContainer.kt | 33 ++++ .../mc/otm/core/collect/StreamyIterators.kt | 31 ++++ .../mc/otm/data/Codec2RecipeSerializer.kt | 109 ++++++----- .../mc/otm/data/IngredientMatrixCodec.kt | 111 ++++++++++++ .../dbotthepony/mc/otm/item/IQuantumLinked.kt | 7 + .../mc/otm/item/QuantumBatteryItem.kt | 10 +- .../mc/otm/menu/ExopackInventoryMenu.kt | 20 +-- .../ru/dbotthepony/mc/otm/menu/MatteryMenu.kt | 28 +-- .../ru/dbotthepony/mc/otm/menu/Slots.kt | 3 +- .../mc/otm/menu/decorative/FluidTankMenu.kt | 5 +- .../menu/{tech => decorative}/PainterMenu.kt | 2 +- .../otm/menu/matter/MatterDecomposerMenu.kt | 12 +- .../mc/otm/menu/matter/MatterEntanglerMenu.kt | 130 ++++++++++++++ .../mc/otm/menu/matter/MatterPanelMenu.kt | 8 +- .../otm/menu/matter/MatterReplicatorMenu.kt | 6 +- .../mc/otm/menu/storage/DriveViewerMenu.kt | 4 +- .../mc/otm/menu/storage/ItemMonitorMenu.kt | 12 +- .../mc/otm/menu/tech/AndroidStationMenu.kt | 4 +- .../mc/otm/menu/tech/CobblerMenu.kt | 4 +- .../mc/otm/menu/tech/EssenceStorageMenu.kt | 4 +- .../mc/otm/menu/tech/PlatePressMenu.kt | 4 +- .../mc/otm/menu/tech/PoweredFurnaceMenu.kt | 4 +- .../mc/otm/menu/tech/TwinPlatePressMenu.kt | 4 +- .../mc/otm/recipe/IMatteryRecipe.kt | 41 +++++ .../mc/otm/recipe/IngredientMatrix.kt | 151 ++++++++++++++++ .../mc/otm/recipe/MatterEntanglerRecipe.kt | 170 ++++++++++++++++++ .../mc/otm/recipe/PainterRecipe.kt | 2 +- .../mc/otm/recipe/PlatePressRecipe.kt | 2 +- .../mc/otm/registry/MBlockEntities.kt | 1 + .../ru/dbotthepony/mc/otm/registry/MBlocks.kt | 2 + .../ru/dbotthepony/mc/otm/registry/MItems.kt | 3 +- .../ru/dbotthepony/mc/otm/registry/MMenus.kt | 8 +- .../ru/dbotthepony/mc/otm/registry/MNames.kt | 1 + .../dbotthepony/mc/otm/registry/MRecipes.kt | 6 + 61 files changed, 1480 insertions(+), 152 deletions(-) create mode 100644 src/data/kotlin/ru/dbotthepony/mc/otm/datagen/recipes/MatterEntanglerRecipes.kt create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/MatterEntanglerBlockEntity.kt create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/block/matter/MatterEntanglerBlock.kt rename src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/{tech => decorative}/PainterScreen.kt (97%) create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/matter/MatterEntanglerScreen.kt create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/compat/jei/Gauges.kt create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/compat/jei/MatterEntanglerRecipeCategory.kt create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/container/MatteryCraftingContainer.kt create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/container/ShadowContainer.kt create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/container/ShadowCraftingContainer.kt create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/data/IngredientMatrixCodec.kt create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/item/IQuantumLinked.kt rename src/main/kotlin/ru/dbotthepony/mc/otm/menu/{tech => decorative}/PainterMenu.kt (98%) create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/menu/matter/MatterEntanglerMenu.kt create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/recipe/IMatteryRecipe.kt create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/recipe/IngredientMatrix.kt create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/recipe/MatterEntanglerRecipe.kt diff --git a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/DataGen.kt b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/DataGen.kt index 4f9fb145e..c148d0f68 100644 --- a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/DataGen.kt +++ b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/DataGen.kt @@ -48,6 +48,7 @@ import ru.dbotthepony.mc.otm.datagen.models.addBlockModels import ru.dbotthepony.mc.otm.datagen.recipes.addBlastingRecipes import ru.dbotthepony.mc.otm.datagen.recipes.addCraftingTableRecipes import ru.dbotthepony.mc.otm.datagen.recipes.addDecorativesRecipes +import ru.dbotthepony.mc.otm.datagen.recipes.addMatterEntanglerRecipes import ru.dbotthepony.mc.otm.datagen.recipes.addPlatePressRecipes import ru.dbotthepony.mc.otm.datagen.recipes.addShapelessRecipes import ru.dbotthepony.mc.otm.datagen.recipes.addOreSmeltingRecipes @@ -564,6 +565,7 @@ object DataGen { addShapelessRecipes(consumer) addOreSmeltingRecipes(consumer) addPainterRecipes(consumer) + addMatterEntanglerRecipes(consumer) } addPlatePressRecipes(recipeProvider) 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 b8efe97d4..552ad9e19 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 @@ -432,6 +432,7 @@ private fun blocks(provider: MatteryLanguageProvider) { add(MBlocks.DEV_CHEST, "Dev Chest") add(MBlocks.DEV_CHEST, "desc", "Contains all items present in game") add(MBlocks.PAINTER, "Painting Table") + add(MBlocks.MATTER_ENTANGLER, "Matter Entangler") add(MBlocks.FLUID_TANK, "Fluid Tank") add(MBlocks.FLUID_TANK, "named", "Fluid Tank (%s)") @@ -713,6 +714,8 @@ private fun gui(provider: MatteryLanguageProvider) { with(provider.english) { gui("quicksearch", "Quick search...") + gui("energy_required", "Energy required: %s") + gui("insert_priority", "Insert priority") gui("extract_priority", "Extract priority") gui("increase", "Increase") 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 6bc478530..6aea07d4d 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 @@ -434,6 +434,7 @@ private fun blocks(provider: MatteryLanguageProvider) { add(MBlocks.DEV_CHEST, "Сундук разработчика") add(MBlocks.DEV_CHEST, "desc", "Хранит все предметы, которые есть в игре") add(MBlocks.PAINTER, "Стол маляра") + add(MBlocks.MATTER_ENTANGLER, "Квантовый запутыватель материи") add(MBlocks.FLUID_TANK, "Жидкостный бак") add(MBlocks.FLUID_TANK, "named", "Жидкостный бак (%s)") 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 b21b8f15a..f3a46e108 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 @@ -134,6 +134,7 @@ fun addLootTables(lootTables: LootTables) { lootTables.tile(MBlocks.MATTER_RECONSTRUCTOR) lootTables.tile(MBlocks.FLUID_TANK) lootTables.tile(MBlocks.PAINTER) + lootTables.tile(MBlocks.MATTER_ENTANGLER) lootTables.tile(MBlocks.ENERGY_SERVO) lootTables.tile(MBlocks.ENERGY_COUNTER) diff --git a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/recipes/MatterEntanglerRecipes.kt b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/recipes/MatterEntanglerRecipes.kt new file mode 100644 index 000000000..dc20725d1 --- /dev/null +++ b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/recipes/MatterEntanglerRecipes.kt @@ -0,0 +1,43 @@ +package ru.dbotthepony.mc.otm.datagen.recipes + +import net.minecraft.data.recipes.FinishedRecipe +import net.minecraft.world.item.ItemStack +import net.minecraft.world.item.crafting.Ingredient +import net.minecraftforge.common.Tags +import ru.dbotthepony.mc.otm.core.math.Decimal +import ru.dbotthepony.mc.otm.datagen.modLocation +import ru.dbotthepony.mc.otm.recipe.IngredientMatrix +import ru.dbotthepony.mc.otm.recipe.MatterEntanglerRecipe +import ru.dbotthepony.mc.otm.registry.MItemTags +import ru.dbotthepony.mc.otm.registry.MItems +import java.util.function.Consumer + +fun addMatterEntanglerRecipes(consumer: Consumer) { + consumer.accept( + MatterEntanglerRecipe( + modLocation("quantum_capacitor"), + IngredientMatrix.of( + listOf(Ingredient.of(MItems.ELECTRIC_PARTS), Ingredient.of(MItemTags.GOLD_WIRES), Ingredient.of(MItems.ELECTRIC_PARTS)), + listOf(Ingredient.of(MItems.BATTERY_CAPACITOR), Ingredient.of(MItems.QUANTUM_TRANSCEIVER), Ingredient.of(MItems.BATTERY_CAPACITOR)), + listOf(Ingredient.of(MItemTags.TRITANIUM_PLATES), Ingredient.of(MItemTags.TRITANIUM_PLATES), Ingredient.of(MItemTags.TRITANIUM_PLATES)), + ), + Decimal(40), + 400.0, + ItemStack(MItems.QUANTUM_CAPACITOR, 2) + ).energetic().toFinished() + ) + + consumer.accept( + MatterEntanglerRecipe( + modLocation("quantum_battery"), + IngredientMatrix.of( + listOf(Ingredient.of(Tags.Items.STORAGE_BLOCKS_REDSTONE), Ingredient.of(MItemTags.GOLD_WIRES), Ingredient.of(Tags.Items.STORAGE_BLOCKS_REDSTONE)), + listOf(Ingredient.of(MItems.BATTERY_DENSE), Ingredient.of(MItems.QUANTUM_TRANSCEIVER), Ingredient.of(MItems.BATTERY_DENSE)), + listOf(Ingredient.of(MItemTags.TRITANIUM_PLATES), Ingredient.of(MItemTags.TRITANIUM_PLATES), Ingredient.of(MItemTags.TRITANIUM_PLATES)), + ), + Decimal(120), + 600.0, + ItemStack(MItems.QUANTUM_BATTERY, 2) + ).energetic().toFinished() + ) +} 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 26236c319..1563e0e75 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 @@ -162,6 +162,7 @@ fun addTags(tagsProvider: TagsProvider) { MBlocks.PLATE_PRESS, MBlocks.TWIN_PLATE_PRESS, MBlocks.MATTER_RECYCLER, + MBlocks.MATTER_ENTANGLER, MBlocks.POWERED_FURNACE, MBlocks.POWERED_SMOKER, diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/Jobs.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/Jobs.kt index 7ae32ce37..4ec6d1285 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/Jobs.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/Jobs.kt @@ -306,8 +306,10 @@ abstract class MachineJobEventLoop(val codec: Codec) : } } + private var lastTickWasError = false + val isUnableToProcess: Boolean - get() = throttleTicks > 0 + get() = throttleTicks > 0 || lastTickWasError val workProgress: Float get() { @@ -392,6 +394,8 @@ abstract class MachineJobEventLoop(val codec: Codec) : isIdling = false } + lastTickWasError = false + if (isIdling) { workingTicksAnim = 0 errorTicksAnim = 0 @@ -430,6 +434,7 @@ abstract class MachineJobEventLoop(val codec: Codec) : idleReason = IdleReason.POWER isIdling = true idleTicksAnim++ + lastTickWasError = true break } @@ -467,6 +472,7 @@ abstract class MachineJobEventLoop(val codec: Codec) : if (!status.success) { throttleTicks += status.throttleTicks + lastTickWasError = true if (status.idleReason != null) { idleReason = status.idleReason @@ -505,6 +511,7 @@ abstract class MachineJobEventLoop(val codec: Codec) : errorTicksAnim = 0 } else { throttleTicks += status.throttleTicks + lastTickWasError = true if (status.idleReason != null) { idleReason = status.idleReason diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/decorative/PainterBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/decorative/PainterBlockEntity.kt index 19dd30f42..6b1bee031 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/decorative/PainterBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/decorative/PainterBlockEntity.kt @@ -17,7 +17,7 @@ import ru.dbotthepony.mc.otm.core.immutableList import ru.dbotthepony.mc.otm.core.immutableMap import ru.dbotthepony.mc.otm.core.nbt.mapPresent import ru.dbotthepony.mc.otm.core.nbt.set -import ru.dbotthepony.mc.otm.menu.tech.PainterMenu +import ru.dbotthepony.mc.otm.menu.decorative.PainterMenu import ru.dbotthepony.mc.otm.registry.MBlockEntities import java.util.* diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/MatterEntanglerBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/MatterEntanglerBlockEntity.kt new file mode 100644 index 000000000..266eb09a4 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/MatterEntanglerBlockEntity.kt @@ -0,0 +1,157 @@ +package ru.dbotthepony.mc.otm.block.entity.matter + +import com.mojang.serialization.Codec +import com.mojang.serialization.codecs.RecordCodecBuilder +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.level.Level +import net.minecraft.world.level.block.state.BlockState +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.capability.FlowDirection +import ru.dbotthepony.mc.otm.capability.MatteryCapability +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.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.ShadowCraftingContainer +import ru.dbotthepony.mc.otm.container.UpgradeContainer +import ru.dbotthepony.mc.otm.core.math.Decimal +import ru.dbotthepony.mc.otm.data.DecimalCodec +import ru.dbotthepony.mc.otm.data.minRange +import ru.dbotthepony.mc.otm.graph.matter.MatterNode +import ru.dbotthepony.mc.otm.menu.matter.MatterEntanglerMenu +import ru.dbotthepony.mc.otm.registry.MBlockEntities +import ru.dbotthepony.mc.otm.registry.MRecipes + +class MatterEntanglerBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryWorkerBlockEntity(MBlockEntities.MATTER_ENTANGLER, blockPos, blockState, Job.CODEC) { + class Job(itemStack: ItemStack, val matter: Decimal, ticks: Double) : ItemJob(itemStack, ticks, MachinesConfig.MATTER_ENTANGLER.energyConsumption) { + val matterPerTick = matter / ticks + + companion object { + val CODEC: Codec = RecordCodecBuilder.create { + it.group( + ItemStack.CODEC.fieldOf("itemStack").forGetter(ItemJob::itemStack), + DecimalCodec.minRange(Decimal.ZERO).fieldOf("matter").forGetter(Job::matter), + Codec.DOUBLE.minRange(0.0).fieldOf("ticks").forGetter(ItemJob::ticks), + ).apply(it, ::Job) + } + } + } + + override val upgrades = UpgradeContainer(::markDirtyFast, 3, UpgradeType.BASIC_MATTER) + override val energy = ProfiledEnergyStorage(WorkerEnergyStorage(::energyLevelUpdated, upgrades.transform(MachinesConfig.MATTER_ENTANGLER))) + val matter = ProfiledMatterStorage(MatterStorageImpl(::markDirtyFast, FlowDirection.INPUT, upgrades.matterCapacity(MachinesConfig.MATTER_ENTANGLER::matterCapacity))) + val node = MatterNode() + + val energyConfig = ConfigurableEnergy(energy) + + val inputs = object : MatteryCraftingContainer(::itemContainerUpdated, 3, 3) { + override fun getMaxStackSize(): Int { + return 1 + } + } + + val output = object : MatteryContainer(::itemContainerUpdated, 1) { + override fun getMaxStackSize(slot: Int, itemStack: ItemStack): Int { + return Int.MAX_VALUE + } + } + + val itemConfig = ConfigurableItemHandler( + input = inputs.handler(object : HandlerFilter { + override fun canInsert(slot: Int, stack: ItemStack): Boolean { + val shadow = ShadowCraftingContainer.shadow(inputs, slot, stack) + + return (level ?: return false) + .recipeManager + .byType(MRecipes.MATTER_ENTANGLER) + .values + .any { it.preemptivelyMatches(shadow, level!!) } + } + + override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean { + return false + } + }), + output = output.handler(HandlerFilter.OnlyOut) + ) + + init { + savetables.stateful(::energy, ENERGY_KEY) + savetables.stateful(::matter, MATTER_STORAGE_KEY) + savetables.stateful(::upgrades) + savetables.stateful(::inputs) + savetables.stateful(::output) + + exposeGlobally(MatteryCapability.MATTER_NODE, node) + exposeGlobally(MatteryCapability.MATTER, matter) + } + + override fun setLevel(level: Level) { + super.setLevel(level) + node.discover(this) + } + + override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu? { + return MatterEntanglerMenu(containerID, inventory, this) + } + + override fun onJobTick(status: JobStatus, id: Int) { + 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) + } + + status.scale(matter.extractMatter(required, false) / required) + } + + override fun tick() { + super.tick() + + if (jobEventLoops[0].currentJob == null) { + matter.extractMatter(node.graph.receiveMatter(matter.storedMatter, false), false) + } + } + + override fun onJobFinish(status: JobStatus, id: Int) { + if (!output.fullyAddItem(status.job.itemStack)) { + status.noItem() + } + } + + override fun computeNextJob(id: Int): JobContainer { + if (!energy.batteryLevel.isPositive) + return JobContainer.noEnergy() + + val recipe = (level ?: return JobContainer.failure()) + .recipeManager + .byType(MRecipes.MATTER_ENTANGLER) + .values + .firstOrNull { it.matches(inputs, level!!) } ?: return JobContainer.noItem() + + val result = recipe.assemble(inputs, level!!.registryAccess()) + + inputs.forEach { it.shrink(1) } + inputs.setChanged() + + return JobContainer.success( + Job( + result, + recipe.matter, + recipe.ticks * MachinesConfig.MATTER_ENTANGLER.workTimeMultiplier + ) + ) + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/matter/MatterEntanglerBlock.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/matter/MatterEntanglerBlock.kt new file mode 100644 index 000000000..23427703f --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/matter/MatterEntanglerBlock.kt @@ -0,0 +1,22 @@ +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.MatterEntanglerBlockEntity + +class MatterEntanglerBlock : RotatableMatteryBlock(), EntityBlock { + override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState): BlockEntity { + return MatterEntanglerBlockEntity(blockPos, blockState) + } + + override fun getTicker(p_153212_: Level, p_153213_: BlockState, p_153214_: BlockEntityType): BlockEntityTicker? { + if (p_153212_.isClientSide) return null + return BlockEntityTicker { _, _, _, tile -> if (tile is MatterEntanglerBlockEntity) tile.tick() } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/Ext.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/Ext.kt index c0bde449d..5c9af7a51 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/Ext.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/Ext.kt @@ -230,6 +230,8 @@ fun Player.items(includeCosmetics: Boolean = true): Iterator { matteryPlayer?.let { if (it.hasExopack) { iterators.add(it.exopackContainer.iterator()) + iterators.add(it.exopackEnergy.parent.iterator()) + iterators.add(it.exopackChargeSlots.iterator()) } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/ExopackInventoryScreen.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/ExopackInventoryScreen.kt index b978a9521..2dad7fb4e 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/ExopackInventoryScreen.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/ExopackInventoryScreen.kt @@ -1,6 +1,5 @@ package ru.dbotthepony.mc.otm.client.screen -import com.mojang.blaze3d.platform.InputConstants import net.minecraft.client.gui.screens.inventory.InventoryScreen import net.minecraft.world.item.ItemStack import net.minecraft.world.item.Items @@ -276,7 +275,7 @@ class ExopackInventoryScreen(menu: ExopackInventoryMenu) : MatteryScreen(menu: T, inventory: Inventory, tit if (menu.playerExoSuitSlots.isEmpty()) { inventoryFrame = FramePanel>(this, null, 0f, 0f, INVENTORY_FRAME_WIDTH, INVENTORY_FRAME_HEIGHT, inventory.displayName).also(this::addPanel) inventoryFrame!!.makeHelpButton().addSlotFiltersHelp().also { - if (menu.ply.matteryPlayer?.hasExopack == true) + if (menu.player.matteryPlayer?.hasExopack == true) it.tooltips.add(TranslatableComponent("otm.gui.help.slot_charging")) } @@ -236,7 +236,7 @@ abstract class MatteryScreen(menu: T, inventory: Inventory, tit } else { inventoryFrame = FramePanel>(this, null, 0f, 0f, INVENTORY_FRAME_WIDTH_EXTENDED, BASE_INVENTORY_FRAME_HEIGHT + AbstractSlotPanel.SIZE * inventoryRows, inventory.displayName).also(this::addPanel) inventoryFrame!!.makeHelpButton().addSlotFiltersHelp().also { - if (menu.ply.matteryPlayer?.hasExopack == true) + if (menu.player.matteryPlayer?.hasExopack == true) it.tooltips.add(TranslatableComponent("otm.gui.help.slot_charging")) } @@ -421,7 +421,9 @@ abstract class MatteryScreen(menu: T, inventory: Inventory, tit } } - if (matter != null) { + if (profiledMatter != null) { + ProfiledMatterGaugePanel(this, gauges, profiledMatter).dock = Dock.LEFT + } else if (matter != null) { MatterGaugePanel(this, gauges, matter).dock = Dock.LEFT } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/tech/PainterScreen.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/decorative/PainterScreen.kt similarity index 97% rename from src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/tech/PainterScreen.kt rename to src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/decorative/PainterScreen.kt index b85fe86df..8ef2980bd 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/tech/PainterScreen.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/decorative/PainterScreen.kt @@ -1,4 +1,4 @@ -package ru.dbotthepony.mc.otm.client.screen.tech +package ru.dbotthepony.mc.otm.client.screen.decorative import net.minecraft.client.gui.GuiGraphics import net.minecraft.network.chat.Component @@ -27,7 +27,7 @@ import ru.dbotthepony.mc.otm.client.screen.panels.util.ScrollableCanvasPanel import ru.dbotthepony.mc.otm.core.TextComponent import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.core.math.RGBAColor -import ru.dbotthepony.mc.otm.menu.tech.PainterMenu +import ru.dbotthepony.mc.otm.menu.decorative.PainterMenu class PainterScreen(menu: PainterMenu, inventory: Inventory, title: Component) : MatteryScreen(menu, inventory, title) { inner class Bar(parent: EditablePanel<*>, val dye: DyeColor) : EditablePanel(this@PainterScreen, parent, width = 5f) { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/matter/MatterEntanglerScreen.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/matter/MatterEntanglerScreen.kt new file mode 100644 index 000000000..1141205bf --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/matter/MatterEntanglerScreen.kt @@ -0,0 +1,75 @@ +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.Dock +import ru.dbotthepony.mc.otm.client.screen.panels.DockResizeMode +import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel +import ru.dbotthepony.mc.otm.client.screen.panels.FramePanel +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.util.GridPanel +import ru.dbotthepony.mc.otm.client.screen.widget.ProgressGaugePanel +import ru.dbotthepony.mc.otm.menu.matter.MatterEntanglerMenu + +class MatterEntanglerScreen(menu: MatterEntanglerMenu, inventory: Inventory, title: Component) : MatteryScreen(menu, inventory, title) { + override fun makeMainFrame(): FramePanel> { + val frame = FramePanel(this, DEFAULT_FRAME_WIDTH, 110f, title) + + makeBars(frame, profiledEnergy = menu.profiledEnergy, profiledMatter = menu.profiledMatter, batterySlot = menu.batterySlot) + + GridPanel.slots(this, frame, 3, 3).also { + it.dock = Dock.LEFT + it.dockLeft = 20f + it.dockRight = 4f + it.dockResize = DockResizeMode.NONE + + for (slot in menu.inputs) + SlotPanel(this, it, slot) + } + + ProgressGaugePanel(this, frame, menu.progress).also { + it.dock = Dock.LEFT + it.dockHorizontal(4f) + it.dockResize = DockResizeMode.NONE + } + + SlotPanel(this, frame, menu.outputs[0]).also { + it.dock = Dock.LEFT + it.dockHorizontal(4f) + it.dockResize = DockResizeMode.NONE + } + + val strip = EditablePanel(this, frame, height = AbstractSlotPanel.SIZE) + strip.dock = Dock.BOTTOM + strip.dockTop = 4f + strip.childrenOrder = -1 + + SlotPanel(this, strip, menu.entanglingA).also { + it.dock = Dock.LEFT + it.dockLeft = 20f + } + + SlotPanel(this, strip, menu.entanglingB).also { + it.dock = Dock.LEFT + it.dockLeft = 4f + } + + SpritePanel(this, strip, ProgressGaugePanel.GAUGE_BACKGROUND).also { + it.dock = Dock.FILL + it.dockResize = DockResizeMode.NONE + } + + SlotPanel(this, strip, menu.entanglingC).also { + it.dock = Dock.RIGHT + it.dockRight = 20f + } + + DeviceControls(this, frame, redstoneConfig = menu.redstoneConfig, upgrades = menu.upgrades, energyConfig = menu.energyConfig, itemConfig = menu.itemConfig) + + return frame + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/EditablePanel.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/EditablePanel.kt index 55fbb498d..c09da8d5c 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/EditablePanel.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/EditablePanel.kt @@ -346,6 +346,10 @@ open class EditablePanel @JvmOverloads constructor( } } + fun dockHorizontal(value: Float) { + dockMargin = dockMargin.copy(left = value, right = value) + } + var dockTop: Float get() = dockMargin.top set(value) { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/tech/EnergyCounterScreen.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/tech/EnergyCounterScreen.kt index b8a2c2060..838d9e2a6 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/tech/EnergyCounterScreen.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/tech/EnergyCounterScreen.kt @@ -41,7 +41,7 @@ class EnergyCounterScreen(menu: EnergyCounterMenu, inventory: Inventory, title: label.dock = Dock.TOP } - if (!menu.ply.isSpectator) { + if (!menu.player.isSpectator) { val button = ButtonPanel(this, frame, 0f, 0f, 0f, 20f, TranslatableComponent("block.overdrive_that_matters.energy_counter.switch"), onPress = Runnable { menu.switchDirection.accept(null) }) button.dock = Dock.TOP button.setDockMargin(4f, 5f, 4f, 0f) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/widget/MatterGaugePanel.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/widget/MatterGaugePanel.kt index ec764b57e..4a94e9d9c 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/widget/MatterGaugePanel.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/widget/MatterGaugePanel.kt @@ -79,9 +79,28 @@ open class MatterGaugePanel @JvmOverloads constructor( } GAUGE_BACKGROUND.render(graphics) - val height = this.height * (1f - widget.percentage) if (widget.percentage > 0.01f) { + renderLevel(graphics, 0f, 0f, width, height, widget.percentage, wavesStrength) + } + } + + override fun innerRenderTooltips(graphics: GuiGraphics, mouseX: Float, mouseY: Float, partialTick: Float): Boolean { + if (isHovered) { + graphics.renderComponentTooltip(font, makeTooltip(), mouseX.toInt(), mouseY.toInt()) + return true + } + + return false + } + + companion object { + val GAUGE_BACKGROUND = WidgetLocation.VERTICAL_GAUGES.sprite(x = 18f, width = 9f) + val GAUGE_FOREGROUND = WidgetLocation.VERTICAL_GAUGES.sprite(x = 27f, width = 9f) + + fun renderLevel(graphics: GuiGraphics, x: Float, y: Float, width: Float, height: Float, percentage: Float, wavesStrength: Float = 0.5f) { + graphics.pose().pushPose() + graphics.pose().translate(x, y, 0f) RenderSystem.setShader(GameRenderer::getPositionTexShader) RenderSystem.enableBlend() RenderSystem.defaultBlendFunc() @@ -98,15 +117,15 @@ open class MatterGaugePanel @JvmOverloads constructor( builder.begin(VertexFormat.Mode.TRIANGLE_FAN, DefaultVertexFormat.POSITION_TEX) - builder.vertex(matrix, 0f, this.height, 0f).uv(u0, v1).endVertex() - builder.vertex(matrix, width, this.height, 0f).uv(u1, v1).endVertex() + builder.vertex(matrix, 0f, height, 0f).uv(u0, v1).endVertex() + builder.vertex(matrix, width, height, 0f).uv(u1, v1).endVertex() for (i in 4 downTo 0) { val sin = sin((System.currentTimeMillis() / 50L + i * 2L).toDouble() / 4.0).toFloat() * wavesStrength.coerceAtMost(4f) val cos = cos((System.currentTimeMillis() / 50L + i * 3L + 27L).toDouble() / 4.0).toFloat() * wavesStrength.coerceAtMost(4f) val thisX = (width * (i / 4f)) - val thisY = (height + sin + cos).coerceAtLeast(0f) + val thisY = (height * (1f - percentage) + sin + cos).coerceAtLeast(0f) builder.vertex(matrix, thisX, thisY, 0f).uv( GAUGE_FOREGROUND.partialU((i / 4f) * GAUGE_FOREGROUND.width), @@ -115,22 +134,9 @@ open class MatterGaugePanel @JvmOverloads constructor( } BufferUploader.drawWithShader(builder.end()) + graphics.pose().popPose() } } - - override fun innerRenderTooltips(graphics: GuiGraphics, mouseX: Float, mouseY: Float, partialTick: Float): Boolean { - if (isHovered) { - graphics.renderComponentTooltip(font, makeTooltip(), mouseX.toInt(), mouseY.toInt()) - return true - } - - return false - } - - companion object { - val GAUGE_BACKGROUND = WidgetLocation.VERTICAL_GAUGES.sprite(x = 18f, width = 9f) - val GAUGE_FOREGROUND = WidgetLocation.VERTICAL_GAUGES.sprite(x = 27f, width = 9f) - } } private fun formatLevel(a: Decimal, b: Decimal): Component { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jei/Gauges.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jei/Gauges.kt new file mode 100644 index 000000000..47c690e47 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jei/Gauges.kt @@ -0,0 +1,50 @@ +package ru.dbotthepony.mc.otm.compat.jei + +import net.minecraft.client.gui.GuiGraphics +import net.minecraft.network.chat.Component +import ru.dbotthepony.mc.otm.client.ShiftPressedCond +import ru.dbotthepony.mc.otm.client.screen.widget.MatterGaugePanel +import ru.dbotthepony.mc.otm.client.screen.widget.PowerGaugePanel +import ru.dbotthepony.mc.otm.core.TranslatableComponent +import ru.dbotthepony.mc.otm.core.math.Decimal +import ru.dbotthepony.mc.otm.core.util.formatMatter +import ru.dbotthepony.mc.otm.core.util.formatPower +import ru.dbotthepony.mc.otm.systemTime + +fun renderMatterGauge( + guiGraphics: GuiGraphics, + x: Float, + y: Float, + width: Float = MatterGaugePanel.GAUGE_BACKGROUND.width, + height: Float = MatterGaugePanel.GAUGE_BACKGROUND.height, + drainSpeed: Float = 1f, + wavesStrength: Float = drainSpeed * 2f, +) { + MatterGaugePanel.GAUGE_BACKGROUND.render(guiGraphics, x, y, width, height) + MatterGaugePanel.renderLevel(guiGraphics, x, y, width, height, 1f - ((systemTime.secondsF * drainSpeed) % 1f), wavesStrength) +} + +fun renderEnergyGauge( + guiGraphics: GuiGraphics, + x: Float, + y: Float, + width: Float = PowerGaugePanel.GAUGE_BACKGROUND.width, + height: Float = PowerGaugePanel.GAUGE_BACKGROUND.height, + drainSpeed: Float = 1f +) { + val perc = 1f - ((systemTime.secondsF * drainSpeed) % 1f) + PowerGaugePanel.GAUGE_BACKGROUND.render(guiGraphics, x, y, width, height) + PowerGaugePanel.GAUGE_FOREGROUND.renderPartial(guiGraphics, x, y + height * (1f - perc) - 1f, width, height * perc) +} + +fun matterGaugeTooltips(target: MutableList, matter: Decimal, mouseX: Double, mouseY: Double, x: Float, y: Float, width: Float = MatterGaugePanel.GAUGE_BACKGROUND.width, height: Float = MatterGaugePanel.GAUGE_BACKGROUND.height) { + if (mouseX in x .. (x + width - 1) && mouseY in y .. (y + height - 1)) { + target.add(TranslatableComponent("otm.gui.matter_panel.matter_required", matter.formatMatter(formatAsReadable = ShiftPressedCond))) + } +} + +fun energyGaugeTooltips(target: MutableList, energy: Decimal, mouseX: Double, mouseY: Double, x: Float, y: Float, width: Float = PowerGaugePanel.GAUGE_BACKGROUND.width, height: Float = PowerGaugePanel.GAUGE_BACKGROUND.height) { + if (mouseX in x .. (x + width - 1) && mouseY in y .. (y + height - 1)) { + target.add(TranslatableComponent("otm.gui.energy_required", energy.formatPower(formatAsReadable = ShiftPressedCond))) + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jei/JEIPlugin.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jei/JEIPlugin.kt index bec5cb7fb..fcc0617eb 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jei/JEIPlugin.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jei/JEIPlugin.kt @@ -69,14 +69,17 @@ class JEIPlugin : IModPlugin { registration.addRecipeCatalyst(ItemStack(MItems.POWERED_BLAST_FURNACE), RecipeTypes.BLASTING) registration.addRecipeCatalyst(ItemStack(MItems.POWERED_SMOKER), RecipeTypes.SMOKING) registration.addRecipeCatalyst(ItemStack(MItems.ExopackUpgrades.CRAFTING_UPGRADE), RecipeTypes.CRAFTING) + registration.addRecipeCatalyst(ItemStack(MItems.ITEM_MONITOR), RecipeTypes.CRAFTING) registration.addRecipeCatalyst(ItemStack(MItems.PLATE_PRESS), PlatePressRecipeCategory.recipeType) registration.addRecipeCatalyst(ItemStack(MItems.PAINTER), PainterRecipeCategory.recipeType) + registration.addRecipeCatalyst(ItemStack(MItems.MATTER_ENTANGLER), MatterEntanglerRecipeCategory.recipeType) } override fun registerCategories(registration: IRecipeCategoryRegistration) { helpers = registration.jeiHelpers registration.addRecipeCategories(PlatePressRecipeCategory) registration.addRecipeCategories(PainterRecipeCategory) + registration.addRecipeCategories(MatterEntanglerRecipeCategory) } override fun registerRecipes(registration: IRecipeRegistration) { @@ -84,6 +87,7 @@ class JEIPlugin : IModPlugin { registration.addRecipes(PlatePressRecipeCategory.recipeType, level.recipeManager.getAllRecipesFor(MRecipes.PLATE_PRESS).filter { !it.isIncomplete }) registration.addRecipes(PainterRecipeCategory.recipeType, level.recipeManager.getAllRecipesFor(MRecipes.PAINTER).filter { !it.isIncomplete }) + registration.addRecipes(MatterEntanglerRecipeCategory.recipeType, level.recipeManager.getAllRecipesFor(MRecipes.MATTER_ENTANGLER).filter { !it.isIncomplete }) } override fun registerRecipeTransferHandlers(registration: IRecipeTransferRegistration) { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jei/MatterEntanglerRecipeCategory.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jei/MatterEntanglerRecipeCategory.kt new file mode 100644 index 000000000..763f17658 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jei/MatterEntanglerRecipeCategory.kt @@ -0,0 +1,91 @@ +package ru.dbotthepony.mc.otm.compat.jei + +import mezz.jei.api.gui.builder.IRecipeLayoutBuilder +import mezz.jei.api.gui.drawable.IDrawable +import mezz.jei.api.gui.ingredient.IRecipeSlotsView +import mezz.jei.api.recipe.IFocusGroup +import mezz.jei.api.recipe.RecipeIngredientRole +import mezz.jei.api.recipe.RecipeType +import mezz.jei.api.recipe.category.IRecipeCategory +import net.minecraft.client.gui.GuiGraphics +import net.minecraft.network.chat.Component +import net.minecraft.world.item.ItemStack +import ru.dbotthepony.mc.otm.OverdriveThatMatters +import ru.dbotthepony.mc.otm.client.minecraft +import ru.dbotthepony.mc.otm.client.render.ItemStackIcon +import ru.dbotthepony.mc.otm.client.render.draw +import ru.dbotthepony.mc.otm.client.screen.panels.slot.AbstractSlotPanel +import ru.dbotthepony.mc.otm.client.screen.widget.ProgressGaugePanel +import ru.dbotthepony.mc.otm.config.MachinesConfig +import ru.dbotthepony.mc.otm.core.TranslatableComponent +import ru.dbotthepony.mc.otm.core.math.Decimal +import ru.dbotthepony.mc.otm.recipe.IMatterEntanglerRecipe +import ru.dbotthepony.mc.otm.registry.MItems +import ru.dbotthepony.mc.otm.registry.MNames + +object MatterEntanglerRecipeCategory : IRecipeCategory, IDrawable { + private val recipeType = RecipeType.create(OverdriveThatMatters.MOD_ID, MNames.MATTER_ENTANGLER, IMatterEntanglerRecipe::class.java) + + override fun getRecipeType(): RecipeType { + return recipeType + } + + override fun getTitle(): Component { + return MItems.MATTER_ENTANGLER.description + } + + override fun getBackground(): IDrawable { + return this + } + + override fun getWidth(): Int { + return 140 + } + + override fun getHeight(): Int { + return 60 + } + + private val icon = IGUIRenderable2IDrawable(ItemStackIcon(ItemStack(MItems.MATTER_ENTANGLER))) + + override fun getIcon(): IDrawable { + return icon + } + + override fun setRecipe(builder: IRecipeLayoutBuilder, recipe: IMatterEntanglerRecipe, focuses: IFocusGroup) { + for (x in 0 until recipe.ingredients.width) { + for (y in 0 until recipe.ingredients.height) { + builder.addSlot(RecipeIngredientRole.INPUT, 30 + x * 18, 4 + y * 18).addIngredients(recipe.ingredients[x, y]) + } + } + + builder.addSlot(RecipeIngredientRole.OUTPUT, 116, 18 + 4).addItemStack(recipe.result) + } + + override fun draw(guiGraphics: GuiGraphics, xOffset: Int, yOffset: Int) { + for (x in 0 until 3) { + for (y in 0 until 3) { + AbstractSlotPanel.SLOT_BACKGROUND.render(guiGraphics, xOffset + x * 18f + 29f, yOffset + y * 18f + 3f) + } + } + + AbstractSlotPanel.SLOT_BACKGROUND.render(guiGraphics, xOffset + 115f, yOffset + 18f + 3f) + ProgressGaugePanel.GAUGE_BACKGROUND.render(guiGraphics, xOffset + 89f, yOffset + 18f + 4f) + } + + override fun draw(recipe: IMatterEntanglerRecipe, recipeSlotsView: IRecipeSlotsView, guiGraphics: GuiGraphics, mouseX: Double, mouseY: Double) { + renderMatterGauge(guiGraphics, 13f, 6f, drainSpeed = (recipe.matter / Decimal(300)).toFloat()) + renderEnergyGauge(guiGraphics, 4f, 6f, drainSpeed = (recipe.ticks / 2000.0).toFloat()) + + guiGraphics.draw(minecraft.font, x = 85f, y = 45f, text = TranslatableComponent("otm.gui.recipe.ticks", recipe.ticks), drawShadow = true) + } + + override fun getTooltipStrings(recipe: IMatterEntanglerRecipe, recipeSlotsView: IRecipeSlotsView, mouseX: Double, mouseY: Double): MutableList { + val result = ArrayList() + + matterGaugeTooltips(result, recipe.matter, mouseX, mouseY, 13f, 6f) + energyGaugeTooltips(result, MachinesConfig.MATTER_ENTANGLER.energyConsumption * recipe.ticks * MachinesConfig.MATTER_ENTANGLER.workTimeMultiplier, mouseX, mouseY, 4f, 6f) + + return result + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jei/PainterRecipeCategory.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jei/PainterRecipeCategory.kt index f5d937bf8..bda128ad6 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jei/PainterRecipeCategory.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jei/PainterRecipeCategory.kt @@ -43,8 +43,10 @@ object PainterRecipeCategory : IRecipeCategory, IDrawable { return 56 } + private val icon = IGUIRenderable2IDrawable(ItemStackIcon(ItemStack(MItems.PAINTER))) + override fun getIcon(): IDrawable { - return IGUIRenderable2IDrawable(ItemStackIcon(ItemStack(MItems.PAINTER))) + return icon } override fun setRecipe(builder: IRecipeLayoutBuilder, recipe: PainterRecipe, focuses: IFocusGroup) { 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 4653d9e97..d0bc04a44 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/config/MachinesConfig.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/config/MachinesConfig.kt @@ -13,6 +13,14 @@ object MachinesConfig : AbstractConfig("machines") { energyConsumption = Decimal(15) ) + val MATTER_ENTANGLER = workerValues( + MNames.MATTER_ENTANGLER, + energyStorage = Decimal(40_000), + energyThroughput = Decimal(400), + energyConsumption = Decimal(120), + matterCapacity = Decimal(60), + ) + val MATTER_DECOMPOSER = workerValues( MNames.MATTER_DECOMPOSER, energyStorage = Decimal(100_000), diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/container/Ext.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/container/Ext.kt index 9b79113b9..fc69f47b8 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/container/Ext.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/container/Ext.kt @@ -7,6 +7,7 @@ import it.unimi.dsi.fastutil.ints.IntSet import it.unimi.dsi.fastutil.objects.Object2ObjectFunction import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap import net.minecraft.world.Container +import net.minecraft.world.inventory.CraftingContainer import net.minecraft.world.item.ItemStack import net.minecraft.world.item.enchantment.EnchantmentHelper.hasVanishingCurse import net.minecraftforge.fluids.capability.IFluidHandler @@ -245,3 +246,14 @@ fun Container.balance() { balance(IntArraySet(containerSize).also { for (i in 0 until containerSize) it.add(i) }, false) } + +operator fun CraftingContainer.get(column: Int, row: Int): ItemStack { + return getItem(column + row * width) +} + +operator fun CraftingContainer.get(column: Int, row: Int, flop: Boolean): ItemStack { + return if (flop) + get(width - column - 1, row) + else + get(column, row) +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/container/MatteryContainer.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/container/MatteryContainer.kt index 95d1624d8..43a135ca2 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/container/MatteryContainer.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/container/MatteryContainer.kt @@ -4,7 +4,8 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap import it.unimi.dsi.fastutil.ints.IntAVLTreeSet import it.unimi.dsi.fastutil.ints.IntArrayList import it.unimi.dsi.fastutil.ints.IntComparators -import it.unimi.dsi.fastutil.objects.ObjectIterators +import it.unimi.dsi.fastutil.ints.IntSpliterator +import it.unimi.dsi.fastutil.objects.ObjectSpliterators import net.minecraft.world.item.ItemStack import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.ListTag @@ -13,11 +14,16 @@ import net.minecraft.resources.ResourceLocation import net.minecraft.world.Container import kotlin.jvm.JvmOverloads 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.Items import net.minecraftforge.common.util.INBTSerializable import net.minecraftforge.registries.ForgeRegistries import ru.dbotthepony.mc.otm.core.addSorted +import ru.dbotthepony.mc.otm.core.collect.any +import ru.dbotthepony.mc.otm.core.collect.count +import ru.dbotthepony.mc.otm.core.collect.filter import ru.dbotthepony.mc.otm.core.isNotEmpty import ru.dbotthepony.mc.otm.core.nbt.map import ru.dbotthepony.mc.otm.core.nbt.set @@ -26,17 +32,18 @@ import ru.dbotthepony.mc.otm.core.util.ItemValueCodec import ru.dbotthepony.mc.otm.core.util.VarIntValueCodec import ru.dbotthepony.mc.otm.network.synchronizer.FieldSynchronizer import ru.dbotthepony.mc.otm.network.synchronizer.IField -import java.lang.ref.PhantomReference -import java.lang.ref.ReferenceQueue import java.lang.ref.WeakReference import java.util.* -import java.util.concurrent.CopyOnWriteArrayList +import java.util.function.Consumer +import java.util.function.Predicate import java.util.function.Supplier +import java.util.stream.Stream +import java.util.stream.StreamSupport import kotlin.NoSuchElementException import kotlin.collections.ArrayList @Suppress("UNUSED") -open class MatteryContainer(protected val watcher: Runnable, private val size: Int) : Container, Iterable, INBTSerializable { +open class MatteryContainer(protected val watcher: Runnable, private val size: Int) : Container, Iterable, INBTSerializable, StackedContentsCompatible { constructor(size: Int) : this({}, size) init { @@ -440,6 +447,12 @@ open class MatteryContainer(protected val watcher: Runnable, private val size: I } } + override fun fillStackedContents(contents: StackedContents) { + for (item in iterator()) { + contents.accountStack(item) + } + } + final override fun removeItem(slot: Int, amount: Int): ItemStack { if (amount <= 0 || slot < 0 || slot >= size || slots[slot].isEmpty) return ItemStack.EMPTY @@ -576,6 +589,26 @@ open class MatteryContainer(protected val watcher: Runnable, private val size: I } } + private inner class Spliterator(private val parent: IntSpliterator) : java.util.Spliterator { + override fun tryAdvance(action: Consumer): Boolean { + return parent.tryAdvance { + action.accept(getItem(it)) + } + } + + override fun trySplit(): java.util.Spliterator? { + return parent.trySplit()?.let(::Spliterator) + } + + override fun estimateSize(): Long { + return parent.estimateSize() + } + + override fun characteristics(): Int { + return parent.characteristics() + } + } + private object EmptyIterator : IContainerIterator { override fun hasNext(): Boolean { return false @@ -601,4 +634,29 @@ open class MatteryContainer(protected val watcher: Runnable, private val size: I return Iterator() } + + final override fun countItem(item: Item): Int { + return iterator().filter { it.item == item }.count().toInt() + } + + final override fun hasAnyOf(set: Set): Boolean { + return iterator().any { it.item in set } + } + + final override fun hasAnyMatching(predicate: Predicate): Boolean { + return iterator().any(predicate) + } + + final override fun spliterator(): java.util.Spliterator { + if (isEmpty) { + return ObjectSpliterators.emptySpliterator() + } + + indicesReferenced = true + return Spliterator(nonEmptyIndices.intSpliterator()) + } + + fun stream(): Stream { + return StreamSupport.stream(spliterator(), false) + } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/container/MatteryCraftingContainer.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/container/MatteryCraftingContainer.kt new file mode 100644 index 000000000..b6904f670 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/container/MatteryCraftingContainer.kt @@ -0,0 +1,22 @@ +package ru.dbotthepony.mc.otm.container + +import net.minecraft.world.inventory.CraftingContainer +import net.minecraft.world.item.ItemStack + +open class MatteryCraftingContainer(watcher: Runnable, private val width: Int, private val height: Int) : MatteryContainer(watcher, width * height), CraftingContainer { + constructor(width: Int, height: Int) : this({}, width, height) + final override fun getWidth(): Int { + return width + } + + final override fun getHeight(): Int { + return height + } + + final override fun getItems(): MutableList { + val i = spliterator() + val result = ArrayList(i.estimateSize().toInt()) + i.forEachRemaining { result.add(it) } + return result + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/container/ShadowContainer.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/container/ShadowContainer.kt new file mode 100644 index 000000000..3f506ffec --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/container/ShadowContainer.kt @@ -0,0 +1,48 @@ +package ru.dbotthepony.mc.otm.container + +import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap +import net.minecraft.world.Container +import net.minecraft.world.item.ItemStack +import java.util.Arrays + +class ShadowContainer(private val parent: Container) : Container by parent { + private val shadowed = Int2ObjectArrayMap(0) + + override fun clearContent() { + shadowed.clear() + parent.clearContent() + } + + override fun isEmpty(): Boolean { + return parent.isEmpty && shadowed.isEmpty() + } + + override fun getItem(slot: Int): ItemStack { + return shadowed[slot] ?: parent.getItem(slot) + } + + override fun removeItem(slot: Int, count: Int): ItemStack { + val shadow = shadowed[slot] ?: return parent.removeItem(slot, count) + val copy = shadow.copyWithCount(shadow.count.coerceAtLeast(count)) + shadow.split(count) + if (shadow.isEmpty) shadowed[slot] = ItemStack.EMPTY + return copy + } + + override fun removeItemNoUpdate(slot: Int): ItemStack { + shadowed[slot] ?: return parent.removeItemNoUpdate(slot) + val old = shadowed[slot] + shadowed[slot] = ItemStack.EMPTY + return old!! + } + + override fun setItem(slot: Int, item: ItemStack) { + shadowed[slot] = item + } + + companion object { + fun shadow(container: Container, slot: Int, itemStack: ItemStack): Container { + return ShadowContainer(container).also { it[slot] = itemStack } + } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/container/ShadowCraftingContainer.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/container/ShadowCraftingContainer.kt new file mode 100644 index 000000000..662d32a3e --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/container/ShadowCraftingContainer.kt @@ -0,0 +1,33 @@ +package ru.dbotthepony.mc.otm.container + +import net.minecraft.world.Container +import net.minecraft.world.entity.player.StackedContents +import net.minecraft.world.inventory.CraftingContainer +import net.minecraft.world.item.ItemStack +import ru.dbotthepony.mc.otm.core.collect.toList + +class ShadowCraftingContainer(private val parent: CraftingContainer) : Container by ShadowContainer(parent), CraftingContainer { + override fun fillStackedContents(contents: StackedContents) { + for (item in iterator()) { + contents.accountStack(item) + } + } + + override fun getWidth(): Int { + return parent.width + } + + override fun getHeight(): Int { + return parent.height + } + + override fun getItems(): MutableList { + return iterator().toList() + } + + companion object { + fun shadow(container: CraftingContainer, slot: Int, itemStack: ItemStack): CraftingContainer { + return ShadowCraftingContainer(container).also { it[slot] = itemStack } + } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/collect/StreamyIterators.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/collect/StreamyIterators.kt index 5711b8ec5..42e4476f5 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/collect/StreamyIterators.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/collect/StreamyIterators.kt @@ -3,9 +3,14 @@ package ru.dbotthepony.mc.otm.core.collect import it.unimi.dsi.fastutil.objects.ObjectIterators import ru.dbotthepony.mc.otm.core.addAll import java.util.Optional +import java.util.Spliterator +import java.util.Spliterators import java.util.function.BinaryOperator import java.util.function.Predicate +import java.util.function.Supplier import java.util.stream.Collector +import java.util.stream.Stream +import java.util.stream.StreamSupport // Purpose of Stream API over Iterators is that it is simple enough for JIT to inline most of it, // unlike actual Streams. @@ -370,3 +375,29 @@ fun Iterator.maybe(): T? { fun emptyIterator(): MutableIterator { return ObjectIterators.emptyIterator() } + +fun Iterator.toStream(): Stream { + return StreamSupport.stream(Spliterators.spliteratorUnknownSize(this, 0), false) +} + +fun Iterator.allEqual(): Boolean { + if (hasNext()) { + val v = next() + + while (hasNext()) { + if (v != next()) { + return false + } + } + + return true + } + + return false +} + +fun Iterator.count(): Long { + var count = 0L + while (hasNext()) count++ + return count +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/data/Codec2RecipeSerializer.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/data/Codec2RecipeSerializer.kt index 40ba63af0..bd43dd751 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/data/Codec2RecipeSerializer.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/data/Codec2RecipeSerializer.kt @@ -17,19 +17,64 @@ import org.apache.logging.log4j.LogManager import ru.dbotthepony.mc.otm.core.set import ru.dbotthepony.mc.otm.core.util.readBinaryJsonWithCodecIndirect import ru.dbotthepony.mc.otm.core.util.writeBinaryJsonWithCodec -import java.util.* -import kotlin.NoSuchElementException -import kotlin.concurrent.getOrSet +import kotlin.collections.ArrayDeque -class Codec2RecipeSerializer>(val empty: S?, val codec: Codec) : Codec by codec, RecipeSerializer { - constructor(supplier: (() -> ResourceLocation) -> Codec) : this(null, supplier.invoke(::context)) - constructor(empty: S, supplier: (() -> ResourceLocation) -> Codec) : this(empty, supplier.invoke(::context)) +class Codec2RecipeSerializer> private constructor( + val empty: S?, + private val id: ArrayDeque, + codec: (Codec2RecipeSerializer.Context) -> Codec, +) : Codec, RecipeSerializer { + constructor(empty: S?, codec: (Codec2RecipeSerializer.Context) -> Codec) : this(empty, ArrayDeque(), codec) + constructor(supplier: (Codec2RecipeSerializer.Context) -> Codec) : this(null, supplier) - override fun fromJson(p_44103_: ResourceLocation, p_44104_: JsonObject): S { + private val codec = codec.invoke(Context()) + + inner class Context() { + val id: ResourceLocation + get() = checkNotNull(this@Codec2RecipeSerializer.id.lastOrNull()) { "Not currently deserializing recipe" } + + fun > wrap(other: Codec2RecipeSerializer): Codec { + return object : Codec { + override fun encode(input: O, ops: DynamicOps, prefix: T): DataResult { + try { + other.id.addLast(this@Context.id) + return other.encode(input, ops, prefix) + } finally { + other.id.removeLast() + } + } + + override fun decode(ops: DynamicOps, input: T): DataResult> { + try { + other.id.addLast(this@Context.id) + return other.decode(ops, input) + } finally { + other.id.removeLast() + } + } + } + } + } + + override fun encode(input: S, ops: DynamicOps, prefix: T): DataResult { + return codec.encode(input, ops, prefix) + } + + override fun decode(ops: DynamicOps, input: T): DataResult> { + return codec.decode(ops, input) + } + + fun > xmap(to: (S) -> O, from: (O) -> S): Codec2RecipeSerializer { + return Codec2RecipeSerializer(empty?.let(to), id) { _ -> + codec.xmap(to, from) + } + } + + override fun fromJson(id: ResourceLocation, data: JsonObject): S { try { - deck.getOrSet(::LinkedList).addLast(p_44103_) + this.id.addLast(id) - return codec.decode(JsonOps.INSTANCE, p_44104_).get().map( + return decode(JsonOps.INSTANCE, data).get().map( { it.first }, @@ -38,29 +83,29 @@ class Codec2RecipeSerializer>(val empty: S?, val codec: Codec) } ) } finally { - deck.get().removeLast() + this.id.removeLast() } } - override fun fromNetwork(p_44105_: ResourceLocation, p_44106_: FriendlyByteBuf): S? { + override fun fromNetwork(id: ResourceLocation, data: FriendlyByteBuf): S? { try { - deck.getOrSet(::LinkedList).addLast(p_44105_) + this.id.addLast(id) - return p_44106_.readBinaryJsonWithCodecIndirect(codec) - .resultOrPartial { LOGGER.error("Failed to read recipe $p_44105_ from network: $it") }.orElse(null) + return data.readBinaryJsonWithCodecIndirect(this) + .resultOrPartial { LOGGER.error("Failed to read recipe $id from network: $it") }.orElse(null) } finally { - deck.get().removeLast() + this.id.removeLast() } } - override fun toNetwork(p_44101_: FriendlyByteBuf, p_44102_: S) { - p_44101_.writeBinaryJsonWithCodec(codec, p_44102_) + override fun toNetwork(data: FriendlyByteBuf, recipe: S) { + data.writeBinaryJsonWithCodec(this, recipe) } fun toFinished(recipe: S): FinishedRecipe { return object : FinishedRecipe { override fun serializeRecipeData(p_125967_: JsonObject) { - codec.encode(recipe, JsonOps.INSTANCE, p_125967_).get().map( + encode(recipe, JsonOps.INSTANCE, p_125967_).get().map( { it as JsonObject @@ -92,33 +137,7 @@ class Codec2RecipeSerializer>(val empty: S?, val codec: Codec) } } - companion object : Codec { - private val deck = ThreadLocal>() - - private fun context(): ResourceLocation { - val deck = deck.getOrSet(::LinkedList) - - if (deck.isEmpty()) { - throw NoSuchElementException("Context stack is empty") - } else { - return deck.last - } - } - - override fun encode(input: ResourceLocation, ops: DynamicOps, prefix: T): DataResult { - return DataResult.success(ops.empty()) - } - - override fun decode(ops: DynamicOps, input: T): DataResult> { - val deck = deck.getOrSet(::LinkedList) - - if (deck.isEmpty()) { - return DataResult.error { "Attempt to use recipe serializer codec ResourceLocation' hack outside Codec2RecipeSerializer" } - } else { - return DataResult.success(Pair(deck.last, ops.empty())) - } - } - + companion object { private val LOGGER = LogManager.getLogger() } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/data/IngredientMatrixCodec.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/data/IngredientMatrixCodec.kt new file mode 100644 index 000000000..f0ff5b1db --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/data/IngredientMatrixCodec.kt @@ -0,0 +1,111 @@ +package ru.dbotthepony.mc.otm.data + +import com.mojang.datafixers.util.Pair +import com.mojang.serialization.Codec +import com.mojang.serialization.DataResult +import com.mojang.serialization.DynamicOps +import com.mojang.serialization.JsonOps +import com.mojang.serialization.codecs.RecordCodecBuilder +import net.minecraft.world.item.crafting.Ingredient +import ru.dbotthepony.mc.otm.core.collect.allEqual +import ru.dbotthepony.mc.otm.core.collect.map +import ru.dbotthepony.mc.otm.core.collect.toStream +import ru.dbotthepony.mc.otm.core.stream +import ru.dbotthepony.mc.otm.recipe.IIngredientMatrix +import ru.dbotthepony.mc.otm.recipe.IngredientMatrix +import java.util.function.Supplier + +object IngredientMatrixCodec : Codec { + private val ingredientList = Codec.list(IngredientCodec) + + override fun encode(input: IIngredientMatrix, ops: DynamicOps, prefix: T): DataResult { + return DataResult.success( + ops.createList( + (0 until input.height).stream().map { row -> + ops.createList((0 until input.width).stream().map { column -> + JsonOps.INSTANCE.convertTo(ops, input[column, row].toJson()) + }) + } + ) + ) + } + + private data class Handwritten(val pattern: List, val key: Map) + + private val handwrittenCodec = RecordCodecBuilder.create { + it.group( + Codec.list(Codec.STRING) + .flatXmap( + { DataResult.success(it) }, + { if (it.iterator().map { it.length }.allEqual()) DataResult.success(it) else DataResult.error { "One or more of patten strings differ in length" } } + ) + .fieldOf("pattern").forGetter(Handwritten::pattern), + Codec.unboundedMap( + Codec.STRING + .flatXmap( + { if (it.length == 1) DataResult.success(it[0]) else DataResult.error { "Ingredient key must be exactly 1 symbol in length, '$it' is invalid" } }, + { DataResult.success(it.toString()) } + ), IngredientCodec).fieldOf("key").forGetter(Handwritten::key) + ).apply(it, ::Handwritten) + } + + override fun decode(ops: DynamicOps, input: T): DataResult> { + return ops.getList(input).get().map( + { + val lines = ArrayList>>() + + it.accept { + lines.add(ingredientList.decode(ops, it).map { it.first }) + } + + val errors = ArrayList>() + val ingredients = ArrayList>() + + lines.withIndex().forEach { + val (line, result) = it + result.get().map({ ingredients.add(it) }, { errors.add { "Line $line: ${it.message()}" } }) + } + + if (errors.isNotEmpty()) { + DataResult.error { "Failed to decode ingredient matrix: ${errors.joinToString { it.get() }}" } + } else if (ingredients.isEmpty()) { + DataResult.error { "Ingredient list is empty" } + } else if (!ingredients.iterator().map { it.size }.allEqual()) { + DataResult.error { "Ingredient list is not a matrix (one or multiple of rows are mismatched size)" } + } else { + val result = IngredientMatrix(ingredients.first().size, ingredients.size) + + for ((row, columns) in ingredients.withIndex()) { + for ((column, ingredient) in columns.withIndex()) { + result[column, row] = ingredient + } + } + + DataResult.success(Pair(result, ops.empty())) + } + }, + { err1 -> + handwrittenCodec.decode(ops, input).get().map( + { + DataResult.success(it) + }, + { + DataResult.error { "Failed to decode ingredients as list: ${err1.message()} and as pattern/dictionary: ${it.message()}" } + } + ).flatMap { + val handwritten = it.first + val result = IngredientMatrix(handwritten.pattern.first().length, handwritten.pattern.size) + + for ((row, pattern) in handwritten.pattern.withIndex()) { + for ((column, symbol) in pattern.withIndex()) { + val ingredient = if (symbol == ' ') handwritten.key[symbol] ?: Ingredient.EMPTY else handwritten.key[symbol] ?: return@flatMap DataResult.error { "Unknown ingredient with index '$symbol'" } + result[column, row] = ingredient + } + } + + DataResult.success(Pair(result, ops.empty())) + } + } + ) + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/item/IQuantumLinked.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/item/IQuantumLinked.kt new file mode 100644 index 000000000..1049bb407 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/item/IQuantumLinked.kt @@ -0,0 +1,7 @@ +package ru.dbotthepony.mc.otm.item + +import net.minecraft.world.item.ItemStack + +interface IQuantumLinked { + fun merge(from: ItemStack, into: ItemStack): ItemStack +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/item/QuantumBatteryItem.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/item/QuantumBatteryItem.kt index d12384485..7a37f586e 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/item/QuantumBatteryItem.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/item/QuantumBatteryItem.kt @@ -63,7 +63,7 @@ import kotlin.collections.forEach import kotlin.collections.iterator import kotlin.collections.set -class QuantumBatteryItem(val savedataID: String, val balanceValues: EnergyBalanceValues?) : Item(Properties().stacksTo(1).rarity(if (balanceValues == null) Rarity.EPIC else Rarity.UNCOMMON)) { +class QuantumBatteryItem(val savedataID: String, val balanceValues: EnergyBalanceValues?) : Item(Properties().stacksTo(1).rarity(if (balanceValues == null) Rarity.EPIC else Rarity.UNCOMMON)), IQuantumLinked { val isCreative = balanceValues == null interface IValues { @@ -169,9 +169,9 @@ class QuantumBatteryItem(val savedataID: String, val balanceValues: EnergyBalanc fun updateValues() { if (!values.isServer && isServerThread()) { - values = serverData.values(stack.tag?.getUUIDSafe("id") ?: UUID.randomUUID().also { stack.tagNotNull["id"] = it }) + values = serverData.values(stack.tag?.getUUIDSafe("uuid") ?: UUID.randomUUID().also { stack.tagNotNull["uuid"] = it }) } else if (isClientThread()) { - val id = stack.tag?.getUUIDSafe("id") ?: return + val id = stack.tag?.getUUIDSafe("uuid") ?: return if (values.uuid != id) values = clientData.computeIfAbsent(id, Function { UnboundValues(it) }) @@ -278,6 +278,10 @@ class QuantumBatteryItem(val savedataID: String, val balanceValues: EnergyBalanc get() = if (isCreative) Decimal.LONG_MAX_VALUE else super.missingPower } + override fun merge(from: ItemStack, into: ItemStack): ItemStack { + return from.copyWithCount(from.count + into.count) + } + override fun isBarVisible(p_150899_: ItemStack): Boolean { if (isCreative) return false return p_150899_.matteryEnergy != null diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/ExopackInventoryMenu.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/ExopackInventoryMenu.kt index 6fbbdb32c..2a936470f 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/ExopackInventoryMenu.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/ExopackInventoryMenu.kt @@ -69,7 +69,7 @@ class ExopackInventoryMenu(val capability: MatteryPlayerCapability) : MatteryMen } val armorSlots = makeArmorSlots() - val curiosSlots: ImmutableList> = ImmutableList.copyOf(ply.curiosSlots) + val curiosSlots: ImmutableList> = ImmutableList.copyOf(player.curiosSlots) init { for ((a, b) in curiosSlots) { @@ -84,10 +84,10 @@ class ExopackInventoryMenu(val capability: MatteryPlayerCapability) : MatteryMen } private fun popFurnaceExp() { - if (capability.isExopackSmeltingInstalled && capability.exopackSmelterExperience >= 1f && !ply.level().isClientSide) { + if (capability.isExopackSmeltingInstalled && capability.exopackSmelterExperience >= 1f && !player.level().isClientSide) { val whole = capability.exopackSmelterExperience.toInt() capability.exopackSmelterExperience -= whole - ExperienceOrb.award(ply.level() as ServerLevel, ply.position(), whole) + ExperienceOrb.award(player.level() as ServerLevel, player.position(), whole) } } @@ -99,8 +99,8 @@ class ExopackInventoryMenu(val capability: MatteryPlayerCapability) : MatteryMen } } - val furnaceOutputs: List = capability.smelters.map { - object : MachineOutputSlot(it.output, 0, onTake = { popFurnaceExp() }) { + val furnaceOutputs: List = capability.smelters.map { + object : OutputSlot(it.output, 0, onTake = { popFurnaceExp() }) { override fun mayPickup(player: Player): Boolean { return super.mayPickup(player) && capability.isExopackSmeltingInstalled } @@ -120,7 +120,7 @@ class ExopackInventoryMenu(val capability: MatteryPlayerCapability) : MatteryMen init { if (capability.isExopackEnderAccessInstalled) { - enderChestSlots = makeSlots(ply.enderChestInventory) { a, b -> + enderChestSlots = makeSlots(player.enderChestInventory) { a, b -> MatterySlot(a, b).also { addStorageSlot(it, condition = enderChestOpenState) } @@ -238,16 +238,16 @@ class ExopackInventoryMenu(val capability: MatteryPlayerCapability) : MatteryMen } fun sendInitialData(container: ExopackInventoryMenu, itemStacks: NonNullList, carried: ItemStack, remoteDataSlots: IntArray) { - MatteryPlayerNetworkChannel.send(container.ply as ServerPlayer, ExopackMenuInitPacket(itemStacks, carried, container.incrementStateId())) + MatteryPlayerNetworkChannel.send(container.player as ServerPlayer, ExopackMenuInitPacket(itemStacks, carried, container.incrementStateId())) } fun sendSlotChange(container: ExopackInventoryMenu, slotId: Int, itemStack: ItemStack) { - if (container.slots[slotId].container != container.ply.inventory || container.ply.containerMenu is ExopackInventoryMenu) - MatteryPlayerNetworkChannel.send(container.ply as ServerPlayer, ExopackSlotPacket(slotId, itemStack, container.stateId)) + if (container.slots[slotId].container != container.player.inventory || container.player.containerMenu is ExopackInventoryMenu) + MatteryPlayerNetworkChannel.send(container.player as ServerPlayer, ExopackSlotPacket(slotId, itemStack, container.stateId)) } fun sendCarriedChange(container: ExopackInventoryMenu, itemStack: ItemStack) { - MatteryPlayerNetworkChannel.send(container.ply as ServerPlayer, ExopackCarriedPacket(itemStack, container.stateId)) + MatteryPlayerNetworkChannel.send(container.player as ServerPlayer, ExopackCarriedPacket(itemStack, container.stateId)) } fun sendDataChange(container: ExopackInventoryMenu, dataSlotId: Int, shortData: Int) { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/MatteryMenu.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/MatteryMenu.kt index 5c20fd3fe..fca2c7320 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/MatteryMenu.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/MatteryMenu.kt @@ -101,7 +101,7 @@ abstract class MatteryMenu( * Server->Client synchronizer */ val mSynchronizer = FieldSynchronizer() - val ply: Player get() = inventory.player + val player: Player get() = inventory.player private val _playerInventorySlots = ArrayList() private val _playerHotbarSlots = ArrayList() @@ -229,7 +229,7 @@ abstract class MatteryMenu( protected var inventorySlotIndexStart = 0 protected var inventorySlotIndexEnd = 0 - private val playerPacketDistributor = PacketDistributor.PLAYER.with { ply as ServerPlayer } + private val playerPacketDistributor = PacketDistributor.PLAYER.with { player as ServerPlayer } fun addFilterSlots(slots: ItemFilter): List> { val result = ArrayList>(slots.size) @@ -277,8 +277,8 @@ abstract class MatteryMenu( } override fun isSameInventory(other: Slot): Boolean { - if (container === inventory || container === ply.matteryPlayer?.exopackContainer) - return (other.container === inventory || other.container === ply.matteryPlayer?.exopackContainer) && isSameFilter(other) + if (container === inventory || container === player.matteryPlayer?.exopackContainer) + return (other.container === inventory || other.container === player.matteryPlayer?.exopackContainer) && isSameFilter(other) return super.isSameInventory(other) } @@ -287,7 +287,7 @@ abstract class MatteryMenu( private set init { - val mattery = ply.matteryPlayer + val mattery = player.matteryPlayer if (mattery != null) { if (container === inventory) { @@ -349,7 +349,7 @@ abstract class MatteryMenu( } } - protected fun addInventorySlots(autoFrame: Boolean = !ply.isSpectator) { + protected fun addInventorySlots(autoFrame: Boolean = !player.isSpectator) { check(_playerInventorySlots.isEmpty()) { "Already created inventory slots" } autoCreateInventoryFrame = autoFrame @@ -378,7 +378,7 @@ abstract class MatteryMenu( addSlot(slot) } - val mattery = ply.matteryPlayer + val mattery = player.matteryPlayer if (mattery != null && mattery.hasExopack) { for (i in 0 until mattery.exopackContainer.containerSize) { @@ -415,9 +415,9 @@ abstract class MatteryMenu( if (payload != null) { if (broadcastOnce) { - MenuNetworkChannel.send(ply, MenuFieldPacket(containerId, payload)) + MenuNetworkChannel.send(player, MenuFieldPacket(containerId, payload)) } else { - MenuNetworkChannel.sendNow(ply, MenuFieldPacket(containerId, payload)) + MenuNetworkChannel.sendNow(player, MenuFieldPacket(containerId, payload)) } } @@ -449,7 +449,7 @@ abstract class MatteryMenu( fun syncCarried() { setRemoteCarried(carried.copy()) - MenuNetworkChannel.send(ply as ServerPlayer, SetCarriedPacket(carried)) + MenuNetworkChannel.send(player as ServerPlayer, SetCarriedPacket(carried)) } fun syncCarried(stack: ItemStack) { @@ -635,11 +635,11 @@ abstract class MatteryMenu( if (remainder.isEmpty) { source.set(ItemStack.EMPTY) - source.onTake(ply, copy) + source.onTake(player, copy) } else { copy.count = source.item.count - remainder.count source.item.count = remainder.count - source.onTake(ply, copy) + source.onTake(player, copy) } return true @@ -745,7 +745,7 @@ abstract class MatteryMenu( return armorSlots!! } - val cosmetic = ply.cosmeticArmorSlots + val cosmetic = player.cosmeticArmorSlots return ImmutableList.of( PlayerSlot(EquipmentSlot(net.minecraft.world.entity.EquipmentSlot.HEAD), cosmetic?.get(net.minecraft.world.entity.EquipmentSlot.HEAD)), @@ -780,7 +780,7 @@ abstract class MatteryMenu( fun makeEquipmentSlots(mapMoveToExternal: Boolean = false): EquipmentSlots { return EquipmentSlots( armorSlots = makeArmorSlots(mapMoveToExternal), - curiosSlots = curiosSlots ?: ImmutableList.copyOf(ply.curiosSlots).also { + curiosSlots = curiosSlots ?: ImmutableList.copyOf(player.curiosSlots).also { for ((a, b) in it) { equipmentSlots.add(a) addSlot(a) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/Slots.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/Slots.kt index 2a4c59ed0..ac20239f9 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/Slots.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/Slots.kt @@ -11,7 +11,6 @@ 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.client.minecraft -import ru.dbotthepony.mc.otm.container.UpgradeContainer import ru.dbotthepony.mc.otm.core.GetterSetter import ru.dbotthepony.mc.otm.core.immutableList import ru.dbotthepony.mc.otm.runOnClient @@ -80,7 +79,7 @@ open class UserFilteredSlot(container: Container, index: Int, x: Int = 0, y: Int } } -open class MachineOutputSlot(container: Container, index: Int, x: Int = 0, y: Int = 0, val onTake: (ItemStack) -> Unit = {}) : MatterySlot(container, index, x, y) { +open class OutputSlot(container: Container, index: Int, x: Int = 0, y: Int = 0, val onTake: (ItemStack) -> Unit = {}) : MatterySlot(container, index, x, y) { override fun mayPlace(itemStack: ItemStack): Boolean { return false } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/decorative/FluidTankMenu.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/decorative/FluidTankMenu.kt index cce696644..401057a94 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/decorative/FluidTankMenu.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/decorative/FluidTankMenu.kt @@ -8,8 +8,7 @@ import net.minecraftforge.fluids.capability.IFluidHandler import ru.dbotthepony.mc.otm.block.entity.RedstoneSetting import ru.dbotthepony.mc.otm.block.entity.decorative.FluidTankBlockEntity import ru.dbotthepony.mc.otm.capability.isNotEmpty -import ru.dbotthepony.mc.otm.capability.isNotFull -import ru.dbotthepony.mc.otm.menu.MachineOutputSlot +import ru.dbotthepony.mc.otm.menu.OutputSlot import ru.dbotthepony.mc.otm.menu.MatteryMenu import ru.dbotthepony.mc.otm.menu.MatterySlot import ru.dbotthepony.mc.otm.menu.input.EnumInputWithFeedback @@ -50,7 +49,7 @@ class FluidTankMenu(containerId: Int, inventory: Inventory, tile: FluidTankBlock } } - val output = MachineOutputSlot(tile?.output ?: SimpleContainer(1), 0) + val output = OutputSlot(tile?.output ?: SimpleContainer(1), 0) init { // сначала слот на заполнение из бака diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/tech/PainterMenu.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/decorative/PainterMenu.kt similarity index 98% rename from src/main/kotlin/ru/dbotthepony/mc/otm/menu/tech/PainterMenu.kt rename to src/main/kotlin/ru/dbotthepony/mc/otm/menu/decorative/PainterMenu.kt index 5d93bddb0..7dd9dc7f1 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/tech/PainterMenu.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/decorative/PainterMenu.kt @@ -1,4 +1,4 @@ -package ru.dbotthepony.mc.otm.menu.tech +package ru.dbotthepony.mc.otm.menu.decorative import net.minecraft.world.SimpleContainer import net.minecraft.world.entity.player.Inventory diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/matter/MatterDecomposerMenu.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/matter/MatterDecomposerMenu.kt index 29e2a6a21..f2eb883a4 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/matter/MatterDecomposerMenu.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/matter/MatterDecomposerMenu.kt @@ -7,10 +7,8 @@ import ru.dbotthepony.mc.otm.menu.widget.ProgressGaugeWidget import ru.dbotthepony.mc.otm.menu.widget.LevelGaugeWidget import net.minecraft.world.SimpleContainer import net.minecraft.world.item.ItemStack -import ru.dbotthepony.mc.otm.capability.MatteryCapability -import ru.dbotthepony.mc.otm.core.orNull import ru.dbotthepony.mc.otm.matter.MatterManager -import ru.dbotthepony.mc.otm.menu.MachineOutputSlot +import ru.dbotthepony.mc.otm.menu.OutputSlot import ru.dbotthepony.mc.otm.menu.MatteryPoweredMenu import ru.dbotthepony.mc.otm.menu.MatterySlot import ru.dbotthepony.mc.otm.menu.input.EnergyConfigPlayerInput @@ -27,8 +25,8 @@ class MatterDecomposerMenu @JvmOverloads constructor( override fun mayPlace(itemStack: ItemStack) = MatterManager.canDecompose(itemStack) } - val outputMain: MachineOutputSlot - val outputStacking: MachineOutputSlot + val outputMain: OutputSlot + val outputStacking: OutputSlot val progressWidget = ProgressGaugeWidget(this, tile) val matterWidget = ProfiledLevelGaugeWidget(this, tile?.matter, LevelGaugeWidget(this, tile?.matter)) val itemConfig = ItemConfigPlayerInput(this, tile?.itemConfig) @@ -40,8 +38,8 @@ class MatterDecomposerMenu @JvmOverloads constructor( val container = tile?.outputContainer ?: SimpleContainer(2) // Выход - outputMain = MachineOutputSlot(container, 0) - outputStacking = MachineOutputSlot(container, 1) + outputMain = OutputSlot(container, 0) + outputStacking = OutputSlot(container, 1) addStorageSlot(outputMain) addStorageSlot(outputStacking) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/matter/MatterEntanglerMenu.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/matter/MatterEntanglerMenu.kt new file mode 100644 index 000000000..99f71e491 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/matter/MatterEntanglerMenu.kt @@ -0,0 +1,130 @@ +package ru.dbotthepony.mc.otm.menu.matter + +import net.minecraft.server.level.ServerPlayer +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.item.ItemStack +import ru.dbotthepony.mc.otm.block.entity.matter.MatterEntanglerBlockEntity +import ru.dbotthepony.mc.otm.container.MatteryContainer +import ru.dbotthepony.mc.otm.container.MatteryCraftingContainer +import ru.dbotthepony.mc.otm.container.ShadowCraftingContainer +import ru.dbotthepony.mc.otm.container.get +import ru.dbotthepony.mc.otm.container.set +import ru.dbotthepony.mc.otm.core.isNotEmpty +import ru.dbotthepony.mc.otm.item.IQuantumLinked +import ru.dbotthepony.mc.otm.menu.OutputSlot +import ru.dbotthepony.mc.otm.menu.MatteryMenu +import ru.dbotthepony.mc.otm.menu.MatteryPoweredMenu +import ru.dbotthepony.mc.otm.menu.MatterySlot +import ru.dbotthepony.mc.otm.menu.input.EnergyConfigPlayerInput +import ru.dbotthepony.mc.otm.menu.input.ItemConfigPlayerInput +import ru.dbotthepony.mc.otm.menu.makeSlots +import ru.dbotthepony.mc.otm.menu.widget.LevelGaugeWidget +import ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget +import ru.dbotthepony.mc.otm.menu.widget.ProgressGaugeWidget +import ru.dbotthepony.mc.otm.registry.MMenus +import ru.dbotthepony.mc.otm.registry.MRecipes +import java.util.* + +class MatterEntanglerMenu( + containerId: Int, inventory: Inventory, tile: MatterEntanglerBlockEntity? = null +) : MatteryPoweredMenu(MMenus.MATTER_ENTANGLER, containerId, inventory, tile) { + val energyConfig = EnergyConfigPlayerInput(this, tile?.energyConfig) + val itemConfig = ItemConfigPlayerInput(this, tile?.itemConfig) + val profiledEnergy = ProfiledLevelGaugeWidget(this, tile?.energy) + val profiledMatter = ProfiledLevelGaugeWidget(this, tile?.matter, LevelGaugeWidget(this, tile?.matter)) + + val progress = ProgressGaugeWidget(this, tile?.jobEventLoops?.get(0)) + + val inputs: List = makeSlots(tile?.inputs ?: object : MatteryCraftingContainer(3, 3) { + override fun getMaxStackSize(): Int { + return 1 + } + }) { it, i -> + object : MatterySlot(it, i) { + override fun mayPlace(itemStack: ItemStack): Boolean { + val shadow = ShadowCraftingContainer.shadow(it, i, itemStack) + val level = player.level() + + return super.mayPlace(itemStack) && (level ?: return false) + .recipeManager + .byType(MRecipes.MATTER_ENTANGLER) + .values + .any { it.preemptivelyMatches(shadow, level) } + } + } + } + + val outputs = makeSlots(tile?.output ?: SimpleContainer(1), ::OutputSlot) + val upgrades = makeUpgradeSlots(3, tile?.upgrades) + + private val entangling: Container = if (tile == null) object : SimpleContainer(2) { + override fun getMaxStackSize(): Int { + return 1 + } + } else object : MatteryContainer(::rescan, 2) { + override fun getMaxStackSize(slot: Int, itemStack: ItemStack): Int { + return 1 + } + } + + private fun rescan() { + if (player is ServerPlayer && entangling[0].item is IQuantumLinked && entangling[1].item == entangling[0].item && entangling[0].isNotEmpty && entangling[1].isNotEmpty) { + entanglingC.container[0] = (entangling[0].item as IQuantumLinked).merge(entangling[0], entangling[1]) + } else if (player is ServerPlayer) { + entanglingC.container[0] = ItemStack.EMPTY + } + } + + private inner class EntanglingInputSlot(index: Int) : MatterySlot(entangling, index) { + override fun mayPlace(itemStack: ItemStack): Boolean { + return super.mayPlace(itemStack) && itemStack.item is IQuantumLinked + } + } + + val entanglingA: MatterySlot = EntanglingInputSlot(0) + val entanglingB: MatterySlot = EntanglingInputSlot(1) + val entanglingC: MatterySlot = object : MatterySlot(SimpleContainer(1), 0) { + override fun mayPlace(itemStack: ItemStack): Boolean { + return false + } + + override fun canTakeItemForPickAll(): Boolean { + return false + } + + override fun tryRemove(p_150642_: Int, p_150643_: Int, p_150644_: Player): Optional { + rescan() + return super.tryRemove(p_150642_, p_150643_, p_150644_) + } + + override fun onTake(p_150645_: Player, p_150646_: ItemStack) { + if (p_150646_.isNotEmpty) { + entangling.removeItem(0, 1) + entangling.removeItem(1, 1) + } + + super.onTake(p_150645_, p_150646_) + } + } + + override fun removed(p_38940_: Player) { + super.removed(p_38940_) + clearContainer(p_38940_, entangling) + } + + init { + addSlot(outputs) + addSlot(entanglingA) + addSlot(entanglingB) + addSlot(entanglingC) + mapQuickMoveToInventory(entanglingA) + mapQuickMoveToInventory(entanglingB) + mapQuickMoveToInventory(entanglingC) + outputs.forEach(::mapQuickMoveToInventory) + addStorageSlot(inputs) + addInventorySlots() + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/matter/MatterPanelMenu.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/matter/MatterPanelMenu.kt index 44a315958..86d586ee0 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/matter/MatterPanelMenu.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/matter/MatterPanelMenu.kt @@ -141,7 +141,7 @@ class MatterPanelMenu( } val sorting: ItemSorter by mSynchronizer.ComputedField( - getter = { tile?.getPlayerSettings(ply)?.sorter ?: ItemSorter.DEFAULT }, + getter = { tile?.getPlayerSettings(player)?.sorter ?: ItemSorter.DEFAULT }, codec = ItemSorter::class.codec(), observer = { patterns.sortWith(actualComparator) @@ -151,7 +151,7 @@ class MatterPanelMenu( }) val isAscending: Boolean by mSynchronizer.ComputedField( - getter = { tile?.getPlayerSettings(ply)?.ascending ?: true }, + getter = { tile?.getPlayerSettings(player)?.ascending ?: true }, codec = BooleanValueCodec, observer = { patterns.sortWith(actualComparator) @@ -184,8 +184,8 @@ class MatterPanelMenu( } } - val changeIsAscending = booleanInput(allowSpectators = true) { tile?.getPlayerSettings(ply)?.ascending = it } - val changeSorting = PlayerInput(ItemSorter::class.codec(), allowSpectators = true) { tile?.getPlayerSettings(ply)?.sorter = it } + val changeIsAscending = booleanInput(allowSpectators = true) { tile?.getPlayerSettings(player)?.ascending = it } + val changeSorting = PlayerInput(ItemSorter::class.codec(), allowSpectators = true) { tile?.getPlayerSettings(player)?.sorter = it } val sortingGS = GetterSetter.of(::sorting, changeSorting::accept) val isAscendingGS = GetterSetter.of(::isAscending, changeIsAscending::accept) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/matter/MatterReplicatorMenu.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/matter/MatterReplicatorMenu.kt index cc5d52ba9..8c50152b7 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/matter/MatterReplicatorMenu.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/matter/MatterReplicatorMenu.kt @@ -10,7 +10,7 @@ import net.minecraft.world.SimpleContainer import ru.dbotthepony.mc.otm.container.CombinedContainer import ru.dbotthepony.mc.otm.core.immutableList import ru.dbotthepony.mc.otm.core.isNotEmpty -import ru.dbotthepony.mc.otm.menu.MachineOutputSlot +import ru.dbotthepony.mc.otm.menu.OutputSlot import ru.dbotthepony.mc.otm.menu.MatteryPoweredMenu import ru.dbotthepony.mc.otm.menu.input.EnergyConfigPlayerInput import ru.dbotthepony.mc.otm.menu.input.ItemConfigPlayerInput @@ -25,7 +25,7 @@ class MatterReplicatorMenu @JvmOverloads constructor( ) : MatteryPoweredMenu(MMenus.MATTER_REPLICATOR, p_38852_, inventory, tile) { val matter = ProfiledLevelGaugeWidget(this, tile?.matter, LevelGaugeWidget(this, tile?.matter)) val progress = ProgressGaugeWidget(this, tile) - val storageSlots: List + val storageSlots: List val itemConfig = ItemConfigPlayerInput(this, tile?.itemConfig) val energyConfig = EnergyConfigPlayerInput(this, tile?.energyConfig) val profiledEnergy = ProfiledLevelGaugeWidget(this, tile?.energy, energyWidget) @@ -36,7 +36,7 @@ class MatterReplicatorMenu @JvmOverloads constructor( val container = CombinedContainer(tile?.outputContainer ?: SimpleContainer(3), tile?.dustContainer ?: SimpleContainer(2)) storageSlots = immutableList(5) { - addStorageSlot(MachineOutputSlot(container, it, onTake = { + addStorageSlot(OutputSlot(container, it, onTake = { if (inventory.player is ServerPlayer && it.isNotEmpty) TakeItemOutOfReplicatorTrigger.trigger(inventory.player as ServerPlayer, it) })) } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/storage/DriveViewerMenu.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/storage/DriveViewerMenu.kt index 9a5e139e9..702149d37 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/storage/DriveViewerMenu.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/storage/DriveViewerMenu.kt @@ -48,8 +48,8 @@ class DriveViewerMenu( val energyConfig = EnergyConfigPlayerInput(this, tile?.energyConfig) val settings = object : DriveViewerBlockEntity.ISettings { - override var sorting: ItemStorageStackSorter by EnumInputWithFeedback(this@DriveViewerMenu, true, tile?.let { it.getSettingsFor(ply)::sorting }).also { it.addListener { changes() } } - override var isAscending: Boolean by BooleanInputWithFeedback(this@DriveViewerMenu, true, tile?.let { it.getSettingsFor(ply)::isAscending }).also { it.addListener { changes() } } + override var sorting: ItemStorageStackSorter by EnumInputWithFeedback(this@DriveViewerMenu, true, tile?.let { it.getSettingsFor(player)::sorting }).also { it.addListener { changes() } } + override var isAscending: Boolean by BooleanInputWithFeedback(this@DriveViewerMenu, true, tile?.let { it.getSettingsFor(player)::isAscending }).also { it.addListener { changes() } } private fun changes() { if (isAscending) { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/storage/ItemMonitorMenu.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/storage/ItemMonitorMenu.kt index 3cebf19d1..82c13664c 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/storage/ItemMonitorMenu.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/storage/ItemMonitorMenu.kt @@ -86,11 +86,11 @@ class ItemMonitorMenu( override val networkedItemView = NetworkedItemView(inventory.player, this, tile == null) val settings = object : IItemMonitorPlayerSettings { - override var ingredientPriority by EnumInputWithFeedback(this@ItemMonitorMenu, true, tile?.let { it.getSettings(ply as ServerPlayer)::ingredientPriority }) - override var resultTarget by EnumInputWithFeedback(this@ItemMonitorMenu, true, tile?.let { it.getSettings(ply as ServerPlayer)::resultTarget }) - override var craftingAmount by EnumInputWithFeedback(this@ItemMonitorMenu, true, tile?.let { it.getSettings(ply as ServerPlayer)::craftingAmount }) - override var sorting by EnumInputWithFeedback(this@ItemMonitorMenu, true, tile?.let { it.getSettings(ply as ServerPlayer)::sorting }).also { it.addListener { changes() } } - override var ascendingSort by BooleanInputWithFeedback(this@ItemMonitorMenu, true, tile?.let { it.getSettings(ply as ServerPlayer)::ascendingSort }).also { it.addListener { changes() } } + override var ingredientPriority by EnumInputWithFeedback(this@ItemMonitorMenu, true, tile?.let { it.getSettings(player as ServerPlayer)::ingredientPriority }) + override var resultTarget by EnumInputWithFeedback(this@ItemMonitorMenu, true, tile?.let { it.getSettings(player as ServerPlayer)::resultTarget }) + override var craftingAmount by EnumInputWithFeedback(this@ItemMonitorMenu, true, tile?.let { it.getSettings(player as ServerPlayer)::craftingAmount }) + override var sorting by EnumInputWithFeedback(this@ItemMonitorMenu, true, tile?.let { it.getSettings(player as ServerPlayer)::sorting }).also { it.addListener { changes() } } + override var ascendingSort by BooleanInputWithFeedback(this@ItemMonitorMenu, true, tile?.let { it.getSettings(player as ServerPlayer)::ascendingSort }).also { it.addListener { changes() } } private fun changes() { if (ascendingSort) { @@ -140,7 +140,7 @@ class ItemMonitorMenu( } if (!simulate && remaining.isNotEmpty) { - ply.spawnAtLocation(remaining) + player.spawnAtLocation(remaining) } if (!simulate) { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/tech/AndroidStationMenu.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/tech/AndroidStationMenu.kt index 60caf7fcb..301f24d27 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/tech/AndroidStationMenu.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/tech/AndroidStationMenu.kt @@ -24,8 +24,8 @@ class AndroidStationMenu @JvmOverloads constructor( tile: AndroidStationBlockEntity? = null ) : MatteryPoweredMenu(MMenus.ANDROID_STATION, containerID, inventory, tile) { private fun container(target: (MatteryPlayerCapability) -> KMutableProperty0): Container { - if (ply is ServerPlayer) - return PartContainer(target.invoke(ply.matteryPlayer ?: throw NullPointerException("OTM player capability is missing"))) + if (player is ServerPlayer) + return PartContainer(target.invoke(player.matteryPlayer ?: throw NullPointerException("OTM player capability is missing"))) else return SimpleContainer(1) } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/tech/CobblerMenu.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/tech/CobblerMenu.kt index af64aef16..23ed2311a 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/tech/CobblerMenu.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/tech/CobblerMenu.kt @@ -5,7 +5,7 @@ import net.minecraft.world.entity.player.Inventory import ru.dbotthepony.mc.otm.block.entity.RedstoneSetting import ru.dbotthepony.mc.otm.block.entity.tech.CobblerBlockEntity import ru.dbotthepony.mc.otm.core.immutableList -import ru.dbotthepony.mc.otm.menu.MachineOutputSlot +import ru.dbotthepony.mc.otm.menu.OutputSlot import ru.dbotthepony.mc.otm.menu.MatteryMenu import ru.dbotthepony.mc.otm.menu.input.EnumInputWithFeedback import ru.dbotthepony.mc.otm.menu.input.ItemConfigPlayerInput @@ -17,7 +17,7 @@ class CobblerMenu @JvmOverloads constructor( inventory: Inventory, tile: CobblerBlockEntity? = null ) : MatteryMenu(MMenus.COBBLESTONE_GENERATOR, p_38852_, inventory, tile) { - val storageSlots = (tile?.container ?: SimpleContainer(CobblerBlockEntity.CONTAINER_SIZE)).let { c -> immutableList(c.containerSize) { addStorageSlot(MachineOutputSlot(c, it)) } } + val storageSlots = (tile?.container ?: SimpleContainer(CobblerBlockEntity.CONTAINER_SIZE)).let { c -> immutableList(c.containerSize) { addStorageSlot(OutputSlot(c, it)) } } val redstone = EnumInputWithFeedback(this) val itemConfig = ItemConfigPlayerInput(this) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/tech/EssenceStorageMenu.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/tech/EssenceStorageMenu.kt index 2c36f97dc..128d4f6d7 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/tech/EssenceStorageMenu.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/tech/EssenceStorageMenu.kt @@ -40,7 +40,7 @@ class EssenceStorageMenu @JvmOverloads constructor( val storeLevels = intInput { if (it > 0) { - val ply = ply as ServerPlayer + val ply = player as ServerPlayer tile!! if (it == 1) { @@ -63,7 +63,7 @@ class EssenceStorageMenu @JvmOverloads constructor( val dispenseLevels = intInput { if (it > 0) { - val ply = ply as ServerPlayer + val ply = player as ServerPlayer tile!! if (it == 1) { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/tech/PlatePressMenu.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/tech/PlatePressMenu.kt index 935922882..2d3bc8512 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/tech/PlatePressMenu.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/tech/PlatePressMenu.kt @@ -4,7 +4,7 @@ import net.minecraft.server.level.ServerPlayer import net.minecraft.world.SimpleContainer import net.minecraft.world.entity.player.Inventory import ru.dbotthepony.mc.otm.block.entity.tech.PlatePressBlockEntity -import ru.dbotthepony.mc.otm.menu.MachineOutputSlot +import ru.dbotthepony.mc.otm.menu.OutputSlot import ru.dbotthepony.mc.otm.menu.MatteryPoweredMenu import ru.dbotthepony.mc.otm.menu.MatterySlot import ru.dbotthepony.mc.otm.menu.input.EnergyConfigPlayerInput @@ -19,7 +19,7 @@ class PlatePressMenu @JvmOverloads constructor( tile: PlatePressBlockEntity? = null ) : MatteryPoweredMenu(MMenus.PLATE_PRESS, containerID, inventory, tile) { val inputSlot = MatterySlot(tile?.inputContainer ?: SimpleContainer(1), 0) - val outputSlot = MachineOutputSlot(tile?.outputContainer ?: SimpleContainer(1), 0) { tile?.popExperience(ply as ServerPlayer) } + val outputSlot = OutputSlot(tile?.outputContainer ?: SimpleContainer(1), 0) { tile?.popExperience(player as ServerPlayer) } val progressGauge = ProgressGaugeWidget(this, tile) val itemConfig = ItemConfigPlayerInput(this, tile?.itemConfig, allowPush = true) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/tech/PoweredFurnaceMenu.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/tech/PoweredFurnaceMenu.kt index c728c159f..b567e9ac3 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/tech/PoweredFurnaceMenu.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/tech/PoweredFurnaceMenu.kt @@ -4,7 +4,7 @@ import net.minecraft.server.level.ServerPlayer import net.minecraft.world.entity.player.Inventory import ru.dbotthepony.mc.otm.block.entity.tech.PoweredFurnaceBlockEntity import ru.dbotthepony.mc.otm.core.immutableList -import ru.dbotthepony.mc.otm.menu.MachineOutputSlot +import ru.dbotthepony.mc.otm.menu.OutputSlot import ru.dbotthepony.mc.otm.menu.MatteryPoweredMenu import ru.dbotthepony.mc.otm.menu.MatterySlot import ru.dbotthepony.mc.otm.menu.input.BooleanInputWithFeedback @@ -21,7 +21,7 @@ class PoweredFurnaceMenu( tile: PoweredFurnaceBlockEntity? = null ) : MatteryPoweredMenu(MMenus.POWERED_FURNACE, containerID, inventory, tile) { val inputSlots = makeSlots(tile?.inputs, 2, ::MatterySlot) - val outputSlots = makeSlots(tile?.outputs, 2) { c, s -> MachineOutputSlot(c, s) { tile?.popExperience(ply as ServerPlayer) } } + val outputSlots = makeSlots(tile?.outputs, 2) { c, s -> OutputSlot(c, s) { tile?.popExperience(player as ServerPlayer) } } val progressGauge = immutableList(2) { ProgressGaugeWidget(this, tile?.jobEventLoops?.get(it)) } val itemConfig = ItemConfigPlayerInput(this, tile?.itemConfig, allowPush = true) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/tech/TwinPlatePressMenu.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/tech/TwinPlatePressMenu.kt index e5168c070..8533dd864 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/tech/TwinPlatePressMenu.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/tech/TwinPlatePressMenu.kt @@ -4,7 +4,7 @@ import net.minecraft.server.level.ServerPlayer import net.minecraft.world.SimpleContainer import net.minecraft.world.entity.player.Inventory import ru.dbotthepony.mc.otm.block.entity.tech.PlatePressBlockEntity -import ru.dbotthepony.mc.otm.menu.MachineOutputSlot +import ru.dbotthepony.mc.otm.menu.OutputSlot import ru.dbotthepony.mc.otm.menu.MatteryPoweredMenu import ru.dbotthepony.mc.otm.menu.MatterySlot import ru.dbotthepony.mc.otm.menu.input.BooleanInputWithFeedback @@ -21,7 +21,7 @@ class TwinPlatePressMenu @JvmOverloads constructor( tile: PlatePressBlockEntity? = null ) : MatteryPoweredMenu(MMenus.TWIN_PLATE_PRESS, containerID, inventory, tile) { val inputSlots = makeSlots(tile?.inputContainer ?: SimpleContainer(2), ::MatterySlot) - val outputSlots = makeSlots(tile?.outputContainer ?: SimpleContainer(2)) { a, b -> MachineOutputSlot(a, b) { tile?.popExperience(ply as ServerPlayer) } } + val outputSlots = makeSlots(tile?.outputContainer ?: SimpleContainer(2)) { a, b -> OutputSlot(a, b) { tile?.popExperience(player as ServerPlayer) } } val progressGauge0 = ProgressGaugeWidget(this, tile?.jobEventLoops?.get(0)) val progressGauge1 = ProgressGaugeWidget(this, tile?.jobEventLoops?.get(1)) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/recipe/IMatteryRecipe.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/recipe/IMatteryRecipe.kt new file mode 100644 index 000000000..ec41ae375 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/recipe/IMatteryRecipe.kt @@ -0,0 +1,41 @@ +package ru.dbotthepony.mc.otm.recipe + +import net.minecraft.core.NonNullList +import net.minecraft.world.Container +import net.minecraft.world.item.ItemStack +import net.minecraft.world.item.crafting.Ingredient +import net.minecraft.world.item.crafting.Recipe +import net.minecraft.world.item.crafting.RecipeSerializer +import ru.dbotthepony.mc.otm.data.Codec2RecipeSerializer + +// overrides all methods to fix Kotlin bug related to implementation delegation (to allow easy optics) +// https://youtrack.jetbrains.com/issue/KT-55080/Change-the-behavior-of-inheritance-delegation-to-delegates-implementing-Java-interfaces-with-default-methods +interface IMatteryRecipe : Recipe { + override fun getRemainingItems(p_44004_: C): NonNullList { + return super.getRemainingItems(p_44004_) + } + + override fun getIngredients(): NonNullList { + return super.getIngredients() + } + + override fun isSpecial(): Boolean { + return super.isSpecial() + } + + override fun showNotification(): Boolean { + return super.showNotification() + } + + override fun getGroup(): String { + return super.getGroup() + } + + override fun getToastSymbol(): ItemStack { + return super.getToastSymbol() + } + + override fun isIncomplete(): Boolean { + return super.isIncomplete() + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/recipe/IngredientMatrix.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/recipe/IngredientMatrix.kt new file mode 100644 index 000000000..41f9c019a --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/recipe/IngredientMatrix.kt @@ -0,0 +1,151 @@ +package ru.dbotthepony.mc.otm.recipe + +import net.minecraft.core.NonNullList +import net.minecraft.world.inventory.CraftingContainer +import net.minecraft.world.item.crafting.Ingredient +import ru.dbotthepony.mc.otm.container.get +import ru.dbotthepony.mc.otm.core.collect.allEqual +import ru.dbotthepony.mc.otm.core.collect.any +import ru.dbotthepony.mc.otm.core.collect.flatMap +import ru.dbotthepony.mc.otm.core.collect.map +import ru.dbotthepony.mc.otm.core.isActuallyEmpty +import ru.dbotthepony.mc.otm.core.isNotEmpty +import java.util.function.Predicate + +interface IIngredientMatrix : Predicate, Iterable { + val width: Int + val height: Int + val isEmpty: Boolean + get() = width == 0 || height == 0 + + val isIncomplete: Boolean + get() = iterator().any { it.isActuallyEmpty } + + operator fun get(column: Int, row: Int): Ingredient + + operator fun get(column: Int, row: Int, flop: Boolean): Ingredient { + return if (flop) + get(width - column - 1, row) + else + get(column, row) + } + + override fun iterator(): Iterator { + return (0 until width).iterator().flatMap { x -> + (0 until height).iterator().map { y -> + get(x, y) + } + } + } + + val ingredients: NonNullList get() { + val result = NonNullList.createWithCapacity(width * height) + + for (x in 0 until width) { + for (y in 0 until height) { + result.add(get(x, y)) + } + } + + return result + } + + fun test(t: CraftingContainer, fromColumn: Int, fromRow: Int, flop: Boolean): Boolean { + if (t.width - fromColumn < width || t.height - fromRow < height) + return false + + for (column in 0 until width) + for (row in 0 until height) + if (!this[column, row, flop].test(t[fromColumn + column, fromRow + row, flop])) + return false + + return true + } + + fun preemptiveTest(t: CraftingContainer, fromColumn: Int, fromRow: Int, flop: Boolean): Boolean { + if (t.width - fromColumn < width || t.height - fromRow < height) + return false + + for (column in 0 until width) { + for (row in 0 until height) { + val item = t[fromColumn + column, fromRow + row, flop] + val ingredient = this[column, row, flop] + + if (!ingredient.test(item) && item.isNotEmpty) { + return false + } + } + } + + return true + } + + override fun test(t: CraftingContainer): Boolean { + if (t.width < width || t.height < height) + return false + + for (column in 0 .. t.width - width) + for (row in 0 .. t.height - height) + if (test(t, column, row, false) || test(t, column, row, true)) + return true + + return false + } + + fun preemptiveTest(t: CraftingContainer): Boolean { + if (t.width < width || t.height < height) + return false + + for (column in 0 .. t.width - width) + for (row in 0 .. t.height - height) + if (preemptiveTest(t, column, row, false) || preemptiveTest(t, column, row, true)) + return true + + return false + } + + companion object : IIngredientMatrix { + override val width: Int + get() = 0 + override val height: Int + get() = 0 + + override fun get(column: Int, row: Int): Ingredient { + return Ingredient.EMPTY + } + } +} + +class IngredientMatrix(override val width: Int, override val height: Int) : IIngredientMatrix { + private val data = Array(width * height) { Ingredient.EMPTY } + + override fun get(column: Int, row: Int): Ingredient { + require(column in 0 until width) { "Column out of bounds: $column (matrix width: $width)" } + require(row in 0 until height) { "Row out of bounds: $row > (matrix height: $height)" } + return data[column + row * width] + } + + operator fun set(column: Int, row: Int, value: Ingredient) { + require(column in 0 until width) { "Column out of bounds: $column (matrix width: $width)" } + require(row in 0 until height) { "Row out of bounds: $row > (matrix height: $height)" } + data[column + row * width] = value + } + + companion object { + fun of(vararg values: Collection): IngredientMatrix { + if (!values.iterator().map { it.size }.allEqual()) { + throw IllegalArgumentException("One or more rows have different number of columns than the rest") + } + + val result = IngredientMatrix(values.first().size, values.size) + + for ((y, l) in values.withIndex()) { + for ((x, i) in l.withIndex()) { + result[x, y] = i + } + } + + return result + } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/recipe/MatterEntanglerRecipe.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/recipe/MatterEntanglerRecipe.kt new file mode 100644 index 000000000..15024b3b1 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/recipe/MatterEntanglerRecipe.kt @@ -0,0 +1,170 @@ +package ru.dbotthepony.mc.otm.recipe + +import com.mojang.serialization.Codec +import com.mojang.serialization.codecs.RecordCodecBuilder +import net.minecraft.core.NonNullList +import net.minecraft.core.RegistryAccess +import net.minecraft.core.UUIDUtil +import net.minecraft.data.recipes.FinishedRecipe +import net.minecraft.resources.ResourceLocation +import net.minecraft.world.inventory.CraftingContainer +import net.minecraft.world.item.ItemStack +import net.minecraft.world.item.crafting.Ingredient +import net.minecraft.world.item.crafting.Recipe +import net.minecraft.world.item.crafting.RecipeSerializer +import net.minecraft.world.item.crafting.RecipeType +import net.minecraft.world.level.Level +import ru.dbotthepony.mc.otm.OverdriveThatMatters +import ru.dbotthepony.mc.otm.capability.matter.matter +import ru.dbotthepony.mc.otm.capability.matteryEnergy +import ru.dbotthepony.mc.otm.container.iterator +import ru.dbotthepony.mc.otm.core.collect.filterNotNull +import ru.dbotthepony.mc.otm.core.collect.forEach +import ru.dbotthepony.mc.otm.core.collect.map +import ru.dbotthepony.mc.otm.core.math.Decimal +import ru.dbotthepony.mc.otm.core.nbt.set +import ru.dbotthepony.mc.otm.core.tagNotNull +import ru.dbotthepony.mc.otm.data.Codec2RecipeSerializer +import ru.dbotthepony.mc.otm.data.DecimalCodec +import ru.dbotthepony.mc.otm.data.IngredientMatrixCodec +import ru.dbotthepony.mc.otm.data.minRange +import ru.dbotthepony.mc.otm.registry.MRecipes +import java.util.Optional +import java.util.UUID +import kotlin.jvm.optionals.getOrElse + +interface IMatterEntanglerRecipe : IMatteryRecipe { + val matter: Decimal + val ticks: Double + val ingredients: IIngredientMatrix + val result: ItemStack + + fun preemptivelyMatches(container: CraftingContainer, level: Level): Boolean +} + +open class MatterEntanglerRecipe( + private val id: ResourceLocation, + override val ingredients: IIngredientMatrix, + override val matter: Decimal, + override val ticks: Double, + override val result: ItemStack, + val uuidKey: String = "uuid", + val fixedUuid: Optional = Optional.empty() +) : IMatterEntanglerRecipe { + override fun matches(container: CraftingContainer, level: Level): Boolean { + if (isIncomplete) return false + return ingredients.test(container) + } + + override fun preemptivelyMatches(container: CraftingContainer, level: Level): Boolean { + if (isIncomplete) return false + return ingredients.preemptiveTest(container) + } + + override fun assemble(container: CraftingContainer, registry: RegistryAccess): ItemStack { + return result.copy().also { + it.tagNotNull[uuidKey] = fixedUuid.getOrElse { UUID.randomUUID() } + } + } + + override fun canCraftInDimensions(width: Int, height: Int): Boolean { + return width >= ingredients.width && height >= ingredients.height + } + + override fun getResultItem(registry: RegistryAccess): ItemStack { + return result + } + + override fun getId(): ResourceLocation { + return id + } + + override fun getSerializer(): RecipeSerializer<*> { + return SERIALIZER + } + + override fun getType(): RecipeType<*> { + return MRecipes.MATTER_ENTANGLER + } + + override fun getIngredients(): NonNullList { + return ingredients.ingredients + } + + override fun isIncomplete(): Boolean { + return result.isEmpty || ingredients.isIncomplete + } + + override fun isSpecial(): Boolean { + return true + } + + fun toFinished(): FinishedRecipe { + return SERIALIZER.toFinished(this) + } + + fun energetic() = Energy(this) + fun matter() = Matter(this) + + open class Energy(val parent: MatterEntanglerRecipe) : IMatterEntanglerRecipe by parent { + override fun assemble(container: CraftingContainer, registry: RegistryAccess): ItemStack { + return parent.assemble(container, registry).also { result -> + container.iterator().map { it.matteryEnergy }.filterNotNull().forEach { + result.matteryEnergy!!.batteryLevel += it.batteryLevel + } + } + } + + fun toFinished(): FinishedRecipe { + return ENERGY_SERIALIZER.toFinished(this) + } + + override fun getSerializer(): RecipeSerializer<*> { + return ENERGY_SERIALIZER + } + } + + open class Matter(val parent: MatterEntanglerRecipe) : IMatterEntanglerRecipe by parent { + override fun assemble(container: CraftingContainer, registry: RegistryAccess): ItemStack { + return parent.assemble(container, registry).also { result -> + container.iterator().map { it.matter }.filterNotNull().forEach { + result.matter!!.storedMatter += it.storedMatter + } + } + } + + fun toFinished(): FinishedRecipe { + return MATTER_SERIALIZER.toFinished(this) + } + + override fun getSerializer(): RecipeSerializer<*> { + return MATTER_SERIALIZER + } + } + + companion object { + val SERIALIZER = Codec2RecipeSerializer( + MatterEntanglerRecipe( + ResourceLocation(OverdriveThatMatters.MOD_ID, "null"), + IIngredientMatrix.Companion, + Decimal.ZERO, + 0.0, + ItemStack.EMPTY, + ) + ) { context -> + RecordCodecBuilder.create { + it.group( + IngredientMatrixCodec.fieldOf("ingredients").forGetter(MatterEntanglerRecipe::ingredients), + DecimalCodec.minRange(Decimal.ZERO).fieldOf("matter").forGetter(MatterEntanglerRecipe::matter), + Codec.DOUBLE.minRange(0.0).fieldOf("ticks").forGetter(MatterEntanglerRecipe::ticks), + ItemStack.CODEC.fieldOf("result").forGetter(MatterEntanglerRecipe::result), + Codec.STRING.optionalFieldOf("uuidKey", "uuid").forGetter(MatterEntanglerRecipe::uuidKey), + UUIDUtil.STRING_CODEC.optionalFieldOf("fixedUuid").forGetter(MatterEntanglerRecipe::fixedUuid) + ).apply(it) { a, b, c, d, e, f -> MatterEntanglerRecipe(context.id, a, b, c, d, e, f) } + } + } + + val ENERGY_SERIALIZER = SERIALIZER.xmap(::Energy, Energy::parent) + val MATTER_SERIALIZER = SERIALIZER.xmap(::Matter, Matter::parent) + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/recipe/PainterRecipe.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/recipe/PainterRecipe.kt index 61cda92bd..0e0d4ee77 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/recipe/PainterRecipe.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/recipe/PainterRecipe.kt @@ -125,7 +125,7 @@ class PainterRecipe( Codec.list(DyeColor.CODEC).xmap({ it.associateWith { 1 } }, { ArrayList(it.keys) }) to Predicate { it.values.all { it == 1 } }, Codec.unboundedMap(DyeColor.CODEC, Codec.INT.minRange(1)) to Predicate { true } ).fieldOf("dyes").forGetter(PainterRecipe::dyes), - ).apply(it) { a, b, c -> PainterRecipe(context.invoke(), a, b, c) } + ).apply(it) { a, b, c -> PainterRecipe(context.id, a, b, c) } } } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/recipe/PlatePressRecipe.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/recipe/PlatePressRecipe.kt index 08bc643f7..49ad6efd3 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/recipe/PlatePressRecipe.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/recipe/PlatePressRecipe.kt @@ -89,7 +89,7 @@ class PlatePressRecipe( Codec.INT.minRange(1).optionalFieldOf("count", 1).forGetter(PlatePressRecipe::count), Codec.INT.minRange(0).optionalFieldOf("workTime", 200).forGetter(PlatePressRecipe::workTime), FloatProvider.CODEC.optionalFieldOf("experience", ConstantFloat.ZERO).forGetter(PlatePressRecipe::experience) - ).apply(it) { a, b, c, d, e -> PlatePressRecipe(context.invoke(), a, b, c, d, e) } + ).apply(it) { a, b, c, d, e -> PlatePressRecipe(context.id, a, b, c, d, e) } } } } 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 0a88fdb2a..8172c022a 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlockEntities.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlockEntities.kt @@ -72,6 +72,7 @@ object MBlockEntities { val INFINITE_WATER_SOURCE by register(MNames.INFINITE_WATER_SOURCE, ::InfiniteWaterSourceBlockEntity, MBlocks::INFINITE_WATER_SOURCE) val DEV_CHEST by register(MNames.DEV_CHEST, ::DevChestBlockEntity, MBlocks::DEV_CHEST) val PAINTER by register(MNames.PAINTER, ::PainterBlockEntity, MBlocks::PAINTER) + val MATTER_ENTANGLER by register(MNames.MATTER_ENTANGLER, ::MatterEntanglerBlockEntity, MBlocks::MATTER_ENTANGLER) val POWERED_FURNACE: BlockEntityType by registry.register(MNames.POWERED_FURNACE) { BlockEntityType.Builder.of({ a, b -> MBlocks.POWERED_FURNACE.newBlockEntity(a, b) }, MBlocks.POWERED_FURNACE).build(null) } val POWERED_BLAST_FURNACE: BlockEntityType by registry.register(MNames.POWERED_BLAST_FURNACE) { BlockEntityType.Builder.of({ a, b -> MBlocks.POWERED_BLAST_FURNACE.newBlockEntity(a, b) }, MBlocks.POWERED_BLAST_FURNACE).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 4772c75de..c4218f292 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlocks.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlocks.kt @@ -64,6 +64,7 @@ import ru.dbotthepony.mc.otm.block.tech.AndroidChargerBlock import ru.dbotthepony.mc.otm.block.tech.CobblerBlock import ru.dbotthepony.mc.otm.block.tech.EssenceStorageBlock import ru.dbotthepony.mc.otm.block.decorative.PainterBlock +import ru.dbotthepony.mc.otm.block.matter.MatterEntanglerBlock import ru.dbotthepony.mc.otm.block.tech.PoweredFurnaceBlock import ru.dbotthepony.mc.otm.config.MachinesConfig import ru.dbotthepony.mc.otm.core.TranslatableComponent @@ -102,6 +103,7 @@ object MBlocks { val ESSENCE_STORAGE: EssenceStorageBlock by registry.register(MNames.ESSENCE_STORAGE) { EssenceStorageBlock() } val MATTER_RECONSTRUCTOR: MatterReconstructorBlock by registry.register(MNames.MATTER_RECONSTRUCTOR) { MatterReconstructorBlock() } val PAINTER: PainterBlock by registry.register(MNames.PAINTER) { PainterBlock() } + val MATTER_ENTANGLER: MatterEntanglerBlock by registry.register(MNames.MATTER_ENTANGLER) { MatterEntanglerBlock() } 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 42e6ab499..ea35f1291 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MItems.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MItems.kt @@ -152,10 +152,11 @@ object MItems { } val PAINTER: BlockItem by registry.register(MNames.PAINTER) { BlockItem(MBlocks.PAINTER, DEFAULT_PROPERTIES) } + val MATTER_ENTANGLER: BlockItem by registry.register(MNames.MATTER_ENTANGLER) { BlockItem(MBlocks.MATTER_ENTANGLER, DEFAULT_PROPERTIES) } val MACHINES = SupplierList( ::ANDROID_STATION, ::ANDROID_CHARGER, ::BATTERY_BANK, ::MATTER_DECOMPOSER, ::MATTER_CAPACITOR_BANK, ::MATTER_CABLE, ::PATTERN_STORAGE, - ::MATTER_SCANNER, ::MATTER_PANEL, ::MATTER_REPLICATOR, ::MATTER_BOTTLER, ::ENERGY_COUNTER, ::CHEMICAL_GENERATOR, + ::MATTER_SCANNER, ::MATTER_PANEL, ::MATTER_REPLICATOR, ::MATTER_BOTTLER, ::MATTER_ENTANGLER, ::ENERGY_COUNTER, ::CHEMICAL_GENERATOR, ::MATTER_RECYCLER, ::PLATE_PRESS, ::TWIN_PLATE_PRESS, ::POWERED_FURNACE, ::POWERED_BLAST_FURNACE, ::POWERED_SMOKER, ::STORAGE_BUS, ::STORAGE_IMPORTER, ::STORAGE_EXPORTER, ::DRIVE_VIEWER, 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 fbc7042df..1c3af7f75 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MMenus.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MMenus.kt @@ -17,6 +17,7 @@ import ru.dbotthepony.mc.otm.client.screen.matter.MatterReconstructorScreen 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 +import ru.dbotthepony.mc.otm.client.screen.matter.MatterEntanglerScreen import ru.dbotthepony.mc.otm.client.screen.matter.MatterPanelScreen import ru.dbotthepony.mc.otm.client.screen.matter.MatterRecyclerScreen import ru.dbotthepony.mc.otm.client.screen.matter.MatterReplicatorScreen @@ -36,7 +37,7 @@ import ru.dbotthepony.mc.otm.client.screen.tech.CobblerScreen import ru.dbotthepony.mc.otm.client.screen.tech.EnergyCounterScreen import ru.dbotthepony.mc.otm.client.screen.tech.EnergyServoScreen import ru.dbotthepony.mc.otm.client.screen.tech.EssenceStorageScreen -import ru.dbotthepony.mc.otm.client.screen.tech.PainterScreen +import ru.dbotthepony.mc.otm.client.screen.decorative.PainterScreen import ru.dbotthepony.mc.otm.client.screen.tech.PlatePressScreen import ru.dbotthepony.mc.otm.client.screen.tech.PoweredFurnaceScreen import ru.dbotthepony.mc.otm.client.screen.tech.TwinPlatePressScreen @@ -48,6 +49,7 @@ import ru.dbotthepony.mc.otm.menu.matter.MatterReconstructorMenu import ru.dbotthepony.mc.otm.menu.matter.MatterBottlerMenu import ru.dbotthepony.mc.otm.menu.matter.MatterCapacitorBankMenu import ru.dbotthepony.mc.otm.menu.matter.MatterDecomposerMenu +import ru.dbotthepony.mc.otm.menu.matter.MatterEntanglerMenu import ru.dbotthepony.mc.otm.menu.matter.MatterPanelMenu import ru.dbotthepony.mc.otm.menu.matter.MatterRecyclerMenu import ru.dbotthepony.mc.otm.menu.matter.MatterReplicatorMenu @@ -67,7 +69,7 @@ import ru.dbotthepony.mc.otm.menu.tech.CobblerMenu import ru.dbotthepony.mc.otm.menu.tech.EnergyCounterMenu import ru.dbotthepony.mc.otm.menu.tech.EnergyServoMenu import ru.dbotthepony.mc.otm.menu.tech.EssenceStorageMenu -import ru.dbotthepony.mc.otm.menu.tech.PainterMenu +import ru.dbotthepony.mc.otm.menu.decorative.PainterMenu import ru.dbotthepony.mc.otm.menu.tech.PlatePressMenu import ru.dbotthepony.mc.otm.menu.tech.PoweredFurnaceMenu import ru.dbotthepony.mc.otm.menu.tech.TwinPlatePressMenu @@ -103,6 +105,7 @@ object MMenus { val ITEM_REPAIER: MenuType by registry.register(MNames.MATTER_RECONSTRUCTOR) { MenuType(::MatterReconstructorMenu, FeatureFlags.VANILLA_SET) } val FLUID_TANK: MenuType by registry.register(MNames.FLUID_TANK) { MenuType(::FluidTankMenu, FeatureFlags.VANILLA_SET) } val PAINTER: MenuType by registry.register(MNames.PAINTER) { MenuType(::PainterMenu, FeatureFlags.VANILLA_SET) } + val MATTER_ENTANGLER: MenuType by registry.register(MNames.MATTER_ENTANGLER) { MenuType(::MatterEntanglerMenu, FeatureFlags.VANILLA_SET) } val STORAGE_BUS: MenuType by registry.register(MNames.STORAGE_BUS) { MenuType(::StorageBusMenu, FeatureFlags.VANILLA_SET) } val STORAGE_IMPORTER_EXPORTER: MenuType by registry.register(MNames.STORAGE_IMPORTER) { MenuType(::StorageImporterExporterMenu, FeatureFlags.VANILLA_SET) } @@ -146,6 +149,7 @@ object MMenus { MenuScreens.register(FLUID_TANK, ::FluidTankScreen) MenuScreens.register(POWERED_FURNACE, ::PoweredFurnaceScreen) MenuScreens.register(PAINTER, ::PainterScreen) + MenuScreens.register(MATTER_ENTANGLER, ::MatterEntanglerScreen) } } } 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 cc48bc828..260392508 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MNames.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MNames.kt @@ -16,6 +16,7 @@ object MNames { const val INFINITE_WATER_SOURCE = "infinite_water_source" const val DEV_CHEST = "dev_chest" const val PAINTER = "painter" + const val MATTER_ENTANGLER = "matter_entangler" // blocks const val ANDROID_STATION = "android_station" diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MRecipes.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MRecipes.kt index 3894c5b2e..e5f4b98ee 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MRecipes.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MRecipes.kt @@ -10,6 +10,8 @@ import net.minecraftforge.registries.RegistryObject import ru.dbotthepony.mc.otm.OverdriveThatMatters import ru.dbotthepony.mc.otm.recipe.EnergyContainerRecipe import ru.dbotthepony.mc.otm.recipe.ExplosiveHammerPrimingRecipe +import ru.dbotthepony.mc.otm.recipe.IMatterEntanglerRecipe +import ru.dbotthepony.mc.otm.recipe.MatterEntanglerRecipe import ru.dbotthepony.mc.otm.recipe.PainterRecipe import ru.dbotthepony.mc.otm.recipe.PlatePressRecipe import ru.dbotthepony.mc.otm.recipe.UpgradeRecipe @@ -38,6 +40,7 @@ object MRecipes { val PLATE_PRESS by register("plate_press") val PAINTER by register("painter") + val MATTER_ENTANGLER by register("matter_entangler") init { serializers.register("plate_press") { PlatePressRecipe.SERIALIZER } @@ -45,5 +48,8 @@ object MRecipes { serializers.register("upgrade") { UpgradeRecipe.Companion } serializers.register("hammer_priming") { ExplosiveHammerPrimingRecipe.Companion } serializers.register("painter") { PainterRecipe.SERIALIZER } + serializers.register("matter_entangler") { MatterEntanglerRecipe.SERIALIZER } + serializers.register("matter_entangler_energetic") { MatterEntanglerRecipe.ENERGY_SERIALIZER } + serializers.register("matter_entangler_matter") { MatterEntanglerRecipe.MATTER_SERIALIZER } } }