aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/de/hysky/skyblocker/skyblock
diff options
context:
space:
mode:
authorRime <81419447+Emirlol@users.noreply.github.com>2024-05-29 13:45:34 +0300
committerRime <81419447+Emirlol@users.noreply.github.com>2024-06-08 04:13:47 +0300
commit38f32048dffcd2b1bcdbba77ecdb16aeeac61690 (patch)
treeb8ba9abf4890d965d88d3312bbb6b3288c141ead /src/main/java/de/hysky/skyblocker/skyblock
parent72988890442db5566287ec5adc8c6dc01676cdda (diff)
downloadSkyblocker-38f32048dffcd2b1bcdbba77ecdb16aeeac61690.tar.gz
Skyblocker-38f32048dffcd2b1bcdbba77ecdb16aeeac61690.tar.bz2
Skyblocker-38f32048dffcd2b1bcdbba77ecdb16aeeac61690.zip
Move tooltip adders from utils/tooltip package to skyblock/item/tooltip/adders
Diffstat (limited to 'src/main/java/de/hysky/skyblocker/skyblock')
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java2
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/TooltipManager.java69
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/AccessoryTooltip.java43
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/AvgBinTooltip.java66
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/BazaarPriceTooltip.java46
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/ColorTooltip.java135
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/DungeonQualityTooltip.java55
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/LBinTooltip.java39
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/LineSmoothener.java31
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/MotesTooltip.java50
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/MuseumTooltip.java49
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/NpcPriceTooltip.java27
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/ObtainedTooltip.java71
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/TooltipAdder.java36
14 files changed, 718 insertions, 1 deletions
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java
index cd622934..fa19119a 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java
@@ -1,11 +1,11 @@
package de.hysky.skyblocker.skyblock.chocolatefactory;
import de.hysky.skyblocker.config.SkyblockerConfigManager;
+import de.hysky.skyblocker.skyblock.item.tooltip.adders.LineSmoothener;
import de.hysky.skyblocker.utils.ItemUtils;
import de.hysky.skyblocker.utils.RegexUtils;
import de.hysky.skyblocker.utils.render.gui.ColorHighlight;
import de.hysky.skyblocker.utils.render.gui.ContainerSolver;
-import de.hysky.skyblocker.utils.tooltip.LineSmoothener;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import net.fabricmc.fabric.api.client.item.v1.ItemTooltipCallback;
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/TooltipManager.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/TooltipManager.java
new file mode 100644
index 00000000..90dc48d6
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/TooltipManager.java
@@ -0,0 +1,69 @@
+package de.hysky.skyblocker.skyblock.item.tooltip;
+
+import de.hysky.skyblocker.skyblock.item.tooltip.adders.*;
+import de.hysky.skyblocker.utils.Utils;
+import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents;
+import net.minecraft.client.gui.screen.Screen;
+import net.minecraft.screen.slot.Slot;
+import net.minecraft.text.Text;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+
+public class TooltipManager {
+ private static final TooltipAdder[] adders = new TooltipAdder[]{
+ new LineSmoothener(),
+ new NpcPriceTooltip(1),
+ new BazaarPriceTooltip(2),
+ new LBinTooltip(3), //Has to come after bz price
+ new AvgBinTooltip(4), //Has to come after lbin price
+ new DungeonQualityTooltip(5),
+ new MotesTooltip(6),
+ new ObtainedTooltip(7),
+ new MuseumTooltip(8),
+ new ColorTooltip(9),
+ new AccessoryTooltip(10),
+ };
+ private static final ArrayList<TooltipAdder> currentScreenAdders = new ArrayList<>();
+
+ private TooltipManager() {
+ }
+
+ public static void init() {
+ ScreenEvents.AFTER_INIT.register((client, screen, width, height) -> {
+ onScreenChange(screen);
+ ScreenEvents.remove(screen).register(ignored -> currentScreenAdders.clear());
+ });
+ }
+
+ private static void onScreenChange(Screen screen) {
+ final String title = screen.getTitle().getString();
+ for (TooltipAdder adder : adders) {
+ if (adder.titlePattern == null || adder.titlePattern.matcher(title).matches()) {
+ currentScreenAdders.add(adder);
+ }
+ }
+ currentScreenAdders.sort(Comparator.comparingInt(adder -> adder.priority));
+ }
+
+ /**
+ * <p>Adds additional text from all adders that are applicable to the current screen.
+ * This method is run on each tooltip render, so don't do any heavy calculations here.</p>
+ *
+ * <p>If you want to add info to the tooltips of multiple items, consider using a switch statement with {@code focusedSlot.getIndex()}</p>
+ *
+ * @param lore The lore of the focused item.
+ * @param focusedSlot The slot that is currently focused by the cursor.
+ * @return The lore itself after all adders have added their text.
+ * @deprecated This method is public only for the sake of the mixin. Don't call directly, not that there is any point to it.
+ */
+ @Deprecated
+ public static List<Text> addToTooltip(List<Text> lore, Slot focusedSlot) {
+ if (!Utils.isOnSkyblock()) return lore;
+ for (TooltipAdder adder : currentScreenAdders) {
+ adder.addToTooltip(lore, focusedSlot);
+ }
+ return lore;
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/AccessoryTooltip.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/AccessoryTooltip.java
new file mode 100644
index 00000000..b35ba0a1
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/AccessoryTooltip.java
@@ -0,0 +1,43 @@
+package de.hysky.skyblocker.skyblock.item.tooltip.adders;
+
+import de.hysky.skyblocker.skyblock.item.tooltip.AccessoriesHelper;
+import de.hysky.skyblocker.skyblock.item.tooltip.ItemTooltip;
+import de.hysky.skyblocker.skyblock.item.tooltip.TooltipInfoType;
+import it.unimi.dsi.fastutil.Pair;
+import net.minecraft.screen.slot.Slot;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+import java.util.List;
+
+public class AccessoryTooltip extends TooltipAdder {
+ public AccessoryTooltip(int priority) {
+ super(priority);
+ }
+
+ @Override
+ public void addToTooltip(List<Text> lore, Slot focusedSlot) {
+ final String internalID = ItemTooltip.getInternalNameFromNBT(focusedSlot.getStack(), true);
+ if (TooltipInfoType.ACCESSORIES.isTooltipEnabledAndHasOrNullWarning(internalID)) {
+ Pair<AccessoriesHelper.AccessoryReport, String> report = AccessoriesHelper.calculateReport4Accessory(internalID);
+
+ if (report.left() != AccessoriesHelper.AccessoryReport.INELIGIBLE) {
+ MutableText title = Text.literal(String.format("%-19s", "Accessory: ")).withColor(0xf57542);
+
+ Text stateText = switch (report.left()) {
+ case HAS_HIGHEST_TIER -> Text.literal("✔ Collected").formatted(Formatting.GREEN);
+ case IS_GREATER_TIER -> Text.literal("✦ Upgrade ").withColor(0x218bff).append(Text.literal(report.right()).withColor(0xf8f8ff));
+ case HAS_GREATER_TIER -> Text.literal("↑ Upgradable ").withColor(0xf8d048).append(Text.literal(report.right()).withColor(0xf8f8ff));
+ case OWNS_BETTER_TIER -> Text.literal("↓ Downgrade ").formatted(Formatting.GRAY).append(Text.literal(report.right()).withColor(0xf8f8ff));
+ case MISSING -> Text.literal("✖ Missing ").formatted(Formatting.RED).append(Text.literal(report.right()).withColor(0xf8f8ff));
+
+ //Should never be the case
+ default -> Text.literal("? Unknown").formatted(Formatting.GRAY);
+ };
+
+ lore.add(title.append(stateText));
+ }
+ }
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/AvgBinTooltip.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/AvgBinTooltip.java
new file mode 100644
index 00000000..70dc3b21
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/AvgBinTooltip.java
@@ -0,0 +1,66 @@
+package de.hysky.skyblocker.skyblock.item.tooltip.adders;
+
+import de.hysky.skyblocker.config.SkyblockerConfigManager;
+import de.hysky.skyblocker.config.configs.GeneralConfig;
+import de.hysky.skyblocker.skyblock.item.tooltip.ItemTooltip;
+import de.hysky.skyblocker.skyblock.item.tooltip.TooltipInfoType;
+import net.minecraft.item.ItemStack;
+import net.minecraft.screen.slot.Slot;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+import java.util.List;
+
+public class AvgBinTooltip extends TooltipAdder {
+ public AvgBinTooltip(int priority) {
+ super(priority);
+ }
+
+ @Override
+ public void addToTooltip(List<Text> lore, Slot focusedSlot) {
+ final ItemStack itemStack = focusedSlot.getStack();
+ String neuName = ItemTooltip.getInternalNameFromNBT(itemStack, false);
+ String internalID = ItemTooltip.getInternalNameFromNBT(itemStack, true);
+ if (neuName == null || internalID == null) return;
+
+ if (neuName.startsWith("ISSHINY_")) neuName = internalID;
+
+ if (SkyblockerConfigManager.get().general.itemTooltip.enableAvgBIN) {
+ if (TooltipInfoType.ONE_DAY_AVERAGE.getData() == null || TooltipInfoType.THREE_DAY_AVERAGE.getData() == null) {
+ ItemTooltip.nullWarning();
+ } else {
+ /*
+ We are skipping check average prices for potions, runes
+ and enchanted books because there is no data for their in API.
+ */
+ neuName = ItemTooltip.getNeuName(internalID, neuName);
+
+ if (!neuName.isEmpty() && LBinTooltip.lbinExist) {
+ GeneralConfig.Average type = ItemTooltip.config.avg;
+
+ // "No data" line because of API not keeping old data, it causes NullPointerException
+ if (type == GeneralConfig.Average.ONE_DAY || type == GeneralConfig.Average.BOTH) {
+ lore.add(
+ Text.literal(String.format("%-19s", "1 Day Avg. Price:"))
+ .formatted(Formatting.GOLD)
+ .append(TooltipInfoType.ONE_DAY_AVERAGE.getData().get(neuName) == null
+ ? Text.literal("No data").formatted(Formatting.RED)
+ : ItemTooltip.getCoinsMessage(TooltipInfoType.ONE_DAY_AVERAGE.getData().get(neuName).getAsDouble(), itemStack.getCount())
+ )
+ );
+ }
+ if (type == GeneralConfig.Average.THREE_DAY || type == GeneralConfig.Average.BOTH) {
+ lore.add(
+ Text.literal(String.format("%-19s", "3 Day Avg. Price:"))
+ .formatted(Formatting.GOLD)
+ .append(TooltipInfoType.THREE_DAY_AVERAGE.getData().get(neuName) == null
+ ? Text.literal("No data").formatted(Formatting.RED)
+ : ItemTooltip.getCoinsMessage(TooltipInfoType.THREE_DAY_AVERAGE.getData().get(neuName).getAsDouble(), itemStack.getCount())
+ )
+ );
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/BazaarPriceTooltip.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/BazaarPriceTooltip.java
new file mode 100644
index 00000000..8266aa87
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/BazaarPriceTooltip.java
@@ -0,0 +1,46 @@
+package de.hysky.skyblocker.skyblock.item.tooltip.adders;
+
+import com.google.gson.JsonObject;
+import de.hysky.skyblocker.skyblock.item.tooltip.ItemTooltip;
+import de.hysky.skyblocker.skyblock.item.tooltip.TooltipInfoType;
+import net.minecraft.item.ItemStack;
+import net.minecraft.screen.slot.Slot;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+import java.util.List;
+
+public class BazaarPriceTooltip extends TooltipAdder {
+ public static boolean bazaarExist = false;
+
+ public BazaarPriceTooltip(int priority) {
+ super(priority);
+ }
+
+ @Override
+ public void addToTooltip(List<Text> lore, Slot focusedSlot) {
+ bazaarExist = false;
+ final ItemStack itemStack = focusedSlot.getStack();
+ final String internalID = ItemTooltip.getInternalNameFromNBT(itemStack, true);
+ if (internalID == null) return;
+ String name = ItemTooltip.getInternalNameFromNBT(itemStack, false);
+ if (name == null) return;
+
+ if (name.startsWith("ISSHINY_")) name = "SHINY_" + internalID;
+
+ if (TooltipInfoType.BAZAAR.isTooltipEnabledAndHasOrNullWarning(name)) {
+ JsonObject getItem = TooltipInfoType.BAZAAR.getData().getAsJsonObject(name);
+ lore.add(Text.literal(String.format("%-18s", "Bazaar buy Price:"))
+ .formatted(Formatting.GOLD)
+ .append(getItem.get("buyPrice").isJsonNull()
+ ? Text.literal("No data").formatted(Formatting.RED)
+ : ItemTooltip.getCoinsMessage(getItem.get("buyPrice").getAsDouble(), itemStack.getCount())));
+ lore.add(Text.literal(String.format("%-19s", "Bazaar sell Price:"))
+ .formatted(Formatting.GOLD)
+ .append(getItem.get("sellPrice").isJsonNull()
+ ? Text.literal("No data").formatted(Formatting.RED)
+ : ItemTooltip.getCoinsMessage(getItem.get("sellPrice").getAsDouble(), itemStack.getCount())));
+ bazaarExist = true;
+ }
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/ColorTooltip.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/ColorTooltip.java
new file mode 100644
index 00000000..253626ef
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/ColorTooltip.java
@@ -0,0 +1,135 @@
+package de.hysky.skyblocker.skyblock.item.tooltip.adders;
+
+import de.hysky.skyblocker.config.SkyblockerConfigManager;
+import de.hysky.skyblocker.skyblock.item.tooltip.ItemTooltip;
+import de.hysky.skyblocker.skyblock.item.tooltip.TooltipInfoType;
+import de.hysky.skyblocker.utils.Constants;
+import de.hysky.skyblocker.utils.ItemUtils;
+import net.minecraft.component.DataComponentTypes;
+import net.minecraft.component.type.DyedColorComponent;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NbtCompound;
+import net.minecraft.screen.slot.Slot;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+import net.minecraft.util.StringIdentifiable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+
+public class ColorTooltip extends TooltipAdder {
+ private static final Logger LOGGER = LoggerFactory.getLogger(ColorTooltip.class);
+
+ public ColorTooltip(int priority) {
+ super(priority);
+ }
+
+ @Override
+ public void addToTooltip(List<Text> lore, Slot focusedSlot) {
+ final ItemStack itemStack = focusedSlot.getStack();
+ final String internalID = ItemTooltip.getInternalNameFromNBT(itemStack, true);
+ if (TooltipInfoType.COLOR.isTooltipEnabledAndHasOrNullWarning(internalID) && itemStack.contains(DataComponentTypes.DYED_COLOR)) {
+ String uuid = ItemUtils.getItemUuid(itemStack);
+ boolean hasCustomDye = SkyblockerConfigManager.get().general.customDyeColors.containsKey(uuid) || SkyblockerConfigManager.get().general.customAnimatedDyes.containsKey(uuid);
+ //DyedColorComponent#getColor returns ARGB so we mask out the alpha bits
+ int dyeColor = DyedColorComponent.getColor(itemStack, 0);
+
+ // dyeColor will have alpha = 255 if it's dyed, and alpha = 0 if it's not dyed,
+ if (!hasCustomDye && dyeColor != 0) {
+ dyeColor = dyeColor & 0x00FFFFFF;
+ String colorHex = String.format("%06X", dyeColor);
+ String expectedHex = getExpectedHex(internalID);
+
+ boolean correctLine = false;
+ for (Text text : lore) {
+ String existingTooltip = text.getString() + " ";
+ if (existingTooltip.startsWith("Color: ")) {
+ correctLine = true;
+
+ addExoticTooltip(lore, internalID, ItemUtils.getCustomData(itemStack), colorHex, expectedHex, existingTooltip);
+ break;
+ }
+ }
+
+ if (!correctLine) {
+ addExoticTooltip(lore, internalID, ItemUtils.getCustomData(itemStack), colorHex, expectedHex, "");
+ }
+ }
+ }
+ }
+
+ private static void addExoticTooltip(List<Text> lines, String internalID, NbtCompound customData, String colorHex, String expectedHex, String existingTooltip) {
+ if (expectedHex != null && !colorHex.equalsIgnoreCase(expectedHex) && !isException(internalID, colorHex) && !intendedDyed(customData)) {
+ final DyeType type = checkDyeType(colorHex);
+ lines.add(1, Text.literal(existingTooltip + Formatting.DARK_GRAY + "(").append(type.getTranslatedText()).append(Formatting.DARK_GRAY + ")"));
+ }
+ }
+
+ public static String getExpectedHex(String id) {
+ String color = TooltipInfoType.COLOR.getData().get(id).getAsString();
+ if (color != null) {
+ String[] RGBValues = color.split(",");
+ return String.format("%02X%02X%02X", Integer.parseInt(RGBValues[0]), Integer.parseInt(RGBValues[1]), Integer.parseInt(RGBValues[2]));
+ } else {
+ LOGGER.warn("[Skyblocker Exotics] No expected color data found for id {}", id);
+ return null;
+ }
+ }
+
+ public static boolean isException(String id, String hex) {
+ return switch (id) {
+ case String it when it.startsWith("LEATHER") || it.equals("GHOST_BOOTS") || Constants.SEYMOUR_IDS.contains(it) -> true;
+ case String it when it.startsWith("RANCHER") -> Constants.RANCHERS.contains(hex);
+ case String it when it.contains("ADAPTIVE_CHESTPLATE") -> Constants.ADAPTIVE_CHEST.contains(hex);
+ case String it when it.contains("ADAPTIVE") -> Constants.ADAPTIVE.contains(hex);
+ case String it when it.contains("REAPER") -> Constants.REAPER.contains(hex);
+ case String it when it.contains("FAIRY") -> Constants.FAIRY_HEXES.contains(hex);
+ case String it when it.contains("CRYSTAL") -> Constants.CRYSTAL_HEXES.contains(hex);
+ case String it when it.contains("SPOOK") -> Constants.SPOOK.contains(hex);
+ default -> false;
+ };
+ }
+
+ public static DyeType checkDyeType(String hex) {
+ return switch (hex) {
+ case String it when Constants.CRYSTAL_HEXES.contains(it) -> DyeType.CRYSTAL;
+ case String it when Constants.FAIRY_HEXES.contains(it) -> DyeType.FAIRY;
+ case String it when Constants.OG_FAIRY_HEXES.contains(it) -> DyeType.OG_FAIRY;
+ case String it when Constants.SPOOK.contains(it) -> DyeType.SPOOK;
+ case String it when Constants.GLITCHED.contains(it) -> DyeType.GLITCHED;
+ default -> DyeType.EXOTIC;
+ };
+ }
+
+ public static boolean intendedDyed(NbtCompound customData) {
+ return customData.contains("dye_item");
+ }
+
+ public enum DyeType implements StringIdentifiable {
+ CRYSTAL("crystal", Formatting.AQUA),
+ FAIRY("fairy", Formatting.LIGHT_PURPLE),
+ OG_FAIRY("og_fairy", Formatting.DARK_PURPLE),
+ SPOOK("spook", Formatting.RED),
+ GLITCHED("glitched", Formatting.BLUE),
+ EXOTIC("exotic", Formatting.GOLD);
+ private final String name;
+ private final Formatting formatting;
+
+ DyeType(String name, Formatting formatting) {
+ this.name = name;
+ this.formatting = formatting;
+ }
+
+ @Override
+ public String asString() {
+ return name;
+ }
+
+ public MutableText getTranslatedText() {
+ return Text.translatable("skyblocker.exotic." + name).formatted(formatting);
+ }
+ }
+
+}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/DungeonQualityTooltip.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/DungeonQualityTooltip.java
new file mode 100644
index 00000000..7f928e75
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/DungeonQualityTooltip.java
@@ -0,0 +1,55 @@
+package de.hysky.skyblocker.skyblock.item.tooltip.adders;
+
+import de.hysky.skyblocker.config.SkyblockerConfigManager;
+import de.hysky.skyblocker.utils.ItemUtils;
+import net.minecraft.nbt.NbtCompound;
+import net.minecraft.screen.slot.Slot;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+import java.util.List;
+
+public class DungeonQualityTooltip extends TooltipAdder {
+ public DungeonQualityTooltip(int priority) {
+ super(priority);
+ }
+
+ @Override
+ public void addToTooltip(List<Text> lore, Slot focusedSlot) {
+ if (!SkyblockerConfigManager.get().general.itemTooltip.dungeonQuality) return;
+ NbtCompound customData = ItemUtils.getCustomData(focusedSlot.getStack());
+ if (customData == null || !customData.contains("baseStatBoostPercentage")) return;
+ int baseStatBoostPercentage = customData.getInt("baseStatBoostPercentage");
+ boolean maxQuality = baseStatBoostPercentage == 50;
+ if (maxQuality) {
+ lore.add(Text.literal(String.format("%-17s", "Item Quality:") + baseStatBoostPercentage + "/50").formatted(Formatting.RED).formatted(Formatting.BOLD));
+ } else {
+ lore.add(Text.literal(String.format("%-21s", "Item Quality:") + baseStatBoostPercentage + "/50").formatted(Formatting.BLUE));
+ }
+
+ if (customData.contains("item_tier")) { // sometimes it just isn't here?
+ int itemTier = customData.getInt("item_tier");
+ if (maxQuality) {
+ lore.add(Text.literal(String.format("%-17s", "Floor Tier:") + itemTier + " (" + getItemTierFloor(itemTier) + ")").formatted(Formatting.RED).formatted(Formatting.BOLD));
+ } else {
+ lore.add(Text.literal(String.format("%-21s", "Floor Tier:") + itemTier + " (" + getItemTierFloor(itemTier) + ")").formatted(Formatting.BLUE));
+ }
+ }
+ }
+
+ final String getItemTierFloor(int tier) {
+ return switch (tier) {
+ case 1 -> "F1";
+ case 2 -> "F2";
+ case 3 -> "F3";
+ case 4 -> "F4/M1";
+ case 5 -> "F5/M2";
+ case 6 -> "F6/M3";
+ case 7 -> "F7/M4";
+ case 8 -> "M5";
+ case 9 -> "M6";
+ case 10 -> "M7";
+ default -> "Unknown";
+ };
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/LBinTooltip.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/LBinTooltip.java
new file mode 100644
index 00000000..f5332215
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/LBinTooltip.java
@@ -0,0 +1,39 @@
+package de.hysky.skyblocker.skyblock.item.tooltip.adders;
+
+import de.hysky.skyblocker.skyblock.item.tooltip.ItemTooltip;
+import de.hysky.skyblocker.skyblock.item.tooltip.TooltipInfoType;
+import net.minecraft.item.ItemStack;
+import net.minecraft.screen.slot.Slot;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+import java.util.List;
+
+public class LBinTooltip extends TooltipAdder {
+ public static boolean lbinExist = false;
+
+ public LBinTooltip(int priority) {
+ super(priority);
+ }
+
+ @Override
+ public void addToTooltip(List<Text> lore, Slot focusedSlot) {
+ lbinExist = false;
+ final ItemStack itemStack = focusedSlot.getStack();
+ final String internalID = ItemTooltip.getInternalNameFromNBT(itemStack, true);
+ if (internalID == null) return;
+ String name = ItemTooltip.getInternalNameFromNBT(itemStack, false);
+ if (name == null) return;
+
+ if (name.startsWith("ISSHINY_")) name = "SHINY_" + internalID;
+
+ // bazaarOpened & bazaarExist check for lbin, because Skytils keeps some bazaar item data in lbin api
+
+ if (TooltipInfoType.LOWEST_BINS.isTooltipEnabledAndHasOrNullWarning(name) && !BazaarPriceTooltip.bazaarExist) {
+ lore.add(Text.literal(String.format("%-19s", "Lowest BIN Price:"))
+ .formatted(Formatting.GOLD)
+ .append(ItemTooltip.getCoinsMessage(TooltipInfoType.LOWEST_BINS.getData().get(name).getAsDouble(), itemStack.getCount())));
+ lbinExist = true;
+ }
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/LineSmoothener.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/LineSmoothener.java
new file mode 100644
index 00000000..b527284a
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/LineSmoothener.java
@@ -0,0 +1,31 @@
+package de.hysky.skyblocker.skyblock.item.tooltip.adders;
+
+import net.minecraft.screen.slot.Slot;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+import java.util.List;
+
+public class LineSmoothener extends TooltipAdder {
+ //This is static to not create a new text object for each line in every item
+ private static final Text BUMPY_LINE = Text.literal("-----------------").formatted(Formatting.DARK_GRAY, Formatting.STRIKETHROUGH);
+
+ public static Text createSmoothLine() {
+ return Text.literal(" ").formatted(Formatting.DARK_GRAY, Formatting.STRIKETHROUGH, Formatting.BOLD);
+ }
+
+ public LineSmoothener() {
+ super(Integer.MIN_VALUE);
+ }
+
+ @Override
+ public void addToTooltip(List<Text> lore, Slot focusedSlot) {
+ for (int i = 0; i < lore.size(); i++) {
+ List<Text> lineSiblings = lore.get(i).getSiblings();
+ //Compare the first sibling rather than the whole object as the style of the root object can change while visually staying the same
+ if (lineSiblings.size() == 1 && lineSiblings.getFirst().equals(BUMPY_LINE)) {
+ lore.set(i, createSmoothLine());
+ }
+ }
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/MotesTooltip.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/MotesTooltip.java
new file mode 100644
index 00000000..23a88d55
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/MotesTooltip.java
@@ -0,0 +1,50 @@
+package de.hysky.skyblocker.skyblock.item.tooltip.adders;
+
+import de.hysky.skyblocker.config.SkyblockerConfigManager;
+import de.hysky.skyblocker.skyblock.item.tooltip.ItemTooltip;
+import de.hysky.skyblocker.skyblock.item.tooltip.TooltipInfoType;
+import net.minecraft.item.ItemStack;
+import net.minecraft.screen.slot.Slot;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+import java.util.List;
+import java.util.Locale;
+
+public class MotesTooltip extends TooltipAdder {
+ public MotesTooltip(int priority) {
+ super(priority);
+ }
+
+ @Override
+ public void addToTooltip(List<Text> lore, Slot focusedSlot) {
+ final ItemStack itemStack = focusedSlot.getStack();
+ final String internalID = ItemTooltip.getInternalNameFromNBT(itemStack, true);
+ if (internalID != null && TooltipInfoType.MOTES.isTooltipEnabledAndHasOrNullWarning(internalID)) {
+ lore.add(Text.literal(String.format("%-20s", "Motes Price:"))
+ .formatted(Formatting.LIGHT_PURPLE)
+ .append(getMotesMessage(TooltipInfoType.MOTES.getData().get(internalID).getAsInt(), itemStack.getCount())));
+ }
+ }
+
+ private static Text getMotesMessage(int price, int count) {
+ float motesMultiplier = SkyblockerConfigManager.get().otherLocations.rift.mcGrubberStacks * 0.05f + 1;
+
+ // Calculate the total price
+ int totalPrice = price * count;
+ String totalPriceString = String.format(Locale.ENGLISH, "%1$,.1f", totalPrice * motesMultiplier);
+
+ // If count is 1, return a simple message
+ if (count == 1) {
+ return Text.literal(totalPriceString.replace(".0", "") + " Motes").formatted(Formatting.DARK_AQUA);
+ }
+
+ // If count is greater than 1, include the "each" information
+ String eachPriceString = String.format(Locale.ENGLISH, "%1$,.1f", price * motesMultiplier);
+ MutableText message = Text.literal(totalPriceString.replace(".0", "") + " Motes ").formatted(Formatting.DARK_AQUA);
+ message.append(Text.literal("(" + eachPriceString.replace(".0", "") + " each)").formatted(Formatting.GRAY));
+
+ return message;
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/MuseumTooltip.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/MuseumTooltip.java
new file mode 100644
index 00000000..ced3fe76
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/MuseumTooltip.java
@@ -0,0 +1,49 @@
+package de.hysky.skyblocker.skyblock.item.tooltip.adders;
+
+import de.hysky.skyblocker.skyblock.item.MuseumItemCache;
+import de.hysky.skyblocker.skyblock.item.tooltip.ItemTooltip;
+import de.hysky.skyblocker.skyblock.item.tooltip.TooltipInfoType;
+import de.hysky.skyblocker.utils.ItemUtils;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NbtCompound;
+import net.minecraft.screen.slot.Slot;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+import java.util.List;
+
+public class MuseumTooltip extends TooltipAdder {
+ public MuseumTooltip(int priority) {
+ super(priority);
+ }
+
+ @Override
+ public void addToTooltip(List<Text> lore, Slot focusedSlot) {
+ final ItemStack itemStack = focusedSlot.getStack();
+ final String internalID = ItemTooltip.getInternalNameFromNBT(itemStack, true);
+ if (TooltipInfoType.MUSEUM.isTooltipEnabledAndHasOrNullWarning(internalID)) {
+ String itemCategory = TooltipInfoType.MUSEUM.getData().get(internalID).getAsString();
+ String format = switch (itemCategory) {
+ case "Weapons" -> "%-18s";
+ case "Armor" -> "%-19s";
+ default -> "%-20s";
+ };
+
+ //Special case the special category so that it doesn't always display not donated
+ if (itemCategory.equals("Special")) {
+ lore.add(Text.literal(String.format(format, "Museum: (" + itemCategory + ")"))
+ .formatted(Formatting.LIGHT_PURPLE));
+ } else {
+ NbtCompound customData = ItemUtils.getCustomData(itemStack);
+ boolean isInMuseum = (customData.contains("donated_museum") && customData.getBoolean("donated_museum")) || MuseumItemCache.hasItemInMuseum(internalID);
+
+ Formatting donatedIndicatorFormatting = isInMuseum ? Formatting.GREEN : Formatting.RED;
+
+ lore.add(Text.literal(String.format(format, "Museum (" + itemCategory + "):"))
+ .formatted(Formatting.LIGHT_PURPLE)
+ .append(Text.literal(isInMuseum ? "✔" : "✖").formatted(donatedIndicatorFormatting, Formatting.BOLD))
+ .append(Text.literal(isInMuseum ? " Donated" : " Not Donated").formatted(donatedIndicatorFormatting)));
+ }
+ }
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/NpcPriceTooltip.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/NpcPriceTooltip.java
new file mode 100644
index 00000000..386e38a5
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/NpcPriceTooltip.java
@@ -0,0 +1,27 @@
+package de.hysky.skyblocker.skyblock.item.tooltip.adders;
+
+import de.hysky.skyblocker.skyblock.item.tooltip.ItemTooltip;
+import de.hysky.skyblocker.skyblock.item.tooltip.TooltipInfoType;
+import net.minecraft.item.ItemStack;
+import net.minecraft.screen.slot.Slot;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+import java.util.List;
+
+public class NpcPriceTooltip extends TooltipAdder {
+ public NpcPriceTooltip(int priority) {
+ super(priority);
+ }
+
+ @Override
+ public void addToTooltip(List<Text> lore, Slot focusedSlot) {
+ final ItemStack stack = focusedSlot.getStack();
+ final String internalID = ItemTooltip.getInternalNameFromNBT(stack, true);
+ if (internalID != null && TooltipInfoType.NPC.isTooltipEnabledAndHasOrNullWarning(internalID)) {
+ lore.add(Text.literal(String.format("%-21s", "NPC Sell Price:"))
+ .formatted(Formatting.YELLOW)
+ .append(ItemTooltip.getCoinsMessage(TooltipInfoType.NPC.getData().get(internalID).getAsDouble(), stack.getCount())));
+ }
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/ObtainedTooltip.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/ObtainedTooltip.java
new file mode 100644
index 00000000..24a31211
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/ObtainedTooltip.java
@@ -0,0 +1,71 @@
+package de.hysky.skyblocker.skyblock.item.tooltip.adders;
+
+import de.hysky.skyblocker.skyblock.item.tooltip.TooltipInfoType;
+import de.hysky.skyblocker.utils.ItemUtils;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NbtCompound;
+import net.minecraft.nbt.NbtElement;
+import net.minecraft.screen.slot.Slot;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.TemporalAccessor;
+import java.util.List;
+import java.util.Locale;
+
+public class ObtainedTooltip extends TooltipAdder {
+ private static final DateTimeFormatter OBTAINED_DATE_FORMATTER = DateTimeFormatter.ofPattern("MMMM d, yyyy").withZone(ZoneId.systemDefault()).localizedBy(Locale.ENGLISH);
+ private static final DateTimeFormatter OLD_OBTAINED_DATE_FORMAT = DateTimeFormatter.ofPattern("M/d/yy h:m a").withZone(ZoneId.of("UTC")).localizedBy(Locale.ENGLISH);
+
+ public ObtainedTooltip(int priority) {
+ super(priority);
+ }
+
+ @Override
+ public void addToTooltip(List<Text> lore, Slot focusedSlot) {
+ if (TooltipInfoType.OBTAINED.isTooltipEnabled()) {
+ String timestamp = getTimestamp(focusedSlot.getStack());
+
+ if (!timestamp.isEmpty()) {
+ lore.add(Text.empty()
+ .append(Text.literal(String.format("%-21s", "Obtained: ")).formatted(Formatting.LIGHT_PURPLE))
+ .append(Text.literal(timestamp).formatted(Formatting.RED)));
+ }
+ }
+ }
+
+ /**
+ * This method converts the "timestamp" variable into the same date format as Hypixel represents it in the museum.
+ * Currently, there are two types of string timestamps the legacy which is built like this
+ * "dd/MM/yy hh:mm" ("25/04/20 16:38") and the current which is built like this
+ * "MM/dd/yy hh:mm aa" ("12/24/20 11:08 PM"). Since Hypixel transforms the two formats into one format without
+ * taking into account of their formats, we do the same. The final result looks like this
+ * "MMMM dd, yyyy" (December 24, 2020).
+ * Since the legacy format has a 25 as "month" SimpleDateFormat converts the 25 into 2 years and 1 month and makes
+ * "25/04/20 16:38" -> "January 04, 2022" instead of "April 25, 2020".
+ * This causes the museum rank to be much worse than it should be.
+ * <p>
+ * This also handles the long timestamp format introduced in January 2024 where the timestamp is in epoch milliseconds.
+ *
+ * @param stack the item under the pointer
+ * @return if the item have a "Timestamp" it will be shown formated on the tooltip
+ */
+ public static String getTimestamp(ItemStack stack) {
+ NbtCompound customData = ItemUtils.getCustomData(stack);
+
+ if (customData != null && customData.contains("timestamp", NbtElement.LONG_TYPE)) {
+ Instant date = Instant.ofEpochMilli(customData.getLong("timestamp"));
+ return OBTAINED_DATE_FORMATTER.format(date);
+ }
+
+ if (customData != null && customData.contains("timestamp", NbtElement.STRING_TYPE)) {
+ TemporalAccessor date = OLD_OBTAINED_DATE_FORMAT.parse(customData.getString("timestamp"));
+ return OBTAINED_DATE_FORMATTER.format(date);
+ }
+
+ return "";
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/TooltipAdder.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/TooltipAdder.java
new file mode 100644
index 00000000..254add09
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/TooltipAdder.java
@@ -0,0 +1,36 @@
+package de.hysky.skyblocker.skyblock.item.tooltip.adders;
+
+import de.hysky.skyblocker.skyblock.item.tooltip.TooltipManager;
+import net.minecraft.screen.slot.Slot;
+import net.minecraft.text.Text;
+
+import java.util.List;
+import java.util.regex.Pattern;
+
+/**
+ * Extend this class and add it to {@link TooltipManager#adders} to add additional text to tooltips.
+ */
+public abstract class TooltipAdder {
+ public final Pattern titlePattern;
+ //Lower priority means it will be applied first
+ public final int priority;
+
+ protected TooltipAdder(String titlePattern, int priority) {
+ this(Pattern.compile(titlePattern), priority);
+ }
+
+ protected TooltipAdder(Pattern titlePattern, int priority) {
+ this.titlePattern = titlePattern;
+ this.priority = priority;
+ }
+
+ /**
+ * Creates a TooltipAdder that will be applied to all screens.
+ */
+ protected TooltipAdder(int priority) {
+ this.titlePattern = null;
+ this.priority = priority;
+ }
+
+ public abstract void addToTooltip(List<Text> lore, Slot focusedSlot);
+}