Iterate IGUIRenderable, update panels to accept IGUIRenderable where applicable

This commit is contained in:
DBotThePony 2023-07-08 02:15:09 +07:00
parent f9c1258afe
commit b957378c60
Signed by: DBot
GPG Key ID: DCC23B5715498507
8 changed files with 126 additions and 69 deletions

View File

@ -22,13 +22,13 @@ sealed class AbstractMatterySprite : IGUIRenderable {
* Expected image width in pixels, used in calculations * Expected image width in pixels, used in calculations
* and as default width argument in render methods * and as default width argument in render methods
*/ */
abstract val width: Float abstract override val width: Float
/** /**
* Expected image height in pixels, used in calculations * Expected image height in pixels, used in calculations
* and as default height argument in render methods * and as default height argument in render methods
*/ */
abstract val height: Float abstract override val height: Float
abstract val u0: Float abstract val u0: Float
abstract val v0: Float abstract val v0: Float
@ -45,7 +45,7 @@ sealed class AbstractMatterySprite : IGUIRenderable {
abstract val type: SpriteType abstract val type: SpriteType
open val winding: UVWindingOrder get() = UVWindingOrder.NORMAL override val winding: UVWindingOrder get() = UVWindingOrder.NORMAL
abstract val texture: ResourceLocation abstract val texture: ResourceLocation
@ -76,23 +76,19 @@ sealed class AbstractMatterySprite : IGUIRenderable {
renderRaw(stack, x, y, width, height, winding) renderRaw(stack, x, y, width, height, winding)
} }
fun render( override fun render(
graphics: GuiGraphics, guiGraphics: GuiGraphics,
x: Float = 0f, x: Float,
y: Float = 0f, y: Float,
width: Float = this.width, width: Float,
height: Float = this.height, height: Float,
winding: UVWindingOrder = this.winding winding: UVWindingOrder
) { ) {
render(graphics.pose(), x, y, width, height, winding) render(guiGraphics.pose(), x, y, width, height, winding)
} }
override fun render(guiGraphics: GuiGraphics, x: Float, y: Float, width: Float, height: Float) { override fun render(guiGraphics: GuiGraphics, x: Float, y: Float, gravity: RenderGravity, winding: UVWindingOrder) {
return render(guiGraphics, x, y, width, height, winding) render(guiGraphics.pose(), x, y, width, height, winding)
}
override fun render(guiGraphics: GuiGraphics, x: Float, y: Float, gravity: RenderGravity) {
return render(guiGraphics, gravity.x(x, width), gravity.y(y, height), width, height, winding)
} }
@JvmOverloads @JvmOverloads

View File

@ -4,23 +4,40 @@ import net.minecraft.client.gui.GuiGraphics
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
interface IGUIRenderable { interface IGUIRenderable {
fun render(guiGraphics: GuiGraphics, x: Float, y: Float, gravity: RenderGravity = RenderGravity.TOP_LEFT) /**
* Expected width of this gui element, in pixels
*/
val width: Float
/** /**
* Render at specified [x], [y] with fixed [width] x [height] * Expected height of this gui element, in pixels
*/ */
fun render(guiGraphics: GuiGraphics, x: Float, y: Float, width: Float, height: Float) val height: Float
/**
* Utilized only for purpose of default argument
*/
val winding: UVWindingOrder get() = UVWindingOrder.NORMAL
fun render(guiGraphics: GuiGraphics, x: Float = 0f, y: Float = 0f, gravity: RenderGravity = RenderGravity.TOP_LEFT, winding: UVWindingOrder = this.winding) {
render(guiGraphics, gravity.x(x, width), gravity.y(y, height), width, height, winding)
}
/**
* Render at specified position [x], [y] with size of [width] x [height], optionally with UV [winding], if we are rendering flat texture/sprite
*/
fun render(guiGraphics: GuiGraphics, x: Float = 0f, y: Float = 0f, width: Float = this.width, height: Float = this.height, winding: UVWindingOrder = this.winding)
fun composeBefore(other: IGUIRenderable): IGUIRenderable { fun composeBefore(other: IGUIRenderable): IGUIRenderable {
return object : IGUIRenderable { return object : IGUIRenderable {
override fun render(guiGraphics: GuiGraphics, x: Float, y: Float, gravity: RenderGravity) { override val width: Float
this@IGUIRenderable.render(guiGraphics, x, y, gravity) get() = this@IGUIRenderable.width.coerceAtLeast(other.width)
other.render(guiGraphics, x, y, gravity) override val height: Float
} get() = this@IGUIRenderable.height.coerceAtLeast(other.height)
override fun render(guiGraphics: GuiGraphics, x: Float, y: Float, width: Float, height: Float) { override fun render(guiGraphics: GuiGraphics, x: Float, y: Float, width: Float, height: Float, winding: UVWindingOrder) {
this@IGUIRenderable.render(guiGraphics, x, y, width, height) this@IGUIRenderable.render(guiGraphics, x, y, width, height, winding)
other.render(guiGraphics, x, y, width, height) other.render(guiGraphics, x, y, width, height, winding)
} }
} }
} }
@ -29,6 +46,53 @@ interface IGUIRenderable {
return other.composeBefore(this) return other.composeBefore(this)
} }
fun padded(left: Float, top: Float, right: Float, bottom: Float): IGUIRenderable {
return object : IGUIRenderable {
override val width: Float
get() = this@IGUIRenderable.width
override val height: Float
get() = this@IGUIRenderable.height
override fun render(guiGraphics: GuiGraphics, x: Float, y: Float, width: Float, height: Float, winding: UVWindingOrder) {
this@IGUIRenderable.render(guiGraphics, x + left, y + top, width + right + left, height + bottom + top, winding)
}
}
}
/**
* Locks argument values to default ones and aligns render position to center of render rectangle
*
* e.g. for example, if we want [ItemStackIcon] to always render as 16x16 pixels icon, even if required render
* dimensions are bigger (e.g. 18x18), after calling [fix] on [ItemStackIcon] it will always render as 16x16 icon,
* positioned on center of render canvas (e.g. rendering on +0+0 with 18x18 size will put icon at +1+1 and render as 16x16;
* rendering on +0+0 with 32x32 canvas will put icon at +8+8 and render as 16x16)
*/
fun fixed(fixedWidth: Boolean = true, fixedHeight: Boolean = true, fixedWinding: Boolean = true): IGUIRenderable {
if (!fixedHeight && !fixedWidth && !fixedWinding) return this
return object : IGUIRenderable {
override val width: Float
get() = this@IGUIRenderable.width
override val height: Float
get() = this@IGUIRenderable.height
override fun render(guiGraphics: GuiGraphics, x: Float, y: Float, width: Float, height: Float, winding: UVWindingOrder) {
var realX = x
var realY = y
if (fixedWidth && width > this.width) {
realX += (width - this.width) / 2f
}
if (fixedHeight && height > this.height) {
realY += (height - this.height) / 2f
}
this@IGUIRenderable.render(guiGraphics, realX, realY, if (fixedWidth) this.width else width, if (fixedHeight) this.height else height, if (fixedWinding) this.winding else winding)
}
}
}
companion object { companion object {
fun of(value: ItemStack, width: Float = 16f, height: Float = 16f): IGUIRenderable { fun of(value: ItemStack, width: Float = 16f, height: Float = 16f): IGUIRenderable {
return ItemStackIcon(value, width, height) return ItemStackIcon(value, width, height)
@ -40,12 +104,8 @@ interface IGUIRenderable {
} }
} }
data class ItemStackIcon(private val itemStack: ItemStack, val width: Float = 16f, val height: Float = 16f) : IGUIRenderable { data class ItemStackIcon(private val itemStack: ItemStack, override val width: Float = 16f, override val height: Float = 16f) : IGUIRenderable {
override fun render(guiGraphics: GuiGraphics, x: Float, y: Float, gravity: RenderGravity) { override fun render(guiGraphics: GuiGraphics, x: Float, y: Float, width: Float, height: Float, winding: UVWindingOrder) {
return render(guiGraphics, gravity.x(x, width), gravity.y(y, height), width, height)
}
override fun render(guiGraphics: GuiGraphics, x: Float, y: Float, width: Float, height: Float) {
if (x % 1f == 0f && y % 1f == 0f && width == 16f && height == 16f) { if (x % 1f == 0f && y % 1f == 0f && width == 16f && height == 16f) {
guiGraphics.renderFakeItem(itemStack, x.toInt(), y.toInt()) guiGraphics.renderFakeItem(itemStack, x.toInt(), y.toInt())
clearDepth(guiGraphics.pose(), x, y, width, height) clearDepth(guiGraphics.pose(), x, y, width, height)

View File

@ -1,12 +1,11 @@
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 net.minecraft.ChatFormatting import net.minecraft.ChatFormatting
import net.minecraft.client.gui.GuiGraphics import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.screens.Screen import net.minecraft.client.gui.screens.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.minecraft
import ru.dbotthepony.mc.otm.client.render.AbstractMatterySprite import ru.dbotthepony.mc.otm.client.render.IGUIRenderable
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.TextComponent import ru.dbotthepony.mc.otm.core.TextComponent
@ -21,8 +20,8 @@ abstract class BooleanRectangleButtonPanel<out S : Screen>(
width: Float, width: Float,
height: Float, height: Float,
val prop: GetterSetter<Boolean>, val prop: GetterSetter<Boolean>,
var skinElementActive: AbstractMatterySprite? = null, var skinElementActive: IGUIRenderable? = null,
var skinElementInactive: AbstractMatterySprite? = null, var skinElementInactive: IGUIRenderable? = null,
val onChange: ((newValue: Boolean) -> Unit)? = null, val onChange: ((newValue: Boolean) -> Unit)? = null,
var tooltipActive: Component? = null, var tooltipActive: Component? = null,
var tooltipInactive: Component? = null, var tooltipInactive: Component? = null,

View File

@ -4,6 +4,8 @@ import com.mojang.blaze3d.platform.InputConstants
import net.minecraft.ChatFormatting import net.minecraft.ChatFormatting
import net.minecraft.client.gui.GuiGraphics import net.minecraft.client.gui.GuiGraphics
import net.minecraft.network.chat.Component import net.minecraft.network.chat.Component
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.block.entity.RedstoneSetting import ru.dbotthepony.mc.otm.block.entity.RedstoneSetting
import ru.dbotthepony.mc.otm.capability.FlowDirection import ru.dbotthepony.mc.otm.capability.FlowDirection
@ -12,6 +14,7 @@ import ru.dbotthepony.mc.otm.client.isCtrlDown
import ru.dbotthepony.mc.otm.client.isShiftDown import ru.dbotthepony.mc.otm.client.isShiftDown
import ru.dbotthepony.mc.otm.client.minecraft 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.ItemStackIcon
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.Dock import ru.dbotthepony.mc.otm.client.screen.panels.Dock
@ -21,6 +24,7 @@ import ru.dbotthepony.mc.otm.client.screen.panels.FramePanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.AbstractSlotPanel import ru.dbotthepony.mc.otm.client.screen.panels.slot.AbstractSlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.util.GridPanel import ru.dbotthepony.mc.otm.client.screen.panels.util.GridPanel
import ru.dbotthepony.mc.otm.config.ClientConfig
import ru.dbotthepony.mc.otm.core.GetterSetter import ru.dbotthepony.mc.otm.core.GetterSetter
import ru.dbotthepony.mc.otm.core.TextComponent import ru.dbotthepony.mc.otm.core.TextComponent
import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.core.TranslatableComponent
@ -36,6 +40,11 @@ import java.util.function.Predicate
import kotlin.math.ceil import kotlin.math.ceil
import kotlin.math.pow import kotlin.math.pow
private val gunpowder = ItemStackIcon(ItemStack(Items.GUNPOWDER)).fixed()
private val barrier = ItemStackIcon(ItemStack(Items.BARRIER)).fixed()
private val redstone = ItemStackIcon(ItemStack(Items.REDSTONE)).fixed()
private val redstoneUnderBarrier = barrier.composeAfter(redstone).fixed()
private fun <S : MatteryScreen<*>> makeRedstoneSettingButton( private fun <S : MatteryScreen<*>> makeRedstoneSettingButton(
screen: S, screen: S,
parent: EditablePanel<*>?, parent: EditablePanel<*>?,
@ -53,9 +62,9 @@ private fun <S : MatteryScreen<*>> makeRedstoneSettingButton(
set(value) {} set(value) {}
init { init {
add(RedstoneSetting.IGNORED, tooltip = RedstoneSetting.IGNORED.description, skinElement = Widgets18.REDSTONE_IGNORED) add(RedstoneSetting.IGNORED, tooltip = RedstoneSetting.IGNORED.description, skinElement = if (ClientConfig.REDSTONE_CONTROLS_ITEM_ICONS) gunpowder else Widgets18.REDSTONE_IGNORED)
add(RedstoneSetting.LOW, tooltip = RedstoneSetting.LOW.description, skinElement = Widgets18.REDSTONE_LOW) add(RedstoneSetting.LOW, tooltip = RedstoneSetting.LOW.description, skinElement = if (ClientConfig.REDSTONE_CONTROLS_ITEM_ICONS) redstoneUnderBarrier else Widgets18.REDSTONE_LOW)
add(RedstoneSetting.HIGH, tooltip = RedstoneSetting.HIGH.description, skinElement = Widgets18.REDSTONE_HIGH) add(RedstoneSetting.HIGH, tooltip = RedstoneSetting.HIGH.description, skinElement = if (ClientConfig.REDSTONE_CONTROLS_ITEM_ICONS) redstone else Widgets18.REDSTONE_HIGH)
} }
} }
} }

View File

@ -8,6 +8,7 @@ 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.minecraft
import ru.dbotthepony.mc.otm.client.render.AbstractMatterySprite import ru.dbotthepony.mc.otm.client.render.AbstractMatterySprite
import ru.dbotthepony.mc.otm.client.render.IGUIRenderable
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
import ru.dbotthepony.mc.otm.core.GetterSetter import ru.dbotthepony.mc.otm.core.GetterSetter
@ -49,14 +50,14 @@ abstract class EnumRectangleButtonPanel<out S : Screen, T : Enum<T>>(
data class Entry<T : Enum<T>>( data class Entry<T : Enum<T>>(
val key: T, val key: T,
val skinElement: AbstractMatterySprite?, val skinElement: IGUIRenderable?,
val winding: UVWindingOrder = UVWindingOrder.NORMAL, val winding: UVWindingOrder = UVWindingOrder.NORMAL,
val tooltip: Component? = null val tooltip: Component? = null
) )
protected val enumMapping = EnumMap<T, Entry<T>>(enum) protected val enumMapping = EnumMap<T, Entry<T>>(enum)
fun add(key: T, skinElement: AbstractMatterySprite? = null, tooltip: Component? = null, winding: UVWindingOrder = UVWindingOrder.NORMAL): EnumRectangleButtonPanel<S, T> { fun add(key: T, skinElement: IGUIRenderable? = 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))
} }

View File

@ -4,6 +4,7 @@ import com.mojang.blaze3d.vertex.PoseStack
import net.minecraft.client.gui.GuiGraphics import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.screens.Screen import net.minecraft.client.gui.screens.Screen
import ru.dbotthepony.mc.otm.client.render.AbstractMatterySprite import ru.dbotthepony.mc.otm.client.render.AbstractMatterySprite
import ru.dbotthepony.mc.otm.client.render.IGUIRenderable
import ru.dbotthepony.mc.otm.client.render.UVWindingOrder import ru.dbotthepony.mc.otm.client.render.UVWindingOrder
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
@ -16,8 +17,8 @@ open class LargeRectangleButtonPanel<out S : Screen>(
width: Float = SIZE, width: Float = SIZE,
height: Float = SIZE, height: Float = SIZE,
onPress: ((clickButton: Int) -> Unit)? = null, onPress: ((clickButton: Int) -> Unit)? = null,
val skinElement: AbstractMatterySprite? = null, var skinElement: IGUIRenderable? = null,
val skinElementWinding: UVWindingOrder? = null, var skinElementWinding: UVWindingOrder? = null,
) : RectangleButtonPanel<S>(screen, parent, x, y, width, height, onPress) { ) : RectangleButtonPanel<S>(screen, parent, x, y, width, height, onPress) {
final override val IDLE = Widgets18.BUTTON_IDLE final override val IDLE = Widgets18.BUTTON_IDLE
final override val HOVERED = Widgets18.BUTTON_HOVERED final override val HOVERED = Widgets18.BUTTON_HOVERED
@ -28,7 +29,7 @@ open class LargeRectangleButtonPanel<out S : Screen>(
super.innerRender(graphics, mouseX, mouseY, partialTick) super.innerRender(graphics, mouseX, mouseY, partialTick)
if (skinElementWinding != null) { if (skinElementWinding != null) {
skinElement?.render(graphics, width = width, height = height, winding = skinElementWinding) skinElement?.render(graphics, width = width, height = height, winding = skinElementWinding!!)
} else { } else {
skinElement?.render(graphics, width = width, height = height) skinElement?.render(graphics, width = width, height = height)
} }

View File

@ -5,6 +5,7 @@ import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.screens.Screen import net.minecraft.client.gui.screens.Screen
import ru.dbotthepony.mc.otm.client.playGuiClickSound import ru.dbotthepony.mc.otm.client.playGuiClickSound
import ru.dbotthepony.mc.otm.client.render.AbstractMatterySprite import ru.dbotthepony.mc.otm.client.render.AbstractMatterySprite
import ru.dbotthepony.mc.otm.client.render.IGUIRenderable
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
import ru.dbotthepony.mc.otm.core.TextComponent import ru.dbotthepony.mc.otm.core.TextComponent
import java.util.function.IntConsumer import java.util.function.IntConsumer
@ -19,10 +20,10 @@ abstract class RectangleButtonPanel<out S : Screen>(
height: Float, height: Float,
var onPress: IntConsumer? = null, var onPress: IntConsumer? = null,
) : AbstractButtonPanel<S>(screen, parent, x, y, width, height) { ) : AbstractButtonPanel<S>(screen, parent, x, y, width, height) {
abstract val PRESSED: AbstractMatterySprite abstract val PRESSED: IGUIRenderable
abstract val HOVERED: AbstractMatterySprite abstract val HOVERED: IGUIRenderable
abstract val IDLE: AbstractMatterySprite abstract val IDLE: IGUIRenderable
abstract val DISABLED: AbstractMatterySprite abstract val DISABLED: IGUIRenderable
override fun onClick(mouseButton: Int) { override fun onClick(mouseButton: Int) {
onPress?.accept(mouseButton) onPress?.accept(mouseButton)

View File

@ -4,38 +4,28 @@ import net.minecraftforge.common.ForgeConfigSpec
import net.minecraftforge.fml.ModLoadingContext import net.minecraftforge.fml.ModLoadingContext
import net.minecraftforge.fml.config.ModConfig import net.minecraftforge.fml.config.ModConfig
object ClientConfig { object ClientConfig : AbstractConfig("client", ModConfig.Type.CLIENT) {
private val specBuilder = ForgeConfigSpec.Builder() var ALWAYS_DISPLAY_MATTER_VALUE: Boolean by builder
@Suppress("JoinDeclarationAndAssignment")
private val spec: ForgeConfigSpec
private var registered = false
var ALWAYS_DISPLAY_MATTER_VALUE: Boolean by specBuilder
.define("ALWAYS_DISPLAY_MATTER_VALUE", false) .define("ALWAYS_DISPLAY_MATTER_VALUE", false)
var EXOPACK_INVENTORY_ROWS: Int by specBuilder var EXOPACK_INVENTORY_ROWS: Int by builder
.comment("Amount of inventory rows to show when wearing Exosuit") .comment("Amount of inventory rows to show when wearing Exosuit")
.defineInRange("EXOPACK_INVENTORY_ROWS", 3, 3, 6) .defineInRange("EXOPACK_INVENTORY_ROWS", 3, 3, 6)
var JUMP_BOOST_LOOK_ANGLE: Double by specBuilder var JUMP_BOOST_LOOK_ANGLE: Double by builder
.comment("If looking below this angle (actually, looking 'above' as you see in game, but not as you expect it, check with debug screen), Crouch + Jump will trigger jump boost android ability") .comment("If looking below this angle (actually, looking 'above' as you see in game, but not as you expect it, check with debug screen), Crouch + Jump will trigger jump boost android ability")
.defineInRange("JUMP_BOOST_LOOK_ANGLE", 30.0, -180.0, 180.0) .defineInRange("JUMP_BOOST_LOOK_ANGLE", 30.0, -180.0, 180.0)
var EXOPACK_FREE_SCROLL: Boolean by specBuilder var EXOPACK_FREE_SCROLL: Boolean by builder
.comment("Allow to scroll Exopack inventory in non OTM inventories when hovering just over inventory slots, not only scrollbar") .comment("Allow to scroll Exopack inventory in non OTM inventories when hovering just over inventory slots, not only scrollbar")
.define("EXOPACK_FREE_SCROLL", true) .define("EXOPACK_FREE_SCROLL", true)
var ANDROID_HEALTH_HUD: Boolean by specBuilder var ANDROID_HEALTH_HUD: Boolean by builder
.comment("Replace hearts with health bar on HUD when you are an android") .comment("Replace hearts with health bar on HUD when you are an android")
.define("ANDROID_HEALTH_HUD", true) .define("ANDROID_HEALTH_HUD", true)
init { var REDSTONE_CONTROLS_ITEM_ICONS: Boolean by builder
spec = specBuilder.build() .comment("Display redstone control button using items instead of custom sprites")
} .comment("For those who want it.")
.define("REDSTONE_CONTROLS_ITEM_ICONS", false)
fun register() {
check(!registered) { "Already registered config" }
registered = true
ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, spec)
}
} }