/*
* 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.miscfeatures;
import com.google.common.collect.Lists;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import io.github.moulberry.notenoughupdates.NEUOverlay;
import io.github.moulberry.notenoughupdates.NotEnoughUpdates;
import io.github.moulberry.notenoughupdates.core.config.Position;
import io.github.moulberry.notenoughupdates.core.util.StringUtils;
import io.github.moulberry.notenoughupdates.core.util.lerp.LerpUtils;
import io.github.moulberry.notenoughupdates.listener.RenderListener;
import io.github.moulberry.notenoughupdates.options.NEUConfig;
import io.github.moulberry.notenoughupdates.overlays.TextOverlay;
import io.github.moulberry.notenoughupdates.overlays.TextOverlayStyle;
import io.github.moulberry.notenoughupdates.profileviewer.GuiProfileViewer;
import io.github.moulberry.notenoughupdates.profileviewer.ProfileViewer;
import io.github.moulberry.notenoughupdates.util.Constants;
import io.github.moulberry.notenoughupdates.util.ProfileApiSyncer;
import io.github.moulberry.notenoughupdates.util.Utils;
import io.github.moulberry.notenoughupdates.util.XPInformation;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.inventory.GuiChest;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.init.Items;
import net.minecraft.inventory.ContainerChest;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.ChatComponentText;
import net.minecraft.util.EnumChatFormatting;
import net.minecraftforge.client.event.ClientChatReceivedEvent;
import net.minecraftforge.event.entity.player.ItemTooltipEvent;
import net.minecraftforge.event.world.WorldEvent;
import net.minecraftforge.fml.common.eventhandler.EventPriority;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent;
import org.apache.commons.lang3.text.WordUtils;
import org.lwjgl.util.vector.Vector2f;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class PetInfoOverlay extends TextOverlay {
private static final Pattern XP_BOOST_PATTERN = Pattern.compile(
"PET_ITEM_(COMBAT|FISHING|MINING|FORAGING|ALL|FARMING)_(SKILL|SKILLS)_BOOST_(COMMON|UNCOMMON|RARE|EPIC)");
private static final Pattern PET_CONTAINER_PAGE = Pattern.compile("\\((\\d)/(\\d)\\) Pets");
private static final Pattern PET_NAME_PATTERN = Pattern.compile("\u00a77\\[Lvl \\d+] \u00a7(.+)");
private static final Pattern XP_LINE_PATTERN = Pattern.compile(
"-------------------- (\\d+(?:,\\d+)*(?:\\.\\d+)?)/(\\d+(?:\\.\\d+)?[B|M|k]?)");
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
public PetInfoOverlay(
Position position,
Supplier> dummyStrings,
Supplier styleSupplier
) {
super(position, dummyStrings, styleSupplier);
}
public enum Rarity {
COMMON(0, 0, 1, EnumChatFormatting.WHITE),
UNCOMMON(6, 1, 2, EnumChatFormatting.GREEN),
RARE(11, 2, 3, EnumChatFormatting.BLUE),
EPIC(16, 3, 4, EnumChatFormatting.DARK_PURPLE),
LEGENDARY(20, 4, 5, EnumChatFormatting.GOLD),
MYTHIC(20, 5, 5, EnumChatFormatting.LIGHT_PURPLE);
public int petOffset;
public EnumChatFormatting chatFormatting;
public int petId;
public int beastcreatMultiplyer;
Rarity(int petOffset, int petId, int beastcreatMultiplyer, EnumChatFormatting chatFormatting) {
this.chatFormatting = chatFormatting;
this.petOffset = petOffset;
this.petId = petId;
this.beastcreatMultiplyer = beastcreatMultiplyer;
}
public static Rarity getRarityFromColor(EnumChatFormatting chatFormatting) {
for (int i = 0; i < Rarity.values().length; i++) {
if (Rarity.values()[i].chatFormatting.equals(chatFormatting))
return Rarity.values()[i];
}
return COMMON;
}
}
public static class Pet {
public String petType;
public Rarity rarity;
public GuiProfileViewer.PetLevel petLevel;
public String petXpType;
public String petItem;
}
private static long lastXpGain = 0;
public static class PetConfig {
public HashMap petMap = new HashMap<>();
private int selectedPet = -1;
private int selectedPet2 = -1;
public int tamingLevel = 1;
public float beastMultiplier = 0;
}
private static long lastPetSelect = -1;
private static PetConfig config = new PetConfig();
private static long lastUpdate = 0;
private static float levelXpLast = 0;
private static final LinkedList xpGainQueue = new LinkedList<>();
private static float xpGainHourLast = -1;
private static float xpGainHour = -1;
private static int pauseCountdown = 0;
private static float xpGainHourSecondPet = -1;
private int xpAddTimer = 0;
public static void loadConfig(File file) {
try (
BufferedReader reader = new BufferedReader(new InputStreamReader(
new FileInputStream(file),
StandardCharsets.UTF_8
))
) {
config = GSON.fromJson(reader, PetConfig.class);
} catch (Exception ignored) {
}
if (config == null) {
config = new PetConfig();
}
}
public static void saveConfig(File file) {
try {
file.createNewFile();
try (
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream(file),
StandardCharsets.UTF_8
))
) {
writer.write(GSON.toJson(config));
}
} catch (Exception ignored) {
}
}
public static void clearPet() {
config.selectedPet = -1;
config.selectedPet2 = -1;
}
public static void setCurrentPet(int index) {
config.selectedPet2 = config.selectedPet;
xpGainHourSecondPet = xpGainHour;
xpGainHourLast = xpGainHour;
xpGainQueue.clear();
config.selectedPet = index;
}
public static Pet getCurrentPet() {
return config.petMap.get(config.selectedPet);
}
public static Pet getCurrentPet2() {
if (!NotEnoughUpdates.INSTANCE.config.petOverlay.dualPets) return null;
if (config.selectedPet == config.selectedPet2) return null;
return config.petMap.get(config.selectedPet2);
}
public float getLevelPercent(Pet pet) {
DecimalFormat df = new DecimalFormat("#.#", DecimalFormatSymbols.getInstance(Locale.ENGLISH));
if (pet == null) return 0;
try {
return Float.parseFloat(df.format(pet.petLevel.levelPercentage * 100f));
} catch (Exception ignored) {
return 0;
}
}
private static int getIdForPet(Pet pet) {
for (Map.Entry entry : config.petMap.entrySet()) {
if (entry.getValue() == pet) {
return entry.getKey();
}
}
return -1;
}
private static int getClosestPetIndex(String petType, int petId, String petItem, float petLevel) {
Pet pet = getClosestPet(petType, petId, petItem, petLevel);
if (pet == null) {
return -1;
} else {
return getIdForPet(pet);
}
}
private static Pet getClosestPet(String petType, int petId, String petItem, float petLevel) {
Set pets = new HashSet() {{
for (Pet pet : config.petMap.values()) {
if (pet.petType.equals(petType) && pet.rarity.petId == petId) {
add(pet);
}
}
}};
if (pets.isEmpty()) {
return null;
}
if (pets.size() == 1) {
return pets.iterator().next();
}
String searchItem = petItem;
Set itemMatches = new HashSet<>();
for (Pet pet : pets) {
if ((searchItem == null && pet.petItem == null) ||
(searchItem != null && searchItem.equals(pet.petItem))) {
itemMatches.add(pet);
}
}
if (itemMatches.size() == 1) {
return itemMatches.iterator().next();
}
if (itemMatches.size() > 1) {
pets = itemMatches;
}
float closestXp = -1;
Pet closestPet = null;
for (Pet pet : pets) {
float distXp = Math.abs(pet.petLevel.level - petLevel);
if (closestPet == null || distXp < closestXp) {
closestXp = distXp;
closestPet = pet;
}
}
if (closestPet != null) {
return closestPet;
} else {
return pets.iterator().next();
}
}
private static void getAndSetPet(ProfileViewer.Profile profile) {
JsonObject skillInfo = profile.getSkillInfo(profile.getLatestProfile());
JsonObject invInfo = profile.getInventoryInfo(profile.getLatestProfile());
JsonObject profileInfo = profile.getProfileInformation(profile.getLatestProfile());
if (invInfo != null && profileInfo != null) {
JsonObject stats = profileInfo.get("stats").getAsJsonObject();
boolean hasBeastmasterCrest = false;
Rarity currentBeastRarity = Rarity.COMMON;
for (JsonElement talisman : invInfo.get("talisman_bag").getAsJsonArray()) {
if (talisman.isJsonNull()) continue;
String internalName = talisman.getAsJsonObject().get("internalname").getAsString();
if (internalName.startsWith("BEASTMASTER_CREST")) {
hasBeastmasterCrest = true;
try {
Rarity talismanRarity = Rarity.valueOf(internalName.replace("BEASTMASTER_CREST_", ""));
if (talismanRarity.beastcreatMultiplyer > currentBeastRarity.beastcreatMultiplyer)
currentBeastRarity = talismanRarity;
} catch (Exception ignored) {
}
}
}
if (hasBeastmasterCrest) {
if (stats.has("mythos_kills")) {
int mk = stats.get("mythos_kills").getAsInt();
float petXpBoost = mk > 10000 ? 1f : mk > 7500 ? 0.9f : mk > 5000 ? 0.8f : mk > 2500 ? 0.7f :
mk > 1000
? 0.6f
: mk > 500
? 0.5f
: mk > 250
? 0.4f
: mk > 100
? 0.3f
: mk > 25 ? 0.2f : 0.1f;
config.beastMultiplier = petXpBoost * currentBeastRarity.beastcreatMultiplyer;
} else {
config.beastMultiplier = 0.1f * currentBeastRarity.beastcreatMultiplyer;
}
}
}
if (skillInfo != null) config.tamingLevel = skillInfo.get("level_skill_taming").getAsInt();
//JsonObject petObject = profile.getPetsInfo(profile.getLatestProfile());
/*JsonObject petsJson = Constants.PETS;
if(petsJson != null) {
if(petObject != null) {
boolean forceUpdateLevels = System.currentTimeMillis() - lastXpGain > 30000;
Set foundPets = new HashSet<>();
Set addedPets = new HashSet<>();
for(int i = 0; i < petObject.getAsJsonArray("pets").size(); i++) {
JsonElement petElement = petObject.getAsJsonArray("pets").get(i);
JsonObject petObj = petElement.getAsJsonObject();
Pet pet = new Pet();
pet.petType = petObj.get("type").getAsString();
Rarity rarity;
try {
rarity = Rarity.valueOf(petObj.get("tier").getAsString());
} catch(Exception ignored) {
rarity = Rarity.COMMON;
}
pet.rarity = rarity;
pet.petLevel = GuiProfileViewer.getPetLevel(petsJson.get("pet_levels").getAsJsonArray(), rarity.petOffset, petObj.get("exp").getAsFloat());
JsonElement heldItem = petObj.get("heldItem");
pet.petItem = heldItem.isJsonNull() ? null : heldItem.getAsString();
if(rarity != Rarity.MYTHIC && pet.petItem != null && pet.petItem.equals("PET_ITEM_TIER_BOOST")) {
rarity = Rarity.values()[rarity.ordinal()+1];
}
JsonObject petTypes = petsJson.get("pet_types").getAsJsonObject();
pet.petXpType = petTypes.has(pet.petType) ? petTypes.get(pet.petType.toUpperCase()).getAsString().toLowerCase() : "unknown";
Pet closest = null;
if(petList.containsKey(pet.petType + ";" + pet.rarity.petId)) {
closest = getClosestPet(pet);
if(addedPets.contains(closest)) {
closest = null;
}
if(closest != null) {
if(!forceUpdateLevels || Math.floor(pet.petLevel.level) < Math.floor(closest.petLevel.level)) {
pet.petLevel = closest.petLevel;
}
petList.get(pet.petType + ";" + pet.rarity.petId).remove(closest);
}
}
foundPets.add(pet.petType + ";" + pet.rarity.petId);
petList.computeIfAbsent(pet.petType + ";" + pet.rarity.petId, k->new HashSet<>()).add(pet);
addedPets.add(pet);
if(petObj.get("active").getAsBoolean()) {
if(currentPet == null && !setActivePet) {
currentPet = pet;
} else if(closest == currentPet) {
currentPet = pet;
}
}
}
petList.keySet().retainAll(foundPets);
setActivePet = true;
}
}*/
}
private float interp(float now, float last) {
float interp = now;
if (last >= 0 && last != now) {
float factor = (System.currentTimeMillis() - lastUpdate) / 1000f;
factor = LerpUtils.clampZeroOne(factor);
interp = last + (now - last) * factor;
}
return interp;
}
private List createStringsForPet(Pet currentPet, boolean secondPet) {
float levelXp = currentPet.petLevel.levelXp;
if (!secondPet) levelXp = interp(currentPet.petLevel.levelXp, levelXpLast);
if (levelXp < 0) levelXp = 0;
String petName =
EnumChatFormatting.GREEN + "[Lvl " + (int) currentPet.petLevel.level + "] " + currentPet.rarity.chatFormatting +
WordUtils.capitalizeFully(currentPet.petType.replace("_", " "));
String lvlStringShort = EnumChatFormatting.AQUA + "" + roundFloat(levelXp) + "/" +
roundFloat(currentPet.petLevel.currentLevelRequirement)
+ EnumChatFormatting.YELLOW + " (" + getLevelPercent(currentPet) + "%)";
String lvlString = EnumChatFormatting.AQUA + "" + Utils.shortNumberFormat(levelXp, 0) + "/" +
Utils.shortNumberFormat(currentPet.petLevel.currentLevelRequirement, 0)
+ EnumChatFormatting.YELLOW + " (" + getLevelPercent(currentPet) + "%)";
float xpGain;
if (!secondPet) {
xpGain = interp(xpGainHour, xpGainHourLast);
} else {
xpGain = xpGainHourSecondPet;
}
if (xpGain < 0) xpGain = 0;
String xpGainString = EnumChatFormatting.AQUA + "XP/h: " +
EnumChatFormatting.YELLOW + roundFloat(xpGain);
if (!secondPet && xpGain > 0 && levelXp != levelXpLast) {
if (pauseCountdown <= 0) {
xpGainString += EnumChatFormatting.RED + " (PAUSED)";
} else {
pauseCountdown--;
}
} else {
pauseCountdown = 60;
}
String totalXpString =
EnumChatFormatting.AQUA + "Total XP: " + EnumChatFormatting.YELLOW + roundFloat(currentPet.petLevel.totalXp);
String petItemStr = EnumChatFormatting.AQUA + "Held Item: " + EnumChatFormatting.RED + "None";
if (currentPet.petItem != null) {
JsonObject json = NotEnoughUpdates.INSTANCE.manager.getItemInformation().get(currentPet.petItem);
if (json != null) {
String name = NotEnoughUpdates.INSTANCE.manager.jsonToStack(json).getDisplayName();
petItemStr = EnumChatFormatting.AQUA + "Held Item: " + name;
}
}
String etaStr = null;
String etaMaxStr = null;
if (currentPet.petLevel.level < 100) {
float remaining = currentPet.petLevel.currentLevelRequirement - currentPet.petLevel.levelXp;
if (remaining > 0) {
if (xpGain < 1000) {
etaStr = EnumChatFormatting.AQUA + "Until L" + (int) (currentPet.petLevel.level + 1) + ": " +
EnumChatFormatting.YELLOW + "N/A";
} else {
etaStr = EnumChatFormatting.AQUA + "Until L" + (int) (currentPet.petLevel.level + 1) + ": " +
EnumChatFormatting.YELLOW + Utils.prettyTime((long) (remaining) * 1000 * 60 * 60 / (long) xpGain);
}
}
if (currentPet.petLevel.level < 99 || !NotEnoughUpdates.INSTANCE.config.petOverlay.petOverlayText.contains(6)) {
float remainingMax = currentPet.petLevel.maxXP - currentPet.petLevel.totalXp;
if (remaining > 0) {
if (xpGain < 1000) {
etaMaxStr = EnumChatFormatting.AQUA + "Until L100: " +
EnumChatFormatting.YELLOW + "N/A";
} else {
etaMaxStr = EnumChatFormatting.AQUA + "Until L100: " +
EnumChatFormatting.YELLOW + Utils.prettyTime((long) (remainingMax) * 1000 * 60 * 60 / (long) xpGain);
}
}
}
}
String finalEtaStr = etaStr;
String finalEtaMaxStr = etaMaxStr;
String finalXpGainString = xpGainString;
String finalPetItemStr = petItemStr;
return new ArrayList() {{
for (int index : NotEnoughUpdates.INSTANCE.config.petOverlay.petOverlayText) {
switch (index) {
case 0:
add(petName);
break;
case 1:
add(lvlStringShort);
break;
case 2:
add(lvlString);
break;
case 3:
add(finalXpGainString);
break;
case 4:
add(totalXpString);
break;
case 5:
add(finalPetItemStr);
break;
case 6:
if (finalEtaStr != null) add(finalEtaStr);
break;
case 7:
if (finalEtaMaxStr != null) add(finalEtaMaxStr);
break;
}
}
}};
}
@Override
public void updateFrequent() {
Pet currentPet = getCurrentPet();
if (!NotEnoughUpdates.INSTANCE.config.petOverlay.enablePetInfo || currentPet == null) {
overlayStrings = null;
} else {
overlayStrings = new ArrayList<>();
overlayStrings.addAll(createStringsForPet(currentPet, false));
Pet currentPet2 = getCurrentPet2();
if (currentPet2 != null) {
overlayStrings.add("");
overlayStrings.addAll(createStringsForPet(currentPet2, true));
}
}
}
public void update() {
if (!NotEnoughUpdates.INSTANCE.config.petOverlay.enablePetInfo &&
!NotEnoughUpdates.INSTANCE.config.itemOverlays.enableMonkeyCheck) {
overlayStrings = null;
return;
}
int updateTime = 60000;
if (NotEnoughUpdates.INSTANCE.hasSkyblockScoreboard()) {
ProfileApiSyncer.getInstance().requestResync("petinfo", updateTime, () -> {}, PetInfoOverlay::getAndSetPet);
}
Pet currentPet = getCurrentPet();
if (currentPet == null) {
overlayStrings = null;
} else {
lastUpdate = System.currentTimeMillis();
levelXpLast = currentPet.petLevel.levelXp;
updatePetLevels();
}
}
private static GuiProfileViewer.PetLevel getMaxLevel(JsonArray levels, int offset) {
float xpTotal = 0;
float level = 1;
float currentLevelRequirement = 0;
for (int i = offset; i < offset + 99; i++) {
currentLevelRequirement = levels.get(i).getAsFloat();
xpTotal += currentLevelRequirement;
level += 1;
}
if (level <= 0) {
level = 1;
} else if (level > 100) {
level = 100;
}
GuiProfileViewer.PetLevel levelObj = new GuiProfileViewer.PetLevel();
levelObj.level = level;
levelObj.currentLevelRequirement = currentLevelRequirement;
levelObj.maxXP = xpTotal;
levelObj.levelPercentage = 1;
levelObj.levelXp = currentLevelRequirement - 5;
levelObj.totalXp = xpTotal - 5;
return levelObj;
}
private static GuiProfileViewer.PetLevel getLevel(
JsonArray levels,
int offset,
float xpThisLevel,
int xpMaxThisLevel
) {
float xpTotal = 0;
float level = 1;
float currentLevelRequirement = 0;
float exp = xpThisLevel;
boolean addLevel = true;
for (int i = offset; i < offset + 99; i++) {
if (addLevel) {
currentLevelRequirement = levels.get(i).getAsFloat();
xpTotal += currentLevelRequirement;
if (currentLevelRequirement >= xpMaxThisLevel) {
addLevel = false;
} else {
exp += currentLevelRequirement;
level += 1;
}
} else {
xpTotal += levels.get(i).getAsFloat();
}
}
level += xpThisLevel / currentLevelRequirement;
if (level <= 0) {
level = 1;
} else if (level > 100) {
level = 100;
}
GuiProfileViewer.PetLevel levelObj = new GuiProfileViewer.PetLevel();
levelObj.level = level;
levelObj.currentLevelRequirement = currentLevelRequirement;
levelObj.maxXP = xpTotal;
levelObj.levelPercentage = xpThisLevel / currentLevelRequirement;
levelObj.levelXp = xpThisLevel;
levelObj.totalXp = exp;
return levelObj;
}
public static Pet getPetFromStack(String name, String[] lore) {
if (Constants.PETS == null || Constants.PETS.get("pet_levels") == null ||
Constants.PETS.get("pet_levels") instanceof JsonNull) {
Utils.showOutdatedRepoNotification();
return null;
}
String petType = null;
Rarity rarity = null;
String heldItem = null;
GuiProfileViewer.PetLevel level = null;
Matcher petNameMatcher = PET_NAME_PATTERN.matcher(name);
if (petNameMatcher.matches()) {
String petStringMatch = petNameMatcher.group(1);
char colChar = petStringMatch.charAt(0);
EnumChatFormatting col = EnumChatFormatting.RESET;
for (EnumChatFormatting formatting : EnumChatFormatting.values()) {
if (formatting.toString().equals("\u00a7" + colChar)) {
col = formatting;
break;
}
}
rarity = Rarity.COMMON;
if (col != EnumChatFormatting.RESET) {
rarity = Rarity.getRarityFromColor(col);
}
petType = Utils.cleanColour(petStringMatch.substring(1))
.replaceAll("[^\\w ]", "").trim()
.replace(" ", "_").toUpperCase();
}
if (petType == null || rarity == null) {
return null;
}
for (String line : lore) {
Matcher xpLineMatcher = XP_LINE_PATTERN.matcher(Utils.cleanColour(line));
if (line.startsWith("\u00a76Held Item: ")) {
String after = line.substring("\u00a76Held Item: ".length());
if (itemMap == null) {
itemMap = new HashMap<>();
for (Map.Entry entry : NotEnoughUpdates.INSTANCE.manager
.getItemInformation()
.entrySet()) {
boolean petItem = false;
if (entry.getKey().startsWith("PET_ITEM_")) {
petItem = true;
} else {
ItemStack stack = NotEnoughUpdates.INSTANCE.manager.jsonToStack(entry.getValue());
if (stack.hasTagCompound()) {
String[] itemLore = NotEnoughUpdates.INSTANCE.manager.getLoreFromNBT(stack.getTagCompound());
for (String itemLoreLine : itemLore) {
if (itemLoreLine.contains("PET ITEM")) {
petItem = true;
break;
}
}
}
}
if (petItem) {
ItemStack stack = NotEnoughUpdates.INSTANCE.manager.jsonToStack(entry.getValue());
itemMap.put(stack.getDisplayName().replace("\u00a7f\u00a7f", ""), entry.getKey());
}
}
}
if (itemMap.containsKey(after)) {
heldItem = itemMap.get(after);
}
} else if (xpLineMatcher.matches()) {
String xpThisLevelS = xpLineMatcher.group(1);
String xpMaxThisLevelS = xpLineMatcher.group(2).toLowerCase();
try {
float xpThisLevel = Float.parseFloat(xpThisLevelS.replace(",", ""));
int mutiplier = 1;
char end = xpMaxThisLevelS.charAt(xpMaxThisLevelS.length() - 1);
if (end < '0' || end > '9') {
xpMaxThisLevelS = xpMaxThisLevelS.substring(0, xpMaxThisLevelS.length() - 1);
switch (end) {
case 'k':
mutiplier = 1000;
break;
case 'm':
mutiplier = 1000000;
break;
case 'b':
mutiplier = 1000000000;
break;
}
}
int xpMaxThisLevel = (int) (Float.parseFloat(xpMaxThisLevelS) * mutiplier);
level = getLevel(
Constants.PETS.get("pet_levels").getAsJsonArray(),
rarity.petOffset,
xpThisLevel,
xpMaxThisLevel
);
} catch (NumberFormatException ignored) {
}
} else if (line.equals("\u00a7b\u00a7lMAX LEVEL")) {
level = getMaxLevel(Constants.PETS.get("pet_levels").getAsJsonArray(), rarity.petOffset);
}
}
if (level != null) {
Pet pet = new Pet();
pet.petItem = heldItem;
pet.petLevel = level;
pet.rarity = rarity;
pet.petType = petType;
JsonObject petTypes = Constants.PETS.get("pet_types").getAsJsonObject();
pet.petXpType =
petTypes.has(pet.petType) ? petTypes.get(pet.petType.toUpperCase()).getAsString().toLowerCase() : "unknown";
return pet;
}
return null;
}
private static final HashMap removeMap = new HashMap<>();
@SubscribeEvent
public void onTick(TickEvent.ClientTickEvent event) {
if (Minecraft.getMinecraft().currentScreen instanceof GuiChest && RenderListener.inventoryLoaded) {
GuiChest chest = (GuiChest) Minecraft.getMinecraft().currentScreen;
ContainerChest container = (ContainerChest) chest.inventorySlots;
IInventory lower = container.getLowerChestInventory();
String containerName = lower.getDisplayName().getUnformattedText();
if (lower.getSizeInventory() >= 54) {
int page = 0;
int maxPage = 1;
boolean isPets = false;
if (containerName.equals("Pets")) {
isPets = true;
} else {
Matcher matcher = PET_CONTAINER_PAGE.matcher(containerName);
if (matcher.matches()) {
try {
page = Integer.parseInt(matcher.group(1)) - 1;
maxPage = Integer.parseInt(matcher.group(2));
isPets = true;
} catch (NumberFormatException ignored) {
}
}
}
if (isPets) {
boolean hasItem = false;
for (int i = 0; i < lower.getSizeInventory(); i++) {
if (lower.getStackInSlot(i) != null) {
hasItem = true;
break;
}
}
if (!hasItem) return;
Set clear = new HashSet<>();
for (int i : config.petMap.keySet()) {
if (i >= maxPage * 28) {
clear.add(i);
}
}
config.petMap.keySet().removeAll(clear);
Set removeSet = new HashSet<>();
long currentTime = System.currentTimeMillis();
for (int index = 0; index < 28; index++) {
int petIndex = page * 28 + index;
int itemIndex = 10 + index + index / 7 * 2;
ItemStack stack = lower.getStackInSlot(itemIndex);
if (stack == null || !stack.hasTagCompound()) {
if (index < 27) {
int itemIndexNext = 10 + (index + 1) + (index + 1) / 7 * 2;
ItemStack stackNext = lower.getStackInSlot(itemIndexNext);
if (stackNext == null || !stackNext.hasTagCompound()) {
int old = removeMap.getOrDefault(petIndex, 0);
if (old >= 20) {
config.petMap.remove(petIndex);
} else {
removeSet.add(petIndex);
removeMap.put(petIndex, old + 1);
}
}
}
} else {
String[] lore = NotEnoughUpdates.INSTANCE.manager.getLoreFromNBT(stack.getTagCompound());
Pet pet = getPetFromStack(stack.getDisplayName(), lore);
if (pet != null) {
config.petMap.put(petIndex, pet);
if (currentTime - lastPetSelect > 500) {
boolean foundDespawn = false;
for (String line : lore) {
if (line.startsWith("\u00a77\u00a7cClick to despawn")) {
config.selectedPet = petIndex;
foundDespawn = true;
break;
}
if (line.equals("\u00a77\u00a77Selected pet: \u00a7cNone")) {
clearPet();
}
}
if (!foundDespawn && config.selectedPet == petIndex && currentTime - lastPetSelect > 500) {
clearPet();
}
}
}
}
}
removeMap.keySet().retainAll(removeSet);
} else if (containerName.equals("Your Equipment")) {
ItemStack petStack = lower.getStackInSlot(47);
if (petStack != null && petStack.getItem() == Items.skull) {
NBTTagCompound tag = petStack.getTagCompound();
if (tag.hasKey("ExtraAttributes", 10)) {
NBTTagCompound ea = tag.getCompoundTag("ExtraAttributes");
if (ea.hasKey("petInfo")) {
JsonParser jsonParser = new JsonParser();
JsonObject petInfoObject = jsonParser.parse(ea.getString("petInfo")).getAsJsonObject();
JsonObject jsonStack = NotEnoughUpdates.INSTANCE.manager.getJsonForItem(petStack);
if (jsonStack == null || !jsonStack.has("lore") || !petInfoObject.has("exp")) {
return;
}
int rarity = Utils.getRarityFromLore(jsonStack.get("lore").getAsJsonArray());
String rarityString = Utils.getRarityFromInt(rarity);
String name = StringUtils.cleanColour(petStack.getDisplayName());
name = name.substring(name.indexOf(']') + 1).trim().replace(' ', '_').toUpperCase();
float petXp = petInfoObject.get("exp").getAsFloat();
double petLevel = XPInformation.getInstance().getPetLevel(name, petXp, rarityString);
int index = getClosestPetIndex(name, rarity, "", (float) petLevel);
if (index != config.selectedPet) {
clearPet();
setCurrentPet(index);
}
}
}
}
}
}
}
}
@Override
protected Vector2f getSize(List strings) {
if (!NotEnoughUpdates.INSTANCE.config.petOverlay.petOverlayIcon) return super.getSize(strings);
return super.getSize(strings).translate(25, 0);
}
@Override
protected Vector2f getTextOffset() {
if (!NotEnoughUpdates.INSTANCE.config.petOverlay.petOverlayIcon) return super.getTextOffset();
if (this.styleSupplier.get() != TextOverlayStyle.BACKGROUND) return super.getTextOffset().translate(30, 0);
return super.getTextOffset().translate(25, 0);
}
@Override
public void renderDummy() {
super.renderDummy();
if (!NotEnoughUpdates.INSTANCE.config.petOverlay.petOverlayIcon) return;
JsonObject petItem = NotEnoughUpdates.INSTANCE.manager.getItemInformation().get("ROCK;0");
if (petItem != null) {
Vector2f position = getPosition(overlayWidth, overlayHeight);
int x = (int) position.x;
int y = (int) position.y;
ItemStack stack = NotEnoughUpdates.INSTANCE.manager.jsonToStack(petItem);
GlStateManager.enableDepth();
GlStateManager.pushMatrix();
GlStateManager.translate(x - 2, y - 2, 0);
GlStateManager.scale(2, 2, 1);
Utils.drawItemStack(stack, 0, 0);
GlStateManager.popMatrix();
}
}
@Override
public void render() {
super.render();
Pet currentPet = getCurrentPet();
if (currentPet == null) {
overlayStrings = null;
return;
}
if (overlayStrings == null) {
return;
}
if (!NotEnoughUpdates.INSTANCE.config.petOverlay.petOverlayIcon) return;
int mythicRarity = currentPet.rarity.petId;
if (currentPet.rarity.petId == 5) {
mythicRarity = 4;
}
JsonObject petItem = NotEnoughUpdates.INSTANCE.manager.getItemInformation().get(
currentPet.petType + ";" + mythicRarity);
if (petItem != null) {
Vector2f position = getPosition(overlayWidth, overlayHeight);
int x = (int) position.x;
int y = (int) position.y;
ItemStack stack = NotEnoughUpdates.INSTANCE.manager.jsonToStack(petItem);
GlStateManager.enableDepth();
GlStateManager.pushMatrix();
GlStateManager.translate(x - 2, y - 2, 0);
GlStateManager.scale(2, 2, 1);
Utils.drawItemStack(stack, 0, 0);
GlStateManager.popMatrix();
}
Pet currentPet2 = getCurrentPet2();
if (currentPet2 != null) {
JsonObject petItem2 = NotEnoughUpdates.INSTANCE.manager.getItemInformation().get(
currentPet2.petType + ";" + currentPet2.rarity.petId);
if (petItem2 != null) {
Vector2f position = getPosition(overlayWidth, overlayHeight);
int x = (int) position.x;
int y = (int) position.y + NotEnoughUpdates.INSTANCE.config.petOverlay.petOverlayText.size() * 10 + 10;
ItemStack stack = NotEnoughUpdates.INSTANCE.manager.jsonToStack(petItem2);
GlStateManager.enableDepth();
GlStateManager.pushMatrix();
GlStateManager.translate(x - 2, y - 2, 0);
GlStateManager.scale(2, 2, 1);
Utils.drawItemStack(stack, 0, 0);
GlStateManager.popMatrix();
}
}
}
public static float getBoostMultiplier(String boostName) {
if (boostName == null) return 1;
boostName = boostName.toLowerCase();
if (boostName.equalsIgnoreCase("PET_ITEM_ALL_SKILLS_BOOST_COMMON")) {
return 1.1f;
} else if (boostName.equalsIgnoreCase("ALL_SKILLS_SUPER_BOOST")) {
return 1.2f;
} else if (boostName.endsWith("epic")) {
return 1.5f;
} else if (boostName.endsWith("rare")) {
return 1.4f;
} else if (boostName.endsWith("uncommon")) {
return 1.3f;
} else if (boostName.endsWith("common")) {
return 1.2f;
} else {
return 1;
}
}
private static List validXpTypes = Lists.newArrayList(
"mining",
"foraging",
"enchanting",
"farming",
"combat",
"fishing",
"alchemy"
);
public static void onStackClick(ItemStack stack, int windowId, int slotId, int mouseButtonClicked, int mode) {
if (mode != 0) return;
if (mouseButtonClicked != 0 && mouseButtonClicked != 1) return;
int slotIdMod = (slotId - 10) % 9;
if (slotId >= 10 && slotId <= 43 && slotIdMod >= 0 && slotIdMod <= 6 &&
Minecraft.getMinecraft().currentScreen instanceof GuiChest) {
GuiChest chest = (GuiChest) Minecraft.getMinecraft().currentScreen;
ContainerChest container = (ContainerChest) chest.inventorySlots;
IInventory lower = container.getLowerChestInventory();
String containerName = lower.getDisplayName().getUnformattedText();
if (lower.getSizeInventory() >= 54 && windowId == container.windowId) {
int page = 0;
boolean isPets = false;
if (containerName.equals("Pets")) {
isPets = true;
} else {
Matcher matcher = PET_CONTAINER_PAGE.matcher(containerName);
if (matcher.matches()) {
try {
page = Integer.parseInt(matcher.group(1)) - 1;
isPets = true;
} catch (NumberFormatException ignored) {
}
}
}
if (isPets) {
ItemStack removingStack = lower.getStackInSlot(50);
boolean isRemoving =
removingStack != null && removingStack.getItem() == Items.dye && removingStack.getItemDamage() == 10;
int newSelected = (slotId - 10) - (slotId - 10) / 9 * 2 + page * 28;
lastPetSelect = System.currentTimeMillis();
if (isRemoving) {
if (newSelected == config.selectedPet) {
clearPet();
} else if (config.selectedPet > newSelected) {
config.selectedPet--;
}
} else {
setCurrentPet(newSelected);
String[] lore = NotEnoughUpdates.INSTANCE.manager.getLoreFromNBT(stack.getTagCompound());
Pet pet = getPetFromStack(stack.getDisplayName(), lore);
if (pet != null) {
config.petMap.put(config.selectedPet, pet);
}
}
}
}
}
}
public static float getXpGain(Pet pet, float xp, String xpType) {
if (pet.petLevel.level >= 100) return 0;
if (validXpTypes == null)
validXpTypes = Lists.newArrayList("mining", "foraging", "enchanting", "farming", "combat", "fishing", "alchemy");
if (!validXpTypes.contains(xpType.toLowerCase())) return 0;
float tamingPercent = 1.0f + (config.tamingLevel / 100f);
xp = xp * tamingPercent;
xp = xp + (xp * config.beastMultiplier / 100f);
if (pet.petXpType != null && !pet.petXpType.equalsIgnoreCase(xpType)) {
xp = xp / 3f;
if (xpType.equalsIgnoreCase("alchemy") || xpType.equalsIgnoreCase("enchanting")) {
xp = xp / 4f;
}
}
if (xpType.equalsIgnoreCase("mining") || xpType.equalsIgnoreCase("fishing")) {
xp = xp * 1.5f;
}
if (pet.petItem != null) {
Matcher petItemMatcher = XP_BOOST_PATTERN.matcher(pet.petItem);
if ((petItemMatcher.matches() && petItemMatcher.group(1).equalsIgnoreCase(xpType))
|| pet.petItem.equalsIgnoreCase("ALL_SKILLS_SUPER_BOOST")) {
xp = xp * getBoostMultiplier(pet.petItem);
}
}
return xp;
}
private final HashMap skillInfoMapLast = new HashMap<>();
public void updatePetLevels() {
HashMap skillInfoMap = XPInformation.getInstance().getSkillInfoMap();
long currentTime = System.currentTimeMillis();
float totalGain = 0;
Pet currentPet = getCurrentPet();
for (Map.Entry entry : skillInfoMap.entrySet()) {
if (entry.getValue().level == 50 && entry.getValue().fromApi) continue;
float skillXp = entry.getValue().totalXp;
if (skillInfoMapLast.containsKey(entry.getKey())) {
float skillXpLast = skillInfoMapLast.get(entry.getKey());
if (skillXpLast <= 0) {
skillInfoMapLast.put(entry.getKey(), skillXp);
} else if (skillXp > skillXpLast) {
lastXpGain = currentTime;
float deltaXp = skillXp - skillXpLast;
float gain = getXpGain(currentPet, deltaXp, entry.getKey().toUpperCase());
totalGain += gain;
skillInfoMapLast.put(entry.getKey(), skillXp);
}
} else {
skillInfoMapLast.put(entry.getKey(), skillXp);
}
}
xpGainHourLast = xpGainHour;
if (xpAddTimer > 0 || totalGain > 0) {
if (totalGain > 0) {
xpAddTimer = 10;
} else {
xpAddTimer--;
}
currentPet.petLevel.totalXp += totalGain;
xpGainQueue.add(0, totalGain);
while (xpGainQueue.size() > 30) {
xpGainQueue.removeLast();
}
if (xpGainQueue.size() > 1) {
float tot = 0;
float greatest = 0;
for (float f : xpGainQueue) {
tot += f;
greatest = Math.max(greatest, f);
}
xpGainHour = (tot - greatest) * (60 * 60) / (xpGainQueue.size() - 1);
}
}
JsonObject petsJson = Constants.PETS;
if (currentPet != null && petsJson != null) {
currentPet.petLevel = GuiProfileViewer.getPetLevel(
currentPet.petItem,
currentPet.rarity.name(),
currentPet.petLevel.totalXp
);
}
}
public String roundFloat(float f) {
if (f % 1 < 0.05f) {
return NumberFormat.getNumberInstance().format((int) f);
} else {
String s = Utils.floatToString(f, 1);
if (s.contains(".")) {
return NumberFormat.getNumberInstance().format((int) f) + '.' + s.split("\\.")[1];
} else if (s.contains(",")) {
return NumberFormat.getNumberInstance().format((int) f) + ',' + s.split(",")[1];
} else {
return s;
}
}
}
@SubscribeEvent
public void switchWorld(WorldEvent.Load event) {
if (NotEnoughUpdates.INSTANCE.hasSkyblockScoreboard()) {
ProfileApiSyncer.getInstance().requestResync("petinfo_quick", 10000, () -> {}, PetInfoOverlay::getAndSetPet);
}
}
private int lastLevelHovered = 0;
private String lastItemHovered = null;
private static HashMap itemMap = null;
@SubscribeEvent(priority = EventPriority.HIGHEST, receiveCanceled = true)
public void onTooltip(ItemTooltipEvent event) {
for (String line : event.toolTip) {
if (line.startsWith("\u00a7o\u00a77[Lvl ")) {
lastItemHovered = null;
String after = line.substring("\u00a7o\u00a77[Lvl ".length());
if (after.contains("]")) {
String levelStr = after.split("]")[0];
try {
lastLevelHovered = Integer.parseInt(levelStr.trim());
} catch (Exception ignored) {
}
}
} else if (line.startsWith("\u00a75\u00a7o\u00a76Held Item: ")) {
String after = line.substring("\u00a75\u00a7o\u00a76Held Item: ".length());
if (itemMap == null) {
itemMap = new HashMap<>();
for (Map.Entry entry : NotEnoughUpdates.INSTANCE.manager
.getItemInformation()
.entrySet()) {
if (entry.getKey().equals("ALL_SKILLS_SUPER_BOOST") ||
XP_BOOST_PATTERN.matcher(entry.getKey()).matches()) {
ItemStack stack = NotEnoughUpdates.INSTANCE.manager.jsonToStack(entry.getValue());
itemMap.put(stack.getDisplayName(), entry.getKey());
}
}
}
if (itemMap.containsKey(after)) {
lastItemHovered = itemMap.get(after);
}
}
}
}
private static final Pattern AUTOPET_EQUIP = Pattern.compile(
"\u00a7cAutopet \u00a7eequipped your \u00a77\\[Lvl (\\d+)] \u00a7(.{2,})\u00a7e! \u00a7a\u00a7lVIEW RULE\u00a7r");
@SubscribeEvent(priority = EventPriority.HIGHEST)
public void onChatReceived(ClientChatReceivedEvent event) {
NEUConfig config = NotEnoughUpdates.INSTANCE.config;
if (NotEnoughUpdates.INSTANCE.hasSkyblockScoreboard() &&
(config.petOverlay.enablePetInfo || config.itemOverlays.enableMonkeyCheck || config.petOverlay.petInvDisplay)) {
if (event.type == 0) {
String chatMessage = Utils.cleanColour(event.message.getUnformattedText());
Matcher autopetMatcher = AUTOPET_EQUIP.matcher(event.message.getFormattedText());
if (event.message.getUnformattedText().startsWith("You summoned your") ||
System.currentTimeMillis() - NEUOverlay.cachedPetTimer < 500) {
NEUOverlay.cachedPetTimer = System.currentTimeMillis();
NEUOverlay.shouldUseCachedPet = false;
} else if (autopetMatcher.matches()) {
NEUOverlay.shouldUseCachedPet = false;
try {
lastLevelHovered = Integer.parseInt(autopetMatcher.group(1));
} catch (NumberFormatException ignored) {
}
String petStringMatch = autopetMatcher.group(2);
char colChar = petStringMatch.charAt(0);
EnumChatFormatting col = EnumChatFormatting.RESET;
for (EnumChatFormatting formatting : EnumChatFormatting.values()) {
if (formatting.toString().equals("\u00a7" + colChar)) {
col = formatting;
break;
}
}
Rarity rarity = Rarity.COMMON;
if (col != EnumChatFormatting.RESET) {
rarity = Rarity.getRarityFromColor(col);
}
String pet = Utils.cleanColour(petStringMatch.substring(1))
.replaceAll("[^\\w ]", "").trim()
.replace(" ", "_").toUpperCase();
setCurrentPet(getClosestPetIndex(pet, rarity.petId, "", lastLevelHovered));
if (PetInfoOverlay.config.selectedPet == -1) {
Minecraft.getMinecraft().thePlayer.addChatMessage(new ChatComponentText(
EnumChatFormatting.RED + "[NEU] Can't find pet \u00a7" + petStringMatch +
EnumChatFormatting.RED + " try revisiting all pages of /pets."));
}
} else if ((chatMessage.toLowerCase().startsWith("you despawned your")) || (chatMessage.toLowerCase().contains(
"switching to profile"))
|| (chatMessage.toLowerCase().contains("transferring you to a new island..."))) {
clearPet();
}
}
}
}
}