From fafa42b9b71c6d9ee4f6a6c5a2ea8495520f455a Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Sat, 24 Sep 2022 14:44:36 +0700 Subject: [PATCH] Cosmetic armor support in exosuit gui --- build.gradle.kts | 2 + gradle.properties | 1 + .../mc/otm/client/ClientEventHandler.kt | 3 +- .../client/screen/ExoSuitInventoryScreen.kt | 20 ++- .../otm/client/screen/panels/EditBoxPanel.kt | 2 +- .../otm/client/screen/panels/EditablePanel.kt | 19 ++- .../client/screen/panels/FoldableSlotPanel.kt | 103 +++++++++++++ .../mc/otm/client/screen/panels/SlotPanel.kt | 2 +- ...inecraftWidgetPanel.kt => Widget2Panel.kt} | 6 +- .../mc/otm/compat/cos/CosmeticArmorCompat.kt | 142 ++++++++++++++++++ .../mc/otm/menu/ExoSuitInventoryMenu.kt | 3 + 11 files changed, 288 insertions(+), 15 deletions(-) create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/FoldableSlotPanel.kt rename src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/{MinecraftWidgetPanel.kt => Widget2Panel.kt} (93%) create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/compat/cos/CosmeticArmorCompat.kt diff --git a/build.gradle.kts b/build.gradle.kts index 29f81e5b7..d42fc07db 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -181,8 +181,10 @@ dependencies { val the_one_probe_version: String by project val mekanism_version: String by project val curios_version: String by project + val cosmetic_armor_reworked_version: String by project implementation(fg.deobf("top.theillusivec4.curios:curios-forge:${mc_version}-${curios_version}")) + implementation(fg.deobf("lain.mods.cos:CosmeticArmorReworked:${mc_version}-${cosmetic_armor_reworked_version}")) compileOnly(fg.deobf("mezz.jei:jei-${mc_version}-common-api:${jei_version}")) compileOnly(fg.deobf("mezz.jei:jei-${mc_version}-forge-api:${jei_version}")) diff --git a/gradle.properties b/gradle.properties index 60d062562..d8886e71e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,6 +18,7 @@ jupiter_version=5.8.2 the_one_probe_version=6.2.1 mekanism_version=10.3.2.homebaked curios_version=5.1.1.0 +cosmetic_armor_reworked_version=v1 kotlin_for_forge_version=3.1.0 kotlin_version=1.6.10 diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/ClientEventHandler.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/ClientEventHandler.kt index a0b81e22d..a67c4cc2a 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/ClientEventHandler.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/ClientEventHandler.kt @@ -12,6 +12,7 @@ import ru.dbotthepony.mc.otm.client.render.Widgets18 import ru.dbotthepony.mc.otm.client.screen.ExoSuitInventoryScreen import ru.dbotthepony.mc.otm.client.screen.panels.LargeRectangleButtonPanel import ru.dbotthepony.mc.otm.client.screen.panels.Panel2Widget +import ru.dbotthepony.mc.otm.compat.cos.isCosmeticArmorScreen import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.registry.AndroidFeatures @@ -52,7 +53,7 @@ fun onPostScreenInit(event: ScreenEvent.Init.Post) { val player = minecraft.player?.matteryPlayer ?: return val eventScreen = event.screen - val screen = if (eventScreen is AbstractContainerScreen<*> && eventScreen.menu is InventoryMenu) eventScreen else return + val screen = if (eventScreen is AbstractContainerScreen<*> && (eventScreen.menu is InventoryMenu || eventScreen.isCosmeticArmorScreen)) eventScreen else return if (player.hasExoSuit) { val widget = Panel2Widget(LargeRectangleButtonPanel(screen, null, diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/ExoSuitInventoryScreen.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/ExoSuitInventoryScreen.kt index 206eded95..6f2414fcd 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/ExoSuitInventoryScreen.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/ExoSuitInventoryScreen.kt @@ -12,12 +12,14 @@ import ru.dbotthepony.mc.otm.client.render.element import ru.dbotthepony.mc.otm.client.screen.panels.* import ru.dbotthepony.mc.otm.client.setMousePos import ru.dbotthepony.mc.otm.client.shouldOpenVanillaInventory +import ru.dbotthepony.mc.otm.compat.cos.CosmeticToggleButton import ru.dbotthepony.mc.otm.compat.curios.curiosSlots import ru.dbotthepony.mc.otm.core.maxScrollDivision import ru.dbotthepony.mc.otm.menu.ExoSuitInventoryMenu import ru.dbotthepony.mc.otm.network.ExoSuitMenuOpen import ru.dbotthepony.mc.otm.network.MatteryPlayerNetworkChannel import yalter.mousetweaks.api.MouseTweaksDisableWheelTweak +import kotlin.math.cos @MouseTweaksDisableWheelTweak class ExoSuitInventoryScreen(menu: ExoSuitInventoryMenu) : MatteryScreen(menu, TranslatableComponent("otm.gui.exosuit")) { @@ -103,8 +105,22 @@ class ExoSuitInventoryScreen(menu: ExoSuitInventoryMenu) : MatteryScreen( width: Float = 0f, height: Float = 20f, val defaultText: Component = TextComponent("") -) : MinecraftWidgetPanel(screen, parent, x, y, width, height) { +) : Widget2Panel(screen, parent, x, y, width, height) { override fun makeNew(): EditBox { return object : EditBox(font, 0, 0, width.toInt(), height.toInt().coerceAtMost(20), defaultText) { override fun isHoveredOrFocused(): Boolean { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/EditablePanel.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/EditablePanel.kt index 41dfd4021..ddd56eacc 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/EditablePanel.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/EditablePanel.kt @@ -41,7 +41,7 @@ enum class DockResizeMode(val changeWidth: Boolean, val changeHeight: Boolean) { ALL(true, true), NONE(false, false), WIDTH(true, false), HEIGHT(false, true) } -interface ISlotPanel { +interface ISlotPanel { val slot: S } @@ -464,13 +464,10 @@ open class EditablePanel @JvmOverloads constructor( return } + val old = field field = value - if (value) { - onHovered() - } else { - onUnHovered() - } + onHoverUpdate(old, value) } fun unsetHovered() { @@ -481,6 +478,14 @@ open class EditablePanel @JvmOverloads constructor( } } + protected open fun onHoverUpdate(oldHover: Boolean, newHover: Boolean) { + if (newHover) { + onHovered() + } else { + onUnHovered() + } + } + protected open fun onHovered() {} protected open fun onUnHovered() {} @@ -1468,6 +1473,7 @@ open class EditablePanel @JvmOverloads constructor( var isRemoved = false private set + protected open fun beforeRemoved() {} protected open fun onRemoved() {} fun remove() { @@ -1476,6 +1482,7 @@ open class EditablePanel @JvmOverloads constructor( } killFocus() + beforeRemoved() if (screen is MatteryScreen<*>) { screen.removePanel(this as EditablePanel>) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/FoldableSlotPanel.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/FoldableSlotPanel.kt new file mode 100644 index 000000000..5c3470f36 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/FoldableSlotPanel.kt @@ -0,0 +1,103 @@ +package ru.dbotthepony.mc.otm.client.screen.panels + +import net.minecraft.world.inventory.Slot +import ru.dbotthepony.mc.otm.client.screen.MatteryScreen + +open class FoldableSlotPanel, out T : Slot> @JvmOverloads constructor( + screen: S, + parent: EditablePanel<*>?, + val mainSlot: SlotPanel, + val extraSlots: Collection>, + x: Float = 0f, + y: Float = 0f, + width: Float = AbstractSlotPanel.SIZE, + height: Float = AbstractSlotPanel.SIZE, +) : EditablePanel(screen, parent, x, y, width, height), ISlotPanel { + init { + mainSlot.parent = this + mainSlot.dock = Dock.FILL + } + + open inner class HoverPanel : BackgroundPanel( + screen, + null, + this@FoldableSlotPanel.absoluteX, + this@FoldableSlotPanel.absoluteY, + width = extraSlots.stream().mapToDouble { (it.width + it.dockMargin.right + it.dockMargin.left).toDouble() }.sum().toFloat() + mainSlot.width + mainSlot.dockMargin.left + mainSlot.dockMargin.right, + height = extraSlots.stream().mapToDouble { (it.height + it.dockMargin.top + it.dockMargin.bottom).toDouble() }.max().orElse(0.0).toFloat().coerceAtLeast(mainSlot.height + mainSlot.dockMargin.top + mainSlot.dockMargin.bottom), + ) { + init { + x -= dockPadding.left + y -= dockPadding.top + + width += dockPadding.left + dockPadding.right + height += dockPadding.top + dockPadding.bottom + + mainSlot.parent = this + mainSlot.dock = Dock.LEFT + + for (slot in extraSlots) { + slot.parent = this + slot.dock = Dock.LEFT + } + + screen.addPanel(this) + } + + override fun beforeRemoved() { + mainSlot.parent = this@FoldableSlotPanel + mainSlot.dock = Dock.FILL + + for (slot in extraSlots) { + slot.parent = null + } + + this@FoldableSlotPanel.hoverPanel = null + } + + override fun onUnHovered() { + remove() + } + + override fun tick() { + super.tick() + + x = this@FoldableSlotPanel.absoluteX - dockPadding.left + y = this@FoldableSlotPanel.absoluteY - dockPadding.top + } + } + + override val slot: T + get() = mainSlot.slot + + var hoveringSince: Long? = null + protected set + var hoverPanel: HoverPanel? = null + protected set + + override fun onHovered() { + hoveringSince = System.nanoTime() + } + + override fun onUnHovered() { + hoveringSince = null + } + + override fun beforeRemoved() { + hoverPanel?.remove() + } + + override fun tick() { + super.tick() + + val hoveringSince = hoveringSince + + if (hoveringSince != null && System.nanoTime() - hoveringSince >= MAX_HOVER_TIME && hoverPanel == null && extraSlots.isNotEmpty()) { + hoverPanel = HoverPanel() + } + } + + companion object { + const val MAX_HOVER_TIME = 1_000_000_000 + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/SlotPanel.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/SlotPanel.kt index 24f709207..983fd3f59 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/SlotPanel.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/SlotPanel.kt @@ -19,7 +19,7 @@ import ru.dbotthepony.mc.otm.client.render.setDrawColor import ru.dbotthepony.mc.otm.client.screen.MatteryScreen import javax.annotation.Nonnull -open class SlotPanel, T : Slot> @JvmOverloads constructor( +open class SlotPanel, out T : Slot> @JvmOverloads constructor( screen: S, parent: EditablePanel<*>?, final override val slot: T, diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/MinecraftWidgetPanel.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/Widget2Panel.kt similarity index 93% rename from src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/MinecraftWidgetPanel.kt rename to src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/Widget2Panel.kt index 9b44556e6..6b5b6fd1e 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/MinecraftWidgetPanel.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/Widget2Panel.kt @@ -4,11 +4,9 @@ import com.mojang.blaze3d.systems.RenderSystem import com.mojang.blaze3d.vertex.PoseStack import net.minecraft.client.gui.components.AbstractWidget import net.minecraft.client.gui.screens.Screen -import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen import org.lwjgl.opengl.GL11 -import ru.dbotthepony.mc.otm.client.screen.MatteryScreen -abstract class MinecraftWidgetPanel( +abstract class Widget2Panel( screen: S, parent: EditablePanel<*>?, x: Float, @@ -114,4 +112,4 @@ abstract class MinecraftWidgetPanel( val (x1, y1) = screenToLocal(x, y) return getOrCreateWidget().mouseReleased(x1.toDouble(), y1.toDouble(), button) } -} \ No newline at end of file +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/compat/cos/CosmeticArmorCompat.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/compat/cos/CosmeticArmorCompat.kt new file mode 100644 index 000000000..5ea3adeb5 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/compat/cos/CosmeticArmorCompat.kt @@ -0,0 +1,142 @@ +package ru.dbotthepony.mc.otm.compat.cos + +import com.mojang.blaze3d.platform.InputConstants +import com.mojang.blaze3d.vertex.PoseStack +import lain.mods.cos.impl.ModObjects +import lain.mods.cos.impl.client.gui.GuiCosArmorInventory +import lain.mods.cos.impl.client.gui.GuiCosArmorToggleButton +import lain.mods.cos.impl.network.packet.PacketSetSkinArmor +import net.minecraft.client.gui.screens.Screen +import net.minecraft.resources.ResourceLocation +import net.minecraft.server.level.ServerPlayer +import net.minecraft.world.Container +import net.minecraft.world.entity.EquipmentSlot +import net.minecraft.world.entity.player.Player +import net.minecraft.world.inventory.InventoryMenu +import net.minecraft.world.inventory.Slot +import net.minecraft.world.item.ItemStack +import net.minecraftforge.fml.ModList +import ru.dbotthepony.mc.otm.client.minecraft +import ru.dbotthepony.mc.otm.client.render.SkinElement +import ru.dbotthepony.mc.otm.client.render.element +import ru.dbotthepony.mc.otm.client.screen.panels.AbstractSlotPanel +import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel +import ru.dbotthepony.mc.otm.client.screen.panels.RectangleButtonPanel +import ru.dbotthepony.mc.otm.menu.MatterySlot + +val isCosmeticArmorLoaded by lazy { + ModList.get().isLoaded("cosmeticarmorreworked") +} + +val Player.cosmeticArmorSlots: Map? get() { + if (!isCosmeticArmorLoaded) { + return null + } + + return cosmeticArmorSlotsImpl +} + +private class CosmeticSlot(container: Container, private val slot: EquipmentSlot, private val player: Player) : MatterySlot(container, when (slot) { + EquipmentSlot.FEET -> 0 + EquipmentSlot.LEGS -> 1 + EquipmentSlot.CHEST -> 2 + EquipmentSlot.HEAD -> 3 + else -> throw IllegalArgumentException() +}) { + override fun mayPlace(itemStack: ItemStack): Boolean { + return super.mayPlace(itemStack) && itemStack.canEquip(slot, player) + } + + override fun getMaxStackSize(): Int { + return 1 + } + + override fun getNoItemIcon(): com.mojang.datafixers.util.Pair? { + return when (slot) { + EquipmentSlot.FEET -> com.mojang.datafixers.util.Pair.of(InventoryMenu.BLOCK_ATLAS, InventoryMenu.EMPTY_ARMOR_SLOT_BOOTS) + EquipmentSlot.LEGS -> com.mojang.datafixers.util.Pair.of(InventoryMenu.BLOCK_ATLAS, InventoryMenu.EMPTY_ARMOR_SLOT_LEGGINGS) + EquipmentSlot.CHEST -> com.mojang.datafixers.util.Pair.of(InventoryMenu.BLOCK_ATLAS, InventoryMenu.EMPTY_ARMOR_SLOT_CHESTPLATE) + EquipmentSlot.HEAD -> com.mojang.datafixers.util.Pair.of(InventoryMenu.BLOCK_ATLAS, InventoryMenu.EMPTY_ARMOR_SLOT_HELMET) + else -> null + } + } +} + +private val Player.cosmeticArmorSlotsImpl: Map? get() { + val manager = ModObjects.invMan ?: return null + + val container = if (this !is ServerPlayer) { + manager.getCosArmorInventoryClient(uuid) + } else { + manager.getCosArmorInventory(uuid) + } + + return mapOf( + EquipmentSlot.HEAD to CosmeticSlot(container, EquipmentSlot.HEAD, this), + EquipmentSlot.CHEST to CosmeticSlot(container, EquipmentSlot.CHEST, this), + EquipmentSlot.LEGS to CosmeticSlot(container, EquipmentSlot.LEGS, this), + EquipmentSlot.FEET to CosmeticSlot(container, EquipmentSlot.FEET, this), + ) +} + +val Screen.isCosmeticArmorScreen: Boolean get() { + if (!isCosmeticArmorLoaded) { + return false + } + + return isCosmeticArmorScreenImpl +} + +private val Screen.isCosmeticArmorScreenImpl: Boolean get() { + return this is GuiCosArmorInventory +} + +class CosmeticToggleButton( + screen: S, + parent: EditablePanel? = null, + index: EquipmentSlot, + x: Float = 0f, + y: Float = 0f, + width: Float = 5f, + height: Float = 5f +) : RectangleButtonPanel(screen, parent, x, y, width, height) { + override val PRESSED: SkinElement + get() = BUTTON_ACTIVE + override val HOVERED: SkinElement + get() = BUTTON_ACTIVE + override val IDLE: SkinElement + get() = BUTTON_INACTIVE + override val DISABLED: SkinElement + get() = BUTTON_INACTIVE + + private val index = when (index) { + EquipmentSlot.FEET -> 0 + EquipmentSlot.LEGS -> 1 + EquipmentSlot.CHEST -> 2 + EquipmentSlot.HEAD -> 3 + else -> throw IllegalArgumentException() + } + + override fun onClick(clickButton: Int) { + if (clickButton == InputConstants.MOUSE_BUTTON_LEFT) { + val inv = ModObjects.invMan.getCosArmorInventoryClient(minecraft.player?.uuid ?: throw ConcurrentModificationException()) + inv.setSkinArmor(index, !inv.isSkinArmor(index)) + ModObjects.network.sendToServer(PacketSetSkinArmor(index, inv.isSkinArmor(index))) + } + } + + override fun innerRender(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float) { + val inv = ModObjects.invMan.getCosArmorInventoryClient(minecraft.player?.uuid ?: throw ConcurrentModificationException()) + + if (inv.isSkinArmor(index)) { + BUTTON_ACTIVE.render(stack, x, y, width, height) + } else { + BUTTON_INACTIVE.render(stack, x, y, width, height) + } + } + + companion object { + val BUTTON_INACTIVE = GuiCosArmorInventory.TEXTURE.element(0f, 176f, 5f, 5f) + val BUTTON_ACTIVE = GuiCosArmorInventory.TEXTURE.element(5f, 176f, 5f, 5f) + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/ExoSuitInventoryMenu.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/ExoSuitInventoryMenu.kt index 1111dcdc5..5cc6ab842 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/ExoSuitInventoryMenu.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/ExoSuitInventoryMenu.kt @@ -10,6 +10,7 @@ import net.minecraft.world.entity.player.Player import net.minecraft.world.inventory.* import net.minecraft.world.item.ItemStack import ru.dbotthepony.mc.otm.capability.MatteryPlayerCapability +import ru.dbotthepony.mc.otm.compat.cos.cosmeticArmorSlots import ru.dbotthepony.mc.otm.compat.curios.curiosSlots import ru.dbotthepony.mc.otm.container.iterator import ru.dbotthepony.mc.otm.network.ExoSuitCarriedPacket @@ -85,6 +86,8 @@ class ExoSuitInventoryMenu(val capability: MatteryPlayerCapability) : MatteryMen } } + val cosmeticArmorSlots: Map = ply.cosmeticArmorSlots?.also { it.values.forEach(this::addSlot) } ?: mapOf() + override fun slotsChanged(container: Container) { super.slotsChanged(container)