Storage bus insert/extract priorities configuration, operation mode, small refactorings

This commit is contained in:
DBotThePony 2023-08-12 01:08:42 +07:00
parent b3249cdcd7
commit f8c2be4d4c
Signed by: DBot
GPG Key ID: DCC23B5715498507
31 changed files with 638 additions and 265 deletions

View File

@ -709,6 +709,11 @@ private fun gui(provider: MatteryLanguageProvider) {
with(provider.english) {
gui("quicksearch", "Quick search...")
gui("insert_priority", "Insert priority")
gui("extract_priority", "Extract priority")
gui("increase", "Increase")
gui("decrease", "Decrease")
gui("color_picker", "Color Picker")
gui("color.short.red", "R")

View File

@ -711,6 +711,11 @@ private fun gui(provider: MatteryLanguageProvider) {
with(provider.russian) {
gui("quicksearch", "Быстрый поиск...")
gui("insert_priority", "Приоритет вставки")
gui("extract_priority", "Приоритет забора")
gui("increase", "Увеличить")
gui("decrease", "Уменьшить")
gui("color_picker", "Выбор цвета")
gui("color.short.red", "К")

View File

@ -44,6 +44,7 @@ import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
import ru.dbotthepony.mc.otm.capability.isMekanismLoaded
import ru.dbotthepony.mc.otm.compat.mekanism.Mattery2MekanismEnergyWrapper
import ru.dbotthepony.mc.otm.compat.mekanism.Mekanism2MatteryEnergyWrapper
import ru.dbotthepony.mc.otm.core.ISubscriptable
import ru.dbotthepony.mc.otm.core.collect.WeakHashSet
import ru.dbotthepony.mc.otm.core.forValidRefs
import ru.dbotthepony.mc.otm.core.get
@ -175,9 +176,7 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc
}
}
interface SideListener<T> : Supplier<LazyOptional<T>> {
fun addListener(listener: Consumer<LazyOptional<T>>)
}
interface SideListener<T> : Supplier<LazyOptional<T>>, ISubscriptable<LazyOptional<T>>
inner class Side(val side: RelativeSide) {
init {
@ -194,17 +193,16 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc
set(value) {
if (value !== field) {
field = value
for (tracker in listeners)
tracker.accept(value)
listeners.accept(value)
}
}
private val listeners = ArrayList<Consumer<LazyOptional<T>>>(0)
private val listeners = ISubscriptable.Impl<LazyOptional<T>>()
override fun addListener(listener: Consumer<LazyOptional<T>>) {
listeners.add(listener)
override fun addListener(listener: Consumer<LazyOptional<T>>): ISubscriptable.L {
val l = listeners.addListener(listener)
listener.accept(value)
return l
}
override fun get(): LazyOptional<T> {
@ -234,15 +232,16 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc
private val mekanism: SubRef<IStrictEnergyHandler>?
private var actualMekanism: LazyOptional<IEnergyStorage>? = null
private val listeners = ArrayList<Consumer<LazyOptional<IEnergyStorage>>>()
private val listeners = ISubscriptable.Impl<LazyOptional<IEnergyStorage>>()
override fun addListener(listener: Consumer<LazyOptional<IEnergyStorage>>) {
listeners.add(listener)
override fun addListener(listener: Consumer<LazyOptional<IEnergyStorage>>): ISubscriptable.L {
val l = listeners.addListener(listener)
listener.accept(get())
return l
}
init {
regular.addListener { a -> listeners.forEach { it.accept(a) } }
regular.addListener { listeners.accept(it) }
if (isMekanismLoaded) {
mekanism = track(MatteryCapability.MEKANISM_ENERGY) as SubRef<IStrictEnergyHandler>
@ -256,7 +255,7 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc
actualMekanism = null
}
listeners.forEach { it.accept(get()) }
listeners.accept(get())
}
} else {
mekanism = null

View File

@ -3,6 +3,8 @@ package ru.dbotthepony.mc.otm.block.entity
import it.unimi.dsi.fastutil.booleans.BooleanConsumer
import net.minecraft.nbt.CompoundTag
import net.minecraftforge.common.util.INBTSerializable
import ru.dbotthepony.mc.otm.core.IBooleanSubscriptable
import ru.dbotthepony.mc.otm.core.ISubscriptable
import ru.dbotthepony.mc.otm.core.nbt.mapString
import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.network.synchronizer.FieldSynchronizer
@ -11,15 +13,15 @@ interface IRedstoneControlled {
val redstoneControl: AbstractRedstoneControl
}
abstract class AbstractRedstoneControl : INBTSerializable<CompoundTag?> {
abstract class AbstractRedstoneControl : INBTSerializable<CompoundTag?>, IBooleanSubscriptable {
abstract var redstoneSetting: RedstoneSetting
abstract var redstoneSignal: Int
protected val listeners = ArrayList<BooleanConsumer>()
protected val listeners = IBooleanSubscriptable.Impl()
val isBlockedByRedstone: Boolean get() = !redstoneSetting.test(redstoneSignal)
fun addListener(callback: BooleanConsumer) {
listeners.add(callback)
final override fun addListener(listener: BooleanConsumer): ISubscriptable.L {
return listeners.addListener(listener)
}
override fun serializeNBT(): CompoundTag {
@ -56,7 +58,7 @@ class RedstoneControl(private val valueChanges: (new: Boolean, old: Boolean) ->
if (state != old) {
valueChanges.invoke(state, old)
listeners.forEach { it.accept(state) }
listeners.accept(state)
}
}
@ -69,7 +71,7 @@ class RedstoneControl(private val valueChanges: (new: Boolean, old: Boolean) ->
if (state != old) {
valueChanges.invoke(state, old)
listeners.forEach { it.accept(state) }
listeners.accept(state)
}
}
}
@ -89,7 +91,7 @@ class SynchronizedRedstoneControl(
if (state != old) {
valueChanges.invoke(state, old)
listeners.forEach { it.accept(state) }
listeners.accept(state)
}
}
})
@ -105,7 +107,7 @@ class SynchronizedRedstoneControl(
if (state != old) {
valueChanges.invoke(state, old)
listeners.forEach { it.accept(state) }
listeners.accept(state)
}
}
}).property

View File

@ -18,7 +18,9 @@ import ru.dbotthepony.mc.otm.*
import ru.dbotthepony.mc.otm.block.CableBlock
import ru.dbotthepony.mc.otm.block.entity.MatteryPoweredBlockEntity
import ru.dbotthepony.mc.otm.block.entity.storage.AbstractStorageImportExport.Companion.FILTER_KEY
import ru.dbotthepony.mc.otm.capability.FlowDirection
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.ItemFilter
@ -45,20 +47,14 @@ private class TrackedTuple(override var stack: ItemStorageStack, override val id
}
}
private fun Long.clamp(): Int {
if (this > Int.MAX_VALUE) {
return Int.MAX_VALUE
}
return this.toInt()
}
class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryPoweredBlockEntity(MBlockEntities.STORAGE_BUS, blockPos, blockState) {
override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu {
return StorageBusMenu(containerID, inventory, this)
}
val energy = WorkerEnergyStorage(this::setChangedLight, MachinesConfig.STORAGE_INTERFACES)
val energy = ProfiledEnergyStorage(WorkerEnergyStorage(this::setChangedLight, MachinesConfig.STORAGE_INTERFACES))
val energyConfig = ConfigurableEnergy(energy, modesFront = FlowDirection.NONE)
val cell: StorageNode = object : StorageNode(energy) {
override fun onNeighbour(link: Link) {
if (link is DirectionLink) {
@ -93,8 +89,13 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter
setChangedLight()
}
var mode: FlowDirection = FlowDirection.BI_DIRECTIONAL
set(value) {
field = value
setChangedLight()
}
init {
exposeEnergyGlobally(energy)
savetable(::energy, ENERGY_KEY)
savetables.int(::insertPriority)
savetables.int(::extractPriority)
@ -103,11 +104,21 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter
side(RelativeSide.FRONT).track(ForgeCapabilities.ITEM_HANDLER).addListener {
component?.let(cell::removeStorageComponent)
component = if (it.isPresent) {
ItemHandlerComponent(it.orThrow()).also(cell::addStorageComponent)
ItemHandlerComponent(it.orThrow()).also { if (!redstoneControl.isBlockedByRedstone) cell.addStorageComponent(it) }
} else {
null
}
}
redstoneControl.addListener {
val component = component ?: return@addListener
if (it) {
cell.removeStorageComponent(component)
} else {
cell.addStorageComponent(component)
}
}
}
val filter = ItemFilter(MAX_FILTERS) { _, _, _ ->
@ -359,7 +370,7 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter
}
override fun insertStack(stack: ItemStorageStack, simulate: Boolean): ItemStorageStack {
if (redstoneControl.isBlockedByRedstone || energy.batteryLevel.isZero || !filter.match(stack.toItemStack()))
if (redstoneControl.isBlockedByRedstone || energy.batteryLevel.isZero || !filter.match(stack.toItemStack()) || !mode.input)
return stack
val required = StorageStack.ITEMS.energyPerInsert(stack)
@ -390,7 +401,7 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter
}
override fun extractStack(id: UUID, amount: BigInteger, simulate: Boolean): ItemStorageStack {
if (redstoneControl.isBlockedByRedstone || !amount.isPositive || !energy.batteryLevel.isPositive)
if (redstoneControl.isBlockedByRedstone || !amount.isPositive || !energy.batteryLevel.isPositive || !mode.output)
return ItemStorageStack.EMPTY
var total = BigInteger.ZERO

View File

@ -21,7 +21,8 @@ import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.UpgradeContainer
import ru.dbotthepony.mc.otm.container.balance
import ru.dbotthepony.mc.otm.core.collect.filter
import ru.dbotthepony.mc.otm.core.collect.findAny
import ru.dbotthepony.mc.otm.core.collect.find
import ru.dbotthepony.mc.otm.core.collect.maybe
import ru.dbotthepony.mc.otm.menu.tech.PlatePressMenu
import ru.dbotthepony.mc.otm.menu.tech.TwinPlatePressMenu
import ru.dbotthepony.mc.otm.registry.MBlockEntities
@ -92,8 +93,7 @@ class PlatePressBlockEntity(
.values
.iterator()
.filter { it.matches(inputContainer, id) }
.findAny()
.orElse(null) ?: return JobContainer.noItem()
.maybe() ?: return JobContainer.noItem()
val toProcess = inputContainer[id].count.coerceAtMost(1 + upgrades.processingItems)

View File

@ -64,7 +64,7 @@ enum class FlowDirection(val input: Boolean, val output: Boolean, val translatio
}.build()
}
val translation: Component
val title: Component
get() = TranslatableComponent(translationKey)
/**
@ -105,6 +105,9 @@ enum class FlowDirection(val input: Boolean, val output: Boolean, val translatio
}
companion object {
@JvmField
val WITHOUT_NONE: ImmutableSet<FlowDirection> = ImmutableSet.of(INPUT, OUTPUT, BI_DIRECTIONAL)
@JvmStatic
fun of(input: Boolean, output: Boolean): FlowDirection {
if (input && output) {

View File

@ -30,6 +30,10 @@ enum class GravityRounding {
return base - subtraction
}
}
override fun round(base: Float): Float {
return base
}
},
/**
@ -39,6 +43,10 @@ enum class GravityRounding {
override fun round(base: Float, subtraction: Float): Float {
return (base - subtraction).roundToInt().toFloat()
}
override fun round(base: Float): Float {
return base.roundToInt().toFloat()
}
},
/**
@ -48,9 +56,14 @@ enum class GravityRounding {
override fun round(base: Float, subtraction: Float): Float {
return base - subtraction
}
override fun round(base: Float): Float {
return base
}
};
abstract fun round(base: Float, subtraction: Float): Float
abstract fun round(base: Float): Float
}
interface IXGravity {
@ -60,6 +73,8 @@ interface IXGravity {
fun x(x: Float, width: FloatSupplier): Float = x(x, width, 1f)
fun x(x: Float, width: Float): Float = x(x, width, 1f)
fun repositionX(outer: Float, inner: Float, rounding: GravityRounding = GravityRounding.YES): Float
fun x(x: Float, font: Font, text: Component): Float = x(x, font, text, 1f)
fun x(x: Float, font: Font, text: Component, scale: Float = 1f, rounding: GravityRounding = GravityRounding.DEFAULT): Float
@ -80,6 +95,8 @@ interface IYGravity {
fun y(y: Float, height: Float, scale: Float = 1f, rounding: GravityRounding = GravityRounding.DEFAULT): Float
fun y(y: Float, height: Float): Float = y(y, height, 1f)
fun repositionY(outer: Float, inner: Float, rounding: GravityRounding = GravityRounding.YES): Float
fun y(y: Float, height: Int, scale: Float = 1f): Float {
return y(y, height.toFloat(), scale).roundToInt().toFloat()
}
@ -106,6 +123,10 @@ enum class XGravity : IXGravity {
override fun x(x: Float, font: Font, text: FormattedCharSequence, scale: Float, rounding: GravityRounding): Float {
return x
}
override fun repositionX(outer: Float, inner: Float, rounding: GravityRounding): Float {
return 0f
}
},
CENTER {
override fun x(x: Float, width: FloatSupplier, scale: Float, rounding: GravityRounding): Float {
@ -127,6 +148,13 @@ enum class XGravity : IXGravity {
override fun x(x: Float, font: Font, text: FormattedCharSequence, scale: Float, rounding: GravityRounding): Float {
return rounding.round(x, font.width(text) / 2f * scale)
}
override fun repositionX(outer: Float, inner: Float, rounding: GravityRounding): Float {
if (outer <= inner)
return 0f
else
return rounding.round((outer - inner) / 2f, 0f)
}
},
RIGHT {
override fun x(x: Float, width: FloatSupplier, scale: Float, rounding: GravityRounding): Float {
@ -148,6 +176,13 @@ enum class XGravity : IXGravity {
override fun x(x: Float, font: Font, text: FormattedCharSequence, scale: Float, rounding: GravityRounding): Float {
return rounding.round(x, font.width(text) * scale)
}
override fun repositionX(outer: Float, inner: Float, rounding: GravityRounding): Float {
if (outer <= inner)
return 0f
else
return rounding.round(outer - inner, 0f)
}
};
}
@ -160,6 +195,10 @@ enum class YGravity : IYGravity {
override fun y(y: Float, height: Float, scale: Float, rounding: GravityRounding): Float {
return y
}
override fun repositionY(outer: Float, inner: Float, rounding: GravityRounding): Float {
return 0f
}
},
CENTER {
@ -170,6 +209,13 @@ enum class YGravity : IYGravity {
override fun y(y: Float, height: Float, scale: Float, rounding: GravityRounding): Float {
return rounding.round(y, height / 2f * scale)
}
override fun repositionY(outer: Float, inner: Float, rounding: GravityRounding): Float {
if (outer <= inner)
return 0f
else
return rounding.round((outer - inner) / 2f, 0f)
}
},
BOTTOM {
@ -180,6 +226,13 @@ enum class YGravity : IYGravity {
override fun y(y: Float, height: Float, scale: Float, rounding: GravityRounding): Float {
return rounding.round(y, height * scale)
}
override fun repositionY(outer: Float, inner: Float, rounding: GravityRounding): Float {
if (outer <= inner)
return 0f
else
return rounding.round(outer - inner, 0f)
}
};
}

View File

@ -6,7 +6,7 @@ import ru.dbotthepony.mc.otm.client.render.sprites.MatteryAtlas
object WidgetLocation {
val LARGE_BUTTON = MatteryAtlas(ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/gui/widgets/large_button.png"), 72f, 18f)
val STORAGE_CONTROLS = MatteryAtlas(ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/gui/widgets/storage_controls.png"), 90f, 36f)
val STORAGE_CONTROLS = MatteryAtlas(ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/gui/widgets/storage_controls.png"), 90f, 54f)
val MISC_18 = MatteryAtlas(ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/gui/widgets/misc18.png"), 72f, 72f)
val SLOT_BACKGROUNDS = MatteryAtlas(ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/gui/widgets/slot_backgrounds.png"), 72f, 72f)

View File

@ -37,7 +37,7 @@ object Widgets18 {
val BUTTON_DISABLED_STRETCHABLE = makeButton(buttonGrids)
val BUTTON_DISABLED = buttonGrids.next()
private val storageGrid = WidgetLocation.STORAGE_CONTROLS.grid(rows = 2, columns = 5)
private val storageGrid = WidgetLocation.STORAGE_CONTROLS.grid(rows = 3, columns = 5)
val ARROW_DOWN = storageGrid.next()
val ARROW_UP = storageGrid.next()
val SORT_DEFAULT = storageGrid.next()
@ -47,6 +47,9 @@ object Widgets18 {
val SORT_ID = storageGrid.next()
val SORT_MATTER_VALUE = storageGrid.next()
val SORT_MATTER_COMPLEXITY = storageGrid.next()
val ONLY_STORE = storageGrid.next()
val ONLY_EXTRACT = storageGrid.next()
val STORE_EXTRACT = storageGrid.next()
private val miscGrid = WidgetLocation.MISC_18.grid(4, 4)

View File

@ -19,6 +19,7 @@ import net.minecraftforge.common.MinecraftForge
import org.lwjgl.opengl.GL11
import ru.dbotthepony.mc.otm.capability.matteryPlayer
import ru.dbotthepony.mc.otm.client.moveMousePosScaled
import ru.dbotthepony.mc.otm.client.render.WidgetLocation
import ru.dbotthepony.mc.otm.client.render.Widgets18
import ru.dbotthepony.mc.otm.client.render.translation
import ru.dbotthepony.mc.otm.client.screen.panels.*
@ -31,14 +32,23 @@ import ru.dbotthepony.mc.otm.client.screen.panels.util.DiscreteScrollBarPanel
import ru.dbotthepony.mc.otm.client.screen.panels.util.HeightControls
import ru.dbotthepony.mc.otm.client.screen.panels.util.ScrollBarConstants
import ru.dbotthepony.mc.otm.client.screen.widget.HorizontalPowerGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.MatterGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.PatternGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.PowerGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.ProfiledPowerGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.ProgressGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.TallHorizontalProfiledPowerGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.WidePowerGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.WideProfiledPowerGaugePanel
import ru.dbotthepony.mc.otm.config.ClientConfig
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.math.component1
import ru.dbotthepony.mc.otm.core.math.component2
import ru.dbotthepony.mc.otm.core.math.integerDivisionDown
import ru.dbotthepony.mc.otm.menu.MatteryMenu
import ru.dbotthepony.mc.otm.menu.MatterySlot
import ru.dbotthepony.mc.otm.menu.widget.LevelGaugeWidget
import ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget
import java.util.*
import kotlin.collections.ArrayDeque
import kotlin.collections.List
@ -374,6 +384,59 @@ abstract class MatteryScreen<T : MatteryMenu>(menu: T, inventory: Inventory, tit
return true
}
protected fun makeBars(
parent: EditablePanel<*>,
energy: LevelGaugeWidget? = null,
profiledEnergy: ProfiledLevelGaugeWidget<*>? = null,
matter: LevelGaugeWidget? = null,
profiledMatter: ProfiledLevelGaugeWidget<*>? = null,
patterns: LevelGaugeWidget? = null,
batterySlot: MatterySlot? = null,
) {
var bars = 0
if (energy != null) bars++
if (profiledEnergy != null) bars++
if (matter != null) bars++
if (patterns != null) bars++
if (profiledMatter != null) bars++
val canvas = EditablePanel(this, parent, width = AbstractSlotPanel.SIZE.coerceAtLeast(9f * bars))
canvas.dock = Dock.LEFT
canvas.childrenOrder = -1000
val gauges = EditablePanel(this, canvas, height = WidgetLocation.VERTICAL_GAUGES.height)
gauges.dock = Dock.TOP
if (profiledEnergy != null) {
if (bars == 1) {
WideProfiledPowerGaugePanel(this, gauges, profiledEnergy).dock = Dock.TOP
} else {
ProfiledPowerGaugePanel(this, gauges, profiledEnergy).dock = Dock.LEFT
}
} else if (energy != null) {
if (bars == 1) {
WidePowerGaugePanel(this, gauges, energy).dock = Dock.TOP
} else {
PowerGaugePanel(this, gauges, energy).dock = Dock.LEFT
}
}
if (matter != null) {
MatterGaugePanel(this, gauges, matter).dock = Dock.LEFT
}
if (patterns != null) {
PatternGaugePanel(this, gauges, patterns).dock = Dock.LEFT
}
if (batterySlot != null) {
BatterySlotPanel(this, canvas, batterySlot).also {
it.dock = Dock.BOTTOM
it.dockResize = DockResizeMode.NONE
}
}
}
/**
* Safely to be overriden. By default, creates well-known dimensions window
*

View File

@ -580,7 +580,7 @@ open class ColorPickerPanel<out S : Screen>(
}
}
override fun acceptsCharacter(codepoint: Char, mods: Int): Boolean {
override fun acceptsCharacter(codepoint: Char, mods: Int, index: Int): Boolean {
return RGBAColor.isHexCharacter(codepoint)
}
}

View File

@ -33,6 +33,8 @@ data class ScreenPos(val x: Float, val y: Float)
data class DockProperty(val left: Float = 0f, val top: Float = 0f, val right: Float = 0f, val bottom: Float = 0f) {
val isEmpty get() = left == 0f && right == 0f && top == 0f && bottom == 0f
val horizontal get() = left + right
val vertical get() = top + bottom
companion object {
val EMPTY = DockProperty()
@ -73,7 +75,6 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
height: Float = 10f,
) : Comparable<EditablePanel<*>> {
// layout engine does not support navigation using keyboard
// fuck off
val listener: GuiEventListener = object : GuiEventListener {
override fun setFocused(p_265728_: Boolean) {
if (p_265728_) {
@ -138,6 +139,9 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
}
}
/**
* Bigger values means lesser priority while docking
*/
var childrenOrder = 0
set(value) {
if (field != value) {
@ -235,17 +239,21 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
}
/**
* Width to be utilized in docking code.
* Width of this panel as considered by docking code, updated inside [performLayout]
*
* Can only differ from [width] if [dockResize] is not [DockResizeMode.ALL] and not [DockResizeMode.WIDTH]
* If panel is not docked ([dock] is [Dock.NONE]), this value equals to [width] plus [DockProperty.horizontal] of [dockMargin]
*
* If panel is docked, this value will be equal to [width], if [dockResize] allows resizing [width], or could-have value, if [dockResize] doesn't
*/
var dockedWidth: Float = 0f
private set
/**
* Height to be utilized in docking code.
* Height of this panel as considered by docking code, updated inside [performLayout]
*
* Can only differ from [height] if [dockResize] is not [DockResizeMode.ALL] and not [DockResizeMode.HEIGHT]
* If panel is not docked ([dock] is [Dock.NONE]), this value equals to [height] plus [DockProperty.vertical] of [dockMargin]
*
* If panel is docked, this value will be equal to [height], if [dockResize] allows resizing [height], or could-have value, if [dockResize] doesn't
*/
var dockedHeight: Float = 0f
private set
@ -362,6 +370,38 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
}
}
var dockPaddingLeft: Float
get() = dockPadding.left
set(value) {
if (value != dockPadding.left) {
dockPadding = dockPadding.copy(left = value)
}
}
var dockPaddingRight: Float
get() = dockPadding.right
set(value) {
if (value != dockPadding.right) {
dockPadding = dockPadding.copy(right = value)
}
}
var dockPaddingTop: Float
get() = dockPadding.top
set(value) {
if (value != dockPadding.top) {
dockPadding = dockPadding.copy(top = value)
}
}
var dockPaddingBottom: Float
get() = dockPadding.bottom
set(value) {
if (value != dockPadding.bottom) {
dockPadding = dockPadding.copy(bottom = value)
}
}
var acceptMouseInput = true
var acceptKeyboardInput = true
var grabMouseInput = false
@ -1060,7 +1100,11 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
for (child in visibleChildrenInternal) {
when (child.dock) {
Dock.NONE -> {}
Dock.NONE -> {
child.dockedWidth = child.width + child.dockMargin.horizontal
child.dockedHeight = child.height + child.dockMargin.vertical
}
Dock.FILL -> {}
Dock.LEFT -> {
@ -1192,8 +1236,8 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
*
* Performs layout if required
*/
open fun sizeToContents() {
if (layoutInvalidated) {
open fun sizeToContents(performLayout: Boolean = true) {
if (layoutInvalidated && performLayout) {
performLayout()
}
@ -1398,8 +1442,7 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
}
fun getChildren(index: Int): EditablePanel<*>? {
if (index < 0 || index >= childrenInternal.size) return null
return childrenInternal[index]
return childrenInternal.getOrNull(index)
}
fun isGrabbingMouseInput(): Boolean {

View File

@ -61,8 +61,8 @@ open class Label<out S : Screen> @JvmOverloads constructor(
}
}
override fun sizeToContents() {
super.sizeToContents()
override fun sizeToContents(performLayout: Boolean) {
super.sizeToContents(performLayout)
val w = font.width(text)
val h = font.lineHeight + 2

View File

@ -56,8 +56,8 @@ open class ButtonPanel<out S : Screen>(
graphics.draw(font, label, width / 2f, height / 2f, color = textColor, gravity = RenderGravity.CENTER_CENTER)
}
override fun sizeToContents() {
super.sizeToContents()
override fun sizeToContents(performLayout: Boolean) {
super.sizeToContents(performLayout)
height = height.coerceAtLeast(HEIGHT).coerceAtLeast(font.lineHeight.toFloat() + 2f)
width = width.coerceAtLeast(HEIGHT).coerceAtLeast(font.width(label) + 4f)

View File

@ -4,12 +4,13 @@ import net.minecraft.client.gui.screens.Screen
import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
import ru.dbotthepony.mc.otm.menu.input.AbstractPlayerInputWithFeedback
import ru.dbotthepony.mc.otm.menu.input.IPlayerInputWithFeedback
import ru.dbotthepony.mc.otm.milliTime
open class NetworkedStringInputPanel<out S : Screen>(
screen: S,
parent: EditablePanel<*>?,
val backend: AbstractPlayerInputWithFeedback<String>,
val backend: IPlayerInputWithFeedback<String>,
x: Float = 0f,
y: Float = 0f,
width: Float = 60f,
@ -29,7 +30,7 @@ open class NetworkedStringInputPanel<out S : Screen>(
private var lastChanges = 0L
override fun onTextChanged(old: String, new: String) {
override fun onTextChanged(new: String, old: String) {
lastChanges = milliTime + 1000L
backend.accept(new)
}
@ -38,7 +39,7 @@ open class NetworkedStringInputPanel<out S : Screen>(
super.tickInner()
if (milliTime >= lastChanges) {
text = backend.value
text = backend.get()
}
}
}

View File

@ -0,0 +1,215 @@
package ru.dbotthepony.mc.otm.client.screen.panels.input
import com.mojang.blaze3d.platform.InputConstants
import net.minecraft.client.gui.screens.Screen
import ru.dbotthepony.mc.otm.client.render.Widgets
import ru.dbotthepony.mc.otm.client.screen.panels.Dock
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
import ru.dbotthepony.mc.otm.client.screen.panels.button.RectangleButtonPanel
import ru.dbotthepony.mc.otm.client.screen.panels.util.HeightControls
import ru.dbotthepony.mc.otm.core.GetterSetter
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.math.Decimal
abstract class NumberInputPanel<out S : Screen, N : Number>(
screen: S,
parent: EditablePanel<*>,
val prop: GetterSetter<N>,
x: Float = 0f,
y: Float = 0f,
width: Float = WIDTH,
height: Float = HeightControls.BUTTON_HEIGHT * 2f,
var min: N? = null,
var max: N? = null,
) : EditablePanel<S>(screen, parent, x, y, width, height) {
abstract fun toNumber(input: String): N?
abstract fun increase(input: N): N
abstract fun decrease(input: N): N
abstract val isFractional: Boolean
fun clamp(value: N): N {
val min = min
val max = max
if (min != null && (value as Comparable<N>) < min) return min
if (max != null && (value as Comparable<N>) > max) return max
return value
}
fun increase() {
prop.accept(clamp(increase(prop.get())))
}
fun decrease() {
prop.accept(clamp(decrease(prop.get())))
}
val textInput = object : TextInputPanel<S>(screen, this@NumberInputPanel) {
init {
allowNumbersAndSign()
dock = Dock.FILL
text = prop.get().toString()
}
override fun acceptsCharacter(codepoint: Char, mods: Int, index: Int): Boolean {
if (!isFractional && codepoint == '.') return false
return super.acceptsCharacter(codepoint, mods, index)
}
override fun tickInner() {
super.tickInner()
if (!hasHierarchicalFocus()) {
text = prop.get().toString()
}
}
override fun onTextChanged(new: String, old: String) {
if (hasHierarchicalFocus()) {
val i = toNumber(new)
if (i != null) prop.accept(i)
}
}
}
val controls = EditablePanel(screen, this, width = HeightControls.BUTTON_WIDTH, height = HeightControls.BUTTON_HEIGHT)
val increaseControl = Control(true)
val decreaseControl = Control(false)
init {
increaseControl.childrenOrder = 1
decreaseControl.childrenOrder = 2
textInput.dockRight = 2f
}
init {
controls.dock = Dock.RIGHT
}
inner class Control(val isIncrease: Boolean) : RectangleButtonPanel<S>(screen, controls, width = HeightControls.BUTTON_WIDTH, height = HeightControls.BUTTON_HEIGHT) {
override val PRESSED = if (!isIncrease) Widgets.ARROW_DOWN_BUTTON_PRESSED else Widgets.ARROW_UP_BUTTON_PRESSED
override val HOVERED = if (!isIncrease) Widgets.ARROW_DOWN_BUTTON_HOVERED else Widgets.ARROW_UP_BUTTON_HOVERED
override val IDLE = if (!isIncrease) Widgets.ARROW_DOWN_BUTTON_IDLE else Widgets.ARROW_UP_BUTTON_IDLE
override val DISABLED = if (!isIncrease) Widgets.ARROW_DOWN_BUTTON_DISABLED else Widgets.ARROW_UP_BUTTON_DISABLED
init {
dock = Dock.TOP
if (isIncrease)
tooltips.add(TranslatableComponent("otm.gui.increase"))
else
tooltips.add(TranslatableComponent("otm.gui.decrease"))
}
override fun test(value: Int): Boolean {
return value == InputConstants.MOUSE_BUTTON_LEFT || value == InputConstants.MOUSE_BUTTON_RIGHT
}
override fun onClick(mouseButton: Int) {
if (mouseButton == InputConstants.MOUSE_BUTTON_LEFT) {
if (isIncrease) {
increase()
} else {
decrease()
}
} else if (mouseButton == InputConstants.MOUSE_BUTTON_RIGHT) {
if (isIncrease) {
decrease()
} else {
increase()
}
}
}
}
companion object {
const val WIDTH = 50f
}
}
abstract class LNumberInputPanel<out S : Screen, N : Number>(
screen: S,
parent: EditablePanel<*>,
prop: GetterSetter<N>,
x: Float = 0f,
y: Float = 0f,
width: Float = WIDTH,
height: Float = HeightControls.BUTTON_HEIGHT * 2f,
min: N? = null,
max: N? = null,
val toNumber: (String) -> N?,
val increase: (N) -> N,
val decrease: (N) -> N,
final override val isFractional: Boolean
) : NumberInputPanel<S, N>(screen, parent, prop, x, y, width, height, min, max) {
final override fun toNumber(input: String): N? {
return toNumber.invoke(input)
}
final override fun increase(input: N): N {
return increase.invoke(input)
}
final override fun decrease(input: N): N {
return decrease.invoke(input)
}
}
open class IntInputPanel<out S : Screen>(
screen: S,
parent: EditablePanel<*>,
prop: GetterSetter<Int>,
x: Float = 0f,
y: Float = 0f,
width: Float = WIDTH,
height: Float = HeightControls.BUTTON_HEIGHT * 2f,
var step: Int = 1,
min: Int? = null,
max: Int? = null,
) : NumberInputPanel<S, Int>(screen, parent, prop, x, y, width, height, min, max) {
final override fun toNumber(input: String): Int? {
return input.toIntOrNull()
}
final override fun increase(input: Int): Int {
return input + step
}
final override fun decrease(input: Int): Int {
return input - step
}
final override val isFractional: Boolean
get() = false
}
open class DecimalInputPanel<out S : Screen>(
screen: S,
parent: EditablePanel<*>,
prop: GetterSetter<Decimal>,
x: Float = 0f,
y: Float = 0f,
width: Float = WIDTH,
height: Float = HeightControls.BUTTON_HEIGHT * 2f,
var step: Decimal = Decimal.ONE,
min: Decimal? = null,
max: Decimal? = null,
) : NumberInputPanel<S, Decimal>(screen, parent, prop, x, y, width, height, min, max) {
final override fun toNumber(input: String): Decimal? {
return try {
Decimal(input)
} catch (err: NumberFormatException) {
null
}
}
final override fun increase(input: Decimal): Decimal {
return input + step
}
final override fun decrease(input: Decimal): Decimal {
return input - step
}
final override val isFractional: Boolean
get() = true
}

View File

@ -27,7 +27,7 @@ import ru.dbotthepony.mc.otm.client.screen.panels.DockProperty
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
import ru.dbotthepony.mc.otm.core.TextComponent
import ru.dbotthepony.mc.otm.core.addAll
import ru.dbotthepony.mc.otm.core.collect.mapToInt
import ru.dbotthepony.mc.otm.core.collect.map
import ru.dbotthepony.mc.otm.core.collect.reduce
import ru.dbotthepony.mc.otm.core.math.RGBAColor
import ru.dbotthepony.mc.otm.milliTime
@ -904,7 +904,7 @@ open class TextInputPanel<out S : Screen>(
pushbackSnapshot()
if (multiLine) {
var index = cursorRow + (0 until cursorLine).iterator().mapToInt { this[it]?.length ?: 0 }.reduce(0, Int::plus)
var index = cursorRow + (0 until cursorLine).iterator().map { this[it]?.length ?: 0 }.reduce(0, Int::plus)
val insert = minecraft.keyboardHandler.clipboard.replace("\t", " ").filter { acceptsCharacter(it, 0, index++) }.split(NEWLINES).toMutableList()
val actualLastSize = insert.lastOrNull()?.length ?: 0
val line = this[cursorLine]
@ -944,7 +944,7 @@ open class TextInputPanel<out S : Screen>(
cursorRow = actualLastSize
}
} else {
var index = cursorRow + (0 until cursorLine).iterator().mapToInt { this[it]?.length ?: 0 }.reduce(0, Int::plus)
var index = cursorRow + (0 until cursorLine).iterator().map { this[it]?.length ?: 0 }.reduce(0, Int::plus)
val insert = minecraft.keyboardHandler.clipboard.replace("\t", " ").replace(NEWLINES, "").filter { acceptsCharacter(it, 0, index++) }
val line = this[cursorLine]
@ -1462,7 +1462,7 @@ open class TextInputPanel<out S : Screen>(
}
private val NUMBERS = CharOpenHashSet().also {
for (char in "0123456789-+")
for (char in "0123456789.")
it.add(char)
}

View File

@ -1,8 +1,12 @@
package ru.dbotthepony.mc.otm.client.screen.panels.util
import net.minecraft.client.gui.screens.Screen
import ru.dbotthepony.mc.otm.client.render.RenderGravity
import ru.dbotthepony.mc.otm.client.screen.panels.Dock
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
import ru.dbotthepony.mc.otm.core.collect.filter
import ru.dbotthepony.mc.otm.core.collect.map
import ru.dbotthepony.mc.otm.core.collect.maybe
open class GridPanel<out S : Screen>(
screen: S,
@ -26,37 +30,54 @@ open class GridPanel<out S : Screen>(
invalidateLayout()
}
override fun performLayout() {
var currentX = 0f
var currentY = 0f
var lineY = 0f
var index = 0
for (row in 0 until rows) {
var column = 0
while (column < columns) {
val child = getChildren(index) ?: break
if (child.visible && child.dock === Dock.NONE) {
lineY = lineY.coerceAtLeast(child.height + child.dockMargin.top + child.dockMargin.bottom)
child.setPos(currentX + child.dockMargin.left, currentY + child.dockMargin.top)
currentX += child.width + child.dockMargin.left + child.dockMargin.right
} else {
column--
}
index++
column++
}
currentY += lineY
currentX = 0f
lineY = 0f
getChildren(index) ?: break
var gravity: RenderGravity = RenderGravity.CENTER_CENTER
set(value) {
field = value
invalidateLayout()
}
override fun performLayout() {
super.performLayout()
var children = visibleChildren.iterator().filter { it.dock == Dock.NONE }
var totalWidth = 0f
var totalHeight = 0f
for (row in 0 until rows) {
var maxHeight = 0f
var width = 0f
for (column in 0 until columns) {
val child = children.maybe() ?: break
width += child.dockedWidth
maxHeight = maxHeight.coerceAtLeast(child.dockedHeight)
}
totalWidth = totalWidth.coerceAtLeast(width)
totalHeight += maxHeight
}
val alignX = gravity.repositionX(width, totalWidth)
val alignY = gravity.repositionY(height, totalHeight)
children = visibleChildren.iterator().filter { it.dock == Dock.NONE }
totalWidth = 0f
totalHeight = 0f
for (row in 0 until rows) {
var maxHeight = 0f
var width = 0f
for (column in 0 until columns) {
val child = children.maybe() ?: break
child.x = alignX + width
child.y = alignY + totalHeight
width += child.dockedWidth
maxHeight = maxHeight.coerceAtLeast(child.dockedHeight)
}
totalWidth = totalWidth.coerceAtLeast(width)
totalHeight += maxHeight
}
}
}

View File

@ -46,7 +46,7 @@ open class HeightControls<out S : Screen>(
decrease.isDisabled = !value.canDecrease
}
open inner class Control(val isIncrease: Boolean) : RectangleButtonPanel<S>(screen, this@HeightControls, width = BUTTON_WIDTH, height = BUTTON_HEIGHT) {
inner class Control(val isIncrease: Boolean) : RectangleButtonPanel<S>(screen, this@HeightControls, width = BUTTON_WIDTH, height = BUTTON_HEIGHT) {
override val PRESSED: AbstractMatterySprite = if (isIncrease) Widgets.ARROW_DOWN_BUTTON_PRESSED else Widgets.ARROW_UP_BUTTON_PRESSED
override val HOVERED: AbstractMatterySprite = if (isIncrease) Widgets.ARROW_DOWN_BUTTON_HOVERED else Widgets.ARROW_UP_BUTTON_HOVERED
override val IDLE: AbstractMatterySprite = if (isIncrease) Widgets.ARROW_DOWN_BUTTON_IDLE else Widgets.ARROW_UP_BUTTON_IDLE
@ -57,10 +57,14 @@ open class HeightControls<out S : Screen>(
dockBottom = 2f
}
override fun onClick(clickButton: Int) {
if (clickButton == InputConstants.MOUSE_BUTTON_LEFT) {
override fun test(value: Int): Boolean {
return value == InputConstants.MOUSE_BUTTON_LEFT || value == InputConstants.MOUSE_BUTTON_RIGHT
}
override fun onClick(mouseButton: Int) {
if (mouseButton == InputConstants.MOUSE_BUTTON_LEFT) {
this@HeightControls.onClick(isIncrease)
} else if (clickButton == InputConstants.MOUSE_BUTTON_RIGHT) {
} else if (mouseButton == InputConstants.MOUSE_BUTTON_RIGHT) {
this@HeightControls.onClick(!isIncrease)
}
}

View File

@ -13,13 +13,13 @@ class HorizontalStripPanel<out S : Screen>(
width: Float = 0f,
height: Float = 0f,
) : EditablePanel<S>(screen, parent, x, y, width, height) {
override fun sizeToContents() {
override fun sizeToContents(performLayout: Boolean) {
var w = 0f
var h = 0f
for (child in children) {
for (child in visibleChildren) {
if (child.dock == Dock.NONE) {
w += child.width + child.dockMargin.left + child.dockMargin.right
w += child.width + child.dockMargin.horizontal
h = h.coerceAtLeast(child.height)
}
}
@ -33,19 +33,19 @@ class HorizontalStripPanel<out S : Screen>(
var w = 0f
for (child in children) {
for (child in visibleChildren) {
if (child.dock == Dock.NONE) {
w += child.width + child.dockMargin.left + child.dockMargin.right
w += child.width + child.dockMargin.horizontal
}
}
w = width / 2f - w / 2f
for (child in children) {
for (child in visibleChildren) {
if (child.dock == Dock.NONE) {
child.y = (height / 2f - child.height / 2f).roundToInt().toFloat()
child.x = (w + child.dockMargin.left).roundToInt().toFloat()
w += child.dockMargin.left + child.width + child.dockMargin.right
w += child.dockMargin.horizontal + child.width
}
}
}

View File

@ -2,57 +2,64 @@ package ru.dbotthepony.mc.otm.client.screen.storage
import net.minecraft.network.chat.Component
import net.minecraft.world.entity.player.Inventory
import ru.dbotthepony.mc.otm.capability.FlowDirection
import ru.dbotthepony.mc.otm.client.render.Widgets18
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.client.screen.panels.*
import ru.dbotthepony.mc.otm.client.screen.panels.button.CheckBoxLabelInputPanel
import ru.dbotthepony.mc.otm.client.screen.panels.button.makeDeviceControls
import ru.dbotthepony.mc.otm.client.screen.panels.input.TextInputPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.BatterySlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.button.DeviceControls
import ru.dbotthepony.mc.otm.client.screen.panels.button.LargeEnumRectangleButtonPanel
import ru.dbotthepony.mc.otm.client.screen.panels.input.IntInputPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.AbstractSlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.FilterSlotPanel
import ru.dbotthepony.mc.otm.client.screen.widget.WidePowerGaugePanel
import ru.dbotthepony.mc.otm.client.screen.panels.util.GridPanel
import ru.dbotthepony.mc.otm.menu.storage.StorageBusMenu
class StorageBusScreen(menu: StorageBusMenu, inventory: Inventory, title: Component) :
MatteryScreen<StorageBusMenu>(menu, inventory, title) {
override fun makeMainFrame(): FramePanel<MatteryScreen<*>> {
val frame = super.makeMainFrame()!!
val frame = FramePanel(this, 150f, 126f, title)
WidePowerGaugePanel(this, frame, menu.energyWidget, LEFT_MARGIN, GAUGE_TOP_WITH_SLOT)
BatterySlotPanel(this, frame, menu.batterySlot, LEFT_MARGIN, SLOT_TOP_UNDER_GAUGE)
makeBars(frame, profiledEnergy = menu.profiledEnergy, batterySlot = menu.batterySlot)
val right = EditablePanel(this, frame, width = AbstractSlotPanel.SIZE * 6f)
right.dock = Dock.RIGHT
val grid = GridPanel(this, right, columns = 6, rows = 3, height = AbstractSlotPanel.SIZE * 3f)
grid.dock = Dock.TOP
grid.dockBottom = 2f
for (row in 0 .. 2) {
for (column in 0 .. 5) {
FilterSlotPanel(this, frame, menu.busFilterSlots[row + column * 3], 55f + 18f * column, 17f + 18f * row)
}
for (slot in menu.busFilterSlots)
FilterSlotPanel(this, grid, slot)
IntInputPanel(this, right, menu.insertPriority).also {
it.dock = Dock.BOTTOM
it.dockBottom = 2f
it.tooltips.add(TranslatableComponent("otm.gui.insert_priority"))
it.childrenOrder = -1
}
CheckBoxLabelInputPanel(this, frame, menu.busFilterState, TranslatableComponent("otm.gui.filter.is_whitelist"), 59f, 78f, width = 170f)
IntInputPanel(this, right, menu.extractPriority).also {
it.dock = Dock.BOTTOM
it.dockBottom = 2f
it.tooltips.add(TranslatableComponent("otm.gui.extract_priority"))
it.childrenOrder = -2
}
makeDeviceControls(this, frame, redstoneConfig = menu.redstoneConfig)
CheckBoxLabelInputPanel(this, right, menu.busFilterState, TranslatableComponent("otm.gui.filter.is_whitelist")).also {
it.dock = Dock.BOTTOM
it.childrenOrder = -3
}
/*object : TextInputPanel<StorageBusScreen>(this@StorageBusScreen, frame) {
init {
allowNumbersAndSign()
dock = Dock.BOTTOM
}
val controls = DeviceControls(this, frame, redstoneConfig = menu.redstoneConfig)
val mode = LargeEnumRectangleButtonPanel(this, frame, prop = menu.mode, defaultValue = FlowDirection.BI_DIRECTIONAL, enum = FlowDirection::class.java)
override fun tickInner() {
super.tickInner()
mode.add(FlowDirection.INPUT, Widgets18.ONLY_STORE, FlowDirection.INPUT.title)
mode.add(FlowDirection.OUTPUT, Widgets18.ONLY_EXTRACT, FlowDirection.OUTPUT.title)
mode.add(FlowDirection.BI_DIRECTIONAL, Widgets18.STORE_EXTRACT, FlowDirection.BI_DIRECTIONAL.title)
mode.finish()
if (!hasHierarchicalFocus()) {
text = menu.insertPriority.value.toString()
}
}
override fun onTextChanged(new: String, old: String) {
if (hasHierarchicalFocus()) {
val i = new.toIntOrNull()
if (i != null) menu.insertPriority.accept(i)
}
}
}*/
controls.addButton(mode)
return frame
}

View File

@ -5,13 +5,8 @@ import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.UpgradeType
import ru.dbotthepony.mc.otm.config.EnergyBalanceValues
import ru.dbotthepony.mc.otm.config.VerboseEnergyBalanceValues
import ru.dbotthepony.mc.otm.core.collect.filter
import ru.dbotthepony.mc.otm.core.collect.map
import ru.dbotthepony.mc.otm.core.collect.mapToDouble
import ru.dbotthepony.mc.otm.core.collect.mapToInt
import ru.dbotthepony.mc.otm.core.collect.reduce
import ru.dbotthepony.mc.otm.core.collect.sum
import ru.dbotthepony.mc.otm.core.isNotEmpty
import ru.dbotthepony.mc.otm.core.math.Decimal
import kotlin.math.pow
@ -28,9 +23,9 @@ open class UpgradeContainer(slotCount: Int, open val allowedUpgrades: Set<Upgrad
}
override val speedBonus: Double
get() = iterator().mapToDouble { it.getCapability(MatteryCapability.UPGRADE).map { it.speedBonus }.orElse(0.0) * it.count }.sum()
get() = iterator().map { it.getCapability(MatteryCapability.UPGRADE).map { it.speedBonus }.orElse(0.0) * it.count }.reduce(0.0) { a, b -> a + b }
override val processingItems: Int
get() = iterator().mapToInt { it.getCapability(MatteryCapability.UPGRADE).map { it.processingItems }.orElse(0).coerceAtLeast(0) * it.count }.reduce(0) { a, b -> a + b }
get() = iterator().map { it.getCapability(MatteryCapability.UPGRADE).map { it.processingItems }.orElse(0).coerceAtLeast(0) * it.count }.reduce(0) { a, b -> a + b }
override val energyStorageFlat: Decimal
get() = decimals(IMatteryUpgrade::energyStorageFlat, Decimal::plus)
override val energyStorage: Decimal
@ -42,7 +37,7 @@ open class UpgradeContainer(slotCount: Int, open val allowedUpgrades: Set<Upgrad
override val energyConsumed: Decimal
get() = decimals(IMatteryUpgrade::energyConsumed, Decimal::plus)
override val failureMultiplier: Double
get() = iterator().mapToDouble { it.getCapability(MatteryCapability.UPGRADE).map { it.failureMultiplier }.orElse(1.0).coerceAtLeast(0.0).pow(it.count.toDouble()) }.reduce(1.0) { a, b -> a * b }
get() = iterator().map { it.getCapability(MatteryCapability.UPGRADE).map { it.failureMultiplier }.orElse(1.0).coerceAtLeast(0.0).pow(it.count.toDouble()) }.reduce(1.0) { a, b -> a * b }
override val energyThroughputFlat: Decimal
get() = decimals(IMatteryUpgrade::energyThroughputFlat, Decimal::plus)
override val energyThroughput: Decimal

View File

@ -0,0 +1,5 @@
package ru.dbotthepony.mc.otm.core
interface ByteSupplier {
fun getAsByte(): Byte
}

View File

@ -0,0 +1,5 @@
package ru.dbotthepony.mc.otm.core
interface ShortSupplier {
fun getAsShort(): Short
}

View File

@ -21,7 +21,7 @@ import java.util.stream.Collector
*
* Resulting [Iterator] is [MutableIterator] if [parent] is
*/
class FilteredIterator<T>(private val parent: Iterator<T>, private val predicate: Predicate<in T>) : MutableIterator<T> {
class FilteringIterator<T>(private val parent: Iterator<T>, private val predicate: Predicate<in T>) : MutableIterator<T> {
private var foundValue: Any? = Companion
override fun hasNext(): Boolean {
@ -124,18 +124,6 @@ class FlatMappingIterator<T, R>(private val parent: Iterator<T>, private val map
}
}
fun <T> concatIterators(): MutableIterator<T> {
return ObjectIterators.EMPTY_ITERATOR as MutableIterator<T>
}
fun <T> concatIterators(a: Iterator<T>): MutableIterator<T> {
return a as MutableIterator<T>
}
fun <T> concatIterators(vararg iterators: Iterator<T>): MutableIterator<T> {
return iterators.iterator().flatMap { it }
}
/**
* Limits amount of values returned by [parent] iterator to return at most [limit] values
*
@ -201,12 +189,24 @@ class SkippingIterator<T>(private val parent: Iterator<T>, skip: Long) : Mutable
}
}
fun <T> concatIterators(): MutableIterator<T> {
return ObjectIterators.EMPTY_ITERATOR as MutableIterator<T>
}
fun <T> concatIterators(a: Iterator<T>): MutableIterator<T> {
return a as MutableIterator<T>
}
fun <T> concatIterators(vararg iterators: Iterator<T>): MutableIterator<T> {
return iterators.iterator().flatMap { it }
}
/**
* Filters elements of [this] iterator
*
* Resulting [Iterator] is [MutableIterator] if [this] is
*/
fun <T> Iterator<T>.filter(condition: Predicate<in T>) = FilteredIterator(this, condition)
fun <T> Iterator<T>.filter(condition: Predicate<in T>) = FilteringIterator(this, condition)
/**
* Maps elements of [this] iterator from values of [T] to [R] using function [mapper]
@ -236,92 +236,16 @@ fun <T, R> Iterator<T>.flatMap(mapper: (T) -> Iterator<R>) = FlatMappingIterator
*/
fun <T, R> Iterator<T>.flatMap(mapper: java.util.function.Function<T, Iterator<R>>) = FlatMappingIterator(this, mapper::apply)
fun interface O2DFunction<T> {
fun apply(value: T): Double
}
fun interface O2IFunction<T> {
fun apply(value: T): Int
}
fun <T> Iterator<T>.mapToDouble(mapper: O2DFunction<T>): it.unimi.dsi.fastutil.doubles.DoubleIterator {
return object : it.unimi.dsi.fastutil.doubles.DoubleIterator {
override fun hasNext(): Boolean {
return this@mapToDouble.hasNext()
}
override fun remove() {
(this@mapToDouble as MutableIterator).remove()
}
override fun nextDouble(): Double {
return mapper.apply(this@mapToDouble.next())
}
}
}
fun <T> Iterator<T>.mapToInt(mapper: O2IFunction<T>): it.unimi.dsi.fastutil.ints.IntIterator {
return object : it.unimi.dsi.fastutil.ints.IntIterator {
override fun hasNext(): Boolean {
return this@mapToInt.hasNext()
}
override fun remove() {
(this@mapToInt as MutableIterator).remove()
}
override fun nextInt(): Int {
return mapper.apply(this@mapToInt.next())
}
}
}
fun it.unimi.dsi.fastutil.doubles.DoubleIterator.sum(): Double {
var value = 0.0
while (hasNext()) value += nextDouble()
return value
}
fun it.unimi.dsi.fastutil.ints.IntIterator.sum(): Int {
var value = 0
while (hasNext()) value += nextInt()
return value
}
fun interface DD2DFunction {
fun apply(a: Double, b: Double): Double
}
fun interface II2IFunction {
fun apply(a: Int, b: Int): Int
}
fun it.unimi.dsi.fastutil.doubles.DoubleIterator.reduce(identity: Double, reducer: DD2DFunction): Double {
var result = identity
while (hasNext()) result = reducer.apply(result, nextDouble())
return result
}
fun it.unimi.dsi.fastutil.ints.IntIterator.reduce(identity: Int, reducer: II2IFunction): Int {
var result = identity
while (hasNext()) result = reducer.apply(result, nextInt())
return result
}
fun <T> Iterator<T>.reduce(identity: T, reducer: (T, T) -> T): T {
var result = identity
for (value in this) {
result = reducer.invoke(result, value)
}
for (value in this) result = reducer.invoke(result, value)
return result
}
fun <T> Iterator<T>.reduce(identity: T, reducer: BinaryOperator<T>): T = reduce(identity, reducer::apply)
fun <T> Iterator<T?>.filterNotNull(): Iterator<T> = filter { it != null } as Iterator<T>
fun <T> Iterator<T>.anyMatch(predicate: Predicate<in T>): Boolean {
fun <T> Iterator<T>.any(predicate: Predicate<in T>): Boolean {
for (value in this) {
if (predicate.test(value)) {
return true
@ -331,7 +255,7 @@ fun <T> Iterator<T>.anyMatch(predicate: Predicate<in T>): Boolean {
return false
}
fun <T> Iterator<T>.allMatch(predicate: Predicate<in T>): Boolean {
fun <T> Iterator<T>.all(predicate: Predicate<in T>): Boolean {
for (value in this) {
if (!predicate.test(value)) {
return false
@ -341,7 +265,7 @@ fun <T> Iterator<T>.allMatch(predicate: Predicate<in T>): Boolean {
return true
}
fun <T> Iterator<T>.noneMatch(predicate: Predicate<in T>): Boolean {
fun <T> Iterator<T>.none(predicate: Predicate<in T>): Boolean {
for (value in this) {
if (predicate.test(value)) {
return false
@ -368,7 +292,7 @@ fun <T> Iterator<T>.toList(): MutableList<T> {
return result
}
fun <T : Any> Iterator<T>.findFirst(): Optional<T> {
fun <T : Any> Iterator<T>.find(): Optional<T> {
if (hasNext()) {
return Optional.of(next())
}
@ -376,7 +300,6 @@ fun <T : Any> Iterator<T>.findFirst(): Optional<T> {
return Optional.empty<T>()
}
fun <T : Any> Iterator<T>.findAny() = findFirst()
fun <T> Iterator<T>.limit(limit: Long) = LimitingIterator(this, limit)
fun <T> Iterator<T>.skip(skip: Long) = if (skip == 0L) this else SkippingIterator(this, skip)
@ -418,8 +341,8 @@ fun <T : Any> Iterator<T>.max(comparator: Comparator<T>): Optional<T> {
return Optional.of(max)
}
fun <T> Iterator<T>.peek(peeker: (T) -> Unit): Iterator<T> {
return object : Iterator<T> {
fun <T> Iterator<T>.peek(peeker: (T) -> Unit): MutableIterator<T> {
return object : MutableIterator<T> {
override fun hasNext(): Boolean {
return this@peek.hasNext()
}
@ -427,5 +350,16 @@ fun <T> Iterator<T>.peek(peeker: (T) -> Unit): Iterator<T> {
override fun next(): T {
return this@peek.next().also(peeker)
}
override fun remove() {
(this@peek as MutableIterator<T>).remove()
}
}
}
fun <T> Iterator<T>.maybe(): T? {
return if (hasNext())
next()
else
null
}

View File

@ -5,40 +5,40 @@ import ru.dbotthepony.mc.otm.core.util.EnumValueCodec
import ru.dbotthepony.mc.otm.menu.MatteryMenu
import kotlin.reflect.KMutableProperty0
inline fun <reified E : Enum<E>> EnumInputWithFeedback(menu: MatteryMenu) = EnumInputWithFeedback(menu, E::class.java)
inline fun <reified E : Enum<E>> EnumInputWithFeedback(menu: MatteryMenu, state: KMutableProperty0<E>?) = EnumInputWithFeedback(menu, E::class.java, state)
inline fun <reified E : Enum<E>> EnumInputWithFeedback(menu: MatteryMenu, state: GetterSetter<E>) = EnumInputWithFeedback(menu, E::class.java, state)
inline fun <reified E : Enum<E>> EnumInputWithFeedback(menu: MatteryMenu, allowedValues: Set<E>? = null) = EnumInputWithFeedback(menu, E::class.java, allowedValues = allowedValues)
inline fun <reified E : Enum<E>> EnumInputWithFeedback(menu: MatteryMenu, state: KMutableProperty0<E>?, allowedValues: Set<E>? = null) = EnumInputWithFeedback(menu, E::class.java, state, allowedValues = allowedValues)
inline fun <reified E : Enum<E>> EnumInputWithFeedback(menu: MatteryMenu, state: GetterSetter<E>?, allowedValues: Set<E>? = null) = EnumInputWithFeedback(menu, E::class.java, state, allowedValues = allowedValues)
inline fun <reified E : Enum<E>> EnumInputWithFeedback(menu: MatteryMenu, allowSpectators: Boolean) = EnumInputWithFeedback(menu, E::class.java, allowSpectators)
inline fun <reified E : Enum<E>> EnumInputWithFeedback(menu: MatteryMenu, allowSpectators: Boolean, state: KMutableProperty0<E>?) = EnumInputWithFeedback(menu, E::class.java, allowSpectators, state)
inline fun <reified E : Enum<E>> EnumInputWithFeedback(menu: MatteryMenu, allowSpectators: Boolean, state: GetterSetter<E>) = EnumInputWithFeedback(menu, E::class.java, allowSpectators, state)
inline fun <reified E : Enum<E>> EnumInputWithFeedback(menu: MatteryMenu, allowSpectators: Boolean, allowedValues: Set<E>? = null) = EnumInputWithFeedback(menu, E::class.java, allowSpectators, allowedValues = allowedValues)
inline fun <reified E : Enum<E>> EnumInputWithFeedback(menu: MatteryMenu, allowSpectators: Boolean, state: KMutableProperty0<E>?, allowedValues: Set<E>? = null) = EnumInputWithFeedback(menu, E::class.java, allowSpectators, state, allowedValues = allowedValues)
inline fun <reified E : Enum<E>> EnumInputWithFeedback(menu: MatteryMenu, allowSpectators: Boolean, state: GetterSetter<E>?, allowedValues: Set<E>? = null) = EnumInputWithFeedback(menu, E::class.java, allowSpectators, state, allowedValues = allowedValues)
class EnumInputWithFeedback<E : Enum<E>>(menu: MatteryMenu, clazz: Class<E>, allowSpectators: Boolean = false) : AbstractPlayerInputWithFeedback<E>() {
class EnumInputWithFeedback<E : Enum<E>>(menu: MatteryMenu, clazz: Class<E>, allowSpectators: Boolean = false, val allowedValues: Set<E>? = null) : AbstractPlayerInputWithFeedback<E>() {
val codec = EnumValueCodec(clazz)
private val default = codec.values.first()
override val input = menu.PlayerInput(codec, allowSpectators) { consumer?.invoke(it) }
override val input = menu.PlayerInput(codec, allowSpectators) { if (allowedValues == null || it in allowedValues) consumer?.invoke(it) }
override val field = menu.mSynchronizer.ComputedField(getter = { supplier?.invoke() ?: default }, codec)
constructor(menu: MatteryMenu, clazz: Class<E>, state: KMutableProperty0<E>?) : this(menu, clazz) {
constructor(menu: MatteryMenu, clazz: Class<E>, state: KMutableProperty0<E>?, allowedValues: Set<E>? = null) : this(menu, clazz, allowedValues = allowedValues) {
if (state != null) {
with(state)
}
}
constructor(menu: MatteryMenu, clazz: Class<E>, state: GetterSetter<E>?) : this(menu, clazz) {
constructor(menu: MatteryMenu, clazz: Class<E>, state: GetterSetter<E>?, allowedValues: Set<E>? = null) : this(menu, clazz, allowedValues = allowedValues) {
if (state != null) {
with(state)
}
}
constructor(menu: MatteryMenu, clazz: Class<E>, allowSpectators: Boolean, state: KMutableProperty0<E>?) : this(menu, clazz, allowSpectators) {
constructor(menu: MatteryMenu, clazz: Class<E>, allowSpectators: Boolean, state: KMutableProperty0<E>?, allowedValues: Set<E>? = null) : this(menu, clazz, allowSpectators, allowedValues = allowedValues) {
if (state != null) {
with(state)
}
}
constructor(menu: MatteryMenu, clazz: Class<E>, allowSpectators: Boolean, state: GetterSetter<E>?) : this(menu, clazz, allowSpectators) {
constructor(menu: MatteryMenu, clazz: Class<E>, allowSpectators: Boolean, state: GetterSetter<E>?, allowedValues: Set<E>? = null) : this(menu, clazz, allowSpectators, allowedValues = allowedValues) {
if (state != null) {
with(state)
}

View File

@ -10,7 +10,7 @@ import ru.dbotthepony.mc.otm.block.entity.storage.IItemMonitorPlayerSettings
import ru.dbotthepony.mc.otm.block.entity.storage.ItemMonitorBlockEntity
import ru.dbotthepony.mc.otm.block.entity.storage.ItemMonitorPlayerSettings
import ru.dbotthepony.mc.otm.container.get
import ru.dbotthepony.mc.otm.core.collect.mapToInt
import ru.dbotthepony.mc.otm.core.collect.map
import ru.dbotthepony.mc.otm.core.collect.reduce
import ru.dbotthepony.mc.otm.core.isNotEmpty
import ru.dbotthepony.mc.otm.menu.MatteryPoweredMenu
@ -206,7 +206,7 @@ class ItemMonitorMenu(
return ItemStack.EMPTY
}
} else {
if (crafted > 0 && crafted >= tile.craftingGrid.iterator().mapToInt { it.maxStackSize }.reduce(Int.MAX_VALUE, Int::coerceAtMost)) {
if (crafted > 0 && crafted >= tile.craftingGrid.iterator().map { it.maxStackSize }.reduce(Int.MAX_VALUE, Int::coerceAtMost)) {
return ItemStack.EMPTY
}
}

View File

@ -2,33 +2,32 @@ package ru.dbotthepony.mc.otm.menu.storage
import net.minecraft.world.entity.player.Inventory
import ru.dbotthepony.mc.otm.block.entity.storage.StorageBusBlockEntity
import ru.dbotthepony.mc.otm.capability.FlowDirection
import ru.dbotthepony.mc.otm.container.ItemFilterNetworkSlot
import ru.dbotthepony.mc.otm.menu.MatteryPoweredMenu
import ru.dbotthepony.mc.otm.menu.input.BooleanInputWithFeedback
import ru.dbotthepony.mc.otm.menu.input.EnumInputWithFeedback
import ru.dbotthepony.mc.otm.menu.input.IntInputWithFeedback
import ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget
import ru.dbotthepony.mc.otm.registry.MMenus
class StorageBusMenu @JvmOverloads constructor(
class StorageBusMenu(
containerId: Int,
inventory: Inventory,
tile: StorageBusBlockEntity? = null
) : MatteryPoweredMenu(MMenus.STORAGE_BUS, containerId, inventory, tile) {
val busFilterSlots: List<ItemFilterNetworkSlot>
val busFilterState: BooleanInputWithFeedback
val insertPriority: IntInputWithFeedback
val extractPriority: IntInputWithFeedback
val busFilterState = BooleanInputWithFeedback(this, tile?.let { it.filter::isWhitelist })
val insertPriority = IntInputWithFeedback(this, tile?.let { it::insertPriority })
val extractPriority = IntInputWithFeedback(this, tile?.let { it::extractPriority })
val profiledEnergy = ProfiledLevelGaugeWidget(this, tile?.energy, energyWidget)
val mode = EnumInputWithFeedback(this, tile?.let { it::mode }, FlowDirection.WITHOUT_NONE)
init {
if (tile != null) {
busFilterSlots = addFilterSlots(tile.filter)
busFilterState = BooleanInputWithFeedback(this, tile.filter::isWhitelist)
insertPriority = IntInputWithFeedback(this, tile::insertPriority)
extractPriority = IntInputWithFeedback(this, tile::extractPriority)
} else {
busFilterSlots = addFilterSlots(StorageBusBlockEntity.MAX_FILTERS)
busFilterState = BooleanInputWithFeedback(this)
insertPriority = IntInputWithFeedback(this)
extractPriority = IntInputWithFeedback(this)
}
addStorageSlot(batterySlot)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 847 B

After

Width:  |  Height:  |  Size: 981 B