Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Farming hud improvements #685

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 18 additions & 44 deletions src/main/java/de/hysky/skyblocker/skyblock/ChestValue.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,11 @@
import de.hysky.skyblocker.mixins.accessors.HandledScreenAccessor;
import de.hysky.skyblocker.mixins.accessors.ScreenAccessor;
import de.hysky.skyblocker.skyblock.item.tooltip.ItemTooltip;
import de.hysky.skyblocker.skyblock.item.tooltip.TooltipInfoType;
import de.hysky.skyblocker.utils.ItemUtils;
import de.hysky.skyblocker.utils.Utils;
import it.unimi.dsi.fastutil.longs.LongBooleanPair;
import it.unimi.dsi.fastutil.doubles.DoubleBooleanPair;
import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents;
import net.fabricmc.fabric.api.client.screen.v1.Screens;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.ingame.GenericContainerScreen;
import net.minecraft.client.gui.tooltip.Tooltip;
import net.minecraft.client.gui.widget.ButtonWidget;
Expand Down Expand Up @@ -45,7 +43,7 @@ public static void init() {
if (DUNGEON_CHESTS.contains(titleString)) {
if (SkyblockerConfigManager.get().dungeons.dungeonChestProfit.enableProfitCalculator) {
ScreenEvents.afterTick(screen).register(screen_ ->
((ScreenAccessor) screen).setTitle(getDungeonChestProfit(genericContainerScreen.getScreenHandler(), title, titleString, client))
((ScreenAccessor) screen).setTitle(getDungeonChestProfit(genericContainerScreen.getScreenHandler(), title, titleString))
);
}
} else if (SkyblockerConfigManager.get().uiAndVisuals.chestValue.enableChestValue && !titleString.equals("SkyBlock Menu")) {
Expand All @@ -65,9 +63,9 @@ public static void init() {
});
}

private static Text getDungeonChestProfit(GenericContainerScreenHandler handler, Text title, String titleString, MinecraftClient client) {
private static Text getDungeonChestProfit(GenericContainerScreenHandler handler, Text title, String titleString) {
try {
long profit = 0;
double profit = 0;
boolean hasIncompleteData = false, usedKismet = false;
List<Slot> slots = handler.slots.subList(0, handler.getRows() * 9);

Expand All @@ -85,12 +83,12 @@ private static Text getDungeonChestProfit(GenericContainerScreenHandler handler,

//Regular item price
if (id != null) {
LongBooleanPair priceData = getItemPrice(id);
DoubleBooleanPair priceData = ItemUtils.getItemPrice(id);

if (!priceData.rightBoolean()) hasIncompleteData = true;

//Add the item price to the profit
profit += priceData.leftLong() * stack.getCount();
profit += priceData.leftDouble() * stack.getCount();

continue;
}
Expand All @@ -103,20 +101,20 @@ private static Text getDungeonChestProfit(GenericContainerScreenHandler handler,
String type = matcher.group("type");
int amount = Integer.parseInt(matcher.group("amount"));

LongBooleanPair priceData = getItemPrice(("ESSENCE_" + type).toUpperCase());
DoubleBooleanPair priceData = ItemUtils.getItemPrice(("ESSENCE_" + type).toUpperCase());

if (!priceData.rightBoolean()) hasIncompleteData = true;

//Add the price of the essence to the profit
profit += priceData.leftLong() * amount;
profit += priceData.leftDouble() * amount;

continue;
}
}

//Determine the cost of the chest
if (name.contains("Open Reward Chest")) {
String foundString = searchLoreFor(stack, client, "Coins");
String foundString = searchLoreFor(stack, "Coins");

//Incase we're searching the free chest
if (!StringUtils.isBlank(foundString)) {
Expand All @@ -128,19 +126,19 @@ private static Text getDungeonChestProfit(GenericContainerScreenHandler handler,

//Determine if a kismet was used or not
if (name.contains("Reroll Chest")) {
usedKismet = !StringUtils.isBlank(searchLoreFor(stack, client, "You already rerolled a chest!"));
usedKismet = !StringUtils.isBlank(searchLoreFor(stack, "You already rerolled a chest!"));
}
}

if (SkyblockerConfigManager.get().dungeons.dungeonChestProfit.includeKismet && usedKismet) {
LongBooleanPair kismetPriceData = getItemPrice("KISMET_FEATHER");
DoubleBooleanPair kismetPriceData = ItemUtils.getItemPrice("KISMET_FEATHER");

if (!kismetPriceData.rightBoolean()) hasIncompleteData = true;

profit -= kismetPriceData.leftLong();
profit -= kismetPriceData.leftDouble();
}

return Text.literal(titleString).append(getProfitText(profit, hasIncompleteData));
return Text.literal(titleString).append(getProfitText((long) profit, hasIncompleteData));
} catch (Exception e) {
LOGGER.error("[Skyblocker Profit Calculator] Failed to calculate dungeon chest profit! ", e);
}
Expand All @@ -150,7 +148,7 @@ private static Text getDungeonChestProfit(GenericContainerScreenHandler handler,

private static Text getChestValue(GenericContainerScreenHandler handler, Text title, String titleString) {
try {
long value = 0;
double value = 0;
boolean hasIncompleteData = false;
List<Slot> slots = handler.slots.subList(0, handler.getRows() * 9);

Expand All @@ -163,50 +161,26 @@ private static Text getChestValue(GenericContainerScreenHandler handler, Text ti
String id = ItemTooltip.getInternalNameFromNBT(stack, false);

if (id != null) {
LongBooleanPair priceData = getItemPrice(id);
DoubleBooleanPair priceData = ItemUtils.getItemPrice(id);

if (!priceData.rightBoolean()) hasIncompleteData = true;

value += priceData.leftLong() * stack.getCount();
value += priceData.leftDouble() * stack.getCount();
}
}

return Text.literal(titleString).append(getValueText(value, hasIncompleteData));
return Text.literal(titleString).append(getValueText((long) value, hasIncompleteData));
} catch (Exception e) {
LOGGER.error("[Skyblocker Value Calculator] Failed to calculate dungeon chest value! ", e);
}

return title;
}

/**
* @return An {@link LongBooleanPair} with the {@code left long} representing the item's price, and the {@code right boolean} indicating if the price
* was based on complete data.
*/
private static LongBooleanPair getItemPrice(String id) {
JsonObject bazaarPrices = TooltipInfoType.BAZAAR.getData();
JsonObject lbinPrices = TooltipInfoType.LOWEST_BINS.getData();

if (bazaarPrices == null || lbinPrices == null) return LongBooleanPair.of(0L, false);

if (bazaarPrices.has(id)) {
JsonObject item = bazaarPrices.get(id).getAsJsonObject();
boolean isPriceNull = item.get("sellPrice").isJsonNull();

return LongBooleanPair.of(isPriceNull ? 0L : (long) item.get("sellPrice").getAsDouble(), !isPriceNull);
}

if (lbinPrices.has(id)) {
return LongBooleanPair.of((long) lbinPrices.get(id).getAsDouble(), true);
}

return LongBooleanPair.of(0L, false);
}

/**
* Searches for a specific string of characters in the name and lore of an item
*/
private static String searchLoreFor(ItemStack stack, MinecraftClient client, String searchString) {
private static String searchLoreFor(ItemStack stack, String searchString) {
return ItemUtils.getLoreLineIf(stack, line -> line.contains(searchString));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,13 @@ protected boolean isEnabled() {
protected List<ColorHighlight> getColors(String[] groups, Int2ObjectMap<ItemStack> slots) {
List<ColorHighlight> highlights = new ArrayList<>();
ItemStack bestChest = null, secondBestChest = null;
long bestValue = 0, secondBestValue = 0; // If negative value of chest - it is out of the question
long dungeonKeyPriceData = getItemPrice("DUNGEON_CHEST_KEY") * 2; // lesser ones don't worth the hassle
double bestValue = 0, secondBestValue = 0; // If negative value of chest - it is out of the question
double dungeonKeyPriceData = getItemPrice("DUNGEON_CHEST_KEY") * 2; // lesser ones don't worth the hassle

for (Int2ObjectMap.Entry<ItemStack> entry : slots.int2ObjectEntrySet()) {
ItemStack stack = entry.getValue();
if (stack.getName().getString().contains("Chest")) {
long value = valueChest(stack);
double value = valueChest(stack);
if (value > bestValue) {
secondBestChest = bestChest;
secondBestValue = bestValue;
Expand All @@ -68,8 +68,8 @@ protected List<ColorHighlight> getColors(String[] groups, Int2ObjectMap<ItemStac
}


private long valueChest(@NotNull ItemStack chest) {
long chestValue = 0;
private double valueChest(@NotNull ItemStack chest) {
double chestValue = 0;
int chestPrice = 0;
List<String> chestItems = new ArrayList<>();

Expand Down Expand Up @@ -107,22 +107,8 @@ private long valueChest(@NotNull ItemStack chest) {
}


private long getItemPrice(String itemDisplayName) {
JsonObject bazaarPrices = TooltipInfoType.BAZAAR.getData();
JsonObject lbinPrices = TooltipInfoType.LOWEST_BINS.getData();
long itemValue = 0;
String id = dungeonDropsNameToId.get(itemDisplayName);

if (bazaarPrices == null || lbinPrices == null) return 0;

if (bazaarPrices.has(id)) {
JsonObject item = bazaarPrices.get(id).getAsJsonObject();
boolean isPriceNull = item.get("sellPrice").isJsonNull();
return (isPriceNull ? 0L : item.get("sellPrice").getAsLong());
} else if (lbinPrices.has(id)) {
return lbinPrices.get(id).getAsLong();
}
return itemValue;
private double getItemPrice(String itemDisplayName) {
return ItemUtils.getItemPrice(dungeonDropsNameToId.get(itemDisplayName)).leftDouble();
}


Expand Down
55 changes: 41 additions & 14 deletions src/main/java/de/hysky/skyblocker/skyblock/garden/FarmingHud.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
import net.fabricmc.fabric.api.event.client.player.ClientPlayerBlockBreakEvents;
import net.minecraft.client.MinecraftClient;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtElement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -33,8 +35,9 @@
public class FarmingHud {
private static final Logger LOGGER = LoggerFactory.getLogger(FarmingHud.class);
public static final NumberFormat NUMBER_FORMAT = NumberFormat.getInstance(Locale.US);
private static final Pattern COUNTER = Pattern.compile("Counter: (?<count>[\\d,]+) .+");
private static final Pattern FARMING_XP = Pattern.compile("§3\\+(?<xp>\\d+.?\\d*) Farming \\((?<percent>[\\d,]+.?\\d*)%\\)");
private static final MinecraftClient client = MinecraftClient.getInstance();
private static CounterType counterType = CounterType.NONE;
private static final Deque<IntLongPair> counter = new ArrayDeque<>();
private static final LongPriorityQueue blockBreaks = new LongArrayFIFOQueue();
private static final Queue<FloatLongPair> farmingXp = new ArrayDeque<>();
Expand All @@ -43,7 +46,7 @@ public class FarmingHud {
public static void init() {
HudRenderEvents.AFTER_MAIN_HUD.register((context, tickDelta) -> {
if (shouldRender()) {
if (!counter.isEmpty() && counter.peek().rightLong() + 10_000 < System.currentTimeMillis()) {
if (!counter.isEmpty() && counter.peek().rightLong() + 5000 < System.currentTimeMillis()) {
counter.poll();
}
if (!blockBreaks.isEmpty() && blockBreaks.firstLong() + 1000 < System.currentTimeMillis()) {
Expand All @@ -53,17 +56,9 @@ public static void init() {
farmingXp.poll();
}

ItemStack stack = MinecraftClient.getInstance().player.getMainHandStack();
Matcher matcher = ItemUtils.getLoreLineIfMatch(stack, FarmingHud.COUNTER);
if (matcher != null) {
try {
int count = NUMBER_FORMAT.parse(matcher.group("count")).intValue();
if (counter.isEmpty() || counter.peekLast().leftInt() != count) {
counter.offer(IntLongPair.of(count, System.currentTimeMillis()));
}
} catch (ParseException e) {
LOGGER.error("[Skyblocker Farming HUD] Failed to parse counter", e);
}
ItemStack stack = client.player.getMainHandStack();
if (stack == null || !tryGetCounter(stack, CounterType.CULTIVATING) && !tryGetCounter(stack, CounterType.COUNTER)) {
counterType = CounterType.NONE;
}

FarmingHudWidget.INSTANCE.update();
Expand Down Expand Up @@ -92,8 +87,26 @@ public static void init() {
.executes(Scheduler.queueOpenScreenCommand(() -> new FarmingHudConfigScreen(null)))))));
}

private static boolean tryGetCounter(ItemStack stack, CounterType counterType) {
NbtCompound customData = ItemUtils.getCustomData(stack);
if (customData == null || !customData.contains(counterType.nbtKey, NbtElement.NUMBER_TYPE)) return false;
int count = customData.getInt(counterType.nbtKey);
if (FarmingHud.counterType != counterType) {
counter.clear();
FarmingHud.counterType = counterType;
}
if (counter.isEmpty() || counter.peekLast().leftInt() != count) {
counter.offer(IntLongPair.of(count, System.currentTimeMillis()));
}
return true;
}

private static boolean shouldRender() {
return SkyblockerConfigManager.get().farming.garden.farmingHud.enableHud && Utils.getLocation() == Location.GARDEN;
return SkyblockerConfigManager.get().farming.garden.farmingHud.enableHud && client.player != null && Utils.getLocation() == Location.GARDEN;
}

public static String counterText() {
return counterType.text;
}

public static int counter() {
Expand All @@ -120,4 +133,18 @@ public static float farmingXpPercentProgress() {
public static double farmingXpPerHour() {
return farmingXp.stream().mapToDouble(FloatLongPair::leftFloat).sum() * blockBreaks() * 1800; // Hypixel only sends xp updates around every half a second
}

public enum CounterType {
NONE("", "No Counter: "),
COUNTER("mined_crops", "Counter: "),
CULTIVATING("farmed_cultivating", "Cultivating Counter: ");

private final String nbtKey;
private final String text;

CounterType(String nbtKey, String text) {
this.nbtKey = nbtKey;
this.text = text;
}
}
}
Loading