diff options
Diffstat (limited to 'src/main/java/de/hysky/skyblocker/utils')
10 files changed, 349 insertions, 238 deletions
diff --git a/src/main/java/de/hysky/skyblocker/utils/container/AbstractContainerMatcher.java b/src/main/java/de/hysky/skyblocker/utils/container/AbstractContainerMatcher.java new file mode 100644 index 00000000..5509b9a4 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/container/AbstractContainerMatcher.java @@ -0,0 +1,17 @@ +package de.hysky.skyblocker.utils.container; + +import net.minecraft.client.gui.screen.ingame.HandledScreen; +import org.jetbrains.annotations.NotNull; + +public interface AbstractContainerMatcher { + /** + * Tests if the given screen should be handled by this matcher. + * @return {@code true} if this matcher should apply to the given screen, {@code false} otherwise + */ + boolean test(@NotNull HandledScreen<?> screen); + + /** + * @return {@code true} if this matcher is enabled, {@code false} otherwise + */ + boolean isEnabled(); +} diff --git a/src/main/java/de/hysky/skyblocker/utils/container/AbstractContainerSolver.java b/src/main/java/de/hysky/skyblocker/utils/container/AbstractContainerSolver.java new file mode 100644 index 00000000..38bc32dd --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/container/AbstractContainerSolver.java @@ -0,0 +1,37 @@ +package de.hysky.skyblocker.utils.container; + +import de.hysky.skyblocker.SkyblockerMod; +import de.hysky.skyblocker.utils.render.gui.ColorHighlight; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import net.minecraft.client.gui.screen.ingame.GenericContainerScreen; +import net.minecraft.item.ItemStack; + +import java.util.List; + +public interface AbstractContainerSolver extends AbstractContainerMatcher { + + List<ColorHighlight> getColors(Int2ObjectMap<ItemStack> slots); + + default void start(GenericContainerScreen screen) {} + + default void reset() {} + + default boolean onClickSlot(int slot, ItemStack stack, int screenId) { + return false; + } + + static void markHighlightsDirty() { + SkyblockerMod.getInstance().containerSolverManager.markDirty(); + } + + static void trimEdges(Int2ObjectMap<ItemStack> slots, int rows) { + for (int i = 0; i < rows; i++) { + slots.remove(9 * i); + slots.remove(9 * i + 8); + } + for (int i = 1; i < 8; i++) { + slots.remove(i); + slots.remove((rows - 1) * 9 + i); + } + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/container/AbstractSlotTextAdder.java b/src/main/java/de/hysky/skyblocker/utils/container/AbstractSlotTextAdder.java new file mode 100644 index 00000000..4034e88d --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/container/AbstractSlotTextAdder.java @@ -0,0 +1,21 @@ +package de.hysky.skyblocker.utils.container; + +import de.hysky.skyblocker.skyblock.item.slottext.SlotText; +import net.minecraft.screen.slot.Slot; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public interface AbstractSlotTextAdder extends AbstractContainerMatcher { + + /** + * This method will be called for each rendered slot. Consider using a switch statement on {@link Slot#id} if you wish to add different text to different slots. + * + * @return A list of positioned text to be rendered. Return {@link List#of()} if no text should be rendered. + * @implNote By minecraft's design, scaled text inexplicably moves around. + * It's also not anti-aliased, so it looks horribly jagged and unreadable when scaled down too much. + * So, limit your text to 3 characters (or roughly less than 20 width) if you want it to not look horrible. + */ + @NotNull + List<SlotText> getText(Slot slot); +} diff --git a/src/main/java/de/hysky/skyblocker/utils/container/AbstractTooltipAdder.java b/src/main/java/de/hysky/skyblocker/utils/container/AbstractTooltipAdder.java new file mode 100644 index 00000000..3cc84f35 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/container/AbstractTooltipAdder.java @@ -0,0 +1,18 @@ +package de.hysky.skyblocker.utils.container; + +import net.minecraft.item.ItemStack; +import net.minecraft.screen.slot.Slot; +import net.minecraft.text.Text; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public interface AbstractTooltipAdder extends AbstractContainerMatcher { + /** + * @implNote The first element of the lines list holds the item's display name, + * as it's a list of all lines that will be displayed in the tooltip. + */ + void addToTooltip(@Nullable Slot focusedSlot, ItemStack stack, List<Text> lines); + + int getPriority(); +} diff --git a/src/main/java/de/hysky/skyblocker/utils/container/ContainerSolver.java b/src/main/java/de/hysky/skyblocker/utils/container/ContainerSolver.java new file mode 100644 index 00000000..6d3240ea --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/container/ContainerSolver.java @@ -0,0 +1,38 @@ +package de.hysky.skyblocker.utils.container; + +import org.intellij.lang.annotations.Language; +import org.jetbrains.annotations.NotNull; + +import java.util.regex.Pattern; + +/** + * Simple implementation of a container solver. Extend this class to add a new gui solver, + * like terminal solvers or experiment solvers and add it to {@link ContainerSolverManager#solvers}. + */ +public abstract class ContainerSolver extends RegexContainerMatcher implements AbstractContainerSolver { + /** + * Utility constructor that will compile the given string into a pattern. + * + * @see #ContainerSolver(Pattern) + */ + protected ContainerSolver(@NotNull @Language("RegExp") String titlePattern) { + super(titlePattern); + } + + /** + * Creates a ContainerSolver that will be applied to screens with titles that match the given pattern. + * + * @param titlePattern The pattern to match the screen title against. + */ + protected ContainerSolver(@NotNull Pattern titlePattern) { + super(titlePattern); + } + + // A container solver that applies to every screen doesn't make sense, + // so we don't provide a constructor for that and force getTitlePattern to be @NotNull + @Override + public @NotNull Pattern getTitlePattern() { + assert super.getTitlePattern() != null; + return super.getTitlePattern(); + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/container/ContainerSolverManager.java b/src/main/java/de/hysky/skyblocker/utils/container/ContainerSolverManager.java new file mode 100644 index 00000000..77833a67 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/container/ContainerSolverManager.java @@ -0,0 +1,150 @@ +package de.hysky.skyblocker.utils.container; + +import com.mojang.blaze3d.systems.RenderSystem; +import de.hysky.skyblocker.mixins.accessors.HandledScreenAccessor; +import de.hysky.skyblocker.skyblock.accessories.newyearcakes.NewYearCakeBagHelper; +import de.hysky.skyblocker.skyblock.accessories.newyearcakes.NewYearCakesHelper; +import de.hysky.skyblocker.skyblock.chocolatefactory.ChocolateFactorySolver; +import de.hysky.skyblocker.skyblock.dungeon.CroesusHelper; +import de.hysky.skyblocker.skyblock.dungeon.CroesusProfit; +import de.hysky.skyblocker.skyblock.dungeon.terminal.ColorTerminal; +import de.hysky.skyblocker.skyblock.dungeon.terminal.LightsOnTerminal; +import de.hysky.skyblocker.skyblock.dungeon.terminal.OrderTerminal; +import de.hysky.skyblocker.skyblock.dungeon.terminal.StartsWithTerminal; +import de.hysky.skyblocker.skyblock.experiment.ChronomatronSolver; +import de.hysky.skyblocker.skyblock.experiment.SuperpairsSolver; +import de.hysky.skyblocker.skyblock.experiment.UltrasequencerSolver; +import de.hysky.skyblocker.utils.Utils; +import de.hysky.skyblocker.utils.render.gui.ColorHighlight; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectRBTreeMap; +import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.screen.ingame.GenericContainerScreen; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.item.ItemStack; +import net.minecraft.screen.slot.Slot; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + * Manager class for {@link ContainerSolver}s like terminal solvers and experiment solvers. To add a new gui solver, extend {@link ContainerSolver} and register it in {@link #ContainerSolverManager()}. + */ +public class ContainerSolverManager { + private final AbstractContainerSolver[] solvers; + private AbstractContainerSolver currentSolver = null; + private List<ColorHighlight> highlights; + /** + * Useful for keeping track of a solver's state in a Screen instance, such as if Hypixel closes & reopens a screen after every click (as they do with terminals). + */ + private int screenId = 0; + + public ContainerSolverManager() { + solvers = new AbstractContainerSolver[]{ + new ColorTerminal(), + new OrderTerminal(), + new StartsWithTerminal(), + new LightsOnTerminal(), + new CroesusHelper(), + new CroesusProfit(), + new ChronomatronSolver(), + new SuperpairsSolver(), + UltrasequencerSolver.INSTANCE, + new NewYearCakeBagHelper(), + NewYearCakesHelper.INSTANCE, + new ChocolateFactorySolver() + }; + } + + public AbstractContainerSolver getCurrentSolver() { + return currentSolver; + } + + public void init() { + ScreenEvents.BEFORE_INIT.register((client, screen, scaledWidth, scaledHeight) -> { + if (Utils.isOnSkyblock() && screen instanceof GenericContainerScreen genericContainerScreen) { + ScreenEvents.afterRender(screen).register((screen1, context, mouseX, mouseY, delta) -> { + MatrixStack matrices = context.getMatrices(); + matrices.push(); + matrices.translate(((HandledScreenAccessor) genericContainerScreen).getX(), ((HandledScreenAccessor) genericContainerScreen).getY(), 300); + onDraw(context, genericContainerScreen.getScreenHandler().slots.subList(0, genericContainerScreen.getScreenHandler().getRows() * 9)); + matrices.pop(); + }); + ScreenEvents.remove(screen).register(screen1 -> clearScreen()); + onSetScreen(genericContainerScreen); + } else { + clearScreen(); + } + }); + } + + public void onSetScreen(@NotNull GenericContainerScreen screen) { + String screenName = screen.getTitle().getString(); + for (AbstractContainerSolver solver : solvers) { + if (solver.isEnabled()) { + if (solver instanceof ContainerSolver containerSolver && containerSolver.test(screenName)) { + ++screenId; + currentSolver = containerSolver; + currentSolver.start(screen); + markDirty(); + + return; + } else if (solver.test(screen)) { + ++screenId; + currentSolver = solver; + currentSolver.start(screen); + markDirty(); + + return; + } + } + } + clearScreen(); + } + + public void clearScreen() { + if (currentSolver != null) { + currentSolver.reset(); + currentSolver = null; + } + } + + public void markDirty() { + highlights = null; + } + + /** + * @return Whether the click should be disallowed. + */ + public boolean onSlotClick(int slot, ItemStack stack) { + if (currentSolver != null) { + return currentSolver.onClickSlot(slot, stack, screenId); + } + + return false; + } + + public void onDraw(DrawContext context, List<Slot> slots) { + if (currentSolver == null) + return; + if (highlights == null) + highlights = currentSolver.getColors(slotMap(slots)); + RenderSystem.enableDepthTest(); + RenderSystem.colorMask(true, true, true, false); + for (ColorHighlight highlight : highlights) { + Slot slot = slots.get(highlight.slot()); + int color = highlight.color(); + context.fill(slot.x, slot.y, slot.x + 16, slot.y + 16, color); + } + RenderSystem.colorMask(true, true, true, true); + } + + private Int2ObjectMap<ItemStack> slotMap(List<Slot> slots) { + Int2ObjectMap<ItemStack> slotMap = new Int2ObjectRBTreeMap<>(); + for (int i = 0; i < slots.size(); i++) { + slotMap.put(i, slots.get(i).getStack()); + } + return slotMap; + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/container/RegexContainerMatcher.java b/src/main/java/de/hysky/skyblocker/utils/container/RegexContainerMatcher.java new file mode 100644 index 00000000..a8a8e3c2 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/container/RegexContainerMatcher.java @@ -0,0 +1,68 @@ +package de.hysky.skyblocker.utils.container; + +import de.hysky.skyblocker.skyblock.ChestValue; +import net.minecraft.client.gui.screen.ingame.HandledScreen; +import org.intellij.lang.annotations.Language; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * A regex implementation of {@link AbstractContainerMatcher} that matches the title of the screen. + */ +public abstract class RegexContainerMatcher implements AbstractContainerMatcher { + /** + * The title of the screen must match this pattern for this to be applied. Null means it will be applied to all screens. + * + * @implNote Don't end your regex with a {@code $} as {@link ChestValue} appends text to the end of the title, + * so the regex will stop matching if the player uses chest value. + */ + @Nullable + public final Pattern titlePattern; + + @Nullable + public String[] groups = null; + + @Override + public boolean test(@NotNull HandledScreen<?> screen) { + return test(screen.getTitle().getString()); + } + + public boolean test(@NotNull String title) { + if (titlePattern == null) return true; + Matcher matcher = titlePattern.matcher(title); + if (matcher.matches()) { + int groupCount = matcher.groupCount(); + if (groupCount >= 1) { //No need to initialize the array if there are no groups + groups = new String[groupCount]; + for (int i = 0; i < groupCount; i++) { + groups[i] = matcher.group(i + 1); // +1 because first group is the whole match, which is useless + } + } + return true; + } + return false; + } + + protected RegexContainerMatcher() { + this((Pattern) null); + } + + protected RegexContainerMatcher(@NotNull @Language("RegExp") String titlePattern) { + this(Pattern.compile(titlePattern)); + } + + protected RegexContainerMatcher(@Nullable Pattern titlePattern) { + this.titlePattern = titlePattern; + } + + public @Nullable Pattern getTitlePattern() { + return titlePattern; + } + + public final @Nullable String[] getGroups() { + return groups; + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/render/gui/AbstractContainerMatcher.java b/src/main/java/de/hysky/skyblocker/utils/render/gui/AbstractContainerMatcher.java deleted file mode 100644 index bf255218..00000000 --- a/src/main/java/de/hysky/skyblocker/utils/render/gui/AbstractContainerMatcher.java +++ /dev/null @@ -1,29 +0,0 @@ -package de.hysky.skyblocker.utils.render.gui; - -import de.hysky.skyblocker.skyblock.ChestValue; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.regex.Pattern; - -public abstract class AbstractContainerMatcher { - /** - * The title of the screen must match this pattern for this to be applied. Null means it will be applied to all screens. - * @implNote Don't end your regex with a {@code $} as {@link ChestValue} appends text to the end of the title, - * so the regex will stop matching if the player uses chest value. - */ - @Nullable - public final Pattern titlePattern; - - protected AbstractContainerMatcher() { - this((Pattern) null); - } - - protected AbstractContainerMatcher(@NotNull String titlePattern) { - this(Pattern.compile(titlePattern)); - } - - protected AbstractContainerMatcher(@Nullable Pattern titlePattern) { - this.titlePattern = titlePattern; - } -} diff --git a/src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolver.java b/src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolver.java index 9a9d0907..e69de29b 100644 --- a/src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolver.java +++ b/src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolver.java @@ -1,52 +0,0 @@ -package de.hysky.skyblocker.utils.render.gui; - -import de.hysky.skyblocker.SkyblockerMod; -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; -import net.minecraft.client.gui.screen.ingame.GenericContainerScreen; -import net.minecraft.item.ItemStack; -import org.intellij.lang.annotations.Language; - -import java.util.List; -import java.util.regex.Pattern; - -/** - * Abstract class for gui solvers. Extend this class to add a new gui solver, like terminal solvers or experiment solvers. - */ -public abstract class ContainerSolver extends AbstractContainerMatcher { - protected ContainerSolver(@Language("RegExp") String titlePattern) { - super(titlePattern); - } - - protected abstract boolean isEnabled(); - - public final Pattern getName() { - return titlePattern; - } - - protected void start(GenericContainerScreen screen) { - } - - protected void reset() { - } - - protected void markHighlightsDirty() { - SkyblockerMod.getInstance().containerSolverManager.markDirty(); - } - - protected boolean onClickSlot(int slot, ItemStack stack, int screenId, String[] groups) { - return false; - } - - protected abstract List<ColorHighlight> getColors(String[] groups, Int2ObjectMap<ItemStack> slots); - - protected final void trimEdges(Int2ObjectMap<ItemStack> slots, int rows) { - for (int i = 0; i < rows; i++) { - slots.remove(9 * i); - slots.remove(9 * i + 8); - } - for (int i = 1; i < 8; i++) { - slots.remove(i); - slots.remove((rows - 1) * 9 + i); - } - } -} diff --git a/src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolverManager.java b/src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolverManager.java index 79cc78f5..e69de29b 100644 --- a/src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolverManager.java +++ b/src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolverManager.java @@ -1,157 +0,0 @@ -package de.hysky.skyblocker.utils.render.gui; - -import com.mojang.blaze3d.systems.RenderSystem; -import de.hysky.skyblocker.mixins.accessors.HandledScreenAccessor; -import de.hysky.skyblocker.skyblock.accessories.newyearcakes.NewYearCakeBagHelper; -import de.hysky.skyblocker.skyblock.accessories.newyearcakes.NewYearCakesHelper; -import de.hysky.skyblocker.skyblock.bazaar.ReorderHelper; -import de.hysky.skyblocker.skyblock.chocolatefactory.ChocolateFactorySolver; -import de.hysky.skyblocker.skyblock.dungeon.CroesusHelper; -import de.hysky.skyblocker.skyblock.dungeon.CroesusProfit; -import de.hysky.skyblocker.skyblock.dungeon.terminal.ColorTerminal; -import de.hysky.skyblocker.skyblock.dungeon.terminal.LightsOnTerminal; -import de.hysky.skyblocker.skyblock.dungeon.terminal.OrderTerminal; -import de.hysky.skyblocker.skyblock.dungeon.terminal.StartsWithTerminal; -import de.hysky.skyblocker.skyblock.dwarven.CommissionHighlight; -import de.hysky.skyblocker.skyblock.experiment.ChronomatronSolver; -import de.hysky.skyblocker.skyblock.experiment.SuperpairsSolver; -import de.hysky.skyblocker.skyblock.experiment.UltrasequencerSolver; -import de.hysky.skyblocker.utils.Utils; -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectRBTreeMap; -import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.gui.screen.ingame.GenericContainerScreen; -import net.minecraft.client.util.math.MatrixStack; -import net.minecraft.item.ItemStack; -import net.minecraft.screen.slot.Slot; -import org.jetbrains.annotations.NotNull; - -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Manager class for {@link ContainerSolver}s like terminal solvers and experiment solvers. To add a new gui solver, extend {@link ContainerSolver} and register it in {@link #ContainerSolverManager()}. - */ -public class ContainerSolverManager { - private static final Pattern PLACEHOLDER_PATTERN = Pattern.compile(""); - private final ContainerSolver[] solvers; - private ContainerSolver currentSolver = null; - private String[] groups; - private List<ColorHighlight> highlights; - /** - * Useful for keeping track of a solver's state in a Screen instance, such as if Hypixel closes & reopens a screen after every click (as they do with terminals). - */ - private int screenId = 0; - - public ContainerSolverManager() { - solvers = new ContainerSolver[]{ - new ColorTerminal(), - new OrderTerminal(), - new StartsWithTerminal(), - new LightsOnTerminal(), - new CroesusHelper(), - new CommissionHighlight(), - new CroesusProfit(), - new ChronomatronSolver(), - new SuperpairsSolver(), - UltrasequencerSolver.INSTANCE, - new NewYearCakeBagHelper(), - NewYearCakesHelper.INSTANCE, - new ChocolateFactorySolver(), - new ReorderHelper() - }; - } - - public ContainerSolver getCurrentSolver() { - return currentSolver; - } - - public void init() { - ScreenEvents.BEFORE_INIT.register((client, screen, scaledWidth, scaledHeight) -> { - if (Utils.isOnSkyblock() && screen instanceof GenericContainerScreen genericContainerScreen) { - ScreenEvents.afterRender(screen).register((screen1, context, mouseX, mouseY, delta) -> { - MatrixStack matrices = context.getMatrices(); - matrices.push(); - matrices.translate(((HandledScreenAccessor) genericContainerScreen).getX(), ((HandledScreenAccessor) genericContainerScreen).getY(), 300); - onDraw(context, genericContainerScreen.getScreenHandler().slots.subList(0, genericContainerScreen.getScreenHandler().getRows() * 9)); - matrices.pop(); - }); - ScreenEvents.remove(screen).register(screen1 -> clearScreen()); - onSetScreen(genericContainerScreen); - } else { - clearScreen(); - } - }); - } - - public void onSetScreen(@NotNull GenericContainerScreen screen) { - String screenName = screen.getTitle().getString(); - Matcher matcher = PLACEHOLDER_PATTERN.matcher(screenName); - for (ContainerSolver solver : solvers) { - if (solver.isEnabled()) { - matcher.usePattern(solver.getName()); - matcher.reset(); - if (matcher.matches()) { - ++screenId; - currentSolver = solver; - groups = new String[matcher.groupCount()]; - for (int i = 0; i < groups.length; i++) { - groups[i] = matcher.group(i + 1); - } - currentSolver.start(screen); - markDirty(); - - return; - } - } - } - clearScreen(); - } - - public void clearScreen() { - if (currentSolver != null) { - currentSolver.reset(); - currentSolver = null; - } - } - - public void markDirty() { - highlights = null; - } - - /** - * @return Whether the click should be disallowed. - */ - public boolean onSlotClick(int slot, ItemStack stack) { - if (currentSolver != null) { - return currentSolver.onClickSlot(slot, stack, screenId, groups); - } - - return false; - } - - public void onDraw(DrawContext context, List<Slot> slots) { - if (currentSolver == null) - return; - if (highlights == null) - highlights = currentSolver.getColors(groups, slotMap(slots)); - RenderSystem.enableDepthTest(); - RenderSystem.colorMask(true, true, true, false); - for (ColorHighlight highlight : highlights) { - Slot slot = slots.get(highlight.slot()); - int color = highlight.color(); - context.fill(slot.x, slot.y, slot.x + 16, slot.y + 16, color); - } - RenderSystem.colorMask(true, true, true, true); - } - - private Int2ObjectMap<ItemStack> slotMap(List<Slot> slots) { - Int2ObjectMap<ItemStack> slotMap = new Int2ObjectRBTreeMap<>(); - for (int i = 0; i < slots.size(); i++) { - slotMap.put(i, slots.get(i).getStack()); - } - return slotMap; - } -} |