From 7d5b63f38e2dda2b58daea928b9bb2a6f293636f Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Mon, 30 Jan 2023 23:19:58 +0700 Subject: [PATCH] Redstone control button, sanitize EnumRectangleButtonPanel, EnumInputWithFeedback --- .../mc/otm/client/render/MatteryAtlas.kt | 3 - .../mc/otm/client/render/WidgetLocation.kt | 1 + .../mc/otm/client/render/Widgets18.kt | 6 + .../mc/otm/client/screen/MatteryScreen.kt | 35 +++++ .../screen/decorative/HoloSignScreen.kt | 3 + .../client/screen/panels/button/Buttons.kt | 59 ++++++++ .../panels/button/EnumRectangleButtonPanel.kt | 128 +++++++----------- .../button/LargeEnumRectangleButtonPanel.kt | 3 +- .../button/SmallEnumRectangleButtonPanel.kt | 3 +- .../screen/panels/slot/AbstractSlotPanel.kt | 35 +---- .../screen/storage/ItemMonitorScreen.kt | 43 +++--- .../dbotthepony/mc/otm/core/GetterSetter.kt | 25 +++- .../mc/otm/core/util/DataStreams.kt | 11 +- .../mc/otm/menu/decorative/HoloSignMenu.kt | 5 + .../input/AbstractPlayerInputWithFeedback.kt | 8 ++ .../otm/menu/input/EnumInputWithFeedback.kt | 22 +++ .../textures/gui/widgets/redstone.png | Bin 0 -> 736 bytes .../textures/gui/widgets/redstone.xcf | Bin 0 -> 2211 bytes 18 files changed, 242 insertions(+), 148 deletions(-) create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/button/Buttons.kt create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/menu/input/EnumInputWithFeedback.kt create mode 100644 src/main/resources/assets/overdrive_that_matters/textures/gui/widgets/redstone.png create mode 100644 src/main/resources/assets/overdrive_that_matters/textures/gui/widgets/redstone.xcf diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/MatteryAtlas.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/MatteryAtlas.kt index 0cb929362..f0b8feab9 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/MatteryAtlas.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/MatteryAtlas.kt @@ -22,9 +22,6 @@ data class MatteryAtlas( winding: UVWindingOrder = this.winding, ) = MatterySprite(texture, x = x, y = y, width = width, height = height, atlasHeight = this.height, atlasWidth = this.width, winding = winding) - @Deprecated("Construct grid directly instead") fun grid(rows: Int, columns: Int) = GridAtlas(texture, this.width / rows, this.height / columns, rows = rows, columns = columns) - - @Deprecated("Construct grid directly instead") fun grid(width: Float, height: Float, rows: Int, columns: Int) = GridAtlas(texture, width, height, rows, columns) } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/WidgetLocation.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/WidgetLocation.kt index a2b160c72..c8fcecade 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/WidgetLocation.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/WidgetLocation.kt @@ -13,4 +13,5 @@ object WidgetLocation { val PROGRESS_ARROWS = MatteryAtlas(ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/gui/widgets/progress_arrows.png"), 22f, 31f) val HORIZONTAL_GAUGES = MatteryAtlas(ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/gui/widgets/horizontal_gauges.png"), 96f, 54f) val VERTICAL_GAUGES = MatteryAtlas(ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/gui/widgets/vertical_gauges.png"), 90f, 48f) + val REDSTONE_CONTROLS = MatteryAtlas(ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/gui/widgets/redstone.png"), 54f, 18f) } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/Widgets18.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/Widgets18.kt index 84cde28be..1acecb411 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/Widgets18.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/Widgets18.kt @@ -46,4 +46,10 @@ object Widgets18 { val BUTTON_DISABLED_STRETCHABLE = makeButton(GRID) val BUTTON_DISABLED = GRID.next() val COOLDOWN = GRID.next() + + private val redstoneGrid = WidgetLocation.REDSTONE_CONTROLS.grid(rows = 1, columns = 3) + + val REDSTONE_IGNORED = redstoneGrid.next() + val REDSTONE_LOW = redstoneGrid.next() + val REDSTONE_HIGH = redstoneGrid.next() } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/MatteryScreen.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/MatteryScreen.kt index 8679606a7..394c95677 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/MatteryScreen.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/MatteryScreen.kt @@ -11,6 +11,7 @@ import net.minecraft.client.renderer.entity.ItemRenderer import net.minecraft.network.chat.Component import net.minecraft.world.entity.player.Inventory import net.minecraft.world.inventory.Slot +import net.minecraft.world.item.ItemStack import net.minecraftforge.client.event.ContainerScreenEvent.Render.Background import net.minecraftforge.client.event.ContainerScreenEvent.Render.Foreground import net.minecraftforge.common.MinecraftForge @@ -62,6 +63,40 @@ abstract class MatteryScreen(menu: T, inventory: Inventory, tit val quickCraftingType get() = quickCraftingType val isQuickCrafting get() = isQuickCrafting + fun renderItemStack(absoluteX: Float, absoluteY: Float, itemstack: ItemStack, countOverride: String? = null) { + if (!itemstack.isEmpty) { + RenderSystem.enableDepthTest() + + val systemPoseStack = RenderSystem.getModelViewStack() + + systemPoseStack.pushPose() + systemPoseStack.translate(absoluteX + 1f, absoluteY + 1f, 0f) + RenderSystem.applyModelViewMatrix() + RenderSystem.depthFunc(GL11.GL_LESS) + + // Thanks Mojang + // Very cool + // (for int x, int y, which are then cast into doubles anyway) + itemRenderer.blitOffset = 1f // Z pos + + itemRenderer.renderAndDecorateItem( + requireNotNull(ru.dbotthepony.mc.otm.client.minecraft.player) { "yo, dude, what the fuck" }, + itemstack, + 0, + 0, + (absoluteX + absoluteY * 1000f).toInt() + ) + + RenderSystem.depthFunc(GL11.GL_ALWAYS) + itemRenderer.renderGuiItemDecorations(font, itemstack, 0, 0, countOverride) + itemRenderer.blitOffset = 0f + + // too big accumulations can lead to Z near clipping issues + systemPoseStack.popPose() + RenderSystem.applyModelViewMatrix() + } + } + init { for (slot in menu.slots) { slot.x = Int.MAX_VALUE diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/decorative/HoloSignScreen.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/decorative/HoloSignScreen.kt index 676642d71..213dba6a2 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/decorative/HoloSignScreen.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/decorative/HoloSignScreen.kt @@ -7,6 +7,7 @@ import ru.dbotthepony.mc.otm.client.screen.panels.Dock import ru.dbotthepony.mc.otm.client.screen.panels.DockProperty import ru.dbotthepony.mc.otm.client.screen.panels.FramePanel 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.NetworkedStringInputPanel import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.menu.decorative.HoloSignMenu @@ -24,6 +25,8 @@ class HoloSignScreen(menu: HoloSignMenu, inventory: Inventory, title: Component) lock.dockMargin = DockProperty(2f, 2f, 2f, 2f) lock.tooltip = TranslatableComponent("otm.gui.lock_holo_screen.tip") + makeDeviceControls(this, frame, redstone = menu.redstone) + return frame } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/button/Buttons.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/button/Buttons.kt new file mode 100644 index 000000000..b3dba870b --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/button/Buttons.kt @@ -0,0 +1,59 @@ +package ru.dbotthepony.mc.otm.client.screen.panels.button + +import com.mojang.blaze3d.vertex.PoseStack +import net.minecraft.world.item.ItemStack +import net.minecraft.world.item.Items +import ru.dbotthepony.mc.otm.block.entity.RedstoneSetting +import ru.dbotthepony.mc.otm.client.minecraft +import ru.dbotthepony.mc.otm.client.render.Widgets18 +import ru.dbotthepony.mc.otm.client.screen.MatteryScreen +import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel +import ru.dbotthepony.mc.otm.client.screen.panels.FramePanel +import ru.dbotthepony.mc.otm.menu.input.IPlayerInputWithFeedback + +fun > makeRedstoneSettingButton( + screen: S, + parent: EditablePanel<*>?, + x: Float = 0f, + y: Float = 0f, + control: IPlayerInputWithFeedback +): LargeEnumRectangleButtonPanel { + return object : LargeEnumRectangleButtonPanel( + screen, parent, x = x, y = y, + defaultValue = RedstoneSetting.LOW, enum = RedstoneSetting::class.java, + prop = control, + ) { + override var isDisabled: Boolean + get() = !control.test(minecraft.player) + set(value) {} + + init { + add(RedstoneSetting.IGNORED, tooltip = RedstoneSetting.IGNORED.description, skinElement = Widgets18.REDSTONE_IGNORED) + add(RedstoneSetting.LOW, tooltip = RedstoneSetting.LOW.description, skinElement = Widgets18.REDSTONE_LOW) + add(RedstoneSetting.HIGH, tooltip = RedstoneSetting.HIGH.description, skinElement = Widgets18.REDSTONE_HIGH) + } + } +} + +fun > makeDeviceControls( + screen: S, + parent: FramePanel, + redstone: IPlayerInputWithFeedback? = null +): EditablePanel { + val panel = object : EditablePanel(screen, parent, width = LargeEnumRectangleButtonPanel.SIZE, height = 0f) { + override fun tick() { + super.tick() + x = parent.width + 3f + y = 0f + } + } + + var y = 0f + + if (redstone != null) { + y += makeRedstoneSettingButton(screen, panel, y = y, control = redstone).height + 2f + } + + panel.height = (y - 2f).coerceAtLeast(0f) + return panel +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/button/EnumRectangleButtonPanel.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/button/EnumRectangleButtonPanel.kt index 88ebba99e..e6e8e65f2 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/button/EnumRectangleButtonPanel.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/button/EnumRectangleButtonPanel.kt @@ -12,6 +12,7 @@ import ru.dbotthepony.mc.otm.core.GetterSetter import ru.dbotthepony.mc.otm.core.TextComponent import ru.dbotthepony.mc.otm.core.next import ru.dbotthepony.mc.otm.core.prev +import ru.dbotthepony.mc.otm.core.util.EnumValueCodec import ru.dbotthepony.mc.otm.core.value import java.util.* import kotlin.collections.ArrayList @@ -23,31 +24,25 @@ abstract class EnumRectangleButtonPanel>( y: Float = 0f, width: Float, height: Float, - val enum: Class, + enum: Class, val prop: GetterSetter, val defaultValue: T, - val onChange: ((newValue: T) -> Unit)? = null, ) : RectangleButtonPanel(screen, parent, x, y, width, height, null) { - private var building = true + val enum = EnumValueCodec.searchClass(enum) + private var isBuilding = true - protected val enumMapping = EnumMap>(enum) - protected val tooltipMapping = EnumMap(enum) + data class Entry>( + val key: T, + val skinElement: AbstractMatterySprite?, + val winding: UVWindingOrder = UVWindingOrder.NORMAL, + val tooltip: Component? = null + ) - fun addTooltip(value: T, component: Component): EnumRectangleButtonPanel { - check(tooltipMapping.put(value, component) == null) { "Already has mapping for $value" } - return this - } - - var mainTooltip: Component? = null - set(value) { - if (field != null) { - throw UnsupportedOperationException("Write once only") - } - - field = value - } + protected val enumMapping = EnumMap>(enum) fun isFullyDefined(): Boolean { + if (!isBuilding) return true + for (value in enum.enumConstants) { if (enumMapping[value] == null) { return false @@ -69,77 +64,54 @@ abstract class EnumRectangleButtonPanel>( return missing } - fun add(value: T, skinElement: AbstractMatterySprite, winding: UVWindingOrder = UVWindingOrder.NORMAL): EnumRectangleButtonPanel { - return add(value, skinElement to winding) + fun add(key: T, skinElement: AbstractMatterySprite? = null, tooltip: Component? = null, winding: UVWindingOrder = UVWindingOrder.NORMAL): EnumRectangleButtonPanel { + return add(Entry(key = key, skinElement = skinElement, winding = winding, tooltip = tooltip)) } - fun add(value: T, skinElement: AbstractMatterySprite, component: Component, winding: UVWindingOrder = UVWindingOrder.NORMAL): EnumRectangleButtonPanel { - return add(value, component, skinElement to winding) - } - - fun add(value: T, component: Component, skinElement: AbstractMatterySprite, winding: UVWindingOrder = UVWindingOrder.NORMAL): EnumRectangleButtonPanel { - return add(value, component, skinElement to winding) - } - - fun add(value: T, pair: Pair): EnumRectangleButtonPanel { - check(building) { "Not building" } - check(enumMapping.put(value, pair) == null) { "Already has mapping for $value" } - - if (enumMapping.size == enum.enumConstants.size) { - finish() - } - + fun add(entry: Entry): EnumRectangleButtonPanel { + check(isBuilding) { "Not building" } + check(enumMapping.put(entry.key, entry) == null) { "Already has mapping for ${entry.key}" } + if (enumMapping.size == enum.enumConstants.size) finish() return this } - fun add(value: T, component: Component, pair: Pair): EnumRectangleButtonPanel { - addTooltip(value, component) - return add(value, pair) - } - fun finish(): EnumRectangleButtonPanel { - check(building) { "Not building" } - check(isFullyDefined()) { - "Not all enums having their mapping defined, missing are: ${missingValues.joinToString(", ")}" - } - - building = false + check(isBuilding) { "Not building" } + check(isFullyDefined()) { "Not all enums having their mapping defined, missing are: ${missingValues.joinToString(", ")}" } + isBuilding = false return this } override fun innerRender(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float) { - check(!building) { "Still building this button!" } + check(!isBuilding) { "Still building this button!" } super.innerRender(stack, mouseX, mouseY, partialTick) - val pair = checkNotNull(enumMapping[prop.get()]) { "HOW" } - pair.first.render(stack, 0f, 0f, width, height, pair.second) + val entry = checkNotNull(enumMapping[prop.get()]) { "HOW" } + entry.skinElement?.render(stack, 0f, 0f, width, height, entry.winding) } override fun mouseClickedInner(x: Double, y: Double, button: Int): Boolean { - check(!building) { "Still building this button!" } + check(!isBuilding) { "Still building this button!" } return super.mouseClickedInner(x, y, button) } override fun mouseReleasedInner(x: Double, y: Double, button: Int): Boolean { - check(!building) { "Still building this button!" } + check(!isBuilding) { "Still building this button!" } return super.mouseReleasedInner(x, y, button) } - override fun onClick(clickButton: Int) { - when (clickButton) { - InputConstants.MOUSE_BUTTON_LEFT -> { - prop.value = prop.value.next(enum.enumConstants) - onChange?.invoke(prop.get()) - } - - InputConstants.MOUSE_BUTTON_RIGHT -> { - prop.value = prop.value.prev(enum.enumConstants) - onChange?.invoke(prop.get()) - } + override fun test(value: Int): Boolean { + return value == InputConstants.MOUSE_BUTTON_LEFT || + value == InputConstants.MOUSE_BUTTON_RIGHT || + value == InputConstants.MOUSE_BUTTON_MIDDLE + } + override fun onClick(mouseButton: Int) { + when (mouseButton) { + InputConstants.MOUSE_BUTTON_LEFT -> prop.value = prop.value.next(enum.enumConstants) + InputConstants.MOUSE_BUTTON_RIGHT -> prop.value = prop.value.prev(enum.enumConstants) InputConstants.MOUSE_BUTTON_MIDDLE -> { - if (prop.get() != defaultValue) { + if (prop.value != defaultValue) { prop.value = defaultValue - onChange?.invoke(prop.get()) } } } @@ -150,33 +122,27 @@ abstract class EnumRectangleButtonPanel>( return super.innerRenderTooltips(stack, mouseX, mouseY, partialTick) } - if (mainTooltip == null && tooltipMapping.size == 0) { + if (tooltip == null && tooltipList == null && enumMapping.values.none { it.tooltip != null }) { return super.innerRenderTooltips(stack, mouseX, mouseY, partialTick) } val listing = ArrayList() - if (mainTooltip != null) { - listing.add(mainTooltip!!) + if (tooltipList != null) { + listing.addAll(tooltipList!!) + listing.add(SPACE) + } else if (tooltip != null) { + listing.add(tooltip!!) listing.add(SPACE) } - for ((key, value) in tooltipMapping) { - if (key == prop.get()) { - listing.add(value.copy().withStyle(ChatFormatting.WHITE)) - } else { - listing.add(value.copy().withStyle(ChatFormatting.GRAY)) + for (entry in enumMapping.values) { + if (entry.tooltip != null) { + listing.add(entry.tooltip.copy().withStyle(if (entry.key == prop.get()) ChatFormatting.WHITE else ChatFormatting.GRAY)) } } - screen.renderComponentTooltip( - stack, - listing, - mouseX.toInt(), - mouseY.toInt(), - font - ) - + screen.renderComponentTooltip(stack, listing, mouseX.toInt(), mouseY.toInt(), font) return true } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/button/LargeEnumRectangleButtonPanel.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/button/LargeEnumRectangleButtonPanel.kt index 2eae319de..2e0925ad0 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/button/LargeEnumRectangleButtonPanel.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/button/LargeEnumRectangleButtonPanel.kt @@ -15,8 +15,7 @@ open class LargeEnumRectangleButtonPanel>( enum: Class, prop: GetterSetter, defaultValue: T, - onChange: ((newValue: T) -> Unit)? = null, -) : EnumRectangleButtonPanel(screen, parent, x, y, width, height, enum, prop, defaultValue, onChange) { +) : EnumRectangleButtonPanel(screen, parent, x, y, width, height, enum, prop, defaultValue) { final override val IDLE = Widgets18.BUTTON_IDLE final override val HOVERED = Widgets18.BUTTON_HOVERED final override val PRESSED = Widgets18.BUTTON_PRESSED diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/button/SmallEnumRectangleButtonPanel.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/button/SmallEnumRectangleButtonPanel.kt index c432c73a2..29444e7db 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/button/SmallEnumRectangleButtonPanel.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/button/SmallEnumRectangleButtonPanel.kt @@ -15,8 +15,7 @@ open class SmallEnumRectangleButtonPanel>( enum: Class, prop: GetterSetter, defaultValue: T, - onChange: ((newValue: T) -> Unit)? = null, -) : EnumRectangleButtonPanel(screen, parent, x, y, width, height, enum, prop, defaultValue, onChange) { +) : EnumRectangleButtonPanel(screen, parent, x, y, width, height, enum, prop, defaultValue) { final override val IDLE = Widgets8.BUTTON_IDLE final override val HOVERED = Widgets8.BUTTON_HOVERED final override val PRESSED = Widgets8.BUTTON_PRESSED diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/slot/AbstractSlotPanel.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/slot/AbstractSlotPanel.kt index 8bb20e1cd..37422cba5 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/slot/AbstractSlotPanel.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/slot/AbstractSlotPanel.kt @@ -6,8 +6,6 @@ import net.minecraft.client.renderer.GameRenderer import net.minecraft.network.chat.Component import net.minecraft.world.item.ItemStack import net.minecraftforge.client.extensions.common.IClientItemExtensions -import org.lwjgl.opengl.GL11 -import ru.dbotthepony.mc.otm.client.minecraft import ru.dbotthepony.mc.otm.client.render.* import ru.dbotthepony.mc.otm.client.screen.MatteryScreen import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel @@ -27,40 +25,11 @@ abstract class AbstractSlotPanel> @JvmOverloads constru SLOT_BACKGROUND.render(stack, width = width, height = height) } - protected open fun renderRegular(stack: PoseStack, itemstack: ItemStack, count_override: String? = null) { + protected open fun renderRegular(stack: PoseStack, itemstack: ItemStack, countOverride: String? = null) { RenderSystem.setShader(GameRenderer::getPositionTexShader) if (!itemstack.isEmpty) { - RenderSystem.enableDepthTest() - - val systemPoseStack = RenderSystem.getModelViewStack() - - systemPoseStack.pushPose() - systemPoseStack.translate((absoluteX + 1f).toDouble(), (absoluteY + 1f).toDouble(), 0.0) - RenderSystem.applyModelViewMatrix() - RenderSystem.depthFunc(GL11.GL_LESS) - - // Thanks Mojang - // Very cool - // (for int x, int y, which are then cast into doubles anyway) - screen.itemRenderer.blitOffset = 1f // Z pos - - screen.itemRenderer.renderAndDecorateItem( - requireNotNull(minecraft.player) { "yo, dude, what the fuck" }, - itemstack, - 0, - 0, - (absoluteX + absoluteY * 1000f).toInt() - ) - - RenderSystem.depthFunc(GL11.GL_ALWAYS) - screen.itemRenderer.renderGuiItemDecorations(screen.font, itemstack, 0, 0, count_override) - screen.itemRenderer.blitOffset = 0f - - // too big accumulations can lead to Z near clipping issues - systemPoseStack.popPose() - RenderSystem.applyModelViewMatrix() - + screen.renderItemStack(absoluteX, absoluteY, itemstack, countOverride) clearDepth(stack) } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/storage/ItemMonitorScreen.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/storage/ItemMonitorScreen.kt index e1b67b61a..902b1e211 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/storage/ItemMonitorScreen.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/storage/ItemMonitorScreen.kt @@ -139,16 +139,15 @@ class ItemMonitorScreen(menu: ItemMonitorMenu, inventory: Inventory, title: Comp val refillPriority = SmallEnumRectangleButtonPanel(this, arrowLine, enum = ItemMonitorPlayerSettings.IngredientPriority::class.java, - prop = menu.settings::ingredientPriority.asGetterSetter(), - defaultValue = ItemMonitorPlayerSettings.IngredientPriority.SYSTEM, - onChange = { menu.sendSettingsToServer() }) + prop = menu.settings::ingredientPriority.asGetterSetter(watch = { _, _ -> menu.sendSettingsToServer() }), + defaultValue = ItemMonitorPlayerSettings.IngredientPriority.SYSTEM) - refillPriority.mainTooltip = TranslatableComponent("otm.gui.item_monitor.refill_source.desc") - refillPriority.add(ItemMonitorPlayerSettings.IngredientPriority.SYSTEM, ItemMonitorPlayerSettings.IngredientPriority.SYSTEM.component, Widgets8.WHITE_ARROW_DOWN, UVWindingOrder.FLIP) - refillPriority.add(ItemMonitorPlayerSettings.IngredientPriority.INVENTORY, ItemMonitorPlayerSettings.IngredientPriority.INVENTORY.component, Widgets8.WHITE_ARROW_DOWN) - refillPriority.add(ItemMonitorPlayerSettings.IngredientPriority.INVENTORY_FIRST, ItemMonitorPlayerSettings.IngredientPriority.INVENTORY_FIRST.component, Widgets8.ARROW_SIDEWAYS, UVWindingOrder.FLIP) - refillPriority.add(ItemMonitorPlayerSettings.IngredientPriority.SYSTEM_FIRST, ItemMonitorPlayerSettings.IngredientPriority.SYSTEM_FIRST.component, Widgets8.ARROW_SIDEWAYS) - refillPriority.add(ItemMonitorPlayerSettings.IngredientPriority.DO_NOT, ItemMonitorPlayerSettings.IngredientPriority.DO_NOT.component, Widgets8.MINUS) + refillPriority.tooltip = TranslatableComponent("otm.gui.item_monitor.refill_source.desc") + refillPriority.add(ItemMonitorPlayerSettings.IngredientPriority.SYSTEM, tooltip = ItemMonitorPlayerSettings.IngredientPriority.SYSTEM.component, skinElement = Widgets8.WHITE_ARROW_DOWN, winding = UVWindingOrder.FLIP) + refillPriority.add(ItemMonitorPlayerSettings.IngredientPriority.INVENTORY, tooltip = ItemMonitorPlayerSettings.IngredientPriority.INVENTORY.component, skinElement = Widgets8.WHITE_ARROW_DOWN) + refillPriority.add(ItemMonitorPlayerSettings.IngredientPriority.INVENTORY_FIRST, tooltip = ItemMonitorPlayerSettings.IngredientPriority.INVENTORY_FIRST.component, skinElement = Widgets8.ARROW_SIDEWAYS, winding = UVWindingOrder.FLIP) + refillPriority.add(ItemMonitorPlayerSettings.IngredientPriority.SYSTEM_FIRST, tooltip = ItemMonitorPlayerSettings.IngredientPriority.SYSTEM_FIRST.component, skinElement = Widgets8.ARROW_SIDEWAYS) + refillPriority.add(ItemMonitorPlayerSettings.IngredientPriority.DO_NOT, tooltip = ItemMonitorPlayerSettings.IngredientPriority.DO_NOT.component, skinElement = Widgets8.MINUS) refillPriority.dock = Dock.LEFT @@ -161,25 +160,23 @@ class ItemMonitorScreen(menu: ItemMonitorMenu, inventory: Inventory, title: Comp val resultTarget = SmallEnumRectangleButtonPanel(this, resultAndButtons, y = 38f, enum = ItemMonitorPlayerSettings.ResultTarget::class.java, - prop = menu.settings::resultTarget.asGetterSetter(), - defaultValue = ItemMonitorPlayerSettings.ResultTarget.MIXED, - onChange = { menu.sendSettingsToServer() }) + prop = menu.settings::resultTarget.asGetterSetter(watch = { _, _ -> menu.sendSettingsToServer() }), + defaultValue = ItemMonitorPlayerSettings.ResultTarget.MIXED) - resultTarget.mainTooltip = TranslatableComponent("otm.gui.item_monitor.result_target.desc") - resultTarget.add(ItemMonitorPlayerSettings.ResultTarget.MIXED, ItemMonitorPlayerSettings.ResultTarget.MIXED.component, Widgets8.ARROW_SIDEWAYS) - resultTarget.add(ItemMonitorPlayerSettings.ResultTarget.ALL_INVENTORY, ItemMonitorPlayerSettings.ResultTarget.ALL_INVENTORY.component, Widgets8.ARROW_PAINTED_UP, UVWindingOrder.FLIP) - resultTarget.add(ItemMonitorPlayerSettings.ResultTarget.ALL_SYSTEM, ItemMonitorPlayerSettings.ResultTarget.ALL_SYSTEM.component, Widgets8.ARROW_PAINTED_UP) + resultTarget.tooltip = TranslatableComponent("otm.gui.item_monitor.result_target.desc") + resultTarget.add(ItemMonitorPlayerSettings.ResultTarget.MIXED, tooltip = ItemMonitorPlayerSettings.ResultTarget.MIXED.component, skinElement = Widgets8.ARROW_SIDEWAYS) + resultTarget.add(ItemMonitorPlayerSettings.ResultTarget.ALL_INVENTORY, tooltip = ItemMonitorPlayerSettings.ResultTarget.ALL_INVENTORY.component, skinElement = Widgets8.ARROW_PAINTED_UP, winding = UVWindingOrder.FLIP) + resultTarget.add(ItemMonitorPlayerSettings.ResultTarget.ALL_SYSTEM, tooltip = ItemMonitorPlayerSettings.ResultTarget.ALL_SYSTEM.component, skinElement = Widgets8.ARROW_PAINTED_UP) val craftingAmount = SmallEnumRectangleButtonPanel(this, resultAndButtons, x = 10f, y = 38f, enum = ItemMonitorPlayerSettings.Amount::class.java, - prop = menu.settings::craftingAmount.asGetterSetter(), - defaultValue = ItemMonitorPlayerSettings.Amount.STACK, - onChange = { menu.sendSettingsToServer() }) + prop = menu.settings::craftingAmount.asGetterSetter(watch = { _, _ -> menu.sendSettingsToServer() }), + defaultValue = ItemMonitorPlayerSettings.Amount.STACK) - craftingAmount.mainTooltip = TranslatableComponent("otm.gui.item_monitor.amount.desc") - craftingAmount.add(ItemMonitorPlayerSettings.Amount.ONE, ItemMonitorPlayerSettings.Amount.ONE.component, Widgets8.ONE) - craftingAmount.add(ItemMonitorPlayerSettings.Amount.STACK, ItemMonitorPlayerSettings.Amount.STACK.component, Widgets8.S) - craftingAmount.add(ItemMonitorPlayerSettings.Amount.FULL, ItemMonitorPlayerSettings.Amount.FULL.component, Widgets8.F) + craftingAmount.tooltip = TranslatableComponent("otm.gui.item_monitor.amount.desc") + craftingAmount.add(ItemMonitorPlayerSettings.Amount.ONE, tooltip = ItemMonitorPlayerSettings.Amount.ONE.component, skinElement = Widgets8.ONE) + craftingAmount.add(ItemMonitorPlayerSettings.Amount.STACK, tooltip = ItemMonitorPlayerSettings.Amount.STACK.component, skinElement = Widgets8.S) + craftingAmount.add(ItemMonitorPlayerSettings.Amount.FULL, tooltip = ItemMonitorPlayerSettings.Amount.FULL.component, skinElement = Widgets8.F) val craftingHistory = GridPanel(this, bottomPanel, width = 3 * 18f, height = 3 * 18f, columns = 3, rows = 3) craftingHistory.dock = Dock.LEFT diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/GetterSetter.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/GetterSetter.kt index 022140131..db519b33e 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/GetterSetter.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/GetterSetter.kt @@ -66,5 +66,28 @@ interface GetterSetter : Supplier, Consumer, ReadWriteProperty } } -fun KMutableProperty0.asGetterSetter() = GetterSetter.of(this) +fun KMutableProperty0.asGetterSetter(watch: ((old: V, new: V) -> Unit)? = null): GetterSetter { + return GetterSetter.of(this).let { + if (watch != null) { + it.watch(watch) + } else { + it + } + } +} + +fun GetterSetter.watch(watch: (old: V, new: V) -> Unit): GetterSetter { + return object : GetterSetter { + override fun get(): V { + return this@watch.get() + } + + override fun accept(t: V) { + val old = get() + this@watch.accept(t) + watch.invoke(old, t) + } + } +} + fun KMutableProperty0.asGetterOnly() = GetterSetter.of(Supplier { this.get() }, Consumer { /* do nothing */ }) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/DataStreams.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/DataStreams.kt index 83ca6a005..95c4308d3 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/DataStreams.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/DataStreams.kt @@ -76,7 +76,8 @@ val VarLongValueCodec = StreamCodec(DataInputStream::readVarLongLE, DataOutputSt val BinaryStringCodec = StreamCodec(DataInputStream::readBinaryString, DataOutputStream::writeBinaryString) class EnumValueCodec>(clazz: Class, val writeByIndices: Boolean = false) : IStreamCodec { - private val values = search(clazz) ?: throw ClassCastException("$clazz does not have enum constants. Not an enum?") + val clazz = searchClass(clazz) + private val values = searchClass(clazz).enumConstants!! override fun read(stream: DataInputStream): V { if (writeByIndices) { @@ -109,14 +110,18 @@ class EnumValueCodec>(clazz: Class, val writeByIndices: Boole * * is there an already existing solution? */ - fun > search(clazz: Class): Array? { + fun > searchClass(clazz: Class): Class { var search: Class<*> = clazz while (search.enumConstants == null && search.superclass != null) { search = search.superclass } - return search.enumConstants as? Array + if (search.enumConstants == null) { + throw ClassCastException("$clazz does not represent an enum or enum subclass") + } + + return search as Class } } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/decorative/HoloSignMenu.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/decorative/HoloSignMenu.kt index 402406f00..a9495dfa8 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/decorative/HoloSignMenu.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/decorative/HoloSignMenu.kt @@ -2,9 +2,11 @@ package ru.dbotthepony.mc.otm.menu.decorative import net.minecraft.world.entity.player.Inventory import net.minecraft.world.inventory.Slot +import ru.dbotthepony.mc.otm.block.entity.RedstoneSetting import ru.dbotthepony.mc.otm.block.entity.decorative.HoloSignBlockEntity import ru.dbotthepony.mc.otm.menu.MatteryMenu import ru.dbotthepony.mc.otm.menu.input.BooleanInputWithFeedback +import ru.dbotthepony.mc.otm.menu.input.EnumInputWithFeedback import ru.dbotthepony.mc.otm.menu.input.StringInputWithFeedback import ru.dbotthepony.mc.otm.registry.MMenus @@ -15,14 +17,17 @@ class HoloSignMenu @JvmOverloads constructor( ) : MatteryMenu(MMenus.HOLO_SIGN, containerId, inventory, tile) { val text = StringInputWithFeedback(this) val locked = BooleanInputWithFeedback(this) + val redstone = EnumInputWithFeedback(this, RedstoneSetting::class.java) init { text.filter { it.isCreative || !locked.value } + redstone.filter { it.isCreative || !locked.value } locked.filter { it.isCreative } if (tile != null) { text.withConsumer { if (tile.locked) tile.text = it else tile.text = HoloSignBlockEntity.truncate(it) }.withSupplier(tile::text) locked.with(tile::locked) + redstone.with(tile.redstoneControl::redstoneSetting) } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/input/AbstractPlayerInputWithFeedback.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/input/AbstractPlayerInputWithFeedback.kt index ae053bb60..8a4893d75 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/input/AbstractPlayerInputWithFeedback.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/input/AbstractPlayerInputWithFeedback.kt @@ -24,6 +24,10 @@ interface IPlayerInputWithFeedback : GetterSetter, Predicate { } } + fun of(getterSetter: GetterSetter, filter: Predicate): IPlayerInputWithFeedback { + return object : IPlayerInputWithFeedback, GetterSetter by getterSetter, Predicate by filter {} + } + fun validPlayer(getterSetter: GetterSetter): IPlayerInputWithFeedback { return object : IPlayerInputWithFeedback, GetterSetter by getterSetter { override fun test(t: Player?): Boolean { @@ -34,6 +38,10 @@ interface IPlayerInputWithFeedback : GetterSetter, Predicate { } } +fun GetterSetter.wrapAsPlayerInput(filter: Predicate = Predicate { it != null }): IPlayerInputWithFeedback { + return IPlayerInputWithFeedback.of(this, filter) +} + /** * Represents Server to Client synchronization and Client to Server input * diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/input/EnumInputWithFeedback.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/input/EnumInputWithFeedback.kt new file mode 100644 index 000000000..f03279bd4 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/input/EnumInputWithFeedback.kt @@ -0,0 +1,22 @@ +package ru.dbotthepony.mc.otm.menu.input + +import ru.dbotthepony.mc.otm.core.GetterSetter +import ru.dbotthepony.mc.otm.core.util.EnumValueCodec +import ru.dbotthepony.mc.otm.menu.MatteryMenu +import kotlin.reflect.KMutableProperty0 + +class EnumInputWithFeedback>(menu: MatteryMenu, clazz: Class) : AbstractPlayerInputWithFeedback() { + val codec = EnumValueCodec(clazz) + private val default = codec.clazz.enumConstants!![0] + + override val input = menu.PlayerInput(codec, false) { consumer?.invoke(it) } + override val value by menu.mSynchronizer.ComputedField(getter = { supplier?.invoke() ?: default }, codec) + + constructor(menu: MatteryMenu, clazz: Class, state: KMutableProperty0) : this(menu, clazz) { + with(state) + } + + constructor(menu: MatteryMenu, clazz: Class, state: GetterSetter) : this(menu, clazz) { + with(state) + } +} diff --git a/src/main/resources/assets/overdrive_that_matters/textures/gui/widgets/redstone.png b/src/main/resources/assets/overdrive_that_matters/textures/gui/widgets/redstone.png new file mode 100644 index 0000000000000000000000000000000000000000..bf86e17f084afd79ccdec439e3b7f132dd8b60ac GIT binary patch literal 736 zcmV<60w4W}P)EX>4Tx04R}tkv&MmP!xqvQ$>+V2Rn#}WQa}{L`58>ibb$c+6t{Yn7s54nlvOS zE{=k0!NH%!s)LKOt`4q(Aov5~>f)s6A|>9J6k5c1;qgAsyXWxUeSpxYFwN?k05sh; zQ}LLX&8><(uLxiSVMvfWLS5n{2}!Ab|SlBp3-6HBV5Q@)V( zSmnIMS*z4o>z@3Dp}fA5<~prm#Ib|~k`SSwhB7L!5v5%x#X^eCV?O>N*PkSpLas6x zITlcb2HEw4|H1FsT7{`eFDVoQx?ddUV-)Dw1)6oo`95}><_Qpd2CnqBzuEw1KS{5* zHR%WlZvz+CZB5w&E_Z;zCqp)6R|?V+3I*W(jJ_!c4BP^JYu?;i=Qw=;GBm5@8{ps& z7%Nity3f0NJLmRqPiuZZxTA8z1ijBF00006VoOIv0RI600RN!9r;`8x010qNS#tmY zE+YT{E+YYWr9XB6000McNliru=K&rMGbR22e#`&>02y>eSad^gZEa<4bO1wgWnpw> zWFU8GbZ8()Nlj2!fese{006j2L_t(Y$75g^1w#uk!WjSm|0mA?M#e$uF{a@fW1}9U z1eoVG{5Q{S_&;o8jGmsu>#=nqA^)+lhl1zeMcUfn;Q#pKVTC8Ug5b4lQPf~Fr%Or~ z!(fLqJ5l(Z$Bz>>NH!RoISg1lM#yZe=1{91tFW^A-*N01 zBP=Nfty#lJPCbU6{7z18V5D18q-6z->@j+HiteQ%y*xGAq#A8`jDk@>HUI$rfJ6$Y S$Bh{P0000bQhA2niMr6lfPg$%e?QA{B{H2?_9)O`154r8udQfZJVGd_jc5 zKaeG#!Pl^@G@LW`G%iF{rEV&&b!P6|du}|QGcyUJ_|QG-^xQA?M#F%;1a8xTeFJU* zriq3@2iUSWU~>ns_dp_GcL4!x0{1hYmG^+(KqbV>YMoIW2g51GhWgpPanPOyT{nrQ zefQ1Vckh|Gsi;xh4ue`aj=GxtV|y|^9t7@WG>E!8&UP>Cj0U5zyREv~I6P>(b@eoM zkUm(GZn)}U`e<_4?nJ}TZPYP3J~PS&wa>$7>PEv(KbWv$)ay-xsjK)oKXuQNIp>_2 z^K?-7koLvbX}`fm{oAf1KyZbM{V42{z%{3p0IstHs`PhfXrTqbIE%`ekIs0U^B?7W zzL4T|q zin#z&fy~9?AEwGxrEfe0V0zw4hruXuLu_y+u99#R;4;E-ljKZ%%nkGcWWtHznjAvn ztBzARN19EE)@nMk&=gW6#iq5)T13h!Lk=ZQB}7duTd0Xq6Qjl}#+IM8h@b`;awv8x zCdM--4)#gtq?a>o8A zwTt|qm6cwt{L^pG)GmEmlS8Pau6*X!9#gS26Km-tB^=p{99H3*p5;4U38G!($gbm` zdvLqaqKgfC;XHweFSG-f15wdr6d|#d{ak2826^X5- j+n0#@xJG%W3eVOOof9`s@DCvc&Q`%F12~syXy5n)`ObX} literal 0 HcmV?d00001