aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Update Notes/2.1.1.md1
-rw-r--r--build.gradle.kts1
-rw-r--r--settings.gradle.kts2
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java1
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/commands/dev/DevTestCommand.java3
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/listener/ItemTooltipListener.java7
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/ItemCooldowns.java2
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/PetInfoOverlay.java599
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/miscgui/GuiItemRecipe.java13
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/miscgui/KatSitterOverlay.java34
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/overlays/TimersOverlay.java2
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/profileviewer/GuiProfileViewer.java73
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/profileviewer/PetsPage.java14
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/profileviewer/PlayerStats.java14
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/recipes/NeuRecipe.java2
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/recipes/RecipeType.java3
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/util/ItemUtils.java30
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/util/Utils.java5
-rw-r--r--src/main/kotlin/io/github/moulberry/notenoughupdates/recipes/KatRecipe.kt164
-rw-r--r--src/main/kotlin/io/github/moulberry/notenoughupdates/util/KotlinJsonUtils.kt31
-rw-r--r--src/main/kotlin/io/github/moulberry/notenoughupdates/util/PetLeveling.kt111
-rw-r--r--src/main/resources/assets/notenoughupdates/textures/gui/katting_tall.pngbin0 -> 13117 bytes
-rw-r--r--src/test/kotlin/io/github/moulberry/notenoughupdates/util/PetLevelingTest.kt403
23 files changed, 1102 insertions, 413 deletions
diff --git a/Update Notes/2.1.1.md b/Update Notes/2.1.1.md
index 0b7cc4a3..b5f660a4 100644
--- a/Update Notes/2.1.1.md
+++ b/Update Notes/2.1.1.md
@@ -7,6 +7,7 @@
- Added Custom Wither Cloak - Cobble8
- Added universal GUI editor - nopo
- Added Essenceupgrades GUI - Lulonaut
+- Added Katsitting recipe upgrade - nea89
### **Minor Changes:**
diff --git a/build.gradle.kts b/build.gradle.kts
index a93fccfc..04207cf2 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -174,6 +174,7 @@ val kotlinDependencyCollectionJar by tasks.creating(Zip::class) {
tasks.shadowJar {
archiveClassifier.set("dep-dev")
+ archiveBaseName.set("NotEnoughUpdates")
configurations = listOf(shadowImplementation, shadowApi)
exclude("**/module-info.class", "LICENSE.txt")
dependencies {
diff --git a/settings.gradle.kts b/settings.gradle.kts
index eb291fb5..408d0f75 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -38,3 +38,5 @@ pluginManagement {
}
}
}
+
+rootProject.name = "NotEnoughUpdates"
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java b/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java
index 32c8aa31..286f667a 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java
@@ -82,6 +82,7 @@ import io.github.moulberry.notenoughupdates.overlays.EquipmentOverlay;
import io.github.moulberry.notenoughupdates.overlays.FuelBar;
import io.github.moulberry.notenoughupdates.overlays.OverlayManager;
import io.github.moulberry.notenoughupdates.profileviewer.ProfileViewer;
+import io.github.moulberry.notenoughupdates.recipes.KatRecipe;
import io.github.moulberry.notenoughupdates.recipes.RecipeGenerator;
import io.github.moulberry.notenoughupdates.util.Constants;
import io.github.moulberry.notenoughupdates.util.SBInfo;
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/commands/dev/DevTestCommand.java b/src/main/java/io/github/moulberry/notenoughupdates/commands/dev/DevTestCommand.java
index 5ee31aa9..69033345 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/commands/dev/DevTestCommand.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/commands/dev/DevTestCommand.java
@@ -19,17 +19,20 @@
package io.github.moulberry.notenoughupdates.commands.dev;
+import com.google.gson.Gson;
import io.github.moulberry.notenoughupdates.BuildFlags;
import io.github.moulberry.notenoughupdates.NotEnoughUpdates;
import io.github.moulberry.notenoughupdates.commands.ClientCommandBase;
import io.github.moulberry.notenoughupdates.core.config.GuiPositionEditor;
import io.github.moulberry.notenoughupdates.core.util.MiscUtils;
import io.github.moulberry.notenoughupdates.miscfeatures.FishingHelper;
+import io.github.moulberry.notenoughupdates.miscfeatures.PetInfoOverlay;
import io.github.moulberry.notenoughupdates.miscfeatures.customblockzones.CustomBiomes;
import io.github.moulberry.notenoughupdates.miscfeatures.customblockzones.LocationChangeEvent;
import io.github.moulberry.notenoughupdates.miscfeatures.customblockzones.SpecialBlockZone;
import io.github.moulberry.notenoughupdates.miscgui.GuiPriceGraph;
import io.github.moulberry.notenoughupdates.miscgui.minionhelper.MinionHelperManager;
+import io.github.moulberry.notenoughupdates.profileviewer.GuiProfileViewer;
import io.github.moulberry.notenoughupdates.util.PronounDB;
import io.github.moulberry.notenoughupdates.util.SBInfo;
import io.github.moulberry.notenoughupdates.util.TabListUtils;
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/listener/ItemTooltipListener.java b/src/main/java/io/github/moulberry/notenoughupdates/listener/ItemTooltipListener.java
index d9a00370..1e3dd7e0 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/listener/ItemTooltipListener.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/listener/ItemTooltipListener.java
@@ -32,6 +32,7 @@ import io.github.moulberry.notenoughupdates.core.util.StringUtils;
import io.github.moulberry.notenoughupdates.miscfeatures.PetInfoOverlay;
import io.github.moulberry.notenoughupdates.profileviewer.GuiProfileViewer;
import io.github.moulberry.notenoughupdates.util.Constants;
+import io.github.moulberry.notenoughupdates.util.PetLeveling;
import io.github.moulberry.notenoughupdates.util.Utils;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.inventory.GuiChest;
@@ -553,7 +554,7 @@ public class ItemTooltipListener {
if (event.toolTip.size() < 7) return;
if (event.itemStack.getTagCompound().hasKey("NEUHIDEPETTOOLTIP")) return;
if (Utils.cleanColour(event.toolTip.get(1)).matches(petToolTipRegex)) {
- GuiProfileViewer.PetLevel petLevel;
+ PetLeveling.PetLevel petLevel;
int xpLine = -1;
for (int i = event.toolTip.size() - 1; i >= 0; i--) {
@@ -581,9 +582,9 @@ public class ItemTooltipListener {
event.toolTip.add(
xpLine + 1,
- EnumChatFormatting.GRAY + "EXP: " + EnumChatFormatting.YELLOW + myFormatter.format(petLevel.levelXp) +
+ EnumChatFormatting.GRAY + "EXP: " + EnumChatFormatting.YELLOW + myFormatter.format(petLevel.getExpInCurrentLevel()) +
EnumChatFormatting.GOLD + "/" + EnumChatFormatting.YELLOW +
- myFormatter.format(petLevel.currentLevelRequirement)
+ myFormatter.format(petLevel.getExpRequiredForNextLevel())
);
}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/ItemCooldowns.java b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/ItemCooldowns.java
index 4fc63734..ee5eef9e 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/ItemCooldowns.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/ItemCooldowns.java
@@ -104,7 +104,7 @@ public class ItemCooldowns {
pet.petType.equalsIgnoreCase("monkey") &&
pet.rarity.equals(PetInfoOverlay.Rarity.LEGENDARY)
) {
- return 2000 - (int) (2000 * (0.005 * (int) pet.petLevel.level));
+ return 2000 - (int) (2000 * (0.005 * pet.petLevel.getCurrentLevel()));
}
}
return 2000;
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/PetInfoOverlay.java b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/PetInfoOverlay.java
index 20fa553f..61ebf7b5 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/PetInfoOverlay.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/PetInfoOverlay.java
@@ -35,9 +35,9 @@ 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.PetLeveling;
import io.github.moulberry.notenoughupdates.util.ProfileApiSyncer;
import io.github.moulberry.notenoughupdates.util.Utils;
import io.github.moulberry.notenoughupdates.util.XPInformation;
@@ -125,24 +125,26 @@ public class PetInfoOverlay extends TextOverlay {
}
return COMMON;
}
- }
-
- public static class Pet {
- public String petType;
- public Rarity rarity;
- public GuiProfileViewer.PetLevel petLevel;
- public String petXpType;
- public String petItem;
- public String skin;
- public int candyUsed;
- public String getPetId(boolean withoutBoost) {
- boolean shouldDecreaseRarity = withoutBoost && "PET_ITEM_TIER_BOOST".equals(petItem);
- return petType + ";" + (shouldDecreaseRarity ? rarity.petId - 1 : rarity.petId);
+ public PetInfoOverlay.Rarity nextRarity() {
+ switch (this) {
+ case COMMON:
+ return PetInfoOverlay.Rarity.UNCOMMON;
+ case UNCOMMON:
+ return PetInfoOverlay.Rarity.RARE;
+ case RARE:
+ return PetInfoOverlay.Rarity.EPIC;
+ case EPIC:
+ return PetInfoOverlay.Rarity.LEGENDARY;
+ case LEGENDARY:
+ return PetInfoOverlay.Rarity.MYTHIC;
+ }
+ return null;
}
-
}
+ private static final HashMap<Integer, Integer> removeMap = new HashMap<>();
+
public static class PetConfig {
public HashMap<Integer, Pet> petMap = new HashMap<>();
@@ -221,33 +223,7 @@ public class PetInfoOverlay extends TextOverlay {
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<Integer, Pet> 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 final HashMap<String, Float> skillInfoMapLast = new HashMap<>();
private static Pet getClosestPet(String petType, int petId, String petItem, float petLevel) {
Set<Pet> pets = config.petMap.values().stream().filter(pet -> pet.petType.equals(petType) &&
@@ -278,7 +254,7 @@ public class PetInfoOverlay extends TextOverlay {
Pet closestPet = null;
for (Pet pet : pets) {
- float distXp = Math.abs(pet.petLevel.level - petLevel);
+ float distXp = Math.abs(pet.petLevel.getCurrentLevel() - petLevel);
if (closestPet == null || distXp < closestXp) {
closestXp = distXp;
@@ -289,6 +265,24 @@ public class PetInfoOverlay extends TextOverlay {
return closestPet;
}
+ private static int getIdForPet(Pet pet) {
+ for (Map.Entry<Integer, Pet> 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 void getAndSetPet(ProfileViewer.Profile profile) {
Map<String, ProfileViewer.Level> skyblockInfo = profile.getSkyblockInfo(profile.getLatestProfile());
JsonObject invInfo = profile.getInventoryInfo(profile.getLatestProfile());
@@ -401,21 +395,129 @@ public class PetInfoOverlay extends TextOverlay {
return interp;
}
+ public static Pet getPetFromStack(NBTTagCompound tag) {
+ 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;
+ PetLeveling.PetLevel level = null;
+ String skin = null;
+
+ if (tag != null && tag.hasKey("ExtraAttributes")) {
+ NBTTagCompound ea = tag.getCompoundTag("ExtraAttributes");
+ if (ea.hasKey("petInfo")) {
+ JsonObject petInfo = new JsonParser().parse(ea.getString("petInfo")).getAsJsonObject();
+ petType = petInfo.get("type").getAsString();
+ rarity = Rarity.valueOf(petInfo.get("tier").getAsString());
+ // Should only default if from item list and repo missing exp: 0
+ level = PetLeveling.getPetLevelingForPet(petType, rarity)
+ .getPetLevel(Utils.getElementAsFloat(petInfo.get("exp"), 0));
+ if (petInfo.has("heldItem")) {
+ heldItem = petInfo.get("heldItem").getAsString();
+ }
+ if (petInfo.has("skin")) {
+ skin = "PET_SKIN_" + petInfo.get("skin").getAsString();
+ }
+ }
+ }
+
+ if (petType == null) {
+ return 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";
+ pet.skin = skin;
+
+ return pet;
+ }
+
+ public static float getXpGain(Pet pet, float xp, String xpType) {
+ if (pet.petLevel.getCurrentLevel() >= pet.petLevel.getMaxLevel()) 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;
+ }
+
+ @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 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.getPercentageToNextLevel() * 100f));
+ } catch (Exception ignored) {
+ return 0;
+ }
+ }
+
private List<String> createStringsForPet(Pet currentPet, boolean secondPet) {
- float levelXp = currentPet.petLevel.levelXp;
- if (!secondPet) levelXp = interp(currentPet.petLevel.levelXp, levelXpLast);
+ float levelXp = currentPet.petLevel.getExpInCurrentLevel();
+ if (!secondPet) levelXp = interp(currentPet.petLevel.getExpInCurrentLevel(), levelXpLast);
if (levelXp < 0) levelXp = 0;
String petName =
- EnumChatFormatting.GREEN + "[Lvl " + (int) currentPet.petLevel.level + "] " + currentPet.rarity.chatFormatting +
+ EnumChatFormatting.GREEN + "[Lvl " + currentPet.petLevel.getCurrentLevel() + "] " +
+ currentPet.rarity.chatFormatting +
WordUtils.capitalizeFully(currentPet.petType.replace("_", " "));
String lvlStringShort = EnumChatFormatting.AQUA + "" + roundFloat(levelXp) + "/" +
- roundFloat(currentPet.petLevel.currentLevelRequirement)
+ roundFloat(currentPet.petLevel.getExpRequiredForNextLevel())
+ EnumChatFormatting.YELLOW + " (" + getLevelPercent(currentPet) + "%)";
String lvlString = EnumChatFormatting.AQUA + "" + Utils.shortNumberFormat(levelXp, 0) + "/" +
- Utils.shortNumberFormat(currentPet.petLevel.currentLevelRequirement, 0)
+ Utils.shortNumberFormat(currentPet.petLevel.getExpRequiredForNextLevel(), 0)
+ EnumChatFormatting.YELLOW + " (" + getLevelPercent(currentPet) + "%)";
float xpGain;
@@ -438,7 +540,8 @@ public class PetInfoOverlay extends TextOverlay {
}
String totalXpString =
- EnumChatFormatting.AQUA + "Total XP: " + EnumChatFormatting.YELLOW + roundFloat(currentPet.petLevel.totalXp);
+ EnumChatFormatting.AQUA + "Total XP: " + EnumChatFormatting.YELLOW +
+ roundFloat(currentPet.petLevel.getExpTotal());
String petItemStr = EnumChatFormatting.AQUA + "Held Item: " + EnumChatFormatting.RED + "None";
if (currentPet.petItem != null) {
@@ -451,27 +554,27 @@ public class PetInfoOverlay extends TextOverlay {
String etaStr = null;
String etaMaxStr = null;
- if (currentPet.petLevel.level < currentPet.petLevel.maxLevel) {
- float remaining = currentPet.petLevel.currentLevelRequirement - currentPet.petLevel.levelXp;
+ if (currentPet.petLevel.getCurrentLevel() < currentPet.petLevel.getMaxLevel()) {
+ float remaining = currentPet.petLevel.getExpRequiredForNextLevel() - currentPet.petLevel.getExpInCurrentLevel();
if (remaining > 0) {
if (xpGain < 1000) {
- etaStr = EnumChatFormatting.AQUA + "Until L" + (int) (currentPet.petLevel.level + 1) + ": " +
+ etaStr = EnumChatFormatting.AQUA + "Until L" + (currentPet.petLevel.getCurrentLevel() + 1) + ": " +
EnumChatFormatting.YELLOW + "N/A";
} else {
- etaStr = EnumChatFormatting.AQUA + "Until L" + (int) (currentPet.petLevel.level + 1) + ": " +
+ etaStr = EnumChatFormatting.AQUA + "Until L" + (currentPet.petLevel.getCurrentLevel() + 1) + ": " +
EnumChatFormatting.YELLOW + Utils.prettyTime((long) (remaining) * 1000 * 60 * 60 / (long) xpGain);
}
}
- if (currentPet.petLevel.level < (currentPet.petLevel.maxLevel - 1) ||
+ if (currentPet.petLevel.getCurrentLevel() < (currentPet.petLevel.getMaxLevel() - 1) ||
!NotEnoughUpdates.INSTANCE.config.petOverlay.petOverlayText.contains(6)) {
- float remainingMax = currentPet.petLevel.maxXP - currentPet.petLevel.totalXp;
+ float remainingMax = currentPet.petLevel.getExpRequiredForMaxLevel() - currentPet.petLevel.getExpTotal();
if (remaining > 0) {
if (xpGain < 1000) {
- etaMaxStr = EnumChatFormatting.AQUA + "Until L" + (int) currentPet.petLevel.maxLevel + ": " +
+ etaMaxStr = EnumChatFormatting.AQUA + "Until L" + currentPet.petLevel.getMaxLevel() + ": " +
EnumChatFormatting.YELLOW + "N/A";
} else {
- etaMaxStr = EnumChatFormatting.AQUA + "Until L" + (int) currentPet.petLevel.maxLevel + ": " +
+ etaMaxStr = EnumChatFormatting.AQUA + "Until L" + currentPet.petLevel.getMaxLevel() + ": " +
EnumChatFormatting.YELLOW + Utils.prettyTime((long) (remainingMax) * 1000 * 60 * 60 / (long) xpGain);
}
}
@@ -514,25 +617,6 @@ public class PetInfoOverlay extends TextOverlay {
}};
}
- @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) {
@@ -551,195 +635,11 @@ public class PetInfoOverlay extends TextOverlay {
overlayStrings = null;
} else {
lastUpdate = System.currentTimeMillis();
- levelXpLast = currentPet.petLevel.levelXp;
+ levelXpLast = currentPet.petLevel.getExpInCurrentLevel();
updatePetLevels();
}
}
- public static Pet getPetFromStack(NBTTagCompound tag) {
- 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;
- String skin = null;
-
- if (tag != null && tag.hasKey("ExtraAttributes")) {
- NBTTagCompound ea = tag.getCompoundTag("ExtraAttributes");
- if (ea.hasKey("petInfo")) {
- JsonObject petInfo = new JsonParser().parse(ea.getString("petInfo")).getAsJsonObject();
- petType = petInfo.get("type").getAsString();
- rarity = Rarity.valueOf(petInfo.get("tier").getAsString());
- level = GuiProfileViewer.getPetLevel(
- petType,
- rarity.name(),
- Utils.getElementAsFloat(petInfo.get("exp"), 0) // Should only default if from item list and repo missing exp:0
- );
- if (petInfo.has("heldItem")) {
- heldItem = petInfo.get("heldItem").getAsString();
- }
- if (petInfo.has("skin")) {
- skin = "PET_SKIN_" + petInfo.get("skin").getAsString();
- }
- }
- }
-
- if (petType == null) {
- return 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";
- pet.skin = skin;
-
- return pet;
- }
-
- private static final HashMap<Integer, Integer> 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<Integer> clear = new HashSet<>();
- for (int i : config.petMap.keySet()) {
- if (i >= maxPage * 28) {
- clear.add(i);
- }
- }
- config.petMap.keySet().removeAll(clear);
-
- Set<Integer> 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.getTagCompound());
- 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 = GuiProfileViewer.getPetLevel(name, rarityString, petXp).level;
- int index = getClosestPetIndex(name, rarity, "", (float) petLevel);
- if (index != config.selectedPet) {
- clearPet();
- setCurrentPet(index);
- }
- }
- }
- }
- }
- }
- }
- }
-
@Override
protected Vector2f getSize(List<String> strings) {
if (!NotEnoughUpdates.INSTANCE.config.petOverlay.petOverlayIcon) return super.getSize(strings);
@@ -922,38 +822,140 @@ public class PetInfoOverlay extends TextOverlay {
}
}
- public static float getXpGain(Pet pet, float xp, String xpType) {
- if (pet.petLevel.level >= pet.petLevel.maxLevel) return 0;
+ @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 (validXpTypes == null)
- validXpTypes = Lists.newArrayList("mining", "foraging", "enchanting", "farming", "combat", "fishing", "alchemy");
- if (!validXpTypes.contains(xpType.toLowerCase())) return 0;
+ if (lower.getSizeInventory() >= 54) {
+ int page = 0;
+ int maxPage = 1;
+ boolean isPets = false;
- 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 (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;
- 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);
+ Set<Integer> clear = new HashSet<>();
+ for (int i : config.petMap.keySet()) {
+ if (i >= maxPage * 28) {
+ clear.add(i);
+ }
+ }
+ config.petMap.keySet().removeAll(clear);
+
+ Set<Integer> 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.getTagCompound());
+ 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 = PetLeveling.getPetLevelingForPet(name, Rarity.valueOf(rarityString))
+ .getPetLevel(petXp)
+ .getCurrentLevel();
+ int index = getClosestPetIndex(name, rarity, "", (float) petLevel);
+ if (index != config.selectedPet) {
+ clearPet();
+ setCurrentPet(index);
+ }
+ }
+ }
+ }
+ }
}
}
- return xp;
}
- private final HashMap<String, Float> skillInfoMapLast = new HashMap<>();
-
public void updatePetLevels() {
HashMap<String, XPInformation.SkillInfo> skillInfoMap = XPInformation.getInstance().getSkillInfoMap();
@@ -991,7 +993,7 @@ public class PetInfoOverlay extends TextOverlay {
xpAddTimer--;
}
- currentPet.petLevel.totalXp += totalGain;
+ currentPet.petLevel.setExpTotal(totalGain + currentPet.petLevel.getExpTotal());
xpGainQueue.add(0, totalGain);
while (xpGainQueue.size() > 30) {
@@ -1012,12 +1014,25 @@ public class PetInfoOverlay extends TextOverlay {
JsonObject petsJson = Constants.PETS;
if (currentPet != null && petsJson != null) {
- currentPet.petLevel = GuiProfileViewer.getPetLevel(
- currentPet.petType,
- currentPet.rarity.name(),
- currentPet.petLevel.totalXp
- );
+ currentPet.petLevel = PetLeveling.getPetLevelingForPet(currentPet.petType, currentPet.rarity)
+ .getPetLevel(currentPet.petLevel.getExpTotal());
+ }
+ }
+
+ public static class Pet {
+ public String petType;
+ public Rarity rarity;
+ public PetLeveling.PetLevel petLevel;
+ public String petXpType;
+ public String petItem;
+ public String skin;
+ public int candyUsed;
+
+ public String getPetId(boolean withoutBoost) {
+ boolean shouldDecreaseRarity = withoutBoost && "PET_ITEM_TIER_BOOST".equals(petItem);
+ return petType + ";" + (shouldDecreaseRarity ? rarity.petId - 1 : rarity.petId);
}
+
}
public String roundFloat(float f) {
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscgui/GuiItemRecipe.java b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/GuiItemRecipe.java
index cc9f36fa..e4b4269d 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/miscgui/GuiItemRecipe.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/GuiItemRecipe.java
@@ -28,6 +28,7 @@ import io.github.moulberry.notenoughupdates.recipes.RecipeHistory;
import io.github.moulberry.notenoughupdates.recipes.RecipeSlot;
import io.github.moulberry.notenoughupdates.recipes.RecipeType;
import io.github.moulberry.notenoughupdates.util.Utils;
+import lombok.var;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.GuiButton;
@@ -314,7 +315,7 @@ public class GuiItemRecipe extends GuiScreen {
TAB_SIZE_X,
TAB_SIZE_Y
)) {
- changeRecipe(i, currentIndex);
+ changeRecipe(i, 0);
Utils.playPressSound();
return;
}
@@ -336,6 +337,16 @@ public class GuiItemRecipe extends GuiScreen {
currentRecipe.mouseClicked(this, mouseX, mouseY, mouseButton);
}
+ @Override
+ public void handleMouseInput() throws IOException {
+ super.handleMouseInput();
+ ScaledResolution scaledResolution = Utils.peekGuiScale();
+ int mouseX = Mouse.getX() * scaledResolution.getScaledWidth() / Minecraft.getMinecraft().displayWidth;
+ int mouseY = scaledResolution.getScaledHeight() -
+ Mouse.getY() * scaledResolution.getScaledHeight() / Minecraft.getMinecraft().displayHeight - 1;
+ getCurrentRecipe().genericMouseInput(mouseX, mouseY);
+ }
+
public void arrowKeyboardInput() {
ArrowPagesUtils.onPageSwitchKey(currentIndex, getCurrentRecipeList().size(), pageChange ->
changeRecipe(currentTab, pageChange));
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscgui/KatSitterOverlay.java b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/KatSitterOverlay.java
index 62aba35b..288c3926 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/miscgui/KatSitterOverlay.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/KatSitterOverlay.java
@@ -21,8 +21,9 @@ package io.github.moulberry.notenoughupdates.miscgui;
import com.google.gson.JsonObject;
import io.github.moulberry.notenoughupdates.NotEnoughUpdates;
+import io.github.moulberry.notenoughupdates.miscfeatures.PetInfoOverlay;
import io.github.moulberry.notenoughupdates.mixins.AccessorGuiContainer;
-import io.github.moulberry.notenoughupdates.profileviewer.GuiProfileViewer;
+import io.github.moulberry.notenoughupdates.util.PetLeveling;
import io.github.moulberry.notenoughupdates.util.Utils;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer;
@@ -63,14 +64,21 @@ public class KatSitterOverlay {
if (petInfo == null || !petInfo.has("exp") || !petInfo.has("tier") || !petInfo.has("type")) return;
String petId = petInfo.get("type").getAsString();
double xp = petInfo.get("exp").getAsDouble();
- String rarity = petInfo.get("tier").getAsString();
+ PetInfoOverlay.Rarity rarity = PetInfoOverlay.Rarity.valueOf(petInfo.get("tier").getAsString());
Slot katSlot = container.getSlot(22);
- String upgradedRarity = nextRarity(rarity);
+ PetInfoOverlay.Rarity upgradedRarity = rarity.nextRarity();
boolean nextRarityPresent = katSlot.getStack() != null && katSlot.getStack().getItem() != Item.getItemFromBlock(
Blocks.barrier) && upgradedRarity != null;
renderPetInformation(
- (int) GuiProfileViewer.getPetLevel(petId, rarity, (float) xp).level,
- nextRarityPresent ? (int) GuiProfileViewer.getPetLevel(petId, upgradedRarity, (float) xp).level : null,
+ PetLeveling
+ .getPetLevelingForPet(petId, rarity)
+ .getPetLevel(xp)
+ .getCurrentLevel(),
+ nextRarityPresent ?
+ PetLeveling
+ .getPetLevelingForPet(petId, rarity)
+ .getPetLevel(xp)
+ .getCurrentLevel() : null,
gui
);
}
@@ -100,20 +108,6 @@ public class KatSitterOverlay {
);
}
- public String nextRarity(String currentRarity) {
- switch (currentRarity.intern()) {
- case "COMMON":
- return "UNCOMMON";
- case "UNCOMMON":
- return "RARE";
- case "RARE":
- return "EPIC";
- case "EPIC":
- return "LEGENDARY";
- case "LEGENDARY":
- return "MYTHIC";
- }
- return null;
- }
+
}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/overlays/TimersOverlay.java b/src/main/java/io/github/moulberry/notenoughupdates/overlays/TimersOverlay.java
index b35f06dc..7edbee6b 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/overlays/TimersOverlay.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/overlays/TimersOverlay.java
@@ -358,7 +358,7 @@ public class TimersOverlay extends TextTabOverlay {
String[] cleanSplit = clean.split(" ");
hidden.cookieBuffRemaining = 0;
- for (int i = 0; i < cleanSplit.length; i++) {
+ for (int i = 0; i + 1 < cleanSplit.length; i++) {
if (i % 2 == 1) continue;
String number = cleanSplit[i];
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/GuiProfileViewer.java b/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/GuiProfileViewer.java
index cc13e820..63717093 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/GuiProfileViewer.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/GuiProfileViewer.java
@@ -231,68 +231,6 @@ public class GuiProfileViewer extends GuiScreen {
return xpTotal;
}
- public static PetLevel getPetLevel(
- String petType,
- String rarity,
- float exp
- ) {
- int offset = PetInfoOverlay.Rarity.valueOf(rarity).petOffset;
- int maxLevel = 100;
-
- JsonArray levels = new JsonArray();
- levels.addAll(Constants.PETS.get("pet_levels").getAsJsonArray());
- JsonElement customLevelingJson = Constants.PETS.get("custom_pet_leveling").getAsJsonObject().get(petType);
- if (customLevelingJson != null) {
- switch (Utils.getElementAsInt(Utils.getElement(customLevelingJson, "type"), 0)) {
- case 1:
- levels.addAll(customLevelingJson.getAsJsonObject().get("pet_levels").getAsJsonArray());
- break;
- case 2:
- levels = customLevelingJson.getAsJsonObject().get("pet_levels").getAsJsonArray();
- break;
- }
- maxLevel = Utils.getElementAsInt(Utils.getElement(customLevelingJson, "max_level"), 100);
- }
-
- float maxXP = getMaxLevelXp(levels, offset, maxLevel);
- boolean isMaxed = exp >= maxXP;
-
- int level = 1;
- float currentLevelRequirement = 0;
- float xpThisLevel = 0;
- float pct = 0;
-
- if (isMaxed) {
- level = maxLevel;
- currentLevelRequirement = levels.get(offset + level - 2).getAsFloat();
- xpThisLevel = currentLevelRequirement;
- pct = 1;
- } else {
- long totalExp = 0;
- for (int i = offset; i < levels.size(); i++) {
- currentLevelRequirement = levels.get(i).getAsLong();
- totalExp += currentLevelRequirement;
- if (totalExp >= exp) {
- xpThisLevel = currentLevelRequirement - (totalExp - exp);
- level = Math.min(i - offset + 1, maxLevel);
- break;
- }
- }
- pct = currentLevelRequirement != 0 ? xpThisLevel / currentLevelRequirement : 0;
- level += pct;
- }
-
- GuiProfileViewer.PetLevel levelObj = new GuiProfileViewer.PetLevel();
- levelObj.level = level;
- levelObj.maxLevel = maxLevel;
- levelObj.currentLevelRequirement = currentLevelRequirement;
- levelObj.maxXP = maxXP;
- levelObj.levelPercentage = pct;
- levelObj.levelXp = xpThisLevel;
- levelObj.totalXp = exp;
- return levelObj;
- }
-
@Deprecated
public static String shortNumberFormat(double n, int iteration) {
return StringUtils.shortNumberFormat(n, iteration
@@ -1352,15 +1290,4 @@ public class GuiProfileViewer extends GuiScreen {
return Optional.ofNullable(stack);
}
}
-
- public static class PetLevel {
-
- public float level;
- public float maxLevel;
- public float currentLevelRequirement;
- public float maxXP;
- public float levelPercentage;
- public float levelXp;
- public float totalXp;
- }
}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/PetsPage.java b/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/PetsPage.java
index cb85bf79..098c252e 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/PetsPage.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/PetsPage.java
@@ -26,6 +26,7 @@ import io.github.moulberry.notenoughupdates.core.util.StringUtils;
import io.github.moulberry.notenoughupdates.miscfeatures.PetInfoOverlay;
import io.github.moulberry.notenoughupdates.util.Constants;
import io.github.moulberry.notenoughupdates.util.ItemUtils;
+import io.github.moulberry.notenoughupdates.util.PetLeveling;
import io.github.moulberry.notenoughupdates.util.SBInfo;
import io.github.moulberry.notenoughupdates.util.Utils;
import net.minecraft.client.Minecraft;
@@ -118,19 +119,18 @@ public class PetsPage extends GuiProfileViewerPage {
PetInfoOverlay.Pet parsedPet = new PetInfoOverlay.Pet();
parsedPet.petType = pet.get("type").getAsString();
parsedPet.rarity = PetInfoOverlay.Rarity.valueOf(pet.get("tier").getAsString());
- parsedPet.petLevel = GuiProfileViewer.getPetLevel(
+ parsedPet.petLevel = PetLeveling.getPetLevelingForPet(
parsedPet.petType,
- parsedPet.rarity.name(),
- pet.get("exp").getAsFloat()
- );
+ parsedPet.rarity
+ ).getPetLevel(pet.get("exp").getAsFloat());
parsedPet.petXpType = "unknown";
parsedPet.petItem = Utils.getElementAsString(pet.get("heldItem"), null);
parsedPet.skin = Utils.getElementAsString(pet.get("skin"), null);
parsedPet.candyUsed = pet.get("candyUsed").getAsInt();
sortedPetsStack.add(ItemUtils.createPetItemstackFromPetInfo(parsedPet));
- pet.addProperty("level", parsedPet.petLevel.level);
- pet.addProperty("currentLevelRequirement", parsedPet.petLevel.currentLevelRequirement);
- pet.addProperty("maxXP", parsedPet.petLevel.maxXP);
+ pet.addProperty("level", parsedPet.petLevel.getCurrentLevel());
+ pet.addProperty("currentLevelRequirement", parsedPet.petLevel.getExpRequiredForNextLevel());
+ pet.addProperty("maxXP", parsedPet.petLevel.getExpRequiredForMaxLevel());
}
}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/PlayerStats.java b/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/PlayerStats.java
index c5a17aef..56ecd3a2 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/PlayerStats.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/PlayerStats.java
@@ -24,9 +24,11 @@ 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.miscfeatures.PetInfoOverlay;
import io.github.moulberry.notenoughupdates.profileviewer.info.QuiverInfo;
import io.github.moulberry.notenoughupdates.util.Constants;
import io.github.moulberry.notenoughupdates.util.JsonUtils;
+import io.github.moulberry.notenoughupdates.util.PetLeveling;
import io.github.moulberry.notenoughupdates.util.Utils;
import net.minecraft.nbt.CompressedStreamTools;
import net.minecraft.nbt.JsonToNBT;
@@ -491,11 +493,13 @@ public class PlayerStats {
tierNum = "" + (Integer.parseInt(tierNum) + 1);
}
- GuiProfileViewer.PetLevel levelObj = GuiProfileViewer.getPetLevel(petname, tier, exp);
- if (levelObj == null) return null;
- float level = levelObj.level;
- float currentLevelRequirement = levelObj.currentLevelRequirement;
- float maxXP = levelObj.maxXP;
+ PetLeveling.PetLevel levelObj = PetLeveling.getPetLevelingForPet(
+ petname,
+ PetInfoOverlay.Rarity.valueOf(tier)
+ ).getPetLevel(exp);
+ float level = levelObj.getCurrentLevel();
+ float currentLevelRequirement = levelObj.getExpRequiredForNextLevel();
+ float maxXP = levelObj.getMaxLevel();
pet.addProperty("level", level);
pet.addProperty("currentLevelRequirement", currentLevelRequirement);
pet.addProperty("maxXP", maxXP);
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/recipes/NeuRecipe.java b/src/main/java/io/github/moulberry/notenoughupdates/recipes/NeuRecipe.java
index a0090050..9f480085 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/recipes/NeuRecipe.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/recipes/NeuRecipe.java
@@ -95,4 +95,6 @@ public interface NeuRecipe {
}
default void actionPerformed(GuiButton button) {}
+
+ default void genericMouseInput(int mouseX, int mouseY) {}
}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/recipes/RecipeType.java b/src/main/java/io/github/moulberry/notenoughupdates/recipes/RecipeType.java
index 087ec6c3..3ef8399f 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/recipes/RecipeType.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/recipes/RecipeType.java
@@ -31,7 +31,8 @@ public enum RecipeType {
TRADE("trade", "Trading", VillagerTradeRecipe::parseStaticRecipe, new ItemStack(Items.emerald)),
MOB_LOOT("drops", "Mob Loot", MobLootRecipe::parseRecipe, new ItemStack(Items.diamond_sword)),
NPC_SHOP("npc_shop", "NPC Item Shop", ItemShopRecipe::parseItemRecipe, new ItemStack(Items.wheat_seeds)),
- ESSENCE_UPGRADES("", "Essence Upgrades", null, new ItemStack(Items.nether_star));
+ ESSENCE_UPGRADES("", "Essence Upgrades", null, new ItemStack(Items.nether_star)),
+ KAT_UPGRADE("katgrade", "Katsitting", KatRecipe::parseRecipe, new ItemStack(Blocks.red_flower));
private final String id;
private final String label;
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/util/ItemUtils.java b/src/main/java/io/github/moulberry/notenoughupdates/util/ItemUtils.java
index 3e2dd465..2ff9692d 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/util/ItemUtils.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/util/ItemUtils.java
@@ -189,6 +189,16 @@ public class ItemUtils {
}
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();
String heldItem = currentPet.petItem;
@@ -196,11 +206,13 @@ public class ItemUtils {
JsonObject heldItemJson = heldItem == null ? null : NotEnoughUpdates.INSTANCE.manager.getItemInformation().get(
heldItem);
String petId = currentPet.getPetId(false);
- float exp = currentPet.petLevel.totalXp;
+ float exp = currentPet.petLevel.getExpTotal();
- GuiProfileViewer.PetLevel levelObj = GuiProfileViewer.getPetLevel(petname, tier, exp);
+ PetLeveling.PetLevel levelObj = PetLeveling
+ .getPetLevelingForPet(petname, PetInfoOverlay.Rarity.valueOf(tier))
+ .getPetLevel(exp);
- float level = levelObj.level;
+ int level = levelObj.getCurrentLevel();
ItemStack petItemstack = NotEnoughUpdates.INSTANCE.manager
.createItemResolutionQuery()
@@ -348,7 +360,7 @@ public class ItemUtils {
if (Utils.cleanColour(lore.getStringTagAt(0)).matches(ItemTooltipListener.petToolTipRegex) &&
lore.tagCount() > 7) {
- GuiProfileViewer.PetLevel petLevel;
+ PetLeveling.PetLevel petLevel;
PetInfoOverlay.Pet pet = PetInfoOverlay.getPetFromStack(
stack.getTagCompound()
@@ -373,13 +385,13 @@ public class ItemUtils {
for (int i = 0; i < lore.tagCount(); i++) {
if (i == lore.tagCount() - 2) {
newLore.appendTag(new NBTTagString(""));
- if (petLevel.level >= maxLvl) {
+ if (petLevel.getCurrentLevel() >= maxLvl) {
newLore.appendTag(new NBTTagString(
EnumChatFormatting.AQUA + "" + EnumChatFormatting.BOLD + "MAX LEVEL"));
} else {
- double levelPercent = (Math.round(petLevel.levelPercentage * 1000) / 10.0);
+ double levelPercent = (Math.round(petLevel.getPercentageToNextLevel() * 1000) / 10.0);
newLore.appendTag(new NBTTagString(
- EnumChatFormatting.GRAY + "Progress to Level " + (int) (petLevel.level + 1) + ": " +
+ EnumChatFormatting.GRAY + "Progress to Level " + (petLevel.getCurrentLevel() + 1) + ": " +
EnumChatFormatting.YELLOW + levelPercent + "%"));
StringBuilder sb = new StringBuilder();
@@ -394,9 +406,9 @@ public class ItemUtils {
newLore.appendTag(new NBTTagString(sb.toString()));
newLore.appendTag(new NBTTagString(
EnumChatFormatting.GRAY + "EXP: " + EnumChatFormatting.YELLOW +
- decimalFormatter.format(petLevel.levelXp) +
+ decimalFormatter.format(petLevel.getExpInCurrentLevel()) +
EnumChatFormatting.GOLD + "/" + EnumChatFormatting.YELLOW +
- decimalFormatter.format(petLevel.currentLevelRequirement)
+ decimalFormatter.format(petLevel.getExpRequiredForNextLevel())
));
}
}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/util/Utils.java b/src/main/java/io/github/moulberry/notenoughupdates/util/Utils.java
index ecc03caf..a3f2296c 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/util/Utils.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/util/Utils.java
@@ -79,6 +79,7 @@ import java.nio.FloatBuffer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.text.DecimalFormat;
+import java.time.Duration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -1766,6 +1767,10 @@ public class Utils {
GlStateManager.enableTexture2D();
}
+ public static String prettyTime(Duration time) {
+ return prettyTime(time.toMillis());
+ }
+
public static String prettyTime(long millis) {
long seconds = millis / 1000 % 60;
long minutes = (millis / 1000 / 60) % 60;
diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/recipes/KatRecipe.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/recipes/KatRecipe.kt
new file mode 100644
index 00000000..c4b64969
--- /dev/null
+++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/recipes/KatRecipe.kt
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2022 Linnea Gräf
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+package io.github.moulberry.notenoughupdates.recipes
+
+import com.google.gson.JsonObject
+import com.google.gson.JsonPrimitive
+import io.github.moulberry.notenoughupdates.NEUManager
+import io.github.moulberry.notenoughupdates.core.util.GuiElementSlider
+import io.github.moulberry.notenoughupdates.miscfeatures.PetInfoOverlay
+import io.github.moulberry.notenoughupdates.miscfeatures.PetInfoOverlay.Pet
+import io.github.moulberry.notenoughupdates.miscgui.GuiItemRecipe
+import io.github.moulberry.notenoughupdates.util.ItemUtils
+import io.github.moulberry.notenoughupdates.util.PetLeveling
+import io.github.moulberry.notenoughupdates.util.Utils
+import io.github.moulberry.notenoughupdates.util.toJsonArray
+import net.minecraft.client.Minecraft
+import net.minecraft.client.renderer.GlStateManager
+import net.minecraft.util.ResourceLocation
+import java.time.Duration
+import kotlin.math.PI
+import kotlin.math.cos
+import kotlin.math.roundToInt
+import kotlin.math.sin
+
+data class KatRecipe(
+ val manager: NEUManager,
+ val inputPet: Ingredient,
+ val outputPet: Ingredient,
+ val items: List<Ingredient>,
+ val coins: Long,
+ val time: Duration,
+) : NeuRecipe {
+ var inputLevel = 1
+ val radius get() = 50 / 2
+ val circleCenter get() = 33 + 110 / 2 to 19 + 110 / 2
+ val textPosition get() = circleCenter.first to circleCenter.second + 90 / 2
+ val sliderPos get() = 40 to 15
+ val levelTextPos
+ get() = sliderPos.first - 4 - Minecraft.getMinecraft().fontRendererObj.getStringWidth("100") to
+ sliderPos.second + 16 / 2 - Minecraft.getMinecraft().fontRendererObj.FONT_HEIGHT / 2
+
+ val levelSlider = GuiElementSlider(0, 0, 100, 1F, 100F, 1F, inputLevel.toFloat()) { inputLevel = it.toInt() }
+ val coinsAdjustedForLevel: Int
+ get() = (coins.toInt() * (1 - 0.003F * (getOutputPetForCurrentLevel()?.petLevel?.currentLevel ?: 0))).toInt()
+ private val basicIngredients = items.toSet() + setOf(inputPet, Ingredient.coinIngredient(manager, coins.toInt()))
+ override fun getIngredients(): Set<Ingredient> = basicIngredients
+
+ override fun getOutputs(): Set<Ingredient> {
+ return setOf(outputPet)
+ }
+
+ fun getInputPetForCurrentLevel(): Pet? {
+ return PetInfoOverlay.getPetFromStack(inputPet.itemStack.tagCompound)?.also {
+ val petLeveling = PetLeveling.getPetLevelingForPet(it.petType, it.rarity)
+ it.petLevel = petLeveling.getPetLevel(petLeveling.getPetExpForLevel(inputLevel).toDouble())
+ }
+ }
+
+ fun getOutputPetForCurrentLevel(): Pet? {
+ return PetInfoOverlay.getPetFromStack(outputPet.itemStack.tagCompound)?.also {
+ val petLeveling = PetLeveling.getPetLevelingForPet(it.petType, it.rarity)
+ it.petLevel = petLeveling.getPetLevel(getInputPetForCurrentLevel()?.petLevel?.expTotal?.toDouble() ?: 0.0)
+ }
+ }
+
+ private fun positionOnCircle(i: Int, max: Int): Pair<Int, Int> {
+ val radians = PI * 2 * i / max
+ val offsetX = cos(radians) * radius
+ val offsetY = sin(radians) * radius
+ return (circleCenter.first + offsetX).roundToInt() to (circleCenter.second + offsetY).roundToInt()
+ }
+
+ override fun drawExtraInfo(gui: GuiItemRecipe, mouseX: Int, mouseY: Int) {
+ levelSlider.x = gui.guiLeft + sliderPos.first
+ levelSlider.y = gui.guiTop + sliderPos.second
+ levelSlider.render()
+ Minecraft.getMinecraft().fontRendererObj.drawString(
+ "$inputLevel",
+ gui.guiLeft + levelTextPos.first,
+ gui.guiTop + levelTextPos.second,
+ 0xFF0000
+ )
+ Utils.drawStringCentered(
+ Utils.prettyTime(time),
+ Minecraft.getMinecraft().fontRendererObj,
+ gui.guiLeft + textPosition.first.toFloat(), gui.guiTop + textPosition.second.toFloat(),
+ false, 0xff00ff
+ )
+ GlStateManager.color(1F, 1F, 1F, 1F)
+ }
+
+ override fun genericMouseInput(mouseX: Int, mouseY: Int) {
+ levelSlider.mouseInput(mouseX, mouseY)
+ }
+
+ override fun getSlots(): List<RecipeSlot> {
+ val advancedIngredients = items.map { it.itemStack } + listOf(
+ ItemUtils.createPetItemstackFromPetInfo(getInputPetForCurrentLevel()),
+ Ingredient.coinIngredient(
+ manager,
+ coinsAdjustedForLevel
+ ).itemStack
+ )
+ return advancedIngredients.mapIndexed { index, itemStack ->
+ val (x, y) = positionOnCircle(index, advancedIngredients.size)
+ RecipeSlot(x - 18 / 2, y - 18 / 2, itemStack)
+ } + listOf(
+ RecipeSlot(
+ circleCenter.first - 9,
+ circleCenter.second - 9,
+ ItemUtils.createPetItemstackFromPetInfo(getOutputPetForCurrentLevel())
+ )
+ )
+ }
+
+ override fun getType(): RecipeType = RecipeType.KAT_UPGRADE
+
+ override fun hasVariableCost(): Boolean = false
+
+ override fun serialize(): JsonObject {
+ return JsonObject().apply {
+ addProperty("type", type.id)
+ addProperty("coins", coins)
+ addProperty("input", inputPet.serialize())
+ addProperty("output", outputPet.serialize())
+ addProperty("time", time.seconds)
+ add("items", items.map { JsonPrimitive(it.serialize()) }.toJsonArray())
+ }
+ }
+
+ companion object {
+ @JvmStatic
+ fun parseRecipe(manager: NEUManager, recipe: JsonObject, output: JsonObject): NeuRecipe {
+ return KatRecipe(
+ manager,
+ Ingredient(manager, recipe["input"].asString),
+ Ingredient(manager, recipe["output"].asString),
+ recipe["items"]?.asJsonArray?.map { Ingredient(manager, it.asString) } ?: emptyList(),
+ recipe["coins"].asLong,
+ Duration.ofSeconds(recipe["time"].asLong)
+ )
+ }
+ }
+
+ override fun getBackground(): ResourceLocation {
+ return ResourceLocation("notenoughupdates:textures/gui/katting_tall.png")
+ }
+}
diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/util/KotlinJsonUtils.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/util/KotlinJsonUtils.kt
new file mode 100644
index 00000000..95b6f1ac
--- /dev/null
+++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/util/KotlinJsonUtils.kt
@@ -0,0 +1,31 @@
+/*
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+
+package io.github.moulberry.notenoughupdates.util
+
+import com.google.gson.JsonArray
+import com.google.gson.JsonElement
+
+
+fun Iterable<JsonElement>.toJsonArray(): JsonArray = JsonArray().also {
+ for (jsonElement in this) {
+ it.add(jsonElement)
+ }
+}
+
diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/util/PetLeveling.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/util/PetLeveling.kt
new file mode 100644
index 00000000..e7d29642
--- /dev/null
+++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/util/PetLeveling.kt
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2022 Linnea Gräf
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+
+package io.github.moulberry.notenoughupdates.util
+
+import com.google.gson.JsonObject
+import io.github.moulberry.notenoughupdates.events.RepositoryReloadEvent
+import io.github.moulberry.notenoughupdates.miscfeatures.PetInfoOverlay.Rarity
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+
+object PetLeveling {
+
+ data class ExpLadder(
+ val individualLevelCost: List<Long>,
+ ) {
+ val cumulativeLevelCost = individualLevelCost.runningFold(0F) { a, b -> a + b }.map { it.toLong() }
+ fun getPetLevel(currentExp: Double): PetLevel {
+ val currentOneIndexedLevel = cumulativeLevelCost.indexOfLast { it <= currentExp } + 1
+ val expForNextLevel = if (currentOneIndexedLevel > individualLevelCost.size) { // Max leveled pet
+ individualLevelCost.last()
+ } else {
+ individualLevelCost[currentOneIndexedLevel - 1]
+ }
+ val expInCurrentLevel =
+ if (currentOneIndexedLevel >= cumulativeLevelCost.size)
+ currentExp.toFloat() - cumulativeLevelCost.last()
+ else
+ (expForNextLevel - (cumulativeLevelCost[currentOneIndexedLevel] - currentExp.toFloat())).coerceAtLeast(0F)
+ return PetLevel(
+ currentLevel = currentOneIndexedLevel,
+ maxLevel = cumulativeLevelCost.size,
+ expRequiredForNextLevel = expForNextLevel,
+ expRequiredForMaxLevel = cumulativeLevelCost.last(),
+ expInCurrentLevel = expInCurrentLevel,
+ expTotal = currentExp.toFloat()
+ )
+ }
+
+ fun getPetExpForLevel(level: Int): Long {
+ if (level < 2) return 0L
+ if (level >= cumulativeLevelCost.size) return cumulativeLevelCost.last()
+ return cumulativeLevelCost[level - 1]
+ }
+ }
+
+ data class PetLevel(
+ val currentLevel: Int,
+ val maxLevel: Int,
+ val expRequiredForNextLevel: Long,
+ val expRequiredForMaxLevel: Long,
+ val expInCurrentLevel: Float,
+ var expTotal: Float,
+ ) {
+ val percentageToNextLevel: Float = expInCurrentLevel / expRequiredForNextLevel
+ }
+
+ private data class Key(val petIdWithoutRarity: String, val rarity: Rarity)
+
+ private val cache = mutableMapOf<Key, ExpLadder>()
+
+ @SubscribeEvent
+ fun onRepoReload(event: RepositoryReloadEvent) {
+ cache.clear()
+ }
+
+ var petConstants: JsonObject? = null
+
+ @JvmStatic
+ fun getPetLevelingForPet(petIdWithoutRarity: String, rarity: Rarity): ExpLadder {
+ return cache.computeIfAbsent(Key(petIdWithoutRarity, rarity)) {
+ getPetLevelingForPet0(
+ petIdWithoutRarity,
+ rarity
+ )
+ }
+ }
+
+ internal fun getPetLevelingForPet0(petIdWithoutRarity: String, rarity: Rarity): ExpLadder {
+ val petConstants = this.petConstants ?: Constants.PETS
+ var levels = petConstants["pet_levels"].asJsonArray.map { it.asLong }.toMutableList()
+ val customLeveling = petConstants["custom_pet_leveling"].asJsonObject[petIdWithoutRarity]
+ val offset = petConstants["pet_rarity_offset"].asJsonObject[rarity.name].asInt
+ var maxLevel = 100
+ if (customLeveling is JsonObject) {
+ val customLevels by lazy { customLeveling["pet_levels"].asJsonArray.map { it.asLong } }
+ when (customLeveling["type"]?.asInt ?: 0) {
+ 1 -> levels.addAll(customLevels)
+ 2 -> levels = customLevels.toMutableList()
+ }
+ maxLevel = customLeveling["max_level"]?.asInt ?: maxLevel
+ }
+ return ExpLadder(levels.drop(offset).take(maxLevel))
+ }
+
+}
diff --git a/src/main/resources/assets/notenoughupdates/textures/gui/katting_tall.png b/src/main/resources/assets/notenoughupdates/textures/gui/katting_tall.png
new file mode 100644
index 00000000..f1dfe7e8
--- /dev/null
+++ b/src/main/resources/assets/notenoughupdates/textures/gui/katting_tall.png
Binary files differ
diff --git a/src/test/kotlin/io/github/moulberry/notenoughupdates/util/PetLevelingTest.kt b/src/test/kotlin/io/github/moulberry/notenoughupdates/util/PetLevelingTest.kt
new file mode 100644
index 00000000..43a0e3fd
--- /dev/null
+++ b/src/test/kotlin/io/github/moulberry/notenoughupdates/util/PetLevelingTest.kt
@@ -0,0 +1,403 @@
+/*
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+
+package io.github.moulberry.notenoughupdates.util
+
+import com.google.gson.Gson
+import com.google.gson.JsonObject
+import io.github.moulberry.notenoughupdates.miscfeatures.PetInfoOverlay
+import org.junit.jupiter.api.Assertions
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.Test
+
+internal class PetLevelingTest {
+
+ val testJsonString = """
+ {
+ "pet_rarity_offset": {
+ "COMMON": 0,
+ "UNCOMMON": 6,
+ "RARE": 11,
+ "EPIC": 16,
+ "LEGENDARY": 20,
+ "MYTHIC": 20
+ },
+ "pet_levels": [
+ 100,
+ 110,
+ 120,
+ 130,
+ 145,
+ 160,
+ 175,
+ 190,
+ 210,
+ 230,
+ 250,
+ 275,
+ 300,
+ 330,
+ 360,
+ 400,
+ 440,
+ 490,
+ 540,
+ 600,
+ 660,
+ 730,
+ 800,
+ 880,
+ 960,
+ 1050,
+ 1150,
+ 1260,
+ 1380,
+ 1510,
+ 1650,
+ 1800,
+ 1960,
+ 2130,
+ 2310,
+ 2500,
+ 2700,
+ 2920,
+ 3160,
+ 3420,
+ 3700,
+ 4000,
+ 4350,
+ 4750,
+ 5200,
+ 5700,
+ 6300,
+ 7000,
+ 7800,
+ 8700,
+ 9700,
+ 10800,
+ 12000,
+ 13300,
+ 14700,
+ 16200,
+ 17800,
+ 19500,
+ 21300,
+ 23200,
+ 25200,
+ 27400,
+ 29800,
+ 32400,
+ 35200,
+ 38200,
+ 41400,
+ 44800,
+ 48400,
+ 52200,
+ 56200,
+ 60400,
+ 64800,
+ 69400,
+ 74200,
+ 79200,
+ 84700,
+ 90700,
+ 97200,
+ 104200,
+ 111700,
+ 119700,
+ 128200,
+ 137200,
+ 146700,
+ 156700,
+ 167700,
+ 179700,
+ 192700,
+ 206700,
+ 221700,
+ 237700,
+ 254700,
+ 272700,
+ 291700,
+ 311700,
+ 333700,
+ 357700,
+ 383700,
+ 411700,
+ 441700,
+ 476700,
+ 516700,
+ 561700,
+ 611700,
+ 666700,
+ 726700,
+ 791700,
+ 861700,
+ 936700,
+ 1016700,
+ 1101700,
+ 1191700,
+ 1286700,
+ 1386700,
+ 1496700,
+ 1616700,
+ 1746700,
+ 1886700
+ ],
+ "custom_pet_leveling": {
+ "GOLDEN_DRAGON": {
+ "type": 1,
+ "pet_levels": [
+ 0,
+ 5555,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700,
+ 1886700
+ ],
+ "max_level": 200
+ }
+ },
+ "pet_types": {
+ "ROCK": "MINING",
+ "BAT": "MINING",
+ "MITHRIL_GOLEM": "MINING",
+ "WITHER_SKELETON": "MINING",
+ "SILVERFISH": "MINING",
+ "ENDERMITE": "MINING",
+ "BEE": "FARMING",
+ "CHICKEN": "FARMING",
+ "PIG": "FARMING",
+ "RABBIT": "FARMING",
+ "ELEPHANT": "FARMING",
+ "BLUE_WHALE": "FISHING",
+ "DOLPHIN": "FISHING",
+ "FLYING_FISH": "FISHING",
+ "BABY_YETI": "FISHING",
+ "MEGALODON": "FISHING",
+ "SQUID": "FISHING",
+ "JELLYFISH": "ALCHEMY",
+ "SHEEP": "ALCHEMY",
+ "PARROT": "ALCHEMY",
+ "MONKEY": "FORAGING",
+ "GIRAFFE": "FORAGING",
+ "LION": "FORAGING",
+ "OCELOT": "FORAGING",
+ "BLACK_CAT": "COMBAT",
+ "BLAZE": "COMBAT",
+ "ENDER_DRAGON": "COMBAT",
+ "ENDERMAN": "COMBAT",
+ "GHOUL": "COMBAT",
+ "GOLEM": "COMBAT",
+ "GRIFFIN": "COMBAT",
+ "HORSE": "COMBAT",
+ "HOUND": "COMBAT",
+ "JERRY": "COMBAT",
+ "MAGMA_CUBE": "COMBAT",
+ "PHOENIX": "COMBAT",
+ "PIGMAN": "COMBAT",
+ "SKELETON": "COMBAT",
+ "SKELETON_HORSE": "COMBAT",
+ "SNOWMAN": "COMBAT",
+ "SPIDER": "COMBAT",
+ "SPIRIT": "COMBAT",
+ "TARANTULA": "COMBAT",
+ "TURTLE": "COMBAT",
+ "TIGER": "COMBAT",
+ "ZOMBIE": "COMBAT",
+ "WOLF": "COMBAT",
+ "GUARDIAN": "ENCHANTING",
+ "GRANDMA_WOLF": "COMBAT",
+ "ARMADILLO": "MINING",
+ "BAL": "COMBAT",
+ "GOLDEN_DRAGON": "COMBAT",
+ "RAT": "COMBAT",
+ "SCATHA": "MINING",
+ "AMMONITE": "FISHING",
+ "SNAIL": "MINING",
+ "MOOSHROOM_COW": "FARMING",
+ "KUUDRA": "COMBAT"
+ }
+ }
+ """
+
+ val testJson = Gson().fromJson(testJsonString, JsonObject::class.java)
+
+ @BeforeEach
+ fun setup() {
+ PetLeveling.petConstants = testJson
+ }
+
+ @Test
+ fun testPetLevelGrandmaWolf() {
+ val leveling = PetLeveling.getPetLevelingForPet0("GRANDMA_WOLF", PetInfoOverlay.Rarity.LEGENDARY)
+ val level = leveling.getPetLevel(22_500_000.0)
+ Assertions.assertEquals(98, level.currentLevel)
+ Assertions.assertEquals(1746700, level.expRequiredForNextLevel)
+ Assertions.assertEquals(780170.0F, level.expInCurrentLevel, 0.5F)
+ }
+
+ @Test
+ fun testPetLevelCommon() {
+ val leveling = PetLeveling.getPetLevelingForPet0("BEE", PetInfoOverlay.Rarity.COMMON)
+ val level = leveling.getPetLevel(197806.0385900295)
+ Assertions.assertEquals(58, level.currentLevel)
+ Assertions.assertEquals(19500, level.expRequiredForNextLevel)
+ Assertions.assertEquals(5321.0F, level.expInCurrentLevel, 0.5F)
+ }
+
+ @Test
+ fun testGoldenDragon() {
+ val leveling = PetLeveling.getPetLevelingForPet0("GOLDEN_DRAGON", PetInfoOverlay.Rarity.LEGENDARY)
+ val level = leveling.getPetLevel(95179565.04472463)
+ Assertions.assertEquals(139, level.currentLevel)
+ Assertions.assertEquals(1886700, level.expRequiredForNextLevel)
+ Assertions.assertEquals(12828.0F, level.expInCurrentLevel, 0.5F)
+ }
+
+ @Test
+ fun testEpicRabbit() {
+ val leveling = PetLeveling.getPetLevelingForPet0("RABBIT", PetInfoOverlay.Rarity.EPIC)
+ val level = leveling.getPetLevel(2864013.5452096704)
+ Assertions.assertEquals(74, level.currentLevel)
+ Assertions.assertEquals(206700, level.expRequiredForNextLevel)
+ Assertions.assertEquals(114713.0F, level.expInCurrentLevel, 0.5F)
+ }
+
+ @Test
+ fun testPetLevelEndermite() {
+ val leveling = PetLeveling.getPetLevelingForPet0("ENDERMITE", PetInfoOverlay.Rarity.LEGENDARY)
+ val level = leveling.getPetLevel(1_206_848.0)
+ Assertions.assertEquals(59, level.currentLevel)
+ Assertions.assertEquals(97200, level.expRequiredForNextLevel)
+ Assertions.assertEquals(1318.0F, level.expInCurrentLevel, 0.5F)
+ }
+ @Test
+ fun testLevel109GoldenDragon() {
+ val leveling = PetLeveling.getPetLevelingForPet0("GOLDEN_DRAGON", PetInfoOverlay.Rarity.LEGENDARY)
+ val level = leveling.getPetLevel(3.947913361545298E7)
+ Assertions.assertEquals(109, level.currentLevel)
+ Assertions.assertEquals(913448.0F, level.expInCurrentLevel, 0.5F)
+ }
+
+ @Test
+ fun testExpRequiredForLevel140() {
+ val leveling = PetLeveling.getPetLevelingForPet0("GOLDEN_DRAGON", PetInfoOverlay.Rarity.LEGENDARY)
+ val petExpForLevel = leveling.getPetExpForLevel(140)
+ val level = leveling.getPetLevel(petExpForLevel.toDouble())
+ Assertions.assertEquals(97053440, petExpForLevel)
+ Assertions.assertEquals(140, level.currentLevel)
+ Assertions.assertEquals(0.0F, level.expInCurrentLevel, 1.0F)
+ Assertions.assertEquals(0.0F, level.percentageToNextLevel, 1.0F)
+ }
+
+}