diff options
Diffstat (limited to 'src/main/kotlin/moe/nea/firmament/features')
6 files changed, 325 insertions, 1 deletions
diff --git a/src/main/kotlin/moe/nea/firmament/features/FeatureManager.kt b/src/main/kotlin/moe/nea/firmament/features/FeatureManager.kt index 2683775..4888f54 100644 --- a/src/main/kotlin/moe/nea/firmament/features/FeatureManager.kt +++ b/src/main/kotlin/moe/nea/firmament/features/FeatureManager.kt @@ -23,6 +23,7 @@ import moe.nea.firmament.features.inventory.ItemRarityCosmetics import moe.nea.firmament.features.inventory.PriceData import moe.nea.firmament.features.inventory.SaveCursorPosition import moe.nea.firmament.features.inventory.SlotLocking +import moe.nea.firmament.features.inventory.buttons.InventoryButtons import moe.nea.firmament.features.inventory.storageoverlay.StorageOverlay import moe.nea.firmament.features.texturepack.CustomSkyBlockTextures import moe.nea.firmament.features.world.FairySouls @@ -56,6 +57,7 @@ object FeatureManager : DataHolder<FeatureManager.Config>(serializer(), "feature loadFeature(CraftingOverlay) loadFeature(PowerUserTools) loadFeature(ChatLinks) + loadFeature(InventoryButtons) loadFeature(CompatibliltyFeatures) loadFeature(QuickCommands) loadFeature(SaveCursorPosition) diff --git a/src/main/kotlin/moe/nea/firmament/features/debug/DeveloperFeatures.kt b/src/main/kotlin/moe/nea/firmament/features/debug/DeveloperFeatures.kt index e045fa8..7e3203b 100644 --- a/src/main/kotlin/moe/nea/firmament/features/debug/DeveloperFeatures.kt +++ b/src/main/kotlin/moe/nea/firmament/features/debug/DeveloperFeatures.kt @@ -64,6 +64,7 @@ object DeveloperFeatures : FirmamentFeature { return reloadFuture.thenCompose { client.reloadResources() } } + override fun onLoad() { HandledScreenKeyPressedEvent.subscribe { if (it.matches(IKeyBinding.ofKeyCode(GLFW.GLFW_KEY_K))) { @@ -78,10 +79,16 @@ object DeveloperFeatures : FirmamentFeature { ).setStyle( Style.EMPTY.withColor(Formatting.AQUA) .withClickEvent(ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, ident)) - .withHoverEvent(HoverEvent(HoverEvent.Action.SHOW_TEXT, Text.translatable("firmament.debug.skyblockid.copy"))) + .withHoverEvent( + HoverEvent( + HoverEvent.Action.SHOW_TEXT, + Text.translatable("firmament.debug.skyblockid.copy") + ) + ) ) ) } } } } + diff --git a/src/main/kotlin/moe/nea/firmament/features/inventory/buttons/InventoryButton.kt b/src/main/kotlin/moe/nea/firmament/features/inventory/buttons/InventoryButton.kt new file mode 100644 index 0000000..0383258 --- /dev/null +++ b/src/main/kotlin/moe/nea/firmament/features/inventory/buttons/InventoryButton.kt @@ -0,0 +1,66 @@ +/* + * SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe> + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package moe.nea.firmament.features.inventory.buttons + +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.item.ItemStack +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 + +@Serializable +data class InventoryButton( + val x: Int, + val y: Int, + val anchorRight: Boolean, + val anchorBottom: Boolean, + var icon: String? = "", + var command: String? = "", +) { + companion object { + val dimensions = Dimension(18, 18) + fun getItemForName(icon: String): ItemStack { + return RepoManager.getNEUItem(SkyblockId(icon)).asItemStack(idHint = SkyblockId(icon)) + } + } + + fun render(context: DrawContext) { + context.drawSprite( + 0, + 0, + 0, + dimensions.width, + dimensions.height, + MC.guiAtlasManager.getSprite(Identifier("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/moe/nea/firmament/features/inventory/buttons/InventoryButtonEditor.kt b/src/main/kotlin/moe/nea/firmament/features/inventory/buttons/InventoryButtonEditor.kt new file mode 100644 index 0000000..66be212 --- /dev/null +++ b/src/main/kotlin/moe/nea/firmament/features/inventory/buttons/InventoryButtonEditor.kt @@ -0,0 +1,128 @@ +/* + * SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe> + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package moe.nea.firmament.features.inventory.buttons + +import io.github.moulberry.moulconfig.common.IItemStack +import io.github.moulberry.moulconfig.xml.Bind +import io.github.notenoughupdates.moulconfig.platform.ModernItemStack +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.text.Text +import moe.nea.firmament.util.ClipboardUtils +import moe.nea.firmament.util.FragmentGuiScreen +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) { + context.fill(lastGuiRect.minX, lastGuiRect.minY, lastGuiRect.maxX, lastGuiRect.maxY, -1) + context.setShaderColor(1f, 1f, 1f, 1f) + super.render(context, mouseX, mouseY, delta) + 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 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) { + createPopup(MoulConfigUtils.loadGui("button_editor_fragment", Editor(clickedButton)), Point(mouseX, mouseY)) + return true + } + if (lastGuiRect.contains(mouseX, mouseY) || lastGuiRect.contains( + Point( + mouseX + InventoryButton.dimensions.width, + mouseY + InventoryButton.dimensions.height, + ) + ) + ) return true + val mx = mouseX.toInt() + val my = mouseY.toInt() + val anchorRight = mx > lastGuiRect.maxX + val anchorBottom = my > lastGuiRect.maxY + val offsetX = mx - if (anchorRight) lastGuiRect.maxX else lastGuiRect.minX + val offsetY = my - if (anchorBottom) lastGuiRect.maxY else lastGuiRect.minY + buttons.add(InventoryButton(offsetX, offsetY, anchorRight, anchorBottom, null, null)) + return true + } + +} diff --git a/src/main/kotlin/moe/nea/firmament/features/inventory/buttons/InventoryButtonTemplates.kt b/src/main/kotlin/moe/nea/firmament/features/inventory/buttons/InventoryButtonTemplates.kt new file mode 100644 index 0000000..6b551b0 --- /dev/null +++ b/src/main/kotlin/moe/nea/firmament/features/inventory/buttons/InventoryButtonTemplates.kt @@ -0,0 +1,39 @@ +/* + * SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe> + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package moe.nea.firmament.features.inventory.buttons + +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.encodeToString +import net.minecraft.text.Text +import moe.nea.firmament.Firmament +import moe.nea.firmament.util.MC +import moe.nea.firmament.util.TemplateUtil + +object InventoryButtonTemplates { + + val legacyPrefix = "NEUBUTTONS/" + val modernPrefix = "MAYBEONEDAYIWILLHAVEMYOWNFORMAT" + + fun loadTemplate(t: String): List<InventoryButton>? { + val buttons = TemplateUtil.maybeDecodeTemplate<List<String>>(legacyPrefix, t) ?: return null + return buttons.mapNotNull { + try { + Firmament.json.decodeFromString<InventoryButton>(it).also { + if (it.icon?.startsWith("extra:") == true || it.command?.any { it.isLowerCase() } == true) { + MC.sendChat(Text.translatable("firmament.inventory-buttons.import-failed")) + } + } + } catch (e: Exception) { + null + } + } + } + + fun saveTemplate(buttons: List<InventoryButton>): String { + return TemplateUtil.encodeTemplate(legacyPrefix, buttons.map { Firmament.json.encodeToString(it) }) + } +} diff --git a/src/main/kotlin/moe/nea/firmament/features/inventory/buttons/InventoryButtons.kt b/src/main/kotlin/moe/nea/firmament/features/inventory/buttons/InventoryButtons.kt new file mode 100644 index 0000000..85e1d88 --- /dev/null +++ b/src/main/kotlin/moe/nea/firmament/features/inventory/buttons/InventoryButtons.kt @@ -0,0 +1,82 @@ +/* + * SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe> + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package moe.nea.firmament.features.inventory.buttons + +import me.shedaniel.math.Rectangle +import kotlinx.serialization.Serializable +import kotlinx.serialization.serializer +import moe.nea.firmament.events.HandledScreenClickEvent +import moe.nea.firmament.events.HandledScreenForegroundEvent +import moe.nea.firmament.events.HandledScreenPushREIEvent +import moe.nea.firmament.features.FirmamentFeature +import moe.nea.firmament.gui.config.ManagedConfig +import moe.nea.firmament.util.MC +import moe.nea.firmament.util.ScreenUtil +import moe.nea.firmament.util.data.DataHolder +import moe.nea.firmament.util.getRectangle + +object InventoryButtons : FirmamentFeature { + override val identifier: String + get() = "inventory-buttons" + + object TConfig : ManagedConfig(identifier) {} + object DConfig : DataHolder<Data>(serializer(), identifier, ::Data) + + @Serializable + data class Data( + var buttons: MutableList<InventoryButton> = mutableListOf() + ) + + + override val config: ManagedConfig + get() = TConfig + + fun getValidButtons() = DConfig.data.buttons.asSequence().filter { it.isValid() } + override fun onLoad() { + HandledScreenForegroundEvent.subscribe { + val bounds = it.screen.getRectangle() + for (button in getValidButtons()) { + val buttonBounds = button.getBounds(bounds) + it.context.matrices.push() + it.context.matrices.translate(buttonBounds.minX.toFloat(), buttonBounds.minY.toFloat(), 0F) + button.render(it.context) + it.context.matrices.pop() + } + lastRectangle = bounds + } + HandledScreenClickEvent.subscribe { + val bounds = it.screen.getRectangle() + for (button in getValidButtons()) { + val buttonBounds = button.getBounds(bounds) + if (buttonBounds.contains(it.mouseX, it.mouseY)) { + MC.sendCommand(button.command!! /* non null invariant covered by getValidButtons */) + break + } + } + } + HandledScreenPushREIEvent.subscribe { + val bounds = it.screen.getRectangle() + for (button in getValidButtons()) { + val buttonBounds = button.getBounds(bounds) + it.block(buttonBounds) + } + } + } + + var lastRectangle: Rectangle? = null + fun openEditor() { + ScreenUtil.setScreenLater( + InventoryButtonEditor( + lastRectangle ?: Rectangle( + MC.window.scaledWidth / 2 - 100, + MC.window.scaledHeight / 2 - 100, + 200, 200, + ) + ) + ) + } +} |