A lot of stuff related to configurable devices sides
This commit is contained in:
parent
993790fbb5
commit
a54d2f940f
@ -611,6 +611,24 @@ private fun gui(provider: MatteryLanguageProvider) {
|
||||
gui("item_monitor.amount.full", "Stack of ingredients. Craft until reaching stack size of one of ingredients. If at least one of inputs is not stackable then 'one stack' mode is used instead")
|
||||
|
||||
gui("stored_amount", "Exact amount stored: %s")
|
||||
|
||||
gui("sides.item_config", "Item Configuration")
|
||||
|
||||
gui("sides.top", "Top")
|
||||
gui("sides.bottom", "Bottom")
|
||||
gui("sides.front", "Front")
|
||||
gui("sides.back", "Back")
|
||||
gui("sides.left", "Left")
|
||||
gui("sides.right", "Right")
|
||||
|
||||
gui("side_mode.disabled", "Disabled")
|
||||
gui("side_mode.input", "Input only")
|
||||
gui("side_mode.output", "Output only")
|
||||
gui("side_mode.input_output", "Input/Output")
|
||||
gui("side_mode.battery", "Battery")
|
||||
|
||||
gui("side_mode.pull", "Pull")
|
||||
gui("side_mode.push", "Push")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -616,6 +616,24 @@ private fun gui(provider: MatteryLanguageProvider) {
|
||||
gui("item_monitor.amount.full", "Стопку ингредиентов. Создание продолжается пока не будет достигнут лимит по стопке одного из ингредиентов. Если хотя бы один из ингредиентов не может быть стопкой, будет использован режим 'одна стопка результата'")
|
||||
|
||||
gui("stored_amount", "Точное количество в хранилище: %s шт.")
|
||||
|
||||
gui("sides.item_config", "Настройка Предметов")
|
||||
|
||||
gui("sides.top", "Верхняя сторона")
|
||||
gui("sides.bottom", "Нижняя сторона")
|
||||
gui("sides.front", "Передняя сторона")
|
||||
gui("sides.back", "Задняя сторона")
|
||||
gui("sides.left", "Левая сторона")
|
||||
gui("sides.right", "Правая сторона")
|
||||
|
||||
gui("side_mode.disabled", "Отключено")
|
||||
gui("side_mode.input", "Только вход")
|
||||
gui("side_mode.output", "Только выход")
|
||||
gui("side_mode.input_output", "Вход/Выход")
|
||||
gui("side_mode.battery", "Батарея")
|
||||
|
||||
gui("side_mode.pull", "Автоматическое вытягивание")
|
||||
gui("side_mode.push", "Автоматическое выталкивание")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,6 @@ import com.google.common.collect.ImmutableSet
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectFunction
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectArrayMap
|
||||
import net.minecraft.client.multiplayer.ClientLevel
|
||||
import net.minecraft.core.BlockPos
|
||||
@ -12,11 +11,10 @@ import net.minecraft.core.Direction
|
||||
import net.minecraft.core.SectionPos
|
||||
import net.minecraft.core.Vec3i
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.nbt.StringTag
|
||||
import net.minecraft.nbt.Tag
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.level.ChunkPos
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.block.Block
|
||||
@ -35,24 +33,30 @@ import net.minecraftforge.event.level.ChunkWatchEvent
|
||||
import net.minecraftforge.event.level.LevelEvent
|
||||
import net.minecraftforge.event.server.ServerStoppingEvent
|
||||
import net.minecraftforge.items.IItemHandler
|
||||
import ru.dbotthepony.mc.otm.SERVER_IS_LIVE
|
||||
import ru.dbotthepony.mc.otm.block.RotatableMatteryBlock
|
||||
import ru.dbotthepony.mc.otm.capability.FlowDirection
|
||||
import ru.dbotthepony.mc.otm.capability.CombinedItemHandler
|
||||
import ru.dbotthepony.mc.otm.capability.EmptyItemHandler
|
||||
import ru.dbotthepony.mc.otm.capability.MatteryCapability
|
||||
import ru.dbotthepony.mc.otm.capability.UnmodifiableItemHandler
|
||||
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
|
||||
import ru.dbotthepony.mc.otm.capability.isMekanismLoaded
|
||||
import ru.dbotthepony.mc.otm.capability.moveBetweenSlots
|
||||
import ru.dbotthepony.mc.otm.compat.mekanism.Mattery2MekanismEnergyWrapper
|
||||
import ru.dbotthepony.mc.otm.core.collect.SupplierList
|
||||
import ru.dbotthepony.mc.otm.core.collect.WeakHashSet
|
||||
import ru.dbotthepony.mc.otm.core.forValidRefs
|
||||
import ru.dbotthepony.mc.otm.core.forValidRefsBreak
|
||||
import ru.dbotthepony.mc.otm.core.get
|
||||
import ru.dbotthepony.mc.otm.core.getValue
|
||||
import ru.dbotthepony.mc.otm.core.ifPresentK
|
||||
import ru.dbotthepony.mc.otm.core.immutableMap
|
||||
import ru.dbotthepony.mc.otm.core.math.BlockRotation
|
||||
import ru.dbotthepony.mc.otm.core.math.RelativeSide
|
||||
import ru.dbotthepony.mc.otm.core.math.minus
|
||||
import ru.dbotthepony.mc.otm.core.math.plus
|
||||
import ru.dbotthepony.mc.otm.core.nbt.set
|
||||
import ru.dbotthepony.mc.otm.core.util.EnumValueCodec
|
||||
import ru.dbotthepony.mc.otm.core.util.ITickable
|
||||
import ru.dbotthepony.mc.otm.core.util.Savetables
|
||||
import ru.dbotthepony.mc.otm.core.util.TickList
|
||||
import ru.dbotthepony.mc.otm.network.BlockEntitySyncPacket
|
||||
import ru.dbotthepony.mc.otm.network.FieldSynchronizer
|
||||
import ru.dbotthepony.mc.otm.network.WorldNetworkChannel
|
||||
@ -88,6 +92,12 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc
|
||||
|
||||
private data class SidelessCap<T : Any>(val cap: T, var optional: LazyOptional<T>)
|
||||
private val sidelessCaps = Reference2ObjectArrayMap<Capability<*>, SidelessCap<*>>()
|
||||
protected val tickList = TickList()
|
||||
protected val savetables = Savetables()
|
||||
|
||||
open fun tick() {
|
||||
tickList.tick()
|
||||
}
|
||||
|
||||
/**
|
||||
* exposes capability when no side is specified
|
||||
@ -138,16 +148,7 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc
|
||||
}
|
||||
}
|
||||
|
||||
protected fun exposeEnergy(side: RelativeSide, value: IMatteryEnergyStorage) {
|
||||
_sides[side]!!.Cap(ForgeCapabilities.ENERGY, value)
|
||||
_sides[side]!!.Cap(MatteryCapability.ENERGY, value)
|
||||
|
||||
if (isMekanismLoaded) {
|
||||
_sides[side]!!.Cap(MatteryCapability.MEKANISM_ENERGY, Mattery2MekanismEnergyWrapper(value))
|
||||
}
|
||||
}
|
||||
|
||||
protected fun exposeEnergySideless( value: IMatteryEnergyStorage) {
|
||||
protected fun exposeEnergySideless(value: IMatteryEnergyStorage) {
|
||||
exposeSideless(ForgeCapabilities.ENERGY, value)
|
||||
exposeSideless(MatteryCapability.ENERGY, value)
|
||||
|
||||
@ -156,6 +157,17 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc
|
||||
}
|
||||
}
|
||||
|
||||
protected fun exposeEnergy(side: RelativeSide, value: IMatteryEnergyStorage) {
|
||||
val thisSide = _sides[side]!!
|
||||
|
||||
thisSide.Cap(ForgeCapabilities.ENERGY, value)
|
||||
thisSide.Cap(MatteryCapability.ENERGY, value)
|
||||
|
||||
if (isMekanismLoaded) {
|
||||
thisSide.Cap(MatteryCapability.MEKANISM_ENERGY, Mattery2MekanismEnergyWrapper(value))
|
||||
}
|
||||
}
|
||||
|
||||
protected fun exposeItemsGlobally(value: IItemHandler) {
|
||||
exposeGlobally(ForgeCapabilities.ITEM_HANDLER, value)
|
||||
}
|
||||
@ -165,47 +177,13 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc
|
||||
return SupplierList(_sides.values.map { it.track(capability)::get })
|
||||
}
|
||||
|
||||
enum class SideMode(val filter: FlowDirection, val automation: FlowDirection) {
|
||||
DISABLED (FlowDirection.NONE, FlowDirection.NONE),
|
||||
NONE (FlowDirection.BI_DIRECTIONAL, FlowDirection.NONE),
|
||||
INPUT (FlowDirection.INPUT, FlowDirection.NONE),
|
||||
OUTPUT (FlowDirection.OUTPUT, FlowDirection.NONE),
|
||||
|
||||
PULL (FlowDirection.INPUT, FlowDirection.INPUT),
|
||||
PUSH (FlowDirection.OUTPUT, FlowDirection.OUTPUT),
|
||||
PULL_ONLY (FlowDirection.BI_DIRECTIONAL, FlowDirection.INPUT),
|
||||
PUSH_ONLY (FlowDirection.BI_DIRECTIONAL, FlowDirection.OUTPUT),
|
||||
PULL_PUSH (FlowDirection.BI_DIRECTIONAL, FlowDirection.BI_DIRECTIONAL);
|
||||
|
||||
val isOpen = filter != FlowDirection.NONE
|
||||
|
||||
companion object {
|
||||
@JvmField
|
||||
val BI_SET: ImmutableSet<SideMode> = ImmutableSet.of(NONE, INPUT, OUTPUT, PULL, PUSH, PULL_ONLY, PUSH_ONLY, PULL_PUSH, DISABLED)
|
||||
|
||||
@JvmField
|
||||
val INPUT_SET: ImmutableSet<SideMode> = ImmutableSet.of(INPUT, PULL, DISABLED)
|
||||
|
||||
@JvmField
|
||||
val OUTPUT_SET: ImmutableSet<SideMode> = ImmutableSet.of(OUTPUT, PUSH, DISABLED)
|
||||
|
||||
@JvmField
|
||||
val CODEC = EnumValueCodec(SideMode::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
fun interface SideModeListener {
|
||||
fun sideModeChanges(config: Side.ModeState, new: SideMode, old: SideMode)
|
||||
}
|
||||
|
||||
inner class Side(val side: RelativeSide) : INBTSerializable<CompoundTag> {
|
||||
inner class Side(val side: RelativeSide) {
|
||||
init {
|
||||
check(!_sides.containsKey(side)) { "dafuq are you trying to do" }
|
||||
_sides[side] = this
|
||||
}
|
||||
|
||||
private val caps = Reference2ObjectArrayMap<Capability<*>, Cap<*>>()
|
||||
private val data = Object2ObjectArrayMap<ResourceLocation, INBTSerializable<Tag>>()
|
||||
private val subscriptions = Reference2ObjectArrayMap<Capability<*>, SubRef<*>>()
|
||||
private val knownLOs = WeakHashSet<LazyOptional<*>>()
|
||||
|
||||
@ -240,7 +218,7 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc
|
||||
}
|
||||
|
||||
private fun updateTracked(capability: Capability<*>) {
|
||||
if (isRemoved) return
|
||||
if (isRemoved || !SERVER_IS_LIVE) return
|
||||
val dir = blockRotation.side2Dir(side)
|
||||
|
||||
val chunk = level
|
||||
@ -282,21 +260,10 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc
|
||||
}
|
||||
}
|
||||
|
||||
val isEmpty get() = caps.isEmpty() && data.isEmpty()
|
||||
|
||||
operator fun <T : Any> get(capability: Capability<T>): Cap<T>? {
|
||||
return caps[capability] as Cap<T>?
|
||||
}
|
||||
|
||||
fun addData(index: ResourceLocation, data: INBTSerializable<*>) {
|
||||
require(!this.data.containsKey(index)) { "Already has data with ID $index on $side!" }
|
||||
this.data[index] = data as INBTSerializable<Tag>
|
||||
}
|
||||
|
||||
fun getData(index: ResourceLocation): INBTSerializable<*>? {
|
||||
return data[index]
|
||||
}
|
||||
|
||||
fun invalidate() {
|
||||
for (cap in caps.values)
|
||||
cap.invalidate()
|
||||
@ -307,87 +274,18 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc
|
||||
cap.revive()
|
||||
}
|
||||
|
||||
override fun serializeNBT(): CompoundTag {
|
||||
return CompoundTag().also {
|
||||
for ((k, v) in data) {
|
||||
it[k.toString()] = v.serializeNBT()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun deserializeNBT(nbt: CompoundTag) {
|
||||
for ((k, v) in data) {
|
||||
val tag = nbt[k.toString()]
|
||||
|
||||
if (tag != null) {
|
||||
v.deserializeNBT(tag)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inner class ModeState(val name: String, val possibleValues: ImmutableSet<SideMode> = SideMode.BI_SET) : INBTSerializable<StringTag> {
|
||||
init {
|
||||
addData(ResourceLocation(name), this)
|
||||
}
|
||||
|
||||
val side get() = this@Side.side
|
||||
|
||||
var mode by synchronizer.Field(possibleValues.first(), SideMode.CODEC, setter = { value, access, setByRemote ->
|
||||
require(value in possibleValues) { "Value $value is not allowed (allowed values: $possibleValues)" }
|
||||
val old = access.read()
|
||||
|
||||
if (value != old) {
|
||||
access.write(value)
|
||||
|
||||
listeners.forValidRefs {
|
||||
it.sideModeChanges(this, value, old)
|
||||
}
|
||||
}
|
||||
}, name = "SideConfig/$side/$name")
|
||||
|
||||
fun isAllowed(value: SideMode): Boolean = value in possibleValues
|
||||
|
||||
private val listeners = ArrayList<WeakReference<SideModeListener>>()
|
||||
|
||||
fun addListener(listener: SideModeListener) {
|
||||
var hit = false
|
||||
|
||||
listeners.forValidRefsBreak {
|
||||
if (it === listener) {
|
||||
hit = true
|
||||
return@forValidRefsBreak true
|
||||
}
|
||||
|
||||
return@forValidRefsBreak false
|
||||
}
|
||||
|
||||
if (!hit) {
|
||||
listeners.add(WeakReference(listener))
|
||||
}
|
||||
}
|
||||
|
||||
override fun serializeNBT(): StringTag {
|
||||
return StringTag.valueOf(mode.name)
|
||||
}
|
||||
|
||||
override fun deserializeNBT(nbt: StringTag) {
|
||||
mode = SideMode.valueOf(nbt.asString)
|
||||
}
|
||||
|
||||
fun deserializeNBT(nbt: String) {
|
||||
mode = SideMode.valueOf(nbt)
|
||||
}
|
||||
}
|
||||
|
||||
inner class Cap<T : Any>(val type: Capability<in T>, val capability: T) {
|
||||
init {
|
||||
check(!caps.containsKey(type)) { "Already has capability $type on side $side" }
|
||||
caps[type] = this
|
||||
}
|
||||
|
||||
private var isExposed = true
|
||||
private var isValid = true
|
||||
private var isRemoved = false
|
||||
var isExposed = true
|
||||
private set
|
||||
var isValid = true
|
||||
private set
|
||||
var isRemoved = false
|
||||
private set
|
||||
|
||||
var optional: LazyOptional<T> by object : ReadWriteProperty<Any?, LazyOptional<T>> {
|
||||
private var value: LazyOptional<T>? = null
|
||||
@ -419,7 +317,9 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc
|
||||
if (!isRemoved && isExposed) {
|
||||
isExposed = false
|
||||
optional.invalidate()
|
||||
setChanged()
|
||||
|
||||
if (SERVER_IS_LIVE)
|
||||
level?.once { setChanged() }
|
||||
}
|
||||
}
|
||||
|
||||
@ -429,7 +329,9 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc
|
||||
|
||||
if (isValid) {
|
||||
optional = LazyOptional.of { capability }
|
||||
setChanged()
|
||||
|
||||
if (SERVER_IS_LIVE)
|
||||
level?.once { setChanged() }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -438,7 +340,9 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc
|
||||
if (!isRemoved && isValid) {
|
||||
isValid = false
|
||||
optional.invalidate()
|
||||
setChanged()
|
||||
|
||||
if (SERVER_IS_LIVE)
|
||||
level?.once { setChanged() }
|
||||
}
|
||||
}
|
||||
|
||||
@ -448,7 +352,9 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc
|
||||
|
||||
if (isExposed) {
|
||||
optional = LazyOptional.of { capability }
|
||||
setChanged()
|
||||
|
||||
if (SERVER_IS_LIVE)
|
||||
level?.once { setChanged() }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -489,41 +395,17 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc
|
||||
side.revive()
|
||||
}
|
||||
|
||||
protected val savetables = Savetables()
|
||||
|
||||
protected inline fun <reified T : Tag, V : INBTSerializable<T?>> savetable(property: KProperty0<V>, name: String = property.name) {
|
||||
savetables.stateful(property, name, T::class.java)
|
||||
}
|
||||
|
||||
override fun saveAdditional(nbt: CompoundTag) {
|
||||
super.saveAdditional(nbt)
|
||||
|
||||
if (_sides.values.any { !it.isEmpty }) {
|
||||
nbt["Sides"] = CompoundTag().also {
|
||||
for (side in _sides.values) {
|
||||
if (!side.isEmpty) {
|
||||
it[side.side.name] = side.serializeNBT()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
savetables.serializeNBT(nbt)
|
||||
}
|
||||
|
||||
override fun load(nbt: CompoundTag) {
|
||||
super.load(nbt)
|
||||
|
||||
if (nbt.contains("Sides")) {
|
||||
val tag = nbt.getCompound("Sides")
|
||||
|
||||
for (side in _sides.values) {
|
||||
if (side.side.name in tag) {
|
||||
side.deserializeNBT(tag.getCompound(side.side.name))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
savetables.deserializeNBT(nbt)
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
package ru.dbotthepony.mc.otm.block.entity
|
||||
|
||||
import com.google.common.collect.ImmutableSet
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
@ -13,12 +14,24 @@ import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity
|
||||
import net.minecraftforge.common.capabilities.ForgeCapabilities
|
||||
import net.minecraftforge.items.IItemHandler
|
||||
import ru.dbotthepony.mc.otm.capability.CombinedItemHandler
|
||||
import ru.dbotthepony.mc.otm.capability.EmptyItemHandler
|
||||
import ru.dbotthepony.mc.otm.capability.UnmodifiableItemHandler
|
||||
import ru.dbotthepony.mc.otm.capability.moveBetweenSlots
|
||||
import ru.dbotthepony.mc.otm.core.TextComponent
|
||||
import ru.dbotthepony.mc.otm.core.getValue
|
||||
import ru.dbotthepony.mc.otm.core.ifPresentK
|
||||
import ru.dbotthepony.mc.otm.core.immutableMap
|
||||
import ru.dbotthepony.mc.otm.core.math.RelativeSide
|
||||
import ru.dbotthepony.mc.otm.core.nbt.getJson
|
||||
import ru.dbotthepony.mc.otm.core.nbt.putJson
|
||||
import ru.dbotthepony.mc.otm.core.nbt.set
|
||||
import ru.dbotthepony.mc.otm.core.util.ITickable
|
||||
import ru.dbotthepony.mc.otm.oncePre
|
||||
|
||||
/**
|
||||
@ -61,4 +74,231 @@ abstract class MatteryDeviceBlockEntity(blockEntityType: BlockEntityType<*>, blo
|
||||
customDisplayName = nbt.getJson("Name")?.let(Component.Serializer::fromJson)
|
||||
redstoneControl.deserializeNBT(nbt[REDSTONE_CONTROL_KEY] as? CompoundTag)
|
||||
}
|
||||
|
||||
|
||||
enum class ItemHandlerView(val translationKey: String) {
|
||||
DISABLED("otm.gui.side_mode.disabled"),
|
||||
INPUT("otm.gui.side_mode.input"),
|
||||
OUTPUT("otm.gui.side_mode.output"),
|
||||
INPUT_OUTPUT("otm.gui.side_mode.input_output"),
|
||||
BATTERY("otm.gui.side_mode.battery");
|
||||
}
|
||||
|
||||
inner class GlobalItemHandler(
|
||||
input: IItemHandler? = null,
|
||||
output: IItemHandler? = null,
|
||||
battery: IItemHandler? = null,
|
||||
val frontDefault: ItemHandlerView = determineDefaultMode(input, output),
|
||||
val backDefault: ItemHandlerView = determineDefaultMode(input, output),
|
||||
val leftDefault: ItemHandlerView = determineDefaultMode(input, output),
|
||||
val rightDefault: ItemHandlerView = determineDefaultMode(input, output),
|
||||
val topDefault: ItemHandlerView = determineDefaultMode(input, output),
|
||||
val bottomDefault: ItemHandlerView = determineDefaultMode(input, output),
|
||||
) {
|
||||
val sideless: IItemHandler
|
||||
|
||||
init {
|
||||
val caps = ArrayList<IItemHandler>()
|
||||
|
||||
if (input != null) caps.add(input)
|
||||
if (output != null) caps.add(output)
|
||||
if (battery != null) caps.add(battery)
|
||||
|
||||
sideless = UnmodifiableItemHandler(CombinedItemHandler(caps))
|
||||
exposeSideless(ForgeCapabilities.ITEM_HANDLER, sideless)
|
||||
}
|
||||
|
||||
val front = ConfigurableItemHandler(RelativeSide.FRONT, input, output, battery).also { it.mode = frontDefault }
|
||||
val back = ConfigurableItemHandler(RelativeSide.BACK, input, output, battery).also { it.mode = backDefault }
|
||||
val left = ConfigurableItemHandler(RelativeSide.LEFT, input, output, battery).also { it.mode = leftDefault }
|
||||
val right = ConfigurableItemHandler(RelativeSide.RIGHT, input, output, battery).also { it.mode = rightDefault }
|
||||
val top = ConfigurableItemHandler(RelativeSide.TOP, input, output, battery).also { it.mode = topDefault }
|
||||
val bottom = ConfigurableItemHandler(RelativeSide.BOTTOM, input, output, battery).also { it.mode = bottomDefault }
|
||||
|
||||
val sides = immutableMap {
|
||||
put(RelativeSide.FRONT, this@GlobalItemHandler.front)
|
||||
put(RelativeSide.BACK, this@GlobalItemHandler.back)
|
||||
put(RelativeSide.LEFT, this@GlobalItemHandler.left)
|
||||
put(RelativeSide.RIGHT, this@GlobalItemHandler.right)
|
||||
put(RelativeSide.TOP, this@GlobalItemHandler.top)
|
||||
put(RelativeSide.BOTTOM, this@GlobalItemHandler.bottom)
|
||||
}
|
||||
|
||||
val defaults = immutableMap {
|
||||
put(RelativeSide.FRONT, frontDefault)
|
||||
put(RelativeSide.BACK, backDefault)
|
||||
put(RelativeSide.LEFT, leftDefault)
|
||||
put(RelativeSide.RIGHT, rightDefault)
|
||||
put(RelativeSide.TOP, topDefault)
|
||||
put(RelativeSide.BOTTOM, bottomDefault)
|
||||
}
|
||||
}
|
||||
|
||||
inner class ConfigurableItemHandler(
|
||||
side: RelativeSide,
|
||||
val input: IItemHandler? = null,
|
||||
val output: IItemHandler? = null,
|
||||
val battery: IItemHandler? = null,
|
||||
) : IItemHandler, ITickable {
|
||||
private var currentHandler: IItemHandler = EmptyItemHandler
|
||||
val capController = sides[side]!!.Cap(ForgeCapabilities.ITEM_HANDLER, this)
|
||||
private val neighbour by sides[side]!!.track(ForgeCapabilities.ITEM_HANDLER)
|
||||
|
||||
val possibleViews: ImmutableSet<ItemHandlerView>
|
||||
val inputOutput: IItemHandler?
|
||||
|
||||
init {
|
||||
tickList.always(this)
|
||||
|
||||
val builder = ImmutableSet.Builder<ItemHandlerView>()
|
||||
|
||||
builder.add(ItemHandlerView.DISABLED)
|
||||
|
||||
if (input != null) builder.add(ItemHandlerView.INPUT)
|
||||
if (output != null) builder.add(ItemHandlerView.OUTPUT)
|
||||
if (input != null && output != null) builder.add(ItemHandlerView.INPUT_OUTPUT)
|
||||
if (battery != null) builder.add(ItemHandlerView.BATTERY)
|
||||
|
||||
possibleViews = builder.build()
|
||||
|
||||
capController.close()
|
||||
|
||||
if (input != null && output != null) {
|
||||
inputOutput = CombinedItemHandler(input, output)
|
||||
} else {
|
||||
inputOutput = null
|
||||
}
|
||||
}
|
||||
|
||||
var mode by synchronizer.enum(ItemHandlerView.DISABLED, setter = { value, access, setByRemote ->
|
||||
require(value in possibleViews) { "View type $value is not allowed (allowed views: $possibleViews)" }
|
||||
|
||||
if (access.read() != value) {
|
||||
access.write(value)
|
||||
|
||||
if (value == ItemHandlerView.DISABLED) {
|
||||
capController.close()
|
||||
} else {
|
||||
capController.close()
|
||||
capController.expose()
|
||||
}
|
||||
|
||||
when (value) {
|
||||
ItemHandlerView.DISABLED -> currentHandler = EmptyItemHandler
|
||||
ItemHandlerView.INPUT -> currentHandler = input!!
|
||||
ItemHandlerView.OUTPUT -> currentHandler = output!!
|
||||
ItemHandlerView.INPUT_OUTPUT -> currentHandler = inputOutput!!
|
||||
ItemHandlerView.BATTERY -> currentHandler = battery!!
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
var automatePull = false
|
||||
set(value) {
|
||||
if (field != value) {
|
||||
field = value
|
||||
|
||||
if (value) {
|
||||
innerSlotPull = 0
|
||||
outerSlotPull = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var automatePush = false
|
||||
set(value) {
|
||||
if (field != value) {
|
||||
field = value
|
||||
|
||||
if (value) {
|
||||
innerSlotPush = 0
|
||||
outerSlotPush = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
savetables.bool(::automatePull, "itemhandler_${side}_automatePull")
|
||||
savetables.bool(::automatePush, "itemhandler_${side}_automatePush")
|
||||
savetables.enum(::mode, "itemhandler_${side}_mode", ItemHandlerView::valueOf)
|
||||
}
|
||||
|
||||
private var innerSlotPull = 0
|
||||
private var outerSlotPull = 0
|
||||
|
||||
private var innerSlotPush = 0
|
||||
private var outerSlotPush = 0
|
||||
|
||||
override fun tick() {
|
||||
if (mode == ItemHandlerView.DISABLED || currentHandler.slots == 0 || redstoneControl.isBlockedByRedstone)
|
||||
return
|
||||
|
||||
neighbour.ifPresentK {
|
||||
if (it.slots == 0)
|
||||
return
|
||||
|
||||
if (automatePull) {
|
||||
if (innerSlotPull !in 0 until currentHandler.slots)
|
||||
innerSlotPull = 0
|
||||
|
||||
if (outerSlotPull !in 0 until it.slots)
|
||||
outerSlotPull = 0
|
||||
|
||||
val (outerSlotPull, innerSlotPull) = moveBetweenSlots(it, outerSlotPull, currentHandler, innerSlotPull)
|
||||
this.innerSlotPull = innerSlotPull
|
||||
this.outerSlotPull = outerSlotPull
|
||||
}
|
||||
|
||||
if (automatePush) {
|
||||
if (innerSlotPush !in 0 until currentHandler.slots)
|
||||
innerSlotPush = 0
|
||||
|
||||
if (outerSlotPush !in 0 until it.slots)
|
||||
outerSlotPush = 0
|
||||
|
||||
val (innerSlotPush, outerSlotPush) = moveBetweenSlots(currentHandler, innerSlotPush, it, outerSlotPush)
|
||||
this.innerSlotPush = innerSlotPush
|
||||
this.outerSlotPush = outerSlotPush
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getSlots(): Int {
|
||||
return currentHandler.slots
|
||||
}
|
||||
|
||||
override fun getStackInSlot(slot: Int): ItemStack {
|
||||
return currentHandler.getStackInSlot(slot)
|
||||
}
|
||||
|
||||
override fun insertItem(slot: Int, stack: ItemStack, simulate: Boolean): ItemStack {
|
||||
return currentHandler.insertItem(slot, stack, simulate)
|
||||
}
|
||||
|
||||
override fun extractItem(slot: Int, amount: Int, simulate: Boolean): ItemStack {
|
||||
return currentHandler.extractItem(slot, amount, simulate)
|
||||
}
|
||||
|
||||
override fun getSlotLimit(slot: Int): Int {
|
||||
return currentHandler.getSlotLimit(slot)
|
||||
}
|
||||
|
||||
override fun isItemValid(slot: Int, stack: ItemStack): Boolean {
|
||||
return currentHandler.isItemValid(slot, stack)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private fun determineDefaultMode(input: IItemHandler?, output: IItemHandler?): ItemHandlerView {
|
||||
if (input == null && output == null)
|
||||
return ItemHandlerView.DISABLED
|
||||
else if (input != null && output != null)
|
||||
return ItemHandlerView.INPUT_OUTPUT
|
||||
else if (output != null)
|
||||
return ItemHandlerView.OUTPUT
|
||||
else
|
||||
return ItemHandlerView.INPUT
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -33,7 +33,9 @@ abstract class MatteryPoweredBlockEntity(p_155228_: BlockEntityType<*>, p_155229
|
||||
savetable(::batteryContainer, BATTERY_KEY)
|
||||
}
|
||||
|
||||
protected fun batteryChargeLoop() {
|
||||
override fun tick() {
|
||||
super.tick()
|
||||
|
||||
val energy = matteryEnergy
|
||||
if (energy == null || !batteryContainer.any { !it.isEmpty })
|
||||
return
|
||||
|
@ -233,7 +233,9 @@ abstract class MatteryWorkerBlockEntity<JobType : MatteryWorkerBlockEntity.Job>(
|
||||
isIdling = newBlocked
|
||||
}
|
||||
|
||||
protected fun workerLoop() {
|
||||
override fun tick() {
|
||||
super.tick()
|
||||
|
||||
if (errorTicksAnim > 20 && blockState.hasProperty(WorkerState.WORKER_STATE) && blockState.getValue(WorkerState.WORKER_STATE) != WorkerState.ERROR) {
|
||||
level?.setBlock(blockPos, blockState.setValue(WorkerState.WORKER_STATE, WorkerState.ERROR), Block.UPDATE_CLIENTS)
|
||||
}
|
||||
@ -379,11 +381,6 @@ abstract class MatteryWorkerBlockEntity<JobType : MatteryWorkerBlockEntity.Job>(
|
||||
}
|
||||
}
|
||||
|
||||
fun basicTicker() {
|
||||
batteryChargeLoop()
|
||||
workerLoop()
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val WORK_TICKS_KEY = "workTicks"
|
||||
const val JOB_KEY = "job"
|
||||
|
@ -215,7 +215,8 @@ class BlackHoleBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mattery
|
||||
}
|
||||
}
|
||||
|
||||
fun tick() {
|
||||
override fun tick() {
|
||||
super.tick()
|
||||
sleepTicks--
|
||||
if (sleepTicks > 0) return
|
||||
val level = level as? ServerLevel ?: return
|
||||
|
@ -169,8 +169,8 @@ class MatterBottlerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
|
||||
matterNode.destroy(::MatterNetworkGraph)
|
||||
}
|
||||
|
||||
fun tick() {
|
||||
batteryChargeLoop()
|
||||
override fun tick() {
|
||||
super.tick()
|
||||
|
||||
if (redstoneControl.isBlockedByRedstone) {
|
||||
if (blockState.getValue(WorkerState.SEMI_WORKER_STATE) !== WorkerState.IDLE) {
|
||||
|
@ -215,9 +215,8 @@ class MatterDecomposerBlockEntity(pos: BlockPos, state: BlockState)
|
||||
return matter
|
||||
}
|
||||
|
||||
fun tick() {
|
||||
batteryChargeLoop()
|
||||
workerLoop()
|
||||
override fun tick() {
|
||||
super.tick()
|
||||
|
||||
val grid = matterNode.graph as MatterNetworkGraph? ?: return
|
||||
|
||||
|
@ -154,8 +154,8 @@ class MatterRecyclerBlockEntity(blockPos: BlockPos, blockState: BlockState)
|
||||
return Status.SUCCESS
|
||||
}
|
||||
|
||||
fun tick() {
|
||||
basicTicker()
|
||||
override fun tick() {
|
||||
super.tick()
|
||||
|
||||
val graph = matterNode.graph as MatterNetworkGraph? ?: return
|
||||
val received = graph.receiveMatter(matter.storedMatter, false)
|
||||
|
@ -57,8 +57,8 @@ class DriveRackBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
|
||||
StorageNetworkGraph.discoverFull(this, cell.storageNode)
|
||||
}
|
||||
|
||||
fun tick() {
|
||||
batteryChargeLoop()
|
||||
override fun tick() {
|
||||
super.tick()
|
||||
cell.tickEnergyDemanding()
|
||||
}
|
||||
|
||||
|
@ -81,8 +81,4 @@ class DriveViewerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Matte
|
||||
override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu {
|
||||
return DriveViewerMenu(containerID, inventory, this)
|
||||
}
|
||||
|
||||
fun tick() {
|
||||
batteryChargeLoop()
|
||||
}
|
||||
}
|
||||
|
@ -481,8 +481,9 @@ class ItemMonitorBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
|
||||
return settings.computeIfAbsent(ply.uuid) { ItemMonitorPlayerSettings() }
|
||||
}
|
||||
|
||||
fun tick() {
|
||||
batteryChargeLoop()
|
||||
override fun tick() {
|
||||
super.tick()
|
||||
|
||||
cell.tickEnergyDemanding()
|
||||
|
||||
if (craftingAmount.size != 0)
|
||||
|
@ -123,8 +123,8 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter
|
||||
private var neighbour: LazyOptional<IItemHandler>? = null
|
||||
private var component: ItemHandlerComponent? = null
|
||||
|
||||
fun tick() {
|
||||
batteryChargeLoop()
|
||||
override fun tick() {
|
||||
super.tick()
|
||||
component?.scan()
|
||||
cell.tickEnergyDemanding()
|
||||
}
|
||||
|
@ -175,11 +175,12 @@ class StorageImporterBlockEntity(blockPos: BlockPos, blockState: BlockState)
|
||||
return filter.match(stack)
|
||||
}
|
||||
|
||||
fun tick() {
|
||||
override fun tick() {
|
||||
super.tick()
|
||||
|
||||
if (redstoneControl.isBlockedByRedstone)
|
||||
return
|
||||
|
||||
batteryChargeLoop()
|
||||
cell.tickEnergyDemanding()
|
||||
|
||||
nextTick--
|
||||
@ -287,11 +288,12 @@ class StorageExporterBlockEntity(blockPos: BlockPos, blockState: BlockState) :
|
||||
return relevantTuples.stream().map { it to view[it] }
|
||||
}
|
||||
|
||||
fun tick() {
|
||||
override fun tick() {
|
||||
super.tick()
|
||||
|
||||
if (redstoneControl.isBlockedByRedstone)
|
||||
return
|
||||
|
||||
batteryChargeLoop()
|
||||
cell.tickEnergyDemanding()
|
||||
|
||||
nextTick--
|
||||
|
@ -53,12 +53,12 @@ class StoragePowerSupplierBlockEntity(blockPos: BlockPos, blockState: BlockState
|
||||
cell.destroy(level)
|
||||
}
|
||||
|
||||
fun tick() {
|
||||
override fun tick() {
|
||||
super.tick()
|
||||
|
||||
if (redstoneControl.isBlockedByRedstone)
|
||||
return
|
||||
|
||||
batteryChargeLoop()
|
||||
|
||||
if (energy.batteryLevel.isZero)
|
||||
return
|
||||
|
||||
|
@ -65,7 +65,9 @@ class AndroidStationBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
|
||||
|
||||
private var tickedOnce = false
|
||||
|
||||
fun tick() {
|
||||
override fun tick() {
|
||||
super.tick()
|
||||
|
||||
if (!tickedOnce) {
|
||||
tickedOnce = true
|
||||
|
||||
@ -76,8 +78,6 @@ class AndroidStationBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
|
||||
}
|
||||
}
|
||||
|
||||
batteryChargeLoop()
|
||||
|
||||
if (redstoneControl.isBlockedByRedstone) return
|
||||
val level = level ?: return
|
||||
val x = blockPos.x.toDouble()
|
||||
|
@ -206,7 +206,9 @@ class BatteryBankBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Matte
|
||||
|
||||
private val consumers by front.track(ForgeCapabilities.ENERGY)
|
||||
|
||||
fun tick() {
|
||||
override fun tick() {
|
||||
super.tick()
|
||||
|
||||
if (redstoneControl.isBlockedByRedstone)
|
||||
return
|
||||
|
||||
|
@ -89,7 +89,9 @@ class ChemicalGeneratorBlockEntity(pos: BlockPos, state: BlockState) : MatteryDe
|
||||
}
|
||||
}
|
||||
|
||||
fun tick() {
|
||||
override fun tick() {
|
||||
super.tick()
|
||||
|
||||
if (workTicks > 0) {
|
||||
workTicks--
|
||||
energy.receiveEnergy(GENERATION_SPEED, false)
|
||||
|
@ -22,14 +22,15 @@ class CobblerBlockEntity(blockPos: BlockPos, blockState: BlockState)
|
||||
|
||||
override val droppableContainer = MatteryContainer(this::itemContainerUpdated, CONTAINER_SIZE)
|
||||
|
||||
val handler = droppableContainer.handler(object : HandlerFilter {
|
||||
val itemHandler = droppableContainer.handler(object : HandlerFilter {
|
||||
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
val itemConfig = GlobalItemHandler(output = itemHandler)
|
||||
|
||||
init {
|
||||
exposeItemsGlobally(handler)
|
||||
savetable(::droppableContainer, INVENTORY_KEY)
|
||||
}
|
||||
|
||||
|
@ -285,7 +285,9 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat
|
||||
}
|
||||
}
|
||||
|
||||
fun tick() {
|
||||
override fun tick() {
|
||||
super.tick()
|
||||
|
||||
lastTick = history[historyTick]
|
||||
historyTick = (historyTick + 1) % history.size
|
||||
history[historyTick] = Decimal.ZERO
|
||||
|
@ -97,7 +97,9 @@ class EnergyServoBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matte
|
||||
return EnergyServoMenu(containerID, inventory, this)
|
||||
}
|
||||
|
||||
fun tick() {
|
||||
override fun tick() {
|
||||
super.tick()
|
||||
|
||||
if (redstoneControl.isBlockedByRedstone)
|
||||
return
|
||||
|
||||
|
@ -35,7 +35,7 @@ class MatterReplicatorBlock : RotatableMatteryBlock(), EntityBlock {
|
||||
if (p_153212_.isClientSide || p_153214_ !== MBlockEntities.MATTER_REPLICATOR)
|
||||
return null
|
||||
|
||||
return BlockEntityTicker { _, _, _, tile -> if (tile is MatterReplicatorBlockEntity) tile.basicTicker() }
|
||||
return BlockEntityTicker { _, _, _, tile -> if (tile is MatterReplicatorBlockEntity) tile.tick() }
|
||||
}
|
||||
|
||||
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
|
||||
|
@ -35,7 +35,7 @@ class MatterScannerBlock : RotatableMatteryBlock(), EntityBlock {
|
||||
if (p_153212_.isClientSide || p_153214_ !== MBlockEntities.MATTER_SCANNER)
|
||||
return null
|
||||
|
||||
return BlockEntityTicker { _, _, _, tile -> if (tile is MatterScannerBlockEntity) tile.basicTicker() }
|
||||
return BlockEntityTicker { _, _, _, tile -> if (tile is MatterScannerBlockEntity) tile.tick() }
|
||||
}
|
||||
|
||||
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
|
||||
|
@ -21,7 +21,7 @@ class CobblerBlock : RotatableMatteryBlock(), EntityBlock {
|
||||
pBlockEntityType: BlockEntityType<T>
|
||||
): BlockEntityTicker<T>? {
|
||||
if (!pLevel.isClientSide) {
|
||||
return BlockEntityTicker { _, _, _, pBlockEntity -> if (pBlockEntity is CobblerBlockEntity) pBlockEntity.basicTicker() }
|
||||
return BlockEntityTicker { _, _, _, pBlockEntity -> if (pBlockEntity is CobblerBlockEntity) pBlockEntity.tick() }
|
||||
}
|
||||
|
||||
return null
|
||||
|
@ -39,7 +39,7 @@ class PlatePressBlock(properties: Properties = DEFAULT_PROPERTIES) : RotatableMa
|
||||
if (p_153212_.isClientSide || p_153214_ !== MBlockEntities.PLATE_PRESS)
|
||||
return null
|
||||
|
||||
return BlockEntityTicker { _, _, _, tile -> if (tile is PlatePressBlockEntity) tile.basicTicker() }
|
||||
return BlockEntityTicker { _, _, _, tile -> if (tile is PlatePressBlockEntity) tile.tick() }
|
||||
}
|
||||
|
||||
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
|
||||
|
@ -0,0 +1,80 @@
|
||||
package ru.dbotthepony.mc.otm.capability
|
||||
|
||||
import com.google.common.collect.ImmutableList
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraftforge.items.IItemHandler
|
||||
import java.util.stream.Stream
|
||||
|
||||
class CombinedItemHandler(val handlers: ImmutableList<IItemHandler>) : IItemHandler {
|
||||
constructor(handlers: Stream<IItemHandler>) : this(handlers.collect(ImmutableList.toImmutableList()))
|
||||
constructor(handlers: Collection<IItemHandler>) : this(ImmutableList.copyOf(handlers))
|
||||
constructor(vararg handlers: IItemHandler) : this(ImmutableList.copyOf(handlers))
|
||||
|
||||
private val lastSizes = IntArray(this.handlers.size)
|
||||
private var totalSize = 0
|
||||
private val mappings = ArrayList<Mapping>()
|
||||
private data class Mapping(val handler: IItemHandler, val slot: Int)
|
||||
|
||||
private fun check() {
|
||||
for ((i, handler) in handlers.withIndex()) {
|
||||
var oldSize = lastSizes[i]
|
||||
|
||||
if (oldSize != handler.slots) {
|
||||
totalSize += handler.slots - lastSizes[i]
|
||||
var edge = 0
|
||||
|
||||
for (i2 in 0 .. i) {
|
||||
edge += lastSizes[i2]
|
||||
}
|
||||
|
||||
edge--
|
||||
|
||||
while (oldSize > handler.slots) {
|
||||
mappings.removeAt(edge--)
|
||||
oldSize--
|
||||
}
|
||||
|
||||
while (oldSize < handler.slots) {
|
||||
mappings.add(++edge, Mapping(handler, oldSize++))
|
||||
}
|
||||
|
||||
lastSizes[i] = handler.slots
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getSlots(): Int {
|
||||
check()
|
||||
return totalSize
|
||||
}
|
||||
|
||||
override fun getStackInSlot(slot: Int): ItemStack {
|
||||
check()
|
||||
val mapping = mappings.getOrNull(slot) ?: return ItemStack.EMPTY
|
||||
return mapping.handler.getStackInSlot(mapping.slot)
|
||||
}
|
||||
|
||||
override fun insertItem(slot: Int, stack: ItemStack, simulate: Boolean): ItemStack {
|
||||
check()
|
||||
val mapping = mappings.getOrNull(slot) ?: return stack
|
||||
return mapping.handler.insertItem(mapping.slot, stack, simulate)
|
||||
}
|
||||
|
||||
override fun extractItem(slot: Int, amount: Int, simulate: Boolean): ItemStack {
|
||||
check()
|
||||
val mapping = mappings.getOrNull(slot) ?: return ItemStack.EMPTY
|
||||
return mapping.handler.extractItem(mapping.slot, amount, simulate)
|
||||
}
|
||||
|
||||
override fun getSlotLimit(slot: Int): Int {
|
||||
check()
|
||||
val mapping = mappings.getOrNull(slot) ?: return 0
|
||||
return mapping.handler.getSlotLimit(mapping.slot)
|
||||
}
|
||||
|
||||
override fun isItemValid(slot: Int, stack: ItemStack): Boolean {
|
||||
check()
|
||||
val mapping = mappings.getOrNull(slot) ?: return false
|
||||
return mapping.handler.isItemValid(mapping.slot, stack)
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package ru.dbotthepony.mc.otm.capability
|
||||
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraftforge.items.IItemHandler
|
||||
|
||||
object EmptyItemHandler : IItemHandler {
|
||||
override fun getSlots(): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
override fun getStackInSlot(slot: Int): ItemStack {
|
||||
return ItemStack.EMPTY
|
||||
}
|
||||
|
||||
override fun insertItem(slot: Int, stack: ItemStack, simulate: Boolean): ItemStack {
|
||||
return stack
|
||||
}
|
||||
|
||||
override fun extractItem(slot: Int, amount: Int, simulate: Boolean): ItemStack {
|
||||
return ItemStack.EMPTY
|
||||
}
|
||||
|
||||
override fun getSlotLimit(slot: Int): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
override fun isItemValid(slot: Int, stack: ItemStack): Boolean {
|
||||
return false
|
||||
}
|
||||
}
|
@ -45,6 +45,10 @@ enum class FlowDirection(val input: Boolean, val output: Boolean) : Predicate<Fl
|
||||
return t === this || (!input || t.input) && (!output || t.output)
|
||||
}
|
||||
|
||||
fun intersect(other: FlowDirection): FlowDirection {
|
||||
return of(other.input && input, other.output && output)
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun of(input: Boolean, output: Boolean): FlowDirection {
|
||||
|
43
src/main/kotlin/ru/dbotthepony/mc/otm/capability/Helpers.kt
Normal file
43
src/main/kotlin/ru/dbotthepony/mc/otm/capability/Helpers.kt
Normal file
@ -0,0 +1,43 @@
|
||||
package ru.dbotthepony.mc.otm.capability
|
||||
|
||||
import net.minecraftforge.items.IItemHandler
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package ru.dbotthepony.mc.otm.capability
|
||||
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraftforge.items.IItemHandler
|
||||
|
||||
class UnmodifiableItemHandler(private val parent: IItemHandler) : IItemHandler by parent {
|
||||
override fun extractItem(slot: Int, amount: Int, simulate: Boolean): ItemStack {
|
||||
return ItemStack.EMPTY
|
||||
}
|
||||
|
||||
override fun isItemValid(slot: Int, stack: ItemStack): Boolean {
|
||||
return false
|
||||
}
|
||||
}
|
@ -162,13 +162,13 @@ interface IMatteryEnergyStorage : IEnergyStorage {
|
||||
if (!energyFlow.input)
|
||||
return 0
|
||||
|
||||
val received = receiveEnergy(Decimal(maxReceive), true).toInt()
|
||||
val received = receiveEnergyChecked(Decimal(maxReceive), true).toInt()
|
||||
|
||||
// Receiving only a fraction
|
||||
if (received == 0)
|
||||
return 0
|
||||
|
||||
return receiveEnergy(Decimal(received), simulate).toInt()
|
||||
return receiveEnergyChecked(Decimal(received), simulate).toInt()
|
||||
}
|
||||
|
||||
override fun extractEnergy(maxReceive: Int, simulate: Boolean): Int {
|
||||
@ -176,13 +176,13 @@ interface IMatteryEnergyStorage : IEnergyStorage {
|
||||
if (energyFlow.output)
|
||||
return 0
|
||||
|
||||
val extracted = extractEnergy(Decimal(maxReceive), true).toInt()
|
||||
val extracted = extractEnergyChecked(Decimal(maxReceive), true).toInt()
|
||||
|
||||
// Extracting only a fraction
|
||||
if (extracted == 0)
|
||||
return 0
|
||||
|
||||
return extractEnergy(Decimal(extracted), simulate).toInt()
|
||||
return extractEnergyChecked(Decimal(extracted), simulate).toInt()
|
||||
}
|
||||
|
||||
override fun getEnergyStored(): Int {
|
||||
|
@ -17,4 +17,5 @@ object WidgetLocation {
|
||||
val HORIZONTAL_GAUGES = MatteryAtlas(ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/gui/widgets/horizontal_gauges.png"), 96f, 54f)
|
||||
val VERTICAL_GAUGES = MatteryAtlas(ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/gui/widgets/vertical_gauges.png"), 90f, 48f)
|
||||
val REDSTONE_CONTROLS = MatteryAtlas(ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/gui/widgets/redstone.png"), 54f, 18f)
|
||||
val SIDE_CONTROLS = MatteryAtlas(ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/gui/widgets/side_controls.png"), 72f, 72f)
|
||||
}
|
||||
|
@ -55,4 +55,17 @@ object Widgets18 {
|
||||
val REDSTONE_IGNORED = redstoneGrid.next()
|
||||
val REDSTONE_LOW = redstoneGrid.next()
|
||||
val REDSTONE_HIGH = redstoneGrid.next()
|
||||
|
||||
private val controlsGrid = WidgetLocation.SIDE_CONTROLS.grid(rows = 4, columns = 4)
|
||||
|
||||
val PULL = controlsGrid.next()
|
||||
val PUSH = controlsGrid.next()
|
||||
val PULL_DISABLED = controlsGrid.next()
|
||||
val PUSH_DISABLED = controlsGrid.next()
|
||||
val DISABLED = controlsGrid.next()
|
||||
val INPUT_ONLY = controlsGrid.next()
|
||||
val OUTPUT_ONLY = controlsGrid.next()
|
||||
val INPUT_OUTPUT = controlsGrid.next()
|
||||
val BATTERY_ONLY = controlsGrid.next()
|
||||
val ITEMS_CONFIGURATION = controlsGrid.next()
|
||||
}
|
||||
|
@ -569,12 +569,12 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
|
||||
val old = field
|
||||
field = value
|
||||
|
||||
onFocusChanged(old, value)
|
||||
onFocusChanged(value, old)
|
||||
findAbsoluteRoot().updateFocus()
|
||||
}
|
||||
}
|
||||
|
||||
var autoKillFocus = false
|
||||
var autoKillFocus = true
|
||||
private var focusedAsParent = false
|
||||
|
||||
val font: Font get() = if (screen is MatteryScreen<*>) screen.font else minecraft.font
|
||||
|
@ -1,12 +1,13 @@
|
||||
package ru.dbotthepony.mc.otm.client.screen.panels.button
|
||||
|
||||
import com.mojang.blaze3d.platform.InputConstants
|
||||
import com.mojang.blaze3d.vertex.PoseStack
|
||||
import net.minecraft.client.gui.screens.Screen
|
||||
import ru.dbotthepony.mc.otm.client.minecraft
|
||||
import ru.dbotthepony.mc.otm.client.render.AbstractMatterySprite
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
|
||||
import ru.dbotthepony.mc.otm.core.GetterSetter
|
||||
import ru.dbotthepony.mc.otm.core.value
|
||||
import ru.dbotthepony.mc.otm.menu.input.IPlayerInputWithFeedback
|
||||
|
||||
abstract class BooleanRectangleButtonPanel<out S : Screen>(
|
||||
screen: S,
|
||||
@ -26,6 +27,16 @@ abstract class BooleanRectangleButtonPanel<out S : Screen>(
|
||||
onChange?.invoke(newValue)
|
||||
}
|
||||
|
||||
override var isDisabled: Boolean
|
||||
get() {
|
||||
if (prop is IPlayerInputWithFeedback<Boolean>) {
|
||||
return !prop.test(minecraft.player)
|
||||
} else {
|
||||
return super.isDisabled
|
||||
}
|
||||
}
|
||||
set(value) { super.isDisabled = value }
|
||||
|
||||
override fun innerRender(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float) {
|
||||
super.innerRender(stack, mouseX, mouseY, partialTick)
|
||||
|
||||
|
@ -1,17 +1,21 @@
|
||||
package ru.dbotthepony.mc.otm.client.screen.panels.button
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.Items
|
||||
import com.mojang.blaze3d.platform.InputConstants
|
||||
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
|
||||
import ru.dbotthepony.mc.otm.block.entity.RedstoneSetting
|
||||
import ru.dbotthepony.mc.otm.client.minecraft
|
||||
import ru.dbotthepony.mc.otm.client.render.Widgets18
|
||||
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.FramePanel
|
||||
import ru.dbotthepony.mc.otm.core.TextComponent
|
||||
import ru.dbotthepony.mc.otm.core.TranslatableComponent
|
||||
import ru.dbotthepony.mc.otm.core.math.RelativeSide
|
||||
import ru.dbotthepony.mc.otm.menu.input.IPlayerInputWithFeedback
|
||||
import ru.dbotthepony.mc.otm.menu.input.ItemHandlerPlayerInput
|
||||
import java.util.function.Predicate
|
||||
|
||||
fun <S : MatteryScreen<*>> makeRedstoneSettingButton(
|
||||
private fun <S : MatteryScreen<*>> makeRedstoneSettingButton(
|
||||
screen: S,
|
||||
parent: EditablePanel<*>?,
|
||||
x: Float = 0f,
|
||||
@ -35,10 +39,115 @@ fun <S : MatteryScreen<*>> makeRedstoneSettingButton(
|
||||
}
|
||||
}
|
||||
|
||||
private fun <S : MatteryScreen<*>> makeItemModeButton(screen: S, parent: FramePanel<S>, input: ItemHandlerPlayerInput.Piece): LargeEnumRectangleButtonPanel<S, MatteryDeviceBlockEntity.ItemHandlerView> {
|
||||
val button = LargeEnumRectangleButtonPanel(screen, parent, enum = MatteryDeviceBlockEntity.ItemHandlerView::class.java, prop = input.input, defaultValue = input.default)
|
||||
|
||||
val values = listOf(
|
||||
MatteryDeviceBlockEntity.ItemHandlerView.DISABLED to Widgets18.DISABLED,
|
||||
MatteryDeviceBlockEntity.ItemHandlerView.INPUT to Widgets18.INPUT_ONLY,
|
||||
MatteryDeviceBlockEntity.ItemHandlerView.OUTPUT to Widgets18.OUTPUT_ONLY,
|
||||
MatteryDeviceBlockEntity.ItemHandlerView.INPUT_OUTPUT to Widgets18.INPUT_OUTPUT,
|
||||
MatteryDeviceBlockEntity.ItemHandlerView.BATTERY to Widgets18.BATTERY_ONLY,
|
||||
)
|
||||
|
||||
for ((k, v) in values) {
|
||||
button.add(k, skinElement = v, tooltip = TranslatableComponent(k.translationKey))
|
||||
}
|
||||
|
||||
button.finish()
|
||||
button.predicate = Predicate { input.isAllowed(it) }
|
||||
|
||||
return button
|
||||
}
|
||||
|
||||
private fun <S : MatteryScreen<*>> makeItemHandlerControlPanel(
|
||||
screen: S,
|
||||
inputs: ItemHandlerPlayerInput
|
||||
): FramePanel<S> {
|
||||
val frame = object : FramePanel<S>(screen, 78f, 80f, TranslatableComponent("otm.gui.sides.item_config")) {
|
||||
override fun tickInner() {
|
||||
super.tickInner()
|
||||
|
||||
if (!isEverFocused()) {
|
||||
remove()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val front = makeItemModeButton(screen, frame, inputs.pieces[RelativeSide.FRONT]!!)
|
||||
val back = makeItemModeButton(screen, frame, inputs.pieces[RelativeSide.BACK]!!)
|
||||
val left = makeItemModeButton(screen, frame, inputs.pieces[RelativeSide.LEFT]!!)
|
||||
val right = makeItemModeButton(screen, frame, inputs.pieces[RelativeSide.RIGHT]!!)
|
||||
val top = makeItemModeButton(screen, frame, inputs.pieces[RelativeSide.TOP]!!)
|
||||
val bottom = makeItemModeButton(screen, frame, inputs.pieces[RelativeSide.BOTTOM]!!)
|
||||
|
||||
if (inputs.pull.test(minecraft.player)) {
|
||||
val pull = LargeBooleanRectangleButtonPanel(
|
||||
screen,
|
||||
frame,
|
||||
skinElementActive = Widgets18.PULL,
|
||||
skinElementInactive = Widgets18.PULL_DISABLED,
|
||||
prop = inputs.pull,
|
||||
)
|
||||
|
||||
pull.tooltip = TranslatableComponent("otm.gui.side_mode.pull")
|
||||
|
||||
pull.x = 30f - 20f
|
||||
pull.y = 14f
|
||||
}
|
||||
|
||||
if (inputs.push.test(minecraft.player)) {
|
||||
val push = LargeBooleanRectangleButtonPanel(
|
||||
screen,
|
||||
frame,
|
||||
skinElementActive = Widgets18.PUSH,
|
||||
skinElementInactive = Widgets18.PUSH_DISABLED,
|
||||
prop = inputs.push,
|
||||
)
|
||||
|
||||
push.tooltip = TranslatableComponent("otm.gui.side_mode.push")
|
||||
|
||||
push.x = 30f + 20f
|
||||
push.y = 14f
|
||||
}
|
||||
|
||||
top.tooltip = TranslatableComponent("otm.gui.sides.top")
|
||||
bottom.tooltip = TranslatableComponent("otm.gui.sides.bottom")
|
||||
back.tooltip = TranslatableComponent("otm.gui.sides.back")
|
||||
front.tooltip = TranslatableComponent("otm.gui.sides.front")
|
||||
left.tooltip = TranslatableComponent("otm.gui.sides.left")
|
||||
right.tooltip = TranslatableComponent("otm.gui.sides.right")
|
||||
|
||||
top.x = 30f
|
||||
top.y = 14f
|
||||
|
||||
right.x = 30f - 20f
|
||||
right.y = 14f + 20f
|
||||
|
||||
left.x = 30f + 20f
|
||||
left.y = 14f + 20f
|
||||
|
||||
front.x = 30f
|
||||
front.y = 14f + 20f
|
||||
|
||||
bottom.x = 30f
|
||||
bottom.y = 14f + 42f
|
||||
|
||||
back.x = 30f - 20f
|
||||
back.y = 14f + 42f
|
||||
|
||||
screen.addPanel(frame)
|
||||
|
||||
frame.requestFocus()
|
||||
|
||||
return frame
|
||||
}
|
||||
|
||||
fun <S : MatteryScreen<*>> makeDeviceControls(
|
||||
screen: S,
|
||||
parent: FramePanel<S>,
|
||||
redstone: IPlayerInputWithFeedback<RedstoneSetting>? = null
|
||||
redstone: IPlayerInputWithFeedback<RedstoneSetting>? = null,
|
||||
itemConfig: ItemHandlerPlayerInput? = null
|
||||
): EditablePanel<S> {
|
||||
val panel = object : EditablePanel<S>(screen, parent, width = LargeEnumRectangleButtonPanel.SIZE, height = 0f, x = parent.width + 3f) {
|
||||
override fun tickInner() {
|
||||
@ -54,6 +163,23 @@ fun <S : MatteryScreen<*>> makeDeviceControls(
|
||||
y += makeRedstoneSettingButton(screen, panel, y = y, control = redstone).height + 2f
|
||||
}
|
||||
|
||||
if (itemConfig != null) {
|
||||
y += object : LargeRectangleButtonPanel<S>(screen, panel, y = y, skinElement = Widgets18.ITEMS_CONFIGURATION) {
|
||||
init {
|
||||
tooltip = TranslatableComponent("otm.gui.sides.item_config")
|
||||
}
|
||||
|
||||
override fun onClick(mouseButton: Int) {
|
||||
if (mouseButton == InputConstants.MOUSE_BUTTON_LEFT) {
|
||||
val frame = makeItemHandlerControlPanel(screen, itemConfig)
|
||||
|
||||
frame.x = absoluteX + width / 2f - frame.width / 2f
|
||||
frame.y = absoluteY + height + 8f
|
||||
}
|
||||
}
|
||||
}.height + 2f
|
||||
}
|
||||
|
||||
panel.height = (y - 2f).coerceAtLeast(0f)
|
||||
return panel
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import com.mojang.blaze3d.vertex.PoseStack
|
||||
import net.minecraft.ChatFormatting
|
||||
import net.minecraft.client.gui.screens.Screen
|
||||
import net.minecraft.network.chat.Component
|
||||
import ru.dbotthepony.mc.otm.client.minecraft
|
||||
import ru.dbotthepony.mc.otm.client.render.AbstractMatterySprite
|
||||
import ru.dbotthepony.mc.otm.client.render.UVWindingOrder
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
|
||||
@ -14,7 +15,9 @@ import ru.dbotthepony.mc.otm.core.next
|
||||
import ru.dbotthepony.mc.otm.core.prev
|
||||
import ru.dbotthepony.mc.otm.core.util.EnumValueCodec
|
||||
import ru.dbotthepony.mc.otm.core.value
|
||||
import ru.dbotthepony.mc.otm.menu.input.IPlayerInputWithFeedback
|
||||
import java.util.*
|
||||
import java.util.function.Predicate
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
abstract class EnumRectangleButtonPanel<out S : Screen, T : Enum<T>>(
|
||||
@ -29,7 +32,19 @@ abstract class EnumRectangleButtonPanel<out S : Screen, T : Enum<T>>(
|
||||
val defaultValue: T,
|
||||
) : RectangleButtonPanel<S>(screen, parent, x, y, width, height, null) {
|
||||
val enum = EnumValueCodec.searchClass(enum)
|
||||
private val constants: Array<T> = enum.enumConstants
|
||||
private var isBuilding = true
|
||||
var predicate: Predicate<T> = Predicate { true }
|
||||
|
||||
override var isDisabled: Boolean
|
||||
get() {
|
||||
if (prop is IPlayerInputWithFeedback<T>) {
|
||||
return !prop.test(minecraft.player)
|
||||
} else {
|
||||
return super.isDisabled
|
||||
}
|
||||
}
|
||||
set(value) { super.isDisabled = value }
|
||||
|
||||
data class Entry<T : Enum<T>>(
|
||||
val key: T,
|
||||
@ -40,30 +55,6 @@ abstract class EnumRectangleButtonPanel<out S : Screen, T : Enum<T>>(
|
||||
|
||||
protected val enumMapping = EnumMap<T, Entry<T>>(enum)
|
||||
|
||||
fun isFullyDefined(): Boolean {
|
||||
if (!isBuilding) return true
|
||||
|
||||
for (value in enum.enumConstants) {
|
||||
if (enumMapping[value] == null) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
val missingValues: List<T> get() {
|
||||
val missing = ArrayList<T>()
|
||||
|
||||
for (value in enum.enumConstants) {
|
||||
if (enumMapping[value] == null) {
|
||||
missing.add(value)
|
||||
}
|
||||
}
|
||||
|
||||
return missing
|
||||
}
|
||||
|
||||
fun add(key: T, skinElement: AbstractMatterySprite? = null, tooltip: Component? = null, winding: UVWindingOrder = UVWindingOrder.NORMAL): EnumRectangleButtonPanel<S, T> {
|
||||
return add(Entry(key = key, skinElement = skinElement, winding = winding, tooltip = tooltip))
|
||||
}
|
||||
@ -76,8 +67,9 @@ abstract class EnumRectangleButtonPanel<out S : Screen, T : Enum<T>>(
|
||||
}
|
||||
|
||||
fun finish(): EnumRectangleButtonPanel<S, T> {
|
||||
check(isBuilding) { "Not building" }
|
||||
check(isFullyDefined()) { "Not all enums having their mapping defined, missing are: ${missingValues.joinToString(", ")}" }
|
||||
if (!isBuilding) return this
|
||||
check(enumMapping.isNotEmpty()) { "No enums were defined, like, at all." }
|
||||
check(enumMapping.containsKey(defaultValue)) { "Default value $defaultValue is missing from mapping" }
|
||||
isBuilding = false
|
||||
return this
|
||||
}
|
||||
@ -85,7 +77,7 @@ abstract class EnumRectangleButtonPanel<out S : Screen, T : Enum<T>>(
|
||||
override fun innerRender(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float) {
|
||||
check(!isBuilding) { "Still building this button!" }
|
||||
super.innerRender(stack, mouseX, mouseY, partialTick)
|
||||
val entry = checkNotNull(enumMapping[prop.get()]) { "HOW" }
|
||||
val entry = enumMapping[prop.get()] ?: return
|
||||
entry.skinElement?.render(stack, 0f, 0f, width, height, entry.winding)
|
||||
}
|
||||
|
||||
@ -106,9 +98,48 @@ abstract class EnumRectangleButtonPanel<out S : Screen, T : Enum<T>>(
|
||||
}
|
||||
|
||||
override fun onClick(mouseButton: Int) {
|
||||
check(!isBuilding) { "Still building this button!" }
|
||||
if (enumMapping.size == 1) return
|
||||
|
||||
when (mouseButton) {
|
||||
InputConstants.MOUSE_BUTTON_LEFT -> prop.value = prop.value.next(enum.enumConstants)
|
||||
InputConstants.MOUSE_BUTTON_RIGHT -> prop.value = prop.value.prev(enum.enumConstants)
|
||||
InputConstants.MOUSE_BUTTON_LEFT -> {
|
||||
var i = constants.size
|
||||
var ordinal = prop.value.ordinal
|
||||
|
||||
while (i >= 0) {
|
||||
i--
|
||||
ordinal++
|
||||
|
||||
if (ordinal >= constants.size) {
|
||||
ordinal = 0
|
||||
}
|
||||
|
||||
if (enumMapping.containsKey(constants[ordinal]) && predicate.test(constants[ordinal])) {
|
||||
prop.value = constants[ordinal]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InputConstants.MOUSE_BUTTON_RIGHT -> {
|
||||
var i = constants.size
|
||||
var ordinal = prop.value.ordinal
|
||||
|
||||
while (i >= 0) {
|
||||
i--
|
||||
ordinal--
|
||||
|
||||
if (ordinal < 0) {
|
||||
ordinal = constants.size - 1
|
||||
}
|
||||
|
||||
if (enumMapping.containsKey(constants[ordinal]) && predicate.test(constants[ordinal])) {
|
||||
prop.value = constants[ordinal]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InputConstants.MOUSE_BUTTON_MIDDLE -> {
|
||||
if (prop.value != defaultValue) {
|
||||
prop.value = defaultValue
|
||||
@ -122,7 +153,7 @@ abstract class EnumRectangleButtonPanel<out S : Screen, T : Enum<T>>(
|
||||
return super.innerRenderTooltips(stack, mouseX, mouseY, partialTick)
|
||||
}
|
||||
|
||||
if (tooltip == null && tooltipList == null && enumMapping.values.none { it.tooltip != null }) {
|
||||
if (tooltip == null && tooltipList == null && enumMapping.entries.none { predicate.test(it.key) && it.value.tooltip != null }) {
|
||||
return super.innerRenderTooltips(stack, mouseX, mouseY, partialTick)
|
||||
}
|
||||
|
||||
@ -136,8 +167,8 @@ abstract class EnumRectangleButtonPanel<out S : Screen, T : Enum<T>>(
|
||||
listing.add(SPACE)
|
||||
}
|
||||
|
||||
for (entry in enumMapping.values) {
|
||||
if (entry.tooltip != null) {
|
||||
for ((k, entry) in enumMapping) {
|
||||
if (entry.tooltip != null && predicate.test(k)) {
|
||||
listing.add(entry.tooltip.copy().withStyle(if (entry.key == prop.get()) ChatFormatting.WHITE else ChatFormatting.GRAY))
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,12 @@
|
||||
package ru.dbotthepony.mc.otm.client.screen.panels.button
|
||||
|
||||
import net.minecraft.client.gui.screens.Screen
|
||||
import ru.dbotthepony.mc.otm.client.minecraft
|
||||
import ru.dbotthepony.mc.otm.client.render.AbstractMatterySprite
|
||||
import ru.dbotthepony.mc.otm.client.render.Widgets18
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
|
||||
import ru.dbotthepony.mc.otm.core.GetterSetter
|
||||
import ru.dbotthepony.mc.otm.menu.input.IPlayerInputWithFeedback
|
||||
|
||||
open class LargeBooleanRectangleButtonPanel<out S : Screen>(
|
||||
screen: S,
|
||||
|
@ -1,9 +1,11 @@
|
||||
package ru.dbotthepony.mc.otm.client.screen.panels.button
|
||||
|
||||
import net.minecraft.client.gui.screens.Screen
|
||||
import ru.dbotthepony.mc.otm.client.minecraft
|
||||
import ru.dbotthepony.mc.otm.client.render.Widgets18
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
|
||||
import ru.dbotthepony.mc.otm.core.GetterSetter
|
||||
import ru.dbotthepony.mc.otm.menu.input.IPlayerInputWithFeedback
|
||||
|
||||
open class LargeEnumRectangleButtonPanel<out S : Screen, T : Enum<T>>(
|
||||
screen: S,
|
||||
|
@ -20,7 +20,7 @@ class CobblerScreen(menu: CobblerMenu, inventory: Inventory, title: Component) :
|
||||
for (column in 0 .. 2)
|
||||
SlotPanel(this, frame, menu.storageSlots[row * 3 + column], 80f + column * AbstractSlotPanel.SIZE, 26f + row * AbstractSlotPanel.SIZE)
|
||||
|
||||
makeDeviceControls(this, frame, redstone = menu.redstone)
|
||||
makeDeviceControls(this, frame, redstone = menu.redstone, itemConfig = menu.itemConfig)
|
||||
|
||||
return frame
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
package ru.dbotthepony.mc.otm.core
|
||||
|
||||
import com.google.common.collect.ImmutableList
|
||||
import com.google.common.collect.ImmutableMap
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonPrimitive
|
||||
@ -128,6 +129,12 @@ inline fun <T : Any> ImmutableList(size: Int, initializer: (index: Int) -> T): I
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <K : Any, V : Any> immutableMap(initializer: ImmutableMap.Builder<K, V>.() -> Unit): ImmutableMap<K, V> {
|
||||
val builder = ImmutableMap.Builder<K, V>()
|
||||
initializer.invoke(builder)
|
||||
return builder.build()
|
||||
}
|
||||
|
||||
fun <T> IForgeRegistry<T>.getID(value: T): Int {
|
||||
return (this as ForgeRegistry<T>).getID(value)
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package ru.dbotthepony.mc.otm.core.collect
|
||||
|
||||
import java.util.stream.Stream
|
||||
|
||||
class ConditionalSet<T> : AbstractSet<T> {
|
||||
// method without boxing
|
||||
fun interface Condition {
|
||||
@ -16,6 +18,10 @@ class ConditionalSet<T> : AbstractSet<T> {
|
||||
this.getters = Array(getters.size) { getters[it] }
|
||||
}
|
||||
|
||||
constructor(getters: Stream<Pair<Condition, T>>) : super() {
|
||||
this.getters = getters.toArray { arrayOfNulls<Pair<Condition, T>>(it) }
|
||||
}
|
||||
|
||||
override val size: Int get() {
|
||||
var i = 0
|
||||
|
||||
|
@ -6,6 +6,7 @@ import net.minecraft.nbt.DoubleTag
|
||||
import net.minecraft.nbt.FloatTag
|
||||
import net.minecraft.nbt.IntTag
|
||||
import net.minecraft.nbt.NumericTag
|
||||
import net.minecraft.nbt.StringTag
|
||||
import net.minecraft.nbt.Tag
|
||||
import net.minecraftforge.common.util.INBTSerializable
|
||||
import ru.dbotthepony.mc.otm.core.GetterSetter
|
||||
@ -32,6 +33,10 @@ class Savetables : INBTSerializable<CompoundTag> {
|
||||
return stateful(getter, name, T::class.java)
|
||||
}
|
||||
|
||||
inline fun <V : INBTSerializable<T?>, reified T : Tag> stateful(getter: V, name: String): Stateful<V, T> {
|
||||
return stateful(getter, name, T::class.java)
|
||||
}
|
||||
|
||||
inline fun <V : INBTSerializable<T?>, reified T : Tag> stateful(getter: KProperty0<V>, name: String = getter.name): Stateful<V, T> {
|
||||
return stateful(getter, name, T::class.java)
|
||||
}
|
||||
@ -42,6 +47,12 @@ class Savetables : INBTSerializable<CompoundTag> {
|
||||
.withDeserializer { v, t -> v.deserializeNBT(t) }
|
||||
}
|
||||
|
||||
fun <V : INBTSerializable<T?>, T : Tag> stateful(getter: V, name: String, type: Class<T>): Stateful<V, T> {
|
||||
return Stateful({ getter }, name, type)
|
||||
.withSerializer { it.serializeNBT() }
|
||||
.withDeserializer { v, t -> v.deserializeNBT(t) }
|
||||
}
|
||||
|
||||
fun <V : INBTSerializable<T?>, T : Tag> stateful(getter: KProperty0<V>, name: String = getter.name, type: Class<T>): Stateful<V, T> {
|
||||
return Stateful(getter, name, type)
|
||||
.withSerializer { it.serializeNBT() }
|
||||
@ -124,6 +135,18 @@ class Savetables : INBTSerializable<CompoundTag> {
|
||||
.withDeserializer { it.asByte > 0 }
|
||||
}
|
||||
|
||||
fun <E : Enum<E>> enum(prop: GetterSetter<E>, name: String, map: (String) -> E): Stateless<E, StringTag> {
|
||||
return Stateless(prop, name, StringTag::class.java)
|
||||
.withSerializer { StringTag.valueOf(it.name) }
|
||||
.withDeserializer { map.invoke(it.asString) }
|
||||
}
|
||||
|
||||
fun <E : Enum<E>> enum(prop: KMutableProperty0<E>, name: String = prop.name, map: (String) -> E): Stateless<E, StringTag> {
|
||||
return Stateless(prop, name, StringTag::class.java)
|
||||
.withSerializer { StringTag.valueOf(it.name) }
|
||||
.withDeserializer { map.invoke(it.asString) }
|
||||
}
|
||||
|
||||
override fun serializeNBT(): CompoundTag {
|
||||
return CompoundTag().also(::serializeNBT)
|
||||
}
|
||||
|
@ -5,6 +5,8 @@ import org.apache.logging.log4j.LogManager
|
||||
class TickList {
|
||||
private val conditional = ArrayDeque<IConditionalTickable>()
|
||||
private val once = ArrayDeque<ITickable>()
|
||||
private val always = ArrayList<ITickable>()
|
||||
private val alwaysValveTime = ArrayList<ITickable>()
|
||||
|
||||
private val conditionalValveTime = ArrayList<IConditionalTickable>()
|
||||
private val onceValveTime = ArrayList<ITickable>()
|
||||
@ -27,6 +29,14 @@ class TickList {
|
||||
}
|
||||
}
|
||||
|
||||
fun always(ticker: ITickable) {
|
||||
if (inTicker) {
|
||||
alwaysValveTime.add(ticker)
|
||||
} else {
|
||||
always.add(ticker)
|
||||
}
|
||||
}
|
||||
|
||||
fun add(ticker: IConditionalTickable, condition: Boolean, reason: String) {
|
||||
if (!condition) {
|
||||
LOGGER.error("Refusing to add tickable $ticker because we $reason", IllegalStateException(reason))
|
||||
@ -79,6 +89,13 @@ class TickList {
|
||||
|
||||
once.clear()
|
||||
|
||||
for (ticker in always) {
|
||||
ticker.tick()
|
||||
}
|
||||
|
||||
always.addAll(alwaysValveTime)
|
||||
alwaysValveTime.clear()
|
||||
|
||||
for (ticker in conditionalValveTime) {
|
||||
conditional.addFirst(ticker)
|
||||
}
|
||||
|
@ -0,0 +1,66 @@
|
||||
package ru.dbotthepony.mc.otm.menu.input
|
||||
|
||||
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
|
||||
import ru.dbotthepony.mc.otm.core.immutableMap
|
||||
import ru.dbotthepony.mc.otm.core.math.RelativeSide
|
||||
import ru.dbotthepony.mc.otm.menu.MatteryMenu
|
||||
|
||||
class ItemHandlerPlayerInput(val menu: MatteryMenu, val allowPull: Boolean = true, val allowPush: Boolean = true) {
|
||||
inner class Piece(val side: RelativeSide) {
|
||||
var isPresent by menu.mSynchronizer.bool()
|
||||
private set
|
||||
|
||||
private val allowedFlags = MatteryDeviceBlockEntity.ItemHandlerView.values().map { menu.mSynchronizer.bool() to it }
|
||||
|
||||
fun isAllowed(value: MatteryDeviceBlockEntity.ItemHandlerView) = allowedFlags[value.ordinal].first.value
|
||||
|
||||
val pull = BooleanInputWithFeedback(menu)
|
||||
val push = BooleanInputWithFeedback(menu)
|
||||
val input = EnumInputWithFeedback<MatteryDeviceBlockEntity.ItemHandlerView>(menu)
|
||||
|
||||
var default by menu.mSynchronizer.enum(MatteryDeviceBlockEntity.ItemHandlerView.DISABLED)
|
||||
|
||||
init {
|
||||
pull.filter { allowPull }
|
||||
push.filter { allowPush }
|
||||
}
|
||||
|
||||
fun configure(config: MatteryDeviceBlockEntity.ConfigurableItemHandler) {
|
||||
isPresent = true
|
||||
|
||||
for ((f, v) in allowedFlags) {
|
||||
f.value = v in config.possibleViews
|
||||
}
|
||||
|
||||
pull.with(config::automatePull)
|
||||
push.with(config::automatePush)
|
||||
input.withSupplier { config.mode }.withConsumer { if (it in config.possibleViews) config.mode = it }
|
||||
}
|
||||
}
|
||||
|
||||
val pieces = immutableMap { for (side in RelativeSide.values()) put(side, Piece(side)) }
|
||||
|
||||
// TODO
|
||||
val pull = BooleanInputWithFeedback(menu)
|
||||
|
||||
// TODO
|
||||
val push = BooleanInputWithFeedback(menu)
|
||||
|
||||
init {
|
||||
pull.filter { allowPull }
|
||||
push.filter { allowPush }
|
||||
}
|
||||
|
||||
fun configure(config: MatteryDeviceBlockEntity.GlobalItemHandler) {
|
||||
for ((side, v) in config.sides) {
|
||||
pieces[side]!!.configure(v)
|
||||
pieces[side]!!.default = config.defaults[side]!!
|
||||
}
|
||||
|
||||
pull.withSupplier { pieces.values.all { it.pull.value } }
|
||||
push.withSupplier { pieces.values.all { it.push.value } }
|
||||
|
||||
pull.withConsumer { v -> pieces.values.forEach { it.pull.input.input(v) } }
|
||||
push.withConsumer { v -> pieces.values.forEach { it.push.input.input(v) } }
|
||||
}
|
||||
}
|
@ -8,6 +8,7 @@ import ru.dbotthepony.mc.otm.core.ImmutableList
|
||||
import ru.dbotthepony.mc.otm.menu.MachineOutputSlot
|
||||
import ru.dbotthepony.mc.otm.menu.MatteryMenu
|
||||
import ru.dbotthepony.mc.otm.menu.input.EnumInputWithFeedback
|
||||
import ru.dbotthepony.mc.otm.menu.input.ItemHandlerPlayerInput
|
||||
import ru.dbotthepony.mc.otm.menu.widget.ProgressGaugeWidget
|
||||
import ru.dbotthepony.mc.otm.registry.MMenus
|
||||
|
||||
@ -18,6 +19,7 @@ class CobblerMenu @JvmOverloads constructor(
|
||||
) : MatteryMenu(MMenus.COBBLESTONE_GENERATOR, p_38852_, inventory, tile) {
|
||||
override val storageSlots = (tile?.droppableContainer ?: SimpleContainer(CobblerBlockEntity.CONTAINER_SIZE)).let { c -> ImmutableList(c.containerSize) { addSlot(MachineOutputSlot(c, it)) } }
|
||||
val redstone = EnumInputWithFeedback<RedstoneSetting>(this)
|
||||
val itemConfig = ItemHandlerPlayerInput(this, false, false)
|
||||
|
||||
val progress: ProgressGaugeWidget
|
||||
|
||||
@ -27,6 +29,7 @@ class CobblerMenu @JvmOverloads constructor(
|
||||
else {
|
||||
progress = ProgressGaugeWidget(this, tile::workProgress, tile::isUnableToProcess)
|
||||
redstone.with(tile.redstoneControl::redstoneSetting)
|
||||
itemConfig.configure(tile.itemConfig)
|
||||
}
|
||||
|
||||
addInventorySlots()
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue
Block a user