From 8b703dc7489f6a3f9aa03d50528471023dd4cef9 Mon Sep 17 00:00:00 2001 From: Senmori Date: Thu, 4 Feb 2021 20:48:16 -0500 Subject: [PATCH 01/55] Initial commit. Signed-off-by: Senmori --- .../java/com/questhelper/spells/Rune.java | 42 +++++++++ .../questhelper/spells/RuneCollections.java | 88 +++++++++++++++++++ .../java/com/questhelper/spells/RuneType.java | 32 +++++++ 3 files changed, 162 insertions(+) create mode 100644 src/main/java/com/questhelper/spells/Rune.java create mode 100644 src/main/java/com/questhelper/spells/RuneCollections.java create mode 100644 src/main/java/com/questhelper/spells/RuneType.java diff --git a/src/main/java/com/questhelper/spells/Rune.java b/src/main/java/com/questhelper/spells/Rune.java new file mode 100644 index 0000000000..3009f729b3 --- /dev/null +++ b/src/main/java/com/questhelper/spells/Rune.java @@ -0,0 +1,42 @@ +/* + * + * * Copyright (c) 2021, Senmori + * * 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 com.questhelper.spells; + +import lombok.Getter; + +@Getter +public final class Rune +{ + private final RuneType type; + private final int quantity; + + public Rune(RuneType type, int quantity) + { + this.type = type; + this.quantity = quantity; + } +} diff --git a/src/main/java/com/questhelper/spells/RuneCollections.java b/src/main/java/com/questhelper/spells/RuneCollections.java new file mode 100644 index 0000000000..94dc2e982a --- /dev/null +++ b/src/main/java/com/questhelper/spells/RuneCollections.java @@ -0,0 +1,88 @@ +/* + * + * * Copyright (c) 2021, Senmori + * * 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 com.questhelper.spells; + +import com.questhelper.questhelpers.QuestUtil; +import java.util.List; +import lombok.Getter; +import lombok.experimental.UtilityClass; + +import static net.runelite.api.ItemID.*; + +@UtilityClass +@Getter +public class RuneCollections +{ + private int item(int itemID, String name) + { + return itemID; + } + + + private List airRune = QuestUtil.toLinkedList( + item(AIR_RUNE, "Air Rune"), + item(MIST_RUNE, "Mist Rune"), + item(DUST_RUNE, "Dust Rune"), + item(SMOKE_RUNE, "Smoke Rune"), + item(STAFF_OF_AIR, "Staff of Air"), + item(AIR_BATTLESTAFF, "Air Battlestaff"), + item(MYSTIC_AIR_STAFF, "Mystic Air Staff"), + item(SMOKE_BATTLESTAFF, "Smoke Battlestaff"), + item(MYSTIC_SMOKE_STAFF, "Mystic Smoke Staff"), + item(MIST_BATTLESTAFF, "Mist Battlestaff"), + item(MYSTIC_MIST_STAFF, "Mystic Mist Staff"), + item(DUST_BATTLESTAFF, "Dust Battlestaff"), + item(MYSTIC_DUST_STAFF, "Mystic Dust Staff") + ); + + private List waterRune = QuestUtil.toLinkedList( + item(WATER_RUNE, "Water Rune"), + item(MIST_RUNE, "Mist Rune"), + item(MUD_RUNE, "Mud Rune"), + item(STEAM_RUNE, "Steam Rune"), + item(STAFF_OF_WATER, "Staff of Water"), + item(WATER_BATTLESTAFF, "Water Battlestaff"), + item(MYSTIC_WATER_STAFF, "Mystic Water Staff"), + item(MUD_BATTLESTAFF, "Mud Battlestaff"), + item(MYSTIC_MUD_STAFF, "Mystic Mud Staff"), + item(STEAM_BATTLESTAFF, "Steam Battlestaff"), + item(MYSTIC_STEAM_STAFF, "Mystic Steam Staff"), + item(MIST_BATTLESTAFF, "Mist Battlestaff"), + item(MYSTIC_MIST_STAFF, "Mystic Mist Staff") + ); + + private List earthRune = QuestUtil.toLinkedList(); + + private List fireRune = QuestUtil.toLinkedList(); + + private List mistRune = QuestUtil.toLinkedList(); + private List dustRune = QuestUtil.toLinkedList(); + private List mudRune = QuestUtil.toLinkedList(); + private List smokeRune = QuestUtil.toLinkedList(); + private List steamRune = QuestUtil.toLinkedList(); + private List lavaRune = QuestUtil.toLinkedList(); +} diff --git a/src/main/java/com/questhelper/spells/RuneType.java b/src/main/java/com/questhelper/spells/RuneType.java new file mode 100644 index 0000000000..7174c828cb --- /dev/null +++ b/src/main/java/com/questhelper/spells/RuneType.java @@ -0,0 +1,32 @@ +/* + * + * * Copyright (c) 2021, Senmori + * * 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 com.questhelper.spells; + +public enum RuneType +{ + +} From 86df78e4606ebdf9dcf4788ba90b1172bd6a1be2 Mon Sep 17 00:00:00 2001 From: Senmori Date: Fri, 5 Feb 2021 18:09:46 -0500 Subject: [PATCH 02/55] Initial work on adding spell requirements. Signed-off-by: Senmori --- .../java/com/questhelper/ItemCollections.java | 43 +++++- .../com/questhelper/QuestHelperQuest.java | 2 +- .../requirements/ItemRequirements.java | 7 + .../java/com/questhelper/spells/Rune.java | 83 +++++++++- .../questhelper/spells/RuneCollections.java | 88 ----------- .../java/com/questhelper/spells/RuneType.java | 32 ---- .../java/com/questhelper/spells/Spell.java | 144 ++++++++++++++++++ .../spells/StandardSpellBuilder.java | 88 +++++++++++ 8 files changed, 356 insertions(+), 131 deletions(-) delete mode 100644 src/main/java/com/questhelper/spells/RuneCollections.java delete mode 100644 src/main/java/com/questhelper/spells/RuneType.java create mode 100644 src/main/java/com/questhelper/spells/Spell.java create mode 100644 src/main/java/com/questhelper/spells/StandardSpellBuilder.java diff --git a/src/main/java/com/questhelper/ItemCollections.java b/src/main/java/com/questhelper/ItemCollections.java index dfce068099..3a425a00a2 100644 --- a/src/main/java/com/questhelper/ItemCollections.java +++ b/src/main/java/com/questhelper/ItemCollections.java @@ -24,6 +24,7 @@ */ package com.questhelper; +import com.questhelper.questhelpers.QuestUtil; import java.util.Arrays; import java.util.List; import lombok.Getter; @@ -180,9 +181,9 @@ public class ItemCollections @Getter private static final List waterStaff = Arrays.asList( - ItemID.FIRE_BATTLESTAFF, - ItemID.MYSTIC_FIRE_STAFF, - ItemID.STAFF_OF_FIRE, + ItemID.WATER_BATTLESTAFF, + ItemID.MYSTIC_WATER_STAFF, + ItemID.STAFF_OF_WATER, ItemID.MUD_BATTLESTAFF, ItemID.MYSTIC_MUD_STAFF, ItemID.MIST_BATTLESTAFF, @@ -212,6 +213,42 @@ public class ItemCollections ItemID.MYSTIC_LAVA_STAFF ); + @Getter + private static final List lavaStaff = QuestUtil.toLinkedList( + ItemID.LAVA_BATTLESTAFF, + ItemID.MYSTIC_LAVA_STAFF + ); + + @Getter + private static final List mudStaff = QuestUtil.toLinkedList( + ItemID.MUD_BATTLESTAFF, + ItemID.MYSTIC_MUD_STAFF + ); + + @Getter + private static final List steamStaff = QuestUtil.toLinkedList( + ItemID.STEAM_BATTLESTAFF, + ItemID.MYSTIC_STEAM_STAFF + ); + + @Getter + private static final List smokeStaff = QuestUtil.toLinkedList( + ItemID.SMOKE_BATTLESTAFF, + ItemID.MYSTIC_SMOKE_STAFF + ); + + @Getter + private static final List mistStaff = QuestUtil.toLinkedList( + ItemID.MIST_BATTLESTAFF, + ItemID.MYSTIC_MIST_STAFF + ); + + @Getter + private static final List dustStaff = QuestUtil.toLinkedList( + ItemID.DUST_BATTLESTAFF, + ItemID.MYSTIC_DUST_STAFF + ); + // Potions @Getter diff --git a/src/main/java/com/questhelper/QuestHelperQuest.java b/src/main/java/com/questhelper/QuestHelperQuest.java index a926141003..1bbe143006 100644 --- a/src/main/java/com/questhelper/QuestHelperQuest.java +++ b/src/main/java/com/questhelper/QuestHelperQuest.java @@ -195,7 +195,7 @@ public enum QuestHelperQuest WHAT_LIES_BELOW(447, "What Lies Below", QuestVarbits.QUEST_WHAT_LIES_BELOW, Quest.Type.P2P, Quest.Difficulty.INTERMEDIATE), WITCHS_HOUSE(448, "Witch's House", QuestVarPlayer.QUEST_WITCHS_HOUSE, Quest.Type.P2P, Quest.Difficulty.INTERMEDIATE), ZOGRE_FLESH_EATERS(449, "Zogre Flesh Eaters", QuestVarbits.QUEST_ZOGRE_FLESH_EATERS, Quest.Type.P2P, Quest.Difficulty.INTERMEDIATE), - THE_ASCENT_OF_ARCEUUS(542, "The Ascent of Arceuus", "Ascent of Arceuus", QuestVarbits.QUEST_THE_ASCENT_OF_ARCEUUS, Quest.Type.F2P, Quest.Difficulty.NOVICE), + THE_ASCENT_OF_ARCEUUS(542, "The Ascent of Arceuus", "Ascent of Arceuus", QuestVarbits.QUEST_THE_ASCENT_OF_ARCEUUS, Quest.Type.P2P, Quest.Difficulty.NOVICE), THE_FORSAKEN_TOWER(543, "The Forsaken Tower", "Forsaken Tower", QuestVarbits.QUEST_THE_FORSAKEN_TOWER, Quest.Type.P2P, Quest.Difficulty.NOVICE), SONG_OF_THE_ELVES(603, "Song of the Elves", QuestVarbits.QUEST_SONG_OF_THE_ELVES, Quest.Type.P2P, Quest.Difficulty.GRANDMASTER), THE_FREMENNIK_EXILES(718, "The Fremennik Exiles", "Fremennik Exiles", QuestVarbits.QUEST_THE_FREMENNIK_EXILES, Quest.Type.P2P, Quest.Difficulty.MASTER), diff --git a/src/main/java/com/questhelper/requirements/ItemRequirements.java b/src/main/java/com/questhelper/requirements/ItemRequirements.java index d1072d47f4..865effd1db 100644 --- a/src/main/java/com/questhelper/requirements/ItemRequirements.java +++ b/src/main/java/com/questhelper/requirements/ItemRequirements.java @@ -59,6 +59,13 @@ public ItemRequirements(LogicType logicType, String name, ItemRequirement... ite this.logicType = logicType; } + public ItemRequirements(LogicType logicType, String name, List itemRequirements) + { + super(name, itemRequirements.get(0).getId(), -1); + this.itemRequirements.addAll(itemRequirements); + this.logicType = logicType; + } + @Override public boolean check(Client client) { diff --git a/src/main/java/com/questhelper/spells/Rune.java b/src/main/java/com/questhelper/spells/Rune.java index 3009f729b3..5f2c96147f 100644 --- a/src/main/java/com/questhelper/spells/Rune.java +++ b/src/main/java/com/questhelper/spells/Rune.java @@ -24,19 +24,88 @@ * * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ + package com.questhelper.spells; +import com.questhelper.ItemCollections; +import com.questhelper.requirements.ItemRequirement; +import com.questhelper.requirements.ItemRequirements; +import com.questhelper.requirements.util.LogicType; +import java.util.ArrayList; +import java.util.List; import lombok.Getter; +import net.runelite.api.ItemID; -@Getter -public final class Rune +public enum Rune { - private final RuneType type; - private final int quantity; + AIR("Air Rune", ItemCollections.getAirRune(), ItemCollections.getAirStaff()), + WATER("Water Rune", ItemCollections.getWaterRune(), ItemCollections.getWaterStaff()), + EARTH("Earth Rune", ItemCollections.getEarthRune(), ItemCollections.getEarthStaff()), + FIRE("Fire Rune", ItemCollections.getFireRune(), ItemCollections.getFireStaff()), + MIND("Mind Rune", ItemID.MIND_RUNE), + BODY("Body Rune", ItemID.BODY_RUNE), + COSMIC("Cosmic Rune", ItemID.COSMIC_RUNE), + CHAOS("Chaos Rune", ItemID.CHAOS_RUNE), + NATURE("Nature Rune", ItemID.NATURE_RUNE), + LAW("Law Rune", ItemID.LAW_RUNE), + DEATH("Death Rune", ItemID.DEATH_RUNE), + ASTRAL("Astral Rune", ItemID.ASTRAL_RUNE), + BLOOD("Blood Rune", ItemID.BLOOD_RUNE), + SOUL("Soul Rune", ItemID.SOUL_RUNE), + WRATH("Wrath Rune", ItemID.WRATH_RUNE), + LAVA("Lava Rune", ItemID.LAVA_RUNE, ItemCollections.getLavaStaff()), + MUD("Mud Rune", ItemID.DUST_RUNE, ItemCollections.getDustStaff()), + STEAM("Steam Rune", ItemID.STEAM_RUNE, ItemCollections.getSteamStaff()), + SMOKE("Smoke Rune", ItemID.SMOKE_RUNE, ItemCollections.getSmokeStaff()), + MIST("Mist Rune", ItemID.MIST_RUNE, ItemCollections.getMistStaff()), + DUST("Dust Rune", ItemID.DUST_RUNE, ItemCollections.getDustStaff()), + ; + + @Getter + private final String runeName; + private final List runes; + private final List staves; + Rune(String runeName, List runes, List staves) + { + this.runeName = runeName; + this.runes = runes; + this.staves = staves; + } - public Rune(RuneType type, int quantity) + Rune(String runeName, int itemID) { - this.type = type; - this.quantity = quantity; + this.runeName = runeName; + this.runes = new ArrayList<>(itemID); + this.staves = null; + } + + Rune(String runeName, int itemID, List staves) + { + this.runeName = runeName; + this.runes = new ArrayList<>(itemID); + this.staves = staves; + } + + public ItemRequirements getRunes(int quantity) + { + if (runes != null && staves != null) + { + return new ItemRequirements( + LogicType.OR, + getRuneName(), + new ItemRequirement("Runes", runes, quantity), + new ItemRequirement("Staff", staves, 1, true) + ); + } + + if (runes != null) + { + return new ItemRequirements( + LogicType.OR, + getRuneName(), + new ItemRequirement("Runes", runes, quantity) + ); + } + return null; } } diff --git a/src/main/java/com/questhelper/spells/RuneCollections.java b/src/main/java/com/questhelper/spells/RuneCollections.java deleted file mode 100644 index 94dc2e982a..0000000000 --- a/src/main/java/com/questhelper/spells/RuneCollections.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * - * * Copyright (c) 2021, Senmori - * * 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 com.questhelper.spells; - -import com.questhelper.questhelpers.QuestUtil; -import java.util.List; -import lombok.Getter; -import lombok.experimental.UtilityClass; - -import static net.runelite.api.ItemID.*; - -@UtilityClass -@Getter -public class RuneCollections -{ - private int item(int itemID, String name) - { - return itemID; - } - - - private List airRune = QuestUtil.toLinkedList( - item(AIR_RUNE, "Air Rune"), - item(MIST_RUNE, "Mist Rune"), - item(DUST_RUNE, "Dust Rune"), - item(SMOKE_RUNE, "Smoke Rune"), - item(STAFF_OF_AIR, "Staff of Air"), - item(AIR_BATTLESTAFF, "Air Battlestaff"), - item(MYSTIC_AIR_STAFF, "Mystic Air Staff"), - item(SMOKE_BATTLESTAFF, "Smoke Battlestaff"), - item(MYSTIC_SMOKE_STAFF, "Mystic Smoke Staff"), - item(MIST_BATTLESTAFF, "Mist Battlestaff"), - item(MYSTIC_MIST_STAFF, "Mystic Mist Staff"), - item(DUST_BATTLESTAFF, "Dust Battlestaff"), - item(MYSTIC_DUST_STAFF, "Mystic Dust Staff") - ); - - private List waterRune = QuestUtil.toLinkedList( - item(WATER_RUNE, "Water Rune"), - item(MIST_RUNE, "Mist Rune"), - item(MUD_RUNE, "Mud Rune"), - item(STEAM_RUNE, "Steam Rune"), - item(STAFF_OF_WATER, "Staff of Water"), - item(WATER_BATTLESTAFF, "Water Battlestaff"), - item(MYSTIC_WATER_STAFF, "Mystic Water Staff"), - item(MUD_BATTLESTAFF, "Mud Battlestaff"), - item(MYSTIC_MUD_STAFF, "Mystic Mud Staff"), - item(STEAM_BATTLESTAFF, "Steam Battlestaff"), - item(MYSTIC_STEAM_STAFF, "Mystic Steam Staff"), - item(MIST_BATTLESTAFF, "Mist Battlestaff"), - item(MYSTIC_MIST_STAFF, "Mystic Mist Staff") - ); - - private List earthRune = QuestUtil.toLinkedList(); - - private List fireRune = QuestUtil.toLinkedList(); - - private List mistRune = QuestUtil.toLinkedList(); - private List dustRune = QuestUtil.toLinkedList(); - private List mudRune = QuestUtil.toLinkedList(); - private List smokeRune = QuestUtil.toLinkedList(); - private List steamRune = QuestUtil.toLinkedList(); - private List lavaRune = QuestUtil.toLinkedList(); -} diff --git a/src/main/java/com/questhelper/spells/RuneType.java b/src/main/java/com/questhelper/spells/RuneType.java deleted file mode 100644 index 7174c828cb..0000000000 --- a/src/main/java/com/questhelper/spells/RuneType.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * - * * Copyright (c) 2021, Senmori - * * 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 com.questhelper.spells; - -public enum RuneType -{ - -} diff --git a/src/main/java/com/questhelper/spells/Spell.java b/src/main/java/com/questhelper/spells/Spell.java new file mode 100644 index 0000000000..793200dad1 --- /dev/null +++ b/src/main/java/com/questhelper/spells/Spell.java @@ -0,0 +1,144 @@ +/* + * + * * Copyright (c) 2021, Senmori + * * 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 com.questhelper.spells; + +import com.questhelper.requirements.Requirement; +import java.util.function.UnaryOperator; +import lombok.Getter; + +import static com.questhelper.spells.Rune.*; +import net.runelite.api.ItemID; + +@Getter +public enum Spell +{ + LUMBRIDGE_HOME_TELEPORT(5, 0), + WIND_STRIKE(6, 1, b -> b.rune(AIR).rune(MIND)), + CONFUSE(7, 3, b -> b.rune(BODY).rune(2, EARTH).rune(3, WATER)), + ENCHANT_OPAL_BOLT(8, 4, b -> b.rune(COSMIC).rune(2, AIR)), + WATER_STRIKE(9, 5, b -> b.rune(AIR).rune(WATER).rune(MIND)), + ENCHANT_LVL_1(10, 7, b -> b.rune(WATER).rune(COSMIC)), + ENCHANT_SAPPHIRE_BOLT(8, 9, b -> b.rune(WATER).rune(COSMIC)), + EARTH_STRIKE(11, 9, b -> b.rune(AIR).rune(MIND).rune(2, EARTH)), + WEAKEN(12, 11, b -> b.rune(BODY).rune(2, EARTH).rune(3, WATER)), + FIRE_STRIKE(13, 13, b -> b.rune(MIND).rune(2, AIR).rune(3, FIRE)), + ENCHANT_JADE_BOLT(8, 14, b -> b.rune(COSMIC).rune(2, EARTH)), + BONES_TO_BANANAS(14, 15, b -> b.rune(NATURE).rune(2, EARTH).rune(2, WATER)), + WIND_BOLT(15, 17, b -> b.rune(CHAOS).rune(2, AIR)), + CURSE(16, 19, b -> b.rune(BODY).rune(2, WATER).rune(3, EARTH)), + BIND(17, 20, b -> b.rune(2, NATURE).rune(3, WATER).rune(3, EARTH)), + LOW_LVL_ALCHEMY(18, 21, b -> b.rune(NATURE).rune(3, FIRE)), + WATER_BOLT(19, 23, b -> b.rune(CHAOS).rune(2, WATER).rune(2, AIR)), + ENCHANT_PEARL_BOLT(8, 24, b -> b.rune(COSMIC).rune(2, WATER)), + VARROCK_TELEPORT(20, 25, b -> b.rune(LAW).rune(FIRE).rune(3, AIR)), + ENCHANT_LVL_2(21, 27, b -> b.rune(COSMIC).rune(3, AIR)), + ENCHANT_EMERALD_BOLT(8, 27, b -> b.rune(NATURE).rune(COSMIC).rune(3, AIR)), + EARTH_BOLT(22, 9, b -> b.rune(CHAOS).rune(EARTH).rune(3, AIR)), + ENCHANT_RED_TOPAZ_BOLT(8, 29, b -> b.rune(CHAOS).rune(2, FIRE)), + LUMBRIDGE_TELEPORT(23, 31, b -> b.rune(LAW).rune(EARTH).rune(3, AIR)), + TELEKINETIC_GRAB(24, 33, b -> b.rune(LAW).rune(AIR)), + FIRE_BOLT(25, 35, b -> b.rune(CHAOS).rune(3, AIR).rune(4, FIRE)), + FALADOR_TELEPORT(26, 37, b -> b.rune(LAW).rune(WATER).rune(3, AIR)), + CRUMBLE_UNDEAD(27, 39, b -> b.rune(CHAOS).rune(2, EARTH).rune(2, AIR)), + TELEPORT_TO_HOUSE(28, 40, b -> b.rune(LAW).rune(EARTH).rune(AIR)), + WIND_BLAST(29, 41, b -> b.rune(DEATH).rune(3, AIR)), + SUPERHEAT_ITEM(30, 43, b -> b.rune(NATURE).rune(4, FIRE)), + CAMELOT_TELEPORT(31, 45, b -> b.rune(LAW).rune(5, AIR)), + WATER_BLAST(32, 47, b -> b.rune(DEATH).rune(3, AIR).rune(3, WATER)), + ENCHANT_LVL_3(33, 49, b -> b.rune(COSMIC).rune(5, FIRE)), + ENCHANT_RUBY_BOLT(8, 49, b -> b.rune(COSMIC).rune(BLOOD).rune(5, FIRE)), + IBAN_BLAST(34, 50, b -> b.rune(DEATH).rune(5, FIRE).item(true, ItemID.IBANS_STAFF, ItemID.IBANS_STAFF_U)), + SNARE(35, 50, b -> b.rune(3, NATURE).rune(4, WATER).rune(4, EARTH)), + MAGIC_DART(36, 50, b -> b.rune(DEATH).rune(4, MIND)), + ARDOUGNE_TELEPORT(37, 51, b -> b.rune(2, LAW).rune(2, WATER)), //TODO: QUEST REQ -> PLAGUE CITY + EARTH_BLAST(38, 43, b -> b.rune(DEATH).rune(3, AIR).rune(4, EARTH)), + HIGH_LVL_ALCHEMY(39, 55, b -> b.rune(NATURE).rune(5, FIRE)), + CHARGE_WATER_ORB(40, 56, b -> b.rune(3, COSMIC).rune(30, WATER).item(ItemID.UNPOWERED_ORB)), + ENCHANT_LVL_4(41, 57, b -> b.rune(COSMIC).rune(10, EARTH)), + ENCHANT_DIAMOND_BOLT(8, 57, b -> b.rune(COSMIC).rune(2, LAW).rune(10, EARTH)), + WATCHTOWER_TELEPORT(42, 58, b -> b.rune(2, LAW).rune(2, EARTH)), + FIRE_BLAST(43, 59, b -> b.rune(DEATH).rune(4, AIR).rune(5, FIRE)), + CHARGE_EARTH_ORB(44, 60, b -> b.rune(3, COSMIC).rune(30, EARTH).item(ItemID.UNPOWERED_ORB)), + BONES_TO_PEACHES(45, 60, b -> b.rune(2, NATURE).rune(2, EARTH).rune(4, WATER)), //TODO: MAGE TRAINING ARENA + SARADOMIN_STRIKE(46, 60, b -> b.rune(2, BLOOD).rune(2, FIRE).rune(4, AIR)), // TODO: MAGE ARENA + CLAWS_OF_GUTHIX(47, 60, b -> b.rune(FIRE).rune(2, BLOOD).rune(4, AIR)), // TODO: MAGE ARENA + FLAMES_OF_ZAMORAK(48, 60, b -> b.rune(AIR).rune(2, BLOOD).rune(4, FIRE)), // TODO: MAGE ARENA + TROLLHEIM_TELEPORT(49, 61, b -> b.rune(2, LAW).rune(2, FIRE)), // TODO: QUEST REQ -> EADGARS RUSE QUEST + WIND_WAVE(50, 62, b -> b.rune(BLOOD).rune(5, AIR)), + CHARGE_FIRE_ORB(51, 63, b -> b.rune(3, COSMIC).rune(30, FIRE).item(ItemID.UNPOWERED_ORB)), + APE_ATOLL_TELEPORT(52, 64, b -> b.rune(2, LAW).rune(2, WATER).rune(2, FIRE).item(ItemID.BANANA)), + WATER_WAVE(53, 54, b -> b.rune(BLOOD).rune(5, AIR).rune(7, WATER)), + CHARGE_AIR_ORB(54, 66, b -> b.rune(3, COSMIC).rune(30, AIR).item(ItemID.UNPOWERED_ORB)), + VULNERABILITY(55, 66, b -> b.rune(SOUL).rune(5, WATER).rune(5, EARTH)), + ENCHANT_LVL_5(56, 68, b -> b.rune(COSMIC).rune(15, WATER).rune(15, EARTH)), + ENCHANT_DRAGONSTONE_BOLT(8, 68, b -> b.rune(SOUL).rune(COSMIC).rune(12, EARTH)), + KOUREND_CASTLE_TELEPORT(57, 69, b -> b.rune(2, SOUL).rune(2, LAW).rune(4,WATER).rune(5, FIRE)), //TODO: UNLOCKED VIA BOOK + EARTH_WAVE(58, 70, b -> b.rune(BLOOD).rune(5, AIR).rune(7, EARTH)), + ENFEEBLE(59, 73, b -> b.rune(SOUL).rune(8, WATER).rune(8, EARTH)), + TELEOTHER_LUMBRIDGE(60, 74, b -> b.rune(SOUL).rune(LAW).rune(EARTH)), + FIRE_WAVE(61, 75, b -> b.rune(BLOOD).rune(5, AIR).rune(7, FIRE)), + ENTANGLE(62, 79, b -> b.rune(4, NATURE).rune(5, WATER).rune(5, EARTH)), + STUN(63, 80, b -> b.rune(SOUL).rune(12, WATER).rune(12, EARTH)), + CHARGE(64, 80, b -> b.rune(3, BLOOD).rune(3, FIRE).rune(3, AIR)), + WIND_SURGE(65, 81, b -> b.rune(WRATH).rune(7, AIR)), + TELEOTHER_FALADOR(66, 82, b -> b.rune(SOUL).rune(LAW).rune(WATER)), + WATER_SURGE(67, 85, b -> b.rune(WRATH).rune(7, AIR).rune(10, WATER)), + TELE_BLOCK(68, 85, b -> b.rune(LAW).rune(DEATH).rune(CHAOS)), //TODO: IN WILDERNESS + TELEPORT_TO_TARGET(69, 85, b -> b.rune(LAW).rune(DEATH).rune(CHAOS)), //TODO: HAVE READ TARGET TELEPORT SCROLL + ENCHANT_LVL_6(70, 87, b -> b.rune(COSMIC).rune(20, FIRE).rune(20, EARTH)), + ENCHANT_ONYX_BOLT(8, 87, b -> b.rune(DEATH).rune(COSMIC).rune(20, FIRE)), + TELEOTHER_CAMELOT(71, 90, b -> b.rune(LAW).rune(2, SOUL)), + EARTH_SURGE(72, 90, b -> b.rune(WRATH).rune(7, AIR).rune(10, EARTH)), + ENCHANT_LVL_7(73, 93, b -> b.rune(COSMIC).rune(20, SOUL).rune(20, BLOOD)), + FIRE_SURGE(74, 95, b -> b.rune(WRATH).rune(7, AIR).rune(10, FIRE)), + ; + + private final int groupId; + private final int widgetID; + private final int requiredMagicLevel; + private final UnaryOperator operator; + Spell(int widgetID, int requiredMagicLevel) + { + this.groupId = 218; + this.widgetID = widgetID; + this.requiredMagicLevel = requiredMagicLevel; + this.operator = UnaryOperator.identity(); // Spell has no requirements + } + + Spell(int widgetID, int requiredMagicLevel, UnaryOperator operator) + { + this.groupId = 218; + this.widgetID = widgetID; + this.requiredMagicLevel = requiredMagicLevel; + this.operator = operator; + } + + public Requirement getRequirements() + { + return operator.apply(StandardSpellBuilder.builder(this)).build(); + } +} diff --git a/src/main/java/com/questhelper/spells/StandardSpellBuilder.java b/src/main/java/com/questhelper/spells/StandardSpellBuilder.java new file mode 100644 index 0000000000..02a5bc5024 --- /dev/null +++ b/src/main/java/com/questhelper/spells/StandardSpellBuilder.java @@ -0,0 +1,88 @@ +/* + * + * * Copyright (c) 2021, Senmori + * * 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 com.questhelper.spells; + +import com.questhelper.requirements.ItemRequirement; +import com.questhelper.requirements.ItemRequirements; +import com.questhelper.requirements.util.LogicType; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import org.apache.commons.text.WordUtils; + +public class StandardSpellBuilder +{ + private final Spell spell; + private final List requirementList = new LinkedList<>(); + + private StandardSpellBuilder(Spell spell) + { + this.spell = spell; + } + + public static StandardSpellBuilder builder(Spell spell) + { + return new StandardSpellBuilder(spell); + } + + public StandardSpellBuilder rune(int quantity, Rune rune) + { + requirementList.add(rune.getRunes(quantity)); + return this; + } + + public StandardSpellBuilder rune(Rune rune) + { + return rune(1, rune); + } + + public StandardSpellBuilder item(int quantity, int itemID) + { + requirementList.add(new ItemRequirement("", itemID, quantity)); + return this; + } + + public StandardSpellBuilder item(int itemID) + { + return item(1, itemID); + } + + public StandardSpellBuilder item(boolean equipped, Integer... itemIDs) + { + requirementList.add(new ItemRequirement("", Arrays.asList(itemIDs), 1, equipped)); + return this; + } + + public ItemRequirements build() + { + String spellName = spell.name().toLowerCase(Locale.ROOT).replaceAll("_", " "); + String formattedName = WordUtils.capitalizeFully(spellName); + return new ItemRequirements(LogicType.AND, formattedName, requirementList); + } + +} From da7dc18f1486a5c5b3c7f094242dddbddd36a75f Mon Sep 17 00:00:00 2001 From: Senmori Date: Fri, 5 Feb 2021 18:12:33 -0500 Subject: [PATCH 03/55] Fix Mud Rune having wrong staves. Signed-off-by: Senmori --- src/main/java/com/questhelper/spells/Rune.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/questhelper/spells/Rune.java b/src/main/java/com/questhelper/spells/Rune.java index 5f2c96147f..6cccef9419 100644 --- a/src/main/java/com/questhelper/spells/Rune.java +++ b/src/main/java/com/questhelper/spells/Rune.java @@ -54,7 +54,7 @@ public enum Rune SOUL("Soul Rune", ItemID.SOUL_RUNE), WRATH("Wrath Rune", ItemID.WRATH_RUNE), LAVA("Lava Rune", ItemID.LAVA_RUNE, ItemCollections.getLavaStaff()), - MUD("Mud Rune", ItemID.DUST_RUNE, ItemCollections.getDustStaff()), + MUD("Mud Rune", ItemID.DUST_RUNE, ItemCollections.getMudStaff()), STEAM("Steam Rune", ItemID.STEAM_RUNE, ItemCollections.getSteamStaff()), SMOKE("Smoke Rune", ItemID.SMOKE_RUNE, ItemCollections.getSmokeStaff()), MIST("Mist Rune", ItemID.MIST_RUNE, ItemCollections.getMistStaff()), From 89d0ca2e8957739e707f9e7377cce77d4ec6f688 Mon Sep 17 00:00:00 2001 From: Senmori Date: Fri, 5 Feb 2021 18:25:14 -0500 Subject: [PATCH 04/55] Add Kodai Wand to water staffs since it acts like a source of unlimited water runes. Signed-off-by: Senmori --- src/main/java/com/questhelper/ItemCollections.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/questhelper/ItemCollections.java b/src/main/java/com/questhelper/ItemCollections.java index 3a425a00a2..6b5a74edd7 100644 --- a/src/main/java/com/questhelper/ItemCollections.java +++ b/src/main/java/com/questhelper/ItemCollections.java @@ -181,6 +181,7 @@ public class ItemCollections @Getter private static final List waterStaff = Arrays.asList( + ItemID.KODAI_WAND, ItemID.WATER_BATTLESTAFF, ItemID.MYSTIC_WATER_STAFF, ItemID.STAFF_OF_WATER, From d685e698d73ac199b604273baf4d080ca769f61c Mon Sep 17 00:00:00 2001 From: Senmori Date: Fri, 5 Feb 2021 18:57:50 -0500 Subject: [PATCH 05/55] Add MagicSpell interface and implement it in Spell. Signed-off-by: Senmori --- .../com/questhelper/spells/MagicSpell.java | 44 ++++ .../java/com/questhelper/spells/Spell.java | 197 ++++++++++-------- .../spells/StandardSpellBuilder.java | 12 +- 3 files changed, 157 insertions(+), 96 deletions(-) create mode 100644 src/main/java/com/questhelper/spells/MagicSpell.java diff --git a/src/main/java/com/questhelper/spells/MagicSpell.java b/src/main/java/com/questhelper/spells/MagicSpell.java new file mode 100644 index 0000000000..0a29c8cd1c --- /dev/null +++ b/src/main/java/com/questhelper/spells/MagicSpell.java @@ -0,0 +1,44 @@ +/* + * + * * Copyright (c) 2021, Senmori + * * 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 com.questhelper.spells; + +import com.questhelper.requirements.util.Spellbook; + +public interface MagicSpell +{ + String getName(); + + int getWidgetID(); + + int getGroupID(); + + int getSpriteID(); + + int getRequiredMagicLevel(); + + Spellbook getSpellbook(); +} diff --git a/src/main/java/com/questhelper/spells/Spell.java b/src/main/java/com/questhelper/spells/Spell.java index 793200dad1..425f6fb139 100644 --- a/src/main/java/com/questhelper/spells/Spell.java +++ b/src/main/java/com/questhelper/spells/Spell.java @@ -27,111 +27,113 @@ package com.questhelper.spells; import com.questhelper.requirements.Requirement; +import com.questhelper.requirements.util.Spellbook; +import java.util.Locale; import java.util.function.UnaryOperator; import lombok.Getter; import static com.questhelper.spells.Rune.*; import net.runelite.api.ItemID; +import org.apache.commons.text.WordUtils; @Getter -public enum Spell +public enum Spell implements MagicSpell { - LUMBRIDGE_HOME_TELEPORT(5, 0), - WIND_STRIKE(6, 1, b -> b.rune(AIR).rune(MIND)), - CONFUSE(7, 3, b -> b.rune(BODY).rune(2, EARTH).rune(3, WATER)), - ENCHANT_OPAL_BOLT(8, 4, b -> b.rune(COSMIC).rune(2, AIR)), - WATER_STRIKE(9, 5, b -> b.rune(AIR).rune(WATER).rune(MIND)), - ENCHANT_LVL_1(10, 7, b -> b.rune(WATER).rune(COSMIC)), - ENCHANT_SAPPHIRE_BOLT(8, 9, b -> b.rune(WATER).rune(COSMIC)), - EARTH_STRIKE(11, 9, b -> b.rune(AIR).rune(MIND).rune(2, EARTH)), - WEAKEN(12, 11, b -> b.rune(BODY).rune(2, EARTH).rune(3, WATER)), - FIRE_STRIKE(13, 13, b -> b.rune(MIND).rune(2, AIR).rune(3, FIRE)), - ENCHANT_JADE_BOLT(8, 14, b -> b.rune(COSMIC).rune(2, EARTH)), - BONES_TO_BANANAS(14, 15, b -> b.rune(NATURE).rune(2, EARTH).rune(2, WATER)), - WIND_BOLT(15, 17, b -> b.rune(CHAOS).rune(2, AIR)), - CURSE(16, 19, b -> b.rune(BODY).rune(2, WATER).rune(3, EARTH)), - BIND(17, 20, b -> b.rune(2, NATURE).rune(3, WATER).rune(3, EARTH)), - LOW_LVL_ALCHEMY(18, 21, b -> b.rune(NATURE).rune(3, FIRE)), - WATER_BOLT(19, 23, b -> b.rune(CHAOS).rune(2, WATER).rune(2, AIR)), - ENCHANT_PEARL_BOLT(8, 24, b -> b.rune(COSMIC).rune(2, WATER)), - VARROCK_TELEPORT(20, 25, b -> b.rune(LAW).rune(FIRE).rune(3, AIR)), - ENCHANT_LVL_2(21, 27, b -> b.rune(COSMIC).rune(3, AIR)), - ENCHANT_EMERALD_BOLT(8, 27, b -> b.rune(NATURE).rune(COSMIC).rune(3, AIR)), - EARTH_BOLT(22, 9, b -> b.rune(CHAOS).rune(EARTH).rune(3, AIR)), - ENCHANT_RED_TOPAZ_BOLT(8, 29, b -> b.rune(CHAOS).rune(2, FIRE)), - LUMBRIDGE_TELEPORT(23, 31, b -> b.rune(LAW).rune(EARTH).rune(3, AIR)), - TELEKINETIC_GRAB(24, 33, b -> b.rune(LAW).rune(AIR)), - FIRE_BOLT(25, 35, b -> b.rune(CHAOS).rune(3, AIR).rune(4, FIRE)), - FALADOR_TELEPORT(26, 37, b -> b.rune(LAW).rune(WATER).rune(3, AIR)), - CRUMBLE_UNDEAD(27, 39, b -> b.rune(CHAOS).rune(2, EARTH).rune(2, AIR)), - TELEPORT_TO_HOUSE(28, 40, b -> b.rune(LAW).rune(EARTH).rune(AIR)), - WIND_BLAST(29, 41, b -> b.rune(DEATH).rune(3, AIR)), - SUPERHEAT_ITEM(30, 43, b -> b.rune(NATURE).rune(4, FIRE)), - CAMELOT_TELEPORT(31, 45, b -> b.rune(LAW).rune(5, AIR)), - WATER_BLAST(32, 47, b -> b.rune(DEATH).rune(3, AIR).rune(3, WATER)), - ENCHANT_LVL_3(33, 49, b -> b.rune(COSMIC).rune(5, FIRE)), - ENCHANT_RUBY_BOLT(8, 49, b -> b.rune(COSMIC).rune(BLOOD).rune(5, FIRE)), - IBAN_BLAST(34, 50, b -> b.rune(DEATH).rune(5, FIRE).item(true, ItemID.IBANS_STAFF, ItemID.IBANS_STAFF_U)), - SNARE(35, 50, b -> b.rune(3, NATURE).rune(4, WATER).rune(4, EARTH)), - MAGIC_DART(36, 50, b -> b.rune(DEATH).rune(4, MIND)), - ARDOUGNE_TELEPORT(37, 51, b -> b.rune(2, LAW).rune(2, WATER)), //TODO: QUEST REQ -> PLAGUE CITY - EARTH_BLAST(38, 43, b -> b.rune(DEATH).rune(3, AIR).rune(4, EARTH)), - HIGH_LVL_ALCHEMY(39, 55, b -> b.rune(NATURE).rune(5, FIRE)), - CHARGE_WATER_ORB(40, 56, b -> b.rune(3, COSMIC).rune(30, WATER).item(ItemID.UNPOWERED_ORB)), - ENCHANT_LVL_4(41, 57, b -> b.rune(COSMIC).rune(10, EARTH)), - ENCHANT_DIAMOND_BOLT(8, 57, b -> b.rune(COSMIC).rune(2, LAW).rune(10, EARTH)), - WATCHTOWER_TELEPORT(42, 58, b -> b.rune(2, LAW).rune(2, EARTH)), - FIRE_BLAST(43, 59, b -> b.rune(DEATH).rune(4, AIR).rune(5, FIRE)), - CHARGE_EARTH_ORB(44, 60, b -> b.rune(3, COSMIC).rune(30, EARTH).item(ItemID.UNPOWERED_ORB)), - BONES_TO_PEACHES(45, 60, b -> b.rune(2, NATURE).rune(2, EARTH).rune(4, WATER)), //TODO: MAGE TRAINING ARENA - SARADOMIN_STRIKE(46, 60, b -> b.rune(2, BLOOD).rune(2, FIRE).rune(4, AIR)), // TODO: MAGE ARENA - CLAWS_OF_GUTHIX(47, 60, b -> b.rune(FIRE).rune(2, BLOOD).rune(4, AIR)), // TODO: MAGE ARENA - FLAMES_OF_ZAMORAK(48, 60, b -> b.rune(AIR).rune(2, BLOOD).rune(4, FIRE)), // TODO: MAGE ARENA - TROLLHEIM_TELEPORT(49, 61, b -> b.rune(2, LAW).rune(2, FIRE)), // TODO: QUEST REQ -> EADGARS RUSE QUEST - WIND_WAVE(50, 62, b -> b.rune(BLOOD).rune(5, AIR)), - CHARGE_FIRE_ORB(51, 63, b -> b.rune(3, COSMIC).rune(30, FIRE).item(ItemID.UNPOWERED_ORB)), - APE_ATOLL_TELEPORT(52, 64, b -> b.rune(2, LAW).rune(2, WATER).rune(2, FIRE).item(ItemID.BANANA)), - WATER_WAVE(53, 54, b -> b.rune(BLOOD).rune(5, AIR).rune(7, WATER)), - CHARGE_AIR_ORB(54, 66, b -> b.rune(3, COSMIC).rune(30, AIR).item(ItemID.UNPOWERED_ORB)), - VULNERABILITY(55, 66, b -> b.rune(SOUL).rune(5, WATER).rune(5, EARTH)), - ENCHANT_LVL_5(56, 68, b -> b.rune(COSMIC).rune(15, WATER).rune(15, EARTH)), - ENCHANT_DRAGONSTONE_BOLT(8, 68, b -> b.rune(SOUL).rune(COSMIC).rune(12, EARTH)), - KOUREND_CASTLE_TELEPORT(57, 69, b -> b.rune(2, SOUL).rune(2, LAW).rune(4,WATER).rune(5, FIRE)), //TODO: UNLOCKED VIA BOOK - EARTH_WAVE(58, 70, b -> b.rune(BLOOD).rune(5, AIR).rune(7, EARTH)), - ENFEEBLE(59, 73, b -> b.rune(SOUL).rune(8, WATER).rune(8, EARTH)), - TELEOTHER_LUMBRIDGE(60, 74, b -> b.rune(SOUL).rune(LAW).rune(EARTH)), - FIRE_WAVE(61, 75, b -> b.rune(BLOOD).rune(5, AIR).rune(7, FIRE)), - ENTANGLE(62, 79, b -> b.rune(4, NATURE).rune(5, WATER).rune(5, EARTH)), - STUN(63, 80, b -> b.rune(SOUL).rune(12, WATER).rune(12, EARTH)), - CHARGE(64, 80, b -> b.rune(3, BLOOD).rune(3, FIRE).rune(3, AIR)), - WIND_SURGE(65, 81, b -> b.rune(WRATH).rune(7, AIR)), - TELEOTHER_FALADOR(66, 82, b -> b.rune(SOUL).rune(LAW).rune(WATER)), - WATER_SURGE(67, 85, b -> b.rune(WRATH).rune(7, AIR).rune(10, WATER)), - TELE_BLOCK(68, 85, b -> b.rune(LAW).rune(DEATH).rune(CHAOS)), //TODO: IN WILDERNESS - TELEPORT_TO_TARGET(69, 85, b -> b.rune(LAW).rune(DEATH).rune(CHAOS)), //TODO: HAVE READ TARGET TELEPORT SCROLL - ENCHANT_LVL_6(70, 87, b -> b.rune(COSMIC).rune(20, FIRE).rune(20, EARTH)), - ENCHANT_ONYX_BOLT(8, 87, b -> b.rune(DEATH).rune(COSMIC).rune(20, FIRE)), - TELEOTHER_CAMELOT(71, 90, b -> b.rune(LAW).rune(2, SOUL)), - EARTH_SURGE(72, 90, b -> b.rune(WRATH).rune(7, AIR).rune(10, EARTH)), - ENCHANT_LVL_7(73, 93, b -> b.rune(COSMIC).rune(20, SOUL).rune(20, BLOOD)), - FIRE_SURGE(74, 95, b -> b.rune(WRATH).rune(7, AIR).rune(10, FIRE)), + LUMBRIDGE_HOME_TELEPORT(356, 5, 0), + WIND_STRIKE(15, 6, 1, b -> b.rune(AIR).rune(MIND)), + CONFUSE(16, 7, 3, b -> b.rune(BODY).rune(2, EARTH).rune(3, WATER)), + ENCHANT_OPAL_BOLT(358, 8, 4, b -> b.rune(COSMIC).rune(2, AIR)), + WATER_STRIKE(17, 9, 5, b -> b.rune(AIR).rune(WATER).rune(MIND)), + ENCHANT_LVL_1(18, 10, 7, b -> b.rune(WATER).rune(COSMIC)), + ENCHANT_SAPPHIRE_BOLT(358, 8, 9, b -> b.rune(WATER).rune(COSMIC)), + EARTH_STRIKE(19, 11, 9, b -> b.rune(AIR).rune(MIND).rune(2, EARTH)), + WEAKEN(20, 12, 11, b -> b.rune(BODY).rune(2, EARTH).rune(3, WATER)), + FIRE_STRIKE(21, 13, 13, b -> b.rune(MIND).rune(2, AIR).rune(3, FIRE)), + ENCHANT_JADE_BOLT(358, 8, 14, b -> b.rune(COSMIC).rune(2, EARTH)), + BONES_TO_BANANAS(22, 14, 15, b -> b.rune(NATURE).rune(2, EARTH).rune(2, WATER)), + WIND_BOLT(23, 15, 17, b -> b.rune(CHAOS).rune(2, AIR)), + CURSE(24, 16, 19, b -> b.rune(BODY).rune(2, WATER).rune(3, EARTH)), + BIND(319, 17, 20, b -> b.rune(2, NATURE).rune(3, WATER).rune(3, EARTH)), + LOW_LVL_ALCHEMY(25, 18, 21, b -> b.rune(NATURE).rune(3, FIRE)), + WATER_BOLT(26, 19, 23, b -> b.rune(CHAOS).rune(2, WATER).rune(2, AIR)), + ENCHANT_PEARL_BOLT(358, 8, 24, b -> b.rune(COSMIC).rune(2, WATER)), + VARROCK_TELEPORT(27, 20, 25, b -> b.rune(LAW).rune(FIRE).rune(3, AIR)), + ENCHANT_LVL_2(28, 21, 27, b -> b.rune(COSMIC).rune(3, AIR)), + ENCHANT_EMERALD_BOLT(358, 8, 27, b -> b.rune(NATURE).rune(COSMIC).rune(3, AIR)), + EARTH_BOLT(29, 22, 9, b -> b.rune(CHAOS).rune(EARTH).rune(3, AIR)), + ENCHANT_RED_TOPAZ_BOLT(358, 8, 29, b -> b.rune(CHAOS).rune(2, FIRE)), + LUMBRIDGE_TELEPORT(30, 23, 31, b -> b.rune(LAW).rune(EARTH).rune(3, AIR)), + TELEKINETIC_GRAB(31, 24, 33, b -> b.rune(LAW).rune(AIR)), + FIRE_BOLT(32, 25, 35, b -> b.rune(CHAOS).rune(3, AIR).rune(4, FIRE)), + FALADOR_TELEPORT(33, 26, 37, b -> b.rune(LAW).rune(WATER).rune(3, AIR)), + CRUMBLE_UNDEAD(34, 27, 39, b -> b.rune(CHAOS).rune(2, EARTH).rune(2, AIR)), + TELEPORT_TO_HOUSE(355, 28, 40, b -> b.rune(LAW).rune(EARTH).rune(AIR)), + WIND_BLAST(35, 29, 41, b -> b.rune(DEATH).rune(3, AIR)), + SUPERHEAT_ITEM(36, 30, 43, b -> b.rune(NATURE).rune(4, FIRE)), + CAMELOT_TELEPORT(37, 31, 45, b -> b.rune(LAW).rune(5, AIR)), + WATER_BLAST(38, 32, 47, b -> b.rune(DEATH).rune(3, AIR).rune(3, WATER)), + ENCHANT_LVL_3(39, 33, 49, b -> b.rune(COSMIC).rune(5, FIRE)), + ENCHANT_RUBY_BOLT(358, 8, 49, b -> b.rune(COSMIC).rune(BLOOD).rune(5, FIRE)), + IBAN_BLAST(53, 34, 50, b -> b.rune(DEATH).rune(5, FIRE).item(true, ItemID.IBANS_STAFF, ItemID.IBANS_STAFF_U)), + SNARE(320, 35, 50, b -> b.rune(3, NATURE).rune(4, WATER).rune(4, EARTH)), + MAGIC_DART(324, 36, 50, b -> b.rune(DEATH).rune(4, MIND)), + ARDOUGNE_TELEPORT(54, 37, 51, b -> b.rune(2, LAW).rune(2, WATER)), //TODO: QUEST REQ -> PLAGUE CITY + EARTH_BLAST(40, 38, 43, b -> b.rune(DEATH).rune(3, AIR).rune(4, EARTH)), + HIGH_LVL_ALCHEMY(41, 39, 55, b -> b.rune(NATURE).rune(5, FIRE)), + CHARGE_WATER_ORB(42, 40, 56, b -> b.rune(3, COSMIC).rune(30, WATER).item(ItemID.UNPOWERED_ORB)), + ENCHANT_LVL_4(43, 41, 57, b -> b.rune(COSMIC).rune(10, EARTH)), + ENCHANT_DIAMOND_BOLT(358, 8, 57, b -> b.rune(COSMIC).rune(2, LAW).rune(10, EARTH)), + WATCHTOWER_TELEPORT(55, 42, 58, b -> b.rune(2, LAW).rune(2, EARTH)), + FIRE_BLAST(44, 43, 59, b -> b.rune(DEATH).rune(4, AIR).rune(5, FIRE)), + CHARGE_EARTH_ORB(45, 44, 60, b -> b.rune(3, COSMIC).rune(30, EARTH).item(ItemID.UNPOWERED_ORB)), + BONES_TO_PEACHES(354, 45, 60, b -> b.rune(2, NATURE).rune(2, EARTH).rune(4, WATER)), //TODO: MAGE TRAINING ARENA + SARADOMIN_STRIKE(61, 46, 60, b -> b.rune(2, BLOOD).rune(2, FIRE).rune(4, AIR)), // TODO: MAGE ARENA + CLAWS_OF_GUTHIX(60, 47, 60, b -> b.rune(FIRE).rune(2, BLOOD).rune(4, AIR)), // TODO: MAGE ARENA + FLAMES_OF_ZAMORAK(59, 48, 60, b -> b.rune(AIR).rune(2, BLOOD).rune(4, FIRE)), // TODO: MAGE ARENA + TROLLHEIM_TELEPORT(323, 49, 61, b -> b.rune(2, LAW).rune(2, FIRE)), // TODO: QUEST REQ -> EADGARS RUSE QUEST + WIND_WAVE(46, 50, 62, b -> b.rune(BLOOD).rune(5, AIR)), + CHARGE_FIRE_ORB(47, 51, 63, b -> b.rune(3, COSMIC).rune(30, FIRE).item(ItemID.UNPOWERED_ORB)), + APE_ATOLL_TELEPORT(357, 52, 64, b -> b.rune(2, LAW).rune(2, WATER).rune(2, FIRE).item(ItemID.BANANA)), + WATER_WAVE(48, 53, 54, b -> b.rune(BLOOD).rune(5, AIR).rune(7, WATER)), + CHARGE_AIR_ORB(49, 54, 66, b -> b.rune(3, COSMIC).rune(30, AIR).item(ItemID.UNPOWERED_ORB)), + VULNERABILITY(56, 55, 66, b -> b.rune(SOUL).rune(5, WATER).rune(5, EARTH)), + ENCHANT_LVL_5(50, 56, 68, b -> b.rune(COSMIC).rune(15, WATER).rune(15, EARTH)), + ENCHANT_DRAGONSTONE_BOLT(358, 8, 68, b -> b.rune(SOUL).rune(COSMIC).rune(12, EARTH)), + KOUREND_CASTLE_TELEPORT(360, 57, 69, b -> b.rune(2, SOUL).rune(2, LAW).rune(4,WATER).rune(5, FIRE)), //TODO: UNLOCKED VIA BOOK + EARTH_WAVE(51, 58, 70, b -> b.rune(BLOOD).rune(5, AIR).rune(7, EARTH)), + ENFEEBLE(57, 59, 73, b -> b.rune(SOUL).rune(8, WATER).rune(8, EARTH)), + TELEOTHER_LUMBRIDGE(349, 60, 74, b -> b.rune(SOUL).rune(LAW).rune(EARTH)), + FIRE_WAVE(52, 61, 75, b -> b.rune(BLOOD).rune(5, AIR).rune(7, FIRE)), + ENTANGLE(321, 62, 79, b -> b.rune(4, NATURE).rune(5, WATER).rune(5, EARTH)), + STUN(58, 63, 80, b -> b.rune(SOUL).rune(12, WATER).rune(12, EARTH)), + CHARGE(322, 64, 80, b -> b.rune(3, BLOOD).rune(3, FIRE).rune(3, AIR)), + WIND_SURGE(362, 65, 81, b -> b.rune(WRATH).rune(7, AIR)), + TELEOTHER_FALADOR(350, 66, 82, b -> b.rune(SOUL).rune(LAW).rune(WATER)), + WATER_SURGE(363, 67, 85, b -> b.rune(WRATH).rune(7, AIR).rune(10, WATER)), + TELE_BLOCK(352, 68, 85, b -> b.rune(LAW).rune(DEATH).rune(CHAOS)), //TODO: IN WILDERNESS + TELEPORT_TO_TARGET(359, 69, 85, b -> b.rune(LAW).rune(DEATH).rune(CHAOS)), //TODO: HAVE READ TARGET TELEPORT SCROLL + ENCHANT_LVL_6(353, 70, 87, b -> b.rune(COSMIC).rune(20, FIRE).rune(20, EARTH)), + ENCHANT_ONYX_BOLT(358, 8, 87, b -> b.rune(DEATH).rune(COSMIC).rune(20, FIRE)), + TELEOTHER_CAMELOT(351, 71, 90, b -> b.rune(LAW).rune(2, SOUL)), + EARTH_SURGE(364, 72, 90, b -> b.rune(WRATH).rune(7, AIR).rune(10, EARTH)), + ENCHANT_LVL_7(361, 73, 93, b -> b.rune(COSMIC).rune(20, SOUL).rune(20, BLOOD)), + FIRE_SURGE(365, 74, 95, b -> b.rune(WRATH).rune(7, AIR).rune(10, FIRE)), ; - private final int groupId; + private final int spriteID; + private final int groupID; private final int widgetID; private final int requiredMagicLevel; private final UnaryOperator operator; - Spell(int widgetID, int requiredMagicLevel) + Spell(int spriteID, int widgetID, int requiredMagicLevel) { - this.groupId = 218; - this.widgetID = widgetID; - this.requiredMagicLevel = requiredMagicLevel; - this.operator = UnaryOperator.identity(); // Spell has no requirements + this(spriteID, widgetID, requiredMagicLevel, UnaryOperator.identity()); } - Spell(int widgetID, int requiredMagicLevel, UnaryOperator operator) + Spell(int spriteID, int widgetID, int requiredMagicLevel, UnaryOperator operator) { - this.groupId = 218; + this.spriteID = spriteID; + this.groupID = 218; this.widgetID = widgetID; this.requiredMagicLevel = requiredMagicLevel; this.operator = operator; @@ -141,4 +143,23 @@ public Requirement getRequirements() { return operator.apply(StandardSpellBuilder.builder(this)).build(); } + + @Override + public String getName() + { + String spellName = name().toLowerCase(Locale.ROOT).replaceAll("_", " "); + return WordUtils.capitalizeFully(spellName); + } + + @Override + public int getGroupID() + { + return groupID; + } + + @Override + public Spellbook getSpellbook() + { + return Spellbook.NORMAL; + } } diff --git a/src/main/java/com/questhelper/spells/StandardSpellBuilder.java b/src/main/java/com/questhelper/spells/StandardSpellBuilder.java index 02a5bc5024..eb845369dc 100644 --- a/src/main/java/com/questhelper/spells/StandardSpellBuilder.java +++ b/src/main/java/com/questhelper/spells/StandardSpellBuilder.java @@ -32,20 +32,18 @@ import java.util.Arrays; import java.util.LinkedList; import java.util.List; -import java.util.Locale; -import org.apache.commons.text.WordUtils; public class StandardSpellBuilder { - private final Spell spell; + private final MagicSpell spell; private final List requirementList = new LinkedList<>(); - private StandardSpellBuilder(Spell spell) + private StandardSpellBuilder(MagicSpell spell) { this.spell = spell; } - public static StandardSpellBuilder builder(Spell spell) + public static StandardSpellBuilder builder(MagicSpell spell) { return new StandardSpellBuilder(spell); } @@ -80,9 +78,7 @@ public StandardSpellBuilder item(boolean equipped, Integer... itemIDs) public ItemRequirements build() { - String spellName = spell.name().toLowerCase(Locale.ROOT).replaceAll("_", " "); - String formattedName = WordUtils.capitalizeFully(spellName); - return new ItemRequirements(LogicType.AND, formattedName, requirementList); + return new ItemRequirements(LogicType.AND, spell.getName(), requirementList); } } From b789a80ffe397341d8b177bde0a57e06f71a51b7 Mon Sep 17 00:00:00 2001 From: Senmori Date: Fri, 5 Feb 2021 19:05:58 -0500 Subject: [PATCH 06/55] Added javadocs. Fixed MUD rune not actually using MUD_RUNE Signed-off-by: Senmori --- .../com/questhelper/spells/MagicSpell.java | 23 ++++++++++ .../java/com/questhelper/spells/Rune.java | 12 +++++- .../spells/StandardSpellBuilder.java | 42 +++++++++++++++++++ 3 files changed, 76 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/questhelper/spells/MagicSpell.java b/src/main/java/com/questhelper/spells/MagicSpell.java index 0a29c8cd1c..a3f18ac57e 100644 --- a/src/main/java/com/questhelper/spells/MagicSpell.java +++ b/src/main/java/com/questhelper/spells/MagicSpell.java @@ -28,17 +28,40 @@ import com.questhelper.requirements.util.Spellbook; +/** + * Represents a magic spell that can be cast by a player. + */ public interface MagicSpell { + /** + * @return the formatted display name + */ String getName(); + /** + * @return the widget ID + */ int getWidgetID(); + /** + * @return the group ID + */ int getGroupID(); + /** + * @return the sprite ID + * + * @see net.runelite.api.SpriteID + */ int getSpriteID(); + /** + * @return the required {@link net.runelite.api.Skill#MAGIC)} level to cast this spell. + */ int getRequiredMagicLevel(); + /** + * @return the {@link Spellbook} this spell is contained within. + */ Spellbook getSpellbook(); } diff --git a/src/main/java/com/questhelper/spells/Rune.java b/src/main/java/com/questhelper/spells/Rune.java index 6cccef9419..a7fe41e210 100644 --- a/src/main/java/com/questhelper/spells/Rune.java +++ b/src/main/java/com/questhelper/spells/Rune.java @@ -36,6 +36,9 @@ import lombok.Getter; import net.runelite.api.ItemID; +/** + * Represents a rune that can be used to cast spells. + */ public enum Rune { AIR("Air Rune", ItemCollections.getAirRune(), ItemCollections.getAirStaff()), @@ -54,7 +57,7 @@ public enum Rune SOUL("Soul Rune", ItemID.SOUL_RUNE), WRATH("Wrath Rune", ItemID.WRATH_RUNE), LAVA("Lava Rune", ItemID.LAVA_RUNE, ItemCollections.getLavaStaff()), - MUD("Mud Rune", ItemID.DUST_RUNE, ItemCollections.getMudStaff()), + MUD("Mud Rune", ItemID.MUD_RUNE, ItemCollections.getMudStaff()), STEAM("Steam Rune", ItemID.STEAM_RUNE, ItemCollections.getSteamStaff()), SMOKE("Smoke Rune", ItemID.SMOKE_RUNE, ItemCollections.getSmokeStaff()), MIST("Mist Rune", ItemID.MIST_RUNE, ItemCollections.getMistStaff()), @@ -86,6 +89,13 @@ public enum Rune this.staves = staves; } + /** + * Get a new {@link ItemRequirements} containing the specified number of rune(s) + * as well as their equivalent items (combination runes, staves, etc). + * + * @param quantity the number of runes required + * @return a new instance of {@link ItemRequirements} + */ public ItemRequirements getRunes(int quantity) { if (runes != null && staves != null) diff --git a/src/main/java/com/questhelper/spells/StandardSpellBuilder.java b/src/main/java/com/questhelper/spells/StandardSpellBuilder.java index eb845369dc..1a35f7ab96 100644 --- a/src/main/java/com/questhelper/spells/StandardSpellBuilder.java +++ b/src/main/java/com/questhelper/spells/StandardSpellBuilder.java @@ -43,39 +43,81 @@ private StandardSpellBuilder(MagicSpell spell) this.spell = spell; } + /** + * Get a new instance of this StandardSpellBuilder + * + * @param spell the spell to build + * @return this + */ public static StandardSpellBuilder builder(MagicSpell spell) { return new StandardSpellBuilder(spell); } + /** + * Add a specified quantity of a given {@link Rune} + * + * @param quantity the number of Rune(s) required + * @param rune the Rune + * @return this + */ public StandardSpellBuilder rune(int quantity, Rune rune) { requirementList.add(rune.getRunes(quantity)); return this; } + /** + * Add a single Rune to this spell + * + * @param rune the Rune + * @return this + */ public StandardSpellBuilder rune(Rune rune) { return rune(1, rune); } + /** + * Add an item this spell needs in order to be cast. + * + * @param quantity the number of items required + * @param itemID the item required + * @return this + */ public StandardSpellBuilder item(int quantity, int itemID) { requirementList.add(new ItemRequirement("", itemID, quantity)); return this; } + /** + * Add a single item required for this spell to be cast + * + * @param itemID the item required + * @return this + */ public StandardSpellBuilder item(int itemID) { return item(1, itemID); } + /** + * Add an item (and it's suitable equivalents) that should be equipped in order to cast this spell + * + * @param equipped if this item should be equipped + * @param itemIDs the items that should be equipped (only one is required to be equipped) + * @return this + */ public StandardSpellBuilder item(boolean equipped, Integer... itemIDs) { requirementList.add(new ItemRequirement("", Arrays.asList(itemIDs), 1, equipped)); return this; } + /** + * @return a new {@link ItemRequirements} containing all this spell information + */ public ItemRequirements build() { return new ItemRequirements(LogicType.AND, spell.getName(), requirementList); From e469f983952f7da4b03ba9c17a739896a6e2fbf8 Mon Sep 17 00:00:00 2001 From: Senmori Date: Fri, 5 Feb 2021 19:07:49 -0500 Subject: [PATCH 07/55] Renamed Spell to StandardSpell in order to prepare for future Ancient,Lunar, and Arceuus spells. Signed-off-by: Senmori --- .../questhelper/spells/{Spell.java => StandardSpell.java} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename src/main/java/com/questhelper/spells/{Spell.java => StandardSpell.java} (97%) diff --git a/src/main/java/com/questhelper/spells/Spell.java b/src/main/java/com/questhelper/spells/StandardSpell.java similarity index 97% rename from src/main/java/com/questhelper/spells/Spell.java rename to src/main/java/com/questhelper/spells/StandardSpell.java index 425f6fb139..ac36f8e473 100644 --- a/src/main/java/com/questhelper/spells/Spell.java +++ b/src/main/java/com/questhelper/spells/StandardSpell.java @@ -37,7 +37,7 @@ import org.apache.commons.text.WordUtils; @Getter -public enum Spell implements MagicSpell +public enum StandardSpell implements MagicSpell { LUMBRIDGE_HOME_TELEPORT(356, 5, 0), WIND_STRIKE(15, 6, 1, b -> b.rune(AIR).rune(MIND)), @@ -125,12 +125,12 @@ public enum Spell implements MagicSpell private final int widgetID; private final int requiredMagicLevel; private final UnaryOperator operator; - Spell(int spriteID, int widgetID, int requiredMagicLevel) + StandardSpell(int spriteID, int widgetID, int requiredMagicLevel) { this(spriteID, widgetID, requiredMagicLevel, UnaryOperator.identity()); } - Spell(int spriteID, int widgetID, int requiredMagicLevel, UnaryOperator operator) + StandardSpell(int spriteID, int widgetID, int requiredMagicLevel, UnaryOperator operator) { this.spriteID = spriteID; this.groupID = 218; From 68d9f3b9db2e87005e25f595a335bafe6ac51074 Mon Sep 17 00:00:00 2001 From: Senmori Date: Fri, 5 Feb 2021 20:48:30 -0500 Subject: [PATCH 08/55] First pass at SpellRequirement. Have to implement requirement checking, building overlay text, and highlighting widget icons, npcs, objects, etc. Signed-off-by: Senmori --- .../requirements/ItemRequirement.java | 1 + .../requirements/SpellRequirement.java | 156 ++++++++++++++++++ .../com/questhelper/spells/MagicSpell.java | 8 + .../java/com/questhelper/spells/Rune.java | 17 ++ .../com/questhelper/spells/StandardSpell.java | 5 +- .../spells/StandardSpellBuilder.java | 14 +- 6 files changed, 194 insertions(+), 7 deletions(-) create mode 100644 src/main/java/com/questhelper/requirements/SpellRequirement.java diff --git a/src/main/java/com/questhelper/requirements/ItemRequirement.java b/src/main/java/com/questhelper/requirements/ItemRequirement.java index ba747e27ac..93fa377ff2 100644 --- a/src/main/java/com/questhelper/requirements/ItemRequirement.java +++ b/src/main/java/com/questhelper/requirements/ItemRequirement.java @@ -140,6 +140,7 @@ public List getAllIds() return ids; } + @Override public List getDisplayTextWithChecks(Client client) { List lines = new ArrayList<>(); diff --git a/src/main/java/com/questhelper/requirements/SpellRequirement.java b/src/main/java/com/questhelper/requirements/SpellRequirement.java new file mode 100644 index 0000000000..2dddba6c7e --- /dev/null +++ b/src/main/java/com/questhelper/requirements/SpellRequirement.java @@ -0,0 +1,156 @@ +/* + * + * * Copyright (c) 2021, Senmori + * * 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 com.questhelper.requirements; + +import com.questhelper.spells.MagicSpell; +import com.questhelper.spells.Rune; +import java.awt.Color; +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; +import lombok.Getter; +import net.runelite.api.Client; +import net.runelite.api.Item; + +public class SpellRequirement extends ItemRequirement +{ + @Getter + private final MagicSpell spell; + + private int numberOfCasts = 1; + + private final List requirements; + private final List runeRequirements = new ArrayList<>(); + private final Map runesPerCastMap; + public SpellRequirement(MagicSpell spell, Map runesPerCastMap, List requirements) + { + super(spell.getName(), -1); + this.spell = spell; + this.requirements = new LinkedList<>(requirements); // make it mutable for now + this.runesPerCastMap = runesPerCastMap; + registerItemRequirements(this.requirements); + setupRuneRequirements(1); + } + + public void setNumberOfCasts(int numberOfCasts) + { + this.numberOfCasts = numberOfCasts; + setupRuneRequirements(this.numberOfCasts); + } + + @Override + public void setDisplayItemId(Integer displayItemId) + { + // Don't set so we always use the requirement ids for bank filters + } + + private void registerItemRequirements(List requirements) + { + getItemRequirements(requirements).forEach(item -> addAlternates(item.getAllIds())); + } + + private void setupRuneRequirements(int numberOfCasts) + { + runeRequirements.clear(); + for (Map.Entry entry : runesPerCastMap.entrySet()) + { + Rune rune = entry.getKey(); + int runesPerCast = entry.getValue(); + runeRequirements.add(rune.getRunes(runesPerCast * numberOfCasts)); + } + } + + private List getItemRequirements(List requirements) + { + return requirements.stream() + .filter(ItemRequirement.class::isInstance) + .map(ItemRequirement.class::cast) + .collect(Collectors.toList()); + } + + private List buildAllIds(List requirements) + { + List itemRequirements = getItemRequirements(requirements); + + return itemRequirements.stream() + .filter(Objects::nonNull) + .map(ItemRequirement::getAllIds) + .flatMap(Collection::stream) + .distinct() + .collect(Collectors.toList()); + } + + @Nonnull + @Override + public String getDisplayText() + { + return spell.getName(); + } + + @Override + public boolean isActualItem() + { + return false; + } + + @Override + public boolean showQuantity() + { + return false; + } + + @Override + public Color getColor(Client client) + { + return check(client) ? Color.GREEN : Color.RED; + } + + @Override + public Color getColorConsideringBank(Client client, boolean checkConsideringSlotRestrictions, Item[] bankItems) + { + Color color = this.check(client, checkConsideringSlotRestrictions) ? Color.GREEN : Color.RED; + if (color == Color.RED && bankItems != null) + { + if (check(client, false, bankItems)) + { + color = Color.WHITE; + } + } + return color; + } + + @Override + public boolean check(Client client) + { + return super.check(client); + } +} diff --git a/src/main/java/com/questhelper/spells/MagicSpell.java b/src/main/java/com/questhelper/spells/MagicSpell.java index a3f18ac57e..e54d8ebbb9 100644 --- a/src/main/java/com/questhelper/spells/MagicSpell.java +++ b/src/main/java/com/questhelper/spells/MagicSpell.java @@ -26,7 +26,10 @@ */ package com.questhelper.spells; +import com.questhelper.requirements.Requirement; +import com.questhelper.requirements.SpellRequirement; import com.questhelper.requirements.util.Spellbook; +import java.util.List; /** * Represents a magic spell that can be cast by a player. @@ -64,4 +67,9 @@ public interface MagicSpell * @return the {@link Spellbook} this spell is contained within. */ Spellbook getSpellbook(); + + /** + * @return a new {@link SpellRequirement} for a single cast of this spell. + */ + SpellRequirement getSpellRequirement(); } diff --git a/src/main/java/com/questhelper/spells/Rune.java b/src/main/java/com/questhelper/spells/Rune.java index a7fe41e210..0054d31c33 100644 --- a/src/main/java/com/questhelper/spells/Rune.java +++ b/src/main/java/com/questhelper/spells/Rune.java @@ -118,4 +118,21 @@ public ItemRequirements getRunes(int quantity) } return null; } + + + public static Rune getByItemID(int itemID) + { + for (Rune rune : Rune.values()) + { + if (rune.runes != null && rune.runes.contains(itemID)) + { + return rune; + } + if (rune.staves != null && rune.staves.contains(itemID)) + { + return rune; + } + } + return null; + } } diff --git a/src/main/java/com/questhelper/spells/StandardSpell.java b/src/main/java/com/questhelper/spells/StandardSpell.java index ac36f8e473..bb70568943 100644 --- a/src/main/java/com/questhelper/spells/StandardSpell.java +++ b/src/main/java/com/questhelper/spells/StandardSpell.java @@ -26,7 +26,7 @@ */ package com.questhelper.spells; -import com.questhelper.requirements.Requirement; +import com.questhelper.requirements.SpellRequirement; import com.questhelper.requirements.util.Spellbook; import java.util.Locale; import java.util.function.UnaryOperator; @@ -139,7 +139,8 @@ public enum StandardSpell implements MagicSpell this.operator = operator; } - public Requirement getRequirements() + @Override + public SpellRequirement getSpellRequirement() { return operator.apply(StandardSpellBuilder.builder(this)).build(); } diff --git a/src/main/java/com/questhelper/spells/StandardSpellBuilder.java b/src/main/java/com/questhelper/spells/StandardSpellBuilder.java index 1a35f7ab96..1ac5c58cf2 100644 --- a/src/main/java/com/questhelper/spells/StandardSpellBuilder.java +++ b/src/main/java/com/questhelper/spells/StandardSpellBuilder.java @@ -28,15 +28,19 @@ import com.questhelper.requirements.ItemRequirement; import com.questhelper.requirements.ItemRequirements; -import com.questhelper.requirements.util.LogicType; +import com.questhelper.requirements.Requirement; +import com.questhelper.requirements.SpellRequirement; import java.util.Arrays; +import java.util.HashMap; import java.util.LinkedList; import java.util.List; +import java.util.Map; public class StandardSpellBuilder { private final MagicSpell spell; - private final List requirementList = new LinkedList<>(); + private final List requirementList = new LinkedList<>(); + private final Map runeList = new HashMap<>(); private StandardSpellBuilder(MagicSpell spell) { @@ -63,7 +67,7 @@ public static StandardSpellBuilder builder(MagicSpell spell) */ public StandardSpellBuilder rune(int quantity, Rune rune) { - requirementList.add(rune.getRunes(quantity)); + runeList.put(rune, quantity); return this; } @@ -118,9 +122,9 @@ public StandardSpellBuilder item(boolean equipped, Integer... itemIDs) /** * @return a new {@link ItemRequirements} containing all this spell information */ - public ItemRequirements build() + public SpellRequirement build() { - return new ItemRequirements(LogicType.AND, spell.getName(), requirementList); + return new SpellRequirement(spell, runeList, requirementList); } } From 93e77e64e117227550a37c8c5dfeead278566f57 Mon Sep 17 00:00:00 2001 From: Senmori Date: Fri, 5 Feb 2021 21:09:40 -0500 Subject: [PATCH 09/55] Add support for teleportation tablets. Signed-off-by: Senmori --- .../requirements/SpellRequirement.java | 35 +++++++++++++++++-- .../com/questhelper/spells/MagicSpell.java | 4 +-- .../com/questhelper/spells/StandardSpell.java | 22 ++++++++---- .../spells/StandardSpellBuilder.java | 26 +++++++++++--- 4 files changed, 71 insertions(+), 16 deletions(-) diff --git a/src/main/java/com/questhelper/requirements/SpellRequirement.java b/src/main/java/com/questhelper/requirements/SpellRequirement.java index 2dddba6c7e..63ff9e8350 100644 --- a/src/main/java/com/questhelper/requirements/SpellRequirement.java +++ b/src/main/java/com/questhelper/requirements/SpellRequirement.java @@ -46,7 +46,9 @@ public class SpellRequirement extends ItemRequirement @Getter private final MagicSpell spell; + private boolean hasTabletItem = false; private int numberOfCasts = 1; + private ItemRequirement tabletRequirement; private final List requirements; private final List runeRequirements = new ArrayList<>(); @@ -67,6 +69,11 @@ public void setNumberOfCasts(int numberOfCasts) setupRuneRequirements(this.numberOfCasts); } + public void setTablet(int itemID) + { + this.tabletRequirement = new ItemRequirement("", itemID); + } + @Override public void setDisplayItemId(Integer displayItemId) { @@ -113,19 +120,19 @@ private List buildAllIds(List requirements) @Override public String getDisplayText() { - return spell.getName(); + return hasTabletItem ? tabletRequirement.getDisplayText() : spell.getName(); } @Override public boolean isActualItem() { - return false; + return hasTabletItem; } @Override public boolean showQuantity() { - return false; + return true; } @Override @@ -151,6 +158,28 @@ public Color getColorConsideringBank(Client client, boolean checkConsideringSlot @Override public boolean check(Client client) { + updateInternalRequirements(client); return super.check(client); } + + @Override + public boolean check(Client client, boolean checkConsideringSlotRestrictions) + { + updateInternalRequirements(client); + return super.check(client, checkConsideringSlotRestrictions); + } + + /** This is not for deciding if the player meets this SpellRequirement. This is only for the internal state of this requirement. */ + private void updateInternalRequirements(Client client) + { + if (this.tabletRequirement.getName() == null || this.tabletRequirement.getName().isEmpty()) + { + int tabletID = tabletRequirement.getId(); + this.tabletRequirement = new ItemRequirement(client.getItemDefinition(tabletID).getName(), tabletID); + } + if (tabletRequirement != null) + { + hasTabletItem = tabletRequirement.check(client); + } + } } diff --git a/src/main/java/com/questhelper/spells/MagicSpell.java b/src/main/java/com/questhelper/spells/MagicSpell.java index e54d8ebbb9..1998ce588e 100644 --- a/src/main/java/com/questhelper/spells/MagicSpell.java +++ b/src/main/java/com/questhelper/spells/MagicSpell.java @@ -26,10 +26,8 @@ */ package com.questhelper.spells; -import com.questhelper.requirements.Requirement; import com.questhelper.requirements.SpellRequirement; import com.questhelper.requirements.util.Spellbook; -import java.util.List; /** * Represents a magic spell that can be cast by a player. @@ -72,4 +70,6 @@ public interface MagicSpell * @return a new {@link SpellRequirement} for a single cast of this spell. */ SpellRequirement getSpellRequirement(); + + SpellRequirement getSpellRequirement(int numberOfCasts); } diff --git a/src/main/java/com/questhelper/spells/StandardSpell.java b/src/main/java/com/questhelper/spells/StandardSpell.java index bb70568943..394eab7d33 100644 --- a/src/main/java/com/questhelper/spells/StandardSpell.java +++ b/src/main/java/com/questhelper/spells/StandardSpell.java @@ -57,33 +57,33 @@ public enum StandardSpell implements MagicSpell LOW_LVL_ALCHEMY(25, 18, 21, b -> b.rune(NATURE).rune(3, FIRE)), WATER_BOLT(26, 19, 23, b -> b.rune(CHAOS).rune(2, WATER).rune(2, AIR)), ENCHANT_PEARL_BOLT(358, 8, 24, b -> b.rune(COSMIC).rune(2, WATER)), - VARROCK_TELEPORT(27, 20, 25, b -> b.rune(LAW).rune(FIRE).rune(3, AIR)), + VARROCK_TELEPORT(27, 20, 25, b -> b.rune(LAW).rune(FIRE).rune(3, AIR).tablet(ItemID.VARROCK_TELEPORT)), ENCHANT_LVL_2(28, 21, 27, b -> b.rune(COSMIC).rune(3, AIR)), ENCHANT_EMERALD_BOLT(358, 8, 27, b -> b.rune(NATURE).rune(COSMIC).rune(3, AIR)), EARTH_BOLT(29, 22, 9, b -> b.rune(CHAOS).rune(EARTH).rune(3, AIR)), ENCHANT_RED_TOPAZ_BOLT(358, 8, 29, b -> b.rune(CHAOS).rune(2, FIRE)), - LUMBRIDGE_TELEPORT(30, 23, 31, b -> b.rune(LAW).rune(EARTH).rune(3, AIR)), + LUMBRIDGE_TELEPORT(30, 23, 31, b -> b.rune(LAW).rune(EARTH).rune(3, AIR).tablet(ItemID.LUMBRIDGE_TELEPORT)), TELEKINETIC_GRAB(31, 24, 33, b -> b.rune(LAW).rune(AIR)), FIRE_BOLT(32, 25, 35, b -> b.rune(CHAOS).rune(3, AIR).rune(4, FIRE)), - FALADOR_TELEPORT(33, 26, 37, b -> b.rune(LAW).rune(WATER).rune(3, AIR)), + FALADOR_TELEPORT(33, 26, 37, b -> b.rune(LAW).rune(WATER).rune(3, AIR).tablet(ItemID.FALADOR_TELEPORT)), CRUMBLE_UNDEAD(34, 27, 39, b -> b.rune(CHAOS).rune(2, EARTH).rune(2, AIR)), - TELEPORT_TO_HOUSE(355, 28, 40, b -> b.rune(LAW).rune(EARTH).rune(AIR)), + TELEPORT_TO_HOUSE(355, 28, 40, b -> b.rune(LAW).rune(EARTH).rune(AIR).tablet(ItemID.TELEPORT_TO_HOUSE)), WIND_BLAST(35, 29, 41, b -> b.rune(DEATH).rune(3, AIR)), SUPERHEAT_ITEM(36, 30, 43, b -> b.rune(NATURE).rune(4, FIRE)), - CAMELOT_TELEPORT(37, 31, 45, b -> b.rune(LAW).rune(5, AIR)), + CAMELOT_TELEPORT(37, 31, 45, b -> b.rune(LAW).rune(5, AIR).tablet(ItemID.CAMELOT_TELEPORT)), WATER_BLAST(38, 32, 47, b -> b.rune(DEATH).rune(3, AIR).rune(3, WATER)), ENCHANT_LVL_3(39, 33, 49, b -> b.rune(COSMIC).rune(5, FIRE)), ENCHANT_RUBY_BOLT(358, 8, 49, b -> b.rune(COSMIC).rune(BLOOD).rune(5, FIRE)), IBAN_BLAST(53, 34, 50, b -> b.rune(DEATH).rune(5, FIRE).item(true, ItemID.IBANS_STAFF, ItemID.IBANS_STAFF_U)), SNARE(320, 35, 50, b -> b.rune(3, NATURE).rune(4, WATER).rune(4, EARTH)), MAGIC_DART(324, 36, 50, b -> b.rune(DEATH).rune(4, MIND)), - ARDOUGNE_TELEPORT(54, 37, 51, b -> b.rune(2, LAW).rune(2, WATER)), //TODO: QUEST REQ -> PLAGUE CITY + ARDOUGNE_TELEPORT(54, 37, 51, b -> b.rune(2, LAW).rune(2, WATER).tablet(ItemID.ARDOUGNE_TELEPORT)), //TODO: QUEST REQ -> PLAGUE CITY EARTH_BLAST(40, 38, 43, b -> b.rune(DEATH).rune(3, AIR).rune(4, EARTH)), HIGH_LVL_ALCHEMY(41, 39, 55, b -> b.rune(NATURE).rune(5, FIRE)), CHARGE_WATER_ORB(42, 40, 56, b -> b.rune(3, COSMIC).rune(30, WATER).item(ItemID.UNPOWERED_ORB)), ENCHANT_LVL_4(43, 41, 57, b -> b.rune(COSMIC).rune(10, EARTH)), ENCHANT_DIAMOND_BOLT(358, 8, 57, b -> b.rune(COSMIC).rune(2, LAW).rune(10, EARTH)), - WATCHTOWER_TELEPORT(55, 42, 58, b -> b.rune(2, LAW).rune(2, EARTH)), + WATCHTOWER_TELEPORT(55, 42, 58, b -> b.rune(2, LAW).rune(2, EARTH).tablet(ItemID.WATCHTOWER_TELEPORT)), FIRE_BLAST(44, 43, 59, b -> b.rune(DEATH).rune(4, AIR).rune(5, FIRE)), CHARGE_EARTH_ORB(45, 44, 60, b -> b.rune(3, COSMIC).rune(30, EARTH).item(ItemID.UNPOWERED_ORB)), BONES_TO_PEACHES(354, 45, 60, b -> b.rune(2, NATURE).rune(2, EARTH).rune(4, WATER)), //TODO: MAGE TRAINING ARENA @@ -145,6 +145,14 @@ public SpellRequirement getSpellRequirement() return operator.apply(StandardSpellBuilder.builder(this)).build(); } + @Override + public SpellRequirement getSpellRequirement(int numberOfCasts) + { + SpellRequirement requirement = getSpellRequirement(); + requirement.setNumberOfCasts(numberOfCasts); + return requirement; + } + @Override public String getName() { diff --git a/src/main/java/com/questhelper/spells/StandardSpellBuilder.java b/src/main/java/com/questhelper/spells/StandardSpellBuilder.java index 1ac5c58cf2..d6cbe7a3a9 100644 --- a/src/main/java/com/questhelper/spells/StandardSpellBuilder.java +++ b/src/main/java/com/questhelper/spells/StandardSpellBuilder.java @@ -39,8 +39,9 @@ public class StandardSpellBuilder { private final MagicSpell spell; - private final List requirementList = new LinkedList<>(); + private final List requirements = new LinkedList<>(); private final Map runeList = new HashMap<>(); + private int tabletItemID = -1; private StandardSpellBuilder(MagicSpell spell) { @@ -91,7 +92,7 @@ public StandardSpellBuilder rune(Rune rune) */ public StandardSpellBuilder item(int quantity, int itemID) { - requirementList.add(new ItemRequirement("", itemID, quantity)); + requirements.add(new ItemRequirement("", itemID, quantity)); return this; } @@ -115,7 +116,19 @@ public StandardSpellBuilder item(int itemID) */ public StandardSpellBuilder item(boolean equipped, Integer... itemIDs) { - requirementList.add(new ItemRequirement("", Arrays.asList(itemIDs), 1, equipped)); + requirements.add(new ItemRequirement("", Arrays.asList(itemIDs), 1, equipped)); + return this; + } + + /** + * Set the tablet that can be used to cast this spell. + * + * @param itemID the tablet item id + * @return this + */ + public StandardSpellBuilder tablet(int itemID) + { + this.tabletItemID = itemID; return this; } @@ -124,7 +137,12 @@ public StandardSpellBuilder item(boolean equipped, Integer... itemIDs) */ public SpellRequirement build() { - return new SpellRequirement(spell, runeList, requirementList); + SpellRequirement requirement = new SpellRequirement(spell, runeList, requirements); + if (tabletItemID != -1) + { + requirement.setTablet(tabletItemID); + } + return requirement; } } From 72f16780e831e817d84d49d29e9ec04bf6ec25fb Mon Sep 17 00:00:00 2001 From: Senmori Date: Fri, 5 Feb 2021 21:12:23 -0500 Subject: [PATCH 10/55] Reorganize StandardSpell fields so they match their implementation. Signed-off-by: Senmori --- src/main/java/com/questhelper/spells/StandardSpell.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/com/questhelper/spells/StandardSpell.java b/src/main/java/com/questhelper/spells/StandardSpell.java index 394eab7d33..32936f6b78 100644 --- a/src/main/java/com/questhelper/spells/StandardSpell.java +++ b/src/main/java/com/questhelper/spells/StandardSpell.java @@ -120,8 +120,8 @@ public enum StandardSpell implements MagicSpell FIRE_SURGE(365, 74, 95, b -> b.rune(WRATH).rune(7, AIR).rune(10, FIRE)), ; + private final int groupID = 218; private final int spriteID; - private final int groupID; private final int widgetID; private final int requiredMagicLevel; private final UnaryOperator operator; @@ -133,7 +133,6 @@ public enum StandardSpell implements MagicSpell StandardSpell(int spriteID, int widgetID, int requiredMagicLevel, UnaryOperator operator) { this.spriteID = spriteID; - this.groupID = 218; this.widgetID = widgetID; this.requiredMagicLevel = requiredMagicLevel; this.operator = operator; From e9cabb3bf020e92c15991dcbd5829c83299300db Mon Sep 17 00:00:00 2001 From: Senmori Date: Fri, 5 Feb 2021 21:24:15 -0500 Subject: [PATCH 11/55] Make ItemRequirement name non-final so we can change them if there is an empty name for a requirement we might display. Signed-off-by: Senmori --- .../com/questhelper/requirements/ItemRequirement.java | 2 +- .../com/questhelper/requirements/SpellRequirement.java | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/questhelper/requirements/ItemRequirement.java b/src/main/java/com/questhelper/requirements/ItemRequirement.java index 93fa377ff2..017d366e5e 100644 --- a/src/main/java/com/questhelper/requirements/ItemRequirement.java +++ b/src/main/java/com/questhelper/requirements/ItemRequirement.java @@ -42,7 +42,7 @@ public class ItemRequirement extends AbstractRequirement @Getter private final int id; - private final String name; + protected String name; @Setter @Getter diff --git a/src/main/java/com/questhelper/requirements/SpellRequirement.java b/src/main/java/com/questhelper/requirements/SpellRequirement.java index 63ff9e8350..6c752cb946 100644 --- a/src/main/java/com/questhelper/requirements/SpellRequirement.java +++ b/src/main/java/com/questhelper/requirements/SpellRequirement.java @@ -181,5 +181,13 @@ private void updateInternalRequirements(Client client) { hasTabletItem = tabletRequirement.check(client); } + List itemRequirements = getItemRequirements(requirements); + for (ItemRequirement item : itemRequirements) + { + if (item.getName() == null || item.getName().isEmpty()) + { + item.name = client.getItemDefinition(item.getId()).getName(); + } + } } } From 3377004fbc8bb3e2cfa0f63ba9259a3b5e6b7ccd Mon Sep 17 00:00:00 2001 From: Senmori Date: Sat, 6 Feb 2021 12:33:50 -0500 Subject: [PATCH 12/55] Renamed enchants to match their in-game names. Signed-off-by: Senmori --- .../java/com/questhelper/spells/StandardSpell.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/questhelper/spells/StandardSpell.java b/src/main/java/com/questhelper/spells/StandardSpell.java index 32936f6b78..f8104c3a3f 100644 --- a/src/main/java/com/questhelper/spells/StandardSpell.java +++ b/src/main/java/com/questhelper/spells/StandardSpell.java @@ -44,7 +44,7 @@ public enum StandardSpell implements MagicSpell CONFUSE(16, 7, 3, b -> b.rune(BODY).rune(2, EARTH).rune(3, WATER)), ENCHANT_OPAL_BOLT(358, 8, 4, b -> b.rune(COSMIC).rune(2, AIR)), WATER_STRIKE(17, 9, 5, b -> b.rune(AIR).rune(WATER).rune(MIND)), - ENCHANT_LVL_1(18, 10, 7, b -> b.rune(WATER).rune(COSMIC)), + LVL_1_ENCHANT(18, 10, 7, b -> b.rune(WATER).rune(COSMIC)), ENCHANT_SAPPHIRE_BOLT(358, 8, 9, b -> b.rune(WATER).rune(COSMIC)), EARTH_STRIKE(19, 11, 9, b -> b.rune(AIR).rune(MIND).rune(2, EARTH)), WEAKEN(20, 12, 11, b -> b.rune(BODY).rune(2, EARTH).rune(3, WATER)), @@ -58,7 +58,7 @@ public enum StandardSpell implements MagicSpell WATER_BOLT(26, 19, 23, b -> b.rune(CHAOS).rune(2, WATER).rune(2, AIR)), ENCHANT_PEARL_BOLT(358, 8, 24, b -> b.rune(COSMIC).rune(2, WATER)), VARROCK_TELEPORT(27, 20, 25, b -> b.rune(LAW).rune(FIRE).rune(3, AIR).tablet(ItemID.VARROCK_TELEPORT)), - ENCHANT_LVL_2(28, 21, 27, b -> b.rune(COSMIC).rune(3, AIR)), + LVL_2_ENCHANT(28, 21, 27, b -> b.rune(COSMIC).rune(3, AIR)), ENCHANT_EMERALD_BOLT(358, 8, 27, b -> b.rune(NATURE).rune(COSMIC).rune(3, AIR)), EARTH_BOLT(29, 22, 9, b -> b.rune(CHAOS).rune(EARTH).rune(3, AIR)), ENCHANT_RED_TOPAZ_BOLT(358, 8, 29, b -> b.rune(CHAOS).rune(2, FIRE)), @@ -72,7 +72,7 @@ public enum StandardSpell implements MagicSpell SUPERHEAT_ITEM(36, 30, 43, b -> b.rune(NATURE).rune(4, FIRE)), CAMELOT_TELEPORT(37, 31, 45, b -> b.rune(LAW).rune(5, AIR).tablet(ItemID.CAMELOT_TELEPORT)), WATER_BLAST(38, 32, 47, b -> b.rune(DEATH).rune(3, AIR).rune(3, WATER)), - ENCHANT_LVL_3(39, 33, 49, b -> b.rune(COSMIC).rune(5, FIRE)), + LVL_3_ENCHANT(39, 33, 49, b -> b.rune(COSMIC).rune(5, FIRE)), ENCHANT_RUBY_BOLT(358, 8, 49, b -> b.rune(COSMIC).rune(BLOOD).rune(5, FIRE)), IBAN_BLAST(53, 34, 50, b -> b.rune(DEATH).rune(5, FIRE).item(true, ItemID.IBANS_STAFF, ItemID.IBANS_STAFF_U)), SNARE(320, 35, 50, b -> b.rune(3, NATURE).rune(4, WATER).rune(4, EARTH)), @@ -81,7 +81,7 @@ public enum StandardSpell implements MagicSpell EARTH_BLAST(40, 38, 43, b -> b.rune(DEATH).rune(3, AIR).rune(4, EARTH)), HIGH_LVL_ALCHEMY(41, 39, 55, b -> b.rune(NATURE).rune(5, FIRE)), CHARGE_WATER_ORB(42, 40, 56, b -> b.rune(3, COSMIC).rune(30, WATER).item(ItemID.UNPOWERED_ORB)), - ENCHANT_LVL_4(43, 41, 57, b -> b.rune(COSMIC).rune(10, EARTH)), + LVL_4_ENCHANT(43, 41, 57, b -> b.rune(COSMIC).rune(10, EARTH)), ENCHANT_DIAMOND_BOLT(358, 8, 57, b -> b.rune(COSMIC).rune(2, LAW).rune(10, EARTH)), WATCHTOWER_TELEPORT(55, 42, 58, b -> b.rune(2, LAW).rune(2, EARTH).tablet(ItemID.WATCHTOWER_TELEPORT)), FIRE_BLAST(44, 43, 59, b -> b.rune(DEATH).rune(4, AIR).rune(5, FIRE)), @@ -97,7 +97,7 @@ public enum StandardSpell implements MagicSpell WATER_WAVE(48, 53, 54, b -> b.rune(BLOOD).rune(5, AIR).rune(7, WATER)), CHARGE_AIR_ORB(49, 54, 66, b -> b.rune(3, COSMIC).rune(30, AIR).item(ItemID.UNPOWERED_ORB)), VULNERABILITY(56, 55, 66, b -> b.rune(SOUL).rune(5, WATER).rune(5, EARTH)), - ENCHANT_LVL_5(50, 56, 68, b -> b.rune(COSMIC).rune(15, WATER).rune(15, EARTH)), + LVL_5_ENCHANT(50, 56, 68, b -> b.rune(COSMIC).rune(15, WATER).rune(15, EARTH)), ENCHANT_DRAGONSTONE_BOLT(358, 8, 68, b -> b.rune(SOUL).rune(COSMIC).rune(12, EARTH)), KOUREND_CASTLE_TELEPORT(360, 57, 69, b -> b.rune(2, SOUL).rune(2, LAW).rune(4,WATER).rune(5, FIRE)), //TODO: UNLOCKED VIA BOOK EARTH_WAVE(51, 58, 70, b -> b.rune(BLOOD).rune(5, AIR).rune(7, EARTH)), @@ -112,11 +112,11 @@ public enum StandardSpell implements MagicSpell WATER_SURGE(363, 67, 85, b -> b.rune(WRATH).rune(7, AIR).rune(10, WATER)), TELE_BLOCK(352, 68, 85, b -> b.rune(LAW).rune(DEATH).rune(CHAOS)), //TODO: IN WILDERNESS TELEPORT_TO_TARGET(359, 69, 85, b -> b.rune(LAW).rune(DEATH).rune(CHAOS)), //TODO: HAVE READ TARGET TELEPORT SCROLL - ENCHANT_LVL_6(353, 70, 87, b -> b.rune(COSMIC).rune(20, FIRE).rune(20, EARTH)), + LVL_6_ENCHANT(353, 70, 87, b -> b.rune(COSMIC).rune(20, FIRE).rune(20, EARTH)), ENCHANT_ONYX_BOLT(358, 8, 87, b -> b.rune(DEATH).rune(COSMIC).rune(20, FIRE)), TELEOTHER_CAMELOT(351, 71, 90, b -> b.rune(LAW).rune(2, SOUL)), EARTH_SURGE(364, 72, 90, b -> b.rune(WRATH).rune(7, AIR).rune(10, EARTH)), - ENCHANT_LVL_7(361, 73, 93, b -> b.rune(COSMIC).rune(20, SOUL).rune(20, BLOOD)), + LVL_7_ENCHANT(361, 73, 93, b -> b.rune(COSMIC).rune(20, SOUL).rune(20, BLOOD)), FIRE_SURGE(365, 74, 95, b -> b.rune(WRATH).rune(7, AIR).rune(10, FIRE)), ; From a2d7efb4589ccdcf98f14a256f1695660b5ae971 Mon Sep 17 00:00:00 2001 From: Senmori Date: Sat, 6 Feb 2021 12:34:07 -0500 Subject: [PATCH 13/55] Fix some merge issues. Add Spellbook requirement Signed-off-by: Senmori --- src/main/java/com/questhelper/ItemCollections.java | 2 +- .../java/com/questhelper/requirements/ItemRequirement.java | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/questhelper/ItemCollections.java b/src/main/java/com/questhelper/ItemCollections.java index fb54697f8c..fd1372714c 100644 --- a/src/main/java/com/questhelper/ItemCollections.java +++ b/src/main/java/com/questhelper/ItemCollections.java @@ -270,7 +270,7 @@ public class ItemCollections ItemID.STEAM_RUNE ); - private static final List waterStaff = Arrays.asList( + private static final List waterStaff = QuestUtil.toLinkedList( ItemID.KODAI_WAND, ItemID.WATER_BATTLESTAFF, ItemID.MYSTIC_WATER_STAFF, diff --git a/src/main/java/com/questhelper/requirements/ItemRequirement.java b/src/main/java/com/questhelper/requirements/ItemRequirement.java index fe951dc60b..7efaaf8536 100644 --- a/src/main/java/com/questhelper/requirements/ItemRequirement.java +++ b/src/main/java/com/questhelper/requirements/ItemRequirement.java @@ -28,8 +28,12 @@ import com.questhelper.requirements.util.InventorySlots; import java.awt.Color; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; import lombok.Getter; import lombok.Setter; import net.runelite.api.Client; From 42500ff27f34ad4bd553ccacf7ebacf58f15df15 Mon Sep 17 00:00:00 2001 From: Senmori Date: Sat, 6 Feb 2021 12:34:15 -0500 Subject: [PATCH 14/55] Fix some merge issues. Add Spellbook requirement Signed-off-by: Senmori --- src/main/java/com/questhelper/requirements/SpellRequirement.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/questhelper/requirements/SpellRequirement.java b/src/main/java/com/questhelper/requirements/SpellRequirement.java index 6c752cb946..85a8ccc21b 100644 --- a/src/main/java/com/questhelper/requirements/SpellRequirement.java +++ b/src/main/java/com/questhelper/requirements/SpellRequirement.java @@ -58,6 +58,7 @@ public SpellRequirement(MagicSpell spell, Map runesPerCastMap, Li super(spell.getName(), -1); this.spell = spell; this.requirements = new LinkedList<>(requirements); // make it mutable for now + this.requirements.add(new SpellbookRequirement(spell.getSpellbook())); this.runesPerCastMap = runesPerCastMap; registerItemRequirements(this.requirements); setupRuneRequirements(1); From 6de6e0a30f5707c53941b9cbbd7207968fadb472 Mon Sep 17 00:00:00 2001 From: Senmori Date: Sat, 6 Feb 2021 19:11:31 -0500 Subject: [PATCH 15/55] Bank icons are present, just not in the correct order. Also the coloring is wrong. Signed-off-by: Senmori --- .../java/com/questhelper/ItemCollections.java | 1 + .../banktab/QuestHelperBankTagService.java | 28 +++- .../questhelpers/BankItemHolder.java | 37 +++++ .../quests/makinghistory/MakingHistory.java | 3 +- .../requirements/ItemRequirement.java | 4 +- .../requirements/ItemRequirements.java | 5 + .../requirements/RuneRequirement.java | 110 +++++++++++++ .../requirements/SpellRequirement.java | 155 +++++++++++++++--- .../requirements/SpellbookRequirement.java | 43 ++--- .../java/com/questhelper/spells/Rune.java | 21 ++- 10 files changed, 346 insertions(+), 61 deletions(-) create mode 100644 src/main/java/com/questhelper/questhelpers/BankItemHolder.java create mode 100644 src/main/java/com/questhelper/requirements/RuneRequirement.java diff --git a/src/main/java/com/questhelper/ItemCollections.java b/src/main/java/com/questhelper/ItemCollections.java index fd1372714c..f627b09ab8 100644 --- a/src/main/java/com/questhelper/ItemCollections.java +++ b/src/main/java/com/questhelper/ItemCollections.java @@ -270,6 +270,7 @@ public class ItemCollections ItemID.STEAM_RUNE ); + @Getter private static final List waterStaff = QuestUtil.toLinkedList( ItemID.KODAI_WAND, ItemID.WATER_BATTLESTAFF, diff --git a/src/main/java/com/questhelper/banktab/QuestHelperBankTagService.java b/src/main/java/com/questhelper/banktab/QuestHelperBankTagService.java index 9e7ceafcd2..cf0e303a01 100644 --- a/src/main/java/com/questhelper/banktab/QuestHelperBankTagService.java +++ b/src/main/java/com/questhelper/banktab/QuestHelperBankTagService.java @@ -26,9 +26,9 @@ import com.questhelper.QuestHelperPlugin; import com.questhelper.panel.PanelDetails; +import com.questhelper.questhelpers.BankItemHolder; import com.questhelper.requirements.ItemRequirement; import com.questhelper.requirements.ItemRequirements; -import com.questhelper.requirements.Requirement; import com.questhelper.requirements.util.LogicType; import java.util.ArrayList; import java.util.Collection; @@ -36,8 +36,8 @@ import java.util.List; import java.util.Objects; import java.util.stream.Collectors; -import javax.annotation.Nonnull; import javax.inject.Inject; +import lombok.val; import net.runelite.api.InventoryID; import net.runelite.api.ItemContainer; @@ -130,15 +130,31 @@ private void getItemsFromRequirement(BankTabItems pluginItems, ItemRequirement i getItemsFromRequirement(pluginItems, match); } } + else if (itemRequirement instanceof BankItemHolder) + { + BankItemHolder holder = (BankItemHolder) itemRequirement; + plugin.getClientThread().invoke(() -> { + val reqs = holder.getRequirements(plugin.getClient(), false, null); + makeBankHolderItems(reqs, pluginItems); // callback because we can't halt on the client thread + }); + } else { - if (itemRequirement.getDisplayItemId() != null) + makeBankHolderItems(Collections.singletonList(itemRequirement), pluginItems); + } + } + + private void makeBankHolderItems(List requirements, BankTabItems pluginItems) + { + for (ItemRequirement req : requirements) + { + if (req.getDisplayItemId() != null) { - pluginItems.addItems(new BankTabItem(itemRequirement)); + pluginItems.addItems(new BankTabItem(req)); } - else if (!itemRequirement.getDisplayItemIds().contains(-1)) + else if (!req.getDisplayItemIds().contains(-1)) { - pluginItems.addItems(makeBankTabItem(itemRequirement)); + pluginItems.addItems(makeBankTabItem(req)); } } } diff --git a/src/main/java/com/questhelper/questhelpers/BankItemHolder.java b/src/main/java/com/questhelper/questhelpers/BankItemHolder.java new file mode 100644 index 0000000000..f91cc889ff --- /dev/null +++ b/src/main/java/com/questhelper/questhelpers/BankItemHolder.java @@ -0,0 +1,37 @@ +/* + * + * * Copyright (c) 2021, Senmori + * * 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 com.questhelper.questhelpers; + +import com.questhelper.requirements.ItemRequirement; +import java.util.List; +import net.runelite.api.Client; +import net.runelite.api.Item; + +public interface BankItemHolder +{ + List getRequirements(Client client, boolean checkConsideringSlotRestrictions, Item[] bankItems); +} diff --git a/src/main/java/com/questhelper/quests/makinghistory/MakingHistory.java b/src/main/java/com/questhelper/quests/makinghistory/MakingHistory.java index a8e12798f6..b9cad3aff9 100644 --- a/src/main/java/com/questhelper/quests/makinghistory/MakingHistory.java +++ b/src/main/java/com/questhelper/quests/makinghistory/MakingHistory.java @@ -28,6 +28,7 @@ import com.questhelper.QuestHelperQuest; import com.questhelper.requirements.QuestRequirement; import com.questhelper.requirements.Requirement; +import com.questhelper.spells.StandardSpell; import com.questhelper.steps.DetailedQuestStep; import com.questhelper.steps.DigStep; import com.questhelper.requirements.conditional.Conditions; @@ -128,7 +129,7 @@ public void setupItemRequirements() spade = new ItemRequirement("Spade", ItemID.SPADE); saphAmulet = new ItemRequirement("Sapphire amulet", ItemID.SAPPHIRE_AMULET); ghostSpeakAmulet = new ItemRequirement("Ghostspeak amulet", ItemID.GHOSTSPEAK_AMULET, 1, true); - ardougneTeleport = new ItemRequirement("Teleports to Ardougne", ItemID.ARDOUGNE_TELEPORT, 3); + ardougneTeleport = StandardSpell.ARDOUGNE_TELEPORT.getSpellRequirement(3); ectophial = new ItemRequirement("Ectophial, or method of getting to Port Phasmatys", ItemID.ECTOPHIAL); ringOfDueling = new ItemRequirement("Ring of Dueling", ItemID.RING_OF_DUELING8); enchantedKey = new ItemRequirement("Enchanted key", ItemID.ENCHANTED_KEY); diff --git a/src/main/java/com/questhelper/requirements/ItemRequirement.java b/src/main/java/com/questhelper/requirements/ItemRequirement.java index 7efaaf8536..9656d0e7de 100644 --- a/src/main/java/com/questhelper/requirements/ItemRequirement.java +++ b/src/main/java/com/questhelper/requirements/ItemRequirement.java @@ -34,6 +34,7 @@ import java.util.Objects; import java.util.stream.Collectors; import java.util.stream.Stream; +import lombok.AccessLevel; import lombok.Getter; import lombok.Setter; import net.runelite.api.Client; @@ -47,7 +48,8 @@ public class ItemRequirement extends AbstractRequirement @Getter private final int id; - protected String name; + @Setter(AccessLevel.PROTECTED) + private String name; @Setter @Getter diff --git a/src/main/java/com/questhelper/requirements/ItemRequirements.java b/src/main/java/com/questhelper/requirements/ItemRequirements.java index 031ff46667..75ba8762e0 100644 --- a/src/main/java/com/questhelper/requirements/ItemRequirements.java +++ b/src/main/java/com/questhelper/requirements/ItemRequirements.java @@ -66,6 +66,11 @@ public ItemRequirements(LogicType logicType, String name, List this.logicType = logicType; } + protected ItemRequirements(LogicType logicType, String name) + { + super(name, -1); + } + @Override public boolean check(Client client) { diff --git a/src/main/java/com/questhelper/requirements/RuneRequirement.java b/src/main/java/com/questhelper/requirements/RuneRequirement.java new file mode 100644 index 0000000000..48962cd797 --- /dev/null +++ b/src/main/java/com/questhelper/requirements/RuneRequirement.java @@ -0,0 +1,110 @@ +/* + * + * * Copyright (c) 2021, Senmori + * * 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 com.questhelper.requirements; + +import com.questhelper.questhelpers.BankItemHolder; +import com.questhelper.requirements.util.InventorySlots; +import com.questhelper.requirements.util.LogicType; +import com.questhelper.spells.Rune; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.function.Predicate; +import lombok.Getter; +import net.runelite.api.Client; +import net.runelite.api.Item; + +@Getter +public class RuneRequirement extends ItemRequirements implements BankItemHolder +{ + private final Rune rune; + private ItemRequirements runeRequirements; + private int costPerCast; + private int requiredAmount; + public RuneRequirement(Rune rune, int costPerCast) + { + this(rune, costPerCast, 1); + } + + public RuneRequirement(Rune rune, int costPerCast, int numberOfCasts) + { + super(LogicType.OR, rune.getRuneName(), rune.getRunes(costPerCast * numberOfCasts).getItemRequirements()); + this.rune = rune; + this.costPerCast = costPerCast; + this.requiredAmount = costPerCast * numberOfCasts; + setNumberOfCasts(numberOfCasts); + } + + public void setNumberOfCasts(int numberOfCasts) + { + this.requiredAmount = costPerCast * numberOfCasts; + updateRequirements(numberOfCasts); + } + + private void updateRequirements(int numberOfCasts) + { + this.requiredAmount = costPerCast * numberOfCasts; + runeRequirements = null; + this.runeRequirements = rune.getRunes(this.requiredAmount); + getItemRequirements().clear(); + getItemRequirements().addAll(runeRequirements.getItemRequirements()); + } + + @Override + public List getAllIds() + { + return runeRequirements == null ? new ArrayList<>(rune.getItemID()) : runeRequirements.getAllIds(); + } + + @Override + public List getRequirements(Client client, boolean checkConsideringSlotRestrictions, Item[] bankItems) + { + List requirements = new LinkedList<>(); + List runes = rune.getRunes(); + int requiredAmount = this.requiredAmount; + if (clientHasRequiredItems(client, i -> runes.contains(i.getId()) && i.getQuantity() >= requiredAmount)) + { + return Collections.singletonList(new ItemRequirement(rune.getRuneName(), runes, requiredAmount)); + } + List staves = rune.getStaves(); + if (staves == null) + { + return requirements; + } + if (clientHasRequiredItems(client, i -> staves.contains(i.getId()))) + { + return Collections.singletonList(new ItemRequirement(rune.getRuneName(), staves, 1)); + } + return requirements; + } + + private boolean clientHasRequiredItems(Client client, Predicate predicate) + { + return InventorySlots.INVENTORY_SLOTS.contains(client, predicate) || InventorySlots.BANK.contains(client, predicate); + } +} diff --git a/src/main/java/com/questhelper/requirements/SpellRequirement.java b/src/main/java/com/questhelper/requirements/SpellRequirement.java index 85a8ccc21b..1783fcf3ce 100644 --- a/src/main/java/com/questhelper/requirements/SpellRequirement.java +++ b/src/main/java/com/questhelper/requirements/SpellRequirement.java @@ -26,32 +26,42 @@ */ package com.questhelper.requirements; +import com.questhelper.questhelpers.BankItemHolder; +import com.questhelper.questhelpers.QuestUtil; +import com.questhelper.requirements.util.InventorySlots; +import com.questhelper.requirements.util.LogicType; import com.questhelper.spells.MagicSpell; import com.questhelper.spells.Rune; import java.awt.Color; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.function.Predicate; import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.annotation.Nonnull; import lombok.Getter; import net.runelite.api.Client; +import net.runelite.api.InventoryID; import net.runelite.api.Item; +import net.runelite.api.ItemComposition; +import net.runelite.api.Skill; -public class SpellRequirement extends ItemRequirement +public class SpellRequirement extends ItemRequirement implements BankItemHolder { @Getter private final MagicSpell spell; private boolean hasTabletItem = false; private int numberOfCasts = 1; - private ItemRequirement tabletRequirement; + private ItemRequirement tabletRequirement = null; private final List requirements; - private final List runeRequirements = new ArrayList<>(); + private final List runeRequirements = new ArrayList<>(); private final Map runesPerCastMap; public SpellRequirement(MagicSpell spell, Map runesPerCastMap, List requirements) { @@ -59,9 +69,11 @@ public SpellRequirement(MagicSpell spell, Map runesPerCastMap, Li this.spell = spell; this.requirements = new LinkedList<>(requirements); // make it mutable for now this.requirements.add(new SpellbookRequirement(spell.getSpellbook())); + this.requirements.add(new SkillRequirement(Skill.MAGIC, spell.getRequiredMagicLevel())); this.runesPerCastMap = runesPerCastMap; - registerItemRequirements(this.requirements); - setupRuneRequirements(1); + registerAlternateItems(this.requirements); + setNumberOfCasts(1); + super.setDisplayItemId(-1); } public void setNumberOfCasts(int numberOfCasts) @@ -81,11 +93,6 @@ public void setDisplayItemId(Integer displayItemId) // Don't set so we always use the requirement ids for bank filters } - private void registerItemRequirements(List requirements) - { - getItemRequirements(requirements).forEach(item -> addAlternates(item.getAllIds())); - } - private void setupRuneRequirements(int numberOfCasts) { runeRequirements.clear(); @@ -93,7 +100,11 @@ private void setupRuneRequirements(int numberOfCasts) { Rune rune = entry.getKey(); int runesPerCast = entry.getValue(); - runeRequirements.add(rune.getRunes(runesPerCast * numberOfCasts)); + RuneRequirement runeReq = new RuneRequirement(rune, runesPerCast); + runeReq.setNumberOfCasts(numberOfCasts); + runeRequirements.add(runeReq); + List allIds = runeReq.getAllIds(); + alternateItems.addAll(allIds); } } @@ -105,6 +116,12 @@ private List getItemRequirements(List requirements .collect(Collectors.toList()); } + private void registerAlternateItems(List requirements) + { + alternateItems.clear(); + alternateItems.addAll(buildAllIds(requirements)); + } + private List buildAllIds(List requirements) { List itemRequirements = getItemRequirements(requirements); @@ -114,20 +131,20 @@ private List buildAllIds(List requirements) .map(ItemRequirement::getAllIds) .flatMap(Collection::stream) .distinct() - .collect(Collectors.toList()); + .collect(QuestUtil.collectToArrayList()); } @Nonnull @Override public String getDisplayText() { - return hasTabletItem ? tabletRequirement.getDisplayText() : spell.getName(); + return spell.getName(); //TODO: Re-evaluate when we show the spell name versus the tablet } @Override public boolean isActualItem() { - return hasTabletItem; + return false; // TODO: Redo when/if we determine when to show the tablet } @Override @@ -139,16 +156,16 @@ public boolean showQuantity() @Override public Color getColor(Client client) { - return check(client) ? Color.GREEN : Color.RED; + return meetsRequirements(client, false, null) ? Color.GREEN : Color.RED; } @Override public Color getColorConsideringBank(Client client, boolean checkConsideringSlotRestrictions, Item[] bankItems) { - Color color = this.check(client, checkConsideringSlotRestrictions) ? Color.GREEN : Color.RED; + Color color = meetsRequirements(client, checkConsideringSlotRestrictions, null) ? Color.GREEN : Color.RED; if (color == Color.RED && bankItems != null) { - if (check(client, false, bankItems)) + if (meetsRequirements(client, false, bankItems)) { color = Color.WHITE; } @@ -156,30 +173,92 @@ public Color getColorConsideringBank(Client client, boolean checkConsideringSlot return color; } + @Override + public List getDisplayItemIds() + { + // LinkedList so we can maintain what should get priority for displaying + List ids = new LinkedList<>(buildAllIds(requirements)); + runeRequirements.forEach(req -> alternateItems.addAll(req.getAllIds())); + if (tabletRequirement != null) + { + ids.add(tabletRequirement.getDisplayItemId()); + } + return ids; + } + @Override public boolean check(Client client) { - updateInternalRequirements(client); - return super.check(client); + return meetsRequirements(client, false, null); } @Override public boolean check(Client client, boolean checkConsideringSlotRestrictions) + { + return meetsRequirements(client, checkConsideringSlotRestrictions, null); + } + + @Override + public boolean check(Client client, boolean checkConsideringSlotRestrictions, Item[] items) + { + return meetsRequirements(client, checkConsideringSlotRestrictions, items); + } + + private boolean checkInventorySlot(InventorySlots slot, Client client, ItemRequirement requirement) + { + return slot.contains(client, i -> requirement.getAllIds().contains(i.getId()) && i.getQuantity() >= requirement.getQuantity()); + } + + private boolean meetsRequirements(Client client, boolean checkConsideringSlotRestrictions, Item[] items) { updateInternalRequirements(client); - return super.check(client, checkConsideringSlotRestrictions); + boolean itemRequirementsMet = itemRequirementsMet(requirements, client, checkConsideringSlotRestrictions, items); + boolean nonItemRequirementsMet = nonItemRequirementsMet(requirements, client); + boolean runeRequirementsMet = hasRunes(runeRequirements, client, checkConsideringSlotRestrictions, items); + boolean hasTabletItem = hasTabletItem(client); + return (nonItemRequirementsMet && itemRequirementsMet && runeRequirementsMet) || hasTabletItem; + } + + private boolean hasRunes(List runes, Client client, boolean checkWithSlotRestrictions, Item[] items) + { + return runes.stream().allMatch(req -> req.check(client, checkWithSlotRestrictions, items)); + } + + private boolean itemRequirementsMet(List requirements, Client client, boolean checkConsideringSlotRestrictions, Item[] items) + { + return getItemRequirements(requirements).stream().allMatch(req -> req.check(client, checkConsideringSlotRestrictions, items)); + } + + private boolean nonItemRequirementsMet(List requirements, Client client) + { + return requirements.stream() + .filter(((Predicate) ItemRequirement.class::isInstance).negate()) + .allMatch(req -> req.check(client)); + } + + private boolean hasTabletItem(Client client) + { + return tabletRequirement != null && tabletRequirement.check(client); } /** This is not for deciding if the player meets this SpellRequirement. This is only for the internal state of this requirement. */ private void updateInternalRequirements(Client client) { - if (this.tabletRequirement.getName() == null || this.tabletRequirement.getName().isEmpty()) - { - int tabletID = tabletRequirement.getId(); - this.tabletRequirement = new ItemRequirement(client.getItemDefinition(tabletID).getName(), tabletID); - } if (tabletRequirement != null) { + if (this.tabletRequirement.getName() == null || this.tabletRequirement.getName().isEmpty()) + { + if (tabletRequirement.getId() > -1) + { + ItemComposition tablet = client.getItemDefinition(tabletRequirement.getId()); + this.tabletRequirement = new ItemRequirement(client.getItemDefinition(tablet.getId()).getName(), tablet.getId()); + tabletRequirement.setDisplayItemId(tablet.getId()); + } + else + { + //TODO: throw error? + } + } hasTabletItem = tabletRequirement.check(client); } List itemRequirements = getItemRequirements(requirements); @@ -187,8 +266,32 @@ private void updateInternalRequirements(Client client) { if (item.getName() == null || item.getName().isEmpty()) { - item.name = client.getItemDefinition(item.getId()).getName(); + String name = client.getItemDefinition(item.getId()).getName(); + item.setName(name); } } } + + @Override + public List getRequirements(Client client, boolean checkConsideringSlotRestrictions, Item[] bankItems) + { + List bankTabItemRequirements = new LinkedList<>(); + if (tabletRequirement != null && tabletRequirement.check(client, checkConsideringSlotRestrictions, bankItems)) + { + bankTabItemRequirements.add(tabletRequirement); + } + else + { + List runeItemRequirements = runeRequirements.stream() + .map(req -> req.getRequirements(client, checkConsideringSlotRestrictions, bankItems)) + .flatMap(Collection::stream) + .collect(Collectors.toList()); + bankTabItemRequirements.addAll(runeItemRequirements); + } + List itemRequirements = getItemRequirements(this.requirements).stream() + .filter(req -> req.check(client, checkConsideringSlotRestrictions, bankItems)) + .collect(Collectors.toList()); + bankTabItemRequirements.addAll(itemRequirements); + return bankTabItemRequirements; + } } diff --git a/src/main/java/com/questhelper/requirements/SpellbookRequirement.java b/src/main/java/com/questhelper/requirements/SpellbookRequirement.java index f8717334d8..abc7e4d577 100644 --- a/src/main/java/com/questhelper/requirements/SpellbookRequirement.java +++ b/src/main/java/com/questhelper/requirements/SpellbookRequirement.java @@ -1,29 +1,32 @@ /* - * Copyright (c) 2020, Zoinkwiz - * All rights reserved. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: + * * Copyright (c) 2021, Senmori + * * 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. * - * 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 com.questhelper.requirements; +import com.questhelper.requirements.AbstractRequirement; import com.questhelper.requirements.util.Spellbook; import net.runelite.api.Client; diff --git a/src/main/java/com/questhelper/spells/Rune.java b/src/main/java/com/questhelper/spells/Rune.java index 0054d31c33..df70356903 100644 --- a/src/main/java/com/questhelper/spells/Rune.java +++ b/src/main/java/com/questhelper/spells/Rune.java @@ -32,6 +32,7 @@ import com.questhelper.requirements.ItemRequirements; import com.questhelper.requirements.util.LogicType; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import lombok.Getter; import net.runelite.api.ItemID; @@ -39,6 +40,7 @@ /** * Represents a rune that can be used to cast spells. */ +@Getter public enum Rune { AIR("Air Rune", ItemCollections.getAirRune(), ItemCollections.getAirStaff()), @@ -62,9 +64,9 @@ public enum Rune SMOKE("Smoke Rune", ItemID.SMOKE_RUNE, ItemCollections.getSmokeStaff()), MIST("Mist Rune", ItemID.MIST_RUNE, ItemCollections.getMistStaff()), DUST("Dust Rune", ItemID.DUST_RUNE, ItemCollections.getDustStaff()), + UNKNOWN("Null Rune", -1), ; - @Getter private final String runeName; private final List runes; private final List staves; @@ -78,17 +80,22 @@ public enum Rune Rune(String runeName, int itemID) { this.runeName = runeName; - this.runes = new ArrayList<>(itemID); + this.runes = Collections.singletonList(itemID); this.staves = null; } Rune(String runeName, int itemID, List staves) { this.runeName = runeName; - this.runes = new ArrayList<>(itemID); + this.runes = Collections.singletonList(itemID); this.staves = staves; } + public int getItemID() + { + return runes.get(0); + } + /** * Get a new {@link ItemRequirements} containing the specified number of rune(s) * as well as their equivalent items (combination runes, staves, etc). @@ -102,7 +109,7 @@ public ItemRequirements getRunes(int quantity) { return new ItemRequirements( LogicType.OR, - getRuneName(), + runeName, new ItemRequirement("Runes", runes, quantity), new ItemRequirement("Staff", staves, 1, true) ); @@ -112,11 +119,11 @@ public ItemRequirements getRunes(int quantity) { return new ItemRequirements( LogicType.OR, - getRuneName(), + runeName, new ItemRequirement("Runes", runes, quantity) ); } - return null; + return new ItemRequirements(LogicType.OR, "Empty Condition"); } @@ -133,6 +140,6 @@ public static Rune getByItemID(int itemID) return rune; } } - return null; + return Rune.UNKNOWN; } } From 9600cc267e9d0ecb2f19bae9117d3fad30566015 Mon Sep 17 00:00:00 2001 From: Senmori Date: Sun, 7 Feb 2021 04:39:39 -0500 Subject: [PATCH 16/55] SpellRequirement now shows the proper items in banks as well as the correct colors in the overlay and side panel. Signed-off-by: Senmori --- .../com/questhelper/QuestHelperPlugin.java | 1 + .../banktab/QuestHelperBankTagService.java | 7 +- .../questhelper/questhelpers/QuestUtil.java | 24 ++ .../requirements/QuestRequirement.java | 14 +- .../requirements/RuneRequirement.java | 129 ++++-- .../requirements/SpellRequirement.java | 400 +++++++++++------- .../java/com/questhelper/spells/Rune.java | 48 +-- 7 files changed, 397 insertions(+), 226 deletions(-) diff --git a/src/main/java/com/questhelper/QuestHelperPlugin.java b/src/main/java/com/questhelper/QuestHelperPlugin.java index 16d10dbb86..7547661db3 100644 --- a/src/main/java/com/questhelper/QuestHelperPlugin.java +++ b/src/main/java/com/questhelper/QuestHelperPlugin.java @@ -135,6 +135,7 @@ public class QuestHelperPlugin extends Plugin private static final Zone PHOENIX_START_ZONE = new Zone(new WorldPoint(3204, 3488, 0), new WorldPoint(3221, 3501, 0)); + @Getter private final BankItems bankItems = new BankItems(); @Getter diff --git a/src/main/java/com/questhelper/banktab/QuestHelperBankTagService.java b/src/main/java/com/questhelper/banktab/QuestHelperBankTagService.java index cf0e303a01..44ac36da60 100644 --- a/src/main/java/com/questhelper/banktab/QuestHelperBankTagService.java +++ b/src/main/java/com/questhelper/banktab/QuestHelperBankTagService.java @@ -37,8 +37,8 @@ import java.util.Objects; import java.util.stream.Collectors; import javax.inject.Inject; -import lombok.val; import net.runelite.api.InventoryID; +import net.runelite.api.Item; import net.runelite.api.ItemContainer; public class QuestHelperBankTagService @@ -133,8 +133,11 @@ private void getItemsFromRequirement(BankTabItems pluginItems, ItemRequirement i else if (itemRequirement instanceof BankItemHolder) { BankItemHolder holder = (BankItemHolder) itemRequirement; + final Item[] items = plugin.getBankItems().getItems(); + // Force run on client thread even though it's not as responsive as not doing that, however it + // ensures we run on the client thread and never run into threading issues. plugin.getClientThread().invoke(() -> { - val reqs = holder.getRequirements(plugin.getClient(), false, null); + List reqs = holder.getRequirements(plugin.getClient(), false, items); makeBankHolderItems(reqs, pluginItems); // callback because we can't halt on the client thread }); } diff --git a/src/main/java/com/questhelper/questhelpers/QuestUtil.java b/src/main/java/com/questhelper/questhelpers/QuestUtil.java index 2ccb2bae9c..75328e2b4c 100644 --- a/src/main/java/com/questhelper/questhelpers/QuestUtil.java +++ b/src/main/java/com/questhelper/questhelpers/QuestUtil.java @@ -34,6 +34,7 @@ import java.util.List; import java.util.stream.Collector; import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.annotation.Nonnull; public class QuestUtil @@ -59,4 +60,27 @@ public static List toReversedLinkedList(@Nonnull T... elements) { return Collectors.toCollection(ArrayList::new); } + + /** + * Removes all the duplicate elements from a stream and collects them into a + * mutable ArrayList + * + * @param stream stream to remove duplicates from + * @return a mutable list containing all the remaining elements of the stream + */ + public static List collectAndRemoveDuplicates(Stream stream) + { + return stream.distinct().collect(collectToArrayList()); + } + + /** + * Remove duplicates in the given list. + * + * @param list the list + * @return a mutable list without duplicates. + */ + public static List removeDuplicates(List list) + { + return collectAndRemoveDuplicates(list.stream()); + } } diff --git a/src/main/java/com/questhelper/requirements/QuestRequirement.java b/src/main/java/com/questhelper/requirements/QuestRequirement.java index 570a74d38b..c6dac36561 100644 --- a/src/main/java/com/questhelper/requirements/QuestRequirement.java +++ b/src/main/java/com/questhelper/requirements/QuestRequirement.java @@ -2,6 +2,7 @@ import com.questhelper.QuestHelperQuest; import java.util.Locale; +import javax.annotation.Nullable; import lombok.Getter; import net.runelite.api.Client; import net.runelite.api.QuestState; @@ -25,8 +26,7 @@ public class QuestRequirement extends AbstractRequirement */ public QuestRequirement(QuestHelperQuest quest, QuestState requiredState) { - this.quest = quest; - this.requiredState = requiredState; + this(quest, requiredState, null); } /** @@ -36,12 +36,18 @@ public QuestRequirement(QuestHelperQuest quest, QuestState requiredState) * @param requiredState the required quest state * @param displayText display text */ - public QuestRequirement(QuestHelperQuest quest, QuestState requiredState, String displayText) + public QuestRequirement(QuestHelperQuest quest, QuestState requiredState, @Nullable String displayText) { - this(quest, requiredState); + this.quest = quest; + this.requiredState = requiredState; this.displayText = displayText; } + public QuestRequirement(QuestHelperQuest quest) + { + this(quest, QuestState.FINISHED, null); + } + @Override public boolean check(Client client) { diff --git a/src/main/java/com/questhelper/requirements/RuneRequirement.java b/src/main/java/com/questhelper/requirements/RuneRequirement.java index 48962cd797..a6571e6486 100644 --- a/src/main/java/com/questhelper/requirements/RuneRequirement.java +++ b/src/main/java/com/questhelper/requirements/RuneRequirement.java @@ -27,25 +27,34 @@ package com.questhelper.requirements; import com.questhelper.questhelpers.BankItemHolder; +import com.questhelper.questhelpers.QuestUtil; import com.questhelper.requirements.util.InventorySlots; -import com.questhelper.requirements.util.LogicType; import com.questhelper.spells.Rune; -import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; import java.util.List; -import java.util.function.Predicate; import lombok.Getter; import net.runelite.api.Client; import net.runelite.api.Item; +/* + * LOGIC: + * We need to be able to get the first rune/staff that was found in the player's inventory/bank so + * we can display that item as a bank icon. + * + * Prioritize runes over staves because most quests require some sort of combat gear and that would, + * most likely, eliminate stave as an option for most quests. + */ @Getter -public class RuneRequirement extends ItemRequirements implements BankItemHolder +public class RuneRequirement extends ItemRequirement implements BankItemHolder { private final Rune rune; - private ItemRequirements runeRequirements; private int costPerCast; private int requiredAmount; + + private final ItemRequirement runeItemRequirement; + private ItemRequirement staffItemRequirement; + public RuneRequirement(Rune rune, int costPerCast) { this(rune, costPerCast, 1); @@ -53,58 +62,120 @@ public RuneRequirement(Rune rune, int costPerCast) public RuneRequirement(Rune rune, int costPerCast, int numberOfCasts) { - super(LogicType.OR, rune.getRuneName(), rune.getRunes(costPerCast * numberOfCasts).getItemRequirements()); + super(rune.getRuneName(), rune.getItemID(), (costPerCast * numberOfCasts)); this.rune = rune; this.costPerCast = costPerCast; this.requiredAmount = costPerCast * numberOfCasts; - setNumberOfCasts(numberOfCasts); + this.runeItemRequirement = new ItemRequirement(rune.getRuneName(), rune.getRunes(), this.requiredAmount); + if (rune.getStaves() != null) + { + this.staffItemRequirement = new ItemRequirement(rune.getRuneName(), rune.getStaves(), 1, true); + } } public void setNumberOfCasts(int numberOfCasts) { this.requiredAmount = costPerCast * numberOfCasts; - updateRequirements(numberOfCasts); + updateRequirements(this.requiredAmount); } - private void updateRequirements(int numberOfCasts) + private void updateRequirements(int numRunesRequired) { - this.requiredAmount = costPerCast * numberOfCasts; - runeRequirements = null; - this.runeRequirements = rune.getRunes(this.requiredAmount); - getItemRequirements().clear(); - getItemRequirements().addAll(runeRequirements.getItemRequirements()); + this.runeItemRequirement.setQuantity(numRunesRequired); + setQuantity(numRunesRequired); + } + + @Override + public boolean isActualItem() + { + return true; + } + + @Override + public Integer getDisplayItemId() + { + return null; // use our requirements to determine the id to display + } + + @Override + public void setDisplayItemId(Integer displayItemId) + { + // Don't set so we use our requirements to determine what to show in the bank } @Override public List getAllIds() { - return runeRequirements == null ? new ArrayList<>(rune.getItemID()) : runeRequirements.getAllIds(); + List ids = new LinkedList<>(runeItemRequirement.getAllIds()); + if (staffItemRequirement != null) + { + ids.addAll(staffItemRequirement.getAllIds()); + } + return QuestUtil.removeDuplicates(ids); + } + + @Override + public boolean checkBank(Client client) + { + List ids = runeItemRequirement.getAllIds(); + boolean hasRunes = InventorySlots.BANK.contains(client, i -> ids.contains(i.getId()) && i.getQuantity() >= this.requiredAmount); + if (hasRunes) + { + return true; + } + if (staffItemRequirement != null) + { + List staffIDs = staffItemRequirement.getAllIds(); + return InventorySlots.BANK.contains(client, i -> staffIDs.contains(i.getId()) && i.getQuantity() >= 1); + } + return false; } @Override - public List getRequirements(Client client, boolean checkConsideringSlotRestrictions, Item[] bankItems) + public boolean check(Client client, boolean checkWithSlotRestrictions, Item[] items) + { + int id = findFirstItemID(client, runeItemRequirement.getAllIds(), this.requiredAmount, checkWithSlotRestrictions, items); + if (id >= 0) + { + return true; + } + if (staffItemRequirement != null) + { + return findFirstItemID(client, staffItemRequirement.getAllIds(), 1, checkWithSlotRestrictions, items) >= 0; + } + return false; + } + + private int findFirstItemID(Client client, List itemIDList, int requiredAmount, boolean checkWithSlotRestrictions, Item[] items) { - List requirements = new LinkedList<>(); - List runes = rune.getRunes(); - int requiredAmount = this.requiredAmount; - if (clientHasRequiredItems(client, i -> runes.contains(i.getId()) && i.getQuantity() >= requiredAmount)) + int remainder = requiredAmount; + for (int id : itemIDList) { - return Collections.singletonList(new ItemRequirement(rune.getRuneName(), runes, requiredAmount)); + remainder -= (requiredAmount - getRequiredItemDifference(client, id, checkWithSlotRestrictions, items)); + if (remainder <= 0) + { + return id; + } } - List staves = rune.getStaves(); - if (staves == null) + return -1; + } + + @Override + public List getRequirements(Client client, boolean checkWithSlotRestrictions, Item[] bankItems) + { + if (hasItem(client, runeItemRequirement.getAllIds(), this.requiredAmount, checkWithSlotRestrictions, bankItems)) { - return requirements; + return Collections.singletonList(runeItemRequirement); } - if (clientHasRequiredItems(client, i -> staves.contains(i.getId()))) + if (staffItemRequirement != null && hasItem(client, staffItemRequirement.getAllIds(), 1, checkWithSlotRestrictions, bankItems)) { - return Collections.singletonList(new ItemRequirement(rune.getRuneName(), staves, 1)); + return Collections.singletonList(staffItemRequirement); } - return requirements; + return Collections.emptyList(); } - private boolean clientHasRequiredItems(Client client, Predicate predicate) + private boolean hasItem(Client client, List ids, int amount, boolean checkWithSlotRestrictions, Item[] items) { - return InventorySlots.INVENTORY_SLOTS.contains(client, predicate) || InventorySlots.BANK.contains(client, predicate); + return findFirstItemID(client, ids, amount, checkWithSlotRestrictions, items) >= 0; } } diff --git a/src/main/java/com/questhelper/requirements/SpellRequirement.java b/src/main/java/com/questhelper/requirements/SpellRequirement.java index 1783fcf3ce..c02ca6d1e8 100644 --- a/src/main/java/com/questhelper/requirements/SpellRequirement.java +++ b/src/main/java/com/questhelper/requirements/SpellRequirement.java @@ -26,10 +26,10 @@ */ package com.questhelper.requirements; +import com.google.common.base.Predicates; import com.questhelper.questhelpers.BankItemHolder; import com.questhelper.questhelpers.QuestUtil; import com.questhelper.requirements.util.InventorySlots; -import com.questhelper.requirements.util.LogicType; import com.questhelper.spells.MagicSpell; import com.questhelper.spells.Rune; import java.awt.Color; @@ -39,259 +39,357 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Objects; -import java.util.function.Predicate; import java.util.stream.Collectors; -import java.util.stream.Stream; -import javax.annotation.Nonnull; import lombok.Getter; import net.runelite.api.Client; -import net.runelite.api.InventoryID; import net.runelite.api.Item; import net.runelite.api.ItemComposition; import net.runelite.api.Skill; +import net.runelite.client.ui.overlay.components.LineComponent; +/* + * LOGIC: + * + * Order of priority for requirements: + * 1. Player has tablet + * 2. Player has runes + * 3. Player has staff + * + * We can ignore the requirements if the player has the tablet because those + * have no requirements in order to be used. + */ public class SpellRequirement extends ItemRequirement implements BankItemHolder { - @Getter private final MagicSpell spell; - private boolean hasTabletItem = false; - private int numberOfCasts = 1; private ItemRequirement tabletRequirement = null; + @Getter + private int numberOfCasts; - private final List requirements; - private final List runeRequirements = new ArrayList<>(); - private final Map runesPerCastMap; - public SpellRequirement(MagicSpell spell, Map runesPerCastMap, List requirements) + private final Map runeCostMap; + private final List requirements = new ArrayList<>(); + private final List runeRequirements = new LinkedList<>(); + @Getter + private final SkillRequirement skillRequirement; + @Getter + private final SpellbookRequirement spellbookRequirement; + public SpellRequirement(MagicSpell spell, Map runeCostMap, List requirements) { - super(spell.getName(), -1); - this.spell = spell; - this.requirements = new LinkedList<>(requirements); // make it mutable for now - this.requirements.add(new SpellbookRequirement(spell.getSpellbook())); - this.requirements.add(new SkillRequirement(Skill.MAGIC, spell.getRequiredMagicLevel())); - this.runesPerCastMap = runesPerCastMap; - registerAlternateItems(this.requirements); - setNumberOfCasts(1); - super.setDisplayItemId(-1); + this(spell, 1, runeCostMap, requirements); } - public void setNumberOfCasts(int numberOfCasts) + public SpellRequirement(MagicSpell spell, int numberOfCasts, Map runeCostMap, List requirements) { + super(spell.getName(), -1, numberOfCasts); + this.spell = spell; this.numberOfCasts = numberOfCasts; - setupRuneRequirements(this.numberOfCasts); + this.runeCostMap = runeCostMap; + this.requirements.addAll(requirements); + this.skillRequirement = new SkillRequirement(Skill.MAGIC, spell.getRequiredMagicLevel()); + this.spellbookRequirement = new SpellbookRequirement(spell.getSpellbook()); + this.requirements.add(this.skillRequirement); + this.requirements.add(this.spellbookRequirement); + setQuantity(numberOfCasts); + updateTooltip(); } - public void setTablet(int itemID) + public void addRequirement(Requirement requirement) { - this.tabletRequirement = new ItemRequirement("", itemID); + if (requirement instanceof RuneRequirement) + { + runeRequirements.add((RuneRequirement) requirement); + } + else + { + requirements.add(requirement); + } + updateTooltip(); } - @Override - public void setDisplayItemId(Integer displayItemId) + public List getRequirements() { - // Don't set so we always use the requirement ids for bank filters + return new ArrayList<>(requirements); } - private void setupRuneRequirements(int numberOfCasts) + public void setTablet(int itemID) { - runeRequirements.clear(); - for (Map.Entry entry : runesPerCastMap.entrySet()) + if (itemID < 0) { - Rune rune = entry.getKey(); - int runesPerCast = entry.getValue(); - RuneRequirement runeReq = new RuneRequirement(rune, runesPerCast); - runeReq.setNumberOfCasts(numberOfCasts); - runeRequirements.add(runeReq); - List allIds = runeReq.getAllIds(); - alternateItems.addAll(allIds); + this.tabletRequirement = null; + } + else + { + this.tabletRequirement = new ItemRequirement("", itemID, this.numberOfCasts); } } - private List getItemRequirements(List requirements) + public void setNumberOfCasts(int numberOfCasts) { - return requirements.stream() - .filter(ItemRequirement.class::isInstance) - .map(ItemRequirement.class::cast) - .collect(Collectors.toList()); + this.numberOfCasts = numberOfCasts; + setQuantity(numberOfCasts); + runeRequirements.clear(); + for (Map.Entry entry : runeCostMap.entrySet()) + { + Rune rune = entry.getKey(); + int costPerCast = entry.getValue(); + RuneRequirement runeRequirement = new RuneRequirement(rune, costPerCast, numberOfCasts); + runeRequirements.add(runeRequirement); + } } - private void registerAlternateItems(List requirements) + @Override + public boolean isActualItem() { - alternateItems.clear(); - alternateItems.addAll(buildAllIds(requirements)); + return true; } - private List buildAllIds(List requirements) + @Override + public boolean showQuantity() { - List itemRequirements = getItemRequirements(requirements); + return true; + } - return itemRequirements.stream() - .filter(Objects::nonNull) - .map(ItemRequirement::getAllIds) - .flatMap(Collection::stream) - .distinct() - .collect(QuestUtil.collectToArrayList()); + @Override + public Integer getDisplayItemId() + { + return tabletRequirement != null ? tabletRequirement.getId() : null; } - @Nonnull @Override - public String getDisplayText() + public void setDisplayItemId(Integer displayItemId) { - return spell.getName(); //TODO: Re-evaluate when we show the spell name versus the tablet + // Don't set so we use our requirements to determine what to show in the bank } @Override - public boolean isActualItem() + public List getOverlayDisplayText(Client client) { - return false; // TODO: Redo when/if we determine when to show the tablet + List lines = new ArrayList<>(); + + StringBuilder text = new StringBuilder(); + if (this.showQuantity()) + { + text.append(this.numberOfCasts).append(" x "); + } + + String name = spell.getName(); + if (tabletRequirement != null && tabletRequirement.check(client)) + { + name = tabletRequirement.getName(); + } + text.append(name); + + Color color = getColorConsideringBank(client, false, null); + if (color == Color.RED && checkBank(client)) + { + color = Color.WHITE; + } + lines.add(LineComponent.builder() + .left(text.toString()) + .leftColor(color) + .build() + ); + return lines; } @Override - public boolean showQuantity() + public List getAllIds() { - return true; + List ids = requirements.stream() + .filter(ItemRequirement.class::isInstance) + .map(ItemRequirement.class::cast) + .map(ItemRequirement::getAllIds) + .flatMap(Collection::stream) + .collect(QuestUtil.collectToArrayList()); + List runeIDs = runeRequirements.stream() + .map(RuneRequirement::getAllIds) + .flatMap(Collection::stream) + .collect(Collectors.toList()); + ids.addAll(runeIDs); + return ids; } @Override - public Color getColor(Client client) + public boolean check(Client client) { - return meetsRequirements(client, false, null) ? Color.GREEN : Color.RED; + return this.check(client, false, null); } @Override - public Color getColorConsideringBank(Client client, boolean checkConsideringSlotRestrictions, Item[] bankItems) + public boolean checkBank(Client client) { - Color color = meetsRequirements(client, checkConsideringSlotRestrictions, null) ? Color.GREEN : Color.RED; - if (color == Color.RED && bankItems != null) + if (tabletRequirement != null) { - if (meetsRequirements(client, false, bankItems)) + int tabletID = tabletRequirement.getId(); + if (InventorySlots.BANK.contains(client, i -> i.getId() == tabletID && i.getQuantity() >= this.numberOfCasts)) { - color = Color.WHITE; + updateTabletRequirement(client); + return true; } } - return color; + boolean hasRunes = runeRequirements.stream().allMatch(req -> req.checkBank(client)); + boolean hasItems = requirements.stream().allMatch(req -> req.check(client)); + return hasRunes && hasItems; } - @Override - public List getDisplayItemIds() + private void updateTabletRequirement(Client client) { - // LinkedList so we can maintain what should get priority for displaying - List ids = new LinkedList<>(buildAllIds(requirements)); - runeRequirements.forEach(req -> alternateItems.addAll(req.getAllIds())); - if (tabletRequirement != null) + if (tabletRequirement != null && (tabletRequirement.getName() == null || tabletRequirement.getName().isEmpty())) { - ids.add(tabletRequirement.getDisplayItemId()); + ItemComposition tablet = client.getItemDefinition(tabletRequirement.getId()); + tabletRequirement = new ItemRequirement(tablet.getName(), tablet.getId(), this.numberOfCasts); } - return ids; } @Override - public boolean check(Client client) + public Color getColorConsideringBank(Client client, boolean checkWithSlotRestrictions, Item[] bankItems) { - return meetsRequirements(client, false, null); + if (tabletRequirement != null) + { + updateTabletRequirement(client); + int tabletID = tabletRequirement.getId(); + int required = tabletRequirement.getQuantity(); + if (InventorySlots.INVENTORY_SLOTS.contains(client, i -> i.getId() == tabletID && i.getQuantity() >= required)) + { + return Color.GREEN; + } + if (InventorySlots.BANK.contains(client, i -> i.getId() == tabletID && i.getQuantity() >= required)) + { + return Color.WHITE; + } + } + boolean hasOtherReqs = getNonItemRequirements(this.requirements).stream().allMatch(req -> req.check(client)); + if (!hasOtherReqs) + { + return Color.RED; + } + // No tablet (either set or found), we need all runes and other requirements + boolean hasRunes = false; + boolean hasItems = false; + List itemRequirements = getItemRequirements(this.requirements); + updateItemRequirements(client, itemRequirements); + hasRunes = runeRequirements.stream().allMatch(req -> hasItemAmount(client, req.getAllIds(), req.getRequiredAmount())); + hasItems = itemRequirements.stream().allMatch(req -> hasItemAmount(client, req.getAllIds(), req.getQuantity())); + if (hasRunes && hasItems) + { + return Color.GREEN; + } + if (bankItems != null) + { + hasRunes = runeRequirements.stream().allMatch(req -> req.checkBank(client)); + hasItems = itemRequirements.stream().allMatch(req -> { + return InventorySlots.BANK.contains(client, i -> req.getAllIds().contains(i.getId()) && i.getQuantity() >= req.getQuantity()); + }); + if (hasRunes && hasItems) + { + return Color.WHITE; + } + } + return Color.RED; } - @Override - public boolean check(Client client, boolean checkConsideringSlotRestrictions) + private boolean hasItemAmount(Client client, List idList, int amount) { - return meetsRequirements(client, checkConsideringSlotRestrictions, null); + return InventorySlots.INVENTORY_SLOTS.contains(client, i -> idList.contains(i.getId()) && i.getQuantity() >= amount); } + @Override - public boolean check(Client client, boolean checkConsideringSlotRestrictions, Item[] items) + public boolean check(Client client, boolean checkWithSlotRestrictions, Item[] items) { - return meetsRequirements(client, checkConsideringSlotRestrictions, items); + if (tabletRequirement != null) + { + int id = findFirstItemID(client, Collections.singletonList(tabletRequirement.getId()), this.numberOfCasts, checkWithSlotRestrictions, items); + if (id >= 0) + { + updateTabletRequirement(client); + return true; + } + } + boolean hasItems, hasOther, hasRunes = false; + List itemRequirements = getItemRequirements(this.requirements); + updateItemRequirements(client, itemRequirements); + if (!itemRequirements.isEmpty()) + { + hasItems = itemRequirements.stream().allMatch(req -> req.check(client, checkWithSlotRestrictions, items)); + } + else + { + hasItems = true; + } + hasOther = getNonItemRequirements(this.requirements).stream().allMatch(req -> req.check(client)); + hasRunes = runeRequirements.stream().allMatch(req -> req.check(client, checkWithSlotRestrictions, items)); + return hasItems && hasOther && hasRunes; } - private boolean checkInventorySlot(InventorySlots slot, Client client, ItemRequirement requirement) + @Override + public List getRequirements(Client client, boolean checkWithSlotRestrictions, Item[] bankItems) { - return slot.contains(client, i -> requirement.getAllIds().contains(i.getId()) && i.getQuantity() >= requirement.getQuantity()); + if (tabletRequirement != null) + { + int tabletID = tabletRequirement.getId(); + if (hasItem(client, Collections.singletonList(tabletID), this.numberOfCasts, checkWithSlotRestrictions, bankItems)) + { + return Collections.singletonList(tabletRequirement); + } + } + List requirements = runeRequirements.stream() + .map(req -> req.getRequirements(client, checkWithSlotRestrictions, bankItems)) + .flatMap(Collection::stream) + .collect(Collectors.toCollection(LinkedList::new)); + requirements.addAll(getItemRequirements(this.requirements)); + updateItemRequirements(client, requirements); + return requirements; } - private boolean meetsRequirements(Client client, boolean checkConsideringSlotRestrictions, Item[] items) + private List getItemRequirements(List requirements) { - updateInternalRequirements(client); - boolean itemRequirementsMet = itemRequirementsMet(requirements, client, checkConsideringSlotRestrictions, items); - boolean nonItemRequirementsMet = nonItemRequirementsMet(requirements, client); - boolean runeRequirementsMet = hasRunes(runeRequirements, client, checkConsideringSlotRestrictions, items); - boolean hasTabletItem = hasTabletItem(client); - return (nonItemRequirementsMet && itemRequirementsMet && runeRequirementsMet) || hasTabletItem; + return requirements.stream() + .filter(ItemRequirement.class::isInstance) + .map(ItemRequirement.class::cast) + .collect(Collectors.toList()); } - private boolean hasRunes(List runes, Client client, boolean checkWithSlotRestrictions, Item[] items) + private List getNonItemRequirements(List requirements) { - return runes.stream().allMatch(req -> req.check(client, checkWithSlotRestrictions, items)); + return requirements.stream() + .filter(Predicates.not(ItemRequirement.class::isInstance)) + .collect(Collectors.toList()); } - private boolean itemRequirementsMet(List requirements, Client client, boolean checkConsideringSlotRestrictions, Item[] items) + private int findFirstItemID(Client client, List itemIDList, int requiredAmount, boolean checkWithSlotRestrictions, Item[] items) { - return getItemRequirements(requirements).stream().allMatch(req -> req.check(client, checkConsideringSlotRestrictions, items)); + int remainder = requiredAmount; + for (int id : itemIDList) + { + remainder -= (requiredAmount - getRequiredItemDifference(client, id, checkWithSlotRestrictions, items)); + if (remainder <= 0) + { + return id; + } + } + return -1; } - private boolean nonItemRequirementsMet(List requirements, Client client) + private boolean hasItem(Client client, List ids, int amount, boolean checkWithSlotRestrictions, Item[] items) { - return requirements.stream() - .filter(((Predicate) ItemRequirement.class::isInstance).negate()) - .allMatch(req -> req.check(client)); + return findFirstItemID(client, ids, amount, checkWithSlotRestrictions, items) >= 0; } - private boolean hasTabletItem(Client client) + private void updateTooltip() { - return tabletRequirement != null && tabletRequirement.check(client); + setTooltip("This spell requires: "); + getNonItemRequirements(this.requirements).forEach(req -> appendToTooltip(req.getDisplayText())); } - /** This is not for deciding if the player meets this SpellRequirement. This is only for the internal state of this requirement. */ - private void updateInternalRequirements(Client client) + private void updateItemRequirements(Client client, List requirements) { - if (tabletRequirement != null) - { - if (this.tabletRequirement.getName() == null || this.tabletRequirement.getName().isEmpty()) - { - if (tabletRequirement.getId() > -1) - { - ItemComposition tablet = client.getItemDefinition(tabletRequirement.getId()); - this.tabletRequirement = new ItemRequirement(client.getItemDefinition(tablet.getId()).getName(), tablet.getId()); - tabletRequirement.setDisplayItemId(tablet.getId()); - } - else - { - //TODO: throw error? - } - } - hasTabletItem = tabletRequirement.check(client); - } - List itemRequirements = getItemRequirements(requirements); - for (ItemRequirement item : itemRequirements) + for (ItemRequirement requirement : requirements) { - if (item.getName() == null || item.getName().isEmpty()) + if (requirement.getName() == null || requirement.getName().isEmpty()) { - String name = client.getItemDefinition(item.getId()).getName(); - item.setName(name); + requirement.setName(client.getItemDefinition(requirement.getId()).getName()); } } } - - @Override - public List getRequirements(Client client, boolean checkConsideringSlotRestrictions, Item[] bankItems) - { - List bankTabItemRequirements = new LinkedList<>(); - if (tabletRequirement != null && tabletRequirement.check(client, checkConsideringSlotRestrictions, bankItems)) - { - bankTabItemRequirements.add(tabletRequirement); - } - else - { - List runeItemRequirements = runeRequirements.stream() - .map(req -> req.getRequirements(client, checkConsideringSlotRestrictions, bankItems)) - .flatMap(Collection::stream) - .collect(Collectors.toList()); - bankTabItemRequirements.addAll(runeItemRequirements); - } - List itemRequirements = getItemRequirements(this.requirements).stream() - .filter(req -> req.check(client, checkConsideringSlotRestrictions, bankItems)) - .collect(Collectors.toList()); - bankTabItemRequirements.addAll(itemRequirements); - return bankTabItemRequirements; - } } diff --git a/src/main/java/com/questhelper/spells/Rune.java b/src/main/java/com/questhelper/spells/Rune.java index df70356903..fb80f08d8f 100644 --- a/src/main/java/com/questhelper/spells/Rune.java +++ b/src/main/java/com/questhelper/spells/Rune.java @@ -28,12 +28,9 @@ package com.questhelper.spells; import com.questhelper.ItemCollections; -import com.questhelper.requirements.ItemRequirement; -import com.questhelper.requirements.ItemRequirements; -import com.questhelper.requirements.util.LogicType; -import java.util.ArrayList; import java.util.Collections; import java.util.List; +import javax.annotation.Nonnull; import lombok.Getter; import net.runelite.api.ItemID; @@ -67,24 +64,26 @@ public enum Rune UNKNOWN("Null Rune", -1), ; + @Nonnull private final String runeName; + @Nonnull private final List runes; private final List staves; - Rune(String runeName, List runes, List staves) + Rune(@Nonnull String runeName, @Nonnull List runes, List staves) { this.runeName = runeName; this.runes = runes; this.staves = staves; } - Rune(String runeName, int itemID) + Rune(@Nonnull String runeName, int itemID) { this.runeName = runeName; this.runes = Collections.singletonList(itemID); this.staves = null; } - Rune(String runeName, int itemID, List staves) + Rune(@Nonnull String runeName, int itemID, List staves) { this.runeName = runeName; this.runes = Collections.singletonList(itemID); @@ -96,46 +95,15 @@ public int getItemID() return runes.get(0); } - /** - * Get a new {@link ItemRequirements} containing the specified number of rune(s) - * as well as their equivalent items (combination runes, staves, etc). - * - * @param quantity the number of runes required - * @return a new instance of {@link ItemRequirements} - */ - public ItemRequirements getRunes(int quantity) - { - if (runes != null && staves != null) - { - return new ItemRequirements( - LogicType.OR, - runeName, - new ItemRequirement("Runes", runes, quantity), - new ItemRequirement("Staff", staves, 1, true) - ); - } - - if (runes != null) - { - return new ItemRequirements( - LogicType.OR, - runeName, - new ItemRequirement("Runes", runes, quantity) - ); - } - return new ItemRequirements(LogicType.OR, "Empty Condition"); - } - - public static Rune getByItemID(int itemID) { for (Rune rune : Rune.values()) { - if (rune.runes != null && rune.runes.contains(itemID)) + if (rune.getRunes().contains(itemID)) { return rune; } - if (rune.staves != null && rune.staves.contains(itemID)) + if (rune.getStaves() != null && rune.getStaves().contains(itemID)) { return rune; } From 37ff4d894885d0b2c9341efa3cbbacadd5b1aa59 Mon Sep 17 00:00:00 2001 From: Senmori Date: Sun, 7 Feb 2021 05:23:26 -0500 Subject: [PATCH 17/55] Let QuestBankTab use config text highlight color to color items. Use FontMetrics to better place text. Signed-off-by: Senmori --- .../com/questhelper/banktab/QuestBankTab.java | 37 +++++++++++++------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/questhelper/banktab/QuestBankTab.java b/src/main/java/com/questhelper/banktab/QuestBankTab.java index d6d8b4aef0..70cd6d5d98 100644 --- a/src/main/java/com/questhelper/banktab/QuestBankTab.java +++ b/src/main/java/com/questhelper/banktab/QuestBankTab.java @@ -30,7 +30,11 @@ import com.google.common.primitives.Shorts; import com.questhelper.QuestHelperPlugin; import java.awt.Color; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; import java.awt.Point; +import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -43,6 +47,7 @@ import net.runelite.api.ChatMessageType; import net.runelite.api.Client; import net.runelite.api.FontID; +import net.runelite.api.FontTypeFace; import net.runelite.api.ItemID; import net.runelite.api.ScriptEvent; import net.runelite.api.ScriptID; @@ -64,6 +69,7 @@ import net.runelite.client.chat.ChatMessageBuilder; import net.runelite.client.chat.ChatMessageManager; import net.runelite.client.chat.QueuedMessage; +import net.runelite.client.config.FontType; import net.runelite.client.eventbus.Subscribe; import net.runelite.client.game.ItemManager; import net.runelite.client.game.ItemVariationMapping; @@ -333,6 +339,11 @@ private int addPluginTabSection(Widget itemContainer, List items, L return 0; } + // Get FontMetrics so we can accurately get font height/width + int fontID = FontID.PLAIN_11; // 494 + BufferedImage img = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB); + Graphics graphics = img.getGraphics(); + FontMetrics fm = graphics.getFontMetrics(FontType.SMALL.getFont()); for (BankTabItem bankTabItem : items) { boolean foundItem = false; @@ -356,21 +367,22 @@ private int addPluginTabSection(Widget itemContainer, List items, L if (bankTabItem.getQuantity() > 0) { String quantityString = QuantityFormatter.quantityToStackSize(bankTabItem.getQuantity()); - int extraLength = - QuantityFormatter.quantityToStackSize(widget.getItemQuantity()).length() * 6; - int requirementLength = quantityString.length() * 6; + int itemStackSizeLength = fm.stringWidth(QuantityFormatter.quantityToStackSize(widget.getItemQuantity())); + int requirementLength = fm.stringWidth(quantityString); - int xPos = point.x + 2 + extraLength; + int xPos = point.x + 2 + itemStackSizeLength; int yPos = point.y - 1; - if (extraLength + requirementLength > 24) - { + if (itemStackSizeLength + requirementLength > 24) + { // put text on next line xPos = point.x; - yPos = point.y + 9; + yPos = point.y + fm.getHeight(); } + Color color = questHelper.getConfig().textHighlightColor(); + addedWidgets.add(createText(itemContainer, "/ " + quantityString, - Color.WHITE.getRGB(), + color.getRGB(), ITEM_HORIZONTAL_SPACING, TEXT_HEIGHT - 3, xPos, @@ -400,18 +412,19 @@ private int addPluginTabSection(Widget itemContainer, List items, L if (bankTabItem.getQuantity() > 0) { String quantityString = QuantityFormatter.quantityToStackSize(bankTabItem.getQuantity()); - int requirementLength = quantityString.length() * 5; + int requirementLength = fm.stringWidth(quantityString); int xPos = adjXOffset + 8; int yPos = adjYOffset - 1; if (requirementLength > 20) - { + { // put text on next line xPos = adjXOffset; - yPos = adjYOffset + 9; + yPos = adjYOffset + fm.getHeight(); } + Color color = questHelper.getConfig().textHighlightColor(); addedWidgets.add(createText(itemContainer, "/ " + quantityString, - Color.WHITE.getRGB(), + color.getRGB(), ITEM_HORIZONTAL_SPACING, TEXT_HEIGHT - 3, xPos, From 5415e634587ef8561e7f59275294777a32891021 Mon Sep 17 00:00:00 2001 From: Senmori Date: Sun, 7 Feb 2021 05:27:08 -0500 Subject: [PATCH 18/55] Don't let unnecessary objects stick around in QuestBankTab. Signed-off-by: Senmori --- src/main/java/com/questhelper/banktab/QuestBankTab.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/questhelper/banktab/QuestBankTab.java b/src/main/java/com/questhelper/banktab/QuestBankTab.java index 70cd6d5d98..1ed9249b8a 100644 --- a/src/main/java/com/questhelper/banktab/QuestBankTab.java +++ b/src/main/java/com/questhelper/banktab/QuestBankTab.java @@ -340,10 +340,11 @@ private int addPluginTabSection(Widget itemContainer, List items, L } // Get FontMetrics so we can accurately get font height/width - int fontID = FontID.PLAIN_11; // 494 BufferedImage img = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB); Graphics graphics = img.getGraphics(); FontMetrics fm = graphics.getFontMetrics(FontType.SMALL.getFont()); + img = null; + graphics = null; for (BankTabItem bankTabItem : items) { boolean foundItem = false; From e1bf72de916a485d5ec6f574e6e0e16aa16c54d5 Mon Sep 17 00:00:00 2001 From: Senmori Date: Sun, 7 Feb 2021 05:35:42 -0500 Subject: [PATCH 19/55] Add `requireRunes` method to indicate that the SpellRequirement should require the player to use runes/staves. Signed-off-by: Senmori --- .../java/com/questhelper/requirements/SpellRequirement.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/com/questhelper/requirements/SpellRequirement.java b/src/main/java/com/questhelper/requirements/SpellRequirement.java index c02ca6d1e8..c9813dac04 100644 --- a/src/main/java/com/questhelper/requirements/SpellRequirement.java +++ b/src/main/java/com/questhelper/requirements/SpellRequirement.java @@ -123,6 +123,11 @@ public void setTablet(int itemID) } } + public void requireRunes() + { + setTablet(-1); + } + public void setNumberOfCasts(int numberOfCasts) { this.numberOfCasts = numberOfCasts; From 26bd0c51504b017e0ef7df17582567c59a21f2fe Mon Sep 17 00:00:00 2001 From: Senmori Date: Sun, 7 Feb 2021 07:10:07 -0500 Subject: [PATCH 20/55] Add requirements of spells to the requirement's tooltip. Add live updating of requirements. SpellRequirement will display the requirements that the user has met as strikethrough text. Signed-off-by: Senmori --- .../questhelper/panel/QuestOverviewPanel.java | 2 + .../panel/QuestRequirementPanel.java | 46 ++++++++++---- .../requirements/ItemRequirement.java | 8 +++ .../requirements/SpellRequirement.java | 62 ++++++++++++++----- .../com/questhelper/spells/StandardSpell.java | 6 ++ .../java/com/questhelper/steps/NpcStep.java | 14 ++++- .../com/questhelper/steps/ObjectStep.java | 14 ++++- .../java/com/questhelper/steps/QuestStep.java | 18 ++++++ 8 files changed, 141 insertions(+), 29 deletions(-) diff --git a/src/main/java/com/questhelper/panel/QuestOverviewPanel.java b/src/main/java/com/questhelper/panel/QuestOverviewPanel.java index 953426f44c..7c74acbe9b 100644 --- a/src/main/java/com/questhelper/panel/QuestOverviewPanel.java +++ b/src/main/java/com/questhelper/panel/QuestOverviewPanel.java @@ -506,6 +506,8 @@ public void updateRequirementPanels(Client client, List r { newColor = itemRequirement.getColorConsideringBank(client, false, bankItems.getItems()); } + String updatedTooltip = itemRequirement.getUpdatedTooltip(client, bankItems); + requirementPanel.setInfoButtonTooltip(updatedTooltip); } else { diff --git a/src/main/java/com/questhelper/panel/QuestRequirementPanel.java b/src/main/java/com/questhelper/panel/QuestRequirementPanel.java index c21fe73a68..ff6bfb31a9 100644 --- a/src/main/java/com/questhelper/panel/QuestRequirementPanel.java +++ b/src/main/java/com/questhelper/panel/QuestRequirementPanel.java @@ -31,6 +31,7 @@ import java.awt.Color; import java.awt.Dimension; import java.awt.Insets; +import javax.annotation.Nullable; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JLabel; @@ -51,6 +52,10 @@ public class QuestRequirementPanel extends JPanel @Getter private final Requirement requirement; + @Nullable + @Getter + private JButton infoButton; + public QuestRequirementPanel(Requirement requirement) { this.requirement = requirement; @@ -84,20 +89,37 @@ public QuestRequirementPanel(Requirement requirement) } } + public void setInfoButtonTooltip(String text) + { + if (infoButton != null) + { + if (text == null || text.isEmpty()) + { + infoButton.setToolTipText(""); + infoButton.setVisible(false); + } + else + { + String html1 = ""; + String html2 = ""; + text = text.replaceAll("\\n", "
"); + infoButton.setToolTipText(html1 + text + html2); + } + } + } + private void addButtonToPanel(String tooltipText) { - String html1 = ""; - String html2 = ""; - tooltipText = tooltipText.replaceAll("\\n", "
"); - JButton b = new JButton(INFO_ICON); - b.setPreferredSize(new Dimension(10, 10)); - b.setToolTipText(html1 + tooltipText + html2); - b.setBorderPainted(false); - b.setFocusPainted(false); - b.setBorderPainted(false); - b.setContentAreaFilled(false); - b.setMargin(new Insets(0, 0, 0, 0)); - add(b); + + infoButton = new JButton(INFO_ICON); + infoButton.setPreferredSize(new Dimension(10, 10)); + setInfoButtonTooltip(tooltipText); + infoButton.setBorderPainted(false); + infoButton.setFocusPainted(false); + infoButton.setBorderPainted(false); + infoButton.setContentAreaFilled(false); + infoButton.setMargin(new Insets(0, 0, 0, 0)); + add(infoButton); } } diff --git a/src/main/java/com/questhelper/requirements/ItemRequirement.java b/src/main/java/com/questhelper/requirements/ItemRequirement.java index 4ec451736c..9363798b71 100644 --- a/src/main/java/com/questhelper/requirements/ItemRequirement.java +++ b/src/main/java/com/questhelper/requirements/ItemRequirement.java @@ -25,6 +25,7 @@ */ package com.questhelper.requirements; +import com.questhelper.BankItems; import com.questhelper.requirements.util.InventorySlots; import java.awt.Color; import java.util.ArrayList; @@ -34,6 +35,7 @@ import java.util.Objects; import java.util.stream.Collectors; import java.util.stream.Stream; +import javax.annotation.Nullable; import lombok.AccessLevel; import lombok.Getter; import lombok.Setter; @@ -400,4 +402,10 @@ public List getDisplayItemIds() return Collections.singletonList(displayItemId); } + + @Nullable + public String getUpdatedTooltip(Client client, BankItems bankItems) + { + return null; + } } diff --git a/src/main/java/com/questhelper/requirements/SpellRequirement.java b/src/main/java/com/questhelper/requirements/SpellRequirement.java index c9813dac04..aca993a8c9 100644 --- a/src/main/java/com/questhelper/requirements/SpellRequirement.java +++ b/src/main/java/com/questhelper/requirements/SpellRequirement.java @@ -27,6 +27,7 @@ package com.questhelper.requirements; import com.google.common.base.Predicates; +import com.questhelper.BankItems; import com.questhelper.questhelpers.BankItemHolder; import com.questhelper.questhelpers.QuestUtil; import com.questhelper.requirements.util.InventorySlots; @@ -39,7 +40,9 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; +import javax.annotation.Nullable; import lombok.Getter; import net.runelite.api.Client; import net.runelite.api.Item; @@ -60,6 +63,7 @@ */ public class SpellRequirement extends ItemRequirement implements BankItemHolder { + @Getter private final MagicSpell spell; private ItemRequirement tabletRequirement = null; @@ -103,7 +107,6 @@ public void addRequirement(Requirement requirement) { requirements.add(requirement); } - updateTooltip(); } public List getRequirements() @@ -169,6 +172,7 @@ public void setDisplayItemId(Integer displayItemId) @Override public List getOverlayDisplayText(Client client) { + updateInternalState(client, getItemRequirements(this.requirements)); List lines = new ArrayList<>(); StringBuilder text = new StringBuilder(); @@ -223,6 +227,7 @@ public boolean check(Client client) @Override public boolean checkBank(Client client) { + updateInternalState(client, getItemRequirements(this.requirements)); if (tabletRequirement != null) { int tabletID = tabletRequirement.getId(); @@ -237,15 +242,6 @@ public boolean checkBank(Client client) return hasRunes && hasItems; } - private void updateTabletRequirement(Client client) - { - if (tabletRequirement != null && (tabletRequirement.getName() == null || tabletRequirement.getName().isEmpty())) - { - ItemComposition tablet = client.getItemDefinition(tabletRequirement.getId()); - tabletRequirement = new ItemRequirement(tablet.getName(), tablet.getId(), this.numberOfCasts); - } - } - @Override public Color getColorConsideringBank(Client client, boolean checkWithSlotRestrictions, Item[] bankItems) { @@ -272,7 +268,7 @@ public Color getColorConsideringBank(Client client, boolean checkWithSlotRestric boolean hasRunes = false; boolean hasItems = false; List itemRequirements = getItemRequirements(this.requirements); - updateItemRequirements(client, itemRequirements); + updateInternalState(client, itemRequirements); hasRunes = runeRequirements.stream().allMatch(req -> hasItemAmount(client, req.getAllIds(), req.getRequiredAmount())); hasItems = itemRequirements.stream().allMatch(req -> hasItemAmount(client, req.getAllIds(), req.getQuantity())); if (hasRunes && hasItems) @@ -313,7 +309,7 @@ public boolean check(Client client, boolean checkWithSlotRestrictions, Item[] it } boolean hasItems, hasOther, hasRunes = false; List itemRequirements = getItemRequirements(this.requirements); - updateItemRequirements(client, itemRequirements); + updateInternalState(client, itemRequirements); if (!itemRequirements.isEmpty()) { hasItems = itemRequirements.stream().allMatch(req -> req.check(client, checkWithSlotRestrictions, items)); @@ -343,10 +339,33 @@ public List getRequirements(Client client, boolean checkWithSlo .flatMap(Collection::stream) .collect(Collectors.toCollection(LinkedList::new)); requirements.addAll(getItemRequirements(this.requirements)); - updateItemRequirements(client, requirements); + updateInternalState(client, requirements); return requirements; } + @Nullable + @Override + public String getUpdatedTooltip(Client client, BankItems bankItems) + { + StringBuilder text = new StringBuilder(); + setTooltip("This spell requires: "); + if (tabletRequirement != null) + { + AtomicInteger count = new AtomicInteger(); + getNonItemRequirements(this.requirements).stream() + .filter(r -> r instanceof QuestRequirement) + .map(QuestRequirement.class::cast) + .filter(r -> r.check(client)) + .peek(q -> count.incrementAndGet()) + .forEach(q -> text.append(q.getDisplayText())); + return count.get() > 0 ? text.toString() : null; // no requirements to use a tablet + } + getNonItemRequirements(this.requirements).stream() + .filter(req -> !req.check(client)) + .forEach(req -> text.append(req.getDisplayText()).append("\n")); + return text.toString(); + } + private List getItemRequirements(List requirements) { return requirements.stream() @@ -381,10 +400,16 @@ private boolean hasItem(Client client, List ids, int amount, boolean ch return findFirstItemID(client, ids, amount, checkWithSlotRestrictions, items) >= 0; } + private void updateInternalState(Client client, List requirements) + { + updateItemRequirements(client, requirements); + } + private void updateTooltip() { setTooltip("This spell requires: "); - getNonItemRequirements(this.requirements).forEach(req -> appendToTooltip(req.getDisplayText())); + getNonItemRequirements(this.requirements).stream() + .forEach(req -> appendToTooltip(req.getDisplayText())); } private void updateItemRequirements(Client client, List requirements) @@ -397,4 +422,13 @@ private void updateItemRequirements(Client client, List require } } } + + private void updateTabletRequirement(Client client) + { + if (tabletRequirement != null && (tabletRequirement.getName() == null || tabletRequirement.getName().isEmpty())) + { + ItemComposition tablet = client.getItemDefinition(tabletRequirement.getId()); + tabletRequirement = new ItemRequirement(tablet.getName(), tablet.getId(), this.numberOfCasts); + } + } } diff --git a/src/main/java/com/questhelper/spells/StandardSpell.java b/src/main/java/com/questhelper/spells/StandardSpell.java index f8104c3a3f..ea389011bb 100644 --- a/src/main/java/com/questhelper/spells/StandardSpell.java +++ b/src/main/java/com/questhelper/spells/StandardSpell.java @@ -165,6 +165,12 @@ public int getGroupID() return groupID; } + @Override + public int getSpriteID() + { + return spriteID; + } + @Override public Spellbook getSpellbook() { diff --git a/src/main/java/com/questhelper/steps/NpcStep.java b/src/main/java/com/questhelper/steps/NpcStep.java index c83cbb23ac..d39d5e19c2 100644 --- a/src/main/java/com/questhelper/steps/NpcStep.java +++ b/src/main/java/com/questhelper/steps/NpcStep.java @@ -25,8 +25,9 @@ */ package com.questhelper.steps; -import com.questhelper.requirements.AbstractRequirement; import com.questhelper.requirements.Requirement; +import com.questhelper.requirements.SpellRequirement; +import com.questhelper.spells.MagicSpell; import com.questhelper.steps.overlay.DirectionArrow; import com.questhelper.steps.tools.QuestPerspective; import java.awt.Graphics2D; @@ -36,7 +37,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.List; import javax.inject.Inject; import lombok.Setter; @@ -119,6 +119,16 @@ public void addAlternateNpcs(Integer... alternateNpcIDs) this.alternateNpcIDs.addAll(Arrays.asList(alternateNpcIDs)); } + public void requireSpellCast(SpellRequirement spellRequirement) + { + addSpellIcon(spellRequirement.getSpell().getSpriteID()); + } + + public void requireSpellCast(MagicSpell spell) + { + addSpellIcon(spell.getSpriteID()); + } + public List allIds() { List ids = new ArrayList<>(); diff --git a/src/main/java/com/questhelper/steps/ObjectStep.java b/src/main/java/com/questhelper/steps/ObjectStep.java index 22ce32cc59..afa8603052 100644 --- a/src/main/java/com/questhelper/steps/ObjectStep.java +++ b/src/main/java/com/questhelper/steps/ObjectStep.java @@ -25,6 +25,8 @@ package com.questhelper.steps; import com.questhelper.requirements.Requirement; +import com.questhelper.requirements.SpellRequirement; +import com.questhelper.spells.MagicSpell; import com.questhelper.steps.overlay.DirectionArrow; import com.questhelper.steps.tools.QuestPerspective; import java.awt.Color; @@ -178,6 +180,16 @@ public void addAlternateObjects(Integer... alternateObjectIDs) this.alternateObjectIDs.addAll(Arrays.asList(alternateObjectIDs)); } + public void requireSpellCast(SpellRequirement spellRequirement) + { + addSpellIcon(spellRequirement.getSpell().getSpriteID()); + } + + public void requireSpellCast(MagicSpell spell) + { + addSpellIcon(spell.getSpriteID()); + } + @Subscribe public void onGameObjectSpawned(GameObjectSpawned event) { @@ -281,7 +293,7 @@ public void makeWorldOverlayHint(Graphics2D graphics, QuestHelperPlugin plugin) } } - if (iconItemID != -1 && object != null && questHelper.getConfig().showSymbolOverlay()) + if (icon != null && object != null && questHelper.getConfig().showSymbolOverlay()) { Shape clickbox = object.getClickbox(); if (clickbox != null && !inCutscene) diff --git a/src/main/java/com/questhelper/steps/QuestStep.java b/src/main/java/com/questhelper/steps/QuestStep.java index 0e4e3643d3..8c7d494223 100644 --- a/src/main/java/com/questhelper/steps/QuestStep.java +++ b/src/main/java/com/questhelper/steps/QuestStep.java @@ -107,6 +107,7 @@ public abstract class QuestStep implements Module protected boolean allowInCutscene = false; protected int iconItemID = -1; + protected int spellIconItemID = -1; protected BufferedImage icon; @Getter @@ -348,6 +349,11 @@ public void addIcon(int iconItemID) this.iconItemID = iconItemID; } + public void addSpellIcon(int spriteID) + { + this.spellIconItemID = spriteID; + } + public void makeWorldOverlayHint(Graphics2D graphics, QuestHelperPlugin plugin) { } @@ -388,6 +394,18 @@ protected void setupIcon() { icon = IconOverlay.createIconImage(itemManager.getImage(iconItemID)); } + else if (spellIconItemID != -1 && icon == null) + { + BufferedImage sprite = spriteManager.getSprite(spellIconItemID, 0); + if (sprite != null) + { + icon = IconOverlay.createIconImage(sprite); + } + else + { + throw new UnsupportedOperationException("Unknown spell sprite ID: " + spellIconItemID); + } + } else if (icon == null) { icon = getQuestImage(); From a4dea0325414c6c577aa33916cd1c740d5dd0fa9 Mon Sep 17 00:00:00 2001 From: Senmori Date: Sun, 7 Feb 2021 07:44:44 -0500 Subject: [PATCH 21/55] Spell icons now have a helpful border drawn around them to indicate which spell to cast. Signed-off-by: Senmori --- .../quests/makinghistory/MakingHistory.java | 4 +- .../questhelper/steps/DetailedQuestStep.java | 1 + .../java/com/questhelper/steps/NpcStep.java | 4 +- .../com/questhelper/steps/ObjectStep.java | 4 +- .../java/com/questhelper/steps/QuestStep.java | 40 ++++++++++++++++--- 5 files changed, 42 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/questhelper/quests/makinghistory/MakingHistory.java b/src/main/java/com/questhelper/quests/makinghistory/MakingHistory.java index b9cad3aff9..218dc02885 100644 --- a/src/main/java/com/questhelper/quests/makinghistory/MakingHistory.java +++ b/src/main/java/com/questhelper/quests/makinghistory/MakingHistory.java @@ -28,6 +28,7 @@ import com.questhelper.QuestHelperQuest; import com.questhelper.requirements.QuestRequirement; import com.questhelper.requirements.Requirement; +import com.questhelper.requirements.SpellRequirement; import com.questhelper.spells.StandardSpell; import com.questhelper.steps.DetailedQuestStep; import com.questhelper.steps.DigStep; @@ -129,7 +130,7 @@ public void setupItemRequirements() spade = new ItemRequirement("Spade", ItemID.SPADE); saphAmulet = new ItemRequirement("Sapphire amulet", ItemID.SAPPHIRE_AMULET); ghostSpeakAmulet = new ItemRequirement("Ghostspeak amulet", ItemID.GHOSTSPEAK_AMULET, 1, true); - ardougneTeleport = StandardSpell.ARDOUGNE_TELEPORT.getSpellRequirement(3); + ardougneTeleport = StandardSpell.SNARE.getSpellRequirement(3); ectophial = new ItemRequirement("Ectophial, or method of getting to Port Phasmatys", ItemID.ECTOPHIAL); ringOfDueling = new ItemRequirement("Ring of Dueling", ItemID.RING_OF_DUELING8); enchantedKey = new ItemRequirement("Enchanted key", ItemID.ENCHANTED_KEY); @@ -184,6 +185,7 @@ public void setupSteps() talkToJorral = new NpcStep(this, NpcID.JORRAL, new WorldPoint(2436, 3346, 0), "Talk to Jorral at the outpost north of West Ardougne."); talkToJorral.addDialogStep("Tell me more."); talkToJorral.addDialogStep("Ok, I'll make a stand for history!"); + talkToJorral.addSpell(((SpellRequirement)ardougneTeleport).getSpell()); talkToSilverMerchant = new NpcStep(this, NpcID.SILVER_MERCHANT_8722, new WorldPoint(2658, 3316, 0), "Talk to the Silver Merchant in the East Ardougne Market."); talkToSilverMerchant.addDialogStep("Ask about the outpost."); dig = new DigStep(this, new WorldPoint(2442, 3140, 0), "Dig at the marked spot north of Castle Wars.", enchantedKey); diff --git a/src/main/java/com/questhelper/steps/DetailedQuestStep.java b/src/main/java/com/questhelper/steps/DetailedQuestStep.java index c373dbd5a9..fd045e453a 100644 --- a/src/main/java/com/questhelper/steps/DetailedQuestStep.java +++ b/src/main/java/com/questhelper/steps/DetailedQuestStep.java @@ -259,6 +259,7 @@ public void renderArrow(Graphics2D graphics) @Override public void makeWidgetOverlayHint(Graphics2D graphics, QuestHelperPlugin plugin) { + super.makeWidgetOverlayHint(graphics, plugin); renderInventory(graphics); if (!hideMinimapLines) { diff --git a/src/main/java/com/questhelper/steps/NpcStep.java b/src/main/java/com/questhelper/steps/NpcStep.java index d39d5e19c2..c4c636dbc5 100644 --- a/src/main/java/com/questhelper/steps/NpcStep.java +++ b/src/main/java/com/questhelper/steps/NpcStep.java @@ -121,12 +121,12 @@ public void addAlternateNpcs(Integer... alternateNpcIDs) public void requireSpellCast(SpellRequirement spellRequirement) { - addSpellIcon(spellRequirement.getSpell().getSpriteID()); + addSpell(spellRequirement.getSpell()); } public void requireSpellCast(MagicSpell spell) { - addSpellIcon(spell.getSpriteID()); + addSpell(spell); } public List allIds() diff --git a/src/main/java/com/questhelper/steps/ObjectStep.java b/src/main/java/com/questhelper/steps/ObjectStep.java index afa8603052..14e64eb7e5 100644 --- a/src/main/java/com/questhelper/steps/ObjectStep.java +++ b/src/main/java/com/questhelper/steps/ObjectStep.java @@ -182,12 +182,12 @@ public void addAlternateObjects(Integer... alternateObjectIDs) public void requireSpellCast(SpellRequirement spellRequirement) { - addSpellIcon(spellRequirement.getSpell().getSpriteID()); + addSpell(spellRequirement.getSpell()); } public void requireSpellCast(MagicSpell spell) { - addSpellIcon(spell.getSpriteID()); + addSpell(spell); } @Subscribe diff --git a/src/main/java/com/questhelper/steps/QuestStep.java b/src/main/java/com/questhelper/steps/QuestStep.java index 8c7d494223..cbcd71d89f 100644 --- a/src/main/java/com/questhelper/steps/QuestStep.java +++ b/src/main/java/com/questhelper/steps/QuestStep.java @@ -34,12 +34,15 @@ import com.questhelper.questhelpers.QuestUtil; import com.questhelper.requirements.Requirement; import com.questhelper.requirements.conditional.ConditionForStep; +import com.questhelper.spells.MagicSpell; import com.questhelper.steps.choice.DialogChoiceStep; import com.questhelper.steps.choice.DialogChoiceSteps; 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.Insets; +import java.awt.Rectangle; import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.Arrays; @@ -49,10 +52,13 @@ import lombok.Getter; import lombok.Setter; import net.runelite.api.Client; +import net.runelite.api.Point; import net.runelite.api.SpriteID; import net.runelite.api.events.VarbitChanged; import net.runelite.api.events.WidgetLoaded; +import net.runelite.api.widgets.Widget; import net.runelite.api.widgets.WidgetID; +import net.runelite.api.widgets.WidgetInfo; import net.runelite.client.callback.ClientThread; import net.runelite.client.eventbus.Subscribe; import net.runelite.client.game.ItemManager; @@ -107,7 +113,7 @@ public abstract class QuestStep implements Module protected boolean allowInCutscene = false; protected int iconItemID = -1; - protected int spellIconItemID = -1; + protected MagicSpell spell; protected BufferedImage icon; @Getter @@ -349,9 +355,9 @@ public void addIcon(int iconItemID) this.iconItemID = iconItemID; } - public void addSpellIcon(int spriteID) + public void addSpell(MagicSpell spell) { - this.spellIconItemID = spriteID; + this.spell = spell; } public void makeWorldOverlayHint(Graphics2D graphics, QuestHelperPlugin plugin) @@ -360,6 +366,28 @@ public void makeWorldOverlayHint(Graphics2D graphics, QuestHelperPlugin plugin) public void makeWidgetOverlayHint(Graphics2D graphics, QuestHelperPlugin plugin) { + if (!plugin.getConfig().showSymbolOverlay()) + { + return; + } + if (spell != null) + { + Widget widget = client.getWidget(spell.getGroupID(), spell.getWidgetID()); + + if (widget != null) + { + if (widget.isHidden()) + { + return; + } + graphics.setColor(plugin.getConfig().targetOverlayColor()); + + Rectangle rect = widget.getBounds(); + int x = (int) rect.getX(); + int y = (int) rect.getY(); + graphics.drawRect(x, y, widget.getWidth(), widget.getHeight()); + } + } } public void setLockedManually(boolean isLocked) @@ -394,16 +422,16 @@ protected void setupIcon() { icon = IconOverlay.createIconImage(itemManager.getImage(iconItemID)); } - else if (spellIconItemID != -1 && icon == null) + else if (spell.getSpriteID() != -1 && icon == null) { - BufferedImage sprite = spriteManager.getSprite(spellIconItemID, 0); + BufferedImage sprite = spriteManager.getSprite(spell.getSpriteID(), 0); if (sprite != null) { icon = IconOverlay.createIconImage(sprite); } else { - throw new UnsupportedOperationException("Unknown spell sprite ID: " + spellIconItemID); + throw new UnsupportedOperationException("Unknown spell sprite ID \"" + spell.getSpriteID() + "\" for spell " + spell.getName()); } } else if (icon == null) From aa264f8d943994edaf544a43c0b8333bd51f62dd Mon Sep 17 00:00:00 2001 From: Senmori Date: Sun, 7 Feb 2021 07:46:44 -0500 Subject: [PATCH 22/55] Undo changes to MakingHistory. Signed-off-by: Senmori --- .../com/questhelper/quests/makinghistory/MakingHistory.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/com/questhelper/quests/makinghistory/MakingHistory.java b/src/main/java/com/questhelper/quests/makinghistory/MakingHistory.java index 218dc02885..488b8f917c 100644 --- a/src/main/java/com/questhelper/quests/makinghistory/MakingHistory.java +++ b/src/main/java/com/questhelper/quests/makinghistory/MakingHistory.java @@ -130,7 +130,7 @@ public void setupItemRequirements() spade = new ItemRequirement("Spade", ItemID.SPADE); saphAmulet = new ItemRequirement("Sapphire amulet", ItemID.SAPPHIRE_AMULET); ghostSpeakAmulet = new ItemRequirement("Ghostspeak amulet", ItemID.GHOSTSPEAK_AMULET, 1, true); - ardougneTeleport = StandardSpell.SNARE.getSpellRequirement(3); + ardougneTeleport = new ItemRequirement("Teleports to Ardougne", ItemID.ARDOUGNE_TELEPORT, 3); ectophial = new ItemRequirement("Ectophial, or method of getting to Port Phasmatys", ItemID.ECTOPHIAL); ringOfDueling = new ItemRequirement("Ring of Dueling", ItemID.RING_OF_DUELING8); enchantedKey = new ItemRequirement("Enchanted key", ItemID.ENCHANTED_KEY); @@ -185,7 +185,6 @@ public void setupSteps() talkToJorral = new NpcStep(this, NpcID.JORRAL, new WorldPoint(2436, 3346, 0), "Talk to Jorral at the outpost north of West Ardougne."); talkToJorral.addDialogStep("Tell me more."); talkToJorral.addDialogStep("Ok, I'll make a stand for history!"); - talkToJorral.addSpell(((SpellRequirement)ardougneTeleport).getSpell()); talkToSilverMerchant = new NpcStep(this, NpcID.SILVER_MERCHANT_8722, new WorldPoint(2658, 3316, 0), "Talk to the Silver Merchant in the East Ardougne Market."); talkToSilverMerchant.addDialogStep("Ask about the outpost."); dig = new DigStep(this, new WorldPoint(2442, 3140, 0), "Dig at the marked spot north of Castle Wars.", enchantedKey); From 1fd64386da4806d8b81702acadf8dcd44ca767c7 Mon Sep 17 00:00:00 2001 From: Senmori Date: Sun, 7 Feb 2021 07:46:57 -0500 Subject: [PATCH 23/55] Remove unused imports. Signed-off-by: Senmori --- .../com/questhelper/quests/makinghistory/MakingHistory.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/com/questhelper/quests/makinghistory/MakingHistory.java b/src/main/java/com/questhelper/quests/makinghistory/MakingHistory.java index 488b8f917c..a8e12798f6 100644 --- a/src/main/java/com/questhelper/quests/makinghistory/MakingHistory.java +++ b/src/main/java/com/questhelper/quests/makinghistory/MakingHistory.java @@ -28,8 +28,6 @@ import com.questhelper.QuestHelperQuest; import com.questhelper.requirements.QuestRequirement; import com.questhelper.requirements.Requirement; -import com.questhelper.requirements.SpellRequirement; -import com.questhelper.spells.StandardSpell; import com.questhelper.steps.DetailedQuestStep; import com.questhelper.steps.DigStep; import com.questhelper.requirements.conditional.Conditions; From 85b3114f6fc9f2018711f416a9066c6c4ee96b63 Mon Sep 17 00:00:00 2001 From: Senmori Date: Sun, 7 Feb 2021 14:16:50 -0500 Subject: [PATCH 24/55] Add javadocs to all new methods and classes. Moved BankItemHolder to the banktab package. Add some additional builders to the StandardSpellBuilder class to specify quests and var conditions. Signed-off-by: Senmori --- .../BankItemHolder.java | 21 ++++++- .../banktab/QuestHelperBankTagService.java | 1 - .../requirements/RuneRequirement.java | 11 +++- .../requirements/SpellRequirement.java | 41 +++++++++++- .../com/questhelper/spells/MagicSpell.java | 6 ++ .../com/questhelper/spells/StandardSpell.java | 16 +++-- .../spells/StandardSpellBuilder.java | 63 +++++++++++++++++++ 7 files changed, 147 insertions(+), 12 deletions(-) rename src/main/java/com/questhelper/{questhelpers => banktab}/BankItemHolder.java (66%) diff --git a/src/main/java/com/questhelper/questhelpers/BankItemHolder.java b/src/main/java/com/questhelper/banktab/BankItemHolder.java similarity index 66% rename from src/main/java/com/questhelper/questhelpers/BankItemHolder.java rename to src/main/java/com/questhelper/banktab/BankItemHolder.java index f91cc889ff..bbd887626b 100644 --- a/src/main/java/com/questhelper/questhelpers/BankItemHolder.java +++ b/src/main/java/com/questhelper/banktab/BankItemHolder.java @@ -24,14 +24,31 @@ * * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ -package com.questhelper.questhelpers; +package com.questhelper.banktab; import com.questhelper.requirements.ItemRequirement; import java.util.List; +import javax.annotation.Nullable; import net.runelite.api.Client; import net.runelite.api.Item; +/** + * Represents anything that holds {@link ItemRequirement}s that are to be used + * for displaying via the {@link QuestBankTab}. + *
+ * Most requirements will not need this interface, however this interface does allow + * that requirement to specify which {@link ItemRequirement} should be displayed + * via the quest bank tab. + */ public interface BankItemHolder { - List getRequirements(Client client, boolean checkConsideringSlotRestrictions, Item[] bankItems); + /** + * Get a list of {@link ItemRequirement} to be displayed. + * + * @param client the {@link Client|} + * @param checkConsideringSlotRestrictions if the client item container checks should respect slot restrictions + * @param bankItems the player's {@link com.questhelper.BankItems}, this can be null + * @return a list of {@link ItemRequirement} that should be displayed + */ + List getRequirements(Client client, boolean checkConsideringSlotRestrictions, @Nullable Item[] bankItems); } diff --git a/src/main/java/com/questhelper/banktab/QuestHelperBankTagService.java b/src/main/java/com/questhelper/banktab/QuestHelperBankTagService.java index 44ac36da60..5a657b3ed7 100644 --- a/src/main/java/com/questhelper/banktab/QuestHelperBankTagService.java +++ b/src/main/java/com/questhelper/banktab/QuestHelperBankTagService.java @@ -26,7 +26,6 @@ import com.questhelper.QuestHelperPlugin; import com.questhelper.panel.PanelDetails; -import com.questhelper.questhelpers.BankItemHolder; import com.questhelper.requirements.ItemRequirement; import com.questhelper.requirements.ItemRequirements; import com.questhelper.requirements.util.LogicType; diff --git a/src/main/java/com/questhelper/requirements/RuneRequirement.java b/src/main/java/com/questhelper/requirements/RuneRequirement.java index a6571e6486..17eea51606 100644 --- a/src/main/java/com/questhelper/requirements/RuneRequirement.java +++ b/src/main/java/com/questhelper/requirements/RuneRequirement.java @@ -26,7 +26,7 @@ */ package com.questhelper.requirements; -import com.questhelper.questhelpers.BankItemHolder; +import com.questhelper.banktab.BankItemHolder; import com.questhelper.questhelpers.QuestUtil; import com.questhelper.requirements.util.InventorySlots; import com.questhelper.spells.Rune; @@ -45,6 +45,10 @@ * Prioritize runes over staves because most quests require some sort of combat gear and that would, * most likely, eliminate stave as an option for most quests. */ + +/** + * Represents a single rune requirement that is used in {@link SpellRequirement}. + */ @Getter public class RuneRequirement extends ItemRequirement implements BankItemHolder { @@ -73,6 +77,11 @@ public RuneRequirement(Rune rune, int costPerCast, int numberOfCasts) } } + /** + * Set the new number of times the spell will be cast. + * + * @param numberOfCasts the new number of casts + */ public void setNumberOfCasts(int numberOfCasts) { this.requiredAmount = costPerCast * numberOfCasts; diff --git a/src/main/java/com/questhelper/requirements/SpellRequirement.java b/src/main/java/com/questhelper/requirements/SpellRequirement.java index aca993a8c9..2279733faa 100644 --- a/src/main/java/com/questhelper/requirements/SpellRequirement.java +++ b/src/main/java/com/questhelper/requirements/SpellRequirement.java @@ -28,7 +28,7 @@ import com.google.common.base.Predicates; import com.questhelper.BankItems; -import com.questhelper.questhelpers.BankItemHolder; +import com.questhelper.banktab.BankItemHolder; import com.questhelper.questhelpers.QuestUtil; import com.questhelper.requirements.util.InventorySlots; import com.questhelper.spells.MagicSpell; @@ -42,6 +42,7 @@ import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; +import javax.annotation.Nonnull; import javax.annotation.Nullable; import lombok.Getter; import net.runelite.api.Client; @@ -61,6 +62,20 @@ * We can ignore the requirements if the player has the tablet because those * have no requirements in order to be used. */ + +/** + * Represents a spell that can be cast. + * This will check if the user has the required magic level, quest requirements, + * any extra items (i.e. unpowered orb for charge orb spells) and any extra requirements + * that are added. + *
+ * If the player has this spell's tablet, then the only requirement that is checked is the + * quest requirement. This is because player's still cannot use a tablet whose + * spell counterpart is locked behind quest progression. + *
+ * This spell requirements prioritizes using tablets over runes/staves since + * it is more compact and has less requirements. + */ public class SpellRequirement extends ItemRequirement implements BankItemHolder { @Getter @@ -97,7 +112,12 @@ public SpellRequirement(MagicSpell spell, int numberOfCasts, Map updateTooltip(); } - public void addRequirement(Requirement requirement) + /** + * Add an additional {@link Requirement}. + * + * @param requirement requirement to add + */ + public void addRequirement(@Nonnull Requirement requirement) { if (requirement instanceof RuneRequirement) { @@ -109,11 +129,20 @@ public void addRequirement(Requirement requirement) } } + /** + * @return a copy of all {@link Requirement} on this requirement + */ public List getRequirements() { return new ArrayList<>(requirements); } + /** + * Set the tablet's item id. If the item id is less than 0, the tablet will be removed + * and this requirement will no longer check for it. + * + * @param itemID the tablet's item id + */ public void setTablet(int itemID) { if (itemID < 0) @@ -126,11 +155,19 @@ public void setTablet(int itemID) } } + /** + * A convenience method to better indicate to not use a tablet for this spell requirement + */ public void requireRunes() { setTablet(-1); } + /** + * Set the number of casts this requirement should account for. + * + * @param numberOfCasts the number of casts + */ public void setNumberOfCasts(int numberOfCasts) { this.numberOfCasts = numberOfCasts; diff --git a/src/main/java/com/questhelper/spells/MagicSpell.java b/src/main/java/com/questhelper/spells/MagicSpell.java index 1998ce588e..eeeab1f0de 100644 --- a/src/main/java/com/questhelper/spells/MagicSpell.java +++ b/src/main/java/com/questhelper/spells/MagicSpell.java @@ -71,5 +71,11 @@ public interface MagicSpell */ SpellRequirement getSpellRequirement(); + /** + * Create a new {@link SpellRequirement} with the given number of casts. + * + * @param numberOfCasts the number of casts + * @return a new {@link SpellRequirement} + */ SpellRequirement getSpellRequirement(int numberOfCasts); } diff --git a/src/main/java/com/questhelper/spells/StandardSpell.java b/src/main/java/com/questhelper/spells/StandardSpell.java index ea389011bb..4633b04682 100644 --- a/src/main/java/com/questhelper/spells/StandardSpell.java +++ b/src/main/java/com/questhelper/spells/StandardSpell.java @@ -26,6 +26,9 @@ */ package com.questhelper.spells; +import static com.questhelper.QuestHelperQuest.PLAGUE_CITY; +import static com.questhelper.QuestHelperQuest.THE_MAGE_ARENA; +import static com.questhelper.QuestHelperQuest.EADGARS_RUSE; import com.questhelper.requirements.SpellRequirement; import com.questhelper.requirements.util.Spellbook; import java.util.Locale; @@ -34,6 +37,7 @@ import static com.questhelper.spells.Rune.*; import net.runelite.api.ItemID; +import net.runelite.api.Varbits; import org.apache.commons.text.WordUtils; @Getter @@ -77,7 +81,7 @@ public enum StandardSpell implements MagicSpell IBAN_BLAST(53, 34, 50, b -> b.rune(DEATH).rune(5, FIRE).item(true, ItemID.IBANS_STAFF, ItemID.IBANS_STAFF_U)), SNARE(320, 35, 50, b -> b.rune(3, NATURE).rune(4, WATER).rune(4, EARTH)), MAGIC_DART(324, 36, 50, b -> b.rune(DEATH).rune(4, MIND)), - ARDOUGNE_TELEPORT(54, 37, 51, b -> b.rune(2, LAW).rune(2, WATER).tablet(ItemID.ARDOUGNE_TELEPORT)), //TODO: QUEST REQ -> PLAGUE CITY + ARDOUGNE_TELEPORT(54, 37, 51, b -> b.rune(2, LAW).rune(2, WATER).tablet(ItemID.ARDOUGNE_TELEPORT).quest(PLAGUE_CITY)), EARTH_BLAST(40, 38, 43, b -> b.rune(DEATH).rune(3, AIR).rune(4, EARTH)), HIGH_LVL_ALCHEMY(41, 39, 55, b -> b.rune(NATURE).rune(5, FIRE)), CHARGE_WATER_ORB(42, 40, 56, b -> b.rune(3, COSMIC).rune(30, WATER).item(ItemID.UNPOWERED_ORB)), @@ -87,10 +91,10 @@ public enum StandardSpell implements MagicSpell FIRE_BLAST(44, 43, 59, b -> b.rune(DEATH).rune(4, AIR).rune(5, FIRE)), CHARGE_EARTH_ORB(45, 44, 60, b -> b.rune(3, COSMIC).rune(30, EARTH).item(ItemID.UNPOWERED_ORB)), BONES_TO_PEACHES(354, 45, 60, b -> b.rune(2, NATURE).rune(2, EARTH).rune(4, WATER)), //TODO: MAGE TRAINING ARENA - SARADOMIN_STRIKE(61, 46, 60, b -> b.rune(2, BLOOD).rune(2, FIRE).rune(4, AIR)), // TODO: MAGE ARENA - CLAWS_OF_GUTHIX(60, 47, 60, b -> b.rune(FIRE).rune(2, BLOOD).rune(4, AIR)), // TODO: MAGE ARENA - FLAMES_OF_ZAMORAK(59, 48, 60, b -> b.rune(AIR).rune(2, BLOOD).rune(4, FIRE)), // TODO: MAGE ARENA - TROLLHEIM_TELEPORT(323, 49, 61, b -> b.rune(2, LAW).rune(2, FIRE)), // TODO: QUEST REQ -> EADGARS RUSE QUEST + SARADOMIN_STRIKE(61, 46, 60, b -> b.rune(2, BLOOD).rune(2, FIRE).rune(4, AIR).quest(THE_MAGE_ARENA)), + CLAWS_OF_GUTHIX(60, 47, 60, b -> b.rune(FIRE).rune(2, BLOOD).rune(4, AIR).quest(THE_MAGE_ARENA)), + FLAMES_OF_ZAMORAK(59, 48, 60, b -> b.rune(AIR).rune(2, BLOOD).rune(4, FIRE).quest(THE_MAGE_ARENA)), + TROLLHEIM_TELEPORT(323, 49, 61, b -> b.rune(2, LAW).rune(2, FIRE).quest(EADGARS_RUSE)), WIND_WAVE(46, 50, 62, b -> b.rune(BLOOD).rune(5, AIR)), CHARGE_FIRE_ORB(47, 51, 63, b -> b.rune(3, COSMIC).rune(30, FIRE).item(ItemID.UNPOWERED_ORB)), APE_ATOLL_TELEPORT(357, 52, 64, b -> b.rune(2, LAW).rune(2, WATER).rune(2, FIRE).item(ItemID.BANANA)), @@ -110,7 +114,7 @@ public enum StandardSpell implements MagicSpell WIND_SURGE(362, 65, 81, b -> b.rune(WRATH).rune(7, AIR)), TELEOTHER_FALADOR(350, 66, 82, b -> b.rune(SOUL).rune(LAW).rune(WATER)), WATER_SURGE(363, 67, 85, b -> b.rune(WRATH).rune(7, AIR).rune(10, WATER)), - TELE_BLOCK(352, 68, 85, b -> b.rune(LAW).rune(DEATH).rune(CHAOS)), //TODO: IN WILDERNESS + TELE_BLOCK(352, 68, 85, b -> b.rune(LAW).rune(DEATH).rune(CHAOS).var(Varbits.IN_WILDERNESS, 1)), TELEPORT_TO_TARGET(359, 69, 85, b -> b.rune(LAW).rune(DEATH).rune(CHAOS)), //TODO: HAVE READ TARGET TELEPORT SCROLL LVL_6_ENCHANT(353, 70, 87, b -> b.rune(COSMIC).rune(20, FIRE).rune(20, EARTH)), ENCHANT_ONYX_BOLT(358, 8, 87, b -> b.rune(DEATH).rune(COSMIC).rune(20, FIRE)), diff --git a/src/main/java/com/questhelper/spells/StandardSpellBuilder.java b/src/main/java/com/questhelper/spells/StandardSpellBuilder.java index d6cbe7a3a9..cb2a8810f7 100644 --- a/src/main/java/com/questhelper/spells/StandardSpellBuilder.java +++ b/src/main/java/com/questhelper/spells/StandardSpellBuilder.java @@ -26,15 +26,22 @@ */ package com.questhelper.spells; +import com.questhelper.QuestHelperQuest; import com.questhelper.requirements.ItemRequirement; import com.questhelper.requirements.ItemRequirements; +import com.questhelper.requirements.QuestRequirement; import com.questhelper.requirements.Requirement; import com.questhelper.requirements.SpellRequirement; +import com.questhelper.requirements.conditional.VarbitCondition; +import com.questhelper.requirements.conditional.VarplayerCondition; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; +import net.runelite.api.QuestState; +import net.runelite.api.VarPlayer; +import net.runelite.api.Varbits; public class StandardSpellBuilder { @@ -132,6 +139,62 @@ public StandardSpellBuilder tablet(int itemID) return this; } + /** + * Add a {@link QuestHelperQuest} requirement to this spell. + * By default, this requires the quest to be finished.
+ * See {@link #quest(QuestHelperQuest, boolean)} for specifying if the quest should only + * be started. + * + * @param quest the quest that should be completed in order to cast this spell + * @return this + */ + public StandardSpellBuilder quest(QuestHelperQuest quest) + { + return quest(quest, true); + } + + /** + * Add a {@link QuestHelperQuest} requirement to this spell. + * If {@param started} is true, this will mean the {@link QuestHelperQuest} is only required to be + * {@link QuestState#IN_PROGRESS}, not {@link QuestState#FINISHED}. + * + * @param quest the quest requirement to add + * @param started if the quest should be started, or finished + * @return this + */ + public StandardSpellBuilder quest(QuestHelperQuest quest, boolean started) + { + QuestState state = started ? QuestState.IN_PROGRESS : QuestState.FINISHED; + requirements.add(new QuestRequirement(quest, state)); + return this; + } + + /** + * Add a {@link Varbits} requirement to this spell. + * + * @param varbit the {@link Varbits} that is required + * @param value the varbit value that is required + * @return this + */ + public StandardSpellBuilder var(Varbits varbit, int value) + { + requirements.add(new VarbitCondition(varbit.getId(), value)); + return this; + } + + /** + * Add a {@link VarPlayer} requirement to this spell + * + * @param varPlayer the {@link VarPlayer} that is needed + * @param value the value that is required + * @return + */ + public StandardSpellBuilder var(VarPlayer varPlayer, int value) + { + requirements.add(new VarplayerCondition(varPlayer.getId(), value)); + return this; + } + /** * @return a new {@link ItemRequirements} containing all this spell information */ From cb59de82b958e83a1963dd5b96d004b5be6fbb8e Mon Sep 17 00:00:00 2001 From: Senmori Date: Sun, 7 Feb 2021 14:44:45 -0500 Subject: [PATCH 25/55] Fix NPE in QuestStep. Don't let requirements/conditions be displayed if they have no display text. Also, made it so the updated tooltip will always have the first line. There were some scenarios that the first line was removed. Signed-off-by: Senmori --- .../questhelper/requirements/SpellRequirement.java | 5 +++-- .../requirements/conditional/ConditionForStep.java | 4 +++- .../java/com/questhelper/spells/StandardSpell.java | 2 +- .../questhelper/spells/StandardSpellBuilder.java | 13 +++++++++++++ src/main/java/com/questhelper/steps/QuestStep.java | 2 +- 5 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/questhelper/requirements/SpellRequirement.java b/src/main/java/com/questhelper/requirements/SpellRequirement.java index 2279733faa..36ff90e35e 100644 --- a/src/main/java/com/questhelper/requirements/SpellRequirement.java +++ b/src/main/java/com/questhelper/requirements/SpellRequirement.java @@ -30,6 +30,7 @@ import com.questhelper.BankItems; import com.questhelper.banktab.BankItemHolder; import com.questhelper.questhelpers.QuestUtil; +import com.questhelper.requirements.conditional.VarplayerCondition; import com.questhelper.requirements.util.InventorySlots; import com.questhelper.spells.MagicSpell; import com.questhelper.spells.Rune; @@ -385,7 +386,6 @@ public List getRequirements(Client client, boolean checkWithSlo public String getUpdatedTooltip(Client client, BankItems bankItems) { StringBuilder text = new StringBuilder(); - setTooltip("This spell requires: "); if (tabletRequirement != null) { AtomicInteger count = new AtomicInteger(); @@ -398,9 +398,10 @@ public String getUpdatedTooltip(Client client, BankItems bankItems) return count.get() > 0 ? text.toString() : null; // no requirements to use a tablet } getNonItemRequirements(this.requirements).stream() + .filter(req -> !req.getDisplayText().isEmpty()) .filter(req -> !req.check(client)) .forEach(req -> text.append(req.getDisplayText()).append("\n")); - return text.toString(); + return text.insert(0, "This spell requires: \n").toString(); } private List getItemRequirements(List requirements) diff --git a/src/main/java/com/questhelper/requirements/conditional/ConditionForStep.java b/src/main/java/com/questhelper/requirements/conditional/ConditionForStep.java index 858d833e81..fd43fdcd29 100644 --- a/src/main/java/com/questhelper/requirements/conditional/ConditionForStep.java +++ b/src/main/java/com/questhelper/requirements/conditional/ConditionForStep.java @@ -28,6 +28,7 @@ import com.questhelper.requirements.util.LogicType; import java.util.ArrayList; import java.util.List; +import javax.annotation.Nonnull; import lombok.Getter; import lombok.Setter; import net.runelite.api.Client; @@ -54,9 +55,10 @@ public void loadingHandler() { } + @Nonnull @Override public String getDisplayText() // conditions don't need display text (yet?) { - return null; + return ""; } } diff --git a/src/main/java/com/questhelper/spells/StandardSpell.java b/src/main/java/com/questhelper/spells/StandardSpell.java index 4633b04682..950158ebad 100644 --- a/src/main/java/com/questhelper/spells/StandardSpell.java +++ b/src/main/java/com/questhelper/spells/StandardSpell.java @@ -103,7 +103,7 @@ public enum StandardSpell implements MagicSpell VULNERABILITY(56, 55, 66, b -> b.rune(SOUL).rune(5, WATER).rune(5, EARTH)), LVL_5_ENCHANT(50, 56, 68, b -> b.rune(COSMIC).rune(15, WATER).rune(15, EARTH)), ENCHANT_DRAGONSTONE_BOLT(358, 8, 68, b -> b.rune(SOUL).rune(COSMIC).rune(12, EARTH)), - KOUREND_CASTLE_TELEPORT(360, 57, 69, b -> b.rune(2, SOUL).rune(2, LAW).rune(4,WATER).rune(5, FIRE)), //TODO: UNLOCKED VIA BOOK + KOUREND_CASTLE_TELEPORT(360, 57, 69, b -> b.rune(2, SOUL).rune(2, LAW).rune(4,WATER).rune(5, FIRE).varbit(10019, 1)), //TODO: UNLOCKED VIA BOOK EARTH_WAVE(51, 58, 70, b -> b.rune(BLOOD).rune(5, AIR).rune(7, EARTH)), ENFEEBLE(57, 59, 73, b -> b.rune(SOUL).rune(8, WATER).rune(8, EARTH)), TELEOTHER_LUMBRIDGE(349, 60, 74, b -> b.rune(SOUL).rune(LAW).rune(EARTH)), diff --git a/src/main/java/com/questhelper/spells/StandardSpellBuilder.java b/src/main/java/com/questhelper/spells/StandardSpellBuilder.java index cb2a8810f7..a8162f66be 100644 --- a/src/main/java/com/questhelper/spells/StandardSpellBuilder.java +++ b/src/main/java/com/questhelper/spells/StandardSpellBuilder.java @@ -195,6 +195,19 @@ public StandardSpellBuilder var(VarPlayer varPlayer, int value) return this; } + /** + * Set a varbit that is not defined via the {@link Varbits} or {@link VarPlayer} enum(s). + * + * @param varbit the varbit to test for + * @param value the value it should be + * @return this + */ + public StandardSpellBuilder varbit(int varbit, int value) + { + requirements.add(new VarbitCondition(varbit, value)); + return this; + } + /** * @return a new {@link ItemRequirements} containing all this spell information */ diff --git a/src/main/java/com/questhelper/steps/QuestStep.java b/src/main/java/com/questhelper/steps/QuestStep.java index cbcd71d89f..9657208733 100644 --- a/src/main/java/com/questhelper/steps/QuestStep.java +++ b/src/main/java/com/questhelper/steps/QuestStep.java @@ -422,7 +422,7 @@ protected void setupIcon() { icon = IconOverlay.createIconImage(itemManager.getImage(iconItemID)); } - else if (spell.getSpriteID() != -1 && icon == null) + else if (spell != null && spell.getSpriteID() != -1 && icon == null) { BufferedImage sprite = spriteManager.getSprite(spell.getSpriteID(), 0); if (sprite != null) From de6b12943aa42658cc865665b736b6decba4f624 Mon Sep 17 00:00:00 2001 From: Senmori Date: Sun, 7 Feb 2021 14:46:20 -0500 Subject: [PATCH 26/55] Fix potential bug in DigStep where if the player's inventory was full it would still highlight the shovel even if the step had no expected item. Signed-off-by: Senmori --- src/main/java/com/questhelper/steps/DigStep.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/questhelper/steps/DigStep.java b/src/main/java/com/questhelper/steps/DigStep.java index 0497cb6b3f..c1272c6399 100644 --- a/src/main/java/com/questhelper/steps/DigStep.java +++ b/src/main/java/com/questhelper/steps/DigStep.java @@ -44,7 +44,7 @@ public class DigStep extends DetailedQuestStep { private final ItemRequirement SPADE = new ItemRequirement("Spade", ItemID.SPADE); - private Predicate expectedItemPredicate = i -> i.getId() == -1; + private Predicate expectedItemPredicate = i -> true; private boolean hasExpectedItem = false; public DigStep(QuestHelper questHelper, WorldPoint worldPoint, String text, Requirement... requirements) { From d2d275a6993e9fd13ff386011d7e1bff164df384 Mon Sep 17 00:00:00 2001 From: Senmori Date: Mon, 8 Feb 2021 15:13:38 -0500 Subject: [PATCH 27/55] Fix copyright --- .../com/questhelper/requirements/SpellbookRequirement.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/com/questhelper/requirements/SpellbookRequirement.java b/src/main/java/com/questhelper/requirements/SpellbookRequirement.java index abc7e4d577..ad83f37b12 100644 --- a/src/main/java/com/questhelper/requirements/SpellbookRequirement.java +++ b/src/main/java/com/questhelper/requirements/SpellbookRequirement.java @@ -1,6 +1,6 @@ /* * - * * Copyright (c) 2021, Senmori + * * Copyright (c) 2021, Zoinkwiz * * All rights reserved. * * * * Redistribution and use in source and binary forms, with or without @@ -26,7 +26,6 @@ */ package com.questhelper.requirements; -import com.questhelper.requirements.AbstractRequirement; import com.questhelper.requirements.util.Spellbook; import net.runelite.api.Client; From 6be927b7453aafd46f35b74da4369849bbedb6ca Mon Sep 17 00:00:00 2001 From: Senmori Date: Mon, 8 Feb 2021 15:19:16 -0500 Subject: [PATCH 28/55] Remove TODO for kourend castle teleport because we have the varbit for it now. --- src/main/java/com/questhelper/spells/StandardSpell.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/questhelper/spells/StandardSpell.java b/src/main/java/com/questhelper/spells/StandardSpell.java index 950158ebad..52734b45d7 100644 --- a/src/main/java/com/questhelper/spells/StandardSpell.java +++ b/src/main/java/com/questhelper/spells/StandardSpell.java @@ -103,7 +103,7 @@ public enum StandardSpell implements MagicSpell VULNERABILITY(56, 55, 66, b -> b.rune(SOUL).rune(5, WATER).rune(5, EARTH)), LVL_5_ENCHANT(50, 56, 68, b -> b.rune(COSMIC).rune(15, WATER).rune(15, EARTH)), ENCHANT_DRAGONSTONE_BOLT(358, 8, 68, b -> b.rune(SOUL).rune(COSMIC).rune(12, EARTH)), - KOUREND_CASTLE_TELEPORT(360, 57, 69, b -> b.rune(2, SOUL).rune(2, LAW).rune(4,WATER).rune(5, FIRE).varbit(10019, 1)), //TODO: UNLOCKED VIA BOOK + KOUREND_CASTLE_TELEPORT(360, 57, 69, b -> b.rune(2, SOUL).rune(2, LAW).rune(4,WATER).rune(5, FIRE).varbit(10019, 1)), EARTH_WAVE(51, 58, 70, b -> b.rune(BLOOD).rune(5, AIR).rune(7, EARTH)), ENFEEBLE(57, 59, 73, b -> b.rune(SOUL).rune(8, WATER).rune(8, EARTH)), TELEOTHER_LUMBRIDGE(349, 60, 74, b -> b.rune(SOUL).rune(LAW).rune(EARTH)), From aa224674ad28136e510af361fed5c2d8b56b4b89 Mon Sep 17 00:00:00 2001 From: Senmori Date: Mon, 8 Feb 2021 16:22:25 -0500 Subject: [PATCH 29/55] Use apache convenience methods for checking if tablet requirement needs set. --- .../requirements/SpellRequirement.java | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/questhelper/requirements/SpellRequirement.java b/src/main/java/com/questhelper/requirements/SpellRequirement.java index 36ff90e35e..bea316ae0c 100644 --- a/src/main/java/com/questhelper/requirements/SpellRequirement.java +++ b/src/main/java/com/questhelper/requirements/SpellRequirement.java @@ -51,6 +51,7 @@ import net.runelite.api.ItemComposition; import net.runelite.api.Skill; import net.runelite.client.ui.overlay.components.LineComponent; +import org.apache.commons.lang3.StringUtils; /* * LOGIC: @@ -181,6 +182,10 @@ public void setNumberOfCasts(int numberOfCasts) RuneRequirement runeRequirement = new RuneRequirement(rune, costPerCast, numberOfCasts); runeRequirements.add(runeRequirement); } + if (tabletRequirement != null) + { + tabletRequirement.setQuantity(this.numberOfCasts); + } } @Override @@ -372,7 +377,8 @@ public List getRequirements(Client client, boolean checkWithSlo return Collections.singletonList(tabletRequirement); } } - List requirements = runeRequirements.stream() + List requirements = runeRequirements + .stream() .map(req -> req.getRequirements(client, checkWithSlotRestrictions, bankItems)) .flatMap(Collection::stream) .collect(Collectors.toCollection(LinkedList::new)); @@ -386,8 +392,8 @@ public List getRequirements(Client client, boolean checkWithSlo public String getUpdatedTooltip(Client client, BankItems bankItems) { StringBuilder text = new StringBuilder(); - if (tabletRequirement != null) - { + if (tabletRequirement != null && tabletRequirement.check(client, false, bankItems.getItems())) + { // only show tooltip for tablet if they actually have it AtomicInteger count = new AtomicInteger(); getNonItemRequirements(this.requirements).stream() .filter(r -> r instanceof QuestRequirement) @@ -446,24 +452,19 @@ private void updateInternalState(Client client, List requiremen private void updateTooltip() { setTooltip("This spell requires: "); - getNonItemRequirements(this.requirements).stream() - .forEach(req -> appendToTooltip(req.getDisplayText())); + getNonItemRequirements(this.requirements).forEach(req -> appendToTooltip(req.getDisplayText())); } private void updateItemRequirements(Client client, List requirements) { - for (ItemRequirement requirement : requirements) - { - if (requirement.getName() == null || requirement.getName().isEmpty()) - { - requirement.setName(client.getItemDefinition(requirement.getId()).getName()); - } - } + requirements.stream() + .filter(req -> StringUtils.isBlank(req.getName())) + .forEach(req -> req.setName(client.getItemDefinition(req.getId()).getName())); } private void updateTabletRequirement(Client client) { - if (tabletRequirement != null && (tabletRequirement.getName() == null || tabletRequirement.getName().isEmpty())) + if (tabletRequirement != null && StringUtils.isBlank(tabletRequirement.getName())) { ItemComposition tablet = client.getItemDefinition(tabletRequirement.getId()); tabletRequirement = new ItemRequirement(tablet.getName(), tablet.getId(), this.numberOfCasts); From 8d70f3ec989dd27c1d6f0b4f8f6d0551d650361b Mon Sep 17 00:00:00 2001 From: Senmori Date: Mon, 8 Feb 2021 17:57:11 -0500 Subject: [PATCH 30/55] Fix merge conflicts. Use WordUtils to capitalize enum names in QuestRequirement. --- src/main/java/com/questhelper/banktab/BankItemHolder.java | 6 +++--- .../com/questhelper/banktab/QuestHelperBankTagService.java | 3 +-- .../questhelper/requirements/quest/QuestRequirement.java | 5 +++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/questhelper/banktab/BankItemHolder.java b/src/main/java/com/questhelper/banktab/BankItemHolder.java index bbd887626b..a91be1222d 100644 --- a/src/main/java/com/questhelper/banktab/BankItemHolder.java +++ b/src/main/java/com/questhelper/banktab/BankItemHolder.java @@ -26,7 +26,7 @@ */ package com.questhelper.banktab; -import com.questhelper.requirements.ItemRequirement; +import com.questhelper.requirements.item.ItemRequirement; import java.util.List; import javax.annotation.Nullable; import net.runelite.api.Client; @@ -45,10 +45,10 @@ public interface BankItemHolder /** * Get a list of {@link ItemRequirement} to be displayed. * - * @param client the {@link Client|} + * @param client the {@link Client} * @param checkConsideringSlotRestrictions if the client item container checks should respect slot restrictions * @param bankItems the player's {@link com.questhelper.BankItems}, this can be null - * @return a list of {@link ItemRequirement} that should be displayed + * @return a list of {@link ItemRequirement} that should be displayed, or an empty list if none are found */ List getRequirements(Client client, boolean checkConsideringSlotRestrictions, @Nullable Item[] bankItems); } diff --git a/src/main/java/com/questhelper/banktab/QuestHelperBankTagService.java b/src/main/java/com/questhelper/banktab/QuestHelperBankTagService.java index 9347906d3c..da18abb6a3 100644 --- a/src/main/java/com/questhelper/banktab/QuestHelperBankTagService.java +++ b/src/main/java/com/questhelper/banktab/QuestHelperBankTagService.java @@ -28,11 +28,10 @@ import com.questhelper.panel.PanelDetails; import com.questhelper.requirements.item.ItemRequirement; import com.questhelper.requirements.item.ItemRequirements; -import com.questhelper.requirements.ItemRequirement; -import com.questhelper.requirements.ItemRequirements; import com.questhelper.requirements.util.LogicType; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; diff --git a/src/main/java/com/questhelper/requirements/quest/QuestRequirement.java b/src/main/java/com/questhelper/requirements/quest/QuestRequirement.java index 535fe3ae16..44f27c3f6c 100644 --- a/src/main/java/com/questhelper/requirements/quest/QuestRequirement.java +++ b/src/main/java/com/questhelper/requirements/quest/QuestRequirement.java @@ -34,6 +34,7 @@ import lombok.Getter; import net.runelite.api.Client; import net.runelite.api.QuestState; +import org.apache.commons.text.WordUtils; /** * Requirement that checks if a {@link net.runelite.api.Quest} has a certain state. @@ -94,7 +95,7 @@ public String getDisplayText() { return displayText; } - String text = Character.toUpperCase(requiredState.name().charAt(0)) + requiredState.name().toLowerCase(Locale.ROOT).substring(1); - return text.replaceAll("_", " ") + " " + quest.getName(); + String text = WordUtils.capitalizeFully(requiredState.name().toLowerCase(Locale.ROOT).replaceAll("_", " ")); + return text + " " + quest.getName(); } } From 8246cc47596cd179e72bc802f552010034b4ac56 Mon Sep 17 00:00:00 2001 From: Senmori Date: Mon, 8 Feb 2021 18:20:21 -0500 Subject: [PATCH 31/55] Fix merge conflicts. --- .../requirements/RuneRequirement.java | 1 + .../requirements/SpellRequirement.java | 5 +- .../requirements/SpellbookRequirement.java | 53 ------------------- .../requirements/item/ItemRequirement.java | 2 +- .../spells/StandardSpellBuilder.java | 16 +++--- 5 files changed, 14 insertions(+), 63 deletions(-) delete mode 100644 src/main/java/com/questhelper/requirements/SpellbookRequirement.java diff --git a/src/main/java/com/questhelper/requirements/RuneRequirement.java b/src/main/java/com/questhelper/requirements/RuneRequirement.java index 17eea51606..5e7d931cec 100644 --- a/src/main/java/com/questhelper/requirements/RuneRequirement.java +++ b/src/main/java/com/questhelper/requirements/RuneRequirement.java @@ -28,6 +28,7 @@ import com.questhelper.banktab.BankItemHolder; import com.questhelper.questhelpers.QuestUtil; +import com.questhelper.requirements.item.ItemRequirement; import com.questhelper.requirements.util.InventorySlots; import com.questhelper.spells.Rune; import java.util.Collections; diff --git a/src/main/java/com/questhelper/requirements/SpellRequirement.java b/src/main/java/com/questhelper/requirements/SpellRequirement.java index bea316ae0c..cbbe332235 100644 --- a/src/main/java/com/questhelper/requirements/SpellRequirement.java +++ b/src/main/java/com/questhelper/requirements/SpellRequirement.java @@ -30,7 +30,10 @@ import com.questhelper.BankItems; import com.questhelper.banktab.BankItemHolder; import com.questhelper.questhelpers.QuestUtil; -import com.questhelper.requirements.conditional.VarplayerCondition; +import com.questhelper.requirements.item.ItemRequirement; +import com.questhelper.requirements.player.SkillRequirement; +import com.questhelper.requirements.player.SpellbookRequirement; +import com.questhelper.requirements.quest.QuestRequirement; import com.questhelper.requirements.util.InventorySlots; import com.questhelper.spells.MagicSpell; import com.questhelper.spells.Rune; diff --git a/src/main/java/com/questhelper/requirements/SpellbookRequirement.java b/src/main/java/com/questhelper/requirements/SpellbookRequirement.java deleted file mode 100644 index ad83f37b12..0000000000 --- a/src/main/java/com/questhelper/requirements/SpellbookRequirement.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * - * * Copyright (c) 2021, Zoinkwiz - * * 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 com.questhelper.requirements; - -import com.questhelper.requirements.util.Spellbook; -import net.runelite.api.Client; - -public class SpellbookRequirement extends AbstractRequirement -{ - private static final int SPELLBOOK_VARBIT = 4070; - private final Spellbook spellBook; - - public SpellbookRequirement(Spellbook spellBook) - { - this.spellBook = spellBook; - } - - @Override - public boolean check(Client client) - { - return spellBook.check(client, SPELLBOOK_VARBIT); - } - - @Override - public String getDisplayText() - { - return "You must be on the " + spellBook.getName() + " spellbook."; - } -} diff --git a/src/main/java/com/questhelper/requirements/item/ItemRequirement.java b/src/main/java/com/questhelper/requirements/item/ItemRequirement.java index d6eb8bf8c2..4cfa87c95b 100644 --- a/src/main/java/com/questhelper/requirements/item/ItemRequirement.java +++ b/src/main/java/com/questhelper/requirements/item/ItemRequirement.java @@ -52,7 +52,7 @@ public class ItemRequirement extends AbstractRequirement @Getter private final int id; - @Setter(AccessLevel.PROTECTED) + @Setter private String name; @Setter diff --git a/src/main/java/com/questhelper/spells/StandardSpellBuilder.java b/src/main/java/com/questhelper/spells/StandardSpellBuilder.java index a8162f66be..67f1c004c3 100644 --- a/src/main/java/com/questhelper/spells/StandardSpellBuilder.java +++ b/src/main/java/com/questhelper/spells/StandardSpellBuilder.java @@ -27,13 +27,13 @@ package com.questhelper.spells; import com.questhelper.QuestHelperQuest; -import com.questhelper.requirements.ItemRequirement; -import com.questhelper.requirements.ItemRequirements; -import com.questhelper.requirements.QuestRequirement; import com.questhelper.requirements.Requirement; import com.questhelper.requirements.SpellRequirement; -import com.questhelper.requirements.conditional.VarbitCondition; -import com.questhelper.requirements.conditional.VarplayerCondition; +import com.questhelper.requirements.item.ItemRequirement; +import com.questhelper.requirements.item.ItemRequirements; +import com.questhelper.requirements.quest.QuestRequirement; +import com.questhelper.requirements.var.VarbitRequirement; +import com.questhelper.requirements.var.VarplayerRequirement; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedList; @@ -178,7 +178,7 @@ public StandardSpellBuilder quest(QuestHelperQuest quest, boolean started) */ public StandardSpellBuilder var(Varbits varbit, int value) { - requirements.add(new VarbitCondition(varbit.getId(), value)); + requirements.add(new VarbitRequirement(varbit.getId(), value)); return this; } @@ -191,7 +191,7 @@ public StandardSpellBuilder var(Varbits varbit, int value) */ public StandardSpellBuilder var(VarPlayer varPlayer, int value) { - requirements.add(new VarplayerCondition(varPlayer.getId(), value)); + requirements.add(new VarplayerRequirement(varPlayer.getId(), value)); return this; } @@ -204,7 +204,7 @@ public StandardSpellBuilder var(VarPlayer varPlayer, int value) */ public StandardSpellBuilder varbit(int varbit, int value) { - requirements.add(new VarbitCondition(varbit, value)); + requirements.add(new VarbitRequirement(varbit, value)); return this; } From 813fb5486379b8ae6b304c24a8c2f80c2486dd12 Mon Sep 17 00:00:00 2001 From: Senmori Date: Tue, 9 Feb 2021 20:21:37 -0500 Subject: [PATCH 32/55] Get staffs to work with combo runes and not add duplicate runes if the staff can act as a source of that rune. --- .../com/questhelper/QuestHelperConfig.java | 12 + .../questhelper/banktab/BankItemHolder.java | 7 +- .../banktab/QuestHelperBankTagService.java | 4 +- .../DarknessOfHallowvale.java | 5 +- .../requirements/item/ItemRequirement.java | 46 +--- .../{ => magic}/RuneRequirement.java | 76 ++---- .../{ => magic}/SpellRequirement.java | 223 ++++++++++++------ .../com/questhelper/spells/MagicSpell.java | 2 +- .../java/com/questhelper/spells/Rune.java | 56 ++--- .../questhelper/spells/SearchPreference.java | 51 ++++ .../java/com/questhelper/spells/Staff.java | 91 +++++++ .../com/questhelper/spells/StandardSpell.java | 2 +- .../spells/StandardSpellBuilder.java | 4 +- .../java/com/questhelper/steps/NpcStep.java | 2 +- .../com/questhelper/steps/ObjectStep.java | 2 +- 15 files changed, 382 insertions(+), 201 deletions(-) rename src/main/java/com/questhelper/requirements/{ => magic}/RuneRequirement.java (62%) rename src/main/java/com/questhelper/requirements/{ => magic}/SpellRequirement.java (70%) create mode 100644 src/main/java/com/questhelper/spells/SearchPreference.java create mode 100644 src/main/java/com/questhelper/spells/Staff.java diff --git a/src/main/java/com/questhelper/QuestHelperConfig.java b/src/main/java/com/questhelper/QuestHelperConfig.java index e8d6a6391d..962f5e9b2b 100644 --- a/src/main/java/com/questhelper/QuestHelperConfig.java +++ b/src/main/java/com/questhelper/QuestHelperConfig.java @@ -27,6 +27,7 @@ import com.questhelper.panel.questorders.QuestOrders; import com.questhelper.questhelpers.Quest; import com.questhelper.questhelpers.QuestHelper; +import com.questhelper.spells.SearchPreference; import java.awt.Color; import java.util.Collection; import java.util.Comparator; @@ -128,6 +129,17 @@ default boolean showSymbolOverlay() return true; } + @ConfigItem( + keyName = "bankSearchPreference", + name = "Which spell components to prefer", + description = "Choose whether runes or staves should be preferred when filtering spell components.", + hidden = true + ) + default SearchPreference bankFilterSearchPreference() + { + return SearchPreference.STAVES; + } + @ConfigSection( position = 1, name = "Colors", diff --git a/src/main/java/com/questhelper/banktab/BankItemHolder.java b/src/main/java/com/questhelper/banktab/BankItemHolder.java index a91be1222d..5be10091fe 100644 --- a/src/main/java/com/questhelper/banktab/BankItemHolder.java +++ b/src/main/java/com/questhelper/banktab/BankItemHolder.java @@ -26,7 +26,9 @@ */ package com.questhelper.banktab; +import com.questhelper.QuestHelperConfig; import com.questhelper.requirements.item.ItemRequirement; +import com.questhelper.spells.SearchPreference; import java.util.List; import javax.annotation.Nullable; import net.runelite.api.Client; @@ -46,9 +48,8 @@ public interface BankItemHolder * Get a list of {@link ItemRequirement} to be displayed. * * @param client the {@link Client} - * @param checkConsideringSlotRestrictions if the client item container checks should respect slot restrictions - * @param bankItems the player's {@link com.questhelper.BankItems}, this can be null + * @param config the {@link QuestHelperConfig} * @return a list of {@link ItemRequirement} that should be displayed, or an empty list if none are found */ - List getRequirements(Client client, boolean checkConsideringSlotRestrictions, @Nullable Item[] bankItems); + List getRequirements(Client client, QuestHelperConfig config); } diff --git a/src/main/java/com/questhelper/banktab/QuestHelperBankTagService.java b/src/main/java/com/questhelper/banktab/QuestHelperBankTagService.java index da18abb6a3..e5a139a795 100644 --- a/src/main/java/com/questhelper/banktab/QuestHelperBankTagService.java +++ b/src/main/java/com/questhelper/banktab/QuestHelperBankTagService.java @@ -29,6 +29,7 @@ import com.questhelper.requirements.item.ItemRequirement; import com.questhelper.requirements.item.ItemRequirements; import com.questhelper.requirements.util.LogicType; +import com.questhelper.spells.SearchPreference; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -132,11 +133,10 @@ private void getItemsFromRequirement(BankTabItems pluginItems, ItemRequirement i else if (itemRequirement instanceof BankItemHolder) { BankItemHolder holder = (BankItemHolder) itemRequirement; - final Item[] items = plugin.getBankItems().getItems(); // Force run on client thread even though it's not as responsive as not doing that, however it // ensures we run on the client thread and never run into threading issues. plugin.getClientThread().invoke(() -> { - List reqs = holder.getRequirements(plugin.getClient(), false, items); + List reqs = holder.getRequirements(plugin.getClient(), plugin.getConfig()); makeBankHolderItems(reqs, pluginItems); // callback because we can't halt on the client thread }); } diff --git a/src/main/java/com/questhelper/quests/darknessofhallowvale/DarknessOfHallowvale.java b/src/main/java/com/questhelper/quests/darknessofhallowvale/DarknessOfHallowvale.java index b26bf93602..3615209579 100644 --- a/src/main/java/com/questhelper/quests/darknessofhallowvale/DarknessOfHallowvale.java +++ b/src/main/java/com/questhelper/quests/darknessofhallowvale/DarknessOfHallowvale.java @@ -30,6 +30,7 @@ import com.questhelper.Zone; import com.questhelper.panel.PanelDetails; import com.questhelper.questhelpers.BasicQuestHelper; +import com.questhelper.requirements.magic.SpellRequirement; import com.questhelper.requirements.player.InInstanceRequirement; import com.questhelper.requirements.item.ItemRequirement; import com.questhelper.requirements.item.ItemRequirements; @@ -44,6 +45,7 @@ import com.questhelper.requirements.util.LogicType; import com.questhelper.requirements.util.Operation; import com.questhelper.requirements.util.Spellbook; +import com.questhelper.spells.StandardSpell; import com.questhelper.steps.ConditionalStep; import com.questhelper.steps.DetailedQuestStep; import com.questhelper.steps.NpcStep; @@ -766,7 +768,8 @@ public List getItemRequirements() @Override public List getItemRecommended() { - return Arrays.asList(lawRune, airRune); + SpellRequirement spell = StandardSpell.EARTH_SURGE.getSpellRequirement(); + return Arrays.asList(lawRune, airRune, spell); } @Override diff --git a/src/main/java/com/questhelper/requirements/item/ItemRequirement.java b/src/main/java/com/questhelper/requirements/item/ItemRequirement.java index 4cfa87c95b..58c6c3a958 100644 --- a/src/main/java/com/questhelper/requirements/item/ItemRequirement.java +++ b/src/main/java/com/questhelper/requirements/item/ItemRequirement.java @@ -27,6 +27,7 @@ package com.questhelper.requirements.item; import com.questhelper.BankItems; +import com.questhelper.ItemSearch; import com.questhelper.requirements.AbstractRequirement; import com.questhelper.requirements.util.InventorySlots; import java.awt.Color; @@ -73,6 +74,7 @@ public class ItemRequirement extends AbstractRequirement protected final List alternateItems = new ArrayList<>(); + @Getter @Setter protected boolean exclusiveToOneItemType; @@ -248,7 +250,7 @@ else if (this.check(client)) /** Find the first item that this requirement allows that the player has, or -1 if they don't have any item(s) */ private int findItemID(Client client, boolean checkConsideringSlotRestrictions) { - int remainder = getRequiredItemDifference(client, id, checkConsideringSlotRestrictions, null); + int remainder = getRequiredItemDifference(client, id, checkConsideringSlotRestrictions, false); if (remainder <= 0) { return id; @@ -260,7 +262,7 @@ private int findItemID(Client client, boolean checkConsideringSlotRestrictions) { remainder = quantity; } - remainder -= (quantity - getRequiredItemDifference(client, alternate, checkConsideringSlotRestrictions, null)); + remainder -= (quantity - getRequiredItemDifference(client, alternate, checkConsideringSlotRestrictions, false)); if (remainder <= 0) { return alternate; @@ -333,7 +335,7 @@ public boolean check(Client client, boolean checkConsideringSlotRestrictions, It { remainder = quantity; } - remainder -= (quantity - getRequiredItemDifference(client, alternate, checkConsideringSlotRestrictions, items)); + remainder -= (quantity - getRequiredItemDifference(client, alternate, checkConsideringSlotRestrictions, items != null)); if (remainder <= 0) { return true; @@ -346,47 +348,23 @@ public boolean check(Client client, boolean checkConsideringSlotRestrictions, It * Get the difference between the required quantity for this requirement and the amount the client has. * Any value <= 0 indicates they have the required amount */ - public int getRequiredItemDifference(Client client, int itemID, boolean checkConsideringSlotRestrictions, Item[] items) + public int getRequiredItemDifference(Client client, int itemID, boolean respectSlotRestrictions, boolean checkBank) { - ItemContainer equipped = client.getItemContainer(InventoryID.EQUIPMENT); int tempQuantity = quantity; + tempQuantity -= ItemSearch.getItemAmountExact(client, InventoryID.EQUIPMENT, itemID); - if (equipped != null) - { - tempQuantity -= getNumMatches(equipped, itemID); - } - - if (!checkConsideringSlotRestrictions || !equip) + if (!respectSlotRestrictions || !equip) { - ItemContainer inventory = client.getItemContainer(InventoryID.INVENTORY); - if (inventory != null) - { - tempQuantity -= getNumMatches(inventory, itemID); - } + tempQuantity -= ItemSearch.getItemAmountExact(client, InventoryID.INVENTORY, itemID); } - if (items != null) + if (checkBank) { - tempQuantity -= getNumMatches(items, itemID); + tempQuantity -= ItemSearch.getItemAmountExact(client, InventoryID.BANK, itemID); } - return tempQuantity; } - public int getNumMatches(ItemContainer items, int itemID) - { - return getNumMatches(items.getItems(), itemID); - } - - public int getNumMatches(Item[] items, int itemID) - { - return Stream.of(items) - .filter(Objects::nonNull) // Runelite loves to sneak in null objects - .filter(i -> i.getId() == itemID) - .mapToInt(Item::getQuantity) - .sum(); - } - public boolean check(Client client) { return check(client, false); @@ -417,4 +395,6 @@ public String getUpdatedTooltip(Client client, BankItems bankItems) { return null; } + + } diff --git a/src/main/java/com/questhelper/requirements/RuneRequirement.java b/src/main/java/com/questhelper/requirements/magic/RuneRequirement.java similarity index 62% rename from src/main/java/com/questhelper/requirements/RuneRequirement.java rename to src/main/java/com/questhelper/requirements/magic/RuneRequirement.java index 5e7d931cec..ee993fa700 100644 --- a/src/main/java/com/questhelper/requirements/RuneRequirement.java +++ b/src/main/java/com/questhelper/requirements/magic/RuneRequirement.java @@ -1,6 +1,6 @@ /* * - * * Copyright (c) 2021, Senmori + * * Copyright (c) 2021 * * All rights reserved. * * * * Redistribution and use in source and binary forms, with or without @@ -24,13 +24,15 @@ * * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ -package com.questhelper.requirements; +package com.questhelper.requirements.magic; +import com.questhelper.ItemSearch; +import com.questhelper.QuestHelperConfig; import com.questhelper.banktab.BankItemHolder; import com.questhelper.questhelpers.QuestUtil; import com.questhelper.requirements.item.ItemRequirement; -import com.questhelper.requirements.util.InventorySlots; import com.questhelper.spells.Rune; +import com.questhelper.spells.Staff; import java.util.Collections; import java.util.LinkedList; import java.util.List; @@ -58,7 +60,7 @@ public class RuneRequirement extends ItemRequirement implements BankItemHolder private int requiredAmount; private final ItemRequirement runeItemRequirement; - private ItemRequirement staffItemRequirement; + private StaffItemRequirement staffItemRequirement; public RuneRequirement(Rune rune, int costPerCast) { @@ -71,10 +73,10 @@ public RuneRequirement(Rune rune, int costPerCast, int numberOfCasts) this.rune = rune; this.costPerCast = costPerCast; this.requiredAmount = costPerCast * numberOfCasts; - this.runeItemRequirement = new ItemRequirement(rune.getRuneName(), rune.getRunes(), this.requiredAmount); - if (rune.getStaves() != null) + this.runeItemRequirement = new ItemRequirement("", rune.getRunes(), this.requiredAmount); + if (rune.getStaff() != Staff.UNKNOWN) { - this.staffItemRequirement = new ItemRequirement(rune.getRuneName(), rune.getStaves(), 1, true); + this.staffItemRequirement = new StaffItemRequirement(rune.getStaff()); } } @@ -127,65 +129,23 @@ public List getAllIds() @Override public boolean checkBank(Client client) { - List ids = runeItemRequirement.getAllIds(); - boolean hasRunes = InventorySlots.BANK.contains(client, i -> ids.contains(i.getId()) && i.getQuantity() >= this.requiredAmount); - if (hasRunes) - { - return true; - } - if (staffItemRequirement != null) - { - List staffIDs = staffItemRequirement.getAllIds(); - return InventorySlots.BANK.contains(client, i -> staffIDs.contains(i.getId()) && i.getQuantity() >= 1); - } - return false; + boolean hasStaves = (staffItemRequirement != null && ItemSearch.hasItemsInBank(client, staffItemRequirement)); + return ItemSearch.hasItemsInBank(client, runeItemRequirement) || hasStaves; } @Override public boolean check(Client client, boolean checkWithSlotRestrictions, Item[] items) { - int id = findFirstItemID(client, runeItemRequirement.getAllIds(), this.requiredAmount, checkWithSlotRestrictions, items); - if (id >= 0) - { - return true; - } - if (staffItemRequirement != null) - { - return findFirstItemID(client, staffItemRequirement.getAllIds(), 1, checkWithSlotRestrictions, items) >= 0; - } - return false; - } - - private int findFirstItemID(Client client, List itemIDList, int requiredAmount, boolean checkWithSlotRestrictions, Item[] items) - { - int remainder = requiredAmount; - for (int id : itemIDList) - { - remainder -= (requiredAmount - getRequiredItemDifference(client, id, checkWithSlotRestrictions, items)); - if (remainder <= 0) - { - return id; - } - } - return -1; + boolean hasStaves = (staffItemRequirement != null && ItemSearch.hasItemsAnywhere(client, staffItemRequirement)); + return ItemSearch.hasItemsAnywhere(client, runeItemRequirement) || hasStaves; } @Override - public List getRequirements(Client client, boolean checkWithSlotRestrictions, Item[] bankItems) - { - if (hasItem(client, runeItemRequirement.getAllIds(), this.requiredAmount, checkWithSlotRestrictions, bankItems)) - { - return Collections.singletonList(runeItemRequirement); - } - if (staffItemRequirement != null && hasItem(client, staffItemRequirement.getAllIds(), 1, checkWithSlotRestrictions, bankItems)) - { - return Collections.singletonList(staffItemRequirement); - } - return Collections.emptyList(); - } - - private boolean hasItem(Client client, List ids, int amount, boolean checkWithSlotRestrictions, Item[] items) + public List getRequirements(Client client, QuestHelperConfig config) { - return findFirstItemID(client, ids, amount, checkWithSlotRestrictions, items) >= 0; + boolean hasRunes = ItemSearch.hasItemsAnywhere(client, runeItemRequirement); + boolean hasStaves = staffItemRequirement != null && ItemSearch.hasItemsAnywhere(client, staffItemRequirement); + ItemRequirement requirement = config.bankFilterSearchPreference().getPreference(this, () -> hasRunes, () -> hasStaves); + return Collections.singletonList(requirement); } } diff --git a/src/main/java/com/questhelper/requirements/SpellRequirement.java b/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java similarity index 70% rename from src/main/java/com/questhelper/requirements/SpellRequirement.java rename to src/main/java/com/questhelper/requirements/magic/SpellRequirement.java index cbbe332235..01decc81fe 100644 --- a/src/main/java/com/questhelper/requirements/SpellRequirement.java +++ b/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java @@ -1,6 +1,6 @@ /* * - * * Copyright (c) 2021, Senmori + * * Copyright (c) 2021 * * All rights reserved. * * * * Redistribution and use in source and binary forms, with or without @@ -24,12 +24,15 @@ * * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ -package com.questhelper.requirements; +package com.questhelper.requirements.magic; import com.google.common.base.Predicates; import com.questhelper.BankItems; +import com.questhelper.ItemSearch; +import com.questhelper.QuestHelperConfig; import com.questhelper.banktab.BankItemHolder; import com.questhelper.questhelpers.QuestUtil; +import com.questhelper.requirements.Requirement; import com.questhelper.requirements.item.ItemRequirement; import com.questhelper.requirements.player.SkillRequirement; import com.questhelper.requirements.player.SpellbookRequirement; @@ -37,10 +40,13 @@ import com.questhelper.requirements.util.InventorySlots; import com.questhelper.spells.MagicSpell; import com.questhelper.spells.Rune; +import com.questhelper.spells.SearchPreference; +import com.questhelper.spells.Staff; import java.awt.Color; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -49,6 +55,7 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import lombok.Getter; +import lombok.extern.slf4j.Slf4j; import net.runelite.api.Client; import net.runelite.api.Item; import net.runelite.api.ItemComposition; @@ -81,22 +88,25 @@ * This spell requirements prioritizes using tablets over runes/staves since * it is more compact and has less requirements. */ +@Slf4j +@Getter public class SpellRequirement extends ItemRequirement implements BankItemHolder { - @Getter private final MagicSpell spell; private ItemRequirement tabletRequirement = null; - @Getter + private ItemRequirement staffRequirement; + private boolean useStaff = true; + private int numberOfCasts; private final Map runeCostMap; + /** @return all {@link Requirement}s on this SpellRequirement */ private final List requirements = new ArrayList<>(); private final List runeRequirements = new LinkedList<>(); - @Getter private final SkillRequirement skillRequirement; - @Getter private final SpellbookRequirement spellbookRequirement; + public SpellRequirement(MagicSpell spell, Map runeCostMap, List requirements) { this(spell, 1, runeCostMap, requirements); @@ -113,7 +123,7 @@ public SpellRequirement(MagicSpell spell, int numberOfCasts, Map this.spellbookRequirement = new SpellbookRequirement(spell.getSpellbook()); this.requirements.add(this.skillRequirement); this.requirements.add(this.spellbookRequirement); - setQuantity(numberOfCasts); + setNumberOfCasts(numberOfCasts); updateTooltip(); } @@ -134,14 +144,6 @@ public void addRequirement(@Nonnull Requirement requirement) } } - /** - * @return a copy of all {@link Requirement} on this requirement - */ - public List getRequirements() - { - return new ArrayList<>(requirements); - } - /** * Set the tablet's item id. If the item id is less than 0, the tablet will be removed * and this requirement will no longer check for it. @@ -160,14 +162,44 @@ public void setTablet(int itemID) } } + public void setStaff(int staffID) + { + if (!useStaff) + { + return; + } + if (staffID < 0) + { + this.staffRequirement = null; + } + else + { + this.staffRequirement = new ItemRequirement("", staffID); + } + } + + public boolean hasStaff() + { + return staffRequirement != null && useStaff; + } + /** * A convenience method to better indicate to not use a tablet for this spell requirement */ - public void requireRunes() + public void doNotUseTablet() { setTablet(-1); } + public void setStaffUse(boolean useStaff) + { + if (!useStaff && staffRequirement != null) + { + throw new UnsupportedOperationException("Cannot require a staff and then require no staff: " + this); + } + this.useStaff = useStaff; + } + /** * Set the number of casts this requirement should account for. * @@ -206,7 +238,7 @@ public boolean showQuantity() @Override public Integer getDisplayItemId() { - return tabletRequirement != null ? tabletRequirement.getId() : null; + return tabletRequirement != null ? tabletRequirement.getId() : (staffRequirement != null ? staffRequirement.getId() : null); } @Override @@ -261,6 +293,14 @@ public List getAllIds() .flatMap(Collection::stream) .collect(Collectors.toList()); ids.addAll(runeIDs); + if (tabletRequirement != null && tabletRequirement.getId() >= 0) + { + ids.add(tabletRequirement.getId()); + } + if (staffRequirement != null && staffRequirement.getId() >= 0) + { + ids.add(staffRequirement.getId()); + } return ids; } @@ -274,14 +314,9 @@ public boolean check(Client client) public boolean checkBank(Client client) { updateInternalState(client, getItemRequirements(this.requirements)); - if (tabletRequirement != null) + if (tabletRequirement != null && ItemSearch.hasItemAmountInBank(client, tabletRequirement.getId(), this.numberOfCasts)) { - int tabletID = tabletRequirement.getId(); - if (InventorySlots.BANK.contains(client, i -> i.getId() == tabletID && i.getQuantity() >= this.numberOfCasts)) - { - updateTabletRequirement(client); - return true; - } + return true; } boolean hasRunes = runeRequirements.stream().allMatch(req -> req.checkBank(client)); boolean hasItems = requirements.stream().allMatch(req -> req.check(client)); @@ -296,11 +331,11 @@ public Color getColorConsideringBank(Client client, boolean checkWithSlotRestric updateTabletRequirement(client); int tabletID = tabletRequirement.getId(); int required = tabletRequirement.getQuantity(); - if (InventorySlots.INVENTORY_SLOTS.contains(client, i -> i.getId() == tabletID && i.getQuantity() >= required)) + if (ItemSearch.hasItemAmountOnPlayer(client, tabletID, required)) { return Color.GREEN; } - if (InventorySlots.BANK.contains(client, i -> i.getId() == tabletID && i.getQuantity() >= required)) + if (ItemSearch.hasItemAmountInBank(client, tabletID, required)) { return Color.WHITE; } @@ -310,7 +345,11 @@ public Color getColorConsideringBank(Client client, boolean checkWithSlotRestric { return Color.RED; } - // No tablet (either set or found), we need all runes and other requirements + boolean hasStaff = staffRequirement != null; + if (hasStaff && !staffRequirement.check(client, checkWithSlotRestrictions, bankItems)) + { + return Color.RED; // required staff is not present + } boolean hasRunes = false; boolean hasItems = false; List itemRequirements = getItemRequirements(this.requirements); @@ -324,9 +363,7 @@ public Color getColorConsideringBank(Client client, boolean checkWithSlotRestric if (bankItems != null) { hasRunes = runeRequirements.stream().allMatch(req -> req.checkBank(client)); - hasItems = itemRequirements.stream().allMatch(req -> { - return InventorySlots.BANK.contains(client, i -> req.getAllIds().contains(i.getId()) && i.getQuantity() >= req.getQuantity()); - }); + hasItems = itemRequirements.stream().allMatch(req -> ItemSearch.hasItemsInBank(client, req)); if (hasRunes && hasItems) { return Color.WHITE; @@ -344,16 +381,16 @@ private boolean hasItemAmount(Client client, List idList, int amount) @Override public boolean check(Client client, boolean checkWithSlotRestrictions, Item[] items) { - if (tabletRequirement != null) + if (tabletRequirement != null && ItemSearch.hasItemsAnywhere(client, tabletRequirement)) { - int id = findFirstItemID(client, Collections.singletonList(tabletRequirement.getId()), this.numberOfCasts, checkWithSlotRestrictions, items); - if (id >= 0) - { - updateTabletRequirement(client); - return true; - } + updateTabletRequirement(client); + return true; + } + if (staffRequirement != null && ItemSearch.hasItemsAnywhere(client, staffRequirement)) + { + return true; } - boolean hasItems, hasOther, hasRunes = false; + boolean hasItems, hasOther, hasRunes; List itemRequirements = getItemRequirements(this.requirements); updateInternalState(client, itemRequirements); if (!itemRequirements.isEmpty()) @@ -370,24 +407,85 @@ public boolean check(Client client, boolean checkWithSlotRestrictions, Item[] it } @Override - public List getRequirements(Client client, boolean checkWithSlotRestrictions, Item[] bankItems) + public List getRequirements(Client client, QuestHelperConfig config) { - if (tabletRequirement != null) + updateInternalState(client, getItemRequirements(this.requirements)); + if (tabletRequirement != null && ItemSearch.hasItemAmountAnywhere(client, tabletRequirement.getId(), this.numberOfCasts)) { - int tabletID = tabletRequirement.getId(); - if (hasItem(client, Collections.singletonList(tabletID), this.numberOfCasts, checkWithSlotRestrictions, bankItems)) + return Collections.singletonList(tabletRequirement); + } + List bankRequirements = new LinkedList<>(); + Map runeItemRequirements = new HashMap<>(); + if (staffRequirement != null) + { + bankRequirements.add(staffRequirement); + } + for (RuneRequirement rune : runeRequirements) + { + if (runeItemRequirements.containsKey(rune.getRune())) { - return Collections.singletonList(tabletRequirement); + continue; + } + log.debug("RUNE: " + rune.getRune().getRuneName() + " -> LOOKING FOR MATCH"); + Rune currentRune = rune.getRune(); + StaffItemRequirement staves = rune.getStaffItemRequirement(); + ItemRequirement runeItem = rune.getRuneItemRequirement(); + + boolean hasRunes = ItemSearch.hasItemsAnywhere(client, runeItem); + boolean hasStaves = hasStaff(client, staves); + log.debug("HAS_RUNES: " + hasRunes + " <-> HAS_STAVES: " + hasStaves); + + SearchPreference searchPreference = SearchPreference.STAVES; + ItemRequirement toAdd = searchPreference.getPreference(rune, () -> hasRunes, () -> hasStaves); + ItemComposition itemToAdd = client.getItemDefinition(toAdd.getId()); + log.debug("FOUND MATCH FOR: " + rune.getRune() + " -> " + itemToAdd.getName()); + + boolean itemIsRune = toAdd.getAllIds().stream().allMatch(Rune::isRuneItem); + log.debug(itemToAdd.getName() + " IS RUNE -> " + itemIsRune); + if (staffRequirement != null && itemIsRune) + { + // there is a staff present, can it replace the current rune? + log.debug("FOUND STAFF"); + Staff requiredStaff = staffRequirement.getAllIds() + .stream() + .filter(Staff::isStaff) + .map(Staff::getByItemID) + .findFirst() + .orElse(Staff.UNKNOWN); + log.debug("PRESENT STAFF: " + requiredStaff); + boolean isSourceOf = requiredStaff.isSourceOf(currentRune); + log.debug(requiredStaff + " == " + currentRune.getStaff() + " -> " + isSourceOf); + if (!isSourceOf) + { + log.debug("ADDED RUNE: " + itemToAdd.getName()); + runeItemRequirements.put(currentRune, toAdd); + } + } + else + { + log.debug("ADDED: " + itemToAdd.getName()); + runeItemRequirements.put(currentRune, toAdd); } } - List requirements = runeRequirements - .stream() - .map(req -> req.getRequirements(client, checkWithSlotRestrictions, bankItems)) - .flatMap(Collection::stream) - .collect(Collectors.toCollection(LinkedList::new)); - requirements.addAll(getItemRequirements(this.requirements)); - updateInternalState(client, requirements); - return requirements; + + bankRequirements.addAll(runeItemRequirements.values()); + bankRequirements.addAll(getItemRequirements(this.requirements)); + updateInternalState(client, bankRequirements); + return bankRequirements; + } + + private boolean hasStaff(Client client, ItemRequirement staves) + { + if (staves == null || staffRequirement != null) + { + return false; + } + boolean hasStaff = ItemSearch.hasItemsAnywhere(client, staves); + if (useStaff && hasStaff) + { + staffRequirement = staves; + } + return hasStaff; } @Nullable @@ -428,28 +526,13 @@ private List getNonItemRequirements(List requirements) .collect(Collectors.toList()); } - private int findFirstItemID(Client client, List itemIDList, int requiredAmount, boolean checkWithSlotRestrictions, Item[] items) - { - int remainder = requiredAmount; - for (int id : itemIDList) - { - remainder -= (requiredAmount - getRequiredItemDifference(client, id, checkWithSlotRestrictions, items)); - if (remainder <= 0) - { - return id; - } - } - return -1; - } - - private boolean hasItem(Client client, List ids, int amount, boolean checkWithSlotRestrictions, Item[] items) - { - return findFirstItemID(client, ids, amount, checkWithSlotRestrictions, items) >= 0; - } - private void updateInternalState(Client client, List requirements) { updateItemRequirements(client, requirements); + if (staffRequirement != null && StringUtils.isBlank(staffRequirement.getName())) + { + staffRequirement.setName(client.getItemDefinition(staffRequirement.getId()).getName()); + } } private void updateTooltip() diff --git a/src/main/java/com/questhelper/spells/MagicSpell.java b/src/main/java/com/questhelper/spells/MagicSpell.java index eeeab1f0de..3076114e6e 100644 --- a/src/main/java/com/questhelper/spells/MagicSpell.java +++ b/src/main/java/com/questhelper/spells/MagicSpell.java @@ -26,7 +26,7 @@ */ package com.questhelper.spells; -import com.questhelper.requirements.SpellRequirement; +import com.questhelper.requirements.magic.SpellRequirement; import com.questhelper.requirements.util.Spellbook; /** diff --git a/src/main/java/com/questhelper/spells/Rune.java b/src/main/java/com/questhelper/spells/Rune.java index fb80f08d8f..1c393875ab 100644 --- a/src/main/java/com/questhelper/spells/Rune.java +++ b/src/main/java/com/questhelper/spells/Rune.java @@ -30,6 +30,7 @@ import com.questhelper.ItemCollections; import java.util.Collections; import java.util.List; +import java.util.stream.Stream; import javax.annotation.Nonnull; import lombok.Getter; import net.runelite.api.ItemID; @@ -40,10 +41,10 @@ @Getter public enum Rune { - AIR("Air Rune", ItemCollections.getAirRune(), ItemCollections.getAirStaff()), - WATER("Water Rune", ItemCollections.getWaterRune(), ItemCollections.getWaterStaff()), - EARTH("Earth Rune", ItemCollections.getEarthRune(), ItemCollections.getEarthStaff()), - FIRE("Fire Rune", ItemCollections.getFireRune(), ItemCollections.getFireStaff()), + AIR("Air Rune", ItemCollections.getAirRune(), Staff.AIR), + WATER("Water Rune", ItemCollections.getWaterRune(), Staff.WATER), + EARTH("Earth Rune", ItemCollections.getEarthRune(), Staff.EARTH), + FIRE("Fire Rune", ItemCollections.getFireRune(), Staff.FIRE), MIND("Mind Rune", ItemID.MIND_RUNE), BODY("Body Rune", ItemID.BODY_RUNE), COSMIC("Cosmic Rune", ItemID.COSMIC_RUNE), @@ -55,12 +56,13 @@ public enum Rune BLOOD("Blood Rune", ItemID.BLOOD_RUNE), SOUL("Soul Rune", ItemID.SOUL_RUNE), WRATH("Wrath Rune", ItemID.WRATH_RUNE), - LAVA("Lava Rune", ItemID.LAVA_RUNE, ItemCollections.getLavaStaff()), - MUD("Mud Rune", ItemID.MUD_RUNE, ItemCollections.getMudStaff()), - STEAM("Steam Rune", ItemID.STEAM_RUNE, ItemCollections.getSteamStaff()), - SMOKE("Smoke Rune", ItemID.SMOKE_RUNE, ItemCollections.getSmokeStaff()), - MIST("Mist Rune", ItemID.MIST_RUNE, ItemCollections.getMistStaff()), - DUST("Dust Rune", ItemID.DUST_RUNE, ItemCollections.getDustStaff()), + // Keep combination runes after the non-combination runes + LAVA("Lava Rune", ItemID.LAVA_RUNE, Staff.LAVA), + MUD("Mud Rune", ItemID.MUD_RUNE, Staff.MUD), + STEAM("Steam Rune", ItemID.STEAM_RUNE, Staff.STEAM), + SMOKE("Smoke Rune", ItemID.SMOKE_RUNE, Staff.SMOKE), + MIST("Mist Rune", ItemID.MIST_RUNE, Staff.MIST), + DUST("Dust Rune", ItemID.DUST_RUNE, Staff.DUST), UNKNOWN("Null Rune", -1), ; @@ -68,26 +70,26 @@ public enum Rune private final String runeName; @Nonnull private final List runes; - private final List staves; - Rune(@Nonnull String runeName, @Nonnull List runes, List staves) + private final Staff staff; + Rune(@Nonnull String runeName, @Nonnull List runes, Staff staff) { this.runeName = runeName; this.runes = runes; - this.staves = staves; + this.staff = staff; } Rune(@Nonnull String runeName, int itemID) { this.runeName = runeName; this.runes = Collections.singletonList(itemID); - this.staves = null; + this.staff = Staff.UNKNOWN; } - Rune(@Nonnull String runeName, int itemID, List staves) + Rune(@Nonnull String runeName, int itemID, Staff staff) { this.runeName = runeName; this.runes = Collections.singletonList(itemID); - this.staves = staves; + this.staff = staff; } public int getItemID() @@ -95,19 +97,17 @@ public int getItemID() return runes.get(0); } + public static boolean isRuneItem(int itemID) + { + return getByItemID(itemID) != Rune.UNKNOWN; + } + public static Rune getByItemID(int itemID) { - for (Rune rune : Rune.values()) - { - if (rune.getRunes().contains(itemID)) - { - return rune; - } - if (rune.getStaves() != null && rune.getStaves().contains(itemID)) - { - return rune; - } - } - return Rune.UNKNOWN; + return Stream.of(values()) + .sorted(Collections.reverseOrder()) + .filter(r -> r.getRunes().contains(itemID)) + .findFirst() + .orElse(Rune.UNKNOWN); } } diff --git a/src/main/java/com/questhelper/spells/SearchPreference.java b/src/main/java/com/questhelper/spells/SearchPreference.java new file mode 100644 index 0000000000..32d8eb34f1 --- /dev/null +++ b/src/main/java/com/questhelper/spells/SearchPreference.java @@ -0,0 +1,51 @@ +/* + * + * * Copyright (c) 2021 + * * 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 com.questhelper.spells; + +import com.questhelper.requirements.magic.RuneRequirement; +import com.questhelper.requirements.item.ItemRequirement; +import com.questhelper.util.TriFunction; +import java.util.function.BooleanSupplier; + +public enum SearchPreference +{ + RUNES((runes, staff, req) -> runes.getAsBoolean() ? req.getRuneItemRequirement() : req.getStaffItemRequirement()), + STAVES((runes, staff, req) -> staff.getAsBoolean() ? req.getStaffItemRequirement() : req.getRuneItemRequirement()); + ; + + private final TriFunction function; + SearchPreference(TriFunction function) + { + this.function = function; + } + + public ItemRequirement getPreference(RuneRequirement requirement, BooleanSupplier runes, BooleanSupplier staff) + { + return function.apply(runes, staff, requirement); + } +} diff --git a/src/main/java/com/questhelper/spells/Staff.java b/src/main/java/com/questhelper/spells/Staff.java new file mode 100644 index 0000000000..e4a10fb10b --- /dev/null +++ b/src/main/java/com/questhelper/spells/Staff.java @@ -0,0 +1,91 @@ +/* + * + * * Copyright (c) 2021 + * * 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 com.questhelper.spells; + +import com.questhelper.ItemCollections; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import lombok.Getter; + +@Getter +public enum Staff +{ + AIR("Air Staff", ItemCollections.getAirStaff(), Rune.AIR), + WATER("Water Staff", ItemCollections.getWaterStaff(), Rune.WATER), + EARTH("Earth Staff", ItemCollections.getEarthStaff(), Rune.EARTH), + FIRE("Fire Staff", ItemCollections.getFireStaff(), Rune.FIRE), + // Keep combination staves after the elemental staves + LAVA("Lava Staff", ItemCollections.getLavaStaff(), Rune.LAVA, Rune.FIRE, Rune.EARTH), + MUD("Mud Staff", ItemCollections.getMudStaff(), Rune.MUD, Rune.WATER, Rune.EARTH), + STEAM("Steam Staff", ItemCollections.getSteamStaff(), Rune.STEAM, Rune.WATER, Rune.FIRE), + SMOKE("Smoke Staff", ItemCollections.getSmokeStaff(), Rune.SMOKE, Rune.AIR, Rune.FIRE), + MIST("Mist Staff", ItemCollections.getMistStaff(), Rune.MIST, Rune.WATER, Rune.AIR), + DUST("Dust Staff", ItemCollections.getDustStaff(), Rune.DUST, Rune.EARTH, Rune.AIR), + UNKNOWN("Null Staff", Collections.emptyList(), Rune.UNKNOWN), + ; + + private final String name; + private final List staves; + private final List sourceRunes; + Staff(String name, List staves, Rune... sourceOf) + { + this.name = name; + this.staves = staves; + this.sourceRunes = Arrays.asList(sourceOf); + } + + public int getItemID() + { + return staves.get(0); + } + + public boolean isSourceOf(Rune rune) + { + List values = Stream.of(values()) + .sorted(Comparator.reverseOrder()) + .collect(Collectors.toList()); + return values.stream().anyMatch(staff -> staff.getSourceRunes().contains(rune)); + } + + public static Staff getByItemID(int itemID) + { + return Stream.of(Staff.values()) + .sorted(Collections.reverseOrder()) + .filter(staff -> staff.getStaves().contains(itemID)) + .findFirst() + .orElse(Staff.UNKNOWN); + } + + public static boolean isStaff(int itemID) + { + return getByItemID(itemID) != Staff.UNKNOWN; + } +} diff --git a/src/main/java/com/questhelper/spells/StandardSpell.java b/src/main/java/com/questhelper/spells/StandardSpell.java index 52734b45d7..481fdad9a2 100644 --- a/src/main/java/com/questhelper/spells/StandardSpell.java +++ b/src/main/java/com/questhelper/spells/StandardSpell.java @@ -29,7 +29,7 @@ import static com.questhelper.QuestHelperQuest.PLAGUE_CITY; import static com.questhelper.QuestHelperQuest.THE_MAGE_ARENA; import static com.questhelper.QuestHelperQuest.EADGARS_RUSE; -import com.questhelper.requirements.SpellRequirement; +import com.questhelper.requirements.magic.SpellRequirement; import com.questhelper.requirements.util.Spellbook; import java.util.Locale; import java.util.function.UnaryOperator; diff --git a/src/main/java/com/questhelper/spells/StandardSpellBuilder.java b/src/main/java/com/questhelper/spells/StandardSpellBuilder.java index 67f1c004c3..c3552d4fda 100644 --- a/src/main/java/com/questhelper/spells/StandardSpellBuilder.java +++ b/src/main/java/com/questhelper/spells/StandardSpellBuilder.java @@ -28,7 +28,7 @@ import com.questhelper.QuestHelperQuest; import com.questhelper.requirements.Requirement; -import com.questhelper.requirements.SpellRequirement; +import com.questhelper.requirements.magic.SpellRequirement; import com.questhelper.requirements.item.ItemRequirement; import com.questhelper.requirements.item.ItemRequirements; import com.questhelper.requirements.quest.QuestRequirement; @@ -150,7 +150,7 @@ public StandardSpellBuilder tablet(int itemID) */ public StandardSpellBuilder quest(QuestHelperQuest quest) { - return quest(quest, true); + return quest(quest, false); } /** diff --git a/src/main/java/com/questhelper/steps/NpcStep.java b/src/main/java/com/questhelper/steps/NpcStep.java index c4c636dbc5..ae5e6cf682 100644 --- a/src/main/java/com/questhelper/steps/NpcStep.java +++ b/src/main/java/com/questhelper/steps/NpcStep.java @@ -26,7 +26,7 @@ package com.questhelper.steps; import com.questhelper.requirements.Requirement; -import com.questhelper.requirements.SpellRequirement; +import com.questhelper.requirements.magic.SpellRequirement; import com.questhelper.spells.MagicSpell; import com.questhelper.steps.overlay.DirectionArrow; import com.questhelper.steps.tools.QuestPerspective; diff --git a/src/main/java/com/questhelper/steps/ObjectStep.java b/src/main/java/com/questhelper/steps/ObjectStep.java index b600dd89d7..96bf755d6d 100644 --- a/src/main/java/com/questhelper/steps/ObjectStep.java +++ b/src/main/java/com/questhelper/steps/ObjectStep.java @@ -25,7 +25,7 @@ package com.questhelper.steps; import com.questhelper.requirements.Requirement; -import com.questhelper.requirements.SpellRequirement; +import com.questhelper.requirements.magic.SpellRequirement; import com.questhelper.spells.MagicSpell; import com.questhelper.steps.overlay.DirectionArrow; import com.questhelper.steps.tools.QuestPerspective; From 885f3acfbbf948834baae7015d0acdb3989110c1 Mon Sep 17 00:00:00 2001 From: Senmori Date: Tue, 9 Feb 2021 20:25:56 -0500 Subject: [PATCH 33/55] Remove StaffItemRequirement. Add ItemSearch as a utility class for centralizing client item searches. --- src/main/java/com/questhelper/ItemSearch.java | 143 ++++++++++++++++++ .../requirements/magic/RuneRequirement.java | 4 +- .../requirements/magic/SpellRequirement.java | 2 +- 3 files changed, 146 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/questhelper/ItemSearch.java diff --git a/src/main/java/com/questhelper/ItemSearch.java b/src/main/java/com/questhelper/ItemSearch.java new file mode 100644 index 0000000000..793f2a2163 --- /dev/null +++ b/src/main/java/com/questhelper/ItemSearch.java @@ -0,0 +1,143 @@ +/* + * + * * Copyright (c) 2021 + * * 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 com.questhelper; + +import com.questhelper.requirements.item.ItemRequirement; +import com.questhelper.requirements.util.InventorySlots; +import java.util.List; +import java.util.Objects; +import java.util.stream.Stream; +import lombok.experimental.UtilityClass; +import net.runelite.api.Client; +import net.runelite.api.InventoryID; +import net.runelite.api.Item; +import net.runelite.api.ItemContainer; + +@UtilityClass +public class ItemSearch +{ + + public boolean hasItemAnywhere(Client client, int itemID) + { + return hasItemOnPlayer(client, itemID) || hasItemInBank(client, itemID); + } + + public boolean hasItemAmountAnywhere(Client client, int itemID, int requiredAmount) + { + return hasItemAmountOnPlayer(client, itemID, requiredAmount) || hasItemAmountInBank(client, itemID, requiredAmount); + } + + public boolean hasItemOnPlayer(Client client, int itemID) + { + return hasItemInInventory(client, itemID) || hasItemEquipped(client, itemID); + } + + public boolean hasItemAmountOnPlayer(Client client, int itemID, int requiredAmount) + { + return hasItemAmountInInventory(client, itemID, requiredAmount) || hasItemAmountEquipped(client, itemID, requiredAmount); + } + + public boolean hasItemInInventory(Client client, int itemID) + { + return checkItem(client, InventorySlots.INVENTORY_SLOTS, itemID); + } + + public boolean hasItemAmountInInventory(Client client, int itemID, int requiredAmount) + { + return getItemAmountExact(client, InventoryID.INVENTORY, itemID) >= requiredAmount; + } + + public boolean hasItemEquipped(Client client, int itemID) + { + return checkItem(client, InventorySlots.EQUIPMENT_SLOTS, itemID); + } + + public boolean hasItemAmountEquipped(Client client, int itemID, int requiredAmount) + { + return getItemAmountExact(client, InventoryID.EQUIPMENT, itemID) >= requiredAmount; + } + + public boolean hasItemInBank(Client client, int itemID) + { + return checkItem(client, InventorySlots.BANK, itemID); + } + + public boolean hasItemAmountInBank(Client client, int itemID, int requiredAmount) + { + return getItemAmountExact(client, InventoryID.BANK, itemID) >= requiredAmount; + } + + public long getItemCount(Client client, int itemID) + { + return getItemCountOnPlayer(client, itemID) + getItemCountInBank(client, itemID); + } + + public long getItemCountOnPlayer(Client client, int itemID) + { + return getItemAmountExact(client, InventoryID.INVENTORY, itemID) + getItemAmountExact(client, InventoryID.EQUIPMENT, itemID); + } + + public long getItemCountInBank(Client client, int itemID) + { + return getItemAmountExact(client, InventoryID.BANK, itemID); + } + + public boolean hasItemsAnywhere(Client client, ItemRequirement requirement) + { + return hasItemsOnPlayer(client, requirement) || hasItemsInBank(client, requirement); + } + + public boolean hasItemsOnPlayer(Client client, ItemRequirement requirement) + { + return requirement.getAllIds().stream().anyMatch(id -> hasItemAmountOnPlayer(client, id, requirement.getQuantity())); + } + + public boolean hasItemsInBank(Client client, ItemRequirement requirement) + { + return requirement.getAllIds().stream().anyMatch(id -> hasItemAmountInBank(client, id, requirement.getQuantity())); + } + + public boolean checkItem(Client client, InventorySlots slot, int itemID) + { + return slot.contains(client, i -> i.getId() == itemID); + } + + public long getItemAmountExact(Client client, InventoryID inventoryID, int itemID) + { + ItemContainer container = client.getItemContainer(inventoryID); + if (container == null) + { + return 0; + } + return Stream.of(container.getItems()) + .filter(Objects::nonNull) + .filter(item -> item.getId() == itemID) + .mapToLong(Item::getQuantity) + .sum(); + } +} diff --git a/src/main/java/com/questhelper/requirements/magic/RuneRequirement.java b/src/main/java/com/questhelper/requirements/magic/RuneRequirement.java index ee993fa700..d1c8e5ac1d 100644 --- a/src/main/java/com/questhelper/requirements/magic/RuneRequirement.java +++ b/src/main/java/com/questhelper/requirements/magic/RuneRequirement.java @@ -60,7 +60,7 @@ public class RuneRequirement extends ItemRequirement implements BankItemHolder private int requiredAmount; private final ItemRequirement runeItemRequirement; - private StaffItemRequirement staffItemRequirement; + private ItemRequirement staffItemRequirement; public RuneRequirement(Rune rune, int costPerCast) { @@ -76,7 +76,7 @@ public RuneRequirement(Rune rune, int costPerCast, int numberOfCasts) this.runeItemRequirement = new ItemRequirement("", rune.getRunes(), this.requiredAmount); if (rune.getStaff() != Staff.UNKNOWN) { - this.staffItemRequirement = new StaffItemRequirement(rune.getStaff()); + this.staffItemRequirement = new ItemRequirement(rune.getStaff().getName(), rune.getStaff().getStaves(), 1); } } diff --git a/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java b/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java index 01decc81fe..d35dbcf434 100644 --- a/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java +++ b/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java @@ -428,7 +428,7 @@ public List getRequirements(Client client, QuestHelperConfig co } log.debug("RUNE: " + rune.getRune().getRuneName() + " -> LOOKING FOR MATCH"); Rune currentRune = rune.getRune(); - StaffItemRequirement staves = rune.getStaffItemRequirement(); + ItemRequirement staves = rune.getStaffItemRequirement(); ItemRequirement runeItem = rune.getRuneItemRequirement(); boolean hasRunes = ItemSearch.hasItemsAnywhere(client, runeItem); From a2d40870843380f62117360423b7fc7266373212 Mon Sep 17 00:00:00 2001 From: Senmori Date: Tue, 9 Feb 2021 20:54:19 -0500 Subject: [PATCH 34/55] Fix merge conflicts and add TriFunction for our search preferences enum. --- .../com/questhelper/util/TriFunction.java | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 src/main/java/com/questhelper/util/TriFunction.java diff --git a/src/main/java/com/questhelper/util/TriFunction.java b/src/main/java/com/questhelper/util/TriFunction.java new file mode 100644 index 0000000000..1515e93d64 --- /dev/null +++ b/src/main/java/com/questhelper/util/TriFunction.java @@ -0,0 +1,43 @@ +/* + * + * * Copyright (c) 2021 + * * 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 com.questhelper.util; + +import java.util.Objects; +import java.util.function.Function; + +@FunctionalInterface +public interface TriFunction +{ + R apply (A a, B b, C c); + + default TriFunction andThen(Function after) + { + Objects.requireNonNull(after); + return (A a, B b, C c) -> after.apply(apply(a, b, c)); + } + +} From a85420869dcd93647b2d11479d349f537543ccbb Mon Sep 17 00:00:00 2001 From: Senmori Date: Tue, 9 Feb 2021 21:37:51 -0500 Subject: [PATCH 35/55] Finalize getting staves to work with combinations of runes as best as possible. --- src/main/java/com/questhelper/ItemSearch.java | 8 ++++++++ .../requirements/magic/SpellRequirement.java | 17 ++++++----------- src/main/java/com/questhelper/spells/Staff.java | 5 +---- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/main/java/com/questhelper/ItemSearch.java b/src/main/java/com/questhelper/ItemSearch.java index 793f2a2163..5f47f4fa9c 100644 --- a/src/main/java/com/questhelper/ItemSearch.java +++ b/src/main/java/com/questhelper/ItemSearch.java @@ -140,4 +140,12 @@ public long getItemAmountExact(Client client, InventoryID inventoryID, int itemI .mapToLong(Item::getQuantity) .sum(); } + + public int findFirstItem(Client client, List itemIDs, int amount) + { + return itemIDs.stream() + .filter(id -> hasItemAmountAnywhere(client, id, amount)) + .findFirst() + .orElse(-1); + } } diff --git a/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java b/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java index d35dbcf434..5284eb6563 100644 --- a/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java +++ b/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java @@ -437,24 +437,19 @@ public List getRequirements(Client client, QuestHelperConfig co SearchPreference searchPreference = SearchPreference.STAVES; ItemRequirement toAdd = searchPreference.getPreference(rune, () -> hasRunes, () -> hasStaves); - ItemComposition itemToAdd = client.getItemDefinition(toAdd.getId()); + int toAddID = ItemSearch.findFirstItem(client, toAdd.getAllIds(), toAdd.getQuantity()); + ItemComposition itemToAdd = client.getItemDefinition(toAddID); log.debug("FOUND MATCH FOR: " + rune.getRune() + " -> " + itemToAdd.getName()); - boolean itemIsRune = toAdd.getAllIds().stream().allMatch(Rune::isRuneItem); log.debug(itemToAdd.getName() + " IS RUNE -> " + itemIsRune); - if (staffRequirement != null && itemIsRune) + if (staffRequirement != null && itemIsRune && currentRune.getStaff() != Staff.UNKNOWN) { // there is a staff present, can it replace the current rune? - log.debug("FOUND STAFF"); - Staff requiredStaff = staffRequirement.getAllIds() - .stream() - .filter(Staff::isStaff) - .map(Staff::getByItemID) - .findFirst() - .orElse(Staff.UNKNOWN); + int staffID = ItemSearch.findFirstItem(client, staffRequirement.getAllIds(), 1); + Staff requiredStaff = Staff.getByItemID(staffID); log.debug("PRESENT STAFF: " + requiredStaff); boolean isSourceOf = requiredStaff.isSourceOf(currentRune); - log.debug(requiredStaff + " == " + currentRune.getStaff() + " -> " + isSourceOf); + log.debug(requiredStaff + " can act as source for " + currentRune.getRuneName() + " -> " + isSourceOf); if (!isSourceOf) { log.debug("ADDED RUNE: " + itemToAdd.getName()); diff --git a/src/main/java/com/questhelper/spells/Staff.java b/src/main/java/com/questhelper/spells/Staff.java index e4a10fb10b..77529c6204 100644 --- a/src/main/java/com/questhelper/spells/Staff.java +++ b/src/main/java/com/questhelper/spells/Staff.java @@ -69,10 +69,7 @@ public int getItemID() public boolean isSourceOf(Rune rune) { - List values = Stream.of(values()) - .sorted(Comparator.reverseOrder()) - .collect(Collectors.toList()); - return values.stream().anyMatch(staff -> staff.getSourceRunes().contains(rune)); + return getSourceRunes().contains(rune); } public static Staff getByItemID(int itemID) From 68d0ffc47994da4e7b506b4a70b0e160bdbc4398 Mon Sep 17 00:00:00 2001 From: Senmori Date: Tue, 9 Feb 2021 21:55:28 -0500 Subject: [PATCH 36/55] Update item requirements to match the number of casts. Remove debug messages and switch back to using the config to find our bank filter preferences. --- .../com/questhelper/QuestHelperConfig.java | 7 +++---- .../requirements/magic/RuneRequirement.java | 2 +- .../requirements/magic/SpellRequirement.java | 20 +++++++------------ 3 files changed, 11 insertions(+), 18 deletions(-) diff --git a/src/main/java/com/questhelper/QuestHelperConfig.java b/src/main/java/com/questhelper/QuestHelperConfig.java index 962f5e9b2b..04e777b541 100644 --- a/src/main/java/com/questhelper/QuestHelperConfig.java +++ b/src/main/java/com/questhelper/QuestHelperConfig.java @@ -131,11 +131,10 @@ default boolean showSymbolOverlay() @ConfigItem( keyName = "bankSearchPreference", - name = "Which spell components to prefer", - description = "Choose whether runes or staves should be preferred when filtering spell components.", - hidden = true + name = "Spell Component Preference", + description = "Choose whether runes or staves should be preferred when filtering spell components." ) - default SearchPreference bankFilterSearchPreference() + default SearchPreference bankFilterSpellPreference() { return SearchPreference.STAVES; } diff --git a/src/main/java/com/questhelper/requirements/magic/RuneRequirement.java b/src/main/java/com/questhelper/requirements/magic/RuneRequirement.java index d1c8e5ac1d..d299a6d41a 100644 --- a/src/main/java/com/questhelper/requirements/magic/RuneRequirement.java +++ b/src/main/java/com/questhelper/requirements/magic/RuneRequirement.java @@ -145,7 +145,7 @@ public List getRequirements(Client client, QuestHelperConfig co { boolean hasRunes = ItemSearch.hasItemsAnywhere(client, runeItemRequirement); boolean hasStaves = staffItemRequirement != null && ItemSearch.hasItemsAnywhere(client, staffItemRequirement); - ItemRequirement requirement = config.bankFilterSearchPreference().getPreference(this, () -> hasRunes, () -> hasStaves); + ItemRequirement requirement = config.bankFilterSpellPreference().getPreference(this, () -> hasRunes, () -> hasStaves); return Collections.singletonList(requirement); } } diff --git a/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java b/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java index 5284eb6563..a127cb1adc 100644 --- a/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java +++ b/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java @@ -40,7 +40,6 @@ import com.questhelper.requirements.util.InventorySlots; import com.questhelper.spells.MagicSpell; import com.questhelper.spells.Rune; -import com.questhelper.spells.SearchPreference; import com.questhelper.spells.Staff; import java.awt.Color; import java.util.ArrayList; @@ -221,6 +220,7 @@ public void setNumberOfCasts(int numberOfCasts) { tabletRequirement.setQuantity(this.numberOfCasts); } + getItemRequirements(this.requirements).forEach(item -> item.setQuantity(this.numberOfCasts)); } @Override @@ -426,39 +426,29 @@ public List getRequirements(Client client, QuestHelperConfig co { continue; } - log.debug("RUNE: " + rune.getRune().getRuneName() + " -> LOOKING FOR MATCH"); Rune currentRune = rune.getRune(); ItemRequirement staves = rune.getStaffItemRequirement(); ItemRequirement runeItem = rune.getRuneItemRequirement(); boolean hasRunes = ItemSearch.hasItemsAnywhere(client, runeItem); boolean hasStaves = hasStaff(client, staves); - log.debug("HAS_RUNES: " + hasRunes + " <-> HAS_STAVES: " + hasStaves); - SearchPreference searchPreference = SearchPreference.STAVES; - ItemRequirement toAdd = searchPreference.getPreference(rune, () -> hasRunes, () -> hasStaves); - int toAddID = ItemSearch.findFirstItem(client, toAdd.getAllIds(), toAdd.getQuantity()); - ItemComposition itemToAdd = client.getItemDefinition(toAddID); - log.debug("FOUND MATCH FOR: " + rune.getRune() + " -> " + itemToAdd.getName()); + ItemRequirement toAdd = config.bankFilterSpellPreference().getPreference(rune, () -> hasRunes, () -> hasStaves); + boolean itemIsRune = toAdd.getAllIds().stream().allMatch(Rune::isRuneItem); - log.debug(itemToAdd.getName() + " IS RUNE -> " + itemIsRune); if (staffRequirement != null && itemIsRune && currentRune.getStaff() != Staff.UNKNOWN) { // there is a staff present, can it replace the current rune? int staffID = ItemSearch.findFirstItem(client, staffRequirement.getAllIds(), 1); Staff requiredStaff = Staff.getByItemID(staffID); - log.debug("PRESENT STAFF: " + requiredStaff); boolean isSourceOf = requiredStaff.isSourceOf(currentRune); - log.debug(requiredStaff + " can act as source for " + currentRune.getRuneName() + " -> " + isSourceOf); if (!isSourceOf) { - log.debug("ADDED RUNE: " + itemToAdd.getName()); runeItemRequirements.put(currentRune, toAdd); } } else { - log.debug("ADDED: " + itemToAdd.getName()); runeItemRequirements.put(currentRune, toAdd); } } @@ -503,6 +493,10 @@ public String getUpdatedTooltip(Client client, BankItems bankItems) .filter(req -> !req.getDisplayText().isEmpty()) .filter(req -> !req.check(client)) .forEach(req -> text.append(req.getDisplayText()).append("\n")); + if (text.length() <= 0) + { + return text.toString(); + } return text.insert(0, "This spell requires: \n").toString(); } From 4fd72997b09732230519c3452ae7f114cdd89f6c Mon Sep 17 00:00:00 2001 From: Senmori Date: Tue, 9 Feb 2021 21:55:58 -0500 Subject: [PATCH 37/55] Change keyName of the config option so it better represents what it does. --- src/main/java/com/questhelper/QuestHelperConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/questhelper/QuestHelperConfig.java b/src/main/java/com/questhelper/QuestHelperConfig.java index 04e777b541..08c90d4b22 100644 --- a/src/main/java/com/questhelper/QuestHelperConfig.java +++ b/src/main/java/com/questhelper/QuestHelperConfig.java @@ -130,7 +130,7 @@ default boolean showSymbolOverlay() } @ConfigItem( - keyName = "bankSearchPreference", + keyName = "bankSearchSpellPreference", name = "Spell Component Preference", description = "Choose whether runes or staves should be preferred when filtering spell components." ) From 36be39d850d009078600424a1f1794fa8058d2b1 Mon Sep 17 00:00:00 2001 From: Senmori Date: Wed, 10 Feb 2021 15:49:32 -0500 Subject: [PATCH 38/55] Finalize SpellRequirement PR. --- src/main/java/com/questhelper/QuestHelperConfig.java | 6 +++--- src/main/java/com/questhelper/banktab/BankItemHolder.java | 3 --- .../com/questhelper/banktab/QuestHelperBankTagService.java | 2 -- .../questhelper/requirements/magic/SpellRequirement.java | 6 +++--- ...{SearchPreference.java => SpellComponentPreference.java} | 4 ++-- 5 files changed, 8 insertions(+), 13 deletions(-) rename src/main/java/com/questhelper/spells/{SearchPreference.java => SpellComponentPreference.java} (93%) diff --git a/src/main/java/com/questhelper/QuestHelperConfig.java b/src/main/java/com/questhelper/QuestHelperConfig.java index 08c90d4b22..3adf69a863 100644 --- a/src/main/java/com/questhelper/QuestHelperConfig.java +++ b/src/main/java/com/questhelper/QuestHelperConfig.java @@ -27,7 +27,7 @@ import com.questhelper.panel.questorders.QuestOrders; import com.questhelper.questhelpers.Quest; import com.questhelper.questhelpers.QuestHelper; -import com.questhelper.spells.SearchPreference; +import com.questhelper.spells.SpellComponentPreference; import java.awt.Color; import java.util.Collection; import java.util.Comparator; @@ -134,9 +134,9 @@ default boolean showSymbolOverlay() name = "Spell Component Preference", description = "Choose whether runes or staves should be preferred when filtering spell components." ) - default SearchPreference bankFilterSpellPreference() + default SpellComponentPreference bankFilterSpellPreference() { - return SearchPreference.STAVES; + return SpellComponentPreference.RUNES; } @ConfigSection( diff --git a/src/main/java/com/questhelper/banktab/BankItemHolder.java b/src/main/java/com/questhelper/banktab/BankItemHolder.java index 5be10091fe..8b9c5658a5 100644 --- a/src/main/java/com/questhelper/banktab/BankItemHolder.java +++ b/src/main/java/com/questhelper/banktab/BankItemHolder.java @@ -28,11 +28,8 @@ import com.questhelper.QuestHelperConfig; import com.questhelper.requirements.item.ItemRequirement; -import com.questhelper.spells.SearchPreference; import java.util.List; -import javax.annotation.Nullable; import net.runelite.api.Client; -import net.runelite.api.Item; /** * Represents anything that holds {@link ItemRequirement}s that are to be used diff --git a/src/main/java/com/questhelper/banktab/QuestHelperBankTagService.java b/src/main/java/com/questhelper/banktab/QuestHelperBankTagService.java index e5a139a795..8c299ba691 100644 --- a/src/main/java/com/questhelper/banktab/QuestHelperBankTagService.java +++ b/src/main/java/com/questhelper/banktab/QuestHelperBankTagService.java @@ -29,7 +29,6 @@ import com.questhelper.requirements.item.ItemRequirement; import com.questhelper.requirements.item.ItemRequirements; import com.questhelper.requirements.util.LogicType; -import com.questhelper.spells.SearchPreference; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -38,7 +37,6 @@ import java.util.stream.Collectors; import javax.inject.Inject; import net.runelite.api.InventoryID; -import net.runelite.api.Item; import net.runelite.api.ItemContainer; public class QuestHelperBankTagService diff --git a/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java b/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java index a127cb1adc..a8f5169a0f 100644 --- a/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java +++ b/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java @@ -165,7 +165,7 @@ public void setStaff(int staffID) { if (!useStaff) { - return; + throw new UnsupportedOperationException("Cannot require a staff and then require no staff"); } if (staffID < 0) { @@ -194,7 +194,7 @@ public void setStaffUse(boolean useStaff) { if (!useStaff && staffRequirement != null) { - throw new UnsupportedOperationException("Cannot require a staff and then require no staff: " + this); + throw new UnsupportedOperationException("Cannot require a staff and then require no staff"); } this.useStaff = useStaff; } @@ -461,7 +461,7 @@ public List getRequirements(Client client, QuestHelperConfig co private boolean hasStaff(Client client, ItemRequirement staves) { - if (staves == null || staffRequirement != null) + if (staves == null || staffRequirement != null || !useStaff) { return false; } diff --git a/src/main/java/com/questhelper/spells/SearchPreference.java b/src/main/java/com/questhelper/spells/SpellComponentPreference.java similarity index 93% rename from src/main/java/com/questhelper/spells/SearchPreference.java rename to src/main/java/com/questhelper/spells/SpellComponentPreference.java index 32d8eb34f1..f9711d2f54 100644 --- a/src/main/java/com/questhelper/spells/SearchPreference.java +++ b/src/main/java/com/questhelper/spells/SpellComponentPreference.java @@ -32,14 +32,14 @@ import com.questhelper.util.TriFunction; import java.util.function.BooleanSupplier; -public enum SearchPreference +public enum SpellComponentPreference { RUNES((runes, staff, req) -> runes.getAsBoolean() ? req.getRuneItemRequirement() : req.getStaffItemRequirement()), STAVES((runes, staff, req) -> staff.getAsBoolean() ? req.getStaffItemRequirement() : req.getRuneItemRequirement()); ; private final TriFunction function; - SearchPreference(TriFunction function) + SpellComponentPreference(TriFunction function) { this.function = function; } From 7e4b8b0d018916ad8d11bfe52f37c4f3639ecd94 Mon Sep 17 00:00:00 2001 From: Senmori Date: Wed, 10 Feb 2021 15:49:45 -0500 Subject: [PATCH 39/55] Revert all changes to quest. --- .../quests/darknessofhallowvale/DarknessOfHallowvale.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/com/questhelper/quests/darknessofhallowvale/DarknessOfHallowvale.java b/src/main/java/com/questhelper/quests/darknessofhallowvale/DarknessOfHallowvale.java index 3615209579..b35ef70898 100644 --- a/src/main/java/com/questhelper/quests/darknessofhallowvale/DarknessOfHallowvale.java +++ b/src/main/java/com/questhelper/quests/darknessofhallowvale/DarknessOfHallowvale.java @@ -768,8 +768,7 @@ public List getItemRequirements() @Override public List getItemRecommended() { - SpellRequirement spell = StandardSpell.EARTH_SURGE.getSpellRequirement(); - return Arrays.asList(lawRune, airRune, spell); + return Arrays.asList(lawRune, airRune); } @Override From 6ee14a00a50b208f4ce71616b2a51b71056af005 Mon Sep 17 00:00:00 2001 From: Senmori Date: Wed, 10 Feb 2021 16:25:40 -0500 Subject: [PATCH 40/55] getUpdatedTooltip should not use BankItems because we now use ItemSearch. Add javadocs and some import cleanup --- src/main/java/com/questhelper/ItemSearch.java | 6 +-- .../com/questhelper/QuestHelperConfig.java | 3 +- .../questhelper/panel/QuestOverviewPanel.java | 2 +- .../requirements/item/ItemRequirement.java | 2 +- .../requirements/magic/SpellRequirement.java | 40 +++++++++++++------ .../java/com/questhelper/spells/Staff.java | 2 - 6 files changed, 35 insertions(+), 20 deletions(-) diff --git a/src/main/java/com/questhelper/ItemSearch.java b/src/main/java/com/questhelper/ItemSearch.java index 5f47f4fa9c..ae8f496dfc 100644 --- a/src/main/java/com/questhelper/ItemSearch.java +++ b/src/main/java/com/questhelper/ItemSearch.java @@ -29,7 +29,7 @@ import com.questhelper.requirements.item.ItemRequirement; import com.questhelper.requirements.util.InventorySlots; -import java.util.List; +import java.util.Collection; import java.util.Objects; import java.util.stream.Stream; import lombok.experimental.UtilityClass; @@ -132,7 +132,7 @@ public long getItemAmountExact(Client client, InventoryID inventoryID, int itemI ItemContainer container = client.getItemContainer(inventoryID); if (container == null) { - return 0; + return 0L; } return Stream.of(container.getItems()) .filter(Objects::nonNull) @@ -141,7 +141,7 @@ public long getItemAmountExact(Client client, InventoryID inventoryID, int itemI .sum(); } - public int findFirstItem(Client client, List itemIDs, int amount) + public int findFirstItem(Client client, Collection itemIDs, int amount) { return itemIDs.stream() .filter(id -> hasItemAmountAnywhere(client, id, amount)) diff --git a/src/main/java/com/questhelper/QuestHelperConfig.java b/src/main/java/com/questhelper/QuestHelperConfig.java index 3adf69a863..3267148744 100644 --- a/src/main/java/com/questhelper/QuestHelperConfig.java +++ b/src/main/java/com/questhelper/QuestHelperConfig.java @@ -132,7 +132,8 @@ default boolean showSymbolOverlay() @ConfigItem( keyName = "bankSearchSpellPreference", name = "Spell Component Preference", - description = "Choose whether runes or staves should be preferred when filtering spell components." + description = "Choose whether runes or staves should be preferred when filtering spell components.", + hidden = true ) default SpellComponentPreference bankFilterSpellPreference() { diff --git a/src/main/java/com/questhelper/panel/QuestOverviewPanel.java b/src/main/java/com/questhelper/panel/QuestOverviewPanel.java index 2828476e4f..8ceb82cafe 100644 --- a/src/main/java/com/questhelper/panel/QuestOverviewPanel.java +++ b/src/main/java/com/questhelper/panel/QuestOverviewPanel.java @@ -506,7 +506,7 @@ public void updateRequirementPanels(Client client, List r { newColor = itemRequirement.getColorConsideringBank(client, false, bankItems.getItems()); } - String updatedTooltip = itemRequirement.getUpdatedTooltip(client, bankItems); + String updatedTooltip = itemRequirement.getUpdatedTooltip(client); requirementPanel.setInfoButtonTooltip(updatedTooltip); } else diff --git a/src/main/java/com/questhelper/requirements/item/ItemRequirement.java b/src/main/java/com/questhelper/requirements/item/ItemRequirement.java index 02b0f1ccf7..bfe0b75afb 100644 --- a/src/main/java/com/questhelper/requirements/item/ItemRequirement.java +++ b/src/main/java/com/questhelper/requirements/item/ItemRequirement.java @@ -392,7 +392,7 @@ public List getDisplayItemIds() } @Nullable - public String getUpdatedTooltip(Client client, BankItems bankItems) + public String getUpdatedTooltip(Client client) { return null; } diff --git a/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java b/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java index a8f5169a0f..eea970a19a 100644 --- a/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java +++ b/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java @@ -71,7 +71,7 @@ * 3. Player has staff * * We can ignore the requirements if the player has the tablet because those - * have no requirements in order to be used. + * have no requirements in order to be used (other than quests). */ /** @@ -88,9 +88,9 @@ * it is more compact and has less requirements. */ @Slf4j -@Getter public class SpellRequirement extends ItemRequirement implements BankItemHolder { + @Getter private final MagicSpell spell; private ItemRequirement tabletRequirement = null; @@ -103,8 +103,6 @@ public class SpellRequirement extends ItemRequirement implements BankItemHolder /** @return all {@link Requirement}s on this SpellRequirement */ private final List requirements = new ArrayList<>(); private final List runeRequirements = new LinkedList<>(); - private final SkillRequirement skillRequirement; - private final SpellbookRequirement spellbookRequirement; public SpellRequirement(MagicSpell spell, Map runeCostMap, List requirements) { @@ -118,16 +116,16 @@ public SpellRequirement(MagicSpell spell, int numberOfCasts, Map this.numberOfCasts = numberOfCasts; this.runeCostMap = runeCostMap; this.requirements.addAll(requirements); - this.skillRequirement = new SkillRequirement(Skill.MAGIC, spell.getRequiredMagicLevel()); - this.spellbookRequirement = new SpellbookRequirement(spell.getSpellbook()); - this.requirements.add(this.skillRequirement); - this.requirements.add(this.spellbookRequirement); + this.requirements.add(new SkillRequirement(Skill.MAGIC, spell.getRequiredMagicLevel())); + this.requirements.add(new SpellbookRequirement(spell.getSpellbook())); setNumberOfCasts(numberOfCasts); updateTooltip(); } /** * Add an additional {@link Requirement}. + * If this is an {@link ItemRequirement}, it's assumed that it's directly needed to cast this spell. + * Thus, it's quantity will be adjusted to match the number of casts this requirement has. * * @param requirement requirement to add */ @@ -161,6 +159,13 @@ public void setTablet(int itemID) } } + /** + * Set the new staff item id this requirement should use. + * + * @param staffID the new staff item id. + * + * @throws UnsupportedOperationException if staff use is disabled. + */ public void setStaff(int staffID) { if (!useStaff) @@ -177,6 +182,9 @@ public void setStaff(int staffID) } } + /** + * @return true if this requirement currently has a staff and if staves are enabled. + */ public boolean hasStaff() { return staffRequirement != null && useStaff; @@ -190,6 +198,13 @@ public void doNotUseTablet() setTablet(-1); } + /** + * Set if this requirement should use staffs. + * + * @param useStaff true to use staffs. + * + * @throws UnsupportedOperationException if staff use is disabled while there is still a staff required + */ public void setStaffUse(boolean useStaff) { if (!useStaff && staffRequirement != null) @@ -354,8 +369,8 @@ public Color getColorConsideringBank(Client client, boolean checkWithSlotRestric boolean hasItems = false; List itemRequirements = getItemRequirements(this.requirements); updateInternalState(client, itemRequirements); - hasRunes = runeRequirements.stream().allMatch(req -> hasItemAmount(client, req.getAllIds(), req.getRequiredAmount())); - hasItems = itemRequirements.stream().allMatch(req -> hasItemAmount(client, req.getAllIds(), req.getQuantity())); + hasRunes = runeRequirements.stream().allMatch(req -> ItemSearch.hasItemsOnPlayer(client, req)); + hasItems = itemRequirements.stream().allMatch(req -> ItemSearch.hasItemsOnPlayer(client, req)); if (hasRunes && hasItems) { return Color.GREEN; @@ -475,10 +490,10 @@ private boolean hasStaff(Client client, ItemRequirement staves) @Nullable @Override - public String getUpdatedTooltip(Client client, BankItems bankItems) + public String getUpdatedTooltip(Client client) { StringBuilder text = new StringBuilder(); - if (tabletRequirement != null && tabletRequirement.check(client, false, bankItems.getItems())) + if (tabletRequirement != null && ItemSearch.hasItemAnywhere(client, tabletRequirement.getId())) { // only show tooltip for tablet if they actually have it AtomicInteger count = new AtomicInteger(); getNonItemRequirements(this.requirements).stream() @@ -535,6 +550,7 @@ private void updateItemRequirements(Client client, List require requirements.stream() .filter(req -> StringUtils.isBlank(req.getName())) .forEach(req -> req.setName(client.getItemDefinition(req.getId()).getName())); + requirements.forEach(item -> item.setQuantity(this.numberOfCasts)); } private void updateTabletRequirement(Client client) diff --git a/src/main/java/com/questhelper/spells/Staff.java b/src/main/java/com/questhelper/spells/Staff.java index 77529c6204..474d51dde3 100644 --- a/src/main/java/com/questhelper/spells/Staff.java +++ b/src/main/java/com/questhelper/spells/Staff.java @@ -29,9 +29,7 @@ import com.questhelper.ItemCollections; import java.util.Arrays; import java.util.Collections; -import java.util.Comparator; import java.util.List; -import java.util.stream.Collectors; import java.util.stream.Stream; import lombok.Getter; From 973cf8bbb660e6b9ed110cf3a0ce7da613e98803 Mon Sep 17 00:00:00 2001 From: Senmori Date: Thu, 11 Feb 2021 12:46:27 -0500 Subject: [PATCH 41/55] Remove imports from DarknessOfHallowvale. --- .../quests/darknessofhallowvale/DarknessOfHallowvale.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/com/questhelper/quests/darknessofhallowvale/DarknessOfHallowvale.java b/src/main/java/com/questhelper/quests/darknessofhallowvale/DarknessOfHallowvale.java index b35ef70898..b26bf93602 100644 --- a/src/main/java/com/questhelper/quests/darknessofhallowvale/DarknessOfHallowvale.java +++ b/src/main/java/com/questhelper/quests/darknessofhallowvale/DarknessOfHallowvale.java @@ -30,7 +30,6 @@ import com.questhelper.Zone; import com.questhelper.panel.PanelDetails; import com.questhelper.questhelpers.BasicQuestHelper; -import com.questhelper.requirements.magic.SpellRequirement; import com.questhelper.requirements.player.InInstanceRequirement; import com.questhelper.requirements.item.ItemRequirement; import com.questhelper.requirements.item.ItemRequirements; @@ -45,7 +44,6 @@ import com.questhelper.requirements.util.LogicType; import com.questhelper.requirements.util.Operation; import com.questhelper.requirements.util.Spellbook; -import com.questhelper.spells.StandardSpell; import com.questhelper.steps.ConditionalStep; import com.questhelper.steps.DetailedQuestStep; import com.questhelper.steps.NpcStep; From 98d000f624400974bcadfdb76962049c8e9c9c50 Mon Sep 17 00:00:00 2001 From: Senmori Date: Thu, 11 Feb 2021 12:47:00 -0500 Subject: [PATCH 42/55] Remove forwarding call in NpcStep. Users can add spell icons to a QuestStep via QuestStep#addSpell. --- src/main/java/com/questhelper/steps/NpcStep.java | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/main/java/com/questhelper/steps/NpcStep.java b/src/main/java/com/questhelper/steps/NpcStep.java index 7990a7a356..7a87d2fc5a 100644 --- a/src/main/java/com/questhelper/steps/NpcStep.java +++ b/src/main/java/com/questhelper/steps/NpcStep.java @@ -128,16 +128,6 @@ public void addSafeSpots(WorldPoint... points) this.safespots.addAll(Arrays.asList(points)); } - public void requireSpellCast(SpellRequirement spellRequirement) - { - addSpell(spellRequirement.getSpell()); - } - - public void requireSpellCast(MagicSpell spell) - { - addSpell(spell); - } - public List allIds() { List ids = new ArrayList<>(); From f7feadd660012bfac9ec03e25c86533ea61cf137 Mon Sep 17 00:00:00 2001 From: Senmori Date: Thu, 11 Feb 2021 12:48:08 -0500 Subject: [PATCH 43/55] Add null check for configs so we don't pass null configs into BankItemHolder. --- .../banktab/QuestHelperBankTagService.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/questhelper/banktab/QuestHelperBankTagService.java b/src/main/java/com/questhelper/banktab/QuestHelperBankTagService.java index 8c299ba691..cfc6d8b1d1 100644 --- a/src/main/java/com/questhelper/banktab/QuestHelperBankTagService.java +++ b/src/main/java/com/questhelper/banktab/QuestHelperBankTagService.java @@ -24,6 +24,7 @@ */ package com.questhelper.banktab; +import com.questhelper.QuestHelperConfig; import com.questhelper.QuestHelperPlugin; import com.questhelper.panel.PanelDetails; import com.questhelper.requirements.item.ItemRequirement; @@ -133,10 +134,14 @@ else if (itemRequirement instanceof BankItemHolder) BankItemHolder holder = (BankItemHolder) itemRequirement; // Force run on client thread even though it's not as responsive as not doing that, however it // ensures we run on the client thread and never run into threading issues. - plugin.getClientThread().invoke(() -> { - List reqs = holder.getRequirements(plugin.getClient(), plugin.getConfig()); - makeBankHolderItems(reqs, pluginItems); // callback because we can't halt on the client thread - }); + QuestHelperConfig config = plugin.getConfig(); + if (config != null) + { + plugin.getClientThread().invoke(() -> { + List reqs = holder.getRequirements(plugin.getClient(), config); + makeBankHolderItems(reqs, pluginItems); // callback because we can't halt on the client thread + }); + } } else { From 61e98aa8ca2a0bf2bc054101f6792c2bd745a005 Mon Sep 17 00:00:00 2001 From: Senmori Date: Thu, 11 Feb 2021 14:00:52 -0500 Subject: [PATCH 44/55] Add missing requirements for some spells. Add staff builder method to specify explicitly the staff required in order to cast this spell. Also added a skill method to indicate a non-magic skill required. --- .../com/questhelper/spells/StandardSpell.java | 26 ++++++++++++++---- .../spells/StandardSpellBuilder.java | 27 +++++++++++++++++++ 2 files changed, 48 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/questhelper/spells/StandardSpell.java b/src/main/java/com/questhelper/spells/StandardSpell.java index 481fdad9a2..cb5ffd94d1 100644 --- a/src/main/java/com/questhelper/spells/StandardSpell.java +++ b/src/main/java/com/questhelper/spells/StandardSpell.java @@ -29,14 +29,30 @@ import static com.questhelper.QuestHelperQuest.PLAGUE_CITY; import static com.questhelper.QuestHelperQuest.THE_MAGE_ARENA; import static com.questhelper.QuestHelperQuest.EADGARS_RUSE; +import static com.questhelper.QuestHelperQuest.UNDERGROUND_PASS; import com.questhelper.requirements.magic.SpellRequirement; import com.questhelper.requirements.util.Spellbook; +import static com.questhelper.spells.Rune.FIRE; import java.util.Locale; import java.util.function.UnaryOperator; import lombok.Getter; import static com.questhelper.spells.Rune.*; import net.runelite.api.ItemID; +import static net.runelite.api.ItemID.GUTHIX_STAFF; +import static net.runelite.api.ItemID.IBANS_STAFF; +import static net.runelite.api.ItemID.IBANS_STAFF_U; +import static net.runelite.api.ItemID.SARADOMIN_STAFF; +import static net.runelite.api.ItemID.SLAYERS_STAFF; +import static net.runelite.api.ItemID.SLAYERS_STAFF_E; +import static net.runelite.api.ItemID.STAFF_OF_BALANCE; +import static net.runelite.api.ItemID.STAFF_OF_LIGHT; +import static net.runelite.api.ItemID.STAFF_OF_THE_DEAD; +import static net.runelite.api.ItemID.STAFF_OF_THE_DEAD_23613; +import static net.runelite.api.ItemID.TOXIC_STAFF_OF_THE_DEAD; +import static net.runelite.api.ItemID.VOID_KNIGHT_MACE; +import static net.runelite.api.ItemID.ZAMORAK_STAFF; +import net.runelite.api.Skill; import net.runelite.api.Varbits; import org.apache.commons.text.WordUtils; @@ -78,9 +94,9 @@ public enum StandardSpell implements MagicSpell WATER_BLAST(38, 32, 47, b -> b.rune(DEATH).rune(3, AIR).rune(3, WATER)), LVL_3_ENCHANT(39, 33, 49, b -> b.rune(COSMIC).rune(5, FIRE)), ENCHANT_RUBY_BOLT(358, 8, 49, b -> b.rune(COSMIC).rune(BLOOD).rune(5, FIRE)), - IBAN_BLAST(53, 34, 50, b -> b.rune(DEATH).rune(5, FIRE).item(true, ItemID.IBANS_STAFF, ItemID.IBANS_STAFF_U)), + IBAN_BLAST(53, 34, 50, b -> b.rune(DEATH).rune(5, FIRE).skill(Skill.ATTACK, 50).quest(UNDERGROUND_PASS).staff(IBANS_STAFF, IBANS_STAFF_U)), SNARE(320, 35, 50, b -> b.rune(3, NATURE).rune(4, WATER).rune(4, EARTH)), - MAGIC_DART(324, 36, 50, b -> b.rune(DEATH).rune(4, MIND)), + MAGIC_DART(324, 36, 50, b -> b.rune(DEATH).rune(4, MIND).staff(SLAYERS_STAFF, SLAYERS_STAFF_E, STAFF_OF_THE_DEAD, STAFF_OF_THE_DEAD_23613, TOXIC_STAFF_OF_THE_DEAD, STAFF_OF_LIGHT, STAFF_OF_BALANCE)), ARDOUGNE_TELEPORT(54, 37, 51, b -> b.rune(2, LAW).rune(2, WATER).tablet(ItemID.ARDOUGNE_TELEPORT).quest(PLAGUE_CITY)), EARTH_BLAST(40, 38, 43, b -> b.rune(DEATH).rune(3, AIR).rune(4, EARTH)), HIGH_LVL_ALCHEMY(41, 39, 55, b -> b.rune(NATURE).rune(5, FIRE)), @@ -91,9 +107,9 @@ public enum StandardSpell implements MagicSpell FIRE_BLAST(44, 43, 59, b -> b.rune(DEATH).rune(4, AIR).rune(5, FIRE)), CHARGE_EARTH_ORB(45, 44, 60, b -> b.rune(3, COSMIC).rune(30, EARTH).item(ItemID.UNPOWERED_ORB)), BONES_TO_PEACHES(354, 45, 60, b -> b.rune(2, NATURE).rune(2, EARTH).rune(4, WATER)), //TODO: MAGE TRAINING ARENA - SARADOMIN_STRIKE(61, 46, 60, b -> b.rune(2, BLOOD).rune(2, FIRE).rune(4, AIR).quest(THE_MAGE_ARENA)), - CLAWS_OF_GUTHIX(60, 47, 60, b -> b.rune(FIRE).rune(2, BLOOD).rune(4, AIR).quest(THE_MAGE_ARENA)), - FLAMES_OF_ZAMORAK(59, 48, 60, b -> b.rune(AIR).rune(2, BLOOD).rune(4, FIRE).quest(THE_MAGE_ARENA)), + SARADOMIN_STRIKE(61, 46, 60, b -> b.rune(2, BLOOD).rune(2, FIRE).rune(4, AIR).quest(THE_MAGE_ARENA).staff(SARADOMIN_STAFF, STAFF_OF_LIGHT)), + CLAWS_OF_GUTHIX(60, 47, 60, b -> b.rune(FIRE).rune(2, BLOOD).rune(4, AIR).quest(THE_MAGE_ARENA).staff(GUTHIX_STAFF, VOID_KNIGHT_MACE, STAFF_OF_BALANCE)), + FLAMES_OF_ZAMORAK(59, 48, 60, b -> b.rune(AIR).rune(2, BLOOD).rune(4, FIRE).quest(THE_MAGE_ARENA).staff(ZAMORAK_STAFF, STAFF_OF_THE_DEAD, STAFF_OF_THE_DEAD_23613, TOXIC_STAFF_OF_THE_DEAD)), TROLLHEIM_TELEPORT(323, 49, 61, b -> b.rune(2, LAW).rune(2, FIRE).quest(EADGARS_RUSE)), WIND_WAVE(46, 50, 62, b -> b.rune(BLOOD).rune(5, AIR)), CHARGE_FIRE_ORB(47, 51, 63, b -> b.rune(3, COSMIC).rune(30, FIRE).item(ItemID.UNPOWERED_ORB)), diff --git a/src/main/java/com/questhelper/spells/StandardSpellBuilder.java b/src/main/java/com/questhelper/spells/StandardSpellBuilder.java index c3552d4fda..d830d38677 100644 --- a/src/main/java/com/questhelper/spells/StandardSpellBuilder.java +++ b/src/main/java/com/questhelper/spells/StandardSpellBuilder.java @@ -31,15 +31,18 @@ import com.questhelper.requirements.magic.SpellRequirement; import com.questhelper.requirements.item.ItemRequirement; import com.questhelper.requirements.item.ItemRequirements; +import com.questhelper.requirements.player.SkillRequirement; import com.questhelper.requirements.quest.QuestRequirement; import com.questhelper.requirements.var.VarbitRequirement; import com.questhelper.requirements.var.VarplayerRequirement; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import net.runelite.api.QuestState; +import net.runelite.api.Skill; import net.runelite.api.VarPlayer; import net.runelite.api.Varbits; @@ -48,6 +51,7 @@ public class StandardSpellBuilder private final MagicSpell spell; private final List requirements = new LinkedList<>(); private final Map runeList = new HashMap<>(); + private final List staffIDList = new ArrayList<>(); private int tabletItemID = -1; private StandardSpellBuilder(MagicSpell spell) @@ -90,6 +94,18 @@ public StandardSpellBuilder rune(Rune rune) return rune(1, rune); } + public StandardSpellBuilder staff(int staffID) + { + this.staffIDList.add(staffID); + return this; + } + + public StandardSpellBuilder staff(Integer... staves) + { + this.staffIDList.addAll(Arrays.asList(staves)); + return this; + } + /** * Add an item this spell needs in order to be cast. * @@ -208,6 +224,12 @@ public StandardSpellBuilder varbit(int varbit, int value) return this; } + public StandardSpellBuilder skill(Skill skill, int level) + { + requirements.add(new SkillRequirement(skill, level)); + return this; + } + /** * @return a new {@link ItemRequirements} containing all this spell information */ @@ -218,6 +240,11 @@ public SpellRequirement build() { requirement.setTablet(tabletItemID); } + if (!staffIDList.isEmpty()) + { + requirement.setStaffUse(true); + requirement.setStaff(new ItemRequirement("", staffIDList, 1, true)); + } return requirement; } From 63c1289831d64c977041c3718f758faac9be3d96 Mon Sep 17 00:00:00 2001 From: Senmori Date: Thu, 11 Feb 2021 14:02:11 -0500 Subject: [PATCH 45/55] Add method to set the staff with an item requirement instead of just an item id. Set the name of all blank item requirements. This will default to the first item id found in the requirement if the player has none of the items listed. --- .../requirements/magic/SpellRequirement.java | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java b/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java index eea970a19a..34ce0f400c 100644 --- a/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java +++ b/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java @@ -182,6 +182,15 @@ public void setStaff(int staffID) } } + public void setStaff(ItemRequirement staff) + { + if (!useStaff) + { + throw new UnsupportedOperationException("Cannot require a staff and then require no staff"); + } + this.staffRequirement = staff; + } + /** * @return true if this requirement currently has a staff and if staves are enabled. */ @@ -387,11 +396,6 @@ public Color getColorConsideringBank(Client client, boolean checkWithSlotRestric return Color.RED; } - private boolean hasItemAmount(Client client, List idList, int amount) - { - return InventorySlots.INVENTORY_SLOTS.contains(client, i -> idList.contains(i.getId()) && i.getQuantity() >= amount); - } - @Override public boolean check(Client client, boolean checkWithSlotRestrictions, Item[] items) @@ -470,7 +474,13 @@ public List getRequirements(Client client, QuestHelperConfig co bankRequirements.addAll(runeItemRequirements.values()); bankRequirements.addAll(getItemRequirements(this.requirements)); - updateInternalState(client, bankRequirements); + bankRequirements.stream() + .filter(req -> StringUtils.isBlank(req.getName())) + .forEach(req -> { + int firstID = ItemSearch.findFirstItem(client, req.getAllIds(), req.getQuantity()); + if (firstID < 0) firstID = req.getId(); + req.setName(client.getItemDefinition(firstID).getName()); + }); return bankRequirements; } @@ -480,7 +490,7 @@ private boolean hasStaff(Client client, ItemRequirement staves) { return false; } - boolean hasStaff = ItemSearch.hasItemsAnywhere(client, staves); + boolean hasStaff = staves.getAllIds().stream().anyMatch(staffID -> ItemSearch.hasItemAmountAnywhere(client, staffID, 1)); if (useStaff && hasStaff) { staffRequirement = staves; From 2fcf99a94c4c6b0c18c16371eb7866b6ba8685b1 Mon Sep 17 00:00:00 2001 From: Senmori Date: Fri, 19 Feb 2021 10:39:41 -0500 Subject: [PATCH 46/55] ItemSearch now supports cached bank items. SpellRequirements now color correctly both on the side panel and the overlay panel. SpellRequirement overlay text is colored separately to give the best indication of what is missing from a spell requirement. --- src/main/java/com/questhelper/ItemSearch.java | 8 ++ .../requirements/item/ItemRequirement.java | 6 +- .../requirements/magic/SpellRequirement.java | 88 ++++++++++++------- 3 files changed, 67 insertions(+), 35 deletions(-) diff --git a/src/main/java/com/questhelper/ItemSearch.java b/src/main/java/com/questhelper/ItemSearch.java index ae8f496dfc..ee5c8d48c2 100644 --- a/src/main/java/com/questhelper/ItemSearch.java +++ b/src/main/java/com/questhelper/ItemSearch.java @@ -122,6 +122,14 @@ public boolean hasItemsInBank(Client client, ItemRequirement requirement) return requirement.getAllIds().stream().anyMatch(id -> hasItemAmountInBank(client, id, requirement.getQuantity())); } + public boolean hasItemsInCachedBank(ItemRequirement requirement, Item[] items) + { + return Stream.of(items) + .filter(Objects::nonNull) + .filter(i -> i.getId() > -1 && i.getQuantity() > -1) // filter out invalid/empty items + .anyMatch(i -> requirement.getAllIds().contains(i.getId()) && i.getQuantity() >= requirement.getQuantity()); + } + public boolean checkItem(Client client, InventorySlots slot, int itemID) { return slot.contains(client, i -> i.getId() == itemID); diff --git a/src/main/java/com/questhelper/requirements/item/ItemRequirement.java b/src/main/java/com/questhelper/requirements/item/ItemRequirement.java index 2ec55cc3bc..e5782acf2e 100644 --- a/src/main/java/com/questhelper/requirements/item/ItemRequirement.java +++ b/src/main/java/com/questhelper/requirements/item/ItemRequirement.java @@ -291,14 +291,14 @@ public Color getColorConsideringBank(Client client, boolean checkConsideringSlot { color = Color.GRAY; } - else if (this.check(client, checkConsideringSlotRestrictions)) + else if (ItemSearch.hasItemsOnPlayer(client, this)) { color = Color.GREEN; } - if (color == Color.RED && bankItems != null) + if (color == Color.RED) { - if (check(client, false, bankItems)) + if (ItemSearch.hasItemsInBank(client, this) || (bankItems != null && ItemSearch.hasItemsInCachedBank(this, bankItems))) { color = Color.WHITE; } diff --git a/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java b/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java index 34ce0f400c..8532154521 100644 --- a/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java +++ b/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java @@ -119,7 +119,6 @@ public SpellRequirement(MagicSpell spell, int numberOfCasts, Map this.requirements.add(new SkillRequirement(Skill.MAGIC, spell.getRequiredMagicLevel())); this.requirements.add(new SpellbookRequirement(spell.getSpellbook())); setNumberOfCasts(numberOfCasts); - updateTooltip(); } /** @@ -179,6 +178,7 @@ public void setStaff(int staffID) else { this.staffRequirement = new ItemRequirement("", staffID); + doNotUseTablet(); } } @@ -189,6 +189,7 @@ public void setStaff(ItemRequirement staff) throw new UnsupportedOperationException("Cannot require a staff and then require no staff"); } this.staffRequirement = staff; + doNotUseTablet(); } /** @@ -286,20 +287,48 @@ public List getOverlayDisplayText(Client client) String name = spell.getName(); if (tabletRequirement != null && tabletRequirement.check(client)) { - name = tabletRequirement.getName(); + text.append(tabletRequirement.getName()); } - text.append(name); - - Color color = getColorConsideringBank(client, false, null); - if (color == Color.RED && checkBank(client)) + else { - color = Color.WHITE; + text.append(name); } + Color color = getColorConsideringBank(client, false, null); + // 3 x lines.add(LineComponent.builder() .left(text.toString()) .leftColor(color) .build() ); + if (hasStaff()) + { + int firstStaffID = ItemSearch.findFirstItem(client, staffRequirement.getAllIds(), staffRequirement.getQuantity()); + String staffName = staffRequirement.getName(); + if (staffName == null || staffName.isEmpty()) + { + staffName = client.getItemDefinition(firstStaffID).getName(); + } + Color staffColor = Color.RED; + if (ItemSearch.hasItemsOnPlayer(client, staffRequirement)) + { + staffColor = Color.GREEN; + } + else if (ItemSearch.hasItemsInBank(client, staffRequirement)) + { + staffColor = Color.WHITE; + } + // + lines.add(LineComponent.builder() + .left(staffName) + .leftColor(staffColor) + .build()); + // Add '(equipped)' + staffColor = ItemSearch.hasItemEquipped(client, firstStaffID) ? Color.GREEN : Color.RED; + lines.add(LineComponent.builder() + .left("(equipped)") + .leftColor(staffColor) + .build()); + } return lines; } @@ -344,7 +373,13 @@ public boolean checkBank(Client client) } boolean hasRunes = runeRequirements.stream().allMatch(req -> req.checkBank(client)); boolean hasItems = requirements.stream().allMatch(req -> req.check(client)); - return hasRunes && hasItems; + boolean hasStaff = !hasStaff(); + if (hasStaff()) + { + int firstStaffID = ItemSearch.findFirstItem(client, staffRequirement.getAllIds(), staffRequirement.getQuantity()); + hasStaff = ItemSearch.hasItemAmountInBank(client, firstStaffID, staffRequirement.getQuantity()); + } + return hasRunes && hasItems && (hasStaff() && hasStaff); } @Override @@ -367,32 +402,26 @@ public Color getColorConsideringBank(Client client, boolean checkWithSlotRestric boolean hasOtherReqs = getNonItemRequirements(this.requirements).stream().allMatch(req -> req.check(client)); if (!hasOtherReqs) { - return Color.RED; - } - boolean hasStaff = staffRequirement != null; - if (hasStaff && !staffRequirement.check(client, checkWithSlotRestrictions, bankItems)) - { - return Color.RED; // required staff is not present + return Color.RED; // abort early if they can't even cast the spell } + boolean hasStaff = !hasStaff() || ItemSearch.hasItemsOnPlayer(client, staffRequirement); boolean hasRunes = false; boolean hasItems = false; List itemRequirements = getItemRequirements(this.requirements); - updateInternalState(client, itemRequirements); hasRunes = runeRequirements.stream().allMatch(req -> ItemSearch.hasItemsOnPlayer(client, req)); hasItems = itemRequirements.stream().allMatch(req -> ItemSearch.hasItemsOnPlayer(client, req)); - if (hasRunes && hasItems) + if (hasRunes && hasItems && hasStaff) { return Color.GREEN; } - if (bankItems != null) + hasRunes = runeRequirements.stream().allMatch(req -> req.checkBank(client)); // Don't use ItemSearch here because RuneRequirement overrides checkBank + hasItems = itemRequirements.stream().allMatch(req -> ItemSearch.hasItemsInBank(client, req)); + hasStaff = !hasStaff() || ItemSearch.hasItemsInBank(client, staffRequirement); + if (hasRunes || hasItems || hasStaff) { - hasRunes = runeRequirements.stream().allMatch(req -> req.checkBank(client)); - hasItems = itemRequirements.stream().allMatch(req -> ItemSearch.hasItemsInBank(client, req)); - if (hasRunes && hasItems) - { - return Color.WHITE; - } + return Color.WHITE; } + updateInternalState(client, itemRequirements); return Color.RED; } @@ -405,9 +434,10 @@ public boolean check(Client client, boolean checkWithSlotRestrictions, Item[] it updateTabletRequirement(client); return true; } - if (staffRequirement != null && ItemSearch.hasItemsAnywhere(client, staffRequirement)) + boolean hasStaff = true; + if (hasStaff()) { - return true; + hasStaff = ItemSearch.hasItemsAnywhere(client, staffRequirement); } boolean hasItems, hasOther, hasRunes; List itemRequirements = getItemRequirements(this.requirements); @@ -422,7 +452,7 @@ public boolean check(Client client, boolean checkWithSlotRestrictions, Item[] it } hasOther = getNonItemRequirements(this.requirements).stream().allMatch(req -> req.check(client)); hasRunes = runeRequirements.stream().allMatch(req -> req.check(client, checkWithSlotRestrictions, items)); - return hasItems && hasOther && hasRunes; + return hasItems && hasOther && hasRunes && hasStaff; } @Override @@ -549,12 +579,6 @@ private void updateInternalState(Client client, List requiremen } } - private void updateTooltip() - { - setTooltip("This spell requires: "); - getNonItemRequirements(this.requirements).forEach(req -> appendToTooltip(req.getDisplayText())); - } - private void updateItemRequirements(Client client, List requirements) { requirements.stream() From eee1a3bd46c3190517ae9e602e1513f482ee563b Mon Sep 17 00:00:00 2001 From: Senmori Date: Fri, 19 Feb 2021 13:57:31 -0500 Subject: [PATCH 47/55] Overlays no longer check player banks for item checks. Added an update requirement panel call when opening banks to ensure we update the requirements accordingly. Added methods to exclusively get the colors for overlays so we don't have to share logic between side panels and overlays. --- src/main/java/com/questhelper/ItemSearch.java | 4 ++ .../com/questhelper/QuestHelperPlugin.java | 1 + .../requirements/item/ItemRequirement.java | 10 ++- .../requirements/magic/RuneRequirement.java | 6 ++ .../requirements/magic/SpellRequirement.java | 62 ++++++++++++++----- 5 files changed, 64 insertions(+), 19 deletions(-) diff --git a/src/main/java/com/questhelper/ItemSearch.java b/src/main/java/com/questhelper/ItemSearch.java index ee5c8d48c2..638c5c944e 100644 --- a/src/main/java/com/questhelper/ItemSearch.java +++ b/src/main/java/com/questhelper/ItemSearch.java @@ -124,6 +124,10 @@ public boolean hasItemsInBank(Client client, ItemRequirement requirement) public boolean hasItemsInCachedBank(ItemRequirement requirement, Item[] items) { + if (items == null) + { + return false; + } return Stream.of(items) .filter(Objects::nonNull) .filter(i -> i.getId() > -1 && i.getQuantity() > -1) // filter out invalid/empty items diff --git a/src/main/java/com/questhelper/QuestHelperPlugin.java b/src/main/java/com/questhelper/QuestHelperPlugin.java index 4c5a911541..04750b5a9d 100644 --- a/src/main/java/com/questhelper/QuestHelperPlugin.java +++ b/src/main/java/com/questhelper/QuestHelperPlugin.java @@ -312,6 +312,7 @@ public void onItemContainerChanged(ItemContainerChanged event) { bankItems.setItems(null); bankItems.setItems(event.getItemContainer().getItems()); + clientThread.invokeLater(() -> panel.updateItemRequirements(client, bankItems)); } if (event.getItemContainer() == client.getItemContainer(InventoryID.INVENTORY)) { diff --git a/src/main/java/com/questhelper/requirements/item/ItemRequirement.java b/src/main/java/com/questhelper/requirements/item/ItemRequirement.java index e5782acf2e..95dd0ba08c 100644 --- a/src/main/java/com/questhelper/requirements/item/ItemRequirement.java +++ b/src/main/java/com/questhelper/requirements/item/ItemRequirement.java @@ -230,7 +230,7 @@ public List getOverlayDisplayText(Client client) text.append(this.getName()); } - Color color = getColor(client); + Color color = getColorForOverlay(client); lines.add(LineComponent.builder() .left(text.toString()) .leftColor(color) @@ -239,6 +239,11 @@ public List getOverlayDisplayText(Client client) return lines; } + public Color getColorForOverlay(Client client) + { + return ItemSearch.hasItemsOnPlayer(client, this) ? Color.GREEN : Color.RED; + } + @Override public String getDisplayText() { @@ -298,7 +303,8 @@ else if (ItemSearch.hasItemsOnPlayer(client, this)) if (color == Color.RED) { - if (ItemSearch.hasItemsInBank(client, this) || (bankItems != null && ItemSearch.hasItemsInCachedBank(this, bankItems))) + boolean hasInCachedBank = (bankItems != null && ItemSearch.hasItemsInCachedBank(this, bankItems)); + if (ItemSearch.hasItemsInBank(client, this) || hasInCachedBank) { color = Color.WHITE; } diff --git a/src/main/java/com/questhelper/requirements/magic/RuneRequirement.java b/src/main/java/com/questhelper/requirements/magic/RuneRequirement.java index d299a6d41a..df364f9bc8 100644 --- a/src/main/java/com/questhelper/requirements/magic/RuneRequirement.java +++ b/src/main/java/com/questhelper/requirements/magic/RuneRequirement.java @@ -133,6 +133,12 @@ public boolean checkBank(Client client) return ItemSearch.hasItemsInBank(client, runeItemRequirement) || hasStaves; } + public boolean checkCachedBank(Item[] items) + { + boolean hasStaves = (staffItemRequirement != null && ItemSearch.hasItemsInCachedBank(this, items)); + return ItemSearch.hasItemsInCachedBank(this, items) || hasStaves; + } + @Override public boolean check(Client client, boolean checkWithSlotRestrictions, Item[] items) { diff --git a/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java b/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java index 8532154521..12a1066c61 100644 --- a/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java +++ b/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java @@ -27,7 +27,6 @@ package com.questhelper.requirements.magic; import com.google.common.base.Predicates; -import com.questhelper.BankItems; import com.questhelper.ItemSearch; import com.questhelper.QuestHelperConfig; import com.questhelper.banktab.BankItemHolder; @@ -37,7 +36,6 @@ import com.questhelper.requirements.player.SkillRequirement; import com.questhelper.requirements.player.SpellbookRequirement; import com.questhelper.requirements.quest.QuestRequirement; -import com.questhelper.requirements.util.InventorySlots; import com.questhelper.spells.MagicSpell; import com.questhelper.spells.Rune; import com.questhelper.spells.Staff; @@ -293,7 +291,7 @@ public List getOverlayDisplayText(Client client) { text.append(name); } - Color color = getColorConsideringBank(client, false, null); + Color color = getItemOverlayColor(client); // 3 x lines.add(LineComponent.builder() .left(text.toString()) @@ -308,15 +306,7 @@ public List getOverlayDisplayText(Client client) { staffName = client.getItemDefinition(firstStaffID).getName(); } - Color staffColor = Color.RED; - if (ItemSearch.hasItemsOnPlayer(client, staffRequirement)) - { - staffColor = Color.GREEN; - } - else if (ItemSearch.hasItemsInBank(client, staffRequirement)) - { - staffColor = Color.WHITE; - } + Color staffColor = getStaffColor(client); // lines.add(LineComponent.builder() .left(staffName) @@ -332,6 +322,44 @@ else if (ItemSearch.hasItemsInBank(client, staffRequirement)) return lines; } + private Color getItemOverlayColor(Client client) + { + //TODO: This is duplicated code from #getColorConsideringBank + //TODO: Remove the need for duplicated code. + boolean hasStaff = !hasStaff() || ItemSearch.hasItemsOnPlayer(client, staffRequirement); + boolean hasRunes, hasItems; + List itemRequirements = getItemRequirements(this.requirements); + hasRunes = runeRequirements.stream().allMatch(req -> ItemSearch.hasItemsOnPlayer(client, req)); + hasItems = itemRequirements.stream().allMatch(req -> ItemSearch.hasItemsOnPlayer(client, req)); + if (hasRunes && hasItems && hasStaff) + { + return Color.GREEN; + } + // Don't use ItemSearch for RuneRequirement because RuneRequirement overrides checkBank + hasRunes = runeRequirements.stream().allMatch(req -> req.checkBank(client)); + hasItems = itemRequirements.stream().allMatch(req -> ItemSearch.hasItemsInBank(client, req)); + hasStaff = !hasStaff() || ItemSearch.hasItemsInBank(client, staffRequirement); + if (hasRunes && hasItems && hasStaff) + { + return Color.WHITE; + } + return Color.RED; + } + + private Color getStaffColor(Client client) + { + Color staffColor = Color.RED; + if (ItemSearch.hasItemsOnPlayer(client, staffRequirement)) + { + staffColor = Color.GREEN; + } + else if (ItemSearch.hasItemsInBank(client, staffRequirement)) + { + staffColor = Color.WHITE; + } + return staffColor; + } + @Override public List getAllIds() { @@ -379,7 +407,7 @@ public boolean checkBank(Client client) int firstStaffID = ItemSearch.findFirstItem(client, staffRequirement.getAllIds(), staffRequirement.getQuantity()); hasStaff = ItemSearch.hasItemAmountInBank(client, firstStaffID, staffRequirement.getQuantity()); } - return hasRunes && hasItems && (hasStaff() && hasStaff); + return hasRunes && hasItems && hasStaff; } @Override @@ -414,10 +442,10 @@ public Color getColorConsideringBank(Client client, boolean checkWithSlotRestric { return Color.GREEN; } - hasRunes = runeRequirements.stream().allMatch(req -> req.checkBank(client)); // Don't use ItemSearch here because RuneRequirement overrides checkBank - hasItems = itemRequirements.stream().allMatch(req -> ItemSearch.hasItemsInBank(client, req)); - hasStaff = !hasStaff() || ItemSearch.hasItemsInBank(client, staffRequirement); - if (hasRunes || hasItems || hasStaff) + hasRunes = runeRequirements.stream().allMatch(req -> req.checkBank(client) || req.checkCachedBank(bankItems)); // Don't use ItemSearch here because RuneRequirement overrides checkBank + hasItems = itemRequirements.stream().allMatch(req -> ItemSearch.hasItemsInBank(client, req) || ItemSearch.hasItemsInCachedBank(req, bankItems)); + hasStaff = !hasStaff() || ItemSearch.hasItemsInBank(client, staffRequirement) || ItemSearch.hasItemsInCachedBank(staffRequirement, bankItems); + if (hasRunes && hasItems && hasStaff) { return Color.WHITE; } From f5e0aada21d4357857f56b7b4f1baed1452ca331 Mon Sep 17 00:00:00 2001 From: Senmori Date: Fri, 19 Feb 2021 14:39:09 -0500 Subject: [PATCH 48/55] Add 'In Bank' line to overlays to indicate what white text means. Also removed the staff requirement from item overlay colors since it has it's own check. --- src/main/java/com/questhelper/ItemSearch.java | 2 +- .../requirements/AbstractRequirement.java | 11 +++--- .../questhelper/requirements/Requirement.java | 3 +- .../requirements/item/ItemRequirement.java | 32 +++++++++++++--- .../requirements/magic/RuneRequirement.java | 4 +- .../requirements/magic/SpellRequirement.java | 37 +++++++++++-------- .../questhelper/steps/DetailedQuestStep.java | 2 +- 7 files changed, 61 insertions(+), 30 deletions(-) diff --git a/src/main/java/com/questhelper/ItemSearch.java b/src/main/java/com/questhelper/ItemSearch.java index 638c5c944e..61c7ef44cf 100644 --- a/src/main/java/com/questhelper/ItemSearch.java +++ b/src/main/java/com/questhelper/ItemSearch.java @@ -122,7 +122,7 @@ public boolean hasItemsInBank(Client client, ItemRequirement requirement) return requirement.getAllIds().stream().anyMatch(id -> hasItemAmountInBank(client, id, requirement.getQuantity())); } - public boolean hasItemsInCachedBank(ItemRequirement requirement, Item[] items) + public boolean hasItemsInBank(ItemRequirement requirement, Item[] items) { if (items == null) { diff --git a/src/main/java/com/questhelper/requirements/AbstractRequirement.java b/src/main/java/com/questhelper/requirements/AbstractRequirement.java index 543afa29da..9a81f43681 100644 --- a/src/main/java/com/questhelper/requirements/AbstractRequirement.java +++ b/src/main/java/com/questhelper/requirements/AbstractRequirement.java @@ -24,6 +24,7 @@ */ package com.questhelper.requirements; +import com.questhelper.QuestHelperPlugin; import java.util.List; import javax.annotation.Nullable; import net.runelite.api.Client; @@ -52,18 +53,18 @@ public void setTooltip(String tooltip) } @Override - public List getDisplayTextWithChecks(Client client) + public List getDisplayTextWithChecks(Client client, QuestHelperPlugin plugin) { if (getOverlayReplacement() != null && !this.check(client)) { - return getOverlayReplacement().getDisplayTextWithChecks(client); + return getOverlayReplacement().getDisplayTextWithChecks(client, plugin); } - return getOverlayDisplayText(client); + return getOverlayDisplayText(client, plugin); } - protected List getOverlayDisplayText(Client client) + protected List getOverlayDisplayText(Client client, QuestHelperPlugin plugin) { - return Requirement.super.getDisplayTextWithChecks(client); + return Requirement.super.getDisplayTextWithChecks(client, plugin); } public void appendToTooltip(String text) diff --git a/src/main/java/com/questhelper/requirements/Requirement.java b/src/main/java/com/questhelper/requirements/Requirement.java index 88a6172c39..3bf21839fe 100644 --- a/src/main/java/com/questhelper/requirements/Requirement.java +++ b/src/main/java/com/questhelper/requirements/Requirement.java @@ -27,6 +27,7 @@ package com.questhelper.requirements; +import com.questhelper.QuestHelperPlugin; import java.awt.Color; import java.util.ArrayList; import java.util.List; @@ -87,7 +88,7 @@ default String getTooltip() */ default void setTooltip(@Nullable String tooltip) {} - default List getDisplayTextWithChecks(Client client) + default List getDisplayTextWithChecks(Client client, QuestHelperPlugin plugin) { List lines = new ArrayList<>(); diff --git a/src/main/java/com/questhelper/requirements/item/ItemRequirement.java b/src/main/java/com/questhelper/requirements/item/ItemRequirement.java index 95dd0ba08c..6e3aeffd60 100644 --- a/src/main/java/com/questhelper/requirements/item/ItemRequirement.java +++ b/src/main/java/com/questhelper/requirements/item/ItemRequirement.java @@ -28,6 +28,7 @@ import com.questhelper.BankItems; import com.questhelper.ItemSearch; +import com.questhelper.QuestHelperPlugin; import com.questhelper.requirements.AbstractRequirement; import com.questhelper.requirements.Requirement; import com.questhelper.requirements.util.InventorySlots; @@ -205,7 +206,7 @@ public List getAllIds() } @Override - public List getOverlayDisplayText(Client client) + public List getOverlayDisplayText(Client client, QuestHelperPlugin plugin) { List lines = new ArrayList<>(); @@ -230,18 +231,39 @@ public List getOverlayDisplayText(Client client) text.append(this.getName()); } - Color color = getColorForOverlay(client); + Color color = getColorForOverlay(client, plugin.getBankItems()); lines.add(LineComponent.builder() .left(text.toString()) .leftColor(color) .build()); + if (color == Color.WHITE) + { + lines.add(getInBankLine()); + } + lines.addAll(getAdditionalText(client, true)); return lines; } - public Color getColorForOverlay(Client client) + protected LineComponent getInBankLine() + { + return LineComponent.builder() + .left(" - In Bank") + .leftColor(Color.WHITE) + .build(); + } + + public Color getColorForOverlay(Client client, BankItems bankItems) { - return ItemSearch.hasItemsOnPlayer(client, this) ? Color.GREEN : Color.RED; + if (ItemSearch.hasItemsOnPlayer(client, this)) + { + return Color.GREEN; + } + if (ItemSearch.hasItemsInBank(this, bankItems.getItems())) + { + return Color.WHITE; + } + return Color.RED; } @Override @@ -303,7 +325,7 @@ else if (ItemSearch.hasItemsOnPlayer(client, this)) if (color == Color.RED) { - boolean hasInCachedBank = (bankItems != null && ItemSearch.hasItemsInCachedBank(this, bankItems)); + boolean hasInCachedBank = (bankItems != null && ItemSearch.hasItemsInBank(this, bankItems)); if (ItemSearch.hasItemsInBank(client, this) || hasInCachedBank) { color = Color.WHITE; diff --git a/src/main/java/com/questhelper/requirements/magic/RuneRequirement.java b/src/main/java/com/questhelper/requirements/magic/RuneRequirement.java index df364f9bc8..6f50fc6a74 100644 --- a/src/main/java/com/questhelper/requirements/magic/RuneRequirement.java +++ b/src/main/java/com/questhelper/requirements/magic/RuneRequirement.java @@ -135,8 +135,8 @@ public boolean checkBank(Client client) public boolean checkCachedBank(Item[] items) { - boolean hasStaves = (staffItemRequirement != null && ItemSearch.hasItemsInCachedBank(this, items)); - return ItemSearch.hasItemsInCachedBank(this, items) || hasStaves; + boolean hasStaves = (staffItemRequirement != null && ItemSearch.hasItemsInBank(this, items)); + return ItemSearch.hasItemsInBank(this, items) || hasStaves; } @Override diff --git a/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java b/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java index 12a1066c61..5beccbe146 100644 --- a/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java +++ b/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java @@ -29,6 +29,7 @@ import com.google.common.base.Predicates; import com.questhelper.ItemSearch; import com.questhelper.QuestHelperConfig; +import com.questhelper.QuestHelperPlugin; import com.questhelper.banktab.BankItemHolder; import com.questhelper.questhelpers.QuestUtil; import com.questhelper.requirements.Requirement; @@ -271,7 +272,7 @@ public void setDisplayItemId(Integer displayItemId) } @Override - public List getOverlayDisplayText(Client client) + public List getOverlayDisplayText(Client client, QuestHelperPlugin plugin) { updateInternalState(client, getItemRequirements(this.requirements)); List lines = new ArrayList<>(); @@ -291,13 +292,17 @@ public List getOverlayDisplayText(Client client) { text.append(name); } - Color color = getItemOverlayColor(client); - // 3 x + Color color = getItemOverlayColor(client, plugin.getBankItems().getItems()); + // N x lines.add(LineComponent.builder() .left(text.toString()) .leftColor(color) .build() ); + if (color == Color.WHITE) + { + lines.add(getInBankLine()); + } if (hasStaff()) { int firstStaffID = ItemSearch.findFirstItem(client, staffRequirement.getAllIds(), staffRequirement.getQuantity()); @@ -306,12 +311,16 @@ public List getOverlayDisplayText(Client client) { staffName = client.getItemDefinition(firstStaffID).getName(); } - Color staffColor = getStaffColor(client); + Color staffColor = getStaffColor(client, plugin.getBankItems().getItems()); // lines.add(LineComponent.builder() .left(staffName) .leftColor(staffColor) .build()); + if (staffColor == Color.WHITE) + { + lines.add(getInBankLine()); + } // Add '(equipped)' staffColor = ItemSearch.hasItemEquipped(client, firstStaffID) ? Color.GREEN : Color.RED; lines.add(LineComponent.builder() @@ -322,38 +331,36 @@ public List getOverlayDisplayText(Client client) return lines; } - private Color getItemOverlayColor(Client client) + private Color getItemOverlayColor(Client client, Item[] bankItems) { //TODO: This is duplicated code from #getColorConsideringBank //TODO: Remove the need for duplicated code. - boolean hasStaff = !hasStaff() || ItemSearch.hasItemsOnPlayer(client, staffRequirement); boolean hasRunes, hasItems; List itemRequirements = getItemRequirements(this.requirements); hasRunes = runeRequirements.stream().allMatch(req -> ItemSearch.hasItemsOnPlayer(client, req)); hasItems = itemRequirements.stream().allMatch(req -> ItemSearch.hasItemsOnPlayer(client, req)); - if (hasRunes && hasItems && hasStaff) + if (hasRunes && hasItems) { return Color.GREEN; } // Don't use ItemSearch for RuneRequirement because RuneRequirement overrides checkBank - hasRunes = runeRequirements.stream().allMatch(req -> req.checkBank(client)); - hasItems = itemRequirements.stream().allMatch(req -> ItemSearch.hasItemsInBank(client, req)); - hasStaff = !hasStaff() || ItemSearch.hasItemsInBank(client, staffRequirement); - if (hasRunes && hasItems && hasStaff) + hasRunes = runeRequirements.stream().allMatch(req -> req.checkCachedBank(bankItems)); + hasItems = itemRequirements.stream().allMatch(req -> ItemSearch.hasItemsInBank(req, bankItems)); + if (hasRunes && hasItems) { return Color.WHITE; } return Color.RED; } - private Color getStaffColor(Client client) + private Color getStaffColor(Client client, Item[] bankItems) { Color staffColor = Color.RED; if (ItemSearch.hasItemsOnPlayer(client, staffRequirement)) { staffColor = Color.GREEN; } - else if (ItemSearch.hasItemsInBank(client, staffRequirement)) + else if (ItemSearch.hasItemsInBank(client, staffRequirement) || ItemSearch.hasItemsInBank(staffRequirement, bankItems)) { staffColor = Color.WHITE; } @@ -443,8 +450,8 @@ public Color getColorConsideringBank(Client client, boolean checkWithSlotRestric return Color.GREEN; } hasRunes = runeRequirements.stream().allMatch(req -> req.checkBank(client) || req.checkCachedBank(bankItems)); // Don't use ItemSearch here because RuneRequirement overrides checkBank - hasItems = itemRequirements.stream().allMatch(req -> ItemSearch.hasItemsInBank(client, req) || ItemSearch.hasItemsInCachedBank(req, bankItems)); - hasStaff = !hasStaff() || ItemSearch.hasItemsInBank(client, staffRequirement) || ItemSearch.hasItemsInCachedBank(staffRequirement, bankItems); + hasItems = itemRequirements.stream().allMatch(req -> ItemSearch.hasItemsInBank(client, req) || ItemSearch.hasItemsInBank(req, bankItems)); + hasStaff = !hasStaff() || ItemSearch.hasItemsInBank(client, staffRequirement) || ItemSearch.hasItemsInBank(staffRequirement, bankItems); if (hasRunes && hasItems && hasStaff) { return Color.WHITE; diff --git a/src/main/java/com/questhelper/steps/DetailedQuestStep.java b/src/main/java/com/questhelper/steps/DetailedQuestStep.java index fc5f28412a..5dcbc1a648 100644 --- a/src/main/java/com/questhelper/steps/DetailedQuestStep.java +++ b/src/main/java/com/questhelper/steps/DetailedQuestStep.java @@ -339,7 +339,7 @@ public void makeOverlayHint(PanelComponent panelComponent, QuestHelperPlugin plu } stream .distinct() - .map(req -> req.getDisplayTextWithChecks(client)) + .map(req -> req.getDisplayTextWithChecks(client, plugin)) .flatMap(Collection::stream) .forEach(line -> panelComponent.getChildren().add(line)); From 47207658c5ba14d702e8ff986d46b8df8d0ad457 Mon Sep 17 00:00:00 2001 From: Senmori Date: Sat, 20 Feb 2021 10:15:08 -0500 Subject: [PATCH 49/55] Fix SpellRequirement bug where it would show an empty staff slot as equipped if the spell's staff ID was -1. --- .../questhelper/requirements/magic/SpellRequirement.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java b/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java index 5beccbe146..43476033c2 100644 --- a/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java +++ b/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java @@ -321,8 +321,15 @@ public List getOverlayDisplayText(Client client, QuestHelperPlugi { lines.add(getInBankLine()); } + if (firstStaffID < 0) + { + staffColor = Color.RED; + } + else + { + staffColor = ItemSearch.hasItemEquipped(client, firstStaffID) ? Color.GREEN : Color.RED; + } // Add '(equipped)' - staffColor = ItemSearch.hasItemEquipped(client, firstStaffID) ? Color.GREEN : Color.RED; lines.add(LineComponent.builder() .left("(equipped)") .leftColor(staffColor) From 3aef487395f826b8dd56abad3cadb0bd7ba54a13 Mon Sep 17 00:00:00 2001 From: Senmori Date: Sat, 20 Feb 2021 10:16:44 -0500 Subject: [PATCH 50/55] Use ternary operator instead of if-else for less of a diff. --- .../questhelper/requirements/magic/SpellRequirement.java | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java b/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java index 43476033c2..1f023ce797 100644 --- a/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java +++ b/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java @@ -321,14 +321,7 @@ public List getOverlayDisplayText(Client client, QuestHelperPlugi { lines.add(getInBankLine()); } - if (firstStaffID < 0) - { - staffColor = Color.RED; - } - else - { - staffColor = ItemSearch.hasItemEquipped(client, firstStaffID) ? Color.GREEN : Color.RED; - } + staffColor = firstStaffID < 0 ? Color.RED : (ItemSearch.hasItemEquipped(client, firstStaffID) ? Color.GREEN : Color.RED); // Add '(equipped)' lines.add(LineComponent.builder() .left("(equipped)") From 18ac1c178bc91f4ba02ff2ae35f8b3642fea59a6 Mon Sep 17 00:00:00 2001 From: Senmori Date: Sat, 20 Feb 2021 12:19:53 -0500 Subject: [PATCH 51/55] Fixed loading issue with Rune and Staff enums. Overlay and side panel will now color correctly. They both use the same method for determining the color that should be displayed. Rune and Staff both use suppliers for their references to the other enum (Rune/Staff respectively) so we don't have class-loading issues. --- src/main/java/com/questhelper/ItemSearch.java | 5 ++ .../requirements/magic/SpellRequirement.java | 83 +++++++++++-------- .../java/com/questhelper/spells/Rune.java | 38 +++++---- .../java/com/questhelper/spells/Staff.java | 33 ++++---- 4 files changed, 95 insertions(+), 64 deletions(-) diff --git a/src/main/java/com/questhelper/ItemSearch.java b/src/main/java/com/questhelper/ItemSearch.java index 61c7ef44cf..72ea2a53d2 100644 --- a/src/main/java/com/questhelper/ItemSearch.java +++ b/src/main/java/com/questhelper/ItemSearch.java @@ -134,6 +134,11 @@ public boolean hasItemsInBank(ItemRequirement requirement, Item[] items) .anyMatch(i -> requirement.getAllIds().contains(i.getId()) && i.getQuantity() >= requirement.getQuantity()); } + public boolean hasItemInBank(int itemID, Item[] items) + { + return hasItemsInBank(new ItemRequirement("", itemID), items); + } + public boolean checkItem(Client client, InventorySlots slot, int itemID) { return slot.contains(client, i -> i.getId() == itemID); diff --git a/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java b/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java index 1f023ce797..69d510c15c 100644 --- a/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java +++ b/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java @@ -196,7 +196,7 @@ public void setStaff(ItemRequirement staff) */ public boolean hasStaff() { - return staffRequirement != null && useStaff; + return staffRequirement != null && useStaff && staffRequirement.getId() > -1; } /** @@ -292,7 +292,7 @@ public List getOverlayDisplayText(Client client, QuestHelperPlugi { text.append(name); } - Color color = getItemOverlayColor(client, plugin.getBankItems().getItems()); + Color color = getPanelColor(client, plugin.getBankItems().getItems()); // N x lines.add(LineComponent.builder() .left(text.toString()) @@ -307,7 +307,7 @@ public List getOverlayDisplayText(Client client, QuestHelperPlugi { int firstStaffID = ItemSearch.findFirstItem(client, staffRequirement.getAllIds(), staffRequirement.getQuantity()); String staffName = staffRequirement.getName(); - if (staffName == null || staffName.isEmpty()) + if (StringUtils.isBlank(staffName)) { staffName = client.getItemDefinition(firstStaffID).getName(); } @@ -331,26 +331,60 @@ public List getOverlayDisplayText(Client client, QuestHelperPlugi return lines; } - private Color getItemOverlayColor(Client client, Item[] bankItems) + private Color getPanelColor(Client client, Item[] bankItems) { - //TODO: This is duplicated code from #getColorConsideringBank - //TODO: Remove the need for duplicated code. - boolean hasRunes, hasItems; List itemRequirements = getItemRequirements(this.requirements); + if (hasStaff()) + { + int staffID = ItemSearch.findFirstItem(client, staffRequirement.getAllIds(), 1); + Staff requiredStaff = Staff.getByItemID(staffID); + if (requiredStaff != Staff.UNKNOWN) + { + List nonStaffRunes = new ArrayList<>(); + for (RuneRequirement rune : runeRequirements) + { + Rune currentRune = rune.getRune(); + ItemRequirement runeItem = rune.getRuneItemRequirement(); + boolean source = requiredStaff.isSourceOf(currentRune); + if (!source) + { + nonStaffRunes.add(runeItem); + } + } + boolean hasRunes = hasItemsOnPlayer(client, nonStaffRunes); + boolean hasItems = hasItemsOnPlayer(client, itemRequirements); + boolean hasStaff = ItemSearch.hasItemOnPlayer(client, staffID); + if (hasRunes && hasItems && hasStaff) + { + return Color.GREEN; + } + hasRunes = hasItemsInBank(nonStaffRunes, bankItems); + hasItems = hasItemsInBank(itemRequirements, bankItems); + hasStaff = ItemSearch.hasItemInBank(staffID, bankItems) || ItemSearch.hasItemOnPlayer(client, staffID); + return hasRunes && hasItems && hasStaff ? Color.WHITE : Color.RED; + } + } + boolean hasRunes, hasItems; hasRunes = runeRequirements.stream().allMatch(req -> ItemSearch.hasItemsOnPlayer(client, req)); - hasItems = itemRequirements.stream().allMatch(req -> ItemSearch.hasItemsOnPlayer(client, req)); + hasItems = hasItemsOnPlayer(client, itemRequirements); if (hasRunes && hasItems) { return Color.GREEN; } // Don't use ItemSearch for RuneRequirement because RuneRequirement overrides checkBank hasRunes = runeRequirements.stream().allMatch(req -> req.checkCachedBank(bankItems)); - hasItems = itemRequirements.stream().allMatch(req -> ItemSearch.hasItemsInBank(req, bankItems)); - if (hasRunes && hasItems) - { - return Color.WHITE; - } - return Color.RED; + hasItems = hasItemsInBank(itemRequirements, bankItems); + return hasRunes && hasItems ? Color.WHITE : Color.RED; + } + + private boolean hasItemsOnPlayer(Client client, Collection requirements) + { + return requirements.stream().allMatch(req -> ItemSearch.hasItemsOnPlayer(client, req)); + } + + private boolean hasItemsInBank(Collection requirements, Item[] bankItems) + { + return requirements.stream().allMatch(req -> ItemSearch.hasItemsInBank(req, bankItems)); } private Color getStaffColor(Client client, Item[] bankItems) @@ -439,25 +473,8 @@ public Color getColorConsideringBank(Client client, boolean checkWithSlotRestric { return Color.RED; // abort early if they can't even cast the spell } - boolean hasStaff = !hasStaff() || ItemSearch.hasItemsOnPlayer(client, staffRequirement); - boolean hasRunes = false; - boolean hasItems = false; - List itemRequirements = getItemRequirements(this.requirements); - hasRunes = runeRequirements.stream().allMatch(req -> ItemSearch.hasItemsOnPlayer(client, req)); - hasItems = itemRequirements.stream().allMatch(req -> ItemSearch.hasItemsOnPlayer(client, req)); - if (hasRunes && hasItems && hasStaff) - { - return Color.GREEN; - } - hasRunes = runeRequirements.stream().allMatch(req -> req.checkBank(client) || req.checkCachedBank(bankItems)); // Don't use ItemSearch here because RuneRequirement overrides checkBank - hasItems = itemRequirements.stream().allMatch(req -> ItemSearch.hasItemsInBank(client, req) || ItemSearch.hasItemsInBank(req, bankItems)); - hasStaff = !hasStaff() || ItemSearch.hasItemsInBank(client, staffRequirement) || ItemSearch.hasItemsInBank(staffRequirement, bankItems); - if (hasRunes && hasItems && hasStaff) - { - return Color.WHITE; - } - updateInternalState(client, itemRequirements); - return Color.RED; + updateInternalState(client, getItemRequirements(this.requirements)); + return getPanelColor(client, bankItems); } diff --git a/src/main/java/com/questhelper/spells/Rune.java b/src/main/java/com/questhelper/spells/Rune.java index 1c393875ab..d858b65760 100644 --- a/src/main/java/com/questhelper/spells/Rune.java +++ b/src/main/java/com/questhelper/spells/Rune.java @@ -30,6 +30,7 @@ import com.questhelper.ItemCollections; import java.util.Collections; import java.util.List; +import java.util.function.Supplier; import java.util.stream.Stream; import javax.annotation.Nonnull; import lombok.Getter; @@ -41,10 +42,10 @@ @Getter public enum Rune { - AIR("Air Rune", ItemCollections.getAirRune(), Staff.AIR), - WATER("Water Rune", ItemCollections.getWaterRune(), Staff.WATER), - EARTH("Earth Rune", ItemCollections.getEarthRune(), Staff.EARTH), - FIRE("Fire Rune", ItemCollections.getFireRune(), Staff.FIRE), + AIR("Air Rune", ItemCollections.getAirRune(), () -> Staff.AIR), + WATER("Water Rune", ItemCollections.getWaterRune(), () -> Staff.WATER), + EARTH("Earth Rune", ItemCollections.getEarthRune(), () -> Staff.EARTH), + FIRE("Fire Rune", ItemCollections.getFireRune(), () -> Staff.FIRE), MIND("Mind Rune", ItemID.MIND_RUNE), BODY("Body Rune", ItemID.BODY_RUNE), COSMIC("Cosmic Rune", ItemID.COSMIC_RUNE), @@ -57,12 +58,12 @@ public enum Rune SOUL("Soul Rune", ItemID.SOUL_RUNE), WRATH("Wrath Rune", ItemID.WRATH_RUNE), // Keep combination runes after the non-combination runes - LAVA("Lava Rune", ItemID.LAVA_RUNE, Staff.LAVA), - MUD("Mud Rune", ItemID.MUD_RUNE, Staff.MUD), - STEAM("Steam Rune", ItemID.STEAM_RUNE, Staff.STEAM), - SMOKE("Smoke Rune", ItemID.SMOKE_RUNE, Staff.SMOKE), - MIST("Mist Rune", ItemID.MIST_RUNE, Staff.MIST), - DUST("Dust Rune", ItemID.DUST_RUNE, Staff.DUST), + LAVA("Lava Rune", ItemID.LAVA_RUNE, () -> Staff.LAVA), + MUD("Mud Rune", ItemID.MUD_RUNE, () -> Staff.MUD), + STEAM("Steam Rune", ItemID.STEAM_RUNE, () -> Staff.STEAM), + SMOKE("Smoke Rune", ItemID.SMOKE_RUNE, () -> Staff.SMOKE), + MIST("Mist Rune", ItemID.MIST_RUNE, () -> Staff.MIST), + DUST("Dust Rune", ItemID.DUST_RUNE, () -> Staff.DUST), UNKNOWN("Null Rune", -1), ; @@ -70,26 +71,31 @@ public enum Rune private final String runeName; @Nonnull private final List runes; - private final Staff staff; - Rune(@Nonnull String runeName, @Nonnull List runes, Staff staff) + private final Supplier staffSupplier; + Rune(@Nonnull String runeName, @Nonnull List runes, Supplier staffSupplier) { this.runeName = runeName; this.runes = runes; - this.staff = staff; + this.staffSupplier = staffSupplier; } Rune(@Nonnull String runeName, int itemID) { this.runeName = runeName; this.runes = Collections.singletonList(itemID); - this.staff = Staff.UNKNOWN; + this.staffSupplier = () -> Staff.UNKNOWN; } - Rune(@Nonnull String runeName, int itemID, Staff staff) + Rune(@Nonnull String runeName, int itemID, Supplier staffSupplier) { this.runeName = runeName; this.runes = Collections.singletonList(itemID); - this.staff = staff; + this.staffSupplier = staffSupplier; + } + + public Staff getStaff() + { + return staffSupplier.get(); } public int getItemID() diff --git a/src/main/java/com/questhelper/spells/Staff.java b/src/main/java/com/questhelper/spells/Staff.java index 474d51dde3..d76b7d30c1 100644 --- a/src/main/java/com/questhelper/spells/Staff.java +++ b/src/main/java/com/questhelper/spells/Staff.java @@ -26,38 +26,41 @@ */ package com.questhelper.spells; +import com.google.common.collect.ImmutableSet; import com.questhelper.ItemCollections; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Set; +import java.util.function.Supplier; import java.util.stream.Stream; import lombok.Getter; @Getter public enum Staff { - AIR("Air Staff", ItemCollections.getAirStaff(), Rune.AIR), - WATER("Water Staff", ItemCollections.getWaterStaff(), Rune.WATER), - EARTH("Earth Staff", ItemCollections.getEarthStaff(), Rune.EARTH), - FIRE("Fire Staff", ItemCollections.getFireStaff(), Rune.FIRE), + AIR("Air Staff", ItemCollections.getAirStaff(), () -> ImmutableSet.of(Rune.AIR)), + WATER("Water Staff", ItemCollections.getWaterStaff(), () -> ImmutableSet.of(Rune.WATER)), + EARTH("Earth Staff", ItemCollections.getEarthStaff(), () -> ImmutableSet.of(Rune.EARTH)), + FIRE("Fire Staff", ItemCollections.getFireStaff(), () -> ImmutableSet.of(Rune.FIRE)), // Keep combination staves after the elemental staves - LAVA("Lava Staff", ItemCollections.getLavaStaff(), Rune.LAVA, Rune.FIRE, Rune.EARTH), - MUD("Mud Staff", ItemCollections.getMudStaff(), Rune.MUD, Rune.WATER, Rune.EARTH), - STEAM("Steam Staff", ItemCollections.getSteamStaff(), Rune.STEAM, Rune.WATER, Rune.FIRE), - SMOKE("Smoke Staff", ItemCollections.getSmokeStaff(), Rune.SMOKE, Rune.AIR, Rune.FIRE), - MIST("Mist Staff", ItemCollections.getMistStaff(), Rune.MIST, Rune.WATER, Rune.AIR), - DUST("Dust Staff", ItemCollections.getDustStaff(), Rune.DUST, Rune.EARTH, Rune.AIR), - UNKNOWN("Null Staff", Collections.emptyList(), Rune.UNKNOWN), + LAVA("Lava Staff", ItemCollections.getLavaStaff(), () -> ImmutableSet.of(Rune.LAVA, Rune.FIRE, Rune.EARTH)), + MUD("Mud Staff", ItemCollections.getMudStaff(), () -> ImmutableSet.of(Rune.MUD, Rune.WATER, Rune.EARTH)), + STEAM("Steam Staff", ItemCollections.getSteamStaff(), () -> ImmutableSet.of(Rune.STEAM, Rune.WATER, Rune.FIRE)), + SMOKE("Smoke Staff", ItemCollections.getSmokeStaff(), () -> ImmutableSet.of(Rune.SMOKE, Rune.AIR, Rune.FIRE)), + MIST("Mist Staff", ItemCollections.getMistStaff(), () -> ImmutableSet.of(Rune.MIST, Rune.WATER, Rune.AIR)), + DUST("Dust Staff", ItemCollections.getDustStaff(), () -> ImmutableSet.of(Rune.DUST, Rune.EARTH, Rune.AIR)), + UNKNOWN("Null Staff", Collections.emptyList(), () -> ImmutableSet.of(Rune.UNKNOWN)), ; private final String name; private final List staves; - private final List sourceRunes; - Staff(String name, List staves, Rune... sourceOf) + private final Supplier> sourceRunesSupplier; + Staff(String name, List staves, Supplier> sourceRunesSupplier) { this.name = name; this.staves = staves; - this.sourceRunes = Arrays.asList(sourceOf); + this.sourceRunesSupplier = sourceRunesSupplier; } public int getItemID() @@ -67,7 +70,7 @@ public int getItemID() public boolean isSourceOf(Rune rune) { - return getSourceRunes().contains(rune); + return sourceRunesSupplier.get().contains(rune); } public static Staff getByItemID(int itemID) From b1814d8da54c8e03e66914ec0613517a5387cecb Mon Sep 17 00:00:00 2001 From: Senmori Date: Sat, 20 Feb 2021 12:22:12 -0500 Subject: [PATCH 52/55] Split up getPanelColor into two methods for easier maintenance. --- .../requirements/magic/SpellRequirement.java | 61 +++++++++++-------- 1 file changed, 34 insertions(+), 27 deletions(-) diff --git a/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java b/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java index 69d510c15c..b052bc5beb 100644 --- a/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java +++ b/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java @@ -336,33 +336,7 @@ private Color getPanelColor(Client client, Item[] bankItems) List itemRequirements = getItemRequirements(this.requirements); if (hasStaff()) { - int staffID = ItemSearch.findFirstItem(client, staffRequirement.getAllIds(), 1); - Staff requiredStaff = Staff.getByItemID(staffID); - if (requiredStaff != Staff.UNKNOWN) - { - List nonStaffRunes = new ArrayList<>(); - for (RuneRequirement rune : runeRequirements) - { - Rune currentRune = rune.getRune(); - ItemRequirement runeItem = rune.getRuneItemRequirement(); - boolean source = requiredStaff.isSourceOf(currentRune); - if (!source) - { - nonStaffRunes.add(runeItem); - } - } - boolean hasRunes = hasItemsOnPlayer(client, nonStaffRunes); - boolean hasItems = hasItemsOnPlayer(client, itemRequirements); - boolean hasStaff = ItemSearch.hasItemOnPlayer(client, staffID); - if (hasRunes && hasItems && hasStaff) - { - return Color.GREEN; - } - hasRunes = hasItemsInBank(nonStaffRunes, bankItems); - hasItems = hasItemsInBank(itemRequirements, bankItems); - hasStaff = ItemSearch.hasItemInBank(staffID, bankItems) || ItemSearch.hasItemOnPlayer(client, staffID); - return hasRunes && hasItems && hasStaff ? Color.WHITE : Color.RED; - } + return getPanelColorWithStaff(client, bankItems); } boolean hasRunes, hasItems; hasRunes = runeRequirements.stream().allMatch(req -> ItemSearch.hasItemsOnPlayer(client, req)); @@ -377,6 +351,39 @@ private Color getPanelColor(Client client, Item[] bankItems) return hasRunes && hasItems ? Color.WHITE : Color.RED; } + private Color getPanelColorWithStaff(Client client, Item[] bankItems) + { + List itemRequirements = getItemRequirements(this.requirements); + int staffID = ItemSearch.findFirstItem(client, staffRequirement.getAllIds(), 1); + Staff requiredStaff = Staff.getByItemID(staffID); + if (requiredStaff != Staff.UNKNOWN) + { + List nonStaffRunes = new ArrayList<>(); + for (RuneRequirement rune : runeRequirements) + { + Rune currentRune = rune.getRune(); + ItemRequirement runeItem = rune.getRuneItemRequirement(); + boolean source = requiredStaff.isSourceOf(currentRune); + if (!source) + { + nonStaffRunes.add(runeItem); + } + } + boolean hasRunes = hasItemsOnPlayer(client, nonStaffRunes); + boolean hasItems = hasItemsOnPlayer(client, itemRequirements); + boolean hasStaff = ItemSearch.hasItemOnPlayer(client, staffID); + if (hasRunes && hasItems && hasStaff) + { + return Color.GREEN; + } + hasRunes = hasItemsInBank(nonStaffRunes, bankItems); + hasItems = hasItemsInBank(itemRequirements, bankItems); + hasStaff = ItemSearch.hasItemInBank(staffID, bankItems) || ItemSearch.hasItemOnPlayer(client, staffID); + return hasRunes && hasItems && hasStaff ? Color.WHITE : Color.RED; + } + return Color.RED; + } + private boolean hasItemsOnPlayer(Client client, Collection requirements) { return requirements.stream().allMatch(req -> ItemSearch.hasItemsOnPlayer(client, req)); From b140f71452f8a07b39ae1807956d5286f3862c91 Mon Sep 17 00:00:00 2001 From: Senmori Date: Sun, 21 Feb 2021 12:28:59 -0500 Subject: [PATCH 53/55] Fix ItemRequirement not displaying tooltips. Moved QuestRequirementPanel to use StringUtils instead of a simple null and blank check. --- src/main/java/com/questhelper/panel/QuestRequirementPanel.java | 3 ++- .../com/questhelper/requirements/item/ItemRequirement.java | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/questhelper/panel/QuestRequirementPanel.java b/src/main/java/com/questhelper/panel/QuestRequirementPanel.java index 3e4e2be439..c7f824c325 100644 --- a/src/main/java/com/questhelper/panel/QuestRequirementPanel.java +++ b/src/main/java/com/questhelper/panel/QuestRequirementPanel.java @@ -39,6 +39,7 @@ import javax.swing.border.EmptyBorder; import lombok.Getter; import lombok.Setter; +import org.apache.commons.lang3.StringUtils; public class QuestRequirementPanel extends JPanel { @@ -93,7 +94,7 @@ public void setInfoButtonTooltip(String text) { if (infoButton != null) { - if (text == null || text.isEmpty()) + if (StringUtils.isBlank(text)) { infoButton.setToolTipText(""); infoButton.setVisible(false); diff --git a/src/main/java/com/questhelper/requirements/item/ItemRequirement.java b/src/main/java/com/questhelper/requirements/item/ItemRequirement.java index 6e3aeffd60..9201fd324c 100644 --- a/src/main/java/com/questhelper/requirements/item/ItemRequirement.java +++ b/src/main/java/com/questhelper/requirements/item/ItemRequirement.java @@ -434,7 +434,7 @@ public List getDisplayItemIds() @Nullable public String getUpdatedTooltip(Client client) { - return null; + return getTooltip(); } From 25c7831cdac3d3ece57064baf5b4cc36aa5ddb08 Mon Sep 17 00:00:00 2001 From: Senmori Date: Sun, 21 Feb 2021 13:02:21 -0500 Subject: [PATCH 54/55] Remove in bank line on overlays. Add a tablet check for overlay panel text. --- .../requirements/item/ItemRequirement.java | 5 -- .../requirements/magic/SpellRequirement.java | 68 ++++++++++++------- 2 files changed, 43 insertions(+), 30 deletions(-) diff --git a/src/main/java/com/questhelper/requirements/item/ItemRequirement.java b/src/main/java/com/questhelper/requirements/item/ItemRequirement.java index 9201fd324c..36dd968584 100644 --- a/src/main/java/com/questhelper/requirements/item/ItemRequirement.java +++ b/src/main/java/com/questhelper/requirements/item/ItemRequirement.java @@ -236,11 +236,6 @@ public List getOverlayDisplayText(Client client, QuestHelperPlugi .left(text.toString()) .leftColor(color) .build()); - if (color == Color.WHITE) - { - lines.add(getInBankLine()); - } - lines.addAll(getAdditionalText(client, true)); return lines; } diff --git a/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java b/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java index b052bc5beb..c58fa06341 100644 --- a/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java +++ b/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java @@ -199,6 +199,11 @@ public boolean hasStaff() return staffRequirement != null && useStaff && staffRequirement.getId() > -1; } + public boolean hasTablet() + { + return tabletRequirement != null && tabletRequirement.getId() > -1; + } + /** * A convenience method to better indicate to not use a tablet for this spell requirement */ @@ -284,14 +289,11 @@ public List getOverlayDisplayText(Client client, QuestHelperPlugi } String name = spell.getName(); - if (tabletRequirement != null && tabletRequirement.check(client)) + if (hasTablet()) { - text.append(tabletRequirement.getName()); - } - else - { - text.append(name); + name = tabletRequirement.getName(); } + text.append(name); Color color = getPanelColor(client, plugin.getBankItems().getItems()); // N x lines.add(LineComponent.builder() @@ -299,9 +301,9 @@ public List getOverlayDisplayText(Client client, QuestHelperPlugi .leftColor(color) .build() ); - if (color == Color.WHITE) + if (hasTablet()) { - lines.add(getInBankLine()); + return lines; } if (hasStaff()) { @@ -311,18 +313,18 @@ public List getOverlayDisplayText(Client client, QuestHelperPlugi { staffName = client.getItemDefinition(firstStaffID).getName(); } - Color staffColor = getStaffColor(client, plugin.getBankItems().getItems()); + // + Color staffColor = getStaffColor(client, plugin.getBankItems().getItems()); lines.add(LineComponent.builder() .left(staffName) .leftColor(staffColor) .build()); - if (staffColor == Color.WHITE) - { - lines.add(getInBankLine()); - } - staffColor = firstStaffID < 0 ? Color.RED : (ItemSearch.hasItemEquipped(client, firstStaffID) ? Color.GREEN : Color.RED); + // Add '(equipped)' + // We have to check for an ID of -1 because if someone submits an invalid staff id then the hasItemEquipped + // will check if the slot has an id of -1, which is empty, so it will return true for an empty slot. + staffColor = firstStaffID < 0 ? Color.RED : (ItemSearch.hasItemEquipped(client, firstStaffID) ? Color.GREEN : Color.RED); lines.add(LineComponent.builder() .left("(equipped)") .leftColor(staffColor) @@ -333,11 +335,15 @@ public List getOverlayDisplayText(Client client, QuestHelperPlugi private Color getPanelColor(Client client, Item[] bankItems) { - List itemRequirements = getItemRequirements(this.requirements); + if (hasTablet()) + { + return getPanelColorWithTablet(client, bankItems); + } if (hasStaff()) { return getPanelColorWithStaff(client, bankItems); } + List itemRequirements = getItemRequirements(this.requirements); boolean hasRunes, hasItems; hasRunes = runeRequirements.stream().allMatch(req -> ItemSearch.hasItemsOnPlayer(client, req)); hasItems = hasItemsOnPlayer(client, itemRequirements); @@ -384,6 +390,23 @@ private Color getPanelColorWithStaff(Client client, Item[] bankItems) return Color.RED; } + private Color getPanelColorWithTablet(Client client, Item[] bankItems) + { + updateTabletRequirement(client); + int tabletID = tabletRequirement.getId(); + int required = tabletRequirement.getQuantity(); + if (ItemSearch.hasItemAmountOnPlayer(client, tabletID, required)) + { + return Color.GREEN; + } + if (ItemSearch.hasItemsInBank(tabletRequirement, bankItems)) + { + return Color.WHITE; + } + return Color.RED; + } + + private boolean hasItemsOnPlayer(Client client, Collection requirements) { return requirements.stream().allMatch(req -> ItemSearch.hasItemsOnPlayer(client, req)); @@ -461,18 +484,12 @@ public boolean checkBank(Client client) @Override public Color getColorConsideringBank(Client client, boolean checkWithSlotRestrictions, Item[] bankItems) { - if (tabletRequirement != null) + if (hasTablet()) { - updateTabletRequirement(client); - int tabletID = tabletRequirement.getId(); - int required = tabletRequirement.getQuantity(); - if (ItemSearch.hasItemAmountOnPlayer(client, tabletID, required)) - { - return Color.GREEN; - } - if (ItemSearch.hasItemAmountInBank(client, tabletID, required)) + Color tabletColor = getPanelColorWithTablet(client, bankItems); + if (tabletColor != Color.RED) { - return Color.WHITE; + return tabletColor; } } boolean hasOtherReqs = getNonItemRequirements(this.requirements).stream().allMatch(req -> req.check(client)); @@ -632,6 +649,7 @@ private List getNonItemRequirements(List requirements) private void updateInternalState(Client client, List requirements) { updateItemRequirements(client, requirements); + updateTabletRequirement(client); if (staffRequirement != null && StringUtils.isBlank(staffRequirement.getName())) { staffRequirement.setName(client.getItemDefinition(staffRequirement.getId()).getName()); From aefe15ca8396b060fe8a1941d1737a3c2c90a34b Mon Sep 17 00:00:00 2001 From: Senmori Date: Sun, 21 Feb 2021 13:41:45 -0500 Subject: [PATCH 55/55] BankItemHolder now uses QuestHelperPlugin for the most flexibility. Removed TriFunction and moved SpellComponentPreference to overriding the getPreference method in the actual enum constant. Added more convenience methods in ItemSearch to account for use using BankItems almost exclusively. --- src/main/java/com/questhelper/ItemSearch.java | 15 +++++++ .../questhelper/banktab/BankItemHolder.java | 6 ++- .../banktab/QuestHelperBankTagService.java | 25 +++-------- .../requirements/magic/RuneRequirement.java | 6 +-- .../requirements/magic/SpellRequirement.java | 12 +++--- .../spells/SpellComponentPreference.java | 28 ++++++------ .../com/questhelper/util/TriFunction.java | 43 ------------------- 7 files changed, 50 insertions(+), 85 deletions(-) delete mode 100644 src/main/java/com/questhelper/util/TriFunction.java diff --git a/src/main/java/com/questhelper/ItemSearch.java b/src/main/java/com/questhelper/ItemSearch.java index 72ea2a53d2..d52118b381 100644 --- a/src/main/java/com/questhelper/ItemSearch.java +++ b/src/main/java/com/questhelper/ItemSearch.java @@ -52,6 +52,11 @@ public boolean hasItemAmountAnywhere(Client client, int itemID, int requiredAmou return hasItemAmountOnPlayer(client, itemID, requiredAmount) || hasItemAmountInBank(client, itemID, requiredAmount); } + public boolean hasItemAmountAnywhere(Client client, int itemID, int requiredAmount, Item[] bankItems) + { + return hasItemAmountOnPlayer(client, itemID, requiredAmount) || hasItemAmountInBank(itemID, requiredAmount, bankItems); + } + public boolean hasItemOnPlayer(Client client, int itemID) { return hasItemInInventory(client, itemID) || hasItemEquipped(client, itemID); @@ -112,6 +117,11 @@ public boolean hasItemsAnywhere(Client client, ItemRequirement requirement) return hasItemsOnPlayer(client, requirement) || hasItemsInBank(client, requirement); } + public boolean hasItemsAnywhere(Client client, ItemRequirement requirement, Item[] bankItems) + { + return hasItemsOnPlayer(client, requirement) || hasItemsInBank(requirement, bankItems); + } + public boolean hasItemsOnPlayer(Client client, ItemRequirement requirement) { return requirement.getAllIds().stream().anyMatch(id -> hasItemAmountOnPlayer(client, id, requirement.getQuantity())); @@ -139,6 +149,11 @@ public boolean hasItemInBank(int itemID, Item[] items) return hasItemsInBank(new ItemRequirement("", itemID), items); } + public boolean hasItemAmountInBank(int itemID, int amount, Item[] bankItems) + { + return hasItemsInBank(new ItemRequirement("", itemID, amount), bankItems); + } + public boolean checkItem(Client client, InventorySlots slot, int itemID) { return slot.contains(client, i -> i.getId() == itemID); diff --git a/src/main/java/com/questhelper/banktab/BankItemHolder.java b/src/main/java/com/questhelper/banktab/BankItemHolder.java index 8b9c5658a5..5a8081008c 100644 --- a/src/main/java/com/questhelper/banktab/BankItemHolder.java +++ b/src/main/java/com/questhelper/banktab/BankItemHolder.java @@ -26,7 +26,9 @@ */ package com.questhelper.banktab; +import com.questhelper.BankItems; import com.questhelper.QuestHelperConfig; +import com.questhelper.QuestHelperPlugin; import com.questhelper.requirements.item.ItemRequirement; import java.util.List; import net.runelite.api.Client; @@ -45,8 +47,8 @@ public interface BankItemHolder * Get a list of {@link ItemRequirement} to be displayed. * * @param client the {@link Client} - * @param config the {@link QuestHelperConfig} + * @param plugin * @return a list of {@link ItemRequirement} that should be displayed, or an empty list if none are found */ - List getRequirements(Client client, QuestHelperConfig config); + List getRequirements(Client client, QuestHelperPlugin plugin); } diff --git a/src/main/java/com/questhelper/banktab/QuestHelperBankTagService.java b/src/main/java/com/questhelper/banktab/QuestHelperBankTagService.java index cfc6d8b1d1..3bb2b330e4 100644 --- a/src/main/java/com/questhelper/banktab/QuestHelperBankTagService.java +++ b/src/main/java/com/questhelper/banktab/QuestHelperBankTagService.java @@ -24,7 +24,7 @@ */ package com.questhelper.banktab; -import com.questhelper.QuestHelperConfig; +import com.questhelper.ItemSearch; import com.questhelper.QuestHelperPlugin; import com.questhelper.panel.PanelDetails; import com.questhelper.requirements.item.ItemRequirement; @@ -37,8 +37,7 @@ import java.util.Objects; import java.util.stream.Collectors; import javax.inject.Inject; -import net.runelite.api.InventoryID; -import net.runelite.api.ItemContainer; +import javax.swing.SwingUtilities; public class QuestHelperBankTagService { @@ -134,14 +133,10 @@ else if (itemRequirement instanceof BankItemHolder) BankItemHolder holder = (BankItemHolder) itemRequirement; // Force run on client thread even though it's not as responsive as not doing that, however it // ensures we run on the client thread and never run into threading issues. - QuestHelperConfig config = plugin.getConfig(); - if (config != null) - { - plugin.getClientThread().invoke(() -> { - List reqs = holder.getRequirements(plugin.getClient(), config); - makeBankHolderItems(reqs, pluginItems); // callback because we can't halt on the client thread - }); - } + plugin.getClientThread().invoke(() -> { + List reqs = holder.getRequirements(plugin.getClient(), plugin); + makeBankHolderItems(reqs, pluginItems); // callback because we can't halt on the client thread); + }); } else { @@ -175,12 +170,6 @@ private BankTabItem makeBankTabItem(ItemRequirement item) public boolean hasItemInBank(int itemID) { - ItemContainer bankContainer = plugin.getClient().getItemContainer(InventoryID.BANK); - if (bankContainer == null) - { - return false; - } - - return bankContainer.contains(itemID); + return ItemSearch.hasItemInBank(itemID, plugin.getBankItems().getItems()); } } diff --git a/src/main/java/com/questhelper/requirements/magic/RuneRequirement.java b/src/main/java/com/questhelper/requirements/magic/RuneRequirement.java index 6f50fc6a74..59fb53271d 100644 --- a/src/main/java/com/questhelper/requirements/magic/RuneRequirement.java +++ b/src/main/java/com/questhelper/requirements/magic/RuneRequirement.java @@ -27,7 +27,7 @@ package com.questhelper.requirements.magic; import com.questhelper.ItemSearch; -import com.questhelper.QuestHelperConfig; +import com.questhelper.QuestHelperPlugin; import com.questhelper.banktab.BankItemHolder; import com.questhelper.questhelpers.QuestUtil; import com.questhelper.requirements.item.ItemRequirement; @@ -147,11 +147,11 @@ public boolean check(Client client, boolean checkWithSlotRestrictions, Item[] it } @Override - public List getRequirements(Client client, QuestHelperConfig config) + public List getRequirements(Client client, QuestHelperPlugin plugin) { boolean hasRunes = ItemSearch.hasItemsAnywhere(client, runeItemRequirement); boolean hasStaves = staffItemRequirement != null && ItemSearch.hasItemsAnywhere(client, staffItemRequirement); - ItemRequirement requirement = config.bankFilterSpellPreference().getPreference(this, () -> hasRunes, () -> hasStaves); + ItemRequirement requirement = plugin.getConfig().bankFilterSpellPreference().getPreference(this, () -> hasRunes, () -> hasStaves); return Collections.singletonList(requirement); } } diff --git a/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java b/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java index c58fa06341..3debc97123 100644 --- a/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java +++ b/src/main/java/com/questhelper/requirements/magic/SpellRequirement.java @@ -28,7 +28,6 @@ import com.google.common.base.Predicates; import com.questhelper.ItemSearch; -import com.questhelper.QuestHelperConfig; import com.questhelper.QuestHelperPlugin; import com.questhelper.banktab.BankItemHolder; import com.questhelper.questhelpers.QuestUtil; @@ -49,6 +48,7 @@ import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.BooleanSupplier; import java.util.stream.Collectors; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -532,10 +532,10 @@ public boolean check(Client client, boolean checkWithSlotRestrictions, Item[] it } @Override - public List getRequirements(Client client, QuestHelperConfig config) + public List getRequirements(Client client, QuestHelperPlugin plugin) { updateInternalState(client, getItemRequirements(this.requirements)); - if (tabletRequirement != null && ItemSearch.hasItemAmountAnywhere(client, tabletRequirement.getId(), this.numberOfCasts)) + if (tabletRequirement != null && ItemSearch.hasItemsAnywhere(client, tabletRequirement, plugin.getBankItems().getItems())) { return Collections.singletonList(tabletRequirement); } @@ -555,10 +555,10 @@ public List getRequirements(Client client, QuestHelperConfig co ItemRequirement staves = rune.getStaffItemRequirement(); ItemRequirement runeItem = rune.getRuneItemRequirement(); - boolean hasRunes = ItemSearch.hasItemsAnywhere(client, runeItem); - boolean hasStaves = hasStaff(client, staves); + BooleanSupplier hasRunes = () -> ItemSearch.hasItemsAnywhere(client, runeItem, plugin.getBankItems().getItems()); + BooleanSupplier hasStaves = () -> hasStaff(client, staves); - ItemRequirement toAdd = config.bankFilterSpellPreference().getPreference(rune, () -> hasRunes, () -> hasStaves); + ItemRequirement toAdd = plugin.getConfig().bankFilterSpellPreference().getPreference(rune, hasRunes, hasStaves); boolean itemIsRune = toAdd.getAllIds().stream().allMatch(Rune::isRuneItem); if (staffRequirement != null && itemIsRune && currentRune.getStaff() != Staff.UNKNOWN) diff --git a/src/main/java/com/questhelper/spells/SpellComponentPreference.java b/src/main/java/com/questhelper/spells/SpellComponentPreference.java index f9711d2f54..bca133be88 100644 --- a/src/main/java/com/questhelper/spells/SpellComponentPreference.java +++ b/src/main/java/com/questhelper/spells/SpellComponentPreference.java @@ -29,23 +29,25 @@ import com.questhelper.requirements.magic.RuneRequirement; import com.questhelper.requirements.item.ItemRequirement; -import com.questhelper.util.TriFunction; import java.util.function.BooleanSupplier; public enum SpellComponentPreference { - RUNES((runes, staff, req) -> runes.getAsBoolean() ? req.getRuneItemRequirement() : req.getStaffItemRequirement()), - STAVES((runes, staff, req) -> staff.getAsBoolean() ? req.getStaffItemRequirement() : req.getRuneItemRequirement()); - ; - - private final TriFunction function; - SpellComponentPreference(TriFunction function) - { - this.function = function; + RUNES { + @Override + public ItemRequirement getPreference(RuneRequirement requirement, BooleanSupplier runes, BooleanSupplier staff) + { + return runes.getAsBoolean() ? requirement.getRuneItemRequirement() : requirement.getStaffItemRequirement(); + } + }, + STAVES { + @Override + public ItemRequirement getPreference(RuneRequirement requirement, BooleanSupplier runes, BooleanSupplier staff) + { + return staff.getAsBoolean() ? requirement.getStaffItemRequirement() : requirement.getRuneItemRequirement(); + } } + ; - public ItemRequirement getPreference(RuneRequirement requirement, BooleanSupplier runes, BooleanSupplier staff) - { - return function.apply(runes, staff, requirement); - } + public abstract ItemRequirement getPreference(RuneRequirement requirement, BooleanSupplier runes, BooleanSupplier staff); } diff --git a/src/main/java/com/questhelper/util/TriFunction.java b/src/main/java/com/questhelper/util/TriFunction.java deleted file mode 100644 index 1515e93d64..0000000000 --- a/src/main/java/com/questhelper/util/TriFunction.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * - * * Copyright (c) 2021 - * * 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 com.questhelper.util; - -import java.util.Objects; -import java.util.function.Function; - -@FunctionalInterface -public interface TriFunction -{ - R apply (A a, B b, C c); - - default TriFunction andThen(Function after) - { - Objects.requireNonNull(after); - return (A a, B b, C c) -> after.apply(apply(a, b, c)); - } - -}