/*
* Copyright (C) 2022-2024 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.util;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import io.github.moulberry.notenoughupdates.NotEnoughUpdates;
import io.github.moulberry.notenoughupdates.core.util.StringUtils;
import io.github.moulberry.notenoughupdates.listener.ItemTooltipListener;
import io.github.moulberry.notenoughupdates.miscfeatures.PetInfoOverlay;
import io.github.moulberry.notenoughupdates.profileviewer.GuiProfileViewer;
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.EnumChatFormatting;
import net.minecraft.util.MathHelper;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.nio.charset.StandardCharsets;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.function.BiFunction;
public class ItemUtils {
private static final Gson smallPrintingGson = new Gson();
public static ItemStack createSkullItemStack(String displayName, String uuid, String skinAbsoluteUrl) {
JsonObject object = new JsonObject();
JsonObject textures = new JsonObject();
JsonObject skin = new JsonObject();
skin.addProperty("url", skinAbsoluteUrl);
textures.add("SKIN", skin);
object.add("textures", textures);
String json = smallPrintingGson.toJson(object);
return Utils.createSkull(
displayName,
uuid,
Base64.getEncoder().encodeToString(json.getBytes(StandardCharsets.UTF_8))
);
}
public static ItemStack getCoinItemStack(double coinAmount) {
String uuid = "2070f6cb-f5db-367a-acd0-64d39a7e5d1b";
String texture =
"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNTM4MDcxNzIxY2M1YjRjZDQwNmNlNDMxYTEzZjg2MDgzYTg5NzNlMTA2NGQyZjg4OTc4Njk5MzBlZTZlNTIzNyJ9fX0=";
if (coinAmount >= 100000) {
uuid = "94fa2455-2881-31fe-bb4e-e3e24d58dbe3";
texture =
"eyJ0aW1lc3RhbXAiOjE2MzU5NTczOTM4MDMsInByb2ZpbGVJZCI6ImJiN2NjYTcxMDQzNDQ0MTI4ZDMwODllMTNiZGZhYjU5IiwicHJvZmlsZU5hbWUiOiJsYXVyZW5jaW8zMDMiLCJzaWduYXR1cmVSZXF1aXJlZCI6dHJ1ZSwidGV4dHVyZXMiOnsiU0tJTiI6eyJ1cmwiOiJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlL2M5Yjc3OTk5ZmVkM2EyNzU4YmZlYWYwNzkzZTUyMjgzODE3YmVhNjQwNDRiZjQzZWYyOTQzM2Y5NTRiYjUyZjYiLCJtZXRhZGF0YSI6eyJtb2RlbCI6InNsaW0ifX19fQo=";
}
if (coinAmount >= 10000000) {
uuid = "0af8df1f-098c-3b72-ac6b-65d65fd0b668";
texture =
"ewogICJ0aW1lc3RhbXAiIDogMTYzNTk1NzQ4ODQxNywKICAicHJvZmlsZUlkIiA6ICJmNThkZWJkNTlmNTA0MjIyOGY2MDIyMjExZDRjMTQwYyIsCiAgInByb2ZpbGVOYW1lIiA6ICJ1bnZlbnRpdmV0YWxlbnQiLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvN2I5NTFmZWQ2YTdiMmNiYzIwMzY5MTZkZWM3YTQ2YzRhNTY0ODE1NjRkMTRmOTQ1YjZlYmMwMzM4Mjc2NmQzYiIsCiAgICAgICJtZXRhZGF0YSIgOiB7CiAgICAgICAgIm1vZGVsIiA6ICJzbGltIgogICAgICB9CiAgICB9CiAgfQp9";
}
ItemStack skull = Utils.createSkull(
"§r§6" + StringUtils.formatNumber(coinAmount) + " Coins",
uuid,
texture
);
NBTTagCompound extraAttributes = skull.getTagCompound().getCompoundTag("ExtraAttributes");
extraAttributes.setString("id", "SKYBLOCK_COIN");
skull.getTagCompound().setTag("ExtraAttributes", extraAttributes);
return skull;
}
public static ItemStack createQuestionMarkSkull(String label) {
return Utils.createSkull(
label,
"00000000-0000-0000-0000-000000000000",
"bc8ea1f51f253ff5142ca11ae45193a4ad8c3ab5e9c6eec8ba7a4fcb7bac40"
);
}
public static NBTTagCompound getOrCreateTag(ItemStack is) {
if (is.hasTagCompound()) return is.getTagCompound();
NBTTagCompound nbtTagCompound = new NBTTagCompound();
is.setTagCompound(nbtTagCompound);
return nbtTagCompound;
}
public static void appendLore(ItemStack is, List moreLore) {
NBTTagCompound tagCompound = is.getTagCompound();
if (tagCompound == null) {
tagCompound = new NBTTagCompound();
}
NBTTagCompound display = tagCompound.getCompoundTag("display");
NBTTagList lore = display.getTagList("Lore", 8);
for (String s : moreLore) {
lore.appendTag(new NBTTagString(s));
}
display.setTag("Lore", lore);
tagCompound.setTag("display", display);
is.setTagCompound(tagCompound);
}
public static void setLore(ItemStack is, List newLore) {
NBTTagCompound tagCompound = is.getTagCompound();
if (tagCompound == null) {
tagCompound = new NBTTagCompound();
}
NBTTagCompound display = tagCompound.getCompoundTag("display");
NBTTagList lore = new NBTTagList();
for (String s : newLore) {
lore.appendTag(new NBTTagString(s));
}
display.setTag("Lore", lore);
tagCompound.setTag("display", display);
is.setTagCompound(tagCompound);
}
public static @NotNull List<@NotNull String> getLore(@Nullable ItemStack is) {
if (is == null) return new ArrayList<>();
return getLore(is.getTagCompound());
}
public static @NotNull List<@NotNull String> getLore(@Nullable NBTTagCompound tagCompound) {
if (tagCompound == null) {
return Collections.emptyList();
}
NBTTagList tagList = tagCompound.getCompoundTag("display").getTagList("Lore", 8);
List list = new ArrayList<>();
for (int i = 0; i < tagList.tagCount(); i++) {
list.add(tagList.getStringTagAt(i));
}
return list;
}
public static @Nullable String getDisplayName(@Nullable ItemStack itemStack) {
if (null == itemStack) return null;
return getDisplayName(itemStack.getTagCompound());
}
public static @Nullable String getDisplayName(@Nullable NBTTagCompound compound) {
if (compound == null) return null;
String string = compound.getCompoundTag("display").getString("Name");
if (string == null || string.isEmpty())
return null;
return string;
}
public static String fixEnchantId(String enchId, boolean useId) {
if (Constants.ENCHANTS != null && Constants.ENCHANTS.has("enchant_mapping_id") &&
Constants.ENCHANTS.has("enchant_mapping_item")) {
JsonArray mappingFrom = Constants.ENCHANTS.getAsJsonArray("enchant_mapping_" + (useId ? "id" : "item"));
JsonArray mappingTo = Constants.ENCHANTS.getAsJsonArray("enchant_mapping_" + (useId ? "item" : "id"));
for (int i = 0; i < mappingFrom.size(); i++) {
if (mappingFrom.get(i).getAsString().equals(enchId)) {
return mappingTo.get(i).getAsString();
}
}
}
return enchId;
}
/**
* Mutates baseValues
*/
public static void modifyReplacement(
Map baseValues,
Map modifiers,
BiFunction mapper
) {
if (modifiers == null || baseValues == null) return;
for (Map.Entry modifier : modifiers.entrySet()) {
String baseValue = baseValues.get(modifier.getKey());
if (baseValue == null) continue;
try {
baseValues.put(modifier.getKey(), mapper.apply(baseValue, modifier.getValue()));
} catch (Exception e) {
System.out.println("Exception during replacement mapping: ");
e.printStackTrace();
}
}
}
public static String applyReplacements(Map replacements, String text) {
for (Map.Entry replacement : replacements.entrySet()) {
String search = "{" + replacement.getKey() + "}";
text = text.replace(search, replacement.getValue());
}
return text;
}
public static @NotNull NBTTagCompound getExtraAttributes(ItemStack itemStack) {
NBTTagCompound tag = getOrCreateTag(itemStack);
NBTTagCompound extraAttributes = tag.getCompoundTag("ExtraAttributes");
tag.setTag("ExtraAttributes", extraAttributes);
return extraAttributes;
}
public static ItemStack createPetItemstackFromPetInfo(PetInfoOverlay.Pet currentPet) {
if (currentPet == null) {
ItemStack stack = ItemUtils.createQuestionMarkSkull(EnumChatFormatting.RED + "Unknown Pet");
appendLore(stack, Arrays.asList(
"§cNull Pet",
"",
"§cIf you expected it to be there please send a message in",
"§c§l#neu-support §r§con §ldiscord.gg/moulberry"
));
return stack;
}
String petname = currentPet.petType;
String tier = Utils.getRarityFromInt(currentPet.rarity.petId).toUpperCase(Locale.ROOT);
String heldItem = currentPet.petItem;
String skin = currentPet.skin;
JsonObject heldItemJson = heldItem == null ? null : NotEnoughUpdates.INSTANCE.manager.getItemInformation().get(
heldItem);
String petId = currentPet.getPetId(false);
float exp = currentPet.petLevel.getExpTotal();
PetLeveling.PetLevel levelObj = PetLeveling
.getPetLevelingForPet(petname, PetInfoOverlay.Rarity.valueOf(tier))
.getPetLevel(exp);
int level = levelObj.getCurrentLevel();
ItemStack petItemstack = NotEnoughUpdates.INSTANCE.manager
.createItemResolutionQuery()
.withKnownInternalName(petId)
.resolveToItemStack(false);
if (petItemstack == null) {
petItemstack = ItemUtils.createQuestionMarkSkull(EnumChatFormatting.RED + "Unknown Pet");
appendLore(petItemstack, Arrays.asList(
"§cThis pet is not saved in the repository",
"",
"§cIf you expected it to be there please send a message in",
"§c§l#neu-support §r§con §ldiscord.gg/moulberry"
));
}
Map replacements = NotEnoughUpdates.INSTANCE.manager.getPetLoreReplacements(
petname,
tier,
MathHelper.floor_float(level)
);
if (heldItem != null) {
modifyReplacement(replacements, GuiProfileViewer.PET_STAT_BOOSTS.get(heldItem), (original, modifier) ->
"" + MathHelper.floor_float(Float.parseFloat(original) + modifier));
modifyReplacement(replacements, GuiProfileViewer.PET_STAT_BOOSTS_MULT.get(heldItem), (original, modifier) ->
"" + MathHelper.floor_float(Float.parseFloat(original) * modifier));
}
NBTTagCompound tag = getOrCreateTag(petItemstack);
if (tag.hasKey("display", 10)) {
NBTTagCompound displayTag = tag.getCompoundTag("display");
if (displayTag.hasKey("Lore", 9)) {
List newLore = new ArrayList<>();
NBTTagList lore = displayTag.getTagList("Lore", 8);
int secondLastBlankLine = -1, lastBlankLine = -1;
for (int j = 0; j < lore.tagCount(); j++) {
String line = lore.getStringTagAt(j);
if (line.trim().isEmpty()) {
secondLastBlankLine = lastBlankLine;
lastBlankLine = j;
}
line = applyReplacements(replacements, line);
newLore.add(line);
}
if (skin != null) {
JsonObject petSkin = NotEnoughUpdates.INSTANCE.manager
.createItemResolutionQuery()
.withKnownInternalName("PET_SKIN_" + skin)
.resolveToItemListJson();
if (petSkin != null) {
try {
NBTTagCompound nbt = JsonToNBT.getTagFromJson(petSkin.get("nbttag").getAsString());
tag.setTag("SkullOwner", nbt.getTag("SkullOwner"));
String name = petSkin.get("displayname").getAsString();
if (name != null) {
name = Utils.cleanColour(name);
newLore.set(0, newLore.get(0) + ", " + name);
}
} catch (NBTException e) {
e.printStackTrace();
}
}
}
for (int i = 0; i < newLore.size(); i++) {
String cleaned = Utils.cleanColour(newLore.get(i));
if (cleaned.startsWith("Right-click to add this pet to")) {
if (heldItem == null) newLore.remove(i + 2);
newLore.remove(i + 1);
newLore.remove(i);
secondLastBlankLine = i - 1;
break;
}
}
if (secondLastBlankLine != -1) {
List petItemLore = new ArrayList<>();
if (heldItem != null) {
if (heldItemJson == null) {
petItemLore.add(EnumChatFormatting.RED + "Could not find held item in repo!");
} else {
petItemLore.add(EnumChatFormatting.GOLD + "Held Item: " + heldItemJson.get("displayname").getAsString());
List heldItemLore = JsonUtils.getJsonArrayOrEmpty(heldItemJson, "lore", JsonElement::getAsString);
int blanks = 0;
for (String heldItemLoreLine : heldItemLore) {
if (heldItemLoreLine.trim().isEmpty()) {
blanks++;
} else if (blanks == 2) {
petItemLore.add(heldItemLoreLine);
} else if (blanks > 2) {
break;
}
}
}
}
if (currentPet.candyUsed > 0) {
if (petItemLore.size() > 0) {
petItemLore.add("");
}
petItemLore.add("§a(" + currentPet.candyUsed + "/10) Pet Candy Used");
if (heldItem == null) petItemLore.add("");
}
newLore.addAll(secondLastBlankLine + 1, petItemLore);
}
NBTTagList temp = new NBTTagList();
for (String loreLine : newLore) {
temp.appendTag(new NBTTagString(loreLine));
}
displayTag.setTag("Lore", temp);
}
if (displayTag.hasKey("Name", 8)) {
String displayName = displayTag.getString("Name");
displayName = applyReplacements(replacements, displayName);
displayTag.setTag("Name", new NBTTagString(displayName));
}
tag.setTag("display", displayTag);
}
// Adds the missing pet fields to the tag
NBTTagCompound extraAttributes = new NBTTagCompound();
JsonObject petInfo = new JsonObject();
if (tag.hasKey("ExtraAttributes", 10)) {
extraAttributes = tag.getCompoundTag("ExtraAttributes");
if (extraAttributes.hasKey("petInfo", 8)) {
petInfo = new JsonParser().parse(extraAttributes.getString("petInfo")).getAsJsonObject();
}
}
petInfo.addProperty("exp", exp);
petInfo.addProperty("tier", tier);
petInfo.addProperty("type", petname);
if (heldItem != null) {
petInfo.addProperty("heldItem", heldItem);
}
if (skin != null) {
petInfo.addProperty("skin", skin);
}
extraAttributes.setString("petInfo", petInfo.toString());
tag.setTag("ExtraAttributes", extraAttributes);
petItemstack.setTagCompound(tag);
return petItemstack;
}
private static final DecimalFormat decimalFormatter = new DecimalFormat("#,###,###.###");
public static ItemStack petToolTipXPExtendPetOverlay(ItemStack stack) {
NBTTagCompound tag = stack.getTagCompound() == null ? new NBTTagCompound() : stack.getTagCompound();
if (tag.hasKey("display", 10)) {
NBTTagCompound display = tag.getCompoundTag("display");
if (display.hasKey("Lore", 9)) {
NBTTagList lore = display.getTagList("Lore", 8);
if (ItemTooltipListener.petToolTipRegex.matcher(Utils.cleanColour(lore.getStringTagAt(0))).matches() &&
lore.tagCount() > 7) {
PetLeveling.PetLevel petLevel;
PetInfoOverlay.Pet pet = PetInfoOverlay.getPetFromStack(
stack.getTagCompound()
);
if (pet == null) return stack;
petLevel = pet.petLevel;
if (petLevel == null) return stack;
NBTTagList newLore = new NBTTagList();
int maxLvl = 100;
if (Constants.PETS != null && Constants.PETS.has("custom_pet_leveling") &&
Constants.PETS.getAsJsonObject("custom_pet_leveling").has(pet.petType.toUpperCase(Locale.ROOT)) &&
Constants.PETS
.getAsJsonObject("custom_pet_leveling")
.getAsJsonObject(pet.petType.toUpperCase(Locale.ROOT))
.has(
"max_level")) {
maxLvl =
Constants.PETS
.getAsJsonObject("custom_pet_leveling")
.getAsJsonObject(pet.petType.toUpperCase(Locale.ROOT))
.get("max_level")
.getAsInt();
}
for (int i = 0; i < lore.tagCount(); i++) {
if (i == lore.tagCount() - 2) {
newLore.appendTag(new NBTTagString(""));
if (petLevel.getCurrentLevel() >= maxLvl) {
newLore.appendTag(new NBTTagString(
EnumChatFormatting.AQUA + "" + EnumChatFormatting.BOLD + "MAX LEVEL"));
} else {
double levelPercent = (Math.round(petLevel.getPercentageToNextLevel() * 1000) / 10.0);
newLore.appendTag(new NBTTagString(
EnumChatFormatting.GRAY + "Progress to Level " + (petLevel.getCurrentLevel() + 1) + ": " +
EnumChatFormatting.YELLOW + levelPercent + "%"));
StringBuilder sb = new StringBuilder();
for (int j = 0; j < 20; j++) {
if (j < (levelPercent / 5)) {
sb.append(EnumChatFormatting.DARK_GREEN);
} else {
sb.append(EnumChatFormatting.WHITE);
}
sb.append(EnumChatFormatting.BOLD + "" + EnumChatFormatting.STRIKETHROUGH + " ");
}
newLore.appendTag(new NBTTagString(sb.toString()));
newLore.appendTag(new NBTTagString(
EnumChatFormatting.GRAY + "EXP: " + EnumChatFormatting.YELLOW +
decimalFormatter.format(petLevel.getExpInCurrentLevel()) +
EnumChatFormatting.GOLD + "/" + EnumChatFormatting.YELLOW +
decimalFormatter.format(petLevel.getExpRequiredForNextLevel())
));
}
}
newLore.appendTag(lore.get(i));
}
display.setTag("Lore", newLore);
tag.setTag("display", display);
}
}
}
stack.setTagCompound(tag);
return stack;
}
public static boolean isSoulbound(ItemStack item) {
return ItemUtils.getLore(item).stream()
.anyMatch(line -> line.equals("§8§l* §8Co-op Soulbound §8§l*") ||
line.equals("§8§l* Soulbound §8§l*"));
}
public static String fixDraconicId(String id) {
if (id.equals("ASPECT_OF_THE_DRAGONS")) return "ASPECT_OF_THE_DRAGON";
if (id.equals("ENDER_HELMET")) return "END_HELMET";
if (id.equals("ENDER_CHESTPLATE")) return "END_CHESTPLATE";
if (id.equals("ENDER_LEGGINGS")) return "END_LEGGINGS";
if (id.equals("ENDER_BOOTS")) return "END_BOOTS";
return id;
}
public static ItemStack createItemStackFromId(String id, String displayname) {
Item item = Item.getByNameOrId(id);
if (item == null) {
return null;
}
ItemStack itemStack = new ItemStack(item);
itemStack.setStackDisplayName(displayname);
return itemStack;
}
}