diff --git a/runelite-api/src/main/java/net/runelite/api/Varbits.java b/runelite-api/src/main/java/net/runelite/api/Varbits.java index 038362c66d..66dfcef672 100644 --- a/runelite-api/src/main/java/net/runelite/api/Varbits.java +++ b/runelite-api/src/main/java/net/runelite/api/Varbits.java @@ -372,6 +372,7 @@ public enum Varbits { DAILY_ESSENCE_COLLECTED(4547), DAILY_RUNES_COLLECTED(4540), DAILY_SAND_COLLECTED(4549), + DAILY_ARROWS_STATE(4563), DAILY_FLAX_STATE(4559), /** * This varbit tracks how much bonemeal has been redeemed from Robin @@ -481,6 +482,14 @@ public enum Varbits { */ GE_OFFER_CREATION_TYPE(4397), + + /* + * Spells being auto-casted + * + * */ + AUTO_CAST_SPELL(276), + + /** * The active tab within the quest interface */ diff --git a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java index 471c0acbe4..360f1a556c 100644 --- a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java +++ b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java @@ -55,6 +55,8 @@ public class WidgetID public static final int DIARY_GROUP_ID = 259; public static final int PEST_CONTROL_BOAT_GROUP_ID = 407; public static final int PEST_CONTROL_GROUP_ID = 408; + public static final int PEST_CONTROL_EXCHANGE_WINDOW_GROUP_ID = 243; + public static final int PEST_CONTROL_DIALOG_GROUP_ID = 229; public static final int CLAN_CHAT_GROUP_ID = 7; public static final int MINIMAP_GROUP_ID = 160; public static final int LOGIN_CLICK_TO_PLAY_GROUP_ID = 378; @@ -127,6 +129,7 @@ public class WidgetID public static final int FULLSCREEN_MAP_GROUP_ID = 165; public static final int QUESTLIST_GROUP_ID = 399; public static final int SKILLS_GROUP_ID = 320; + public static final int EQUIPMENT_PAGE_GROUP_ID = 84; public static final int QUESTTAB_GROUP_ID = 629; public static final int MUSIC_GROUP_ID = 239; public static final int MUSICTAB_GROUP_ID = 239; @@ -164,12 +167,32 @@ public class WidgetID static class PestControlBoat { static final int INFO = 3; + + static final int NEXT_DEPARTURE = 4; + static final int PLAYERS_READY = 5; + static final int POINTS = 6; + } + + static class PestControlExchangeWindow + { + static final int ITEM_LIST = 2; + static final int BOTTOM = 5; + static final int POINTS = 8; + static final int CONFIRM_BUTTON = 6; + } + + static class PestControlDialog + { + static final int TEXT = 1; + static final int CONTINUE = 2; } static class PestControl { static final int INFO = 3; + static final int TIME = 6; + static final int ACTIVITY_BAR = 12; static final int ACTIVITY_PROGRESS = 14; @@ -675,6 +698,28 @@ public class WidgetID static final int DESTROY_ITEM_NO = 3; } + static class EquipmentWidgetIdentifiers + { + static final int EQUIP_YOUR_CHARACTER = 3; + static final int STAB_ATTACK_BONUS = 23; + static final int SLASH_ATTACK_BONUS = 24; + static final int CRUSH_ATTACK_BONUS = 25; + static final int MAGIC_ATTACK_BONUS = 26; + static final int RANGED_ATTACK_BONUS = 27; + static final int STAB_DEFENCE_BONUS = 29; + static final int SLASH_DEFENCE_BONUS = 30; + static final int CRUSH_DEFENCE_BONUS = 31; + static final int MAGIC_DEFENCE_BONUS = 32; + static final int RANGED_DEFENCE_BONUS = 33; + static final int MELEE_STRENGTH = 35; + static final int RANGED_STRENGTH = 36; + static final int MAGIC_DAMAGE = 37; + static final int PRAYER_BONUS = 38; + static final int UNDEAD_DAMAGE_BONUS = 40; + static final int SLAYER_DAMAGE_BONUS = 41; + static final int WEIGHT = 43; + } + static class VarrockMuseum { static final int VARROCK_MUSEUM_QUESTION = 28; diff --git a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java index df7951b840..9872efeb23 100644 --- a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java +++ b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java @@ -83,8 +83,14 @@ public enum WidgetInfo DIARY_QUEST_WIDGET_TITLE(WidgetID.DIARY_QUEST_GROUP_ID, WidgetID.Diary.DIARY_TITLE), DIARY_QUEST_WIDGET_TEXT(WidgetID.DIARY_QUEST_GROUP_ID, WidgetID.Diary.DIARY_TEXT), + PEST_CONTROL_DIALOG(WidgetID.PEST_CONTROL_DIALOG_GROUP_ID, 0), + PEST_CONTROL_DIALOG_TEXT(WidgetID.PEST_CONTROL_DIALOG_GROUP_ID, WidgetID.PestControlDialog.TEXT), + PEST_CONTROL_EXCHANGE_WINDOW(WidgetID.PEST_CONTROL_EXCHANGE_WINDOW_GROUP_ID, 0), + PEST_CONTROL_EXCHANGE_WINDOW_POINTS(WidgetID.PEST_CONTROL_EXCHANGE_WINDOW_GROUP_ID, WidgetID.PestControlExchangeWindow.POINTS), PEST_CONTROL_BOAT_INFO(WidgetID.PEST_CONTROL_BOAT_GROUP_ID, WidgetID.PestControlBoat.INFO), + PEST_CONTROL_BOAT_INFO_POINTS(WidgetID.PEST_CONTROL_BOAT_GROUP_ID, WidgetID.PestControlBoat.POINTS), PEST_CONTROL_INFO(WidgetID.PEST_CONTROL_GROUP_ID, WidgetID.PestControl.INFO), + PEST_CONTROL_INFO_TIME(WidgetID.PEST_CONTROL_GROUP_ID, WidgetID.PestControl.TIME), PEST_CONTROL_PURPLE_SHIELD(WidgetID.PEST_CONTROL_GROUP_ID, WidgetID.PestControl.PURPLE_SHIELD), PEST_CONTROL_BLUE_SHIELD(WidgetID.PEST_CONTROL_GROUP_ID, WidgetID.PestControl.BLUE_SHIELD), PEST_CONTROL_YELLOW_SHIELD(WidgetID.PEST_CONTROL_GROUP_ID, WidgetID.PestControl.YELLOW_SHIELD), @@ -550,7 +556,12 @@ public enum WidgetInfo MUSICTAB_LOOP_BUTTON(WidgetID.MUSICTAB_GROUP_ID, 12), MUSICTAB_UNLOCKED_SONGS(WidgetID.MUSICTAB_GROUP_ID, 13), - QUESTTAB_QUEST_TAB(WidgetID.QUESTTAB_GROUP_ID, WidgetID.QuestTab.QUEST_TAB); + QUESTTAB_QUEST_TAB(WidgetID.QUESTTAB_GROUP_ID, WidgetID.QuestTab.QUEST_TAB), + + EQUIPMENT_MELEE_STRENGTH(WidgetID.EQUIPMENT_PAGE_GROUP_ID, WidgetID.EquipmentWidgetIdentifiers.MELEE_STRENGTH), + EQUIPMENT_RANGED_STRENGTH(WidgetID.EQUIPMENT_PAGE_GROUP_ID, WidgetID.EquipmentWidgetIdentifiers.RANGED_STRENGTH), + EQUIPMENT_MAGIC_DAMAGE(WidgetID.EQUIPMENT_PAGE_GROUP_ID, WidgetID.EquipmentWidgetIdentifiers.MAGIC_DAMAGE), + EQUIP_YOUR_CHARACTER(WidgetID.EQUIPMENT_PAGE_GROUP_ID, WidgetID.EquipmentWidgetIdentifiers.EQUIP_YOUR_CHARACTER); private final int groupId; private final int childId; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/dailytaskindicators/DailyTasksConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/dailytaskindicators/DailyTasksConfig.java index 09752b89a7..a1bd666f93 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/dailytaskindicators/DailyTasksConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/dailytaskindicators/DailyTasksConfig.java @@ -109,4 +109,15 @@ public interface DailyTasksConfig extends Config { return true; } + + @ConfigItem( + position = 8, + keyName = "showArrows", + name = "Show Claimable Ogre Arrows", + description = "Show a message when you can collect ogre arrows from Rantz." + ) + default boolean showArrows() + { + return true; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/dailytaskindicators/DailyTasksPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/dailytaskindicators/DailyTasksPlugin.java index 4076ef693c..97404aacd6 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/dailytaskindicators/DailyTasksPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/dailytaskindicators/DailyTasksPlugin.java @@ -63,6 +63,7 @@ public class DailyTasksPlugin extends Plugin private static final String SAND_MESSAGE = "You have sand waiting to be collected from Bert."; private static final int SAND_QUEST_COMPLETE = 160; private static final String FLAX_MESSAGE = "You have bowstrings waiting to be converted from flax from the Flax keeper."; + private static final String ARROWS_MESSAGE = "You have ogre arrows waiting to be collected from Rantz."; private static final String BONEMEAL_MESSAGE = "You have bonemeal and slime waiting to be collected from Robin."; private static final int BONEMEAL_PER_DIARY = 13; private static final String RELOG_MESSAGE = " (May require a relog)"; @@ -153,22 +154,21 @@ public class DailyTasksPlugin extends Plugin { checkBonemeal(dailyReset); } + + if (config.showArrows()) + { + checkArrows(dailyReset); + } } } private void checkHerbBoxes(boolean dailyReset) { - if (client.getAccountType() == AccountType.NORMAL + if ((client.getAccountType() == AccountType.NORMAL && client.getVar(VarPlayer.NMZ_REWARD_POINTS) >= HERB_BOX_COST) + && (dailyReset || client.getVar(Varbits.DAILY_HERB_BOXES_COLLECTED) < HERB_BOX_MAX)) { - if (client.getVar(Varbits.DAILY_HERB_BOXES_COLLECTED) < HERB_BOX_MAX) - { - sendChatMessage(HERB_BOX_MESSAGE); - } - else if (dailyReset) - { - sendChatMessage(HERB_BOX_MESSAGE); - } + sendChatMessage(HERB_BOX_MESSAGE); } } @@ -189,61 +189,46 @@ public class DailyTasksPlugin extends Plugin private void checkEssence(boolean dailyReset) { - if (client.getVar(Varbits.DIARY_ARDOUGNE_MEDIUM) == 1) + if ((client.getVar(Varbits.DIARY_ARDOUGNE_MEDIUM) == 1) + && (dailyReset || client.getVar(Varbits.DAILY_ESSENCE_COLLECTED) == 0)) { - if (client.getVar(Varbits.DAILY_ESSENCE_COLLECTED) == 0) - { - sendChatMessage(ESSENCE_MESSAGE); - } - else if (dailyReset) - { - sendChatMessage(ESSENCE_MESSAGE); - } + sendChatMessage(ESSENCE_MESSAGE); } } private void checkRunes(boolean dailyReset) { - if (client.getVar(Varbits.DIARY_WILDERNESS_EASY) == 1) + if ((client.getVar(Varbits.DIARY_WILDERNESS_EASY) == 1) + && (dailyReset || client.getVar(Varbits.DAILY_RUNES_COLLECTED) == 0)) { - if (client.getVar(Varbits.DAILY_RUNES_COLLECTED) == 0) - { - sendChatMessage(RUNES_MESSAGE); - } - else if (dailyReset) - { - sendChatMessage(RUNES_MESSAGE); - } + sendChatMessage(RUNES_MESSAGE); } } private void checkSand(boolean dailyReset) { - if (client.getVar(Varbits.QUEST_THE_HAND_IN_THE_SAND) >= SAND_QUEST_COMPLETE) + if ((client.getVar(Varbits.QUEST_THE_HAND_IN_THE_SAND) >= SAND_QUEST_COMPLETE) + && (dailyReset || client.getVar(Varbits.DAILY_SAND_COLLECTED) == 0)) { - if (client.getVar(Varbits.DAILY_SAND_COLLECTED) == 0) - { - sendChatMessage(SAND_MESSAGE); - } - else if (dailyReset) - { - sendChatMessage(SAND_MESSAGE); - } + sendChatMessage(SAND_MESSAGE); } } private void checkFlax(boolean dailyReset) { - if (client.getVar(Varbits.DIARY_KANDARIN_EASY) == 1) + if ((client.getVar(Varbits.DIARY_KANDARIN_EASY) == 1) + && (dailyReset || client.getVar(Varbits.DAILY_FLAX_STATE) == 0)) { - if (client.getVar(Varbits.DAILY_FLAX_STATE) == 0) - { - sendChatMessage(FLAX_MESSAGE); - } - else if (dailyReset) - { - sendChatMessage(FLAX_MESSAGE); - } + sendChatMessage(FLAX_MESSAGE); + } + } + + private void checkArrows(boolean dailyReset) + { + if ((client.getVar(Varbits.DIARY_WESTERN_EASY) == 1) + && (dailyReset || client.getVar(Varbits.DAILY_ARROWS_STATE) == 0)) + { + sendChatMessage(ARROWS_MESSAGE); } } @@ -261,11 +246,7 @@ public class DailyTasksPlugin extends Plugin max += BONEMEAL_PER_DIARY; } } - if (collected < max) - { - sendChatMessage(BONEMEAL_MESSAGE); - } - else if (dailyReset) + if (dailyReset || collected < max) { sendChatMessage(BONEMEAL_MESSAGE); } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/MaxHitPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/MaxHitPlugin.java new file mode 100644 index 0000000000..874f038774 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/MaxHitPlugin.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2019, Bartvollebregt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.maxhit; + +import net.runelite.api.Client; +import net.runelite.api.InventoryID; +import net.runelite.api.Item; +import net.runelite.api.ItemContainer; +import net.runelite.api.events.ConfigChanged; +import net.runelite.api.events.ItemContainerChanged; +import net.runelite.api.events.VarbitChanged; +import net.runelite.api.widgets.Widget; +import net.runelite.api.widgets.WidgetInfo; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.maxhit.calculators.MagicMaxHitCalculator; +import net.runelite.client.plugins.maxhit.calculators.MeleeMaxHitCalculator; +import net.runelite.client.plugins.maxhit.calculators.RangeMaxHitCalculator; + +import javax.inject.Inject; + +@PluginDescriptor( + name = "Max Hit", + description = "Max Hit Calculator", + type = "PVM", + enabledByDefault = false +) +public class MaxHitPlugin extends Plugin +{ + + @Inject + private Client client; + + @Subscribe + public void onItemContainerChanged(final ItemContainerChanged event) + { + this.updateMaxHitWidget(); + } + + @Subscribe + public void onConfigChanged(final ConfigChanged event) + { + this.updateMaxHitWidget(); + } + + @Subscribe + public void onVarbitChanged(VarbitChanged event) + { + this.updateMaxHitWidget(); + } + + private void updateMaxHitWidget() + { + Widget equipmentStats = client.getWidget(WidgetInfo.EQUIPMENT_INVENTORY_ITEMS_CONTAINER); + + ItemContainer equipmentContainer = client.getItemContainer(InventoryID.EQUIPMENT); + Item[] equipedItems = new Item[14]; + + if (equipmentStats != null && !equipmentStats.isHidden()) + { + if (equipmentContainer != null) + { + equipedItems = equipmentContainer.getItems(); + } + + MeleeMaxHitCalculator meleeMaxHitCalculator = new MeleeMaxHitCalculator(this.client, equipedItems); + RangeMaxHitCalculator rangeMaxHitCalculator = new RangeMaxHitCalculator(this.client, equipedItems); + MagicMaxHitCalculator magicMaxHitCalculator = new MagicMaxHitCalculator(this.client, equipedItems); + + MaxHit maxHit = new MaxHit(meleeMaxHitCalculator.getMaxHit(), rangeMaxHitCalculator.getMaxHit(), magicMaxHitCalculator.getMaxHit()); + this.setWidgetMaxHit(maxHit); + } + } + + private void setWidgetMaxHit(MaxHit maxhit) + { + Widget equipYourCharacter = client.getWidget(WidgetInfo.EQUIP_YOUR_CHARACTER); + String maxHitText = "Melee Max Hit: " + maxhit.getMaxMeleeHit(); + maxHitText += "
Range Max Hit: " + maxhit.getMaxRangeHit(); + maxHitText += "
Magic Max Hit: " + maxhit.getMaxMagicHit(); + + + equipYourCharacter.setText(maxHitText); + } + + private class MaxHit + { + private final double maxMeleeHit; + private final double maxRangeHit; + private final double maxMagicHit; + + MaxHit(double maxMeleeHit, double maxRangeHit, double maxMagicHit) + { + this.maxMeleeHit = maxMeleeHit; + this.maxRangeHit = maxRangeHit; + this.maxMagicHit = maxMagicHit; + } + + double getMaxMeleeHit() + { + return maxMeleeHit; + } + + double getMaxRangeHit() + { + return maxRangeHit; + } + + double getMaxMagicHit() + { + return maxMagicHit; + } + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/attackstyle/AttackStyle.java b/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/attackstyle/AttackStyle.java new file mode 100644 index 0000000000..e9e9d9b18d --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/attackstyle/AttackStyle.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2019, Bartvollebregt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.maxhit.attackstyle; + +public enum AttackStyle +{ + ACCURATE(0), + AGGRESSIVE(3), + DEFENSIVE(0), + CONTROLLED(1), + ACCURATERANGING(3), + RANGING(0), + LONGRANGE(0), + CASTING(0), + DEFENSIVE_CASTING(0), + OTHER(0); + + private final int maxHitBonus; + + AttackStyle(int maxHitBonus) + { + this.maxHitBonus = maxHitBonus; + } + + public double getMaxHitBonus() + { + return this.maxHitBonus; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/attackstyle/WeaponType.java b/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/attackstyle/WeaponType.java new file mode 100644 index 0000000000..97ed177f27 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/attackstyle/WeaponType.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2019, Bartvollebregt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.maxhit.attackstyle; + +import java.util.HashMap; +import java.util.Map; + +import static net.runelite.client.plugins.maxhit.attackstyle.AttackStyle.*; + +public enum WeaponType +{ + TYPE_0(ACCURATE, AGGRESSIVE, null, DEFENSIVE), + TYPE_1(ACCURATE, AGGRESSIVE, AGGRESSIVE, DEFENSIVE), + TYPE_2(ACCURATE, AGGRESSIVE, null, DEFENSIVE), + TYPE_3(ACCURATERANGING, RANGING, null, LONGRANGE), + TYPE_4(ACCURATE, AGGRESSIVE, CONTROLLED, DEFENSIVE), + TYPE_5(ACCURATERANGING, RANGING, null, LONGRANGE), + TYPE_6(AGGRESSIVE, RANGING, DEFENSIVE_CASTING, null), + TYPE_7(ACCURATERANGING, RANGING, null, LONGRANGE), + TYPE_8(OTHER, AGGRESSIVE, null, null), + TYPE_9(ACCURATE, AGGRESSIVE, CONTROLLED, DEFENSIVE), + TYPE_10(ACCURATE, AGGRESSIVE, AGGRESSIVE, DEFENSIVE), + TYPE_11(ACCURATE, AGGRESSIVE, AGGRESSIVE, DEFENSIVE), + TYPE_12(CONTROLLED, AGGRESSIVE, null, DEFENSIVE), + TYPE_13(ACCURATE, AGGRESSIVE, null, DEFENSIVE), + TYPE_14(ACCURATE, AGGRESSIVE, AGGRESSIVE, DEFENSIVE), + TYPE_15(CONTROLLED, CONTROLLED, CONTROLLED, DEFENSIVE), + TYPE_16(ACCURATE, AGGRESSIVE, CONTROLLED, DEFENSIVE), + TYPE_17(ACCURATE, AGGRESSIVE, AGGRESSIVE, DEFENSIVE), + TYPE_18(ACCURATE, AGGRESSIVE, null, DEFENSIVE, CASTING, DEFENSIVE_CASTING), + TYPE_19(ACCURATERANGING, RANGING, null, LONGRANGE), + TYPE_20(ACCURATE, CONTROLLED, null, DEFENSIVE), + TYPE_21(ACCURATE, AGGRESSIVE, null, DEFENSIVE, CASTING, DEFENSIVE_CASTING), + TYPE_22(ACCURATE, AGGRESSIVE, AGGRESSIVE, DEFENSIVE), + TYPE_23(CASTING, CASTING, null, DEFENSIVE_CASTING), + TYPE_24(ACCURATE, AGGRESSIVE, CONTROLLED, DEFENSIVE), + TYPE_25(CONTROLLED, AGGRESSIVE, null, DEFENSIVE), + TYPE_26(AGGRESSIVE, AGGRESSIVE, null, AGGRESSIVE), + TYPE_27(ACCURATE, null, null, OTHER); + + private static final Map weaponTypes = new HashMap<>(); + + static + { + for (WeaponType weaponType : values()) + { + weaponTypes.put(weaponType.ordinal(), weaponType); + } + } + + private final AttackStyle[] attackStyles; + + WeaponType(AttackStyle... attackStyles) + { + this.attackStyles = attackStyles; + } + + public static WeaponType getWeaponType(int id) + { + return weaponTypes.get(id); + } + + public AttackStyle[] getAttackStyles() + { + return attackStyles; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/calculators/MagicMaxHitCalculator.java b/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/calculators/MagicMaxHitCalculator.java new file mode 100644 index 0000000000..8b14e3d988 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/calculators/MagicMaxHitCalculator.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2019, Bartvollebregt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.maxhit.calculators; + +import net.runelite.api.Client; +import net.runelite.api.Item; +import net.runelite.api.Skill; +import net.runelite.api.Varbits; +import net.runelite.api.widgets.Widget; +import net.runelite.api.widgets.WidgetInfo; +import net.runelite.client.plugins.maxhit.config.EquipmentBonusConfig; +import net.runelite.client.plugins.maxhit.config.SpellBaseDamageConfig; + +public class MagicMaxHitCalculator extends MaxHitCalculator +{ + + public MagicMaxHitCalculator(Client client, Item[] equipedItems) + { + super(client, CombatMethod.MAGIC, equipedItems); + } + + @Override + protected String getSkillStrengthText(String equipmentText) + { + return equipmentText.replace("Magic damage: ", "").replace(".", "").replace("%", ""); + } + + @Override + Widget equipmentSkillPower() + { + return this.client.getWidget(WidgetInfo.EQUIPMENT_MAGIC_DAMAGE); + } + + @Override + public double getCurrentSkillPower() + { + return this.client.getBoostedSkillLevel(Skill.MAGIC); + } + + /* + * Damage formula based on: + * http://services.runescape.com/m=forum/forums.ws?317,318,712,65587452 + * Section 4. + * */ + @Override + public double calculate() + { + int spellBaseDamage = this.baseDamage; + + if (spellBaseDamage == 0) + { + int autoCastSpellId = client.getVar(Varbits.AUTO_CAST_SPELL); + if (autoCastSpellId == 0) + { + return 0.0; + } + + SpellBaseDamageConfig autoCastSpell = SpellBaseDamageConfig.findSpellById(autoCastSpellId); + spellBaseDamage = autoCastSpell.getBaseDamage(); + } + +// a.Find the base maximum damage a spell can deal. +// See CustomFormulaConfig for spells based on magic lvl + double maxHit = spellBaseDamage; + +// b. Increase the base damage (Chaos Gauntlets) + maxHit = this.applyEquipmentBonus(maxHit, EquipmentBonusConfig.BonusType.SPECIAL); + +// c. The following bonuses stack by adding up. (List of bonus items) + maxHit = this.applyEquipmentBonus(maxHit, EquipmentBonusConfig.BonusType.EQUIPMENT); + +// d. Round down to the nearest integer. + maxHit = Math.floor(maxHit); + +// e. On a slayer task, multiply by 1.15 (imbued) + maxHit = this.applyEquipmentBonus(maxHit, EquipmentBonusConfig.BonusType.SLAYER); + +// f. Round down to the nearest integer. + maxHit = Math.floor(maxHit); + +// g. If a fire spell is used, multiply by 1.5 (Tome of fire) + maxHit = this.applyEquipmentBonus(maxHit, EquipmentBonusConfig.BonusType.MAGIC_SPECIAL); + +// h. Round down to the nearest integer. + maxHit = Math.floor(maxHit); + +// i, j. Castle Wars will not be included + return maxHit; + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/calculators/MaxHitCalculator.java b/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/calculators/MaxHitCalculator.java new file mode 100644 index 0000000000..95cc98a266 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/calculators/MaxHitCalculator.java @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2019, Bartvollebregt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.maxhit.calculators; + +import net.runelite.api.Client; +import net.runelite.api.Item; +import net.runelite.api.VarPlayer; +import net.runelite.api.Varbits; +import net.runelite.api.widgets.Widget; +import net.runelite.client.plugins.maxhit.attackstyle.AttackStyle; +import net.runelite.client.plugins.maxhit.attackstyle.WeaponType; +import net.runelite.client.plugins.maxhit.config.CustomFormulaConfig; +import net.runelite.client.plugins.maxhit.config.EquipmentBonusConfig; +import net.runelite.client.plugins.maxhit.config.PrayerBonusConfig; +import net.runelite.client.plugins.maxhit.equipment.EquipmentHelper; +import net.runelite.client.plugins.maxhit.equipment.EquipmentItemset; + +import java.util.ArrayList; +import java.util.function.BiFunction; + +public abstract class MaxHitCalculator +{ + + final Client client; + private final CombatMethod combatMethod; + private final Item[] equipedItems; + int baseDamage = 0; + + MaxHitCalculator(Client client, CombatMethod combatMethod, Item[] equipedItems) + { + this.client = client; + this.combatMethod = combatMethod; + this.equipedItems = equipedItems; + } + + protected abstract String getSkillStrengthText(String equipmentText); + + abstract Widget equipmentSkillPower(); + + public abstract double getCurrentSkillPower(); + + AttackStyle getAttackStyle() + { + int equippedWeaponTypeId = client.getVar(Varbits.EQUIPPED_WEAPON_TYPE); + int attackStyleId = client.getVar(VarPlayer.ATTACK_STYLE); + + AttackStyle[] attackStyles = WeaponType.getWeaponType(equippedWeaponTypeId).getAttackStyles(); + + if (attackStyleId < attackStyles.length) + { + AttackStyle attackStyle = attackStyles[attackStyleId]; + if (attackStyle != null) + { + return attackStyle; + } + } + + return AttackStyle.OTHER; + } + + double getPrayerBonus() + { + double bonus = 1; + for (PrayerBonusConfig prayerBonus : PrayerBonusConfig.values()) + { + boolean prayerActive = client.getVar(prayerBonus.getPrayerVarbit()) == 1; + boolean sameCombatMethod = prayerBonus.getCombatMethod() == this.combatMethod; + if (prayerActive && sameCombatMethod) + { + bonus += prayerBonus.getStrengthBonus(); + } + } + return bonus; + } + + double applyEquipmentBonus(double maxhit, EquipmentBonusConfig.BonusType bonusType) + { + double bonus = 1; + ArrayList addList = new ArrayList<>(); + + ArrayList equipmentBonuses = EquipmentBonusConfig.getBonusByType(bonusType); + + for (EquipmentBonusConfig equipmentBonus : equipmentBonuses) + { + EquipmentItemset itemSet = equipmentBonus.getItemset(); + boolean wearsSet = EquipmentHelper.wearsItemSet(this.equipedItems, itemSet); + if (wearsSet && equipmentBonus.meetsRequirements(this.client)) + { + if (equipmentBonus.getOperation() == EquipmentBonusConfig.Operation.MULTIPLY) + { + bonus += equipmentBonus.getBonus(this.combatMethod); + } + else if (equipmentBonus.getOperation() == EquipmentBonusConfig.Operation.ADD) + { + addList.add(equipmentBonus.getBonus(this.combatMethod)); + } + } + } + + maxhit *= bonus; + + maxhit = maxhit + addList.stream().reduce(0.0, Double::sum); + + return maxhit; + } + + public double getMaxHit() + { + BiFunction customFormula = this.getCustomFormula(); + if (customFormula != null) + { + return customFormula.apply(this.client, this); + } + + return this.calculate(); + } + + private BiFunction getCustomFormula() + { + for (CustomFormulaConfig customFormula : CustomFormulaConfig.values()) + { + if (this.combatMethod != customFormula.getRequiredCombatMethod()) + { + continue; + } + + boolean meetsRequirements = customFormula.getRequirements().stream().allMatch(requirement -> requirement.meetsRequirements(this.client)); + + if (meetsRequirements) + { + return customFormula.getCustomFormula(); + } + } + + return null; + } + + public abstract double calculate(); + + public void setBaseDamage(int baseDamage) + { + this.baseDamage = baseDamage; + } + + double getSkillStrength() + { + return Double.parseDouble(this.getSkillStrengthText(this.equipmentSkillPower().getText())); + } + + public enum CombatMethod + { + MELEE, + RANGE, + MAGIC + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/calculators/MeleeMaxHitCalculator.java b/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/calculators/MeleeMaxHitCalculator.java new file mode 100644 index 0000000000..d3f6d78a0b --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/calculators/MeleeMaxHitCalculator.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2019, Bartvollebregt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.maxhit.calculators; + +import net.runelite.api.Client; +import net.runelite.api.Item; +import net.runelite.api.Skill; +import net.runelite.api.widgets.Widget; +import net.runelite.api.widgets.WidgetInfo; +import net.runelite.client.plugins.maxhit.attackstyle.AttackStyle; +import net.runelite.client.plugins.maxhit.config.EquipmentBonusConfig; + +public class MeleeMaxHitCalculator extends MaxHitCalculator +{ + + public MeleeMaxHitCalculator(Client client, Item[] equipedItems) + { + super(client, CombatMethod.MELEE, equipedItems); + } + + MeleeMaxHitCalculator(Client client, CombatMethod combatMethod, Item[] equipedItems) + { + super(client, combatMethod, equipedItems); + } + + @Override + protected String getSkillStrengthText(String equipmentText) + { + return equipmentText.replace("Melee strength: ", ""); + } + + @Override + public Widget equipmentSkillPower() + { + return this.client.getWidget(WidgetInfo.EQUIPMENT_MELEE_STRENGTH); + } + + @Override + public double getCurrentSkillPower() + { + return this.client.getBoostedSkillLevel(Skill.STRENGTH); + } + + private double getEffectiveLevel() + { +// a. Take the visible strength or ranged level from the skills interface. + double skillPower = this.getCurrentSkillPower(); + +// b. Multiply the level by the prayer adjustment + double effectiveLevel = skillPower * this.getPrayerBonus(); + +// c. Round down to the nearest integer. + effectiveLevel = Math.floor(effectiveLevel); + +// d. Add the stance bonus from the combat options interface. +// Melee: +// • Aggressive: +3 +// • Controlled: +1 +// Ranged: +// * Accurate: +3 + AttackStyle attackStyle = this.getAttackStyle(); + effectiveLevel += attackStyle.getMaxHitBonus(); + +// e. Add up +8. + effectiveLevel += 8; + +// f. Multiply by the void bonus: +// • Void melee: multiply by 1.10. Round down. +// • Void ranged: multiply by 1.10. Round down. +// • Elite void ranged: multiply by 1.125. Round down. + effectiveLevel = this.applyEquipmentBonus(effectiveLevel, EquipmentBonusConfig.BonusType.VOID_KNIGHT); + +// g. This is the effective (ranged) strength level. Let this equal 'A' in the formula in + return effectiveLevel; + } + + // 3.3 Take the melee or ranged strength bonus from the equipment stats interface and let this equal 'B' in the formula in 3.1. + private double getEquipmentBonus() + { + return this.getSkillStrength(); + } + + /* + * Damage formula based on: + * http://services.runescape.com/m=forum/forums.ws?317,318,712,65587452 + * Section 3.1 + * */ + @Override + public double calculate() + { + +// a. Max hit = 0.5 + A * (B+64) /640 (A is effective level, B is Equipment bonus) + double maxHit = 0.5 + this.getEffectiveLevel() * (this.getEquipmentBonus() + 64) / 640; + +// b. Round down the max hit to the nearest integer. + maxHit = Math.floor(maxHit); + +// 3.4 Special attacks (not actually taking weapon special attacks into account) +// a. Multiply by the bonus of one of the following items (slayer) + maxHit = this.applyEquipmentBonus(maxHit, EquipmentBonusConfig.BonusType.SLAYER); + +// b. Round down the max hit to the nearest integer. + maxHit = Math.floor(maxHit); + +// c. Multiply by the bonus of one of the following items + maxHit = this.applyEquipmentBonus(maxHit, EquipmentBonusConfig.BonusType.SPECIAL); + +// d. Round down to the nearest integer. + maxHit = Math.floor(maxHit); + + return maxHit; + } +} + + diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/calculators/RangeMaxHitCalculator.java b/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/calculators/RangeMaxHitCalculator.java new file mode 100644 index 0000000000..0b397e76d5 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/calculators/RangeMaxHitCalculator.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2019, Bartvollebregt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.maxhit.calculators; + +import net.runelite.api.Client; +import net.runelite.api.Item; +import net.runelite.api.Skill; +import net.runelite.api.widgets.Widget; +import net.runelite.api.widgets.WidgetInfo; + +public class RangeMaxHitCalculator extends MeleeMaxHitCalculator +{ + + public RangeMaxHitCalculator(Client client, Item[] equipedItems) + { + super(client, CombatMethod.RANGE, equipedItems); + } + + @Override + protected String getSkillStrengthText(String equipmentText) + { + return equipmentText.replace("Ranged strength: ", "").replace(".", "").replace("%", ""); + } + + @Override + public Widget equipmentSkillPower() + { + return this.client.getWidget(WidgetInfo.EQUIPMENT_RANGED_STRENGTH); + } + + @Override + public double getCurrentSkillPower() + { + return this.client.getBoostedSkillLevel(Skill.RANGED); + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/config/CustomFormulaConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/config/CustomFormulaConfig.java new file mode 100644 index 0000000000..b050af543d --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/config/CustomFormulaConfig.java @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2019, Bartvollebregt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.maxhit.config; + +import net.runelite.api.Client; +import net.runelite.api.EquipmentInventorySlot; +import net.runelite.api.ItemID; +import net.runelite.api.Skill; +import net.runelite.client.plugins.maxhit.calculators.MaxHitCalculator; +import net.runelite.client.plugins.maxhit.equipment.EquipmentItemset; +import net.runelite.client.plugins.maxhit.equipment.EquipmentSlotItem; +import net.runelite.client.plugins.maxhit.requirements.EquipmentItemRequirement; +import net.runelite.client.plugins.maxhit.requirements.EquipmentItemSetRequirement; +import net.runelite.client.plugins.maxhit.requirements.Requirement; +import net.runelite.client.plugins.maxhit.requirements.SpellRequirement; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.function.BiFunction; + +public enum CustomFormulaConfig +{ + + MAGIC_DART( + MaxHitCalculator.CombatMethod.MAGIC, + new ArrayList<>(Arrays.asList( + new EquipmentItemRequirement(new EquipmentSlotItem(EquipmentInventorySlot.WEAPON, new ArrayList<>(Arrays.asList( + ItemID.SLAYERS_STAFF, + ItemID.SLAYERS_STAFF_E + )))), + new SpellRequirement(SpellBaseDamageConfig.MAGIC_DART) + )), + (client, calculator) -> + { + int magicLevel = client.getBoostedSkillLevel(Skill.MAGIC); + return Math.floor((magicLevel / 10.0) + 10.0); + } + ), + + TRIDENT_OF_SEAS( + MaxHitCalculator.CombatMethod.MAGIC, + new ArrayList<>(Collections.singletonList( + new EquipmentItemRequirement(new EquipmentSlotItem(EquipmentInventorySlot.WEAPON, new ArrayList<>(Arrays.asList( + ItemID.TRIDENT_OF_THE_SEAS_FULL, + ItemID.TRIDENT_OF_THE_SEAS, + ItemID.TRIDENT_OF_THE_SEAS_E + )))) + )), + (client, calculator) -> + { + int magicLevel = client.getBoostedSkillLevel(Skill.MAGIC); + + int baseDamage = (int) Math.floor(magicLevel / 3.0) - 5; + calculator.setBaseDamage(baseDamage); + + return calculator.calculate(); + } + ), + + TRIDENT_OF_SWAMP( + MaxHitCalculator.CombatMethod.MAGIC, + new ArrayList<>(Collections.singletonList( + new EquipmentItemRequirement(new EquipmentSlotItem(EquipmentInventorySlot.WEAPON, new ArrayList<>(Arrays.asList( + ItemID.TRIDENT_OF_THE_SWAMP, + ItemID.TRIDENT_OF_THE_SWAMP_E + )))) + )), + (client, calculator) -> + { + int magicLevel = client.getBoostedSkillLevel(Skill.MAGIC); + + int baseDamage = (int) Math.floor(magicLevel / 3.0) - 2; + calculator.setBaseDamage(baseDamage); + + return calculator.calculate(); + } + ), + + SWAMP_LIZARD( + MaxHitCalculator.CombatMethod.MAGIC, + new ArrayList<>(Collections.singletonList( + new EquipmentItemRequirement(new EquipmentSlotItem(EquipmentInventorySlot.WEAPON, new ArrayList<>(Collections.singletonList( + ItemID.SWAMP_LIZARD + )))) + )), + (client, calculator) -> + { + int magicLevel = client.getBoostedSkillLevel(Skill.MAGIC); + return Math.floor(0.5 + magicLevel * (64.0 + 56.0) / 640.0); + } + ), + + ORANGE_SALAMANDER( + MaxHitCalculator.CombatMethod.MAGIC, + new ArrayList<>(Collections.singletonList( + new EquipmentItemRequirement(new EquipmentSlotItem(EquipmentInventorySlot.WEAPON, new ArrayList<>(Collections.singletonList( + ItemID.ORANGE_SALAMANDER + )))) + )), + (client, calculator) -> + { + int magicLevel = client.getBoostedSkillLevel(Skill.MAGIC); + return Math.floor(0.5 + magicLevel * (64.0 + 59.0) / 640.0); + } + ), + + RED_SALAMANDER( + MaxHitCalculator.CombatMethod.MAGIC, + new ArrayList<>(Collections.singletonList( + new EquipmentItemRequirement(new EquipmentSlotItem(EquipmentInventorySlot.WEAPON, new ArrayList<>(Collections.singletonList( + ItemID.RED_SALAMANDER + )))) + )), + (client, calculator) -> + { + int magicLevel = client.getBoostedSkillLevel(Skill.MAGIC); + return Math.floor(0.5 + magicLevel * (64.0 + 77.0) / 640.0); + } + ), + + BLACK_SALAMANDER( + MaxHitCalculator.CombatMethod.MAGIC, + new ArrayList<>(Collections.singletonList( + new EquipmentItemRequirement(new EquipmentSlotItem(EquipmentInventorySlot.WEAPON, new ArrayList<>(Collections.singletonList( + ItemID.BLACK_SALAMANDER + )))) + )), + (client, calculator) -> + { + int magicLevel = client.getBoostedSkillLevel(Skill.MAGIC); + return Math.floor(0.5 + magicLevel * (64.0 + 92.0) / 640.0); + } + ), + + DHAROK( + MaxHitCalculator.CombatMethod.MELEE, + new ArrayList<>(Collections.singletonList(new EquipmentItemSetRequirement(new EquipmentItemset(Arrays.asList( + new EquipmentSlotItem(EquipmentInventorySlot.HEAD, new ArrayList<>(Arrays.asList( + ItemID.DHAROKS_HELM, + ItemID.DHAROKS_HELM_100, + ItemID.DHAROKS_HELM_75, + ItemID.DHAROKS_HELM_50, + ItemID.DHAROKS_HELM_25, + ItemID.DHAROKS_HELM_0 + ))), + new EquipmentSlotItem(EquipmentInventorySlot.BODY, new ArrayList<>(Arrays.asList( + ItemID.DHAROKS_PLATEBODY, + ItemID.DHAROKS_PLATEBODY_100, + ItemID.DHAROKS_PLATEBODY_75, + ItemID.DHAROKS_PLATEBODY_50, + ItemID.DHAROKS_PLATEBODY_25, + ItemID.DHAROKS_PLATEBODY_0 + ))), + new EquipmentSlotItem(EquipmentInventorySlot.LEGS, new ArrayList<>(Arrays.asList( + ItemID.DHAROKS_PLATELEGS, + ItemID.DHAROKS_PLATELEGS_100, + ItemID.DHAROKS_PLATELEGS_75, + ItemID.DHAROKS_PLATELEGS_50, + ItemID.DHAROKS_PLATELEGS_25, + ItemID.DHAROKS_PLATELEGS_0 + ))), + new EquipmentSlotItem(EquipmentInventorySlot.WEAPON, new ArrayList<>(Arrays.asList( + ItemID.DHAROKS_GREATAXE, + ItemID.DHAROKS_GREATAXE_100, + ItemID.DHAROKS_GREATAXE_75, + ItemID.DHAROKS_GREATAXE_50, + ItemID.DHAROKS_GREATAXE_25, + ItemID.DHAROKS_GREATAXE_0 + ))) + ))))), + (client, calculator) -> + { + int currentHP = client.getBoostedSkillLevel(Skill.HITPOINTS); + int maxHP = client.getRealSkillLevel(Skill.HITPOINTS); + int lostHP = maxHP - currentHP; + + double initialMaxHit = calculator.calculate(); + + double multiplier = (1.0 + ((lostHP / 100.0) * (maxHP / 100.0))); + + return Math.floor(initialMaxHit * multiplier); + } + ); + + private final MaxHitCalculator.CombatMethod requiredCombatMethod; + private final ArrayList requirements; + private final BiFunction customFormula; + + CustomFormulaConfig(MaxHitCalculator.CombatMethod requiredCombatMethod, ArrayList requirements, BiFunction customFormula) + { + this.requiredCombatMethod = requiredCombatMethod; + this.requirements = requirements; + this.customFormula = customFormula; + } + + public MaxHitCalculator.CombatMethod getRequiredCombatMethod() + { + return requiredCombatMethod; + } + + public BiFunction getCustomFormula() + { + return customFormula; + } + + public ArrayList getRequirements() + { + return this.requirements; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/config/EquipmentBonusConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/config/EquipmentBonusConfig.java new file mode 100644 index 0000000000..fce5816c64 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/config/EquipmentBonusConfig.java @@ -0,0 +1,418 @@ +/* + * Copyright (c) 2019, Bartvollebregt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.maxhit.config; + +import net.runelite.api.Client; +import net.runelite.api.EquipmentInventorySlot; +import net.runelite.api.ItemID; +import net.runelite.client.plugins.maxhit.calculators.MaxHitCalculator; +import net.runelite.client.plugins.maxhit.equipment.EquipmentCombatBonus; +import net.runelite.client.plugins.maxhit.equipment.EquipmentItemset; +import net.runelite.client.plugins.maxhit.equipment.EquipmentSlotItem; +import net.runelite.client.plugins.maxhit.requirements.AutocastSpellRequirement; +import net.runelite.client.plugins.maxhit.requirements.Requirement; +import net.runelite.client.plugins.maxhit.requirements.SpellBookRequirement; + +import java.util.*; + + +public enum EquipmentBonusConfig +{ + + /* + * Slayer bonus items + */ + BLACKMASK(BonusType.SLAYER, new EquipmentItemset(Collections.singletonList( + new EquipmentSlotItem(EquipmentInventorySlot.HEAD, new ArrayList<>(Arrays.asList( + ItemID.BLACK_MASK_10, + ItemID.BLACK_MASK_9, + ItemID.BLACK_MASK_8, + ItemID.BLACK_MASK_7, + ItemID.BLACK_MASK_6, + ItemID.BLACK_MASK_5, + ItemID.BLACK_MASK_4, + ItemID.BLACK_MASK_3, + ItemID.BLACK_MASK_2, + ItemID.BLACK_MASK_1, + ItemID.BLACK_MASK + ))) + )), new EquipmentCombatBonus(((7.0 / 6.0) - 1), 0, 0)), + + SLAYERHELM(BonusType.SLAYER, new EquipmentItemset(Collections.singletonList( + new EquipmentSlotItem(EquipmentInventorySlot.HEAD, new ArrayList<>(Arrays.asList( + ItemID.SLAYER_HELMET, + ItemID.BLACK_SLAYER_HELMET, + ItemID.GREEN_SLAYER_HELMET, + ItemID.RED_SLAYER_HELMET, + ItemID.PURPLE_SLAYER_HELMET, + ItemID.TURQUOISE_SLAYER_HELMET, + ItemID.HYDRA_SLAYER_HELMET + ))) + )), new EquipmentCombatBonus(((7.0 / 6.0) - 1), 0, 0)), + + BLACKMASKI(BonusType.SLAYER, new EquipmentItemset(Collections.singletonList( + new EquipmentSlotItem(EquipmentInventorySlot.HEAD, new ArrayList<>(Arrays.asList( + ItemID.BLACK_MASK_10_I, + ItemID.BLACK_MASK_9_I, + ItemID.BLACK_MASK_8_I, + ItemID.BLACK_MASK_7_I, + ItemID.BLACK_MASK_6_I, + ItemID.BLACK_MASK_5_I, + ItemID.BLACK_MASK_4_I, + ItemID.BLACK_MASK_3_I, + ItemID.BLACK_MASK_2_I, + ItemID.BLACK_MASK_1_I, + ItemID.BLACK_MASK_I + ))) + )), new EquipmentCombatBonus(((7.0 / 6.0) - 1), 0.15, 0.15)), + + SLAYERHELMI(BonusType.SLAYER, new EquipmentItemset(Collections.singletonList( + new EquipmentSlotItem(EquipmentInventorySlot.HEAD, new ArrayList<>(Arrays.asList( + ItemID.SLAYER_HELMET_I, + ItemID.BLACK_SLAYER_HELMET_I, + ItemID.GREEN_SLAYER_HELMET_I, + ItemID.RED_SLAYER_HELMET_I, + ItemID.PURPLE_SLAYER_HELMET_I, + ItemID.TURQUOISE_SLAYER_HELMET_I, + ItemID.HYDRA_SLAYER_HELMET_I + ))) + )), new EquipmentCombatBonus(((7.0 / 6.0) - 1), 0.15, 0.15)), + + /* + * Void bonus items + * */ + MELEEVOID(BonusType.VOID_KNIGHT, new EquipmentItemset(Arrays.asList( + new EquipmentSlotItem(EquipmentInventorySlot.HEAD, new ArrayList<>(Arrays.asList( + ItemID.VOID_MELEE_HELM, + ItemID.VOID_MELEE_HELM_11676 + ))), + new EquipmentSlotItem(EquipmentInventorySlot.BODY, new ArrayList<>(Arrays.asList( + ItemID.VOID_KNIGHT_TOP, + ItemID.VOID_KNIGHT_TOP_10611 + ))), + new EquipmentSlotItem(EquipmentInventorySlot.LEGS, new ArrayList<>(Collections.singletonList( + ItemID.VOID_KNIGHT_ROBE + ))), + new EquipmentSlotItem(EquipmentInventorySlot.GLOVES, new ArrayList<>(Collections.singletonList( + ItemID.VOID_KNIGHT_GLOVES + ))) + )), new EquipmentCombatBonus(0.1, 0, 0)), + + RANGERVOID(BonusType.VOID_KNIGHT, new EquipmentItemset(Arrays.asList( + new EquipmentSlotItem(EquipmentInventorySlot.HEAD, new ArrayList<>(Arrays.asList( + ItemID.VOID_RANGER_HELM, + ItemID.VOID_RANGER_HELM_11675 + ))), + new EquipmentSlotItem(EquipmentInventorySlot.BODY, new ArrayList<>(Arrays.asList( + ItemID.VOID_KNIGHT_TOP, + ItemID.VOID_KNIGHT_TOP_10611 + ))), + new EquipmentSlotItem(EquipmentInventorySlot.LEGS, new ArrayList<>(Collections.singletonList( + ItemID.VOID_KNIGHT_ROBE + ))), + new EquipmentSlotItem(EquipmentInventorySlot.GLOVES, new ArrayList<>(Collections.singletonList( + ItemID.VOID_KNIGHT_GLOVES + ))) + )), new EquipmentCombatBonus(0, 0.1, 0)), + + + ELITEMELEERVOID(BonusType.VOID_KNIGHT, new EquipmentItemset(Arrays.asList( + new EquipmentSlotItem(EquipmentInventorySlot.HEAD, new ArrayList<>(Arrays.asList( + ItemID.VOID_MELEE_HELM, + ItemID.VOID_MELEE_HELM_11676 + ))), + new EquipmentSlotItem(EquipmentInventorySlot.BODY, new ArrayList<>(Collections.singletonList( + ItemID.ELITE_VOID_TOP + ))), + new EquipmentSlotItem(EquipmentInventorySlot.LEGS, new ArrayList<>(Collections.singletonList( + ItemID.ELITE_VOID_ROBE + ))), + new EquipmentSlotItem(EquipmentInventorySlot.GLOVES, new ArrayList<>(Collections.singletonList( + ItemID.VOID_KNIGHT_GLOVES + ))) + )), new EquipmentCombatBonus(0.125, 0, 0)), + + + ELITERANGERVOID(BonusType.VOID_KNIGHT, new EquipmentItemset(Arrays.asList( + new EquipmentSlotItem(EquipmentInventorySlot.HEAD, new ArrayList<>(Arrays.asList( + ItemID.VOID_RANGER_HELM, + ItemID.VOID_RANGER_HELM_11675 + ))), + new EquipmentSlotItem(EquipmentInventorySlot.BODY, new ArrayList<>(Collections.singletonList( + ItemID.ELITE_VOID_TOP + ))), + new EquipmentSlotItem(EquipmentInventorySlot.LEGS, new ArrayList<>(Collections.singletonList( + ItemID.ELITE_VOID_ROBE + ))), + new EquipmentSlotItem(EquipmentInventorySlot.GLOVES, new ArrayList<>(Collections.singletonList( + ItemID.VOID_KNIGHT_GLOVES + ))) + )), new EquipmentCombatBonus(0, 0.125, 0)), + + ELITEMAGICVOID(BonusType.EQUIPMENT, new EquipmentItemset(Arrays.asList( + new EquipmentSlotItem(EquipmentInventorySlot.HEAD, new ArrayList<>(Arrays.asList( + ItemID.VOID_MAGE_HELM, + ItemID.VOID_MAGE_HELM_11674 + ))), + new EquipmentSlotItem(EquipmentInventorySlot.BODY, new ArrayList<>(Collections.singletonList( + ItemID.ELITE_VOID_TOP + ))), + new EquipmentSlotItem(EquipmentInventorySlot.LEGS, new ArrayList<>(Collections.singletonList( + ItemID.ELITE_VOID_ROBE + ))), + new EquipmentSlotItem(EquipmentInventorySlot.GLOVES, new ArrayList<>(Collections.singletonList( + ItemID.VOID_KNIGHT_GLOVES + ))) + )), new EquipmentCombatBonus(0, 0, 0.025)), + + /* + * Special Melee Equipment + * */ + OBISIDIAN(BonusType.SPECIAL, new EquipmentItemset(Arrays.asList( + new EquipmentSlotItem(EquipmentInventorySlot.HEAD, new ArrayList<>(Collections.singletonList( + ItemID.OBSIDIAN_HELMET + ))), + new EquipmentSlotItem(EquipmentInventorySlot.BODY, new ArrayList<>(Collections.singletonList( + ItemID.OBSIDIAN_PLATEBODY + ))), + new EquipmentSlotItem(EquipmentInventorySlot.LEGS, new ArrayList<>(Collections.singletonList( + ItemID.OBSIDIAN_PLATELEGS + ))), + new EquipmentSlotItem(EquipmentInventorySlot.WEAPON, new ArrayList<>(Arrays.asList( + ItemID.TOKTZXILAK, + ItemID.TOKTZXILEK, + ItemID.TZHAARKETEM, + ItemID.TZHAARKETOM, + ItemID.TOKTZXILAK_20554 + ))) + )), new EquipmentCombatBonus(0.1, 0, 0)), + + BERSERKERNECKLACE(BonusType.SPECIAL, new EquipmentItemset(Arrays.asList( + new EquipmentSlotItem(EquipmentInventorySlot.AMULET, new ArrayList<>(Collections.singletonList(ItemID.BERSERKER_NECKLACE))), + new EquipmentSlotItem(EquipmentInventorySlot.WEAPON, new ArrayList<>(Arrays.asList( + ItemID.TOKTZXILAK, + ItemID.TOKTZXILEK, + ItemID.TZHAARKETEM, + ItemID.TZHAARKETOM, + ItemID.TOKTZXILAK_20554 + ))) + )), new EquipmentCombatBonus(0.2, 0, 0)), + + + /* + * Magic Equipment + */ + ANCESTRAL_HAT(BonusType.EQUIPMENT, new EquipmentItemset(Collections.singletonList( + new EquipmentSlotItem(EquipmentInventorySlot.HEAD, new ArrayList<>(Collections.singletonList( + ItemID.ANCESTRAL_HAT + ))) + )), new EquipmentCombatBonus(0, 0, 0.02)), + + ANCESTRAL_ROBE_TOP(BonusType.EQUIPMENT, new EquipmentItemset(Collections.singletonList( + new EquipmentSlotItem(EquipmentInventorySlot.BODY, new ArrayList<>(Collections.singletonList( + ItemID.ANCESTRAL_ROBE_TOP + ))) + )), new EquipmentCombatBonus(0, 0, 0.02)), + + ANCESTRAL_ROBE_BOTTOM(BonusType.EQUIPMENT, new EquipmentItemset(Collections.singletonList( + new EquipmentSlotItem(EquipmentInventorySlot.LEGS, new ArrayList<>(Collections.singletonList( + ItemID.ANCESTRAL_ROBE_BOTTOM + ))) + )), new EquipmentCombatBonus(0, 0, 0.02)), + + IMBUED_GOD_CAPE(BonusType.EQUIPMENT, new EquipmentItemset(Collections.singletonList( + new EquipmentSlotItem(EquipmentInventorySlot.CAPE, new ArrayList<>(Arrays.asList( + ItemID.IMBUED_SARADOMIN_MAX_CAPE, + ItemID.IMBUED_SARADOMIN_CAPE, + ItemID.IMBUED_ZAMORAK_MAX_CAPE, + ItemID.IMBUED_ZAMORAK_CAPE, + ItemID.IMBUED_GUTHIX_MAX_CAPE, + ItemID.IMBUED_GUTHIX_CAPE + ))) + )), new EquipmentCombatBonus(0, 0, 0.02)), + + KODAI_WAND(BonusType.EQUIPMENT, new EquipmentItemset(Collections.singletonList( + new EquipmentSlotItem(EquipmentInventorySlot.WEAPON, new ArrayList<>(Collections.singletonList( + ItemID.KODAI_WAND + ))) + )), new EquipmentCombatBonus(0, 0, 0.15)), + + OCCULT_NECKLACE(BonusType.EQUIPMENT, new EquipmentItemset(Collections.singletonList( + new EquipmentSlotItem(EquipmentInventorySlot.AMULET, new ArrayList<>(Arrays.asList( + ItemID.OCCULT_NECKLACE, + ItemID.OCCULT_NECKLACE_OR + ))) + )), new EquipmentCombatBonus(0, 0, 0.10)), + + STAFF_OF_THE_DEAD(BonusType.EQUIPMENT, new EquipmentItemset(Collections.singletonList( + new EquipmentSlotItem(EquipmentInventorySlot.WEAPON, new ArrayList<>(Arrays.asList( + ItemID.STAFF_OF_THE_DEAD, + ItemID.TOXIC_STAFF_OF_THE_DEAD, + ItemID.STAFF_OF_LIGHT + ))) + )), new EquipmentCombatBonus(0, 0, 0.15)), + + TORMENTED_BRACELET(BonusType.EQUIPMENT, new EquipmentItemset(Collections.singletonList( + new EquipmentSlotItem(EquipmentInventorySlot.GLOVES, new ArrayList<>(Collections.singletonList( + ItemID.TORMENTED_BRACELET + ))) + )), new EquipmentCombatBonus(0, 0, 0.05)), + + SMOKE_STAFF(BonusType.EQUIPMENT, new EquipmentItemset(Collections.singletonList( + new EquipmentSlotItem(EquipmentInventorySlot.WEAPON, new ArrayList<>(Arrays.asList( + ItemID.SMOKE_BATTLESTAFF, + ItemID.MYSTIC_SMOKE_STAFF + ))) + )), new EquipmentCombatBonus(0, 0, 0.10), Collections.singletonList(new SpellBookRequirement(SpellBaseDamageConfig.SpellBook.NORMAL))), + + + /* + * Special magic bonusses + * */ + + CHAOS_GAUNTLETS(BonusType.SPECIAL, new EquipmentItemset(Collections.singletonList( + new EquipmentSlotItem(EquipmentInventorySlot.GLOVES, new ArrayList<>(Collections.singletonList( + ItemID.CHAOS_GAUNTLETS + ))))), + new EquipmentCombatBonus(0, 0, 3), + Collections.singletonList( + new AutocastSpellRequirement(new ArrayList<>(Arrays.asList( + SpellBaseDamageConfig.AIR_BOLT, + SpellBaseDamageConfig.WATER_BOLT, + SpellBaseDamageConfig.EARTH_BOLT, + SpellBaseDamageConfig.FIRE_BOLT + ))) + ), + Operation.ADD + ), + + TOME_OF_FIRE(BonusType.MAGIC_SPECIAL, new EquipmentItemset(Collections.singletonList( + new EquipmentSlotItem(EquipmentInventorySlot.SHIELD, new ArrayList<>(Collections.singletonList( + ItemID.TOME_OF_FIRE + ))))), + new EquipmentCombatBonus(0, 0, 0.5), + Collections.singletonList( + new AutocastSpellRequirement(new ArrayList<>(Arrays.asList( + SpellBaseDamageConfig.FIRE_BLAST, + SpellBaseDamageConfig.FIRE_BOLT, + SpellBaseDamageConfig.FIRE_STRIKE, + SpellBaseDamageConfig.FIRE_SURGE, + SpellBaseDamageConfig.FIRE_WAVE + ))) + ) + ); + + + private static final Map> bonusTypes = new HashMap<>(); + + static + { + for (EquipmentBonusConfig equipmentBonus : values()) + { + BonusType bonusType = equipmentBonus.bonusType; + if (!bonusTypes.containsKey(bonusType)) + { + bonusTypes.put(bonusType, new ArrayList<>()); + } + ArrayList list = bonusTypes.get(bonusType); + list.add(equipmentBonus); + bonusTypes.put(bonusType, list); + } + } + + private final EquipmentItemset itemset; + private BonusType bonusType; + private EquipmentCombatBonus equipmentCombatBonus; + private List requirements = new ArrayList<>(); + private Operation operation = Operation.MULTIPLY; + EquipmentBonusConfig(BonusType bonusType, EquipmentItemset itemset, EquipmentCombatBonus equipmentCombatBonus) + { + this.bonusType = bonusType; + this.itemset = itemset; + this.equipmentCombatBonus = equipmentCombatBonus; + } + EquipmentBonusConfig(BonusType bonusType, EquipmentItemset itemset, EquipmentCombatBonus equipmentCombatBonus, List requirements) + { + this.bonusType = bonusType; + this.itemset = itemset; + this.equipmentCombatBonus = equipmentCombatBonus; + this.requirements = requirements; + } + + EquipmentBonusConfig(BonusType bonusType, EquipmentItemset itemset, EquipmentCombatBonus equipmentCombatBonus, List requirements, Operation operation) + { + this.bonusType = bonusType; + this.itemset = itemset; + this.equipmentCombatBonus = equipmentCombatBonus; + this.requirements = requirements; + this.operation = operation; + } + + public static ArrayList getBonusByType(BonusType bonusType) + { + if (!bonusTypes.containsKey(bonusType)) + { + return new ArrayList<>(); + } + return bonusTypes.get(bonusType); + } + + public EquipmentItemset getItemset() + { + return itemset; + } + + public Operation getOperation() + { + return operation; + } + + public double getBonus(MaxHitCalculator.CombatMethod combatMethod) + { + return this.equipmentCombatBonus.getCombatBonus(combatMethod); + } + + public boolean meetsRequirements(Client client) + { + return requirements.stream().allMatch(requirement -> requirement.meetsRequirements(client)); + } + + public enum BonusType + { + EQUIPMENT, + SLAYER, + VOID_KNIGHT, + SPECIAL, + MAGIC_SPECIAL + } + + public enum Operation + { + ADD, + MULTIPLY + } + +} + diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/config/PrayerBonusConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/config/PrayerBonusConfig.java new file mode 100644 index 0000000000..fda776f170 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/config/PrayerBonusConfig.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2019, Bartvollebregt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.maxhit.config; + +import net.runelite.api.Varbits; +import net.runelite.client.plugins.maxhit.calculators.MaxHitCalculator; + +public enum PrayerBonusConfig +{ + BURST_OF_STRENGTH(MaxHitCalculator.CombatMethod.MELEE, Varbits.PRAYER_BURST_OF_STRENGTH, 0.05), + SUPERHUMAN_STRENGTH(MaxHitCalculator.CombatMethod.MELEE, Varbits.PRAYER_SUPERHUMAN_STRENGTH, 0.1), + ULTIMATE_STRENGTH(MaxHitCalculator.CombatMethod.MELEE, Varbits.PRAYER_ULTIMATE_STRENGTH, 0.15), + CHIVALRY(MaxHitCalculator.CombatMethod.MELEE, Varbits.PRAYER_CHIVALRY, 0.18), + PIETY(MaxHitCalculator.CombatMethod.MELEE, Varbits.PRAYER_PIETY, 0.23), + + SHARP_EYE(MaxHitCalculator.CombatMethod.RANGE, Varbits.PRAYER_SHARP_EYE, 0.05), + HAWK_EYE(MaxHitCalculator.CombatMethod.RANGE, Varbits.PRAYER_HAWK_EYE, 0.1), + EAGLE_EYE(MaxHitCalculator.CombatMethod.RANGE, Varbits.PRAYER_EAGLE_EYE, 0.15), + RIGOUR(MaxHitCalculator.CombatMethod.RANGE, Varbits.PRAYER_RIGOUR, 0.23); + + private final MaxHitCalculator.CombatMethod combatMethod; + private final Varbits prayerVarbit; + private final double strengthBonus; + + PrayerBonusConfig(MaxHitCalculator.CombatMethod combatMethod, Varbits prayerVarbit, double strengthBonus) + { + this.combatMethod = combatMethod; + this.prayerVarbit = prayerVarbit; + this.strengthBonus = strengthBonus; + } + + public MaxHitCalculator.CombatMethod getCombatMethod() + { + return combatMethod; + } + + public Varbits getPrayerVarbit() + { + return prayerVarbit; + } + + public double getStrengthBonus() + { + return strengthBonus; + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/config/SpellBaseDamageConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/config/SpellBaseDamageConfig.java new file mode 100644 index 0000000000..cc47af1562 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/config/SpellBaseDamageConfig.java @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2019, Bartvollebregt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.maxhit.config; + +import java.util.Arrays; + +public enum SpellBaseDamageConfig +{ + + /* + * Normal Spellbook + * */ + AIR_STRIKE(SpellBook.NORMAL, 1, 2), + WATER_STRIKE(SpellBook.NORMAL, 2, 4), + EARTH_STRIKE(SpellBook.NORMAL, 3, 6), + FIRE_STRIKE(SpellBook.NORMAL, 4, 8), + + AIR_BOLT(SpellBook.NORMAL, 5, 9), + WATER_BOLT(SpellBook.NORMAL, 6, 10), + EARTH_BOLT(SpellBook.NORMAL, 7, 11), + FIRE_BOLT(SpellBook.NORMAL, 8, 12), + + WIND_BLAST(SpellBook.NORMAL, 9, 13), + WATER_BLAST(SpellBook.NORMAL, 10, 14), + EARTH_BLAST(SpellBook.NORMAL, 11, 15), + FIRE_BLAST(SpellBook.NORMAL, 12, 16), + + AIR_WAVE(SpellBook.NORMAL, 13, 17), + WATER_WAVE(SpellBook.NORMAL, 14, 18), + EARTH_WAVE(SpellBook.NORMAL, 15, 19), + FIRE_WAVE(SpellBook.NORMAL, 16, 20), + + AIR_SURGE(SpellBook.NORMAL, 48, 21), + WATER_SURGE(SpellBook.NORMAL, 49, 22), + EARTH_SURGE(SpellBook.NORMAL, 50, 23), + FIRE_SURGE(SpellBook.NORMAL, 51, 24), + + /* + * Ancient Spellbook + * */ + SMOKE_RUSH(SpellBook.ANCIENT, 31, 14), + SHADOW_RUSH(SpellBook.ANCIENT, 32, 15), + BLOOD_RUSH(SpellBook.ANCIENT, 33, 16), + ICE_RUSH(SpellBook.ANCIENT, 34, 17), + + SMOKE_BURST(SpellBook.ANCIENT, 35, 18), + SHADOW_BURST(SpellBook.ANCIENT, 36, 19), + BLOOD_BURST(SpellBook.ANCIENT, 37, 21), + ICE_BURST(SpellBook.ANCIENT, 38, 22), + + SMOKE_BLITZ(SpellBook.ANCIENT, 39, 23), + SHADOW_BLITZ(SpellBook.ANCIENT, 40, 24), + BLOOD_BLITZ(SpellBook.ANCIENT, 41, 25), + ICE_BLITZ(SpellBook.ANCIENT, 42, 26), + + SMOKE_BARRAGE(SpellBook.ANCIENT, 43, 27), + SHADOW_BARRAGE(SpellBook.ANCIENT, 44, 28), + BLOOD_BARRAGE(SpellBook.ANCIENT, 55, 29), + ICE_BARRAGE(SpellBook.ANCIENT, 46, 30), + + /* + * Other spells + * */ + CRUMBLE_UNDEAD(SpellBook.OTHER, 17, 15), + IBAN_BLAST(SpellBook.OTHER, 47, 25), + FLAMES_OF_ZAMAROK(SpellBook.OTHER, 20, 20), + CLAWS_OF_GUTHIX(SpellBook.OTHER, 19, 20), + SARADOMIN_STRIKE(SpellBook.OTHER, 52, 20), + + /* + * Custom Formula spells + * */ + MAGIC_DART(SpellBook.OTHER, 18, 0); + + private final SpellBook spellBook; + private final int spellID; + private final int baseDamage; + + SpellBaseDamageConfig(SpellBook spellBook, int spellID, int baseDamage) + { + this.spellBook = spellBook; + this.spellID = spellID; + this.baseDamage = baseDamage; + } + + public static SpellBaseDamageConfig findSpellById(int spellID) + { + return Arrays.stream(values()).filter(spell -> spell.getSpellID() == spellID).findFirst().orElse(null); + } + + public int getSpellID() + { + return spellID; + } + + public int getBaseDamage() + { + return baseDamage; + } + + public SpellBook getSpellBook() + { + return spellBook; + } + + public enum SpellBook + { + NORMAL, + ANCIENT, + OTHER + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/equipment/EquipmentCombatBonus.java b/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/equipment/EquipmentCombatBonus.java new file mode 100644 index 0000000000..33ca175d4d --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/equipment/EquipmentCombatBonus.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2019, Bartvollebregt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.maxhit.equipment; + +import net.runelite.client.plugins.maxhit.calculators.MaxHitCalculator; + +public class EquipmentCombatBonus +{ + + private final double meleeBonus; + private final double rangeBonus; + private final double magicBonus; + + public EquipmentCombatBonus(double meleeBonus, double rangeBonus, double magicBonus) + { + this.meleeBonus = meleeBonus; + this.rangeBonus = rangeBonus; + this.magicBonus = magicBonus; + } + + public double getCombatBonus(MaxHitCalculator.CombatMethod combatMethod) + { + switch (combatMethod) + { + default: + case MELEE: + return this.meleeBonus; + case RANGE: + return this.rangeBonus; + case MAGIC: + return this.magicBonus; + } + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/equipment/EquipmentHelper.java b/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/equipment/EquipmentHelper.java new file mode 100644 index 0000000000..e13dee215c --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/equipment/EquipmentHelper.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2019, Bartvollebregt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.maxhit.equipment; + +import net.runelite.api.EquipmentInventorySlot; +import net.runelite.api.Item; + +public class EquipmentHelper +{ + + public static boolean wearsItemSet(Item[] equipedItems, EquipmentItemset itemSet) + { + return itemSet.getItems().stream().allMatch(item -> wearsItem(equipedItems, item)); + } + + private static boolean wearsItem(Item[] equipedItems, EquipmentInventorySlot slot, int itemId) + { + Item item = equipedItems[slot.getSlotIdx()]; + if (item == null) + { + return false; + } + return item.getId() == itemId; + } + + public static boolean wearsItem(Item[] equipedItems, EquipmentSlotItem equipmentSlotItem) + { + return equipmentSlotItem.getItems().stream().anyMatch(itemId -> + wearsItem(equipedItems, equipmentSlotItem.getEquipmentSlot(), itemId) + ); + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/equipment/EquipmentItemset.java b/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/equipment/EquipmentItemset.java new file mode 100644 index 0000000000..12c295443f --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/equipment/EquipmentItemset.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2019, Bartvollebregt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.maxhit.equipment; + +import java.util.List; + +public class EquipmentItemset +{ + private final List items; + + public EquipmentItemset(List items) + { + this.items = items; + } + + public List getItems() + { + return items; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/equipment/EquipmentSlotItem.java b/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/equipment/EquipmentSlotItem.java new file mode 100644 index 0000000000..dbc44b3a53 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/equipment/EquipmentSlotItem.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2019, Bartvollebregt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.maxhit.equipment; + +import net.runelite.api.EquipmentInventorySlot; + +import java.util.ArrayList; + +public class EquipmentSlotItem +{ + private final EquipmentInventorySlot equipmentSlot; + private final ArrayList itemIds; + + public EquipmentSlotItem(EquipmentInventorySlot equipmentSlot, ArrayList itemIds) + { + this.equipmentSlot = equipmentSlot; + this.itemIds = itemIds; + } + + public ArrayList getItems() + { + return this.itemIds; + } + + public EquipmentInventorySlot getEquipmentSlot() + { + return this.equipmentSlot; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/requirements/AutocastSpellRequirement.java b/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/requirements/AutocastSpellRequirement.java new file mode 100644 index 0000000000..988e459631 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/requirements/AutocastSpellRequirement.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2019, Bartvollebregt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.maxhit.requirements; + +import net.runelite.api.Client; +import net.runelite.api.Varbits; +import net.runelite.client.plugins.maxhit.config.SpellBaseDamageConfig; + +import java.util.ArrayList; + +public class AutocastSpellRequirement implements Requirement +{ + + private final ArrayList autocastSpells; + + public AutocastSpellRequirement(ArrayList autocastSpells) + { + this.autocastSpells = autocastSpells; + } + + @Override + public boolean meetsRequirements(Client client) + { + int autoCastSpellId = client.getVar(Varbits.AUTO_CAST_SPELL); + + if (autoCastSpellId == 0) + { + + return false; + + } + + return this.autocastSpells.stream().anyMatch(spell -> spell.getSpellID() == autoCastSpellId); + + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/requirements/EquipmentItemRequirement.java b/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/requirements/EquipmentItemRequirement.java new file mode 100644 index 0000000000..d0ed360f43 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/requirements/EquipmentItemRequirement.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2019, Bartvollebregt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.maxhit.requirements; + +import net.runelite.api.Client; +import net.runelite.api.InventoryID; +import net.runelite.api.Item; +import net.runelite.api.ItemContainer; +import net.runelite.client.plugins.maxhit.equipment.EquipmentHelper; +import net.runelite.client.plugins.maxhit.equipment.EquipmentSlotItem; + +public class EquipmentItemRequirement implements Requirement +{ + private final EquipmentSlotItem item; + + public EquipmentItemRequirement(EquipmentSlotItem item) + { + this.item = item; + } + + @Override + public boolean meetsRequirements(Client client) + { + ItemContainer equipmentContainer = client.getItemContainer(InventoryID.EQUIPMENT); + if (equipmentContainer == null) + { + return false; + } + Item[] equipedItems = equipmentContainer.getItems(); + return EquipmentHelper.wearsItem(equipedItems, this.item); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/requirements/EquipmentItemSetRequirement.java b/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/requirements/EquipmentItemSetRequirement.java new file mode 100644 index 0000000000..31b322c02d --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/requirements/EquipmentItemSetRequirement.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2019, Bartvollebregt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.maxhit.requirements; + +import net.runelite.api.Client; +import net.runelite.api.InventoryID; +import net.runelite.api.Item; +import net.runelite.api.ItemContainer; +import net.runelite.client.plugins.maxhit.equipment.EquipmentHelper; +import net.runelite.client.plugins.maxhit.equipment.EquipmentItemset; + +public class EquipmentItemSetRequirement implements Requirement +{ + private final EquipmentItemset itemSet; + + public EquipmentItemSetRequirement(EquipmentItemset itemSet) + { + this.itemSet = itemSet; + } + + @Override + public boolean meetsRequirements(Client client) + { + ItemContainer equipmentContainer = client.getItemContainer(InventoryID.EQUIPMENT); + if (equipmentContainer == null) + { + return false; + } + Item[] equipedItems = equipmentContainer.getItems(); + + return EquipmentHelper.wearsItemSet(equipedItems, this.itemSet); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/requirements/Requirement.java b/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/requirements/Requirement.java new file mode 100644 index 0000000000..48e9cc677a --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/requirements/Requirement.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2019, Bartvollebregt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.maxhit.requirements; + +import net.runelite.api.Client; + +public interface Requirement +{ + boolean meetsRequirements(Client client); +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/requirements/SpellBookRequirement.java b/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/requirements/SpellBookRequirement.java new file mode 100644 index 0000000000..d59723577e --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/requirements/SpellBookRequirement.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2019, Bartvollebregt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.maxhit.requirements; + +import net.runelite.api.Client; +import net.runelite.api.Varbits; +import net.runelite.client.plugins.maxhit.config.SpellBaseDamageConfig; + +public class SpellBookRequirement implements Requirement +{ + private final SpellBaseDamageConfig.SpellBook spellBook; + + public SpellBookRequirement(SpellBaseDamageConfig.SpellBook spellBook) + { + this.spellBook = spellBook; + } + + @Override + public boolean meetsRequirements(Client client) + { + int autoCastSpellId = client.getVar(Varbits.AUTO_CAST_SPELL); + if (autoCastSpellId == 0) + { + return false; + } + + SpellBaseDamageConfig autoCastSpell = SpellBaseDamageConfig.findSpellById(autoCastSpellId); + return autoCastSpell.getSpellBook() == this.spellBook; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/requirements/SpellRequirement.java b/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/requirements/SpellRequirement.java new file mode 100644 index 0000000000..4084044048 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/maxhit/requirements/SpellRequirement.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2019, Bartvollebregt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.maxhit.requirements; + +import net.runelite.api.Client; +import net.runelite.api.Varbits; +import net.runelite.client.plugins.maxhit.config.SpellBaseDamageConfig; + +public class SpellRequirement implements Requirement +{ + private final SpellBaseDamageConfig spellBaseDamageConfig; + + public SpellRequirement(SpellBaseDamageConfig spellBaseDamageConfig) + { + this.spellBaseDamageConfig = spellBaseDamageConfig; + } + + @Override + public boolean meetsRequirements(Client client) + { + int autoCastSpellId = client.getVar(Varbits.AUTO_CAST_SPELL); + if (autoCastSpellId == 0) + { + return false; + } + + return autoCastSpellId == this.spellBaseDamageConfig.getSpellID(); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperConfig.java index 36185d8aed..cb68438e51 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperConfig.java @@ -282,4 +282,14 @@ public interface MenuEntrySwapperConfig extends Config { return true; } + + @ConfigItem( + keyName = "swapRogueschests", + name = "Rogueschests", + description = "Swap Rogueschests from open to Search for traps" + ) + default boolean swapRogueschests() + { + return true; + } } \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPlugin.java index b164d10b86..d051028e5b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPlugin.java @@ -544,6 +544,10 @@ public class MenuEntrySwapperPlugin extends Plugin { swap("pick-lots", option, target, true); } + else if (config.swapRogueschests() && target.contains("chest")) + { + swap("search for traps", option, target, true); + } else if (config.shiftClickCustomization() && shiftModifier && !option.equals("use")) { Integer customOption = getSwapConfig(eventId); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/Game.java b/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/Game.java index db9659994f..6e9e4274bd 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/Game.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/Game.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2017, Adam + * Copyright (c) 2019, Yani * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,97 +25,220 @@ */ package net.runelite.client.plugins.pestcontrol; +import java.time.Duration; +import java.time.Instant; import java.util.ArrayList; import java.util.Collection; import java.util.List; import lombok.Getter; import lombok.extern.slf4j.Slf4j; -import static net.runelite.client.plugins.pestcontrol.Portal.BLUE; -import static net.runelite.client.plugins.pestcontrol.Portal.PURPLE; -import static net.runelite.client.plugins.pestcontrol.Portal.RED; -import static net.runelite.client.plugins.pestcontrol.Portal.YELLOW; +import net.runelite.api.Client; +import net.runelite.api.NPC; +import net.runelite.api.coords.WorldPoint; +import net.runelite.api.events.GameTick; @Slf4j -class Game +public class Game { + private Client client; + + private PestControlPlugin plugin; + + @Getter + private Portal bluePortal = new Portal(PortalColor.BLUE, WidgetPortal.BLUE); + + @Getter + private Portal purplePortal = new Portal(PortalColor.PURPLE, WidgetPortal.PURPLE); + + @Getter + private Portal yellowPortal = new Portal(PortalColor.YELLOW, WidgetPortal.YELLOW); + + @Getter + private Portal redPortal = new Portal(PortalColor.RED, WidgetPortal.RED); + + @Getter + private int shieldsDropped = 0; + // Game starts with all possible rotations - private Rotation[] possibleRotations = Rotation.values(); - // Number of shields dropped - private int shieldsDropped; + private PortalRotation[] possibleRotations = PortalRotation.values(); - @Getter - private final PortalContext purple = new PortalContext(PURPLE); - @Getter - private final PortalContext blue = new PortalContext(BLUE); - @Getter - private final PortalContext yellow = new PortalContext(YELLOW); - @Getter - private final PortalContext red = new PortalContext(RED); + private boolean portalLocationsSet = false; - void fall(String color) + + private Instant startTime = Instant.now(); + + public Game(Client client, PestControlPlugin plugin) { - switch (color.toLowerCase()) + this.client = client; + this.plugin = plugin; + } + + public void onGameTick(GameTick gameTickEvent) + { + if (!portalLocationsSet) + { + loadPortalLocations(); + } + + WidgetOverlay widgetOverlay = plugin.getWidgetOverlay(); + + if (!purplePortal.isDead() && widgetOverlay.getPortalHitpoints(PortalColor.PURPLE) == 0) + { + killPortal(purplePortal); + } + + if (!yellowPortal.isDead() && widgetOverlay.getPortalHitpoints(PortalColor.YELLOW) == 0) + { + killPortal(yellowPortal); + } + + if (!redPortal.isDead() && widgetOverlay.getPortalHitpoints(PortalColor.RED) == 0) + { + killPortal(redPortal); + } + + if (!bluePortal.isDead() && widgetOverlay.getPortalHitpoints(PortalColor.BLUE) == 0) + { + killPortal(bluePortal); + } + } + + public void lowerPortalShield(String portalColor) + { + switch (portalColor.toLowerCase()) { case "purple": - fall(purple); + lowerPortalShield(purplePortal); break; case "red": - fall(red); + lowerPortalShield(redPortal); break; case "yellow": - fall(yellow); + lowerPortalShield(yellowPortal); break; case "blue": - fall(blue); + lowerPortalShield(bluePortal); break; } } - private void fall(PortalContext portal) + private void lowerPortalShield(Portal portal) { if (!portal.isShielded()) { return; } - log.debug("Shield dropped for {}", portal.getPortal()); + log.debug("Shield dropped for {}", portal.getColor()); + + portal.setPortalState(PortalState.ACTIVE); - portal.setShielded(false); int shieldDrop = shieldsDropped++; // Remove impossible rotations - List rotations = new ArrayList<>(); + List rotations = new ArrayList<>(); - for (Rotation rotation : possibleRotations) + for (PortalRotation rotation : possibleRotations) { - if (rotation.getPortal(shieldDrop) == portal.getPortal()) + if (rotation.getPortal(this, shieldDrop) == portal) { rotations.add(rotation); } } - possibleRotations = rotations.toArray(new Rotation[rotations.size()]); + possibleRotations = rotations.toArray(new PortalRotation[rotations.size()]); } - void die(PortalContext portal) + public void killPortal(Portal portal) { if (portal.isDead()) { return; } - log.debug("Portal {} died", portal.getPortal()); + log.debug("Portal {} died", portal.getColor()); - portal.setDead(true); + portal.setPortalState(PortalState.DEAD); } - Collection getNextPortals() + private boolean loadPortalLocations() + { + NPC squire = null; + + for (NPC npc : client.getNpcs()) + { + if (PestControlNpc.isIngameSquireId(npc.getId())) + { + squire = npc; + break; + } + } + + if (squire != null) + { + log.debug("In-game Squire found: {}", squire); + setPortalLocations(squire.getWorldLocation()); + + return true; + } + + return false; + } + + public void setPortalLocations(WorldPoint squireLocation) + { + int x = squireLocation.getX(); + int y = squireLocation.getY(); + + purplePortal.setLocation(new WorldPoint(x - 26, y - 15, 0)); + bluePortal.setLocation(new WorldPoint(x + 26, y - 18, 0)); + yellowPortal.setLocation(new WorldPoint(x + 15, y - 36, 0)); + redPortal.setLocation(new WorldPoint(x - 9, y - 37, 0)); + + portalLocationsSet = true; + } + + public List getPortals() + { + List portalList = new ArrayList(); + + portalList.add(getPurplePortal()); + portalList.add(getBluePortal()); + portalList.add(getYellowPortal()); + portalList.add(getRedPortal()); + + return portalList; + } + + public Portal getPortal(PortalColor portalColor) + { + if (bluePortal.getColor() == portalColor) + { + return bluePortal; + } + if (redPortal.getColor() == portalColor) + { + return redPortal; + } + if (purplePortal.getColor() == portalColor) + { + return purplePortal; + } + if (yellowPortal.getColor() == portalColor) + { + return yellowPortal; + } + + return null; + } + + public Collection getNextPortals() { List portals = new ArrayList<>(); - for (Rotation rotation : possibleRotations) + for (PortalRotation rotation : possibleRotations) { - Portal portal = rotation.getPortal(shieldsDropped); + Portal portal = rotation.getPortal(this, shieldsDropped); if (portal != null && !portals.contains(portal)) { @@ -124,4 +248,40 @@ class Game return portals; } + + public Collection getActivePortals() + { + List portals = new ArrayList<>(); + + for (Portal portal : getPortals()) + { + if (portal.isActive()) + { + portals.add(portal); + } + } + + return portals; + } + + public Duration getTimeTillNextPortal() + { + Instant nextShieldDrop; + + if (shieldsDropped == 4) + { + return null; + } + + if (shieldsDropped == 0) + { + nextShieldDrop = Instant.ofEpochSecond(startTime.getEpochSecond() + 17); + } + else + { + nextShieldDrop = Instant.ofEpochSecond(startTime.getEpochSecond() + 17 + (30 * shieldsDropped)); + } + + return Duration.between(Instant.now(), nextShieldDrop); + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/GangplankOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/GangplankOverlay.java new file mode 100644 index 0000000000..db8abe87b7 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/GangplankOverlay.java @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2019, Yani + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.pestcontrol; + +import com.google.inject.Inject; +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.Polygon; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.Client; +import net.runelite.api.Player; +import net.runelite.api.Point; +import net.runelite.api.Tile; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayLayer; +import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.tooltip.Tooltip; +import net.runelite.client.ui.overlay.tooltip.TooltipManager; +import net.runelite.client.util.ColorUtil; + +@Slf4j +public class GangplankOverlay extends Overlay +{ + private final Client client; + private final PestControlConfig config; + private final PestControlPlugin plugin; + private final TooltipManager tooltipManager; + + @Inject + GangplankOverlay(Client client, PestControlConfig config, PestControlPlugin plugin, TooltipManager toolTipManager) + { + this.config = config; + this.plugin = plugin; + this.client = client; + this.tooltipManager = toolTipManager; + + setPosition(OverlayPosition.DYNAMIC); + setLayer(OverlayLayer.ABOVE_SCENE); + } + + @Override + public Dimension render(Graphics2D graphics) + { + if (!plugin.isOnPestControlMainIsland()) + { + return null; + } + + Player localPlayer = client.getLocalPlayer(); + + if (localPlayer == null) + { + return null; + } + + int combatLevel = localPlayer.getCombatLevel(); + + Color noviceCbColor = (combatLevel >= 40) ? Color.GREEN : Color.RED; + Color intermediateCbColor = (combatLevel >= 70) ? Color.GREEN : Color.RED; + Color veteranCbColor = (combatLevel >= 100) ? Color.GREEN : Color.RED; + + Tile noviceGangplankTile = plugin.getNoviceGangplankTile(); + Tile intermediateGangplankTile = plugin.getIntermediateGangplankTile(); + Tile veteranGangplankTile = plugin.getVeteranGangplankTile(); + + Point mousePosition = client.getMouseCanvasPosition(); + String tooltipString = null; + + if (noviceGangplankTile != null) + { + Polygon polygon = noviceGangplankTile.getGameObjects()[0].getConvexHull(); + if (polygon != null) + { + graphics.setColor(noviceCbColor); + graphics.setStroke(new BasicStroke(2)); + graphics.drawPolygon(polygon); + graphics.setColor(setColorAlpha(noviceCbColor, 45)); + graphics.fill(polygon); + + if (polygon.contains(mousePosition.getX(), mousePosition.getY())) + { + tooltipString = ColorUtil.wrapWithColorTag("Combat 40+", noviceCbColor) + " (3 points)"; + graphics.setColor(setColorAlpha(noviceCbColor, 65)); + graphics.fill(polygon); + } + } + } + + if (intermediateGangplankTile != null) + { + Polygon polygon = intermediateGangplankTile.getGameObjects()[0].getConvexHull(); + if (polygon != null) + { + graphics.setColor(intermediateCbColor); + graphics.setStroke(new BasicStroke(2)); + graphics.drawPolygon(polygon); + graphics.setColor(setColorAlpha(intermediateCbColor, 45)); + graphics.fill(polygon); + + if (polygon.contains(mousePosition.getX(), mousePosition.getY())) + { + tooltipString = ColorUtil.wrapWithColorTag("Combat 70+", intermediateCbColor) + " (4 points)"; + graphics.setColor(setColorAlpha(intermediateCbColor, 65)); + graphics.fill(polygon); + } + } + } + + if (veteranGangplankTile != null) + { + Polygon polygon = veteranGangplankTile.getGameObjects()[0].getConvexHull(); + if (polygon != null) + { + graphics.setColor(veteranCbColor); + graphics.setStroke(new BasicStroke(2)); + graphics.drawPolygon(polygon); + graphics.setColor(setColorAlpha(veteranCbColor, 45)); + graphics.fill(polygon); + + if (polygon.contains(mousePosition.getX(), mousePosition.getY())) + { + tooltipString = ColorUtil.wrapWithColorTag("Combat 100+", veteranCbColor) + " (5 points)"; + graphics.setColor(setColorAlpha(veteranCbColor, 65)); + graphics.fill(polygon); + } + } + } + + if (tooltipString != null) + { + tooltipManager.add(new Tooltip(tooltipString)); + } + + return null; + } + + private Color setColorAlpha(Color color, int alpha) + { + return new Color( + color.getRed(), + color.getGreen(), + color.getBlue(), + alpha + ); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/HintArrowOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/HintArrowOverlay.java new file mode 100644 index 0000000000..0d9660ed37 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/HintArrowOverlay.java @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2019, Yani + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.pestcontrol; + +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import javax.inject.Inject; +import net.runelite.api.Client; +import net.runelite.api.NPC; +import net.runelite.api.coords.WorldPoint; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayLayer; +import net.runelite.client.ui.overlay.OverlayPosition; + +public class HintArrowOverlay extends Overlay +{ + private final PestControlConfig config; + private final PestControlPlugin plugin; + private final Client client; + + @Inject + HintArrowOverlay(PestControlConfig config, PestControlPlugin plugin, Client client) + { + this.config = config; + this.plugin = plugin; + this.client = client; + + setPosition(OverlayPosition.DYNAMIC); + setLayer(OverlayLayer.ABOVE_SCENE); + } + + @Override + public Dimension render(Graphics2D graphics) + { + if (plugin.getGame() == null) + { + return null; + } + + List visibleActivePortals = new ArrayList<>(); + List visibleShieldedPortals = new ArrayList<>(); + + for (NPC npc : client.getNpcs()) + { + if (PestControlNpc.isActivePortalId(npc.getId())) + { + visibleActivePortals.add(npc); + } + } + + if (!visibleActivePortals.isEmpty()) + { + NPC closestPortalNpc = getClosestNpc(visibleActivePortals); + + if (closestPortalNpc != null) + { + NPC currentHintArrowTarget = client.getHintArrowNpc(); + + if (currentHintArrowTarget == null || currentHintArrowTarget != closestPortalNpc) + { + client.setHintArrow(closestPortalNpc); + } + } + + return null; + } + + Portal closestActivePortal = getClosestPortal(PortalState.ACTIVE); + + if (closestActivePortal != null) + { + WorldPoint currentHintArrowLocation = client.getHintArrowPoint(); + WorldPoint closestActivePortalLocation = closestActivePortal.getLocation(); + + if (currentHintArrowLocation == null || currentHintArrowLocation != closestActivePortalLocation) + { + client.setHintArrow(closestActivePortalLocation); + } + + return null; + } + + Collection nextPortalList = plugin.getGame().getNextPortals(); + + if (nextPortalList.size() == 1) + { + client.setHintArrow(nextPortalList.iterator().next().getLocation()); + + return null; + } + + return null; + } + + private NPC getClosestNpc(List npcList) + { + WorldPoint currentLocation = client.getLocalPlayer().getWorldLocation(); + + NPC closestNpc = null; + int currentShortestDistance = 1337; + int distanceToNpc; + + for (NPC npc : npcList) + { + if (closestNpc != null) + { + distanceToNpc = npc.getWorldLocation().distanceTo(currentLocation); + + if (distanceToNpc < currentShortestDistance) + { + closestNpc = npc; + currentShortestDistance = distanceToNpc; + } + } + else + { + closestNpc = npc; + } + } + + return closestNpc; + } + + private Portal getClosestPortal(PortalState portalState) + { + WorldPoint currentLocation = client.getLocalPlayer().getWorldLocation(); + + Portal closestPortal = null; + int currentShortestDistance = 1337; + int distanceToWorldPoint; + + for (Portal portal : plugin.getGame().getPortals()) + { + if (portal.getPortalState() != portalState) + { + continue; + } + + distanceToWorldPoint = portal.getLocation().distanceTo(currentLocation); + + if (distanceToWorldPoint < currentShortestDistance) + { + closestPortal = portal; + currentShortestDistance = distanceToWorldPoint; + } + } + + return closestPortal; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/NpcHighlightContext.java b/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/NpcHighlightContext.java new file mode 100644 index 0000000000..56c51fdd8d --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/NpcHighlightContext.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019, Yani + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.pestcontrol; + +import java.awt.Color; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; +import net.runelite.client.plugins.pestcontrol.config.NpcHighlightStyle; + +@AllArgsConstructor +@Getter +@Setter +public class NpcHighlightContext +{ + private NpcHighlightStyle npcRenderStyle; + private Color color; + private boolean showNpcName = false; +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/NpcHighlightOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/NpcHighlightOverlay.java new file mode 100644 index 0000000000..bb8e94c175 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/NpcHighlightOverlay.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2019, Yani + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.pestcontrol; + +import com.google.inject.Inject; +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.Polygon; +import java.util.HashMap; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.Client; +import net.runelite.api.NPC; +import net.runelite.api.Perspective; +import net.runelite.api.Point; +import net.runelite.client.plugins.pestcontrol.config.NpcHighlightStyle; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayLayer; +import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.OverlayUtil; + +@Slf4j +public class NpcHighlightOverlay extends Overlay +{ + private final PestControlConfig config; + private final PestControlPlugin plugin; + private final Client client; + + @Inject + NpcHighlightOverlay(PestControlConfig config, PestControlPlugin plugin, Client client) + { + this.config = config; + this.plugin = plugin; + this.client = client; + + setPosition(OverlayPosition.DYNAMIC); + setLayer(OverlayLayer.ABOVE_SCENE); + } + + @Override + public Dimension render(Graphics2D graphics) + { + if (plugin.getGame() == null) + { + return null; + } + + HashMap highlightedNpcList = plugin.getHighlightedNpcList(); + + for (NPC npc : client.getNpcs()) + { + if (!highlightedNpcList.containsKey(npc.getId())) + { + continue; + } + + NpcHighlightContext npcHighlightContext = plugin.getHighlightedNpcList().get(npc.getId()); + + String name = npcHighlightContext.isShowNpcName() ? npc.getName() : null; + Color color = npcHighlightContext.getColor(); + NpcHighlightStyle highlightStyle = npcHighlightContext.getNpcRenderStyle(); + + switch (highlightStyle) + { + case HULL: + { + renderHullOverlay(graphics, npc, color); + break; + } + case TILE: + { + renderTileOverlay(graphics, npc, color); + break; + } + case BOTH: + { + renderHullOverlay(graphics, npc, color); + renderTileOverlay(graphics, npc, color); + break; + } + } + + if (name != null) + { + renderTextOverlay(graphics, npc, name, color); + } + } + + return null; + } + + private void renderTileOverlay(Graphics2D graphics, NPC npc, Color color) + { + Polygon polygon; + Color fillColor; + + // Double the polygon size if it's a Brawler + if (PestControlNpc.isBrawlerId(npc.getId())) + { + polygon = Perspective.getCanvasTileAreaPoly(client, npc.getLocalLocation(), 2); + fillColor = new Color(color.getRed(), color.getGreen(), color.getBlue(), 35); + } + else + { + polygon = npc.getCanvasTilePoly(); + fillColor = new Color(0, 0, 0, 50); + } + + if (polygon != null) + { + graphics.setColor(color); + graphics.setStroke(new BasicStroke(2)); + graphics.drawPolygon(polygon); + graphics.setColor(fillColor); + graphics.fillPolygon(polygon); + } + } + + private void renderHullOverlay(Graphics2D graphics, NPC npc, Color color) + { + Polygon objectClickbox = npc.getConvexHull(); + if (objectClickbox != null) + { + graphics.setColor(color); + graphics.setStroke(new BasicStroke(2)); + graphics.draw(objectClickbox); + graphics.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), 20)); + graphics.fill(objectClickbox); + } + } + + private void renderTextOverlay(Graphics2D graphics, NPC npc, String text, Color color) + { + Point textLocation = npc.getCanvasTextLocation(graphics, text, npc.getLogicalHeight() + 40); + if (textLocation != null) + { + OverlayUtil.renderTextLocation(graphics, textLocation, text, color); + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/PestControlConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/PestControlConfig.java new file mode 100644 index 0000000000..03162389ad --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/PestControlConfig.java @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2019, Yani + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.pestcontrol; + +import java.awt.Color; +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; +import net.runelite.client.plugins.pestcontrol.config.HighlightPortalOption; +import net.runelite.client.plugins.pestcontrol.config.NpcHighlightStyle; + +@ConfigGroup("pestcontrol") +public interface PestControlConfig extends Config +{ + @ConfigItem( + keyName = "showHintArrow", + name = "Show hint arrows", + description = "Show hint arrows to the portals that can be attacked.", + position = 1 + ) + default boolean showHintArrow() + { + return true; + } + + @ConfigItem( + keyName = "showPortalWeakness", + name = "Show portal weakness", + description = "Show the combat style weakness of the portals. For melee the attack styles are shown: Stab/Crush/Slash", + position = 2 + ) + default boolean showPortalWeakness() + { + return false; + } + + @ConfigItem( + keyName = "highlightGangplanks", + name = "Highlight gangplanks", + description = "Highlight the boarding gangplanks and show the required combat level.", + position = 3 + ) + default boolean highlightGangplanks() + { + return true; + } + + @ConfigItem( + keyName = "highlightPortals", + name = "Highlight portals", + description = "Highlight all, active or shielded portals.", + position = 4 + ) + default HighlightPortalOption portalHighlight() + { + return HighlightPortalOption.ACTIVE; + } + + @ConfigItem( + keyName = "activePortalColor", + name = "Active portal color", + description = "Color of the portals that can be attacked.", + position = 5 + ) + default Color activePortalColor() + { + return Color.GREEN; + } + + @ConfigItem( + keyName = "shieldedPortalColor", + name = "Shielded portal color", + description = "Color of the portals that are shielded.", + position = 6 + ) + default Color shieldedPortalColor() + { + return Color.BLUE; + } + + @ConfigItem( + keyName = "highlightSpinners", + name = "Highlight Spinners", + description = "Highlights Spinners. Highlighting them is recommended as Spinners heal the portals.", + position = 7 + ) + default NpcHighlightStyle highlightSpinners() + { + return NpcHighlightStyle.BOTH; + } + + @ConfigItem( + keyName = "spinnerColor", + name = "Spinner color", + description = "Color of highlighted Spinners.", + position = 8 + ) + default Color spinnerColor() + { + return Color.CYAN; + } + + @ConfigItem( + keyName = "highlightBrawlers", + name = "Highlight Brawlers", + description = "Highlights Brawlers.", + position = 9 + ) + default NpcHighlightStyle highlightBrawlers() + { + return NpcHighlightStyle.TILE; + } + + @ConfigItem( + keyName = "brawlerColor", + name = "Brawler color", + description = "Color of highlighted Brawlers.", + position = 10 + ) + default Color brawlerColor() + { + return Color.ORANGE; + } + + @ConfigItem( + keyName = "highlightRepairables", + name = "Highlight repairables", + description = "Highlight repairable barricades and gates.", + position = 11 + ) + default boolean highlightRepairables() + { + return false; + } + + @ConfigItem( + keyName = "repairableColor", + name = "Repairable color", + description = "Color of highlighted repairables.", + position = 12 + ) + /*default Color repairableColor() + { + return new Color(193, 141, 255); + }*/ + default Color repairableColor() + { + return Color.YELLOW; + } + + @ConfigItem( + keyName = "showPoints", + name = "Show points indicator", + description = "Always display your points when on the island or in the minigame.", + position = 13 + ) + default boolean showPoints() + { + return true; + } + + @ConfigItem( + keyName = "showTimeTillNextPortal", + name = "Show time till next portal", + description = "Show a timer that counts down till the next portal is attackable.", + position = 14 + ) + default boolean showTimeTillNextPortal() + { + return true; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/PestControlNpc.java b/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/PestControlNpc.java new file mode 100644 index 0000000000..e1b9ef143e --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/PestControlNpc.java @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2019, Yani + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.pestcontrol; + +import com.google.common.collect.ImmutableSet; +import java.util.Set; +import lombok.Getter; +import net.runelite.api.NpcID; + +@Getter +public class PestControlNpc +{ + @Getter + private static final Set splatterIdSet = ImmutableSet.of( + NpcID.SPLATTER, + NpcID.SPLATTER_1690, + NpcID.SPLATTER_1691, + NpcID.SPLATTER_1692, + NpcID.SPLATTER_1693 + ); + + @Getter + public static final Set shifterIdSet = ImmutableSet.of( + NpcID.SHIFTER, + NpcID.SHIFTER_1695, + NpcID.SHIFTER_1696, + NpcID.SHIFTER_1697, + NpcID.SHIFTER_1698, + NpcID.SHIFTER_1699, + NpcID.SHIFTER_1700, + NpcID.SHIFTER_1701, + NpcID.SHIFTER_1702, + NpcID.SHIFTER_1703 + ); + + @Getter + private static final Set spinnerIdSet = ImmutableSet.of( + NpcID.SPINNER, + NpcID.SPINNER_1710, + NpcID.SPINNER_1711, + NpcID.SPINNER_1712, + NpcID.SPINNER_1713 + ); + + @Getter + private static final Set torcherIdSet = ImmutableSet.of( + NpcID.TORCHER, + NpcID.TORCHER_1715, + NpcID.TORCHER_1716, + NpcID.TORCHER_1717, + NpcID.TORCHER_1718, + NpcID.TORCHER_1719, + NpcID.TORCHER_1720, + NpcID.TORCHER_1721, + NpcID.TORCHER_1722, + NpcID.TORCHER_1723 + ); + + @Getter + private static final Set defilerIdSet = ImmutableSet.of( + NpcID.DEFILER, + NpcID.DEFILER_1725, + NpcID.DEFILER_1726, + NpcID.DEFILER_1727, + NpcID.DEFILER_1728, + NpcID.DEFILER_1729, + NpcID.DEFILER_1730, + NpcID.DEFILER_1731, + NpcID.DEFILER_1732, + NpcID.DEFILER_1733 + ); + + @Getter + private static final Set brawlerIdSet = ImmutableSet.of( + NpcID.BRAWLER, + NpcID.BRAWLER_1735, + NpcID.BRAWLER_1736, + NpcID.BRAWLER_1737, + NpcID.BRAWLER_1738 + ); + + @Getter + private static final Set ravagerIdSet = ImmutableSet.of( + NpcID.RAVAGER, + NpcID.RAVAGER_1705, + NpcID.RAVAGER_1706, + NpcID.RAVAGER_1707, + NpcID.RAVAGER_1708 + ); + + @Getter + private static final Set activePortalIdSet = ImmutableSet.of( + NpcID.PORTAL_1747, // Novice Purple Active + NpcID.PORTAL_1748, // Novice Blue Active + NpcID.PORTAL_1749, // Novice Yellow Active + NpcID.PORTAL_1750, // Novice Red Active + NpcID.PORTAL, // Intermediate Purple Active + NpcID.PORTAL_1740, // Intermediate Blue Active + NpcID.PORTAL_1741, // Intermediate Yellow Active + NpcID.PORTAL_1742 // Intermediate Red Active + ); + + @Getter + private static final Set shieldedPortalIdSet = ImmutableSet.of( + NpcID.PORTAL_1751, // Novice Purple Shielded + NpcID.PORTAL_1752, // Novice Blue Shielded + NpcID.PORTAL_1753, // Novice Yellow Shielded + NpcID.PORTAL_1754, // Novice Red Shielded + NpcID.PORTAL_1743, // Intermediate Purple Shielded + NpcID.PORTAL_1744, // Intermediate Blue Shielded + NpcID.PORTAL_1745, // Intermediate Yellow Shielded + NpcID.PORTAL_1746 // Intermediate Red Shielded + ); + + @Getter + private static final Set purplePortalIdSet = ImmutableSet.of( + NpcID.PORTAL_1747, // Novice Purple Active + NpcID.PORTAL_1751, // Novice Purple Shielded + NpcID.PORTAL, // Intermediate Purple Active + NpcID.PORTAL_1743 // Intermediate Purple Shielded + ); + + @Getter + private static final Set bluePortalIdSet = ImmutableSet.of( + NpcID.PORTAL_1748, // Novice Blue Active + NpcID.PORTAL_1752, // Novice Blue Shielded + NpcID.PORTAL_1740, // Intermediate Blue Active + NpcID.PORTAL_1744 // Intermediate Blue Shielded + ); + + @Getter + private static final Set yellowPortalIdSet = ImmutableSet.of( + NpcID.PORTAL_1749, // Novice Yellow Active + NpcID.PORTAL_1753, // Novice Yellow Shielded + NpcID.PORTAL_1741, // Intermediate Yellow Active + NpcID.PORTAL_1745 // Intermediate Yellow Shielded + ); + + @Getter + private static final Set redPortalIdSet = ImmutableSet.of( + NpcID.PORTAL_1750, // Novice Red Active + NpcID.PORTAL_1754, // Novice Red Shielded + NpcID.PORTAL_1742, // Intermediate Red Active + NpcID.PORTAL_1746 // Intermediate Red Shielded + ); + + @Getter + private static final Set ingameVoidKnightIdSet = ImmutableSet.of( + NpcID.VOID_KNIGHT_2950, + NpcID.VOID_KNIGHT_2951, + NpcID.VOID_KNIGHT_2952, + NpcID.VOID_KNIGHT_2953 + ); + + @Getter + private static final Integer ingameSquireId = NpcID.SQUIRE_2949; + + public static boolean isSplatterId(int npcId) + { + return splatterIdSet.contains(npcId); + } + + public static boolean isShifterId(int npcId) + { + return shifterIdSet.contains(npcId); + } + + public static boolean isSpinnerId(int npcId) + { + return brawlerIdSet.contains(npcId); + } + + public static boolean isTorcherId(int npcId) + { + return torcherIdSet.contains(npcId); + } + + public static boolean isDefilerId(int npcId) + { + return defilerIdSet.contains(npcId); + } + + public static boolean isBrawlerId(int npcId) + { + return brawlerIdSet.contains(npcId); + } + + public static boolean isRavagerId(int npcId) + { + return ravagerIdSet.contains(npcId); + } + + public static boolean isIngameVoidKnightId(int npcId) + { + return ingameVoidKnightIdSet.contains(npcId); + } + + public static boolean isIngameSquireId(int npcId) + { + return ingameSquireId == npcId; + } + + public static boolean isPortalId(int npcId) + { + return (isActivePortalId(npcId) || isShieldedPortalId(npcId)); + } + + public static boolean isActivePortalId(int npcId) + { + return activePortalIdSet.contains(npcId); + } + + public static boolean isShieldedPortalId(int npcId) + { + return shieldedPortalIdSet.contains(npcId); + } + + public static boolean isPurplePortalId(int npcId) + { + return purplePortalIdSet.contains(npcId); + } + + public static boolean isBluePortalId(int npcId) + { + return bluePortalIdSet.contains(npcId); + } + + public static boolean isYellowPortalId(int npcId) + { + return yellowPortalIdSet.contains(npcId); + } + + public static boolean isRedPortalId(int npcId) + { + return redPortalIdSet.contains(npcId); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/PestControlPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/PestControlPlugin.java index 960b589737..090874604a 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/PestControlPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/PestControlPlugin.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2017, Kronos + * Copyright (c) 2019, Yani * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,29 +25,47 @@ */ package net.runelite.client.plugins.pestcontrol; -import com.google.common.collect.ImmutableSet; +import com.google.inject.Inject; +import com.google.inject.Provides; +import java.awt.image.BufferedImage; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; -import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; -import javax.inject.Inject; -import lombok.AccessLevel; import lombok.Getter; +import lombok.extern.slf4j.Slf4j; import net.runelite.api.ChatMessageType; import net.runelite.api.Client; import net.runelite.api.GameState; -import net.runelite.api.NPC; -import net.runelite.api.NpcID; +import net.runelite.api.ItemID; +import net.runelite.api.Tile; +import net.runelite.api.TileObject; import net.runelite.api.events.ChatMessage; +import net.runelite.api.events.ConfigChanged; +import net.runelite.api.events.GameObjectChanged; +import net.runelite.api.events.GameObjectDespawned; +import net.runelite.api.events.GameObjectSpawned; import net.runelite.api.events.GameStateChanged; -import net.runelite.api.events.NpcDespawned; -import net.runelite.api.events.NpcSpawned; +import net.runelite.api.events.GameTick; +import net.runelite.api.events.GroundObjectChanged; +import net.runelite.api.events.GroundObjectDespawned; +import net.runelite.api.events.GroundObjectSpawned; +import net.runelite.api.events.WidgetLoaded; +import net.runelite.api.widgets.Widget; +import net.runelite.api.widgets.WidgetInfo; +import net.runelite.client.config.ConfigManager; import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.game.ItemManager; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.pestcontrol.config.HighlightPortalOption; +import net.runelite.client.plugins.pestcontrol.config.NpcHighlightStyle; import net.runelite.client.ui.overlay.OverlayManager; +import net.runelite.client.ui.overlay.infobox.InfoBoxManager; +import net.runelite.client.util.Text; +@Slf4j @PluginDescriptor( name = "Pest Control", description = "Show helpful information for the Pest Control minigame", @@ -54,77 +73,598 @@ import net.runelite.client.ui.overlay.OverlayManager; ) public class PestControlPlugin extends Plugin { - private static final Set SPINNER_IDS = ImmutableSet.of( - NpcID.SPINNER, - NpcID.SPINNER_1710, - NpcID.SPINNER_1711, - NpcID.SPINNER_1712, - NpcID.SPINNER_1713 - ); + private final int NOVICE_GANGPLANK = 14315; // Combat 40+ (3 points) + private final int INTERMEDIATE_GANGPLANK = 25631; // Combat 70+ (4 points) + private final int VETERAN_GANGPLANK = 25632; // Combat 100+ (5 points) - private final Pattern SHIELD_DROP = Pattern.compile("The ([a-z]+), [^ ]+ portal shield has dropped!", Pattern.CASE_INSENSITIVE); + private final Pattern SHIELD_DROP_PATTERN = Pattern.compile("The ([a-z]+), [^ ]+ portal shield has dropped!"); + private final Pattern EXCHANGE_WINDOW_POINTS_PATTERN = Pattern.compile("Points: ([0-9]+)"); + private final Pattern BOAT_POINTS_PATTERN = Pattern.compile("Pest Points: ([0-9]+)"); + private final Pattern AWARDED_PATTERN = Pattern.compile("We've awarded you ([0-9]+) Void Knight Commendation points."); + private final Pattern PURCHASE_PATTERN = Pattern.compile("Remaining Void Knight Commendation Points: ([0-9]+)"); - @Getter(AccessLevel.PACKAGE) - private List spinners = new ArrayList<>(); + @Inject + @Getter + private Client client; @Inject private OverlayManager overlayManager; @Inject - private Client client; + private ConfigManager configManager; @Inject - private PestControlOverlay overlay; + private InfoBoxManager infoBoxManager; + + @Inject + @Getter + private ItemManager itemManager; + + @Inject + @Getter + private PestControlConfig config; + + @Inject + @Getter + private WidgetOverlay widgetOverlay; + + @Inject + private HintArrowOverlay hintArrowOverlay; + + @Inject + private NpcHighlightOverlay npcHighlightOverlay; + + @Inject + private RepairOverlay repairOverlay; + + @Inject + private GangplankOverlay gangplankOverlay; + + @Inject + private TimerOverlay timerOverlay; + + @Inject + private PortalWeaknessOverlay portalWeaknessOverlay; + + @Getter + private Game game; + + @Getter + private HashMap highlightedNpcList = new HashMap(); + + @Getter + private List highlightedRepairList = new ArrayList(); + + @Getter + private Tile noviceGangplankTile; + + @Getter + private Tile intermediateGangplankTile; + + @Getter + private Tile veteranGangplankTile; + + @Getter + private Integer commendationPoints; + + private String userConfigKey; + + private boolean checkForPointWidgets; + + private boolean pointsRewarded = false; + + private PointsInfoboxCounter pointsInfoboxCounter; + + + @Provides + PestControlConfig provideConfig(ConfigManager configManager) + { + return configManager.getConfig(PestControlConfig.class); + } @Override protected void startUp() throws Exception { - overlayManager.add(overlay); + loadPlugin(); } @Override protected void shutDown() throws Exception { - overlayManager.remove(overlay); - spinners.clear(); + unloadPlugin(); + } + + @Subscribe + public void onConfigChanged(ConfigChanged configEvent) + { + if (configEvent.getGroup().equals("pestcontrol")) + { + unloadPlugin(); + loadPlugin(); + } + } + + private boolean loadLocalUserPoints() + { + if (userConfigKey != null) + { + String configKey = "points." + userConfigKey; + String pointString = configManager.getConfiguration("pestcontrol", configKey); + if (pointString != null) + { + commendationPoints = Integer.parseInt(pointString); + + return true; + } + } + + return false; + } + + private void loadPlugin() + { + if (loadLocalUserPoints()) + { + handlePointsInfoboxCounter(); + } + + overlayManager.add(widgetOverlay); + + if (config.highlightSpinners() != NpcHighlightStyle.OFF) + { + for (Integer npcId : PestControlNpc.getSpinnerIdSet()) + { + highlightedNpcList.put(npcId, new NpcHighlightContext( + config.highlightSpinners(), + config.spinnerColor(), + true + )); + } + } + + if (config.highlightBrawlers() != NpcHighlightStyle.OFF) + { + for (Integer npcId : PestControlNpc.getBrawlerIdSet()) + { + highlightedNpcList.put(npcId, new NpcHighlightContext( + config.highlightBrawlers(), + config.brawlerColor(), + false + )); + } + } + + if (config.portalHighlight() != HighlightPortalOption.OFF) + { + if (config.portalHighlight() == HighlightPortalOption.ACTIVE || + config.portalHighlight() == HighlightPortalOption.ALL) + { + for (Integer portalNpcId : PestControlNpc.getActivePortalIdSet()) + { + highlightedNpcList.put(portalNpcId, new NpcHighlightContext( + NpcHighlightStyle.HULL, + config.activePortalColor(), + false + )); + } + } + + if (config.portalHighlight() == HighlightPortalOption.SHIELDED || + config.portalHighlight() == HighlightPortalOption.ALL) + { + for (Integer portalNpcId : PestControlNpc.getShieldedPortalIdSet()) + { + highlightedNpcList.put(portalNpcId, new NpcHighlightContext( + NpcHighlightStyle.HULL, + config.shieldedPortalColor(), + false + )); + } + } + } + + if (!highlightedNpcList.isEmpty()) + { + overlayManager.add(npcHighlightOverlay); + } + + if (config.highlightRepairables()) + { + overlayManager.add(repairOverlay); + } + + if (config.showHintArrow()) + { + overlayManager.add(hintArrowOverlay); + + if (game != null && client.hasHintArrow()) + { + client.clearHintArrow(); + } + } + + if (config.highlightGangplanks()) + { + overlayManager.add(gangplankOverlay); + } + + if (config.showTimeTillNextPortal()) + { + overlayManager.add(timerOverlay); + } + + if (config.showPortalWeakness()) + { + overlayManager.add(portalWeaknessOverlay); + } + } + + private void unloadPlugin() + { + overlayManager.remove(widgetOverlay); + overlayManager.remove(npcHighlightOverlay); + overlayManager.remove(repairOverlay); + overlayManager.remove(gangplankOverlay); + overlayManager.remove(hintArrowOverlay); + overlayManager.remove(timerOverlay); + overlayManager.remove(portalWeaknessOverlay); + + infoBoxManager.removeInfoBox(pointsInfoboxCounter); + pointsInfoboxCounter = null; + + highlightedNpcList.clear(); + + if (game != null && config.showHintArrow() && client.hasHintArrow()) + { + client.clearHintArrow(); + } } @Subscribe public void onGameStateChanged(GameStateChanged event) { - GameState gameState = event.getGameState(); - if (gameState == GameState.CONNECTION_LOST || gameState == GameState.LOGIN_SCREEN || gameState == GameState.HOPPING) + // LOGGED_IN also triggers when teleporting to the island + if (event.getGameState() == GameState.LOGGED_IN) { - spinners.clear(); + handlePointsInfoboxCounter(); } } + public void handlePointsInfoboxCounter() + { + if (!config.showPoints()) + { + return; + } + + if (!isOnPestControlMainIsland() && !isInPestControlInstance()) + { + infoBoxManager.removeInfoBox(pointsInfoboxCounter); + pointsInfoboxCounter = null; + + return; + } + + if (commendationPoints == null) + { + if (pointsInfoboxCounter != null) + { + infoBoxManager.removeInfoBox(pointsInfoboxCounter); + pointsInfoboxCounter = null; + } + + return; + } + + if (pointsInfoboxCounter != null) + { + pointsInfoboxCounter.setCount(commendationPoints); + } + else + { + BufferedImage image = itemManager.getImage(ItemID.VOID_SEAL1); + pointsInfoboxCounter = new PointsInfoboxCounter(image, this, commendationPoints); + pointsInfoboxCounter.setTooltip("Void Knight Commendation Points"); + infoBoxManager.addInfoBox(pointsInfoboxCounter); + } + } + + + public void getPointsFromWidgets() + { + + // Get points from dialog after the game + Widget npcDialog = client.getWidget(WidgetInfo.DIALOG_NPC_TEXT); + if (npcDialog != null) + { + String npcText = Text.sanitizeMultilineText(npcDialog.getText()); + Matcher matcher = AWARDED_PATTERN.matcher(npcText); + + if (matcher.find()) + { + int newPoints = Integer.parseInt(matcher.group(1)); + + if (commendationPoints != null) + { + newPoints += commendationPoints; + } + + setCommendationPoints(newPoints); + log.debug("PEST CONTROL [POINTS REWARDED] UPDATE: {}", commendationPoints); + return; + } + } + + // Get points from dialog after purchase + Widget pestControlDialog = client.getWidget(WidgetInfo.PEST_CONTROL_DIALOG_TEXT); + if (pestControlDialog != null) + { + String pestControlDialogText = Text.sanitizeMultilineText(pestControlDialog.getText()); + Matcher matcher = PURCHASE_PATTERN.matcher(pestControlDialogText); + + if (matcher.find()) + { + setCommendationPoints(Integer.parseInt(matcher.group(1))); + log.debug("PEST CONTROL [DIALOG] POINTS UPDATE: {}", commendationPoints); + return; + } + } + + // Get points from exchange window + Widget exchangeWindowPointsWidget = client.getWidget(WidgetInfo.PEST_CONTROL_EXCHANGE_WINDOW_POINTS); + if (exchangeWindowPointsWidget != null) + { + String pointsString = exchangeWindowPointsWidget.getText(); + + Matcher matcher = EXCHANGE_WINDOW_POINTS_PATTERN.matcher(pointsString); + if (matcher.lookingAt()) + { + setCommendationPoints(Integer.parseInt(matcher.group(1))); + log.debug("PEST CONTROL [EXCHANGE WINDOW] POINTS UPDATE: {}", commendationPoints); + return; + } + } + + // Get points in boat + // NOTE: The boat info widget is still active right after the game + // We should therefor only check for point updates if there are no dialogs + Widget boatPointsWidget = client.getWidget(WidgetInfo.PEST_CONTROL_BOAT_INFO_POINTS); + if (boatPointsWidget != null && npcDialog == null && pestControlDialog == null) + { + String pointsString = boatPointsWidget.getText(); + Matcher matcher = BOAT_POINTS_PATTERN.matcher(pointsString); + + if (matcher.lookingAt()) + { + log.debug(matcher.toString()); + log.debug("MATCHER GROUP 1: {}", matcher.group(1)); + setCommendationPoints(Integer.parseInt(matcher.group(1))); + log.debug("PEST CONTROL [BOAT] POINTS UPDATE: {}", commendationPoints); + } + } + } + + public void setCommendationPoints(int newPoints) + { + if (userConfigKey == null) + { + return; + } + + if (commendationPoints == null || commendationPoints != newPoints) + { + commendationPoints = newPoints; + + configManager.setConfiguration( + "pestcontrol", + "points." + userConfigKey, + String.valueOf(commendationPoints) + ); + } + + handlePointsInfoboxCounter(); + } + + @Subscribe + public void onGameTick(GameTick gameTickEvent) + { + // Check for widgets on main island + if (game == null && isOnPestControlMainIsland()) + { + // This must be synchronized for some reason + synchronized (this) + { + if (checkForPointWidgets) + { + checkForPointWidgets = false; + getPointsFromWidgets(); + } + } + } + + // Load the points of the user + if (userConfigKey == null) + { + String username = client.getUsername(); + + if (username == null) + { + return; + } + + userConfigKey = String.valueOf(username.hashCode()); + + log.debug("USER CONFIG SCOPE: {}", userConfigKey); + + if (loadLocalUserPoints()) + { + handlePointsInfoboxCounter(); + } + } + + // Check if the game has started + if (game == null && isInPestControlInstance()) + { + log.debug("Pest control game has started"); + game = new Game(client, this); + pointsRewarded = false; + } + + // Check if we are in a game + if (game == null) + { + return; + } + + // Check if we left the game + if (!isInPestControlInstance()) + { + if (game != null) + { + log.debug("Pest control game has ended"); + game = null; + } + + return; + } + + game.onGameTick(gameTickEvent); + } + @Subscribe public void onChatMessage(ChatMessage chatMessage) { - if (overlay.getGame() != null && chatMessage.getType() == ChatMessageType.GAMEMESSAGE) + if (game != null && chatMessage.getType() == ChatMessageType.GAMEMESSAGE) { - Matcher matcher = SHIELD_DROP.matcher(chatMessage.getMessage()); + Matcher matcher = SHIELD_DROP_PATTERN.matcher(chatMessage.getMessage()); if (matcher.lookingAt()) { - overlay.getGame().fall(matcher.group(1)); + game.lowerPortalShield(matcher.group(1)); } } } @Subscribe - public void onNpcSpawned(NpcSpawned event) + public void onWidgetLoaded(WidgetLoaded event) { - final NPC npc = event.getNpc(); - if (SPINNER_IDS.contains(npc.getId())) + if (game != null) { - spinners.add(npc); + log.debug(event.toString()); + } + + if (isOnPestControlMainIsland() && game == null) + { + checkForPointWidgets = true; + } + } + + private void unlistTileObject(TileObject tileObject) + { + int tileObjectId = tileObject.getId(); + + if (PestControlRepairObject.isRepairableBarricadeId(tileObjectId) || + PestControlRepairObject.isRepairableGateId(tileObjectId)) + { + highlightedRepairList.remove(tileObject); + return; + } + + switch (tileObjectId) + { + case NOVICE_GANGPLANK: + { + noviceGangplankTile = null; + break; + } + case INTERMEDIATE_GANGPLANK: + { + intermediateGangplankTile = null; + break; + } + case VETERAN_GANGPLANK: + { + veteranGangplankTile = null; + break; + } + } + } + + private void handleTileObject(Tile tile, TileObject tileObject) + { + int tileObjectId = tileObject.getId(); + + if (PestControlRepairObject.isRepairableBarricadeId(tileObjectId) || + PestControlRepairObject.isRepairableGateId(tileObjectId)) + { + highlightedRepairList.add(tileObject); + return; + } + + switch (tileObjectId) + { + case NOVICE_GANGPLANK: + { + noviceGangplankTile = tile; + break; + } + case INTERMEDIATE_GANGPLANK: + { + intermediateGangplankTile = tile; + break; + } + case VETERAN_GANGPLANK: + { + veteranGangplankTile = tile; + break; + } } } @Subscribe - public void onNpcDespawned(NpcDespawned event) + public void onGameObjectSpawned(GameObjectSpawned event) { - spinners.remove(event.getNpc()); + handleTileObject(event.getTile(), event.getGameObject()); + } + + @Subscribe + public void onGameObjectChanged(GameObjectChanged event) + { + unlistTileObject(event.getPrevious()); + handleTileObject(event.getTile(), event.getGameObject()); + } + + @Subscribe + public void onGameObjectDespawned(GameObjectDespawned event) + { + unlistTileObject(event.getGameObject()); + } + + @Subscribe + public void onGroundObjectSpawned(GroundObjectSpawned event) + { + handleTileObject(event.getTile(), event.getGroundObject()); + } + + @Subscribe + public void onGroundObjectChanged(GroundObjectChanged event) + { + unlistTileObject(event.getPrevious()); + handleTileObject(event.getTile(), event.getGroundObject()); + } + + @Subscribe + public void onGroundObjectDespawned(GroundObjectDespawned event) + { + unlistTileObject(event.getGroundObject()); + } + + public boolean isInPestControlInstance() + { + return client.getWidget(WidgetInfo.PEST_CONTROL_BLUE_SHIELD) != null; + } + + public boolean isOnPestControlMainIsland() + { + return client.getLocalPlayer().getWorldLocation().getRegionID() == 10537; } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/PestControlRepairObject.java b/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/PestControlRepairObject.java new file mode 100644 index 0000000000..f3a8d457dd --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/PestControlRepairObject.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2019, Yani + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.pestcontrol; + +import com.google.common.collect.ImmutableSet; +import java.util.Set; +import lombok.Getter; +import net.runelite.api.ObjectID; + +@Getter +public class PestControlRepairObject +{ + @Getter + private static final Set repairableBarricades = ImmutableSet.of( + //ObjectID.BARRICADE_14224, + ObjectID.BARRICADE_14227, + ObjectID.BARRICADE_14228, + ObjectID.BARRICADE_14229, + ObjectID.BARRICADE_14230, + ObjectID.BARRICADE_14231, + ObjectID.BARRICADE_14232 + ); + + @Getter + private static final Set repairableGates = ImmutableSet.of( + ObjectID.GATE_14238, + ObjectID.GATE_14239, + ObjectID.GATE_14240, + ObjectID.GATE_14241, + ObjectID.GATE_14242, + ObjectID.GATE_14243, + ObjectID.GATE_14244, + ObjectID.GATE_14245, + ObjectID.GATE_14246, + ObjectID.GATE_14247, + ObjectID.GATE_14248 + ); + + public static boolean isRepairableBarricadeId(int objectId) + { + return repairableBarricades.contains(objectId); + } + + public static boolean isRepairableGateId(int objectId) + { + return repairableGates.contains(objectId); + } + + public static boolean isRepairableId(int objectId) + { + return isRepairableBarricadeId(objectId) || isRepairableGateId(objectId); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/PointsInfoboxCounter.java b/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/PointsInfoboxCounter.java new file mode 100644 index 0000000000..1fa3155625 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/PointsInfoboxCounter.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2019, Yani + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.pestcontrol; + +import java.awt.image.BufferedImage; +import net.runelite.client.ui.overlay.infobox.Counter; + +public class PointsInfoboxCounter extends Counter +{ + private final PestControlPlugin plugin; + + PointsInfoboxCounter(BufferedImage image, PestControlPlugin plugin, int count) + { + super(image, plugin, count); + this.plugin = plugin; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/Portal.java b/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/Portal.java index 2807ff9fcb..2ad85a24ea 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/Portal.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/Portal.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2017, Adam + * Copyright (c) 2019, Yani * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -23,22 +24,39 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package net.runelite.client.plugins.pestcontrol; -import lombok.AllArgsConstructor; + import lombok.Getter; -import lombok.ToString; -import net.runelite.api.widgets.WidgetInfo; +import lombok.Setter; +import net.runelite.api.coords.WorldPoint; -@AllArgsConstructor @Getter -@ToString -enum Portal +@Setter +class Portal { - PURPLE(WidgetInfo.PEST_CONTROL_PURPLE_SHIELD, WidgetInfo.PEST_CONTROL_PURPLE_HEALTH, WidgetInfo.PEST_CONTROL_PURPLE_ICON), - BLUE(WidgetInfo.PEST_CONTROL_BLUE_SHIELD, WidgetInfo.PEST_CONTROL_BLUE_HEALTH, WidgetInfo.PEST_CONTROL_BLUE_ICON), - YELLOW(WidgetInfo.PEST_CONTROL_YELLOW_SHIELD, WidgetInfo.PEST_CONTROL_YELLOW_HEALTH, WidgetInfo.PEST_CONTROL_YELLOW_ICON), - RED(WidgetInfo.PEST_CONTROL_RED_SHIELD, WidgetInfo.PEST_CONTROL_RED_HEALTH, WidgetInfo.PEST_CONTROL_RED_ICON); + private PortalColor color; + private WidgetPortal widget; + private WorldPoint location; - private final WidgetInfo shield; - private final WidgetInfo hitpoints; - private final WidgetInfo icon; + private PortalState portalState = PortalState.SHIELDED; + + public Portal(PortalColor color, WidgetPortal widget) + { + this.color = color; + this.widget = widget; + } + + public boolean isShielded() + { + return portalState == PortalState.SHIELDED; + } + + public boolean isDead() + { + return portalState == PortalState.DEAD; + } + + public boolean isActive() + { + return (!isShielded() && !isDead()); + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/PortalContext.java b/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/PortalColor.java similarity index 84% rename from runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/PortalContext.java rename to runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/PortalColor.java index 6ad91ea6e3..07e7336c0b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/PortalContext.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/PortalColor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Adam + * Copyright (c) 2019, Yani * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -25,15 +25,12 @@ package net.runelite.client.plugins.pestcontrol; import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.Setter; -@RequiredArgsConstructor @Getter -@Setter -class PortalContext +public enum PortalColor { - private final Portal portal; - private boolean isShielded = true; - private boolean isDead; + BLUE, + PURPLE, + YELLOW, + RED } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/Rotation.java b/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/PortalRotation.java similarity index 72% rename from runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/Rotation.java rename to runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/PortalRotation.java index e761edf383..b49d536bb8 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/Rotation.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/PortalRotation.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2017, Adam + * Copyright (c) 2019, Yani * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,12 +25,12 @@ */ package net.runelite.client.plugins.pestcontrol; -import static net.runelite.client.plugins.pestcontrol.Portal.BLUE; -import static net.runelite.client.plugins.pestcontrol.Portal.PURPLE; -import static net.runelite.client.plugins.pestcontrol.Portal.RED; -import static net.runelite.client.plugins.pestcontrol.Portal.YELLOW; +import static net.runelite.client.plugins.pestcontrol.PortalColor.BLUE; +import static net.runelite.client.plugins.pestcontrol.PortalColor.PURPLE; +import static net.runelite.client.plugins.pestcontrol.PortalColor.RED; +import static net.runelite.client.plugins.pestcontrol.PortalColor.YELLOW; -enum Rotation +enum PortalRotation { PBYR(PURPLE, BLUE, YELLOW, RED), PYBR(PURPLE, YELLOW, BLUE, RED), @@ -38,23 +39,23 @@ enum Rotation YRPB(YELLOW, RED, PURPLE, BLUE), YPRB(YELLOW, PURPLE, RED, BLUE); - private final Portal[] portals; + private final PortalColor[] portals; - Rotation(Portal first, Portal second, Portal third, Portal fourth) + PortalRotation(PortalColor first, PortalColor second, PortalColor third, PortalColor fourth) { - portals = new Portal[] - { - first, second, third, fourth - }; + portals = new PortalColor[] + { + first, second, third, fourth + }; } - public Portal getPortal(int index) + public Portal getPortal(Game game, int index) { if (index < 0 || index >= portals.length) { return null; } - return portals[index]; + return game.getPortal(portals[index]); } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/PortalState.java b/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/PortalState.java new file mode 100644 index 0000000000..0a569d54b3 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/PortalState.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2019, Yani + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.pestcontrol; + +public enum PortalState +{ + ACTIVE, + SHIELDED, + DEAD +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/PortalWeaknessOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/PortalWeaknessOverlay.java new file mode 100644 index 0000000000..ff0deee9c2 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/PortalWeaknessOverlay.java @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2019, Yani + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.pestcontrol; + +import com.google.inject.Inject; +import java.awt.AlphaComposite; +import java.awt.Composite; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.Client; +import net.runelite.api.ItemID; +import net.runelite.api.Perspective; +import net.runelite.api.Point; +import net.runelite.api.Skill; +import net.runelite.api.coords.LocalPoint; +import net.runelite.api.coords.WorldPoint; +import net.runelite.client.game.ItemManager; +import net.runelite.client.game.SkillIconManager; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayLayer; +import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.OverlayUtil; + +@Slf4j +public class PortalWeaknessOverlay extends Overlay +{ + private int zOffset = 100; + + private final PestControlConfig config; + private final PestControlPlugin plugin; + private final Client client; + + private BufferedImage magicImage; + private BufferedImage rangedImage; + private BufferedImage stabImage; + private BufferedImage slashImage; + private BufferedImage crushImage; + + @Inject + PortalWeaknessOverlay( + PestControlConfig config, + PestControlPlugin plugin, + Client client, + ItemManager itemManager, + SkillIconManager skillIconManager + ) + { + this.config = config; + this.plugin = plugin; + this.client = client; + + this.magicImage = skillIconManager.getSkillImage(Skill.MAGIC); + this.rangedImage = skillIconManager.getSkillImage(Skill.RANGED); + + this.stabImage = itemManager.getImage(ItemID.WHITE_DAGGER); + this.slashImage = itemManager.getImage(ItemID.WHITE_SCIMITAR); + this.crushImage = itemManager.getImage(ItemID.WHITE_WARHAMMER); + + setPosition(OverlayPosition.DYNAMIC); + setLayer(OverlayLayer.UNDER_WIDGETS); + } + + private Point getPortalPoint(Portal portal) + { + WorldPoint portalLocation = portal.getLocation(); + LocalPoint localLocation = LocalPoint.fromWorld(client, portalLocation); + + if (localLocation == null) + { + return null; + } + + // We can use any image here as it's only needed to calculate the position + Point imageLocation = Perspective.getCanvasImageLocation(client, localLocation, magicImage, zOffset); + + if (imageLocation != null) + { + return imageLocation; + } + + return null; + } + + private void renderPortalWeakness(Graphics2D graphics, Portal portal, BufferedImage image) + { + Point portalPoint = getPortalPoint(portal); + + if (portalPoint != null) + { + Composite originalComposite = graphics.getComposite(); + Composite translucentComposite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f); + graphics.setComposite(translucentComposite); + + OverlayUtil.renderImageLocation(graphics, portalPoint, image); + + graphics.setComposite(originalComposite); + } + } + + private void renderDoublePortalWeakness( + Graphics2D graphics, + Portal portal, + BufferedImage imageLeft, + BufferedImage imageRight + ) + { + Point portalPoint = getPortalPoint(portal); + + if (portalPoint != null) + { + Point portalLeft = new Point( + portalPoint.getX() - (imageLeft.getWidth() / 2) - 5, + portalPoint.getY() + ); + + Point portalRight = new Point( + portalPoint.getX() + (imageRight.getWidth() / 2) + 5, + portalPoint.getY() + ); + + Composite originalComposite = graphics.getComposite(); + Composite translucentComposite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.6f); + graphics.setComposite(translucentComposite); + + OverlayUtil.renderImageLocation(graphics, portalLeft, imageLeft); + OverlayUtil.renderImageLocation(graphics, portalPoint, imageRight); + + graphics.setComposite(originalComposite); + } + } + + @Override + public Dimension render(Graphics2D graphics) + { + Game game = plugin.getGame(); + + if (game == null) + { + return null; + } + + for (Portal portal : game.getPortals()) + { + if (!portal.isDead()) + { + switch (portal.getColor()) + { + case BLUE: + { + renderPortalWeakness(graphics, portal, magicImage); + break; + } + case YELLOW: + { + renderDoublePortalWeakness(graphics, portal, stabImage, slashImage); + break; + } + case RED: + { + renderPortalWeakness(graphics, portal, crushImage); + break; + } + case PURPLE: + { + renderPortalWeakness(graphics, portal, rangedImage); + break; + } + } + } + } + + return null; + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/RepairOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/RepairOverlay.java new file mode 100644 index 0000000000..a0ee3bf09f --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/RepairOverlay.java @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2019, Yani + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.pestcontrol; + +import com.google.inject.Inject; +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.geom.Area; +import net.runelite.api.Client; +import net.runelite.api.Constants; +import net.runelite.api.GameObject; +import net.runelite.api.GroundObject; +import net.runelite.api.Player; +import net.runelite.api.Point; +import net.runelite.api.Scene; +import net.runelite.api.Tile; +import net.runelite.api.WallObject; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayLayer; +import net.runelite.client.ui.overlay.OverlayPosition; + +public class RepairOverlay extends Overlay +{ + private final PestControlConfig config; + private final PestControlPlugin plugin; + private final Client client; + + private static final int MAX_DISTANCE = 2400; + + @Inject + RepairOverlay(PestControlConfig config, PestControlPlugin plugin, Client client) + { + this.config = config; + this.plugin = plugin; + this.client = client; + + setPosition(OverlayPosition.DYNAMIC); + setLayer(OverlayLayer.ABOVE_SCENE); + } + + @Override + public Dimension render(Graphics2D graphics) + { + if (plugin.getGame() == null) + { + return null; + } + + Point mousePosition = client.getMouseCanvasPosition(); + Scene scene = client.getScene(); + Color color = config.repairableColor(); + Tile[][][] tiles = scene.getTiles(); + int z = client.getPlane(); + + for (int x = 0; x < Constants.SCENE_SIZE; ++x) + { + for (int y = 0; y < Constants.SCENE_SIZE; ++y) + { + Tile tile = tiles[z][x][y]; + + if (tile == null) + { + continue; + } + + Player player = client.getLocalPlayer(); + if (player == null) + { + continue; + } + + // Render GameObjects + GameObject[] gameObjects = tile.getGameObjects(); + if (gameObjects != null) + { + for (GameObject gameObject : gameObjects) + { + if (gameObject == null) + { + continue; + } + + if (PestControlRepairObject.isRepairableId(gameObject.getId())) + { + + if (player.getLocalLocation().distanceTo(gameObject.getLocalLocation()) <= MAX_DISTANCE) + { + renderObjectOverlay(graphics, gameObject.getClickbox(), color, mousePosition); + } + } + } + } + + // Render GameObject + GroundObject groundObject = tile.getGroundObject(); + if (groundObject != null) + { + if (groundObject == null) + { + continue; + } + + if (PestControlRepairObject.isRepairableId(groundObject.getId())) + { + + if (player.getLocalLocation().distanceTo(groundObject.getLocalLocation()) <= MAX_DISTANCE) + { + renderObjectOverlay(graphics, groundObject.getClickbox(), color, mousePosition); + } + } + } + + // Render WallObject + WallObject wallObject = tile.getWallObject(); + if (wallObject != null) + { + if (wallObject == null) + { + continue; + } + + if (PestControlRepairObject.isRepairableId(wallObject.getId())) + { + + if (player.getLocalLocation().distanceTo(wallObject.getLocalLocation()) <= MAX_DISTANCE) + { + renderObjectOverlay(graphics, wallObject.getClickbox(), color, mousePosition); + } + } + } + } + } + + + + + + + + /*if(plugin.getGame() == null) + { + return null; + } + + Point mousePosition = client.getMouseCanvasPosition(); + + List repairList = plugin.getHighlightedRepairList(); + + for(TileObject tileObject: repairList) + { + //tileObject.getWorldLocation().distanceTo(client.getLocalPlayer().getWorldLocation()); + + Polygon polygon = tileObject.getCanvasTilePoly(); + + if(polygon != null) + { + graphics.setColor(color); + graphics.setStroke(new BasicStroke(2)); + graphics.drawPolygon(polygon); + graphics.setColor(setColorAlpha(color, 40)); + graphics.fill(polygon); + + if(polygon.contains(mousePosition.getX(), mousePosition.getY())) + { + graphics.setColor(setColorAlpha(color, 65)); + graphics.fill(polygon); + } + } + } +*/ + return null; + } + + private void renderObjectOverlay(Graphics2D graphics, Area area, Color color, Point mousePosition) + { + if (area == null) + { + return; + } + + graphics.setColor(color); + graphics.setStroke(new BasicStroke(2)); + graphics.draw(area); + graphics.setColor(setColorAlpha(color, 50)); + graphics.fill(area); + + if (area.contains(mousePosition.getX(), mousePosition.getY())) + { + graphics.setColor(setColorAlpha(color, 60)); + graphics.fill(area); + } + } + + private Color setColorAlpha(Color color, int alpha) + { + return new Color( + color.getRed(), + color.getGreen(), + color.getBlue(), + alpha + ); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/TimerOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/TimerOverlay.java new file mode 100644 index 0000000000..be45fc728e --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/TimerOverlay.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2019, Yani + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.pestcontrol; + +import com.google.inject.Inject; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.time.Duration; +import net.runelite.api.Client; +import net.runelite.api.Point; +import net.runelite.api.widgets.Widget; +import net.runelite.api.widgets.WidgetInfo; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayLayer; +import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.OverlayUtil; + +public class TimerOverlay extends Overlay +{ + private final PestControlConfig config; + private final PestControlPlugin plugin; + private final Client client; + + @Inject + TimerOverlay(PestControlConfig config, PestControlPlugin plugin, Client client) + { + this.config = config; + this.plugin = plugin; + this.client = client; + + setPosition(OverlayPosition.DYNAMIC); + setLayer(OverlayLayer.ABOVE_SCENE); + } + + @Override + public Dimension render(Graphics2D graphics) + { + if (plugin.getGame() == null) + { + return null; + } + + Widget timeWidget = client.getWidget(WidgetInfo.PEST_CONTROL_INFO_TIME); + + if (timeWidget == null) + { + return null; + } + + int x = timeWidget.getCanvasLocation().getX() + 38; + int y = timeWidget.getCanvasLocation().getY() + 11; + + Duration timeTillNextPortal = plugin.getGame().getTimeTillNextPortal(); + + if (timeTillNextPortal != null) + { + String firstOrNext = (plugin.getGame().getShieldsDropped() == 0) ? "first" : "next"; + String string = String.format("- %s portal: %ds", firstOrNext, timeTillNextPortal.getSeconds()); + + OverlayUtil.renderTextLocation(graphics, new Point(x, y), string, new Color(204, 204, 204)); + } + + return null; + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/PestControlOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/WidgetOverlay.java similarity index 62% rename from runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/PestControlOverlay.java rename to runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/WidgetOverlay.java index d8126c966e..4633128d6d 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/PestControlOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/WidgetOverlay.java @@ -1,6 +1,7 @@ /* * Copyright (c) 2017, Kronos * Copyright (c) 2017, Adam + * Copyright (c) 2019, Yani * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,151 +32,107 @@ import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import javax.inject.Inject; -import lombok.AccessLevel; -import lombok.Getter; import lombok.extern.slf4j.Slf4j; import net.runelite.api.Client; -import net.runelite.api.NPC; import net.runelite.api.widgets.Widget; import net.runelite.api.widgets.WidgetInfo; -import static net.runelite.client.plugins.pestcontrol.Portal.BLUE; -import static net.runelite.client.plugins.pestcontrol.Portal.PURPLE; -import static net.runelite.client.plugins.pestcontrol.Portal.RED; -import static net.runelite.client.plugins.pestcontrol.Portal.YELLOW; import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayLayer; import net.runelite.client.ui.overlay.OverlayPosition; -import net.runelite.client.ui.overlay.OverlayUtil; @Slf4j -public class PestControlOverlay extends Overlay +public class WidgetOverlay extends Overlay { - private final PestControlPlugin plugin; private final Client client; - // Pest control game - @Getter(AccessLevel.PACKAGE) - private Game game; + private final PestControlPlugin plugin; @Inject - public PestControlOverlay(PestControlPlugin plugin, Client client) + public WidgetOverlay(Client client, PestControlPlugin plugin) { - setPosition(OverlayPosition.DYNAMIC); this.plugin = plugin; this.client = client; + + setPosition(OverlayPosition.DYNAMIC); + setLayer(OverlayLayer.ABOVE_SCENE); + } + + public Integer getPortalHitpoints(PortalColor color) + { + if (plugin.getGame() == null) + { + return null; + } + + WidgetInfo healthWidgetInfo = null; + + switch (color) + { + case RED: + { + healthWidgetInfo = WidgetPortal.RED.getHitpoints(); + break; + } + case BLUE: + { + healthWidgetInfo = WidgetPortal.BLUE.getHitpoints(); + break; + } + case PURPLE: + { + healthWidgetInfo = WidgetPortal.PURPLE.getHitpoints(); + break; + } + case YELLOW: + { + healthWidgetInfo = WidgetPortal.YELLOW.getHitpoints(); + break; + } + } + + if (healthWidgetInfo == null) + { + return null; + } + + Widget healthWidget = client.getWidget(healthWidgetInfo); + + if (healthWidget == null) + { + return null; + } + + return Integer.parseInt(healthWidget.getText().trim()); } @Override public Dimension render(Graphics2D graphics) { - // See if we are in a game or not - if (client.getWidget(WidgetInfo.PEST_CONTROL_BLUE_SHIELD) == null) + if (plugin.getGame() == null) { - if (game != null) - { - log.debug("Pest control game has ended"); - game = null; - } - return null; } - if (game == null) - { - log.debug("Pest control game has started"); - game = new Game(); - } - - renderSpinners(graphics); - renderPortalWidgets(graphics); - - return null; - } - - private void renderSpinners(Graphics2D graphics) - { - for (NPC npc : plugin.getSpinners()) - { - OverlayUtil.renderActorOverlay(graphics, npc, npc.getName(), Color.CYAN); - } - } - - private void renderPortalWidgets(Graphics2D graphics) - { - PortalContext purple = game.getPurple(); - PortalContext blue = game.getBlue(); - PortalContext yellow = game.getYellow(); - PortalContext red = game.getRed(); - - Widget purpleHealth = client.getWidget(PURPLE.getHitpoints()); - Widget blueHealth = client.getWidget(BLUE.getHitpoints()); - Widget yellowHealth = client.getWidget(YELLOW.getHitpoints()); - Widget redHealth = client.getWidget(RED.getHitpoints()); - - // Check for dead portals - if (isZero(purpleHealth)) - { - game.die(purple); - } - if (isZero(blueHealth)) - { - game.die(blue); - } - if (isZero(yellowHealth)) - { - game.die(yellow); - } - if (isZero(redHealth)) - { - game.die(red); - } - - // display "ATK" overlay on recorded portals without shields - renderAttack(graphics, purple); - renderAttack(graphics, blue); - renderAttack(graphics, yellow); - renderAttack(graphics, red); - - // display "NEXT" overlay on predicted portals - for (Portal portal : game.getNextPortals()) + for (Portal portal : plugin.getGame().getNextPortals()) { renderWidgetOverlay(graphics, portal, "NEXT", Color.ORANGE); } - renderProgressWidget(graphics); - } - - private void renderProgressWidget(Graphics2D graphics) - { - Widget bar = client.getWidget(WidgetInfo.PEST_CONTROL_ACTIVITY_BAR).getChild(0); - Rectangle2D bounds = bar.getBounds().getBounds2D(); - - Widget prgs = client.getWidget(WidgetInfo.PEST_CONTROL_ACTIVITY_PROGRESS).getChild(0); - int perc = (int) ((prgs.getBounds().getWidth() / bounds.getWidth()) * 100); - - Color color = Color.GREEN; - if (perc < 25) + for (Portal portal : plugin.getGame().getActivePortals()) { - color = Color.RED; + renderWidgetOverlay(graphics, portal, "ATT", Color.RED); } - String text = String.valueOf(perc) + "%"; + renderProgressWidget(graphics); - FontMetrics fm = graphics.getFontMetrics(); - Rectangle2D textBounds = fm.getStringBounds(text, graphics); - int x = (int) (bounds.getX() - textBounds.getWidth()); - int y = (int) (bounds.getY() + fm.getHeight() - 2); - - graphics.setColor(Color.BLACK); - graphics.drawString(text, x + 1, y + 1); - graphics.setColor(color); - graphics.drawString(text, x, y); + return null; } private void renderWidgetOverlay(Graphics2D graphics, Portal portal, String text, Color color) { - Widget shield = client.getWidget(portal.getShield()); - Widget icon = client.getWidget(portal.getIcon()); - Widget hp = client.getWidget(portal.getHitpoints()); + Widget shield = client.getWidget(portal.getWidget().getShield()); + Widget icon = client.getWidget(portal.getWidget().getIcon()); + Widget hp = client.getWidget(portal.getWidget().getHitpoints()); Widget bar = client.getWidget(WidgetInfo.PEST_CONTROL_ACTIVITY_BAR).getChild(0); @@ -199,6 +156,45 @@ public class PestControlOverlay extends Overlay graphics.drawString(text, x, y + 4); } + private void renderProgressWidget(Graphics2D graphics) + { + String text; + int percentage; + + Widget bar = client.getWidget(WidgetInfo.PEST_CONTROL_ACTIVITY_BAR).getChild(0); + Rectangle2D bounds = bar.getBounds().getBounds2D(); + + Widget prgs = client.getWidget(WidgetInfo.PEST_CONTROL_ACTIVITY_PROGRESS).getChild(0); + + // At 0% the inner widget changes and your progress will not increase anymore + if ((int) (prgs.getBounds().getX()) - bounds.getX() != 2) + { + percentage = 0; + text = "FAILED"; + } + else + { + percentage = (int) ((prgs.getBounds().getWidth() / bounds.getWidth()) * 100); + text = String.valueOf(percentage) + "%"; + } + + Color color = Color.GREEN; + if (percentage < 25) + { + color = Color.RED; + } + + FontMetrics fm = graphics.getFontMetrics(); + Rectangle2D textBounds = fm.getStringBounds(text, graphics); + int x = (int) (bounds.getX() - textBounds.getWidth() - 4); + int y = (int) (bounds.getY() + fm.getHeight() - 2); + + graphics.setColor(Color.BLACK); + graphics.drawString(text, x + 1, y + 1); + graphics.setColor(color); + graphics.drawString(text, x, y); + } + private static Rectangle2D union(Rectangle2D src1, Rectangle2D src2) { double x1 = Math.min(src1.getMinX(), src2.getMinX()); @@ -211,19 +207,4 @@ public class PestControlOverlay extends Overlay return result; } - - private void renderAttack(Graphics2D graphics, PortalContext portal) - { - if (portal.isShielded() || portal.isDead()) - { - return; - } - - renderWidgetOverlay(graphics, portal.getPortal(), "ATK", Color.RED); - } - - private static boolean isZero(Widget widget) - { - return widget.getText().trim().equals("0"); - } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/WidgetPortal.java b/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/WidgetPortal.java new file mode 100644 index 0000000000..7570cfb87f --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/WidgetPortal.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2017, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.pestcontrol; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.ToString; +import net.runelite.api.widgets.WidgetInfo; + +@AllArgsConstructor +@Getter +@ToString +enum WidgetPortal +{ + PURPLE(WidgetInfo.PEST_CONTROL_PURPLE_SHIELD, WidgetInfo.PEST_CONTROL_PURPLE_HEALTH, WidgetInfo.PEST_CONTROL_PURPLE_ICON), + BLUE(WidgetInfo.PEST_CONTROL_BLUE_SHIELD, WidgetInfo.PEST_CONTROL_BLUE_HEALTH, WidgetInfo.PEST_CONTROL_BLUE_ICON), + YELLOW(WidgetInfo.PEST_CONTROL_YELLOW_SHIELD, WidgetInfo.PEST_CONTROL_YELLOW_HEALTH, WidgetInfo.PEST_CONTROL_YELLOW_ICON), + RED(WidgetInfo.PEST_CONTROL_RED_SHIELD, WidgetInfo.PEST_CONTROL_RED_HEALTH, WidgetInfo.PEST_CONTROL_RED_ICON); + + private final WidgetInfo shield; + private final WidgetInfo hitpoints; + private final WidgetInfo icon; +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/config/HighlightPortalOption.java b/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/config/HighlightPortalOption.java new file mode 100644 index 0000000000..8e169edde9 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/config/HighlightPortalOption.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2019, Yani + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.pestcontrol.config; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum HighlightPortalOption +{ + OFF("Off"), + ACTIVE("Active"), + SHIELDED("Shielded"), + ALL("All"); + + private final String option; + + @Override + public String toString() + { + return option; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/config/NpcHighlightStyle.java b/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/config/NpcHighlightStyle.java new file mode 100644 index 0000000000..af60c7b789 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/config/NpcHighlightStyle.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2019, Yani + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.pestcontrol.config; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum NpcHighlightStyle +{ + OFF("Off"), + TILE("Tile"), + HULL("Hull"), + BOTH("Hull + Tile"); + + private final String style; + + @Override + public String toString() + { + return style; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsPlugin.java index d2b39880ec..4e1689eaee 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsPlugin.java @@ -59,6 +59,9 @@ import net.runelite.api.Varbits; import net.runelite.api.events.ChatMessage; import net.runelite.api.events.ConfigChanged; import net.runelite.api.events.VarbitChanged; +import net.runelite.api.events.WidgetHiddenChanged; +import net.runelite.api.widgets.Widget; +import net.runelite.api.widgets.WidgetInfo; import net.runelite.client.callback.ClientThread; import net.runelite.client.chat.ChatColorType; import net.runelite.client.chat.ChatMessageBuilder; @@ -126,6 +129,9 @@ public class RaidsPlugin extends Plugin @Inject private RaidsOverlay overlay; + @Inject + private RaidsPointsOverlay pointsOverlay; + @Inject private LayoutSolver layoutSolver; @@ -177,6 +183,7 @@ public class RaidsPlugin extends Plugin protected void startUp() throws Exception { overlayManager.add(overlay); + overlayManager.add(pointsOverlay); updateLists(); clientThread.invokeLater(() -> checkRaidPresence(true)); } @@ -185,10 +192,17 @@ public class RaidsPlugin extends Plugin protected void shutDown() throws Exception { overlayManager.remove(overlay); + overlayManager.remove(pointsOverlay); infoBoxManager.removeInfoBox(timer); inRaidChambers = false; raid = null; timer = null; + + final Widget widget = client.getWidget(WidgetInfo.RAIDS_POINTS_INFOBOX); + if (widget != null) + { + widget.setHidden(false); + } } @Subscribe @@ -209,6 +223,22 @@ public class RaidsPlugin extends Plugin clientThread.invokeLater(() -> checkRaidPresence(true)); } + @Subscribe + public void onWidgetHiddenChanged(WidgetHiddenChanged event) + { + if (!inRaidChambers || event.isHidden()) + { + return; + } + + Widget widget = event.getWidget(); + + if (widget == client.getWidget(WidgetInfo.RAIDS_POINTS_INFOBOX)) + { + widget.setHidden(true); + } + } + @Subscribe public void onVarbitChanged(VarbitChanged event) { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsPointsOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsPointsOverlay.java new file mode 100644 index 0000000000..3c2cc3e5f1 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsPointsOverlay.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2018, Kamiel + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.raids; + +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.text.NumberFormat; +import java.util.Locale; +import javax.inject.Inject; +import net.runelite.api.Client; +import static net.runelite.api.MenuAction.RUNELITE_OVERLAY_CONFIG; +import net.runelite.api.Varbits; +import static net.runelite.client.plugins.raids.RaidsPlugin.POINTS_FORMAT; +import net.runelite.client.ui.overlay.Overlay; +import static net.runelite.client.ui.overlay.OverlayManager.OPTION_CONFIGURE; +import net.runelite.client.ui.overlay.OverlayMenuEntry; +import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.OverlayPriority; +import net.runelite.client.ui.overlay.components.LineComponent; +import net.runelite.client.ui.overlay.components.PanelComponent; + +public class RaidsPointsOverlay extends Overlay +{ + @Inject + private Client client; + + @Inject + private RaidsPlugin plugin; + + private final PanelComponent panel = new PanelComponent(); + + private static final NumberFormat UNIQUE_FORMAT = NumberFormat.getPercentInstance(Locale.ENGLISH); + static + { + UNIQUE_FORMAT.setMaximumFractionDigits(2); + UNIQUE_FORMAT.setMinimumFractionDigits(2); + } + + @Inject + private RaidsPointsOverlay(RaidsPlugin plugin) + { + super(plugin); + setPosition(OverlayPosition.TOP_RIGHT); + setPriority(OverlayPriority.HIGH); + getMenuEntries().add(new OverlayMenuEntry(RUNELITE_OVERLAY_CONFIG, OPTION_CONFIGURE, "Raids overlay")); + } + + @Override + public Dimension render(Graphics2D graphics) + { + if (!plugin.isInRaidChambers()) + { + return null; + } + + int totalPoints = client.getVar(Varbits.TOTAL_POINTS); + int personalPoints = client.getVar(Varbits.PERSONAL_POINTS); + int partySize = client.getVar(Varbits.RAID_PARTY_SIZE); + double uniqueChance = totalPoints / 867500f; + + panel.getChildren().clear(); + panel.getChildren().add(LineComponent.builder() + .left("Total:") + .right(POINTS_FORMAT.format(totalPoints)) + .build()); + + panel.getChildren().add(LineComponent.builder() + .left(client.getLocalPlayer().getName() + ":") + .right(POINTS_FORMAT.format(personalPoints)) + .build()); + + + if (partySize > 1) + { + panel.getChildren().add(LineComponent.builder() + .left("Party size:") + .right(String.valueOf(partySize)) + .build()); + } + + panel.getChildren().add(LineComponent.builder() + .left("Unique:") + .right(UNIQUE_FORMAT.format(uniqueChance)) + .build()); + + if (partySize > 1) + { + double personalChance = uniqueChance * (personalPoints / totalPoints); + + panel.getChildren().add(LineComponent.builder() + .left("Personal:") + .right(UNIQUE_FORMAT.format(personalChance)) + .build()); + } + + return panel.render(graphics); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/suppliestracker/back_icon.png b/runelite-client/src/main/resources/net/runelite/client/plugins/suppliestracker/back_icon.png new file mode 100644 index 0000000000..96b9200f60 Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/suppliestracker/back_icon.png differ diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/suppliestracker/panel_icon.png b/runelite-client/src/main/resources/net/runelite/client/plugins/suppliestracker/panel_icon.png new file mode 100644 index 0000000000..833bbc37c4 Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/suppliestracker/panel_icon.png differ diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/suppliestracker/plus_icon.png b/runelite-client/src/main/resources/net/runelite/client/plugins/suppliestracker/plus_icon.png new file mode 100644 index 0000000000..9634216e10 Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/suppliestracker/plus_icon.png differ diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/suppliestracker/sarabrew.jpg b/runelite-client/src/main/resources/net/runelite/client/plugins/suppliestracker/sarabrew.jpg new file mode 100644 index 0000000000..11cf0728cb Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/suppliestracker/sarabrew.jpg differ diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/maxhit/calculators/MaxHitCalculatorTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/maxhit/calculators/MaxHitCalculatorTest.java new file mode 100644 index 0000000000..8c28256b89 --- /dev/null +++ b/runelite-client/src/test/java/net/runelite/client/plugins/maxhit/calculators/MaxHitCalculatorTest.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2019, Bartvollebregt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.maxhit.calculators; + +import com.google.inject.Guice; +import com.google.inject.testing.fieldbinder.Bind; +import com.google.inject.testing.fieldbinder.BoundFieldModule; +import net.runelite.api.Client; +import net.runelite.client.plugins.maxhit.calculators.testconfig.MagicMaxHitConfig; +import net.runelite.client.plugins.maxhit.calculators.testconfig.MaxHitConfig; +import net.runelite.client.plugins.maxhit.calculators.testconfig.MeleeMaxHitConfig; +import net.runelite.client.plugins.maxhit.calculators.testconfig.RangeMaxHitConfig; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class MaxHitCalculatorTest +{ + @Mock + @Bind + protected Client client; + + @Before + public void setUp() + { + Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this); + } + + @Test + public void calculate() + { + testMaxHitConfig(MeleeMaxHitConfig.values()); + testMaxHitConfig(RangeMaxHitConfig.values()); + testMaxHitConfig(MagicMaxHitConfig.values()); + } + + private void testMaxHitConfig(MaxHitConfig[] maxHitConfigs) + { + for (MaxHitConfig maxHitConfig : maxHitConfigs) + { + maxHitConfig.test(client); + } + } +} diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/maxhit/calculators/testconfig/MagicMaxHitConfig.java b/runelite-client/src/test/java/net/runelite/client/plugins/maxhit/calculators/testconfig/MagicMaxHitConfig.java new file mode 100644 index 0000000000..452aa484fc --- /dev/null +++ b/runelite-client/src/test/java/net/runelite/client/plugins/maxhit/calculators/testconfig/MagicMaxHitConfig.java @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2019, Bartvollebregt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.maxhit.calculators.testconfig; + +import net.runelite.api.*; +import net.runelite.client.plugins.maxhit.calculators.MagicMaxHitCalculator; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public enum MagicMaxHitConfig implements MaxHitConfig +{ + + TRIDENT_SLAYER(new int[] {75, 83, 99}, 0, new Item[] + { + mockItem(ItemID.SLAYER_HELMET_I), + mockItem(ItemID.SARADOMIN_CAPE), + mockItem(ItemID.OCCULT_NECKLACE), + mockItem(ItemID.TRIDENT_OF_THE_SEAS), + mockItem(ItemID.MYSTIC_ROBE_TOP), + mockItem(ItemID.BROODOO_SHIELD), + null, + mockItem(ItemID.MYSTIC_ROBE_BOTTOM), + null, + mockItem(ItemID.MYSTIC_GLOVES), + mockItem(ItemID.WIZARD_BOOTS), + mockItem(ItemID.RING_OF_WEALTH) + }, new int[] {25, 27, 34}), + + TRIDENT_OF_SEAS(new int[] {75, 83, 99}, 0, new Item[] + { + mockItem(ItemID.MYSTIC_HAT), + mockItem(ItemID.SARADOMIN_CAPE), + mockItem(ItemID.AMULET_OF_GLORY), + mockItem(ItemID.TRIDENT_OF_THE_SEAS), + mockItem(ItemID.MYSTIC_ROBE_TOP), + mockItem(ItemID.BROODOO_SHIELD), + null, + mockItem(ItemID.MYSTIC_ROBE_BOTTOM), + null, + mockItem(ItemID.MYSTIC_GLOVES), + mockItem(ItemID.WIZARD_BOOTS), + mockItem(ItemID.RING_OF_WEALTH) + }, new int[] {20, 22, 28}), + + TRIDENT_OF_SWAMP(new int[] {75, 83, 99}, 0, new Item[] + { + mockItem(ItemID.MYSTIC_HAT), + mockItem(ItemID.SARADOMIN_CAPE), + mockItem(ItemID.AMULET_OF_GLORY), + mockItem(ItemID.TRIDENT_OF_THE_SWAMP), + mockItem(ItemID.MYSTIC_ROBE_TOP), + mockItem(ItemID.BROODOO_SHIELD), + null, + mockItem(ItemID.MYSTIC_ROBE_BOTTOM), + null, + mockItem(ItemID.MYSTIC_GLOVES), + mockItem(ItemID.WIZARD_BOOTS), + mockItem(ItemID.RING_OF_WEALTH) + }, new int[] {23, 25, 31}), + + MAGIC_DART(new int[] {75, 83, 99}, 18, new Item[] + { + mockItem(ItemID.MYSTIC_HAT), + mockItem(ItemID.SARADOMIN_CAPE), + mockItem(ItemID.AMULET_OF_GLORY), + mockItem(ItemID.SLAYERS_STAFF), + mockItem(ItemID.MYSTIC_ROBE_TOP), + mockItem(ItemID.BROODOO_SHIELD), + null, + mockItem(ItemID.MYSTIC_ROBE_BOTTOM), + null, + mockItem(ItemID.MYSTIC_GLOVES), + mockItem(ItemID.WIZARD_BOOTS), + mockItem(ItemID.RING_OF_WEALTH) + }, new int[] {17, 18, 19}), + + + FIRE_BOLT(75, 8, new Item[] + { + mockItem(ItemID.SLAYER_HELMET_I), + mockItem(ItemID.IMBUED_SARADOMIN_CAPE), + mockItem(ItemID.OCCULT_NECKLACE), + mockItem(ItemID.STAFF_OF_THE_DEAD), + mockItem(ItemID.MYSTIC_ROBE_TOP), + mockItem(ItemID.TOME_OF_FIRE), + null, + mockItem(ItemID.MYSTIC_ROBE_BOTTOM), + null, + mockItem(ItemID.CHAOS_GAUNTLETS), + mockItem(ItemID.WIZARD_BOOTS), + mockItem(ItemID.RING_OF_WEALTH) + }, 31), + + + WIND_BLAST(75, 9, new Item[] + { + mockItem(ItemID.MYSTIC_HAT), + mockItem(ItemID.SARADOMIN_CAPE), + mockItem(ItemID.AMULET_OF_GLORY), + mockItem(ItemID.STAFF_OF_AIR), + mockItem(ItemID.MYSTIC_ROBE_TOP), + mockItem(ItemID.BROODOO_SHIELD), + null, + mockItem(ItemID.MYSTIC_ROBE_BOTTOM), + null, + mockItem(ItemID.MYSTIC_GLOVES), + mockItem(ItemID.WIZARD_BOOTS), + mockItem(ItemID.RING_OF_WEALTH) + }, 13), + + + EARTH_WAVE(75, 15, new Item[] + { + mockItem(ItemID.MYSTIC_HAT), + mockItem(ItemID.SARADOMIN_CAPE), + mockItem(ItemID.OCCULT_NECKLACE), + mockItem(ItemID.STAFF_OF_EARTH), + mockItem(ItemID.MYSTIC_ROBE_TOP), + mockItem(ItemID.TOME_OF_FIRE), + null, + mockItem(ItemID.MYSTIC_ROBE_BOTTOM), + null, + mockItem(ItemID.MYSTIC_GLOVES), + mockItem(ItemID.WIZARD_BOOTS), + mockItem(ItemID.RING_OF_WEALTH) + }, 20), + + FLAMES_OF_ZAMORAK(75, 20, new Item[] + { + mockItem(ItemID.MYSTIC_HAT), + mockItem(ItemID.SARADOMIN_CAPE), + mockItem(ItemID.AMULET_OF_GLORY), + mockItem(ItemID.STAFF_OF_THE_DEAD), + mockItem(ItemID.MYSTIC_ROBE_TOP), + mockItem(ItemID.BROODOO_SHIELD), + null, + mockItem(ItemID.MYSTIC_ROBE_BOTTOM), + null, + mockItem(ItemID.MYSTIC_GLOVES), + mockItem(ItemID.WIZARD_BOOTS), + mockItem(ItemID.RING_OF_WEALTH) + }, 23), + + SARADOMIN_STRIKE(75, 52, new Item[] + { + mockItem(ItemID.MYSTIC_HAT), + mockItem(ItemID.SARADOMIN_CAPE), + mockItem(ItemID.AMULET_OF_GLORY), + mockItem(ItemID.STAFF_OF_LIGHT), + mockItem(ItemID.MYSTIC_ROBE_TOP), + mockItem(ItemID.BROODOO_SHIELD), + null, + mockItem(ItemID.MYSTIC_ROBE_BOTTOM), + null, + mockItem(ItemID.MYSTIC_GLOVES), + mockItem(ItemID.WIZARD_BOOTS), + mockItem(ItemID.RING_OF_WEALTH) + }, 23), + + + ; + + + private final int[] magicLevels; + private final int spellId; + private final Item[] equipedItems; + private final int[] expectedMaxHits; + + MagicMaxHitConfig(int magicLevel, int spellId, Item[] equipedItems, int expectedMaxHit) + { + this.magicLevels = new int[] {magicLevel}; + this.spellId = spellId; + this.equipedItems = equipedItems; + this.expectedMaxHits = new int[] {expectedMaxHit}; + } + + MagicMaxHitConfig(int[] magicLevels, int spellId, Item[] equipedItems, int[] expectedMaxHits) + { + this.magicLevels = magicLevels; + this.spellId = spellId; + this.equipedItems = equipedItems; + this.expectedMaxHits = expectedMaxHits; + } + + + private static Item mockItem(int itemId) + { + Item item = mock(Item.class); + when(item.getId()).thenReturn(itemId); + return item; + } + + public void test(Client client) + { + int[] magicLevels = this.magicLevels; + for (int i = 0, magicLevelsLength = magicLevels.length; i < magicLevelsLength; i++) + { + int magicLevel = magicLevels[i]; + int expectedMaxHit = this.expectedMaxHits[i]; + + // Mock equipment container + ItemContainer equipmentContainer = mock(ItemContainer.class); + when(equipmentContainer.getItems()) + .thenReturn(this.equipedItems); + when(client.getItemContainer(InventoryID.EQUIPMENT)).thenReturn(equipmentContainer); + + // Mock Varbits + when(client.getBoostedSkillLevel(Skill.MAGIC)).thenReturn(magicLevel); + when(client.getVar(Varbits.AUTO_CAST_SPELL)).thenReturn(this.spellId); + + // Test + MagicMaxHitCalculator maxHitCalculator = new MagicMaxHitCalculator(client, this.equipedItems); + assertEquals(this.toString(), expectedMaxHit, maxHitCalculator.getMaxHit(), 0); + + } + + } + +} diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/maxhit/calculators/testconfig/MaxHitConfig.java b/runelite-client/src/test/java/net/runelite/client/plugins/maxhit/calculators/testconfig/MaxHitConfig.java new file mode 100644 index 0000000000..309abe1727 --- /dev/null +++ b/runelite-client/src/test/java/net/runelite/client/plugins/maxhit/calculators/testconfig/MaxHitConfig.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2019, Bartvollebregt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.maxhit.calculators.testconfig; + +import net.runelite.api.Client; + +public interface MaxHitConfig +{ + void test(Client client); +} diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/maxhit/calculators/testconfig/MeleeMaxHitConfig.java b/runelite-client/src/test/java/net/runelite/client/plugins/maxhit/calculators/testconfig/MeleeMaxHitConfig.java new file mode 100644 index 0000000000..dd03f9487e --- /dev/null +++ b/runelite-client/src/test/java/net/runelite/client/plugins/maxhit/calculators/testconfig/MeleeMaxHitConfig.java @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2019, Bartvollebregt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.maxhit.calculators.testconfig; + +import net.runelite.api.*; +import net.runelite.api.widgets.Widget; +import net.runelite.api.widgets.WidgetInfo; +import net.runelite.client.plugins.maxhit.attackstyle.WeaponType; +import net.runelite.client.plugins.maxhit.calculators.MeleeMaxHitCalculator; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public enum MeleeMaxHitConfig implements MaxHitConfig +{ + + DRAGON_SCIMITAR(new int[] {75, 83, 99}, 66, WeaponType.TYPE_9, 1, new Item[] + { + mockItem(ItemID.IRON_FULL_HELM), + mockItem(ItemID.BLACK_CAPE), + mockItem(ItemID.GOLD_NECKLACE), + mockItem(ItemID.DRAGON_SCIMITAR), + mockItem(ItemID.IRON_PLATEBODY), + mockItem(ItemID.IRON_KITESHIELD), + null, + mockItem(ItemID.IRON_PLATELEGS), + null, + mockItem(ItemID.LEATHER_GLOVES), + mockItem(ItemID.LEATHER_BOOTS), + mockItem(ItemID.GOLD_RING) + }, new int[] {17, 19, 22}), + + DRAGON_SCIMITAR_DEFENDER(new int[] {75, 83, 99}, 76, WeaponType.TYPE_9, 1, new Item[] + { + mockItem(ItemID.IRON_FULL_HELM), + mockItem(ItemID.BLACK_CAPE), + mockItem(ItemID.GOLD_NECKLACE), + mockItem(ItemID.DRAGON_SCIMITAR), + mockItem(ItemID.IRON_PLATEBODY), + mockItem(ItemID.DRAGON_DEFENDER), + null, + mockItem(ItemID.IRON_PLATELEGS), + null, + mockItem(ItemID.LEATHER_GLOVES), + mockItem(ItemID.LEATHER_BOOTS), + mockItem(ItemID.GOLD_RING) + }, new int[] {19, 21, 24}), + + DRAGON_SCIMITAR_COMPLETE(new int[] {75, 83, 99}, 108, WeaponType.TYPE_9, 1, new Item[] + { + mockItem(ItemID.SLAYER_HELMET), + mockItem(ItemID.FIRE_CAPE), + mockItem(ItemID.AMULET_OF_FURY), + mockItem(ItemID.DRAGON_SCIMITAR), + mockItem(ItemID.FIGHTER_TORSO), + mockItem(ItemID.DRAGON_DEFENDER), + null, + mockItem(ItemID.IRON_PLATELEGS), + null, + mockItem(ItemID.BARROWS_GLOVES), + mockItem(ItemID.DRAGON_BOOTS), + mockItem(ItemID.BERSERKER_RING) + }, new int[] {26, 29, 35}), + + OBSIDIAN_SET(new int[] {75, 83, 99}, 61, WeaponType.TYPE_17, 2, new Item[] + { + mockItem(ItemID.OBSIDIAN_HELMET), + mockItem(ItemID.OBSIDIAN_CAPE), + mockItem(ItemID.GOLD_NECKLACE), + mockItem(ItemID.TOKTZXILAK), + mockItem(ItemID.OBSIDIAN_PLATEBODY), + mockItem(ItemID.TOKTZKETXIL), + null, + mockItem(ItemID.OBSIDIAN_PLATELEGS), + null, + mockItem(ItemID.LEATHER_GLOVES), + mockItem(ItemID.LEATHER_BOOTS), + mockItem(ItemID.GOLD_RING) + }, new int[] {18, 19, 23}), + + DHAROK_SET(new int[] {75, 75, 75, 83, 83, 83, 99, 99, 99}, 105, WeaponType.TYPE_1, 1, + new int[][] {{99, 99}, {1, 99}, {32, 75}, {99, 99}, {1, 99}, {32, 75}, {99, 99}, {1, 99}, {32, 75}}, + new Item[] + { + mockItem(ItemID.DHAROKS_HELM_100), + mockItem(ItemID.BLACK_CAPE), + mockItem(ItemID.GOLD_NECKLACE), + mockItem(ItemID.DHAROKS_GREATAXE_100), + mockItem(ItemID.DHAROKS_PLATEBODY_100), + null, + null, + mockItem(ItemID.DHAROKS_PLATELEGS_100), + null, + mockItem(ItemID.LEATHER_GLOVES), + mockItem(ItemID.LEATHER_BOOTS), + mockItem(ItemID.GOLD_RING) + }, new int[] {23, 45, 30, 25, 49, 33, 29, 57, 38}), + + VOID_SET(new int[] {75, 83, 99}, 66, WeaponType.TYPE_9, 1, new Item[] + { + mockItem(ItemID.VOID_MELEE_HELM), + mockItem(ItemID.BLACK_CAPE), + mockItem(ItemID.GOLD_NECKLACE), + mockItem(ItemID.DRAGON_SCIMITAR), + mockItem(ItemID.VOID_KNIGHT_TOP), + mockItem(ItemID.IRON_KITESHIELD), + null, + mockItem(ItemID.VOID_KNIGHT_ROBE), + null, + mockItem(ItemID.VOID_KNIGHT_GLOVES), + mockItem(ItemID.LEATHER_BOOTS), + mockItem(ItemID.GOLD_RING) + }, new int[] {19, 21, 25}), + ; + + + private final int[] strengthLevels; + private final WeaponType weaponType; + private final int attackStyleId; + private final Item[] equipedItems; + private final int[] expectedMaxHits; + private final int[][] hitpoints; + private final int meleeEquipmentStrength; + + MeleeMaxHitConfig(int[] strengthLevels, int meleeEquipmentStrength, WeaponType weaponType, int attackStyleId, int[][] hitpoints, Item[] equipedItems, int[] expectedMaxHits) + { + this.strengthLevels = strengthLevels; + this.meleeEquipmentStrength = meleeEquipmentStrength; + this.weaponType = weaponType; + this.attackStyleId = attackStyleId; + this.hitpoints = hitpoints; + this.equipedItems = equipedItems; + this.expectedMaxHits = expectedMaxHits; + } + + MeleeMaxHitConfig(int[] strengthLevels, int meleeEquipmentStrength, WeaponType weaponType, int attackStyleId, Item[] equipedItems, int[] expectedMaxHits) + { + this.strengthLevels = strengthLevels; + this.hitpoints = new int[strengthLevels.length][2]; + this.meleeEquipmentStrength = meleeEquipmentStrength; + this.weaponType = weaponType; + this.attackStyleId = attackStyleId; + this.equipedItems = equipedItems; + this.expectedMaxHits = expectedMaxHits; + } + + + private static Item mockItem(int itemId) + { + Item item = mock(Item.class); + when(item.getId()).thenReturn(itemId); + return item; + } + + public void test(Client client) + { + int[] strengthLevels = this.strengthLevels; + for (int i = 0, strengthLevelsLength = strengthLevels.length; i < strengthLevelsLength; i++) + { + int strengthLevel = strengthLevels[i]; + int[] hitpoints = this.hitpoints[i]; + int expectedMaxHit = this.expectedMaxHits[i]; + + // Mock equipment container + ItemContainer equipmentContainer = mock(ItemContainer.class); + when(equipmentContainer.getItems()) + .thenReturn(this.equipedItems); + when(client.getItemContainer(InventoryID.EQUIPMENT)).thenReturn(equipmentContainer); + + // Mock equipment strength + Widget equipmentWidget = mock(Widget.class); + when(client.getWidget(WidgetInfo.EQUIPMENT_MELEE_STRENGTH)).thenReturn(equipmentWidget); + when(equipmentWidget.getText()).thenReturn("Melee strength: " + this.meleeEquipmentStrength); + + // Mock Varbits + when(client.getVar(Varbits.EQUIPPED_WEAPON_TYPE)).thenReturn(this.weaponType.ordinal()); + when(client.getVar(VarPlayer.ATTACK_STYLE)).thenReturn(this.attackStyleId); + + // Mock strength + when(client.getBoostedSkillLevel(Skill.STRENGTH)).thenReturn(strengthLevel); + + // Mock hitpoints + when(client.getBoostedSkillLevel(Skill.HITPOINTS)).thenReturn(hitpoints[0]); + when(client.getRealSkillLevel(Skill.HITPOINTS)).thenReturn(hitpoints[1]); + + // Test + MeleeMaxHitCalculator maxHitCalculator = new MeleeMaxHitCalculator(client, this.equipedItems); + assertEquals(this.toString(), expectedMaxHit, maxHitCalculator.getMaxHit(), 0); + + } + + } + +} diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/maxhit/calculators/testconfig/RangeMaxHitConfig.java b/runelite-client/src/test/java/net/runelite/client/plugins/maxhit/calculators/testconfig/RangeMaxHitConfig.java new file mode 100644 index 0000000000..31ac795bb5 --- /dev/null +++ b/runelite-client/src/test/java/net/runelite/client/plugins/maxhit/calculators/testconfig/RangeMaxHitConfig.java @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2019, Bartvollebregt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.maxhit.calculators.testconfig; + +import net.runelite.api.*; +import net.runelite.api.widgets.Widget; +import net.runelite.api.widgets.WidgetInfo; +import net.runelite.client.plugins.maxhit.attackstyle.WeaponType; +import net.runelite.client.plugins.maxhit.calculators.RangeMaxHitCalculator; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public enum RangeMaxHitConfig implements MaxHitConfig +{ + + MAGIC_SHORTBOW(new int[] {75, 83, 99}, 49, WeaponType.TYPE_3, 1, new Item[] + { + mockItem(ItemID.IRON_FULL_HELM), + mockItem(ItemID.BLACK_CAPE), + mockItem(ItemID.GOLD_NECKLACE), + mockItem(ItemID.MAGIC_SHORTBOW), + mockItem(ItemID.IRON_PLATEBODY), + null, + null, + mockItem(ItemID.IRON_PLATELEGS), + null, + mockItem(ItemID.LEATHER_GLOVES), + mockItem(ItemID.LEATHER_BOOTS), + mockItem(ItemID.GOLD_RING), + mockItem(ItemID.RUNE_ARROW) + }, new int[] {15, 16, 19}), + + RUNE_CROSSBOW(new int[] {75, 83, 99}, 115, WeaponType.TYPE_5, 0, new Item[] + { + mockItem(ItemID.IRON_FULL_HELM), + mockItem(ItemID.BLACK_CAPE), + mockItem(ItemID.GOLD_NECKLACE), + mockItem(ItemID.RUNE_CROSSBOW), + mockItem(ItemID.IRON_PLATEBODY), + null, + null, + mockItem(ItemID.IRON_PLATELEGS), + null, + mockItem(ItemID.LEATHER_GLOVES), + mockItem(ItemID.LEATHER_BOOTS), + mockItem(ItemID.GOLD_RING), + mockItem(ItemID.RUNITE_BOLTS) + }, new int[] {24, 26, 31}), + + BLOwPIPE(new int[] {75, 83, 99}, 50, WeaponType.TYPE_19, 1, new Item[] + { + mockItem(ItemID.IRON_FULL_HELM), + mockItem(ItemID.BLACK_CAPE), + mockItem(ItemID.GOLD_NECKLACE), + mockItem(ItemID.TOXIC_BLOWPIPE), + mockItem(ItemID.IRON_PLATEBODY), + null, + null, + mockItem(ItemID.IRON_PLATELEGS), + null, + mockItem(ItemID.LEATHER_GLOVES), + mockItem(ItemID.LEATHER_BOOTS), + mockItem(ItemID.GOLD_RING) + }, new int[] {15, 16, 19}), + + VOID_SET(new int[] {75, 83, 99}, 115, WeaponType.TYPE_5, 1, new Item[] + { + mockItem(ItemID.VOID_RANGER_HELM), + mockItem(ItemID.BLACK_CAPE), + mockItem(ItemID.GOLD_NECKLACE), + mockItem(ItemID.RUNE_CROSSBOW), + mockItem(ItemID.VOID_KNIGHT_TOP), + mockItem(ItemID.IRON_KITESHIELD), + null, + mockItem(ItemID.VOID_KNIGHT_ROBE), + null, + mockItem(ItemID.VOID_KNIGHT_GLOVES), + mockItem(ItemID.LEATHER_BOOTS), + mockItem(ItemID.GOLD_RING) + }, new int[] {26, 28, 33}), + + ; + + private final int[] rangeLevels; + private final WeaponType weaponType; + private final int attackStyleId; + private final Item[] equipedItems; + private final int[] expectedMaxHits; + private final int ammoEquipmentStrength; + + RangeMaxHitConfig(int[] rangeLevels, int ammoEquipmentStrength, WeaponType weaponType, int attackStyleId, Item[] equipedItems, int[] expectedMaxHits) + { + this.rangeLevels = rangeLevels; + this.ammoEquipmentStrength = ammoEquipmentStrength; + this.weaponType = weaponType; + this.attackStyleId = attackStyleId; + this.equipedItems = equipedItems; + this.expectedMaxHits = expectedMaxHits; + } + + + private static Item mockItem(int itemId) + { + Item item = mock(Item.class); + when(item.getId()).thenReturn(itemId); + return item; + } + + public void test(Client client) + { + int[] rangeLevels = this.rangeLevels; + for (int i = 0, rangeLevelsLength = rangeLevels.length; i < rangeLevelsLength; i++) + { + int rangeLevel = rangeLevels[i]; + int expectedMaxHit = this.expectedMaxHits[i]; + + // Mock equipment container + ItemContainer equipmentContainer = mock(ItemContainer.class); + when(equipmentContainer.getItems()) + .thenReturn(this.equipedItems); + when(client.getItemContainer(InventoryID.EQUIPMENT)).thenReturn(equipmentContainer); + + // Mock equipment strength + Widget equipmentWidget = mock(Widget.class); + when(client.getWidget(WidgetInfo.EQUIPMENT_RANGED_STRENGTH)).thenReturn(equipmentWidget); + when(equipmentWidget.getText()).thenReturn("Ranged strength: " + this.ammoEquipmentStrength); + + // Mock Varbits + when(client.getVar(Varbits.EQUIPPED_WEAPON_TYPE)).thenReturn(this.weaponType.ordinal()); + when(client.getVar(VarPlayer.ATTACK_STYLE)).thenReturn(this.attackStyleId); + + // Mock strength + when(client.getBoostedSkillLevel(Skill.RANGED)).thenReturn(rangeLevel); + + // Test + RangeMaxHitCalculator maxHitCalculator = new RangeMaxHitCalculator(client, this.equipedItems); + assertEquals(this.toString(), expectedMaxHit, maxHitCalculator.getMaxHit(), 0); + + } + + } + +}