package moe.nea.firmament.features.mining import me.shedaniel.math.Rectangle import kotlinx.serialization.Serializable import kotlin.time.Duration.Companion.seconds import net.minecraft.block.Blocks import net.minecraft.client.gui.DrawContext import net.minecraft.client.gui.screen.ingame.HandledScreen import net.minecraft.entity.player.PlayerInventory import net.minecraft.item.Items import net.minecraft.screen.GenericContainerScreenHandler import net.minecraft.screen.ScreenHandler import net.minecraft.screen.slot.Slot import net.minecraft.screen.slot.SlotActionType import net.minecraft.text.Text import moe.nea.firmament.Firmament import moe.nea.firmament.annotations.Subscribe import moe.nea.firmament.commands.thenExecute import moe.nea.firmament.events.ChestInventoryUpdateEvent import moe.nea.firmament.events.CommandEvent import moe.nea.firmament.events.ScreenChangeEvent import moe.nea.firmament.events.SlotRenderEvents import moe.nea.firmament.gui.config.ManagedConfig import moe.nea.firmament.mixins.accessor.AccessorHandledScreen import moe.nea.firmament.util.ClipboardUtils import moe.nea.firmament.util.MC import moe.nea.firmament.util.TemplateUtil import moe.nea.firmament.util.TimeMark import moe.nea.firmament.util.customgui.CustomGui import moe.nea.firmament.util.customgui.customGui import moe.nea.firmament.util.mc.CommonTextures import moe.nea.firmament.util.mc.SlotUtils.clickRightMouseButton import moe.nea.firmament.util.mc.displayNameAccordingToNbt import moe.nea.firmament.util.unformattedString import moe.nea.firmament.util.useMatch object HotmPresets { object Config : ManagedConfig("hotm-presets", Category.MINING) { } val SHARE_PREFIX = "FIRMHOTM/" @Serializable data class HotmPreset( val perks: List, ) @Serializable data class PerkPreset(val perkName: String) var hotmCommandSent = TimeMark.farPast() val hotmInventoryName = "Heart of the Mountain" @Subscribe fun onScreenOpen(event: ScreenChangeEvent) { val title = event.new?.title?.unformattedString if (title != hotmInventoryName) return val screen = event.new as? HandledScreen<*> ?: return val oldHandler = (event.old as? HandledScreen<*>)?.customGui if (oldHandler is HotmScrollPrompt) { event.new.customGui = oldHandler oldHandler.setNewScreen(screen) return } if (hotmCommandSent.passedTime() > 5.seconds) return hotmCommandSent = TimeMark.farPast() screen.customGui = HotmScrollPrompt(screen) } class HotmScrollPrompt(var screen: HandledScreen<*>) : CustomGui() { var bounds = Rectangle( 0, 0, 0, 0 ) fun setNewScreen(screen: HandledScreen<*>) { this.screen = screen onInit() hasScrolled = false } override fun render(drawContext: DrawContext, delta: Float, mouseX: Int, mouseY: Int) { drawContext.drawGuiTexture( CommonTextures.genericWidget(), bounds.x, bounds.y, 0, bounds.width, bounds.height, ) drawContext.drawCenteredTextWithShadow( MC.font, if (hasAll) { Text.translatable("firmament.hotmpreset.copied") } else if (!hasScrolled) { Text.translatable("firmament.hotmpreset.scrollprompt") } else { Text.translatable("firmament.hotmpreset.scrolled") }, bounds.centerX, bounds.centerY - 5, -1 ) } var hasScrolled = false var hasAll = false override fun mouseClick(mouseX: Double, mouseY: Double, button: Int): Boolean { if (!hasScrolled) { val slot = screen.screenHandler.getSlot(8) println("Clicking ${slot.stack}") slot.clickRightMouseButton(screen.screenHandler) } hasScrolled = true return super.mouseClick(mouseX, mouseY, button) } override fun shouldDrawForeground(): Boolean { return false } override fun getBounds(): List { return listOf(bounds) } override fun onInit() { bounds = Rectangle( screen.width / 2 - 150, screen.height / 2 - 100, 300, 200 ) val screen = screen as AccessorHandledScreen screen.x_Firmament = bounds.x screen.y_Firmament = bounds.y screen.backgroundWidth_Firmament = bounds.width screen.backgroundHeight_Firmament = bounds.height } override fun moveSlot(slot: Slot) { slot.x = -10000 } val coveredRows = mutableSetOf() val unlockedPerks = mutableSetOf() val allRows = (1..10).toSet() fun onNewItems(event: ChestInventoryUpdateEvent) { val handler = screen.screenHandler as? GenericContainerScreenHandler ?: return for (it in handler.slots) { if (it.inventory is PlayerInventory) continue val stack = it.stack val name = stack.displayNameAccordingToNbt.unformattedString tierRegex.useMatch(name) { coveredRows.add(group("tier").toInt()) } if (stack.item == Items.DIAMOND || stack.item == Items.EMERALD || stack.item == Blocks.EMERALD_BLOCK.asItem() ) { unlockedPerks.add(name) } } if (allRows == coveredRows) { ClipboardUtils.setTextContent(TemplateUtil.encodeTemplate(SHARE_PREFIX, HotmPreset( unlockedPerks.map { PerkPreset(it) } ))) hasAll = true } } } val tierRegex = "Tier (?[0-9]+)".toPattern() var highlightedPerks: Set = emptySet() @Subscribe fun onSlotUpdates(event: ChestInventoryUpdateEvent) { val customGui = (event.inventory as? HandledScreen<*>)?.customGui if (customGui is HotmScrollPrompt) { customGui.onNewItems(event) } } @Subscribe fun resetOnScreen(event: ScreenChangeEvent) { if (event.new != null && event.new.title.unformattedString != hotmInventoryName) { highlightedPerks = emptySet() } } @Subscribe fun onSlotRender(event: SlotRenderEvents.Before) { if (hotmInventoryName == MC.screenName && event.slot.stack.displayNameAccordingToNbt.unformattedString in highlightedPerks ) { event.highlight(MC.guiAtlasManager.getSprite(Firmament.identifier("hotm_perk_preset"))) } } @Subscribe fun onCommand(event: CommandEvent.SubCommand) { event.subcommand("exporthotm") { thenExecute { hotmCommandSent = TimeMark.now() MC.sendCommand("hotm") source.sendFeedback(Text.translatable("firmament.hotmpreset.openinghotm")) } } event.subcommand("importhotm") { thenExecute { val template = TemplateUtil.maybeDecodeTemplate(SHARE_PREFIX, ClipboardUtils.getTextContents()) if (template == null) { source.sendFeedback(Text.translatable("firmament.hotmpreset.failedimport")) } else { highlightedPerks = template.perks.mapTo(mutableSetOf()) { it.perkName } source.sendFeedback(Text.translatable("firmament.hotmpreset.okayimport")) MC.sendCommand("hotm") } } } } }