From 56a6a3c8214281261d9b8f81dee37b78a2795b00 Mon Sep 17 00:00:00 2001 From: Bixilon Date: Tue, 17 May 2022 18:03:21 +0200 Subject: [PATCH] proper enchanting screen --- .../minosoft/data/container/Container.kt | 1 + .../container/types/EnchantingContainer.kt | 19 +++++++++++- .../data/registries/ResourceLocation.kt | 9 +++++- .../input/button/AbstractButtonElement.kt | 2 +- .../enchanting/EnchantingContainerScreen.kt | 26 ++++++++++++++-- .../enchanting/EnchantmentButtonElement.kt | 30 +++++++++++++++++-- .../c2s/play/container/ContainerButtonC2SP.kt | 4 +-- .../play/container/ContainerPropertiesS2CP.kt | 6 ++-- .../s2c/play/container/OpenContainerS2CP.kt | 2 +- 9 files changed, 85 insertions(+), 14 deletions(-) diff --git a/src/main/java/de/bixilon/minosoft/data/container/Container.kt b/src/main/java/de/bixilon/minosoft/data/container/Container.kt index 153342bce..6abd70803 100644 --- a/src/main/java/de/bixilon/minosoft/data/container/Container.kt +++ b/src/main/java/de/bixilon/minosoft/data/container/Container.kt @@ -43,6 +43,7 @@ open class Container( @Deprecated("Should not be accessed directly") val slots: MutableMap by watchedMap(mutableMapOf()) val lock = SimpleLock() + var propertiesRevision by watched(0L) var revision by watched(0L) var serverRevision = 0 private var lastActionId = 0 diff --git a/src/main/java/de/bixilon/minosoft/data/container/types/EnchantingContainer.kt b/src/main/java/de/bixilon/minosoft/data/container/types/EnchantingContainer.kt index f0e0968c0..37b7949bf 100644 --- a/src/main/java/de/bixilon/minosoft/data/container/types/EnchantingContainer.kt +++ b/src/main/java/de/bixilon/minosoft/data/container/types/EnchantingContainer.kt @@ -22,6 +22,7 @@ import de.bixilon.minosoft.data.container.slots.SlotType import de.bixilon.minosoft.data.container.stack.ItemStack import de.bixilon.minosoft.data.registries.MultiResourceLocationAble import de.bixilon.minosoft.data.registries.ResourceLocation +import de.bixilon.minosoft.data.registries.enchantment.Enchantment import de.bixilon.minosoft.data.registries.items.DefaultItems import de.bixilon.minosoft.data.registries.other.containers.ContainerFactory import de.bixilon.minosoft.data.registries.other.containers.ContainerType @@ -29,8 +30,13 @@ import de.bixilon.minosoft.data.text.ChatComponent import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection import de.bixilon.minosoft.util.KUtil.toResourceLocation -class EnchantingContainer(connection: PlayConnection, type: ContainerType, title: ChatComponent?) : InventorySynchronizedContainer(connection, type, title, (ENCHANTING_SLOTS + 1)..(ENCHANTING_SLOTS + PlayerInventory.MAIN_SLOTS)) { +class EnchantingContainer(connection: PlayConnection, type: ContainerType, title: ChatComponent?) : InventorySynchronizedContainer(connection, type, title, ENCHANTING_SLOTS until (ENCHANTING_SLOTS + PlayerInventory.MAIN_SLOTS)) { override val sections: Array = arrayOf(0 until ENCHANTING_SLOTS, ENCHANTING_SLOTS until ENCHANTING_SLOTS + PlayerInventory.MAIN_SLOTS) + val costs = IntArray(ENCHANTING_OPTIONS) { -1 } + val enchantments: Array = arrayOfNulls(ENCHANTING_OPTIONS) + var enchantmentLevels = IntArray(ENCHANTING_OPTIONS) { -1 } + var seed = -1 + private set override fun getSlotType(slotId: Int): SlotType? { return when (slotId) { @@ -58,6 +64,15 @@ class EnchantingContainer(connection: PlayConnection, type: ContainerType, title return 1 + ENCHANTING_SLOTS + PlayerInventory.MAIN_SLOTS_PER_ROW * 3 + slot.ordinal } + override fun readProperty(property: Int, value: Int) { + when (property) { + 0, 1, 2 -> costs[property] = value + 3 -> seed = value + 4, 5, 6 -> enchantments[property - 4] = connection.registries.enchantmentRegistry.getOrNull(value) + 7, 8, 9 -> enchantmentLevels[property - 7] = value + } + } + private object LapislazuliSlot : SlotType { override fun canPut(container: Container, slot: Int, stack: ItemStack): Boolean { return stack.item.item.resourceLocation == DefaultItems.LAPISLAZULI @@ -68,7 +83,9 @@ class EnchantingContainer(connection: PlayConnection, type: ContainerType, title companion object : ContainerFactory, MultiResourceLocationAble { override val RESOURCE_LOCATION: ResourceLocation = "minecraft:enchantment".toResourceLocation() override val ALIASES: Set = setOf("minecraft:enchanting_table".toResourceLocation(), "EnchantTable".toResourceLocation()) + const val LAPISLAZULI_SLOT = 1 const val ENCHANTING_SLOTS = 2 + const val ENCHANTING_OPTIONS = 3 override fun build(connection: PlayConnection, type: ContainerType, title: ChatComponent?): EnchantingContainer { return EnchantingContainer(connection, type, title) diff --git a/src/main/java/de/bixilon/minosoft/data/registries/ResourceLocation.kt b/src/main/java/de/bixilon/minosoft/data/registries/ResourceLocation.kt index 6fa812fad..c0b2ceab9 100644 --- a/src/main/java/de/bixilon/minosoft/data/registries/ResourceLocation.kt +++ b/src/main/java/de/bixilon/minosoft/data/registries/ResourceLocation.kt @@ -1,6 +1,6 @@ /* * Minosoft - * Copyright (C) 2020 Moritz Zwerger + * Copyright (C) 2020-2022 Moritz Zwerger * * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. * @@ -54,6 +54,13 @@ open class ResourceLocation( return full } + fun toMinifiedString(): String { + if (namespace == ProtocolDefinition.DEFAULT_NAMESPACE) { + return path + } + return toString() + } + companion object { val String.namespace: String get() { diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/input/button/AbstractButtonElement.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/input/button/AbstractButtonElement.kt index d7013a5eb..7bdf38c56 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/input/button/AbstractButtonElement.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/input/button/AbstractButtonElement.kt @@ -37,7 +37,7 @@ abstract class AbstractButtonElement( text: Any, disabled: Boolean = false, ) : Element(guiRenderer) { - protected val textElement = TextElement(guiRenderer, text, background = false).apply { parent = this@AbstractButtonElement } + protected val textElement = TextElement(guiRenderer, text, background = false, parent = this) protected abstract val disabledAtlas: AtlasElement? protected abstract val normalAtlas: AtlasElement? protected abstract val hoveredAtlas: AtlasElement? diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/gui/screen/container/enchanting/EnchantingContainerScreen.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/gui/screen/container/enchanting/EnchantingContainerScreen.kt index 82e223edf..9be451734 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/gui/screen/container/enchanting/EnchantingContainerScreen.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/gui/screen/container/enchanting/EnchantingContainerScreen.kt @@ -14,6 +14,8 @@ package de.bixilon.minosoft.gui.rendering.gui.gui.screen.container.enchanting import de.bixilon.kotlinglm.vec2.Vec2i +import de.bixilon.kutil.watcher.DataWatcher.Companion.observe +import de.bixilon.minosoft.data.abilities.Gamemodes import de.bixilon.minosoft.data.container.types.EnchantingContainer import de.bixilon.minosoft.gui.rendering.gui.GUIRenderer import de.bixilon.minosoft.gui.rendering.gui.elements.Element @@ -27,8 +29,14 @@ import de.bixilon.minosoft.util.KUtil.toResourceLocation import kotlin.reflect.KClass class EnchantingContainerScreen(guiRenderer: GUIRenderer, container: EnchantingContainer) : LabeledContainerScreen(guiRenderer, container, guiRenderer.atlasManager["minecraft:enchanting_container".toResourceLocation()]) { - private val cards: Array = Array(CARDS) { EnchantmentButtonElement(guiRenderer, this, guiRenderer.atlasManager["minecraft:level_requirement_${it}"], guiRenderer.atlasManager["minecraft:level_requirement_${it}_disabled"]) } - private val cardAreas = arrayOf(atlasElement?.areas?.get("card_0"), atlasElement?.areas?.get("card_1"), atlasElement?.areas?.get("card_2")) + private val cards: Array = Array(EnchantingContainer.ENCHANTING_OPTIONS) { EnchantmentButtonElement(guiRenderer, this, guiRenderer.atlasManager["minecraft:level_requirement_${it}"], guiRenderer.atlasManager["minecraft:level_requirement_${it}_disabled"], it) } + private val cardAreas = Array(EnchantingContainer.ENCHANTING_OPTIONS) { atlasElement?.areas?.get("card_$it") } + + + init { + container::propertiesRevision.observe(this) { forceApply() } + container::revision.observe(this) { forceApply() } + } override fun forceRenderContainerScreen(offset: Vec2i, consumer: GUIVertexConsumer, options: GUIVertexOptions?) { super.forceRenderContainerScreen(offset, consumer, options) @@ -56,9 +64,21 @@ class EnchantingContainerScreen(guiRenderer: GUIRenderer, container: EnchantingC return super.getContainerAt(position) } + override fun forceSilentApply() { + super.forceSilentApply() + var lapisCount = container[EnchantingContainer.LAPISLAZULI_SLOT]?.item?._count ?: 0 + if (guiRenderer.connection.player.gamemode == Gamemodes.CREATIVE) { + lapisCount = 64 + } + + for (index in 0 until EnchantingContainer.ENCHANTING_OPTIONS) { + val card = cards[index] + card.update(lapisCount < index + 1, container.costs[index], container.enchantments[index], container.enchantmentLevels[index]) + } + } + companion object : ContainerGUIFactory { override val clazz: KClass = EnchantingContainer::class - const val CARDS = 3 override fun build(guiRenderer: GUIRenderer, container: EnchantingContainer): EnchantingContainerScreen { return EnchantingContainerScreen(guiRenderer, container) diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/gui/screen/container/enchanting/EnchantmentButtonElement.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/gui/screen/container/enchanting/EnchantmentButtonElement.kt index 3611b5c4e..dd3608471 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/gui/screen/container/enchanting/EnchantmentButtonElement.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/gui/screen/container/enchanting/EnchantmentButtonElement.kt @@ -14,31 +14,46 @@ package de.bixilon.minosoft.gui.rendering.gui.gui.screen.container.enchanting import de.bixilon.kotlinglm.vec2.Vec2i +import de.bixilon.minosoft.data.registries.enchantment.Enchantment +import de.bixilon.minosoft.data.text.ChatColors +import de.bixilon.minosoft.data.text.ChatComponent +import de.bixilon.minosoft.data.text.TextComponent +import de.bixilon.minosoft.gui.rendering.RenderConstants import de.bixilon.minosoft.gui.rendering.gui.GUIRenderer import de.bixilon.minosoft.gui.rendering.gui.atlas.AtlasElement import de.bixilon.minosoft.gui.rendering.gui.elements.HorizontalAlignments import de.bixilon.minosoft.gui.rendering.gui.elements.HorizontalAlignments.Companion.getOffset +import de.bixilon.minosoft.gui.rendering.gui.elements.VerticalAlignments +import de.bixilon.minosoft.gui.rendering.gui.elements.VerticalAlignments.Companion.getOffset import de.bixilon.minosoft.gui.rendering.gui.elements.input.button.AbstractButtonElement import de.bixilon.minosoft.gui.rendering.gui.elements.primitive.AtlasImageElement +import de.bixilon.minosoft.gui.rendering.gui.elements.text.TextElement import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions +import de.bixilon.minosoft.protocol.packets.c2s.play.container.ContainerButtonC2SP class EnchantmentButtonElement( guiRenderer: GUIRenderer, val container: EnchantingContainerScreen, val levelAtlas: AtlasElement?, val disabledLevelAtlas: AtlasElement?, -) : AbstractButtonElement(guiRenderer, "< text >", true) { + val index: Int, +) : AbstractButtonElement(guiRenderer, "", true) { override val disabledAtlas = guiRenderer.atlasManager["enchanting_table_card_disabled"] override val normalAtlas = guiRenderer.atlasManager["enchanting_table_card_normal"] override val hoveredAtlas = guiRenderer.atlasManager["enchanting_table_card_hovered"] + private val levelText = TextElement(guiRenderer, ChatComponent.EMPTY, background = false) override fun forceRender(offset: Vec2i, consumer: GUIVertexConsumer, options: GUIVertexOptions?) { super.forceRender(offset, consumer, options) val level = AtlasImageElement(guiRenderer, if (disabled) disabledLevelAtlas else levelAtlas) val size = size - level.render(offset + Vec2i(5, HorizontalAlignments.CENTER.getOffset(size.y, level.size.y)), consumer, options) + level.render(offset + Vec2i(5, VerticalAlignments.CENTER.getOffset(size.y, level.size.y)), consumer, options) + + if (!_disabled) { + levelText.render(offset + Vec2i(HorizontalAlignments.RIGHT.getOffset(size.x, levelText.size.x) - 3, VerticalAlignments.BOTTOM.getOffset(size.y, levelText.size.y) - 2), consumer, options) + } } init { @@ -48,6 +63,15 @@ class EnchantmentButtonElement( } override fun submit() { - TODO("Not yet implemented") + container.container.id?.let { guiRenderer.connection.network.send(ContainerButtonC2SP(it, index)) } + } + + fun update(disabled: Boolean, cost: Int, enchantment: Enchantment?, level: Int) { + _disabled = disabled || cost <= 0 + levelText.text = TextComponent(cost).color(RenderConstants.EXPERIENCE_BAR_LEVEL_COLOR) + textElement._chatComponent = if (enchantment == null) ChatComponent.EMPTY else TextComponent(enchantment.resourceLocation.toMinifiedString() + " $level").color(ChatColors.BLUE) + textElement.forceSilentApply() + + forceSilentApply() } } diff --git a/src/main/java/de/bixilon/minosoft/protocol/packets/c2s/play/container/ContainerButtonC2SP.kt b/src/main/java/de/bixilon/minosoft/protocol/packets/c2s/play/container/ContainerButtonC2SP.kt index 722b04a6c..4600fca1c 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/packets/c2s/play/container/ContainerButtonC2SP.kt +++ b/src/main/java/de/bixilon/minosoft/protocol/packets/c2s/play/container/ContainerButtonC2SP.kt @@ -21,8 +21,8 @@ import de.bixilon.minosoft.util.logging.LogMessageType @LoadPacket class ContainerButtonC2SP( - private val containerId: Byte, - private val buttonId: Byte, // up, middle, bottom (0, 1, 2); in later versions: lectern page, etc + private val containerId: Int, + private val buttonId: Int, ) : PlayC2SPacket { override fun write(buffer: PlayOutByteBuffer) { diff --git a/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/container/ContainerPropertiesS2CP.kt b/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/container/ContainerPropertiesS2CP.kt index c95026204..dbebe43fe 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/container/ContainerPropertiesS2CP.kt +++ b/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/container/ContainerPropertiesS2CP.kt @@ -20,14 +20,16 @@ import de.bixilon.minosoft.util.logging.Log import de.bixilon.minosoft.util.logging.LogLevels import de.bixilon.minosoft.util.logging.LogMessageType -@LoadPacket +@LoadPacket(threadSafe = false) class ContainerPropertiesS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket { val containerId = buffer.readUnsignedByte() val property = buffer.readUnsignedShort() val value = buffer.readUnsignedShort() override fun handle(connection: PlayConnection) { - connection.player.containers[containerId]?.readProperty(property, value) + val container = connection.player.containers[containerId] ?: return + container.readProperty(property, value) + container.propertiesRevision++ } override fun log(reducedLog: Boolean) { diff --git a/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/container/OpenContainerS2CP.kt b/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/container/OpenContainerS2CP.kt index 8345907b1..0b5560563 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/container/OpenContainerS2CP.kt +++ b/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/container/OpenContainerS2CP.kt @@ -29,7 +29,7 @@ import de.bixilon.minosoft.util.logging.Log import de.bixilon.minosoft.util.logging.LogLevels import de.bixilon.minosoft.util.logging.LogMessageType -@LoadPacket +@LoadPacket(threadSafe = false) class OpenContainerS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket { val containerId = if (buffer.versionId <= V_1_14) { // ToDo: This is completely guessed, it has changed between 1.13 and 1.14, same as #L38 buffer.readUnsignedByte()