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("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("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("item_monitor.amount.full", "Стопку ингредиентов. Создание продолжается пока не будет достигнут лимит по стопке одного из ингредиентов. Если хотя бы один из ингредиентов не может быть стопкой, будет использован режим 'одна стопка результата'")
|
||||||
|
|
||||||
gui("stored_amount", "Точное количество в хранилище: %s шт.")
|
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.ints.Int2ObjectOpenHashMap
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectFunction
|
import it.unimi.dsi.fastutil.longs.Long2ObjectFunction
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap
|
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
|
|
||||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectArrayMap
|
import it.unimi.dsi.fastutil.objects.Reference2ObjectArrayMap
|
||||||
import net.minecraft.client.multiplayer.ClientLevel
|
import net.minecraft.client.multiplayer.ClientLevel
|
||||||
import net.minecraft.core.BlockPos
|
import net.minecraft.core.BlockPos
|
||||||
@ -12,11 +11,10 @@ import net.minecraft.core.Direction
|
|||||||
import net.minecraft.core.SectionPos
|
import net.minecraft.core.SectionPos
|
||||||
import net.minecraft.core.Vec3i
|
import net.minecraft.core.Vec3i
|
||||||
import net.minecraft.nbt.CompoundTag
|
import net.minecraft.nbt.CompoundTag
|
||||||
import net.minecraft.nbt.StringTag
|
|
||||||
import net.minecraft.nbt.Tag
|
import net.minecraft.nbt.Tag
|
||||||
import net.minecraft.resources.ResourceLocation
|
|
||||||
import net.minecraft.server.level.ServerLevel
|
import net.minecraft.server.level.ServerLevel
|
||||||
import net.minecraft.server.level.ServerPlayer
|
import net.minecraft.server.level.ServerPlayer
|
||||||
|
import net.minecraft.world.item.ItemStack
|
||||||
import net.minecraft.world.level.ChunkPos
|
import net.minecraft.world.level.ChunkPos
|
||||||
import net.minecraft.world.level.Level
|
import net.minecraft.world.level.Level
|
||||||
import net.minecraft.world.level.block.Block
|
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.level.LevelEvent
|
||||||
import net.minecraftforge.event.server.ServerStoppingEvent
|
import net.minecraftforge.event.server.ServerStoppingEvent
|
||||||
import net.minecraftforge.items.IItemHandler
|
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.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.MatteryCapability
|
||||||
|
import ru.dbotthepony.mc.otm.capability.UnmodifiableItemHandler
|
||||||
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
|
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
|
||||||
import ru.dbotthepony.mc.otm.capability.isMekanismLoaded
|
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.compat.mekanism.Mattery2MekanismEnergyWrapper
|
||||||
import ru.dbotthepony.mc.otm.core.collect.SupplierList
|
import ru.dbotthepony.mc.otm.core.collect.SupplierList
|
||||||
import ru.dbotthepony.mc.otm.core.collect.WeakHashSet
|
import ru.dbotthepony.mc.otm.core.collect.WeakHashSet
|
||||||
import ru.dbotthepony.mc.otm.core.forValidRefs
|
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.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.BlockRotation
|
||||||
import ru.dbotthepony.mc.otm.core.math.RelativeSide
|
import ru.dbotthepony.mc.otm.core.math.RelativeSide
|
||||||
import ru.dbotthepony.mc.otm.core.math.minus
|
import ru.dbotthepony.mc.otm.core.math.minus
|
||||||
import ru.dbotthepony.mc.otm.core.math.plus
|
import ru.dbotthepony.mc.otm.core.math.plus
|
||||||
import ru.dbotthepony.mc.otm.core.nbt.set
|
import ru.dbotthepony.mc.otm.core.util.ITickable
|
||||||
import ru.dbotthepony.mc.otm.core.util.EnumValueCodec
|
|
||||||
import ru.dbotthepony.mc.otm.core.util.Savetables
|
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.BlockEntitySyncPacket
|
||||||
import ru.dbotthepony.mc.otm.network.FieldSynchronizer
|
import ru.dbotthepony.mc.otm.network.FieldSynchronizer
|
||||||
import ru.dbotthepony.mc.otm.network.WorldNetworkChannel
|
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 data class SidelessCap<T : Any>(val cap: T, var optional: LazyOptional<T>)
|
||||||
private val sidelessCaps = Reference2ObjectArrayMap<Capability<*>, SidelessCap<*>>()
|
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
|
* 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) {
|
protected fun exposeEnergySideless(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) {
|
|
||||||
exposeSideless(ForgeCapabilities.ENERGY, value)
|
exposeSideless(ForgeCapabilities.ENERGY, value)
|
||||||
exposeSideless(MatteryCapability.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) {
|
protected fun exposeItemsGlobally(value: IItemHandler) {
|
||||||
exposeGlobally(ForgeCapabilities.ITEM_HANDLER, value)
|
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 })
|
return SupplierList(_sides.values.map { it.track(capability)::get })
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class SideMode(val filter: FlowDirection, val automation: FlowDirection) {
|
inner class Side(val side: RelativeSide) {
|
||||||
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> {
|
|
||||||
init {
|
init {
|
||||||
check(!_sides.containsKey(side)) { "dafuq are you trying to do" }
|
check(!_sides.containsKey(side)) { "dafuq are you trying to do" }
|
||||||
_sides[side] = this
|
_sides[side] = this
|
||||||
}
|
}
|
||||||
|
|
||||||
private val caps = Reference2ObjectArrayMap<Capability<*>, Cap<*>>()
|
private val caps = Reference2ObjectArrayMap<Capability<*>, Cap<*>>()
|
||||||
private val data = Object2ObjectArrayMap<ResourceLocation, INBTSerializable<Tag>>()
|
|
||||||
private val subscriptions = Reference2ObjectArrayMap<Capability<*>, SubRef<*>>()
|
private val subscriptions = Reference2ObjectArrayMap<Capability<*>, SubRef<*>>()
|
||||||
private val knownLOs = WeakHashSet<LazyOptional<*>>()
|
private val knownLOs = WeakHashSet<LazyOptional<*>>()
|
||||||
|
|
||||||
@ -240,7 +218,7 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun updateTracked(capability: Capability<*>) {
|
private fun updateTracked(capability: Capability<*>) {
|
||||||
if (isRemoved) return
|
if (isRemoved || !SERVER_IS_LIVE) return
|
||||||
val dir = blockRotation.side2Dir(side)
|
val dir = blockRotation.side2Dir(side)
|
||||||
|
|
||||||
val chunk = level
|
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>? {
|
operator fun <T : Any> get(capability: Capability<T>): Cap<T>? {
|
||||||
return caps[capability] as 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() {
|
fun invalidate() {
|
||||||
for (cap in caps.values)
|
for (cap in caps.values)
|
||||||
cap.invalidate()
|
cap.invalidate()
|
||||||
@ -307,87 +274,18 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc
|
|||||||
cap.revive()
|
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) {
|
inner class Cap<T : Any>(val type: Capability<in T>, val capability: T) {
|
||||||
init {
|
init {
|
||||||
check(!caps.containsKey(type)) { "Already has capability $type on side $side" }
|
check(!caps.containsKey(type)) { "Already has capability $type on side $side" }
|
||||||
caps[type] = this
|
caps[type] = this
|
||||||
}
|
}
|
||||||
|
|
||||||
private var isExposed = true
|
var isExposed = true
|
||||||
private var isValid = true
|
private set
|
||||||
private var isRemoved = false
|
var isValid = true
|
||||||
|
private set
|
||||||
|
var isRemoved = false
|
||||||
|
private set
|
||||||
|
|
||||||
var optional: LazyOptional<T> by object : ReadWriteProperty<Any?, LazyOptional<T>> {
|
var optional: LazyOptional<T> by object : ReadWriteProperty<Any?, LazyOptional<T>> {
|
||||||
private var value: LazyOptional<T>? = null
|
private var value: LazyOptional<T>? = null
|
||||||
@ -419,7 +317,9 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc
|
|||||||
if (!isRemoved && isExposed) {
|
if (!isRemoved && isExposed) {
|
||||||
isExposed = false
|
isExposed = false
|
||||||
optional.invalidate()
|
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) {
|
if (isValid) {
|
||||||
optional = LazyOptional.of { capability }
|
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) {
|
if (!isRemoved && isValid) {
|
||||||
isValid = false
|
isValid = false
|
||||||
optional.invalidate()
|
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) {
|
if (isExposed) {
|
||||||
optional = LazyOptional.of { capability }
|
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()
|
side.revive()
|
||||||
}
|
}
|
||||||
|
|
||||||
protected val savetables = Savetables()
|
|
||||||
|
|
||||||
protected inline fun <reified T : Tag, V : INBTSerializable<T?>> savetable(property: KProperty0<V>, name: String = property.name) {
|
protected inline fun <reified T : Tag, V : INBTSerializable<T?>> savetable(property: KProperty0<V>, name: String = property.name) {
|
||||||
savetables.stateful(property, name, T::class.java)
|
savetables.stateful(property, name, T::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun saveAdditional(nbt: CompoundTag) {
|
override fun saveAdditional(nbt: CompoundTag) {
|
||||||
super.saveAdditional(nbt)
|
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)
|
savetables.serializeNBT(nbt)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun load(nbt: CompoundTag) {
|
override fun load(nbt: CompoundTag) {
|
||||||
super.load(nbt)
|
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)
|
savetables.deserializeNBT(nbt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package ru.dbotthepony.mc.otm.block.entity
|
package ru.dbotthepony.mc.otm.block.entity
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet
|
||||||
import net.minecraft.world.level.block.entity.BlockEntityType
|
import net.minecraft.world.level.block.entity.BlockEntityType
|
||||||
import net.minecraft.core.BlockPos
|
import net.minecraft.core.BlockPos
|
||||||
import net.minecraft.world.level.block.state.BlockState
|
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.world.inventory.AbstractContainerMenu
|
||||||
import net.minecraft.nbt.CompoundTag
|
import net.minecraft.nbt.CompoundTag
|
||||||
import net.minecraft.network.chat.Component
|
import net.minecraft.network.chat.Component
|
||||||
|
import net.minecraft.world.item.ItemStack
|
||||||
import net.minecraft.world.level.Level
|
import net.minecraft.world.level.Level
|
||||||
import net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity
|
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.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.getJson
|
||||||
import ru.dbotthepony.mc.otm.core.nbt.putJson
|
import ru.dbotthepony.mc.otm.core.nbt.putJson
|
||||||
import ru.dbotthepony.mc.otm.core.nbt.set
|
import ru.dbotthepony.mc.otm.core.nbt.set
|
||||||
|
import ru.dbotthepony.mc.otm.core.util.ITickable
|
||||||
import ru.dbotthepony.mc.otm.oncePre
|
import ru.dbotthepony.mc.otm.oncePre
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -61,4 +74,231 @@ abstract class MatteryDeviceBlockEntity(blockEntityType: BlockEntityType<*>, blo
|
|||||||
customDisplayName = nbt.getJson("Name")?.let(Component.Serializer::fromJson)
|
customDisplayName = nbt.getJson("Name")?.let(Component.Serializer::fromJson)
|
||||||
redstoneControl.deserializeNBT(nbt[REDSTONE_CONTROL_KEY] as? CompoundTag)
|
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)
|
savetable(::batteryContainer, BATTERY_KEY)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun batteryChargeLoop() {
|
override fun tick() {
|
||||||
|
super.tick()
|
||||||
|
|
||||||
val energy = matteryEnergy
|
val energy = matteryEnergy
|
||||||
if (energy == null || !batteryContainer.any { !it.isEmpty })
|
if (energy == null || !batteryContainer.any { !it.isEmpty })
|
||||||
return
|
return
|
||||||
|
@ -233,7 +233,9 @@ abstract class MatteryWorkerBlockEntity<JobType : MatteryWorkerBlockEntity.Job>(
|
|||||||
isIdling = newBlocked
|
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) {
|
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)
|
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 {
|
companion object {
|
||||||
const val WORK_TICKS_KEY = "workTicks"
|
const val WORK_TICKS_KEY = "workTicks"
|
||||||
const val JOB_KEY = "job"
|
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--
|
sleepTicks--
|
||||||
if (sleepTicks > 0) return
|
if (sleepTicks > 0) return
|
||||||
val level = level as? ServerLevel ?: return
|
val level = level as? ServerLevel ?: return
|
||||||
|
@ -169,8 +169,8 @@ class MatterBottlerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
|
|||||||
matterNode.destroy(::MatterNetworkGraph)
|
matterNode.destroy(::MatterNetworkGraph)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun tick() {
|
override fun tick() {
|
||||||
batteryChargeLoop()
|
super.tick()
|
||||||
|
|
||||||
if (redstoneControl.isBlockedByRedstone) {
|
if (redstoneControl.isBlockedByRedstone) {
|
||||||
if (blockState.getValue(WorkerState.SEMI_WORKER_STATE) !== WorkerState.IDLE) {
|
if (blockState.getValue(WorkerState.SEMI_WORKER_STATE) !== WorkerState.IDLE) {
|
||||||
|
@ -215,9 +215,8 @@ class MatterDecomposerBlockEntity(pos: BlockPos, state: BlockState)
|
|||||||
return matter
|
return matter
|
||||||
}
|
}
|
||||||
|
|
||||||
fun tick() {
|
override fun tick() {
|
||||||
batteryChargeLoop()
|
super.tick()
|
||||||
workerLoop()
|
|
||||||
|
|
||||||
val grid = matterNode.graph as MatterNetworkGraph? ?: return
|
val grid = matterNode.graph as MatterNetworkGraph? ?: return
|
||||||
|
|
||||||
|
@ -154,8 +154,8 @@ class MatterRecyclerBlockEntity(blockPos: BlockPos, blockState: BlockState)
|
|||||||
return Status.SUCCESS
|
return Status.SUCCESS
|
||||||
}
|
}
|
||||||
|
|
||||||
fun tick() {
|
override fun tick() {
|
||||||
basicTicker()
|
super.tick()
|
||||||
|
|
||||||
val graph = matterNode.graph as MatterNetworkGraph? ?: return
|
val graph = matterNode.graph as MatterNetworkGraph? ?: return
|
||||||
val received = graph.receiveMatter(matter.storedMatter, false)
|
val received = graph.receiveMatter(matter.storedMatter, false)
|
||||||
|
@ -57,8 +57,8 @@ class DriveRackBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
|
|||||||
StorageNetworkGraph.discoverFull(this, cell.storageNode)
|
StorageNetworkGraph.discoverFull(this, cell.storageNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun tick() {
|
override fun tick() {
|
||||||
batteryChargeLoop()
|
super.tick()
|
||||||
cell.tickEnergyDemanding()
|
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 {
|
override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu {
|
||||||
return DriveViewerMenu(containerID, inventory, this)
|
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() }
|
return settings.computeIfAbsent(ply.uuid) { ItemMonitorPlayerSettings() }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun tick() {
|
override fun tick() {
|
||||||
batteryChargeLoop()
|
super.tick()
|
||||||
|
|
||||||
cell.tickEnergyDemanding()
|
cell.tickEnergyDemanding()
|
||||||
|
|
||||||
if (craftingAmount.size != 0)
|
if (craftingAmount.size != 0)
|
||||||
|
@ -123,8 +123,8 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter
|
|||||||
private var neighbour: LazyOptional<IItemHandler>? = null
|
private var neighbour: LazyOptional<IItemHandler>? = null
|
||||||
private var component: ItemHandlerComponent? = null
|
private var component: ItemHandlerComponent? = null
|
||||||
|
|
||||||
fun tick() {
|
override fun tick() {
|
||||||
batteryChargeLoop()
|
super.tick()
|
||||||
component?.scan()
|
component?.scan()
|
||||||
cell.tickEnergyDemanding()
|
cell.tickEnergyDemanding()
|
||||||
}
|
}
|
||||||
|
@ -175,11 +175,12 @@ class StorageImporterBlockEntity(blockPos: BlockPos, blockState: BlockState)
|
|||||||
return filter.match(stack)
|
return filter.match(stack)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun tick() {
|
override fun tick() {
|
||||||
|
super.tick()
|
||||||
|
|
||||||
if (redstoneControl.isBlockedByRedstone)
|
if (redstoneControl.isBlockedByRedstone)
|
||||||
return
|
return
|
||||||
|
|
||||||
batteryChargeLoop()
|
|
||||||
cell.tickEnergyDemanding()
|
cell.tickEnergyDemanding()
|
||||||
|
|
||||||
nextTick--
|
nextTick--
|
||||||
@ -287,11 +288,12 @@ class StorageExporterBlockEntity(blockPos: BlockPos, blockState: BlockState) :
|
|||||||
return relevantTuples.stream().map { it to view[it] }
|
return relevantTuples.stream().map { it to view[it] }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun tick() {
|
override fun tick() {
|
||||||
|
super.tick()
|
||||||
|
|
||||||
if (redstoneControl.isBlockedByRedstone)
|
if (redstoneControl.isBlockedByRedstone)
|
||||||
return
|
return
|
||||||
|
|
||||||
batteryChargeLoop()
|
|
||||||
cell.tickEnergyDemanding()
|
cell.tickEnergyDemanding()
|
||||||
|
|
||||||
nextTick--
|
nextTick--
|
||||||
|
@ -53,12 +53,12 @@ class StoragePowerSupplierBlockEntity(blockPos: BlockPos, blockState: BlockState
|
|||||||
cell.destroy(level)
|
cell.destroy(level)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun tick() {
|
override fun tick() {
|
||||||
|
super.tick()
|
||||||
|
|
||||||
if (redstoneControl.isBlockedByRedstone)
|
if (redstoneControl.isBlockedByRedstone)
|
||||||
return
|
return
|
||||||
|
|
||||||
batteryChargeLoop()
|
|
||||||
|
|
||||||
if (energy.batteryLevel.isZero)
|
if (energy.batteryLevel.isZero)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -65,7 +65,9 @@ class AndroidStationBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
|
|||||||
|
|
||||||
private var tickedOnce = false
|
private var tickedOnce = false
|
||||||
|
|
||||||
fun tick() {
|
override fun tick() {
|
||||||
|
super.tick()
|
||||||
|
|
||||||
if (!tickedOnce) {
|
if (!tickedOnce) {
|
||||||
tickedOnce = true
|
tickedOnce = true
|
||||||
|
|
||||||
@ -76,8 +78,6 @@ class AndroidStationBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
batteryChargeLoop()
|
|
||||||
|
|
||||||
if (redstoneControl.isBlockedByRedstone) return
|
if (redstoneControl.isBlockedByRedstone) return
|
||||||
val level = level ?: return
|
val level = level ?: return
|
||||||
val x = blockPos.x.toDouble()
|
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)
|
private val consumers by front.track(ForgeCapabilities.ENERGY)
|
||||||
|
|
||||||
fun tick() {
|
override fun tick() {
|
||||||
|
super.tick()
|
||||||
|
|
||||||
if (redstoneControl.isBlockedByRedstone)
|
if (redstoneControl.isBlockedByRedstone)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -89,7 +89,9 @@ class ChemicalGeneratorBlockEntity(pos: BlockPos, state: BlockState) : MatteryDe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun tick() {
|
override fun tick() {
|
||||||
|
super.tick()
|
||||||
|
|
||||||
if (workTicks > 0) {
|
if (workTicks > 0) {
|
||||||
workTicks--
|
workTicks--
|
||||||
energy.receiveEnergy(GENERATION_SPEED, false)
|
energy.receiveEnergy(GENERATION_SPEED, false)
|
||||||
|
@ -22,14 +22,15 @@ class CobblerBlockEntity(blockPos: BlockPos, blockState: BlockState)
|
|||||||
|
|
||||||
override val droppableContainer = MatteryContainer(this::itemContainerUpdated, CONTAINER_SIZE)
|
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 {
|
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
val itemConfig = GlobalItemHandler(output = itemHandler)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
exposeItemsGlobally(handler)
|
|
||||||
savetable(::droppableContainer, INVENTORY_KEY)
|
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]
|
lastTick = history[historyTick]
|
||||||
historyTick = (historyTick + 1) % history.size
|
historyTick = (historyTick + 1) % history.size
|
||||||
history[historyTick] = Decimal.ZERO
|
history[historyTick] = Decimal.ZERO
|
||||||
|
@ -97,7 +97,9 @@ class EnergyServoBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matte
|
|||||||
return EnergyServoMenu(containerID, inventory, this)
|
return EnergyServoMenu(containerID, inventory, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun tick() {
|
override fun tick() {
|
||||||
|
super.tick()
|
||||||
|
|
||||||
if (redstoneControl.isBlockedByRedstone)
|
if (redstoneControl.isBlockedByRedstone)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ class MatterReplicatorBlock : RotatableMatteryBlock(), EntityBlock {
|
|||||||
if (p_153212_.isClientSide || p_153214_ !== MBlockEntities.MATTER_REPLICATOR)
|
if (p_153212_.isClientSide || p_153214_ !== MBlockEntities.MATTER_REPLICATOR)
|
||||||
return null
|
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>) {
|
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)
|
if (p_153212_.isClientSide || p_153214_ !== MBlockEntities.MATTER_SCANNER)
|
||||||
return null
|
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>) {
|
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
|
||||||
|
@ -21,7 +21,7 @@ class CobblerBlock : RotatableMatteryBlock(), EntityBlock {
|
|||||||
pBlockEntityType: BlockEntityType<T>
|
pBlockEntityType: BlockEntityType<T>
|
||||||
): BlockEntityTicker<T>? {
|
): BlockEntityTicker<T>? {
|
||||||
if (!pLevel.isClientSide) {
|
if (!pLevel.isClientSide) {
|
||||||
return BlockEntityTicker { _, _, _, pBlockEntity -> if (pBlockEntity is CobblerBlockEntity) pBlockEntity.basicTicker() }
|
return BlockEntityTicker { _, _, _, pBlockEntity -> if (pBlockEntity is CobblerBlockEntity) pBlockEntity.tick() }
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
|
@ -39,7 +39,7 @@ class PlatePressBlock(properties: Properties = DEFAULT_PROPERTIES) : RotatableMa
|
|||||||
if (p_153212_.isClientSide || p_153214_ !== MBlockEntities.PLATE_PRESS)
|
if (p_153212_.isClientSide || p_153214_ !== MBlockEntities.PLATE_PRESS)
|
||||||
return null
|
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>) {
|
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)
|
return t === this || (!input || t.input) && (!output || t.output)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun intersect(other: FlowDirection): FlowDirection {
|
||||||
|
return of(other.input && input, other.output && output)
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun of(input: Boolean, output: Boolean): FlowDirection {
|
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)
|
if (!energyFlow.input)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
val received = receiveEnergy(Decimal(maxReceive), true).toInt()
|
val received = receiveEnergyChecked(Decimal(maxReceive), true).toInt()
|
||||||
|
|
||||||
// Receiving only a fraction
|
// Receiving only a fraction
|
||||||
if (received == 0)
|
if (received == 0)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
return receiveEnergy(Decimal(received), simulate).toInt()
|
return receiveEnergyChecked(Decimal(received), simulate).toInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun extractEnergy(maxReceive: Int, simulate: Boolean): Int {
|
override fun extractEnergy(maxReceive: Int, simulate: Boolean): Int {
|
||||||
@ -176,13 +176,13 @@ interface IMatteryEnergyStorage : IEnergyStorage {
|
|||||||
if (energyFlow.output)
|
if (energyFlow.output)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
val extracted = extractEnergy(Decimal(maxReceive), true).toInt()
|
val extracted = extractEnergyChecked(Decimal(maxReceive), true).toInt()
|
||||||
|
|
||||||
// Extracting only a fraction
|
// Extracting only a fraction
|
||||||
if (extracted == 0)
|
if (extracted == 0)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
return extractEnergy(Decimal(extracted), simulate).toInt()
|
return extractEnergyChecked(Decimal(extracted), simulate).toInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getEnergyStored(): Int {
|
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 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 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 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_IGNORED = redstoneGrid.next()
|
||||||
val REDSTONE_LOW = redstoneGrid.next()
|
val REDSTONE_LOW = redstoneGrid.next()
|
||||||
val REDSTONE_HIGH = 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
|
val old = field
|
||||||
field = value
|
field = value
|
||||||
|
|
||||||
onFocusChanged(old, value)
|
onFocusChanged(value, old)
|
||||||
findAbsoluteRoot().updateFocus()
|
findAbsoluteRoot().updateFocus()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var autoKillFocus = false
|
var autoKillFocus = true
|
||||||
private var focusedAsParent = false
|
private var focusedAsParent = false
|
||||||
|
|
||||||
val font: Font get() = if (screen is MatteryScreen<*>) screen.font else minecraft.font
|
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
|
package ru.dbotthepony.mc.otm.client.screen.panels.button
|
||||||
|
|
||||||
import com.mojang.blaze3d.platform.InputConstants
|
|
||||||
import com.mojang.blaze3d.vertex.PoseStack
|
import com.mojang.blaze3d.vertex.PoseStack
|
||||||
import net.minecraft.client.gui.screens.Screen
|
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.AbstractMatterySprite
|
||||||
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
|
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
|
||||||
import ru.dbotthepony.mc.otm.core.GetterSetter
|
import ru.dbotthepony.mc.otm.core.GetterSetter
|
||||||
import ru.dbotthepony.mc.otm.core.value
|
import ru.dbotthepony.mc.otm.core.value
|
||||||
|
import ru.dbotthepony.mc.otm.menu.input.IPlayerInputWithFeedback
|
||||||
|
|
||||||
abstract class BooleanRectangleButtonPanel<out S : Screen>(
|
abstract class BooleanRectangleButtonPanel<out S : Screen>(
|
||||||
screen: S,
|
screen: S,
|
||||||
@ -26,6 +27,16 @@ abstract class BooleanRectangleButtonPanel<out S : Screen>(
|
|||||||
onChange?.invoke(newValue)
|
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) {
|
override fun innerRender(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float) {
|
||||||
super.innerRender(stack, mouseX, mouseY, partialTick)
|
super.innerRender(stack, mouseX, mouseY, partialTick)
|
||||||
|
|
||||||
|
@ -1,17 +1,21 @@
|
|||||||
package ru.dbotthepony.mc.otm.client.screen.panels.button
|
package ru.dbotthepony.mc.otm.client.screen.panels.button
|
||||||
|
|
||||||
import com.mojang.blaze3d.vertex.PoseStack
|
import com.mojang.blaze3d.platform.InputConstants
|
||||||
import net.minecraft.world.item.ItemStack
|
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
|
||||||
import net.minecraft.world.item.Items
|
|
||||||
import ru.dbotthepony.mc.otm.block.entity.RedstoneSetting
|
import ru.dbotthepony.mc.otm.block.entity.RedstoneSetting
|
||||||
import ru.dbotthepony.mc.otm.client.minecraft
|
import ru.dbotthepony.mc.otm.client.minecraft
|
||||||
import ru.dbotthepony.mc.otm.client.render.Widgets18
|
import ru.dbotthepony.mc.otm.client.render.Widgets18
|
||||||
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
|
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.EditablePanel
|
||||||
import ru.dbotthepony.mc.otm.client.screen.panels.FramePanel
|
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.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,
|
screen: S,
|
||||||
parent: EditablePanel<*>?,
|
parent: EditablePanel<*>?,
|
||||||
x: Float = 0f,
|
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(
|
fun <S : MatteryScreen<*>> makeDeviceControls(
|
||||||
screen: S,
|
screen: S,
|
||||||
parent: FramePanel<S>,
|
parent: FramePanel<S>,
|
||||||
redstone: IPlayerInputWithFeedback<RedstoneSetting>? = null
|
redstone: IPlayerInputWithFeedback<RedstoneSetting>? = null,
|
||||||
|
itemConfig: ItemHandlerPlayerInput? = null
|
||||||
): EditablePanel<S> {
|
): EditablePanel<S> {
|
||||||
val panel = object : EditablePanel<S>(screen, parent, width = LargeEnumRectangleButtonPanel.SIZE, height = 0f, x = parent.width + 3f) {
|
val panel = object : EditablePanel<S>(screen, parent, width = LargeEnumRectangleButtonPanel.SIZE, height = 0f, x = parent.width + 3f) {
|
||||||
override fun tickInner() {
|
override fun tickInner() {
|
||||||
@ -54,6 +163,23 @@ fun <S : MatteryScreen<*>> makeDeviceControls(
|
|||||||
y += makeRedstoneSettingButton(screen, panel, y = y, control = redstone).height + 2f
|
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)
|
panel.height = (y - 2f).coerceAtLeast(0f)
|
||||||
return panel
|
return panel
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import com.mojang.blaze3d.vertex.PoseStack
|
|||||||
import net.minecraft.ChatFormatting
|
import net.minecraft.ChatFormatting
|
||||||
import net.minecraft.client.gui.screens.Screen
|
import net.minecraft.client.gui.screens.Screen
|
||||||
import net.minecraft.network.chat.Component
|
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.AbstractMatterySprite
|
||||||
import ru.dbotthepony.mc.otm.client.render.UVWindingOrder
|
import ru.dbotthepony.mc.otm.client.render.UVWindingOrder
|
||||||
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
|
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.prev
|
||||||
import ru.dbotthepony.mc.otm.core.util.EnumValueCodec
|
import ru.dbotthepony.mc.otm.core.util.EnumValueCodec
|
||||||
import ru.dbotthepony.mc.otm.core.value
|
import ru.dbotthepony.mc.otm.core.value
|
||||||
|
import ru.dbotthepony.mc.otm.menu.input.IPlayerInputWithFeedback
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import java.util.function.Predicate
|
||||||
import kotlin.collections.ArrayList
|
import kotlin.collections.ArrayList
|
||||||
|
|
||||||
abstract class EnumRectangleButtonPanel<out S : Screen, T : Enum<T>>(
|
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,
|
val defaultValue: T,
|
||||||
) : RectangleButtonPanel<S>(screen, parent, x, y, width, height, null) {
|
) : RectangleButtonPanel<S>(screen, parent, x, y, width, height, null) {
|
||||||
val enum = EnumValueCodec.searchClass(enum)
|
val enum = EnumValueCodec.searchClass(enum)
|
||||||
|
private val constants: Array<T> = enum.enumConstants
|
||||||
private var isBuilding = true
|
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>>(
|
data class Entry<T : Enum<T>>(
|
||||||
val key: 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)
|
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> {
|
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))
|
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> {
|
fun finish(): EnumRectangleButtonPanel<S, T> {
|
||||||
check(isBuilding) { "Not building" }
|
if (!isBuilding) return this
|
||||||
check(isFullyDefined()) { "Not all enums having their mapping defined, missing are: ${missingValues.joinToString(", ")}" }
|
check(enumMapping.isNotEmpty()) { "No enums were defined, like, at all." }
|
||||||
|
check(enumMapping.containsKey(defaultValue)) { "Default value $defaultValue is missing from mapping" }
|
||||||
isBuilding = false
|
isBuilding = false
|
||||||
return this
|
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) {
|
override fun innerRender(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float) {
|
||||||
check(!isBuilding) { "Still building this button!" }
|
check(!isBuilding) { "Still building this button!" }
|
||||||
super.innerRender(stack, mouseX, mouseY, partialTick)
|
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)
|
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) {
|
override fun onClick(mouseButton: Int) {
|
||||||
|
check(!isBuilding) { "Still building this button!" }
|
||||||
|
if (enumMapping.size == 1) return
|
||||||
|
|
||||||
when (mouseButton) {
|
when (mouseButton) {
|
||||||
InputConstants.MOUSE_BUTTON_LEFT -> prop.value = prop.value.next(enum.enumConstants)
|
InputConstants.MOUSE_BUTTON_LEFT -> {
|
||||||
InputConstants.MOUSE_BUTTON_RIGHT -> prop.value = prop.value.prev(enum.enumConstants)
|
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 -> {
|
InputConstants.MOUSE_BUTTON_MIDDLE -> {
|
||||||
if (prop.value != defaultValue) {
|
if (prop.value != defaultValue) {
|
||||||
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)
|
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)
|
return super.innerRenderTooltips(stack, mouseX, mouseY, partialTick)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,8 +167,8 @@ abstract class EnumRectangleButtonPanel<out S : Screen, T : Enum<T>>(
|
|||||||
listing.add(SPACE)
|
listing.add(SPACE)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (entry in enumMapping.values) {
|
for ((k, entry) in enumMapping) {
|
||||||
if (entry.tooltip != null) {
|
if (entry.tooltip != null && predicate.test(k)) {
|
||||||
listing.add(entry.tooltip.copy().withStyle(if (entry.key == prop.get()) ChatFormatting.WHITE else ChatFormatting.GRAY))
|
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
|
package ru.dbotthepony.mc.otm.client.screen.panels.button
|
||||||
|
|
||||||
import net.minecraft.client.gui.screens.Screen
|
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.AbstractMatterySprite
|
||||||
import ru.dbotthepony.mc.otm.client.render.Widgets18
|
import ru.dbotthepony.mc.otm.client.render.Widgets18
|
||||||
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
|
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
|
||||||
import ru.dbotthepony.mc.otm.core.GetterSetter
|
import ru.dbotthepony.mc.otm.core.GetterSetter
|
||||||
|
import ru.dbotthepony.mc.otm.menu.input.IPlayerInputWithFeedback
|
||||||
|
|
||||||
open class LargeBooleanRectangleButtonPanel<out S : Screen>(
|
open class LargeBooleanRectangleButtonPanel<out S : Screen>(
|
||||||
screen: S,
|
screen: S,
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
package ru.dbotthepony.mc.otm.client.screen.panels.button
|
package ru.dbotthepony.mc.otm.client.screen.panels.button
|
||||||
|
|
||||||
import net.minecraft.client.gui.screens.Screen
|
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.render.Widgets18
|
||||||
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
|
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
|
||||||
import ru.dbotthepony.mc.otm.core.GetterSetter
|
import ru.dbotthepony.mc.otm.core.GetterSetter
|
||||||
|
import ru.dbotthepony.mc.otm.menu.input.IPlayerInputWithFeedback
|
||||||
|
|
||||||
open class LargeEnumRectangleButtonPanel<out S : Screen, T : Enum<T>>(
|
open class LargeEnumRectangleButtonPanel<out S : Screen, T : Enum<T>>(
|
||||||
screen: S,
|
screen: S,
|
||||||
|
@ -20,7 +20,7 @@ class CobblerScreen(menu: CobblerMenu, inventory: Inventory, title: Component) :
|
|||||||
for (column in 0 .. 2)
|
for (column in 0 .. 2)
|
||||||
SlotPanel(this, frame, menu.storageSlots[row * 3 + column], 80f + column * AbstractSlotPanel.SIZE, 26f + row * AbstractSlotPanel.SIZE)
|
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
|
return frame
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
package ru.dbotthepony.mc.otm.core
|
package ru.dbotthepony.mc.otm.core
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList
|
import com.google.common.collect.ImmutableList
|
||||||
|
import com.google.common.collect.ImmutableMap
|
||||||
import com.google.gson.JsonElement
|
import com.google.gson.JsonElement
|
||||||
import com.google.gson.JsonObject
|
import com.google.gson.JsonObject
|
||||||
import com.google.gson.JsonPrimitive
|
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 {
|
fun <T> IForgeRegistry<T>.getID(value: T): Int {
|
||||||
return (this as ForgeRegistry<T>).getID(value)
|
return (this as ForgeRegistry<T>).getID(value)
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package ru.dbotthepony.mc.otm.core.collect
|
package ru.dbotthepony.mc.otm.core.collect
|
||||||
|
|
||||||
|
import java.util.stream.Stream
|
||||||
|
|
||||||
class ConditionalSet<T> : AbstractSet<T> {
|
class ConditionalSet<T> : AbstractSet<T> {
|
||||||
// method without boxing
|
// method without boxing
|
||||||
fun interface Condition {
|
fun interface Condition {
|
||||||
@ -16,6 +18,10 @@ class ConditionalSet<T> : AbstractSet<T> {
|
|||||||
this.getters = Array(getters.size) { getters[it] }
|
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() {
|
override val size: Int get() {
|
||||||
var i = 0
|
var i = 0
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import net.minecraft.nbt.DoubleTag
|
|||||||
import net.minecraft.nbt.FloatTag
|
import net.minecraft.nbt.FloatTag
|
||||||
import net.minecraft.nbt.IntTag
|
import net.minecraft.nbt.IntTag
|
||||||
import net.minecraft.nbt.NumericTag
|
import net.minecraft.nbt.NumericTag
|
||||||
|
import net.minecraft.nbt.StringTag
|
||||||
import net.minecraft.nbt.Tag
|
import net.minecraft.nbt.Tag
|
||||||
import net.minecraftforge.common.util.INBTSerializable
|
import net.minecraftforge.common.util.INBTSerializable
|
||||||
import ru.dbotthepony.mc.otm.core.GetterSetter
|
import ru.dbotthepony.mc.otm.core.GetterSetter
|
||||||
@ -32,6 +33,10 @@ class Savetables : INBTSerializable<CompoundTag> {
|
|||||||
return stateful(getter, name, T::class.java)
|
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> {
|
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)
|
return stateful(getter, name, T::class.java)
|
||||||
}
|
}
|
||||||
@ -42,6 +47,12 @@ class Savetables : INBTSerializable<CompoundTag> {
|
|||||||
.withDeserializer { v, t -> v.deserializeNBT(t) }
|
.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> {
|
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)
|
return Stateful(getter, name, type)
|
||||||
.withSerializer { it.serializeNBT() }
|
.withSerializer { it.serializeNBT() }
|
||||||
@ -124,6 +135,18 @@ class Savetables : INBTSerializable<CompoundTag> {
|
|||||||
.withDeserializer { it.asByte > 0 }
|
.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 {
|
override fun serializeNBT(): CompoundTag {
|
||||||
return CompoundTag().also(::serializeNBT)
|
return CompoundTag().also(::serializeNBT)
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,8 @@ import org.apache.logging.log4j.LogManager
|
|||||||
class TickList {
|
class TickList {
|
||||||
private val conditional = ArrayDeque<IConditionalTickable>()
|
private val conditional = ArrayDeque<IConditionalTickable>()
|
||||||
private val once = ArrayDeque<ITickable>()
|
private val once = ArrayDeque<ITickable>()
|
||||||
|
private val always = ArrayList<ITickable>()
|
||||||
|
private val alwaysValveTime = ArrayList<ITickable>()
|
||||||
|
|
||||||
private val conditionalValveTime = ArrayList<IConditionalTickable>()
|
private val conditionalValveTime = ArrayList<IConditionalTickable>()
|
||||||
private val onceValveTime = ArrayList<ITickable>()
|
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) {
|
fun add(ticker: IConditionalTickable, condition: Boolean, reason: String) {
|
||||||
if (!condition) {
|
if (!condition) {
|
||||||
LOGGER.error("Refusing to add tickable $ticker because we $reason", IllegalStateException(reason))
|
LOGGER.error("Refusing to add tickable $ticker because we $reason", IllegalStateException(reason))
|
||||||
@ -79,6 +89,13 @@ class TickList {
|
|||||||
|
|
||||||
once.clear()
|
once.clear()
|
||||||
|
|
||||||
|
for (ticker in always) {
|
||||||
|
ticker.tick()
|
||||||
|
}
|
||||||
|
|
||||||
|
always.addAll(alwaysValveTime)
|
||||||
|
alwaysValveTime.clear()
|
||||||
|
|
||||||
for (ticker in conditionalValveTime) {
|
for (ticker in conditionalValveTime) {
|
||||||
conditional.addFirst(ticker)
|
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.MachineOutputSlot
|
||||||
import ru.dbotthepony.mc.otm.menu.MatteryMenu
|
import ru.dbotthepony.mc.otm.menu.MatteryMenu
|
||||||
import ru.dbotthepony.mc.otm.menu.input.EnumInputWithFeedback
|
import ru.dbotthepony.mc.otm.menu.input.EnumInputWithFeedback
|
||||||
|
import ru.dbotthepony.mc.otm.menu.input.ItemHandlerPlayerInput
|
||||||
import ru.dbotthepony.mc.otm.menu.widget.ProgressGaugeWidget
|
import ru.dbotthepony.mc.otm.menu.widget.ProgressGaugeWidget
|
||||||
import ru.dbotthepony.mc.otm.registry.MMenus
|
import ru.dbotthepony.mc.otm.registry.MMenus
|
||||||
|
|
||||||
@ -18,6 +19,7 @@ class CobblerMenu @JvmOverloads constructor(
|
|||||||
) : MatteryMenu(MMenus.COBBLESTONE_GENERATOR, p_38852_, inventory, tile) {
|
) : 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)) } }
|
override val storageSlots = (tile?.droppableContainer ?: SimpleContainer(CobblerBlockEntity.CONTAINER_SIZE)).let { c -> ImmutableList(c.containerSize) { addSlot(MachineOutputSlot(c, it)) } }
|
||||||
val redstone = EnumInputWithFeedback<RedstoneSetting>(this)
|
val redstone = EnumInputWithFeedback<RedstoneSetting>(this)
|
||||||
|
val itemConfig = ItemHandlerPlayerInput(this, false, false)
|
||||||
|
|
||||||
val progress: ProgressGaugeWidget
|
val progress: ProgressGaugeWidget
|
||||||
|
|
||||||
@ -27,6 +29,7 @@ class CobblerMenu @JvmOverloads constructor(
|
|||||||
else {
|
else {
|
||||||
progress = ProgressGaugeWidget(this, tile::workProgress, tile::isUnableToProcess)
|
progress = ProgressGaugeWidget(this, tile::workProgress, tile::isUnableToProcess)
|
||||||
redstone.with(tile.redstoneControl::redstoneSetting)
|
redstone.with(tile.redstoneControl::redstoneSetting)
|
||||||
|
itemConfig.configure(tile.itemConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
addInventorySlots()
|
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