/* * Copyright (C) 2022 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.itemeditor; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; import io.github.moulberry.notenoughupdates.NotEnoughUpdates; import io.github.moulberry.notenoughupdates.core.util.lerp.LerpingInteger; import io.github.moulberry.notenoughupdates.util.Utils; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.gui.ScaledResolution; import net.minecraft.client.renderer.GlStateManager; import net.minecraft.client.renderer.RenderHelper; import net.minecraft.client.renderer.entity.RenderItem; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.nbt.JsonToNBT; import net.minecraft.nbt.NBTException; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.nbt.NBTTagString; import net.minecraft.util.ResourceLocation; import org.lwjgl.input.Keyboard; import org.lwjgl.input.Mouse; import org.lwjgl.opengl.GL11; import java.awt.*; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.function.Supplier; import static io.github.moulberry.notenoughupdates.itemeditor.GuiElementTextField.COLOUR; import static io.github.moulberry.notenoughupdates.itemeditor.GuiElementTextField.FORCE_CAPS; import static io.github.moulberry.notenoughupdates.itemeditor.GuiElementTextField.MULTILINE; import static io.github.moulberry.notenoughupdates.itemeditor.GuiElementTextField.NO_SPACE; import static io.github.moulberry.notenoughupdates.itemeditor.GuiElementTextField.NUM_ONLY; public class NEUItemEditor extends GuiScreen { private final List options = new ArrayList<>(); private final List rightOptions = new ArrayList<>(); private JsonObject item; private final JsonObject savedRepoItem; private static final int PADDING = 10; private static final int SCROLL_AMOUNT = 20; private final LerpingInteger scrollHeight = new LerpingInteger(0); private final Supplier internalName; private final Supplier itemId; private final Supplier displayName; private final Supplier lore; private final Supplier craftText; private final Supplier infoType; private final Supplier info; private final Supplier clickCommand; private final Supplier damage; private NBTTagCompound nbtTag; private int saved = 0; public NEUItemEditor(String internalName, JsonObject item) { this.item = item; if (item.has("nbttag")) { try { nbtTag = JsonToNBT.getTagFromJson(item.get("nbttag").getAsString()); } catch (NBTException ignored) { } } NBTTagCompound extraAttributes = nbtTag.getCompoundTag("ExtraAttributes"); extraAttributes.removeTag("uuid"); extraAttributes.removeTag("timestamp"); if (extraAttributes.hasKey("petInfo")) { String petInfo = extraAttributes.getString("petInfo"); JsonObject jsonObject = NotEnoughUpdates.INSTANCE.manager.gson.fromJson(petInfo, JsonObject.class); jsonObject.remove("heldItem"); jsonObject.add("exp", new JsonPrimitive(0)); jsonObject.add("candyUsed", new JsonPrimitive(0)); extraAttributes.setString("petInfo", jsonObject.toString()); } savedRepoItem = NotEnoughUpdates.INSTANCE.manager.getItemInformation().getOrDefault(internalName, null); internalName = internalName == null ? "" : internalName; options.add(new GuiElementText("Internal Name: ", Color.WHITE.getRGB())); this.internalName = addTextFieldWithSupplier(internalName, NO_SPACE | FORCE_CAPS); options.add(new GuiElementText("Item ID: ", Color.WHITE.getRGB())); String itemid = item.has("itemid") ? item.get("itemid").getAsString() : ""; this.itemId = addTextFieldWithSupplier(itemid, NO_SPACE); options.add(new GuiElementText("Display name: ", Color.WHITE.getRGB())); String displayName = item.has("displayname") ? item.get("displayname").getAsString() : ""; if (displayName.startsWith("§f§f")) { displayName = displayName.substring(4); } this.displayName = addTextFieldWithSupplier(displayName, COLOUR); options.add(new GuiElementText("Lore: ", Color.WHITE.getRGB())); JsonElement loreElement = getItemInfo("lore"); JsonArray lore = loreElement != null ? loreElement.getAsJsonArray() : new JsonArray(); String[] loreA = new String[lore.size()]; for (int i = 0; i < lore.size(); i++) loreA[i] = lore.get(i).getAsString(); this.lore = addTextFieldWithSupplier(String.join("\n", loreA), COLOUR | MULTILINE); options.add(new GuiElementText("Craft text: ", Color.WHITE.getRGB())); JsonElement craftTextElement = getItemInfo("crafttext"); String craftText = craftTextElement != null ? craftTextElement.getAsString() : ""; this.craftText = addTextFieldWithSupplier(craftText, COLOUR); options.add(new GuiElementText("Info type: ", Color.WHITE.getRGB())); JsonElement infoTypeElement = getItemInfo("infoType"); String infoType = infoTypeElement != null ? infoTypeElement.getAsString() : ""; this.infoType = addTextFieldWithSupplier(infoType, NO_SPACE | FORCE_CAPS); options.add(new GuiElementText("Additional information: ", Color.WHITE.getRGB())); JsonElement infoElement = getItemInfo("info"); JsonArray info = infoElement != null ? infoElement.getAsJsonArray() : new JsonArray(); String[] infoA = new String[info.size()]; for (int i = 0; i < info.size(); i++) infoA[i] = info.get(i).getAsString(); this.info = addTextFieldWithSupplier(String.join("\n", infoA), COLOUR | MULTILINE); options.add(new GuiElementText("Click-command (viewrecipe or viewpotion): ", Color.WHITE.getRGB())); JsonElement clickCommandElement = getItemInfo("clickcommand"); String clickCommand = clickCommandElement != null ? clickCommandElement.getAsString() : ""; this.clickCommand = addTextFieldWithSupplier(clickCommand, NO_SPACE); options.add(new GuiElementText("Damage: ", Color.WHITE.getRGB())); JsonElement damageElement = getItemInfo("damage"); String damage = damageElement != null ? damageElement.getAsString() : ""; this.damage = addTextFieldWithSupplier(damage, NO_SPACE | NUM_ONLY); rightOptions.add(new GuiElementButton("Close (discards changes)", Color.LIGHT_GRAY.getRGB(), () -> Minecraft.getMinecraft().displayGuiScreen(null))); rightOptions.add(new GuiElementText("", Color.WHITE.getRGB())); GuiElementButton button = new Object() { //Used to make the compiler shut the fuck up final GuiElementButton b = new GuiElementButton("Save to local disk", Color.GREEN.getRGB(), new Runnable() { public void run() { if (save()) { b.setText(saved == 0 ? "Saved" : "Saved (" + saved + ")"); saved++; } else { b.setText("Saving FAILED!"); } } }); }.b; rightOptions.add(button); rightOptions.add(new GuiElementButton("Remove enchants", Color.RED.getRGB(), () -> { nbtTag.removeTag("ench"); extraAttributes.removeTag("enchantments"); })); rightOptions.add(new GuiElementButton( "Add enchant glint", Color.ORANGE.getRGB(), () -> nbtTag.setTag("ench", new NBTTagList()) )); resetScrollToTop(); if (savedRepoItem != null) { this.item = savedRepoItem; } else { this.item = item; } } /** * Creates a new ItemEditor object and instantly saves. This will update the lore/nbt tag and other item infos in the repo without removing things like recipes and wiki URLs and without showing the GUI * * @param internalName the internal name for the item * @param item the Item as a JsonObject * @return weather the saving was successful or not * @see io.github.moulberry.notenoughupdates.NEUManager#getInternalNameForItem(ItemStack) * @see io.github.moulberry.notenoughupdates.NEUManager#getJsonForItem(ItemStack) */ public static boolean saveOnly(String internalName, JsonObject item) { NEUItemEditor editor = new NEUItemEditor(internalName, item); return editor.save(); } /** * Returns the Element from the item being edited or the already existing item, prioritizing the item currently being edited * * @param key The JSON key * @return the element found, or null */ private JsonElement getItemInfo(String key) { if (item.has(key)) { return item.get(key); } else if (savedRepoItem != null && savedRepoItem.has(key)) { return savedRepoItem.get(key); } else { return null; } } public boolean save() { int damageI = 0; try { damageI = Integer.parseInt(damage.get()); } catch (NumberFormatException ignored) { } resyncNbttag(); String[] infoA = info.get().trim().split("\n"); if (infoA.length == 0 || infoA[0].isEmpty()) { infoA = new String[0]; } NotEnoughUpdates.INSTANCE.manager.removeItemFromCache(internalName.get()); return NotEnoughUpdates.INSTANCE.manager.writeItemJson( item, internalName.get(), itemId.get(), displayName.get(), lore.get().split("\n"), craftText.get(), infoType.get(), infoA, clickCommand.get(), damageI, nbtTag ); } public Supplier addTextFieldWithSupplier(String initialText, int options) { GuiElementTextField textField = new GuiElementTextField(initialText, options); this.options.add(textField); return textField::toString; } public void resyncNbttag() { if (nbtTag == null) nbtTag = new NBTTagCompound(); //Item lore NBTTagList list = new NBTTagList(); for (String lore : this.lore.get().split("\n")) { list.appendTag(new NBTTagString(lore)); } NBTTagCompound display = nbtTag.getCompoundTag("display"); display.setTag("Lore", list); //Name display.setString("Name", displayName.get()); nbtTag.setTag("display", display); //Internal ID NBTTagCompound ea = nbtTag.getCompoundTag("ExtraAttributes"); ea.setString("id", internalName.get()); nbtTag.setTag("ExtraAttributes", ea); } public void resetScrollToTop() { int totalHeight = PADDING; for (GuiElement gui : options) { totalHeight += gui.getHeight(); } ScaledResolution scaledresolution = new ScaledResolution(Minecraft.getMinecraft()); int height = scaledresolution.getScaledHeight(); scrollHeight.setValue(totalHeight - height + PADDING); } public int calculateYScroll() { ScaledResolution scaledresolution = new ScaledResolution(Minecraft.getMinecraft()); int height = scaledresolution.getScaledHeight(); int totalHeight = PADDING; for (GuiElement gui : options) { totalHeight += gui.getHeight(); } if (scrollHeight.getValue() < 0) scrollHeight.setValue(0); int yScroll = 0; if (totalHeight > height - PADDING) { yScroll = totalHeight - height + PADDING - scrollHeight.getValue(); } else { scrollHeight.setValue(0); } if (yScroll < 0) { yScroll = 0; scrollHeight.setValue(totalHeight - height + PADDING); } return yScroll; } @Override public void drawScreen(int mouseX, int mouseY, float partialTicks) { scrollHeight.tick(); ScaledResolution scaledresolution = new ScaledResolution(Minecraft.getMinecraft()); int width = scaledresolution.getScaledWidth(); int height = scaledresolution.getScaledHeight(); GlStateManager.disableLighting(); Color backgroundColour = new Color(10, 10, 10, 240); drawRect(0, 0, width, height, backgroundColour.getRGB()); int yScroll = calculateYScroll(); int currentY = PADDING - yScroll; for (GuiElement gui : options) { gui.render(PADDING, currentY); currentY += gui.getHeight(); } currentY = PADDING; for (GuiElement gui : rightOptions) { gui.render(width - PADDING - gui.getWidth(), currentY); currentY += gui.getHeight(); } int itemX = 424; int itemY = 32; int itemSize = 128; Color itemBorder = new Color(100, 50, 150, 255); Color itemBackground = new Color(120, 120, 120, 255); drawRect(itemX - 10, itemY - 10, itemX + itemSize + 10, itemY + itemSize + 10, Color.DARK_GRAY.getRGB()); drawRect(itemX - 9, itemY - 9, itemX + itemSize + 9, itemY + itemSize + 9, itemBorder.getRGB()); drawRect(itemX - 6, itemY - 6, itemX + itemSize + 6, itemY + itemSize + 6, Color.DARK_GRAY.getRGB()); drawRect(itemX - 5, itemY - 5, itemX + itemSize + 5, itemY + itemSize + 5, itemBackground.getRGB()); ItemStack stack = new ItemStack(Item.itemRegistry.getObject(new ResourceLocation(itemId.get()))); if (stack.getItem() != null) { try { stack.setItemDamage(Integer.parseInt(damage.get())); } catch (NumberFormatException ignored) { } resyncNbttag(); stack.setTagCompound(nbtTag); int scaleFactor = itemSize / 16; GL11.glPushMatrix(); GlStateManager.scale(scaleFactor, scaleFactor, 1); drawItemStack(stack, itemX / scaleFactor, itemY / scaleFactor); GL11.glPopMatrix(); } //Tooltip List text = new ArrayList<>(); text.add(displayName.get()); text.addAll(Arrays.asList(lore.get().split("\n"))); Utils.drawHoveringText(text, itemX - 20, itemY + itemSize + 28, width, height, -1); GlStateManager.disableLighting(); } @Override protected void keyTyped(char typedChar, int keyCode) { boolean hasChanges = false; if (keyCode == Keyboard.KEY_ESCAPE && !hasChanges) { Minecraft.getMinecraft().displayGuiScreen(null); return; } for (GuiElement gui : options) { gui.keyTyped(typedChar, keyCode); } } @Override protected void mouseClickMove(int mouseX, int mouseY, int clickedMouseButton, long timeSinceLastClick) { ScaledResolution scaledresolution = new ScaledResolution(Minecraft.getMinecraft()); int width = scaledresolution.getScaledWidth(); int yScroll = calculateYScroll(); int currentY = PADDING - yScroll; for (GuiElement gui : options) { if (mouseY > currentY && mouseY < currentY + gui.getHeight() && mouseX < gui.getWidth()) { gui.mouseClickMove(mouseX, mouseY, clickedMouseButton, timeSinceLastClick); return; } currentY += gui.getHeight(); } currentY = PADDING; for (GuiElement gui : rightOptions) { if (mouseY > currentY && mouseY < currentY + gui.getHeight() && mouseX > width - PADDING - gui.getWidth()) { gui.mouseClickMove(mouseX, mouseY, clickedMouseButton, timeSinceLastClick); return; } currentY += gui.getHeight(); } } @Override public void handleMouseInput() throws IOException { ScaledResolution scaledresolution = new ScaledResolution(Minecraft.getMinecraft()); int maxWidth = 0; for (GuiElement gui : options) { if (gui.getWidth() > maxWidth) maxWidth = gui.getWidth(); } if (Mouse.getX() < maxWidth * scaledresolution.getScaleFactor()) { int dWheel = Mouse.getEventDWheel(); if (dWheel < 0) { scrollHeight.setTarget(scrollHeight.getTarget() - SCROLL_AMOUNT); scrollHeight.resetTimer(); } else if (dWheel > 0) { scrollHeight.setTarget(scrollHeight.getTarget() + SCROLL_AMOUNT); scrollHeight.resetTimer(); } } super.handleMouseInput(); } @Override protected void mouseClicked(int mouseX, int mouseY, int mouseButton) { ScaledResolution scaledresolution = new ScaledResolution(Minecraft.getMinecraft()); int width = scaledresolution.getScaledWidth(); int yScroll = calculateYScroll(); int currentY = PADDING - yScroll; for (GuiElement gui : options) { if (mouseY > currentY && mouseY < currentY + gui.getHeight() && mouseX < gui.getWidth()) { gui.mouseClicked(mouseX, mouseY, mouseButton); for (GuiElement gui2 : options) { if (gui2 != gui) { gui2.otherComponentClick(); } } for (GuiElement gui2 : rightOptions) { if (gui2 != gui) { gui2.otherComponentClick(); } } return; } currentY += gui.getHeight(); } currentY = PADDING; for (GuiElement gui : rightOptions) { if (mouseY > currentY && mouseY < currentY + gui.getHeight() && mouseX > width - PADDING - gui.getWidth()) { gui.mouseClicked(mouseX, mouseY, mouseButton); for (GuiElement gui2 : options) { if (gui2 != gui) { gui2.otherComponentClick(); } } for (GuiElement gui2 : rightOptions) { if (gui2 != gui) { gui2.otherComponentClick(); } } return; } currentY += gui.getHeight(); } } private void drawItemStack(ItemStack stack, int x, int y) { RenderItem itemRender = Minecraft.getMinecraft().getRenderItem(); FontRenderer font = Minecraft.getMinecraft().fontRendererObj; RenderHelper.enableGUIStandardItemLighting(); itemRender.renderItemAndEffectIntoGUI(stack, x, y); RenderHelper.disableStandardItemLighting(); itemRender.renderItemOverlayIntoGUI(font, stack, x, y, null); } }