Refactor source layout
Introduce compat source sets and move all kotlin sources to the main directory [no changelog]
This commit is contained in:
@@ -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 ?: "")
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
|
||||
|
||||
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) })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
|
||||
|
||||
package moe.nea.firmament.features.inventory.buttons
|
||||
|
||||
import me.shedaniel.math.Rectangle
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.serializer
|
||||
import moe.nea.firmament.annotations.Subscribe
|
||||
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) {
|
||||
val _openEditor by button("open-editor") {
|
||||
openEditor()
|
||||
}
|
||||
}
|
||||
|
||||
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() }
|
||||
|
||||
@Subscribe
|
||||
fun onRectangles(it: HandledScreenPushREIEvent) {
|
||||
val bounds = it.screen.getRectangle()
|
||||
for (button in getValidButtons()) {
|
||||
val buttonBounds = button.getBounds(bounds)
|
||||
it.block(buttonBounds)
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun onClickScreen(it: HandledScreenClickEvent) {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun onRenderForeground(it: HandledScreenForegroundEvent) {
|
||||
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
|
||||
}
|
||||
|
||||
var lastRectangle: Rectangle? = null
|
||||
fun openEditor() {
|
||||
ScreenUtil.setScreenLater(
|
||||
InventoryButtonEditor(
|
||||
lastRectangle ?: Rectangle(
|
||||
MC.window.scaledWidth / 2 - 100,
|
||||
MC.window.scaledHeight / 2 - 100,
|
||||
200, 200,
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user