diff --git a/runelite-api/src/main/java/net/runelite/api/queries/BankItemQuery.java b/runelite-api/src/main/java/net/runelite/api/queries/BankItemQuery.java index 2153072fb8..64c79712d3 100644 --- a/runelite-api/src/main/java/net/runelite/api/queries/BankItemQuery.java +++ b/runelite-api/src/main/java/net/runelite/api/queries/BankItemQuery.java @@ -67,7 +67,7 @@ public class BankItemQuery extends WidgetItemQuery Rectangle bounds = child.getBounds(); bounds.setBounds(bounds.x - 1, bounds.y - 1, 32, 32); // Index is set to 0 because the widget's index does not correlate to the order in the bank - widgetItems.add(new WidgetItem(child.getItemId(), child.getItemQuantity(), 0, bounds, child, false)); + widgetItems.add(new WidgetItem(child.getItemId(), child.getItemQuantity(), 0, bounds, child, null)); } } return widgetItems; diff --git a/runelite-api/src/main/java/net/runelite/api/queries/DialogQuery.java b/runelite-api/src/main/java/net/runelite/api/queries/DialogQuery.java index 0b4cfb8c03..fff6f4c5f6 100644 --- a/runelite-api/src/main/java/net/runelite/api/queries/DialogQuery.java +++ b/runelite-api/src/main/java/net/runelite/api/queries/DialogQuery.java @@ -73,7 +73,7 @@ public class DialogQuery extends WidgetItemQuery // set bounds to same size as default inventory Rectangle bounds = child.getBounds(); bounds.setBounds(bounds.x - 1, bounds.y - 1, 32, 32); - widgetItems.add(new WidgetItem(child.getId(), child.getItemQuantity(), i - 1, bounds, child, false)); + widgetItems.add(new WidgetItem(child.getId(), child.getItemQuantity(), i - 1, bounds, child, null)); } } return widgetItems; diff --git a/runelite-api/src/main/java/net/runelite/api/queries/InventoryWidgetItemQuery.java b/runelite-api/src/main/java/net/runelite/api/queries/InventoryWidgetItemQuery.java index 9072dbeeda..4598439082 100644 --- a/runelite-api/src/main/java/net/runelite/api/queries/InventoryWidgetItemQuery.java +++ b/runelite-api/src/main/java/net/runelite/api/queries/InventoryWidgetItemQuery.java @@ -92,8 +92,10 @@ public class InventoryWidgetItemQuery extends WidgetItemQuery } // set bounds to same size as default inventory Rectangle bounds = child.getBounds(); - bounds.setBounds(bounds.x + dragOffsetX, bounds.y + dragOffsetY, 32, 32); - widgetItems.add(new WidgetItem(child.getItemId(), child.getItemQuantity(), i, bounds, child, isDragged)); + bounds.setBounds(bounds.x - 1, bounds.y - 1, 32, 32); + Rectangle dragBounds = child.getBounds(); + dragBounds.setBounds(bounds.x + dragOffsetX, bounds.y + dragOffsetY, 32, 32); + widgetItems.add(new WidgetItem(child.getItemId(), child.getItemQuantity(), i, bounds, child, dragBounds)); } break; } diff --git a/runelite-api/src/main/java/net/runelite/api/queries/ShopItemQuery.java b/runelite-api/src/main/java/net/runelite/api/queries/ShopItemQuery.java index a76851fe0a..a768df8928 100644 --- a/runelite-api/src/main/java/net/runelite/api/queries/ShopItemQuery.java +++ b/runelite-api/src/main/java/net/runelite/api/queries/ShopItemQuery.java @@ -60,7 +60,7 @@ public class ShopItemQuery extends WidgetItemQuery // set bounds to same size as default inventory Rectangle bounds = child.getBounds(); bounds.setBounds(bounds.x - 1, bounds.y - 1, 32, 32); - widgetItems.add(new WidgetItem(child.getItemId(), child.getItemQuantity(), i - 1, bounds, child, false)); // todo: maybe this shouldnt just be "false" + widgetItems.add(new WidgetItem(child.getItemId(), child.getItemQuantity(), i - 1, bounds, child, null)); // todo: maybe this shouldnt just be "false" } } return widgetItems; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/HotColdSolver.java b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/HotColdSolver.java index 0205272737..538dc2b3fe 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/HotColdSolver.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/HotColdSolver.java @@ -125,7 +125,7 @@ public class HotColdSolver * @see WorldPoint#distanceTo2D */ @VisibleForTesting - private static boolean isFirstPointCloserRect(final WorldPoint firstPoint, final WorldPoint secondPoint, final Rectangle rect) + static boolean isFirstPointCloserRect(final WorldPoint firstPoint, final WorldPoint secondPoint, final Rectangle rect) { final WorldPoint nePoint = new WorldPoint((rect.x + rect.width), (rect.y + rect.height), 0); @@ -163,7 +163,7 @@ public class HotColdSolver * @see WorldPoint#distanceTo2D */ @VisibleForTesting - private static boolean isFirstPointCloser(final WorldPoint firstPoint, final WorldPoint secondPoint, final WorldPoint worldPoint) + static boolean isFirstPointCloser(final WorldPoint firstPoint, final WorldPoint secondPoint, final WorldPoint worldPoint) { return firstPoint.distanceTo2D(worldPoint) < secondPoint.distanceTo2D(worldPoint); } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/customcursor/CustomCursor.java b/runelite-client/src/main/java/net/runelite/client/plugins/customcursor/CustomCursor.java index d2d36f67aa..e11840114d 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/customcursor/CustomCursor.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/customcursor/CustomCursor.java @@ -25,10 +25,12 @@ package net.runelite.client.plugins.customcursor; import java.awt.image.BufferedImage; +import javax.annotation.Nullable; import lombok.AccessLevel; import lombok.Getter; import net.runelite.client.util.ImageUtil; +@Getter(AccessLevel.PUBLIC) public enum CustomCursor { RS3_GOLD("RS3 Gold", "cursor-rs3-gold.png"), @@ -42,12 +44,19 @@ public enum CustomCursor MOUSE("Mouse", "cursor-mouse.png"), SARADOMIN_GODSWORD("Saradomin Godsword", "cursor-saradomin-godsword.png"), ZAMORAK_GODSWORD("Zamorak Godsword", "cursor-zamorak-godsword.png"), - SKILL_SPECS("Skill Specs", "cursor-skill-specs.png"); + SKILL_SPECS("Skill Specs", "cursor-skill-specs.png"), + CUSTOM_IMAGE("Custom Image"); private final String name; - @Getter(AccessLevel.PUBLIC) + @Nullable private final BufferedImage cursorImage; + CustomCursor(String name) + { + this.name = name; + this.cursorImage = null; + } + CustomCursor(final String name, final String icon) { this.name = name; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/customcursor/CustomCursorPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/customcursor/CustomCursorPlugin.java index 723b17e7cf..556703a840 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/customcursor/CustomCursorPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/customcursor/CustomCursorPlugin.java @@ -25,7 +25,10 @@ package net.runelite.client.plugins.customcursor; import com.google.inject.Provides; +import java.awt.image.BufferedImage; +import java.io.File; import java.io.IOException; +import javax.imageio.ImageIO; import javax.inject.Inject; import javax.inject.Singleton; import javax.sound.sampled.AudioInputStream; @@ -35,6 +38,7 @@ import javax.sound.sampled.FloatControl; import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.UnsupportedAudioFileException; import lombok.extern.slf4j.Slf4j; +import net.runelite.client.RuneLite; import net.runelite.client.config.ConfigManager; import net.runelite.client.eventbus.Subscribe; import net.runelite.client.events.ConfigChanged; @@ -53,6 +57,8 @@ import net.runelite.client.ui.ClientUI; @Singleton public class CustomCursorPlugin extends Plugin { + private static final File CUSTOM_IMAGE_FILE = new File(RuneLite.RUNELITE_DIR, "cursor.png"); + @Inject private ClientUI clientUI; @@ -125,6 +131,34 @@ public class CustomCursorPlugin extends Plugin skillSpecsRage.start(); } } + else if (selectedCursor == CustomCursor.CUSTOM_IMAGE) + { + if (CUSTOM_IMAGE_FILE.exists()) + { + try + { + BufferedImage image; + synchronized (ImageIO.class) + { + image = ImageIO.read(CUSTOM_IMAGE_FILE); + } + clientUI.setCursor(image, selectedCursor.getName()); + } + catch (Exception e) + { + log.error("error setting custom cursor", e); + clientUI.resetCursor(); + } + } + else + { + clientUI.resetCursor(); + } + return; + } + + assert selectedCursor.getCursorImage() != null; + clientUI.setCursor(selectedCursor.getCursorImage(), selectedCursor.getName()); clientUI.setCursor(selectedCursor.getCursorImage(), selectedCursor.toString()); } diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/BeginnerHotColdLocationTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/BeginnerHotColdLocationTest.java new file mode 100644 index 0000000000..caa93278a4 --- /dev/null +++ b/runelite-client/src/test/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/BeginnerHotColdLocationTest.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2019, Jordan Atwood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.cluescrolls.clues.hotcold; + +import java.util.Arrays; +import java.util.Set; +import java.util.stream.Collectors; +import static org.junit.Assert.assertEquals; +import org.junit.Test; + +public class BeginnerHotColdLocationTest +{ + private static final Set BEGINNER_HOT_COLD_LOCATIONS = Arrays.stream(HotColdLocation.values()) + .filter(HotColdLocation::isBeginnerClue) + .collect(Collectors.toSet()); + private static final int EXPECTED_DIMENSION_SIZE = 7; + + @Test + public void beginnerHotColdLocationAreaTest() + { + + for (final HotColdLocation location : BEGINNER_HOT_COLD_LOCATIONS) + { + assertEquals(EXPECTED_DIMENSION_SIZE, location.getRect().height); + assertEquals(EXPECTED_DIMENSION_SIZE, location.getRect().width); + } + } +} \ No newline at end of file diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/HotColdSolverTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/HotColdSolverTest.java new file mode 100644 index 0000000000..eca21c473e --- /dev/null +++ b/runelite-client/src/test/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/HotColdSolverTest.java @@ -0,0 +1,278 @@ +/* + * Copyright (c) 2019, Jordan Atwood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.cluescrolls.clues.hotcold; + +import com.google.common.collect.Sets; +import java.awt.Rectangle; +import java.util.EnumSet; +import java.util.Set; +import java.util.stream.Collectors; +import static junit.framework.TestCase.assertTrue; +import net.runelite.api.coords.WorldPoint; +import static net.runelite.client.plugins.cluescrolls.clues.hotcold.HotColdSolver.isFirstPointCloser; +import static net.runelite.client.plugins.cluescrolls.clues.hotcold.HotColdSolver.isFirstPointCloserRect; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import org.junit.Test; + +public class HotColdSolverTest +{ + private static final String RESPONSE_TEXT_ICE_COLD_COLDER = "The device is ice cold, but colder than last time."; + private static final String RESPONSE_TEXT_VERY_COLD_WARMER = "The device is very cold, and warmer than last time."; + private static final String RESPONSE_TEXT_COLD = "The device is cold."; + private static final String RESPONSE_TEXT_COLD_COLDER = "The device is cold, but colder than last time."; + private static final String RESPONSE_TEXT_COLD_WARMER = "The device is cold, and warmer than last time."; + private static final String RESPONSE_TEXT_COLD_SAME_TEMP = "The device is cold, and the same temperature as last time."; + private static final String RESPONSE_TEXT_WARM = "The device is warm."; + private static final String RESPONSE_TEXT_WARM_SAME_TEMP = "The device is warm, and the same temperature as last time."; + private static final String RESPONSE_TEXT_VERY_HOT = "The device is very hot."; + private static final String RESPONSE_TEXT_VERY_HOT_COLDER = "The device is very hot, but colder than last time."; + private static final String RESPONSE_TEXT_VERY_HOT_WARMER = "The device is very hot, and warmer than last time."; + private static final String RESPONSE_TEXT_VERY_HOT_SAME_TEMP = "The device is very hot, and the same temperature as last time."; + + @Test + public void testOneStepSolution() + { + final Set foundLocation = Sets.immutableEnumSet(HotColdLocation.KARAMJA_KHARAZI_NE); + + testSolver(createHotColdSolver(), new WorldPoint(2852, 2992, 0), RESPONSE_TEXT_VERY_HOT, foundLocation); + } + + @Test + public void testIgnoreStartingTemperatureDifference() + { + final WorldPoint testedPoint = new WorldPoint(2852, 2992, 0); + final Set foundLocations = Sets.immutableEnumSet(HotColdLocation.KARAMJA_KHARAZI_NE); + + testSolver(createHotColdSolver(), testedPoint, RESPONSE_TEXT_VERY_HOT, foundLocations); + testSolver(createHotColdSolver(), testedPoint, RESPONSE_TEXT_VERY_HOT_COLDER, foundLocations); + testSolver(createHotColdSolver(), testedPoint, RESPONSE_TEXT_VERY_HOT_WARMER, foundLocations); + testSolver(createHotColdSolver(), testedPoint, RESPONSE_TEXT_VERY_HOT_SAME_TEMP, foundLocations); + } + + @Test + public void testSameTempNoChanges() + { + final HotColdSolver solver = createHotColdSolver(); + final WorldPoint testedPoint = new WorldPoint(2851, 2955, 0); + final Set foundLocations = Sets.immutableEnumSet( + HotColdLocation.KARAMJA_KHARAZI_NE, + HotColdLocation.KARAMJA_KHARAZI_SW); + + testSolver(solver, testedPoint, RESPONSE_TEXT_VERY_HOT, foundLocations); + testSolver(solver, testedPoint, RESPONSE_TEXT_VERY_HOT_SAME_TEMP, foundLocations); + } + + @Test + public void testNoChangesAfterSolutionFound() + { + final HotColdSolver solver = createHotColdSolver(); + final Set intermediateFoundLocations = Sets.immutableEnumSet( + HotColdLocation.KARAMJA_KHARAZI_NE, + HotColdLocation.KARAMJA_KHARAZI_SW); + final Set finalLocation = Sets.immutableEnumSet(HotColdLocation.KARAMJA_KHARAZI_NE); + + testSolver(solver, new WorldPoint(2851, 2955, 0), RESPONSE_TEXT_VERY_HOT, intermediateFoundLocations); + testSolver(solver, new WorldPoint(2852, 2955, 0), RESPONSE_TEXT_VERY_HOT_WARMER, finalLocation); + testSolver(solver, new WorldPoint(2851, 2955, 0), RESPONSE_TEXT_VERY_HOT_COLDER, finalLocation); + testSolver(solver, new WorldPoint(2465, 3495, 0), RESPONSE_TEXT_ICE_COLD_COLDER, finalLocation); + testSolver(solver, new WorldPoint(3056, 3291, 0), RESPONSE_TEXT_VERY_COLD_WARMER, finalLocation); + testSolver(solver, new WorldPoint(2571, 2956, 0), RESPONSE_TEXT_VERY_COLD_WARMER, finalLocation); + } + + @Test + public void testNarrowToFindSolutions() + { + final HotColdSolver solver = createHotColdSolver(); + final Set firstLocationsSet = Sets.immutableEnumSet( + HotColdLocation.FELDIP_HILLS_GNOME_GLITER, + HotColdLocation.FELDIP_HILLS_RED_CHIN, + HotColdLocation.KARAMJA_KHARAZI_NE, + HotColdLocation.KARAMJA_CRASH_ISLAND); + final Set secondLocationsSet = firstLocationsSet.stream() + .filter(location -> location != HotColdLocation.FELDIP_HILLS_GNOME_GLITER) + .collect(Collectors.toSet()); + final Set thirdLocationSet = secondLocationsSet.stream() + .filter(location -> location != HotColdLocation.FELDIP_HILLS_RED_CHIN) + .collect(Collectors.toSet()); + final Set finalLocation = thirdLocationSet.stream() + .filter(location -> location != HotColdLocation.KARAMJA_CRASH_ISLAND) + .collect(Collectors.toSet()); + + testSolver(solver, new WorldPoint(2711, 2803, 0), RESPONSE_TEXT_COLD, firstLocationsSet); + testSolver(solver, new WorldPoint(2711, 2802, 0), RESPONSE_TEXT_COLD_SAME_TEMP, secondLocationsSet); + testSolver(solver, new WorldPoint(2716, 2802, 0), RESPONSE_TEXT_COLD_WARMER, thirdLocationSet); + testSolver(solver, new WorldPoint(2739, 2808, 0), RESPONSE_TEXT_COLD_WARMER, thirdLocationSet); + testSolver(solver, new WorldPoint(2810, 2757, 0), RESPONSE_TEXT_COLD_COLDER, finalLocation); + } + + @Test + public void testSomewhatDistantLocations() + { + // Activate device on Ape Atoll when solution point is HotColdLocation.KARAMJA_KHARAZI_NE + testSolver(createHotColdSolver(), new WorldPoint(2723, 2743, 0), RESPONSE_TEXT_COLD, + Sets.immutableEnumSet( + HotColdLocation.KARAMJA_KHARAZI_NE, + HotColdLocation.KARAMJA_KHARAZI_SW, + HotColdLocation.KARAMJA_CRASH_ISLAND, + HotColdLocation.FELDIP_HILLS_SW, + HotColdLocation.FELDIP_HILLS_RANTZ, + HotColdLocation.FELDIP_HILLS_RED_CHIN, + HotColdLocation.FELDIP_HILLS_SE)); + + // Activate device near fairy ring DKP when solution point is HotColdLocation.KARAMJA_KHARAZI_NE + testSolver(createHotColdSolver(), new WorldPoint(2900, 3111, 0), RESPONSE_TEXT_COLD, + Sets.immutableEnumSet( + HotColdLocation.KARAMJA_WEST_BRIMHAVEN, + HotColdLocation.KARAMJA_KHARAZI_NE, + HotColdLocation.ASGARNIA_COW, + HotColdLocation.ASGARNIA_CRAFT_GUILD, + HotColdLocation.KANDARIN_WITCHHAVEN, + HotColdLocation.MISTHALIN_DRAYNOR_BANK)); + + // Activate device on Mudskipper Point when solution point is HotColdLocation.KARAMJA_KHARAZI_NE + testSolver(createHotColdSolver(), new WorldPoint(2985, 3106, 0), RESPONSE_TEXT_COLD, + Sets.immutableEnumSet( + HotColdLocation.KARAMJA_BRIMHAVEN_FRUIT_TREE, + HotColdLocation.KARAMJA_KHARAZI_NE, + HotColdLocation.ASGARNIA_COW, + HotColdLocation.ASGARNIA_CRAFT_GUILD, + HotColdLocation.MISTHALIN_LUMBRIDGE_2, + HotColdLocation.DESERT_BEDABIN_CAMP)); + } + + @Test + public void testZeahLocationNarrowing() + { + // Start with western Lovakengj sulphur mine and west of farming guild locations remaining + HotColdSolver solver = new HotColdSolver(EnumSet.of( + HotColdLocation.ZEAH_SULPHR_MINE, + HotColdLocation.ZEAH_FARMING_GUILD_W + )); + + testSolver(solver, new WorldPoint(1348, 3740, 0), RESPONSE_TEXT_WARM, + Sets.immutableEnumSet( + HotColdLocation.ZEAH_SULPHR_MINE, + HotColdLocation.ZEAH_FARMING_GUILD_W)); + testSolver(solver, new WorldPoint(1347, 3740, 0), RESPONSE_TEXT_WARM_SAME_TEMP, + Sets.immutableEnumSet(HotColdLocation.ZEAH_SULPHR_MINE)); + } + + @Test + public void testIsFirstPointCloserRect() + { + assertFalse(isFirstPointCloserRect(new WorldPoint(0, 0, 0), new WorldPoint(0, 0, 0), new Rectangle(0, 0, 1, 1))); + assertFalse(isFirstPointCloserRect(new WorldPoint(1, 0, 0), new WorldPoint(5, 0, 0), new Rectangle(2, 1, 5, 5))); + assertFalse(isFirstPointCloserRect(new WorldPoint(1, 0, 0), new WorldPoint(0, 0, 0), new Rectangle(2, 0, 1, 2))); + assertFalse(isFirstPointCloserRect(new WorldPoint(0, 0, 0), new WorldPoint(1, 1, 1), new Rectangle(2, 2, 2, 2))); + assertFalse(isFirstPointCloserRect(new WorldPoint(0, 0, 0), new WorldPoint(4, 4, 4), new Rectangle(1, 1, 2, 2))); + assertFalse(isFirstPointCloserRect(new WorldPoint(3, 2, 0), new WorldPoint(1, 5, 0), new Rectangle(0, 0, 4, 4))); + + assertTrue(isFirstPointCloserRect(new WorldPoint(1, 1, 0), new WorldPoint(0, 1, 0), new Rectangle(2, 0, 3, 2))); + assertTrue(isFirstPointCloserRect(new WorldPoint(4, 4, 0), new WorldPoint(1, 1, 0), new Rectangle(3, 3, 2, 2))); + assertTrue(isFirstPointCloserRect(new WorldPoint(3, 2, 0), new WorldPoint(7, 0, 0), new Rectangle(1, 3, 4, 2))); + + } + + @Test + public void testIsFirstPointCloser() + { + assertFalse(isFirstPointCloser(new WorldPoint(0, 0, 0), new WorldPoint(0, 0, 0), new WorldPoint(0, 0, 0))); + assertFalse(isFirstPointCloser(new WorldPoint(0, 0, 0), new WorldPoint(0, 0, 1), new WorldPoint(0, 0, 0))); + assertFalse(isFirstPointCloser(new WorldPoint(1, 0, 0), new WorldPoint(0, 0, 0), new WorldPoint(1, 1, 0))); + assertFalse(isFirstPointCloser(new WorldPoint(2, 2, 0), new WorldPoint(0, 0, 0), new WorldPoint(1, 1, 0))); + + assertTrue(isFirstPointCloser(new WorldPoint(1, 0, 0), new WorldPoint(0, 0, 0), new WorldPoint(2, 0, 0))); + assertTrue(isFirstPointCloser(new WorldPoint(1, 1, 0), new WorldPoint(1, 0, 0), new WorldPoint(2, 2, 0))); + assertTrue(isFirstPointCloser(new WorldPoint(1, 1, 1), new WorldPoint(0, 1, 0), new WorldPoint(1, 1, 0))); + } + + /** + * Tests a hot-cold solver by signalling a test point, temperature, and temperature change to it and asserting the + * resulting possible location set is equal to that of a given set of expected locations. + * + * @param solver The hot-cold solver to signal to. + *
+ * Note: This will mutate the passed solver, which is helpful for testing + * multiple sequential steps. + * @param testPoint The {@link WorldPoint} where the signal occurs. + * @param deviceResponse The string containing the temperature and temperature change which is + * given when the hot-cold checking device is activated. + * @param expectedRemainingPossibleLocations A {@link Set} of {@link HotColdLocation}s which is expected to be + * given by {@link HotColdSolver#getPossibleLocations()} after it receives + * the signal formed by the other given arguments. + */ + private static void testSolver(final HotColdSolver solver, final WorldPoint testPoint, final String deviceResponse, final Set expectedRemainingPossibleLocations) + { + final HotColdTemperature temperature = HotColdTemperature.getFromTemperatureSet(HotColdTemperature.MASTER_HOT_COLD_TEMPERATURES, deviceResponse); + final HotColdTemperatureChange temperatureChange = HotColdTemperatureChange.of(deviceResponse); + + assertNotNull(temperature); + assertEquals(expectedRemainingPossibleLocations, solver.signal(testPoint, temperature, temperatureChange)); + } + + /** + * @return A hot-cold solver with a starting set of master hot-cold locations nearby the KARAMJA_KHARAZI_NE + * location. {@link HotColdLocation#values()} is not used as it may change with future game updates, and + * such changes would break this test suite. + */ + private static HotColdSolver createHotColdSolver() + { + final Set hotColdLocations = EnumSet.of( + HotColdLocation.KARAMJA_KHARAZI_NE, + HotColdLocation.KARAMJA_KHARAZI_SW, + HotColdLocation.KARAMJA_GLIDER, + HotColdLocation.KARAMJA_MUSA_POINT, + HotColdLocation.KARAMJA_BRIMHAVEN_FRUIT_TREE, + HotColdLocation.KARAMJA_WEST_BRIMHAVEN, + HotColdLocation.KARAMJA_CRASH_ISLAND, + HotColdLocation.DESERT_BEDABIN_CAMP, + HotColdLocation.DESERT_MENAPHOS_GATE, + HotColdLocation.DESERT_POLLNIVNEACH, + HotColdLocation.DESERT_SHANTY, + HotColdLocation.MISTHALIN_LUMBRIDGE, + HotColdLocation.MISTHALIN_LUMBRIDGE_2, + HotColdLocation.MISTHALIN_DRAYNOR_BANK, + HotColdLocation.ASGARNIA_COW, + HotColdLocation.ASGARNIA_PARTY_ROOM, + HotColdLocation.ASGARNIA_CRAFT_GUILD, + HotColdLocation.ASGARNIA_RIMMINGTON, + HotColdLocation.ASGARNIA_MUDSKIPPER, + HotColdLocation.KANDARIN_WITCHHAVEN, + HotColdLocation.KANDARIN_NECRO_TOWER, + HotColdLocation.KANDARIN_FIGHT_ARENA, + HotColdLocation.KANDARIN_TREE_GNOME_VILLAGE, + HotColdLocation.FELDIP_HILLS_GNOME_GLITER, + HotColdLocation.FELDIP_HILLS_JIGGIG, + HotColdLocation.FELDIP_HILLS_RANTZ, + HotColdLocation.FELDIP_HILLS_RED_CHIN, + HotColdLocation.FELDIP_HILLS_SE, + HotColdLocation.FELDIP_HILLS_SOUTH, + HotColdLocation.FELDIP_HILLS_SW + ); + return new HotColdSolver(hotColdLocations); + } +} \ No newline at end of file diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/HotColdTemperatureChangeTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/HotColdTemperatureChangeTest.java new file mode 100644 index 0000000000..27a29c07fd --- /dev/null +++ b/runelite-client/src/test/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/HotColdTemperatureChangeTest.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2019, Jordan Atwood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.cluescrolls.clues.hotcold; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import org.junit.Test; + +public class HotColdTemperatureChangeTest +{ + private static final String[] VALID_MESSAGES = { + "The device is warm, and warmer than last time.", + "The device is cold, but colder than last time.", + "The device is very hot, and the same temperature as last time.", + }; + private static final String[] INVALID_MESSAGES = { + "The device is cold.", + "The device is ice cold.", + "The device is very cold.", + "The device is hot.", + "The device is incredibly hot.", + "The device is an octopus, and is wetter than last time", + "foobar", + "a q p w", + "My feet are cold, I should put them in some lukewarm water, or run hot water over them.", + "and warmer than and colder than and the same temperature", + }; + + @Test + public void testValidTemperatureChangeMessages() + { + for (final String message : VALID_MESSAGES) + { + assertNotNull(message, HotColdTemperatureChange.of(message)); + } + } + + @Test + public void testInvalidTemperatureChangeMessages() + { + for (final String message : INVALID_MESSAGES) + { + assertNull(message, HotColdTemperatureChange.of(message)); + } + } +} \ No newline at end of file diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/MasterHotColdLocationTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/MasterHotColdLocationTest.java new file mode 100644 index 0000000000..8830cb0575 --- /dev/null +++ b/runelite-client/src/test/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/MasterHotColdLocationTest.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2019, Jordan Atwood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.cluescrolls.clues.hotcold; + +import java.util.Arrays; +import java.util.Set; +import java.util.stream.Collectors; +import static org.junit.Assert.assertEquals; +import org.junit.Test; + +public class MasterHotColdLocationTest +{ + private static final Set MASTER_HOT_COLD_LOCATIONS = Arrays.stream(HotColdLocation.values()) + .filter(l -> !l.isBeginnerClue()) + .collect(Collectors.toSet()); + private static final int EXPECTED_DIMENSION_SIZE = 9; + + @Test + public void beginnerHotColdLocationAreaTest() + { + for (final HotColdLocation location : MASTER_HOT_COLD_LOCATIONS) + { + assertEquals(EXPECTED_DIMENSION_SIZE, location.getRect().height); + assertEquals(EXPECTED_DIMENSION_SIZE, location.getRect().width); + } + } +} \ No newline at end of file diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSClientMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSClientMixin.java index 4ab9ccf557..a079fbaa53 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/RSClientMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSClientMixin.java @@ -1529,7 +1529,7 @@ public abstract class RSClientMixin implements RSClient { if (renderX >= minX && renderX <= maxX && renderY >= minY && renderY <= maxY) { - WidgetItem widgetItem = new WidgetItem(widget.getItemId(), widget.getItemQuantity(), -1, widget.getBounds(), widget, false); + WidgetItem widgetItem = new WidgetItem(widget.getItemId(), widget.getItemQuantity(), -1, widget.getBounds(), widget, null); callbacks.drawItem(widget.getItemId(), widgetItem); } } diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSWidgetMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSWidgetMixin.java index f10da765f4..b4f7a9eefb 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/RSWidgetMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSWidgetMixin.java @@ -301,8 +301,9 @@ public abstract class RSWidgetMixin implements RSWidget dragOffsetY = p.getY(); } - Rectangle bounds = new Rectangle(itemX + dragOffsetX, itemY + dragOffsetY, ITEM_SLOT_SIZE, ITEM_SLOT_SIZE); - return new WidgetItem(itemId - 1, itemQuantity, index, bounds, this, isDragged); + Rectangle bounds = new Rectangle(itemX - 1, itemY - 1, ITEM_SLOT_SIZE, ITEM_SLOT_SIZE); + Rectangle draggedBounds = new Rectangle(itemX + dragOffsetX, itemY + dragOffsetY, ITEM_SLOT_SIZE, ITEM_SLOT_SIZE); + return new WidgetItem(itemId - 1, itemQuantity, index, bounds, this, draggedBounds); } @Inject