diff options
Diffstat (limited to 'src/main')
23 files changed, 866 insertions, 38 deletions
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/NEUManager.java b/src/main/java/io/github/moulberry/notenoughupdates/NEUManager.java index af190244..71aa10b6 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/NEUManager.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/NEUManager.java @@ -34,6 +34,7 @@ import io.github.moulberry.notenoughupdates.recipes.CraftingOverlay; import io.github.moulberry.notenoughupdates.recipes.CraftingRecipe; import io.github.moulberry.notenoughupdates.recipes.Ingredient; import io.github.moulberry.notenoughupdates.recipes.NeuRecipe; +import io.github.moulberry.notenoughupdates.recipes.RecipeHistory; import io.github.moulberry.notenoughupdates.util.ApiUtil; import io.github.moulberry.notenoughupdates.util.Constants; import io.github.moulberry.notenoughupdates.util.HotmInformation; @@ -113,12 +114,16 @@ public class NEUManager { new KeyBinding("Show usages for item", Keyboard.KEY_U, "NotEnoughUpdates"); public final KeyBinding keybindViewRecipe = new KeyBinding("Show recipe for item", Keyboard.KEY_R, "NotEnoughUpdates"); + public final KeyBinding keybindPreviousRecipe = + new KeyBinding("Show previous recipe", Keyboard.KEY_LBRACKET, "NotEnoughUpdates"); + public final KeyBinding keybindNextRecipe = + new KeyBinding("Show next recipe", Keyboard.KEY_RBRACKET, "NotEnoughUpdates"); public final KeyBinding keybindToggleDisplay = new KeyBinding("Toggle NEU overlay", 0, "NotEnoughUpdates"); public final KeyBinding keybindClosePanes = new KeyBinding("Close NEU panes", 0, "NotEnoughUpdates"); public final KeyBinding keybindItemSelect = new KeyBinding("Select Item", -98 /*middle*/, "NotEnoughUpdates"); public final KeyBinding[] keybinds = new KeyBinding[]{ - keybindGive, keybindFavourite, keybindViewUsages, keybindViewRecipe, - keybindToggleDisplay, keybindClosePanes, keybindItemSelect + keybindGive, keybindFavourite, keybindViewUsages, keybindViewRecipe, keybindPreviousRecipe, + keybindNextRecipe, keybindToggleDisplay, keybindClosePanes, keybindItemSelect }; public String viewItemAttemptID = null; @@ -884,7 +889,6 @@ public class NEUManager { case "viewpotion": neu.sendChatMessage("/viewpotion " + internalName.split(";")[0].toLowerCase(Locale.ROOT)); } - displayGuiItemRecipe(internalName); } public void showRecipe(String internalName) { @@ -988,6 +992,7 @@ public class NEUManager { List<NeuRecipe> usages = getAvailableUsagesFor(internalName); if (usages.isEmpty()) return false; NotEnoughUpdates.INSTANCE.openGui = (new GuiItemRecipe(usages, this)); + RecipeHistory.add(NotEnoughUpdates.INSTANCE.openGui); return true; } @@ -996,6 +1001,7 @@ public class NEUManager { List<NeuRecipe> recipes = getAvailableRecipesFor(internalName); if (recipes.isEmpty()) return false; NotEnoughUpdates.INSTANCE.openGui = (new GuiItemRecipe(recipes, this)); + RecipeHistory.add(NotEnoughUpdates.INSTANCE.openGui); return true; } diff --git a/src/main/java/io/github/moulberry/notenoughupdates/NEUOverlay.java b/src/main/java/io/github/moulberry/notenoughupdates/NEUOverlay.java index 2f55c338..87ce915b 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/NEUOverlay.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/NEUOverlay.java @@ -1219,8 +1219,8 @@ public class NEUOverlay extends Gui { String internal1 = o1.get("internalname").getAsString(); String internal2 = o2.get("internalname").getAsString(); - double cost1 = manager.auctionManager.getBazaarOrBin(internal1); - double cost2 = manager.auctionManager.getBazaarOrBin(internal2); + double cost1 = manager.auctionManager.getBazaarOrBin(internal1, false); + double cost2 = manager.auctionManager.getBazaarOrBin(internal2, false); if (cost1 < cost2) return mult; if (cost1 > cost2) return -mult; diff --git a/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java b/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java index 143033b7..b514e419 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java @@ -45,6 +45,7 @@ import io.github.moulberry.notenoughupdates.miscfeatures.CrystalOverlay; import io.github.moulberry.notenoughupdates.miscfeatures.CrystalWishingCompassSolver; import io.github.moulberry.notenoughupdates.miscfeatures.CustomItemEffects; import io.github.moulberry.notenoughupdates.miscfeatures.CustomSkulls; +import io.github.moulberry.notenoughupdates.miscfeatures.DungeonNpcProfitOverlay; import io.github.moulberry.notenoughupdates.miscfeatures.DwarvenMinesWaypoints; import io.github.moulberry.notenoughupdates.miscfeatures.EnchantingSolvers; import io.github.moulberry.notenoughupdates.miscfeatures.FairySouls; @@ -78,6 +79,7 @@ import io.github.moulberry.notenoughupdates.profileviewer.ProfileViewer; import io.github.moulberry.notenoughupdates.recipes.RecipeGenerator; import io.github.moulberry.notenoughupdates.util.Constants; import io.github.moulberry.notenoughupdates.util.SBInfo; +import io.github.moulberry.notenoughupdates.util.TitleUtil; import io.github.moulberry.notenoughupdates.util.Utils; import io.github.moulberry.notenoughupdates.util.XPInformation; import net.minecraft.client.Minecraft; @@ -295,6 +297,7 @@ public class NotEnoughUpdates { MinecraftForge.EVENT_BUS.register(new DwarvenMinesWaypoints()); MinecraftForge.EVENT_BUS.register(new FuelBar()); MinecraftForge.EVENT_BUS.register(new AuctionProfit()); + MinecraftForge.EVENT_BUS.register(new DungeonNpcProfitOverlay()); MinecraftForge.EVENT_BUS.register(XPInformation.getInstance()); MinecraftForge.EVENT_BUS.register(OverlayManager.petInfoOverlay); MinecraftForge.EVENT_BUS.register(OverlayManager.timersOverlay); @@ -322,6 +325,7 @@ public class NotEnoughUpdates { MinecraftForge.EVENT_BUS.register(MinionHelperManager.getInstance()); MinecraftForge.EVENT_BUS.register(navigation); MinecraftForge.EVENT_BUS.register(new WorldListener(this)); + MinecraftForge.EVENT_BUS.register(TitleUtil.getInstance()); if (Minecraft.getMinecraft().getResourceManager() instanceof IReloadableResourceManager) { IReloadableResourceManager manager = (IReloadableResourceManager) Minecraft.getMinecraft().getResourceManager(); diff --git a/src/main/java/io/github/moulberry/notenoughupdates/auction/APIManager.java b/src/main/java/io/github/moulberry/notenoughupdates/auction/APIManager.java index 1b6896db..755e53f5 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/auction/APIManager.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/auction/APIManager.java @@ -822,10 +822,11 @@ public class APIManager { return keys; } - public double getBazaarOrBin(String internalName) { + public double getBazaarOrBin(String internalName, boolean useSellingPrice) { + String curr = (useSellingPrice ? "curr_sell" : "curr_buy"); JsonObject bazaarInfo = manager.auctionManager.getBazaarInfo(internalName); - if (bazaarInfo != null && bazaarInfo.get("curr_buy") != null) { - return bazaarInfo.get("curr_buy").getAsFloat(); + if (bazaarInfo != null && bazaarInfo.get(curr) != null) { + return bazaarInfo.get(curr).getAsFloat(); } else { return manager.auctionManager.getLowestBin(internalName); } diff --git a/src/main/java/io/github/moulberry/notenoughupdates/core/util/render/RenderUtils.java b/src/main/java/io/github/moulberry/notenoughupdates/core/util/render/RenderUtils.java index e7ce29c3..d7bd097a 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/core/util/render/RenderUtils.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/core/util/render/RenderUtils.java @@ -378,14 +378,18 @@ public class RenderUtils { } public static void renderWayPoint(List<String> str, Vec3i loc, float partialTicks) { - renderWayPoint(str, new Vector3f(loc.getX(), loc.getY(), loc.getZ()), partialTicks); + renderWayPoint(str, new Vector3f(loc.getX(), loc.getY(), loc.getZ()), partialTicks, false); } public static void renderWayPoint(String str, Vector3f loc, float partialTicks) { - renderWayPoint(Arrays.asList(str), loc, partialTicks); + renderWayPoint(Arrays.asList(str), loc, partialTicks, false); + } + + public static void renderWayPoint(Vec3i loc, float partialTicks) { + renderWayPoint(Arrays.asList(""), new Vector3f(loc.getX(), loc.getY(), loc.getZ()), partialTicks, true); } - public static void renderWayPoint(List<String> lines, Vector3f loc, float partialTicks) { + public static void renderWayPoint(List<String> lines, Vector3f loc, float partialTicks, boolean onlyShowDistance) { GlStateManager.alphaFunc(516, 0.1F); GlStateManager.pushMatrix(); @@ -409,7 +413,7 @@ public class RenderUtils { GlStateManager.translate(x, y, z); GlStateManager.translate(0, viewer.getEyeHeight(), 0); - lines = new ArrayList<>(lines); + lines = onlyShowDistance ? new ArrayList<>() : new ArrayList<>(lines); lines.add(EnumChatFormatting.YELLOW.toString() + Math.round(dist) + "m"); renderNametag(lines); diff --git a/src/main/java/io/github/moulberry/notenoughupdates/listener/ChatListener.java b/src/main/java/io/github/moulberry/notenoughupdates/listener/ChatListener.java index a5acd5b4..6a94c0b3 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/listener/ChatListener.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/listener/ChatListener.java @@ -23,6 +23,7 @@ import io.github.moulberry.notenoughupdates.NotEnoughUpdates; import io.github.moulberry.notenoughupdates.dungeons.DungeonWin; import io.github.moulberry.notenoughupdates.miscfeatures.CookieWarning; import io.github.moulberry.notenoughupdates.miscfeatures.CrystalMetalDetectorSolver; +import io.github.moulberry.notenoughupdates.miscfeatures.EnderNodes; import io.github.moulberry.notenoughupdates.miscfeatures.StreamerMode; import io.github.moulberry.notenoughupdates.overlays.OverlayManager; import io.github.moulberry.notenoughupdates.overlays.SlayerOverlay; @@ -306,5 +307,9 @@ public class ChatListener { unformatted.equals("You have successfully picked the lock on this chest!") || (unformatted.startsWith("You received +") && unformatted.endsWith(" Powder"))) OverlayManager.powderGrindingOverlay.message(unformatted); + + if (unformatted.equals("ENDER NODE! You found Endermite Nest!")) { + EnderNodes.dispalyEndermiteNotif(); + } } } diff --git a/src/main/java/io/github/moulberry/notenoughupdates/listener/ItemTooltipRngListener.java b/src/main/java/io/github/moulberry/notenoughupdates/listener/ItemTooltipRngListener.java index fdae53ea..2b7a9bef 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/listener/ItemTooltipRngListener.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/listener/ItemTooltipRngListener.java @@ -139,7 +139,7 @@ public class ItemTooltipRngListener { private String getFormatCoinsPer(ItemStack stack, int needed, int multiplier, String label) { String internalName = neu.manager.createItemResolutionQuery().withItemStack(stack).resolveInternalName(); - double profit = neu.manager.auctionManager.getBazaarOrBin(internalName); + double profit = neu.manager.auctionManager.getBazaarOrBin(internalName, false); if (profit <= 0) return null; //ask hypixel nicely to release a 'chest price api' with 4 dimensions for us. the 4 dimensions needed are: item name, floor, normal/mm, s/s+ diff --git a/src/main/java/io/github/moulberry/notenoughupdates/listener/NEUEventListener.java b/src/main/java/io/github/moulberry/notenoughupdates/listener/NEUEventListener.java index 43a1daf9..5b388dea 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/listener/NEUEventListener.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/listener/NEUEventListener.java @@ -34,11 +34,13 @@ import io.github.moulberry.notenoughupdates.miscfeatures.ItemCustomizeManager; import io.github.moulberry.notenoughupdates.miscfeatures.NPCRetexturing; import io.github.moulberry.notenoughupdates.miscgui.AccessoryBagOverlay; import io.github.moulberry.notenoughupdates.miscgui.GuiCustomEnchant; +import io.github.moulberry.notenoughupdates.miscgui.GuiItemRecipe; import io.github.moulberry.notenoughupdates.miscgui.hex.GuiCustomHex; import io.github.moulberry.notenoughupdates.miscgui.StorageOverlay; import io.github.moulberry.notenoughupdates.overlays.OverlayManager; import io.github.moulberry.notenoughupdates.overlays.TextOverlay; import io.github.moulberry.notenoughupdates.overlays.TextTabOverlay; +import io.github.moulberry.notenoughupdates.recipes.RecipeHistory; import io.github.moulberry.notenoughupdates.util.Constants; import io.github.moulberry.notenoughupdates.util.NotificationHandler; import io.github.moulberry.notenoughupdates.util.ProfileApiSyncer; @@ -162,6 +164,11 @@ public class NEUEventListener { if (longUpdate) { + + if (!(Minecraft.getMinecraft().currentScreen instanceof GuiItemRecipe)) { + RecipeHistory.clear(); + } + CrystalOverlay.tick(); FairySouls.getInstance().tick(); XPInformation.getInstance().tick(); diff --git a/src/main/java/io/github/moulberry/notenoughupdates/listener/RenderListener.java b/src/main/java/io/github/moulberry/notenoughupdates/listener/RenderListener.java index 2eb4c23a..949358ad 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/listener/RenderListener.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/listener/RenderListener.java @@ -40,6 +40,7 @@ import io.github.moulberry.notenoughupdates.miscfeatures.AuctionBINWarning; import io.github.moulberry.notenoughupdates.miscfeatures.AuctionProfit; import io.github.moulberry.notenoughupdates.miscfeatures.BetterContainers; import io.github.moulberry.notenoughupdates.miscfeatures.CrystalMetalDetectorSolver; +import io.github.moulberry.notenoughupdates.miscfeatures.DungeonNpcProfitOverlay; import io.github.moulberry.notenoughupdates.miscfeatures.EnchantingSolvers; import io.github.moulberry.notenoughupdates.miscfeatures.StorageManager; import io.github.moulberry.notenoughupdates.miscgui.AccessoryBagOverlay; @@ -136,6 +137,8 @@ public class RenderListener { public static long lastGuiClosed = 0; public static boolean inventoryLoaded = false; private final NotEnoughUpdates neu; + private final NumberFormat format = new DecimalFormat("#,##0.#", new DecimalFormatSymbols(Locale.US)); + private final Pattern ESSENCE_PATTERN = Pattern.compile("§d(.+) Essence §8x([\\d,]+)"); ScheduledExecutorService ses = Executors.newScheduledThreadPool(1); JsonObject essenceJson = new JsonObject(); private boolean hoverInv = false; @@ -150,9 +153,6 @@ public class RenderListener { private boolean typing; private HashMap<String, String> cachedDefinitions; private boolean inDungeonPage = false; - private final NumberFormat format = new DecimalFormat("#,##0.#", new DecimalFormatSymbols(Locale.US)); - - private final Pattern ESSENCE_PATTERN = Pattern.compile("§d(.+) Essence §8x([\\d,]+)"); public RenderListener(NotEnoughUpdates neu) { this.neu = neu; @@ -458,7 +458,6 @@ public class RenderListener { return; } - boolean tradeWindowActive = TradeWindow.tradeWindowActive(containerName); boolean storageOverlayActive = StorageManager.getInstance().shouldRenderStorageOverlay(containerName); boolean customAhActive = @@ -559,7 +558,7 @@ public class RenderListener { x -= 25; } } - if (inDungeonPage) { + if (inDungeonPage || DungeonNpcProfitOverlay.isRendering()) { if (x + 10 > guiLeft + xSize && x + 18 < guiLeft + xSize + 4 + 28 + 20 && y > guiTop - 180 && y < guiTop + 100) { x += 185; @@ -694,7 +693,7 @@ public class RenderListener { } } - if (inDungeonPage) { + if (inDungeonPage || DungeonNpcProfitOverlay.isRendering()) { if (x + 10 > guiLeft + xSize && x + 18 < guiLeft + xSize + 4 + 28 + 20 && y > guiTop - 180 && y < guiTop + 100) { x += 185; @@ -812,7 +811,7 @@ public class RenderListener { if (bazaarPrice < 5000000 && internal.equals("RECOMBOBULATOR_3000")) bazaarPrice = 5000000; double worth = -1; - boolean isOnBz = false; + boolean isOnBz = false; if (bazaarPrice >= 0) { worth = bazaarPrice; isOnBz = true; @@ -1086,7 +1085,6 @@ public class RenderListener { return; } - boolean tradeWindowActive = TradeWindow.tradeWindowActive(containerName); boolean storageOverlayActive = StorageManager.getInstance().shouldRenderStorageOverlay(containerName); boolean customAhActive = @@ -1176,7 +1174,7 @@ public class RenderListener { x -= 25; } } - if (inDungeonPage) { + if (inDungeonPage || DungeonNpcProfitOverlay.isRendering()) { if (x + 10 > guiLeft + xSize && x + 18 < guiLeft + xSize + 4 + 28 + 20 && y > guiTop - 180 && y < guiTop + 100) { x += 185; @@ -1564,7 +1562,6 @@ public class RenderListener { return; } - boolean tradeWindowActive = TradeWindow.tradeWindowActive(containerName); boolean storageOverlayActive = StorageManager.getInstance().shouldRenderStorageOverlay(containerName); boolean customAhActive = diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/DungeonNpcProfitOverlay.java b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/DungeonNpcProfitOverlay.java new file mode 100644 index 00000000..7caa4d6b --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/DungeonNpcProfitOverlay.java @@ -0,0 +1,391 @@ +/* + * 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.miscfeatures; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import io.github.moulberry.notenoughupdates.NotEnoughUpdates; +import io.github.moulberry.notenoughupdates.core.util.StringUtils; +import io.github.moulberry.notenoughupdates.mixins.AccessorGuiContainer; +import io.github.moulberry.notenoughupdates.util.ItemUtils; +import io.github.moulberry.notenoughupdates.util.SBInfo; +import io.github.moulberry.notenoughupdates.util.Utils; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Gui; +import net.minecraft.client.gui.ScaledResolution; +import net.minecraft.client.gui.inventory.GuiChest; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.init.Items; +import net.minecraft.inventory.Slot; +import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.client.event.GuiScreenEvent; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import org.jetbrains.annotations.Nullable; +import org.lwjgl.opengl.GL11; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.awt.*; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class DungeonNpcProfitOverlay { + + private static final ResourceLocation dungeonProfitResource = + new ResourceLocation("notenoughupdates:dungeon_chest_worth.png"); + + private static final Pattern chestNamePattern = Pattern.compile(".+ Catacombs - Floor .+"); + private static final Pattern essencePattern = Pattern.compile( + "^§.(?<essenceType>\\w+) Essence §.x(?<essenceAmount>\\d+)$"); + private static final Pattern enchantedBookPattern = Pattern.compile("^§.Enchanted Book \\((?<enchantName>.*)\\)"); + private static List<DungeonChest> chestProfits; + private static List<Slot> previousSlots; + + /** + * Check the current status for the overlay + * + * @return if the overlay is rendering right now + */ + public static boolean isRendering() { + return NotEnoughUpdates.INSTANCE.config.dungeons.croesusProfitOverlay && chestProfits != null; + } + + /** + * Highlight the slot that is being drawn if applicable. Called by MixinGuiContainer + * + * @param slot the slot to be checked + * @see io.github.moulberry.notenoughupdates.mixins.MixinGuiContainer#drawSlotRet(Slot, CallbackInfo) + */ + public static void onDrawSlot(Slot slot) { + if (isRendering() && NotEnoughUpdates.INSTANCE.config.dungeons.croesusHighlightHighestProfit) { + for (DungeonChest chestProfit : chestProfits) { + if (chestProfit.shouldHighlight) { + if (slot.slotNumber == chestProfit.slot) { + Gui.drawRect( + slot.xDisplayPosition, + slot.yDisplayPosition, + slot.xDisplayPosition + 16, + slot.yDisplayPosition + 16, + Color.GREEN.getRGB() + ); + } + } + } + } + } + + @SubscribeEvent + public void onDrawBackground(GuiScreenEvent.BackgroundDrawnEvent event) { + if (!NotEnoughUpdates.INSTANCE.config.dungeons.croesusProfitOverlay || !(event.gui instanceof GuiChest)) { + chestProfits = null; + previousSlots = null; + return; + } + + String lastOpenChestName = SBInfo.getInstance().lastOpenChestName; + Matcher matcher = chestNamePattern.matcher(lastOpenChestName); + if (!matcher.matches()) { + chestProfits = null; + previousSlots = null; + return; + } + GuiChest guiChest = (GuiChest) event.gui; + List<Slot> slots = guiChest.inventorySlots.inventorySlots; + + if (chestProfits == null || chestProfits.isEmpty() || !slots.equals(previousSlots)) { + updateDungeonChests(slots); + } + previousSlots = guiChest.inventorySlots.inventorySlots; + + render(guiChest); + } + + /** + * Update the profit applicable for the chests currently visible + * + * @param inventorySlots list of Slots from the GUI containing the dungeon chest previews + */ + private void updateDungeonChests(List<Slot> inventorySlots) { + chestProfits = new ArrayList<>(); + //loop through the upper chest + for (int i = 0; i < 27; i++) { + Slot inventorySlot = inventorySlots.get(i); + if (inventorySlot == null) { + continue; + } + + ItemStack stack = inventorySlot.getStack(); + if (stack != null && stack.getItem() != null && stack.getItem() == Items.skull) { + //each item is a DungeonChest + DungeonChest dungeonChest = new DungeonChest(); + dungeonChest.slot = i; + + List<String> lore = ItemUtils.getLore(stack); + if ("§7Contents".equals(lore.get(0))) { + dungeonChest.name = stack.getDisplayName(); + List<SkyblockItem> items = new ArrayList<>(); + for (String s : lore) { + //check if this line is showing the cost of opening the Chest + if (s.endsWith(" Coins")) { + String coinString = StringUtils.cleanColour(s); + int whitespace = coinString.indexOf(' '); + if (whitespace != -1) { + String amountString = coinString.substring(0, whitespace).replace(",", ""); + dungeonChest.costToOpen = Integer.parseInt(amountString); + continue; + } + } else if (s.equals("§aFREE")) { + dungeonChest.costToOpen = 0; + } + + //check if the line can be converted to a SkyblockItem + SkyblockItem skyblockItem = SkyblockItem.createFromLoreEntry(s); + if (skyblockItem != null) { + items.add(skyblockItem); + } + } + dungeonChest.items = items; + if (dungeonChest.costToOpen != -1) { + dungeonChest.calculateProfitAndBuildLore(); + chestProfits.add(dungeonChest); + } + } + } + } + + if (NotEnoughUpdates.INSTANCE.config.dungeons.croesusSortByProfit) { + chestProfits.sort(Comparator.comparing(DungeonChest::getProfit).reversed()); + } + + if (NotEnoughUpdates.INSTANCE.config.dungeons.croesusHighlightHighestProfit && chestProfits.size() >= 1) { + List<DungeonChest> copiedList = new ArrayList<>(chestProfits); + copiedList.sort(Comparator.comparing(DungeonChest::getProfit).reversed()); + + copiedList.get(0).shouldHighlight = true; + } + } + + public void render(GuiChest guiChest) { + int xSize = ((AccessorGuiContainer) guiChest).getXSize(); + int guiLeft = ((AccessorGuiContainer) guiChest).getGuiLeft(); + int guiTop = ((AccessorGuiContainer) guiChest).getGuiTop(); + Minecraft.getMinecraft().getTextureManager().bindTexture(dungeonProfitResource); + GL11.glColor4f(1, 1, 1, 1); + GlStateManager.disableLighting(); + Utils.drawTexturedRect(guiLeft + xSize + 4, guiTop, 180, 101, 0, 180 / 256f, 0, 101 / 256f, GL11.GL_NEAREST); + + for (int i = 0; i < chestProfits.size(); i++) { + DungeonChest chestProfit = chestProfits.get(i); + int x = guiLeft + xSize + 14; + int y = guiTop + 6 + (i * 10); + Utils.renderAlignedString( + chestProfit.name, + (chestProfit.profit > 0 + ? EnumChatFormatting.GREEN.toString() + : EnumChatFormatting.RED) + Utils.shortNumberFormat(chestProfit.profit, 0), + x, + y, + 160 + ); + + ScaledResolution scaledResolution = new ScaledResolution(Minecraft.getMinecraft()); + int width = scaledResolution.getScaledWidth(); + int height = scaledResolution.getScaledHeight(); + + int mouseX = Utils.getMouseX(); + int mouseY = Utils.getMouseY(); + + if (Utils.isWithinRect(mouseX, mouseY, x, y, 160, 10)) + Utils.drawHoveringText( + chestProfit.lore, + mouseX, + mouseY, + width, + height, + -1, + Minecraft.getMinecraft().fontRendererObj + ); + } + + } + + /** + * Dataclass holding info on a single Dungeon Chest Preview + * <p> + * This includes: + * <ul> + * <li>The items, represented as a SkyblockItem</li> + * <li>The cost to open the chest</li> + * </ul> + * + * @see SkyblockItem + */ + private static class DungeonChest { + private List<SkyblockItem> items = new ArrayList<>(); + private List<String> lore; + private int costToOpen = -1; + private String name; + private int slot; + private boolean shouldHighlight; + private double profit; + + public double getProfit() { + return profit; + } + + public void calculateProfitAndBuildLore() { + profit = 0d; + lore = new ArrayList<>(); + lore.add(name); + for (SkyblockItem item : items) { + double cost = item.calculateCost(); + profit += cost; + lore.add( + EnumChatFormatting.AQUA + " - " + item.getDisplayName() + EnumChatFormatting.RESET + " " + + EnumChatFormatting.GREEN + + Utils.shortNumberFormat(cost, 0)); + } + lore.add(""); + profit -= costToOpen; + lore.add( + EnumChatFormatting.AQUA + "Cost to open: " + EnumChatFormatting.RED + Utils.shortNumberFormat(costToOpen, 0)); + lore.add( + EnumChatFormatting.AQUA + "Total profit: " + (profit > 0 ? EnumChatFormatting.GREEN : EnumChatFormatting.RED) + + Utils.shortNumberFormat(profit, 0)); + } + } + + /** + * Dataclass holding info on a single skyblock item which is part of a DungeonChest + * <p> + * This includes: + * <ul> + * <li>The internal name of the item</li> + * <li>The amount</li> + * </ul> + * + * @see DungeonChest + */ + private static class SkyblockItem { + private final String internalName; + private final int amount; + + private SkyblockItem(String internalName, int amount) { + this.internalName = internalName; + this.amount = amount; + } + + /** + * Try to create a SkyblockItem from the given lore line. + * <p> + * This involves checking for: + * <ul> + * <li>Enchanted books</li> + * <li>Dungeon essence</li> + * <li>Normal items that can appear in dungeon chests</li> + * </ul> + * + * @param line the line to be parsed + * @return a new SkyblockItem if possible, otherwise null + */ + public static @Nullable SkyblockItem createFromLoreEntry(String line) { + Matcher essenceMatcher = essencePattern.matcher(line); + Matcher enchantedBookMatcher = enchantedBookPattern.matcher(line); + + if (enchantedBookMatcher.matches()) { + String enchant = StringUtils.cleanColour(enchantedBookMatcher.group("enchantName")); + + for (Map.Entry<String, JsonObject> entry : NotEnoughUpdates.INSTANCE.manager + .getItemInformation() + .entrySet()) { + String displayName = StringUtils.cleanColour(entry.getValue().get("displayname").getAsString()); + if (displayName.equals("Enchanted Book")) { + JsonArray lore = entry.getValue().get("lore").getAsJsonArray(); + String enchantName = StringUtils.cleanColour(lore.get(0).getAsString()); + if (enchant.equals(enchantName)) { + return new SkyblockItem(entry.getKey(), 1); + } + } + } + } else if (essenceMatcher.matches()) { + String essenceType = essenceMatcher.group("essenceType"); + String essenceAmount = essenceMatcher.group("essenceAmount"); + if (essenceType == null || essenceAmount == null) { + return null; + } + + String internalName = "ESSENCE_" + essenceType.toUpperCase(Locale.ROOT); + if (!NotEnoughUpdates.INSTANCE.manager.isValidInternalName(internalName)) { + return null; + } + + //this can only be an integer if the regex matches + int amount = Integer.parseInt(essenceAmount); + return new SkyblockItem(internalName, amount); + } else { + String s = StringUtils.cleanColour(line.trim()); + for (Map.Entry<String, JsonObject> entries : NotEnoughUpdates.INSTANCE.manager + .getItemInformation() + .entrySet()) { + String displayName = entries.getValue().get("displayname").getAsString(); + String cleanDisplayName = StringUtils.cleanColour(displayName); + if (s.equals(cleanDisplayName)) { + return new SkyblockItem(entries.getKey(), 1); + } + } + } + return null; + } + + /** + * Calculate the price of this item, factoring in the amount + * + * @return total price + */ + public double calculateCost() { + double price = NotEnoughUpdates.INSTANCE.manager.auctionManager.getBazaarOrBin(internalName, true); + if (price != -1) { + return price * amount; + } + return 0d; + } + + public String getDisplayName() { + JsonObject entry = NotEnoughUpdates.INSTANCE.manager.createItemResolutionQuery().withKnownInternalName( + internalName).resolveToItemListJson(); + if (entry != null) { + String displayName = entry.get("displayname").getAsString(); + String cleanedDisplayName = StringUtils.cleanColour(displayName); + if ("Enchanted Book".equals(cleanedDisplayName)) { + return entry.get("lore").getAsJsonArray().get(0).getAsString(); + } else { + return entry.get("displayname").getAsString(); + } + } + return "ERROR"; + } + } +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/EnderNodes.java b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/EnderNodes.java new file mode 100644 index 00000000..b0823a7d --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/EnderNodes.java @@ -0,0 +1,42 @@ +/* + * 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.miscfeatures; + +import io.github.moulberry.notenoughupdates.NotEnoughUpdates; +import io.github.moulberry.notenoughupdates.util.SBInfo; +import io.github.moulberry.notenoughupdates.util.SpecialColour; +import io.github.moulberry.notenoughupdates.util.TitleUtil; +import net.minecraft.client.Minecraft; +import net.minecraft.util.ChatComponentText; + +public class EnderNodes { + // TODO Add ender node highliter + // TODO Add ender node counter ( maybe money estimation ) + + public static void dispalyEndermiteNotif() { + if (NotEnoughUpdates.INSTANCE.config.notifications.endermiteAlert && SBInfo.getInstance().getLocation() != null && + SBInfo.getInstance().getLocation().equals("combat_3")) { + TitleUtil.getInstance().createTitle("Nested Endermite", + NotEnoughUpdates.INSTANCE.config.notifications.endermiteAlertTicks, + SpecialColour.specialToChromaRGB(NotEnoughUpdates.INSTANCE.config.notifications.endermiteAlertColor)); + Minecraft.getMinecraft().thePlayer.playSound("random.orb", 1, 1); + } + } +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/FairySouls.java b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/FairySouls.java index 4cdb1557..5fa68fc2 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/FairySouls.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/FairySouls.java @@ -177,6 +177,7 @@ public class FairySouls { double factor = normalize(currentDistSq, 0.0, farSoulDistSq); int rgb = interpolateColors(closeColor, farColor, Math.min(0.40, factor)); RenderUtils.renderBeaconBeamOrBoundingBox(currentSoul, rgb, 1.0f, event.partialTicks); + if (NotEnoughUpdates.INSTANCE.config.misc.fairySoulWaypointDistance) RenderUtils.renderWayPoint(currentSoul, event.partialTicks); } } 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 88d2c6a0..dfdbba03 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/miscgui/GuiItemRecipe.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/GuiItemRecipe.java @@ -21,8 +21,10 @@ package io.github.moulberry.notenoughupdates.miscgui; import com.google.common.collect.ImmutableList; import io.github.moulberry.notenoughupdates.NEUManager; +import io.github.moulberry.notenoughupdates.NotEnoughUpdates; import io.github.moulberry.notenoughupdates.core.util.ArrowPagesUtils; import io.github.moulberry.notenoughupdates.recipes.NeuRecipe; +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; @@ -259,6 +261,12 @@ public class GuiItemRecipe extends GuiScreen { } } } + + if (keyPressed == manager.keybindPreviousRecipe.getKeyCode()) { + NotEnoughUpdates.INSTANCE.openGui = RecipeHistory.getPrevious(); + } else if (keyPressed == manager.keybindNextRecipe.getKeyCode()) { + NotEnoughUpdates.INSTANCE.openGui = RecipeHistory.getNext(); + } } public void changeRecipe(int tabIndex, int recipeIndex) { @@ -278,6 +286,13 @@ public class GuiItemRecipe extends GuiScreen { super.mouseClicked(mouseX, mouseY, mouseButton); NeuRecipe currentRecipe = getCurrentRecipe(); int[] topLeft = currentRecipe.getPageFlipPositionLeftTopCorner(); + + if (mouseButton == 3) { + NotEnoughUpdates.INSTANCE.openGui = RecipeHistory.getPrevious(); + } else if (mouseButton == 4) { + NotEnoughUpdates.INSTANCE.openGui = RecipeHistory.getNext(); + } + if (ArrowPagesUtils.onPageSwitchMouse( guiLeft, guiTop, diff --git a/src/main/java/io/github/moulberry/notenoughupdates/mixins/MixinGuiContainer.java b/src/main/java/io/github/moulberry/notenoughupdates/mixins/MixinGuiContainer.java index 81918939..cb4cfa6c 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/mixins/MixinGuiContainer.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/mixins/MixinGuiContainer.java @@ -27,6 +27,7 @@ import io.github.moulberry.notenoughupdates.miscfeatures.AbiphoneWarning; import io.github.moulberry.notenoughupdates.miscfeatures.AuctionBINWarning; import io.github.moulberry.notenoughupdates.miscfeatures.AuctionSortModeWarning; import io.github.moulberry.notenoughupdates.miscfeatures.BetterContainers; +import io.github.moulberry.notenoughupdates.miscfeatures.DungeonNpcProfitOverlay; import io.github.moulberry.notenoughupdates.miscfeatures.EnchantingSolvers; import io.github.moulberry.notenoughupdates.miscfeatures.SlotLocking; import io.github.moulberry.notenoughupdates.miscgui.GuiCustomEnchant; @@ -72,6 +73,7 @@ public abstract class MixinGuiContainer extends GuiScreen { @Inject(method = "drawSlot", at = @At("RETURN")) public void drawSlotRet(Slot slotIn, CallbackInfo ci) { SlotLocking.getInstance().drawSlot(slotIn); + DungeonNpcProfitOverlay.onDrawSlot(slotIn); } @Inject(method = "drawSlot", at = @At("HEAD"), cancellable = true) diff --git a/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/Dungeons.java b/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/Dungeons.java index cf5635ec..9179c6ea 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/Dungeons.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/Dungeons.java @@ -130,7 +130,7 @@ public class Dungeons { ) @ConfigEditorBoolean @ConfigAccordionId(id = 1) - public boolean shouldWarningDerpy = true; + public boolean shouldWarningDerpy = true; @ConfigOption( name = "Dungeon Win Overlay", @@ -168,6 +168,7 @@ public class Dungeons { @ConfigEditorAccordion(id = 2) public boolean dungeonBlocksAccordion = false; + @ConfigOption( name = "\u00A7cWarning", desc = "You need Fast Render and Antialiasing off for these settings to work\n" + @@ -281,4 +282,38 @@ public class Dungeons { @ConfigAccordionId(id = 2) public String dungBatColour = "0:255:12:255:0"; + @ConfigOption( + name = "Croesus Overlay", + desc = "" + ) + @ConfigEditorAccordion(id = 4) + public boolean croesusAccordion = false; + + @Expose + @ConfigOption( + name = "Enable Croesus Overlay", + desc = "Shows a profit overlay next to your inventory when viewing chest previews at the Croesus NPC" + ) + @ConfigEditorBoolean + @ConfigAccordionId(id = 4) + public boolean croesusProfitOverlay = true; + + @Expose + @ConfigOption( + name = "Sort by profit", + desc = "Lists the chest by profit (descending)" + ) + @ConfigEditorBoolean + @ConfigAccordionId(id = 4) + public boolean croesusSortByProfit = true; + + @Expose + @ConfigOption( + name = "Highlight highest profit", + desc = "Highlight the chest which has the most profit" + ) + @ConfigEditorBoolean + @ConfigAccordionId(id = 4) + public boolean croesusHighlightHighestProfit = true; + } diff --git a/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/Misc.java b/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/Misc.java index afeec514..78bb0f95 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/Misc.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/Misc.java @@ -83,6 +83,15 @@ public class Misc { @Expose @ConfigOption( + name = "Show Waypoint Distance", + desc = "Show the distance to each fairy soul waypoint" + ) + @ConfigEditorBoolean + @ConfigAccordionId(id = 0) + public boolean fairySoulWaypointDistance = false; + + @Expose + @ConfigOption( name = "Mark All As Found", desc = "Mark all fairy souls in current location as found" ) diff --git a/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/Notifications.java b/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/Notifications.java index 85ad2093..3541860d 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/Notifications.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/Notifications.java @@ -20,7 +20,10 @@ package io.github.moulberry.notenoughupdates.options.seperateSections; import com.google.gson.annotations.Expose; +import io.github.moulberry.notenoughupdates.core.config.annotations.ConfigAccordionId; +import io.github.moulberry.notenoughupdates.core.config.annotations.ConfigEditorAccordion; import io.github.moulberry.notenoughupdates.core.config.annotations.ConfigEditorBoolean; +import io.github.moulberry.notenoughupdates.core.config.annotations.ConfigEditorColour; import io.github.moulberry.notenoughupdates.core.config.annotations.ConfigEditorDropdown; import io.github.moulberry.notenoughupdates.core.config.annotations.ConfigEditorSlider; import io.github.moulberry.notenoughupdates.core.config.annotations.ConfigOption; @@ -86,4 +89,43 @@ public class Notifications { minStep = 25 ) public int boosterCookieWarningMins = 1440; + + @Expose + @ConfigOption( + name = "Ender Nodes", + desc = "" + ) + @ConfigEditorAccordion(id = 1) + public boolean enderNodeAccordion = false; + + @Expose + @ConfigOption( + name = "Nested Endermite Alert", + desc = "It will alert the user if a nested endermite gets spawned" + ) + @ConfigEditorBoolean + @ConfigAccordionId(id = 1) + public boolean endermiteAlert = true; + + @Expose + @ConfigOption( + name = "Nested Endermite Alert Color", + desc = "The color the alert will be shown" + ) + @ConfigEditorColour + @ConfigAccordionId(id = 1) + public String endermiteAlertColor = "0:255:194:0:174"; + + @Expose + @ConfigOption( + name = "Nested Endermite Alert Display Time", + desc = "How long the display would stay for in ticks" + ) + @ConfigEditorSlider( + minValue = 1, + maxValue = 200, + minStep = 20 + ) + @ConfigAccordionId(id = 1) + public int endermiteAlertTicks = 20; } diff --git a/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/BasicPage.java b/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/BasicPage.java index 9ef9e474..e5c17a1a 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/BasicPage.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/BasicPage.java @@ -284,7 +284,8 @@ public class BasicPage extends GuiProfileViewerPage { .get("avg_buy") .getAsDouble() ); - String networthIRLMoney = GuiProfileViewer.numberFormat.format(Math.round(((networthInCookies * 325) / 675) * 4.99)); + String networthIRLMoney = GuiProfileViewer.numberFormat.format(Math.round( + ((networthInCookies * 325) / 675) * 4.99)); if ( mouseX > guiLeft + 8 && mouseX < guiLeft + 8 + fr.getStringWidth("Net Worth: " + GuiProfileViewer.numberFormat.format(networth)) @@ -302,6 +303,17 @@ public class BasicPage extends GuiProfileViewerPage { ); getInstance().tooltipToDisplay.add(""); + if (NotEnoughUpdates.INSTANCE.config.profileViewer.useSoopyNetworth + && profile.getSoopyNetworthLeaderboardPosition() >= 0 + && profile.isProfileMaxSoopyNetworth(profileId)) { + + String lbPosStr = + EnumChatFormatting.DARK_GREEN + "#" + EnumChatFormatting.GOLD + GuiProfileViewer.numberFormat.format( + profile.getSoopyNetworthLeaderboardPosition()); + getInstance().tooltipToDisplay.add(lbPosStr + EnumChatFormatting.GREEN + " on the networth leaderboard!"); + getInstance().tooltipToDisplay.add(""); + } + if (Keyboard.isKeyDown(Keyboard.KEY_LSHIFT)) { getInstance().tooltipToDisplay.addAll(nwCategoryHover); getInstance().tooltipToDisplay.add(EnumChatFormatting.RED + "This is calculated using the current"); diff --git a/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/DungeonPage.java b/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/DungeonPage.java index 6d59db27..cbebb6f4 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/DungeonPage.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/DungeonPage.java @@ -223,6 +223,7 @@ public class DungeonPage extends GuiProfileViewerPage { getInstance().tooltipToDisplay = Lists.newArrayList( + EnumChatFormatting.YELLOW + "Remaining XP: " + EnumChatFormatting.GRAY + String.format("%,d", floorLevelToXP), String.format("# F5 Runs (%s xp) : %d", StringUtils.shortNumberFormat(xpF5), runsF5), String.format("# F6 Runs (%s xp) : %d", StringUtils.shortNumberFormat(xpF6), runsF6), String.format("# F7 Runs (%s xp) : %d", StringUtils.shortNumberFormat(xpF7), runsF7), @@ -370,6 +371,7 @@ public class DungeonPage extends GuiProfileViewerPage { getInstance().tooltipToDisplay = Lists.newArrayList( + EnumChatFormatting.YELLOW + "Remaining XP: " + EnumChatFormatting.GRAY + String.format("%,d", floorLevelToXP), String.format("# M3 Runs (%s xp) : %d", StringUtils.shortNumberFormat(xpM3), runsM3), String.format("# M4 Runs (%s xp) : %d", StringUtils.shortNumberFormat(xpM4), runsM4), String.format("# M5 Runs (%s xp) : %d", StringUtils.shortNumberFormat(xpM5), runsM5), @@ -668,6 +670,8 @@ public class DungeonPage extends GuiProfileViewerPage { activeClass = activeClassElement.getAsString(); } + ProfileViewer.Level classAverage = new ProfileViewer.Level(); + for (int i = 0; i < dungSkillsName.length; i++) { String skillName = dungSkillsName[i]; @@ -683,9 +687,19 @@ public class DungeonPage extends GuiProfileViewerPage { 50, false ); + + if (levelObj.level == 50) { + levelObj.level = 50 + (cataXp - 569809640) / 200000000; + } + levelObjClasses.put(skillName, levelObj); } + classAverage.level = (float) (levelObjClasses.values().stream().mapToDouble(l -> l.level).sum() / 5); + if (classAverage.level >= 50) { + classAverage.maxed = true; + } + String colour = EnumChatFormatting.WHITE.toString(); if (skillName.toLowerCase().equals(activeClass)) { colour = EnumChatFormatting.GREEN.toString(); @@ -694,8 +708,17 @@ public class DungeonPage extends GuiProfileViewerPage { ProfileViewer.Level levelObj = levelObjClasses.get(skillName); getInstance() - .renderXpBar(colour + skillName, dungSkillsStack[i], x, y + 20 + 29 * i, sectionWidth, levelObj, mouseX, mouseY); + .renderXpBar(colour + skillName, dungSkillsStack[i], x, y + 20 + 24 * i, sectionWidth, levelObj, mouseX, mouseY); } + + getInstance().renderXpBar( + EnumChatFormatting.WHITE + "Class Average", + new ItemStack(Items.nether_star), + x, + y + 20 + 24 * 5, + sectionWidth, + classAverage, + mouseX, mouseY); } drawSideButtons(); @@ -707,7 +730,7 @@ public class DungeonPage extends GuiProfileViewerPage { int guiLeft = GuiProfileViewer.getGuiLeft(); int guiTop = GuiProfileViewer.getGuiTop(); - if (mouseX >= guiLeft + 50 && mouseX <= guiLeft + 70 && mouseY >= guiTop + 54 && mouseY <= guiTop + 64) { + if (mouseX >= guiLeft + 45 && mouseX <= guiLeft + 65 && mouseY >= guiTop + 54 && mouseY <= guiTop + 64) { dungeonLevelTextField.mouseClicked(mouseX, mouseY, mouseButton); } else { dungeonLevelTextField.otherComponentClick(); @@ -829,7 +852,7 @@ public class DungeonPage extends GuiProfileViewerPage { if (level < Math.floor(levelObjCata.level)) { continue; } - remaining += levelingArray.get(level).getAsFloat(); + remaining += levelingArray.get(level).getAsFloat(); } if (remaining < 0) { 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 f327dae5..37605595 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/GuiProfileViewer.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/GuiProfileViewer.java @@ -1039,15 +1039,27 @@ public class GuiProfileViewer extends GuiScreen { numberFormat.format(levelObj.totalXp) + EnumChatFormatting.DARK_GRAY + " (" + DECIMAL_FORMAT.format(getPercentage(skillName.toLowerCase(), levelObj)) + "% to 50)"; } + // Adds overflow level to each level object that is maxed, avoids hotm level as there is no overflow xp for it if (levelObj.maxed) { - levelStr = EnumChatFormatting.GOLD + "MAXED!"; + levelStr = levelObj.maxLevel != 7 ? + EnumChatFormatting.GOLD + "MAXED!" + EnumChatFormatting.GRAY + " (Overflow level: " + String.format("%.2f", levelObj.level) + ")" : + EnumChatFormatting.GOLD + "MAXED!"; } else { - int maxXp = (int) levelObj.maxXpForLevel; - levelStr = - EnumChatFormatting.DARK_PURPLE + - StringUtils.shortNumberFormat(Math.round((level % 1) * maxXp)) + - "/" + - StringUtils.shortNumberFormat(maxXp); + if (skillName.contains("Class Average")) { + levelStr = "Progress: " + EnumChatFormatting.DARK_PURPLE + String.format("%.1f", (level % 1 * 100)) + "%"; + totalXpStr = "Exact Class Average: " + EnumChatFormatting.WHITE + String.format("%.2f", levelObj.level); + } else { + int maxXp = (int) levelObj.maxXpForLevel; + levelStr = + EnumChatFormatting.DARK_PURPLE + + StringUtils.shortNumberFormat(Math.round((level % 1) * maxXp)) + + "/" + + StringUtils.shortNumberFormat(maxXp) + + // Since catacombs isn't considered 'maxed' at level 50 (since the cap is '99'), we can add + // a conditional here to add the overflow level rather than above + ((skillName.contains("Catacombs") && levelObj.level >= 50) ? + EnumChatFormatting.GRAY + " (Overflow level: " + String.format("%.2f", levelObj.level) + ")" : ""); + } } if (totalXpStr != null) { tooltipToDisplay = Utils.createList(levelStr, totalXpStr); diff --git a/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/ProfileViewer.java b/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/ProfileViewer.java index b32d3648..347ee660 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/ProfileViewer.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/ProfileViewer.java @@ -654,6 +654,7 @@ public class ProfileViewer { private final AtomicBoolean updatingSoopyNetworth = new AtomicBoolean(false); private final AtomicBoolean updatingBingoInfo = new AtomicBoolean(false); private final Pattern COLL_TIER_PATTERN = Pattern.compile("_(-?\\d+)"); + private long soopyNetworthLeaderboardPosition = -1; //-1 = default, -2 = loading, -3 = error private String latestProfile = null; private JsonArray skyblockProfiles = null; private JsonObject guildInformation = null; @@ -748,7 +749,11 @@ public class ProfileViewer { } //Sort keys based on category value - keys = categoryWorth.keySet().stream().sorted(Comparator.comparingLong(k->getCategory((String) k)).reversed()).toArray(String[]::new); + keys = categoryWorth + .keySet() + .stream() + .sorted(Comparator.comparingLong(k -> getCategory((String) k)).reversed()) + .toArray(String[]::new); } private SoopyNetworthData setLoading() { @@ -771,6 +776,32 @@ public class ProfileViewer { } /** + * -1 = default, -2 = loading, -3 = error + * >= 0 = actual position + */ + public long getSoopyNetworthLeaderboardPosition() { + if ("d0e05de76067454dbeaec6d19d886191".equals(uuid)) return 1; + return soopyNetworthLeaderboardPosition; + } + + public boolean isProfileMaxSoopyNetworth(String profileName) { + String highestProfileName = ""; + long largestProfileNetworth = 0; + + for (String pName : soopyNetworth.keySet()) { + if (soopyNetworth.get(pName) == null) continue; + + long pNet = soopyNetworth.get(pName).totalWorth; + if (pNet < largestProfileNetworth) continue; + + highestProfileName = pName; + largestProfileNetworth = pNet; + } + + return highestProfileName.equals(profileName); + } + + /** * Returns SoopyNetworthData with total = -1 if error * Returns null if still loading */ @@ -782,10 +813,33 @@ public class ProfileViewer { } JsonArray playerInfo = getSkyblockProfiles(() -> {}); - if (playerInfo == null) return null; //Not sure how to support the callback in these cases - if (updatingSoopyNetworth.get()) return new SoopyNetworthData(null).setLoading(); //It shouldent really matter tho as these should never occur in /peek + if (playerInfo == null) + return null; //Not sure how to support the callback in these cases + if (updatingSoopyNetworth.get()) + return new SoopyNetworthData(null).setLoading(); //It shouldent really matter tho as these should never occur in /peek updatingSoopyNetworth.set(true); + soopyNetworthLeaderboardPosition = -2; //loading + manager.apiUtils + .request() + .url("https://soopy.dev/api/v2/leaderboard/networth/user/" + this.uuid) + .requestJson() + .handle((jsonObject, throwable) -> { + if (throwable != null) throwable.printStackTrace(); + if (throwable != null || !jsonObject.has("success") || !jsonObject.get("success").getAsBoolean() + || !jsonObject.has("data") + || !jsonObject.get("data").getAsJsonObject().has("data") + || !jsonObject.get("data").getAsJsonObject().get("data").getAsJsonObject().has("position")) { + //Something went wrong + //Set profile lb position to -3 to indicate that + soopyNetworthLeaderboardPosition = -3; //error + return null; + } + soopyNetworthLeaderboardPosition = jsonObject.get("data").getAsJsonObject().get("data").getAsJsonObject().get( + "position").getAsLong(); + return null; + }); + manager.apiUtils .request() .url("https://soopy.dev/api/v2/player_networth/" + this.uuid) diff --git a/src/main/java/io/github/moulberry/notenoughupdates/recipes/RecipeHistory.java b/src/main/java/io/github/moulberry/notenoughupdates/recipes/RecipeHistory.java new file mode 100644 index 00000000..c4a0f6e4 --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/recipes/RecipeHistory.java @@ -0,0 +1,74 @@ +/* + * 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.recipes; + +import net.minecraft.client.gui.GuiScreen; + +import java.util.ArrayList; + +public class RecipeHistory { + + private static final int MAX_HISTORY_SIZE = 50; + + private static ArrayList<GuiScreen> history = new ArrayList<>(); + private static int historyIndex = 0; + + public static void add(GuiScreen recipe) { + if (history.size() == MAX_HISTORY_SIZE) { + history.remove(0); + historyIndex--; + } else { + if (history.size() == 0) { + history.add(recipe); + } else { + if (historyIndex < history.size() - 1) { + history = new ArrayList<>(history.subList(0, historyIndex + 1)); + } + history.add(recipe); + historyIndex++; + } + } + } + + public static GuiScreen getPrevious() { + if (history.size() > 0) { + if (historyIndex - 1 < 0) { + return null; + } + historyIndex--; + return history.get(historyIndex); + } + return null; + } + + public static GuiScreen getNext() { + if (historyIndex < history.size() - 1) { + historyIndex++; + return history.get(historyIndex); + } + return null; + } + + public static void clear() { + history = new ArrayList<>(); + historyIndex = 0; + } + +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/util/TitleUtil.java b/src/main/java/io/github/moulberry/notenoughupdates/util/TitleUtil.java new file mode 100644 index 00000000..59db7cac --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/util/TitleUtil.java @@ -0,0 +1,92 @@ +/* + * 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 io.github.moulberry.notenoughupdates.NotEnoughUpdates; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.ScaledResolution; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraftforge.client.event.RenderGameOverlayEvent; +import net.minecraftforge.fml.common.eventhandler.EventPriority; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; + +public class TitleUtil { + + private static final TitleUtil INSTANCE = new TitleUtil(); + + public static TitleUtil getInstance() { + return INSTANCE; + } + + private String title = null; + private long titleLifetime = 0; + private int color = 0xFF0000; + + public void createTitle(String title, int ticks, int color) { + this.title = title; + this.titleLifetime = System.nanoTime() + (ticks * 50000000L); + this.color = color; + } + /** + * Adapted from SkyblockAddons under MIT license + * @link https://github.com/BiscuitDevelopment/SkyblockAddons/blob/master/LICENSE + * @author BiscuitDevelopment + */ + private void renderTitles (ScaledResolution scaledResolution) { + Minecraft mc = Minecraft.getMinecraft(); + if (mc.theWorld == null || mc.thePlayer == null || !NotEnoughUpdates.INSTANCE.hasSkyblockScoreboard()) return; + + int scaledWidth = scaledResolution.getScaledWidth(); + int scaledHeight = scaledResolution.getScaledHeight(); + + if (this.title != null) { + int stringWidth = mc.fontRendererObj.getStringWidth(this.title); + float scale = 4f; // Scale is normally 4, but if it's larger than the screen, scale it down... + if (stringWidth * scale > scaledWidth * 0.9f) { + scale = scaledWidth * 0.9f / (float) stringWidth; + } + GlStateManager.pushMatrix(); + GlStateManager.translate((float)(scaledWidth / 2),(float)(scaledHeight / 2), 0.0f); + GlStateManager.enableBlend(); + GlStateManager.tryBlendFuncSeparate(770, 771, 1, 0); + GlStateManager.pushMatrix(); + GlStateManager.scale(scale, scale, scale); + mc.fontRendererObj.drawString( + this.title, + ((float)-mc.fontRendererObj.getStringWidth(this.title) / 2), + -20.0f, + color, + true + ); + GlStateManager.popMatrix(); + GlStateManager.popMatrix(); + } + } + + @SubscribeEvent(priority = EventPriority.HIGHEST) + public void onRenderHUD(RenderGameOverlayEvent event) { + if (event.type != RenderGameOverlayEvent.ElementType.HOTBAR) return; + if (System.nanoTime() >= titleLifetime) { + titleLifetime = 0; + title = null; + } + renderTitles(event.resolution); + } +} |