From 4d93f475aadc42c4bf83c3a0749af41659235c71 Mon Sep 17 00:00:00 2001 From: nea Date: Tue, 11 Jul 2023 21:01:58 +0200 Subject: Bulk commit --- .../moe/nea/firmament/features/FeatureManager.kt | 9 +- .../moe/nea/firmament/features/FirmamentFeature.kt | 1 - .../moe/nea/firmament/features/debug/DebugView.kt | 53 ++++++++ .../firmament/features/debug/DeveloperFeatures.kt | 2 - .../nea/firmament/features/debug/ObjectRenderer.kt | 52 ++++++++ .../firmament/features/fishing/FishingWarning.kt | 4 +- .../features/inventory/CraftingOverlay.kt | 2 - .../features/inventory/SaveCursorPosition.kt | 67 ++++++++++ .../firmament/features/inventory/SlotLocking.kt | 2 - .../storageoverlay/StorageBackingHandle.kt | 56 +++++++++ .../inventory/storageoverlay/StorageData.kt | 16 +++ .../inventory/storageoverlay/StorageOverlay.kt | 94 ++++++++++++++ .../storageoverlay/StorageOverlayScreen.kt | 135 +++++++++++++++++++++ .../inventory/storageoverlay/StoragePageSlot.kt | 64 ++++++++++ .../inventory/storageoverlay/VirtualInventory.kt | 53 ++++++++ .../moe/nea/firmament/features/world/FairySouls.kt | 30 ++++- 16 files changed, 626 insertions(+), 14 deletions(-) create mode 100644 src/main/kotlin/moe/nea/firmament/features/debug/DebugView.kt create mode 100644 src/main/kotlin/moe/nea/firmament/features/debug/ObjectRenderer.kt create mode 100644 src/main/kotlin/moe/nea/firmament/features/inventory/SaveCursorPosition.kt create mode 100644 src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/StorageBackingHandle.kt create mode 100644 src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/StorageData.kt create mode 100644 src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/StorageOverlay.kt create mode 100644 src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/StorageOverlayScreen.kt create mode 100644 src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/StoragePageSlot.kt create mode 100644 src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/VirtualInventory.kt (limited to 'src/main/kotlin/moe/nea/firmament/features') diff --git a/src/main/kotlin/moe/nea/firmament/features/FeatureManager.kt b/src/main/kotlin/moe/nea/firmament/features/FeatureManager.kt index e465349..0f0a166 100644 --- a/src/main/kotlin/moe/nea/firmament/features/FeatureManager.kt +++ b/src/main/kotlin/moe/nea/firmament/features/FeatureManager.kt @@ -21,10 +21,13 @@ package moe.nea.firmament.features import kotlinx.serialization.Serializable import kotlinx.serialization.serializer import moe.nea.firmament.Firmament +import moe.nea.firmament.features.debug.DebugView import moe.nea.firmament.features.debug.DeveloperFeatures import moe.nea.firmament.features.fishing.FishingWarning import moe.nea.firmament.features.inventory.CraftingOverlay +import moe.nea.firmament.features.inventory.SaveCursorPosition import moe.nea.firmament.features.inventory.SlotLocking +import moe.nea.firmament.features.inventory.storageoverlay.StorageOverlay import moe.nea.firmament.features.world.FairySouls import moe.nea.firmament.util.data.DataHolder @@ -50,9 +53,13 @@ object FeatureManager : DataHolder(serializer(), "feature loadFeature(FairySouls) loadFeature(FishingWarning) loadFeature(SlotLocking) + loadFeature(StorageOverlay) loadFeature(CraftingOverlay) - if (Firmament.DEBUG) + loadFeature(SaveCursorPosition) + if (Firmament.DEBUG) { loadFeature(DeveloperFeatures) + loadFeature(DebugView) + } hasAutoloaded = true } } diff --git a/src/main/kotlin/moe/nea/firmament/features/FirmamentFeature.kt b/src/main/kotlin/moe/nea/firmament/features/FirmamentFeature.kt index e394fb7..94b69f1 100644 --- a/src/main/kotlin/moe/nea/firmament/features/FirmamentFeature.kt +++ b/src/main/kotlin/moe/nea/firmament/features/FirmamentFeature.kt @@ -21,7 +21,6 @@ package moe.nea.firmament.features import moe.nea.firmament.gui.config.ManagedConfig interface FirmamentFeature { - val name: String val identifier: String val defaultEnabled: Boolean get() = true diff --git a/src/main/kotlin/moe/nea/firmament/features/debug/DebugView.kt b/src/main/kotlin/moe/nea/firmament/features/debug/DebugView.kt new file mode 100644 index 0000000..e8c85d8 --- /dev/null +++ b/src/main/kotlin/moe/nea/firmament/features/debug/DebugView.kt @@ -0,0 +1,53 @@ +package moe.nea.firmament.features.debug + +import io.github.cottonmc.cotton.gui.client.CottonHud +import io.github.cottonmc.cotton.gui.widget.WBox +import io.github.cottonmc.cotton.gui.widget.data.Axis +import java.util.Optional +import kotlin.time.Duration.Companion.seconds +import net.minecraft.scoreboard.Scoreboard +import net.minecraft.scoreboard.Team +import net.minecraft.text.StringVisitable +import net.minecraft.text.Style +import net.minecraft.text.Text +import net.minecraft.util.Formatting +import moe.nea.firmament.Firmament +import moe.nea.firmament.events.TickEvent +import moe.nea.firmament.features.FirmamentFeature +import moe.nea.firmament.util.MC +import moe.nea.firmament.util.TimeMark + +object DebugView : FirmamentFeature { + private data class StoredVariable( + val obj: T, + val timer: TimeMark, + ) + + private val storedVariables: MutableMap> = mutableMapOf() + override val identifier: String + get() = "debug-view" + override val defaultEnabled: Boolean + get() = Firmament.DEBUG + + fun showVariable(label: String, obj: T) { + synchronized(this) { + storedVariables[label] = StoredVariable(obj, TimeMark.now()) + } + } + + val debugWidget = WBox(Axis.VERTICAL) + + + override fun onLoad() { + TickEvent.subscribe { + synchronized(this) { + storedVariables.entries.removeIf { it.value.timer.passedTime() > 1.seconds } + if (storedVariables.isEmpty()) { + CottonHud.add(debugWidget, 20, 20) + } else { + CottonHud.remove(debugWidget) + } + } + } + } +} 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 e89ee6f..253bc0d 100644 --- a/src/main/kotlin/moe/nea/firmament/features/debug/DeveloperFeatures.kt +++ b/src/main/kotlin/moe/nea/firmament/features/debug/DeveloperFeatures.kt @@ -14,8 +14,6 @@ import moe.nea.firmament.util.TimeMark import moe.nea.firmament.util.iterate object DeveloperFeatures : FirmamentFeature { - override val name: String - get() = "developer" override val identifier: String get() = "developer" override val config: TConfig diff --git a/src/main/kotlin/moe/nea/firmament/features/debug/ObjectRenderer.kt b/src/main/kotlin/moe/nea/firmament/features/debug/ObjectRenderer.kt new file mode 100644 index 0000000..f5185cd --- /dev/null +++ b/src/main/kotlin/moe/nea/firmament/features/debug/ObjectRenderer.kt @@ -0,0 +1,52 @@ +package moe.nea.firmament.features.debug + +import io.github.cottonmc.cotton.gui.widget.WBox +import io.github.cottonmc.cotton.gui.widget.WLabel +import io.github.cottonmc.cotton.gui.widget.WWidget +import io.github.cottonmc.cotton.gui.widget.data.Axis +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import kotlin.reflect.KProperty1 +import net.minecraft.text.Text +import moe.nea.firmament.gui.WSpacer + +class ObjectRenderer(val box: WBox) { + var indent = 0 + + fun beginObject() { + indent++ + } + + fun endObject() { + indent-- + } + + fun emit(label: String, widget: WWidget) { + WSpacer(WBox(Axis.VERTICAL).also { + it.add(WWidget()) + it.add(widget) + }, indent * 18) + } + + fun getDebuggingView(label: String, obj: T) { + if (obj == null) { + emit(label, WLabel(Text.literal("§cnull"))) + return + } + if (obj is String) { + emit(label, WLabel(Text.literal(Json.encodeToString(obj)))) + } + getObject(label, obj) + } + + fun getObject(label: String, obj: T) { + emit(label, WLabel(Text.literal(obj::class.simpleName ?: ""))) + beginObject() + for (prop in obj::class.members.filterIsInstance>()) { + val child = prop.get(obj) + getDebuggingView(prop.name, child) + } + endObject() + } + +} diff --git a/src/main/kotlin/moe/nea/firmament/features/fishing/FishingWarning.kt b/src/main/kotlin/moe/nea/firmament/features/fishing/FishingWarning.kt index fa8d779..4c20450 100644 --- a/src/main/kotlin/moe/nea/firmament/features/fishing/FishingWarning.kt +++ b/src/main/kotlin/moe/nea/firmament/features/fishing/FishingWarning.kt @@ -38,8 +38,6 @@ import moe.nea.firmament.util.TimeMark import moe.nea.firmament.util.render.RenderInWorldContext.Companion.renderInWorld object FishingWarning : FirmamentFeature { - override val name: String - get() = "Fishing Warning" override val identifier: String get() = "fishing-warning" @@ -137,7 +135,7 @@ object FishingWarning : FirmamentFeature { WorldRenderLastEvent.subscribe { recentParticles.removeIf { it.second.passedTime() > 5.seconds } recentCandidates.removeIf { it.timeMark.passedTime() > 5.seconds } - renderInWorld(it.matrices, it.camera) { + renderInWorld(it) { color(0f, 0f, 1f, 1f) recentParticles.forEach { tinyBlock(it.first, 0.1F) diff --git a/src/main/kotlin/moe/nea/firmament/features/inventory/CraftingOverlay.kt b/src/main/kotlin/moe/nea/firmament/features/inventory/CraftingOverlay.kt index 31d2b23..bb73513 100644 --- a/src/main/kotlin/moe/nea/firmament/features/inventory/CraftingOverlay.kt +++ b/src/main/kotlin/moe/nea/firmament/features/inventory/CraftingOverlay.kt @@ -26,8 +26,6 @@ object CraftingOverlay : FirmamentFeature { this.recipe = recipe } - override val name: String - get() = "Crafting Overlay" override val identifier: String get() = "crafting-overlay" diff --git a/src/main/kotlin/moe/nea/firmament/features/inventory/SaveCursorPosition.kt b/src/main/kotlin/moe/nea/firmament/features/inventory/SaveCursorPosition.kt new file mode 100644 index 0000000..577f19b --- /dev/null +++ b/src/main/kotlin/moe/nea/firmament/features/inventory/SaveCursorPosition.kt @@ -0,0 +1,67 @@ +package moe.nea.firmament.features.inventory + +import kotlin.math.absoluteValue +import kotlin.time.Duration.Companion.milliseconds +import kotlin.time.Duration.Companion.seconds +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 + + override fun onLoad() { + + } + + var savedPositionedP1: Pair? = null + var savedPosition: SavedPosition? = null + + data class SavedPosition( + val middle: Pair, + val cursor: Pair, + val savedAt: TimeMark = TimeMark.now() + ) + + @JvmStatic + fun saveCursorOriginal(positionedX: Double, positionedY: Double) { + savedPositionedP1 = Pair(positionedX, positionedY) + } + + @JvmStatic + fun loadCursor(middleX: Double, middleY: Double): Pair? { + val lastPosition = savedPosition?.takeIf { it.savedAt.passedTime() < 1.seconds } + 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) { + val cursorPos = assertNotNullOr(savedPositionedP1) { return } + savedPosition = SavedPosition(Pair(middleX, middleY), cursorPos) + } +} diff --git a/src/main/kotlin/moe/nea/firmament/features/inventory/SlotLocking.kt b/src/main/kotlin/moe/nea/firmament/features/inventory/SlotLocking.kt index eab0da0..a601489 100644 --- a/src/main/kotlin/moe/nea/firmament/features/inventory/SlotLocking.kt +++ b/src/main/kotlin/moe/nea/firmament/features/inventory/SlotLocking.kt @@ -33,8 +33,6 @@ import moe.nea.firmament.util.MC import moe.nea.firmament.util.data.ProfileSpecificDataHolder object SlotLocking : FirmamentFeature { - override val name: String - get() = "Slot Locking" override val identifier: String get() = "slot-locking" diff --git a/src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/StorageBackingHandle.kt b/src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/StorageBackingHandle.kt new file mode 100644 index 0000000..5802cb7 --- /dev/null +++ b/src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/StorageBackingHandle.kt @@ -0,0 +1,56 @@ +package moe.nea.firmament.features.inventory.storageoverlay + +import net.minecraft.client.gui.screen.Screen +import net.minecraft.client.gui.screen.ingame.GenericContainerScreen +import net.minecraft.screen.GenericContainerScreenHandler +import moe.nea.firmament.util.ifMatches +import moe.nea.firmament.util.unformattedString + +/** + * A handle representing the state of the "server side" screens. + */ +sealed interface StorageBackingHandle { + + sealed interface HasBackingScreen { + val handler: GenericContainerScreenHandler + } + + /** + * No open "server side" screen. + */ + object None : StorageBackingHandle + + /** + * The main storage overview is open. Clicking on a slot will open that page. This page is accessible via `/storage` + */ + data class Overview(override val handler: GenericContainerScreenHandler) : StorageBackingHandle, HasBackingScreen + + /** + * An individual storage page is open. This may be a backpack or an enderchest page. This page is accessible via + * the [Overview] or via `/ec ` for enderchest pages. + */ + data class Page(override val handler: GenericContainerScreenHandler, val storagePageSlot: StoragePageSlot) : + StorageBackingHandle, HasBackingScreen + + companion object { + private val enderChestName = "^Ender Chest \\(([1-9])/[1-9]\\)$".toRegex() + private val backPackName = "^.+Backpack \\(Slot #([0-9]+)\\)$".toRegex() + + /** + * Parse a screen into a [StorageBackingHandle]. If this returns null it means that the screen is not + * representable as a [StorageBackingHandle], meaning another screen is open, for example the enderchest icon + * selection screen. + */ + fun fromScreen(screen: Screen?): StorageBackingHandle? { + if (screen == null) return None + if (screen !is GenericContainerScreen) return null + val title = screen.title.unformattedString + if (title == "Storage") return Overview(screen.screenHandler) + return title.ifMatches(enderChestName) { + Page(screen.screenHandler, StoragePageSlot.ofEnderChestPage(it.groupValues[1].toInt())) + } ?: title.ifMatches(backPackName) { + Page(screen.screenHandler, StoragePageSlot.ofBackPackPage(it.groupValues[1].toInt())) + } + } + } +} diff --git a/src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/StorageData.kt b/src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/StorageData.kt new file mode 100644 index 0000000..1c2ac08 --- /dev/null +++ b/src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/StorageData.kt @@ -0,0 +1,16 @@ +package moe.nea.firmament.features.inventory.storageoverlay + +import java.util.SortedMap +import kotlinx.serialization.Serializable + +@Serializable +data class StorageData( + val storageInventories: SortedMap = sortedMapOf() +) { + @Serializable + data class StorageInventory( + var title: String, + val slot: StoragePageSlot, + var inventory: VirtualInventory?, + ) +} diff --git a/src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/StorageOverlay.kt b/src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/StorageOverlay.kt new file mode 100644 index 0000000..fae3867 --- /dev/null +++ b/src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/StorageOverlay.kt @@ -0,0 +1,94 @@ +package moe.nea.firmament.features.inventory.storageoverlay + +import java.util.* +import kotlinx.serialization.serializer +import moe.nea.firmament.events.ScreenOpenEvent +import moe.nea.firmament.events.TickEvent +import moe.nea.firmament.features.FirmamentFeature +import moe.nea.firmament.gui.config.ManagedConfig +import moe.nea.firmament.util.data.ProfileSpecificDataHolder + +object StorageOverlay : FirmamentFeature { + + + object Data : ProfileSpecificDataHolder(serializer(), "storage-data", ::StorageData) + + override val identifier: String + get() = "storage-overlay" + + object TConfig : ManagedConfig(identifier) { + val rows by integer("rows", 1, 5) { 3 } + val scrollSpeed by integer("scroll-speed", 1, 50) { 10 } + val inverseScroll by toggle("inverse-scroll") { false } + val padding by integer("padding", 1, 20) { 5 } + val margin by integer("margin", 1, 60) { 20 } + } + + override val config: TConfig + get() = TConfig + + var currentHandler: StorageBackingHandle? = StorageBackingHandle.None + + override fun onLoad() { + ScreenOpenEvent.subscribe { event -> + currentHandler = StorageBackingHandle.fromScreen(event.new) + if (event.old is StorageOverlayScreen && !event.old.isClosing) { + event.old.setHandler(currentHandler) + if (currentHandler != null) + // TODO: Consider instead only replacing rendering? might make a lot of stack handling easier + event.cancel() + } + } + TickEvent.subscribe { + rememberContent(currentHandler ?: return@subscribe) + } + } + + private fun rememberContent(handler: StorageBackingHandle) { + // TODO: Make all of these functions work on deltas / updates instead of the entire contents + val data = Data.data?.storageInventories ?: return + when (handler) { + StorageBackingHandle.None -> {} + is StorageBackingHandle.Overview -> rememberStorageOverview(handler, data) + is StorageBackingHandle.Page -> rememberPage(handler, data) + } + } + + private fun rememberStorageOverview( + handler: StorageBackingHandle.Overview, + data: SortedMap + ) { + for ((index, stack) in handler.handler.stacks.withIndex()) { + // Ignore unloaded item stacks + if (stack.isEmpty) continue + val slot = StoragePageSlot.fromOverviewSlotIndex(index) ?: continue + val isEmpty = stack.item in StorageOverlayScreen.emptyStorageSlotItems + if (slot in data) { + if (isEmpty) + data.remove(slot) + continue + } + if (!isEmpty) { + data[slot] = StorageData.StorageInventory(slot.defaultName(), slot, null) + } + } + } + + private fun rememberPage( + handler: StorageBackingHandle.Page, + data: SortedMap + ) { + // TODO: FIXME: FIXME NOW: Definitely don't copy all of this every tick into persistence + val newStacks = + VirtualInventory(handler.handler.stacks.take(handler.handler.rows * 9).drop(9).map { it.copy() }) + data.compute(handler.storagePageSlot) { slot, existingInventory -> + (existingInventory ?: StorageData.StorageInventory( + slot.defaultName(), + slot, + null + )).also { + it.inventory = newStacks + } + } + } +} diff --git a/src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/StorageOverlayScreen.kt b/src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/StorageOverlayScreen.kt new file mode 100644 index 0000000..1c896db --- /dev/null +++ b/src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/StorageOverlayScreen.kt @@ -0,0 +1,135 @@ +package moe.nea.firmament.features.inventory.storageoverlay + +import moe.nea.firmament.util.MC +import moe.nea.firmament.util.assertNotNullOr +import moe.nea.firmament.util.toShedaniel +import net.minecraft.block.Blocks +import net.minecraft.client.gui.DrawContext +import net.minecraft.client.gui.screen.Screen +import net.minecraft.item.Item +import net.minecraft.item.Items +import net.minecraft.network.packet.c2s.play.CloseHandledScreenC2SPacket +import net.minecraft.text.Text +import net.minecraft.util.DyeColor +import kotlin.math.max + +class StorageOverlayScreen() : Screen(Text.empty()) { + companion object { + val emptyStorageSlotItems = listOf( + Blocks.RED_STAINED_GLASS_PANE.asItem(), + Blocks.BROWN_STAINED_GLASS_PANE.asItem(), + Items.GRAY_DYE + ) + val pageWidth get() = 19 * 9 + } + + private var handler: StorageBackingHandle = StorageBackingHandle.None + val content = StorageOverlay.Data.data ?: StorageData() + var isClosing = false + + private fun discardOldHandle() { + val player = assertNotNullOr(MC.player) { return } + val handle = this.handler + if (handle is StorageBackingHandle.HasBackingScreen) { + player.networkHandler.sendPacket(CloseHandledScreenC2SPacket(handle.handler.syncId)) + if (player.currentScreenHandler === handle.handler) { + player.currentScreenHandler = player.playerScreenHandler + } + } + } + + fun setHandler(handler: StorageBackingHandle?) { + discardOldHandle() + if (handler != null) + this.handler = handler + } + + var scroll = 0 + var lastRenderedHeight = 0 + + override fun render(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) { + super.render(context, mouseX, mouseY, delta) + context.fill(0, 0, width, height, 0x90000000.toInt()) + layoutedForEach { (key, value), offsetX, offsetY -> + context.matrices.push() + context.matrices.translate(offsetX.toFloat(), offsetY.toFloat(), 0F) + renderStoragePage(context, value, mouseX - offsetX, mouseY - offsetY) + context.matrices.pop() + } + } + + inline fun layoutedForEach(onEach: (data: Pair, offsetX: Int, offsetY: Int) -> Unit) { + var offsetY = 0 + var currentMaxHeight = StorageOverlay.config.margin - StorageOverlay.config.padding - scroll + var totalHeight = -currentMaxHeight + content.storageInventories.onEachIndexed { index, (key, value) -> + val pageX = (index % StorageOverlay.config.rows) + if (pageX == 0) { + currentMaxHeight += StorageOverlay.config.padding + offsetY += currentMaxHeight + totalHeight += currentMaxHeight + currentMaxHeight = 0 + } + val xPosition = + width / 2 - (StorageOverlay.config.rows * (pageWidth + StorageOverlay.config.padding) - StorageOverlay.config.padding) / 2 + pageX * (pageWidth + StorageOverlay.config.padding) + onEach(Pair(key, value), xPosition, offsetY) + val height = getStorePageHeight(value) + currentMaxHeight = max(currentMaxHeight, height) + } + lastRenderedHeight = totalHeight + currentMaxHeight + } + + override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean { + layoutedForEach { (k, p), x, y -> + val rx = mouseX - x + val ry = mouseY - y + if (rx in (0.0..pageWidth.toDouble()) && ry in (0.0..getStorePageHeight(p).toDouble())) { + close() + k.navigateTo() + return true + } + } + return super.mouseClicked(mouseX, mouseY, button) + } + + fun getStorePageHeight(page: StorageData.StorageInventory): Int { + return page.inventory?.rows?.let { it * 19 + MC.font.fontHeight + 2 } ?: 60 + } + + override fun mouseScrolled(mouseX: Double, mouseY: Double, amount: Double): Boolean { + scroll = + (scroll + amount * StorageOverlay.config.scrollSpeed * + (if (StorageOverlay.config.inverseScroll) 1 else -1)).toInt() + .coerceAtMost(lastRenderedHeight - height + 2 * StorageOverlay.config.margin).coerceAtLeast(0) + return true + } + + private fun renderStoragePage(context: DrawContext, page: StorageData.StorageInventory, mouseX: Int, mouseY: Int) { + context.drawText(MC.font, page.title, 2, 2, -1, true) + val inventory = page.inventory + if (inventory == null) { + // TODO: Missing texture + context.fill(0, 0, pageWidth, 60, DyeColor.RED.toShedaniel().darker(4.0).color) + context.drawCenteredTextWithShadow(MC.font, Text.literal("Not loaded yet"), pageWidth / 2, 30, -1) + return + } + + for ((index, stack) in inventory.stacks.withIndex()) { + val x = (index % 9) * 19 + val y = (index / 9) * 19 + MC.font.fontHeight + 2 + if (((mouseX - x) in 0 until 18) && ((mouseY - y) in 0 until 18)) { + context.fill(x, y, x + 18, y + 18, 0x80808080.toInt()) + } else { + context.fill(x, y, x + 18, y + 18, 0x40808080.toInt()) + } + context.drawItem(stack, x + 1, y + 1) + context.drawItemInSlot(MC.font, stack, x + 1, y + 1) + } + } + + override fun close() { + discardOldHandle() + isClosing = true + super.close() + } +} diff --git a/src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/StoragePageSlot.kt b/src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/StoragePageSlot.kt new file mode 100644 index 0000000..19afeef --- /dev/null +++ b/src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/StoragePageSlot.kt @@ -0,0 +1,64 @@ +package moe.nea.firmament.features.inventory.storageoverlay + +import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializable +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import moe.nea.firmament.util.MC + +@Serializable(with = StoragePageSlot.Serializer::class) +data class StoragePageSlot(val index: Int) : Comparable { + object Serializer : KSerializer { + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("StoragePageSlot", PrimitiveKind.INT) + + override fun deserialize(decoder: Decoder): StoragePageSlot { + return StoragePageSlot(decoder.decodeInt()) + } + + override fun serialize(encoder: Encoder, value: StoragePageSlot) { + encoder.encodeInt(value.index) + } + } + + init { + assert(index in 0 until (3 * 9)) + } + + val isEnderChest get() = index < 9 + val isBackPack get() = !isEnderChest + val slotIndexInOverviewPage get() = if (isEnderChest) index + 9 else index + 18 + fun defaultName(): String = if (isEnderChest) "Ender Chest #${index + 1}" else "Backpack #${index - 9 + 1}" + + fun navigateTo() { + if (isBackPack) { + MC.sendCommand("backpack ${index - 9 + 1}") + } else { + MC.sendCommand("enderchest ${index + 1}") + } + } + + companion object { + fun fromOverviewSlotIndex(slot: Int): StoragePageSlot? { + if (slot in 9 until 18) return StoragePageSlot(slot - 9) + if (slot in 27 until 45) return StoragePageSlot(slot - 27 + 9) + return null + } + + fun ofEnderChestPage(slot: Int): StoragePageSlot { + assert(slot in 1..9) + return StoragePageSlot(slot - 1) + } + + fun ofBackPackPage(slot: Int): StoragePageSlot { + assert(slot in 1..18) + return StoragePageSlot(slot - 1 + 9) + } + } + + override fun compareTo(other: StoragePageSlot): Int { + return this.index - other.index + } +} diff --git a/src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/VirtualInventory.kt b/src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/VirtualInventory.kt new file mode 100644 index 0000000..28280f9 --- /dev/null +++ b/src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/VirtualInventory.kt @@ -0,0 +1,53 @@ +package moe.nea.firmament.features.inventory.storageoverlay + +import io.ktor.util.decodeBase64Bytes +import io.ktor.util.encodeBase64 +import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializable +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import net.minecraft.item.ItemStack +import net.minecraft.nbt.NbtCompound +import net.minecraft.nbt.NbtIo +import net.minecraft.nbt.NbtList +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream + +@Serializable(with = VirtualInventory.Serializer::class) +data class VirtualInventory( + val stacks: List +) { + val rows = stacks.size / 9 + + init { + assert(stacks.size % 9 == 0) + assert(stacks.size / 9 in 1..5) + } + + + object Serializer : KSerializer { + const val INVENTORY = "INVENTORY" + override val descriptor: SerialDescriptor + get() = PrimitiveSerialDescriptor("VirtualInventory", PrimitiveKind.STRING) + + override fun deserialize(decoder: Decoder): VirtualInventory { + val s = decoder.decodeString() + val n = NbtIo.readCompressed(ByteArrayInputStream(s.decodeBase64Bytes())) + val items = n.getList(INVENTORY, NbtCompound.COMPOUND_TYPE.toInt()) + return VirtualInventory(items.map { ItemStack.fromNbt(it as NbtCompound) }) + } + + override fun serialize(encoder: Encoder, value: VirtualInventory) { + val list = NbtList() + value.stacks.forEach { + list.add(NbtCompound().also(it::writeNbt)) + } + val baos = ByteArrayOutputStream() + NbtIo.writeCompressed(NbtCompound().also { it.put(INVENTORY, list) }, baos) + encoder.encodeString(baos.toByteArray().encodeBase64()) + } + } +} diff --git a/src/main/kotlin/moe/nea/firmament/features/world/FairySouls.kt b/src/main/kotlin/moe/nea/firmament/features/world/FairySouls.kt index d9d7f3d..91821f6 100644 --- a/src/main/kotlin/moe/nea/firmament/features/world/FairySouls.kt +++ b/src/main/kotlin/moe/nea/firmament/features/world/FairySouls.kt @@ -21,7 +21,14 @@ package moe.nea.firmament.features.world import io.github.moulberry.repo.data.Coordinate import kotlinx.serialization.Serializable import kotlinx.serialization.serializer -import net.minecraft.util.math.Direction +import net.minecraft.client.render.RenderLayer +import net.minecraft.client.render.RenderLayer.ALWAYS_DEPTH_TEST +import net.minecraft.client.render.RenderLayer.MultiPhaseParameters +import net.minecraft.client.render.RenderPhase +import net.minecraft.client.render.VertexFormat +import net.minecraft.client.render.VertexFormats +import net.minecraft.text.Text +import net.minecraft.util.math.Vec3d import moe.nea.firmament.events.ServerChatLineReceivedEvent import moe.nea.firmament.events.SkyblockServerUpdateEvent import moe.nea.firmament.events.WorldRenderLastEvent @@ -32,6 +39,7 @@ import moe.nea.firmament.util.MC import moe.nea.firmament.util.SBData import moe.nea.firmament.util.blockPos import moe.nea.firmament.util.data.ProfileSpecificDataHolder +import moe.nea.firmament.util.render.RenderInWorldContext import moe.nea.firmament.util.render.RenderInWorldContext.Companion.renderInWorld import moe.nea.firmament.util.unformattedString @@ -59,7 +67,6 @@ object FairySouls : FirmamentFeature { } - override val name: String get() = "Fairy Souls" override val identifier: String get() = "fairy-souls" val playerReach = 5 @@ -107,6 +114,22 @@ object FairySouls : FirmamentFeature { updateMissingSouls() } + val NODEPTH: RenderLayer = RenderLayer.of( + "firmamentnodepth", + VertexFormats.POSITION_COLOR_TEXTURE, + VertexFormat.DrawMode.QUADS, + 256, + true, + true, + MultiPhaseParameters.builder() + .program(RenderPhase.COLOR_PROGRAM) + .writeMaskState(RenderPhase.COLOR_MASK) + .depthTest(ALWAYS_DEPTH_TEST) + .cull(RenderPhase.DISABLE_CULLING) + .layering(RenderLayer.VIEW_OFFSET_Z_LAYERING) + .target(RenderPhase.MAIN_TARGET) + .build(true) + ) override fun onLoad() { SkyblockServerUpdateEvent.subscribe { @@ -127,7 +150,8 @@ object FairySouls : FirmamentFeature { } WorldRenderLastEvent.subscribe { if (!TConfig.displaySouls) return@subscribe - renderInWorld(it.matrices, it.camera) { + renderInWorld(it) { + text(Vec3d(0.0, 0.0, 0.0), Text.literal("Test String") , Text.literal("Short"), Text.literal("just lik"), verticalAlign = RenderInWorldContext.VerticalAlign.BOTTOM) color(1F, 1F, 0F, 0.8F) currentMissingSouls.forEach { block(it.blockPos) -- cgit