/* * Copyright (C) 2023 NotEnoughUpdates contributors * * This file is part of NotEnoughUpdates. * * NotEnoughUpdates is free software: you can redistribute it * and/or modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * NotEnoughUpdates is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with NotEnoughUpdates. If not, see . */ package io.github.moulberry.notenoughupdates.miscgui import io.github.moulberry.notenoughupdates.NotEnoughUpdates import io.github.moulberry.notenoughupdates.autosubscribe.NEUAutoSubscribe import io.github.moulberry.notenoughupdates.util.Utils import net.minecraft.client.Minecraft import net.minecraft.client.gui.GuiScreen import net.minecraft.client.renderer.GlStateManager import net.minecraft.item.ItemStack import net.minecraft.util.ResourceLocation import net.minecraftforge.fml.common.eventhandler.SubscribeEvent import net.minecraftforge.fml.common.gameevent.TickEvent import net.minecraftforge.fml.common.registry.GameRegistry import org.lwjgl.opengl.GL11 import java.awt.Color import java.io.File import kotlin.math.ceil class DynamicLightItemsEditor() : GuiScreen() { val isOfLightsEnabled = run { val ofDynamicLights = File("optionsof.txt").takeIf { it.exists() }?.readLines() ?.find { it.startsWith("ofDynamicLights:") } ofDynamicLights != null && !ofDynamicLights.endsWith(":3") } val background = ResourceLocation("notenoughupdates:dynamic_light_items_editor.png") val enabledButton = ResourceLocation("notenoughupdates:enabled_button.png") val disabledButton = ResourceLocation("notenoughupdates:disabled_button.png") val chestGui = ResourceLocation("textures/gui/container/generic_54.png") val widgets = ResourceLocation("textures/gui/widgets.png") val help = ResourceLocation("notenoughupdates:help.png") var xSize = 217 var ySize = 88 var guiLeft = 0 var guiTop = 0 var stackToRender: String? = null var itemSelected: String? = null override fun drawScreen(mouseX: Int, mouseY: Int, partialTicks: Float) { drawDefaultBackground() val numOfItems = NotEnoughUpdates.INSTANCE.config.hidden.dynamicLightItems.size val numOfRows = if (didApplyMixin) ceil(numOfItems / 9f).toInt() else 0 ySize = 70 + 18 * numOfRows guiLeft = (width - xSize) / 2 guiTop = (height - ySize) / 2 // Top and bottom half of gui Minecraft.getMinecraft().textureManager.bindTexture(background) Utils.drawTexturedRect(guiLeft.toFloat(), guiTop.toFloat(), xSize.toFloat(), 24F, 0F, 1F, 0F, 24 / 88f, GL11.GL_NEAREST) Utils.drawTexturedRect(guiLeft.toFloat(), (guiTop + ySize - 46).toFloat(), xSize.toFloat(), 46F, 0F, 1F, 42 / 88f, 1F, GL11.GL_NEAREST) fontRendererObj.drawString("Dynamic Light Items Editor", guiLeft + 10, guiTop + 7, 4210752) GlStateManager.color(1f, 1f, 1f, 1f) Minecraft.getMinecraft().textureManager.bindTexture(help) Utils.drawTexturedRect((guiLeft + xSize + 3).toFloat(), guiTop.toFloat(), 16F, 16F, GL11.GL_NEAREST) if (mouseX >= guiLeft + xSize + 3 && mouseX <= guiLeft + xSize + 19 && mouseY >= guiTop && mouseY <= guiTop + 16) { val tooltip = listOf( "§bDynamic Light Item Editor", "§eWhat is this?", "§eNEU makes use of OptiFine's feature of certain items", "§eemitting dynamic light. By default OptiFine only implements", "§ethis feature for a select few minecraft items.", "", "§eThis editor however, allows you to add specific skyblock", "§eitems that will emit dynamic light when held. Simply hold the", "§eitem you wish to add, then open this menu again and click", "§e'Add Held Item', now if you have OptiFine installed and the", "§edynamic lights option enabled, the added items will emit light!", "", "§eTo remove an item, click the item in this menu and click", "§ethe 'Remove Item' button in the bottom right.", ) Utils.drawHoveringText(tooltip, mouseX, mouseY, width, height, -1) } if (!didApplyMixin) { fontRendererObj.drawString("Could not find OptiFine!", guiLeft + 50, guiTop + 22, Color.RED.rgb) fontRendererObj.drawString("Go to #neu-support in", guiLeft + 50, guiTop + 32, Color.RED.rgb) fontRendererObj.drawString("the discord for help", guiLeft + 52, guiTop + 42, Color.RED.rgb) return } if (!isOfLightsEnabled) { fontRendererObj.drawString("Dynamic lights have", guiLeft + 50, guiTop + 22, Color.RED.rgb) fontRendererObj.drawString("been disabled in OptiFine.", guiLeft + 50, guiTop + 32, Color.RED.rgb) fontRendererObj.drawString("Enable in Video Settings.", guiLeft + 52, guiTop + 42, Color.RED.rgb) return } // Buttons GlStateManager.color(1f, 1f, 1f, 1f) Minecraft.getMinecraft().textureManager.bindTexture(enabledButton) Utils.drawTexturedRect(guiLeft.toFloat() + 15, (guiTop + ySize - 32).toFloat(), 88F, 20F, 0F, 1F, 0F, 1F, GL11.GL_NEAREST) if (itemSelected != null) { Minecraft.getMinecraft().textureManager.bindTexture(enabledButton) } else { Minecraft.getMinecraft().textureManager.bindTexture(disabledButton) } Utils.drawTexturedRect(guiLeft.toFloat() + 114, (guiTop + ySize - 32).toFloat(), 88F, 20F, 0F, 1F, 0F, 1F, GL11.GL_NEAREST) fontRendererObj.drawString("Add Held Item", guiLeft + 27, guiTop + ySize - 26, 4210752) fontRendererObj.drawString("Remove Item", guiLeft + 130, guiTop + ySize - 26, 4210752) GlStateManager.color(1f, 1f, 1f, 1f) // Add in some part of the gui for every row Minecraft.getMinecraft().textureManager.bindTexture(background) for (i in 0 until numOfRows) { Utils.drawTexturedRect(guiLeft.toFloat(), ((guiTop + 24) + (i * 18)).toFloat(), xSize.toFloat(), 18f, 0f, 1f, 24 / 88f, 42 / 88f, GL11.GL_NEAREST) } var hoveredItem: String? = null var selectedPosition: Pair = Pair(-999, -999) // Draw a slot for each item and the ItemStack for ((index, item) in NotEnoughUpdates.INSTANCE.config.hidden.dynamicLightItems.withIndex()) { val i = index % 9 val j = index / 9 GlStateManager.color(1f, 1f, 1f, 1f) Minecraft.getMinecraft().textureManager.bindTexture(chestGui) drawTexturedModalRect(guiLeft + 27 + i % 9 * 18, guiTop + 24 + j * 18, 7, 17, 18, 18) val itemStack = resolveItemStack(item) ?: return Utils.drawItemStack(itemStack, guiLeft + 28 + i % 9 * 18, guiTop + 25 + j * 18) if (mouseX >= guiLeft + 27 + i % 9 * 18 && mouseX <= guiLeft + 45 + i % 9 * 18) { if (mouseY >= guiTop + 24 + j * 18 && mouseY <= guiTop + 42 + j * 18) { hoveredItem = item val tooltip = itemStack.getTooltip(Minecraft.getMinecraft().thePlayer, false) Utils.drawHoveringText(tooltip, mouseX, mouseY, width, height, -1) } } if (itemSelected != null && itemSelected.equals(item)) { // Save the position, so when we render the selected box its renders on top of everything selectedPosition = Pair(guiLeft + 24 + i % 9 * 18, guiTop + 21 + j * 18) } } stackToRender = hoveredItem GlStateManager.color(1f, 1f, 1f, 1f) Minecraft.getMinecraft().textureManager.bindTexture(widgets) drawTexturedModalRect(selectedPosition.first, selectedPosition.second, 0, 22, 24, 24) super.drawScreen(mouseX, mouseY, partialTicks) } override fun mouseClicked(mouseX: Int, mouseY: Int, mouseButton: Int) { if (didApplyMixin) { // Add Held Item button if (mouseX >= guiLeft + 15 && mouseX <= guiLeft + 103 && mouseY >= (guiTop + ySize - 32) && mouseY <= (guiTop + ySize - 12)) { val heldItem = Minecraft.getMinecraft().thePlayer.heldItem if (heldItem == null) { Utils.addChatMessage("§c[NEU] You can't add your hand to the list of dynamic light items.") return } val internalName = resolveInternalName(heldItem) if (internalName == null) { Utils.addChatMessage("§c[NEU] Couldn't resolve an internal name for this item!") return } NotEnoughUpdates.INSTANCE.config.hidden.dynamicLightItems.add(internalName) } // Remove Item button if (mouseX >= guiLeft + 114 && mouseX <= guiLeft + 202 && mouseY >= guiTop + ySize - 32 && mouseY <= guiTop + ySize - 12 && itemSelected != null) { NotEnoughUpdates.INSTANCE.config.hidden.dynamicLightItems.remove(itemSelected) itemSelected = null } if (stackToRender != null) { itemSelected = stackToRender } } super.mouseClicked(mouseX, mouseY, mouseButton) } @NEUAutoSubscribe companion object { var hasAttemptedToLoadOptifine = false @SubscribeEvent fun autoloadOptifine(event: TickEvent) { if (Minecraft.getMinecraft().thePlayer == null || hasAttemptedToLoadOptifine) return try { Class.forName("net.optifine.DynamicLights") println("Loaded dynamic lights successfully.") } catch (_: Exception) { } hasAttemptedToLoadOptifine = true if (!didApplyMixin) { println("Loaded optifine dynamic lights class without applying mixin!") } } @JvmStatic var didApplyMixin = false fun resolveItemStack(internalName: String): ItemStack? { var itemStack = NotEnoughUpdates.INSTANCE.manager .createItemResolutionQuery() .withKnownInternalName(internalName) .resolveToItemStack() if (itemStack == null) { // Try resolve the item stack through forge itemStack = GameRegistry.makeItemStack(internalName, 0, 1, null) if (itemStack == null) { Utils.addChatMessage("§c[NEU] Couldn't resolve the ItemStack for $internalName") return null } } return itemStack } @JvmStatic fun resolveInternalName(itemStack: ItemStack): String? { var internalName = NotEnoughUpdates.INSTANCE.manager.createItemResolutionQuery().withItemStack(itemStack) .resolveInternalName() if (internalName == null) { // If resolving internal name failed, the item may be a minecraft item internalName = itemStack.item?.registryName if (internalName == null) { // Check if minecraft searching also fails // Leave error handling for caller since this method is also called in MixinOFDynamicLights which // is run every tick, and we don't want to flood the chat return null } } return internalName } @JvmStatic fun findDynamicLightItems(itemStack: ItemStack): Int { val internalName: String = resolveInternalName(itemStack) ?: return 0 if (NotEnoughUpdates.INSTANCE.config.hidden.dynamicLightItems.contains(internalName)) { return 15 } return 0 } } }