Matter entangler, ingredient matrix, shadow containers, quantum battery recipes, more JEI compat

This commit is contained in:
DBotThePony 2023-08-17 22:26:43 +07:00
parent 04524db1a5
commit e6758f1e27
Signed by: DBot
GPG Key ID: DCC23B5715498507
61 changed files with 1480 additions and 152 deletions

View File

@ -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.addBlastingRecipes
import ru.dbotthepony.mc.otm.datagen.recipes.addCraftingTableRecipes import ru.dbotthepony.mc.otm.datagen.recipes.addCraftingTableRecipes
import ru.dbotthepony.mc.otm.datagen.recipes.addDecorativesRecipes 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.addPlatePressRecipes
import ru.dbotthepony.mc.otm.datagen.recipes.addShapelessRecipes import ru.dbotthepony.mc.otm.datagen.recipes.addShapelessRecipes
import ru.dbotthepony.mc.otm.datagen.recipes.addOreSmeltingRecipes import ru.dbotthepony.mc.otm.datagen.recipes.addOreSmeltingRecipes
@ -564,6 +565,7 @@ object DataGen {
addShapelessRecipes(consumer) addShapelessRecipes(consumer)
addOreSmeltingRecipes(consumer) addOreSmeltingRecipes(consumer)
addPainterRecipes(consumer) addPainterRecipes(consumer)
addMatterEntanglerRecipes(consumer)
} }
addPlatePressRecipes(recipeProvider) addPlatePressRecipes(recipeProvider)

View File

@ -432,6 +432,7 @@ private fun blocks(provider: MatteryLanguageProvider) {
add(MBlocks.DEV_CHEST, "Dev Chest") add(MBlocks.DEV_CHEST, "Dev Chest")
add(MBlocks.DEV_CHEST, "desc", "Contains all items present in game") add(MBlocks.DEV_CHEST, "desc", "Contains all items present in game")
add(MBlocks.PAINTER, "Painting Table") add(MBlocks.PAINTER, "Painting Table")
add(MBlocks.MATTER_ENTANGLER, "Matter Entangler")
add(MBlocks.FLUID_TANK, "Fluid Tank") add(MBlocks.FLUID_TANK, "Fluid Tank")
add(MBlocks.FLUID_TANK, "named", "Fluid Tank (%s)") add(MBlocks.FLUID_TANK, "named", "Fluid Tank (%s)")
@ -713,6 +714,8 @@ private fun gui(provider: MatteryLanguageProvider) {
with(provider.english) { with(provider.english) {
gui("quicksearch", "Quick search...") gui("quicksearch", "Quick search...")
gui("energy_required", "Energy required: %s")
gui("insert_priority", "Insert priority") gui("insert_priority", "Insert priority")
gui("extract_priority", "Extract priority") gui("extract_priority", "Extract priority")
gui("increase", "Increase") gui("increase", "Increase")

View File

@ -434,6 +434,7 @@ private fun blocks(provider: MatteryLanguageProvider) {
add(MBlocks.DEV_CHEST, "Сундук разработчика") add(MBlocks.DEV_CHEST, "Сундук разработчика")
add(MBlocks.DEV_CHEST, "desc", "Хранит все предметы, которые есть в игре") add(MBlocks.DEV_CHEST, "desc", "Хранит все предметы, которые есть в игре")
add(MBlocks.PAINTER, "Стол маляра") add(MBlocks.PAINTER, "Стол маляра")
add(MBlocks.MATTER_ENTANGLER, "Квантовый запутыватель материи")
add(MBlocks.FLUID_TANK, "Жидкостный бак") add(MBlocks.FLUID_TANK, "Жидкостный бак")
add(MBlocks.FLUID_TANK, "named", "Жидкостный бак (%s)") add(MBlocks.FLUID_TANK, "named", "Жидкостный бак (%s)")

View File

@ -134,6 +134,7 @@ fun addLootTables(lootTables: LootTables) {
lootTables.tile(MBlocks.MATTER_RECONSTRUCTOR) lootTables.tile(MBlocks.MATTER_RECONSTRUCTOR)
lootTables.tile(MBlocks.FLUID_TANK) lootTables.tile(MBlocks.FLUID_TANK)
lootTables.tile(MBlocks.PAINTER) lootTables.tile(MBlocks.PAINTER)
lootTables.tile(MBlocks.MATTER_ENTANGLER)
lootTables.tile(MBlocks.ENERGY_SERVO) lootTables.tile(MBlocks.ENERGY_SERVO)
lootTables.tile(MBlocks.ENERGY_COUNTER) lootTables.tile(MBlocks.ENERGY_COUNTER)

View File

@ -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<FinishedRecipe>) {
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()
)
}

View File

@ -162,6 +162,7 @@ fun addTags(tagsProvider: TagsProvider) {
MBlocks.PLATE_PRESS, MBlocks.PLATE_PRESS,
MBlocks.TWIN_PLATE_PRESS, MBlocks.TWIN_PLATE_PRESS,
MBlocks.MATTER_RECYCLER, MBlocks.MATTER_RECYCLER,
MBlocks.MATTER_ENTANGLER,
MBlocks.POWERED_FURNACE, MBlocks.POWERED_FURNACE,
MBlocks.POWERED_SMOKER, MBlocks.POWERED_SMOKER,

View File

@ -306,8 +306,10 @@ abstract class MachineJobEventLoop<JobType : IJob>(val codec: Codec<JobType>) :
} }
} }
private var lastTickWasError = false
val isUnableToProcess: Boolean val isUnableToProcess: Boolean
get() = throttleTicks > 0 get() = throttleTicks > 0 || lastTickWasError
val workProgress: Float val workProgress: Float
get() { get() {
@ -392,6 +394,8 @@ abstract class MachineJobEventLoop<JobType : IJob>(val codec: Codec<JobType>) :
isIdling = false isIdling = false
} }
lastTickWasError = false
if (isIdling) { if (isIdling) {
workingTicksAnim = 0 workingTicksAnim = 0
errorTicksAnim = 0 errorTicksAnim = 0
@ -430,6 +434,7 @@ abstract class MachineJobEventLoop<JobType : IJob>(val codec: Codec<JobType>) :
idleReason = IdleReason.POWER idleReason = IdleReason.POWER
isIdling = true isIdling = true
idleTicksAnim++ idleTicksAnim++
lastTickWasError = true
break break
} }
@ -467,6 +472,7 @@ abstract class MachineJobEventLoop<JobType : IJob>(val codec: Codec<JobType>) :
if (!status.success) { if (!status.success) {
throttleTicks += status.throttleTicks throttleTicks += status.throttleTicks
lastTickWasError = true
if (status.idleReason != null) { if (status.idleReason != null) {
idleReason = status.idleReason idleReason = status.idleReason
@ -505,6 +511,7 @@ abstract class MachineJobEventLoop<JobType : IJob>(val codec: Codec<JobType>) :
errorTicksAnim = 0 errorTicksAnim = 0
} else { } else {
throttleTicks += status.throttleTicks throttleTicks += status.throttleTicks
lastTickWasError = true
if (status.idleReason != null) { if (status.idleReason != null) {
idleReason = status.idleReason idleReason = status.idleReason

View File

@ -17,7 +17,7 @@ import ru.dbotthepony.mc.otm.core.immutableList
import ru.dbotthepony.mc.otm.core.immutableMap import ru.dbotthepony.mc.otm.core.immutableMap
import ru.dbotthepony.mc.otm.core.nbt.mapPresent import ru.dbotthepony.mc.otm.core.nbt.mapPresent
import ru.dbotthepony.mc.otm.core.nbt.set 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 ru.dbotthepony.mc.otm.registry.MBlockEntities
import java.util.* import java.util.*

View File

@ -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<MatterEntanglerBlockEntity.Job>(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<Job> = 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<Job>, 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<Job>, id: Int) {
if (!output.fullyAddItem(status.job.itemStack)) {
status.noItem()
}
}
override fun computeNextJob(id: Int): JobContainer<Job> {
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
)
)
}
}

View File

@ -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 <T : BlockEntity?> getTicker(p_153212_: Level, p_153213_: BlockState, p_153214_: BlockEntityType<T>): BlockEntityTicker<T>? {
if (p_153212_.isClientSide) return null
return BlockEntityTicker { _, _, _, tile -> if (tile is MatterEntanglerBlockEntity) tile.tick() }
}
}

View File

@ -230,6 +230,8 @@ fun Player.items(includeCosmetics: Boolean = true): Iterator<ItemStack> {
matteryPlayer?.let { matteryPlayer?.let {
if (it.hasExopack) { if (it.hasExopack) {
iterators.add(it.exopackContainer.iterator()) iterators.add(it.exopackContainer.iterator())
iterators.add(it.exopackEnergy.parent.iterator())
iterators.add(it.exopackChargeSlots.iterator())
} }
} }

View File

@ -1,6 +1,5 @@
package ru.dbotthepony.mc.otm.client.screen package ru.dbotthepony.mc.otm.client.screen
import com.mojang.blaze3d.platform.InputConstants
import net.minecraft.client.gui.screens.inventory.InventoryScreen import net.minecraft.client.gui.screens.inventory.InventoryScreen
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items import net.minecraft.world.item.Items
@ -276,7 +275,7 @@ class ExopackInventoryScreen(menu: ExopackInventoryMenu) : MatteryScreen<Exopack
curios.x = x curios.x = x
} }
EffectListPanel(this, frame, menu.ply, x - 56f, gridHeight = 6).tick() EffectListPanel(this, frame, menu.player, x - 56f, gridHeight = 6).tick()
return frame return frame
} }

View File

@ -6,7 +6,6 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
import net.minecraft.ChatFormatting import net.minecraft.ChatFormatting
import net.minecraft.client.gui.Font import net.minecraft.client.gui.Font
import net.minecraft.client.gui.GuiGraphics import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.screens.Screen
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen
import net.minecraft.network.chat.Component import net.minecraft.network.chat.Component
import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Inventory
@ -35,6 +34,7 @@ import ru.dbotthepony.mc.otm.client.screen.widget.HorizontalPowerGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.MatterGaugePanel import ru.dbotthepony.mc.otm.client.screen.widget.MatterGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.PatternGaugePanel import ru.dbotthepony.mc.otm.client.screen.widget.PatternGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.PowerGaugePanel import ru.dbotthepony.mc.otm.client.screen.widget.PowerGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.ProfiledMatterGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.ProfiledPowerGaugePanel import ru.dbotthepony.mc.otm.client.screen.widget.ProfiledPowerGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.ProgressGaugePanel import ru.dbotthepony.mc.otm.client.screen.widget.ProgressGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.TallHorizontalProfiledPowerGaugePanel import ru.dbotthepony.mc.otm.client.screen.widget.TallHorizontalProfiledPowerGaugePanel
@ -209,7 +209,7 @@ abstract class MatteryScreen<T : MatteryMenu>(menu: T, inventory: Inventory, tit
if (menu.playerExoSuitSlots.isEmpty()) { if (menu.playerExoSuitSlots.isEmpty()) {
inventoryFrame = FramePanel<MatteryScreen<*>>(this, null, 0f, 0f, INVENTORY_FRAME_WIDTH, INVENTORY_FRAME_HEIGHT, inventory.displayName).also(this::addPanel) inventoryFrame = FramePanel<MatteryScreen<*>>(this, null, 0f, 0f, INVENTORY_FRAME_WIDTH, INVENTORY_FRAME_HEIGHT, inventory.displayName).also(this::addPanel)
inventoryFrame!!.makeHelpButton().addSlotFiltersHelp().also { 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")) it.tooltips.add(TranslatableComponent("otm.gui.help.slot_charging"))
} }
@ -236,7 +236,7 @@ abstract class MatteryScreen<T : MatteryMenu>(menu: T, inventory: Inventory, tit
} else { } else {
inventoryFrame = FramePanel<MatteryScreen<*>>(this, null, 0f, 0f, INVENTORY_FRAME_WIDTH_EXTENDED, BASE_INVENTORY_FRAME_HEIGHT + AbstractSlotPanel.SIZE * inventoryRows, inventory.displayName).also(this::addPanel) inventoryFrame = FramePanel<MatteryScreen<*>>(this, null, 0f, 0f, INVENTORY_FRAME_WIDTH_EXTENDED, BASE_INVENTORY_FRAME_HEIGHT + AbstractSlotPanel.SIZE * inventoryRows, inventory.displayName).also(this::addPanel)
inventoryFrame!!.makeHelpButton().addSlotFiltersHelp().also { 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")) it.tooltips.add(TranslatableComponent("otm.gui.help.slot_charging"))
} }
@ -421,7 +421,9 @@ abstract class MatteryScreen<T : MatteryMenu>(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 MatterGaugePanel(this, gauges, matter).dock = Dock.LEFT
} }

View File

@ -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.client.gui.GuiGraphics
import net.minecraft.network.chat.Component 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.TextComponent
import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.math.RGBAColor 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<PainterMenu>(menu, inventory, title) { class PainterScreen(menu: PainterMenu, inventory: Inventory, title: Component) : MatteryScreen<PainterMenu>(menu, inventory, title) {
inner class Bar(parent: EditablePanel<*>, val dye: DyeColor) : EditablePanel<PainterScreen>(this@PainterScreen, parent, width = 5f) { inner class Bar(parent: EditablePanel<*>, val dye: DyeColor) : EditablePanel<PainterScreen>(this@PainterScreen, parent, width = 5f) {

View File

@ -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<MatterEntanglerMenu>(menu, inventory, title) {
override fun makeMainFrame(): FramePanel<MatteryScreen<*>> {
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
}
}

View File

@ -346,6 +346,10 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
} }
} }
fun dockHorizontal(value: Float) {
dockMargin = dockMargin.copy(left = value, right = value)
}
var dockTop: Float var dockTop: Float
get() = dockMargin.top get() = dockMargin.top
set(value) { set(value) {

View File

@ -41,7 +41,7 @@ class EnergyCounterScreen(menu: EnergyCounterMenu, inventory: Inventory, title:
label.dock = Dock.TOP 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) }) 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.dock = Dock.TOP
button.setDockMargin(4f, 5f, 4f, 0f) button.setDockMargin(4f, 5f, 4f, 0f)

View File

@ -79,9 +79,28 @@ open class MatterGaugePanel<out S : Screen> @JvmOverloads constructor(
} }
GAUGE_BACKGROUND.render(graphics) GAUGE_BACKGROUND.render(graphics)
val height = this.height * (1f - widget.percentage)
if (widget.percentage > 0.01f) { 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.setShader(GameRenderer::getPositionTexShader)
RenderSystem.enableBlend() RenderSystem.enableBlend()
RenderSystem.defaultBlendFunc() RenderSystem.defaultBlendFunc()
@ -98,15 +117,15 @@ open class MatterGaugePanel<out S : Screen> @JvmOverloads constructor(
builder.begin(VertexFormat.Mode.TRIANGLE_FAN, DefaultVertexFormat.POSITION_TEX) builder.begin(VertexFormat.Mode.TRIANGLE_FAN, DefaultVertexFormat.POSITION_TEX)
builder.vertex(matrix, 0f, this.height, 0f).uv(u0, v1).endVertex() builder.vertex(matrix, 0f, height, 0f).uv(u0, v1).endVertex()
builder.vertex(matrix, width, this.height, 0f).uv(u1, v1).endVertex() builder.vertex(matrix, width, height, 0f).uv(u1, v1).endVertex()
for (i in 4 downTo 0) { for (i in 4 downTo 0) {
val sin = sin((System.currentTimeMillis() / 50L + i * 2L).toDouble() / 4.0).toFloat() * wavesStrength.coerceAtMost(4f) 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 cos = cos((System.currentTimeMillis() / 50L + i * 3L + 27L).toDouble() / 4.0).toFloat() * wavesStrength.coerceAtMost(4f)
val thisX = (width * (i / 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( builder.vertex(matrix, thisX, thisY, 0f).uv(
GAUGE_FOREGROUND.partialU((i / 4f) * GAUGE_FOREGROUND.width), GAUGE_FOREGROUND.partialU((i / 4f) * GAUGE_FOREGROUND.width),
@ -115,22 +134,9 @@ open class MatterGaugePanel<out S : Screen> @JvmOverloads constructor(
} }
BufferUploader.drawWithShader(builder.end()) 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 { private fun formatLevel(a: Decimal, b: Decimal): Component {

View File

@ -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<Component>, 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<Component>, 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)))
}
}

View File

@ -69,14 +69,17 @@ class JEIPlugin : IModPlugin {
registration.addRecipeCatalyst(ItemStack(MItems.POWERED_BLAST_FURNACE), RecipeTypes.BLASTING) registration.addRecipeCatalyst(ItemStack(MItems.POWERED_BLAST_FURNACE), RecipeTypes.BLASTING)
registration.addRecipeCatalyst(ItemStack(MItems.POWERED_SMOKER), RecipeTypes.SMOKING) registration.addRecipeCatalyst(ItemStack(MItems.POWERED_SMOKER), RecipeTypes.SMOKING)
registration.addRecipeCatalyst(ItemStack(MItems.ExopackUpgrades.CRAFTING_UPGRADE), RecipeTypes.CRAFTING) 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.PLATE_PRESS), PlatePressRecipeCategory.recipeType)
registration.addRecipeCatalyst(ItemStack(MItems.PAINTER), PainterRecipeCategory.recipeType) registration.addRecipeCatalyst(ItemStack(MItems.PAINTER), PainterRecipeCategory.recipeType)
registration.addRecipeCatalyst(ItemStack(MItems.MATTER_ENTANGLER), MatterEntanglerRecipeCategory.recipeType)
} }
override fun registerCategories(registration: IRecipeCategoryRegistration) { override fun registerCategories(registration: IRecipeCategoryRegistration) {
helpers = registration.jeiHelpers helpers = registration.jeiHelpers
registration.addRecipeCategories(PlatePressRecipeCategory) registration.addRecipeCategories(PlatePressRecipeCategory)
registration.addRecipeCategories(PainterRecipeCategory) registration.addRecipeCategories(PainterRecipeCategory)
registration.addRecipeCategories(MatterEntanglerRecipeCategory)
} }
override fun registerRecipes(registration: IRecipeRegistration) { 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(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(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) { override fun registerRecipeTransferHandlers(registration: IRecipeTransferRegistration) {

View File

@ -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<IMatterEntanglerRecipe>, IDrawable {
private val recipeType = RecipeType.create(OverdriveThatMatters.MOD_ID, MNames.MATTER_ENTANGLER, IMatterEntanglerRecipe::class.java)
override fun getRecipeType(): RecipeType<IMatterEntanglerRecipe> {
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<Component> {
val result = ArrayList<Component>()
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
}
}

View File

@ -43,8 +43,10 @@ object PainterRecipeCategory : IRecipeCategory<PainterRecipe>, IDrawable {
return 56 return 56
} }
private val icon = IGUIRenderable2IDrawable(ItemStackIcon(ItemStack(MItems.PAINTER)))
override fun getIcon(): IDrawable { override fun getIcon(): IDrawable {
return IGUIRenderable2IDrawable(ItemStackIcon(ItemStack(MItems.PAINTER))) return icon
} }
override fun setRecipe(builder: IRecipeLayoutBuilder, recipe: PainterRecipe, focuses: IFocusGroup) { override fun setRecipe(builder: IRecipeLayoutBuilder, recipe: PainterRecipe, focuses: IFocusGroup) {

View File

@ -13,6 +13,14 @@ object MachinesConfig : AbstractConfig("machines") {
energyConsumption = Decimal(15) 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( val MATTER_DECOMPOSER = workerValues(
MNames.MATTER_DECOMPOSER, MNames.MATTER_DECOMPOSER,
energyStorage = Decimal(100_000), energyStorage = Decimal(100_000),

View File

@ -7,6 +7,7 @@ import it.unimi.dsi.fastutil.ints.IntSet
import it.unimi.dsi.fastutil.objects.Object2ObjectFunction import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap
import net.minecraft.world.Container import net.minecraft.world.Container
import net.minecraft.world.inventory.CraftingContainer
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.enchantment.EnchantmentHelper.hasVanishingCurse import net.minecraft.world.item.enchantment.EnchantmentHelper.hasVanishingCurse
import net.minecraftforge.fluids.capability.IFluidHandler 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) 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)
}

View File

@ -4,7 +4,8 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
import it.unimi.dsi.fastutil.ints.IntAVLTreeSet import it.unimi.dsi.fastutil.ints.IntAVLTreeSet
import it.unimi.dsi.fastutil.ints.IntArrayList import it.unimi.dsi.fastutil.ints.IntArrayList
import it.unimi.dsi.fastutil.ints.IntComparators 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.world.item.ItemStack
import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.ListTag import net.minecraft.nbt.ListTag
@ -13,11 +14,16 @@ import net.minecraft.resources.ResourceLocation
import net.minecraft.world.Container import net.minecraft.world.Container
import kotlin.jvm.JvmOverloads import kotlin.jvm.JvmOverloads
import net.minecraft.world.entity.player.Player 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.Item
import net.minecraft.world.item.Items import net.minecraft.world.item.Items
import net.minecraftforge.common.util.INBTSerializable import net.minecraftforge.common.util.INBTSerializable
import net.minecraftforge.registries.ForgeRegistries import net.minecraftforge.registries.ForgeRegistries
import ru.dbotthepony.mc.otm.core.addSorted 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.isNotEmpty
import ru.dbotthepony.mc.otm.core.nbt.map import ru.dbotthepony.mc.otm.core.nbt.map
import ru.dbotthepony.mc.otm.core.nbt.set 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.core.util.VarIntValueCodec
import ru.dbotthepony.mc.otm.network.synchronizer.FieldSynchronizer import ru.dbotthepony.mc.otm.network.synchronizer.FieldSynchronizer
import ru.dbotthepony.mc.otm.network.synchronizer.IField 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.lang.ref.WeakReference
import java.util.* 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.function.Supplier
import java.util.stream.Stream
import java.util.stream.StreamSupport
import kotlin.NoSuchElementException import kotlin.NoSuchElementException
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
@Suppress("UNUSED") @Suppress("UNUSED")
open class MatteryContainer(protected val watcher: Runnable, private val size: Int) : Container, Iterable<ItemStack>, INBTSerializable<Tag?> { open class MatteryContainer(protected val watcher: Runnable, private val size: Int) : Container, Iterable<ItemStack>, INBTSerializable<Tag?>, StackedContentsCompatible {
constructor(size: Int) : this({}, size) constructor(size: Int) : this({}, size)
init { 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 { final override fun removeItem(slot: Int, amount: Int): ItemStack {
if (amount <= 0 || slot < 0 || slot >= size || slots[slot].isEmpty) if (amount <= 0 || slot < 0 || slot >= size || slots[slot].isEmpty)
return ItemStack.EMPTY 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<ItemStack> {
override fun tryAdvance(action: Consumer<in ItemStack>): Boolean {
return parent.tryAdvance {
action.accept(getItem(it))
}
}
override fun trySplit(): java.util.Spliterator<ItemStack>? {
return parent.trySplit()?.let(::Spliterator)
}
override fun estimateSize(): Long {
return parent.estimateSize()
}
override fun characteristics(): Int {
return parent.characteristics()
}
}
private object EmptyIterator : IContainerIterator { private object EmptyIterator : IContainerIterator {
override fun hasNext(): Boolean { override fun hasNext(): Boolean {
return false return false
@ -601,4 +634,29 @@ open class MatteryContainer(protected val watcher: Runnable, private val size: I
return Iterator() return Iterator()
} }
final override fun countItem(item: Item): Int {
return iterator().filter { it.item == item }.count().toInt()
}
final override fun hasAnyOf(set: Set<Item>): Boolean {
return iterator().any { it.item in set }
}
final override fun hasAnyMatching(predicate: Predicate<ItemStack>): Boolean {
return iterator().any(predicate)
}
final override fun spliterator(): java.util.Spliterator<ItemStack> {
if (isEmpty) {
return ObjectSpliterators.emptySpliterator()
}
indicesReferenced = true
return Spliterator(nonEmptyIndices.intSpliterator())
}
fun stream(): Stream<ItemStack> {
return StreamSupport.stream(spliterator(), false)
}
} }

View File

@ -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<ItemStack> {
val i = spliterator()
val result = ArrayList<ItemStack>(i.estimateSize().toInt())
i.forEachRemaining { result.add(it) }
return result
}
}

View File

@ -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<ItemStack>(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 }
}
}
}

View File

@ -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<ItemStack> {
return iterator().toList()
}
companion object {
fun shadow(container: CraftingContainer, slot: Int, itemStack: ItemStack): CraftingContainer {
return ShadowCraftingContainer(container).also { it[slot] = itemStack }
}
}
}

View File

@ -3,9 +3,14 @@ package ru.dbotthepony.mc.otm.core.collect
import it.unimi.dsi.fastutil.objects.ObjectIterators import it.unimi.dsi.fastutil.objects.ObjectIterators
import ru.dbotthepony.mc.otm.core.addAll import ru.dbotthepony.mc.otm.core.addAll
import java.util.Optional import java.util.Optional
import java.util.Spliterator
import java.util.Spliterators
import java.util.function.BinaryOperator import java.util.function.BinaryOperator
import java.util.function.Predicate import java.util.function.Predicate
import java.util.function.Supplier
import java.util.stream.Collector 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, // Purpose of Stream API over Iterators is that it is simple enough for JIT to inline most of it,
// unlike actual Streams. // unlike actual Streams.
@ -370,3 +375,29 @@ fun <T> Iterator<T>.maybe(): T? {
fun <T> emptyIterator(): MutableIterator<T> { fun <T> emptyIterator(): MutableIterator<T> {
return ObjectIterators.emptyIterator() return ObjectIterators.emptyIterator()
} }
fun <T> Iterator<T>.toStream(): Stream<T> {
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(this, 0), false)
}
fun <T> Iterator<T>.allEqual(): Boolean {
if (hasNext()) {
val v = next()
while (hasNext()) {
if (v != next()) {
return false
}
}
return true
}
return false
}
fun <T> Iterator<T>.count(): Long {
var count = 0L
while (hasNext()) count++
return count
}

View File

@ -17,19 +17,64 @@ import org.apache.logging.log4j.LogManager
import ru.dbotthepony.mc.otm.core.set import ru.dbotthepony.mc.otm.core.set
import ru.dbotthepony.mc.otm.core.util.readBinaryJsonWithCodecIndirect import ru.dbotthepony.mc.otm.core.util.readBinaryJsonWithCodecIndirect
import ru.dbotthepony.mc.otm.core.util.writeBinaryJsonWithCodec import ru.dbotthepony.mc.otm.core.util.writeBinaryJsonWithCodec
import java.util.* import kotlin.collections.ArrayDeque
import kotlin.NoSuchElementException
import kotlin.concurrent.getOrSet
class Codec2RecipeSerializer<S : Recipe<*>>(val empty: S?, val codec: Codec<S>) : Codec<S> by codec, RecipeSerializer<S> { class Codec2RecipeSerializer<S : Recipe<*>> private constructor(
constructor(supplier: (() -> ResourceLocation) -> Codec<S>) : this(null, supplier.invoke(::context)) val empty: S?,
constructor(empty: S, supplier: (() -> ResourceLocation) -> Codec<S>) : this(empty, supplier.invoke(::context)) private val id: ArrayDeque<ResourceLocation>,
codec: (Codec2RecipeSerializer<S>.Context) -> Codec<S>,
) : Codec<S>, RecipeSerializer<S> {
constructor(empty: S?, codec: (Codec2RecipeSerializer<S>.Context) -> Codec<S>) : this(empty, ArrayDeque(), codec)
constructor(supplier: (Codec2RecipeSerializer<S>.Context) -> Codec<S>) : 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 <O : Recipe<*>> wrap(other: Codec2RecipeSerializer<O>): Codec<O> {
return object : Codec<O> {
override fun <T : Any> encode(input: O, ops: DynamicOps<T>, prefix: T): DataResult<T> {
try { try {
deck.getOrSet(::LinkedList).addLast(p_44103_) other.id.addLast(this@Context.id)
return other.encode(input, ops, prefix)
} finally {
other.id.removeLast()
}
}
return codec.decode(JsonOps.INSTANCE, p_44104_).get().map( override fun <T : Any> decode(ops: DynamicOps<T>, input: T): DataResult<Pair<O, T>> {
try {
other.id.addLast(this@Context.id)
return other.decode(ops, input)
} finally {
other.id.removeLast()
}
}
}
}
}
override fun <T : Any> encode(input: S, ops: DynamicOps<T>, prefix: T): DataResult<T> {
return codec.encode(input, ops, prefix)
}
override fun <T : Any> decode(ops: DynamicOps<T>, input: T): DataResult<Pair<S, T>> {
return codec.decode(ops, input)
}
fun <O : Recipe<*>> xmap(to: (S) -> O, from: (O) -> S): Codec2RecipeSerializer<O> {
return Codec2RecipeSerializer(empty?.let(to), id) { _ ->
codec.xmap(to, from)
}
}
override fun fromJson(id: ResourceLocation, data: JsonObject): S {
try {
this.id.addLast(id)
return decode(JsonOps.INSTANCE, data).get().map(
{ {
it.first it.first
}, },
@ -38,29 +83,29 @@ class Codec2RecipeSerializer<S : Recipe<*>>(val empty: S?, val codec: Codec<S>)
} }
) )
} finally { } 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 { try {
deck.getOrSet(::LinkedList).addLast(p_44105_) this.id.addLast(id)
return p_44106_.readBinaryJsonWithCodecIndirect(codec) return data.readBinaryJsonWithCodecIndirect(this)
.resultOrPartial { LOGGER.error("Failed to read recipe $p_44105_ from network: $it") }.orElse(null) .resultOrPartial { LOGGER.error("Failed to read recipe $id from network: $it") }.orElse(null)
} finally { } finally {
deck.get().removeLast() this.id.removeLast()
} }
} }
override fun toNetwork(p_44101_: FriendlyByteBuf, p_44102_: S) { override fun toNetwork(data: FriendlyByteBuf, recipe: S) {
p_44101_.writeBinaryJsonWithCodec(codec, p_44102_) data.writeBinaryJsonWithCodec(this, recipe)
} }
fun toFinished(recipe: S): FinishedRecipe { fun toFinished(recipe: S): FinishedRecipe {
return object : FinishedRecipe { return object : FinishedRecipe {
override fun serializeRecipeData(p_125967_: JsonObject) { 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 it as JsonObject
@ -92,33 +137,7 @@ class Codec2RecipeSerializer<S : Recipe<*>>(val empty: S?, val codec: Codec<S>)
} }
} }
companion object : Codec<ResourceLocation> { companion object {
private val deck = ThreadLocal<LinkedList<ResourceLocation>>()
private fun context(): ResourceLocation {
val deck = deck.getOrSet(::LinkedList)
if (deck.isEmpty()) {
throw NoSuchElementException("Context stack is empty")
} else {
return deck.last
}
}
override fun <T : Any> encode(input: ResourceLocation, ops: DynamicOps<T>, prefix: T): DataResult<T> {
return DataResult.success(ops.empty())
}
override fun <T : Any> decode(ops: DynamicOps<T>, input: T): DataResult<Pair<ResourceLocation, T>> {
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()))
}
}
private val LOGGER = LogManager.getLogger() private val LOGGER = LogManager.getLogger()
} }
} }

View File

@ -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<IIngredientMatrix> {
private val ingredientList = Codec.list(IngredientCodec)
override fun <T : Any> encode(input: IIngredientMatrix, ops: DynamicOps<T>, prefix: T): DataResult<T> {
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<String>, val key: Map<Char, Ingredient>)
private val handwrittenCodec = RecordCodecBuilder.create<Handwritten> {
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 <T : Any> decode(ops: DynamicOps<T>, input: T): DataResult<Pair<IIngredientMatrix, T>> {
return ops.getList(input).get().map(
{
val lines = ArrayList<DataResult<List<Ingredient>>>()
it.accept {
lines.add(ingredientList.decode(ops, it).map { it.first })
}
val errors = ArrayList<Supplier<String>>()
val ingredients = ArrayList<List<Ingredient>>()
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()))
}
}
)
}
}

View File

@ -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
}

View File

@ -63,7 +63,7 @@ import kotlin.collections.forEach
import kotlin.collections.iterator import kotlin.collections.iterator
import kotlin.collections.set 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 val isCreative = balanceValues == null
interface IValues { interface IValues {
@ -169,9 +169,9 @@ class QuantumBatteryItem(val savedataID: String, val balanceValues: EnergyBalanc
fun updateValues() { fun updateValues() {
if (!values.isServer && isServerThread()) { 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()) { } else if (isClientThread()) {
val id = stack.tag?.getUUIDSafe("id") ?: return val id = stack.tag?.getUUIDSafe("uuid") ?: return
if (values.uuid != id) if (values.uuid != id)
values = clientData.computeIfAbsent(id, Function { UnboundValues(it) }) 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 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 { override fun isBarVisible(p_150899_: ItemStack): Boolean {
if (isCreative) return false if (isCreative) return false
return p_150899_.matteryEnergy != null return p_150899_.matteryEnergy != null

View File

@ -69,7 +69,7 @@ class ExopackInventoryMenu(val capability: MatteryPlayerCapability) : MatteryMen
} }
val armorSlots = makeArmorSlots() val armorSlots = makeArmorSlots()
val curiosSlots: ImmutableList<PlayerSlot<Slot, Slot>> = ImmutableList.copyOf(ply.curiosSlots) val curiosSlots: ImmutableList<PlayerSlot<Slot, Slot>> = ImmutableList.copyOf(player.curiosSlots)
init { init {
for ((a, b) in curiosSlots) { for ((a, b) in curiosSlots) {
@ -84,10 +84,10 @@ class ExopackInventoryMenu(val capability: MatteryPlayerCapability) : MatteryMen
} }
private fun popFurnaceExp() { 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() val whole = capability.exopackSmelterExperience.toInt()
capability.exopackSmelterExperience -= whole 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<MachineOutputSlot> = capability.smelters.map { val furnaceOutputs: List<OutputSlot> = capability.smelters.map {
object : MachineOutputSlot(it.output, 0, onTake = { popFurnaceExp() }) { object : OutputSlot(it.output, 0, onTake = { popFurnaceExp() }) {
override fun mayPickup(player: Player): Boolean { override fun mayPickup(player: Player): Boolean {
return super.mayPickup(player) && capability.isExopackSmeltingInstalled return super.mayPickup(player) && capability.isExopackSmeltingInstalled
} }
@ -120,7 +120,7 @@ class ExopackInventoryMenu(val capability: MatteryPlayerCapability) : MatteryMen
init { init {
if (capability.isExopackEnderAccessInstalled) { if (capability.isExopackEnderAccessInstalled) {
enderChestSlots = makeSlots(ply.enderChestInventory) { a, b -> enderChestSlots = makeSlots(player.enderChestInventory) { a, b ->
MatterySlot(a, b).also { MatterySlot(a, b).also {
addStorageSlot(it, condition = enderChestOpenState) addStorageSlot(it, condition = enderChestOpenState)
} }
@ -238,16 +238,16 @@ class ExopackInventoryMenu(val capability: MatteryPlayerCapability) : MatteryMen
} }
fun sendInitialData(container: ExopackInventoryMenu, itemStacks: NonNullList<ItemStack>, carried: ItemStack, remoteDataSlots: IntArray) { fun sendInitialData(container: ExopackInventoryMenu, itemStacks: NonNullList<ItemStack>, 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) { fun sendSlotChange(container: ExopackInventoryMenu, slotId: Int, itemStack: ItemStack) {
if (container.slots[slotId].container != container.ply.inventory || container.ply.containerMenu is ExopackInventoryMenu) if (container.slots[slotId].container != container.player.inventory || container.player.containerMenu is ExopackInventoryMenu)
MatteryPlayerNetworkChannel.send(container.ply as ServerPlayer, ExopackSlotPacket(slotId, itemStack, container.stateId)) MatteryPlayerNetworkChannel.send(container.player as ServerPlayer, ExopackSlotPacket(slotId, itemStack, container.stateId))
} }
fun sendCarriedChange(container: ExopackInventoryMenu, itemStack: ItemStack) { 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) { fun sendDataChange(container: ExopackInventoryMenu, dataSlotId: Int, shortData: Int) {

View File

@ -101,7 +101,7 @@ abstract class MatteryMenu(
* Server->Client synchronizer * Server->Client synchronizer
*/ */
val mSynchronizer = FieldSynchronizer() val mSynchronizer = FieldSynchronizer()
val ply: Player get() = inventory.player val player: Player get() = inventory.player
private val _playerInventorySlots = ArrayList<InventorySlot>() private val _playerInventorySlots = ArrayList<InventorySlot>()
private val _playerHotbarSlots = ArrayList<InventorySlot>() private val _playerHotbarSlots = ArrayList<InventorySlot>()
@ -229,7 +229,7 @@ abstract class MatteryMenu(
protected var inventorySlotIndexStart = 0 protected var inventorySlotIndexStart = 0
protected var inventorySlotIndexEnd = 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<GetterSetter<ItemStack>> { fun addFilterSlots(slots: ItemFilter): List<GetterSetter<ItemStack>> {
val result = ArrayList<GetterSetter<ItemStack>>(slots.size) val result = ArrayList<GetterSetter<ItemStack>>(slots.size)
@ -277,8 +277,8 @@ abstract class MatteryMenu(
} }
override fun isSameInventory(other: Slot): Boolean { override fun isSameInventory(other: Slot): Boolean {
if (container === inventory || container === ply.matteryPlayer?.exopackContainer) if (container === inventory || container === player.matteryPlayer?.exopackContainer)
return (other.container === inventory || other.container === ply.matteryPlayer?.exopackContainer) && isSameFilter(other) return (other.container === inventory || other.container === player.matteryPlayer?.exopackContainer) && isSameFilter(other)
return super.isSameInventory(other) return super.isSameInventory(other)
} }
@ -287,7 +287,7 @@ abstract class MatteryMenu(
private set private set
init { init {
val mattery = ply.matteryPlayer val mattery = player.matteryPlayer
if (mattery != null) { if (mattery != null) {
if (container === inventory) { 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" } check(_playerInventorySlots.isEmpty()) { "Already created inventory slots" }
autoCreateInventoryFrame = autoFrame autoCreateInventoryFrame = autoFrame
@ -378,7 +378,7 @@ abstract class MatteryMenu(
addSlot(slot) addSlot(slot)
} }
val mattery = ply.matteryPlayer val mattery = player.matteryPlayer
if (mattery != null && mattery.hasExopack) { if (mattery != null && mattery.hasExopack) {
for (i in 0 until mattery.exopackContainer.containerSize) { for (i in 0 until mattery.exopackContainer.containerSize) {
@ -415,9 +415,9 @@ abstract class MatteryMenu(
if (payload != null) { if (payload != null) {
if (broadcastOnce) { if (broadcastOnce) {
MenuNetworkChannel.send(ply, MenuFieldPacket(containerId, payload)) MenuNetworkChannel.send(player, MenuFieldPacket(containerId, payload))
} else { } else {
MenuNetworkChannel.sendNow(ply, MenuFieldPacket(containerId, payload)) MenuNetworkChannel.sendNow(player, MenuFieldPacket(containerId, payload))
} }
} }
@ -449,7 +449,7 @@ abstract class MatteryMenu(
fun syncCarried() { fun syncCarried() {
setRemoteCarried(carried.copy()) setRemoteCarried(carried.copy())
MenuNetworkChannel.send(ply as ServerPlayer, SetCarriedPacket(carried)) MenuNetworkChannel.send(player as ServerPlayer, SetCarriedPacket(carried))
} }
fun syncCarried(stack: ItemStack) { fun syncCarried(stack: ItemStack) {
@ -635,11 +635,11 @@ abstract class MatteryMenu(
if (remainder.isEmpty) { if (remainder.isEmpty) {
source.set(ItemStack.EMPTY) source.set(ItemStack.EMPTY)
source.onTake(ply, copy) source.onTake(player, copy)
} else { } else {
copy.count = source.item.count - remainder.count copy.count = source.item.count - remainder.count
source.item.count = remainder.count source.item.count = remainder.count
source.onTake(ply, copy) source.onTake(player, copy)
} }
return true return true
@ -745,7 +745,7 @@ abstract class MatteryMenu(
return armorSlots!! return armorSlots!!
} }
val cosmetic = ply.cosmeticArmorSlots val cosmetic = player.cosmeticArmorSlots
return ImmutableList.of( return ImmutableList.of(
PlayerSlot(EquipmentSlot(net.minecraft.world.entity.EquipmentSlot.HEAD), cosmetic?.get(net.minecraft.world.entity.EquipmentSlot.HEAD)), 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 { fun makeEquipmentSlots(mapMoveToExternal: Boolean = false): EquipmentSlots {
return EquipmentSlots( return EquipmentSlots(
armorSlots = makeArmorSlots(mapMoveToExternal), armorSlots = makeArmorSlots(mapMoveToExternal),
curiosSlots = curiosSlots ?: ImmutableList.copyOf(ply.curiosSlots).also { curiosSlots = curiosSlots ?: ImmutableList.copyOf(player.curiosSlots).also {
for ((a, b) in it) { for ((a, b) in it) {
equipmentSlots.add(a) equipmentSlots.add(a)
addSlot(a) addSlot(a)

View File

@ -11,7 +11,6 @@ import ru.dbotthepony.mc.otm.capability.FlowDirection
import ru.dbotthepony.mc.otm.capability.MatteryCapability import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.energy import ru.dbotthepony.mc.otm.capability.energy
import ru.dbotthepony.mc.otm.client.minecraft 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.GetterSetter
import ru.dbotthepony.mc.otm.core.immutableList import ru.dbotthepony.mc.otm.core.immutableList
import ru.dbotthepony.mc.otm.runOnClient 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 { override fun mayPlace(itemStack: ItemStack): Boolean {
return false return false
} }

View File

@ -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.RedstoneSetting
import ru.dbotthepony.mc.otm.block.entity.decorative.FluidTankBlockEntity import ru.dbotthepony.mc.otm.block.entity.decorative.FluidTankBlockEntity
import ru.dbotthepony.mc.otm.capability.isNotEmpty import ru.dbotthepony.mc.otm.capability.isNotEmpty
import ru.dbotthepony.mc.otm.capability.isNotFull import ru.dbotthepony.mc.otm.menu.OutputSlot
import ru.dbotthepony.mc.otm.menu.MachineOutputSlot
import ru.dbotthepony.mc.otm.menu.MatteryMenu import ru.dbotthepony.mc.otm.menu.MatteryMenu
import ru.dbotthepony.mc.otm.menu.MatterySlot import ru.dbotthepony.mc.otm.menu.MatterySlot
import ru.dbotthepony.mc.otm.menu.input.EnumInputWithFeedback 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 { init {
// сначала слот на заполнение из бака // сначала слот на заполнение из бака

View File

@ -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.SimpleContainer
import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Inventory

View File

@ -7,10 +7,8 @@ import ru.dbotthepony.mc.otm.menu.widget.ProgressGaugeWidget
import ru.dbotthepony.mc.otm.menu.widget.LevelGaugeWidget import ru.dbotthepony.mc.otm.menu.widget.LevelGaugeWidget
import net.minecraft.world.SimpleContainer import net.minecraft.world.SimpleContainer
import net.minecraft.world.item.ItemStack 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.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.MatteryPoweredMenu
import ru.dbotthepony.mc.otm.menu.MatterySlot import ru.dbotthepony.mc.otm.menu.MatterySlot
import ru.dbotthepony.mc.otm.menu.input.EnergyConfigPlayerInput import ru.dbotthepony.mc.otm.menu.input.EnergyConfigPlayerInput
@ -27,8 +25,8 @@ class MatterDecomposerMenu @JvmOverloads constructor(
override fun mayPlace(itemStack: ItemStack) = MatterManager.canDecompose(itemStack) override fun mayPlace(itemStack: ItemStack) = MatterManager.canDecompose(itemStack)
} }
val outputMain: MachineOutputSlot val outputMain: OutputSlot
val outputStacking: MachineOutputSlot val outputStacking: OutputSlot
val progressWidget = ProgressGaugeWidget(this, tile) val progressWidget = ProgressGaugeWidget(this, tile)
val matterWidget = ProfiledLevelGaugeWidget(this, tile?.matter, LevelGaugeWidget(this, tile?.matter)) val matterWidget = ProfiledLevelGaugeWidget(this, tile?.matter, LevelGaugeWidget(this, tile?.matter))
val itemConfig = ItemConfigPlayerInput(this, tile?.itemConfig) val itemConfig = ItemConfigPlayerInput(this, tile?.itemConfig)
@ -40,8 +38,8 @@ class MatterDecomposerMenu @JvmOverloads constructor(
val container = tile?.outputContainer ?: SimpleContainer(2) val container = tile?.outputContainer ?: SimpleContainer(2)
// Выход // Выход
outputMain = MachineOutputSlot(container, 0) outputMain = OutputSlot(container, 0)
outputStacking = MachineOutputSlot(container, 1) outputStacking = OutputSlot(container, 1)
addStorageSlot(outputMain) addStorageSlot(outputMain)
addStorageSlot(outputStacking) addStorageSlot(outputStacking)

View File

@ -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<MatterySlot> = 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<ItemStack> {
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()
}
}

View File

@ -141,7 +141,7 @@ class MatterPanelMenu(
} }
val sorting: ItemSorter by mSynchronizer.ComputedField( val sorting: ItemSorter by mSynchronizer.ComputedField(
getter = { tile?.getPlayerSettings(ply)?.sorter ?: ItemSorter.DEFAULT }, getter = { tile?.getPlayerSettings(player)?.sorter ?: ItemSorter.DEFAULT },
codec = ItemSorter::class.codec(), codec = ItemSorter::class.codec(),
observer = { observer = {
patterns.sortWith(actualComparator) patterns.sortWith(actualComparator)
@ -151,7 +151,7 @@ class MatterPanelMenu(
}) })
val isAscending: Boolean by mSynchronizer.ComputedField( val isAscending: Boolean by mSynchronizer.ComputedField(
getter = { tile?.getPlayerSettings(ply)?.ascending ?: true }, getter = { tile?.getPlayerSettings(player)?.ascending ?: true },
codec = BooleanValueCodec, codec = BooleanValueCodec,
observer = { observer = {
patterns.sortWith(actualComparator) patterns.sortWith(actualComparator)
@ -184,8 +184,8 @@ class MatterPanelMenu(
} }
} }
val changeIsAscending = booleanInput(allowSpectators = true) { tile?.getPlayerSettings(ply)?.ascending = it } val changeIsAscending = booleanInput(allowSpectators = true) { tile?.getPlayerSettings(player)?.ascending = it }
val changeSorting = PlayerInput(ItemSorter::class.codec(), allowSpectators = true) { tile?.getPlayerSettings(ply)?.sorter = it } val changeSorting = PlayerInput(ItemSorter::class.codec(), allowSpectators = true) { tile?.getPlayerSettings(player)?.sorter = it }
val sortingGS = GetterSetter.of(::sorting, changeSorting::accept) val sortingGS = GetterSetter.of(::sorting, changeSorting::accept)
val isAscendingGS = GetterSetter.of(::isAscending, changeIsAscending::accept) val isAscendingGS = GetterSetter.of(::isAscending, changeIsAscending::accept)

View File

@ -10,7 +10,7 @@ import net.minecraft.world.SimpleContainer
import ru.dbotthepony.mc.otm.container.CombinedContainer import ru.dbotthepony.mc.otm.container.CombinedContainer
import ru.dbotthepony.mc.otm.core.immutableList import ru.dbotthepony.mc.otm.core.immutableList
import ru.dbotthepony.mc.otm.core.isNotEmpty 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.MatteryPoweredMenu
import ru.dbotthepony.mc.otm.menu.input.EnergyConfigPlayerInput import ru.dbotthepony.mc.otm.menu.input.EnergyConfigPlayerInput
import ru.dbotthepony.mc.otm.menu.input.ItemConfigPlayerInput import ru.dbotthepony.mc.otm.menu.input.ItemConfigPlayerInput
@ -25,7 +25,7 @@ class MatterReplicatorMenu @JvmOverloads constructor(
) : MatteryPoweredMenu(MMenus.MATTER_REPLICATOR, p_38852_, inventory, tile) { ) : MatteryPoweredMenu(MMenus.MATTER_REPLICATOR, p_38852_, inventory, tile) {
val matter = ProfiledLevelGaugeWidget(this, tile?.matter, LevelGaugeWidget(this, tile?.matter)) val matter = ProfiledLevelGaugeWidget(this, tile?.matter, LevelGaugeWidget(this, tile?.matter))
val progress = ProgressGaugeWidget(this, tile) val progress = ProgressGaugeWidget(this, tile)
val storageSlots: List<MachineOutputSlot> val storageSlots: List<OutputSlot>
val itemConfig = ItemConfigPlayerInput(this, tile?.itemConfig) val itemConfig = ItemConfigPlayerInput(this, tile?.itemConfig)
val energyConfig = EnergyConfigPlayerInput(this, tile?.energyConfig) val energyConfig = EnergyConfigPlayerInput(this, tile?.energyConfig)
val profiledEnergy = ProfiledLevelGaugeWidget(this, tile?.energy, energyWidget) 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)) val container = CombinedContainer(tile?.outputContainer ?: SimpleContainer(3), tile?.dustContainer ?: SimpleContainer(2))
storageSlots = immutableList(5) { storageSlots = immutableList(5) {
addStorageSlot(MachineOutputSlot(container, it, onTake = { addStorageSlot(OutputSlot(container, it, onTake = {
if (inventory.player is ServerPlayer && it.isNotEmpty) if (inventory.player is ServerPlayer && it.isNotEmpty)
TakeItemOutOfReplicatorTrigger.trigger(inventory.player as ServerPlayer, it) })) TakeItemOutOfReplicatorTrigger.trigger(inventory.player as ServerPlayer, it) }))
} }

View File

@ -48,8 +48,8 @@ class DriveViewerMenu(
val energyConfig = EnergyConfigPlayerInput(this, tile?.energyConfig) val energyConfig = EnergyConfigPlayerInput(this, tile?.energyConfig)
val settings = object : DriveViewerBlockEntity.ISettings { 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 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(ply)::isAscending }).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() { private fun changes() {
if (isAscending) { if (isAscending) {

View File

@ -86,11 +86,11 @@ class ItemMonitorMenu(
override val networkedItemView = NetworkedItemView(inventory.player, this, tile == null) override val networkedItemView = NetworkedItemView(inventory.player, this, tile == null)
val settings = object : IItemMonitorPlayerSettings { val settings = object : IItemMonitorPlayerSettings {
override var ingredientPriority by EnumInputWithFeedback(this@ItemMonitorMenu, true, tile?.let { it.getSettings(ply as ServerPlayer)::ingredientPriority }) 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(ply as ServerPlayer)::resultTarget }) 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(ply as ServerPlayer)::craftingAmount }) 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(ply as ServerPlayer)::sorting }).also { it.addListener { changes() } } 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(ply as ServerPlayer)::ascendingSort }).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() { private fun changes() {
if (ascendingSort) { if (ascendingSort) {
@ -140,7 +140,7 @@ class ItemMonitorMenu(
} }
if (!simulate && remaining.isNotEmpty) { if (!simulate && remaining.isNotEmpty) {
ply.spawnAtLocation(remaining) player.spawnAtLocation(remaining)
} }
if (!simulate) { if (!simulate) {

View File

@ -24,8 +24,8 @@ class AndroidStationMenu @JvmOverloads constructor(
tile: AndroidStationBlockEntity? = null tile: AndroidStationBlockEntity? = null
) : MatteryPoweredMenu(MMenus.ANDROID_STATION, containerID, inventory, tile) { ) : MatteryPoweredMenu(MMenus.ANDROID_STATION, containerID, inventory, tile) {
private fun container(target: (MatteryPlayerCapability) -> KMutableProperty0<ItemStack>): Container { private fun container(target: (MatteryPlayerCapability) -> KMutableProperty0<ItemStack>): Container {
if (ply is ServerPlayer) if (player is ServerPlayer)
return PartContainer(target.invoke(ply.matteryPlayer ?: throw NullPointerException("OTM player capability is missing"))) return PartContainer(target.invoke(player.matteryPlayer ?: throw NullPointerException("OTM player capability is missing")))
else else
return SimpleContainer(1) return SimpleContainer(1)
} }

View File

@ -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.RedstoneSetting
import ru.dbotthepony.mc.otm.block.entity.tech.CobblerBlockEntity import ru.dbotthepony.mc.otm.block.entity.tech.CobblerBlockEntity
import ru.dbotthepony.mc.otm.core.immutableList 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.MatteryMenu
import ru.dbotthepony.mc.otm.menu.input.EnumInputWithFeedback import ru.dbotthepony.mc.otm.menu.input.EnumInputWithFeedback
import ru.dbotthepony.mc.otm.menu.input.ItemConfigPlayerInput import ru.dbotthepony.mc.otm.menu.input.ItemConfigPlayerInput
@ -17,7 +17,7 @@ class CobblerMenu @JvmOverloads constructor(
inventory: Inventory, inventory: Inventory,
tile: CobblerBlockEntity? = null tile: CobblerBlockEntity? = null
) : MatteryMenu(MMenus.COBBLESTONE_GENERATOR, p_38852_, inventory, tile) { ) : 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<RedstoneSetting>(this) val redstone = EnumInputWithFeedback<RedstoneSetting>(this)
val itemConfig = ItemConfigPlayerInput(this) val itemConfig = ItemConfigPlayerInput(this)

View File

@ -40,7 +40,7 @@ class EssenceStorageMenu @JvmOverloads constructor(
val storeLevels = intInput { val storeLevels = intInput {
if (it > 0) { if (it > 0) {
val ply = ply as ServerPlayer val ply = player as ServerPlayer
tile!! tile!!
if (it == 1) { if (it == 1) {
@ -63,7 +63,7 @@ class EssenceStorageMenu @JvmOverloads constructor(
val dispenseLevels = intInput { val dispenseLevels = intInput {
if (it > 0) { if (it > 0) {
val ply = ply as ServerPlayer val ply = player as ServerPlayer
tile!! tile!!
if (it == 1) { if (it == 1) {

View File

@ -4,7 +4,7 @@ import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.SimpleContainer import net.minecraft.world.SimpleContainer
import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Inventory
import ru.dbotthepony.mc.otm.block.entity.tech.PlatePressBlockEntity 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.MatteryPoweredMenu
import ru.dbotthepony.mc.otm.menu.MatterySlot import ru.dbotthepony.mc.otm.menu.MatterySlot
import ru.dbotthepony.mc.otm.menu.input.EnergyConfigPlayerInput import ru.dbotthepony.mc.otm.menu.input.EnergyConfigPlayerInput
@ -19,7 +19,7 @@ class PlatePressMenu @JvmOverloads constructor(
tile: PlatePressBlockEntity? = null tile: PlatePressBlockEntity? = null
) : MatteryPoweredMenu(MMenus.PLATE_PRESS, containerID, inventory, tile) { ) : MatteryPoweredMenu(MMenus.PLATE_PRESS, containerID, inventory, tile) {
val inputSlot = MatterySlot(tile?.inputContainer ?: SimpleContainer(1), 0) 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 progressGauge = ProgressGaugeWidget(this, tile)
val itemConfig = ItemConfigPlayerInput(this, tile?.itemConfig, allowPush = true) val itemConfig = ItemConfigPlayerInput(this, tile?.itemConfig, allowPush = true)

View File

@ -4,7 +4,7 @@ import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Inventory
import ru.dbotthepony.mc.otm.block.entity.tech.PoweredFurnaceBlockEntity import ru.dbotthepony.mc.otm.block.entity.tech.PoweredFurnaceBlockEntity
import ru.dbotthepony.mc.otm.core.immutableList 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.MatteryPoweredMenu
import ru.dbotthepony.mc.otm.menu.MatterySlot import ru.dbotthepony.mc.otm.menu.MatterySlot
import ru.dbotthepony.mc.otm.menu.input.BooleanInputWithFeedback import ru.dbotthepony.mc.otm.menu.input.BooleanInputWithFeedback
@ -21,7 +21,7 @@ class PoweredFurnaceMenu(
tile: PoweredFurnaceBlockEntity? = null tile: PoweredFurnaceBlockEntity? = null
) : MatteryPoweredMenu(MMenus.POWERED_FURNACE, containerID, inventory, tile) { ) : MatteryPoweredMenu(MMenus.POWERED_FURNACE, containerID, inventory, tile) {
val inputSlots = makeSlots(tile?.inputs, 2, ::MatterySlot) 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 progressGauge = immutableList(2) { ProgressGaugeWidget(this, tile?.jobEventLoops?.get(it)) }
val itemConfig = ItemConfigPlayerInput(this, tile?.itemConfig, allowPush = true) val itemConfig = ItemConfigPlayerInput(this, tile?.itemConfig, allowPush = true)

View File

@ -4,7 +4,7 @@ import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.SimpleContainer import net.minecraft.world.SimpleContainer
import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Inventory
import ru.dbotthepony.mc.otm.block.entity.tech.PlatePressBlockEntity 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.MatteryPoweredMenu
import ru.dbotthepony.mc.otm.menu.MatterySlot import ru.dbotthepony.mc.otm.menu.MatterySlot
import ru.dbotthepony.mc.otm.menu.input.BooleanInputWithFeedback import ru.dbotthepony.mc.otm.menu.input.BooleanInputWithFeedback
@ -21,7 +21,7 @@ class TwinPlatePressMenu @JvmOverloads constructor(
tile: PlatePressBlockEntity? = null tile: PlatePressBlockEntity? = null
) : MatteryPoweredMenu(MMenus.TWIN_PLATE_PRESS, containerID, inventory, tile) { ) : MatteryPoweredMenu(MMenus.TWIN_PLATE_PRESS, containerID, inventory, tile) {
val inputSlots = makeSlots(tile?.inputContainer ?: SimpleContainer(2), ::MatterySlot) 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 progressGauge0 = ProgressGaugeWidget(this, tile?.jobEventLoops?.get(0))
val progressGauge1 = ProgressGaugeWidget(this, tile?.jobEventLoops?.get(1)) val progressGauge1 = ProgressGaugeWidget(this, tile?.jobEventLoops?.get(1))

View File

@ -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<C : Container> : Recipe<C> {
override fun getRemainingItems(p_44004_: C): NonNullList<ItemStack> {
return super.getRemainingItems(p_44004_)
}
override fun getIngredients(): NonNullList<Ingredient> {
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()
}
}

View File

@ -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<CraftingContainer>, Iterable<Ingredient> {
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<Ingredient> {
return (0 until width).iterator().flatMap { x ->
(0 until height).iterator().map { y ->
get(x, y)
}
}
}
val ingredients: NonNullList<Ingredient> get() {
val result = NonNullList.createWithCapacity<Ingredient>(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<Ingredient>): 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
}
}
}

View File

@ -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<CraftingContainer> {
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<UUID> = 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<Ingredient> {
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>(
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)
}
}

View File

@ -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.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 } Codec.unboundedMap(DyeColor.CODEC, Codec.INT.minRange(1)) to Predicate { true }
).fieldOf("dyes").forGetter(PainterRecipe::dyes), ).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) }
} }
} }
} }

View File

@ -89,7 +89,7 @@ class PlatePressRecipe(
Codec.INT.minRange(1).optionalFieldOf("count", 1).forGetter(PlatePressRecipe::count), Codec.INT.minRange(1).optionalFieldOf("count", 1).forGetter(PlatePressRecipe::count),
Codec.INT.minRange(0).optionalFieldOf("workTime", 200).forGetter(PlatePressRecipe::workTime), Codec.INT.minRange(0).optionalFieldOf("workTime", 200).forGetter(PlatePressRecipe::workTime),
FloatProvider.CODEC.optionalFieldOf("experience", ConstantFloat.ZERO).forGetter(PlatePressRecipe::experience) 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) }
} }
} }
} }

View File

@ -72,6 +72,7 @@ object MBlockEntities {
val INFINITE_WATER_SOURCE by register(MNames.INFINITE_WATER_SOURCE, ::InfiniteWaterSourceBlockEntity, MBlocks::INFINITE_WATER_SOURCE) 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 DEV_CHEST by register(MNames.DEV_CHEST, ::DevChestBlockEntity, MBlocks::DEV_CHEST)
val PAINTER by register(MNames.PAINTER, ::PainterBlockEntity, MBlocks::PAINTER) 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<PoweredFurnaceBlockEntity> by registry.register(MNames.POWERED_FURNACE) { BlockEntityType.Builder.of({ a, b -> MBlocks.POWERED_FURNACE.newBlockEntity(a, b) }, MBlocks.POWERED_FURNACE).build(null) } val POWERED_FURNACE: BlockEntityType<PoweredFurnaceBlockEntity> 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<PoweredFurnaceBlockEntity> 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) } val POWERED_BLAST_FURNACE: BlockEntityType<PoweredFurnaceBlockEntity> 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) }

View File

@ -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.CobblerBlock
import ru.dbotthepony.mc.otm.block.tech.EssenceStorageBlock import ru.dbotthepony.mc.otm.block.tech.EssenceStorageBlock
import ru.dbotthepony.mc.otm.block.decorative.PainterBlock 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.block.tech.PoweredFurnaceBlock
import ru.dbotthepony.mc.otm.config.MachinesConfig import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.core.TranslatableComponent 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 ESSENCE_STORAGE: EssenceStorageBlock by registry.register(MNames.ESSENCE_STORAGE) { EssenceStorageBlock() }
val MATTER_RECONSTRUCTOR: MatterReconstructorBlock by registry.register(MNames.MATTER_RECONSTRUCTOR) { MatterReconstructorBlock() } val MATTER_RECONSTRUCTOR: MatterReconstructorBlock by registry.register(MNames.MATTER_RECONSTRUCTOR) { MatterReconstructorBlock() }
val PAINTER: PainterBlock by registry.register(MNames.PAINTER) { PainterBlock() } 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_BUS: Block by registry.register(MNames.STORAGE_BUS) { StorageBusBlock() }
val STORAGE_IMPORTER: Block by registry.register(MNames.STORAGE_IMPORTER) { StorageImporterBlock() } val STORAGE_IMPORTER: Block by registry.register(MNames.STORAGE_IMPORTER) { StorageImporterBlock() }

View File

@ -152,10 +152,11 @@ object MItems {
} }
val PAINTER: BlockItem by registry.register(MNames.PAINTER) { BlockItem(MBlocks.PAINTER, DEFAULT_PROPERTIES) } 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( val MACHINES = SupplierList(
::ANDROID_STATION, ::ANDROID_CHARGER, ::BATTERY_BANK, ::MATTER_DECOMPOSER, ::MATTER_CAPACITOR_BANK, ::MATTER_CABLE, ::PATTERN_STORAGE, ::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, ::MATTER_RECYCLER, ::PLATE_PRESS, ::TWIN_PLATE_PRESS, ::POWERED_FURNACE, ::POWERED_BLAST_FURNACE,
::POWERED_SMOKER, ::POWERED_SMOKER,
::STORAGE_BUS, ::STORAGE_IMPORTER, ::STORAGE_EXPORTER, ::DRIVE_VIEWER, ::STORAGE_BUS, ::STORAGE_IMPORTER, ::STORAGE_EXPORTER, ::DRIVE_VIEWER,

View File

@ -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.MatterBottlerScreen
import ru.dbotthepony.mc.otm.client.screen.matter.MatterCapacitorBankScreen 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.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.MatterPanelScreen
import ru.dbotthepony.mc.otm.client.screen.matter.MatterRecyclerScreen import ru.dbotthepony.mc.otm.client.screen.matter.MatterRecyclerScreen
import ru.dbotthepony.mc.otm.client.screen.matter.MatterReplicatorScreen 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.EnergyCounterScreen
import ru.dbotthepony.mc.otm.client.screen.tech.EnergyServoScreen 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.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.PlatePressScreen
import ru.dbotthepony.mc.otm.client.screen.tech.PoweredFurnaceScreen import ru.dbotthepony.mc.otm.client.screen.tech.PoweredFurnaceScreen
import ru.dbotthepony.mc.otm.client.screen.tech.TwinPlatePressScreen 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.MatterBottlerMenu
import ru.dbotthepony.mc.otm.menu.matter.MatterCapacitorBankMenu import ru.dbotthepony.mc.otm.menu.matter.MatterCapacitorBankMenu
import ru.dbotthepony.mc.otm.menu.matter.MatterDecomposerMenu 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.MatterPanelMenu
import ru.dbotthepony.mc.otm.menu.matter.MatterRecyclerMenu import ru.dbotthepony.mc.otm.menu.matter.MatterRecyclerMenu
import ru.dbotthepony.mc.otm.menu.matter.MatterReplicatorMenu 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.EnergyCounterMenu
import ru.dbotthepony.mc.otm.menu.tech.EnergyServoMenu import ru.dbotthepony.mc.otm.menu.tech.EnergyServoMenu
import ru.dbotthepony.mc.otm.menu.tech.EssenceStorageMenu 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.PlatePressMenu
import ru.dbotthepony.mc.otm.menu.tech.PoweredFurnaceMenu import ru.dbotthepony.mc.otm.menu.tech.PoweredFurnaceMenu
import ru.dbotthepony.mc.otm.menu.tech.TwinPlatePressMenu import ru.dbotthepony.mc.otm.menu.tech.TwinPlatePressMenu
@ -103,6 +105,7 @@ object MMenus {
val ITEM_REPAIER: MenuType<MatterReconstructorMenu> by registry.register(MNames.MATTER_RECONSTRUCTOR) { MenuType(::MatterReconstructorMenu, FeatureFlags.VANILLA_SET) } val ITEM_REPAIER: MenuType<MatterReconstructorMenu> by registry.register(MNames.MATTER_RECONSTRUCTOR) { MenuType(::MatterReconstructorMenu, FeatureFlags.VANILLA_SET) }
val FLUID_TANK: MenuType<FluidTankMenu> by registry.register(MNames.FLUID_TANK) { MenuType(::FluidTankMenu, FeatureFlags.VANILLA_SET) } val FLUID_TANK: MenuType<FluidTankMenu> by registry.register(MNames.FLUID_TANK) { MenuType(::FluidTankMenu, FeatureFlags.VANILLA_SET) }
val PAINTER: MenuType<PainterMenu> by registry.register(MNames.PAINTER) { MenuType(::PainterMenu, FeatureFlags.VANILLA_SET) } val PAINTER: MenuType<PainterMenu> by registry.register(MNames.PAINTER) { MenuType(::PainterMenu, FeatureFlags.VANILLA_SET) }
val MATTER_ENTANGLER: MenuType<MatterEntanglerMenu> by registry.register(MNames.MATTER_ENTANGLER) { MenuType(::MatterEntanglerMenu, FeatureFlags.VANILLA_SET) }
val STORAGE_BUS: MenuType<StorageBusMenu> by registry.register(MNames.STORAGE_BUS) { MenuType(::StorageBusMenu, FeatureFlags.VANILLA_SET) } val STORAGE_BUS: MenuType<StorageBusMenu> by registry.register(MNames.STORAGE_BUS) { MenuType(::StorageBusMenu, FeatureFlags.VANILLA_SET) }
val STORAGE_IMPORTER_EXPORTER: MenuType<StorageImporterExporterMenu> by registry.register(MNames.STORAGE_IMPORTER) { MenuType(::StorageImporterExporterMenu, FeatureFlags.VANILLA_SET) } val STORAGE_IMPORTER_EXPORTER: MenuType<StorageImporterExporterMenu> by registry.register(MNames.STORAGE_IMPORTER) { MenuType(::StorageImporterExporterMenu, FeatureFlags.VANILLA_SET) }
@ -146,6 +149,7 @@ object MMenus {
MenuScreens.register(FLUID_TANK, ::FluidTankScreen) MenuScreens.register(FLUID_TANK, ::FluidTankScreen)
MenuScreens.register(POWERED_FURNACE, ::PoweredFurnaceScreen) MenuScreens.register(POWERED_FURNACE, ::PoweredFurnaceScreen)
MenuScreens.register(PAINTER, ::PainterScreen) MenuScreens.register(PAINTER, ::PainterScreen)
MenuScreens.register(MATTER_ENTANGLER, ::MatterEntanglerScreen)
} }
} }
} }

View File

@ -16,6 +16,7 @@ object MNames {
const val INFINITE_WATER_SOURCE = "infinite_water_source" const val INFINITE_WATER_SOURCE = "infinite_water_source"
const val DEV_CHEST = "dev_chest" const val DEV_CHEST = "dev_chest"
const val PAINTER = "painter" const val PAINTER = "painter"
const val MATTER_ENTANGLER = "matter_entangler"
// blocks // blocks
const val ANDROID_STATION = "android_station" const val ANDROID_STATION = "android_station"

View File

@ -10,6 +10,8 @@ import net.minecraftforge.registries.RegistryObject
import ru.dbotthepony.mc.otm.OverdriveThatMatters import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.recipe.EnergyContainerRecipe import ru.dbotthepony.mc.otm.recipe.EnergyContainerRecipe
import ru.dbotthepony.mc.otm.recipe.ExplosiveHammerPrimingRecipe 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.PainterRecipe
import ru.dbotthepony.mc.otm.recipe.PlatePressRecipe import ru.dbotthepony.mc.otm.recipe.PlatePressRecipe
import ru.dbotthepony.mc.otm.recipe.UpgradeRecipe import ru.dbotthepony.mc.otm.recipe.UpgradeRecipe
@ -38,6 +40,7 @@ object MRecipes {
val PLATE_PRESS by register<PlatePressRecipe>("plate_press") val PLATE_PRESS by register<PlatePressRecipe>("plate_press")
val PAINTER by register<PainterRecipe>("painter") val PAINTER by register<PainterRecipe>("painter")
val MATTER_ENTANGLER by register<IMatterEntanglerRecipe>("matter_entangler")
init { init {
serializers.register("plate_press") { PlatePressRecipe.SERIALIZER } serializers.register("plate_press") { PlatePressRecipe.SERIALIZER }
@ -45,5 +48,8 @@ object MRecipes {
serializers.register("upgrade") { UpgradeRecipe.Companion } serializers.register("upgrade") { UpgradeRecipe.Companion }
serializers.register("hammer_priming") { ExplosiveHammerPrimingRecipe.Companion } serializers.register("hammer_priming") { ExplosiveHammerPrimingRecipe.Companion }
serializers.register("painter") { PainterRecipe.SERIALIZER } 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 }
} }
} }