diff --git a/build.gradle b/build.gradle index a56ad58040..62c4fb9a7d 100644 --- a/build.gradle +++ b/build.gradle @@ -30,6 +30,7 @@ dependencies { testImplementation group: "org.junit.jupiter", name: "junit-jupiter-engine", version: junitVersion testImplementation group: 'org.mockito', name:'mockito-core', version: "4.11.0" // runelite uses 3.1.0 + testImplementation group: 'org.mockito', name:'mockito-inline', version: "4.11.0" testImplementation(group: 'com.google.inject.extensions', name:'guice-testlib', version: "4.1.0") { exclude group: 'com.google.inject', module: 'guice' // already provided by runelite } diff --git a/src/main/java/com/questhelper/helpers/quests/thecurseofarrav/MetalDoorSolver.java b/src/main/java/com/questhelper/helpers/quests/thecurseofarrav/MetalDoorSolver.java index 782bbfd11b..052f020ee4 100644 --- a/src/main/java/com/questhelper/helpers/quests/thecurseofarrav/MetalDoorSolver.java +++ b/src/main/java/com/questhelper/helpers/quests/thecurseofarrav/MetalDoorSolver.java @@ -1,5 +1,8 @@ package com.questhelper.helpers.quests.thecurseofarrav; +import com.questhelper.QuestHelperPlugin; +import com.questhelper.requirements.ManualRequirement; +import com.questhelper.requirements.Requirement; import com.questhelper.requirements.item.ItemRequirement; import com.questhelper.requirements.widget.WidgetModelRequirement; import com.questhelper.requirements.widget.WidgetPresenceRequirement; @@ -10,8 +13,12 @@ import com.questhelper.steps.ItemStep; import com.questhelper.steps.ObjectStep; import com.questhelper.steps.QuestStep; +import java.awt.*; +import java.util.Arrays; import java.util.List; +import java.util.Objects; import java.util.regex.Pattern; +import java.util.stream.IntStream; import javax.inject.Inject; import com.questhelper.steps.WidgetStep; import com.questhelper.steps.widget.WidgetDetails; @@ -23,6 +30,7 @@ import net.runelite.api.coords.WorldPoint; import net.runelite.api.events.GameTick; import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.ui.FontManager; import static com.questhelper.requirements.util.LogicHelper.and; import static com.questhelper.requirements.util.LogicHelper.not; @@ -132,6 +140,10 @@ public class MetalDoorSolver extends DetailedOwnerStep private WidgetModelRequirement inputThirdCorrect; private WidgetModelRequirement inputFourthCorrect; + private int distanceUp = 69; + private int distanceDown = 69; + private ManualRequirement shouldClickDownInteadOfUp; + public MetalDoorSolver(TheCurseOfArrav theCurseOfArrav) { super(theCurseOfArrav, "Solve the Metal door puzzle by following the instructions in the overlay."); @@ -212,11 +224,105 @@ public static int[] calculate(String code) }; } + public static int calculateDistanceUp(int currentNumber, int targetNumber) + { + if (currentNumber == targetNumber) + { + return 0; + } + + if (currentNumber > targetNumber) + { + return targetNumber - currentNumber + 10; + } + else + { + return targetNumber - currentNumber; + } + } + + public static int calculateDistanceDown(int currentNumber, int targetNumber) + { + if (currentNumber == targetNumber) + { + return 0; + } + + if (currentNumber < targetNumber) + { + return currentNumber - targetNumber + 10; + } + else + { + return currentNumber - targetNumber; + } + } + @Subscribe public void onGameTick(GameTick event) { if (this.code != null) { + var currentNumberWidget = client.getWidget(PUZZLE_GROUP_ID, PUZZLE_PASSWORD_CURRENT_CHILD_ID); + if (currentNumberWidget != null) + { + var currentNumberModel = currentNumberWidget.getModelId(); + var currentNumber = IntStream.range(0, PUZZLE_NUMBERS.length) + .filter(i -> PUZZLE_NUMBERS[i] == currentNumberModel) + .findFirst() + .orElse(-1); + + var input1 = client.getWidget(PUZZLE_GROUP_ID, PUZZLE_PASSWORD_1_CHILD_ID); + var input2 = client.getWidget(PUZZLE_GROUP_ID, PUZZLE_PASSWORD_2_CHILD_ID); + var input3 = client.getWidget(PUZZLE_GROUP_ID, PUZZLE_PASSWORD_3_CHILD_ID); + var input4 = client.getWidget(PUZZLE_GROUP_ID, PUZZLE_PASSWORD_4_CHILD_ID); + + if (input1 == null || input2 == null || input3 == null || input4 == null) + { + // something very wrong + this.distanceUp = -1; + this.distanceDown = -1; + return; + } + + var input1Text = input1.getText(); + var input2Text = input2.getText(); + var input3Text = input3.getText(); + var input4Text = input4.getText(); + + var targetNumber = -1; + + if (Objects.equals(input1Text, "-") || Integer.parseInt(input1.getText()) != this.doorPassword[0]) + { + targetNumber = this.doorPassword[0]; + } + else if (Objects.equals(input2Text, "-") || Integer.parseInt(input2.getText()) != this.doorPassword[1]) + { + targetNumber = this.doorPassword[1]; + } + else if (Objects.equals(input3Text, "-") || Integer.parseInt(input3.getText()) != this.doorPassword[2]) + { + targetNumber = this.doorPassword[2]; + } + else if (Objects.equals(input4Text, "-") || Integer.parseInt(input4.getText()) != this.doorPassword[3]) + { + targetNumber = this.doorPassword[3]; + } + + if (currentNumber == -1 || targetNumber == -1) + { + // something very wrong + this.distanceUp = -1; + this.distanceDown = -1; + } + else + { + this.distanceUp = calculateDistanceUp(currentNumber, targetNumber); + this.distanceDown = calculateDistanceDown(currentNumber, targetNumber); + + this.shouldClickDownInteadOfUp.setShouldPass(this.distanceDown < this.distanceUp); + } + } return; } @@ -231,7 +337,8 @@ public void onGameTick(GameTick event) { this.code = matcher.group(1); this.doorPassword = calculate(this.code); - if (this.doorPassword != null) { + if (this.doorPassword != null) + { firstNumberCorrect.setText(String.valueOf(this.doorPassword[0])); inputFirstCorrect.setId(PUZZLE_NUMBERS[this.doorPassword[0]]); secondNumberCorrect.setText(String.valueOf(this.doorPassword[1])); @@ -243,10 +350,7 @@ public void onGameTick(GameTick event) } updateSteps(); } - } - private void updatePuzzleSteps() - { } @Override @@ -258,11 +362,11 @@ public void startUp() @Override protected void setupSteps() { - var decoderStrips = new ItemRequirement("Decoder strips", ItemID.DECODER_STRIPS); + this.shouldClickDownInteadOfUp = new ManualRequirement(); var codeKey = new ItemRequirement("Code key", ItemID.CODE_KEY); - readCode = new ItemStep(getQuestHelper(), "Read the Code key in your inventory.", codeKey.highlighted(), decoderStrips); // TODO + readCode = new ItemStep(getQuestHelper(), "Read the Code key in your inventory.", codeKey.highlighted()); - clickMetalDoors = new ObjectStep(getQuestHelper(), ObjectID.METAL_DOORS, new WorldPoint(3612, 4582, 0), "Open the metal doors and solve the puzzle.", codeKey, decoderStrips); + clickMetalDoors = new ObjectStep(getQuestHelper(), ObjectID.METAL_DOORS, new WorldPoint(3612, 4582, 0), "Open the metal doors and solve the puzzle.", codeKey); var puzzleWidgetOpen = new WidgetPresenceRequirement(PUZZLE_GROUP_ID, PUZZLE_BTN_UP_CHILD_ID); @@ -283,20 +387,60 @@ protected void setupSteps() inputFourthCorrect = new WidgetModelRequirement(PUZZLE_GROUP_ID, PUZZLE_PASSWORD_CURRENT_CHILD_ID, -1); var clickUp = new WidgetStep(getQuestHelper(), "Click the Up button.", new WidgetDetails(PUZZLE_GROUP_ID, PUZZLE_BTN_UP_CHILD_ID)); + clickUp.addExtraWidgetOverlayHintFunction(this::drawDistanceUp); + var clickDown = new WidgetStep(getQuestHelper(), "Click the Down button.", new WidgetDetails(PUZZLE_GROUP_ID, PUZZLE_BTN_DOWN_CHILD_ID)); + clickDown.addExtraWidgetOverlayHintFunction(this::drawDistanceDown); var submitNumber = new WidgetStep(getQuestHelper(), "Click the Enter button.", new WidgetDetails(PUZZLE_GROUP_ID, PUZZLE_ENTER_CHILD_ID)); var pressBack = new WidgetStep(getQuestHelper(), "Click the Back button.", new WidgetDetails(PUZZLE_GROUP_ID, PUZZLE_BACK_CHILD_ID)); + var clickUpOrDown = new ConditionalStep(getQuestHelper(), clickUp); + clickUpOrDown.addStep(shouldClickDownInteadOfUp, clickDown); + solvePuzzle = new ConditionalStep(getQuestHelper(), pressBack); solvePuzzle.addStep(not(puzzleWidgetOpen), clickMetalDoors); solvePuzzle.addStep(and(fourthNumberCorrect, thirdNumberCorrect, secondNumberCorrect, firstNumberCorrect), submitNumber); solvePuzzle.addStep(and(fourthNumberEmpty, inputFourthCorrect, firstNumberCorrect, secondNumberCorrect, thirdNumberCorrect), submitNumber); - solvePuzzle.addStep(and(fourthNumberEmpty, firstNumberCorrect, secondNumberCorrect, thirdNumberCorrect), clickUp); + solvePuzzle.addStep(and(fourthNumberEmpty, firstNumberCorrect, secondNumberCorrect, thirdNumberCorrect), clickUpOrDown); solvePuzzle.addStep(and(thirdNumberEmpty, inputThirdCorrect, firstNumberCorrect, secondNumberCorrect), submitNumber); - solvePuzzle.addStep(and(thirdNumberEmpty, firstNumberCorrect, secondNumberCorrect), clickUp); + solvePuzzle.addStep(and(thirdNumberEmpty, firstNumberCorrect, secondNumberCorrect), clickUpOrDown); solvePuzzle.addStep(and(secondNumberEmpty, inputSecondCorrect, firstNumberCorrect), submitNumber); - solvePuzzle.addStep(and(secondNumberEmpty, firstNumberCorrect), clickUp); + solvePuzzle.addStep(and(secondNumberEmpty, firstNumberCorrect), clickUpOrDown); solvePuzzle.addStep(and(firstNumberEmpty, inputFirstCorrect), submitNumber); - solvePuzzle.addStep(firstNumberEmpty, clickUp); + solvePuzzle.addStep(firstNumberEmpty, clickUpOrDown); + } + + public void drawDistanceUp(Graphics2D graphics, QuestHelperPlugin plugin) + { + super.makeWidgetOverlayHint(graphics, plugin); + + var arrow = client.getWidget(PUZZLE_GROUP_ID, PUZZLE_BTN_DOWN_CHILD_ID); + if (arrow == null) + { + return; + } + + int widgetX = arrow.getCanvasLocation().getX() + (arrow.getWidth() / 2) - 30; + int widgetY = arrow.getCanvasLocation().getY() + (arrow.getHeight() / 2) + 4; + Font font = FontManager.getRunescapeFont().deriveFont(Font.BOLD, 16); + graphics.setFont(font); + graphics.drawString(Integer.toString(this.distanceUp), widgetX, widgetY); + } + + public void drawDistanceDown(Graphics2D graphics, QuestHelperPlugin plugin) + { + super.makeWidgetOverlayHint(graphics, plugin); + + var arrow = client.getWidget(PUZZLE_GROUP_ID, PUZZLE_BTN_DOWN_CHILD_ID); + if (arrow == null) + { + return; + } + + int widgetX = arrow.getCanvasLocation().getX() + (arrow.getWidth() / 2) - 30; + int widgetY = arrow.getCanvasLocation().getY() + (arrow.getHeight() / 2) + 4; + Font font = FontManager.getRunescapeFont().deriveFont(Font.BOLD, 16); + graphics.setFont(font); + graphics.drawString(Integer.toString(this.distanceDown), widgetX, widgetY); } protected void updateSteps() @@ -307,7 +451,8 @@ protected void updateSteps() return; } - if (this.doorPassword == null) { + if (this.doorPassword == null) + { startUpStep(solvePuzzleFallback); return; } @@ -323,6 +468,6 @@ public List getSteps() this.clickMetalDoors, this.solvePuzzleFallback, this.solvePuzzle - ); + ); } } diff --git a/src/main/java/com/questhelper/helpers/quests/thecurseofarrav/TheCurseOfArrav.java b/src/main/java/com/questhelper/helpers/quests/thecurseofarrav/TheCurseOfArrav.java index 8092383d9c..fb7fe31440 100644 --- a/src/main/java/com/questhelper/helpers/quests/thecurseofarrav/TheCurseOfArrav.java +++ b/src/main/java/com/questhelper/helpers/quests/thecurseofarrav/TheCurseOfArrav.java @@ -35,14 +35,17 @@ import com.questhelper.questinfo.QuestHelperQuest; import com.questhelper.requirements.Requirement; import com.questhelper.requirements.item.ItemRequirement; +import com.questhelper.requirements.item.ItemRequirements; import com.questhelper.requirements.item.TeleportItemRequirement; import com.questhelper.requirements.player.CombatLevelRequirement; import com.questhelper.requirements.player.FreeInventorySlotRequirement; import com.questhelper.requirements.player.SkillRequirement; import com.questhelper.requirements.quest.QuestRequirement; import static com.questhelper.requirements.util.LogicHelper.and; +import static com.questhelper.requirements.util.LogicHelper.nor; import static com.questhelper.requirements.util.LogicHelper.not; import static com.questhelper.requirements.util.LogicHelper.or; +import com.questhelper.requirements.util.Operation; import com.questhelper.requirements.var.VarbitRequirement; import com.questhelper.requirements.zone.Zone; import com.questhelper.requirements.zone.ZoneRequirement; @@ -79,16 +82,16 @@ public class TheCurseOfArrav extends BasicQuestHelper // Required items private ItemRequirement dwellberries3; private ItemRequirement ringOfLife; - private ItemRequirement anyPickaxe; + public ItemRequirement anyPickaxe; private ItemRequirement anyGrappleableCrossbow; private ItemRequirement mithrilGrapple; private ItemRequirement insulatedBoots; // Recommended items private TeleportItemRequirement fairyRingDLQ; + private TeleportItemRequirement lumberyardTeleport; // trollheim teleport / ghommal's hilt // antivenom - // lumberyard teleport // melee (crush) combat gear for golem // ranged combat gear for arrav private ItemRequirement golemCombatGear; @@ -120,22 +123,22 @@ public class TheCurseOfArrav extends BasicQuestHelper // Steps /// 0 + 2 - private NpcStep startQuest; + NpcStep startQuest; /// 4 - private ObjectStep enterTomb; + ObjectStep enterTomb; /// 6 + 8 - private ConditionalStep unlockImposingDoors; - private ObjectStep getFirstKey; - private ObjectStep getSecondKey; - private ObjectStep pullSouthLever; - private ObjectStep pullNorthLever; + ConditionalStep unlockImposingDoors; + ObjectStep getFirstKey; + ObjectStep getSecondKey; + ObjectStep pullSouthLever; + ObjectStep pullNorthLever; /// 10 - private ConditionalStep fightGolemCond; - private ObjectStep enterGolemArena; - private NpcStep fightGolemGuard; + ConditionalStep fightGolemCond; + ObjectStep enterGolemArena; + NpcStep fightGolemGuard; /// 12 + 14 private ConditionalStep finishTilePuzzleAndGetCanopicJar; @@ -185,6 +188,21 @@ public class TheCurseOfArrav extends BasicQuestHelper private ObjectStep enterBossRoom; private ObjectStep grappleAcross; private ObjectStep openMetalDoors; + ObjectStep getToSouthLever; + ObjectStep leaveSouthLever; + ObjectStep getToNorthLever; + ObjectStep leaveNorthLever; + ItemStep combineJarWithDwellberries; + ItemStep combineJarWithRingOfLife; + NpcStep returnToElias; + ObjectStep headToTrollheim; + ObjectStep continueThroughTrollheimCave; + ObjectStep enterTrollweissCave; + ItemRequirement canopicJarFull; + ObjectStep climbUpstairsAndTalkToArrav; + NpcStep talkToArrav; + ObjectStep goToNextRoom; + ObjectStep searchTapestry; @Override @@ -232,7 +250,7 @@ public Map loadSteps() @Override protected void setupZones() { - insideTomb = new ZoneRequirement(new Zone(new WorldPoint(3842, 4603, 0), new WorldPoint(3900, 4547, 0))); + insideTomb = new ZoneRequirement(new Zone(new WorldPoint(3842, 4547, 0), new WorldPoint(3900, 4603, 0))); insideTombSecondFloor = new ZoneRequirement( new Zone(new WorldPoint(3719, 4674, 0), new WorldPoint(3770, 4732, 0)), new Zone(new WorldPoint(3845, 4674, 0), new WorldPoint(3900, 4732, 0)) @@ -279,6 +297,7 @@ protected void setupRequirements() // Recommended items fairyRingDLQ = new TeleportItemRequirement("Fairy Ring [DLQ]", ItemCollections.FAIRY_STAFF); + lumberyardTeleport = new TeleportItemRequirement("Lumberyard teleport", ItemID.LUMBERYARD_TELEPORT); staminaPotion = new ItemRequirement("Stamina potion", ItemCollections.STAMINA_POTIONS, 1); prayerPotion = new ItemRequirement("Prayer potion", ItemCollections.PRAYER_POTIONS, 1); golemCombatGear = new ItemRequirement("Crush or ranged combat gear to fight the Golem guard", -1, -1); @@ -293,6 +312,7 @@ protected void setupRequirements() // Mid-quest item requirements firstMastabaKey = new ItemRequirement("Mastaba Key", ItemID.MASTABA_KEY); secondMastabaKey = new ItemRequirement("Mastaba Key", ItemID.MASTABA_KEY_30309); + canopicJarFull = new ItemRequirement("Canopic jar (full)", ItemID.CANOPIC_JAR_FULL); canopicJarFullForHeist = new ItemRequirement("Canopic jar (full)", ItemID.CANOPIC_JAR_FULL); canopicJarFullForHeist.setTooltip("You can get a new one from Elias at the entrance of Zemouregal's base if you've lost it."); } @@ -306,57 +326,65 @@ public void setupSteps() startQuest.addDialogStep("Yes."); enterTomb = new ObjectStep(this, ObjectID.ENTRY_50201, new WorldPoint(3486, 3023, 0), "Enter the tomb south-west of Elias."); + // TODO: Add item requirements to enterTomb step + // - 2 free inventory slots - getFirstKey = new ObjectStep(this, ObjectID.SKELETON_50350, new WorldPoint(3875, 4554, 0), "Get the first Mastaba key from the skeleton south of the entrance."); + var anyMastabaKey = new ItemRequirement("Mastaba Key", List.of(ItemID.MASTABA_KEY, ItemID.MASTABA_KEY_30309)); + + getFirstKey = new ObjectStep(this, ObjectID.SKELETON_50350, new WorldPoint(3875, 4554, 0), "Get the first Mastaba key from the skeleton in the cave south of the entrance."); getSecondKey = new ObjectStep(this, ObjectID.SKELETON_50353, new WorldPoint(3880, 4585, 0), "Get the second Mastaba key from the skeleton east of the entrance."); var bySouthLever = new Zone(new WorldPoint(3893, 4554, 0), new WorldPoint(3894, 4552, 0)); var bySouthLeverReq = new ZoneRequirement(bySouthLever); - pullSouthLever = new ObjectStep(this, ObjectID.LEVER_50205, new WorldPoint(3894, 4553, 0), "Pull the lever to the south-east.", secondMastabaKey); + pullSouthLever = new ObjectStep(this, ObjectID.LEVER_50205, new WorldPoint(3894, 4553, 0), "Pull the lever to the south-east."); pullSouthLever.addDialogStep("Yes."); - var getToSouthLever = new ObjectStep(this, ObjectID.ODD_MARKINGS_50207, new WorldPoint(3891, 4554, 0), "Search the Odd markings to the south to get to the south lever. Search the markings again if you fail."); + getToSouthLever = new ObjectStep(this, ObjectID.ODD_MARKINGS_50207, new WorldPoint(3891, 4554, 0), "Search the Odd markings to the south to get to the south lever. Search the markings again if you fail."); var needToInsertKeyInSouthLever = new VarbitRequirement(11482, 0); var needToFlipSouthLever = new VarbitRequirement(11482, 1); var haveFlippedSouthLever = new VarbitRequirement(11482, 2); - var leaveSouthLever = new ObjectStep(this, ObjectID.ODD_MARKINGS_50208, new WorldPoint(3892, 4554, 0), "Search the Odd markings next to you to get out."); + leaveSouthLever = new ObjectStep(this, ObjectID.ODD_MARKINGS_50208, new WorldPoint(3892, 4554, 0), "Search the Odd markings next to you to get out."); pullSouthLever.addSubSteps(getToSouthLever, leaveSouthLever); var byNorthLever = new Zone(new WorldPoint(3894, 4597, 0), new WorldPoint(3893, 4599, 0)); var byNorthLeverReq = new ZoneRequirement(byNorthLever); - var getToNorthLever = new ObjectStep(this, ObjectID.ODD_MARKINGS_50208, new WorldPoint(3891, 4597, 0), "Search the Odd markings to the north to get to the north lever. Search the markings again if you fail."); - pullNorthLever = new ObjectStep(this, ObjectID.LEVER_50205, new WorldPoint(3894, 4598, 0), "Pull the lever to the north-east.", firstMastabaKey); + getToNorthLever = new ObjectStep(this, ObjectID.ODD_MARKINGS_50208, new WorldPoint(3891, 4597, 0), "Search the Odd markings to the north to get to the north lever. Search the markings again if you fail."); + pullNorthLever = new ObjectStep(this, ObjectID.LEVER_50205, new WorldPoint(3894, 4598, 0), "Pull the lever to the north-east."); pullNorthLever.addDialogStep("Yes."); var needToInsertKeyInNorthLever = new VarbitRequirement(11481, 0); var needToFlipNorthLever = new VarbitRequirement(11481, 1); var haveFlippedNorthLever = new VarbitRequirement(11481, 2); - var leaveNorthLever = new ObjectStep(this, ObjectID.ODD_MARKINGS_50207, new WorldPoint(3892, 4597, 0), "Search the Odd markings next to you to get out."); + leaveNorthLever = new ObjectStep(this, ObjectID.ODD_MARKINGS_50207, new WorldPoint(3892, 4597, 0), "Search the Odd markings next to you to get out."); pullNorthLever.addSubSteps(getToNorthLever, leaveNorthLever); - unlockImposingDoors = new ConditionalStep(this, enterTomb); + // var anyMastabaKey = new ItemRequirements(firstMastabaKey, secondMastabaKey); + var bothMastabaKeys = and(firstMastabaKey, secondMastabaKey); + firstMastabaKey = new ItemRequirement("Mastaba Key", ItemID.MASTABA_KEY); - // Get inside the tomb if you're not already inside. In case the user has teleported out or died to golem? - unlockImposingDoors.addStep(not(insideTomb), enterTomb); + // ADDING THIS CONDITION TO HIDE BREAKS ALL ITEMREQUIREMENT CHECKS HERE?? + // firstMastabaKey.setConditionToHide(not(needToInsertKeyInSouthLever)); + // secondMastabaKey.setConditionToHide(not(needToInsertKeyInNorthLever)); - // If the user has flipped the south lever & needs to get out of the little room + // This step is complicated because you can use either key for either lever (I think). This could use some testing where the user does not follow instructions and: + // 1. Gets south key, pulls south lever, then what? + // 2. Gets north key, pulls south lever, then what? + // 3. Gets south key, pulls north lever, then what? + // 4. Gets north key, pulls north lever, then what? + // 5. Gets both keys, pulls north lever, then what? + unlockImposingDoors = new ConditionalStep(this, enterTomb); + unlockImposingDoors.addStep(not(insideTomb), enterTomb); unlockImposingDoors.addStep(and(haveFlippedSouthLever, bySouthLeverReq), leaveSouthLever); - // If the user has flipped the north lever & needs to get out of the little room unlockImposingDoors.addStep(and(haveFlippedNorthLever, byNorthLeverReq), leaveNorthLever); - - // If the user has not already put the key in the south lever, and does not have the key - unlockImposingDoors.addStep(and(needToInsertKeyInSouthLever, not(secondMastabaKey)), getSecondKey); - // If the user has not already put the key in the north lever, and does not have the key - unlockImposingDoors.addStep(and(needToInsertKeyInNorthLever, not(firstMastabaKey)), getFirstKey); - - // If the user has the key & stands by the south lever unlockImposingDoors.addStep(and(needToFlipSouthLever, bySouthLeverReq), pullSouthLever); - // If the user needs to flip the south lever, but is not inside the little room, get to the little room - unlockImposingDoors.addStep(not(haveFlippedSouthLever), getToSouthLever); - - // If the user has the key & stands by the north lever unlockImposingDoors.addStep(and(needToFlipNorthLever, byNorthLeverReq), pullNorthLever); - // If the user needs to flip the north lever, but is not inside the little room, get to the little room - unlockImposingDoors.addStep(needToFlipNorthLever, getToNorthLever); - - // Once last lever was pulled, quest varbit changed from 6 to 8, then 8 to 10 at the same tick + unlockImposingDoors.addStep(and(not(bySouthLeverReq), bothMastabaKeys, needToInsertKeyInSouthLever), getToSouthLever); + unlockImposingDoors.addStep(and(not(bySouthLeverReq), needToFlipSouthLever), getToSouthLever); + unlockImposingDoors.addStep(and(bySouthLeverReq, bothMastabaKeys, needToInsertKeyInSouthLever), pullSouthLever); + unlockImposingDoors.addStep(and(not(byNorthLeverReq), haveFlippedSouthLever, anyMastabaKey, needToInsertKeyInNorthLever), getToNorthLever); + unlockImposingDoors.addStep(and(not(byNorthLeverReq), haveFlippedSouthLever, needToFlipNorthLever), getToNorthLever); + unlockImposingDoors.addStep(and(haveFlippedSouthLever, anyMastabaKey, needToInsertKeyInNorthLever, byNorthLeverReq), pullNorthLever); + unlockImposingDoors.addStep(and(or(needToInsertKeyInNorthLever, needToInsertKeyInSouthLever), not(firstMastabaKey)), getFirstKey); + unlockImposingDoors.addStep(and(or(needToInsertKeyInNorthLever, needToInsertKeyInSouthLever), not(secondMastabaKey)), getSecondKey); + + // Once the north lever is pulled, quest varbit changed from 6 to 8, then 8 to 10 at the same tick // This might have to do with which order you pulled the levers in var golemArenaZone = new Zone(new WorldPoint(3856, 4592, 0), new WorldPoint(3884, 4599, 0)); @@ -390,8 +418,8 @@ public void setupSteps() var oilAndBerryFilledCanopicJar = new ItemRequirement("Canopic jar (oil and berries)", ItemID.CANOPIC_JAR_OIL_AND_BERRIES); - var combineJarWithDwellberries = new ItemStep(this, "Put the Dwellberries in the Canopic jar.", oilFilledCanopicJar.highlighted(), dwellberries3.highlighted(), ringOfLife); - var combineJarWithRingOfLife = new ItemStep(this, "Put the Ring of life in the Canopic jar.", oilAndBerryFilledCanopicJar.highlighted(), ringOfLife.highlighted()); + combineJarWithDwellberries = new ItemStep(this, "Put the Dwellberries in the Canopic jar.", oilFilledCanopicJar.highlighted(), dwellberries3.highlighted(), ringOfLife); + combineJarWithRingOfLife = new ItemStep(this, "Put the Ring of life in the Canopic jar.", oilAndBerryFilledCanopicJar.highlighted(), ringOfLife.highlighted()); unsortedStep16 = new ConditionalStep(this, todo); unsortedStep16.addStep(oilAndBerryFilledCanopicJar, combineJarWithRingOfLife); @@ -402,7 +430,7 @@ public void setupSteps() unsortedStep16.addStep(and(insideTomb), enterGolemArenaWithoutFight); - var returnToElias = new NpcStep(this, NpcID.ELIAS_WHITE, new WorldPoint(3505, 3037, 0), "Return to Elias south of Ruins of Uzer, either by walking out of the tomb or using the fairy ring."); + returnToElias = new NpcStep(this, NpcID.ELIAS_WHITE, new WorldPoint(3505, 3037, 0), "Return to Elias south of Ruins of Uzer, either by walking out of the tomb or using the fairy ring.", canopicJarFull); returnToElias.addTeleport(fairyRingDLQ); var returnToEliasByWalking = new ObjectStep(this, ObjectID.STAIRS_55786, new WorldPoint(3894, 4714, 0), "Return to Elias south of Ruins of Uzer, either by walking out of the tomb or using the fairy ring."); returnToEliasByWalking.addTeleport(fairyRingDLQ); @@ -421,26 +449,32 @@ public void setupSteps() var trollheimTeleport = new TeleportItemRequirement("Trollheim Teleport", ItemID.TROLLHEIM_TELEPORT); trollheimTeleport.addAlternates(ItemCollections.GHOMMALS_HILT); - var headToTrollheim = new ObjectStep(this, ObjectID.CAVE_ENTRANCE_5007, new WorldPoint(2821, 3744, 0), "Enter the cave next to Trollheim. You can use a Trollheim teleport tablet or the GWD Ghommal's Hilt teleport to get close."); + headToTrollheim = new ObjectStep(this, ObjectID.CAVE_ENTRANCE_5007, new WorldPoint(2821, 3744, 0), "Enter the cave next to Trollheim. You can use a Trollheim teleport tablet or the GWD Ghommal's Hilt teleport to get close.", anyPickaxe); headToTrollheim.addTeleport(trollheimTeleport); var trollheimCave = new Zone(11167); var inTrollheimCave = new ZoneRequirement(trollheimCave); - var continueThroughTrollheimCave = new ObjectStep(this, ObjectID.CREVASSE, new WorldPoint(2772, 10233, 0), "Continue through the Trollheim cave, exiting at the Crevasse to the north-west. Use Protect from Melee to avoid taking damage from the Ice Trolls."); + continueThroughTrollheimCave = new ObjectStep(this, ObjectID.CREVASSE, new WorldPoint(2772, 10233, 0), "Continue through the Trollheim cave, exiting at the Crevasse to the north-west. Use Protect from Melee to avoid taking damage from the Ice Trolls.", anyPickaxe); var trollweissMountain = new Zone(11068); var onTrollweissMountain = new ZoneRequirement(trollweissMountain); - var enterTrollweissCave = new ObjectStep(this, ObjectID.CAVE_55779, new WorldPoint(2809, 3861, 0), "Enter the Trollweiss cave to the east."); + enterTrollweissCave = new ObjectStep(this, ObjectID.CAVE_55779, new WorldPoint(2809, 3861, 0), "Enter the Trollweiss cave to the east.", anyPickaxe); var trollweissCave1 = new Zone(11168); var inTrollweissCave = new ZoneRequirement(trollweissCave1); - rubbleMiner1 = new RubbleSolverOne(this).puzzleWrapStep("Mine the rubble and make your way through the cave."); - rubbleMiner2 = new RubbleSolverTwo(this).puzzleWrapStep("Mine the rubble and make your way through the cave."); - rubbleMiner3 = new RubbleSolverThree(this).puzzleWrapStep("Mine the rubble and make your way through the cave."); - rubbleMiner4 = new RubbleSolverFour(this).puzzleWrapStep("Mine the rubble and make your way through the cave."); + var rubbleMiner1Real = new RubbleSolverOne(this); + var rubbleMiner2Real = new RubbleSolverTwo(this); + var rubbleMiner3Real = new RubbleSolverThree(this); + var rubbleMiner4Real = new RubbleSolverFour(this); + + rubbleMiner1 = rubbleMiner1Real.puzzleWrapStep("Mine the rubble and make your way through the cave."); + rubbleMiner2 = rubbleMiner2Real.puzzleWrapStep("Mine the rubble and make your way through the cave."); + rubbleMiner3 = rubbleMiner3Real.puzzleWrapStep("Mine the rubble and make your way through the cave."); + rubbleMiner4 = rubbleMiner4Real.puzzleWrapStep("Mine the rubble and make your way through the cave."); + + rubbleMiner1Real.addSubSteps(rubbleMiner2, rubbleMiner3, rubbleMiner4); - rubbleMiner1.addSubSteps(rubbleMiner2, rubbleMiner3, rubbleMiner4); unsortedStep20 = new ConditionalStep(this, headToTrollheim); unsortedStep20.addStep(inTrollweissCave, rubbleMiner1); @@ -463,7 +497,7 @@ public void setupSteps() unsortedStep28.addStep(inTrollheimCave, continueThroughTrollheimCave); - var climbUpstairsAndTalkToArrav = new ObjectStep(this, ObjectID.STAIRS_50508, new WorldPoint(2811, 10267, 0), "Climb up the stairs in the room with the red tile floor and talk to Arrav."); + climbUpstairsAndTalkToArrav = new ObjectStep(this, ObjectID.STAIRS_50508, new WorldPoint(2811, 10267, 0), "Climb up the stairs in the room with the red tile floor and talk to Arrav."); unsortedStep30 = new ConditionalStep(this, headToTrollheim); unsortedStep30.addStep(inTrollweissCave, climbUpstairsAndTalkToArrav); unsortedStep30.addStep(onTrollweissMountain, enterTrollweissCave); @@ -471,7 +505,7 @@ public void setupSteps() var arravHouseFirstRoom = new Zone(new WorldPoint(2848, 3868, 0), new WorldPoint(2858, 3873, 0)); var inArravHouseFirstRoom = new ZoneRequirement(arravHouseFirstRoom); - var talkToArrav = new NpcStep(this, NpcID.ARRAV_14129, new WorldPoint(2856, 3871, 0), "Talk to Arrav."); + talkToArrav = new NpcStep(this, NpcID.ARRAV_14129, new WorldPoint(2856, 3871, 0), "Talk to Arrav."); unsortedStep32 = new ConditionalStep(this, headToTrollheim); unsortedStep32.addStep(inArravHouseFirstRoom, talkToArrav); @@ -482,8 +516,8 @@ public void setupSteps() var arravHouseSecondRoom = new Zone(new WorldPoint(2863, 3865, 0), new WorldPoint(2859, 3873, 0)); var inArravHouseSecondRoom = new ZoneRequirement(arravHouseSecondRoom); - var goToNextRoom = new ObjectStep(this, ObjectID.DOOR_50514, new WorldPoint(2859, 3870, 0), "Enter the room to your east and search the tapestry for ?."); - var searchTapestry = new ObjectStep(this, ObjectID.TAPESTRY_50516, new WorldPoint(2861, 3865, 0), "Search the tapestry in the south of the room."); + goToNextRoom = new ObjectStep(this, ObjectID.DOOR_50514, new WorldPoint(2859, 3870, 0), "Enter the room to your east and search the tapestry for something to help you with your heist."); + searchTapestry = new ObjectStep(this, ObjectID.TAPESTRY_50516, new WorldPoint(2861, 3865, 0), "Search the tapestry in the south of the room."); unsortedStep34 = new ConditionalStep(this, headToTrollheim); unsortedStep34.addStep(inArravHouseSecondRoom, searchTapestry); unsortedStep34.addStep(inArravHouseFirstRoom, goToNextRoom); @@ -514,6 +548,7 @@ public void setupSteps() // need 1 inventory slot free headToZemouregalsBaseAndTalkToElias = new NpcStep(this, NpcID.ELIAS_WHITE, new WorldPoint(3341, 3516, 0), "Head to Zemouregal's base east of Varrock's sawmill and talk to Elias.", anyGrappleableCrossbow, mithrilGrapple, arravCombatGear, insulatedBoots, canopicJarFullForHeist, baseKey); // todo add teleport headToZemouregalsBaseAndTalkToElias.addDialogStep("Ready when you are."); + headToZemouregalsBaseAndTalkToElias.addTeleport(lumberyardTeleport); unsortedStep42 = new ConditionalStep(this, headToZemouregalsBaseAndTalkToElias); enterZemouregalsBase = new ObjectStep(this, NullObjectID.NULL_50689, new WorldPoint(3343, 3515, 0), "Enter Zemouregal's base east of Varrock's sawmill.", anyGrappleableCrossbow, mithrilGrapple, arravCombatGear, insulatedBoots, canopicJarFullForHeist, baseKey); // todo add teleport @@ -563,13 +598,10 @@ public void setupSteps() // also used for 50 unsortedStep48 = new ConditionalStep(this, enterZemouregalsBase); - // TODO: DECODER STRIPS ARE NOT NECESSARY. REMOVE THEM :) - unsortedStep48.addStep(and(inSecondPart, not(inStorageRoom), decoderStrips, codeKey), metalDoorSolver); - unsortedStep48.addStep(and(inStorageRoom, not(decoderStrips)), searchTableForDecoderStrips); - unsortedStep48.addStep(and(inStorageRoom, not(codeKey)), openChestForCodeKey); - unsortedStep48.addStep(and(inStorageRoom, decoderStrips), exitStorageRoom); + unsortedStep48.addStep(and(inSecondPart, not(inStorageRoom), codeKey), metalDoorSolver); + unsortedStep48.addStep(and(inStorageRoom, codeKey), exitStorageRoom); + unsortedStep48.addStep(and(inStorageRoom), openChestForCodeKey); unsortedStep48.addStep(and(inSecondPart, not(codeKey)), enterStorageRoom); - unsortedStep48.addStep(and(inSecondPart, not(decoderStrips)), enterStorageRoom); unsortedStep48.addStep(inZemouregalsBaseSewer, exitZemouregalsBaseSewer); unsortedStep48.addStep(inZemouregalsBaseKitchen, enterZemouregalsBaseSewer); unsortedStep48.addStep(inZemouregalsBaseSection4, passZemouregalsBaseDoor4); @@ -579,12 +611,13 @@ public void setupSteps() openMetalDoors = new ObjectStep(this, ObjectID.METAL_DOORS, new WorldPoint(3612, 4582, 0), "Step through through the metal doors.", canopicJarFullForHeist, anyGrappleableCrossbow, mithrilGrapple); - var inGrapplePuzzleRoom = new ZoneRequirement(new Zone(new WorldPoint(3612, 4587, 0), new WorldPoint(3625, 4579, 0))); - grappleAcross = new ObjectStep(this, ObjectID.PIPE_50542, new WorldPoint(3615, 4582, 0), "Grapple across the pipe", anyGrappleableCrossbow.highlighted().equipped(), mithrilGrapple.highlighted().equipped()); + var inGrapplePuzzleRoom = new ZoneRequirement(new Zone(new WorldPoint(3613, 4587, 0), new WorldPoint(3625, 4579, 0))); + grappleAcross = new ObjectStep(this, ObjectID.PIPE_50542, new WorldPoint(3615, 4582, 0), "Grapple across the pipe", canopicJarFullForHeist, anyGrappleableCrossbow.highlighted().equipped(), mithrilGrapple.highlighted().equipped()); var pastGrapplePuzzleRoom = new ZoneRequirement(new Zone(new WorldPoint(3621, 4589, 0), new WorldPoint(3645, 4578, 0))); - enterBossRoom = new ObjectStep(this, ObjectID.PEDESTAL_50539, new WorldPoint(3638, 4582, 0), "Attempt to take Arrav's heart from the pedestal, ready for a fight. Kill zombies as they appear (ranged weapons are handy here). Avoid the venom pools they spawn. Use Protect from Melee to avoid some of the incoming damage. When Arrav throws an axe towards you, step to the side or behind him.", arravCombatGear); + enterBossRoom = new ObjectStep(this, ObjectID.PEDESTAL_50539, new WorldPoint(3638, 4582, 0), "Attempt to take Arrav's heart from the pedestal, ready for a fight with Arrav. Kill zombies as they appear (ranged weapons are handy here). Avoid the venom pools they spawn. Use Protect from Melee to avoid some of the incoming damage. When Arrav throws an axe towards you, step to the side or behind him.", canopicJarFullForHeist, arravCombatGear); + enterBossRoom.setOverlayText("Attempt to take Arrav's heart from the pedestal, ready for a fight with Arrav. Some hints are available in the sidebar."); unsortedStep52 = new ConditionalStep(this, enterZemouregalsBase); unsortedStep52.addStep(pastGrapplePuzzleRoom, enterBossRoom); @@ -599,7 +632,8 @@ public void setupSteps() // User has engaged Arrav - fightArrav = new NpcStep(this, NpcID.ARRAV_14132, new WorldPoint(3635, 4582, 0), "fight arrav xd"); + fightArrav = new NpcStep(this, NpcID.ARRAV_14132, new WorldPoint(3635, 4582, 0), "Fight Arrav. Kill zombies as they appear (ranged weapons are handy here). Avoid the venom pools they spawn. Use Protect from Melee to avoid some of the incoming damage. When Arrav throws an axe towards you, step to the side or behind him.", canopicJarFullForHeist); + fightArrav.setOverlayText("Fight Arrav. Some hints are available in the sidebar."); unsortedStep54 = new ConditionalStep(this, enterZemouregalsBase); unsortedStep54.addStep(or(pastGrapplePuzzleRoom, inGrapplePuzzleRoom), fightArrav); unsortedStep54.addStep(inSecondPart, openMetalDoors); @@ -649,7 +683,9 @@ public List getItemRecommended() prayerPotion, golemCombatGear, arravCombatGear, - food + food, + lumberyardTeleport, + fairyRingDLQ ); } @@ -729,8 +765,9 @@ public List getPanels() solveTilePuzzle, searchShelvesForUrn, inspectMurals, - unsortedStep16, - unsortedStep18 + combineJarWithDwellberries, + combineJarWithRingOfLife, + returnToElias ), List.of( dwellberries3, ringOfLife, @@ -744,15 +781,14 @@ public List getPanels() food ))); panels.add(new PanelDetails("Fort Invasion", List.of( - unsortedStep20, + headToTrollheim, + continueThroughTrollheimCave, + enterTrollweissCave, rubbleMiner1, - rubbleMiner2, - rubbleMiner3, - rubbleMiner4, - unsortedStep30, - unsortedStep32, - unsortedStep34 - // TODO + climbUpstairsAndTalkToArrav, + talkToArrav, + goToNextRoom, + searchTapestry ), List.of( // Requirements anyPickaxe @@ -787,7 +823,6 @@ public List getPanels() canopicJarFullForHeist ), List.of( // Recommended - new FreeInventorySlotRequirement(1), staminaPotion, prayerPotion, food diff --git a/src/main/java/com/questhelper/helpers/quests/thecurseofarrav/TilePuzzleSolver.java b/src/main/java/com/questhelper/helpers/quests/thecurseofarrav/TilePuzzleSolver.java index 07c0a56328..2d799b7364 100644 --- a/src/main/java/com/questhelper/helpers/quests/thecurseofarrav/TilePuzzleSolver.java +++ b/src/main/java/com/questhelper/helpers/quests/thecurseofarrav/TilePuzzleSolver.java @@ -363,7 +363,7 @@ protected void updateSteps() } } else { log.debug("player is outside of puzzle: {} / {} / {}/{}", playerWp, localPoint, xInPuzzle, yInPuzzle); - var userIsPastPuzzle = localPoint.getX() <= baseX; // TODO: If the user walks to the cave to the south, we might tell the user to just click the lever. I need to figure out the correct zone here. polish pass! + var userIsPastPuzzle = localPoint.getX() <= 3730 || (localPoint.getX() <= baseX && localPoint.getY() >= 4701); if (userIsPastPuzzle) { // highlight lever startUpStep(finishPuzzleStep); diff --git a/src/main/java/com/questhelper/helpers/quests/thecurseofarrav/rubblesolvers/RubbleSolver.java b/src/main/java/com/questhelper/helpers/quests/thecurseofarrav/rubblesolvers/RubbleSolver.java index 437e1ca4c7..69e845c12e 100644 --- a/src/main/java/com/questhelper/helpers/quests/thecurseofarrav/rubblesolvers/RubbleSolver.java +++ b/src/main/java/com/questhelper/helpers/quests/thecurseofarrav/rubblesolvers/RubbleSolver.java @@ -30,7 +30,7 @@ public abstract class RubbleSolver extends DetailedOwnerStep { private int stepCounter; public RubbleSolver(TheCurseOfArrav theCurseOfArrav, String number) { - super(theCurseOfArrav, "Make your way through the Trollweiss cave, mining rubble with your pickaxe. " + number); + super(theCurseOfArrav, "Make your way through the Trollweiss cave, mining rubble with your pickaxe from the direction indicated. Rubble can only be mined from the same direction once."); } protected void addMineRubbleStep(int x, int y, RubbleType rubbleType, Direction direction) { @@ -40,10 +40,11 @@ protected void addMineRubbleStep(int x, int y, RubbleType rubbleType, Direction var validIDSet = new HashSet<>(validObjectIDs); var wp = new WorldPoint(x, y, 0); - var stepCounter = this.stepCounter++; - var text = String.format("[%d] Mine the rubble from the %s side", stepCounter, direction.toString().toLowerCase()); + // Useful for debugging + // var stepCounter = this.stepCounter++; + var text = String.format("Mine the rubble from the %s side", direction.toString().toLowerCase()); var mainObjectID = validObjectIDs.get(0); - var step = new ObjectStep(getQuestHelper(), mainObjectID, wp, text); + var step = new ObjectStep(getQuestHelper(), mainObjectID, wp, text, ((TheCurseOfArrav) getQuestHelper()).anyPickaxe); var offsetX = x; var offsetY = y; switch (direction) { @@ -67,8 +68,8 @@ protected void addMineRubbleStep(int x, int y, RubbleType rubbleType, Direction step.addAlternateObjects(alternateIDs); } - var conditionText = String.format("[%d] Rubble mined from the %s side", stepCounter, direction.toString().toLowerCase()); - var inverseConditionText = String.format("[%d] Rubble needs to be mined from the %s side", stepCounter, direction.toString().toLowerCase()); + var conditionText = String.format("Rubble mined from the %s side", direction.toString().toLowerCase()); + var inverseConditionText = String.format("Rubble needs to be mined from the %s side", direction.toString().toLowerCase()); var conditionThatRubbleIsStillThere = new ObjectCondition(validIDSet, wp); var conditionThatRubbleHasBeenMined = new Conditions(true, LogicType.NAND, conditionThatRubbleIsStillThere); conditionThatRubbleIsStillThere.setText(inverseConditionText); @@ -102,56 +103,22 @@ protected void setupSteps() { this.setupRubbleSteps(); - // after reversing - // mineStep 0: mine C - // mineStep 1: mine B - // mineStep 2: mine A - - // condition 0: A is mined - // condition 1: B is mined - // condition 2: C is mined - - // i = 0: Mine C, if B and A are mined, and C is not mined - // i = 1: Mine B, if A is mined - // i = 2: Mine A, with no condition - conditionalStep = new ConditionalStep(getQuestHelper(), todo); - // Collections.reverse(this.mineSteps); - // Collections.reverse(this.inverseConditions); assert this.mineSteps.size() == this.conditions.size(); assert this.mineSteps.size() == this.inverseConditions.size(); - // { - // var allDone = new DetailedQuestStep(getQuestHelper(), "you are all done lol"); - // var conditionList = new ArrayList(); - // for (var condition : this.conditions) { - // allDone.addRequirement(condition); - // } - // var xd = new Conditions(LogicType.AND, conditionList); - // conditionalStep.addStep(xd, allDone); - // } for (var i = 0; i < mineSteps.size(); i++) { var mineStep = mineSteps.get(i); var inverseCondition = this.inverseConditions.get(i); - // var conditionList = new ArrayList(); - - mineStep.addRequirement(inverseCondition); - // conditionList.add(this.inverseConditions.get(i)); - - // StringBuilder text = new StringBuilder(); - // for (var j = 0; j < this.conditions.size() - i - 1; j++) { - // var condition = this.conditions.get(j); - // conditionList.add(condition); - // text.append(this.conditions.get(j).getDisplayText()); - // mineStep.addRequirement(condition); - // } + // Useful for debugging + // mineStep.addRequirement(inverseCondition); - var xd = new Conditions(LogicType.AND, inverseCondition); - xd.setText(inverseCondition.getDisplayText()); + // var xd = new Conditions(LogicType.AND, inverseCondition); + // xd.setText(inverseCondition.getDisplayText()); - conditionalStep.addStep(xd, mineStep); + conditionalStep.addStep(inverseCondition, mineStep); } } diff --git a/src/main/java/com/questhelper/helpers/quests/thecurseofarrav/rubblesolvers/RubbleSolverFour.java b/src/main/java/com/questhelper/helpers/quests/thecurseofarrav/rubblesolvers/RubbleSolverFour.java index 1f2f21f969..85002a855e 100644 --- a/src/main/java/com/questhelper/helpers/quests/thecurseofarrav/rubblesolvers/RubbleSolverFour.java +++ b/src/main/java/com/questhelper/helpers/quests/thecurseofarrav/rubblesolvers/RubbleSolverFour.java @@ -5,7 +5,7 @@ import net.runelite.api.coords.Direction; /** - * This class describes the rubble mining steps required for Roadblock 3 (when quest state varbit is 26) + * This class describes the rubble mining steps required for Roadblock 4 (when quest state varbit is 28) */ @Slf4j public class RubbleSolverFour extends RubbleSolver @@ -16,34 +16,34 @@ public RubbleSolverFour(TheCurseOfArrav theCurseOfArrav) { @Override protected void setupRubbleSteps() { - this.addMineRubbleStep(2787, 10267, RubbleType.Three, Direction.WEST); // 1 - this.addMineRubbleStep(2787, 10266, RubbleType.Three, Direction.WEST); // 2 - this.addMineRubbleStep(2787, 10267, RubbleType.Two, Direction.SOUTH); // 3 - this.addMineRubbleStep(2788, 10267, RubbleType.Two, Direction.NORTH); // 4 - this.addMineRubbleStep(2787, 10267, RubbleType.One, Direction.NORTH); // 5 - this.addMineRubbleStep(2788, 10267, RubbleType.One, Direction.WEST); // 6 + this.addMineRubbleStep(2787, 10267, RubbleType.Three, Direction.WEST); + this.addMineRubbleStep(2787, 10266, RubbleType.Three, Direction.WEST); + this.addMineRubbleStep(2787, 10267, RubbleType.Two, Direction.SOUTH); + this.addMineRubbleStep(2788, 10267, RubbleType.Two, Direction.NORTH); + this.addMineRubbleStep(2787, 10267, RubbleType.One, Direction.NORTH); + this.addMineRubbleStep(2788, 10267, RubbleType.One, Direction.WEST); // Last part from south - this.addMineRubbleStep(2803, 10264, RubbleType.Three, Direction.SOUTH); // 7 - this.addMineRubbleStep(2803, 10265, RubbleType.Two, Direction.SOUTH); // 8 + this.addMineRubbleStep(2803, 10264, RubbleType.Three, Direction.SOUTH); + this.addMineRubbleStep(2803, 10265, RubbleType.Two, Direction.SOUTH); // Last part from north - this.addMineRubbleStep(2803, 10267, RubbleType.One, Direction.NORTH); // 9 - this.addMineRubbleStep(2803, 10266, RubbleType.Three, Direction.NORTH); // 10 - this.addMineRubbleStep(2804, 10266, RubbleType.Two, Direction.NORTH); // 11 + this.addMineRubbleStep(2803, 10267, RubbleType.One, Direction.NORTH); + this.addMineRubbleStep(2803, 10266, RubbleType.Three, Direction.NORTH); + this.addMineRubbleStep(2804, 10266, RubbleType.Two, Direction.NORTH); // Last part from west - this.addMineRubbleStep(2802, 10266, RubbleType.Two, Direction.WEST); // 12 - this.addMineRubbleStep(2801, 10265, RubbleType.One, Direction.NORTH); // 13 - this.addMineRubbleStep(2802, 10265, RubbleType.One, Direction.WEST); // 14 - this.addMineRubbleStep(2803, 10265, RubbleType.One, Direction.WEST); // 15 - this.addMineRubbleStep(2802, 10266, RubbleType.One, Direction.SOUTH); // 16 - this.addMineRubbleStep(2803, 10266, RubbleType.Two, Direction.WEST); // 17 - this.addMineRubbleStep(2804, 10265, RubbleType.Two, Direction.WEST); // 18 - this.addMineRubbleStep(2803, 10266, RubbleType.One, Direction.SOUTH); // 19 - this.addMineRubbleStep(2804, 10266, RubbleType.One, Direction.WEST); // 20 - this.addMineRubbleStep(2804, 10265, RubbleType.One, Direction.NORTH); // 21 - this.addMineRubbleStep(2805, 10265, RubbleType.One, Direction.WEST); // 22 - this.addMineRubbleStep(2806, 10265, RubbleType.One, Direction.WEST); // 23 + this.addMineRubbleStep(2802, 10266, RubbleType.Two, Direction.WEST); + this.addMineRubbleStep(2801, 10265, RubbleType.One, Direction.NORTH); + this.addMineRubbleStep(2802, 10265, RubbleType.One, Direction.WEST); + this.addMineRubbleStep(2803, 10265, RubbleType.One, Direction.WEST); + this.addMineRubbleStep(2802, 10266, RubbleType.One, Direction.SOUTH); + this.addMineRubbleStep(2804, 10265, RubbleType.Two, Direction.WEST); + this.addMineRubbleStep(2803, 10266, RubbleType.Two, Direction.SOUTH); + this.addMineRubbleStep(2803, 10266, RubbleType.One, Direction.WEST); + this.addMineRubbleStep(2804, 10266, RubbleType.One, Direction.WEST); + this.addMineRubbleStep(2804, 10265, RubbleType.One, Direction.NORTH); + this.addMineRubbleStep(2805, 10265, RubbleType.One, Direction.WEST); + this.addMineRubbleStep(2806, 10265, RubbleType.One, Direction.WEST); } } diff --git a/src/main/java/com/questhelper/helpers/quests/thecurseofarrav/rubblesolvers/RubbleSolverThree.java b/src/main/java/com/questhelper/helpers/quests/thecurseofarrav/rubblesolvers/RubbleSolverThree.java index 43697f6927..9b1f823655 100644 --- a/src/main/java/com/questhelper/helpers/quests/thecurseofarrav/rubblesolvers/RubbleSolverThree.java +++ b/src/main/java/com/questhelper/helpers/quests/thecurseofarrav/rubblesolvers/RubbleSolverThree.java @@ -17,13 +17,13 @@ public RubbleSolverThree(TheCurseOfArrav theCurseOfArrav) { @Override protected void setupRubbleSteps() { // These 3 steps are technically part of the next solver, but it's better to get these done asap - this.addMineRubbleStep(2787, 10267, RubbleType.Three, Direction.WEST); // 1 - this.addMineRubbleStep(2787, 10266, RubbleType.Three, Direction.WEST); // 2 - this.addMineRubbleStep(2787, 10267, RubbleType.Two, Direction.SOUTH); // 3 + this.addMineRubbleStep(2787, 10267, RubbleType.Three, Direction.WEST); + this.addMineRubbleStep(2787, 10266, RubbleType.Three, Direction.WEST); + this.addMineRubbleStep(2787, 10267, RubbleType.Two, Direction.SOUTH); - this.addMineRubbleStep(2789, 10286, RubbleType.One, Direction.WEST); // 4 - this.addMineRubbleStep(2789, 10285, RubbleType.Three, Direction.NORTH); // 5 - this.addMineRubbleStep(2789, 10285, RubbleType.Two, Direction.WEST); // 6 + this.addMineRubbleStep(2789, 10286, RubbleType.One, Direction.WEST); + this.addMineRubbleStep(2789, 10285, RubbleType.Three, Direction.NORTH); + this.addMineRubbleStep(2789, 10285, RubbleType.Two, Direction.WEST); this.addMineRubbleStep(2789, 10283, RubbleType.Two, Direction.WEST); this.addMineRubbleStep(2789, 10284, RubbleType.Three, Direction.WEST); @@ -41,6 +41,6 @@ protected void setupRubbleSteps() { this.addMineRubbleStep(2792, 10285, RubbleType.One, Direction.WEST); this.addMineRubbleStep(2793, 10285, RubbleType.One, Direction.WEST); - this.addMineRubbleStep(2787, 10267, RubbleType.One, Direction.NORTH); // 26 (or when??) + this.addMineRubbleStep(2787, 10267, RubbleType.One, Direction.NORTH); } } diff --git a/src/main/java/com/questhelper/helpers/quests/thecurseofarrav/rubblesolvers/RubbleSolverTwo.java b/src/main/java/com/questhelper/helpers/quests/thecurseofarrav/rubblesolvers/RubbleSolverTwo.java index c65e280dde..177ab888aa 100644 --- a/src/main/java/com/questhelper/helpers/quests/thecurseofarrav/rubblesolvers/RubbleSolverTwo.java +++ b/src/main/java/com/questhelper/helpers/quests/thecurseofarrav/rubblesolvers/RubbleSolverTwo.java @@ -16,23 +16,23 @@ public RubbleSolverTwo(TheCurseOfArrav theCurseOfArrav) { @Override protected void setupRubbleSteps() { - this.addMineRubbleStep(2766, 10279, RubbleType.Three, Direction.WEST); // 5 - this.addMineRubbleStep(2766, 10280, RubbleType.One, Direction.WEST); // 6 - this.addMineRubbleStep(2767, 10281, RubbleType.Two, Direction.WEST); // 7 - this.addMineRubbleStep(2766, 10279, RubbleType.Two, Direction.NORTH); // 8 - this.addMineRubbleStep(2766, 10278, RubbleType.Two, Direction.WEST); // 9 - this.addMineRubbleStep(2766, 10278, RubbleType.One, Direction.SOUTH); // 10 - this.addMineRubbleStep(2766, 10279, RubbleType.One, Direction.SOUTH); // 11 - this.addMineRubbleStep(2767, 10278, RubbleType.One, Direction.WEST); // 12 - this.addMineRubbleStep(2767, 10279, RubbleType.Two, Direction.WEST); // 13 - this.addMineRubbleStep(2768, 10279, RubbleType.One, Direction.WEST); // 14 - this.addMineRubbleStep(2767, 10279, RubbleType.One, Direction.SOUTH); // 15 - this.addMineRubbleStep(2768, 10280, RubbleType.Three, Direction.SOUTH); // 16: THIS TRIGGERS A STONE FALL OR SOMETHING :) - this.addMineRubbleStep(2768, 10281, RubbleType.One, Direction.SOUTH); // 17 - this.addMineRubbleStep(2769, 10281, RubbleType.Two, Direction.WEST); // 18 - this.addMineRubbleStep(2767, 10281, RubbleType.One, Direction.EAST); // 19 - this.addMineRubbleStep(2767, 10282, RubbleType.One, Direction.SOUTH); // 20 - this.addMineRubbleStep(2769, 10281, RubbleType.One, Direction.NORTH); // 21 - this.addMineRubbleStep(2770, 10281, RubbleType.One, Direction.WEST); // 22 + this.addMineRubbleStep(2766, 10279, RubbleType.Three, Direction.WEST); + this.addMineRubbleStep(2766, 10280, RubbleType.One, Direction.WEST); + this.addMineRubbleStep(2767, 10281, RubbleType.Two, Direction.WEST); + this.addMineRubbleStep(2766, 10279, RubbleType.Two, Direction.NORTH); + this.addMineRubbleStep(2766, 10278, RubbleType.Two, Direction.WEST); + this.addMineRubbleStep(2766, 10278, RubbleType.One, Direction.SOUTH); + this.addMineRubbleStep(2766, 10279, RubbleType.One, Direction.SOUTH); + this.addMineRubbleStep(2767, 10278, RubbleType.One, Direction.WEST); + this.addMineRubbleStep(2767, 10279, RubbleType.Two, Direction.SOUTH); + this.addMineRubbleStep(2767, 10279, RubbleType.One, Direction.WEST); + this.addMineRubbleStep(2768, 10279, RubbleType.One, Direction.WEST); + this.addMineRubbleStep(2768, 10280, RubbleType.Three, Direction.SOUTH); + this.addMineRubbleStep(2768, 10281, RubbleType.One, Direction.SOUTH); + this.addMineRubbleStep(2769, 10281, RubbleType.Two, Direction.WEST); + this.addMineRubbleStep(2767, 10281, RubbleType.One, Direction.EAST); + this.addMineRubbleStep(2767, 10282, RubbleType.One, Direction.SOUTH); + this.addMineRubbleStep(2769, 10281, RubbleType.One, Direction.NORTH); + this.addMineRubbleStep(2770, 10281, RubbleType.One, Direction.WEST); } } diff --git a/src/main/java/com/questhelper/helpers/quests/thecurseofarrav/rubblesolvers/RubbleType.java b/src/main/java/com/questhelper/helpers/quests/thecurseofarrav/rubblesolvers/RubbleType.java index 4350f47e76..6715b77c19 100644 --- a/src/main/java/com/questhelper/helpers/quests/thecurseofarrav/rubblesolvers/RubbleType.java +++ b/src/main/java/com/questhelper/helpers/quests/thecurseofarrav/rubblesolvers/RubbleType.java @@ -10,7 +10,7 @@ public enum RubbleType { Three(ObjectID.RUBBLE_50603, ObjectID.RUBBLE_50604), Two(ObjectID.RUBBLE_50598, ObjectID.RUBBLE_50602, ObjectID.RUBBLE_50601, ObjectID.RUBBLE_50599), - One(ObjectID.RUBBLE_50587, ObjectID.RUBBLE_50589, ObjectID.RUBBLE_50590, ObjectID.RUBBLE_50594, ObjectID.RUBBLE_50597, ObjectID.RUBBLE_50588); + One(ObjectID.RUBBLE_50587, ObjectID.RUBBLE_50589, ObjectID.RUBBLE_50590, ObjectID.RUBBLE_50594, ObjectID.RUBBLE_50597, ObjectID.RUBBLE_50588, ObjectID.RUBBLE_50593); private final List objectIDs; diff --git a/src/main/java/com/questhelper/panel/QuestStepPanel.java b/src/main/java/com/questhelper/panel/QuestStepPanel.java index 211db88e75..b1952b7a39 100644 --- a/src/main/java/com/questhelper/panel/QuestStepPanel.java +++ b/src/main/java/com/questhelper/panel/QuestStepPanel.java @@ -223,7 +223,7 @@ public void updateHighlightCheck(Client client, QuestStep newStep, QuestHelper c for (QuestStep step : getSteps()) { if (step.getConditionToHide() != null && step.getConditionToHide().check(client)) continue; - if (step == newStep || step.getSubsteps().contains(newStep)) + if (step == newStep || step.hasOtherStepAsSubstep(newStep)) { highlighted = true; updateHighlight(step); diff --git a/src/main/java/com/questhelper/questhelpers/BasicQuestHelper.java b/src/main/java/com/questhelper/questhelpers/BasicQuestHelper.java index 879ff9c4ba..59cbaa6c64 100644 --- a/src/main/java/com/questhelper/questhelpers/BasicQuestHelper.java +++ b/src/main/java/com/questhelper/questhelpers/BasicQuestHelper.java @@ -33,14 +33,21 @@ import java.util.Map; import com.questhelper.panel.PanelDetails; import com.questhelper.steps.QuestStep; +import lombok.Getter; public abstract class BasicQuestHelper extends QuestHelper { protected Map steps; protected int var; - @Override - public void init() + public Map getStepList() { + return this.steps; + } + + /** + * Attempt to load steps from the quest if steps have not yet been loaded + */ + private void tryLoadSteps() { if (steps == null) { @@ -48,9 +55,17 @@ public void init() } } + @Override + public void init() + { + this.tryLoadSteps(); + } + @Override public void startUp(QuestHelperConfig config) { + // this.tryLoadSteps(); + // TODO: THIS SHOULD USE tryLoadSteps steps = loadSteps(); this.config = config; instantiateSteps(steps.values()); diff --git a/src/main/java/com/questhelper/steps/DetailedQuestStep.java b/src/main/java/com/questhelper/steps/DetailedQuestStep.java index b2b65b977f..cdbd8b1002 100644 --- a/src/main/java/com/questhelper/steps/DetailedQuestStep.java +++ b/src/main/java/com/questhelper/steps/DetailedQuestStep.java @@ -672,6 +672,9 @@ protected void addItemTiles(Collection requirements) { return; } + var scene = client.getScene(); + var tilesxd = scene.getTiles(); + var plane = client.getPlane(); Tile[][] squareOfTiles = client.getScene().getTiles()[client.getPlane()]; // Reduce the two-dimensional array into a single list for processing. @@ -910,12 +913,16 @@ public void setShortestPath() { if (worldPoint != null) { - WorldPoint playerWp = client.getLocalPlayer().getWorldLocation(); - if (getQuestHelper().getConfig().useShortestPath() && playerWp != null) { - Map data = new HashMap<>(); - data.put("start", playerWp); - data.put("target", worldPoint); - eventBus.post(new PluginMessage("shortestpath", "path", data)); + if (getQuestHelper().getConfig().useShortestPath()) + { + WorldPoint playerWp = client.getLocalPlayer().getWorldLocation(); + if (getQuestHelper().getConfig().useShortestPath() && playerWp != null) + { + Map data = new HashMap<>(); + data.put("start", playerWp); + data.put("target", worldPoint); + eventBus.post(new PluginMessage("shortestpath", "path", data)); + } } } } diff --git a/src/main/java/com/questhelper/steps/ObjectStep.java b/src/main/java/com/questhelper/steps/ObjectStep.java index 58424a60b2..3eaa37ddec 100644 --- a/src/main/java/com/questhelper/steps/ObjectStep.java +++ b/src/main/java/com/questhelper/steps/ObjectStep.java @@ -499,4 +499,9 @@ private Zone objZone(GameObject obj) bottomLeftCorner.getPlane()) ); } + + @Override + public String toString() { + return super.toString() + "(" + this.getText() + ")"; + } } diff --git a/src/main/java/com/questhelper/steps/QuestStep.java b/src/main/java/com/questhelper/steps/QuestStep.java index 74ae932c5f..bc12e42aaa 100644 --- a/src/main/java/com/questhelper/steps/QuestStep.java +++ b/src/main/java/com/questhelper/steps/QuestStep.java @@ -45,13 +45,15 @@ import com.questhelper.steps.choice.WidgetChoiceStep; import com.questhelper.steps.choice.WidgetChoiceSteps; import com.questhelper.steps.overlay.IconOverlay; -import java.awt.Graphics2D; +import java.awt.*; import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; import java.util.regex.Pattern; import lombok.Getter; @@ -594,4 +596,8 @@ public PuzzleWrapperStep puzzleWrapStep(String alternateText) { return new PuzzleWrapperStep(getQuestHelper(), this, alternateText); } + + public boolean hasOtherStepAsSubstep(QuestStep otherStep) { + return this.getSubsteps().contains(otherStep); + } } diff --git a/src/main/java/com/questhelper/steps/WidgetStep.java b/src/main/java/com/questhelper/steps/WidgetStep.java index 83398da076..d78a103893 100644 --- a/src/main/java/com/questhelper/steps/WidgetStep.java +++ b/src/main/java/com/questhelper/steps/WidgetStep.java @@ -30,6 +30,9 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; import lombok.Setter; import net.runelite.api.widgets.Widget; import com.questhelper.QuestHelperPlugin; @@ -40,6 +43,8 @@ public class WidgetStep extends DetailedQuestStep @Setter protected List widgetDetails = new ArrayList<>(); + protected List> extraWidgetOverlayHintFunctions = new ArrayList<>(); + public WidgetStep(QuestHelper questHelper, String text, int groupID, int childID) { super(questHelper, text); @@ -52,6 +57,10 @@ public WidgetStep(QuestHelper questHelper, String text, WidgetDetails... widgetD this.widgetDetails.addAll(Arrays.asList(widgetDetails)); } + public void addExtraWidgetOverlayHintFunction(BiConsumer function) { + this.extraWidgetOverlayHintFunctions.add(function); + } + @Override public void makeWidgetOverlayHint(Graphics2D graphics, QuestHelperPlugin plugin) { @@ -79,5 +88,9 @@ public void makeWidgetOverlayHint(Graphics2D graphics, QuestHelperPlugin plugin) graphics.setColor(questHelper.getConfig().targetOverlayColor()); graphics.draw(widget.getBounds()); } + + for (var extraWidgetOverlayHintFunction : extraWidgetOverlayHintFunctions) { + extraWidgetOverlayHintFunction.accept(graphics, plugin); + } } } diff --git a/src/test/java/com/questhelper/MockedTest.java b/src/test/java/com/questhelper/MockedTest.java index 048f8461de..139eded7e3 100644 --- a/src/test/java/com/questhelper/MockedTest.java +++ b/src/test/java/com/questhelper/MockedTest.java @@ -32,18 +32,26 @@ import com.questhelper.statemanagement.AchievementDiaryStepManager; import com.questhelper.statemanagement.PlayerStateManager; import net.runelite.api.Client; +import net.runelite.api.SpriteID; import net.runelite.client.callback.ClientThread; import net.runelite.client.callback.Hooks; import net.runelite.client.chat.ChatMessageManager; import net.runelite.client.config.ConfigManager; +import net.runelite.client.config.RuneLiteConfig; import net.runelite.client.eventbus.EventBus; import net.runelite.client.game.ItemManager; +import net.runelite.client.game.SpriteManager; import net.runelite.client.ui.ClientToolbar; import net.runelite.client.ui.overlay.OverlayManager; import org.junit.jupiter.api.BeforeEach; import org.mockito.Mockito; import javax.inject.Named; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.util.Random; import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ThreadLocalRandom; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; /** @@ -69,9 +77,15 @@ public abstract class MockedTest extends MockedTestBase @Bind protected QuestHelperConfig questHelperConfig = Mockito.mock(QuestHelperConfig.class); + @Bind + protected RuneLiteConfig runeLiteConfig = Mockito.mock(RuneLiteConfig.class); + @Bind protected QuestOverlayManager questOverlayManager = Mockito.mock(QuestOverlayManager.class); + @Bind + protected SpriteManager spriteManager = Mockito.mock(SpriteManager.class); + @Bind protected RuneliteObjectManager runeliteObjectManager = Mockito.mock(RuneliteObjectManager.class); @@ -99,6 +113,18 @@ public abstract class MockedTest extends MockedTestBase @Bind @Named("developerMode") private boolean developerMode; + + public BufferedImage random(final int width, final int height) { + Random rand = ThreadLocalRandom.current(); + BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + for (int x = 0; x < width; x++) { + for (int y = 0; y < height; y++) { + image.setRGB(x, y, new Color(rand.nextFloat(), rand.nextFloat(), rand.nextFloat()).getRGB()); + } + } + return image; + } + @Override @BeforeEach @@ -110,6 +136,7 @@ protected void setUp() when(playerStateManager.getAccountType()).thenReturn(AccountType.NORMAL); when(client.getIntStack()).thenReturn(new int[] { 1, 1, 1, 1 }); when(questHelperConfig.solvePuzzles()).thenReturn(true); + when(spriteManager.getSprite(SpriteID.TAB_QUESTS, 0)).thenReturn(new BufferedImage(10, 10, BufferedImage.TYPE_INT_ARGB)); AchievementDiaryStepManager.setup(configManager); } diff --git a/src/test/java/com/questhelper/helpers/quests/thecurseofarrav/KeysAndLeversTest.java b/src/test/java/com/questhelper/helpers/quests/thecurseofarrav/KeysAndLeversTest.java new file mode 100644 index 0000000000..c5585d195e --- /dev/null +++ b/src/test/java/com/questhelper/helpers/quests/thecurseofarrav/KeysAndLeversTest.java @@ -0,0 +1,313 @@ +package com.questhelper.helpers.quests.thecurseofarrav; + +import com.questhelper.MockedTest; +import com.questhelper.domain.AccountType; +import com.questhelper.questinfo.QuestHelperQuest; +import com.questhelper.statemanagement.AchievementDiaryStepManager; +import com.questhelper.steps.ConditionalStep; +import com.questhelper.steps.tools.QuestPerspective; +import net.runelite.api.InventoryID; +import net.runelite.api.Item; +import net.runelite.api.ItemContainer; +import net.runelite.api.ItemID; +import net.runelite.api.Node; +import net.runelite.api.Player; +import net.runelite.api.Scene; +import net.runelite.api.Skill; +import net.runelite.api.SpriteID; +import net.runelite.api.Tile; +import net.runelite.api.coords.LocalPoint; +import net.runelite.api.coords.WorldPoint; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.awt.image.BufferedImage; +import java.util.ArrayList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +public class KeysAndLeversTest extends MockedTest +{ + private MockedStatic questPerspectiveMockedStatic; + private MockedStatic worldPointMockedStatic; + private TheCurseOfArrav helper; + + @BeforeEach + protected void lol() + { + when(playerStateManager.getAccountType()).thenReturn(AccountType.NORMAL); + + var mockedPlayer = Mockito.mock(Player.class); + // when(mockedPlayer.getLocalLocation()).thenReturn(new LocalPoint(1, 1, 1)); + when(client.getLocalPlayer()).thenReturn(mockedPlayer); + + questPerspectiveMockedStatic = Mockito.mockStatic(QuestPerspective.class); + + worldPointMockedStatic = Mockito.mockStatic(WorldPoint.class); + + questPerspectiveMockedStatic.when(() -> QuestPerspective.getInstanceLocalPointFromReal(any(), any())) + .thenReturn(null); + + helper = new TheCurseOfArrav(); + + } + + @AfterEach + protected void lel() + { + questPerspectiveMockedStatic.close(); + worldPointMockedStatic.close(); + } + + private ConditionalStep init(WorldPoint playerLocation) + { + return this.init(playerLocation, null); + } + + private ConditionalStep init(WorldPoint playerLocation, Item[] mockedItems) + { + worldPointMockedStatic.when(() -> WorldPoint.fromLocalInstance(any(), any())) + .thenReturn(playerLocation); + + var mockedItemContainer = Mockito.mock(ItemContainer.class); + if (mockedItems != null) { + when(mockedItemContainer.getItems()).thenReturn(mockedItems); + when(client.getItemContainer(InventoryID.INVENTORY)).thenReturn(mockedItemContainer); + } + + when(client.getPlane()).thenReturn(0); + + var mockedScene = Mockito.mock(Scene.class); + when(mockedScene.getTiles()).thenReturn(new Tile[][][]{ + {} + }); + when(client.getScene()).thenReturn(mockedScene); + + this.injector.injectMembers(helper); + helper.setInjector(injector); + helper.setQuest(QuestHelperQuest.THE_CURSE_OF_ARRAV); + helper.setQuestHelperPlugin(questHelperPlugin); + helper.setConfig(questHelperConfig); + helper.init(); + + helper.startUp(questHelperConfig); + var conditionalStep = helper.unlockImposingDoors; + conditionalStep.startUp(); + return conditionalStep; + } + + @Test + void ensureOutsideTomb() + { + var conditionalStep = this.init(new WorldPoint(3305, 3037, 0)); + + assertEquals(this.helper.enterTomb, conditionalStep.getActiveStep()); + } + + @Test + void getFirstKey() + { + var conditionalStep = this.init(new WorldPoint(3845, 4547, 0)); + + assertEquals(this.helper.getFirstKey, conditionalStep.getActiveStep()); + } + + @Test + void getSecondKey() + { + var mockedItems = new Item[]{new Item(ItemID.MASTABA_KEY, 1)}; + var conditionalStep = this.init(new WorldPoint(3845, 4547, 0), mockedItems); + + assertEquals(this.helper.getSecondKey, conditionalStep.getActiveStep()); + } + + @Test + void getToSouthLever() + { + var mockedItems = new Item[]{ + new Item(ItemID.MASTABA_KEY, 1), + new Item(ItemID.MASTABA_KEY_30309, 1), + }; + var conditionalStep = this.init(new WorldPoint(3845, 4547, 0), mockedItems); + + assertEquals(this.helper.getToSouthLever, conditionalStep.getActiveStep()); + } + + @Test + void insertKeyIntoSouthLever() + { + var mockedItems = new Item[]{ + new Item(ItemID.MASTABA_KEY, 1), + new Item(ItemID.MASTABA_KEY_30309, 1), + }; + when(client.getVarbitValue(11482)).thenReturn(0); + var conditionalStep = this.init(new WorldPoint(3893, 4552, 0), mockedItems); + + assertEquals(this.helper.pullSouthLever, conditionalStep.getActiveStep()); + } + + @Test + void getToSouthLeverAfterInsertingKey1() + { + var mockedItems = new Item[]{ + new Item(ItemID.MASTABA_KEY, 1), + }; + when(client.getVarbitValue(11482)).thenReturn(1); + var conditionalStep = this.init(new WorldPoint(3845, 4547, 0), mockedItems); + + assertEquals(this.helper.getToSouthLever, conditionalStep.getActiveStep()); + } + + @Test + void getToSouthLeverAfterInsertingKey2() + { + var mockedItems = new Item[]{ + new Item(ItemID.MASTABA_KEY_30309, 1), + }; + when(client.getVarbitValue(11482)).thenReturn(1); + var conditionalStep = this.init(new WorldPoint(3845, 4547, 0), mockedItems); + + assertEquals(this.helper.getToSouthLever, conditionalStep.getActiveStep()); + } + + @Test + void pullSouthLeverAfterInsertingKey1() + { + var mockedItems = new Item[]{ + new Item(ItemID.MASTABA_KEY, 1), + }; + when(client.getVarbitValue(11482)).thenReturn(1); + var conditionalStep = this.init(new WorldPoint(3893, 4552, 0), mockedItems); + + assertEquals(this.helper.pullSouthLever, conditionalStep.getActiveStep()); + } + + @Test + void pullSouthLeverAfterInsertingKey2() + { + var mockedItems = new Item[]{ + new Item(ItemID.MASTABA_KEY_30309, 1), + }; + when(client.getVarbitValue(11482)).thenReturn(1); + var conditionalStep = this.init(new WorldPoint(3893, 4552, 0), mockedItems); + + assertEquals(this.helper.pullSouthLever, conditionalStep.getActiveStep()); + } + + @Test + void leaveSouthLeverAfterInsertingKey1() + { + var mockedItems = new Item[]{ + new Item(ItemID.MASTABA_KEY, 1), + }; + when(client.getVarbitValue(11482)).thenReturn(2); + var conditionalStep = this.init(new WorldPoint(3893, 4552, 0), mockedItems); + + assertEquals(this.helper.leaveSouthLever, conditionalStep.getActiveStep()); + } + + @Test + void leaveSouthLeverAfterInsertingKey2() + { + var mockedItems = new Item[]{ + new Item(ItemID.MASTABA_KEY_30309, 1), + }; + when(client.getVarbitValue(11482)).thenReturn(2); + var conditionalStep = this.init(new WorldPoint(3893, 4552, 0), mockedItems); + + assertEquals(this.helper.leaveSouthLever, conditionalStep.getActiveStep()); + } + + @Test + void goToNorthLeverAfterPullingSouthLeverKey1() + { + var mockedItems = new Item[]{ + new Item(ItemID.MASTABA_KEY, 1), + }; + when(client.getVarbitValue(11482)).thenReturn(2); + var conditionalStep = this.init(new WorldPoint(3845, 4547, 0), mockedItems); + + assertEquals(this.helper.getToNorthLever, conditionalStep.getActiveStep()); + } + + @Test + void goToNorthLeverAfterPullingSouthLeverKey2() + { + var mockedItems = new Item[]{ + new Item(ItemID.MASTABA_KEY_30309, 1), + }; + when(client.getVarbitValue(11482)).thenReturn(2); + var conditionalStep = this.init(new WorldPoint(3845, 4547, 0), mockedItems); + + assertEquals(this.helper.getToNorthLever, conditionalStep.getActiveStep()); + } + + @Test + void insertKeyIntoNorthLeverAfterPullingSouthLeverKey1() + { + var mockedItems = new Item[]{ + new Item(ItemID.MASTABA_KEY, 1), + }; + when(client.getVarbitValue(11482)).thenReturn(2); + var conditionalStep = this.init(new WorldPoint(3894, 4597, 0), mockedItems); + + assertEquals(this.helper.pullNorthLever, conditionalStep.getActiveStep()); + } + + @Test + void insertKeyIntoNorthLeverAfterPullingSouthLeverKey2() + { + var mockedItems = new Item[]{ + new Item(ItemID.MASTABA_KEY_30309, 1), + }; + when(client.getVarbitValue(11482)).thenReturn(2); + var conditionalStep = this.init(new WorldPoint(3894, 4597, 0), mockedItems); + + assertEquals(this.helper.pullNorthLever, conditionalStep.getActiveStep()); + } + + @Test + void getToSouthLeverAfterInsertingKey() + { + var mockedItems = new Item[]{ + }; + when(client.getVarbitValue(11482)).thenReturn(2); + when(client.getVarbitValue(11481)).thenReturn(1); + var conditionalStep = this.init(new WorldPoint(3845, 4547, 0), mockedItems); + + assertEquals(this.helper.getToNorthLever, conditionalStep.getActiveStep()); + } + + @Test + void pullNorthLeverAfterPullingSouthLeverKey1() + { + var mockedItems = new Item[]{ + }; + when(client.getVarbitValue(11482)).thenReturn(2); + when(client.getVarbitValue(11481)).thenReturn(1); + var conditionalStep = this.init(new WorldPoint(3894, 4597, 0), mockedItems); + + assertEquals(this.helper.pullNorthLever, conditionalStep.getActiveStep()); + } + + @Test + void pullNorthLeverAfterPullingSouthLeverKey2() + { + var mockedItems = new Item[]{ + }; + when(client.getVarbitValue(11482)).thenReturn(2); + when(client.getVarbitValue(11481)).thenReturn(1); + var conditionalStep = this.init(new WorldPoint(3894, 4597, 0), mockedItems); + + assertEquals(this.helper.pullNorthLever, conditionalStep.getActiveStep()); + } +} diff --git a/src/test/java/com/questhelper/helpers/quests/thecurseofarrav/MetalDoorSolverTest.java b/src/test/java/com/questhelper/helpers/quests/thecurseofarrav/MetalDoorSolverTest.java index e3f2cd1b4d..f361a4a3bb 100644 --- a/src/test/java/com/questhelper/helpers/quests/thecurseofarrav/MetalDoorSolverTest.java +++ b/src/test/java/com/questhelper/helpers/quests/thecurseofarrav/MetalDoorSolverTest.java @@ -26,6 +26,9 @@ public void testValidCodes() // from Zoinkwiz assertArrayEquals(new int[]{6, 3, 6, 4}, MetalDoorSolver.calculate("AEGH")); + + // from pajdank + assertArrayEquals(new int[]{1, 3, 4, 2}, MetalDoorSolver.calculate("BIAF")); } @Test @@ -70,4 +73,78 @@ public void testCodeIsNull() { assertNull(MetalDoorSolver.calculate(null)); } + + @Test + public void testDistanceUp() + { + assertEquals(0, MetalDoorSolver.calculateDistanceUp(0, 0)); + assertEquals(0, MetalDoorSolver.calculateDistanceUp(1, 1)); + assertEquals(0, MetalDoorSolver.calculateDistanceUp(2, 2)); + assertEquals(0, MetalDoorSolver.calculateDistanceUp(3, 3)); + assertEquals(0, MetalDoorSolver.calculateDistanceUp(4, 4)); + assertEquals(0, MetalDoorSolver.calculateDistanceUp(5, 5)); + assertEquals(0, MetalDoorSolver.calculateDistanceUp(6, 6)); + assertEquals(0, MetalDoorSolver.calculateDistanceUp(7, 7)); + assertEquals(0, MetalDoorSolver.calculateDistanceUp(8, 8)); + assertEquals(0, MetalDoorSolver.calculateDistanceUp(9, 9)); + + assertEquals(1, MetalDoorSolver.calculateDistanceUp(0, 1)); + assertEquals(1, MetalDoorSolver.calculateDistanceUp(1, 2)); + assertEquals(1, MetalDoorSolver.calculateDistanceUp(2, 3)); + assertEquals(1, MetalDoorSolver.calculateDistanceUp(3, 4)); + assertEquals(1, MetalDoorSolver.calculateDistanceUp(4, 5)); + assertEquals(1, MetalDoorSolver.calculateDistanceUp(5, 6)); + assertEquals(1, MetalDoorSolver.calculateDistanceUp(6, 7)); + assertEquals(1, MetalDoorSolver.calculateDistanceUp(7, 8)); + assertEquals(1, MetalDoorSolver.calculateDistanceUp(8, 9)); + assertEquals(1, MetalDoorSolver.calculateDistanceUp(9, 0)); + + assertEquals(9, MetalDoorSolver.calculateDistanceUp(0, 9)); + assertEquals(9, MetalDoorSolver.calculateDistanceUp(1, 0)); + assertEquals(9, MetalDoorSolver.calculateDistanceUp(2, 1)); + assertEquals(9, MetalDoorSolver.calculateDistanceUp(3, 2)); + assertEquals(9, MetalDoorSolver.calculateDistanceUp(4, 3)); + assertEquals(9, MetalDoorSolver.calculateDistanceUp(5, 4)); + assertEquals(9, MetalDoorSolver.calculateDistanceUp(6, 5)); + assertEquals(9, MetalDoorSolver.calculateDistanceUp(7, 6)); + assertEquals(9, MetalDoorSolver.calculateDistanceUp(8, 7)); + assertEquals(9, MetalDoorSolver.calculateDistanceUp(9, 8)); + } + + @Test + public void testDistanceDown() + { + assertEquals(0, MetalDoorSolver.calculateDistanceDown(0, 0)); + assertEquals(0, MetalDoorSolver.calculateDistanceDown(1, 1)); + assertEquals(0, MetalDoorSolver.calculateDistanceDown(2, 2)); + assertEquals(0, MetalDoorSolver.calculateDistanceDown(3, 3)); + assertEquals(0, MetalDoorSolver.calculateDistanceDown(4, 4)); + assertEquals(0, MetalDoorSolver.calculateDistanceDown(5, 5)); + assertEquals(0, MetalDoorSolver.calculateDistanceDown(6, 6)); + assertEquals(0, MetalDoorSolver.calculateDistanceDown(7, 7)); + assertEquals(0, MetalDoorSolver.calculateDistanceDown(8, 8)); + assertEquals(0, MetalDoorSolver.calculateDistanceDown(9, 9)); + + assertEquals(1, MetalDoorSolver.calculateDistanceDown(0, 9)); + assertEquals(1, MetalDoorSolver.calculateDistanceDown(1, 0)); + assertEquals(1, MetalDoorSolver.calculateDistanceDown(2, 1)); + assertEquals(1, MetalDoorSolver.calculateDistanceDown(3, 2)); + assertEquals(1, MetalDoorSolver.calculateDistanceDown(4, 3)); + assertEquals(1, MetalDoorSolver.calculateDistanceDown(5, 4)); + assertEquals(1, MetalDoorSolver.calculateDistanceDown(6, 5)); + assertEquals(1, MetalDoorSolver.calculateDistanceDown(7, 6)); + assertEquals(1, MetalDoorSolver.calculateDistanceDown(8, 7)); + assertEquals(1, MetalDoorSolver.calculateDistanceDown(9, 8)); + + assertEquals(9, MetalDoorSolver.calculateDistanceDown(0, 1)); + assertEquals(9, MetalDoorSolver.calculateDistanceDown(1, 2)); + assertEquals(9, MetalDoorSolver.calculateDistanceDown(2, 3)); + assertEquals(9, MetalDoorSolver.calculateDistanceDown(3, 4)); + assertEquals(9, MetalDoorSolver.calculateDistanceDown(4, 5)); + assertEquals(9, MetalDoorSolver.calculateDistanceDown(5, 6)); + assertEquals(9, MetalDoorSolver.calculateDistanceDown(6, 7)); + assertEquals(9, MetalDoorSolver.calculateDistanceDown(7, 8)); + assertEquals(9, MetalDoorSolver.calculateDistanceDown(8, 9)); + assertEquals(9, MetalDoorSolver.calculateDistanceDown(9, 0)); + } } diff --git a/src/test/java/com/questhelper/questhelpers/QuestHelperTest.java b/src/test/java/com/questhelper/questhelpers/QuestHelperTest.java index 7cbc628dd0..8414b98e6c 100644 --- a/src/test/java/com/questhelper/questhelpers/QuestHelperTest.java +++ b/src/test/java/com/questhelper/questhelpers/QuestHelperTest.java @@ -2,15 +2,19 @@ import com.questhelper.MockedTest; import com.questhelper.domain.AccountType; +import com.questhelper.panel.PanelDetails; import com.questhelper.questinfo.QuestHelperQuest; import com.questhelper.requirements.Requirement; import com.questhelper.requirements.item.ItemRequirement; import com.questhelper.requirements.zone.ZoneRequirement; import com.questhelper.statemanagement.AchievementDiaryStepManager; import java.lang.reflect.Field; +import java.util.stream.Collectors; import static org.junit.jupiter.api.Assertions.assertFalse; +import com.questhelper.steps.ConditionalStep; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; @@ -169,4 +173,58 @@ void ensureAllVariablesCorrectlySet() } } } + + @Test + void ensureAllStepsHaveSidebarLink() + { + when(questHelperConfig.solvePuzzles()).thenReturn(true); + + AchievementDiaryStepManager.setup(configManager); + + for (var quest : QuestHelperQuest.values()) + { + var helper = quest.getQuestHelper(); + helper.setQuest(quest); + if (quest.getPlayerQuests() != null) + { + continue; + } + + this.injector.injectMembers(helper); + helper.setQuestHelperPlugin(questHelperPlugin); + helper.setConfig(questHelperConfig); + helper.init(); + + if (quest != QuestHelperQuest.THE_CURSE_OF_ARRAV) { + continue; + } + + if (helper instanceof BasicQuestHelper) { + var basicHelper = (BasicQuestHelper) helper; + var panels = helper.getPanels(); + var panelSteps = panels.stream().flatMap(panelDetails -> panelDetails.getSteps().stream()).collect(Collectors.toList()); + var steps = basicHelper.getStepList().values(); + for (var step : steps) { + assertNotNull(step); + var rawText = step.getText(); + var text = rawText == null ? "" : String.join("\n", step.getText()); + if (step instanceof ConditionalStep) { + // + } else { + var isInPanelSteps = panelSteps.contains(step); + /* TODO + var isSubstepOf = steps.stream().filter(questStep -> { + if (questStep instanceof BasicQuest) { + return questStep.getSubSteps(); + } + return null; + }); + */ + var isInAnyStepSubStepsThatIsInPanelSteps = true; // todo + assertTrue(isInPanelSteps); + } + } + } + } + } }