Fluid capsules test

This commit is contained in:
DBotThePony 2023-04-02 14:48:28 +07:00
parent 6c4c40073e
commit 516de507e4
Signed by: DBot
GPG Key ID: DCC23B5715498507
17 changed files with 577 additions and 149 deletions

View File

@ -261,39 +261,41 @@ private fun misc(provider: MatteryLanguageProvider) {
misc("pill.message", "Nothing happened, but you feel... exhausted?.. Maybe get rest.") misc("pill.message", "Nothing happened, but you feel... exhausted?.. Maybe get rest.")
misc("pill.message_finish", "§kONE OF US ONE OF US ONE OF US") misc("pill.message_finish", "§kONE OF US ONE OF US ONE OF US")
misc("gui.power.percentage_level", "Energy level: %s%%") gui("power.percentage_level", "Energy level: %s%%")
misc("gui.level", "%s / %s") gui("level", "%s / %s")
misc("gui.power.name", "MtJ") gui("power.name", "MtJ")
gui("fluid.name", "mB")
gui("fluid.level", "%s / %s of %s")
misc("gui.power.burn_time", "Burn time left: %s ticks") gui("power.burn_time", "Burn time left: %s ticks")
misc("gui.progress_widget", "Progress: %s%%") gui("progress_widget", "Progress: %s%%")
misc("gui.progress_widget_stuck", "The machine can not work, check configuration") gui("progress_widget_stuck", "The machine can not work, check configuration")
misc("gui.total_raw", "Total:") gui("total_raw", "Total:")
misc("gui.matter.percentage_level", "Matter level: %s%%") gui("matter.percentage_level", "Matter level: %s%%")
misc("gui.matter.format", "Matter: %s") gui("matter.format", "Matter: %s")
misc("gui.matter.format_and_complexity", "%s / Complexity: %s") gui("matter.format_and_complexity", "%s / Complexity: %s")
misc("gui.matter.format_and_complexity2", "%s (%s) / Complexity: %s (%s)") gui("matter.format_and_complexity2", "%s (%s) / Complexity: %s (%s)")
misc("gui.matter.name", "MtU") gui("matter.name", "MtU")
misc("gui.filter.is_whitelist", "Is Whitelist") gui("filter.is_whitelist", "Is Whitelist")
misc("gui.filter.match_nbt", "Match NBT") gui("filter.match_nbt", "Match NBT")
misc("gui.filter.match_tag", "Match Tag") gui("filter.match_tag", "Match Tag")
misc("gui.android_research", "Research Tree") gui("android_research", "Research Tree")
misc("gui.pattern.percentage_level", "Fill level: %s%%") gui("pattern.percentage_level", "Fill level: %s%%")
misc("gui.pattern.format", "Stored patterns: %s / %s") gui("pattern.format", "Stored patterns: %s / %s")
misc("gui.redstone.ignored", "Redstone mode: Ignored") gui("redstone.ignored", "Redstone mode: Ignored")
misc("gui.redstone.low", "Redstone mode: Low") gui("redstone.low", "Redstone mode: Low")
misc("gui.redstone.high", "Redstone mode: High") gui("redstone.high", "Redstone mode: High")
misc("gui.redstone.ignored.description", "Redstone signal does not affect machine's function") gui("redstone.ignored.description", "Redstone signal does not affect machine's function")
misc("gui.redstone.low.description", "Machine work if no redstone signal is present") gui("redstone.low.description", "Machine work if no redstone signal is present")
misc("gui.redstone.high.description", "Machine work only if any redstone signal is present") gui("redstone.high.description", "Machine work only if any redstone signal is present")
misc("3d2d.gravitation_stabilizer.mass", "Singularity mass: %s") misc("3d2d.gravitation_stabilizer.mass", "Singularity mass: %s")
misc("3d2d.gravitation_stabilizer.strength", "Gravitation strength: %s") misc("3d2d.gravitation_stabilizer.strength", "Gravitation strength: %s")
@ -458,6 +460,9 @@ private fun items(provider: MatteryLanguageProvider) {
add(MItems.NUTRIENT_PASTE, "Nutrient Paste") add(MItems.NUTRIENT_PASTE, "Nutrient Paste")
add(MItems.FLUID_CAPSULE, "Fluid Capsule")
add(MItems.FLUID_CAPSULE, "named", "Fluid Capsule (%s)")
add(MItems.BLACK_HOLE_SCANNER, "Singularity Scanner") add(MItems.BLACK_HOLE_SCANNER, "Singularity Scanner")
add(MItems.BLACK_HOLE_SCANNER, "desc", "Scans singularities for their properties") add(MItems.BLACK_HOLE_SCANNER, "desc", "Scans singularities for their properties")
add(MItems.BLACK_HOLE_SCANNER, "desc2", "Hold in hand to determine mass of singularities") add(MItems.BLACK_HOLE_SCANNER, "desc2", "Hold in hand to determine mass of singularities")

View File

@ -268,39 +268,41 @@ private fun misc(provider: MatteryLanguageProvider) {
misc("pill.message", "Ничего не произошло, но вы чувствуете... себя уставшим?.. Возможно надо отдохнуть.") misc("pill.message", "Ничего не произошло, но вы чувствуете... себя уставшим?.. Возможно надо отдохнуть.")
misc("pill.message_finish", "§kОДИН ИЗ НАС ОДИН ИЗ НАС ОДИН ИЗ НАС ОДИН ИЗ НАС ОДИН ИЗ НАС") misc("pill.message_finish", "§kОДИН ИЗ НАС ОДИН ИЗ НАС ОДИН ИЗ НАС ОДИН ИЗ НАС ОДИН ИЗ НАС")
misc("gui.power.percentage_level", "Уровень энергии: %s%%") gui("power.percentage_level", "Уровень энергии: %s%%")
misc("gui.level", "%s / %s") gui("level", "%s / %s")
misc("gui.power.name", "МтДж") gui("power.name", "МтДж")
gui("fluid.name", "мВ")
gui("fluid.level", "%s / %s с %s")
misc("gui.power.burn_time", "Оставшееся время горения: %s тиков") gui("power.burn_time", "Оставшееся время горения: %s тиков")
misc("gui.progress_widget", "Прогресс: %s%%") gui("progress_widget", "Прогресс: %s%%")
misc("gui.progress_widget_stuck", "Это устройство не может продолжить работу, проверьте конфигурацию") gui("progress_widget_stuck", "Это устройство не может продолжить работу, проверьте конфигурацию")
misc("gui.total_raw", "Всего:") gui("total_raw", "Всего:")
misc("gui.matter.percentage_level", "Уровень материи: %s%%") gui("matter.percentage_level", "Уровень материи: %s%%")
misc("gui.matter.format", "Материя: %s") gui("matter.format", "Материя: %s")
misc("gui.matter.format_and_complexity", "%s / Сложность: %s") gui("matter.format_and_complexity", "%s / Сложность: %s")
misc("gui.matter.format_and_complexity2", "%s (%s) / Сложность: %s (%s)") gui("matter.format_and_complexity2", "%s (%s) / Сложность: %s (%s)")
misc("gui.matter.name", "МтЕд") gui("matter.name", "МтЕд")
misc("gui.filter.is_whitelist", "Белый список") gui("filter.is_whitelist", "Белый список")
misc("gui.filter.match_nbt", "Сравнивать NBT") gui("filter.match_nbt", "Сравнивать NBT")
misc("gui.filter.match_tag", "Сравнить Теги") gui("filter.match_tag", "Сравнить Теги")
misc("gui.android_research", "Дерево Исследований") gui("android_research", "Дерево Исследований")
misc("gui.pattern.percentage_level", "Уровень заполнения: %s%%") gui("pattern.percentage_level", "Уровень заполнения: %s%%")
misc("gui.pattern.format", "Хранимые шаблоны: %s / %s") gui("pattern.format", "Хранимые шаблоны: %s / %s")
misc("gui.redstone.ignored", "Режим красного камня: Игнорирование") gui("redstone.ignored", "Режим красного камня: Игнорирование")
misc("gui.redstone.low", "Режим красного камня: Нет сигнала") gui("redstone.low", "Режим красного камня: Нет сигнала")
misc("gui.redstone.high", "Режим красного камня: Есть сигнал") gui("redstone.high", "Режим красного камня: Есть сигнал")
misc("gui.redstone.ignored.description", "Сигнал красного камня не влияет на работу устройства") gui("redstone.ignored.description", "Сигнал красного камня не влияет на работу устройства")
misc("gui.redstone.low.description", "Устройство работает если нет сигнала красного камня") gui("redstone.low.description", "Устройство работает если нет сигнала красного камня")
misc("gui.redstone.high.description", "Устройство работает если есть сигнал красного камня") gui("redstone.high.description", "Устройство работает если есть сигнал красного камня")
misc("3d2d.gravitation_stabilizer.mass", "Масса сингулярности: %s") misc("3d2d.gravitation_stabilizer.mass", "Масса сингулярности: %s")
misc("3d2d.gravitation_stabilizer.strength", "Гравитационная сила: %s") misc("3d2d.gravitation_stabilizer.strength", "Гравитационная сила: %s")
@ -465,6 +467,9 @@ private fun items(provider: MatteryLanguageProvider) {
add(MItems.NUTRIENT_PASTE, "Питательная паста") add(MItems.NUTRIENT_PASTE, "Питательная паста")
add(MItems.FLUID_CAPSULE, "Жидкостная капсула")
add(MItems.FLUID_CAPSULE, "named", "Жидкостная капсула (%s)")
add(MItems.BLACK_HOLE_SCANNER, "Сканер сингулярностей") add(MItems.BLACK_HOLE_SCANNER, "Сканер сингулярностей")
add(MItems.BLACK_HOLE_SCANNER, "desc", "Сканирует сингулярности и считывает их свойства") add(MItems.BLACK_HOLE_SCANNER, "desc", "Сканирует сингулярности и считывает их свойства")
add(MItems.BLACK_HOLE_SCANNER, "desc2", "Держите в любой их рук для считывания массы сингулярностей") add(MItems.BLACK_HOLE_SCANNER, "desc2", "Держите в любой их рук для считывания массы сингулярностей")

View File

@ -15,6 +15,7 @@ import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import ru.dbotthepony.mc.otm.android.AndroidResearchManager; import ru.dbotthepony.mc.otm.android.AndroidResearchManager;
import ru.dbotthepony.mc.otm.android.feature.EnderTeleporterFeature; import ru.dbotthepony.mc.otm.android.feature.EnderTeleporterFeature;
import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity; import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity;
@ -46,6 +47,7 @@ import ru.dbotthepony.mc.otm.config.ServerConfig;
import ru.dbotthepony.mc.otm.config.ToolsConfig; import ru.dbotthepony.mc.otm.config.ToolsConfig;
import ru.dbotthepony.mc.otm.core.math.Decimal; import ru.dbotthepony.mc.otm.core.math.Decimal;
import ru.dbotthepony.mc.otm.item.ItemTritaniumArmor; import ru.dbotthepony.mc.otm.item.ItemTritaniumArmor;
import ru.dbotthepony.mc.otm.item.PriorityUseItemKt;
import ru.dbotthepony.mc.otm.item.QuantumBatteryItem; import ru.dbotthepony.mc.otm.item.QuantumBatteryItem;
import ru.dbotthepony.mc.otm.item.weapon.AbstractWeaponItem; import ru.dbotthepony.mc.otm.item.weapon.AbstractWeaponItem;
import ru.dbotthepony.mc.otm.item.PortableCondensationDriveItem; import ru.dbotthepony.mc.otm.item.PortableCondensationDriveItem;
@ -59,6 +61,7 @@ import top.theillusivec4.curios.api.CuriosApi;
import static net.minecraftforge.common.MinecraftForge.EVENT_BUS; import static net.minecraftforge.common.MinecraftForge.EVENT_BUS;
import javax.annotation.ParametersAreNonnullByDefault; import javax.annotation.ParametersAreNonnullByDefault;
import java.util.Objects;
// The value here should match an entry in the META-INF/mods.toml file // The value here should match an entry in the META-INF/mods.toml file
@Mod(OverdriveThatMatters.MOD_ID) @Mod(OverdriveThatMatters.MOD_ID)
@ -72,8 +75,9 @@ public final class OverdriveThatMatters {
public static OverdriveThatMatters INSTANCE; public static OverdriveThatMatters INSTANCE;
private StorageStackType<ItemStackWrapper> ITEM_STORAGE; private StorageStackType<ItemStackWrapper> ITEM_STORAGE;
@NotNull
public StorageStackType<ItemStackWrapper> ITEM_STORAGE() { public StorageStackType<ItemStackWrapper> ITEM_STORAGE() {
return ITEM_STORAGE; return Objects.requireNonNull(ITEM_STORAGE);
} }
public static ResourceLocation loc(String path) { public static ResourceLocation loc(String path) {
@ -183,6 +187,8 @@ public final class OverdriveThatMatters {
EVENT_BUS.addListener(EventPriority.NORMAL, MatteryBlockEntity.Companion::playerDisconnected); EVENT_BUS.addListener(EventPriority.NORMAL, MatteryBlockEntity.Companion::playerDisconnected);
EVENT_BUS.addListener(EventPriority.LOWEST, MatteryBlockEntity.Companion::postLevelTick); EVENT_BUS.addListener(EventPriority.LOWEST, MatteryBlockEntity.Companion::postLevelTick);
EVENT_BUS.addListener(EventPriority.NORMAL, PriorityUseItemKt::onItemRightClick);
EVENT_BUS.addListener(EventPriority.LOWEST, KillAsAndroidTrigger.INSTANCE::onKill); EVENT_BUS.addListener(EventPriority.LOWEST, KillAsAndroidTrigger.INSTANCE::onKill);
EVENT_BUS.addListener(EventPriority.NORMAL, EnderTeleporterFeature.Companion::onEntityDeath); EVENT_BUS.addListener(EventPriority.NORMAL, EnderTeleporterFeature.Companion::onEntityDeath);

View File

@ -1,15 +1,23 @@
package ru.dbotthepony.mc.otm.capability package ru.dbotthepony.mc.otm.capability
import com.google.common.collect.Streams import com.google.common.collect.Streams
import earth.terrarium.botarium.common.registry.fluid.FluidSounds
import net.minecraft.ChatFormatting
import net.minecraft.core.Direction import net.minecraft.core.Direction
import net.minecraft.network.chat.Component
import net.minecraft.world.entity.player.Player import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import net.minecraftforge.common.capabilities.ForgeCapabilities import net.minecraftforge.common.capabilities.ForgeCapabilities
import net.minecraftforge.common.capabilities.ICapabilityProvider import net.minecraftforge.common.capabilities.ICapabilityProvider
import net.minecraftforge.common.util.LazyOptional import net.minecraftforge.common.util.LazyOptional
import net.minecraftforge.energy.IEnergyStorage import net.minecraftforge.energy.IEnergyStorage
import net.minecraftforge.fluids.FluidStack
import net.minecraftforge.fluids.capability.IFluidHandler
import net.minecraftforge.fml.ModList import net.minecraftforge.fml.ModList
import net.minecraftforge.items.IItemHandler
import org.apache.logging.log4j.LogManager
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
import ru.dbotthepony.mc.otm.client.ShiftPressedCond
import ru.dbotthepony.mc.otm.compat.cos.cosmeticArmorAwareStream import ru.dbotthepony.mc.otm.compat.cos.cosmeticArmorAwareStream
import ru.dbotthepony.mc.otm.compat.cos.cosmeticArmorStream import ru.dbotthepony.mc.otm.compat.cos.cosmeticArmorStream
import ru.dbotthepony.mc.otm.compat.cos.isCosmeticArmorLoaded import ru.dbotthepony.mc.otm.compat.cos.isCosmeticArmorLoaded
@ -24,10 +32,13 @@ import ru.dbotthepony.mc.otm.core.collect.AwareItemStack
import ru.dbotthepony.mc.otm.core.collect.ContainerItemStackEntry import ru.dbotthepony.mc.otm.core.collect.ContainerItemStackEntry
import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.orNull import ru.dbotthepony.mc.otm.core.orNull
import ru.dbotthepony.mc.otm.core.util.formatFluidLevel
import java.util.IdentityHashMap import java.util.IdentityHashMap
import java.util.LinkedList import java.util.LinkedList
import java.util.stream.Stream import java.util.stream.Stream
private val LOGGER = LogManager.getLogger()
val ICapabilityProvider.matteryPlayer: MatteryPlayerCapability? get() = getCapability(MatteryCapability.MATTERY_PLAYER).orNull() val ICapabilityProvider.matteryPlayer: MatteryPlayerCapability? get() = getCapability(MatteryCapability.MATTERY_PLAYER).orNull()
/** /**
@ -297,3 +308,119 @@ fun Player.awareAllItemsStream(includeCosmetics: Boolean = false): Stream<out Aw
seen.stream()) seen.stream())
} }
/**
* Attempts to safely exchange/move item between slots of two handlers
*
* @return pair of new (advanced) [sourceSlot] and [destinationSlot]
*/
internal fun moveBetweenSlots(source: IItemHandler, sourceSlot: Int, destination: IItemHandler, destinationSlot: Int): Pair<Int, Int> {
val getItem = source.extractItem(sourceSlot, Int.MAX_VALUE, true)
if (getItem.isEmpty) {
return sourceSlot + 1 to destinationSlot
} else {
val leftover = destination.insertItem(destinationSlot, getItem, true)
if (leftover.count == getItem.count) {
return sourceSlot to destinationSlot + 1
} else {
val getItem2 = source.extractItem(sourceSlot, getItem.count - leftover.count, true)
if (getItem2.isEmpty) {
return sourceSlot + 1 to destinationSlot
} else {
val leftover2 = destination.insertItem(destinationSlot, getItem2, true)
if (leftover2.isEmpty) {
source.extractItem(sourceSlot, getItem2.count, false)
destination.insertItem(destinationSlot, getItem2, false)
if (getItem2.count == getItem.count) {
return sourceSlot + 1 to destinationSlot
} else {
return sourceSlot to destinationSlot
}
}
}
}
}
return sourceSlot to destinationSlot
}
@Suppress("name_shadowing")
internal fun moveEnergy(source: IEnergyStorage, destination: IEnergyStorage, amount: Decimal = Decimal.LONG_MAX_VALUE, simulate: Boolean, ignoreFlowRestrictions: Boolean = false): Decimal {
val extracted = if (ignoreFlowRestrictions && source is IMatteryEnergyStorage) source.extractEnergy(amount, true) else source.extractEnergy(amount, true)
if (extracted.isPositive) {
val received = destination.receiveEnergy(extracted, true)
if (received.isPositive) {
val extracted = if (ignoreFlowRestrictions && source is IMatteryEnergyStorage) source.extractEnergy(received, true) else source.extractEnergy(received, true)
if (extracted.isPositive) {
if (simulate) {
return extracted
}
val received = destination.receiveEnergy(extracted, false)
return if (ignoreFlowRestrictions && source is IMatteryEnergyStorage) source.extractEnergy(received, false) else source.extractEnergy(received, false)
}
}
}
return Decimal.ZERO
}
internal fun fluidLevel(it: IFluidHandler, tooltips: MutableList<Component>) {
val fluid = it.getFluidInTank(0)
if (fluid.isEmpty) {
tooltips.add(formatFluidLevel(0, it.getTankCapacity(0), formatAsReadable = ShiftPressedCond).withStyle(ChatFormatting.GRAY))
} else {
tooltips.add(formatFluidLevel(fluid.amount, it.getTankCapacity(0), fluid.displayName, formatAsReadable = ShiftPressedCond).withStyle(ChatFormatting.GRAY))
}
}
internal fun moveFluid(source: IFluidHandler, sourceTank: Int? = null, destination: IFluidHandler, limit: Int = Int.MAX_VALUE, simulate: Boolean = false, actuallyDrain: Boolean = true): FluidStack {
val drained: FluidStack
if (sourceTank == null) {
drained = source.drain(limit, IFluidHandler.FluidAction.SIMULATE)
} else {
drained = source.drain(source.getFluidInTank(sourceTank), IFluidHandler.FluidAction.SIMULATE)
}
if (drained.isEmpty) return FluidStack.EMPTY
val filled = destination.fill(drained, IFluidHandler.FluidAction.SIMULATE)
if (filled == 0) return FluidStack.EMPTY
val drained2 = source.drain(FluidStack(drained, filled), IFluidHandler.FluidAction.SIMULATE)
if (drained2.amount != filled) return FluidStack.EMPTY
val filled2 = destination.fill(drained2, IFluidHandler.FluidAction.SIMULATE)
if (filled2 != drained2.amount) return FluidStack.EMPTY
if (simulate) return FluidStack(drained2, filled2)
val drained3: FluidStack
if (actuallyDrain) {
drained3 = source.drain(FluidStack(drained2, filled2), IFluidHandler.FluidAction.EXECUTE)
if (drained3.amount != filled2) {
LOGGER.warn("Inconsistency of fluid extraction from $source between simulate and execute modes (simulated $drained2; extracted $drained3); This can lead to duping!!!")
}
} else {
drained3 = drained2
}
val filled3 = destination.fill(drained3, IFluidHandler.FluidAction.EXECUTE)
if (filled3 != drained3.amount) {
LOGGER.warn("Inconsistency of fluid insertion to $destination between simulate and execute modes (simulated $filled2; inserted $filled3); This can lead to duping!!!")
}
return FluidStack(drained3, filled3)
}

View File

@ -1,70 +0,0 @@
package ru.dbotthepony.mc.otm.capability
import net.minecraftforge.energy.IEnergyStorage
import net.minecraftforge.items.IItemHandler
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
import ru.dbotthepony.mc.otm.core.math.Decimal
/**
* Attempts to safely exchange/move item between slots of two handlers
*
* @return pair of new (advanced) [sourceSlot] and [destinationSlot]
*/
fun moveBetweenSlots(source: IItemHandler, sourceSlot: Int, destination: IItemHandler, destinationSlot: Int): Pair<Int, Int> {
val getItem = source.extractItem(sourceSlot, Int.MAX_VALUE, true)
if (getItem.isEmpty) {
return sourceSlot + 1 to destinationSlot
} else {
val leftover = destination.insertItem(destinationSlot, getItem, true)
if (leftover.count == getItem.count) {
return sourceSlot to destinationSlot + 1
} else {
val getItem2 = source.extractItem(sourceSlot, getItem.count - leftover.count, true)
if (getItem2.isEmpty) {
return sourceSlot + 1 to destinationSlot
} else {
val leftover2 = destination.insertItem(destinationSlot, getItem2, true)
if (leftover2.isEmpty) {
source.extractItem(sourceSlot, getItem2.count, false)
destination.insertItem(destinationSlot, getItem2, false)
if (getItem2.count == getItem.count) {
return sourceSlot + 1 to destinationSlot
} else {
return sourceSlot to destinationSlot
}
}
}
}
}
return sourceSlot to destinationSlot
}
@Suppress("name_shadowing")
fun moveEnergy(source: IEnergyStorage, destination: IEnergyStorage, amount: Decimal = Decimal.LONG_MAX_VALUE, simulate: Boolean, ignoreFlowRestrictions: Boolean = false): Decimal {
val extracted = if (ignoreFlowRestrictions && source is IMatteryEnergyStorage) source.extractEnergy(amount, true) else source.extractEnergy(amount, true)
if (extracted.isPositive) {
val received = destination.receiveEnergy(extracted, true)
if (received.isPositive) {
val extracted = if (ignoreFlowRestrictions && source is IMatteryEnergyStorage) source.extractEnergy(received, true) else source.extractEnergy(received, true)
if (extracted.isPositive) {
if (simulate) {
return extracted
}
val received = destination.receiveEnergy(extracted, false)
return if (ignoreFlowRestrictions && source is IMatteryEnergyStorage) source.extractEnergy(received, false) else source.extractEnergy(received, false)
}
}
}
return Decimal.ZERO
}

View File

@ -91,6 +91,10 @@ object ItemsConfig : AbstractConfig("items") {
} }
init { init {
builder.push("PatternDrives")
PatternDrives PatternDrives
builder.pop()
} }
val FLUID_CAPSULE_CAPACITY: Int by builder.defineInRange("LIQUID_CAPSULE_CAPACITY", 1000, 1, Int.MAX_VALUE)
} }

View File

@ -47,7 +47,7 @@ class AwareContainerSpliterator(private val container: Container, offset: Int =
} }
} }
fun Container.spliterator() = ContainerSpliterator(this) fun Container.spliterator(offset: Int = 0) = ContainerSpliterator(this, offset)
fun Container.awareSpliterator() = AwareContainerSpliterator(this) fun Container.awareSpliterator(offset: Int = 0) = AwareContainerSpliterator(this, offset)
fun Container.stream(): Stream<out ItemStack> = StreamSupport.stream(spliterator(), false) fun Container.stream(offset: Int = 0): Stream<out ItemStack> = StreamSupport.stream(spliterator(offset), false)
fun Container.awareStream(): Stream<out AwareItemStack> = StreamSupport.stream(awareSpliterator(), false) fun Container.awareStream(offset: Int = 0): Stream<out AwareItemStack> = StreamSupport.stream(awareSpliterator(offset), false)

View File

@ -3,12 +3,15 @@ package ru.dbotthepony.mc.otm.container
import net.minecraft.world.Container import net.minecraft.world.Container
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import net.minecraftforge.common.capabilities.Capability import net.minecraftforge.common.capabilities.Capability
import net.minecraftforge.fluids.capability.IFluidHandler
import ru.dbotthepony.mc.otm.core.collect.iterator import ru.dbotthepony.mc.otm.core.collect.iterator
import ru.dbotthepony.mc.otm.core.collect.nonEmpty import ru.dbotthepony.mc.otm.core.collect.nonEmpty
operator fun Container.set(index: Int, value: ItemStack) = setItem(index, value) operator fun Container.set(index: Int, value: ItemStack) = setItem(index, value)
operator fun Container.get(index: Int): ItemStack = getItem(index) operator fun Container.get(index: Int): ItemStack = getItem(index)
operator fun IFluidHandler.get(index: Int) = getFluidInTank(index)
fun Container.addItem(stack: ItemStack, range: IntRange, simulate: Boolean = false): ItemStack { fun Container.addItem(stack: ItemStack, range: IntRange, simulate: Boolean = false): ItemStack {
if (this is MatteryContainer) { if (this is MatteryContainer) {
return this.addItem(stack, range, simulate) return this.addItem(stack, range, simulate)

View File

@ -0,0 +1,25 @@
package ru.dbotthepony.mc.otm.container
import it.unimi.dsi.fastutil.objects.ObjectIterators.AbstractIndexBasedIterator
import net.minecraftforge.fluids.FluidStack
import net.minecraftforge.fluids.capability.IFluidHandler
class FluidHandlerIterator(private val handler: IFluidHandler, initialPosition: Int = 0) : AbstractIndexBasedIterator<FluidStack>(0, initialPosition) {
init {
require(initialPosition in 0 .. handler.tanks) { "Invalid initial position: $initialPosition" }
}
override fun remove(location: Int) {
throw UnsupportedOperationException()
}
override fun get(location: Int): FluidStack {
return handler[location]
}
override fun getMaxPos(): Int {
return handler.tanks
}
}
fun IFluidHandler.iterator(position: Int = 0) = FluidHandlerIterator(this, position)

View File

@ -0,0 +1,31 @@
package ru.dbotthepony.mc.otm.container
import it.unimi.dsi.fastutil.objects.ObjectSpliterator
import it.unimi.dsi.fastutil.objects.ObjectSpliterators.AbstractIndexBasedSpliterator
import net.minecraftforge.fluids.FluidStack
import net.minecraftforge.fluids.capability.IFluidHandler
import java.util.Spliterator
import java.util.stream.Stream
import java.util.stream.StreamSupport
class FluidHandlerSpliterator(private val handler: IFluidHandler, offset: Int = 0, private val maxPos: Int = handler.tanks) : AbstractIndexBasedSpliterator<FluidStack>(offset) {
init {
require(offset in 0 until handler.tanks) { "Invalid offset $offset" }
require(maxPos >= offset && maxPos in 0 until handler.tanks) { "Invalid spliterator configuration: maxPos $maxPos offset $offset max tanks ${handler.tanks}" }
}
override fun get(location: Int): FluidStack {
return handler[location]
}
override fun getMaxPos(): Int {
return maxPos
}
override fun makeForSplit(pos: Int, maxPos: Int): ObjectSpliterator<FluidStack> {
return FluidHandlerSpliterator(handler, pos, maxPos)
}
}
fun IFluidHandler.spliterator(offset: Int = 0): Spliterator<FluidStack> = FluidHandlerSpliterator(this, offset)
fun IFluidHandler.stream(offset: Int = 0): Stream<FluidStack> = StreamSupport.stream(spliterator(offset), false)

View File

@ -14,6 +14,7 @@ import net.minecraft.resources.ResourceLocation
import net.minecraft.sounds.SoundEvent import net.minecraft.sounds.SoundEvent
import net.minecraft.world.item.Item import net.minecraft.world.item.Item
import net.minecraft.world.level.block.Block import net.minecraft.world.level.block.Block
import net.minecraft.world.level.material.Fluid
import net.minecraftforge.registries.ForgeRegistries import net.minecraftforge.registries.ForgeRegistries
import net.minecraftforge.registries.IForgeRegistry import net.minecraftforge.registries.IForgeRegistry
@ -65,6 +66,7 @@ fun <T> IForgeRegistry<T>.getKeyNullable(value: T): ResourceLocation? {
} }
val Item.registryName get() = ForgeRegistries.ITEMS.getKeyNullable(this) val Item.registryName get() = ForgeRegistries.ITEMS.getKeyNullable(this)
val Fluid.registryName get() = ForgeRegistries.FLUIDS.getKeyNullable(this)
val Block.registryName get() = ForgeRegistries.BLOCKS.getKeyNullable(this) val Block.registryName get() = ForgeRegistries.BLOCKS.getKeyNullable(this)
fun FriendlyByteBuf.writeRegistryId(value: Item) = writeRegistryId(ForgeRegistries.ITEMS, value) fun FriendlyByteBuf.writeRegistryId(value: Item) = writeRegistryId(ForgeRegistries.ITEMS, value)

View File

@ -1,26 +0,0 @@
package ru.dbotthepony.mc.otm.core.collect
import net.minecraft.world.inventory.AbstractContainerMenu
import net.minecraft.world.inventory.Slot
import net.minecraft.world.item.ItemStack
private class SlotIterator(private val parent: Iterator<Slot>) : MutableIterator<ItemStack> {
private var last: Slot? = null
override fun hasNext(): Boolean {
return parent.hasNext()
}
override fun next(): ItemStack {
return parent.next().also { last = it }.item
}
override fun remove() {
val last = last ?: throw IllegalStateException("Never called next()")
last.set(ItemStack.EMPTY)
}
}
fun AbstractContainerMenu.itemStackIterator() : MutableIterator<ItemStack> = SlotIterator(slots.iterator())
fun Iterable<Slot>.itemStackIterator() : MutableIterator<ItemStack> = SlotIterator(iterator())
fun Iterator<Slot>.asItemStackIterator() : MutableIterator<ItemStack> = SlotIterator(this)

View File

@ -209,6 +209,7 @@ fun Decimal.formatSiComponent(suffix: Any = "", decimalPlaces: Int = 2, formatAs
} }
fun Int.formatPower(decimalPlaces: Int = 2, formatAsReadable: BooleanSupplier = never) = formatSiComponent(TranslatableComponent("otm.gui.power.name"), decimalPlaces, formatAsReadable = formatAsReadable) fun Int.formatPower(decimalPlaces: Int = 2, formatAsReadable: BooleanSupplier = never) = formatSiComponent(TranslatableComponent("otm.gui.power.name"), decimalPlaces, formatAsReadable = formatAsReadable)
fun Int.formatFluid(decimalPlaces: Int = 2, formatAsReadable: BooleanSupplier = never) = formatSiComponent(TranslatableComponent("otm.gui.fluid.name"), decimalPlaces, formatAsReadable = formatAsReadable)
fun Decimal.formatPower(decimalPlaces: Int = 2, formatAsReadable: BooleanSupplier = never) = formatSiComponent(TranslatableComponent("otm.gui.power.name"), decimalPlaces, formatAsReadable = formatAsReadable) fun Decimal.formatPower(decimalPlaces: Int = 2, formatAsReadable: BooleanSupplier = never) = formatSiComponent(TranslatableComponent("otm.gui.power.name"), decimalPlaces, formatAsReadable = formatAsReadable)
fun Decimal.formatMatter(decimalPlaces: Int = 2, formatAsReadable: BooleanSupplier = never) = formatSiComponent(TranslatableComponent("otm.gui.matter.name"), decimalPlaces, formatAsReadable = formatAsReadable) fun Decimal.formatMatter(decimalPlaces: Int = 2, formatAsReadable: BooleanSupplier = never) = formatSiComponent(TranslatableComponent("otm.gui.matter.name"), decimalPlaces, formatAsReadable = formatAsReadable)
fun Decimal.formatMatterFull(decimalPlaces: Int = 2, formatAsReadable: BooleanSupplier = never) = TranslatableComponent("otm.gui.matter.format", formatSiComponent(TranslatableComponent("otm.gui.matter.name"), decimalPlaces, formatAsReadable = formatAsReadable)) fun Decimal.formatMatterFull(decimalPlaces: Int = 2, formatAsReadable: BooleanSupplier = never) = TranslatableComponent("otm.gui.matter.format", formatSiComponent(TranslatableComponent("otm.gui.matter.name"), decimalPlaces, formatAsReadable = formatAsReadable))
@ -219,6 +220,8 @@ fun BigInteger.formatMatterFull(decimalPlaces: Int = 2, formatAsReadable: Boolea
fun formatPowerLevel(a: Decimal, b: Decimal, decimalPlaces: Int = 2, formatAsReadable: BooleanSupplier = never) = TranslatableComponent("otm.gui.level", a.formatPower(decimalPlaces, formatAsReadable = formatAsReadable), b.formatPower(decimalPlaces, formatAsReadable = formatAsReadable)) fun formatPowerLevel(a: Decimal, b: Decimal, decimalPlaces: Int = 2, formatAsReadable: BooleanSupplier = never) = TranslatableComponent("otm.gui.level", a.formatPower(decimalPlaces, formatAsReadable = formatAsReadable), b.formatPower(decimalPlaces, formatAsReadable = formatAsReadable))
fun formatMatterLevel(a: Decimal, b: Decimal, decimalPlaces: Int = 2, formatAsReadable: BooleanSupplier = never) = TranslatableComponent("otm.gui.level", a.formatMatter(decimalPlaces, formatAsReadable = formatAsReadable), b.formatMatter(decimalPlaces, formatAsReadable = formatAsReadable)) fun formatMatterLevel(a: Decimal, b: Decimal, decimalPlaces: Int = 2, formatAsReadable: BooleanSupplier = never) = TranslatableComponent("otm.gui.level", a.formatMatter(decimalPlaces, formatAsReadable = formatAsReadable), b.formatMatter(decimalPlaces, formatAsReadable = formatAsReadable))
fun formatFluidLevel(a: Int, b: Int, decimalPlaces: Int = 2, formatAsReadable: BooleanSupplier = never) = TranslatableComponent("otm.gui.level", a.formatFluid(decimalPlaces, formatAsReadable = formatAsReadable), b.formatFluid(decimalPlaces, formatAsReadable = formatAsReadable))
fun formatFluidLevel(a: Int, b: Int, name: Component, decimalPlaces: Int = 2, formatAsReadable: BooleanSupplier = never) = TranslatableComponent("otm.gui.fluid.level", a.formatFluid(decimalPlaces, formatAsReadable = formatAsReadable), b.formatFluid(decimalPlaces, formatAsReadable = formatAsReadable), name)
private fun padded(num: Int): String { private fun padded(num: Int): String {
if (num in 0 .. 9) { if (num in 0 .. 9) {

View File

@ -0,0 +1,274 @@
package ru.dbotthepony.mc.otm.item
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.chat.Component
import net.minecraft.server.level.ServerLevel
import net.minecraft.sounds.SoundEvent
import net.minecraft.sounds.SoundEvents
import net.minecraft.sounds.SoundSource
import net.minecraft.world.InteractionResult
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.TooltipFlag
import net.minecraft.world.item.context.UseOnContext
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.Blocks
import net.minecraft.world.level.block.LayeredCauldronBlock
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.material.Fluid
import net.minecraft.world.level.material.Fluids
import net.minecraftforge.common.SoundActions
import net.minecraftforge.common.capabilities.ForgeCapabilities
import net.minecraftforge.common.capabilities.ICapabilityProvider
import net.minecraftforge.event.entity.player.PlayerInteractEvent
import net.minecraftforge.fluids.FluidStack
import net.minecraftforge.fluids.capability.IFluidHandler
import net.minecraftforge.fluids.capability.templates.FluidHandlerItemStack
import ru.dbotthepony.mc.otm.capability.fluidLevel
import ru.dbotthepony.mc.otm.capability.moveFluid
import ru.dbotthepony.mc.otm.container.get
import ru.dbotthepony.mc.otm.container.stream
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.ifPresentK
import ru.dbotthepony.mc.otm.core.immutableList
import ru.dbotthepony.mc.otm.core.immutableMap
import ru.dbotthepony.mc.otm.core.orNull
class FluidCapsuleItem(val capacity: () -> Int) : Item(Properties().stacksTo(64)), PriorityUseItem {
private inner class Cap(itemStack: ItemStack) : FluidHandlerItemStack(itemStack, capacity.invoke()) {
override fun fill(resource: FluidStack, doFill: IFluidHandler.FluidAction): Int {
this.capacity = this@FluidCapsuleItem.capacity.invoke()
return super.fill(resource, doFill)
}
override fun getTankCapacity(tank: Int): Int {
this.capacity = this@FluidCapsuleItem.capacity.invoke()
return super.getTankCapacity(tank)
}
override fun drain(resource: FluidStack?, action: IFluidHandler.FluidAction?): FluidStack {
this.capacity = this@FluidCapsuleItem.capacity.invoke()
return super.drain(resource, action)
}
override fun drain(maxDrain: Int, action: IFluidHandler.FluidAction?): FluidStack {
this.capacity = this@FluidCapsuleItem.capacity.invoke()
return super.drain(maxDrain, action)
}
}
override fun isPriorityConsumer(event: PlayerInteractEvent.RightClickBlock): Boolean {
return canInteract(event.itemStack, event.entity, Context(event.hitVec.blockPos, event.entity.level.getBlockState(event.hitVec.blockPos), event.hitVec.direction))
}
override fun useOn(pContext: UseOnContext): InteractionResult {
val context = Context(pContext.clickedPos, pContext.level.getBlockState(pContext.clickedPos), pContext.clickedFace)
if (canInteract(pContext.itemInHand, pContext.player ?: return InteractionResult.FAIL, context))
return interact(pContext.itemInHand, pContext.player!!, context)
return InteractionResult.FAIL
}
override fun getName(pStack: ItemStack): Component {
pStack.getCapability(ForgeCapabilities.FLUID_HANDLER_ITEM).ifPresentK {
it.getFluidInTank(0).also {
if (!it.isEmpty) {
return TranslatableComponent("$descriptionId.named", it.displayName)
}
}
}
return super.getName(pStack)
}
override fun appendHoverText(pStack: ItemStack, pLevel: Level?, pTooltipComponents: MutableList<Component>, pIsAdvanced: TooltipFlag) {
super.appendHoverText(pStack, pLevel, pTooltipComponents, pIsAdvanced)
pStack.getCapability(ForgeCapabilities.FLUID_HANDLER_ITEM).ifPresentK {
fluidLevel(it, pTooltipComponents)
}
}
override fun initCapabilities(stack: ItemStack, nbt: CompoundTag?): ICapabilityProvider {
return Cap(stack)
}
interface Interaction {
fun canInteract(item: ItemStack, player: Player, context: Context): Boolean
fun interact(item: ItemStack, player: Player, context: Context): InteractionResult
}
private object FillCauldron : Interaction {
private data class Mapping(val fluid: Fluid, val blockState: BlockState, val soundEvent: SoundEvent)
private val mapping = immutableList {
accept(Mapping(Fluids.WATER, Blocks.WATER_CAULDRON.defaultBlockState().setValue(LayeredCauldronBlock.LEVEL, 3), SoundEvents.BUCKET_EMPTY))
accept(Mapping(Fluids.LAVA, Blocks.LAVA_CAULDRON.defaultBlockState(), SoundEvents.BUCKET_EMPTY_LAVA))
}
override fun canInteract(item: ItemStack, player: Player, context: Context): Boolean {
return player.level.getBlockState(context.blockPos).`is`(Blocks.CAULDRON)
}
override fun interact(item: ItemStack, player: Player, context: Context): InteractionResult {
if (item.isEmpty || !context.blockState.`is`(Blocks.CAULDRON)) return InteractionResult.FAIL
val targetItem = if (item.count == 1) item else item.copyWithCount(1)
val cap = targetItem.getCapability(ForgeCapabilities.FLUID_HANDLER_ITEM).orNull() ?: return InteractionResult.FAIL
for ((fluid, newState, soundEvent) in mapping) {
val toDrain = FluidStack(fluid, 1000)
val drained = cap.drain(toDrain, IFluidHandler.FluidAction.SIMULATE)
if (!drained.isEmpty && drained.amount == 1000) {
player.level.playSound(player, context.blockPos, soundEvent, SoundSource.BLOCKS)
val level = player.level as? ServerLevel ?: return InteractionResult.SUCCESS
level.setBlock(context.blockPos, newState, Block.UPDATE_ALL)
cap.drain(toDrain, IFluidHandler.FluidAction.EXECUTE)
if (item.count != 1 && !player.level.isClientSide) {
item.count--
if (!player.inventory.add(targetItem)) {
player.spawnAtLocation(targetItem)
}
}
return InteractionResult.sidedSuccess(player.level.isClientSide)
}
}
return InteractionResult.FAIL
}
}
private object EmptyCauldron : Interaction {
private val mapping = immutableMap {
put(Blocks.WATER_CAULDRON.defaultBlockState().setValue(LayeredCauldronBlock.LEVEL, 3), Fluids.WATER to SoundEvents.BUCKET_FILL)
put(Blocks.LAVA_CAULDRON.defaultBlockState(), Fluids.LAVA to SoundEvents.BUCKET_FILL_LAVA)
}
override fun canInteract(item: ItemStack, player: Player, context: Context): Boolean {
return player.level.getBlockState(context.blockPos) in mapping
}
override fun interact(item: ItemStack, player: Player, context: Context): InteractionResult {
if (item.isEmpty) return InteractionResult.FAIL
val targetItem = if (item.count == 1) item else item.copyWithCount(1)
val cap = targetItem.getCapability(ForgeCapabilities.FLUID_HANDLER_ITEM).orNull() ?: return InteractionResult.FAIL
val mapped = mapping[context.blockState] ?: return InteractionResult.FAIL
val toFill = FluidStack(mapped.first, 1000)
val fill = cap.fill(toFill, IFluidHandler.FluidAction.SIMULATE)
if (fill != 1000) return InteractionResult.FAIL
player.level.playSound(player, context.blockPos, mapped.second, SoundSource.BLOCKS)
val level = player.level as? ServerLevel ?: return InteractionResult.SUCCESS
level.setBlock(context.blockPos, Blocks.CAULDRON.defaultBlockState(), Block.UPDATE_ALL)
cap.fill(toFill, IFluidHandler.FluidAction.EXECUTE)
if (item.count != 1 && !player.level.isClientSide) {
item.count--
if (!player.inventory.add(targetItem)) {
player.spawnAtLocation(targetItem)
}
}
return InteractionResult.sidedSuccess(player.level.isClientSide)
}
}
private object FillEmptyCapability : Interaction {
override fun canInteract(item: ItemStack, player: Player, context: Context): Boolean {
val target = player.level.getBlockEntity(context.blockPos)?.getCapability(ForgeCapabilities.FLUID_HANDLER, context.side)?.orNull() ?: return false
val cap = item.getCapability(ForgeCapabilities.FLUID_HANDLER_ITEM).orNull() ?: return false
return target.stream().anyMatch { !it.isEmpty } || cap.stream().anyMatch { !it.isEmpty }
}
override fun interact(item: ItemStack, player: Player, context: Context): InteractionResult {
if (item.isEmpty) return InteractionResult.FAIL
val targetItem = if (item.count == 1) item else item.copyWithCount(1)
val itemCap = targetItem.getCapability(ForgeCapabilities.FLUID_HANDLER_ITEM).orNull() ?: return InteractionResult.FAIL
val blockCap = player.level.getBlockEntity(context.blockPos)?.getCapability(ForgeCapabilities.FLUID_HANDLER, context.side)?.orNull() ?: return InteractionResult.FAIL
val fluid = itemCap[0]
if (fluid.isEmpty || player.isCrouching) {
// заполняем из блока
val moveResult = moveFluid(source = blockCap, destination = itemCap, simulate = player.level.isClientSide)
if (!moveResult.isEmpty) {
val sound = moveResult.fluid.fluidType.getSound(moveResult, SoundActions.BUCKET_FILL)
if (sound != null) {
player.level.playSound(player, context.blockPos, sound, SoundSource.BLOCKS)
}
if (item.count != 1 && !player.level.isClientSide) {
item.count--
if (!player.inventory.add(targetItem)) {
player.spawnAtLocation(targetItem)
}
}
return InteractionResult.sidedSuccess(player.level.isClientSide)
} else {
return InteractionResult.FAIL
}
} else {
val moveResult = moveFluid(source = itemCap, destination = blockCap, simulate = player.level.isClientSide)
if (!moveResult.isEmpty) {
val sound = moveResult.fluid.fluidType.getSound(moveResult, SoundActions.BUCKET_EMPTY)
if (sound != null) {
player.level.playSound(player, context.blockPos, sound, SoundSource.BLOCKS)
}
if (item.count != 1 && !player.level.isClientSide) {
item.count--
if (!player.inventory.add(targetItem)) {
player.spawnAtLocation(targetItem)
}
}
return InteractionResult.sidedSuccess(player.level.isClientSide)
} else {
return InteractionResult.FAIL
}
}
}
}
data class Context(val blockPos: BlockPos, val blockState: BlockState, val side: Direction)
companion object : Interaction {
val interactions = immutableList {
accept(FillCauldron)
accept(EmptyCauldron)
accept(FillEmptyCapability)
}
override fun canInteract(item: ItemStack, player: Player, context: Context): Boolean {
return interactions.any { it.canInteract(item, player, context) }
}
override fun interact(item: ItemStack, player: Player, context: Context): InteractionResult {
return interactions.firstOrNull { it.canInteract(item, player, context) }?.interact(item, player, context) ?: InteractionResult.PASS
}
}
}

View File

@ -0,0 +1,18 @@
package ru.dbotthepony.mc.otm.item
import net.minecraftforge.event.entity.player.PlayerInteractEvent
import net.minecraftforge.eventbus.api.Event
interface PriorityUseItem {
fun isPriorityConsumer(event: PlayerInteractEvent.RightClickBlock): Boolean
}
internal fun onItemRightClick(event: PlayerInteractEvent.RightClickBlock) {
val item = event.itemStack.item
if (!event.itemStack.isEmpty && item is PriorityUseItem) {
if (item.isPriorityConsumer(event)) {
event.useBlock = Event.Result.DENY
}
}
}

View File

@ -4,8 +4,15 @@ import net.minecraft.world.item.CreativeModeTab
import net.minecraft.world.item.DyeColor import net.minecraft.world.item.DyeColor
import net.minecraft.world.item.Item import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.material.FlowingFluid
import net.minecraft.world.level.material.Fluids
import net.minecraftforge.common.capabilities.ForgeCapabilities
import net.minecraftforge.fluids.FluidStack
import net.minecraftforge.fluids.capability.IFluidHandler
import net.minecraftforge.registries.ForgeRegistries
import ru.dbotthepony.mc.otm.capability.matter.matter import ru.dbotthepony.mc.otm.capability.matter.matter
import ru.dbotthepony.mc.otm.capability.matteryEnergy import ru.dbotthepony.mc.otm.capability.matteryEnergy
import ru.dbotthepony.mc.otm.core.ifPresentK
import ru.dbotthepony.mc.otm.core.registryName import ru.dbotthepony.mc.otm.core.registryName
private fun CreativeModeTab.Output.accept(values: Collection<Item>) { private fun CreativeModeTab.Output.accept(values: Collection<Item>) {
@ -140,6 +147,18 @@ internal fun addMainCreativeTabItems(consumer: CreativeModeTab.Output) {
accept(MItems.PATTERN_DRIVE_CREATIVE) accept(MItems.PATTERN_DRIVE_CREATIVE)
accept(MItems.PATTERN_DRIVE_CREATIVE2) accept(MItems.PATTERN_DRIVE_CREATIVE2)
accept(MItems.FLUID_CAPSULE)
for (fluid in ForgeRegistries.FLUIDS.values) {
if (fluid != Fluids.EMPTY && fluid.isSource(fluid.defaultFluidState())) {
accept(ItemStack(MItems.FLUID_CAPSULE, 1).also {
it.getCapability(ForgeCapabilities.FLUID_HANDLER_ITEM).ifPresentK {
it.fill(FluidStack(fluid, it.getTankCapacity(0)), IFluidHandler.FluidAction.EXECUTE)
}
})
}
}
base(MItems.CARGO_CRATE_MINECARTS) base(MItems.CARGO_CRATE_MINECARTS)
accept(MItems.NUTRIENT_PASTE) accept(MItems.NUTRIENT_PASTE)

View File

@ -155,6 +155,8 @@ object MItems {
val ESSENCE_CAPSULE: EssenceCapsuleItem by registry.register("essence_capsule") { EssenceCapsuleItem() } val ESSENCE_CAPSULE: EssenceCapsuleItem by registry.register("essence_capsule") { EssenceCapsuleItem() }
val ESSENCE_DRIVE: EssenceCapsuleItem by registry.register("essence_drive") { EssenceCapsuleItem() } val ESSENCE_DRIVE: EssenceCapsuleItem by registry.register("essence_drive") { EssenceCapsuleItem() }
val FLUID_CAPSULE: FluidCapsuleItem by registry.register("fluid_capsule") { FluidCapsuleItem(ItemsConfig::FLUID_CAPSULE_CAPACITY) }
val TRITANIUM_COMPONENT: ForgeTier = ForgeTier( val TRITANIUM_COMPONENT: ForgeTier = ForgeTier(
Tiers.IRON.level, Tiers.IRON.level,
3072, 3072,