diff --git a/build.gradle.kts b/build.gradle.kts index 6a761bc..f84e8fc 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -18,7 +18,6 @@ val baseGroup: String by project val mcVersion: String by project val modid: String by project val version: String by project -val mixinGroup = "$baseGroup.$modid.mixin" // Toolchains: java { @@ -31,9 +30,6 @@ loom { launchConfigs { "client" { property("devauth.enabled", "true") - property("mixin.debug", "true") - property("asmhelper.verbose", "true") - arg("--tweakClass", "org.spongepowered.asm.launch.MixinTweaker") } } runConfigs { @@ -59,10 +55,6 @@ loom { } forge { pack200Provider.set(dev.architectury.pack200.java.Pack200Adapter()) - mixinConfig("mixins.$modid.json") - } - mixin { - defaultRefmapName.set("mixins.$modid.refmap.json") } } @@ -82,11 +74,6 @@ dependencies { minecraft("com.mojang:minecraft:1.8.9") mappings("de.oceanlabs.mcp:mcp_stable:22-1.8.9") forge("net.minecraftforge:forge:1.8.9-11.15.1.2318-1.8.9") - - shadowImpl("org.spongepowered:mixin:0.7.11-SNAPSHOT") { - isTransitive = false - } - annotationProcessor("org.spongepowered:mixin:0.8.5-SNAPSHOT") runtimeOnly("me.djtheredstoner:DevAuth-forge-legacy:1.2.0") } @@ -101,10 +88,6 @@ tasks.withType(Jar::class) { manifest.attributes.run { this["FMLCorePluginContainsFMLMod"] = "true" this["ForceLoadAsMod"] = "true" - - // If you don't want mixins, remove these lines - this["TweakClass"] = "org.spongepowered.asm.launch.MixinTweaker" - this["MixinConfigs"] = "mixins.$modid.json" } } @@ -120,9 +103,8 @@ tasks.processResources { inputs.property("version", project.version) inputs.property("mcversion", mcVersion) inputs.property("modid", modid) - inputs.property("mixinGroup", mixinGroup) - filesMatching(listOf("mcmod.info", "mixins.$modid.json")) { + filesMatching("mcmod.info") { expand(inputs.properties) } diff --git a/src/main/java/ca/tirelesstraveler/fancywarpmenu/data/skyblockconstants/SkyBlockConstants.java b/src/main/java/ca/tirelesstraveler/fancywarpmenu/data/skyblockconstants/SkyBlockConstants.java index 94854b6..75c2517 100644 --- a/src/main/java/ca/tirelesstraveler/fancywarpmenu/data/skyblockconstants/SkyBlockConstants.java +++ b/src/main/java/ca/tirelesstraveler/fancywarpmenu/data/skyblockconstants/SkyBlockConstants.java @@ -22,8 +22,8 @@ package ca.tirelesstraveler.fancywarpmenu.data.skyblockconstants; +import ca.tirelesstraveler.fancywarpmenu.data.skyblockconstants.menu.ItemMatchCondition; import ca.tirelesstraveler.fancywarpmenu.data.skyblockconstants.menu.Menu; -import ca.tirelesstraveler.fancywarpmenu.data.skyblockconstants.menu.matchconditions.MenuMatchCondition; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; @@ -34,11 +34,10 @@ @SuppressWarnings("unused") public class SkyBlockConstants { - private SkyBlockConstants() { - } + public static final String WARP_COMMAND_BASE = "/warp"; - /** A map with SkyBlock menus as keys and lists of conditions used to identify them as values */ - private Map> menuMatchingMap; + /** Map of match conditions used to identify SkyBlock menus */ + private Map> menuMatchingMap; /** Chat messages sent by the server when a warp attempt succeeds or fails */ private WarpMessages warpMessages; @@ -47,10 +46,25 @@ private SkyBlockConstants() { /** Chat messages are checked to see if they start with this string in order to see if the player joined SkyBlock */ private String skyBlockJoinMessage; - public Map> getMenuMatchingMap() { + private SkyBlockConstants() { + } + + public Map> getMenuMatchingMap() { return menuMatchingMap; } + /** + * Returns the inventory slot index of the last {@link ItemMatchCondition} for the given {@link Menu}. + * + * @param menu the {@code Menu} to get the inventory slot index from + * @return the inventory slot index of the last {@code ItemMatchCondition} for the given {@code Menu} + */ + public int getLastMatchConditionInventorySlotIndex(Menu menu) { + List matchConditions = menuMatchingMap.get(menu); + + return matchConditions.get(matchConditions.size() - 1).getInventorySlotIndex(); + } + public WarpMessages getWarpMessages() { return warpMessages; } @@ -73,11 +87,11 @@ public static void validateSkyBlockConstants(SkyBlockConstants skyBlockConstants throw new NullPointerException("SkyBlock constants cannot be null"); } - for(Map.Entry> menuMatchingMapEntry : skyBlockConstants.menuMatchingMap.entrySet()) { - List matchConditions = getMenuMatchConditions(menuMatchingMapEntry); + for(Map.Entry> menuMatchingMapEntry : skyBlockConstants.menuMatchingMap.entrySet()) { + List matchConditions = getMenuMatchConditions(menuMatchingMapEntry); - for (MenuMatchCondition menuMatchCondition : matchConditions) { - menuMatchCondition.validateCondition(); + for (ItemMatchCondition matchCondition : matchConditions) { + matchCondition.validateCondition(); } } @@ -97,8 +111,8 @@ public static void validateSkyBlockConstants(SkyBlockConstants skyBlockConstants } @NotNull - private static List getMenuMatchConditions(Map.Entry> menuMatchingMapEntry) { - List matchConditions = menuMatchingMapEntry.getValue(); + private static List getMenuMatchConditions(Map.Entry> menuMatchingMapEntry) { + List matchConditions = menuMatchingMapEntry.getValue(); if (matchConditions == null) { throw new NullPointerException(String.format("Menu %s's menu match conditions list cannot be null", diff --git a/src/main/java/ca/tirelesstraveler/fancywarpmenu/data/skyblockconstants/menu/matchconditions/ItemMatchCondition.java b/src/main/java/ca/tirelesstraveler/fancywarpmenu/data/skyblockconstants/menu/ItemMatchCondition.java similarity index 76% rename from src/main/java/ca/tirelesstraveler/fancywarpmenu/data/skyblockconstants/menu/matchconditions/ItemMatchCondition.java rename to src/main/java/ca/tirelesstraveler/fancywarpmenu/data/skyblockconstants/menu/ItemMatchCondition.java index 18f2e02..708e362 100644 --- a/src/main/java/ca/tirelesstraveler/fancywarpmenu/data/skyblockconstants/menu/matchconditions/ItemMatchCondition.java +++ b/src/main/java/ca/tirelesstraveler/fancywarpmenu/data/skyblockconstants/menu/ItemMatchCondition.java @@ -20,7 +20,7 @@ * OR OTHER DEALINGS IN THE SOFTWARE. */ -package ca.tirelesstraveler.fancywarpmenu.data.skyblockconstants.menu.matchconditions; +package ca.tirelesstraveler.fancywarpmenu.data.skyblockconstants.menu; import net.minecraft.inventory.IInventory; import net.minecraft.item.ItemStack; @@ -28,15 +28,19 @@ import net.minecraft.nbt.NBTTagList; import net.minecraft.util.StringUtils; import net.minecraftforge.common.util.Constants; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import java.util.regex.Pattern; /** - * A {@link MenuMatchCondition} that checks if a given SkyBlock {@code GuiChest} menu contains an item with the same + * A match condition that checks if a given SkyBlock {@code GuiChest} menu contains an item with the same * item name, inventory slot index, Minecraft item ID, and SkyBlock item ID as the item specified in this condition. */ @SuppressWarnings({"FieldMayBeFinal", "FieldCanBeLocal", "unused"}) -public class ItemMatchCondition extends MenuMatchCondition { +public class ItemMatchCondition { + public static final Logger logger = LogManager.getLogger(); + /** Display name of the {@code ItemStack} (excluding formatting codes) */ private String itemName; /** The slot index of the slot the item occupies in the {@code IInventory} provided to {@link #inventoryContainsMatchingItem(IInventory)} */ @@ -100,35 +104,41 @@ public boolean inventoryContainsMatchingItem(IInventory inventory) { if (itemName != null) { itemNameMatches = itemStack.hasDisplayName() && - !StringUtils.stripControlCodes(itemStack.getDisplayName()).equals(itemName); + StringUtils.stripControlCodes(itemStack.getDisplayName()).equals(itemName); + logger.debug("Item name matches: {}", itemNameMatches); if (!itemNameMatches) return false; } if (minecraftItemID != null) { minecraftItemIDMatches = itemStack.getItem().getRegistryName().equals(minecraftItemID); + logger.debug("Minecraft registry ID matches: {}", minecraftItemIDMatches); if (!minecraftItemIDMatches) return false; } + // Following checks require NBT data, fail if NBT data not present + if (!itemStack.hasTagCompound()) return false; + if (skyBlockItemID != null) { - if (!itemStack.hasTagCompound() || !itemStack.getTagCompound().hasKey("ExtraAttributes", Constants.NBT.TAG_COMPOUND)) { + if (!itemStack.getTagCompound().hasKey("ExtraAttributes", Constants.NBT.TAG_COMPOUND)) { return false; } NBTTagCompound extraAttributesTag = itemStack.getSubCompound("ExtraAttributes", false); skyBlockItemIDMatches = extraAttributesTag.hasKey("id", Constants.NBT.TAG_STRING) && extraAttributesTag.getString("id").equals(skyBlockItemID); + logger.debug("SkyBlock Item ID matches: {}", skyBlockItemIDMatches); if (!skyBlockItemIDMatches) return false; } - if (loreMatchPattern != null) { - if (!itemStack.hasTagCompound() || !itemStack.getTagCompound().hasKey("display")) { + if (loreMatchPattern != null && loreMatchPattern.pattern() != null) { + if (!itemStack.getTagCompound().hasKey("display", Constants.NBT.TAG_COMPOUND)) { return false; } - NBTTagCompound displayTag = itemStack.getTagCompound().getCompoundTag("display"); + NBTTagCompound displayTag = itemStack.getSubCompound("display", false); if (displayTag.hasKey("Lore", Constants.NBT.TAG_LIST)) { NBTTagList loreTag = displayTag.getTagList("Lore", Constants.NBT.TAG_STRING); @@ -139,12 +149,12 @@ public boolean inventoryContainsMatchingItem(IInventory inventory) { for (int i = 0; i < loreTag.tagCount(); i++) { loreStringBuilder.append(loreTag.getStringTagAt(i)).append("\n"); } - - loreStringBuilder.setLength(loreStringBuilder.length() - 1); + loreStringBuilder.deleteCharAt(loreStringBuilder.length() - 1); String loreString = loreStringBuilder.toString(); lorePatternMatches = loreMatchPattern.asPredicate().test(loreString); + logger.debug("Lore pattern matches: {}", lorePatternMatches); return lorePatternMatches; } @@ -157,15 +167,23 @@ public boolean inventoryContainsMatchingItem(IInventory inventory) { return false; } + /** + * Verifies that this match condition's properties are valid. + * This is called for conditions that have just been deserialized. + */ public void validateCondition() { if (this.inventorySlotIndex <= -1) { throw new IllegalArgumentException("inventorySlotIndex must be greater than or equal to 0"); } - if (this.itemName == null && this.minecraftItemID == null && - this.skyBlockItemID == null && this.loreMatchPattern == null) { - throw new IllegalArgumentException("No item name, Minecraft item ID, SkyBlock item ID, or lore criteria specified"); + if (this.itemName == null && this.minecraftItemID == null && this.skyBlockItemID == null && this.loreMatchPattern == null) { + throw new IllegalArgumentException("No item name, Minecraft item ID, SkyBlock item ID, or lore criteria specified."); + } + + if (this.loreMatchPattern != null && loreMatchPattern.pattern() == null) { + throw new IllegalArgumentException( + String.format("Lore match pattern for item in slot %d lacks a regex string.", inventorySlotIndex)); } } } diff --git a/src/main/java/ca/tirelesstraveler/fancywarpmenu/data/skyblockconstants/menu/Menu.java b/src/main/java/ca/tirelesstraveler/fancywarpmenu/data/skyblockconstants/menu/Menu.java index 61bcfc7..c4b3d22 100644 --- a/src/main/java/ca/tirelesstraveler/fancywarpmenu/data/skyblockconstants/menu/Menu.java +++ b/src/main/java/ca/tirelesstraveler/fancywarpmenu/data/skyblockconstants/menu/Menu.java @@ -27,8 +27,21 @@ */ public enum Menu { /** Value used when player is not in a menu or in an unknown or irrelevant menu */ - NONE, - SKYBLOCK_MENU, - FAST_TRAVEL, - PORHTAL + NONE(""), + SKYBLOCK_MENU("SkyBlock Menu"), + FAST_TRAVEL("Fast Travel"), + PORHTAL("Porhtal"); + + /** + * Menu name as displayed at the top of the {@code GuiChest} + */ + final String MENU_DISPLAY_NAME; + + Menu(String menuDisplayName) { + this.MENU_DISPLAY_NAME = menuDisplayName; + } + + public String getMenuDisplayName() { + return MENU_DISPLAY_NAME; + } } diff --git a/src/main/java/ca/tirelesstraveler/fancywarpmenu/data/skyblockconstants/menu/matchconditions/MenuMatchCondition.java b/src/main/java/ca/tirelesstraveler/fancywarpmenu/data/skyblockconstants/menu/matchconditions/MenuMatchCondition.java deleted file mode 100644 index d81dfca..0000000 --- a/src/main/java/ca/tirelesstraveler/fancywarpmenu/data/skyblockconstants/menu/matchconditions/MenuMatchCondition.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2024. TirelessTraveler - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE - * OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package ca.tirelesstraveler.fancywarpmenu.data.skyblockconstants.menu.matchconditions; - -/** - * A condition that is checked to identify the current SkyBlock {@code GuiChest} menu the player has open. - */ -public abstract class MenuMatchCondition { - - /** - * Checks that this {@code MenuMatchCondition} contains valid field values after deserialization. - * Implementations should throw {@code RuntimeException} when invalid values are found. - */ - public void validateCondition() {} -} diff --git a/src/main/java/ca/tirelesstraveler/fancywarpmenu/data/skyblockconstants/menu/matchconditions/MenuNameMatchCondition.java b/src/main/java/ca/tirelesstraveler/fancywarpmenu/data/skyblockconstants/menu/matchconditions/MenuNameMatchCondition.java deleted file mode 100644 index 9e23dbb..0000000 --- a/src/main/java/ca/tirelesstraveler/fancywarpmenu/data/skyblockconstants/menu/matchconditions/MenuNameMatchCondition.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2024. TirelessTraveler - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE - * OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package ca.tirelesstraveler.fancywarpmenu.data.skyblockconstants.menu.matchconditions; - -/** - * A {@link MenuMatchCondition} that checks if a given SkyBlock {@code GuiChest} menu has the same display name as the - * name specified in this condition. - */ -@SuppressWarnings("unused") -public class MenuNameMatchCondition extends MenuMatchCondition { - /** The name of the menu, as displayed at the top of the {@code GuiChest} */ - private String menuName; - - private MenuNameMatchCondition() {} - - public String getMenuName() { - return menuName; - } - - public boolean menuNameMatches(String menuName) { - return this.menuName.equals(menuName); - } - - public void validateCondition() { - if (this.menuName == null) { - throw new NullPointerException("menuName cannot be null"); - } - } -} diff --git a/src/main/java/ca/tirelesstraveler/fancywarpmenu/gui/GuiFancyWarp.java b/src/main/java/ca/tirelesstraveler/fancywarpmenu/gui/GuiFancyWarp.java index 012b725..66a43e6 100644 --- a/src/main/java/ca/tirelesstraveler/fancywarpmenu/gui/GuiFancyWarp.java +++ b/src/main/java/ca/tirelesstraveler/fancywarpmenu/gui/GuiFancyWarp.java @@ -27,10 +27,11 @@ import ca.tirelesstraveler.fancywarpmenu.data.layout.Island; import ca.tirelesstraveler.fancywarpmenu.data.layout.Layout; import ca.tirelesstraveler.fancywarpmenu.data.layout.Warp; +import ca.tirelesstraveler.fancywarpmenu.data.skyblockconstants.SkyBlockConstants; import ca.tirelesstraveler.fancywarpmenu.data.skyblockconstants.menu.Menu; import ca.tirelesstraveler.fancywarpmenu.gui.buttons.*; import ca.tirelesstraveler.fancywarpmenu.gui.grid.ScaledGrid; -import ca.tirelesstraveler.fancywarpmenu.listeners.ChestInventoryListener; +import ca.tirelesstraveler.fancywarpmenu.listeners.InventoryChangeListener; import ca.tirelesstraveler.fancywarpmenu.state.EnvironmentDetails; import ca.tirelesstraveler.fancywarpmenu.state.FancyWarpMenuState; import ca.tirelesstraveler.fancywarpmenu.utils.GameChecks; @@ -43,8 +44,6 @@ import net.minecraft.inventory.IInventory; import net.minecraft.inventory.InventoryBasic; import net.minecraft.inventory.Slot; -import net.minecraft.item.ItemStack; -import net.minecraft.network.play.client.C01PacketChatMessage; import net.minecraft.util.ChatComponentTranslation; import net.minecraft.util.ChatStyle; import net.minecraft.util.EnumChatFormatting; @@ -69,19 +68,32 @@ public class GuiFancyWarp extends GuiChestMenu { /** The amount of time in ms that the error message remains on-screen after a failed warp attempt */ private static final long WARP_FAIL_TOOLTIP_DISPLAY_TIME = 2000L; + protected Menu menu; protected Layout layout; - private final ChestInventoryListener inventoryListener; + private final InventoryBasic chestInventory; + private GuiButton configButton; + private InventoryChangeListener inventoryListener; + private String warpFailMessage; + /** + * Last slot index in the {@link ca.tirelesstraveler.fancywarpmenu.data.skyblockconstants.menu.ItemMatchCondition} + * list for {@link #menu} + * + * @see SkyBlockConstants#getMenuMatchingMap() + */ + protected int lastSlotIndexToCheck; protected long warpFailCoolDownExpiryTime; private boolean screenDrawn; private long warpFailTooltipExpiryTime; - private String warpFailMessage; - private GuiButton configButton; public GuiFancyWarp(IInventory playerInventory, IInventory chestInventory, Layout layout) { super(playerInventory, chestInventory); this.layout = layout; - inventoryListener = new ChestInventoryListener(new ChestItemChangeCallback(this)); - ((InventoryBasic) chestInventory).addInventoryChangeListener(inventoryListener); + this.chestInventory = (InventoryBasic) chestInventory; + + if (Settings.isWarpMenuEnabled()) { + inventoryListener = new InventoryChangeListener(new ChestItemChangeCallback(this)); + this.chestInventory.addInventoryChangeListener(inventoryListener); + } } @Override @@ -224,11 +236,17 @@ protected void actionPerformed(GuiButton button) { setCustomUIState(false, false); } } else if (button instanceof GuiButtonConfig) { - Settings.setWarpMenuEnabled(true); - mc.thePlayer.addChatMessage(new ChatComponentTranslation( - "fancywarpmenu.messages.fancyWarpMenuEnabled").setChatStyle( - new ChatStyle().setColor(EnumChatFormatting.GREEN))); + Settings.setWarpMenuEnabled(true); + mc.thePlayer.addChatMessage(new ChatComponentTranslation( + "fancywarpmenu.messages.fancyWarpMenuEnabled").setChatStyle( + new ChatStyle().setColor(EnumChatFormatting.GREEN))); + + if (GameChecks.menuItemsMatch(menu, chestInventory)) { setCustomUIState(true, true); + } else { + FancyWarpMenuState.setOpenConfigMenuRequested(true); + mc.thePlayer.closeScreen(); + } } } @@ -272,11 +290,15 @@ protected void handleCustomUIKeyboardInput(char typedChar, int keyCode) { */ @Override protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOException { - super.mouseClicked(mouseX, mouseY, mouseButton); - - // Process button presses on the default UI too since the config button is also active there. - if (!customUIInteractionEnabled) { - handlePotentialButtonPress(configButton, mouseX, mouseY); + if (customUIInteractionEnabled) { + handleCustomUIMouseInput(mouseX, mouseY, mouseButton); + } else { + // Don't send a C0EPacketClickWindow when clicking the config button while the custom UI is disabled + if (mouseButton == 0 && configButton.isMouseOver()) { + actionPerformed(configButton); + } else { + super.mouseClicked(mouseX, mouseY, mouseButton); + } } } @@ -329,19 +351,6 @@ protected void clickSlot(int slotIndex) { } } - protected void sendCommand(String command) { - try { - // Packets are used to bypass EntityPlayerSPHook - mc.thePlayer.sendQueue.addToSendQueue(new C01PacketChatMessage(command)); - - if (Settings.shouldAddWarpCommandToChatHistory()) { - mc.ingameGUI.getChatGUI().addToSentMessages(command); - } - } catch (Exception e) { - logger.error(String.format("Failed to send command \"%s\": %s", command, e.getMessage()), e); - } - } - protected void drawButtons(int mouseX, int mouseY) { for (GuiButton button : buttonList) { if (button instanceof GuiButtonConfig || Settings.isWarpMenuEnabled()) { @@ -363,33 +372,28 @@ protected void updateButtonStates() { } } - private void onChestItemChange(InventoryBasic chestInventory, int triggerCount) { - ItemStack lastStack = chestInventory.getStackInSlot(chestInventory.getSizeInventory() - 1); + /** + * Called whenever an item in the inventory of the {@link GuiChestMenu} changes. + * This is used to enable the fancy warp menu when the SkyBlock menu the player has open is a warp menu. + * + * @param triggerCount number of times {@link #inventoryListener} was triggered + */ + private void onChestItemChange(int triggerCount) { + /* + Don't start checking until the item in the last slot to check has been loaded. - if (lastStack != null) { - if (GameChecks.determineOpenMenu(chestInventory, false) == Menu.SKYBLOCK_MENU) { + The item change event is triggered twice for each item, and the item stack is set on the 2nd time it's + triggered. For example, slot 53 is actually set on the 106th time the item change event triggers. + */ + if (triggerCount > lastSlotIndexToCheck * 2 && chestInventory.getStackInSlot(lastSlotIndexToCheck) != null) { + if (GameChecks.menuItemsMatch(menu, chestInventory)) { setCustomUIState(true, true); - chestInventory.removeInventoryChangeListener(inventoryListener); - return; } - } - if (triggerCount > chestInventory.getSizeInventory()) { chestInventory.removeInventoryChangeListener(inventoryListener); } } - private void drawDebugStrings(ArrayList debugStrings, int drawX, int drawY, int nearestGridX, int nearestGridY, int zLevel) { - debugStrings.add("gridX: " + nearestGridX); - debugStrings.add("gridY: " + nearestGridY); - // zLevel of -1 means z is not relevant, like in the case of screen coordinates - if (zLevel > -1) { - debugStrings.add("zLevel: " + zLevel); - } - drawHoveringText(debugStrings, drawX, drawY); - drawRect(drawX - 2, drawY - 2, drawX + 2, drawY + 2, Color.RED.getRGB()); - } - /** * Tests for and handles a {@code GuiButton} press if the provided button was pressed. This re-implements most of * the logic from {@link net.minecraft.client.gui.GuiScreen#mouseClicked(int, int, int)} that is skipped when @@ -420,6 +424,17 @@ private boolean handlePotentialButtonPress(GuiButton button, int mouseX, int mou return false; } + private void drawDebugStrings(ArrayList debugStrings, int drawX, int drawY, int nearestGridX, int nearestGridY, int zLevel) { + debugStrings.add("gridX: " + nearestGridX); + debugStrings.add("gridY: " + nearestGridY); + // zLevel of -1 means z is not relevant, like in the case of screen coordinates + if (zLevel > -1) { + debugStrings.add("zLevel: " + zLevel); + } + drawHoveringText(debugStrings, drawX, drawY); + drawRect(drawX - 2, drawY - 2, drawX + 2, drawY + 2, Color.RED.getRGB()); + } + /** * A callback called when any item in the chest it is attached to changes */ @@ -435,7 +450,7 @@ private static class ChestItemChangeCallback implements Consumer @Override public void accept(InventoryBasic chestInventory) { triggerCount++; - GUI_FANCY_WARP.onChestItemChange(chestInventory, triggerCount); + GUI_FANCY_WARP.onChestItemChange(triggerCount); } } } diff --git a/src/main/java/ca/tirelesstraveler/fancywarpmenu/gui/GuiFastTravel.java b/src/main/java/ca/tirelesstraveler/fancywarpmenu/gui/GuiFastTravel.java index 094fd53..7628e91 100644 --- a/src/main/java/ca/tirelesstraveler/fancywarpmenu/gui/GuiFastTravel.java +++ b/src/main/java/ca/tirelesstraveler/fancywarpmenu/gui/GuiFastTravel.java @@ -22,9 +22,11 @@ package ca.tirelesstraveler.fancywarpmenu.gui; +import ca.tirelesstraveler.fancywarpmenu.FancyWarpMenu; import ca.tirelesstraveler.fancywarpmenu.data.Settings; import ca.tirelesstraveler.fancywarpmenu.data.layout.Island; import ca.tirelesstraveler.fancywarpmenu.data.layout.Layout; +import ca.tirelesstraveler.fancywarpmenu.data.skyblockconstants.menu.Menu; import ca.tirelesstraveler.fancywarpmenu.gui.buttons.GuiButtonIsland; import ca.tirelesstraveler.fancywarpmenu.gui.buttons.GuiButtonWarp; import ca.tirelesstraveler.fancywarpmenu.state.FancyWarpMenuState; @@ -40,6 +42,8 @@ public class GuiFastTravel extends GuiFancyWarp { public GuiFastTravel(IInventory playerInventory, IInventory chestInventory, Layout layout) { super(playerInventory, chestInventory, layout); + menu = Menu.FAST_TRAVEL; + lastSlotIndexToCheck = FancyWarpMenu.getSkyBlockConstants().getLastMatchConditionInventorySlotIndex(menu); } @Override @@ -54,14 +58,14 @@ protected void actionPerformed(GuiButton button) { // Don't send command twice for single warp islands if (warpButton.getIsland().getWarpCount() > 1) { String warpCommand = warpButton.getWarpCommand(); - sendCommand(warpCommand); + mc.thePlayer.sendChatMessage(warpCommand); } } else if (button instanceof GuiButtonIsland) { Island island = ((GuiButtonIsland) button).getIsland(); if (island.getWarpCount() == 1) { String warpCommand = island.getWarps().get(0).getWarpCommand(); - sendCommand(warpCommand); + mc.thePlayer.sendChatMessage(warpCommand); } } } diff --git a/src/main/java/ca/tirelesstraveler/fancywarpmenu/gui/GuiRiftFastTravel.java b/src/main/java/ca/tirelesstraveler/fancywarpmenu/gui/GuiRiftFastTravel.java index 3df7a36..33423c3 100644 --- a/src/main/java/ca/tirelesstraveler/fancywarpmenu/gui/GuiRiftFastTravel.java +++ b/src/main/java/ca/tirelesstraveler/fancywarpmenu/gui/GuiRiftFastTravel.java @@ -22,9 +22,11 @@ package ca.tirelesstraveler.fancywarpmenu.gui; +import ca.tirelesstraveler.fancywarpmenu.FancyWarpMenu; import ca.tirelesstraveler.fancywarpmenu.data.Settings; import ca.tirelesstraveler.fancywarpmenu.data.layout.Island; import ca.tirelesstraveler.fancywarpmenu.data.layout.Layout; +import ca.tirelesstraveler.fancywarpmenu.data.skyblockconstants.menu.Menu; import ca.tirelesstraveler.fancywarpmenu.gui.buttons.GuiButtonIsland; import ca.tirelesstraveler.fancywarpmenu.gui.buttons.GuiButtonWarp; import ca.tirelesstraveler.fancywarpmenu.state.FancyWarpMenuState; @@ -39,6 +41,8 @@ public class GuiRiftFastTravel extends GuiFancyWarp { public GuiRiftFastTravel(IInventory playerInventory, IInventory chestInventory, Layout layout) { super(playerInventory, chestInventory, layout); + menu = Menu.PORHTAL; + lastSlotIndexToCheck = FancyWarpMenu.getSkyBlockConstants().getLastMatchConditionInventorySlotIndex(menu); } @Override diff --git a/src/main/java/ca/tirelesstraveler/fancywarpmenu/hooks/EntityPlayerSPHook.java b/src/main/java/ca/tirelesstraveler/fancywarpmenu/hooks/EntityPlayerSPHook.java deleted file mode 100644 index 249c227..0000000 --- a/src/main/java/ca/tirelesstraveler/fancywarpmenu/hooks/EntityPlayerSPHook.java +++ /dev/null @@ -1,27 +0,0 @@ -package ca.tirelesstraveler.fancywarpmenu.hooks; - -import ca.tirelesstraveler.fancywarpmenu.LogHelper; -import ca.tirelesstraveler.fancywarpmenu.data.Settings; -import ca.tirelesstraveler.fancywarpmenu.data.skyblockconstants.WarpCommandVariant; -import ca.tirelesstraveler.fancywarpmenu.listeners.WarpMenuListener; -import ca.tirelesstraveler.fancywarpmenu.state.GameState; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -import java.util.Locale; - -public class EntityPlayerSPHook { - public static void onSendChatMessage(String message, CallbackInfo ci) { - if (Settings.isWarpMenuEnabled() && GameState.isOnSkyBlock() && message.startsWith("/")) { - WarpCommandVariant warpCommandVariant = WarpMenuListener.getWarpCommandVariant(message); - - LogHelper.logDebug("Caught sent command: {}", message); - - // Check if the command is a warp command alias without any arguments - if (warpCommandVariant != null && warpCommandVariant.getType() == WarpCommandVariant.WarpCommandType.ALIAS && - message.trim().toLowerCase(Locale.US).equals("/" + warpCommandVariant.getCommand()) && - Settings.shouldSuggestWarpMenuOnWarpCommand()) { - WarpMenuListener.sendReminderToUseWarpScreen(); - } - } - } -} diff --git a/src/main/java/ca/tirelesstraveler/fancywarpmenu/listeners/ChestInventoryListener.java b/src/main/java/ca/tirelesstraveler/fancywarpmenu/listeners/InventoryChangeListener.java similarity index 86% rename from src/main/java/ca/tirelesstraveler/fancywarpmenu/listeners/ChestInventoryListener.java rename to src/main/java/ca/tirelesstraveler/fancywarpmenu/listeners/InventoryChangeListener.java index 397ed62..8fa303b 100644 --- a/src/main/java/ca/tirelesstraveler/fancywarpmenu/listeners/ChestInventoryListener.java +++ b/src/main/java/ca/tirelesstraveler/fancywarpmenu/listeners/InventoryChangeListener.java @@ -27,10 +27,13 @@ import java.util.function.Consumer; -public class ChestInventoryListener implements IInvBasic { +/** + * A simple listener that calls a callback when an item changes in the inventory it is assigned to. + */ +public class InventoryChangeListener implements IInvBasic { private final Consumer CALLBACK; - public ChestInventoryListener(Consumer callback) { + public InventoryChangeListener(Consumer callback) { CALLBACK = callback; } diff --git a/src/main/java/ca/tirelesstraveler/fancywarpmenu/listeners/WarpMenuListener.java b/src/main/java/ca/tirelesstraveler/fancywarpmenu/listeners/WarpMenuListener.java index 53098c6..6258e48 100644 --- a/src/main/java/ca/tirelesstraveler/fancywarpmenu/listeners/WarpMenuListener.java +++ b/src/main/java/ca/tirelesstraveler/fancywarpmenu/listeners/WarpMenuListener.java @@ -24,6 +24,7 @@ import ca.tirelesstraveler.fancywarpmenu.FancyWarpMenu; import ca.tirelesstraveler.fancywarpmenu.data.Settings; +import ca.tirelesstraveler.fancywarpmenu.data.skyblockconstants.SkyBlockConstants; import ca.tirelesstraveler.fancywarpmenu.data.skyblockconstants.WarpCommandVariant; import ca.tirelesstraveler.fancywarpmenu.data.skyblockconstants.menu.Menu; import ca.tirelesstraveler.fancywarpmenu.gui.FancyWarpMenuConfigScreen; @@ -31,6 +32,7 @@ import ca.tirelesstraveler.fancywarpmenu.gui.GuiFastTravel; import ca.tirelesstraveler.fancywarpmenu.gui.GuiRiftFastTravel; import ca.tirelesstraveler.fancywarpmenu.state.FancyWarpMenuState; +import ca.tirelesstraveler.fancywarpmenu.state.GameState; import ca.tirelesstraveler.fancywarpmenu.utils.GameChecks; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.inventory.GuiChest; @@ -45,6 +47,7 @@ import net.minecraftforge.client.event.GuiOpenEvent; import net.minecraftforge.fml.client.event.ConfigChangedEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.gameevent.InputEvent; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -82,7 +85,7 @@ public void onGuiOpen(GuiOpenEvent event) { /* Minecraft closes the current screen after executing a command, meaning any GuiScreen opened by the command is closed. This section interrupts the closing of the current screen to get around this behavior. - */ + */ if (event.gui == null) { if (FancyWarpMenuState.isOpenConfigMenuRequested()) { event.gui = new FancyWarpMenuConfigScreen(null); @@ -92,7 +95,7 @@ public void onGuiOpen(GuiOpenEvent event) { IInventory playerInventory = mc.thePlayer.inventory; IInventory chestInventory = ((ContainerChest) ((GuiChest) event.gui).inventorySlots).getLowerChestInventory(); - Menu currentMenu = GameChecks.determineOpenMenu(chestInventory, true); + Menu currentMenu = GameChecks.determineOpenMenu(chestInventory); if (currentMenu == Menu.FAST_TRAVEL) { event.gui = new GuiFastTravel(playerInventory, chestInventory, FancyWarpMenuState.getOverworldLayout()); @@ -102,6 +105,14 @@ public void onGuiOpen(GuiOpenEvent event) { } } + @SubscribeEvent + public void keyTyped(InputEvent.KeyInputEvent event) { + if (Settings.isWarpMenuEnabled() && (GameState.isOnSkyBlock() || Settings.shouldSkipSkyBlockCheck()) && + FancyWarpMenu.getKeyBindingOpenWarpMenu().isPressed()) { + mc.thePlayer.sendChatMessage(SkyBlockConstants.WARP_COMMAND_BASE); + } + } + @SubscribeEvent public void onConfigChange(ConfigChangedEvent.OnConfigChangedEvent event) { if (event.modID.equals(modInstance.getModId())) { diff --git a/src/main/java/ca/tirelesstraveler/fancywarpmenu/mixin/MixinEntityPlayerSP.java b/src/main/java/ca/tirelesstraveler/fancywarpmenu/mixin/MixinEntityPlayerSP.java deleted file mode 100644 index 4f380f5..0000000 --- a/src/main/java/ca/tirelesstraveler/fancywarpmenu/mixin/MixinEntityPlayerSP.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2023. TirelessTraveler - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE - * OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package ca.tirelesstraveler.fancywarpmenu.mixin; - -import ca.tirelesstraveler.fancywarpmenu.hooks.EntityPlayerSPHook; -import net.minecraft.client.entity.EntityPlayerSP; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -/** - * This mixin intercepts warp menu requests that use {@link EntityPlayerSP#sendChatMessage(String)} to send - * commands. This includes requests from {@link net.minecraft.client.gui.GuiScreen#sendChatMessage(String, boolean)} and - * other mods. - */ -@SuppressWarnings("unused") -@Mixin(EntityPlayerSP.class) -public class MixinEntityPlayerSP { - @Inject(method = "sendChatMessage", at = @At("HEAD"), cancellable = true, require = 1) - public void onSendChatMessage(String message, CallbackInfo ci) { - EntityPlayerSPHook.onSendChatMessage(message, ci); - } -} diff --git a/src/main/java/ca/tirelesstraveler/fancywarpmenu/resourceloaders/ResourceLoader.java b/src/main/java/ca/tirelesstraveler/fancywarpmenu/resourceloaders/ResourceLoader.java index 12bb3f2..fe8555b 100644 --- a/src/main/java/ca/tirelesstraveler/fancywarpmenu/resourceloaders/ResourceLoader.java +++ b/src/main/java/ca/tirelesstraveler/fancywarpmenu/resourceloaders/ResourceLoader.java @@ -23,8 +23,6 @@ package ca.tirelesstraveler.fancywarpmenu.resourceloaders; import ca.tirelesstraveler.fancywarpmenu.FancyWarpMenu; -import ca.tirelesstraveler.fancywarpmenu.data.skyblockconstants.menu.matchconditions.MenuMatchCondition; -import ca.tirelesstraveler.fancywarpmenu.resourceloaders.typeadapters.MenuMatchConditionAdapter; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import net.minecraft.client.Minecraft; @@ -43,7 +41,6 @@ public abstract class ResourceLoader { public static final Gson gson = new GsonBuilder() .setPrettyPrinting() - .registerTypeAdapter(MenuMatchCondition.class, new MenuMatchConditionAdapter()) .create(); protected static final Logger logger = LogManager.getLogger(); diff --git a/src/main/java/ca/tirelesstraveler/fancywarpmenu/resourceloaders/SkyBlockConstantsLoader.java b/src/main/java/ca/tirelesstraveler/fancywarpmenu/resourceloaders/SkyBlockConstantsLoader.java index e0dc0ee..a0fe04e 100644 --- a/src/main/java/ca/tirelesstraveler/fancywarpmenu/resourceloaders/SkyBlockConstantsLoader.java +++ b/src/main/java/ca/tirelesstraveler/fancywarpmenu/resourceloaders/SkyBlockConstantsLoader.java @@ -24,6 +24,7 @@ import ca.tirelesstraveler.fancywarpmenu.FancyWarpMenu; import ca.tirelesstraveler.fancywarpmenu.data.skyblockconstants.SkyBlockConstants; +import ca.tirelesstraveler.fancywarpmenu.data.skyblockconstants.menu.ItemMatchCondition; import com.google.gson.stream.JsonReader; import net.minecraft.client.Minecraft; import net.minecraft.client.resources.IResource; @@ -32,11 +33,19 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.util.Comparator; +import java.util.List; public class SkyBlockConstantsLoader extends ResourceLoader { private static final ResourceLocation SKY_BLOCK_CONSTANTS_LOCATION = new ResourceLocation("fancywarpmenu", "data/skyBlockConstants.json"); + /** + * Creates a {@link SkyBlockConstants} instance from {@code SKY_BLOCK_CONSTANTS_LOCATION}. + * Match conditions in {@code SkyBlockConstants#menuMatchingMap} are sorted by ascending item slot index. + * + * @return the created {@code SkyBlockConstants} instance + */ public static SkyBlockConstants loadSkyBlockConstants() { try { IResource skyBlockConstantsResource = Minecraft.getMinecraft().getResourceManager().getResource(SKY_BLOCK_CONSTANTS_LOCATION); @@ -44,6 +53,11 @@ public static SkyBlockConstants loadSkyBlockConstants() { try (InputStream stream = skyBlockConstantsResource.getInputStream(); JsonReader reader = new JsonReader(new InputStreamReader(stream))) { SkyBlockConstants skyBlockConstants = gson.fromJson(reader, SkyBlockConstants.class); + + for (List matchConditionList : skyBlockConstants.getMenuMatchingMap().values()) { + matchConditionList.sort(Comparator.comparing(ItemMatchCondition::getInventorySlotIndex)); + } + SkyBlockConstants.validateSkyBlockConstants(skyBlockConstants); return skyBlockConstants; diff --git a/src/main/java/ca/tirelesstraveler/fancywarpmenu/resourceloaders/typeadapters/MenuMatchConditionAdapter.java b/src/main/java/ca/tirelesstraveler/fancywarpmenu/resourceloaders/typeadapters/MenuMatchConditionAdapter.java deleted file mode 100644 index b59b952..0000000 --- a/src/main/java/ca/tirelesstraveler/fancywarpmenu/resourceloaders/typeadapters/MenuMatchConditionAdapter.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2024. TirelessTraveler - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE - * OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package ca.tirelesstraveler.fancywarpmenu.resourceloaders.typeadapters; - -import ca.tirelesstraveler.fancywarpmenu.data.skyblockconstants.menu.matchconditions.ItemMatchCondition; -import ca.tirelesstraveler.fancywarpmenu.data.skyblockconstants.menu.matchconditions.MenuMatchCondition; -import ca.tirelesstraveler.fancywarpmenu.data.skyblockconstants.menu.matchconditions.MenuNameMatchCondition; -import com.google.gson.*; - -import java.lang.reflect.Type; - -public class MenuMatchConditionAdapter implements JsonSerializer, JsonDeserializer { - - @Override - public JsonElement serialize(MenuMatchCondition src, Type typeOfSrc, JsonSerializationContext context) { - if (src.getClass().equals(MenuNameMatchCondition.class)) { - return context.serialize(src, MenuNameMatchCondition.class); - } else if (src.getClass().equals(ItemMatchCondition.class)) { - return context.serialize(src, ItemMatchCondition.class); - } else { - return context.serialize(src, MenuMatchCondition.class); - } - } - - @Override - public MenuMatchCondition deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { - JsonObject menuConditionObject = json.getAsJsonObject(); - - if (menuConditionObject.has("menuName")) { - return context.deserialize(json, MenuNameMatchCondition.class); - } else if (menuConditionObject.has("inventorySlotIndex")) { - return context.deserialize(json, ItemMatchCondition.class); - } else { - throw new JsonParseException(String.format("%s\n is not an object of a subclass of MenuMatchCondition", json)); - } - } -} diff --git a/src/main/java/ca/tirelesstraveler/fancywarpmenu/utils/GameChecks.java b/src/main/java/ca/tirelesstraveler/fancywarpmenu/utils/GameChecks.java index aa14dea..9dc7798 100644 --- a/src/main/java/ca/tirelesstraveler/fancywarpmenu/utils/GameChecks.java +++ b/src/main/java/ca/tirelesstraveler/fancywarpmenu/utils/GameChecks.java @@ -25,9 +25,7 @@ import ca.tirelesstraveler.fancywarpmenu.FancyWarpMenu; import ca.tirelesstraveler.fancywarpmenu.data.Settings; import ca.tirelesstraveler.fancywarpmenu.data.skyblockconstants.menu.Menu; -import ca.tirelesstraveler.fancywarpmenu.data.skyblockconstants.menu.matchconditions.ItemMatchCondition; -import ca.tirelesstraveler.fancywarpmenu.data.skyblockconstants.menu.matchconditions.MenuMatchCondition; -import ca.tirelesstraveler.fancywarpmenu.data.skyblockconstants.menu.matchconditions.MenuNameMatchCondition; +import ca.tirelesstraveler.fancywarpmenu.data.skyblockconstants.menu.ItemMatchCondition; import ca.tirelesstraveler.fancywarpmenu.state.GameState; import net.minecraft.client.Minecraft; import net.minecraft.inventory.IInventory; @@ -78,46 +76,54 @@ public static void checkLateWinter() { } /** - * Determines which SkyBlock {@code GuiChest} menu the player is in using - * {@link ca.tirelesstraveler.fancywarpmenu.data.skyblockconstants.menu.matchconditions.MenuMatchCondition}s, saves - * the determined {@code Menu} to {@link ca.tirelesstraveler.fancywarpmenu.state.GameState}, and also returns it. + * Determines which SkyBlock {@code GuiChest} menu the player is in using the {@link net.minecraft.client.gui.inventory.GuiChest} + * display name. This is used for initial checks when the items haven't loaded in yet. + * The matched menu will be saved to {@code GameState#currentMenu}. * * @param chestInventory the inventory of the chest holding the menu - * @param titleOnly check only the chest title, used for initial checks when the items haven't loaded in yet * @return a {@code Menu} value representing the current menu the player has open */ - public static Menu determineOpenMenu(IInventory chestInventory, boolean titleOnly) { - Menu matchedMenu = Menu.NONE; - + public static Menu determineOpenMenu(IInventory chestInventory) { if (chestInventory.hasCustomName()) { String chestTitle = chestInventory.getDisplayName().getUnformattedText(); - for (Map.Entry> menuMatchingEntry : + for (Map.Entry> menuMatchingEntry : FancyWarpMenu.getSkyBlockConstants().getMenuMatchingMap().entrySet()) { - List matchConditions = menuMatchingEntry.getValue(); - boolean conditionFailed = false; - - for (MenuMatchCondition matchCondition : matchConditions) { - if (matchCondition.getClass().equals(MenuNameMatchCondition.class)) { - if (!((MenuNameMatchCondition) matchCondition).menuNameMatches(chestTitle)) { - conditionFailed = true; - break; - } - } else if (!titleOnly && matchCondition.getClass().equals(ItemMatchCondition.class)) { - if (!((ItemMatchCondition) matchCondition).inventoryContainsMatchingItem(chestInventory)) { - conditionFailed = true; - break; - } - } + if (chestTitle.equals(menuMatchingEntry.getKey().getMenuDisplayName())) { + GameState.setCurrentMenu(menuMatchingEntry.getKey()); + return menuMatchingEntry.getKey(); } + } + } - if (!conditionFailed) { - matchedMenu = menuMatchingEntry.getKey(); - } + GameState.setCurrentMenu(Menu.NONE); + return Menu.NONE; + } + + /** + * Determines if the player is in the given menu by checking whether all the {@link ItemMatchCondition}s for that menu + * match the given inventory. This should be used after the inventory has loaded the slot index returned by + * {@link ca.tirelesstraveler.fancywarpmenu.data.skyblockconstants.SkyBlockConstants#getLastMatchConditionInventorySlotIndex(Menu)}. + * + * @param menu the {@code Menu} whose match conditions will be checked + * @param chestInventory the inventory to check against the match conditions + * @return {@code true} if all the {@link ItemMatchCondition}s match, {@code false} otherwise + */ + public static boolean menuItemsMatch(Menu menu, IInventory chestInventory) { + List matchConditions = FancyWarpMenu.getSkyBlockConstants().getMenuMatchingMap().get(menu); + + for (ItemMatchCondition matchCondition : matchConditions) { + logger.debug("Starting item match on slot {} for menu {}.", + matchCondition.getInventorySlotIndex(), menu); + + if (!matchCondition.inventoryContainsMatchingItem(chestInventory)) { + logger.debug("Item match on slot {} failed.", matchCondition.getInventorySlotIndex()); + GameState.setCurrentMenu(Menu.NONE); + return false; } } - GameState.setCurrentMenu(matchedMenu); - return matchedMenu; + GameState.setCurrentMenu(menu); + return true; } } diff --git a/src/main/resources/assets/fancywarpmenu/data/skyBlockConstants.json b/src/main/resources/assets/fancywarpmenu/data/skyBlockConstants.json index 3301d4d..48c4311 100644 --- a/src/main/resources/assets/fancywarpmenu/data/skyBlockConstants.json +++ b/src/main/resources/assets/fancywarpmenu/data/skyBlockConstants.json @@ -1,18 +1,31 @@ { "menuMatchingMap": { - "SKYBLOCK_MENU": [ - { - "menuName": "SkyBlock Menu" - } - ], "FAST_TRAVEL": [ { - "menuName": "Fast Travel" + "inventorySlotIndex": 45, + "itemName": "Island Browser", + "minecraftItemID": "minecraft:blaze_powder" + }, + { + "inventorySlotIndex": 49, + "itemName": "Close", + "minecraftItemID": "minecraft:barrier" + }, + { + "inventorySlotIndex": 53, + "itemName": "Paper Icons", + "minecraftItemID": "minecraft:map", + "loreMatchPattern": { + "pattern": ".*§eClick to toggle!$", + "flags": 0 + } } ], "PORHTAL": [ { - "menuName": "Porhtal" + "inventorySlotIndex": 31, + "itemName": "Close", + "minecraftItemID": "minecraft:barrier" } ] }, diff --git a/src/main/resources/mixins.fancywarpmenu.json b/src/main/resources/mixins.fancywarpmenu.json deleted file mode 100644 index 9aefa27..0000000 --- a/src/main/resources/mixins.fancywarpmenu.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "package": "${mixinGroup}", - "refmap": "mixins.${modid}.refmap.json", - "minVersion": "0.7", - "compatibilityLevel": "JAVA_8", - "mixins": [ - ], - "client": [ - "MixinEntityPlayerSP" - ] -} \ No newline at end of file