diff --git a/src/main/java/de/hysky/skyblocker/skyblock/experiment/ChronomatronSolver.java b/src/main/java/de/hysky/skyblocker/skyblock/experiment/ChronomatronSolver.java index fe63b0a6d0..14bd3278f9 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/experiment/ChronomatronSolver.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/experiment/ChronomatronSolver.java @@ -27,26 +27,27 @@ public final class ChronomatronSolver extends ExperimentSolver { ) ); + /** + * The list of items to remember, in order. + */ private final List chronomatronSlots = new ArrayList<>(); + /** + * The index of the current item shown in the chain, used for remembering. + */ private int chronomatronChainLengthCount; + /** + * The slot id of the current item shown, used for detecting when the experiment finishes showing the current item. + */ private int chronomatronCurrentSlot; + /** + * The next index in the chain to click. + */ private int chronomatronCurrentOrdinal; public ChronomatronSolver() { super("^Chronomatron \\(\\w+\\)$"); } - @Override - public boolean onClickSlot(int slot, ItemStack stack, int screenId) { - if (getState() == State.SHOW) { - Item item = chronomatronSlots.get(chronomatronCurrentOrdinal); - if ((stack.isOf(item) || ChronomatronSolver.TERRACOTTA_TO_GLASS.get(stack.getItem()) == item) && ++chronomatronCurrentOrdinal >= chronomatronSlots.size()) { - setState(ExperimentSolver.State.END); - } - } - return super.onClickSlot(slot, stack, screenId); - } - @Override protected boolean isEnabled(HelperConfig.Experiments experimentsConfig) { return experimentsConfig.enableChronomatronSolver; @@ -57,19 +58,24 @@ protected void tick(GenericContainerScreen screen) { switch (getState()) { case REMEMBER -> { Inventory inventory = screen.getScreenHandler().getInventory(); + // Only try to look for items with enchantment glint if there is no item being currently shown. if (chronomatronCurrentSlot == 0) { for (int index = 10; index < 43; index++) { if (inventory.getStack(index).hasGlint()) { + // If the list of items is smaller than the index of the current item shown, add the item to the list and set the state to wait. if (chronomatronSlots.size() <= chronomatronChainLengthCount) { chronomatronSlots.add(TERRACOTTA_TO_GLASS.get(inventory.getStack(index).getItem())); setState(State.WAIT); } else { + // If the item is already in the list, increment the current item shown index. chronomatronChainLengthCount++; } + // Remember the slot shown to detect when the experiment finishes showing the current item. chronomatronCurrentSlot = index; return; } } + // If the current item shown no longer has enchantment glint, the experiment finished showing the current item. } else if (!inventory.getStack(chronomatronCurrentSlot).hasGlint()) { chronomatronCurrentSlot = 0; } @@ -82,6 +88,7 @@ protected void tick(GenericContainerScreen screen) { case END -> { String name = screen.getScreenHandler().getInventory().getStack(49).getName().getString(); if (!name.startsWith("Timer: ")) { + // Get ready for another round if the instructions say to remember the pattern. if (name.equals("Remember the pattern!")) { chronomatronChainLengthCount = 0; chronomatronCurrentOrdinal = 0; @@ -94,6 +101,9 @@ protected void tick(GenericContainerScreen screen) { } } + /** + * Highlights the slots that contain the item at index {@link #chronomatronCurrentOrdinal} of {@link #chronomatronSlots} in the chain. + */ @Override public List getColors(Int2ObjectMap slots) { List highlights = new ArrayList<>(); @@ -110,6 +120,20 @@ public List getColors(Int2ObjectMap slots) { return highlights; } + /** + * Increments {@link #chronomatronCurrentOrdinal} if the item clicked matches the item at {@link #chronomatronCurrentOrdinal the current index} in the chain. + */ + @Override + public boolean onClickSlot(int slot, ItemStack stack, int screenId) { + if (getState() == State.SHOW) { + Item item = chronomatronSlots.get(chronomatronCurrentOrdinal); + if ((stack.isOf(item) || ChronomatronSolver.TERRACOTTA_TO_GLASS.get(stack.getItem()) == item) && ++chronomatronCurrentOrdinal >= chronomatronSlots.size()) { + setState(ExperimentSolver.State.END); + } + } + return super.onClickSlot(slot, stack, screenId); + } + @Override public void reset() { chronomatronSlots.clear(); diff --git a/src/main/java/de/hysky/skyblocker/skyblock/experiment/ExperimentSolver.java b/src/main/java/de/hysky/skyblocker/skyblock/experiment/ExperimentSolver.java index 931052b5cd..9172f550bc 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/experiment/ExperimentSolver.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/experiment/ExperimentSolver.java @@ -11,11 +11,10 @@ import org.intellij.lang.annotations.Language; import org.jetbrains.annotations.NotNull; +/** + * The general class for all experiment solvers, implemented with a state machine. + */ public abstract sealed class ExperimentSolver extends SimpleContainerSolver permits ChronomatronSolver, SuperpairsSolver, UltrasequencerSolver { - public enum State { - REMEMBER, WAIT, SHOW, END - } - private State state = State.REMEMBER; private final Int2ObjectMap slots = new Int2ObjectOpenHashMap<>(); @@ -56,4 +55,23 @@ public void reset() { } protected abstract void tick(GenericContainerScreen screen); + + public enum State { + /** + * The state where the player has to remember the pattern. + */ + REMEMBER, + /** + * The state where the solver has remembered the pattern and is waiting for the show state. + */ + WAIT, + /** + * The state where the player has to recall the pattern and where the solver shows the player the pattern. + */ + SHOW, + /** + * The state where the player has finished recalling the pattern and the current round is over. + */ + END + } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/experiment/SuperpairsSolver.java b/src/main/java/de/hysky/skyblocker/skyblock/experiment/SuperpairsSolver.java index d9699351a0..31909564a6 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/experiment/SuperpairsSolver.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/experiment/SuperpairsSolver.java @@ -13,19 +13,19 @@ import java.util.List; public final class SuperpairsSolver extends ExperimentSolver { + /** + * The slot id of the last clicked slot. + */ private int superpairsPrevClickedSlot = 0; + /** + * The item stack of the last clicked slot. + */ private ItemStack superpairsCurrentSlot = ItemStack.EMPTY; + /** + * The set of slot ids that contain duplicated items. + */ private final IntSet superpairsDuplicatedSlots = new IntArraySet(28); - @Override - public boolean onClickSlot(int slot, ItemStack stack, int screenId) { - if (getState() == State.SHOW) { - this.superpairsPrevClickedSlot = slot; - this.superpairsCurrentSlot = ItemStack.EMPTY; - } - return super.onClickSlot(slot, stack, screenId); - } - public SuperpairsSolver() { super("^Superpairs \\(\\w+\\)$"); } @@ -35,37 +35,45 @@ protected boolean isEnabled(HelperConfig.Experiments experimentsConfig) { return experimentsConfig.enableSuperpairsSolver; } + /** + * Sets the state to {@link State#SHOW} since Superpairs does not require a state machine. + * @param screen + */ @Override public void start(GenericContainerScreen screen) { super.start(screen); setState(State.SHOW); } - @Override - public void reset() { - superpairsPrevClickedSlot = 0; - superpairsCurrentSlot = ItemStack.EMPTY; - superpairsDuplicatedSlots.clear(); - } - + /** + * Checks the screen if the item of the last clicked slot is unknown. + * Adds duplicated items to the item of the last clicked slot to {@link #superpairsDuplicatedSlots}, + * save the item of the last clicked slot to the slot map, + * and sets {@link #superpairsCurrentSlot} to the item of the last clicked slot. + */ @Override protected void tick(GenericContainerScreen screen) { if (getState() == State.SHOW && getSlots().get(superpairsPrevClickedSlot) == null) { ItemStack itemStack = screen.getScreenHandler().getInventory().getStack(superpairsPrevClickedSlot); if (!(itemStack.isOf(Items.CYAN_STAINED_GLASS) || itemStack.isOf(Items.BLACK_STAINED_GLASS_PANE) || itemStack.isOf(Items.AIR))) { getSlots().int2ObjectEntrySet().stream() - .filter(entry -> ItemStack.areEqual(entry.getValue(), itemStack)) - .findAny() - .ifPresent(entry -> { - superpairsDuplicatedSlots.add(entry.getIntKey()); - superpairsDuplicatedSlots.add(superpairsPrevClickedSlot); - }); + .filter(entry -> ItemStack.areEqual(entry.getValue(), itemStack)) + .findAny() + .ifPresent(entry -> { + superpairsDuplicatedSlots.add(entry.getIntKey()); + superpairsDuplicatedSlots.add(superpairsPrevClickedSlot); + }); getSlots().put(superpairsPrevClickedSlot, itemStack); superpairsCurrentSlot = itemStack; } } } + /** + * Displays a green highlight on the slot that matches {@link #superpairsCurrentSlot} and the experiment is waiting for a second click. + * Displays a yellow highlight on the slots that contain duplicated items. + * Displays a red highlight on the slots that do not match anything. + */ @Override public List getColors(Int2ObjectMap displaySlots) { List highlights = new ArrayList<>(); @@ -87,4 +95,20 @@ public List getColors(Int2ObjectMap displaySlots) { } return highlights; } + + @Override + public boolean onClickSlot(int slot, ItemStack stack, int screenId) { + if (getState() == State.SHOW) { + this.superpairsPrevClickedSlot = slot; + this.superpairsCurrentSlot = ItemStack.EMPTY; + } + return super.onClickSlot(slot, stack, screenId); + } + + @Override + public void reset() { + superpairsPrevClickedSlot = 0; + superpairsCurrentSlot = ItemStack.EMPTY; + superpairsDuplicatedSlots.clear(); + } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/experiment/UltrasequencerSolver.java b/src/main/java/de/hysky/skyblocker/skyblock/experiment/UltrasequencerSolver.java index 9d0df2a9b4..1b73156547 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/experiment/UltrasequencerSolver.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/experiment/UltrasequencerSolver.java @@ -8,26 +8,15 @@ import net.minecraft.inventory.Inventory; import net.minecraft.item.ItemStack; -import java.util.ArrayList; import java.util.List; public final class UltrasequencerSolver extends ExperimentSolver { public static final UltrasequencerSolver INSTANCE = new UltrasequencerSolver(); + /** + * The slot id of the next slot to click. + */ private int ultrasequencerNextSlot; - @Override - public boolean onClickSlot(int slot, ItemStack stack, int screenId) { - if (getState() == State.SHOW && slot == ultrasequencerNextSlot) { - int count = getSlots().get(ultrasequencerNextSlot).getCount() + 1; - getSlots().int2ObjectEntrySet().stream() - .filter(entry -> entry.getValue().getCount() == count) - .findAny() - .map(Int2ObjectMap.Entry::getIntKey) - .ifPresentOrElse(nextSlot -> this.ultrasequencerNextSlot = nextSlot, () -> setState(ExperimentSolver.State.END)); - } - return super.onClickSlot(slot, stack, screenId); - } - private UltrasequencerSolver() { super("^Ultrasequencer \\(\\w+\\)$"); } @@ -37,6 +26,10 @@ protected boolean isEnabled(HelperConfig.Experiments experimentsConfig) { return experimentsConfig.enableUltrasequencerSolver; } + /** + * Saves the shown items to {@link #slots the slot map}. + */ + @SuppressWarnings("JavadocReference") @Override protected void tick(GenericContainerScreen screen) { switch (getState()) { @@ -46,10 +39,13 @@ protected void tick(GenericContainerScreen screen) { for (int index = 9; index < 45; index++) { ItemStack itemStack = inventory.getStack(index); String name = itemStack.getName().getString(); + // Remember the item if its name is a number if (name.matches("\\d+")) { + // Set the next slot to click to the item with the name "1" if (name.equals("1")) { ultrasequencerNextSlot = index; } + // Save the item to the slot map getSlots().put(index, itemStack); } } @@ -78,6 +74,23 @@ protected void tick(GenericContainerScreen screen) { @Override public List getColors(Int2ObjectMap slots) { - return getState() == State.SHOW && ultrasequencerNextSlot != 0 ? List.of(ColorHighlight.green(ultrasequencerNextSlot)) : new ArrayList<>(); + return getState() == State.SHOW && ultrasequencerNextSlot != 0 ? List.of(ColorHighlight.green(ultrasequencerNextSlot)) : List.of(); + } + + /** + * Finds the next slot to click via searching for the item stack count in {@link #slots the slot map} and sets {@link #ultrasequencerNextSlot} if the current slot was clicked. + */ + @SuppressWarnings("JavadocReference") + @Override + public boolean onClickSlot(int slot, ItemStack stack, int screenId) { + if (getState() == State.SHOW && slot == ultrasequencerNextSlot) { + int count = getSlots().get(ultrasequencerNextSlot).getCount() + 1; + getSlots().int2ObjectEntrySet().stream() + .filter(entry -> entry.getValue().getCount() == count) + .findAny() + .map(Int2ObjectMap.Entry::getIntKey) + .ifPresentOrElse(nextSlot -> this.ultrasequencerNextSlot = nextSlot, () -> setState(ExperimentSolver.State.END)); + } + return super.onClickSlot(slot, stack, screenId); } }