cluescrolls: Add beginner hot-cold clues

This commit is contained in:
Jordan Atwood
2019-06-12 23:28:52 -07:00
committed by Adam
parent e5d4e7a897
commit 76d4031445
7 changed files with 240 additions and 27 deletions

View File

@@ -34,7 +34,10 @@ import java.util.EnumMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.NPC;
import net.runelite.api.coords.LocalPoint;
import net.runelite.api.coords.WorldPoint;
@@ -51,26 +54,37 @@ import net.runelite.client.ui.overlay.components.LineComponent;
import net.runelite.client.ui.overlay.components.PanelComponent;
import net.runelite.client.ui.overlay.components.TitleComponent;
@EqualsAndHashCode(callSuper = false, exclude = { "hotColdSolver", "location" })
@Getter
@Slf4j
public class HotColdClue extends ClueScroll implements LocationClueScroll, LocationsClueScroll, TextClueScroll, NpcClueScroll
{
private static final int HOT_COLD_PANEL_WIDTH = 200;
private static final HotColdClue CLUE =
new HotColdClue("Buried beneath the ground, who knows where it's found. Lucky for you, A man called Jorral may have a clue.",
"Jorral",
"Speak to Jorral to receive a strange device.");
private static final HotColdClue BEGINNER_CLUE = new HotColdClue("Buried beneath the ground, who knows where it's found. Lucky for you, A man called Reldo may have a clue.",
"Reldo",
"Speak to Reldo to receive a strange device.");
private static final HotColdClue MASTER_CLUE = new HotColdClue("Buried beneath the ground, who knows where it's found. Lucky for you, A man called Jorral may have a clue.",
"Jorral",
"Speak to Jorral to receive a strange device.");
private final String text;
private final String npc;
private final String solution;
@Nullable
private HotColdSolver hotColdSolver;
private WorldPoint location;
public static HotColdClue forText(String text)
{
if (CLUE.text.equalsIgnoreCase(text))
if (BEGINNER_CLUE.text.equalsIgnoreCase(text))
{
return CLUE;
BEGINNER_CLUE.reset();
return BEGINNER_CLUE;
}
else if (MASTER_CLUE.text.equalsIgnoreCase(text))
{
MASTER_CLUE.reset();
return MASTER_CLUE;
}
return null;
@@ -88,12 +102,22 @@ public class HotColdClue extends ClueScroll implements LocationClueScroll, Locat
@Override
public WorldPoint[] getLocations()
{
if (hotColdSolver == null)
{
return new WorldPoint[0];
}
return hotColdSolver.getPossibleLocations().stream().map(HotColdLocation::getWorldPoint).toArray(WorldPoint[]::new);
}
@Override
public void makeOverlayHint(PanelComponent panelComponent, ClueScrollPlugin plugin)
{
if (hotColdSolver == null)
{
return;
}
panelComponent.getChildren().add(TitleComponent.builder()
.text("Hot/Cold Clue")
.build());
@@ -181,6 +205,11 @@ public class HotColdClue extends ClueScroll implements LocationClueScroll, Locat
@Override
public void makeWorldOverlayHint(Graphics2D graphics, ClueScrollPlugin plugin)
{
if (hotColdSolver == null)
{
return;
}
// when final location has been found
if (location != null)
{
@@ -194,7 +223,7 @@ public class HotColdClue extends ClueScroll implements LocationClueScroll, Locat
return;
}
// when strange device hasn't been activated yet, show Jorral
// when strange device hasn't been activated yet, show npc who gives you the strange device
if (hotColdSolver.getLastWorldPoint() == null && plugin.getNpcsToMark() != null)
{
for (NPC npcToMark : plugin.getNpcsToMark())
@@ -225,7 +254,27 @@ public class HotColdClue extends ClueScroll implements LocationClueScroll, Locat
public boolean update(final String message, final ClueScrollPlugin plugin)
{
final HotColdTemperature temperature = HotColdTemperature.of(message);
if (hotColdSolver == null)
{
return false;
}
final Set<HotColdTemperature> temperatureSet;
if (this.equals(BEGINNER_CLUE))
{
temperatureSet = HotColdTemperature.BEGINNER_HOT_COLD_TEMPERATURES;
}
else if (this.equals(MASTER_CLUE))
{
temperatureSet = HotColdTemperature.MASTER_HOT_COLD_TEMPERATURES;
}
else
{
temperatureSet = null;
}
final HotColdTemperature temperature = HotColdTemperature.getFromTemperatureSet(temperatureSet, message);
if (temperature == null)
{
@@ -239,7 +288,8 @@ public class HotColdClue extends ClueScroll implements LocationClueScroll, Locat
return false;
}
if (temperature == HotColdTemperature.VISIBLY_SHAKING)
if ((this.equals(BEGINNER_CLUE) && temperature == HotColdTemperature.BEGINNER_VISIBLY_SHAKING)
|| (this.equals(MASTER_CLUE) && temperature == HotColdTemperature.MASTER_VISIBLY_SHAKING))
{
markFinalSpot(localWorld);
}
@@ -262,7 +312,26 @@ public class HotColdClue extends ClueScroll implements LocationClueScroll, Locat
private void initializeSolver()
{
final boolean isBeginner;
if (this.equals(BEGINNER_CLUE))
{
isBeginner = true;
}
else if (this.equals(MASTER_CLUE))
{
isBeginner = false;
}
else
{
log.warn("Hot cold solver could not be initialized, clue type is unknown; text: {}, npc: {}, solution: {}",
text, npc, solution);
hotColdSolver = null;
return;
}
final Set<HotColdLocation> locations = Arrays.stream(HotColdLocation.values())
.filter(l -> l.isBeginnerClue() == isBeginner)
.collect(Collectors.toSet());
hotColdSolver = new HotColdSolver(locations);
}

View File

@@ -1,6 +1,7 @@
/*
* Copyright (c) 2018, Eadgars Ruse <https://github.com/Eadgars-Ruse>
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* Copyright (c) 2019, Jordan Atwood <nightfirecat@protonmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -68,6 +69,8 @@ public enum HotColdLocation
DESERT_POLLNIVNEACH(new WorldPoint(3287, 2975, 0), DESERT, "West of Pollnivneach."),
DESERT_MTA(new WorldPoint(3350, 3293, 0), DESERT, "Next to Mage Training Arena."),
DESERT_SHANTY(new WorldPoint(3294, 3106, 0), DESERT, "South-west of Shantay Pass."),
DRAYNOR_MANOR_MUSHROOMS(true, new WorldPoint(3096, 3379, 0), MISTHALIN, "Patch of mushrooms just northwest of Draynor Manor"),
DRAYNOR_WHEAT_FIELD(true, new WorldPoint(3120, 3282, 0), MISTHALIN, "Inside the wheat field next to Draynor Village"),
FELDIP_HILLS_JIGGIG(new WorldPoint(2413, 3055, 0), FELDIP_HILLS, "West of Jiggig, east of the fairy ring bkp."),
FELDIP_HILLS_SW(new WorldPoint(2582, 2895, 0), FELDIP_HILLS, "West of the southeasternmost lake in Feldip Hills."),
FELDIP_HILLS_GNOME_GLITER(new WorldPoint(2553, 2972, 0), FELDIP_HILLS, "East of the gnome glider (Lemantolly Undri)."),
@@ -90,6 +93,7 @@ public enum HotColdLocation
FREMENNIK_PROVINCE_ASTRAL_ALTER(new WorldPoint(2147, 3862, 0), FREMENNIK_PROVINCE, "Astral altar"),
FREMENNIK_PROVINCE_LUNAR_VILLAGE(new WorldPoint(2087, 3915, 0), FREMENNIK_PROVINCE, "Lunar Isle, inside the village."),
FREMENNIK_PROVINCE_LUNAR_NORTH(new WorldPoint(2106, 3949, 0), FREMENNIK_PROVINCE, "Lunar Isle, north of the village."),
ICE_MOUNTAIN(true, new WorldPoint(3007, 3475, 0), MISTHALIN, "Atop Ice Mountain"),
KANDARIN_SINCLAR_MANSION(new WorldPoint(2726, 3588, 0), KANDARIN, "North-west of the Sinclair Mansion, near the log balance shortcut."),
KANDARIN_CATHERBY(new WorldPoint(2774, 3433, 0), KANDARIN, "Catherby, between the bank and the beehives, near small rock formation."),
KANDARIN_GRAND_TREE(new WorldPoint(2444, 3503, 0), KANDARIN, "Grand Tree, just east of the terrorchick gnome enclosure."),
@@ -114,6 +118,7 @@ public enum HotColdLocation
KARAMJA_KHARAZI_NE(new WorldPoint(2904, 2925, 0), KARAMJA, "North-eastern part of Kharazi Jungle."),
KARAMJA_KHARAZI_SW(new WorldPoint(2783, 2898, 0), KARAMJA, "South-western part of Kharazi Jungle."),
KARAMJA_CRASH_ISLAND(new WorldPoint(2910, 2737, 0), KARAMJA, "Northern part of Crash Island."),
LUMBRIDGE_COW_FIELD(true, new WorldPoint(3174, 3336, 0), MISTHALIN, "Cow field north of Lumbridge"),
MISTHALIN_VARROCK_STONE_CIRCLE(new WorldPoint(3225, 3355, 0), MISTHALIN, "South of the stone circle near Varrock's entrance."),
MISTHALIN_LUMBRIDGE(new WorldPoint(3238, 3169, 0), MISTHALIN, "Just north-west of the Lumbridge Fishing tutor."),
MISTHALIN_LUMBRIDGE_2(new WorldPoint(3170, 3278, 0), MISTHALIN, "North of the pond between Lumbridge and Draynor Village."),
@@ -130,6 +135,7 @@ public enum HotColdLocation
MORYTANIA_MOS_LES_HARMLESS_BAR(new WorldPoint(3670, 2974, 0), MORYTANIA, "Near Mos Le'Harmless southern bar."),
MORYTANIA_DRAGONTOOTH_NORTH(new WorldPoint(3813, 3567, 0), MORYTANIA, "Northern part of Dragontooth Island."),
MORYTANIA_DRAGONTOOTH_SOUTH(new WorldPoint(3803, 3532, 0), MORYTANIA, "Southern part of Dragontooth Island."),
NORTHEAST_OF_AL_KHARID_MINE(true, new WorldPoint(3332, 3313, 0), MISTHALIN, "Northeast of Al Kharid Mine"),
WESTERN_PROVINCE_EAGLES_PEAK(new WorldPoint(2297, 3530, 0), WESTERN_PROVINCE, "North-west of Eagles' Peak."),
WESTERN_PROVINCE_PISCATORIS(new WorldPoint(2337, 3689, 0), WESTERN_PROVINCE, "Piscatoris Fishing Colony"),
WESTERN_PROVINCE_PISCATORIS_HUNTER_AREA(new WorldPoint(2361, 3566, 0), WESTERN_PROVINCE, "Eastern part of Piscatoris Hunter area, south-west of the Falconry."),
@@ -175,12 +181,20 @@ public enum HotColdLocation
ZEAH_DAIRY_COW(new WorldPoint(1320, 3718, 0), ZEAH, "North-east of the Kebos Lowlands, east of the dairy cow."),
ZEAH_CRIMSON_SWIFTS(new WorldPoint(1186, 3583, 0), ZEAH, "South-west of the Kebos Swamp, below the crimson swifts.");
private final boolean beginnerClue;
private final WorldPoint worldPoint;
private final HotColdArea hotColdArea;
private final String area;
HotColdLocation(WorldPoint worldPoint, HotColdArea hotColdArea, String areaDescription)
{
this(false, worldPoint, hotColdArea, areaDescription);
}
public Rectangle getRect()
{
return new Rectangle(worldPoint.getX() - 4, worldPoint.getY() - 4, 9, 9);
final int digRadius = beginnerClue ? HotColdTemperature.BEGINNER_VISIBLY_SHAKING.getMaxDistance() :
HotColdTemperature.MASTER_VISIBLY_SHAKING.getMaxDistance();
return new Rectangle(worldPoint.getX() - digRadius, worldPoint.getY() - digRadius, digRadius * 2 + 1, digRadius * 2 + 1);
}
}

View File

@@ -24,9 +24,12 @@
*/
package net.runelite.client.plugins.cluescrolls.clues.hotcold;
import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import javax.annotation.Nullable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@@ -40,8 +43,31 @@ public enum HotColdTemperature
WARM("warm", 100, 149),
HOT("hot", 70, 99),
VERY_HOT("very hot", 30, 69),
INCREDIBLY_HOT("incredibly hot", 5, 29),
VISIBLY_SHAKING("visibly shaking", 0, 4);
BEGINNER_INCREDIBLY_HOT("incredibly hot", 4, 29),
BEGINNER_VISIBLY_SHAKING("visibly shaking", 0, 3),
MASTER_INCREDIBLY_HOT("incredibly hot", 5, 29),
MASTER_VISIBLY_SHAKING("visibly shaking", 0, 4);
public static final Set<HotColdTemperature> BEGINNER_HOT_COLD_TEMPERATURES = Sets.immutableEnumSet(
ICE_COLD,
VERY_COLD,
COLD,
WARM,
HOT,
VERY_HOT,
BEGINNER_INCREDIBLY_HOT,
BEGINNER_VISIBLY_SHAKING
);
public static final Set<HotColdTemperature> MASTER_HOT_COLD_TEMPERATURES = Sets.immutableEnumSet(
ICE_COLD,
VERY_COLD,
COLD,
WARM,
HOT,
VERY_HOT,
MASTER_INCREDIBLY_HOT,
MASTER_VISIBLY_SHAKING
);
private final String text;
private final int minDistance;
@@ -50,24 +76,26 @@ public enum HotColdTemperature
private static final String DEVICE_USED_START_TEXT = "The device is ";
/**
* Gets the temperature corresponding to the passed string.
* Gets the temperature from a set of temperatures corresponding to the passed string.
*
* @param message A string containing a temperature value
* @return The corresponding enum for the passed string.
* @param temperatureSet A set of temperature values to select from
* @param message A string containing a temperature value
* @return The corresponding enum from the given temperature set.
* <p>
* Note that in cases where two temperature enums are equally likely to be the given temperature (say, two
* temperatures with identical text values), the behavior is undefined.
* Note that in cases where two temperature values in the given set are equally likely to be the given
* temperature (say, two temperatures with identical text values), the behavior is undefined.
*/
public static HotColdTemperature of(final String message)
@Nullable
public static HotColdTemperature getFromTemperatureSet(final Set<HotColdTemperature> temperatureSet, final String message)
{
if (!message.startsWith(DEVICE_USED_START_TEXT))
if (!message.startsWith(DEVICE_USED_START_TEXT) || temperatureSet == null)
{
return null;
}
final List<HotColdTemperature> possibleTemperatures = new ArrayList<>();
for (final HotColdTemperature temperature : values())
for (final HotColdTemperature temperature : temperatureSet)
{
if (message.contains(temperature.getText()))
{

View File

@@ -0,0 +1,50 @@
/*
* Copyright (c) 2019, Jordan Atwood <nightfirecat@protonmail.com>
* 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<HotColdLocation> 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);
}
}
}

View File

@@ -26,6 +26,7 @@ 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;
@@ -207,7 +208,7 @@ public class HotColdSolverTest
*/
private static void testSolver(final HotColdSolver solver, final WorldPoint testPoint, final String deviceResponse, final Set<HotColdLocation> expectedRemainingPossibleLocations)
{
final HotColdTemperature temperature = HotColdTemperature.of(deviceResponse);
final HotColdTemperature temperature = HotColdTemperature.getFromTemperatureSet(HotColdTemperature.MASTER_HOT_COLD_TEMPERATURES, deviceResponse);
final HotColdTemperatureChange temperatureChange = HotColdTemperatureChange.of(deviceResponse);
assertNotNull(temperature);
@@ -221,7 +222,7 @@ public class HotColdSolverTest
*/
private static HotColdSolver createHotColdSolver()
{
final Set<HotColdLocation> hotColdLocations = Sets.immutableEnumSet(
final Set<HotColdLocation> hotColdLocations = EnumSet.of(
HotColdLocation.KARAMJA_KHARAZI_NE,
HotColdLocation.KARAMJA_KHARAZI_SW,
HotColdLocation.KARAMJA_GLIDER,

View File

@@ -52,7 +52,8 @@ public class HotColdTemperatureTest
{
for (final String message : VALID_MESSAGES)
{
assertNotNull(message, HotColdTemperature.of(message));
assertNotNull(message, HotColdTemperature.getFromTemperatureSet(HotColdTemperature.BEGINNER_HOT_COLD_TEMPERATURES, message));
assertNotNull(message, HotColdTemperature.getFromTemperatureSet(HotColdTemperature.MASTER_HOT_COLD_TEMPERATURES, message));
}
}
@@ -61,15 +62,16 @@ public class HotColdTemperatureTest
{
for (final String message : INVALID_MESSAGES)
{
assertNull(message, HotColdTemperature.of(message));
assertNull(message, HotColdTemperature.getFromTemperatureSet(HotColdTemperature.BEGINNER_HOT_COLD_TEMPERATURES, message));
assertNull(message, HotColdTemperature.getFromTemperatureSet(HotColdTemperature.MASTER_HOT_COLD_TEMPERATURES, message));
}
}
@Test
public void testAmbiguousTemperatureMessages()
{
assertEquals(HotColdTemperature.ICE_COLD, HotColdTemperature.of("The device is ice cold."));
assertEquals(HotColdTemperature.VERY_COLD, HotColdTemperature.of("The device is very cold."));
assertEquals(HotColdTemperature.VERY_HOT, HotColdTemperature.of("The device is very hot."));
assertEquals(HotColdTemperature.ICE_COLD, HotColdTemperature.getFromTemperatureSet(HotColdTemperature.MASTER_HOT_COLD_TEMPERATURES, "The device is ice cold."));
assertEquals(HotColdTemperature.VERY_COLD, HotColdTemperature.getFromTemperatureSet(HotColdTemperature.MASTER_HOT_COLD_TEMPERATURES, "The device is very cold."));
assertEquals(HotColdTemperature.VERY_HOT, HotColdTemperature.getFromTemperatureSet(HotColdTemperature.MASTER_HOT_COLD_TEMPERATURES, "The device is very hot."));
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright (c) 2019, Jordan Atwood <nightfirecat@protonmail.com>
* 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<HotColdLocation> 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);
}
}
}