diff options
| author | Linnea Gräf <nea@nea.moe> | 2024-08-28 19:04:24 +0200 |
|---|---|---|
| committer | Linnea Gräf <nea@nea.moe> | 2024-08-28 19:04:24 +0200 |
| commit | d2f240ff0ca0d27f417f837e706c781a98c31311 (patch) | |
| tree | 0db7aff6cc14deaf36eed83889d59fd6b3a6f599 /src/main/kotlin/features/inventory | |
| parent | a6906308163aa3b2d18fa1dc1aa71ac9bbcc83ab (diff) | |
| download | Firmament-d2f240ff0ca0d27f417f837e706c781a98c31311.tar.gz Firmament-d2f240ff0ca0d27f417f837e706c781a98c31311.tar.bz2 Firmament-d2f240ff0ca0d27f417f837e706c781a98c31311.zip | |
Refactor source layout
Introduce compat source sets and move all kotlin sources to the main directory
[no changelog]
Diffstat (limited to 'src/main/kotlin/features/inventory')
17 files changed, 1739 insertions, 0 deletions
diff --git a/src/main/kotlin/features/inventory/CraftingOverlay.kt b/src/main/kotlin/features/inventory/CraftingOverlay.kt new file mode 100644 index 0000000..031ef78 --- /dev/null +++ b/src/main/kotlin/features/inventory/CraftingOverlay.kt @@ -0,0 +1,66 @@ + + +package moe.nea.firmament.features.inventory + +import net.minecraft.client.gui.screen.ingame.GenericContainerScreen +import net.minecraft.item.ItemStack +import net.minecraft.util.Formatting +import moe.nea.firmament.annotations.Subscribe +import moe.nea.firmament.events.SlotRenderEvents +import moe.nea.firmament.features.FirmamentFeature +import moe.nea.firmament.rei.FirmamentReiPlugin.Companion.asItemEntry +import moe.nea.firmament.rei.SBItemEntryDefinition +import moe.nea.firmament.rei.recipes.SBCraftingRecipe +import moe.nea.firmament.util.MC + +object CraftingOverlay : FirmamentFeature { + + private var screen: GenericContainerScreen? = null + private var recipe: SBCraftingRecipe? = null + private val craftingOverlayIndices = listOf( + 10, 11, 12, + 19, 20, 21, + 28, 29, 30, + ) + + + fun setOverlay(screen: GenericContainerScreen, recipe: SBCraftingRecipe) { + this.screen = screen + this.recipe = recipe + } + + override val identifier: String + get() = "crafting-overlay" + + @Subscribe + fun onSlotRender(event: SlotRenderEvents.After) { + val slot = event.slot + val recipe = this.recipe ?: return + if (slot.inventory != screen?.screenHandler?.inventory) return + val recipeIndex = craftingOverlayIndices.indexOf(slot.index) + if (recipeIndex < 0) return + val expectedItem = recipe.neuRecipe.inputs[recipeIndex] + val actualStack = slot.stack ?: ItemStack.EMPTY!! + val actualEntry = SBItemEntryDefinition.getEntry(actualStack).value + if ((actualEntry.skyblockId.neuItem != expectedItem.itemId || actualEntry.getStackSize() < expectedItem.amount) && expectedItem.amount.toInt() != 0) { + event.context.fill( + event.slot.x, + event.slot.y, + event.slot.x + 16, + event.slot.y + 16, + 0x80FF0000.toInt() + ) + } + if (!slot.hasStack()) { + val itemStack = SBItemEntryDefinition.getEntry(expectedItem).asItemEntry().value + event.context.drawItem(itemStack, event.slot.x, event.slot.y) + event.context.drawItemInSlot( + MC.font, + itemStack, + event.slot.x, + event.slot.y, + "${Formatting.RED}${expectedItem.amount.toInt()}" + ) + } + } +} diff --git a/src/main/kotlin/features/inventory/ItemRarityCosmetics.kt b/src/main/kotlin/features/inventory/ItemRarityCosmetics.kt new file mode 100644 index 0000000..566a813 --- /dev/null +++ b/src/main/kotlin/features/inventory/ItemRarityCosmetics.kt @@ -0,0 +1,85 @@ + + +package moe.nea.firmament.features.inventory + +import java.awt.Color +import net.minecraft.client.gui.DrawContext +import net.minecraft.item.ItemStack +import net.minecraft.util.Formatting +import net.minecraft.util.Identifier +import moe.nea.firmament.annotations.Subscribe +import moe.nea.firmament.events.HotbarItemRenderEvent +import moe.nea.firmament.events.SlotRenderEvents +import moe.nea.firmament.features.FirmamentFeature +import moe.nea.firmament.gui.config.ManagedConfig +import moe.nea.firmament.util.MC +import moe.nea.firmament.util.item.loreAccordingToNbt +import moe.nea.firmament.util.lastNotNullOfOrNull +import moe.nea.firmament.util.memoize +import moe.nea.firmament.util.memoizeIdentity +import moe.nea.firmament.util.unformattedString + +object ItemRarityCosmetics : FirmamentFeature { + override val identifier: String + get() = "item-rarity-cosmetics" + + object TConfig : ManagedConfig(identifier) { + val showItemRarityBackground by toggle("background") { false } + val showItemRarityInHotbar by toggle("background-hotbar") { false } + } + + override val config: ManagedConfig + get() = TConfig + + private val rarityToColor = mapOf( + "UNCOMMON" to Formatting.GREEN, + "COMMON" to Formatting.WHITE, + "RARE" to Formatting.DARK_BLUE, + "EPIC" to Formatting.DARK_PURPLE, + "LEGENDARY" to Formatting.GOLD, + "LEGENJERRY" to Formatting.GOLD, + "MYTHIC" to Formatting.LIGHT_PURPLE, + "DIVINE" to Formatting.BLUE, + "SPECIAL" to Formatting.DARK_RED, + "SUPREME" to Formatting.DARK_RED, + ).mapValues { + val c = Color(it.value.colorValue!!) + Triple(c.red / 255F, c.green / 255F, c.blue / 255F) + } + + private fun getSkyblockRarity0(itemStack: ItemStack): Triple<Float, Float, Float>? { + return itemStack.loreAccordingToNbt.lastNotNullOfOrNull { + val entry = it.unformattedString + rarityToColor.entries.find { (k, v) -> k in entry }?.value + } + } + + val getSkyblockRarity = ::getSkyblockRarity0.memoizeIdentity(100) + + + fun drawItemStackRarity(drawContext: DrawContext, x: Int, y: Int, item: ItemStack) { + val (r, g, b) = getSkyblockRarity(item) ?: return + drawContext.drawSprite( + x, y, + 0, + 16, 16, + MC.guiAtlasManager.getSprite(Identifier.of("firmament:item_rarity_background")), + r, g, b, 1F + ) + } + + + @Subscribe + fun onRenderSlot(it: SlotRenderEvents.Before) { + if (!TConfig.showItemRarityBackground) return + val stack = it.slot.stack ?: return + drawItemStackRarity(it.context, it.slot.x, it.slot.y, stack) + } + + @Subscribe + fun onRenderHotbarItem(it: HotbarItemRenderEvent) { + if (!TConfig.showItemRarityInHotbar) return + val stack = it.item + drawItemStackRarity(it.context, it.x, it.y, stack) + } +} diff --git a/src/main/kotlin/features/inventory/PriceData.kt b/src/main/kotlin/features/inventory/PriceData.kt new file mode 100644 index 0000000..c61f8e8 --- /dev/null +++ b/src/main/kotlin/features/inventory/PriceData.kt @@ -0,0 +1,51 @@ + + +package moe.nea.firmament.features.inventory + +import net.minecraft.text.Text +import moe.nea.firmament.annotations.Subscribe +import moe.nea.firmament.events.ItemTooltipEvent +import moe.nea.firmament.features.FirmamentFeature +import moe.nea.firmament.gui.config.ManagedConfig +import moe.nea.firmament.repo.HypixelStaticData +import moe.nea.firmament.util.FirmFormatters +import moe.nea.firmament.util.skyBlockId + +object PriceData : FirmamentFeature { + override val identifier: String + get() = "price-data" + + object TConfig : ManagedConfig(identifier) { + val tooltipEnabled by toggle("enable-always") { true } + val enableKeybinding by keyBindingWithDefaultUnbound("enable-keybind") + } + + override val config get() = TConfig + + @Subscribe + fun onItemTooltip(it: ItemTooltipEvent) { + if (!TConfig.tooltipEnabled && !TConfig.enableKeybinding.isPressed()) { + return + } + val sbId = it.stack.skyBlockId + val bazaarData = HypixelStaticData.bazaarData[sbId] + val lowestBin = HypixelStaticData.lowestBin[sbId] + if (bazaarData != null) { + it.lines.add(Text.literal("")) + it.lines.add( + Text.stringifiedTranslatable("firmament.tooltip.bazaar.sell-order", + FirmFormatters.formatCommas(bazaarData.quickStatus.sellPrice, 1)) + ) + it.lines.add( + Text.stringifiedTranslatable("firmament.tooltip.bazaar.buy-order", + FirmFormatters.formatCommas(bazaarData.quickStatus.buyPrice, 1)) + ) + } else if (lowestBin != null) { + it.lines.add(Text.literal("")) + it.lines.add( + Text.stringifiedTranslatable("firmament.tooltip.ah.lowestbin", + FirmFormatters.formatCommas(lowestBin, 1)) + ) + } + } +} diff --git a/src/main/kotlin/features/inventory/SaveCursorPosition.kt b/src/main/kotlin/features/inventory/SaveCursorPosition.kt new file mode 100644 index 0000000..1c55753 --- /dev/null +++ b/src/main/kotlin/features/inventory/SaveCursorPosition.kt @@ -0,0 +1,66 @@ + + +package moe.nea.firmament.features.inventory + +import kotlin.math.absoluteValue +import kotlin.time.Duration.Companion.milliseconds +import net.minecraft.client.util.InputUtil +import moe.nea.firmament.features.FirmamentFeature +import moe.nea.firmament.gui.config.ManagedConfig +import moe.nea.firmament.util.MC +import moe.nea.firmament.util.TimeMark +import moe.nea.firmament.util.assertNotNullOr + +object SaveCursorPosition : FirmamentFeature { + override val identifier: String + get() = "save-cursor-position" + + object TConfig : ManagedConfig(identifier) { + val enable by toggle("enable") { true } + val tolerance by duration("tolerance", 10.milliseconds, 5000.milliseconds) { 500.milliseconds } + } + + override val config: TConfig + get() = TConfig + + var savedPositionedP1: Pair<Double, Double>? = null + var savedPosition: SavedPosition? = null + + data class SavedPosition( + val middle: Pair<Double, Double>, + val cursor: Pair<Double, Double>, + val savedAt: TimeMark = TimeMark.now() + ) + + @JvmStatic + fun saveCursorOriginal(positionedX: Double, positionedY: Double) { + savedPositionedP1 = Pair(positionedX, positionedY) + } + + @JvmStatic + fun loadCursor(middleX: Double, middleY: Double): Pair<Double, Double>? { + if (!TConfig.enable) return null + val lastPosition = savedPosition?.takeIf { it.savedAt.passedTime() < TConfig.tolerance } + savedPosition = null + if (lastPosition != null && + (lastPosition.middle.first - middleX).absoluteValue < 1 && + (lastPosition.middle.second - middleY).absoluteValue < 1 + ) { + InputUtil.setCursorParameters( + MC.window.handle, + InputUtil.GLFW_CURSOR_NORMAL, + lastPosition.cursor.first, + lastPosition.cursor.second + ) + return lastPosition.cursor + } + return null + } + + @JvmStatic + fun saveCursorMiddle(middleX: Double, middleY: Double) { + if (!TConfig.enable) return + val cursorPos = assertNotNullOr(savedPositionedP1) { return } + savedPosition = SavedPosition(Pair(middleX, middleY), cursorPos) + } +} diff --git a/src/main/kotlin/features/inventory/SlotLocking.kt b/src/main/kotlin/features/inventory/SlotLocking.kt new file mode 100644 index 0000000..a50d8fb --- /dev/null +++ b/src/main/kotlin/features/inventory/SlotLocking.kt @@ -0,0 +1,203 @@ + + +@file:UseSerializers(DashlessUUIDSerializer::class) + +package moe.nea.firmament.features.inventory + +import com.mojang.blaze3d.systems.RenderSystem +import java.util.UUID +import org.lwjgl.glfw.GLFW +import kotlinx.serialization.Serializable +import kotlinx.serialization.UseSerializers +import kotlinx.serialization.serializer +import net.minecraft.client.gui.screen.ingame.HandledScreen +import net.minecraft.entity.player.PlayerInventory +import net.minecraft.screen.GenericContainerScreenHandler +import net.minecraft.screen.slot.SlotActionType +import net.minecraft.util.Identifier +import moe.nea.firmament.annotations.Subscribe +import moe.nea.firmament.events.HandledScreenKeyPressedEvent +import moe.nea.firmament.events.IsSlotProtectedEvent +import moe.nea.firmament.events.SlotRenderEvents +import moe.nea.firmament.features.FirmamentFeature +import moe.nea.firmament.gui.config.ManagedConfig +import moe.nea.firmament.keybindings.SavedKeyBinding +import moe.nea.firmament.mixins.accessor.AccessorHandledScreen +import moe.nea.firmament.util.CommonSoundEffects +import moe.nea.firmament.util.MC +import moe.nea.firmament.util.SBData +import moe.nea.firmament.util.SkyBlockIsland +import moe.nea.firmament.util.data.ProfileSpecificDataHolder +import moe.nea.firmament.util.item.displayNameAccordingToNbt +import moe.nea.firmament.util.item.loreAccordingToNbt +import moe.nea.firmament.util.json.DashlessUUIDSerializer +import moe.nea.firmament.util.skyblockUUID +import moe.nea.firmament.util.unformattedString + +object SlotLocking : FirmamentFeature { + override val identifier: String + get() = "slot-locking" + + @Serializable + data class Data( + val lockedSlots: MutableSet<Int> = mutableSetOf(), + val lockedSlotsRift: MutableSet<Int> = mutableSetOf(), + + val lockedUUIDs: MutableSet<UUID> = mutableSetOf(), + ) + + object TConfig : ManagedConfig(identifier) { + val lockSlot by keyBinding("lock") { GLFW.GLFW_KEY_L } + val lockUUID by keyBindingWithOutDefaultModifiers("lock-uuid") { + SavedKeyBinding(GLFW.GLFW_KEY_L, shift = true) + } + } + + override val config: TConfig + get() = TConfig + + object DConfig : ProfileSpecificDataHolder<Data>(serializer(), "locked-slots", ::Data) + + val lockedUUIDs get() = DConfig.data?.lockedUUIDs + + val lockedSlots + get() = when (SBData.skyblockLocation) { + SkyBlockIsland.RIFT -> DConfig.data?.lockedSlotsRift + null -> null + else -> DConfig.data?.lockedSlots + } + + fun isSalvageScreen(screen: HandledScreen<*>?): Boolean { + if (screen == null) return false + return screen.title.unformattedString.contains("Salvage Item") + } + + fun isTradeScreen(screen: HandledScreen<*>?): Boolean { + if (screen == null) return false + val handler = screen.screenHandler as? GenericContainerScreenHandler ?: return false + if (handler.inventory.size() < 9) return false + val middlePane = handler.inventory.getStack(handler.inventory.size() - 5) + if (middlePane == null) return false + return middlePane.displayNameAccordingToNbt?.unformattedString == "⇦ Your stuff" + } + + fun isNpcShop(screen: HandledScreen<*>?): Boolean { + if (screen == null) return false + val handler = screen.screenHandler as? GenericContainerScreenHandler ?: return false + if (handler.inventory.size() < 9) return false + val sellItem = handler.inventory.getStack(handler.inventory.size() - 5) + if (sellItem == null) return false + if (sellItem.displayNameAccordingToNbt?.unformattedString == "Sell Item") return true + val lore = sellItem.loreAccordingToNbt + return (lore.lastOrNull() ?: return false).unformattedString == "Click to buyback!" + } + + @Subscribe + fun onSalvageProtect(event: IsSlotProtectedEvent) { + if (event.slot == null) return + if (!event.slot.hasStack()) return + if (event.slot.stack.displayNameAccordingToNbt?.unformattedString != "Salvage Items") return + val inv = event.slot.inventory + var anyBlocked = false + for (i in 0 until event.slot.index) { + val stack = inv.getStack(i) + if (IsSlotProtectedEvent.shouldBlockInteraction(null, SlotActionType.THROW, stack)) + anyBlocked = true + } + if (anyBlocked) { + event.protectSilent() + } + } + + @Subscribe + fun onProtectUuidItems(event: IsSlotProtectedEvent) { + val doesNotDeleteItem = event.actionType == SlotActionType.SWAP + || event.actionType == SlotActionType.PICKUP + || event.actionType == SlotActionType.QUICK_MOVE + || event.actionType == SlotActionType.QUICK_CRAFT + || event.actionType == SlotActionType.CLONE + || event.actionType == SlotActionType.PICKUP_ALL + val isSellOrTradeScreen = + isNpcShop(MC.handledScreen) || isTradeScreen(MC.handledScreen) || isSalvageScreen(MC.handledScreen) + if ((!isSellOrTradeScreen || event.slot?.inventory !is PlayerInventory) + && doesNotDeleteItem + ) return + val stack = event.itemStack ?: return + val uuid = stack.skyblockUUID ?: return + if (uuid in (lockedUUIDs ?: return)) { + event.protect() + } + } + + @Subscribe + fun onProtectSlot(it: IsSlotProtectedEvent) { + if (it.slot != null && it.slot.inventory is PlayerInventory && it.slot.index in (lockedSlots ?: setOf())) { + it.protect() + } + } + + @Subscribe + fun onLockUUID(it: HandledScreenKeyPressedEvent) { + if (!it.matches(TConfig.lockUUID)) return + val inventory = MC.handledScreen ?: return + inventory as AccessorHandledScreen + + val slot = inventory.focusedSlot_Firmament ?: return + val stack = slot.stack ?: return + val uuid = stack.skyblockUUID ?: return + val lockedUUIDs = lockedUUIDs ?: return + if (uuid in lockedUUIDs) { + lockedUUIDs.remove(uuid) + } else { + lockedUUIDs.add(uuid) + } + DConfig.markDirty() + CommonSoundEffects.playSuccess() + it.cancel() + } + + @Subscribe + fun onLockSlot(it: HandledScreenKeyPressedEvent) { + if (!it.matches(TConfig.lockSlot)) return + val inventory = MC.handledScreen ?: return + inventory as AccessorHandledScreen + + val slot = inventory.focusedSlot_Firmament ?: return + val lockedSlots = lockedSlots ?: return + if (slot.inventory is PlayerInventory) { + if (slot.index in lockedSlots) { + lockedSlots.remove(slot.index) + } else { + lockedSlots.add(slot.index) + } + DConfig.markDirty() + CommonSoundEffects.playSuccess() + } + } + + @Subscribe + fun onRenderSlotOverlay(it: SlotRenderEvents.After) { + val isSlotLocked = it.slot.inventory is PlayerInventory && it.slot.index in (lockedSlots ?: setOf()) + val isUUIDLocked = (it.slot.stack?.skyblockUUID) in (lockedUUIDs ?: setOf()) + if (isSlotLocked || isUUIDLocked) { + RenderSystem.disableDepthTest() + it.context.drawSprite( + it.slot.x, it.slot.y, 0, + 16, 16, + MC.guiAtlasManager.getSprite( + when { + isSlotLocked -> + (Identifier.of("firmament:slot_locked")) + + isUUIDLocked -> + (Identifier.of("firmament:uuid_locked")) + + else -> + error("unreachable") + } + ) + ) + RenderSystem.enableDepthTest() + } + } +} diff --git a/src/main/kotlin/features/inventory/buttons/InventoryButton.kt b/src/main/kotlin/features/inventory/buttons/InventoryButton.kt new file mode 100644 index 0000000..539edf2 --- /dev/null +++ b/src/main/kotlin/features/inventory/buttons/InventoryButton.kt @@ -0,0 +1,85 @@ + + +package moe.nea.firmament.features.inventory.buttons + +import com.mojang.brigadier.StringReader +import me.shedaniel.math.Dimension +import me.shedaniel.math.Point +import me.shedaniel.math.Rectangle +import kotlinx.serialization.Serializable +import net.minecraft.client.gui.DrawContext +import net.minecraft.command.CommandRegistryAccess +import net.minecraft.command.argument.ItemStackArgumentType +import net.minecraft.item.ItemStack +import net.minecraft.resource.featuretoggle.FeatureFlags +import net.minecraft.util.Identifier +import moe.nea.firmament.repo.ItemCache.asItemStack +import moe.nea.firmament.repo.RepoManager +import moe.nea.firmament.util.MC +import moe.nea.firmament.util.SkyblockId +import moe.nea.firmament.util.memoize + +@Serializable +data class InventoryButton( + var x: Int, + var y: Int, + var anchorRight: Boolean, + var anchorBottom: Boolean, + var icon: String? = "", + var command: String? = "", +) { + companion object { + val itemStackParser by lazy { + ItemStackArgumentType.itemStack(CommandRegistryAccess.of(MC.defaultRegistries, + FeatureFlags.VANILLA_FEATURES)) + } + val dimensions = Dimension(18, 18) + val getItemForName = ::getItemForName0.memoize(1024) + fun getItemForName0(icon: String): ItemStack { + val repoItem = RepoManager.getNEUItem(SkyblockId(icon)) + var itemStack = repoItem.asItemStack(idHint = SkyblockId(icon)) + if (repoItem == null) { + val giveSyntaxItem = if (icon.startsWith("/give") || icon.startsWith("give")) + icon.split(" ", limit = 3).getOrNull(2) ?: icon + else icon + val componentItem = + runCatching { + itemStackParser.parse(StringReader(giveSyntaxItem)).createStack(1, false) + }.getOrNull() + if (componentItem != null) + itemStack = componentItem + } + return itemStack + } + } + + fun render(context: DrawContext) { + context.drawSprite( + 0, + 0, + 0, + dimensions.width, + dimensions.height, + MC.guiAtlasManager.getSprite(Identifier.of("firmament:inventory_button_background")) + ) + context.drawItem(getItem(), 1, 1) + } + + fun isValid() = !icon.isNullOrBlank() && !command.isNullOrBlank() + + fun getPosition(guiRect: Rectangle): Point { + return Point( + (if (anchorRight) guiRect.maxX else guiRect.minX) + x, + (if (anchorBottom) guiRect.maxY else guiRect.minY) + y, + ) + } + + fun getBounds(guiRect: Rectangle): Rectangle { + return Rectangle(getPosition(guiRect), dimensions) + } + + fun getItem(): ItemStack { + return getItemForName(icon ?: "") + } + +} diff --git a/src/main/kotlin/features/inventory/buttons/InventoryButtonEditor.kt b/src/main/kotlin/features/inventory/buttons/InventoryButtonEditor.kt new file mode 100644 index 0000000..c57563e --- /dev/null +++ b/src/main/kotlin/features/inventory/buttons/InventoryButtonEditor.kt @@ -0,0 +1,184 @@ + + +package moe.nea.firmament.features.inventory.buttons + +import io.github.notenoughupdates.moulconfig.common.IItemStack +import io.github.notenoughupdates.moulconfig.platform.ModernItemStack +import io.github.notenoughupdates.moulconfig.xml.Bind +import me.shedaniel.math.Point +import me.shedaniel.math.Rectangle +import org.lwjgl.glfw.GLFW +import net.minecraft.client.gui.DrawContext +import net.minecraft.client.gui.widget.ButtonWidget +import net.minecraft.client.util.InputUtil +import net.minecraft.text.Text +import net.minecraft.util.math.MathHelper +import net.minecraft.util.math.Vec2f +import moe.nea.firmament.util.ClipboardUtils +import moe.nea.firmament.util.FragmentGuiScreen +import moe.nea.firmament.util.MC +import moe.nea.firmament.util.MoulConfigUtils + +class InventoryButtonEditor( + val lastGuiRect: Rectangle, +) : FragmentGuiScreen() { + inner class Editor(val originalButton: InventoryButton) { + @field:Bind + var command: String = originalButton.command ?: "" + + @field:Bind + var icon: String = originalButton.icon ?: "" + + @Bind + fun getItemIcon(): IItemStack { + save() + return ModernItemStack.of(InventoryButton.getItemForName(icon)) + } + + @Bind + fun delete() { + buttons.removeIf { it === originalButton } + popup = null + } + + fun save() { + originalButton.icon = icon + originalButton.command = command + } + } + + var buttons: MutableList<InventoryButton> = + InventoryButtons.DConfig.data.buttons.map { it.copy() }.toMutableList() + + override fun close() { + InventoryButtons.DConfig.data.buttons = buttons + InventoryButtons.DConfig.markDirty() + super.close() + } + + override fun init() { + super.init() + addDrawableChild( + ButtonWidget.builder(Text.translatable("firmament.inventory-buttons.load-preset")) { + val t = ClipboardUtils.getTextContents() + val newButtons = InventoryButtonTemplates.loadTemplate(t) + if (newButtons != null) + buttons = newButtons.toMutableList() + } + .position(lastGuiRect.minX + 10, lastGuiRect.minY + 35) + .width(lastGuiRect.width - 20) + .build() + ) + addDrawableChild( + ButtonWidget.builder(Text.translatable("firmament.inventory-buttons.save-preset")) { + ClipboardUtils.setTextContent(InventoryButtonTemplates.saveTemplate(buttons)) + } + .position(lastGuiRect.minX + 10, lastGuiRect.minY + 60) + .width(lastGuiRect.width - 20) + .build() + ) + } + + override fun render(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) { + super.render(context, mouseX, mouseY, delta) + context.matrices.push() + context.matrices.translate(0f, 0f, -10f) + context.fill(lastGuiRect.minX, lastGuiRect.minY, lastGuiRect.maxX, lastGuiRect.maxY, -1) + context.setShaderColor(1f, 1f, 1f, 1f) + context.matrices.pop() + for (button in buttons) { + val buttonPosition = button.getBounds(lastGuiRect) + context.matrices.push() + context.matrices.translate(buttonPosition.minX.toFloat(), buttonPosition.minY.toFloat(), 0F) + button.render(context) + context.matrices.pop() + } + } + + override fun keyPressed(keyCode: Int, scanCode: Int, modifiers: Int): Boolean { + if (super.keyPressed(keyCode, scanCode, modifiers)) return true + if (keyCode == GLFW.GLFW_KEY_ESCAPE) { + close() + return true + } + return false + } + + override fun mouseReleased(mouseX: Double, mouseY: Double, button: Int): Boolean { + if (super.mouseReleased(mouseX, mouseY, button)) return true + val clickedButton = buttons.firstOrNull { it.getBounds(lastGuiRect).contains(Point(mouseX, mouseY)) } + if (clickedButton != null && !justPerformedAClickAction) { + createPopup(MoulConfigUtils.loadGui("button_editor_fragment", Editor(clickedButton)), Point(mouseX, mouseY)) + return true + } + justPerformedAClickAction = false + lastDraggedButton = null + return false + } + + override fun mouseDragged(mouseX: Double, mouseY: Double, button: Int, deltaX: Double, deltaY: Double): Boolean { + if (super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)) return true + + if (initialDragMousePosition.distanceSquared(Vec2f(mouseX.toFloat(), mouseY.toFloat())) >= 4 * 4) { + initialDragMousePosition = Vec2f(-10F, -10F) + lastDraggedButton?.let { dragging -> + justPerformedAClickAction = true + val (anchorRight, anchorBottom, offsetX, offsetY) = getCoordsForMouse(mouseX.toInt(), mouseY.toInt()) + ?: return true + dragging.x = offsetX + dragging.y = offsetY + dragging.anchorRight = anchorRight + dragging.anchorBottom = anchorBottom + } + } + return false + } + + var lastDraggedButton: InventoryButton? = null + var justPerformedAClickAction = false + var initialDragMousePosition = Vec2f(-10F, -10F) + + data class AnchoredCoords( + val anchorRight: Boolean, + val anchorBottom: Boolean, + val offsetX: Int, + val offsetY: Int, + ) + + fun getCoordsForMouse(mx: Int, my: Int): AnchoredCoords? { + if (lastGuiRect.contains(mx, my) || lastGuiRect.contains( + Point( + mx + InventoryButton.dimensions.width, + my + InventoryButton.dimensions.height, + ) + ) + ) return null + + val anchorRight = mx > lastGuiRect.maxX + val anchorBottom = my > lastGuiRect.maxY + var offsetX = mx - if (anchorRight) lastGuiRect.maxX else lastGuiRect.minX + var offsetY = my - if (anchorBottom) lastGuiRect.maxY else lastGuiRect.minY + if (InputUtil.isKeyPressed(MC.window.handle, InputUtil.GLFW_KEY_LEFT_SHIFT)) { + offsetX = MathHelper.floor(offsetX / 20F) * 20 + offsetY = MathHelper.floor(offsetY / 20F) * 20 + } + return AnchoredCoords(anchorRight, anchorBottom, offsetX, offsetY) + } + + override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean { + if (super.mouseClicked(mouseX, mouseY, button)) return true + val clickedButton = buttons.firstOrNull { it.getBounds(lastGuiRect).contains(Point(mouseX, mouseY)) } + if (clickedButton != null) { + lastDraggedButton = clickedButton + initialDragMousePosition = Vec2f(mouseX.toFloat(), mouseY.toFloat()) + return true + } + val mx = mouseX.toInt() + val my = mouseY.toInt() + val (anchorRight, anchorBottom, offsetX, offsetY) = getCoordsForMouse(mx, my) ?: return true + buttons.add(InventoryButton(offsetX, offsetY, anchorRight, anchorBottom, null, null)) + justPerformedAClickAction = true + return true + } + +} diff --git a/src/main/kotlin/features/inventory/buttons/InventoryButtonTemplates.kt b/src/main/kotlin/features/inventory/buttons/InventoryButtonTemplates.kt new file mode 100644 index 0000000..99b544b --- /dev/null +++ b/src/main/kotlin/features/inventory/buttons/InventoryButtonTemplates.kt @@ -0,0 +1,35 @@ + + +package moe.nea.firmament.features.inventory.buttons + |
