diff options
Diffstat (limited to 'src/main/java')
19 files changed, 678 insertions, 4 deletions
diff --git a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java index 6bdf619e..71623ea7 100644 --- a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java +++ b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java @@ -5,6 +5,7 @@ import com.google.gson.GsonBuilder; import de.hysky.skyblocker.config.SkyblockerConfigManager; import de.hysky.skyblocker.debug.Debug; import de.hysky.skyblocker.skyblock.*; +import de.hysky.skyblocker.skyblock.crimson.kuudra.Kuudra; import de.hysky.skyblocker.skyblock.dungeon.*; import de.hysky.skyblocker.skyblock.dungeon.partyfinder.PartyFinderScreen; import de.hysky.skyblocker.skyblock.dungeon.puzzle.CreeperBeams; @@ -139,6 +140,7 @@ public class SkyblockerMod implements ClientModInitializer { SecretsTracker.init(); ApiUtils.init(); Debug.init(); + Kuudra.init(); RenderHelper.init(); containerSolverManager.init(); statusBarTracker.init(); diff --git a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java index 1bfd1b3c..175c3bdf 100644 --- a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java +++ b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java @@ -175,6 +175,9 @@ public class SkyblockerConfig { public boolean dontStripSkinAlphaValues = true; @SerialEntry + public boolean visitorHelper = true; + + @SerialEntry public TabHudConf tabHud = new TabHudConf(); @SerialEntry @@ -637,6 +640,9 @@ public class SkyblockerConfig { public Barn barn = new Barn(); @SerialEntry + public CrimsonIsle crimsonIsle = new CrimsonIsle(); + + @SerialEntry public Dungeons dungeons = new Dungeons(); @SerialEntry @@ -647,6 +653,9 @@ public class SkyblockerConfig { @SerialEntry public SpidersDen spidersDen = new SpidersDen(); + + @SerialEntry + public Garden garden = new Garden(); } public static class Dungeons { @@ -985,6 +994,37 @@ public class SkyblockerConfig { public boolean solveTreasureHunter = true; } + public static class CrimsonIsle { + @SerialEntry + public Kuudra kuudra = new Kuudra(); + } + + public static class Kuudra { + @SerialEntry + public boolean supplyWaypoints = true; + + @SerialEntry + public boolean fuelWaypoints = true; + + @SerialEntry + public Waypoint.Type suppliesAndFuelWaypointType = Waypoint.Type.WAYPOINT; + + @SerialEntry + public boolean ballistaBuildWaypoints = true; + + @SerialEntry + public boolean safeSpotWaypoints = true; + + @SerialEntry + public boolean pearlWaypoints = true; + + @SerialEntry + public boolean noArrowPoisonWarning = true; + + @SerialEntry + public int arrowPoisonThreshold = 16; + } + public static class Rift { @SerialEntry public boolean mirrorverseWaypoints = true; @@ -1015,6 +1055,11 @@ public class SkyblockerConfig { public boolean highlightFoundRelics = true; } + public static class Garden { + @SerialEntry + public boolean dicerTitlePrevent = true; + } + public static class Slayer { @SerialEntry public EndermanSlayer endermanSlayer = new EndermanSlayer(); @@ -1111,6 +1156,9 @@ public class SkyblockerConfig { @SerialEntry public boolean hideMana = false; + + @SerialEntry + public ChatFilterResult hideDicer = ChatFilterResult.PASS; } public enum Info { diff --git a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfigManager.java b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfigManager.java index 3a2601ad..70578822 100644 --- a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfigManager.java +++ b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfigManager.java @@ -73,6 +73,7 @@ public class SkyblockerConfigManager { .category(GeneralCategory.create(defaults, config)) .category(DungeonsCategory.create(defaults, config)) .category(DwarvenMinesCategory.create(defaults, config)) + .category(CrimsonIsleCategory.create(defaults, config)) .category(LocationsCategory.create(defaults, config)) .category(SlayersCategory.create(defaults, config)) .category(QuickNavigationCategory.create(defaults, config)) diff --git a/src/main/java/de/hysky/skyblocker/config/categories/CrimsonIsleCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/CrimsonIsleCategory.java new file mode 100644 index 00000000..3c2dc3b6 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/config/categories/CrimsonIsleCategory.java @@ -0,0 +1,85 @@ +package de.hysky.skyblocker.config.categories; + +import de.hysky.skyblocker.config.ConfigUtils; +import de.hysky.skyblocker.config.SkyblockerConfig; +import de.hysky.skyblocker.utils.waypoint.Waypoint; +import dev.isxander.yacl3.api.ConfigCategory; +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.api.OptionDescription; +import dev.isxander.yacl3.api.OptionGroup; +import dev.isxander.yacl3.api.controller.IntegerFieldControllerBuilder; +import net.minecraft.text.Text; + +public class CrimsonIsleCategory { + + public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig config) { + return ConfigCategory.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.category.crimsonIsle")) + + //Kuudra + .group(OptionGroup.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.crimsonIsle.kuudra")) + .collapsed(false) + .option(Option.<Boolean>createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.crimsonIsle.kuudra.supplyWaypoints")) + .binding(defaults.locations.crimsonIsle.kuudra.supplyWaypoints, + () -> config.locations.crimsonIsle.kuudra.supplyWaypoints, + newValue -> config.locations.crimsonIsle.kuudra.supplyWaypoints = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.<Boolean>createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.crimsonIsle.kuudra.fuelWaypoints")) + .binding(defaults.locations.crimsonIsle.kuudra.fuelWaypoints, + () -> config.locations.crimsonIsle.kuudra.fuelWaypoints, + newValue -> config.locations.crimsonIsle.kuudra.fuelWaypoints = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.<Waypoint.Type>createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.crimsonIsle.kuudra.suppliesAndFuelWaypointType")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.general.waypoints.waypointType.@Tooltip"))) + .binding(defaults.locations.crimsonIsle.kuudra.suppliesAndFuelWaypointType, + () -> config.locations.crimsonIsle.kuudra.suppliesAndFuelWaypointType, + newValue -> config.locations.crimsonIsle.kuudra.suppliesAndFuelWaypointType = newValue) + .controller(ConfigUtils::createEnumCyclingListController) + .build()) + .option(Option.<Boolean>createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.crimsonIsle.kuudra.ballistaBuildWaypoints")) + .binding(defaults.locations.crimsonIsle.kuudra.ballistaBuildWaypoints, + () -> config.locations.crimsonIsle.kuudra.ballistaBuildWaypoints, + newValue -> config.locations.crimsonIsle.kuudra.ballistaBuildWaypoints = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.<Boolean>createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.crimsonIsle.kuudra.safeSpotWaypoints")) + .binding(defaults.locations.crimsonIsle.kuudra.safeSpotWaypoints, + () -> config.locations.crimsonIsle.kuudra.safeSpotWaypoints, + newValue -> config.locations.crimsonIsle.kuudra.safeSpotWaypoints = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.<Boolean>createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.crimsonIsle.kuudra.pearlWaypoints")) + .binding(defaults.locations.crimsonIsle.kuudra.pearlWaypoints, + () -> config.locations.crimsonIsle.kuudra.pearlWaypoints, + newValue -> config.locations.crimsonIsle.kuudra.pearlWaypoints = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.<Boolean>createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.crimsonIsle.kuudra.noArrowPoisonWarning")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.crimsonIsle.kuudra.noArrowPoisonWarning.@Tooltip"))) + .binding(defaults.locations.crimsonIsle.kuudra.noArrowPoisonWarning, + () -> config.locations.crimsonIsle.kuudra.noArrowPoisonWarning, + newValue -> config.locations.crimsonIsle.kuudra.noArrowPoisonWarning = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.<Integer>createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.crimsonIsle.kuudra.arrowPoisonThreshold")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.crimsonIsle.kuudra.arrowPoisonThreshold.@Tooltip"))) + .binding(defaults.locations.crimsonIsle.kuudra.arrowPoisonThreshold, + () -> config.locations.crimsonIsle.kuudra.arrowPoisonThreshold, + newValue -> config.locations.crimsonIsle.kuudra.arrowPoisonThreshold = newValue) + .controller(IntegerFieldControllerBuilder::create) + .build()) + .build()) + .build(); + } +} diff --git a/src/main/java/de/hysky/skyblocker/config/categories/GeneralCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/GeneralCategory.java index 9c3b672f..bb333f79 100644 --- a/src/main/java/de/hysky/skyblocker/config/categories/GeneralCategory.java +++ b/src/main/java/de/hysky/skyblocker/config/categories/GeneralCategory.java @@ -78,6 +78,13 @@ public class GeneralCategory { .controller(ConfigUtils::createBooleanController) .flag(OptionFlag.ASSET_RELOAD) .build()) + .option(Option.<Boolean>createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.visitorHelper")) + .binding(defaults.general.visitorHelper, + () -> config.general.visitorHelper, + newValue -> config.general.visitorHelper = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) //Tab Hud .group(OptionGroup.createBuilder() diff --git a/src/main/java/de/hysky/skyblocker/config/categories/LocationsCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/LocationsCategory.java index 0b388d16..9bdcf2e9 100644 --- a/src/main/java/de/hysky/skyblocker/config/categories/LocationsCategory.java +++ b/src/main/java/de/hysky/skyblocker/config/categories/LocationsCategory.java @@ -98,6 +98,19 @@ public class LocationsCategory { .controller(ConfigUtils::createBooleanController) .build()) .build()) + + //Garden + .group(OptionGroup.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.garden")) + .collapsed(false) + .option(Option.<Boolean>createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.garden.dicerTitlePrevent")) + .binding(defaults.locations.garden.dicerTitlePrevent, + () -> config.locations.garden.dicerTitlePrevent, + newValue -> config.locations.garden.dicerTitlePrevent = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .build()) .build(); } } diff --git a/src/main/java/de/hysky/skyblocker/config/categories/MessageFilterCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/MessageFilterCategory.java index ce349049..acdc8169 100644 --- a/src/main/java/de/hysky/skyblocker/config/categories/MessageFilterCategory.java +++ b/src/main/java/de/hysky/skyblocker/config/categories/MessageFilterCategory.java @@ -118,6 +118,14 @@ public class MessageFilterCategory { newValue -> config.messages.hideDeath = newValue) .controller(ConfigUtils::createEnumCyclingListController) .build()) + .option(Option.<ChatFilterResult>createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.messages.hideDicer")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.messages.hideDicer.@Tooltip"))) + .binding(defaults.messages.hideDicer, + () -> config.messages.hideDicer, + newValue -> config.messages.hideDicer = newValue) + .controller(ConfigUtils::createEnumCyclingListController) + .build()) .build(); } } diff --git a/src/main/java/de/hysky/skyblocker/mixin/HandledScreenMixin.java b/src/main/java/de/hysky/skyblocker/mixin/HandledScreenMixin.java index 71203381..8a1af570 100644 --- a/src/main/java/de/hysky/skyblocker/mixin/HandledScreenMixin.java +++ b/src/main/java/de/hysky/skyblocker/mixin/HandledScreenMixin.java @@ -7,6 +7,7 @@ import de.hysky.skyblocker.skyblock.experiment.ChronomatronSolver; 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.item.ItemProtection; import de.hysky.skyblocker.skyblock.item.ItemRarityBackgrounds; import de.hysky.skyblocker.skyblock.item.WikiLookup; @@ -37,7 +38,6 @@ import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.ModifyVariable; -import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @@ -71,6 +71,20 @@ public abstract class HandledScreenMixin<T extends ScreenHandler> extends Screen } } + @Inject(at = @At("RETURN"), method = "render") + public void skyblocker$renderScreen(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci) { + if (!Utils.isOnSkyblock()) return; + + if (SkyblockerConfigManager.get().general.visitorHelper && (Utils.getLocationRaw().equals("garden") && !getTitle().getString().contains("Logbook") || getTitle().getString().startsWith("Bazaar"))) + VisitorHelper.renderScreen(this.getTitle().getString(), context, textRenderer, handler, mouseX, mouseY); + } + + @Inject(at = @At("HEAD"), method = "mouseClicked") + public void skyblocker$mouseClicked(double mouseX, double mouseY, int button, CallbackInfoReturnable<Boolean> cir) { + if (SkyblockerConfigManager.get().general.visitorHelper && (Utils.getLocationRaw().equals("garden") && !getTitle().getString().contains("Logbook") || getTitle().getString().startsWith("Bazaar"))) + VisitorHelper.onMouseClicked(mouseX, mouseY, button, this.textRenderer); + } + @SuppressWarnings("DataFlowIssue") // makes intellij be quiet about this.focusedSlot maybe being null. It's already null checked in mixined method. @Inject(method = "drawMouseoverTooltip", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;drawTooltip(Lnet/minecraft/client/font/TextRenderer;Ljava/util/List;Ljava/util/Optional;II)V"), cancellable = true) @@ -167,8 +181,11 @@ public abstract class HandledScreenMixin<T extends ScreenHandler> extends Screen if (ItemProtection.isItemProtected(stack)) ci.cancel(); } - //Prevent selling to NPC shops + if (this.client != null && this.handler instanceof GenericContainerScreenHandler genericContainerScreenHandler && genericContainerScreenHandler.getRows() == 6) { + VisitorHelper.onSlotClick(slot, slotId, this.getTitle().getString()); + + //Prevent selling to NPC shops ItemStack sellItem = this.handler.slots.get(49).getStack(); if (sellItem.getName().getString().equals("Sell Item") || skyblocker$doesLoreContain(sellItem, this.client, "buyback")) { diff --git a/src/main/java/de/hysky/skyblocker/mixin/InGameHudMixin.java b/src/main/java/de/hysky/skyblocker/mixin/InGameHudMixin.java index c8f6f272..88be60cd 100644 --- a/src/main/java/de/hysky/skyblocker/mixin/InGameHudMixin.java +++ b/src/main/java/de/hysky/skyblocker/mixin/InGameHudMixin.java @@ -19,9 +19,11 @@ import net.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.hud.InGameHud; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; +import net.minecraft.text.Text; import net.minecraft.util.Identifier; import java.util.function.Supplier; +import java.util.regex.Pattern; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; @@ -37,6 +39,9 @@ public abstract class InGameHudMixin { @Unique private static final Supplier<Identifier> SLOT_LOCK_ICON = () -> SkyblockerConfigManager.get().general.itemProtection.slotLockStyle.tex; @Unique + private static final Pattern DICER_TITLE_BLACKLIST = Pattern.compile(".+? DROP!"); + + @Unique private final FancyStatusBars statusBars = new FancyStatusBars(); @Shadow @@ -101,4 +106,11 @@ public abstract class InGameHudMixin { return cooldownProgress; } + + @Inject(method = "setTitle", at = @At("HEAD"), cancellable = true) + private void skyblocker$dicerTitlePrevent(Text title, CallbackInfo ci) { + if (Utils.isOnSkyblock() && SkyblockerConfigManager.get().locations.garden.dicerTitlePrevent && title != null && DICER_TITLE_BLACKLIST.matcher(title.getString()).matches()) { + ci.cancel(); + } + } } diff --git a/src/main/java/de/hysky/skyblocker/mixin/PlayerInventoryMixin.java b/src/main/java/de/hysky/skyblocker/mixin/PlayerInventoryMixin.java new file mode 100644 index 00000000..4795a28b --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/mixin/PlayerInventoryMixin.java @@ -0,0 +1,21 @@ +package de.hysky.skyblocker.mixin; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import de.hysky.skyblocker.skyblock.crimson.kuudra.ArrowPoisonWarning; +import net.minecraft.entity.player.PlayerInventory; + +@Mixin(PlayerInventory.class) +public class PlayerInventoryMixin { + @Shadow + public int selectedSlot; + + @Inject(method = "scrollInHotbar", at = @At("TAIL")) + private void skyblocker$onHotbarScroll(CallbackInfo ci) { + ArrowPoisonWarning.tryWarn(selectedSlot); + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/crimson/kuudra/ArrowPoisonWarning.java b/src/main/java/de/hysky/skyblocker/skyblock/crimson/kuudra/ArrowPoisonWarning.java new file mode 100644 index 00000000..7ddbeb99 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/crimson/kuudra/ArrowPoisonWarning.java @@ -0,0 +1,54 @@ +package de.hysky.skyblocker.skyblock.crimson.kuudra; + +import java.util.function.Supplier; + +import de.hysky.skyblocker.config.SkyblockerConfig; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.skyblock.crimson.kuudra.Kuudra.KuudraPhase; +import de.hysky.skyblocker.utils.ItemUtils; +import de.hysky.skyblocker.utils.Utils; +import de.hysky.skyblocker.utils.render.RenderHelper; +import de.hysky.skyblocker.utils.render.title.Title; +import net.minecraft.client.MinecraftClient; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.item.BowItem; +import net.minecraft.item.ItemStack; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +public class ArrowPoisonWarning { + private static final Supplier<SkyblockerConfig.Kuudra> CONFIG = () -> SkyblockerConfigManager.get().locations.crimsonIsle.kuudra; + private static final int THREE_SECONDS = 20 * 3; + private static final Title NONE_TITLE = new Title(Text.translatable("skyblocker.crimson.kuudra.noArrowPoison").formatted(Formatting.GREEN)); + private static final Title LOW_TITLE = new Title(Text.translatable("skyblocker.crimson.kuudra.lowArrowPoison").formatted(Formatting.GREEN)); + + public static void tryWarn(int newSlot) { + //Exclude skyblock menu + if (Utils.isInKuudra() && CONFIG.get().noArrowPoisonWarning && Kuudra.phase == KuudraPhase.DPS && newSlot != 8) { + MinecraftClient client = MinecraftClient.getInstance(); + PlayerInventory inventory = client.player.getInventory(); + ItemStack heldItem = inventory.getStack(newSlot); + + if (heldItem.getItem() instanceof BowItem) { + boolean hasToxicArrowPoison = false; + int arrowPoisonAmount = 0; + + for (int i = 0; i < inventory.size(); ++i) { + ItemStack stack = inventory.getStack(i); + String itemId = ItemUtils.getItemId(stack); + + if (itemId.equals("TOXIC_ARROW_POISON")) { + hasToxicArrowPoison = true; + arrowPoisonAmount += stack.getCount(); + } + } + + if (!hasToxicArrowPoison) { + RenderHelper.displayInTitleContainerAndPlaySound(NONE_TITLE, THREE_SECONDS); + } else if (arrowPoisonAmount < CONFIG.get().arrowPoisonThreshold) { + RenderHelper.displayInTitleContainerAndPlaySound(LOW_TITLE, THREE_SECONDS); + } + } + } + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/crimson/kuudra/Kuudra.java b/src/main/java/de/hysky/skyblocker/skyblock/crimson/kuudra/Kuudra.java new file mode 100644 index 00000000..033a919d --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/crimson/kuudra/Kuudra.java @@ -0,0 +1,51 @@ +package de.hysky.skyblocker.skyblock.crimson.kuudra; + +import de.hysky.skyblocker.utils.Utils; +import de.hysky.skyblocker.utils.scheduler.Scheduler; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; +import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents; +import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; +import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +public class Kuudra { + public static final String LOCATION = "kuudra"; + static KuudraPhase phase = KuudraPhase.OTHER; + + public static void init() { + WorldRenderEvents.AFTER_TRANSLUCENT.register(KuudraWaypoints::render); + ClientLifecycleEvents.CLIENT_STARTED.register(KuudraWaypoints::load); + ClientPlayConnectionEvents.JOIN.register((_handler, _sender, _client) -> reset()); + ClientReceiveMessageEvents.GAME.register(Kuudra::onMessage); + Scheduler.INSTANCE.scheduleCyclic(KuudraWaypoints::tick, 20); + } + + private static void onMessage(Text text, boolean overlay) { + if (Utils.isInKuudra() && !overlay) { + String message = Formatting.strip(text.getString()); + + if (message.equals("[NPC] Elle: ARGH! All of the supplies fell into the lava! You need to retrieve them quickly!")) { + phase = KuudraPhase.RETRIEVE_SUPPLIES; + } + + if (message.equals("[NPC] Elle: Phew! The Ballista is finally ready! It should be strong enough to tank Kuudra's blows now!")) { + phase = KuudraPhase.DPS; + } + + if (message.equals("[NPC] Elle: POW! SURELY THAT'S IT! I don't think he has any more in him!")) { + phase = KuudraPhase.OTHER; + } + } + } + + private static void reset() { + phase = KuudraPhase.OTHER; + } + + enum KuudraPhase { + OTHER, + RETRIEVE_SUPPLIES, + DPS; + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/crimson/kuudra/KuudraWaypoints.java b/src/main/java/de/hysky/skyblocker/skyblock/crimson/kuudra/KuudraWaypoints.java new file mode 100644 index 00000000..790d434a --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/crimson/kuudra/KuudraWaypoints.java @@ -0,0 +1,161 @@ +package de.hysky.skyblocker.skyblock.crimson.kuudra; + +import java.io.BufferedReader; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.Function; +import java.util.function.Supplier; + +import org.slf4j.Logger; + +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; +import com.mojang.logging.LogUtils; +import com.mojang.serialization.Codec; +import com.mojang.serialization.JsonOps; + +import de.hysky.skyblocker.SkyblockerMod; +import de.hysky.skyblocker.config.SkyblockerConfig; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.PosUtils; +import de.hysky.skyblocker.utils.Utils; +import de.hysky.skyblocker.utils.waypoint.Waypoint; +import de.hysky.skyblocker.utils.waypoint.Waypoint.Type; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; +import net.minecraft.client.MinecraftClient; +import net.minecraft.entity.decoration.ArmorStandEntity; +import net.minecraft.entity.mob.GiantEntity; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Box; +import net.minecraft.util.math.MathHelper; + +public class KuudraWaypoints { + private static final Logger LOGGER = LogUtils.getLogger(); + private static final float[] SUPPLIES_COLOR = { 255f / 255f, 0f, 0f }; + private static final float[] PEARL_COLOR = { 57f / 255f, 117f / 255f, 125f / 255f }; + private static final float[] SAFE_SPOT_COLOR = { 255f / 255f, 85f / 255f, 255f / 255f }; + private static final Supplier<Type> SUPPLIES_AND_FUEL_TYPE = () -> SkyblockerConfigManager.get().locations.crimsonIsle.kuudra.suppliesAndFuelWaypointType; + private static final ObjectArrayList<Waypoint> SAFE_SPOT_WAYPOINTS = new ObjectArrayList<>(); + private static final ObjectArrayList<Waypoint> PEARL_WAYPOINTS = new ObjectArrayList<>(); + private static final Function<float[], Codec<List<Waypoint>>> CODEC = cc -> PosUtils.ALT_BLOCK_POS_CODEC.xmap( + pos -> new Waypoint(pos, () -> Waypoint.Type.HIGHLIGHT, cc, false), + waypoint -> waypoint.pos) + .listOf(); + + //Use non final lists and swap them out to avoid ConcurrentModificationExceptions + private static ObjectArrayList<Waypoint> supplyWaypoints = ObjectArrayList.of(); + private static ObjectArrayList<Waypoint> ballistaBuildWaypoints = ObjectArrayList.of(); + private static ObjectArrayList<Waypoint> fuelWaypoints = ObjectArrayList.of(); + private static boolean loaded; + + static void load(MinecraftClient client) { + CompletableFuture<Void> safeSpots = loadWaypoints(client, new Identifier(SkyblockerMod.NAMESPACE, "crimson/kuudra/safe_spot_waypoints.json"), SAFE_SPOT_WAYPOINTS, SAFE_SPOT_COLOR); + CompletableFuture<Void> pearls = loadWaypoints(client, new Identifier(SkyblockerMod.NAMESPACE, "crimson/kuudra/pearl_waypoints.json"), PEARL_WAYPOINTS, PEARL_COLOR); + + CompletableFuture.allOf(safeSpots, pearls).whenComplete((_result, _throwable) -> loaded = true); + } + + private static CompletableFuture<Void> loadWaypoints(MinecraftClient client, Identifier file, ObjectArrayList<Waypoint> list, float[] colorComponents) { + return CompletableFuture.supplyAsync(() -> { + try (BufferedReader reader = client.getResourceManager().openAsReader(file)) { + return CODEC.apply(colorComponents).parse(JsonOps.INSTANCE, getWaypoints(reader)).result().orElseThrow(); + } catch (Exception e) { + LOGGER.error("[Skyblocker Kuudra Waypoints] Failed to load kuudra waypoints from: {}", file, e); + + return List.<Waypoint>of(); + } + }).thenAccept(list::addAll); + } + + private static JsonElement getWaypoints(BufferedReader reader) { + return JsonParser.parseReader(reader).getAsJsonObject().getAsJsonArray("waypoints"); + } + + static void tick() { + MinecraftClient client = MinecraftClient.getInstance(); + SkyblockerConfig.Kuudra config = SkyblockerConfigManager.get().locations.crimsonIsle.kuudra; + + if (Utils.isInKuudra() && (config.supplyWaypoints || config.fuelWaypoints || config.ballistaBuildWaypoints)) { + Box searchBox = client.player.getBoundingBox().expand(500d); + ObjectArrayList<Waypoint> supplies = new ObjectArrayList<>(); + ObjectArrayList<Waypoint> fuelCells = new ObjectArrayList<>(); + + if (config.supplyWaypoints || config.fuelWaypoints) { + List<GiantEntity> giants = client.world.getEntitiesByClass(GiantEntity.class, searchBox, giant -> giant.getY() < 67); + + for (GiantEntity giant : giants) { + double yawOffset = giant.getYaw() + 115; + + double x = giant.getX() + 4.5 * Math.cos((yawOffset) * MathHelper.RADIANS_PER_DEGREE); + double y = 75; + double z = giant.getZ() + 4.5 * Math.sin((yawOffset) * MathHelper.RADIANS_PER_DEGREE); + + Waypoint waypoint = new Waypoint(BlockPos.ofFloored(x, y, z), SUPPLIES_AND_FUEL_TYPE, SUPPLIES_COLOR, false); + + switch (Kuudra.phase) { + case RETRIEVE_SUPPLIES -> supplies.add(waypoint); + case DPS -> fuelCells.add(waypoint); + default -> supplies.add(waypoint); + } + } + } + + ObjectArrayList<Waypoint> ballistaBuildSpots = new ObjectArrayList<>(); + + if (config.ballistaBuildWaypoints) { + List<ArmorStandEntity> armorStands = client.world.getEntitiesByClass(ArmorStandEntity.class, searchBox, ArmorStandEntity::hasCustomName); + + for (ArmorStandEntity armorStand : armorStands) { + String name = armorStand.getName().getString(); + + if (config.ballistaBuildWaypoints && name.contains("SNEAK + PUNCH")) { + ballistaBuildSpots.add(new Waypoint(armorStand.getBlockPos(), () -> Waypoint.Type.WAYPOINT, SUPPLIES_COLOR, false)); + } + } + } + + supplyWaypoints = supplies; + ballistaBuildWaypoints = ballistaBuildSpots; + fuelWaypoints = fuelCells; + } + } + + static void render(WorldRenderContext context) { + SkyblockerConfig.Kuudra config = SkyblockerConfigManager.get().locations.crimsonIsle.kuudra; + + if (Utils.isInKuudra() && loaded) { + if (config.supplyWaypoints) { + for (Waypoint waypoint : supplyWaypoints) { + waypoint.render(context); + } + } + + if (config.ballistaBuildWaypoints) { + for (Waypoint waypoint : ballistaBuildWaypoints) { + waypoint.render(context); + } + } + + if (config.fuelWaypoints) { + for (Waypoint waypoint : fuelWaypoints) { + waypoint.render(context); + } + } + + if (config.safeSpotWaypoints) { + for (Waypoint waypoint : SAFE_SPOT_WAYPOINTS) { + waypoint.render(context); + } + } + + //TODO maybe have "dynamic" waypoints that draw a line to the actual spot + if (config.pearlWaypoints) { + for (Waypoint waypoint : PEARL_WAYPOINTS) { + waypoint.render(context); + } + } + } + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/filters/DicerFilter.java b/src/main/java/de/hysky/skyblocker/skyblock/filters/DicerFilter.java new file mode 100644 index 00000000..a3f78684 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/filters/DicerFilter.java @@ -0,0 +1,15 @@ +package de.hysky.skyblocker.skyblock.filters; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.chat.ChatFilterResult; + +public class DicerFilter extends SimpleChatFilter { + public DicerFilter() { + super("[A-Z]+ DROP! .*Dicer dropped [0-9]+x.+!$"); + } + + @Override + public ChatFilterResult state() { + return SkyblockerConfigManager.get().messages.hideDicer; + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/garden/VisitorHelper.java b/src/main/java/de/hysky/skyblocker/skyblock/garden/VisitorHelper.java new file mode 100644 index 00000000..3c049f69 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/garden/VisitorHelper.java @@ -0,0 +1,162 @@ +package de.hysky.skyblocker.skyblock.garden; + +import de.hysky.skyblocker.skyblock.itemlist.ItemRepository; +import de.hysky.skyblocker.utils.NEURepoManager; +import de.hysky.skyblocker.utils.scheduler.MessageScheduler; +import io.github.moulberry.repo.data.NEUItem; +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import net.minecraft.client.font.TextRenderer; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtElement; +import net.minecraft.nbt.NbtList; +import net.minecraft.screen.ScreenHandler; +import net.minecraft.screen.slot.Slot; +import net.minecraft.text.Text; +import net.minecraft.text.Text.Serialization; +import net.minecraft.util.Formatting; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.text.NumberFormat; +import java.util.HashMap; +import java.util.Map; + +//TODO: check inventory items, sum all repeated items into one +public class VisitorHelper { + private static final Logger LOGGER = LoggerFactory.getLogger("Skyblocker Visitor Helper"); + + private static final Map<String, Object2IntMap<String>> itemMap = new HashMap<>(); + private static final Map<String, ItemStack> itemCache = new HashMap<>(); + private static final int TEXT_START_X = 4; + private static final int TEXT_START_Y = 4; + private static final int LINE_SPACING = 3; + + public static void renderScreen(String title, DrawContext context, TextRenderer textRenderer, ScreenHandler handler, int mouseX, int mouseY) { + if (handler.getCursorStack() == ItemStack.EMPTY) + processVisitorItem(title, handler); + drawScreenItems(context, textRenderer, mouseX, mouseY); + } + + public static void onMouseClicked(double mouseX, double mouseY, int mouseButton, TextRenderer textRenderer) { + int yPosition = TEXT_START_Y; + + for (Map.Entry<String, Object2IntMap<String>> visitorEntry : itemMap.entrySet()) { + int textWidth; + int textHeight = textRenderer.fontHeight; + + yPosition += LINE_SPACING + textHeight; + + for (Object2IntMap.Entry<String> itemEntry : visitorEntry.getValue().object2IntEntrySet()) { + String itemText = itemEntry.getKey(); + textWidth = textRenderer.getWidth(itemText); + + if (isMouseOverText(mouseX, mouseY, TEXT_START_X, yPosition, textWidth, textHeight)) { + MessageScheduler.INSTANCE.sendMessageAfterCooldown("/bz " + itemText); + return; + } + yPosition += LINE_SPACING + textHeight; + } + } + } + + public static void onSlotClick(Slot slot, int slotId, String title) { + if (slotId == 29 || slotId == 13) { + itemMap.remove(title); + } + } + + private static void processVisitorItem(String visitorName, ScreenHandler handler) { + ItemStack visitorItem = handler.getSlot(13).getStack(); + if (visitorItem == null || !visitorItem.hasNbt() || !visitorItem.getNbt().asString().contains("Times Visited")) return; + ItemStack acceptButton = handler.getSlot(29).getStack(); + if (acceptButton == null) return; + NbtCompound acceptButtonNbt = acceptButton.getSubNbt("display"); + if (acceptButtonNbt == null || !acceptButtonNbt.contains("Lore", NbtElement.LIST_TYPE)) return; + processLore(visitorName, acceptButtonNbt.getList("Lore", NbtElement.STRING_TYPE)); + } + + private static void processLore(String visitorName, NbtList loreList) { + boolean saveRequiredItems = false; + for (int i = 0; i < loreList.size(); i++) { + String lore = loreList.getString(i); + if (lore.contains("Items Required")) + saveRequiredItems = true; + else if (lore.contains("Rewards")) + break; + else if (saveRequiredItems) + updateItemMap(visitorName, lore); + } + } + + private static void updateItemMap(String visitorName, String lore) { + Text itemText = Serialization.fromJson(lore); + String[] splitItemText = itemText.getString().split(" x"); + String itemName = splitItemText[0].trim(); + if (itemName.isEmpty()) return; + try { + int amount = splitItemText.length == 2 ? NumberFormat.getInstance().parse(splitItemText[1].trim()).intValue() : 1; + Object2IntMap<String> visitorMap = itemMap.getOrDefault(visitorName, new Object2IntOpenHashMap<>()); + visitorMap.putIfAbsent(itemName, amount); + itemMap.putIfAbsent(visitorName, visitorMap); + } catch (Exception e) { + LOGGER.error("[Skyblocker Visitor Helper] Failed to parse item: " + itemText.getString(), e); + } + } + + private static void drawScreenItems(DrawContext context, TextRenderer textRenderer, int mouseX, int mouseY) { + int index = 0; + for (Map.Entry<String, Object2IntMap<String>> visitorEntry : itemMap.entrySet()) { + String visitorName = visitorEntry.getKey(); + drawTextWithOptionalUnderline(context, textRenderer, Text.literal(visitorName), TEXT_START_X, TEXT_START_Y + index * (LINE_SPACING + textRenderer.fontHeight), mouseX, mouseY); + index++; + + for (Object2IntMap.Entry<String> itemEntry : visitorEntry.getValue().object2IntEntrySet()) { + index = drawItemEntryWithHover(context, textRenderer, itemEntry, index, mouseX, mouseY); + } + } + } + + private static int drawItemEntryWithHover(DrawContext context, TextRenderer textRenderer, Map.Entry<String, Integer> itemEntry, int index, int mouseX, int mouseY) { + String itemName = itemEntry.getKey(); + int amount = itemEntry.getValue(); + ItemStack stack = getCachedItem(itemName); + if (stack != null) { + drawItemEntryWithHover(context, textRenderer, stack, amount, index, mouseX, mouseY); + } + return index + 1; + } + + private static ItemStack getCachedItem(String displayName) { + String strippedName = Formatting.strip(displayName); + ItemStack cachedStack = itemCache.get(strippedName); + if (cachedStack != null) return cachedStack; + NEUItem neuItem = NEURepoManager.NEU_REPO.getItems().getItems().values().stream() + .filter(i -> Formatting.strip(i.getDisplayName()).equals(strippedName)) + .findFirst() + .orElse(null); + if (neuItem == null) return null; + ItemStack stack = ItemRepository.getItemStack(neuItem.getSkyblockItemId()); + itemCache.put(strippedName, stack); + return stack; + } + + private static void drawItemEntryWithHover(DrawContext context, TextRenderer textRenderer, ItemStack stack, int amount, int index, int mouseX, int mousseY) { + Text text = Serialization.fromJson(stack.getSubNbt("display").getString("Name")).append(" x" + amount); + drawTextWithOptionalUnderline(context, textRenderer, text, TEXT_START_X + 8, TEXT_START_Y + (index * (LINE_SPACING + textRenderer.fontHeight)), mouseX, mousseY); + context.drawItem(stack, TEXT_START_X + 10 + textRenderer.getWidth(text), TEXT_START_Y + (index * (LINE_SPACING + textRenderer.fontHeight)) - textRenderer.fontHeight + 5); + } + + private static void drawTextWithOptionalUnderline(DrawContext context, TextRenderer textRenderer, Text text, int x, int y, int mouseX, int mouseY) { + context.drawText(textRenderer, text, x, y, -1, true); + if (isMouseOverText(mouseX, mouseY, x, y, textRenderer.getWidth(text), textRenderer.fontHeight)) { + context.drawHorizontalLine(x, x + textRenderer.getWidth(text), y + textRenderer.fontHeight, -1); + } + } + + private static boolean isMouseOverText(double mouseX, double mouseY, int x, int y, int width, int height) { + return mouseX >= x && mouseX <= x + width && mouseY >= y && mouseY <= y + height; + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/PosUtils.java b/src/main/java/de/hysky/skyblocker/utils/PosUtils.java index 6a34b060..73ada889 100644 --- a/src/main/java/de/hysky/skyblocker/utils/PosUtils.java +++ b/src/main/java/de/hysky/skyblocker/utils/PosUtils.java @@ -1,8 +1,17 @@ package de.hysky.skyblocker.utils; +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; + import net.minecraft.util.math.BlockPos; public final class PosUtils { + public static final Codec<BlockPos> ALT_BLOCK_POS_CODEC = RecordCodecBuilder.create(instance -> instance.group( + Codec.INT.fieldOf("x").forGetter(BlockPos::getX), + Codec.INT.fieldOf("y").forGetter(BlockPos::getY), + Codec.INT.fieldOf("z").forGetter(BlockPos::getZ)) + .apply(instance, BlockPos::new)); + public static BlockPos parsePosString(String posData) { String[] posArray = posData.split(","); return new BlockPos(Integer.parseInt(posArray[0]), Integer.parseInt(posArray[1]), Integer.parseInt(posArray[2])); diff --git a/src/main/java/de/hysky/skyblocker/utils/Utils.java b/src/main/java/de/hysky/skyblocker/utils/Utils.java index 0c95985e..cd739a0c 100644 --- a/src/main/java/de/hysky/skyblocker/utils/Utils.java +++ b/src/main/java/de/hysky/skyblocker/utils/Utils.java @@ -3,6 +3,7 @@ package de.hysky.skyblocker.utils; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import de.hysky.skyblocker.events.SkyblockEvents; +import de.hysky.skyblocker.skyblock.crimson.kuudra.Kuudra; import de.hysky.skyblocker.skyblock.item.MuseumItemCache; import de.hysky.skyblocker.skyblock.item.tooltip.ItemTooltip; import de.hysky.skyblocker.skyblock.rift.TheRift; @@ -110,6 +111,10 @@ public class Utils { return getLocationRaw().equals("combat_3"); } + public static boolean isInKuudra() { + return getLocationRaw().equals(Kuudra.LOCATION); + } + public static boolean isInjected() { return isInjected; } diff --git a/src/main/java/de/hysky/skyblocker/utils/chat/ChatMessageListener.java b/src/main/java/de/hysky/skyblocker/utils/chat/ChatMessageListener.java index ee43bc4c..bdff2d94 100644 --- a/src/main/java/de/hysky/skyblocker/utils/chat/ChatMessageListener.java +++ b/src/main/java/de/hysky/skyblocker/utils/chat/ChatMessageListener.java @@ -56,7 +56,8 @@ public interface ChatMessageListener { new ShowOffFilter(), new ToggleSkyMallFilter(), new MimicFilter(), - new DeathFilter() + new DeathFilter(), + new DicerFilter() }; // Register all listeners to EVENT for (ChatMessageListener listener : listeners) { diff --git a/src/main/java/de/hysky/skyblocker/utils/waypoint/Waypoint.java b/src/main/java/de/hysky/skyblocker/utils/waypoint/Waypoint.java index 2f9c9f63..7f3d4eda 100644 --- a/src/main/java/de/hysky/skyblocker/utils/waypoint/Waypoint.java +++ b/src/main/java/de/hysky/skyblocker/utils/waypoint/Waypoint.java @@ -1,13 +1,14 @@ package de.hysky.skyblocker.utils.waypoint; import de.hysky.skyblocker.utils.render.RenderHelper; +import de.hysky.skyblocker.utils.render.Renderable; import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Box; import java.util.function.Supplier; -public class Waypoint { +public class Waypoint implements Renderable { protected static final float DEFAULT_HIGHLIGHT_ALPHA = 0.5f; protected static final float DEFAULT_LINE_WIDTH = 5f; public final BlockPos pos; @@ -74,6 +75,7 @@ public class Waypoint { return colorComponents; } + @Override public void render(WorldRenderContext context) { switch (typeSupplier.get()) { case WAYPOINT -> RenderHelper.renderFilledWithBeaconBeam(context, pos, getColorComponents(), alpha, throughWalls); |