aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorupfault.lol <johian2004@gmail.com>2025-03-02 22:08:45 -0600
committerGitHub <noreply@github.com>2025-03-03 12:08:45 +0800
commit0d0aae6b88b34d7f85ba397eea2469fb4a95a51e (patch)
tree305dfaaf2ae59cbe7ec0ab176b1514613bc17ad0 /src
parente428f5aca33d42bf35f6ee0dc8edc8df2cc753b3 (diff)
downloadSkyblocker-0d0aae6b88b34d7f85ba397eea2469fb4a95a51e.tar.gz
Skyblocker-0d0aae6b88b34d7f85ba397eea2469fb4a95a51e.tar.bz2
Skyblocker-0d0aae6b88b34d7f85ba397eea2469fb4a95a51e.zip
Garden Tweaks & Features (#1118)
* Sum visitors with alike crops, fix Farming XP/h regex, and improve precision for XP/h and Blocks/s * Added Show Stacks in Visitor Helper * Re-added the drawItemEntry into drawScreen * VacuumSolver, VisitorHelper, PestHighlighter * Forgot to fix highlight flicker in VacuumSolver * removed imports from MBB * Updated Regex & MayorUtils catch block * Forgot to add the visitor head and name coloring back into the render method during some tests * Refactor visitor package name * Clean up visitor helper and garden * Clean up visitor helper some more * Fix visitor requirements * Fix block breaks * Use primitive types and fixes * Missed a primitive * Remove vacuum solver * Fix amount parsing --------- Co-authored-by: Kevinthegreat <92656833+kevinthegreat1@users.noreply.github.com>
Diffstat (limited to 'src')
-rw-r--r--src/main/java/de/hysky/skyblocker/config/categories/CrimsonIsleCategory.java4
-rw-r--r--src/main/java/de/hysky/skyblocker/config/categories/FarmingCategory.java44
-rw-r--r--src/main/java/de/hysky/skyblocker/config/configs/FarmingConfig.java18
-rw-r--r--src/main/java/de/hysky/skyblocker/mixins/HandledScreenMixin.java21
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/entity/MobBoundingBoxes.java2
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/entity/MobGlow.java39
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/garden/FarmingHud.java241
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/garden/FarmingHudWidget.java6
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/garden/VisitorHelper.java224
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/garden/visitor/Visitor.java16
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/garden/visitor/VisitorHelper.java256
-rw-r--r--src/main/java/de/hysky/skyblocker/utils/Formatters.java30
-rw-r--r--src/main/java/de/hysky/skyblocker/utils/ItemUtils.java2
-rw-r--r--src/main/java/de/hysky/skyblocker/utils/Utils.java5
-rw-r--r--src/main/java/de/hysky/skyblocker/utils/mayor/MayorUtils.java17
-rw-r--r--src/main/java/de/hysky/skyblocker/utils/render/RenderHelper.java2
-rw-r--r--src/main/resources/assets/skyblocker/lang/en_us.json11
-rw-r--r--src/test/java/de/hysky/skyblocker/utils/FormattersTest.java7
18 files changed, 561 insertions, 384 deletions
diff --git a/src/main/java/de/hysky/skyblocker/config/categories/CrimsonIsleCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/CrimsonIsleCategory.java
index 242bfd24..5a210017 100644
--- a/src/main/java/de/hysky/skyblocker/config/categories/CrimsonIsleCategory.java
+++ b/src/main/java/de/hysky/skyblocker/config/categories/CrimsonIsleCategory.java
@@ -36,8 +36,8 @@ public class CrimsonIsleCategory {
.build())
.option(Option.<Waypoint.Type>createBuilder()
.name(Text.translatable("skyblocker.config.crimsonIsle.kuudra.suppliesAndFuelWaypointType"))
- .description(OptionDescription.of(Text.translatable("skyblocker.config.dungeons.secretWaypoints.waypointType.@Tooltip"),
- Text.translatable("skyblocker.config.dungeons.secretWaypoints.waypointType.generalNote")))
+ .description(OptionDescription.of(Text.translatable("skyblocker.config.uiAndVisuals.waypoints.waypointType.@Tooltip"),
+ Text.translatable("skyblocker.config.uiAndVisuals.waypoints.waypointType.generalNote")))
.binding(defaults.crimsonIsle.kuudra.suppliesAndFuelWaypointType,
() -> config.crimsonIsle.kuudra.suppliesAndFuelWaypointType,
newValue -> config.crimsonIsle.kuudra.suppliesAndFuelWaypointType = newValue)
diff --git a/src/main/java/de/hysky/skyblocker/config/categories/FarmingCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/FarmingCategory.java
index 929256c6..9b90c096 100644
--- a/src/main/java/de/hysky/skyblocker/config/categories/FarmingCategory.java
+++ b/src/main/java/de/hysky/skyblocker/config/categories/FarmingCategory.java
@@ -38,13 +38,14 @@ public class FarmingCategory {
newValue -> config.farming.garden.dicerTitlePrevent = newValue)
.controller(ConfigUtils::createBooleanController)
.build())
- .option(Option.<Boolean>createBuilder()
- .name(Text.translatable("skyblocker.config.farming.garden.visitorHelper"))
- .binding(defaults.farming.garden.visitorHelper,
- () -> config.farming.garden.visitorHelper,
- newValue -> config.farming.garden.visitorHelper = newValue)
- .controller(ConfigUtils::createBooleanController)
- .build())
+ .option(Option.<Boolean>createBuilder()
+ .name(Text.translatable("skyblocker.config.farming.garden.pestHighlighter"))
+ .description(OptionDescription.of(Text.translatable("skyblocker.config.farming.garden.pestHighlighter.@Tooltip")))
+ .binding(defaults.farming.garden.pestHighlighter,
+ () -> config.farming.garden.pestHighlighter,
+ newValue -> config.farming.garden.pestHighlighter = newValue)
+ .controller(ConfigUtils::createBooleanController)
+ .build())
.option(Option.<Boolean>createBuilder()
.name(Text.translatable("skyblocker.config.farming.garden.lockMouseTool"))
.binding(defaults.farming.garden.lockMouseTool,
@@ -76,6 +77,35 @@ public class FarmingCategory {
.controller(ConfigUtils::createBooleanController)
.build())
.build())
+ .group(OptionGroup.createBuilder()
+ .name(Text.translatable("skyblocker.config.farming.visitorHelper"))
+ .collapsed(false)
+ .option(Option.<Boolean>createBuilder()
+ .name(Text.translatable("skyblocker.config.farming.visitorHelper.visitorHelper"))
+ .description(OptionDescription.of(Text.translatable("skyblocker.config.farming.visitorHelper.visitorHelper.@Tooltip")))
+ .binding(defaults.farming.visitorHelper.visitorHelper,
+ () -> config.farming.visitorHelper.visitorHelper,
+ newValue -> config.farming.visitorHelper.visitorHelper = newValue)
+ .controller(ConfigUtils::createBooleanController)
+ .build())
+ .option(Option.<Boolean>createBuilder()
+ .name(Text.translatable("skyblocker.config.farming.visitorHelper.visitorHelperGardenOnly"))
+ .description(OptionDescription.of(Text.translatable("skyblocker.config.farming.visitorHelper.visitorHelperGardenOnly.@Tooltip")))
+ .binding(defaults.farming.visitorHelper.visitorHelperGardenOnly,
+ () -> config.farming.visitorHelper.visitorHelperGardenOnly,
+ newValue -> config.farming.visitorHelper.visitorHelperGardenOnly = newValue)
+ .controller(ConfigUtils::createBooleanController)
+ .build())
+ .option(Option.<Boolean>createBuilder()
+ .name(Text.translatable("skyblocker.config.farming.visitorHelper.showStacksInVisitorHelper"))
+ .description(OptionDescription.of(Text.translatable("skyblocker.config.farming.visitorHelper.showStacksInVisitorHelper.@Tooltip")))
+ .binding(defaults.farming.visitorHelper.showStacksInVisitorHelper,
+ () -> config.farming.visitorHelper.showStacksInVisitorHelper,
+ newValue -> config.farming.visitorHelper.showStacksInVisitorHelper = newValue)
+ .controller(ConfigUtils::createBooleanController)
+ .build())
+ .build())
.build();
+
}
}
diff --git a/src/main/java/de/hysky/skyblocker/config/configs/FarmingConfig.java b/src/main/java/de/hysky/skyblocker/config/configs/FarmingConfig.java
index 568165a7..88506bfe 100644
--- a/src/main/java/de/hysky/skyblocker/config/configs/FarmingConfig.java
+++ b/src/main/java/de/hysky/skyblocker/config/configs/FarmingConfig.java
@@ -7,6 +7,9 @@ public class FarmingConfig {
@SerialEntry
public Garden garden = new Garden();
+ @SerialEntry
+ public VisitorHelper visitorHelper = new VisitorHelper();
+
public static class Garden {
@SerialEntry
public FarmingHud farmingHud = new FarmingHud();
@@ -14,8 +17,8 @@ public class FarmingConfig {
@SerialEntry
public boolean dicerTitlePrevent = true;
- @SerialEntry
- public boolean visitorHelper = true;
+ @SerialEntry
+ public boolean pestHighlighter = true;
@SerialEntry
public boolean lockMouseTool = false;
@@ -30,6 +33,17 @@ public class FarmingConfig {
public boolean closeScreenOnPlotClick = false;
}
+ public static class VisitorHelper {
+ @SerialEntry
+ public boolean visitorHelper = true;
+
+ @SerialEntry
+ public boolean visitorHelperGardenOnly = true;
+
+ @SerialEntry
+ public boolean showStacksInVisitorHelper = false;
+ }
+
public static class FarmingHud {
@SerialEntry
public boolean enableHud = true;
diff --git a/src/main/java/de/hysky/skyblocker/mixins/HandledScreenMixin.java b/src/main/java/de/hysky/skyblocker/mixins/HandledScreenMixin.java
index 426f45df..1b6842f6 100644
--- a/src/main/java/de/hysky/skyblocker/mixins/HandledScreenMixin.java
+++ b/src/main/java/de/hysky/skyblocker/mixins/HandledScreenMixin.java
@@ -2,7 +2,6 @@ package de.hysky.skyblocker.mixins;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import com.llamalad7.mixinextras.sugar.Local;
-
import com.mojang.blaze3d.systems.RenderSystem;
import de.hysky.skyblocker.config.SkyblockerConfig;
import de.hysky.skyblocker.config.SkyblockerConfigManager;
@@ -11,7 +10,7 @@ import de.hysky.skyblocker.skyblock.PetCache;
import de.hysky.skyblocker.skyblock.experiment.ExperimentSolver;
import de.hysky.skyblocker.skyblock.experiment.SuperpairsSolver;
import de.hysky.skyblocker.skyblock.experiment.UltrasequencerSolver;
-import de.hysky.skyblocker.skyblock.garden.VisitorHelper;
+import de.hysky.skyblocker.skyblock.garden.visitor.VisitorHelper;
import de.hysky.skyblocker.skyblock.item.*;
import de.hysky.skyblocker.skyblock.item.slottext.SlotTextManager;
import de.hysky.skyblocker.skyblock.item.tooltip.BackpackPreview;
@@ -136,13 +135,6 @@ public abstract class HandledScreenMixin<T extends ScreenHandler> extends Screen
}
}
- @Inject(at = @At("HEAD"), method = "mouseClicked")
- public void skyblocker$mouseClicked(double mouseX, double mouseY, int button, CallbackInfoReturnable<Boolean> cir) {
- if (SkyblockerConfigManager.get().farming.garden.visitorHelper && (Utils.getLocationRaw().equals("garden") && !getTitle().getString().contains("Logbook") || getTitle().getString().startsWith("Bazaar"))) {
- VisitorHelper.onMouseClicked(mouseX, mouseY, button, this.textRenderer);
- }
- }
-
@ModifyExpressionValue(method = "mouseClicked", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/Screen;mouseClicked(DDI)Z"))
public boolean skyblocker$passThroughSearchFieldUnfocusedClicks(boolean superClicked, double mouseX, double mouseY, int button) {
//Handle Search Field clicks - as of 1.21.4 the game will only send clicks to the selected element rather than trying to send one to each and stopping when the first returns true (if any).
@@ -224,6 +216,7 @@ public abstract class HandledScreenMixin<T extends ScreenHandler> extends Screen
/**
* Avoids getting currentSolver again when it's already in the scope for some usages of this method.
+ *
* @see #skyblocker$experimentSolvers$getStack(Slot, ItemStack, ContainerSolver)
*/
@Unique
@@ -297,8 +290,7 @@ public abstract class HandledScreenMixin<T extends ScreenHandler> extends Screen
switch (this.handler) {
case GenericContainerScreenHandler genericContainerScreenHandler when genericContainerScreenHandler.getRows() == 6 -> {
- VisitorHelper.onSlotClick(slot, slotId, title, genericContainerScreenHandler.getSlot(13).getStack());
-
+ VisitorHelper.onSlotClick(slot, slotId, title);
// Prevent selling to NPC shops
ItemStack sellStack = this.handler.slots.get(49).getStack();
if (sellStack.getName().getString().equals("Sell Item") || ItemUtils.getLoreLineIf(sellStack, text -> text.contains("buyback")) != null) {
@@ -329,6 +321,13 @@ public abstract class HandledScreenMixin<T extends ScreenHandler> extends Screen
}
}
+ @Inject(at = @At("HEAD"), method = "mouseClicked")
+ public void skyblocker$mouseClicked(double mouseX, double mouseY, int button, CallbackInfoReturnable<Boolean> cir) {
+ if (VisitorHelper.shouldRender()) {
+ VisitorHelper.handleMouseClick(mouseX, mouseY, button, this.textRenderer);
+ }
+ }
+
@Inject(method = "drawSlot", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;drawItem(Lnet/minecraft/item/ItemStack;III)V"))
private void skyblocker$drawOnItem(DrawContext context, Slot slot, CallbackInfo ci) {
if (Utils.isOnSkyblock() && SkyblockerConfigManager.get().general.itemInfoDisplay.itemRarityBackgrounds)
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/entity/MobBoundingBoxes.java b/src/main/java/de/hysky/skyblocker/skyblock/entity/MobBoundingBoxes.java
index 65ad6918..d5081668 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/entity/MobBoundingBoxes.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/entity/MobBoundingBoxes.java
@@ -50,7 +50,7 @@ public class MobBoundingBoxes {
return false;
}
-
+
public static float[] getBoxColor(Entity entity) {
int color = MobGlow.getMobGlow(entity);
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/entity/MobGlow.java b/src/main/java/de/hysky/skyblocker/skyblock/entity/MobGlow.java
index 91469c38..76930bd8 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/entity/MobGlow.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/entity/MobGlow.java
@@ -33,7 +33,7 @@ import net.minecraft.util.Formatting;
import net.minecraft.util.math.Box;
import net.minecraft.world.World;
-import java.util.List;
+import java.util.*;
public class MobGlow {
public static final int NO_GLOW = 0;
@@ -42,6 +42,31 @@ public class MobGlow {
*/
private static final String NUKEKUBI_HEAD_TEXTURE = "eyJ0aW1lc3RhbXAiOjE1MzQ5NjM0MzU5NjIsInByb2ZpbGVJZCI6ImQzNGFhMmI4MzFkYTRkMjY5NjU1ZTMzYzE0M2YwOTZjIiwicHJvZmlsZU5hbWUiOiJFbmRlckRyYWdvbiIsInNpZ25hdHVyZVJlcXVpcmVkIjp0cnVlLCJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZWIwNzU5NGUyZGYyNzM5MjFhNzdjMTAxZDBiZmRmYTExMTVhYmVkNWI5YjIwMjllYjQ5NmNlYmE5YmRiYjRiMyJ9fX0=";
private static final String FEL_HEAD_TEXTURE = "ewogICJ0aW1lc3RhbXAiIDogMTcyMDAyNTQ4Njg2MywKICAicHJvZmlsZUlkIiA6ICIzZDIxZTYyMTk2NzQ0Y2QwYjM3NjNkNTU3MWNlNGJlZSIsCiAgInByb2ZpbGVOYW1lIiA6ICJTcl83MUJsYWNrYmlyZCIsCiAgInNpZ25hdHVyZVJlcXVpcmVkIiA6IHRydWUsCiAgInRleHR1cmVzIiA6IHsKICAgICJTS0lOIiA6IHsKICAgICAgInVybCIgOiAiaHR0cDovL3RleHR1cmVzLm1pbmVjcmFmdC5uZXQvdGV4dHVyZS9jMjg2ZGFjYjBmMjE0NGQ3YTQxODdiZTM2YmJhYmU4YTk4ODI4ZjdjNzlkZmY1Y2UwMTM2OGI2MzAwMTU1NjYzIiwKICAgICAgIm1ldGFkYXRhIiA6IHsKICAgICAgICAibW9kZWwiIDogInNsaW0iCiAgICAgIH0KICAgIH0KICB9Cn0=";
+ private static final Set<String> PEST_HEAD_TEXTURES = Set.of(
+ // Mosquito
+ "ewogICJ0aW1lc3RhbXAiIDogMTY5Njk0NTAyOTQ2MSwKICAicHJvZmlsZUlkIiA6ICI3NTE0NDQ4MTkxZTY0NTQ2OGM5NzM5YTZlMzk1N2JlYiIsCiAgInByb2ZpbGVOYW1lIiA6ICJUaGFua3NNb2phbmciLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNTJhOWZlMDViYzY2M2VmY2QxMmU1NmEzY2NjNWVjMDM1YmY1NzdiNzg3MDg1NDhiNmY0ZmZjZjFkMzBlY2NmZSIKICAgIH0KICB9Cn0=",
+ // Rat
+ "ewogICJ0aW1lc3RhbXAiIDogMTYxODQxOTcwMTc1MywKICAicHJvZmlsZUlkIiA6ICI3MzgyZGRmYmU0ODU0NTVjODI1ZjkwMGY4OGZkMzJmOCIsCiAgInByb2ZpbGVOYW1lIiA6ICJCdUlJZXQiLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYThhYmI0NzFkYjBhYjc4NzAzMDExOTc5ZGM4YjQwNzk4YTk0MWYzYTRkZWMzZWM2MWNiZWVjMmFmOGNmZmU4IiwKICAgICAgIm1ldGFkYXRhIiA6IHsKICAgICAgICAibW9kZWwiIDogInNsaW0iCiAgICAgIH0KICAgIH0KICB9Cn0=",
+ // Locust
+ "ewogICJ0aW1lc3RhbXAiIDogMTY5NzU1NzA3NzAzNywKICAicHJvZmlsZUlkIiA6ICI0YjJlMGM1ODliZjU0ZTk1OWM1ZmJlMzg5MjQ1MzQzZSIsCiAgInByb2ZpbGVOYW1lIiA6ICJfTmVvdHJvbl8iLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNGIyNGE0ODJhMzJkYjFlYTc4ZmI5ODA2MGIwYzJmYTRhMzczY2JkMThhNjhlZGRkZWI3NDE5NDU1YTU5Y2RhOSIKICAgIH0KICB9Cn0=",
+ // Cricket
+ "ewogICJ0aW1lc3RhbXAiIDogMTcyMzE3OTgxMTI2NCwKICAicHJvZmlsZUlkIiA6ICJjZjc4YzFkZjE3ZTI0Y2Q5YTIxYmU4NWQ0NDk5ZWE4ZiIsCiAgInByb2ZpbGVOYW1lIiA6ICJNYXR0c0FybW9yU3RhbmRzIiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlL2EyNGM2OWY5NmNlNTU2MjIxZTE5NWM4ZWYyYmZhZDcxZWJmN2Y5NWY1YWU5MTRhNDg0YThkMGVjMjE2NzI2NzQiCiAgICB9CiAgfQp9",
+ // Fly
+ "ewogICJ0aW1lc3RhbXAiIDogMTY5Njk0NTA2MzI4MSwKICAicHJvZmlsZUlkIiA6ICJjN2FmMWNkNjNiNTE0Y2YzOGY4NWQ2ZDUxNzhjYThlNCIsCiAgInByb2ZpbGVOYW1lIiA6ICJtb25zdGVyZ2FtZXIzMTUiLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOWQ5MGU3Nzc4MjZhNTI0NjEzNjhlMjZkMWIyZTE5YmZhMWJhNTgyZDYwMjQ4M2U1NDVmNDEyNGQwZjczMTg0MiIKICAgIH0KICB9Cn0=",
+ // Beetle
+ "ewogICJ0aW1lc3RhbXAiIDogMTcyMzE3OTc4OTkzNCwKICAicHJvZmlsZUlkIiA6ICJlMjc5NjliODYyNWY0NDg1YjkyNmM5NTBhMDljMWMwMSIsCiAgInByb2ZpbGVOYW1lIiA6ICJLRVZJTktFTE9LRSIsCiAgInNpZ25hdHVyZVJlcXVpcmVkIiA6IHRydWUsCiAgInRleHR1cmVzIiA6IHsKICAgICJTS0lOIiA6IHsKICAgICAgInVybCIgOiAiaHR0cDovL3RleHR1cmVzLm1pbmVjcmFmdC5uZXQvdGV4dHVyZS83MGExZTgzNmJmMTk2OGIyZWFhNDgzNzIyN2ExOTIwNGYxNzI5NWQ4NzBlZTllNzU0YmQ2YjZkNjBkZGJlZDNjIgogICAgfQogIH0KfQ==",
+ // Slug
+ "ewogICJ0aW1lc3RhbXAiIDogMTY5NzQ3MDQ0MzA4MiwKICAicHJvZmlsZUlkIiA6ICJkOGNkMTNjZGRmNGU0Y2IzODJmYWZiYWIwOGIyNzQ4OSIsCiAgInByb2ZpbGVOYW1lIiA6ICJaYWNoeVphY2giLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvN2E3OWQwZmQ2NzdiNTQ1MzA5NjExMTdlZjg0YWRjMjA2ZTJjYzUwNDVjMTM0NGQ2MWQ3NzZiZjhhYzJmZTFiYSIKICAgIH0KICB9Cn0=",
+ // Moth
+ "ewogICJ0aW1lc3RhbXAiIDogMTY5Njg3MDQwNTk1NCwKICAicHJvZmlsZUlkIiA6ICJiMTUyZDlhZTE1MTM0OWNmOWM2NmI0Y2RjMTA5NTZjOCIsCiAgInByb2ZpbGVOYW1lIiA6ICJNaXNxdW90aCIsCiAgInNpZ25hdHVyZVJlcXVpcmVkIiA6IHRydWUsCiAgInRleHR1cmVzIiA6IHsKICAgICJTS0lOIiA6IHsKICAgICAgInVybCIgOiAiaHR0cDovL3RleHR1cmVzLm1pbmVjcmFmdC5uZXQvdGV4dHVyZS82NTQ4NWM0YjM0ZTViNTQ3MGJlOTRkZTEwMGU2MWY3ODE2ZjgxYmM1YTExZGZkZjBlY2NmODkwMTcyZGE1ZDBhIgogICAgfQogIH0KfQ==",
+ // Mite
+ "ewogICJ0aW1lc3RhbXAiIDogMTY5Njg3MDQxOTcyNSwKICAicHJvZmlsZUlkIiA6ICJkYjYzNWE3MWI4N2U0MzQ5YThhYTgwOTMwOWFhODA3NyIsCiAgInByb2ZpbGVOYW1lIiA6ICJFbmdlbHMxNzQiLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYmU2YmFmNjQzMWE5ZGFhMmNhNjA0ZDVhM2MyNmU5YTc2MWQ1OTUyZjA4MTcxNzRhNGZlMGI3NjQ2MTZlMjFmZiIKICAgIH0KICB9Cn0=",
+ // Earthworm
+ "ewogICJ0aW1lc3RhbXAiIDogMTY5NzQ3MDQ1OTc0NywKICAicHJvZmlsZUlkIiA6ICIyNTBlNzc5MjZkNDM0ZDIyYWM2MTQ4N2EyY2M3YzAwNCIsCiAgInByb2ZpbGVOYW1lIiA6ICJMdW5hMTIxMDUiLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNjQwM2JhNDAyN2EzMzNkOGQyZmQzMmFiNTlkMWNmZGJhYTdkOTA4ZDgwZDIzODFkYjJhNjljYmU2NTQ1MGFkOCIKICAgIH0KICB9Cn0=",
+ // Field Mouse
+ "ewogICJ0aW1lc3RhbXAiIDogMTcyNzkwNDc5NzQ1OSwKICAicHJvZmlsZUlkIiA6ICI0MmIwOTMyZDUwMWI0MWQ1YTM4YjEwOTcxYTYwYmYxMyIsCiAgInByb2ZpbGVOYW1lIiA6ICJBaXJib2x0MDc4IiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlL2YzNzllMDkyNTI4MTczMTRiZDBiNjk0ZjdkNTNiNDhhZjJjN2ZhODQ5OTEwOTgwMmE0MWJiMjk0ZDJmOTNlM2UiLAogICAgICAibWV0YWRhdGEiIDogewogICAgICAgICJtb2RlbCIgOiAic2xpbSIKICAgICAgfQogICAgfQogIH0KfQ=="
+ );
+
/**
* Cache for mob glow. Absence means the entity does not have custom glow.
* If an entity is in the cache, it must have custom glow.
@@ -141,6 +166,9 @@ public class MobGlow {
// Enderman Slayer's Nukekubi Skulls
case ArmorStandEntity armorStand when SkyblockerConfigManager.get().slayers.endermanSlayer.highlightNukekubiHeads && Utils.isInTheEnd() && armorStand.isMarker() && SlayerManager.isInSlayer() && isNukekubiHead(armorStand) -> 0x990099;
+ // Pests
+ case ArmorStandEntity armorStand when SkyblockerConfigManager.get().farming.garden.pestHighlighter && Utils.isInGarden() && isPestHead(armorStand) -> 0xb62f00;
+
// Blaze Slayer's Demonic minions
case WitherSkeletonEntity e when SkyblockerConfigManager.get().slayers.highlightBosses == SlayersConfig.HighlightSlayerEntities.GLOW && SlayerManager.isInSlayerType(SlayerType.DEMONLORD) && e.distanceTo(MinecraftClient.getInstance().player) <= 15 -> AttunementColors.getColor(e);
case ZombifiedPiglinEntity e when SkyblockerConfigManager.get().slayers.highlightBosses == SlayersConfig.HighlightSlayerEntities.GLOW && SlayerManager.isInSlayerType(SlayerType.DEMONLORD) && e.distanceTo(MinecraftClient.getInstance().player) <= 15 -> AttunementColors.getColor(e);
@@ -192,4 +220,13 @@ public class MobGlow {
private static boolean isNukekubiHead(ArmorStandEntity entity) {
return Streams.stream(entity.getArmorItems()).map(ItemUtils::getHeadTexture).anyMatch(headTexture -> headTexture.contains(NUKEKUBI_HEAD_TEXTURE));
}
+
+ /**
+ * Compares the armor items of an armor stand to the Pest head texture to determine if it is a Pest head.
+ */
+ private static boolean isPestHead(ArmorStandEntity entity) {
+ return Streams.stream(entity.getArmorItems())
+ .map(ItemUtils::getHeadTexture)
+ .anyMatch(PEST_HEAD_TEXTURES::contains);
+ }
}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/garden/FarmingHud.java b/src/main/java/de/hysky/skyblocker/skyblock/garden/FarmingHud.java
index eea3a3b7..ab1cfe2d 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/garden/FarmingHud.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/garden/FarmingHud.java
@@ -20,6 +20,7 @@ import net.minecraft.client.MinecraftClient;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtElement;
+import net.minecraft.util.Formatting;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -35,119 +36,129 @@ import java.util.regex.Pattern;
import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal;
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 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<>();
- private static float farmingXpPercentProgress;
-
- @Init
- public static void init() {
- HudRenderEvents.AFTER_MAIN_HUD.register((context, tickCounter) -> {
- if (shouldRender()) {
- if (!counter.isEmpty() && counter.peek().rightLong() + 5000 < System.currentTimeMillis()) {
- counter.poll();
- }
- if (!blockBreaks.isEmpty() && blockBreaks.firstLong() + 1000 < System.currentTimeMillis()) {
- blockBreaks.dequeueLong();
- }
- if (!farmingXp.isEmpty() && farmingXp.peek().rightLong() + 1000 < System.currentTimeMillis()) {
- farmingXp.poll();
- }
-
- ItemStack stack = client.player.getMainHandStack();
- if (stack == null || !tryGetCounter(stack, CounterType.CULTIVATING) && !tryGetCounter(stack, CounterType.COUNTER)) {
- counterType = CounterType.NONE;
- }
- }
- });
- ClientPlayerBlockBreakEvents.AFTER.register((world, player, pos, state) -> {
- if (shouldRender()) {
- blockBreaks.enqueue(System.currentTimeMillis());
- }
- });
- ClientReceiveMessageEvents.GAME.register((message, overlay) -> {
- if (shouldRender() && overlay) {
- Matcher matcher = FARMING_XP.matcher(message.getString());
- if (matcher.matches()) {
- try {
- farmingXp.offer(FloatLongPair.of(NUMBER_FORMAT.parse(matcher.group("xp")).floatValue(), System.currentTimeMillis()));
- farmingXpPercentProgress = NUMBER_FORMAT.parse(matcher.group("percent")).floatValue();
- } catch (ParseException e) {
- LOGGER.error("[Skyblocker Farming HUD] Failed to parse farming xp", e);
- }
- }
- }
- });
- ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(literal(SkyblockerMod.NAMESPACE).then(literal("hud").then(literal("farming")
- .executes(Scheduler.queueOpenScreenCommand(() -> new WidgetsConfigurationScreen(Location.GARDEN, "hud_garden", 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 && client.player != null && Utils.getLocation() == Location.GARDEN;
- }
-
- public static String counterText() {
- return counterType.text;
- }
-
- public static int counter() {
- return counter.isEmpty() ? 0 : counter.peekLast().leftInt();
- }
-
- public static float cropsPerMinute() {
- if (counter.isEmpty()) {
- return 0;
- }
- IntLongPair first = counter.peek();
- IntLongPair last = counter.peekLast();
- return (float) (last.leftInt() - first.leftInt()) / (last.rightLong() - first.rightLong()) * 60_000f;
- }
-
- public static int blockBreaks() {
- return blockBreaks.size();
- }
-
- public static float farmingXpPercentProgress() {
- return 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;
- }
- public boolean matchesText(String textToMatch) {
- return this.text.equals(textToMatch);
- }
- }
+ private static final Logger LOGGER = LoggerFactory.getLogger(FarmingHud.class);
+ public static final NumberFormat NUMBER_FORMAT = NumberFormat.getInstance(Locale.US);
+ private static final Pattern FARMING_XP = Pattern.compile("\\+(?<xp>\\d+(?:\\.\\d+)?) Farming \\((?<percent>[\\d,]+(?:\\.\\d+)?%|[\\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<>();
+ private static float farmingXpPercentProgress;
+
+ @Init
+ public static void init() {
+ HudRenderEvents.AFTER_MAIN_HUD.register((context, tickCounter) -> {
+ if (shouldRender()) {
+ if (!counter.isEmpty() && counter.peek().rightLong() + 5000 < System.currentTimeMillis()) {
+ counter.poll();
+ }
+ if (!blockBreaks.isEmpty() && blockBreaks.firstLong() + 1000 < System.currentTimeMillis()) {
+ blockBreaks.dequeueLong();
+ }
+ if (!farmingXp.isEmpty() && farmingXp.peek().rightLong() + 1000 < System.currentTimeMillis()) {
+ farmingXp.poll();
+ }
+
+ assert client.player != null;
+ ItemStack stack = client.player.getMainHandStack();
+ if (stack == null || tryGetCounter(stack, CounterType.CULTIVATING) && tryGetCounter(stack, CounterType.COUNTER)) {
+ counterType = CounterType.NONE;
+ }
+ }
+ });
+ ClientPlayerBlockBreakEvents.AFTER.register((world, player, pos, state) -> {
+ if (shouldRender()) {
+ blockBreaks.enqueue(System.currentTimeMillis());
+ }
+ });
+ ClientReceiveMessageEvents.GAME.register((message, overlay) -> {
+ if (shouldRender() && overlay) {
+ Matcher matcher = FARMING_XP.matcher(Formatting.strip(message.getString()));
+ if (matcher.matches()) {
+ try {
+ farmingXp.offer(FloatLongPair.of(NUMBER_FORMAT.parse(matcher.group("xp")).floatValue(), System.currentTimeMillis()));
+ farmingXpPercentProgress = NUMBER_FORMAT.parse(matcher.group("percent")).floatValue();
+ } catch (ParseException e) {
+ LOGGER.error("[Skyblocker Farming HUD] Failed to parse farming xp", e);
+ }
+ }
+ }
+ });
+ ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(literal(SkyblockerMod.NAMESPACE).then(literal("hud").then(literal("farming")
+ .executes(Scheduler.queueOpenScreenCommand(() -> new WidgetsConfigurationScreen(Location.GARDEN, "hud_garden", null)))))));
+ }
+
+ private static boolean tryGetCounter(ItemStack stack, CounterType counterType) {
+ NbtCompound customData = ItemUtils.getCustomData(stack);
+ if (customData.isEmpty() || !customData.contains(counterType.nbtKey, NbtElement.NUMBER_TYPE)) return true;
+ 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 false;
+ }
+
+ private static boolean shouldRender() {
+ 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() {
+ return counter.isEmpty() ? 0 : counter.peekLast().leftInt();
+ }
+
+ public static float cropsPerMinute() {
+ if (counter.isEmpty()) {
+ return 0;
+ }
+ IntLongPair first = counter.peek();
+ IntLongPair last = counter.peekLast();
+ return (float) (last.leftInt() - first.leftInt()) / (last.rightLong() - first.rightLong()) * 60_000f;
+ }
+
+ public static double blockBreaks() {
+ if (blockBreaks.isEmpty()) {
+ return 0;
+ }
+ long firstTimestamp = blockBreaks.firstLong();
+ long lastTimestamp = blockBreaks.lastLong();
+ return Math.round((blockBreaks.size() - 1) / (double) (lastTimestamp - firstTimestamp) * 10000) / 10d;
+ }
+
+ public static float farmingXpPercentProgress() {
+ return Math.clamp(farmingXpPercentProgress, 0, 100);
+ }
+
+ public static double farmingXpPerHour() {
+ if (farmingXp.isEmpty()) {
+ return 0;
+ }
+ return Math.round(farmingXp.peek().leftFloat() * blockBreaks() * 3600 * 10) / 10d;
+ }
+
+ 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;
+ }
+
+ public boolean matchesText(String textToMatch) {
+ return this.text.equals(textToMatch);
+ }
+ }
}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/garden/FarmingHudWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/garden/FarmingHudWidget.java
index ab37080f..efe67cc4 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/garden/FarmingHudWidget.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/garden/FarmingHudWidget.java
@@ -94,10 +94,10 @@ public class FarmingHudWidget extends ComponentBasedWidget {
float cropsPerMinute = FarmingHud.cropsPerMinute();
addSimpleIcoText(cropStack, "Crops/min: ", Formatting.YELLOW, FarmingHud.NUMBER_FORMAT.format((int) cropsPerMinute / 10 * 10));
addSimpleIcoText(Ico.GOLD, "Coins/h: ", Formatting.GOLD, getPriceText(cropItemId, cropsPerMinute));
- addSimpleIcoText(cropStack, "Blocks/s: ", Formatting.YELLOW, Integer.toString(FarmingHud.blockBreaks()));
+ addSimpleIcoText(cropStack, "Blocks/s: ", Formatting.YELLOW, Double.toString(FarmingHud.blockBreaks()));
//noinspection DataFlowIssue
- addComponent(Components.progressComponent(Ico.LANTERN, Text.literal("Farming Level: "), FarmingHud.farmingXpPercentProgress(), Formatting.GOLD.getColorValue()));
- addSimpleIcoText(Ico.LIME_DYE, "Farming XP/h: ", Formatting.YELLOW, FarmingHud.NUMBER_FORMAT.format((int) FarmingHud.farmingXpPerHour()));
+ addComponent(Components.progressComponent(Ico.LANTERN, Text.literal("Farming Level:"), FarmingHud.farmingXpPercentProgress(), Formatting.GOLD.getColorValue()));
+ addSimpleIcoText(Ico.LIME_DYE, "Farming XP/h: ", Formatting.YELLOW, FarmingHud.NUMBER_FORMAT.format(FarmingHud.farmingXpPerHour()));
Entity cameraEntity = client.getCameraEntity();
String yaw = cameraEntity == null ? "No Camera Entity" : String.format("%.2f", MathHelper.wrapDegrees(cameraEntity.getYaw()));
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/garden/VisitorHelper.java b/src/main/java/de/hysky/skyblocker/skyblock/garden/VisitorHelper.java
deleted file mode 100644
index db946317..00000000
--- a/src/main/java/de/hysky/skyblocker/skyblock/garden/VisitorHelper.java
+++ /dev/null
@@ -1,224 +0,0 @@
-package de.hysky.skyblocker.skyblock.garden;
-
-import de.hysky.skyblocker.annotations.Init;
-import de.hysky.skyblocker.config.SkyblockerConfigManager;
-import de.h