Skip to content

Commit c42be5d

Browse files
committed
feat: add lowest bin prices, gzip support, petSoulbound field to PetInfo, and bold price lines
1 parent 8b2b940 commit c42be5d

File tree

12 files changed

+326
-43
lines changed

12 files changed

+326
-43
lines changed

src/main/java/com/fix3dll/skyblockaddons/SkyblockAddons.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
import java.time.ZoneId;
6666
import java.time.ZonedDateTime;
6767
import java.util.EnumMap;
68+
import java.util.Map;
6869
import java.util.UUID;
6970
import java.util.concurrent.LinkedBlockingQueue;
7071
import java.util.concurrent.ThreadPoolExecutor;
@@ -141,6 +142,7 @@ public class SkyblockAddons implements ClientModInitializer {
141142
@Setter private MayorJerryData mayorJerryData;
142143
@Setter private BazaarData bazaarData = new BazaarData();
143144
@Setter private ItemsData itemsData = new ItemsData();
145+
@Setter private Map<String, Double> lowestBinData = Map.of();
144146

145147
public SkyblockAddons() {
146148
instance = this;

src/main/java/com/fix3dll/skyblockaddons/core/PetInfo.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,12 @@ public class PetInfo {
3434
private boolean hideRightClick;
3535
@SerializedName("noMove")
3636
private boolean noMove;
37+
@SerializedName("petSoulbound")
38+
private boolean petSoulbound;
3739

3840
public boolean equals(PetInfo other) {
3941
if (other == null) return false;
42+
if (this.petSoulbound != other.petSoulbound) return false;
4043
if (this.active != other.active) return false;
4144
if (!Objects.equals(this.petSkyblockId, other.petSkyblockId)) return false;
4245
if (this.petRarity != other.petRarity) return false;
@@ -47,4 +50,5 @@ public boolean equals(PetInfo other) {
4750

4851
return Objects.equals(this.uniqueId, other.uniqueId); // the last castle
4952
}
50-
}
53+
54+
}

src/main/java/com/fix3dll/skyblockaddons/core/feature/FeatureSetting.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,10 +140,13 @@ public enum FeatureSetting {
140140
BIGGER_WAKE("settings.coloredFishingParticles.biggerWake", Feature.COLORED_FISHING_PARTICLES),
141141
SHOW_ONLY_WHEN_SCORPIUS_IS_MAYOR("settings.showOnlyWhenScorpiusIsMayor", Feature.DARK_AUCTION_TIMER),
142142
ALLOW_CHAT_MESSAGE_COPYING_OUTSIDE_SKYBLOCK("settings.allowChatMessageCopyingOutsideSkyblock", Feature.CHAT_MESSAGE_COPYING),
143+
BOLD_PRICE_LINES("settings.itemPricesInTooltip.boldPriceLines", Feature.ITEM_PRICES_IN_TOOLTIP),
144+
ALWAYS_SHOW_BULK_PRICE("settings.itemPricesInTooltip.alwaysShowBulkPrice", Feature.ITEM_PRICES_IN_TOOLTIP),
143145
NPC_SELL_PRICES_IN_TOOLTIP("settings.itemPricesInTooltip.npcSellPrices", Feature.ITEM_PRICES_IN_TOOLTIP),
146+
LOWEST_BIN_PRICES_IN_TOOLTIP("settings.itemPricesInTooltip.lowestBinPrices", Feature.ITEM_PRICES_IN_TOOLTIP),
144147
BAZAAR_PRICES_IN_TOOLTIP("settings.itemPricesInTooltip.bazaarPrices", Feature.ITEM_PRICES_IN_TOOLTIP),
145-
ALWAYS_SHOW_BULK_PRICE("settings.itemPricesInTooltip.alwaysShowBulkPrice", Feature.ITEM_PRICES_IN_TOOLTIP),
146-
BAZAAR_PRICES_UPDATE_INTERVAL("settings.itemPricesInTooltip.updateInterval", Feature.ITEM_PRICES_IN_TOOLTIP),
148+
LOWEST_BIN_PRICES_UPDATE_INTERVAL("settings.itemPricesInTooltip.lowestBinUpdateInterval", Feature.ITEM_PRICES_IN_TOOLTIP),
149+
BAZAAR_PRICES_UPDATE_INTERVAL("settings.itemPricesInTooltip.bazaarUpdateInterval", Feature.ITEM_PRICES_IN_TOOLTIP),
147150

148151

149152
DISCORD_RP_DETAILS("messages.firstStatus", Feature.DISCORD_RPC),

src/main/java/com/fix3dll/skyblockaddons/gui/screens/SettingsGui.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -396,10 +396,10 @@ private void addButton(FeatureSetting setting, Object settingValue) {
396396
break;
397397

398398
case BAZAAR_PRICES_UPDATE_INTERVAL:
399-
boxWidth = 120;
399+
boxWidth = 150;
400400
x = halfWidth - (boxWidth / 2);
401401
y = getRowHeightSetting(row);
402-
addRenderableWidget(new ButtonText(halfWidth, (int) y - 10, Translations.getMessage("settings.itemPricesInTooltip.updateInterval"), true, ColorCode.GRAY.getColor()));
402+
addRenderableWidget(new ButtonText(halfWidth, (int) y - 10, Translations.getMessage("settings.itemPricesInTooltip.bazaarUpdateInterval"), true, ColorCode.GRAY.getColor()));
403403
row += .1F;
404404
y = getRowHeightSetting(row);
405405
addRenderableWidget(
@@ -412,6 +412,23 @@ private void addButton(FeatureSetting setting, Object settingValue) {
412412
row += .1F;
413413
break;
414414

415+
case LOWEST_BIN_PRICES_UPDATE_INTERVAL:
416+
boxWidth = 150;
417+
x = halfWidth - (boxWidth / 2);
418+
y = getRowHeightSetting(row);
419+
addRenderableWidget(new ButtonText(halfWidth, (int) y - 10, Translations.getMessage("settings.itemPricesInTooltip.lowestBinUpdateInterval"), true, ColorCode.GRAY.getColor()));
420+
row += .1F;
421+
y = getRowHeightSetting(row);
422+
addRenderableWidget(
423+
new ButtonSlider(
424+
x, y, boxWidth, 20,
425+
feature.getAsNumber(setting).floatValue(), 60.0F, 300.0F, 1.0F,
426+
updatedValue -> feature.set(setting, updatedValue)
427+
).setSuffix(" seconds")
428+
);
429+
row += .1F;
430+
break;
431+
415432
default:
416433
if (setting.isUniversal()) return; // see addUniversalButton()
417434

src/main/java/com/fix3dll/skyblockaddons/listeners/NetworkListener.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import com.fix3dll.skyblockaddons.utils.Utils;
1414
import com.fix3dll.skyblockaddons.utils.data.DataUtils;
1515
import com.fix3dll.skyblockaddons.utils.data.requests.BazaarRequest;
16+
import com.fix3dll.skyblockaddons.utils.data.requests.LowestBinRequest;
1617
import com.google.common.cache.Cache;
1718
import com.google.common.cache.CacheBuilder;
1819
import io.netty.channel.ChannelHandlerContext;
@@ -59,6 +60,7 @@ private void onSkyblockJoined() {
5960
);
6061

6162
DataUtils.loadOnlineData(new BazaarRequest());
63+
DataUtils.loadOnlineData(new LowestBinRequest());
6264
DataUtils.onSkyblockJoined();
6365
}
6466

@@ -74,6 +76,7 @@ private void onSkyblockLeft() {
7476
updateHealth = null;
7577
}
7678
BazaarRequest.cancelUpdateTask();
79+
LowestBinRequest.cancelUpdateTask();
7780
}
7881

7982
private void onPacketRead(ChannelHandlerContext channelHandlerContext, Packet<?> packet) {

src/main/java/com/fix3dll/skyblockaddons/listeners/PlayerListener.java

Lines changed: 103 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@
66
import com.fix3dll.skyblockaddons.core.InventoryType;
77
import com.fix3dll.skyblockaddons.core.Island;
88
import com.fix3dll.skyblockaddons.core.ItemType;
9+
import com.fix3dll.skyblockaddons.core.PetInfo;
910
import com.fix3dll.skyblockaddons.core.PlayerStat;
1011
import com.fix3dll.skyblockaddons.core.SkillType;
1112
import com.fix3dll.skyblockaddons.core.SkyblockKeyBinding;
1213
import com.fix3dll.skyblockaddons.core.SkyblockOre;
1314
import com.fix3dll.skyblockaddons.core.SkyblockRarity;
15+
import com.fix3dll.skyblockaddons.core.SkyblockRune;
1416
import com.fix3dll.skyblockaddons.core.Translations;
1517
import com.fix3dll.skyblockaddons.core.feature.Feature;
1618
import com.fix3dll.skyblockaddons.core.feature.FeatureSetting;
@@ -1302,12 +1304,16 @@ private void flyingCheck() {
13021304
*
13031305
* <p>Depending on the enabled {@link FeatureSetting settings}, the following lines may be added:
13041306
* <ul>
1307+
* <li><b>Lowest BIN prices</b> - from <a href="https://moulberry.codes">moulberry.codes</a></li>
13051308
* <li><b>Bazaar buy/sell prices</b> — resolved via the Hypixel Bazaar API.
13061309
* Enchanted books are looked up by their enchantment ID
13071310
* (e.g. {@code ENCHANTMENT_SHARPNESS_5}) rather than {@code ENCHANTED_BOOK}.</li>
13081311
* <li><b>NPC sell price</b> — resolved from the SkyBlock items API.</li>
13091312
* </ul>
13101313
*
1314+
* <p>Special item types (PET, RUNE, NEW_YEAR_CAKE, POTION, perfect stat boost items)
1315+
* are resolved to their API-specific item IDs before lookup.
1316+
*
13111317
* <p>If {@link FeatureSetting#ALWAYS_SHOW_BULK_PRICE} is disabled and
13121318
* {@code LEFT SHIFT} is not held, prices are shown for a single item.
13131319
* When the stack count is greater than one and shift is held, prices are
@@ -1334,15 +1340,106 @@ private void addItemPricesToTooltip(String itemId,
13341340
TooltipFlag tooltipFlag,
13351341
List<Component> components) {
13361342
Feature feature = Feature.ITEM_PRICES_IN_TOOLTIP;
1343+
boolean boldLines = feature.isEnabled(FeatureSetting.BOLD_PRICE_LINES);
13371344
UnaryOperator<Style> textColor = style -> feature.isChroma()
1338-
? style.withColor(DrawUtils.CHROMA_TEXT_COLOR) // TextColor
1339-
: style.withColor(feature.getColor()); // int
1345+
? style.withBold(boldLines).withColor(DrawUtils.CHROMA_TEXT_COLOR) // TextColor
1346+
: style.withBold(boldLines).withColor(feature.getColor()); // int
13401347
boolean lshift = feature.isEnabled(FeatureSetting.ALWAYS_SHOW_BULK_PRICE)
13411348
|| InputConstants.isKeyDown(MC.getWindow(), GLFW.GLFW_KEY_LEFT_SHIFT);
13421349
int count = itemStack.getCount();
13431350
int countToBeShown = lshift ? count : 1;
13441351

13451352
int addedLines = 0;
1353+
1354+
if (feature.isEnabled(FeatureSetting.LOWEST_BIN_PRICES_IN_TOOLTIP)) {
1355+
if (itemId == null) itemId = ItemUtils.getSkyblockItemID(itemStack);
1356+
1357+
String apiItemId = itemId;
1358+
String extraString = null;
1359+
switch (apiItemId) {
1360+
case "PET" -> {
1361+
PetManager.Pet pet = PetManager.getInstance().getPetFromItemStack(itemStack);
1362+
1363+
if (pet != null) {
1364+
PetInfo petInfo = pet.getPetInfo();
1365+
1366+
if (petInfo != null) {
1367+
apiItemId = petInfo.getPetSkyblockId() + ";" + petInfo.getPetRarity().ordinal();
1368+
1369+
int petLevel = pet.getPetLevel();
1370+
if (petLevel != 0 && pet.getPetLevel() % 100 == 0) {
1371+
extraString = "+" + petLevel;
1372+
apiItemId += extraString;
1373+
}
1374+
}
1375+
}
1376+
}
1377+
case "RUNE" -> {
1378+
CompoundTag extraAttributes = ItemUtils.getExtraAttributes(itemStack);
1379+
1380+
if (extraAttributes != null) {
1381+
SkyblockRune rune = ItemUtils.getRuneData(extraAttributes);
1382+
1383+
if (rune != null) {
1384+
apiItemId = rune.getType() + "_RUNE;" + rune.getLevel();
1385+
}
1386+
}
1387+
}
1388+
case "NEW_YEAR_CAKE" -> {
1389+
CompoundTag extraAttributes = ItemUtils.getExtraAttributes(itemStack);
1390+
1391+
if (extraAttributes != null) {
1392+
int cakeYears = extraAttributes.getIntOr("new_years_cake", -1);
1393+
1394+
if (cakeYears != -1) {
1395+
apiItemId += "+" + cakeYears;
1396+
}
1397+
}
1398+
}
1399+
case "POTION" -> {
1400+
CompoundTag extraAttributes = ItemUtils.getExtraAttributes(itemStack);
1401+
1402+
if (extraAttributes != null) {
1403+
String potion = extraAttributes.getStringOr("potion", "");
1404+
int potionLevel = extraAttributes.getIntOr("potion_level", -1);
1405+
1406+
if (!potion.isEmpty() && potionLevel != -1) {
1407+
apiItemId += "_" + potion.toUpperCase(Locale.ENGLISH) + ";" + potionLevel;
1408+
}
1409+
}
1410+
}
1411+
case null -> {
1412+
return;
1413+
}
1414+
default -> {
1415+
CompoundTag extraAttributes = ItemUtils.getExtraAttributes(itemStack);
1416+
1417+
if (extraAttributes != null) {
1418+
int baseStatBoost = extraAttributes.getIntOr("baseStatBoostPercentage", -1);
1419+
1420+
if (baseStatBoost == 50) {
1421+
extraString = "+PERFECT";
1422+
apiItemId += extraString;
1423+
}
1424+
}
1425+
}
1426+
}
1427+
1428+
double lowestBinPrice = main.getLowestBinData().getOrDefault(apiItemId, -1.0D);
1429+
if (extraString != null && lowestBinPrice == -1.0D) {
1430+
lowestBinPrice = main.getLowestBinData().getOrDefault(
1431+
apiItemId.replace(extraString, ""), -1.0D
1432+
);
1433+
}
1434+
1435+
if (lowestBinPrice > 0.0D) {
1436+
Component priceComponent = TextUtils.formatPrice(lowestBinPrice, 0, boldLines);
1437+
1438+
components.add(Component.literal(Translations.getMessage("tooltip.lowestBinPrice"))
1439+
.withStyle(textColor).append(priceComponent));
1440+
}
1441+
}
1442+
13461443
if (feature.isEnabled(FeatureSetting.BAZAAR_PRICES_IN_TOOLTIP)) {
13471444
if (itemId == null) itemId = ItemUtils.getSkyblockItemID(itemStack);
13481445

@@ -1360,8 +1457,8 @@ private void addItemPricesToTooltip(String itemId,
13601457
BazaarData.Product product = main.getBazaarData().getProducts().get(apiItemId);
13611458

13621459
if (product != null) {
1363-
Component buyPrice = TextUtils.formatPrice(product.getInstaBuyPrice() * countToBeShown);
1364-
Component sellPrice = TextUtils.formatPrice(product.getInstaSellPrice() * countToBeShown);
1460+
Component buyPrice = TextUtils.formatPrice(product.getInstaBuyPrice() * countToBeShown, 1, boldLines);
1461+
Component sellPrice = TextUtils.formatPrice(product.getInstaSellPrice() * countToBeShown, 1, boldLines);
13651462

13661463
components.add(Component.literal(Translations.getMessage("tooltip.buyPrice"))
13671464
.withStyle(textColor).append(buyPrice));
@@ -1379,7 +1476,8 @@ private void addItemPricesToTooltip(String itemId,
13791476
ItemsData.Item item = main.getItemsData().getItemMap().get(itemId);
13801477

13811478
if (item != null && item.getNpcSellPrice() != 0.0D) {
1382-
Component npcSellPrice = TextUtils.formatPrice(item.getNpcSellPrice() * countToBeShown);
1479+
double price = item.getNpcSellPrice() * countToBeShown;
1480+
Component npcSellPrice = TextUtils.formatPrice(price, price <= 10.0D ? 2 : 0, boldLines);
13831481

13841482
components.add(Component.literal(Translations.getMessage("tooltip.npcSellPrice"))
13851483
.withStyle(textColor).append(npcSellPrice));

src/main/java/com/fix3dll/skyblockaddons/utils/TextUtils.java

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public class TextUtils {
5252
* style
5353
*/
5454
public static final NumberFormat NUMBER_FORMAT_NO_GROUPING = NumberFormat.getInstance(Locale.US);
55-
public static final NumberFormat COIN_FORMAT = NumberFormat.getInstance(Locale.US);
55+
private static final NumberFormat[] COIN_FORMATS = new NumberFormat[6]; // 0-5
5656

5757
private static final Pattern STRIP_COLOR_PATTERN = Pattern.compile("(?i)§[0-9A-FK-ORZ]");
5858
private static final Pattern STRIP_ICONS_PATTERN = Pattern.compile("\\[✌]|[♲Ⓑ⚒ቾ]+");
@@ -79,8 +79,11 @@ public class TextUtils {
7979
NUMBER_FORMAT.setMaximumFractionDigits(2);
8080
NUMBER_FORMAT_NO_GROUPING.setMaximumFractionDigits(2);
8181
NUMBER_FORMAT_NO_GROUPING.setGroupingUsed(false);
82-
COIN_FORMAT.setMaximumFractionDigits(1);
83-
COIN_FORMAT.setMinimumFractionDigits(1);
82+
for (int i = 0; i < COIN_FORMATS.length; i++) {
83+
COIN_FORMATS[i] = NumberFormat.getInstance(Locale.US);
84+
COIN_FORMATS[i].setMaximumFractionDigits(i);
85+
COIN_FORMATS[i].setMinimumFractionDigits(i);
86+
}
8487
}
8588

8689
/** For test environment */
@@ -106,22 +109,31 @@ public static String formatNumber(Number number) {
106109
}
107110

108111
/**
109-
* Formats a coin value with US thousands separator and one decimal place,
110-
* e.g. {@code 123456.0} → {@code "123,456.0"}.
112+
* Formats a coin value with US thousands separator and the given number of decimal places,
113+
* e.g. {@code formatCoin(123456.0, 1)} → {@code "123,456.0"}, {@code formatCoin(123456.0, 0)} → {@code "123,456"}.
114+
* Supports 0–5 decimal places.
115+
* @param number the coin value to format
116+
* @param decimals the number of decimal places (0–5)
117+
* @return formatted coin string
111118
* @since 2.2.3
112119
*/
113-
public static String formatCoin(Number number) {
114-
return COIN_FORMAT.format(number);
120+
public static String formatCoin(Number number, int decimals) {
121+
return COIN_FORMATS[decimals].format(number);
115122
}
116123

117124
/**
118-
* Returns a yellow formatted coin value, or a red localized {@code "None"} if the price is {@code -1}.
125+
* Returns a gold formatted coin value, or a red localized {@code "None"} if the price is negative.
126+
* @param price the coin value to format
127+
* @param decimal the number of decimal places (0–5), passed to {@link #formatCoin(Number, int)}
128+
* @return a gold {@link Component} with the formatted price, or a red {@link Component} with {@code "None"}
119129
* @since 2.2.3
120130
*/
121-
public static Component formatPrice(double price) {
122-
return price < 0
123-
? Component.literal(Translations.getMessage("tooltip.none")).withColor(ColorCode.RED.getColor())
124-
: Component.literal(TextUtils.formatCoin(price)).withColor(ColorCode.GOLD.getColor());
131+
public static Component formatPrice(double price, int decimal, boolean bold) {
132+
return price < 0.0D
133+
? Component.literal(Translations.getMessage("tooltip.none"))
134+
.withStyle(style -> style.withBold(bold).withColor(ColorCode.RED.getColor()))
135+
: Component.literal(TextUtils.formatCoin(price, decimal) + " coins")
136+
.withStyle(style -> style.withBold(bold).withColor(ColorCode.GOLD.getColor()));
125137
}
126138

127139
/**

0 commit comments

Comments
 (0)