diff --git a/runelite-api/src/main/java/net/runelite/api/Actor.java b/runelite-api/src/main/java/net/runelite/api/Actor.java index 6357a91ebc..53166fba5b 100644 --- a/runelite-api/src/main/java/net/runelite/api/Actor.java +++ b/runelite-api/src/main/java/net/runelite/api/Actor.java @@ -38,6 +38,7 @@ import net.runelite.api.coords.WorldPoint; */ public interface Actor extends Renderable { + /** * Gets the combat level of the actor. * @@ -246,4 +247,10 @@ public interface Actor extends Renderable * @param overheadText the overhead text */ void setOverheadText(String overheadText); + + /** + * Used by the "Tick Counter Plugin + */ + int getActionFrame(); + int getActionFrameCycle(); } diff --git a/runelite-api/src/main/java/net/runelite/api/AnimationID.java b/runelite-api/src/main/java/net/runelite/api/AnimationID.java index e3fa08fd88..b488d21512 100644 --- a/runelite-api/src/main/java/net/runelite/api/AnimationID.java +++ b/runelite-api/src/main/java/net/runelite/api/AnimationID.java @@ -154,10 +154,26 @@ public final class AnimationID public static final int PISCARILIUS_CRANE_REPAIR = 7199; public static final int HOME_MAKE_TABLET = 4067; + //block animations for players and perhaps npcs as well? + public static final int BLOCK_DEFENDER = 4177; + public static final int BLOCK_NO_SHIELD = 420; + public static final int BLOCK_SHIELD = 1156; + public static final int BLOCK_SWORD = 388; + public static final int BLOCK_UNARMED = 424; + // NPC animations public static final int TZTOK_JAD_MAGIC_ATTACK = 2656; public static final int TZTOK_JAD_RANGE_ATTACK = 2652; public static final int HELLHOUND_DEFENCE = 6566; + public static final int VORKATH_WAKE_UP = 7950; + public static final int VORKATH_DEATH = 7949; + public static final int VORKATH_SLASH_ATTACK = 7951; + public static final int VORKATH_ATTACK = 7952; + public static final int VORKATH_FIRE_BOMB_ATTACK = 7960; + public static final int VORKATH_ACID_ATTACK = 7957; + public static final int BLACKJACK_KO = 838; + public static final int VETION_EARTHQUAKE = 5507; + public static final int ZULRAH_DEATH = 5804; // Farming public static final int FARMING_HARVEST_FRUIT_TREE = 2280; @@ -194,4 +210,7 @@ public final class AnimationID // POH Animations public static final int INCENSE_BURNER = 3687; -} + public static final int LOW_LEVEL_MAGIC_ATTACK = 1162; + public static final int HIGH_LEVEL_MAGIC_ATTACK = 1167; + public static final int BLOWPIPE_ATTACK = 5061; +} \ No newline at end of file diff --git a/runelite-api/src/main/java/net/runelite/api/VarPlayer.java b/runelite-api/src/main/java/net/runelite/api/VarPlayer.java index 62489df3f0..36ff1a18f2 100644 --- a/runelite-api/src/main/java/net/runelite/api/VarPlayer.java +++ b/runelite-api/src/main/java/net/runelite/api/VarPlayer.java @@ -52,7 +52,8 @@ public enum VarPlayer IN_RAID_PARTY(1427), NMZ_REWARD_POINTS(1060), - + + ATTACKING_PLAYER(1075), /** * -1 : Poison immune * Normal poison damage is ceil( this / 5.0f ) 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 88ce84f272..3c46a0b71e 100644 --- a/runelite-api/src/main/java/net/runelite/api/Varbits.java +++ b/runelite-api/src/main/java/net/runelite/api/Varbits.java @@ -480,7 +480,16 @@ public enum Varbits /** * The active tab within the quest interface */ - QUEST_TAB(8168); + QUEST_TAB(8168), + + /** + * Temple Trekking + */ + TREK_POINTS(1955), + TREK_STARTED(1956), + TREK_EVENT(1958), + TREK_STATUS(6719); + /** * The raw varbit ID. diff --git a/runelite-api/src/main/java/net/runelite/api/events/CannonballFired.java b/runelite-api/src/main/java/net/runelite/api/events/CannonballFired.java new file mode 100644 index 0000000000..077a88096d --- /dev/null +++ b/runelite-api/src/main/java/net/runelite/api/events/CannonballFired.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2019, Davis Cook + * 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.api.events; + +/** + * an event posted when a cannonball is fired + */ +public class CannonballFired +{ +} diff --git a/runelite-api/src/main/java/net/runelite/api/kit/KitType.java b/runelite-api/src/main/java/net/runelite/api/kit/KitType.java index fdbf92ebfd..a808f2e899 100644 --- a/runelite-api/src/main/java/net/runelite/api/kit/KitType.java +++ b/runelite-api/src/main/java/net/runelite/api/kit/KitType.java @@ -36,6 +36,7 @@ import net.runelite.api.PlayerComposition; */ public enum KitType { + HELMET(0), CAPE(1), AMULET(2), WEAPON(3), @@ -45,7 +46,9 @@ public enum KitType HEAD(8), HANDS(9), BOOTS(10), - JAW(11); + JAW(11), + RING(12), + AMMUNITION(13); /** * Raw equipment index. 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 d84bae473e..8577438af2 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 @@ -129,6 +129,7 @@ public class WidgetID public static final int SKILLS_GROUP_ID = 320; public static final int QUESTTAB_GROUP_ID = 629; public static final int MUSIC_GROUP_ID = 239; + public static final int MUSICTAB_GROUP_ID = 239; public static final int BARROWS_PUZZLE_GROUP_ID = 25; static class WorldMap @@ -701,16 +702,152 @@ public class WidgetID static class StandardSpellBook { static final int LUMBRIDGE_HOME_TELEPORT = 4; +static final int WIND_STRIKE = 5; + static final int CONFUSE = 6; + static final int ENCHANT_CROSSBOW_BOLT = 7; + static final int WATER_STRIKE = 8; + static final int LVL_1_ENCHANT = 9; + static final int EARTH_STRIKE = 10; + static final int WEAKEN = 11; + static final int FIRE_STRIKE = 12; + static final int BONES_TO_BANANAS = 13; + static final int WIND_BOLT = 14; + static final int CURSE = 15; + static final int BIND = 16; + static final int LOW_LEVEL_ALCHEMY = 17; + static final int WATER_BOLT = 18; + static final int VARROCK_TELEPORT = 19; + static final int LVL_2_ENCHANT = 20; + static final int EARTH_BOLT = 21; + static final int LUMBRIDGE_TELEPORT = 22; + static final int TELEKINETIC_GRAB = 23; + static final int FIRE_BOLT = 24; + static final int FALADOR_TELEPORT = 25; + static final int CRUMBLE_UNDEAD = 26; + static final int TELEPORT_TO_HOUSE = 27; + static final int WIND_BLAST = 28; + static final int SUPERHEAT_ITEM = 29; + static final int CAMELOT_TELEPORT = 30; + static final int WATER_BLAST = 31; + static final int LVL_3_ENCHANT = 32; + static final int IBAN_BLAST = 33; + static final int SNARE = 34; + static final int MAGIC_DART = 35; + static final int ARDOUGNE_TELEPORT = 36; + static final int EARTH_BLAST = 37; + static final int HIGH_LEVEL_ALCHEMY = 38; + static final int CHARGE_WATER_ORB = 39; + static final int LVL_4_ENCHANT = 40; + static final int WATCHTOWER_TELEPORT = 41; + static final int FIRE_BLAST = 42; + static final int CHARGE_EARTH_ORB = 43; + static final int BONES_TO_PEACHES = 44; + static final int SARADOMIN_STRIKE = 45; + static final int CLAWS_OF_GUTHIX = 46; + static final int FLAMES_OF_ZAMORAK = 47; + static final int TROLLHEIM_TELEPORT = 48; + static final int WIND_WAVE = 49; + static final int HARGE_FIRE_ORB = 50; + static final int TELEPORT_TO_APE_ATOLL = 51; + static final int WATER_WAVE = 52; + static final int CHARGE_AIR_ORB = 53; + static final int VULNERABILITY = 54; + static final int LVL_5_ENCHANT = 55; + static final int TELEPORT_TO_KOUREND = 56; + static final int EARTH_WAVE = 57; + static final int ENFEEBLE = 58; + static final int TELEOTHER_LUMBRIDGE = 59; + static final int FIRE_WAVE = 60; + static final int ENTANGLE = 61; + static final int STUN = 62; + static final int CHARGE = 63; + static final int WIND_SURGE = 64; + static final int TELEOTHER_FALADOR = 65; + static final int WATER_SURGE = 66; + static final int TELE_BLOCK = 67; + static final int BOUNTY_TARGET_TELEPORT = 68; + static final int LVL_6_ENCHANT = 69; + static final int TELEOTHER_CAMELOT = 70; + static final int EARTH_SURGE = 71; + static final int LVL_7_ENCHANT = 72; + static final int FIRE_SURGE = 73; } - static class AncientSpellBook - { + static class AncientSpellBook { + static final int BOUNTY_TARGET_TELEPORT = 68; + static final int ICE_RUSH = 74; + static final int ICE_BLITZ = 75; + static final int ICE_BURST = 76; + static final int ICE_BARRAGE = 77; + static final int BLOOD_RUSH = 78; + static final int BLOOD_BLITZ = 79; + static final int BLOOD_BURST = 80; + static final int BLOOD_BARRAGE = 81; + static final int SMOKE_RUSH = 82; + static final int SMOKE_BLITZ = 83; + static final int SMOKE_BURST = 84; + static final int SMOKE_BARRAGE = 85; + static final int SHADOW_RUSH = 86; + static final int SHADOW_BLITZ = 87; + static final int SHADOW_BURST = 88; + static final int SHADOW_BARRAGE = 89; + static final int PADDEWWA_TELEPORT = 90; + static final int SENNTISTEN_TELEPORT = 91; + static final int KHARYRLL_TELEPORT = 92; + static final int LASSAR_TELEPORT = 93; + static final int DAREEYAK_TELEPORT = 94; + static final int CARRALLANGER_TELEPORT = 95; + static final int ANNAKARL_TELEPORT = 96; + static final int GHORROCK_TELEPORT = 97; static final int EDGEVILLE_HOME_TELEPORT = 98; } - static class LunarSpellBook - { + static class LunarSpellBook { + static final int BOUNTY_TARGET_TELEPORT = 68; static final int LUNAR_HOME_TELEPORT = 99; + static final int BAKE_PIE = 100; + static final int CURE_PLANT = 101; + static final int MONSTER_EXAMINE = 102; + static final int NPC_CONTACT = 103; + static final int CURE_OTHER = 104; + static final int HUMIDIFY = 105; + static final int MOONCLAN_TELEPORT = 106; + static final int TELE_GROUP_MOONCLAN = 107; + static final int CURE_ME = 108; + static final int HUNTER_KIT = 109; + static final int WATERBIRTH_TELEPORT = 110; + static final int TELE_GROUP_WATERBIRTH = 111; + static final int CURE_GROUP = 112; + static final int STAT_SPY = 113; + static final int BARBARIAN_TELEPORT = 114; + static final int TELE_GROUP_BARBARIAN = 115; + static final int SUPERGLASS_MAKE = 116; + static final int TAN_LEATHER = 117; + static final int KHAZARD_TELEPORT = 118; + static final int TELE_GROUP_KHAZARD = 119; + static final int DREAM = 120; + static final int STRING_JEWELLERY = 121; + static final int STAT_RESTORE_POT_SHARE = 122; + static final int MAGIC_IMBUE = 123; + static final int FERTILE_SOIL = 124; + static final int BOOST_POTION_SHARE = 125; + static final int FISHING_GUILD_TELEPORT = 126; + static final int TELE_GROUP_FISHING_GUILD = 127; + static final int PLANK_MAKE = 128; + static final int CATHERBY_TELEPORT = 129; + static final int TELE_GROUP_CATHERBY = 130; + static final int RECHARGE_DRAGONSTONE = 131; + static final int ICE_PLATEAU_TELEPORT = 132; + static final int TELE_GROUP_ICE_PLATEAU = 133; + static final int ENERGY_TRANSFER = 134; + static final int HEAL_OTHER = 135; + static final int VENGEANCE_OTHER = 136; + static final int VENGEANCE = 137; + static final int HEAL_GROUP = 138; + static final int SPELLBOOK_SWAP = 139; + static final int GEOMANCY = 140; + static final int SPIN_FLAX = 141; + static final int OURANIA_TELEPORT = 142; } static class ArceuusSpellBook 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 5f12519d5d..65e82471d7 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 @@ -450,10 +450,56 @@ public enum WidgetInfo MINIGAME_TELEPORT_BUTTON(WidgetID.MINIGAME_TAB_ID, WidgetID.Minigames.TELEPORT_BUTTON), +/* STANDARD SPELL BOOK WIDGETS*/ SPELL_LUMBRIDGE_HOME_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.StandardSpellBook.LUMBRIDGE_HOME_TELEPORT), - SPELL_EDGEVILLE_HOME_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.AncientSpellBook.EDGEVILLE_HOME_TELEPORT), + SPELL_BIND(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.StandardSpellBook.BIND), + SPELL_SNARE(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.StandardSpellBook.SNARE), + SPELL_ENTANGLE(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.StandardSpellBook.ENTANGLE), + SPELL_TELE_BLOCK(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.StandardSpellBook.TELE_BLOCK), + SPELL_FIRE_SURGE(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.StandardSpellBook.FIRE_SURGE), + SPELL_BOUNTY_TARGET_TELEPORT2(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.StandardSpellBook.BOUNTY_TARGET_TELEPORT), + /* END OF STANDARD SPELL BOOK WIDGETS*/ + + /* LUNAR SPELL BOOK WIDGETS*/ SPELL_LUNAR_HOME_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.LunarSpellBook.LUNAR_HOME_TELEPORT), + SPELL_VENGEANCE_OTHER(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.LunarSpellBook.VENGEANCE_OTHER), + SPELL_VENGEANCE(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.LunarSpellBook.VENGEANCE), + SPELL_BOUNTY_TARGET_TELEPORT3(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.LunarSpellBook.BOUNTY_TARGET_TELEPORT), + /* LUNA SPELL BOOK WIDGETS*/ + + /* ARCEUUS SPELL BOOK WIDGETS*/ SPELL_ARCEUUS_HOME_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.ArceuusSpellBook.ARCEUUS_HOME_TELEPORT), + /* END OF ARCEUUS SPELL BOOK WIDGETS*/ + + /* ANCIENT SPELL BOOK WIDGETS*/ + SPELL_ICE_RUSH(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.AncientSpellBook.ICE_RUSH), + SPELL_ICE_BLITZ(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.AncientSpellBook.ICE_BLITZ), + SPELL_ICE_BURST(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.AncientSpellBook.ICE_BURST), + SPELL_ICE_BARRAGE(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.AncientSpellBook.ICE_BARRAGE), + SPELL_BLOOD_RUSH(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.AncientSpellBook.BLOOD_RUSH), + SPELL_BLOOD_BLITZ(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.AncientSpellBook.BLOOD_BLITZ), + SPELL_BLOOD_BURST(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.AncientSpellBook.BLOOD_BURST), + SPELL_BLOOD_BARRAGE(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.AncientSpellBook.BLOOD_BARRAGE), + SPELL_SMOKE_RUSH(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.AncientSpellBook.SMOKE_RUSH), + SPELL_SMOKE_BLITZ(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.AncientSpellBook.SMOKE_BLITZ), + SPELL_SMOKE_BURST(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.AncientSpellBook.SMOKE_BURST), + SPELL_SMOKE_BARRAGE(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.AncientSpellBook.SMOKE_BARRAGE), + SPELL_SHADOW_RUSH(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.AncientSpellBook.SHADOW_RUSH), + SPELL_SHADOW_BLITZ(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.AncientSpellBook.SHADOW_BLITZ), + SPELL_SHADOW_BURST(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.AncientSpellBook.SHADOW_BURST), + SPELL_SHADOW_BARRAGE(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.AncientSpellBook.SHADOW_BARRAGE), + SPELL_PADDEWWA_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.AncientSpellBook.PADDEWWA_TELEPORT), + SPELL_SENNTISTEN_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.AncientSpellBook.SENNTISTEN_TELEPORT), + SPELL_KHARYRLL_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.AncientSpellBook.KHARYRLL_TELEPORT), + SPELL_LASSAR_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.AncientSpellBook.LASSAR_TELEPORT), + SPELL_DAREEYAK_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.AncientSpellBook.DAREEYAK_TELEPORT), + SPELL_CARRALLANGER_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.AncientSpellBook.CARRALLANGER_TELEPORT), + SPELL_ANNAKARL_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.AncientSpellBook.ANNAKARL_TELEPORT), + SPELL_GHORROCK_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.AncientSpellBook.GHORROCK_TELEPORT), + SPELL_EDGEVILLE_HOME_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.AncientSpellBook.EDGEVILLE_HOME_TELEPORT), + SPELL_BOUNTY_TARGET_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.AncientSpellBook.BOUNTY_TARGET_TELEPORT), + + /* END OF ANCIENT SPELL BOOK WIDGETS*/ PVP_SKULL_CONTAINER(WidgetID.PVP_GROUP_ID, WidgetID.Pvp.SKULL_CONTAINER), PVP_WORLD_SAFE_ZONE(WidgetID.PVP_GROUP_ID, WidgetID.Pvp.SAFE_ZONE), @@ -477,6 +523,20 @@ public enum WidgetInfo QUESTLIST_FREE_CONTAINER(WidgetID.QUESTLIST_GROUP_ID, WidgetID.QuestList.FREE_CONTAINER), QUESTLIST_MEMBERS_CONTAINER(WidgetID.QUESTLIST_GROUP_ID, WidgetID.QuestList.MEMBERS_CONTAINER), QUESTLIST_MINIQUEST_CONTAINER(WidgetID.QUESTLIST_GROUP_ID, WidgetID.QuestList.MINIQUEST_CONTAINER), + + MUSICTAB_INTERFACE(WidgetID.MUSICTAB_GROUP_ID, 1), + MUSICTAB_SONG_BOX(WidgetID.MUSICTAB_GROUP_ID, 2), + MUSICTAB_ALL_SONGS(WidgetID.MUSICTAB_GROUP_ID, 3), + MUSICTAB_SCROLLBAR(WidgetID.MUSICTAB_GROUP_ID, 4), + MUSICTAB_PLAYING(WidgetID.MUSICTAB_GROUP_ID, 5), + MUSICTAB_CURRENT_SONG_NAME(WidgetID.MUSICTAB_GROUP_ID, 6), + MUSICTAB_AUTO_BUTTON_LISTENER(WidgetID.MUSICTAB_GROUP_ID, 7), + MUSICTAB_AUTO_BUTTON(WidgetID.MUSICTAB_GROUP_ID, 8), + MUSICTAB_MANUAL_BUTTON_LISTENER(WidgetID.MUSICTAB_GROUP_ID, 9), + MUSICTAB_MANUAL_BUTTON(WidgetID.MUSICTAB_GROUP_ID, 10), + MUSICTAB_LOOP_BUTTON_LISTENER(WidgetID.MUSICTAB_GROUP_ID, 11), + 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); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/aoewarnings/AoeProjectile.java b/runelite-client/src/main/java/net/runelite/client/plugins/aoewarnings/AoeProjectile.java new file mode 100644 index 0000000000..8bd41f2610 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/aoewarnings/AoeProjectile.java @@ -0,0 +1,58 @@ +/* + * 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.aoewarnings; + +import net.runelite.api.coords.LocalPoint; + +import java.time.Instant; + +public class AoeProjectile +{ + private final Instant startTime; + private final LocalPoint targetPoint; + private final AoeProjectileInfo aoeProjectileInfo; + + public AoeProjectile(Instant startTime, LocalPoint targetPoint, AoeProjectileInfo aoeProjectileInfo) + { + this.startTime = startTime; + this.targetPoint = targetPoint; + this.aoeProjectileInfo = aoeProjectileInfo; + } + + public Instant getStartTime() + { + return startTime; + } + + public LocalPoint getTargetPoint() + { + return targetPoint; + } + + public AoeProjectileInfo getAoeProjectileInfo() + { + return aoeProjectileInfo; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/aoewarnings/AoeProjectileInfo.java b/runelite-client/src/main/java/net/runelite/client/plugins/aoewarnings/AoeProjectileInfo.java new file mode 100644 index 0000000000..4d4f59a650 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/aoewarnings/AoeProjectileInfo.java @@ -0,0 +1,128 @@ +package net.runelite.client.plugins.aoewarnings; + +import java.time.Duration; +import java.util.HashMap; +import java.util.Map; +import net.runelite.api.ProjectileID; + +public enum AoeProjectileInfo +{ + LIZARDMAN_SHAMAN_AOE(ProjectileID.LIZARDMAN_SHAMAN_AOE, 3000, 3), + CRAZY_ARCHAEOLOGIST_AOE(ProjectileID.CRAZY_ARCHAEOLOGIST_AOE, 3000, 3), + ICE_DEMON_RANGED_AOE(ProjectileID.ICE_DEMON_RANGED_AOE, 3000, 3), + /** + * When you don't have pray range on ice demon does an ice barrage + */ + ICE_DEMON_ICE_BARRAGE_AOE(ProjectileID.ICE_DEMON_ICE_BARRAGE_AOE, 3000, 3), + /** + * The AOE when vasa first starts + */ + VASA_AWAKEN_AOE(ProjectileID.VASA_AWAKEN_AOE, 4500, 3), + VASA_RANGED_AOE(ProjectileID.VASA_RANGED_AOE, 3000, 3), + TEKTON_METEOR_AOE(ProjectileID.TEKTON_METEOR_AOE, 4000, 3), + + /** + * The AOEs of Vorkath + */ + VORKATH_BOMB(ProjectileID.VORKATH_BOMB_AOE, 2400, 3), + VORKATH_POISON_POOL(ProjectileID.VORKATH_POISON_POOL_AOE, 1800, 1), + VORKATH_SPAWN(ProjectileID.VORKATH_SPAWN_AOE, 3000, 1), //extra tick because hard to see otherwise + VORKATH_TICK_FIRE(ProjectileID.VORKATH_TICK_FIRE_AOE, 600, 1), + + /** + * the AOEs of Galvek + */ + GALVEK_MINE(ProjectileID.GALVEK_MINE, 3600, 3), + GALVEK_BOMB(ProjectileID.GALVEK_BOMB, 2400, 3), + + DAWN_FREEZE(ProjectileID.DAWN_FREEZE, 3000, 3), + DUSK_CEILING(ProjectileID.DUSK_CEILING, 3000, 3), + + /** + * the AOE of Vet'ion + */ + VETION_LIGHTNING(ProjectileID.VETION_LIGHTNING, 3000, 1), + + /** + * the AOE of Chaos Fanatic + */ + CHAOS_FANATIC(ProjectileID.CHAOS_FANATIC_AOE, 3000, 1), + + /** + * the AOE of the Corporeal Beast + */ + + CORPOREAL_BEAST(ProjectileID.CORPOREAL_BEAST_AOE, 3000, 1), + CORPOREAL_BEAST_DARK_CORE(ProjectileID.CORPOREAL_BEAST_DARK_CORE_AOE, 3000, 3), + + /** + * the AOEs of The Great Olm + * missing ids and length, please help + */ + OLM_FALLING_CRYSTAL(1357, 3000, 3), + OLM_BURNING(1349, 2400, 1), + OLM_FALLING_CRYSTAL_TRAIL(1352, 2400, 1), + OLM_ACID_TRAIL(1354, 2400, 1), + OLM_FIRE_LINE(1347, 2400, 1), + + /** + * the AOE of the Wintertodt snow that falls + */ + WINTERTODT_SNOW_FALL(1310, 4000, 3); + + + /** + * The id of the projectile to trigger this AoE warning + */ + private final int id; + + /** + * How long the indicator should last for this AoE warning This might + * need to be a bit longer than the projectile actually takes to land as + * there is a fade effect on the warning + */ + private final Duration lifeTime; + + /** + * The size of the splash radius of the AoE warning Ex. Lizardman shaman + * AoE is a 3x3, so aoeSize = 3 + */ + private final int aoeSize; + + private static final Map map = new HashMap<>(); + + static + { + for (AoeProjectileInfo aoe : values()) + { + map.put(aoe.id, aoe); + } + } + + AoeProjectileInfo(int id, int lifeTimeMillis, int aoeSize) + { + this.id = id; + this.lifeTime = Duration.ofMillis(lifeTimeMillis); + this.aoeSize = aoeSize; + } + + public Duration getLifeTime() + { + return lifeTime; + } + + public int getId() + { + return id; + } + + public int getAoeSize() + { + return aoeSize; + } + + public static AoeProjectileInfo getById(int id) + { + return map.get(id); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/aoewarnings/AoeWarningConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/aoewarnings/AoeWarningConfig.java new file mode 100644 index 0000000000..af1b361ec0 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/aoewarnings/AoeWarningConfig.java @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2018, 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.aoewarnings; + +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("aoe") +public interface AoeWarningConfig extends Config +{ + @ConfigItem( + keyName = "enabled", + name = "AoE Warnings Enabled", + description = "Configures whether or not AoE Projectile Warnings plugin is displayed" + ) + default boolean enabled() + { + return true; + } + + @ConfigItem( + keyName = "lizardmanaoe", + name = "Lizardman Shamans", + description = "Configures whether or not AoE Projectile Warnings for Lizardman Shamans is displayed" + ) + default boolean isShamansEnabled() + { + return true; + } + + @ConfigItem( + keyName = "archaeologistaoe", + name = "Crazy Archaeologist", + description = "Configures whether or not AoE Projectile Warnings for Archaeologist is displayed" + ) + default boolean isArchaeologistEnabled() + { + return true; + } + + @ConfigItem( + keyName = "icedemon", + name = "Ice Demon", + description = "Configures whether or not AoE Projectile Warnings for Ice Demon is displayed" + ) + default boolean isIceDemonEnabled() + { + return true; + } + + @ConfigItem( + keyName = "vasa", + name = "Vasa", + description = "Configures whether or not AoE Projectile Warnings for Vasa is displayed" + ) + default boolean isVasaEnabled() + { + return true; + } + + @ConfigItem( + keyName = "tekton", + name = "Tekton", + description = "Configures whether or not AoE Projectile Warnings for Tekton is displayed" + ) + default boolean isTektonEnabled() + { + return true; + } + + @ConfigItem( + keyName = "vorkath", + name = "Vorkath", + description = "Configures whether or not AoE Projectile Warnings for Vorkath are displayed" + ) + default boolean isVorkathEnabled() + { + return true; + } + + @ConfigItem( + keyName = "galvek", + name = "Galvek", + description = "Configures whether or not AoE Projectile Warnings for Galvek are displayed" + ) + default boolean isGalvekEnabled() + { + return true; + } + + @ConfigItem( + keyName = "gargboss", + name = "Gargoyle Boss", + description = "Configs whether or not AoE Projectile Warnings for Dawn/Dusk are displayed" + ) + default boolean isGargBossEnabled() + { + return true; + } + + @ConfigItem( + keyName = "vetion", + name = "Vet'ion", + description = "Configures whether or not AoE Projectile Warnings for Vet'ion are displayed" + ) + default boolean isVetionEnabled() + { + return true; + } + + @ConfigItem( + keyName = "chaosfanatic", + name = "Chaos Fanatic", + description = "Configures whether or not AoE Projectile Warnings for Chaos Fanatic are displayed" + ) + default boolean isChaosFanaticEnabled() + { + return true; + } + + @ConfigItem( + keyName = "olm", + name = "Great Olm", + description = "Configures whether or not AoE Projectile Warnings for The Great Olm are displayed" + ) + default boolean isOlmEnabled() + { + return true; + } + + @ConfigItem( + keyName = "bombDisplay", + name = "Display crystal phase bomb tracker", + description = "Display a timer and colour-coded AoE for Olm's crystal-phase bombs." + ) + default boolean bombDisplay() + { + return true; + } + + @ConfigItem( + keyName = "corp", + name = "Corporeal Beast", + description = "Configures whether or not AoE Projectile Warnings for the Corporeal Beast are displayed" + ) + default boolean isCorpEnabled() + { + return true; + } + + @ConfigItem( + keyName = "wintertodt", + name = "Wintertodt Snow Fall", + description = "Configures whether or not AOE Projectile Warnings for the Wintertodt snow fall are displayed" + ) + default boolean isWintertodtEnabled() + { + return true; + } + + @ConfigItem( + keyName = "outline", + name = "Display Outline", + description = "Configures whether or not AoE Projectile Warnings have an outline" + ) + default boolean isOutlineEnabled() + { + return true; + } + + @ConfigItem( + keyName = "lightning", + name = "Show Lightning Trails", + description = "Show Lightning Trails" + ) + default boolean LightningTrail() + { + return true; + } + + @ConfigItem( + keyName = "fade", + name = "Fade Warnings", + description = "Configures whether or not AoE Projectile Warnings fade over time" + ) + default boolean isFadeEnabled() + { + return true; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/aoewarnings/AoeWarningOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/aoewarnings/AoeWarningOverlay.java new file mode 100644 index 0000000000..7e6dd2409a --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/aoewarnings/AoeWarningOverlay.java @@ -0,0 +1,166 @@ +/* + * 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.aoewarnings; + +import net.runelite.api.Client; +import net.runelite.api.Perspective; +import net.runelite.api.Projectile; +import net.runelite.api.coords.LocalPoint; +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; + +import javax.annotation.Nullable; +import javax.inject.Inject; +import java.awt.*; +import java.time.Instant; +import java.util.Iterator; +import java.util.Map; + +public class AoeWarningOverlay extends Overlay +{ + private static final int FILL_START_ALPHA = 25; + private static final int OUTLINE_START_ALPHA = 255; + + private final Client client; + private final AoeWarningPlugin plugin; + private final AoeWarningConfig config; + + @Inject + public AoeWarningOverlay(@Nullable Client client, AoeWarningPlugin plugin, AoeWarningConfig config) + { + setPosition(OverlayPosition.DYNAMIC); + setLayer(OverlayLayer.UNDER_WIDGETS); + this.client = client; + this.plugin = plugin; + this.config = config; + } + + @Override + public Dimension render(Graphics2D graphics) + { + if (!config.enabled()) + { + return null; + } + for (WorldPoint point : plugin.getLightningTrail()) + { + drawTile(graphics, point, new Color(0,150,200), 2, 150, 50); + } + for (WorldPoint point : plugin.getAcidTrail()) + { + drawTile(graphics, point, new Color(69, 241, 44), 2, 150, 50); + } + for (WorldPoint point : plugin.getCrystalSpike()) + { + drawTile(graphics, point, new Color(255, 0, 84), 2, 150, 50); + } + + Instant now = Instant.now(); + Map projectiles = plugin.getProjectiles(); + for (Iterator it = projectiles.values().iterator(); it.hasNext();) + { + AoeProjectile aoeProjectile = it.next(); + + if (now.isAfter(aoeProjectile.getStartTime().plus(aoeProjectile.getAoeProjectileInfo().getLifeTime()))) + { + it.remove(); + continue; + } + + Polygon tilePoly = Perspective.getCanvasTileAreaPoly(client, aoeProjectile.getTargetPoint(), aoeProjectile.getAoeProjectileInfo().getAoeSize()); + if (tilePoly == null) + { + continue; + } + + // how far through the projectiles lifetime between 0-1. + double progress = (System.currentTimeMillis() - aoeProjectile.getStartTime().toEpochMilli()) / (double) aoeProjectile.getAoeProjectileInfo().getLifeTime().toMillis(); + + int fillAlpha, outlineAlpha; + if (config.isFadeEnabled()) + { + fillAlpha = (int) ((1 - progress) * FILL_START_ALPHA);//alpha drop off over lifetime + outlineAlpha = (int) ((1 - progress) * OUTLINE_START_ALPHA); + } + else + { + fillAlpha = FILL_START_ALPHA; + outlineAlpha = OUTLINE_START_ALPHA; + } + + if (fillAlpha < 0) + { + fillAlpha = 0; + } + if (outlineAlpha < 0) + { + outlineAlpha = 0; + } + + if (fillAlpha > 255) + { + fillAlpha = 255; + } + if (outlineAlpha > 255) + { + outlineAlpha = 255;//Make sure we don't pass in an invalid alpha + } + + if (config.isOutlineEnabled()) + { + graphics.setColor(new Color(0, 150, 200, outlineAlpha)); + graphics.drawPolygon(tilePoly); + } + + graphics.setColor(new Color(0, 150, 200, fillAlpha)); + graphics.fillPolygon(tilePoly); + } + return null; + } + + private void drawTile(Graphics2D graphics, WorldPoint point, Color color, int strokeWidth, int outlineAlpha, int fillAlpha) { + WorldPoint playerLocation = client.getLocalPlayer().getWorldLocation(); + if (point.distanceTo(playerLocation) >= 32) { + return; + } + LocalPoint lp = LocalPoint.fromWorld(client, point); + if (lp == null) { + return; + } + + Polygon poly = Perspective.getCanvasTilePoly(client, lp); + if (poly == null) { + return; + } + //OverlayUtil.renderPolygon(graphics, poly, color); + graphics.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), outlineAlpha)); + graphics.setStroke(new BasicStroke(strokeWidth)); + graphics.draw(poly); + graphics.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), fillAlpha)); + graphics.fill(poly); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/aoewarnings/AoeWarningPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/aoewarnings/AoeWarningPlugin.java new file mode 100644 index 0000000000..c3219d53b7 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/aoewarnings/AoeWarningPlugin.java @@ -0,0 +1,307 @@ +/* + * Copyright (c) 2018, 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.aoewarnings; + +import com.google.inject.Provides; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.GameObject; +import net.runelite.api.GameState; +import net.runelite.api.GraphicID; +import net.runelite.api.GraphicsObject; +import net.runelite.api.ObjectID; +import net.runelite.api.Projectile; +import net.runelite.api.Client; +import net.runelite.api.Tile; +import net.runelite.api.coords.LocalPoint; +import net.runelite.api.coords.WorldPoint; +import net.runelite.api.events.GameObjectDespawned; +import net.runelite.api.events.GameObjectSpawned; +import net.runelite.api.events.GameStateChanged; +import net.runelite.api.events.GameTick; +import net.runelite.api.events.GraphicsObjectCreated; +import net.runelite.api.events.ProjectileMoved; +import net.runelite.client.Notifier; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.ui.overlay.OverlayManager; + +import javax.inject.Inject; +import java.time.Instant; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Logger; + +@PluginDescriptor( + name = "!AoE Warnings", + description = "Shows the final destination for AoE Attack projectiles", + tags = {"bosses", "combat", "pve", "overlay"} +) + +@Slf4j +public class AoeWarningPlugin extends Plugin +{ + + @Inject + private OverlayManager overlayManager; + + @Inject + private AoeWarningOverlay coreOverlay; + + @Inject + public AoeWarningConfig config; + + @Inject + private BombOverlay bombOverlay; + + @Inject + private Client client; + + @Inject + private Notifier notifier; + + @Getter + private final Map bombs = new HashMap<>(); + @Getter(AccessLevel.PACKAGE) + private List LightningTrail = new ArrayList<>(); + @Getter(AccessLevel.PACKAGE) + private List AcidTrail = new ArrayList<>(); + @Getter(AccessLevel.PACKAGE) + private List CrystalSpike = new ArrayList<>(); + + + @Provides + AoeWarningConfig getConfig(ConfigManager configManager) + { + return configManager.getConfig(AoeWarningConfig.class); + } + + + private final Map projectiles = new HashMap<>(); + + public Map getProjectiles() + { + return projectiles; + } + + @Override + protected void startUp() throws Exception + { + overlayManager.add(coreOverlay); + overlayManager.add(bombOverlay); + LightningTrail.clear(); + AcidTrail.clear(); + CrystalSpike.clear(); + } + + @Override + protected void shutDown() throws Exception + { + overlayManager.remove(coreOverlay); + overlayManager.remove(bombOverlay); + } + + @Subscribe + public void onProjectileMoved(ProjectileMoved event) + { + Projectile projectile = event.getProjectile(); + + int projectileId = projectile.getId(); + AoeProjectileInfo aoeProjectileInfo = AoeProjectileInfo.getById(projectileId); + if (aoeProjectileInfo != null && isConfigEnabledForProjectileId(projectileId)) + { + LocalPoint targetPoint = event.getPosition(); + AoeProjectile aoeProjectile = new AoeProjectile(Instant.now(), targetPoint, aoeProjectileInfo); + projectiles.put(projectile, aoeProjectile); + } + } + + @Subscribe + public void onGameObjectSpawned(GameObjectSpawned event) + { + final GameObject gameObject = event.getGameObject(); + final WorldPoint bombLocation = gameObject.getWorldLocation(); + + switch (gameObject.getId()) + { + case ObjectID.CRYSTAL_BOMB: + bombs.put(bombLocation, new CrystalBomb(gameObject, client.getTickCount())); + break; + case ObjectID.ACID_POOL: + AcidTrail.add(bombLocation); + break; + case ObjectID.SMALL_CRYSTALS: + //todo + CrystalSpike.add(bombLocation); + break; + } + } + + @Subscribe + public void onGameObjectDespawned(GameObjectDespawned event) + { + GameObject gameObject = event.getGameObject(); + WorldPoint bombLocation = gameObject.getWorldLocation(); + switch (gameObject.getId()) + { + case ObjectID.CRYSTAL_BOMB: + //might as well check the ObjectID to save some time. + purgeBombs(bombs); + break; + case ObjectID.ACID_POOL: + AcidTrail.remove(bombLocation); + break; + case ObjectID.SMALL_CRYSTALS: + //todo + CrystalSpike.remove(bombLocation); + break; + } + } + + @Subscribe + public void onGameStateChanged(GameStateChanged delta) + { + if (client.getGameState() == GameState.LOGGED_IN) + { + purgeBombs(bombs); + } + } + + @Subscribe + public void onGameTick(GameTick event) + { + if (config.LightningTrail()) + { + LightningTrail.clear(); + for (GraphicsObject o : client.getGraphicsObjects()) + { + if (o.getId() == 1356) + { + LightningTrail.add(WorldPoint.fromLocal(client, o.getLocation())); + } + } + } + + Iterator> it = bombs.entrySet().iterator(); + + while (it.hasNext()) + { + Map.Entry entry = it.next(); + CrystalBomb bomb = entry.getValue(); + bomb.bombClockUpdate(); + //bombClockUpdate smooths the shown timer; not using this results in 1.2 --> .6 vs. 1.2 --> 1.1, etc. + } + } + + private void purgeBombs(Map bombs) + { + Iterator> it = bombs.entrySet().iterator(); + Tile[][][] tiles = client.getScene().getTiles(); + + while (it.hasNext()) + { + Map.Entry entry = it.next(); + WorldPoint world = entry.getKey(); + LocalPoint local = LocalPoint.fromWorld(client, world); + Tile tile = tiles[world.getPlane()][local.getSceneX()][local.getSceneY()]; + GameObject[] objects = tile.getGameObjects(); + boolean containsObjects = false; + + for (GameObject object : objects) + { + if (object != null) + { + containsObjects = true; + } + } + + if (!containsObjects) + { + it.remove(); + } + + } + } + + private boolean isConfigEnabledForProjectileId(int projectileId) + { + AoeProjectileInfo projectileInfo = AoeProjectileInfo.getById(projectileId); + if (projectileInfo == null) + { + return false; + } + + switch (projectileInfo) + { + case LIZARDMAN_SHAMAN_AOE: + return config.isShamansEnabled(); + case CRAZY_ARCHAEOLOGIST_AOE: + return config.isArchaeologistEnabled(); + case ICE_DEMON_RANGED_AOE: + case ICE_DEMON_ICE_BARRAGE_AOE: + return config.isIceDemonEnabled(); + case VASA_AWAKEN_AOE: + case VASA_RANGED_AOE: + return config.isVasaEnabled(); + case TEKTON_METEOR_AOE: + return config.isTektonEnabled(); + case VORKATH_BOMB: + case VORKATH_POISON_POOL: + case VORKATH_SPAWN: + case VORKATH_TICK_FIRE: + return config.isVorkathEnabled(); + case VETION_LIGHTNING: + return config.isVetionEnabled(); + case CHAOS_FANATIC: + return config.isChaosFanaticEnabled(); + case GALVEK_BOMB: + case GALVEK_MINE: + return config.isGalvekEnabled(); + case DAWN_FREEZE: + case DUSK_CEILING: + return config.isGargBossEnabled(); + case OLM_FALLING_CRYSTAL: + case OLM_BURNING: + case OLM_FALLING_CRYSTAL_TRAIL: + case OLM_ACID_TRAIL: + case OLM_FIRE_LINE: + return config.isOlmEnabled(); + case CORPOREAL_BEAST: + case CORPOREAL_BEAST_DARK_CORE: + return config.isCorpEnabled(); + case WINTERTODT_SNOW_FALL: + return config.isWintertodtEnabled(); + } + + return false; + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/aoewarnings/BombOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/aoewarnings/BombOverlay.java new file mode 100644 index 0000000000..0ee6162a76 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/aoewarnings/BombOverlay.java @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2018, PallasDieKatze (Pallas Cat) + * 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.aoewarnings; + +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.Client; +import net.runelite.api.Perspective; +import net.runelite.api.Player; +import net.runelite.api.Point; +import net.runelite.api.coords.LocalPoint; +import net.runelite.api.coords.WorldPoint; +import net.runelite.client.plugins.aoewarnings.CrystalBomb; +import net.runelite.client.ui.overlay.*; +import javax.inject.Inject; +import java.awt.Graphics2D; +import java.awt.Dimension; +import java.awt.Color; +import java.awt.Polygon; +import java.awt.BasicStroke; +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.time.Instant; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; + +@Slf4j +public class BombOverlay extends Overlay +{ + + private static final String SAFE = "#00cc00"; + //safe + private static final String CAUTION = "#ffff00"; + //1 tile in range (minor damage) + private static final String WARNING = "#ff9933"; + //2 tiles in range (moderate damage) + private static final String DANGER = "#ff6600"; + //3 tiles in range/adjacent to bomb (major damage) + private static final String LETHAL = "#cc0000"; + //On the bomb, using it as a makeshift space launch vehicle. (massive damage) + + private static final int BOMB_AOE = 7; + private static final int BOMB_DETONATE_TIME = 8; + //This is in ticks. It should be 10, but it varies from 8 to 11. + private static final double ESTIMATED_TICK_LENGTH = .6; + //Thank you Woox & co. for this assumption. .6 seconds/tick. + + + //Utilized from the npc highlight code for formatting text being displayed on the client canvas. + private static final NumberFormat TIME_LEFT_FORMATTER = + DecimalFormat.getInstance(Locale.US); + + static + { + ((DecimalFormat) TIME_LEFT_FORMATTER).applyPattern("#0.0"); + } + + private final Client client; + private final AoeWarningConfig config; + private final AoeWarningPlugin plugin; + + @Inject + public BombOverlay(Client client, AoeWarningPlugin plugin, AoeWarningConfig config) + { + this.client = client; + this.plugin = plugin; + this.config = config; + setPosition(OverlayPosition.DYNAMIC); + setLayer(OverlayLayer.ABOVE_SCENE); + setPriority(OverlayPriority.MED); + } + + @Override + public Dimension render(Graphics2D graphics) + { + if (config.bombDisplay()) + { + drawBombs(graphics); + } + return null; + } + + private void drawBombs(Graphics2D graphics) + //I can condense drawDangerZone into this. Ambivalent though. + { + Iterator> it = plugin.getBombs().entrySet().iterator(); + while (it.hasNext()) + { + Map.Entry entry = it.next(); + CrystalBomb bomb = entry.getValue(); + drawDangerZone(graphics, bomb); + } + } + + private void drawDangerZone(Graphics2D graphics, CrystalBomb bomb) + { + final Player localPlayer = client.getLocalPlayer(); + LocalPoint localLoc = LocalPoint.fromWorld(client, bomb.getWorldLocation()); + double distance_x = Math.abs(bomb.getWorldLocation().getX() - localPlayer.getWorldLocation().getX()); + double distance_y = Math.abs(bomb.getWorldLocation().getY() - localPlayer.getWorldLocation().getY()); + Color color_code = Color.decode(SAFE); + //defaults to this unless conditionals met below. + + if (distance_x < 1 && distance_y < 1) + { + color_code = Color.decode(LETHAL); + } + else if (distance_x < 2 && distance_y < 2) + { + color_code = Color.decode(DANGER); + } + else if (distance_x < 3 && distance_y < 3) + { + color_code = Color.decode(WARNING); + } + else if (distance_x < 4 && distance_y < 4) + { + color_code = Color.decode(CAUTION); + } + LocalPoint CenterPoint = new LocalPoint(localLoc.getX() + 0, localLoc.getY() + 0); + Polygon poly = Perspective.getCanvasTileAreaPoly(client, CenterPoint, BOMB_AOE); + + if (poly != null) + { + //manually generating the polygon so as to assign a custom alpha value. Request adtl' arg for alpha maybe? + graphics.setColor(color_code); + graphics.setStroke(new BasicStroke(1)); + graphics.drawPolygon(poly); + graphics.setColor(new Color(0, 0, 0, 10)); + graphics.fillPolygon(poly); + } + + Instant now = Instant.now(); + double timeLeft = ((BOMB_DETONATE_TIME - (client.getTickCount() - + bomb.getTickStarted())) * ESTIMATED_TICK_LENGTH) - + (now.toEpochMilli() - bomb.getLastClockUpdate().toEpochMilli()) / 1000.0; + //divided by 1000.00 because of milliseconds :) + + timeLeft = Math.max(0.0, timeLeft); + String bombTimerString = TIME_LEFT_FORMATTER.format(timeLeft); + int textWidth = graphics.getFontMetrics().stringWidth(bombTimerString); + int textHeight = graphics.getFontMetrics().getAscent(); + Point canvasPoint = Perspective.localToCanvas(client, localLoc.getX(), + localLoc.getY(), bomb.getWorldLocation().getPlane()); + + if (canvasPoint != null) + { + Point canvasCenterPoint = new Point( + canvasPoint.getX() - textWidth / 2, + canvasPoint.getY() + textHeight / 2); + OverlayUtil.renderTextLocation(graphics, canvasCenterPoint, bombTimerString, color_code); + } + + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/aoewarnings/CrystalBomb.java b/runelite-client/src/main/java/net/runelite/client/plugins/aoewarnings/CrystalBomb.java new file mode 100644 index 0000000000..d138aed20c --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/aoewarnings/CrystalBomb.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2018, PallasDieKatze (Pallas Cat) + * 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.aoewarnings; + +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.GameObject; +import net.runelite.api.coords.WorldPoint; +import java.time.Instant; + +@Slf4j +public class CrystalBomb +{ + @Getter + private Instant plantedOn; + + @Getter + private Instant lastClockUpdate; + + @Getter + private int objectId; + + @Getter + private int tickStarted; + // + + @Getter + private WorldPoint worldLocation; + + public CrystalBomb(GameObject gameObject, int startTick) + { + this.plantedOn = Instant.now(); + this.objectId = gameObject.getId(); + this.worldLocation = gameObject.getWorldLocation(); + this.tickStarted = startTick; + } + + public void bombClockUpdate() + { + lastClockUpdate = Instant.now(); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/batools/BAToolsConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/batools/BAToolsConfig.java new file mode 100644 index 0000000000..64cf45dc8f --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/batools/BAToolsConfig.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2018, Cameron + * Copyright (c) 2018, Jacob M + * 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.batools; + +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("BATools") +public interface BAToolsConfig extends Config +{ + @ConfigItem( + keyName = "defTimer", + name = "Defender Tick Timer", + description = "Shows the current cycle tick of runners." + ) + default boolean defTimer() + { + return false; + } + + @ConfigItem( + keyName = "calls", + name = "Remove Incorrect Calls", + description = "Remove incorrect calls." + ) + default boolean calls() + { + return false; + } + + @ConfigItem( + keyName = "swapLadder", + name = "Swap ladder option", + description = "Swap Climb-down with Quick-start in the wave lobbies" + ) + default boolean swapLadder() + { + return true; + } + + @ConfigItem( + keyName = "healerCodes", + name = "Healer Codes", + description = "Overlay to show healer codes" + ) + default boolean healerCodes() + { + return false; + } + + @ConfigItem( + keyName = "healerMenuOption", + name = "Healer menu options", + description = "asd" + ) + default boolean healerMenuOption() + { + return false; + } + + @ConfigItem( + keyName = "antiDrag", + name = "Anti Drag", + description = "asd" + ) + default boolean antiDrag() + { + return false; + } + + @ConfigItem( + keyName = "antiDragDelay", + name = "Anti Drag Delay", + description = "asd" + ) + default int antiDragDelay() + { + return 5; + } + + @ConfigItem( + keyName = "eggBoi", + name = "Collector helper", + description = "asd" + ) + default boolean eggBoi() + { + return false; + } + + @ConfigItem( + keyName = "osHelp", + name = "Shift OS", + description = "asd" + ) + default boolean osHelp() + { + return false; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/batools/BAToolsOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/batools/BAToolsOverlay.java new file mode 100644 index 0000000000..43a7bf4e7f --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/batools/BAToolsOverlay.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2018, Woox + * 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.batools; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import net.runelite.api.NPCComposition; +import javax.inject.Inject; +import net.runelite.api.Client; +import net.runelite.client.ui.overlay.OverlayUtil; +import net.runelite.client.ui.overlay.Overlay; +import java.time.Duration; +import java.time.Instant; +import net.runelite.client.ui.overlay.OverlayLayer; +import net.runelite.client.ui.overlay.OverlayPosition; +import lombok.extern.slf4j.Slf4j; +@Slf4j + +public class BAToolsOverlay extends Overlay +{ + private static final Color RED = new Color(221, 44, 0); + private static final Color GREEN = new Color(0, 200, 83); + private static final Color ORANGE = new Color(255, 109, 0); + private static final Color YELLOW = new Color(255, 214, 0); + private static final Color CYAN = new Color(0, 184, 212); + private static final Color BLUE = new Color(41, 98, 255); + private static final Color DEEP_PURPLE = new Color(98, 0, 234); + private static final Color PURPLE = new Color(170, 0, 255); + private static final Color GRAY = new Color(158, 158, 158); + + private final BAToolsConfig config; + private Client client; + private BAToolsPlugin plugin; + + @Inject + public BAToolsOverlay(Client client, BAToolsPlugin plugin, BAToolsConfig config) + { + setPosition(OverlayPosition.DYNAMIC); + setLayer(OverlayLayer.ABOVE_SCENE); + this.config = config; + this.client = client; + this.plugin = plugin; + } + + + @Override + public Dimension render(Graphics2D graphics) + { + if(!config.healerCodes()) + { + return null; + } + + for (Healer healer : plugin.getHealers().values()) + { + NPCComposition composition = healer.getNpc().getComposition(); + Color color = composition.getCombatLevel() > 1 ? YELLOW : ORANGE; + if (composition.getConfigs() != null) + { + NPCComposition transformedComposition = composition.transform(); + if (transformedComposition == null) + { + color = GRAY; + } + else + { + composition = transformedComposition; + } + } + int timeLeft = healer.getLastFoodTime() - (int)Duration.between(plugin.getWave_start(), Instant.now()).getSeconds(); + timeLeft = timeLeft < 1 ? 0 : timeLeft; + + if(healer.getFoodRemaining() > 1) + { + color = GREEN; + } + else if(healer.getFoodRemaining() == 1) + { + if(timeLeft > 0) + { + color = RED; + } + else + { + color = GREEN; + } + } + else + { + continue; + } + + String text = String.format("%d %d", + healer.getFoodRemaining(), + timeLeft); + + + + OverlayUtil.renderActorOverlay(graphics, healer.getNpc(), text, color); + } + return null; + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/batools/BAToolsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/batools/BAToolsPlugin.java new file mode 100644 index 0000000000..7b9e195848 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/batools/BAToolsPlugin.java @@ -0,0 +1,643 @@ +/* + * Copyright (c) 2018, Cameron + * Copyright (c) 2018, Jacob M + * 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.batools; + +import net.runelite.client.eventbus.EventBus; +import net.runelite.client.eventbus.Subscribe; +import com.google.inject.Provides; +import java.awt.event.KeyEvent; +import java.awt.image.BufferedImage; +import java.time.Duration; +import java.time.Instant; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.inject.Inject; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.Actor; +import net.runelite.api.ChatMessageType; +import net.runelite.api.Client; +import static net.runelite.api.Constants.CHUNK_SIZE; +import net.runelite.api.ItemID; +import net.runelite.api.MenuEntry; +import net.runelite.api.NPC; +import net.runelite.api.NpcID; +import net.runelite.api.Varbits; +import net.runelite.api.coords.WorldPoint; +import net.runelite.api.events.ChatMessage; +import net.runelite.api.events.ConfigChanged; +import net.runelite.api.events.GameTick; +import net.runelite.api.events.HitsplatApplied; +import net.runelite.api.events.InteractingChanged; +import net.runelite.api.events.MenuEntryAdded; +import net.runelite.api.events.MenuOptionClicked; +import net.runelite.api.events.NpcDespawned; +import net.runelite.api.events.NpcSpawned; +import net.runelite.api.events.VarbitChanged; +import net.runelite.api.events.WidgetLoaded; +import net.runelite.api.widgets.Widget; +import net.runelite.api.widgets.WidgetID; +import net.runelite.api.widgets.WidgetInfo; +import net.runelite.client.chat.ChatMessageManager; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.game.ItemManager; +import net.runelite.client.input.KeyListener; +import net.runelite.client.input.KeyManager; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.ui.overlay.OverlayManager; +import net.runelite.client.ui.overlay.infobox.InfoBoxManager; +import net.runelite.client.util.Text; + +@Slf4j +@PluginDescriptor( + name = "BA Tools", + description = "Custom tools for Barbarian Assault", + tags = {"minigame", "overlay", "timer"} +) +public class BAToolsPlugin extends Plugin implements KeyListener +{ + int inGameBit = 0; + int tickNum; + int pastCall = 0; + private int currentWave = 1; + private static final int BA_WAVE_NUM_INDEX = 2; + private final List entries = new ArrayList<>(); + private HashMap foodPressed = new HashMap<>(); + private CycleCounter counter; + private Actor lastInteracted; + + private boolean shiftDown; + + @Inject + private Client client; + + @Inject + private ConfigManager configManager; + + @Inject + private ChatMessageManager chatMessageManager; + + @Inject + private OverlayManager overlayManager; + + @Inject + private BAToolsConfig config; + + @Inject + private ItemManager itemManager; + + @Inject + private InfoBoxManager infoBoxManager; + + @Inject + private BAToolsOverlay overlay; + + @Getter + private Map healers; + + @Getter + private Instant wave_start; + + @Inject + private KeyManager keyManager; + + + @Provides + BAToolsConfig provideConfig(ConfigManager configManager) + { + return configManager.getConfig(BAToolsConfig.class); + } + + @Override + protected void startUp() throws Exception + { + overlayManager.add(overlay); + healers = new HashMap<>(); + wave_start = Instant.now(); + lastInteracted = null; + foodPressed.clear(); + client.setInventoryDragDelay(config.antiDragDelay()); + keyManager.registerKeyListener(this); + } + + @Override + protected void shutDown() throws Exception + { + removeCounter(); + healers.clear(); + inGameBit = 0; + lastInteracted = null; + overlayManager.remove(overlay); + client.setInventoryDragDelay(5); + keyManager.unregisterKeyListener(this); + shiftDown = false; + } + + @Subscribe + public void onWidgetLoaded(WidgetLoaded event) + { + switch (event.getGroupId()) + { + case WidgetID.BA_REWARD_GROUP_ID: + { + Widget rewardWidget = client.getWidget(WidgetInfo.BA_REWARD_TEXT); + + if (rewardWidget != null && rewardWidget.getText().contains("
5")) + { + tickNum = 0; + } + } + } + } + + @Subscribe + public void onGameTick(GameTick event) + { + if (config.antiDrag()) + { + client.setInventoryDragDelay(config.antiDragDelay()); + } + + Widget callWidget = getWidget(); + + if (callWidget != null) + { + if (callWidget.getTextColor() != pastCall && callWidget.getTextColor() == 16316664) + { + tickNum = 0; + } + pastCall = callWidget.getTextColor(); + } + if (inGameBit == 1) + { + if (tickNum > 9) + { + tickNum = 0; + } + if (counter == null) + { + addCounter(); + } + //counter.setText(String.valueOf(tickNum)); + counter.setCount(tickNum); + if (config.defTimer()) + { + log.info("" + tickNum++); + } + } + } + + private Widget getWidget() + { + if (client.getWidget(WidgetInfo.BA_DEF_CALL_TEXT) != null) + { + return client.getWidget(WidgetInfo.BA_DEF_CALL_TEXT); + } + else if (client.getWidget(WidgetInfo.BA_ATK_CALL_TEXT) != null) + { + return client.getWidget(WidgetInfo.BA_ATK_CALL_TEXT); + } + else if (client.getWidget(WidgetInfo.BA_COLL_CALL_TEXT) != null) + { + return client.getWidget(WidgetInfo.BA_COLL_CALL_TEXT); + } + else if (client.getWidget(WidgetInfo.BA_HEAL_CALL_TEXT) != null) + { + return client.getWidget(WidgetInfo.BA_HEAL_CALL_TEXT); + } + return null; + } + + @Subscribe + public void onVarbitChanged(VarbitChanged event) + { + int inGame = client.getVar(Varbits.IN_GAME_BA); + + if (inGameBit != inGame) + { + if (inGameBit == 1) + { + pastCall = 0; + removeCounter(); + foodPressed.clear(); + } + else + { + addCounter(); + } + } + + inGameBit = inGame; + } + + @Subscribe + public void onChatMessage(ChatMessage event) + { + if (event.getType() == ChatMessageType.CONSOLE + && event.getMessage().startsWith("---- Wave:")) + { + String[] message = event.getMessage().split(" "); + currentWave = Integer.parseInt(message[BA_WAVE_NUM_INDEX]); + wave_start = Instant.now(); + healers.clear(); + } + } + + @Subscribe + public void onNpcSpawned(NpcSpawned event) + { + NPC npc = event.getNpc(); + + if (isNpcHealer(npc.getId())) + { + if (checkNewSpawn(npc) || Duration.between(wave_start, Instant.now()).getSeconds() < 16) + { + int spawnNumber = healers.size(); + healers.put(npc, new Healer(npc, spawnNumber, currentWave)); + log.info("spawn number: " + spawnNumber + " on wave " + currentWave); + } + } + } + + @Subscribe + public void onHitsplatApplied(HitsplatApplied hitsplatApplied) + { + Actor actor = hitsplatApplied.getActor(); + + if (healers.isEmpty() && !(actor instanceof NPC) && lastInteracted == null) + { + return; + } + + for (Healer healer : healers.values()) + { + if (healer.getNpc() == actor && actor == lastInteracted) + { + healer.setFoodRemaining(healer.getFoodRemaining() - 1); + } + } + } + + @Subscribe + public void onNpcDespawned(NpcDespawned event) + { + if (healers.remove(event.getNpc()) != null && healers.isEmpty()) + { + healers.clear(); + } + } + + @Subscribe + public void onInteractingChanged(InteractingChanged event) + { + Actor opponent = event.getTarget(); + + if (opponent != null && opponent instanceof NPC && isNpcHealer(((NPC) opponent).getId()) && event.getSource() != client.getLocalPlayer()) + { + lastInteracted = opponent; + } + } + + public static boolean isNpcHealer(int npcId) + { + return npcId == NpcID.PENANCE_HEALER || + npcId == NpcID.PENANCE_HEALER_5766 || + npcId == NpcID.PENANCE_HEALER_5767 || + npcId == NpcID.PENANCE_HEALER_5768 || + npcId == NpcID.PENANCE_HEALER_5769 || + npcId == NpcID.PENANCE_HEALER_5770 || + npcId == NpcID.PENANCE_HEALER_5771 || + npcId == NpcID.PENANCE_HEALER_5772 || + npcId == NpcID.PENANCE_HEALER_5773 || + npcId == NpcID.PENANCE_HEALER_5774; + } + + @Subscribe + public void onMenuEntryAdded(MenuEntryAdded event) + { + if (config.calls() && getWidget() != null && event.getTarget().endsWith("horn") && !event.getTarget().contains("Unicorn")) + { + MenuEntry[] menuEntries = client.getMenuEntries(); + Widget callWidget = getWidget(); + String call = Calls.getOption(callWidget.getText()); + MenuEntry correctCall = null; + + entries.clear(); + for (MenuEntry entry : menuEntries) + { + String option = entry.getOption(); + if (option.equals(call)) + { + correctCall = entry; + } + else if (!option.startsWith("Tell-")) + { + entries.add(entry); + } + } + + if (correctCall != null) //&& callWidget.getTextColor()==16316664) + { + entries.add(correctCall); + client.setMenuEntries(entries.toArray(new MenuEntry[entries.size()])); + } + } + else if (config.calls() && event.getTarget().endsWith("horn")) + { + entries.clear(); + client.setMenuEntries(entries.toArray(new MenuEntry[entries.size()])); + } + + String option = Text.removeTags(event.getOption()).toLowerCase(); + String target = Text.removeTags(event.getTarget()).toLowerCase(); + + if (config.swapLadder() && option.equals("climb-down") && target.equals("ladder")) + { + swap("quick-start", option, target, true); + } + + if (inGameBit == 1 && config.healerMenuOption() && event.getTarget().contains("Penance Healer")) + { + + MenuEntry[] menuEntries = client.getMenuEntries(); + MenuEntry lastEntry = menuEntries[menuEntries.length - 1]; + String targett = lastEntry.getTarget(); + + if (foodPressed.containsKey(lastEntry.getIdentifier())) + { + lastEntry.setTarget(lastEntry.getTarget().split("\\(")[0] + "(" + Duration.between(foodPressed.get(lastEntry.getIdentifier()), Instant.now()).getSeconds() + ")"); + if (Duration.between(foodPressed.get(lastEntry.getIdentifier()), Instant.now()).getSeconds() > 20) + { + lastEntry.setTarget(lastEntry.getTarget().replace("", "")); + } + } + else + { + lastEntry.setTarget(targett.replace("", "")); + + } + + client.setMenuEntries(menuEntries); + } + + if (client.getWidget(WidgetInfo.BA_COLL_LISTEN_TEXT) != null && inGameBit == 1 && config.eggBoi() && event.getTarget().endsWith("egg") && shiftDown) + { + String[] currentCall = client.getWidget(WidgetInfo.BA_COLL_LISTEN_TEXT).getText().split(" "); + log.info("1 " + currentCall[0]); + MenuEntry[] menuEntries = client.getMenuEntries(); + MenuEntry correctEgg = null; + entries.clear(); + + for (MenuEntry entry : menuEntries) + { + if (entry.getTarget().contains(currentCall[0]) && entry.getOption().equals("Take")) + { + correctEgg = entry; + } + } + if (correctEgg != null) + { + entries.add(correctEgg); + client.setMenuEntries(entries.toArray(new MenuEntry[entries.size()])); + } + } + + if (client.getWidget(WidgetInfo.BA_HEAL_LISTEN_TEXT) != null && inGameBit == 1 && config.osHelp() && event.getTarget().equals("Healer item machine") && shiftDown) + { + String[] currentCall = client.getWidget(WidgetInfo.BA_HEAL_LISTEN_TEXT).getText().split(" "); + + if (!currentCall[0].contains("Pois.")) + { + return; + } + + MenuEntry[] menuEntries = client.getMenuEntries(); + MenuEntry correctEgg = null; + entries.clear(); + + for (MenuEntry entry : menuEntries) + { + if (entry.getOption().equals("Take-" + currentCall[1])) + { + correctEgg = entry; + } + } + if (correctEgg != null) + { + entries.add(correctEgg); + client.setMenuEntries(entries.toArray(new MenuEntry[entries.size()])); + } + } + } + + @Subscribe + public void onMenuOptionClicked(MenuOptionClicked event) + { + if (!config.healerMenuOption() || !event.getMenuTarget().contains("Penance Healer") || client.getWidget(WidgetInfo.BA_HEAL_CALL_TEXT) == null) + { + return; + } + + String currentCall = client.getWidget(WidgetInfo.BA_HEAL_CALL_TEXT).getText(); + String target = event.getMenuTarget(); + + if ((currentCall.equals("Pois. Worms") && (target.contains("Poisoned worms") && target.contains("->") && target.contains("Penance Healer"))) + || (currentCall.equals("Pois. Meat") && (target.contains("Poisoned meat") && target.contains("->") && target.contains("Penance Healer"))) + || (currentCall.equals("Pois. Tofu") && (target.contains("Poisoned tofu") && target.contains("->") && target.contains("Penance Healer")))) + { + foodPressed.put(event.getId(), Instant.now()); + } + + if (target.contains("->") && target.contains("Penance Healer")) + { + foodPressed.put(event.getId(), Instant.now()); + } + } + + @Subscribe + public void onConfigChanged(ConfigChanged event) + { + if (config.antiDrag()) + { + client.setInventoryDragDelay(config.antiDragDelay()); + } + } + + + private void addCounter() + { + if (!config.defTimer() || counter != null) + { + return; + } + + int itemSpriteId = ItemID.FIGHTER_TORSO; + + BufferedImage taskImg = itemManager.getImage(itemSpriteId); + counter = new CycleCounter(taskImg, this, tickNum); + + infoBoxManager.addInfoBox(counter); + } + + private void removeCounter() + { + if (counter == null) + { + return; + } + + infoBoxManager.removeInfoBox(counter); + counter = null; + } + + private void swap(String optionA, String optionB, String target, boolean strict) + { + MenuEntry[] entries = client.getMenuEntries(); + + int idxA = searchIndex(entries, optionA, target, strict); + int idxB = searchIndex(entries, optionB, target, strict); + + if (idxA >= 0 && idxB >= 0) + { + MenuEntry entry = entries[idxA]; + entries[idxA] = entries[idxB]; + entries[idxB] = entry; + + client.setMenuEntries(entries); + } + } + + private int searchIndex(MenuEntry[] entries, String option, String target, boolean strict) + { + for (int i = entries.length - 1; i >= 0; i--) + { + MenuEntry entry = entries[i]; + String entryOption = Text.removeTags(entry.getOption()).toLowerCase(); + String entryTarget = Text.removeTags(entry.getTarget()).toLowerCase(); + + if (strict) + { + if (entryOption.equals(option) && entryTarget.equals(target)) + { + return i; + } + } + else + { + if (entryOption.contains(option.toLowerCase()) && entryTarget.equals(target)) + { + return i; + } + } + } + + return -1; + } + + private static WorldPoint rotate(WorldPoint point, int rotation) + { + int chunkX = point.getX() & ~(CHUNK_SIZE - 1); + int chunkY = point.getY() & ~(CHUNK_SIZE - 1); + int x = point.getX() & (CHUNK_SIZE - 1); + int y = point.getY() & (CHUNK_SIZE - 1); + switch (rotation) + { + case 1: + return new WorldPoint(chunkX + y, chunkY + (CHUNK_SIZE - 1 - x), point.getPlane()); + case 2: + return new WorldPoint(chunkX + (CHUNK_SIZE - 1 - x), chunkY + (CHUNK_SIZE - 1 - y), point.getPlane()); + case 3: + return new WorldPoint(chunkX + (CHUNK_SIZE - 1 - y), chunkY + x, point.getPlane()); + } + return point; + } + + private boolean checkNewSpawn(NPC npc) + { + int regionId = 7509; + int regionX = 42; + int regionY = 46; + int z = 0; + + // world point of the tile marker + WorldPoint worldPoint = new WorldPoint( + ((regionId >>> 8) << 6) + regionX, + ((regionId & 0xff) << 6) + regionY, + z + ); + + int[][][] instanceTemplateChunks = client.getInstanceTemplateChunks(); + for (int x = 0; x < instanceTemplateChunks[z].length; ++x) + { + for (int y = 0; y < instanceTemplateChunks[z][x].length; ++y) + { + int chunkData = instanceTemplateChunks[z][x][y]; + int rotation = chunkData >> 1 & 0x3; + int templateChunkY = (chunkData >> 3 & 0x7FF) * CHUNK_SIZE; + int templateChunkX = (chunkData >> 14 & 0x3FF) * CHUNK_SIZE; + if (worldPoint.getX() >= templateChunkX && worldPoint.getX() < templateChunkX + CHUNK_SIZE + && worldPoint.getY() >= templateChunkY && worldPoint.getY() < templateChunkY + CHUNK_SIZE) + { + WorldPoint p = new WorldPoint(client.getBaseX() + x * CHUNK_SIZE + (worldPoint.getX() & (CHUNK_SIZE - 1)), + client.getBaseY() + y * CHUNK_SIZE + (worldPoint.getY() & (CHUNK_SIZE - 1)), + worldPoint.getPlane()); + p = rotate(p, rotation); + if (p.distanceTo(npc.getWorldLocation()) < 5) + { + return true; + } + } + } + } + return false; + } + + @Override + public void keyTyped(KeyEvent e) + { + } + + @Override + public void keyPressed(KeyEvent e) + { + if (e.getKeyCode() == KeyEvent.VK_SHIFT) + { + shiftDown = true; + } + } + + @Override + public void keyReleased(KeyEvent e) + { + if (e.getKeyCode() == KeyEvent.VK_SHIFT) + { + shiftDown = false; + } + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/batools/Calls.java b/runelite-client/src/main/java/net/runelite/client/plugins/batools/Calls.java new file mode 100644 index 0000000000..0c273f5be5 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/batools/Calls.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2018, Cameron + * 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.batools; + +import java.util.HashMap; +import java.util.Map; + +public enum Calls +{ + //Attacker Calls + RED_EGG("Red egg", "Tell-red"), + GREEN_EGG("Green egg", "Tell-green"), + BLUE_EGG("Blue egg", "Tell-blue"), + //Collector Calls + CONTROLLED("Controlled/Bullet/Wind", "Tell-controlled"), + ACCURATE("Accurate/Field/Water", "Tell-accurate"), + AGGRESSIVE("Aggressive/Blunt/Earth", "Tell-aggressive"), + DEFENSIVE("Defensive/Barbed/Fire", "Tell-defensive"), + //Healer Calls + TOFU("Tofu", "Tell-tofu"), + CRACKERS("Crackers", "Tell-crackers"), + WORMS("Worms", "Tell-worms"), + //Defender Calls + POIS_WORMS("Pois. Worms", "Tell-worms"), + POIS_TOFU("Pois. Tofu", "Tell-tofu"), + POIS_MEAT("Pois. Meat", "Tell-meat"); + + private final String call; + private final String option; + + private static final Map CALL_MENU = new HashMap<>(); + + static + { + for (Calls s : values()) + { + CALL_MENU.put(s.getCall(), s.getOption()); + } + } + + Calls(String call, String option) + { + this.call = call; + this.option = option; + } + + public String getCall() + { + return call; + } + + public String getOption() + { + return option; + } + + public static String getOption(String call) + { + return CALL_MENU.get(call); + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/batools/CycleCounter.java b/runelite-client/src/main/java/net/runelite/client/plugins/batools/CycleCounter.java new file mode 100644 index 0000000000..e8f6e3e639 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/batools/CycleCounter.java @@ -0,0 +1,14 @@ +package net.runelite.client.plugins.batools; + +import net.runelite.client.plugins.Plugin; +import net.runelite.client.ui.overlay.infobox.Counter; + +import java.awt.image.BufferedImage; + +public class CycleCounter extends Counter +{ + public CycleCounter(BufferedImage img, Plugin plugin, int tick) + { + super(img, plugin, tick); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/batools/Healer.java b/runelite-client/src/main/java/net/runelite/client/plugins/batools/Healer.java new file mode 100644 index 0000000000..9fb07e90e8 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/batools/Healer.java @@ -0,0 +1,84 @@ +package net.runelite.client.plugins.batools; + + +import lombok.Getter; +import lombok.Setter; + +import net.runelite.api.NPC; +import net.runelite.api.Actor; + + +public class Healer +{ + + @Getter + private NPC npc; + + @Getter + @Setter + private int wave; + + @Getter + @Setter + private int spawnNumber; + + @Getter + @Setter + private int foodRemaining; + + @Getter + @Setter + private int lastFoodTime; + + @Getter + @Setter + private int firstCallFood; + + @Getter + @Setter + private int secondCallFood; + + + + public Healer(NPC npc, int spawnNumber, int wave) + { + this.npc = npc; + this.wave = wave; + this.spawnNumber = spawnNumber; + this.firstCallFood = getCode(wave).getFirstCallFood()[spawnNumber]; + this.secondCallFood = getCode(wave).getSecondCallFood()[spawnNumber]; + this.foodRemaining = firstCallFood + secondCallFood; + this.lastFoodTime = getCode(wave).getSpacing()[spawnNumber]; + } + + private HealerCode getCode(int wave) + { + switch(wave) + { + case 1: + return HealerCode.WAVEONE; + case 2: + return HealerCode.WAVETWO; + case 3: + return HealerCode.WAVETHREE; + case 4: + return HealerCode.WAVEFOUR; + case 5: + return HealerCode.WAVEFIVE; + case 6: + return HealerCode.WAVESIX; + case 7: + return HealerCode.WAVESEVEN; + case 8: + return HealerCode.WAVEEIGHT; + case 9: + return HealerCode.WAVENINE; + case 10: + return HealerCode.WAVETEN; + default: return null; + } + } + + + +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/batools/HealerCode.java b/runelite-client/src/main/java/net/runelite/client/plugins/batools/HealerCode.java new file mode 100644 index 0000000000..ee7f492585 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/batools/HealerCode.java @@ -0,0 +1,34 @@ +package net.runelite.client.plugins.batools; + +import lombok.Getter; + + +enum HealerCode +{ + + WAVEONE(new int[] {1,1}, new int[] {0,0}, new int[] {0,0}), + WAVETWO(new int[] {1,1,2}, new int[] {0,0,0}, new int[] {0,0,21}), + WAVETHREE(new int[] {1,6,2}, new int[] {0,0,0}, new int[] {0,0,0}), + WAVEFOUR(new int[] {2,5,2,0}, new int[] {0,0,7,10}, new int[] {0,0,0,0}), + WAVEFIVE(new int[] {2,5,2,3,0}, new int[] {0,0,0,0,7}, new int[] {0,0,21,30,0}), + WAVESIX(new int[] {3,5,3,1,0,0}, new int[] {0,0,0,2,9,10}, new int[] {18,0,0,0,0,0}), + WAVESEVEN(new int[] {5,2,1,1,0,0,0}, new int[] {0,0,0,0,6,8,10}, new int[] {27,33,0,0,51,0,0}), + WAVEEIGHT(new int[] {2,8,1,1,0,0,0}, new int[] {1,0,1,1,3,1,10}, new int[] {36,0,33,39,45,48,0}), + WAVENINE(new int[] {2,8,1,1,0,0,0,0}, new int[] {1,1,1,1,1,1,1,10}, new int[] {0,21,0,0,0,0,0,0,0}), + WAVETEN(new int[] {5,2,1,1,0,0,0}, new int[] {0,1,1,1,3,3,10}, new int[] {27,33,0,0,51,0,0}); + + + @Getter + private final int[] firstCallFood; + @Getter + private final int[] secondCallFood; + @Getter + private final int[] spacing; + + HealerCode(int[] firstCallFood, int[] secondCallFood, int[] spacing) + { + this.firstCallFood = firstCallFood; + this.secondCallFood = secondCallFood; + this.spacing = spacing; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/clanmanmode/ClanManModeConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/clanmanmode/ClanManModeConfig.java new file mode 100644 index 0000000000..3f2d8da467 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/clanmanmode/ClanManModeConfig.java @@ -0,0 +1,168 @@ +package net.runelite.client.plugins.clanmanmode; + +import java.awt.Color; +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("clanmanmode") +public interface ClanManModeConfig extends Config +{ + @ConfigItem( + position = 0, + keyName = "highlightattackable", + name = "Highlight attackable targets", + description = "Highlights targets attackable by all clan members" + ) + default boolean highlightAttackable() + { + return false; + } + + @ConfigItem( + position = 1, + keyName = "attackablecolor", + name = "Attackable target c olor", + description = "Color of targets all clan members can target" + ) + default Color getAttackableColor() + { + return new Color(0, 184, 212); + } + + @ConfigItem( + position = 2, + keyName = "highlightattacked", + name = "Highlight clan targets", + description = "Highlights people being attacked by your clan" + ) + default boolean highlightAttacked() + { + return false; + } + + @ConfigItem( + position = 3, + keyName = "attackedcolor", + name = "Clan target color", + description = "Color of players being attacked by clan" + ) + default Color getClanAttackableColor() + { + return new Color(0, 184, 212); + } + + @ConfigItem( + position = 4, + keyName = "drawPlayerTiles", + name = "Draw tiles under players", + description = "Configures whether or not tiles under highlighted players should be drawn" + ) + default boolean drawTiles() + { + return false; + } + + @ConfigItem( + position = 5, + keyName = "drawOverheadPlayerNames", + name = "Draw names above players", + description = "Configures whether or not player names should be drawn above players" + ) + default boolean drawOverheadPlayerNames() + { + return true; + } + + @ConfigItem( + position = 6, + keyName = "drawMinimapNames", + name = "Draw names on minimap", + description = "Configures whether or not minimap names for players with rendered names should be drawn" + ) + default boolean drawMinimapNames() + { + return false; + } + + @ConfigItem( + position = 7, + keyName = "showtargets", + name = "Highlight My Attackers", + description = "Shows players interacting with you" + ) + default boolean showAttackers() + { + return false; + } + + @ConfigItem( + position = 8, + keyName = "attackcolor", + name = "Attacker Color", + description = "Color of attackers" + ) + default Color getAttackerColor() + { + return new Color(255, 0, 0); + } + + @ConfigItem( + position = 9, + keyName = "showbold", + name = "Bold names of clan targets", + description = "Turns names of clan targets bold" + ) + default boolean ShowBold() { return false; } + + @ConfigItem( + position = 10, + keyName = "hideafter", + name = "Hide attackable targets after login", + description = "Automatically disables attackable player highlighting after login" + ) + default boolean hideAttackable() { return false; } + + @ConfigItem( + position = 11, + keyName = "hidetime", + name = "Ticks to hide", + description = "How many ticks after you are logged in that attackbles are hidden (1 tick = 0.6 seconds)" + ) + default int hideTime() { return 5; } + + @ConfigItem( + position = 12, + keyName = "mycblvl", + name = "Calc targets on my own combat level", + description = "Calculates potential targets based off your own combat lvl instead of clans" + ) + default boolean CalcSelfCB() { return false; } + + @ConfigItem( + position = 13, + keyName = "hideatkopt", + name = "Hide attack option for clan members", + description = "Disables attack option for clan members" + ) + default boolean hideAtkOpt() { return false; } + + @ConfigItem( + position = 14, + keyName = "showclanmembers", + name = "Persistent Clan Members", + description = "Will highlight clan members even when not in clan chat" + ) + default boolean PersistentClan() { return false; } + + @ConfigItem( + position = 15, + keyName = "clancolor", + name = "Clan Member Color", + description = "Color of clan members" + ) + default Color getClanMemberColor() + { + return new Color(255, 0, 0); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/clanmanmode/ClanManModeMinimapOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/clanmanmode/ClanManModeMinimapOverlay.java new file mode 100644 index 0000000000..be94ee06f3 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/clanmanmode/ClanManModeMinimapOverlay.java @@ -0,0 +1,52 @@ +package net.runelite.client.plugins.clanmanmode; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import javax.inject.Inject; +import javax.inject.Singleton; +import net.runelite.api.Player; +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.OverlayPriority; +import net.runelite.client.ui.overlay.OverlayUtil; + +@Singleton +public class ClanManModeMinimapOverlay extends Overlay +{ + private final ClanManModeService ClanManModeService; + private final ClanManModeConfig config; + + @Inject + private ClanManModeMinimapOverlay(ClanManModeConfig config, ClanManModeService ClanManModeService) + { + this.config = config; + this.ClanManModeService = ClanManModeService; + setLayer(OverlayLayer.ABOVE_WIDGETS); + setPosition(OverlayPosition.DYNAMIC); + setPriority(OverlayPriority.HIGH); + } + + @Override + public Dimension render(Graphics2D graphics) + { + ClanManModeService.forEachPlayer((player, color) -> renderPlayerOverlay(graphics, player, color)); + return null; + } + + private void renderPlayerOverlay(Graphics2D graphics, Player actor, Color color) + { + final String name = actor.getName().replace('\u00A0', ' '); + + if (config.drawMinimapNames()) + { + final net.runelite.api.Point minimapLocation = actor.getMinimapLocation(); + + if (minimapLocation != null) + { + OverlayUtil.renderTextLocation(graphics, minimapLocation, name, color); + } + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/clanmanmode/ClanManModeOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/clanmanmode/ClanManModeOverlay.java new file mode 100644 index 0000000000..ff058f675c --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/clanmanmode/ClanManModeOverlay.java @@ -0,0 +1,63 @@ +package net.runelite.client.plugins.clanmanmode; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; +import javax.inject.Inject; +import javax.inject.Singleton; +import net.runelite.api.ClanMemberRank; +import net.runelite.api.Player; +import net.runelite.api.Point; +import net.runelite.client.game.ClanManager; +import net.runelite.client.ui.FontManager; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.OverlayPriority; +import net.runelite.client.ui.overlay.OverlayUtil; + +@Singleton +public class ClanManModeOverlay extends Overlay +{ + private final ClanManModeService ClanManModeService; + private final ClanManModeConfig config; + private final ClanManager clanManager; + + @Inject + private ClanManModeOverlay(ClanManModeConfig config, ClanManModeService ClanManModeService, + ClanManager clanManager) + { + this.config = config; + this.ClanManModeService = ClanManModeService; + this.clanManager = clanManager; + setPosition(OverlayPosition.DYNAMIC); + setPriority(OverlayPriority.MED); + } + + @Override + public Dimension render(Graphics2D graphics) + { + ClanManModeService.forEachPlayer((player, color) -> renderPlayerOverlay(graphics, player, color)); + return null; + } + + private void renderPlayerOverlay(Graphics2D graphics, Player actor, Color color) + { + if (!config.drawOverheadPlayerNames()) + { + return; + } + + String name = actor.getName().replace('\u00A0', ' '); + int offset = actor.getLogicalHeight() + 40; + Point textLocation = actor.getCanvasTextLocation(graphics, name, offset); + + if (textLocation != null) + { + if (config.getClanAttackableColor().equals(color) && config.ShowBold()) { + graphics.setFont(FontManager.getRunescapeBoldFont()); + } + OverlayUtil.renderTextLocation(graphics, textLocation, name, color); + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/clanmanmode/ClanManModePlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/clanmanmode/ClanManModePlugin.java new file mode 100644 index 0000000000..d71d054674 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/clanmanmode/ClanManModePlugin.java @@ -0,0 +1,137 @@ +package net.runelite.client.plugins.clanmanmode; + +import net.runelite.client.eventbus.Subscribe; +import com.google.inject.Provides; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.inject.Inject; +import net.runelite.api.*; +import net.runelite.api.coords.WorldPoint; +import net.runelite.api.events.GameStateChanged; +import net.runelite.api.events.GameTick; +import net.runelite.api.events.MenuEntryAdded; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.game.ClanManager; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.ui.overlay.OverlayManager; +import net.runelite.client.util.Text; +import org.apache.commons.lang3.ArrayUtils; + +@PluginDescriptor( + name = "!Clan Man Mode", + description = "Assists in clan PVP scenarios", + tags = {"highlight", "minimap", "overlay", "players"} +) +public class ClanManModePlugin extends Plugin +{ + @Inject + private OverlayManager overlayManager; + + @Inject + private ClanManModeConfig config; + + @Inject + private ClanManModeOverlay ClanManModeOverlay; + + @Inject + private ClanManModeTileOverlay ClanManModeTileOverlay; + + @Inject + private ClanManModeMinimapOverlay ClanManModeMinimapOverlay; + + @Inject + private Client client; + + @Inject + private ClanManager clanManager; + + @Provides + ClanManModeConfig provideConfig(ConfigManager configManager) + { + return configManager.getConfig(ClanManModeConfig.class); + } + + int wildernessLevel; + int clanmin; + int clanmax; + int inwildy; + int ticks; + Map clan = new HashMap<>(); + + @Override + protected void startUp() throws Exception { + overlayManager.add(ClanManModeOverlay); + overlayManager.add(ClanManModeTileOverlay); + overlayManager.add(ClanManModeMinimapOverlay); + } + + @Override + protected void shutDown() throws Exception { + overlayManager.remove(ClanManModeOverlay); + overlayManager.remove(ClanManModeTileOverlay); + overlayManager.remove(ClanManModeMinimapOverlay); + clan.clear(); + ticks = 0; + wildernessLevel = 0; + clanmin = 0; + clanmax = 0; + inwildy = 0; + } + + @Subscribe + public void onGameStateChanged(GameStateChanged gameStateChanged) { + if (gameStateChanged.getGameState() == GameState.LOGIN_SCREEN || gameStateChanged.getGameState() == GameState.HOPPING) { + ticks = 0; + } + } + + @Subscribe + public void onGameTick(GameTick event) { + ticks++; + final Player localPlayer = client.getLocalPlayer(); + if (!clan.containsKey(localPlayer.getName())) { + clan.put(localPlayer.getName(), localPlayer.getCombatLevel()); + } + WorldPoint a = localPlayer.getWorldLocation(); + int underLevel = ((a.getY() - 9920) / 8) + 1; + int upperLevel = ((a.getY() - 3520) / 8) + 1; + wildernessLevel = a.getY() > 6400 ? underLevel : upperLevel; + inwildy = client.getVar(Varbits.IN_WILDERNESS); + if (clan.size() > 0) { + clanmin = Collections.min(clan.values()); + clanmax = Collections.max(clan.values()); + } + } + + @Subscribe + public void onMenuEntryAdded(MenuEntryAdded event) { + if (!config.hideAtkOpt()) { + return; + } + if (client.getGameState() != GameState.LOGGED_IN) { + return; + } + + final String option = Text.removeTags(event.getOption()).toLowerCase(); + + if (option.equals("attack")) { + final Pattern ppattern = Pattern.compile("(.+?) interactors = new HashMap<>(); + + public void forEachPlayer(final BiConsumer consumer) + { + int minatk = plugin.clanmax - plugin.wildernessLevel; + int maxatk = plugin.clanmin + plugin.wildernessLevel; + final Player localPlayer = client.getLocalPlayer(); + final String localName = localPlayer.getName(); + int selfmin = localPlayer.getCombatLevel() - plugin.wildernessLevel; + int selfmax = localPlayer.getCombatLevel() + plugin.wildernessLevel; + for (Player player : client.getPlayers()) + { + if (player == null || player.getName() == null) { + continue; + } + + if (player == localPlayer) { + continue; + } + + boolean isClanMember = player.isClanMember(); + Actor interacting = player.getInteracting(); + Player interactor = null; + if (interacting != null && !(interacting instanceof NPC)) { + interactor = ((Player) interacting); + } + + if (config.showAttackers()) { + if (interactor != null) { + if (interactor.getName().equals(localName)) { + consumer.accept(player, config.getAttackerColor()); + } + } + } + + if (plugin.inwildy == 1) { + if (isClanMember) { + if (!plugin.clan.containsKey(player.getName())) { + plugin.clan.put(player.getName(), player.getCombatLevel()); + } + if (config.highlightAttacked()) { + if (interactor != null) { + if (!interactors.containsKey(interactor.getName())) { + WorldPoint a = interactor.getWorldLocation(); + int underLevel = ((a.getY() - 9920) / 8) + 1; + int upperLevel = ((a.getY() - 3520) / 8) + 1; + int wildernessLevel = a.getY() > 6400 ? underLevel : upperLevel; + int wildydiff = plugin.wildernessLevel - wildernessLevel; + if (wildydiff < 0) { + wildydiff = 0; + } + if (config.CalcSelfCB()) { + if (interacting.getCombatLevel() <= selfmax && interacting.getCombatLevel() - wildydiff >= selfmin && !interactor.isClanMember()) { + interactors.put(interactor.getName(), player.getName()); + consumer.accept(interactor, config.getClanAttackableColor()); + } + } else { + if (interacting.getCombatLevel() <= maxatk && interacting.getCombatLevel() - wildydiff >= minatk && !interactor.isClanMember()) { + interactors.put(interactor.getName(), player.getName()); + consumer.accept(interactor, config.getClanAttackableColor()); + } + } + } + } + } + } else { + if (config.PersistentClan()) { + if (plugin.clan.containsKey(player.getName())) { + consumer.accept(player, config.getClanMemberColor()); + } + } + if (config.highlightAttacked()) { + if (interactors.containsKey(player.getName())) { + String attackername = interactors.get(player.getName()); + Boolean found = false; + for (Player attacker : client.getPlayers()) { + if (attacker == null || attacker.getName() == null) { + continue; + } + if (attacker.getName().equals(attackername)) { + found = true; + Actor ainteract = attacker.getInteracting(); + if (ainteract != null) { + if (ainteract.getName().equals(player.getName())) { + consumer.accept(player, config.getClanAttackableColor()); + } else { + interactors.remove(player.getName()); + } + } else { + interactors.remove(player.getName()); + } + break; + } + } + if (!found) { + interactors.remove(player.getName()); + } + continue; + } + } + if (config.highlightAttackable()) { + if ((config.hideAttackable() && plugin.ticks >= config.hideTime()) || plugin.clan.containsKey(player.getName())) { + continue; + } + WorldPoint a = player.getWorldLocation(); + int underLevel = ((a.getY() - 9920) / 8) + 1; + int upperLevel = ((a.getY() - 3520) / 8) + 1; + int wildernessLevel = a.getY() > 6400 ? underLevel : upperLevel; + int wildydiff = plugin.wildernessLevel - wildernessLevel; + if (wildydiff < 0) { + wildydiff = 0; + } + if (config.CalcSelfCB()) { + if (player.getCombatLevel() <= selfmax && player.getCombatLevel() - wildydiff >= selfmin) { + consumer.accept(player, config.getAttackableColor()); + } + } else { + if (player.getCombatLevel() <= maxatk && player.getCombatLevel() - wildydiff >= minatk) { + consumer.accept(player, config.getAttackableColor()); + } + } + } + } + } + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/clanmanmode/ClanManModeTileOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/clanmanmode/ClanManModeTileOverlay.java new file mode 100644 index 0000000000..5aea2e108f --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/clanmanmode/ClanManModeTileOverlay.java @@ -0,0 +1,48 @@ +package net.runelite.client.plugins.clanmanmode; + +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.Polygon; +import javax.inject.Inject; +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.OverlayPriority; +import net.runelite.client.ui.overlay.OverlayUtil; + +public class ClanManModeTileOverlay extends Overlay +{ + private final ClanManModeService ClanManModeService; + private final ClanManModeConfig config; + + @Inject + private ClanManModeTileOverlay(ClanManModeConfig config, ClanManModeService ClanManModeService) + { + this.config = config; + this.ClanManModeService = ClanManModeService; + setLayer(OverlayLayer.ABOVE_SCENE); + setPosition(OverlayPosition.DYNAMIC); + setPriority(OverlayPriority.MED); + } + + @Override + public Dimension render(Graphics2D graphics) + { + if (!config.drawTiles()) + { + return null; + } + + ClanManModeService.forEachPlayer((player, color) -> + { + final Polygon poly = player.getCanvasTilePoly(); + + if (poly != null) + { + OverlayUtil.renderPolygon(graphics, poly, color); + } + }); + + return null; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/equipmentinspector/EquipmentInspectorConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/equipmentinspector/EquipmentInspectorConfig.java new file mode 100644 index 0000000000..98392d4539 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/equipmentinspector/EquipmentInspectorConfig.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2017, Aria + * 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.equipmentinspector; + +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.grounditems.config.ItemHighlightMode; +import net.runelite.client.plugins.grounditems.config.MenuHighlightMode; +import net.runelite.client.plugins.grounditems.config.PriceDisplayMode; + +@ConfigGroup("grounditems") +public interface EquipmentInspectorConfig extends Config +{ + @ConfigItem( + keyName = "ShowValue", + name = "Show the total value of the items", + description = "shows the total value of the items", + position = 1 + ) + default boolean ShowValue() + { + return true; + } + @ConfigItem( + keyName = "protecteditems", + name = "# of protected items", + description = "Limit 4", + position = 2 + ) + default int protecteditems() + { return 1; } + @ConfigItem( + keyName = "ExactValue", + name = "Show exact value", + description = "shows the excact gp value", + position = 3 + ) + default boolean ExactValue() + { return false; } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/equipmentinspector/EquipmentInspectorPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/equipmentinspector/EquipmentInspectorPanel.java new file mode 100644 index 0000000000..e630c28b4f --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/equipmentinspector/EquipmentInspectorPanel.java @@ -0,0 +1,98 @@ +package net.runelite.client.plugins.equipmentinspector; + +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.ItemComposition; +import net.runelite.api.kit.KitType; +import net.runelite.client.game.AsyncBufferedImage; +import net.runelite.client.game.ItemManager; +import net.runelite.client.ui.ColorScheme; +import net.runelite.client.ui.PluginPanel; + +import javax.inject.Inject; +import javax.inject.Singleton; +import javax.swing.*; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import java.awt.*; +import java.util.HashMap; +import java.util.Map; + +@Slf4j +@Singleton +public class EquipmentInspectorPanel extends PluginPanel +{ + private final static String NO_PLAYER_SELECTED = "No player selected"; + + private GridBagConstraints c; + private JPanel equipmentPanels; + private JPanel header; + private JLabel nameLabel; + + @Inject + private ItemManager itemManager; + + public EquipmentInspectorPanel() + { + GroupLayout layout = new GroupLayout(this); + setLayout(layout); + setBorder(new EmptyBorder(10, 10, 10, 10)); + setBackground(ColorScheme.DARK_GRAY_COLOR); + + equipmentPanels = new JPanel(new GridBagLayout()); + c = new GridBagConstraints(); + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1; + c.gridx = 0; + c.gridy = 0; + + header = new JPanel(); + header.setLayout(new BorderLayout()); + header.setBorder(new CompoundBorder( + BorderFactory.createMatteBorder(0, 0, 1, 0, new Color(58, 58, 58)), + BorderFactory.createEmptyBorder(0, 0, 10, 0))); + + nameLabel = new JLabel(NO_PLAYER_SELECTED); + nameLabel.setForeground(Color.WHITE); + + header.add(nameLabel, BorderLayout.CENTER); + + layout.setHorizontalGroup(layout.createParallelGroup() + .addComponent(equipmentPanels) + .addComponent(header) + ); + layout.setVerticalGroup(layout.createSequentialGroup() + .addComponent(header) + .addGap(10) + .addComponent(equipmentPanels) + ); + + update(new HashMap<>(), ""); + } + + public void update(Map playerEquipment, String playerName) + { + if (playerName.isEmpty() || playerName == null) + { + nameLabel.setText(NO_PLAYER_SELECTED); + } + else + { + nameLabel.setText("Player: " + playerName); + } + + SwingUtilities.invokeLater(() -> + { + equipmentPanels.removeAll(); + playerEquipment.forEach((kitType, itemComposition) -> + { + AsyncBufferedImage itemImage = itemManager.getImage(itemComposition.getId()); + equipmentPanels.add(new ItemPanel(itemComposition, kitType, itemImage), c); + c.gridy++; + + }); + header.revalidate(); + header.repaint(); + } + ); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/equipmentinspector/EquipmentInspectorPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/equipmentinspector/EquipmentInspectorPlugin.java new file mode 100644 index 0000000000..8af1567609 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/equipmentinspector/EquipmentInspectorPlugin.java @@ -0,0 +1,240 @@ +package net.runelite.client.plugins.equipmentinspector; + + +import com.google.inject.Provides; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.*; +import net.runelite.api.events.PlayerMenuOptionClicked; +import net.runelite.api.kit.KitType; +import net.runelite.client.chat.ChatColorType; +import net.runelite.client.chat.ChatMessageBuilder; +import net.runelite.client.chat.ChatMessageManager; +import net.runelite.client.chat.QueuedMessage; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.game.ItemManager; +import net.runelite.client.menus.MenuManager; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.ui.ClientToolbar; +import net.runelite.client.ui.NavigationButton; +import net.runelite.client.util.Text; +import net.runelite.http.api.item.ItemPrice; + +import javax.annotation.Nullable; +import javax.imageio.ImageIO; +import javax.inject.Inject; +import javax.swing.*; +import java.awt.image.BufferedImage; +import java.lang.reflect.InvocationTargetException; +import java.text.NumberFormat; +import java.util.*; +import java.util.concurrent.ScheduledExecutorService; + +@PluginDescriptor( + name = "!Equipment Inspector", + enabledByDefault = false +) + +@Slf4j + +public class EquipmentInspectorPlugin extends Plugin { + + private static final String INSPECT_EQUIPMENT = "Gear"; + private static final String KICK_OPTION = "Kick"; + + @Inject + @Nullable + private Client client; + + @Inject + private ItemManager itemManager; + + @Inject + private EquipmentInspectorConfig config; + + @Inject + private ChatMessageManager chatMessageManager; + @Inject + private MenuManager menuManager; + + @Inject + private ScheduledExecutorService executor; + + @Inject + private ClientToolbar pluginToolbar; + + @Provides + EquipmentInspectorConfig provideConfig(ConfigManager configManager) + { + return configManager.getConfig(EquipmentInspectorConfig.class); + } + + private NavigationButton navButton; + private EquipmentInspectorPanel equipmentInspectorPanel; + private int TotalPrice = 0; + private int Prot1 = 0; + private int Prot2 = 0; + private int Prot3 = 0; + private int Prot4 = 0; + + + @Override + protected void startUp() throws Exception + { + + equipmentInspectorPanel = injector.getInstance(EquipmentInspectorPanel.class); + if(client != null) { + menuManager.addPlayerMenuItem(INSPECT_EQUIPMENT); + } + + BufferedImage icon; + synchronized (ImageIO.class) + { + icon = ImageIO.read(getClass().getResourceAsStream("normal.png")); + } + + navButton = NavigationButton.builder() + .tooltip("Equipment Inspector") + .icon(icon) + .priority(5) + .panel(equipmentInspectorPanel) + .build(); + + + pluginToolbar.addNavigation(navButton); + + } + + @Override + protected void shutDown() throws Exception + { + + menuManager.removePlayerMenuItem(INSPECT_EQUIPMENT); + } + + @Subscribe + public void onPlayerMenuOptionClicked(PlayerMenuOptionClicked event) + { + if (event.getMenuOption().equals(INSPECT_EQUIPMENT)) + { + + + executor.execute(() -> + { + try + { + SwingUtilities.invokeAndWait(() -> + { + if (!navButton.isSelected()) + { + navButton.getOnSelect().run(); + } + }); + } + catch (InterruptedException | InvocationTargetException e) + { + + throw new RuntimeException(e); + + } + String playerName = Text.removeTags(event.getMenuTarget()); + // The player menu uses a non-breaking space in the player name, we need to replace this to compare + // against the playerName in the player cache. + String finalPlayerName = playerName.replace('\u00A0', ' '); + System.out.println(finalPlayerName); + List players = client.getPlayers(); + Optional targetPlayer = players.stream() + .filter(Objects::nonNull) + .filter(p -> p.getName().equals(finalPlayerName)).findFirst(); + + if (targetPlayer.isPresent()) + { + TotalPrice = 0; + Prot1 = 0; + Prot2 = 0; + Prot3 = 0; + Prot4 = 0; + Player p = targetPlayer.get(); + Map playerEquipment = new HashMap<>(); + + for (KitType kitType : KitType.values()) + { + int itemId = p.getPlayerComposition().getEquipmentId(kitType); + if (itemId != -1) + { + ItemComposition itemComposition = client.getItemDefinition(itemId); + playerEquipment.put(kitType, itemComposition); + int ItemPrice = itemManager.getItemPrice(itemId); + TotalPrice += ItemPrice; + if (ItemPrice > Prot1 ) { + Prot4 = Prot3; + Prot3 = Prot2; + Prot2 = Prot1; + + Prot1 = ItemPrice; + } else if (ItemPrice > Prot2){ + Prot4 = Prot3; + Prot3 = Prot2; + Prot2 = ItemPrice; + } else if (ItemPrice > Prot3){ + Prot4 = Prot3; + Prot3 = ItemPrice; + } else if (ItemPrice > Prot4){ + Prot4 = ItemPrice; + } + } + } + int IgnoredItems = config.protecteditems(); + if (IgnoredItems != 0 && IgnoredItems != 1 && IgnoredItems != 2 && IgnoredItems != 3) { + IgnoredItems = 4; + + } + if (config.ShowValue()) { + switch (IgnoredItems) { + case 1: + TotalPrice = TotalPrice - Prot1; + break; + case 2: + TotalPrice = TotalPrice - Prot1; + TotalPrice = TotalPrice - Prot2; + + break; + case 3: + TotalPrice = TotalPrice - Prot1; + TotalPrice = TotalPrice - Prot2; + TotalPrice = TotalPrice - Prot3; + break; + case 4: + TotalPrice = TotalPrice - Prot1; + TotalPrice = TotalPrice - Prot2; + TotalPrice = TotalPrice - Prot3; + TotalPrice = TotalPrice - Prot4; + break; + } + String StringPrice = ""; + if (!config.ExactValue()) { + TotalPrice = TotalPrice / 1000; + StringPrice = NumberFormat.getIntegerInstance().format(TotalPrice); + StringPrice = StringPrice + 'K'; + } + if (config.ExactValue()) { + StringPrice = NumberFormat.getIntegerInstance().format(TotalPrice); + } + chatMessageManager.queue(QueuedMessage.builder() + .type(ChatMessageType.FRIENDSCHATNOTIFICATION) + .runeLiteFormattedMessage(new ChatMessageBuilder() + .append(ChatColorType.HIGHLIGHT) + .append("Risked Value: ") + .append(ChatColorType.NORMAL) + .append(StringPrice) + .build()) + .build()); + } + equipmentInspectorPanel.update(playerEquipment, playerName); + + } + }); + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/equipmentinspector/ItemPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/equipmentinspector/ItemPanel.java new file mode 100644 index 0000000000..873bf058b6 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/equipmentinspector/ItemPanel.java @@ -0,0 +1,52 @@ +package net.runelite.client.plugins.equipmentinspector; + +import net.runelite.api.ItemComposition; +import net.runelite.api.kit.KitType; +import net.runelite.client.game.AsyncBufferedImage; +import net.runelite.client.ui.ColorScheme; +import net.runelite.client.ui.FontManager; +import org.apache.commons.lang3.StringUtils; + +import javax.swing.*; +import javax.swing.border.EmptyBorder; + +class ItemPanel extends JPanel +{ + + ItemPanel(ItemComposition item, KitType kitType, AsyncBufferedImage icon) + { + setBorder(new EmptyBorder(3, 3, 3, 3)); + setBackground(ColorScheme.DARK_GRAY_COLOR); + + GroupLayout layout = new GroupLayout(this); + this.setLayout(layout); + + JLabel name = new JLabel(item.getName()); + + JLabel location = new JLabel(StringUtils.capitalize(kitType.toString().toLowerCase())); + location.setFont(FontManager.getRunescapeSmallFont()); + + JLabel imageLabel = new JLabel(); + icon.addTo(imageLabel); + + layout.setVerticalGroup(layout.createParallelGroup() + .addComponent(imageLabel) + .addGroup(layout.createSequentialGroup() + .addComponent(name) + .addComponent(location) + ) + ); + + layout.setHorizontalGroup(layout.createSequentialGroup() + .addComponent(imageLabel) + .addGap(8) + .addGroup(layout.createParallelGroup() + .addComponent(name) + .addComponent(location) + ) + ); + + // AWT's Z order is weird. This put image at the back of the stack + setComponentZOrder(imageLabel, getComponentCount() - 1); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/equipmentinspector/normal.png b/runelite-client/src/main/java/net/runelite/client/plugins/equipmentinspector/normal.png new file mode 100644 index 0000000000..613f95e46d Binary files /dev/null and b/runelite-client/src/main/java/net/runelite/client/plugins/equipmentinspector/normal.png differ diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/fightcavejadhelper/FightCaveJadHelperOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/fightcavejadhelper/FightCaveJadHelperOverlay.java new file mode 100644 index 0000000000..3698b395c1 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/fightcavejadhelper/FightCaveJadHelperOverlay.java @@ -0,0 +1,63 @@ +package net.runelite.client.plugins.fightcavejadhelper; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; +import javax.inject.Inject; +import net.runelite.api.Client; +import net.runelite.api.SpriteID; +import net.runelite.client.game.SpriteManager; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.OverlayPriority; +import net.runelite.client.ui.overlay.components.ComponentConstants; +import net.runelite.client.ui.overlay.components.ImageComponent; +import net.runelite.client.ui.overlay.components.PanelComponent; + +public class FightCaveJadHelperOverlay extends Overlay +{ + private static final Color NOT_ACTIVATED_BACKGROUND_COLOR = new Color(150, 0, 0, 150); + + private final Client client; + private final FightCaveJadHelperPlugin plugin; + private final SpriteManager spriteManager; + private final PanelComponent imagePanelComponent = new PanelComponent(); + + @Inject + private FightCaveJadHelperOverlay(Client client, FightCaveJadHelperPlugin plugin, SpriteManager spriteManager) + { + setPosition(OverlayPosition.BOTTOM_RIGHT); + setPriority(OverlayPriority.HIGH); + this.client = client; + this.plugin = plugin; + this.spriteManager = spriteManager; + } + + @Override + public Dimension render(Graphics2D graphics) + { + final JadAttack attack = plugin.getAttack(); + + if (attack == null) + { + return null; + } + + final BufferedImage prayerImage = getPrayerImage(attack); + + imagePanelComponent.getChildren().clear(); + imagePanelComponent.getChildren().add(new ImageComponent(prayerImage)); + imagePanelComponent.setBackgroundColor(client.isPrayerActive(attack.getPrayer()) + ? ComponentConstants.STANDARD_BACKGROUND_COLOR + : NOT_ACTIVATED_BACKGROUND_COLOR); + + return imagePanelComponent.render(graphics); + } + + private BufferedImage getPrayerImage(JadAttack attack) + { + final int prayerSpriteID = attack == JadAttack.MAGIC ? SpriteID.PRAYER_PROTECT_FROM_MAGIC : SpriteID.PRAYER_PROTECT_FROM_MISSILES; + return spriteManager.getSprite(prayerSpriteID, 0); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/fightcavejadhelper/FightCaveJadHelperPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/fightcavejadhelper/FightCaveJadHelperPlugin.java new file mode 100644 index 0000000000..4eeb311101 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/fightcavejadhelper/FightCaveJadHelperPlugin.java @@ -0,0 +1,89 @@ +package net.runelite.client.plugins.fightcavejadhelper; + +import javax.annotation.Nullable; +import javax.inject.Inject; +import lombok.AccessLevel; +import lombok.Getter; +import net.runelite.api.NPC; +import net.runelite.api.NpcID; +import net.runelite.api.events.AnimationChanged; +import net.runelite.api.events.NpcDespawned; +import net.runelite.api.events.NpcSpawned; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.ui.overlay.OverlayManager; + +@PluginDescriptor( + name = "!Fight Cave - Jad", + description = "Show what to pray against Jad", + tags = {"bosses", "combat", "minigame", "overlay", "prayer", "pve", "pvm"}, + enabledByDefault = false +) +public class FightCaveJadHelperPlugin extends Plugin +{ + @Inject + private OverlayManager overlayManager; + + @Inject + private FightCaveJadHelperOverlay overlay; + + @Getter(AccessLevel.PACKAGE) + @Nullable + private JadAttack attack; + + private NPC jad; + + @Override + protected void startUp() throws Exception + { + overlayManager.add(overlay); + } + + @Override + protected void shutDown() throws Exception + { + overlayManager.remove(overlay); + jad = null; + attack = null; + } + + @Subscribe + public void onNpcSpawned(final NpcSpawned event) + { + final int id = event.getNpc().getId(); + + if (id == NpcID.TZTOKJAD || id == NpcID.TZTOKJAD_6506) + { + jad = event.getNpc(); + } + } + + @Subscribe + public void onNpcDespawned(final NpcDespawned event) + { + if (jad == event.getNpc()) + { + jad = null; + attack = null; + } + } + + @Subscribe + public void onAnimationChanged(final AnimationChanged event) + { + if (event.getActor() != jad) + { + return; + } + + if (jad.getAnimation() == JadAttack.MAGIC.getAnimation()) + { + attack = JadAttack.MAGIC; + } + else if (jad.getAnimation() == JadAttack.RANGE.getAnimation()) + { + attack = JadAttack.RANGE; + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/fightcavejadhelper/JadAttack.java b/runelite-client/src/main/java/net/runelite/client/plugins/fightcavejadhelper/JadAttack.java new file mode 100644 index 0000000000..9d9ec47a4b --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/fightcavejadhelper/JadAttack.java @@ -0,0 +1,29 @@ +package net.runelite.client.plugins.fightcavejadhelper; + +import net.runelite.api.AnimationID; +import net.runelite.api.Prayer; + +public enum JadAttack +{ + MAGIC(AnimationID.TZTOK_JAD_MAGIC_ATTACK, Prayer.PROTECT_FROM_MAGIC), + RANGE(AnimationID.TZTOK_JAD_RANGE_ATTACK, Prayer.PROTECT_FROM_MISSILES); + + private final int animation; + private final Prayer prayer; + + JadAttack(int animation, Prayer prayer) + { + this.animation = animation; + this.prayer = prayer; + } + + public int getAnimation() + { + return animation; + } + + public Prayer getPrayer() + { + return prayer; + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/fightcavewavehelper/FightCaveWaveHelperConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/fightcavewavehelper/FightCaveWaveHelperConfig.java new file mode 100644 index 0000000000..989c4965e8 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/fightcavewavehelper/FightCaveWaveHelperConfig.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018, Jordan Atwood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.fightcavewavehelper; + +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("Fight Cave - Wave Helper") +public interface FightCaveWaveHelperConfig extends Config +{ + @ConfigItem( + keyName = "waveDisplay", + name = "Wave display", + description = "Shows monsters that will spawn on the selected wave(s)." + ) + default WaveDisplayMode waveDisplay() + { + return WaveDisplayMode.BOTH; + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/fightcavewavehelper/FightCaveWaveHelperPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/fightcavewavehelper/FightCaveWaveHelperPlugin.java new file mode 100644 index 0000000000..d06451a9d0 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/fightcavewavehelper/FightCaveWaveHelperPlugin.java @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2018, Jordan Atwood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.fightcavewavehelper; + +import com.google.inject.Provides; +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.inject.Inject; +import lombok.Getter; +import net.runelite.api.ChatMessageType; +import net.runelite.api.Client; +import net.runelite.api.GameState; +import net.runelite.api.events.ChatMessage; +import net.runelite.api.events.GameStateChanged; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.ui.overlay.OverlayManager; +import org.apache.commons.lang3.ArrayUtils; + +@PluginDescriptor( + name = "!Fight Cave - Waves", + description = "Displays current and upcoming wave monsters in the Fight Caves", + tags = {"bosses", "combat", "minigame", "overlay", "pve", "pvm", "jad", "fire", "cape", "wave"}, + enabledByDefault = false +) +public class FightCaveWaveHelperPlugin extends Plugin +{ + private static final Pattern WAVE_PATTERN = Pattern.compile(".*Wave: (\\d+).*"); + private static final int FIGHT_CAVE_REGION = 9551; + private static final int MAX_MONSTERS_OF_TYPE_PER_WAVE = 2; + + static final int MAX_WAVE = 63; + + @Getter + static final List> WAVES = new ArrayList<>(); + + @Getter + private int currentWave = -1; + + @Inject + private Client client; + + @Inject + private OverlayManager overlayManager; + + @Inject + private WaveOverlay waveOverlay; + + static + { + final WaveMonster[] waveMonsters = WaveMonster.values(); + + // Add wave 1, future waves are derived from its contents + final EnumMap waveOne = new EnumMap<>(WaveMonster.class); + waveOne.put(waveMonsters[0], 1); + WAVES.add(waveOne); + + for (int wave = 1; wave < MAX_WAVE; wave++) + { + final EnumMap prevWave = WAVES.get(wave - 1).clone(); + int maxMonsterOrdinal = -1; + + for (int i = 0; i < waveMonsters.length; i++) + { + final int ordinalMonsterQuantity = prevWave.getOrDefault(waveMonsters[i], 0); + + if (ordinalMonsterQuantity == MAX_MONSTERS_OF_TYPE_PER_WAVE) + { + maxMonsterOrdinal = i; + break; + } + } + + if (maxMonsterOrdinal >= 0) + { + prevWave.remove(waveMonsters[maxMonsterOrdinal]); + } + + final int addedMonsterOrdinal = maxMonsterOrdinal >= 0 ? maxMonsterOrdinal + 1 : 0; + final WaveMonster addedMonster = waveMonsters[addedMonsterOrdinal]; + final int addedMonsterQuantity = prevWave.getOrDefault(addedMonster, 0); + + prevWave.put(addedMonster, addedMonsterQuantity + 1); + + WAVES.add(prevWave); + } + } + + @Provides + FightCaveWaveHelperConfig provideConfig(ConfigManager configManager) + { + return configManager.getConfig(FightCaveWaveHelperConfig.class); + } + + @Override + public void startUp() + { + overlayManager.add(waveOverlay); + } + + @Override + public void shutDown() + { + overlayManager.remove(waveOverlay); + currentWave = -1; + } + + @Subscribe + public void onGameStateChanged(GameStateChanged event) + { + if (event.getGameState() != GameState.LOGGED_IN) + { + return; + } + + if (!inFightCave()) + { + currentWave = -1; + } + } + + @Subscribe + public void onChatMessage(ChatMessage event) + { + final Matcher waveMatcher = WAVE_PATTERN.matcher(event.getMessage()); + + if (event.getType() != ChatMessageType.GAMEMESSAGE + || !inFightCave() + || !waveMatcher.matches()) + { + return; + } + + currentWave = Integer.parseInt(waveMatcher.group(1)); + } + + boolean inFightCave() + { + return ArrayUtils.contains(client.getMapRegions(), FIGHT_CAVE_REGION); + } + + static String formatMonsterQuantity(final WaveMonster monster, final int quantity) + { + return String.format("%dx %s", quantity, monster); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/fightcavewavehelper/WaveDisplayMode.java b/runelite-client/src/main/java/net/runelite/client/plugins/fightcavewavehelper/WaveDisplayMode.java new file mode 100644 index 0000000000..79a9d8174e --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/fightcavewavehelper/WaveDisplayMode.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018, Jordan Atwood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.fightcavewavehelper; + +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public enum WaveDisplayMode +{ + CURRENT("Current wave"), + NEXT("Next wave"), + BOTH("Both"); + + private final String name; + + @Override + public String toString() + { + return name; + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/fightcavewavehelper/WaveMonster.java b/runelite-client/src/main/java/net/runelite/client/plugins/fightcavewavehelper/WaveMonster.java new file mode 100644 index 0000000000..df2fa9b7af --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/fightcavewavehelper/WaveMonster.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018, Jordan Atwood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.fightcavewavehelper; + +import lombok.AllArgsConstructor; + +@AllArgsConstructor +enum WaveMonster +{ + TZ_KIH("Tz-Kih", 22), + TZ_KEK("Tz-Kek", 45), + TOK_XIL("Tok-Xil", 90), + YT_MEJKOT("Yt-MejKot", 180), + KET_ZEK("Ket-Zek", 360), + TZKOK_JAD("TzTok-Jad", 702); + + private final String name; + private final int level; + + @Override + public String toString() + { + return String.format("%s - Level %s", name, level); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/fightcavewavehelper/WaveOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/fightcavewavehelper/WaveOverlay.java new file mode 100644 index 0000000000..b5e6878b02 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/fightcavewavehelper/WaveOverlay.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2018, Jordan Atwood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.fightcavewavehelper; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import javax.inject.Inject; +import net.runelite.client.ui.ColorScheme; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.components.LineComponent; +import net.runelite.client.ui.overlay.components.PanelComponent; +import net.runelite.client.ui.overlay.components.TitleComponent; + +class WaveOverlay extends Overlay +{ + private static final Color HEADER_COLOR = ColorScheme.BRAND_ORANGE; + + private final FightCaveWaveHelperConfig config; + private final FightCaveWaveHelperPlugin plugin; + + private final PanelComponent panelComponent = new PanelComponent(); + + @Inject + private WaveOverlay(FightCaveWaveHelperConfig config, FightCaveWaveHelperPlugin plugin) + { + setPosition(OverlayPosition.TOP_RIGHT); + this.config = config; + this.plugin = plugin; + } + + @Override + public Dimension render(Graphics2D graphics) + { + if (!plugin.inFightCave() + || plugin.getCurrentWave() < 0) + { + return null; + } + + panelComponent.getChildren().clear(); + + final int currentWave = plugin.getCurrentWave(); + final int waveIndex = currentWave - 1; + + if (config.waveDisplay() == WaveDisplayMode.CURRENT + || config.waveDisplay() == WaveDisplayMode.BOTH) + { + final Map waveContents = FightCaveWaveHelperPlugin.getWAVES().get(waveIndex); + + addWaveInfo("Wave " + plugin.getCurrentWave(), waveContents); + } + + if ((config.waveDisplay() == WaveDisplayMode.NEXT + || config.waveDisplay() == WaveDisplayMode.BOTH) + && currentWave != FightCaveWaveHelperPlugin.MAX_WAVE) + { + final Map waveContents = FightCaveWaveHelperPlugin.getWAVES().get(waveIndex + 1); + + addWaveInfo("Next wave", waveContents); + } + + return panelComponent.render(graphics); + } + + private void addWaveInfo(final String headerText, final Map waveContents) + { + panelComponent.getChildren().add(TitleComponent.builder() + .text(headerText) + .color(HEADER_COLOR) + .build()); + + for (LineComponent line : buildWaveLines(waveContents)) + { + panelComponent.getChildren().add(line); + } + } + + private static Collection buildWaveLines(final Map wave) + { + final List> monsters = new ArrayList<>(wave.entrySet()); + monsters.sort(Map.Entry.comparingByKey()); + final List outputLines = new ArrayList<>(); + + for (Map.Entry monsterEntry : monsters) + { + final WaveMonster monster = monsterEntry.getKey(); + final int quantity = monsterEntry.getValue(); + final LineComponent line = LineComponent.builder() + .left(FightCaveWaveHelperPlugin.formatMonsterQuantity(monster, quantity)) + .build(); + + outputLines.add(line); + } + + return outputLines; + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/freezetimers/Barrage.java b/runelite-client/src/main/java/net/runelite/client/plugins/freezetimers/Barrage.java new file mode 100644 index 0000000000..6b80dd4ce3 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/freezetimers/Barrage.java @@ -0,0 +1,43 @@ +package net.runelite.client.plugins.freezetimers; + +import net.runelite.api.Actor; +import net.runelite.client.plugins.freezetimers.Spell; +import net.runelite.client.util.Text; + +public class Barrage +extends Spell { + public static final long DURATION = 20000L; + private long remainingTime; + private boolean isFinished; + + public Barrage(Actor affectedTarget, Actor caster) { + super(affectedTarget, caster); + } + + public long getRemainingTime() { + long elapsedTime = System.currentTimeMillis() - this.startTime; + if (Barrage.getDURATION() > elapsedTime) { + return Barrage.getDURATION() - elapsedTime; + } + this.isFinished = true; + return 0L; + } + + public boolean equals(Object o) { + if (o instanceof Barrage) { + Barrage barrage = (Barrage)o; + return Text.standardize(this.getAffectedTarget().getName()).equals(Text.standardize(((Barrage)o).getAffectedTarget().getName())) && this.getStartTime() == ((Barrage)o).getStartTime(); + } + return false; + } + + public static long getDURATION() { + return 20000L; + } + + @Override + public boolean isFinished() { + return this.isFinished; + } +} + diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/freezetimers/FreezeTimersConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/freezetimers/FreezeTimersConfig.java new file mode 100644 index 0000000000..512905759d --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/freezetimers/FreezeTimersConfig.java @@ -0,0 +1,51 @@ +package net.runelite.client.plugins.freezetimers; + +import java.awt.Color; +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup(value="freezetimers") +public interface FreezeTimersConfig +extends Config { + @ConfigItem(position=0, keyName="freezeenable", name="Enable PvP freeze timers", description="Configures whether or not to show freeze timers.") + default public boolean EnableFreezeTimers() { + return false; + } + + @ConfigItem(position=1, keyName="tilehighlight", name="Frozen opponent tile highlighting", description="Configures whether or not to highlight tiles frozen opponents are standing on.") + default public boolean drawTiles() { + return false; + } + + @ConfigItem(position=2, keyName="timercolor", name="Freeze Timer Color", description="Color of freeze timer") + default public Color FreezeTimerColor() { + return new Color(0, 184, 212); + } + + @ConfigItem(position=3, keyName="spellIcon", name="Show spell icon", description="Shows the spell icon for the freeze spell affecting the target") + default public boolean spellIcon() { + return true; + } + + @ConfigItem(position=4, keyName="refreezeTimer", name="Refreeze Timer", description="Show a timer that counts up until the target can be refrozen") + default public boolean refreezeTimer() { + return true; + } + + @ConfigItem(position=5, keyName="refreezeTimerColor", name="Refreeze color", description="The color for the timer that counts until the target can be refrozen") + default public Color RefreezeTimerColor() { + return Color.red; + } + + @ConfigItem(position = 6, keyName = "tbtimer", name = "Tele Block Timer", description = "Enables tele block timer") + default boolean TBTimer() { + return true; + } + + @ConfigItem(position = 7, keyName = "timerpos", name = "Freeze Timer Position", description = "Position of freeze timer") + default int FreezeTimerPos() { + return 80; + } +} + diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/freezetimers/FreezeTimersOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/freezetimers/FreezeTimersOverlay.java new file mode 100644 index 0000000000..1d7dbd162e --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/freezetimers/FreezeTimersOverlay.java @@ -0,0 +1,157 @@ +package net.runelite.client.plugins.freezetimers; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics2D; +import java.awt.Stroke; +import java.awt.image.BufferedImage; +import java.util.function.BiConsumer; +import javax.inject.Inject; +import javax.inject.Singleton; +import net.runelite.api.Client; +import net.runelite.api.HeadIcon; +import net.runelite.api.Player; +import net.runelite.api.Point; +import net.runelite.client.game.SpriteManager; +import net.runelite.client.plugins.freezetimers.FreezeTimersConfig; +import net.runelite.client.plugins.freezetimers.FreezeTimersPlugin; +import net.runelite.client.plugins.freezetimers.FreezeTimersService; +import net.runelite.client.ui.FontManager; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.OverlayPriority; +import net.runelite.client.ui.overlay.OverlayUtil; + +@Singleton +public class FreezeTimersOverlay +extends Overlay { + private final FreezeTimersService FreezeTimersService; + private final FreezeTimersConfig config; + private final FreezeTimersPlugin plugin; + private final SpriteManager spriteManager; + private final Client client; + + @Inject + private FreezeTimersOverlay(FreezeTimersConfig config, FreezeTimersService FreezeTimersService2, FreezeTimersPlugin plugin, Client client, SpriteManager spriteManager) { + this.config = config; + this.FreezeTimersService = FreezeTimersService2; + this.plugin = plugin; + this.client = client; + this.spriteManager = spriteManager; + this.setPosition(OverlayPosition.DYNAMIC); + this.setPriority(OverlayPriority.MED); + } + + @Override + public Dimension render(Graphics2D graphics) { + if (!this.config.EnableFreezeTimers()) { + return null; + } + this.FreezeTimersService.forEachPlayer((player, color) -> this.renderPlayerOverlay(graphics, (Player)player, (Color)color)); + return null; + } + + private void renderPlayerOverlay(Graphics2D graphics, Player actor, Color color) { + BufferedImage clanchatImage; + int timer = 0; + String name = actor.getName(); + int freezetype = this.plugin.freezetype(name); + boolean frozenoverlay = false; + int offset = 5; + long dtime = this.plugin.opponentfreezetime(name); + long tbed = plugin.istbed(name); + Point textLocation = null; + HeadIcon headIcon = actor.getOverheadIcon(); + int freezetime = 0; + if (freezetype == 1 || freezetype == 4) { + freezetime = 5000; + } else if (freezetype == 2 || freezetype == 5) { + freezetime = 10000; + } else if (freezetype == 3 || freezetype == 6) { + freezetime = 15000; + } else if (freezetype == 7) { + freezetime = 20000; + } else if (freezetype == 8) { + freezetime = 2500; + } else if (freezetype == 9) { + freezetime = 5000; + } else if (freezetype == 10) { + freezetime = 7500; + } + long currenttime = System.currentTimeMillis(); + long timediff = currenttime - dtime; + timer = (freezetime - (int)timediff) / 1000; + if (timediff < (long)freezetime) { + textLocation = actor.getCanvasTextLocation(graphics, String.valueOf(timer), actor.getLogicalHeight() + config.FreezeTimerPos()); + textLocation = new Point(textLocation.getX(), textLocation.getY() - config.FreezeTimerPos()); + } else if (timediff < (long)(freezetime + 3000)) { + timer = Math.abs(timer); + ++timer; + if (this.config.refreezeTimer()) { + textLocation = actor.getCanvasTextLocation(graphics, String.valueOf(timer), actor.getLogicalHeight() + config.FreezeTimerPos()); + textLocation = new Point(textLocation.getX(), textLocation.getY() - config.FreezeTimerPos()); + graphics.setFont(FontManager.getRunescapeBoldFont()); + if (headIcon != null) { + textLocation = new Point(textLocation.getX(), textLocation.getY() - config.FreezeTimerPos()); + } + frozenoverlay = true; + OverlayUtil.renderTextLocation(graphics, textLocation, String.valueOf(timer), this.config.RefreezeTimerColor()); + return; + } + } else { + this.plugin.deleteopponent(name); + } + if (textLocation != null && (clanchatImage = this.plugin.GetFreezeIcon(freezetype - 1)) != null) { + int width = clanchatImage.getWidth(); + int textHeight = graphics.getFontMetrics().getHeight() - graphics.getFontMetrics().getMaxDescent(); + Point imageLocation = new Point(textLocation.getX(), textLocation.getY() - (config.FreezeTimerPos() / 2)); + graphics.setFont(FontManager.getRunescapeFont()); + graphics.setStroke(new BasicStroke(3.0f)); + if (this.config.spellIcon()) { + frozenoverlay = true; + graphics.drawOval(imageLocation.getX(), imageLocation.getY(), clanchatImage.getWidth(), clanchatImage.getHeight()); + OverlayUtil.renderImageLocation(graphics, imageLocation, clanchatImage); + OverlayUtil.renderTextLocation(graphics, textLocation, String.valueOf(timer), color); + } else { + graphics.setColor(Color.cyan); + graphics.drawOval(textLocation.getX() - 3, textLocation.getY() - 15, clanchatImage.getWidth(), graphics.getFontMetrics().getHeight()); + graphics.setColor(Color.blue); + graphics.fillOval(textLocation.getX() - 3, textLocation.getY() - 15, clanchatImage.getWidth(), graphics.getFontMetrics().getHeight()); + OverlayUtil.renderTextLocation(graphics, textLocation, String.valueOf(timer), Color.WHITE); + } + } + + if (config.TBTimer()) { + if (tbed > 0) { + int type = plugin.tbtype(name); + int tbexpiry; + if (type > 0) { + if (type == 1) { + tbexpiry = 300000; + } else if (type == 2) { + tbexpiry = 150000; + } else { + return; + } + long tbtime = currenttime - tbed; + int tbtimer = (tbexpiry - (int) tbtime) / 1000; + if (tbtime < tbexpiry) { + textLocation = actor.getCanvasTextLocation(graphics, Integer.toString(tbtimer), actor.getLogicalHeight() + config.FreezeTimerPos()); + if (frozenoverlay) { + textLocation = new Point(textLocation.getX() + 40, textLocation.getY() - config.FreezeTimerPos()); + } else { + textLocation = new Point(textLocation.getX(), textLocation.getY() - config.FreezeTimerPos()); + } + } else { + plugin.deletetb(name); + } + } + + } + } + } +} + diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/freezetimers/FreezeTimersPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/freezetimers/FreezeTimersPlugin.java new file mode 100644 index 0000000000..878270bfd4 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/freezetimers/FreezeTimersPlugin.java @@ -0,0 +1,402 @@ +package net.runelite.client.plugins.freezetimers; + + + + + +import com.google.inject.Provides; +import java.awt.*; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.image.*; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.DataBuffer; +import java.awt.image.DataBufferByte; +import java.awt.image.ImageObserver; +import java.awt.image.IndexColorModel; +import java.awt.image.WritableRaster; +import java.util.*; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.inject.Inject; +import net.runelite.api.*; +import net.runelite.api.Actor; +import net.runelite.api.Client; +import net.runelite.api.events.*; +import net.runelite.api.GameState; +import net.runelite.api.HeadIcon; +import net.runelite.api.IndexedSprite; +import net.runelite.api.Player; +import net.runelite.api.Skill; +import net.runelite.api.coords.WorldPoint; +import net.runelite.api.events.AnimationChanged; +import net.runelite.api.events.ExperienceChanged; +import net.runelite.api.events.GameStateChanged; +import net.runelite.api.events.GameTick; +import net.runelite.api.events.MenuOptionClicked; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.game.SpriteManager; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.freezetimers.Barrage; +import net.runelite.client.plugins.freezetimers.FreezeTimersConfig; +import net.runelite.client.plugins.freezetimers.FreezeTimersOverlay; +import net.runelite.client.plugins.freezetimers.FreezeTimersTileOverlay; +import net.runelite.client.plugins.freezetimers.Spell; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayManager; +import net.runelite.client.util.ImageUtil; +import org.slf4j.Logger; + +@PluginDescriptor( + name = "!Freeze Timers", + description = "PVP Freeze Timers", + tags = {"PvP", "Freeze", "Timers"} +) + +public class FreezeTimersPlugin +extends Plugin { + @Inject + private OverlayManager overlayManager; + @Inject + private FreezeTimersConfig config; + @Inject + private FreezeTimersOverlay FreezeTimersOverlay; + @Inject + private FreezeTimersTileOverlay FreezeTimersTileOverlay; + @Inject + private Client client; + @Inject + private SpriteManager spriteManager; + + private static final int[] FREEZE_ICONS = { + SpriteID.SPELL_BIND, + SpriteID.SPELL_SNARE, + SpriteID.SPELL_ENTANGLE, + SpriteID.SPELL_ICE_RUSH, + SpriteID.SPELL_ICE_BURST, + SpriteID.SPELL_ICE_BLITZ, + SpriteID.SPELL_ICE_BARRAGE, + SpriteID.SPELL_BIND, + SpriteID.SPELL_SNARE, + SpriteID.SPELL_ENTANGLE, + SpriteID.SPELL_TELE_BLOCK + }; + private static final Dimension FREEZE_ICON_DIMENSION = new Dimension(25, 25); + private static final Color FREEZE_ICON_OUTLINE_COLOR = new Color(33, 33, 33); + private final BufferedImage[] FreezeIcons = new BufferedImage[FREEZE_ICONS.length]; + private final int SPLASH_ID = 85; + Map tbedthings = new HashMap<>(); + Map tbtypes = new HashMap<>(); + Map testMap = new HashMap(); + Map frozenthings = new HashMap(); + Map frozenthingpoints = new HashMap(); + Map freezetype = new HashMap(); + Map magexp = new HashMap(); + int lastxp; + int ticks; + int currticks; + String currtarget; + String spell; + + @Provides + FreezeTimersConfig provideConfig(ConfigManager configManager) { + return configManager.getConfig(FreezeTimersConfig.class); + } + + @Subscribe + public void onGameStateChanged(GameStateChanged gameStateChanged) { + if (gameStateChanged.getGameState() == GameState.LOGGED_IN) { + this.loadFreezeIcons(); + } + } + + @Override + protected void startUp() throws Exception { + this.overlayManager.add(this.FreezeTimersOverlay); + this.overlayManager.add(this.FreezeTimersTileOverlay); + } + + @Override + protected void shutDown() throws Exception { + this.overlayManager.remove(this.FreezeTimersOverlay); + this.overlayManager.remove(this.FreezeTimersTileOverlay); + this.frozenthings.clear(); + this.frozenthingpoints.clear(); + this.tbedthings.clear(); + this.tbtypes.clear(); + } + + @Subscribe + public void onMenuOptionClicked(MenuOptionClicked event) { + if (event.getMenuTarget().contains("->")) { + Pattern spattern = Pattern.compile(">(.+?)"); + Pattern ppattern = Pattern.compile("> (.+?) 0 && this.currtarget != null) { + if (this.frozenthings.containsKey(this.currtarget)) { + this.currtarget = null; + return; + } + WorldPoint targetPosition = null; + for (Player player : this.client.getPlayers()) { + String playerName; + if (player == null || !(playerName = player.getName()).equals(this.currtarget)) continue; + if (player.getOverheadIcon() != null && player.getOverheadIcon().equals((Object)HeadIcon.MAGIC)) { + praymage = true; + } + targetPosition = player.getWorldLocation(); + break; + } + if (targetPosition != null) { + if (this.spell.equals("Bind") && xp > 30) { + this.frozenthings.put(this.currtarget, System.currentTimeMillis()); + this.frozenthingpoints.put(this.currtarget, targetPosition); + if (praymage) { + this.freezetype.put(this.currtarget, 8); + } else { + this.freezetype.put(this.currtarget, 1); + } + } else if (this.spell.equals("Snare") && xp > 60) { + this.frozenthings.put(this.currtarget, System.currentTimeMillis()); + this.frozenthingpoints.put(this.currtarget, targetPosition); + if (praymage) { + this.freezetype.put(this.currtarget, 9); + } else { + this.freezetype.put(this.currtarget, 2); + } + } else if (this.spell.equals("Entangle") && xp >= 89) { + this.frozenthings.put(this.currtarget, System.currentTimeMillis()); + this.frozenthingpoints.put(this.currtarget, targetPosition); + if (praymage) { + this.freezetype.put(this.currtarget, 10); + } else { + this.freezetype.put(this.currtarget, 3); + } + } else if (this.spell.equals("Ice Rush") && xp > 34) { + this.frozenthings.put(this.currtarget, System.currentTimeMillis()); + this.frozenthingpoints.put(this.currtarget, targetPosition); + this.freezetype.put(this.currtarget, 4); + } else if (this.spell.equals("Ice Burst") && xp > 40) { + this.frozenthings.put(this.currtarget, System.currentTimeMillis()); + this.frozenthingpoints.put(this.currtarget, targetPosition); + this.freezetype.put(this.currtarget, 5); + } else if (this.spell.equals("Ice Blitz") && xp > 46) { + this.frozenthings.put(this.currtarget, System.currentTimeMillis()); + this.frozenthingpoints.put(this.currtarget, targetPosition); + this.freezetype.put(this.currtarget, 6); + } else if (this.spell.equals("Ice Barrage") && xp > 52) { + Barrage barrage = new Barrage(this.client.getLocalPlayer().getInteracting(), this.client.getLocalPlayer()); + this.testMap.put(this.currtarget, barrage); + this.frozenthings.put(this.currtarget, System.currentTimeMillis()); + this.frozenthingpoints.put(this.currtarget, targetPosition); + this.freezetype.put(this.currtarget, 7); + } else if (spell.equals("Tele Block") && xp == 95) { + if (config.TBTimer()) { + if (praymage) { + this.tbtypes.put(this.currtarget, 2); + } else { + this.tbtypes.put(this.currtarget, 1); + } + this.tbedthings.put(this.currtarget, System.currentTimeMillis()); + } + } + } + } + if (this.currtarget != null && this.ticks > this.currticks + 1) { + Player local = this.client.getLocalPlayer(); + Actor interacting = local.getInteracting(); + if (interacting != null) { + if (!interacting.getName().equals(this.currtarget)) { + this.currtarget = null; + } + } else { + this.currtarget = null; + } + } + ++this.ticks; + } + + public long opponentfreezetime(String name) { + if (this.frozenthings.containsKey(name)) { + return this.frozenthings.get(name); + } + return 0L; + } + + public WorldPoint playerpos(String name) { + if (this.frozenthingpoints.containsKey(name)) { + return this.frozenthingpoints.get(name); + } + return null; + } + + public void updatePosition(String name, WorldPoint point) { + if (this.frozenthingpoints.containsKey(name)) { + this.frozenthingpoints.remove(name); + this.frozenthingpoints.put(name, point); + } + } + + public int freezetype(String name) { + if (this.freezetype.containsKey(name)) { + return this.freezetype.get(name); + } + return 0; + } + public long istbed(String name) { + if (this.tbedthings.containsKey(name)) { + return this.tbedthings.get(name); + } + return 0; + } + public int tbtype(String name) { + if (this.tbtypes.containsKey(name)) { + return this.tbtypes.get(name); + } + return 0; + } + public void deleteopponent(String name) { + if (this.frozenthings.containsKey(name)) { + this.frozenthings.remove(name); + } + if (this.frozenthingpoints.containsKey(name)) { + this.frozenthingpoints.remove(name); + } + if (this.freezetype.containsKey(name)) { + this.freezetype.remove(name); + } + } + public void deletetb(String name) { + if (this.tbedthings.containsKey(name)) { + this.tbedthings.remove(name); + } + if (this.tbtypes.containsKey(name)) { + this.tbtypes.remove(name); + } + } + private void loadFreezeIcons() { + IndexedSprite[] freezeIcons = new IndexedSprite[]{}; + IndexedSprite[] newfreezeIcons = Arrays.copyOf(freezeIcons, FREEZE_ICONS.length); + int curPosition = 0; + int i = 0; + while (i < FREEZE_ICONS.length) { + int resource = FREEZE_ICONS[i]; + this.FreezeIcons[i] = FreezeTimersPlugin.rgbaToIndexedBufferedImage(FreezeTimersPlugin.FreezeIconFromSprite(this.spriteManager.getSprite(resource, 0))); + newfreezeIcons[curPosition] = FreezeTimersPlugin.createIndexedSprite(this.client, this.FreezeIcons[i]); + ++i; + ++curPosition; + } + } + + private static IndexedSprite createIndexedSprite(Client client, BufferedImage bufferedImage) { + IndexColorModel indexedCM = (IndexColorModel)bufferedImage.getColorModel(); + int width = bufferedImage.getWidth(); + int height = bufferedImage.getHeight(); + byte[] pixels = ((DataBufferByte)bufferedImage.getRaster().getDataBuffer()).getData(); + int[] palette = new int[indexedCM.getMapSize()]; + indexedCM.getRGBs(palette); + IndexedSprite newIndexedSprite = client.createIndexedSprite(); + newIndexedSprite.setPixels(pixels); + newIndexedSprite.setPalette(palette); + newIndexedSprite.setWidth(width); + newIndexedSprite.setHeight(height); + newIndexedSprite.setOriginalWidth(width); + newIndexedSprite.setOriginalHeight(height); + newIndexedSprite.setOffsetX(0); + newIndexedSprite.setOffsetY(0); + return newIndexedSprite; + } + + private static BufferedImage rgbaToIndexedBufferedImage(BufferedImage sourceBufferedImage) { + BufferedImage indexedImage = new BufferedImage(sourceBufferedImage.getWidth(), sourceBufferedImage.getHeight(), 13); + ColorModel cm = indexedImage.getColorModel(); + IndexColorModel icm = (IndexColorModel)cm; + int size = icm.getMapSize(); + byte[] reds = new byte[size]; + byte[] greens = new byte[size]; + byte[] blues = new byte[size]; + icm.getReds(reds); + icm.getGreens(greens); + icm.getBlues(blues); + WritableRaster raster = indexedImage.getRaster(); + int pixel = raster.getSample(0, 0, 0); + IndexColorModel resultIcm = new IndexColorModel(8, size, reds, greens, blues, pixel); + BufferedImage resultIndexedImage = new BufferedImage(resultIcm, raster, sourceBufferedImage.isAlphaPremultiplied(), null); + resultIndexedImage.getGraphics().drawImage(sourceBufferedImage, 0, 0, null); + return resultIndexedImage; + } + + private static BufferedImage FreezeIconFromSprite(BufferedImage freezeSprite) { + BufferedImage freezeCanvas = ImageUtil.resizeCanvas(freezeSprite, FreezeTimersPlugin.FREEZE_ICON_DIMENSION.width, FreezeTimersPlugin.FREEZE_ICON_DIMENSION.height); + return ImageUtil.outlineImage(freezeCanvas, FREEZE_ICON_OUTLINE_COLOR); + } + + BufferedImage GetFreezeIcon(int id) { + return this.FreezeIcons[id]; + } +} + diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/freezetimers/FreezeTimersService.java b/runelite-client/src/main/java/net/runelite/client/plugins/freezetimers/FreezeTimersService.java new file mode 100644 index 0000000000..257aae69b8 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/freezetimers/FreezeTimersService.java @@ -0,0 +1,81 @@ +package net.runelite.client.plugins.freezetimers; + +import java.awt.Color; +import java.util.List; +import java.util.function.BiConsumer; +import javax.inject.Inject; +import javax.inject.Singleton; +import net.runelite.api.Client; +import net.runelite.api.Player; +import net.runelite.api.coords.WorldPoint; +import net.runelite.client.plugins.freezetimers.FreezeTimersConfig; +import net.runelite.client.plugins.freezetimers.FreezeTimersPlugin; + +@Singleton +public class FreezeTimersService { + private final Client client; + private final FreezeTimersConfig config; + private final FreezeTimersPlugin plugin; + + @Inject + private FreezeTimersService(Client client, FreezeTimersConfig config, FreezeTimersPlugin plugin) { + this.config = config; + this.plugin = plugin; + this.client = client; + } + + public void forEachPlayer(BiConsumer consumer) { + for (Player player : this.client.getPlayers()) { + if (player == null || player.getName() == null) continue; + String name = player.getName(); + int freezetype = this.plugin.freezetype(name); + long tbed = plugin.istbed(name); + long dtime = this.plugin.opponentfreezetime(name); + int freezetime = 0; + if (freezetype == 1 || freezetype == 4) { + freezetime = 5000; + } else if (freezetype == 2 || freezetype == 5) { + freezetime = 10000; + } else if (freezetype == 3 || freezetype == 6) { + freezetime = 15000; + } else if (freezetype == 7) { + freezetime = 20000; + } else if (freezetype == 8) { + freezetime = 2500; + } else if (freezetype == 9) { + freezetime = 5000; + } else if (freezetype == 10) { + freezetime = 7500; + } + if (dtime <= 0L) continue; + long currenttime = System.currentTimeMillis(); + long timediff = currenttime - dtime; + if (timediff < (long)freezetime) { + WorldPoint lastWorldPoint; + WorldPoint currentWorldPoint = player.getWorldLocation(); + if (currentWorldPoint.equals(lastWorldPoint = this.plugin.playerpos(name))) { + consumer.accept(player, this.config.FreezeTimerColor()); + continue; + } + if (timediff < 605L) { + this.plugin.updatePosition(name, currentWorldPoint); + consumer.accept(player, this.config.FreezeTimerColor()); + continue; + } + this.plugin.deleteopponent(name); + continue; + } + if (timediff < (long)(freezetime + 3000)) { + consumer.accept(player, Color.YELLOW); + continue; + } else { + this.plugin.deleteopponent(name); + } + if (tbed > 0) { + consumer.accept(player, config.FreezeTimerColor()); + return; + } + } + } +} + diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/freezetimers/FreezeTimersTileOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/freezetimers/FreezeTimersTileOverlay.java new file mode 100644 index 0000000000..a945470c85 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/freezetimers/FreezeTimersTileOverlay.java @@ -0,0 +1,46 @@ +package net.runelite.client.plugins.freezetimers; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.Polygon; +import java.util.function.BiConsumer; +import javax.inject.Inject; +import net.runelite.api.Player; +import net.runelite.client.plugins.freezetimers.FreezeTimersConfig; +import net.runelite.client.plugins.freezetimers.FreezeTimersService; +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.OverlayPriority; +import net.runelite.client.ui.overlay.OverlayUtil; + +public class FreezeTimersTileOverlay +extends Overlay { + private final FreezeTimersService FreezeTimersService; + private final FreezeTimersConfig config; + + @Inject + private FreezeTimersTileOverlay(FreezeTimersConfig config, FreezeTimersService FreezeTimersService2) { + this.config = config; + this.FreezeTimersService = FreezeTimersService2; + this.setLayer(OverlayLayer.ABOVE_SCENE); + this.setPosition(OverlayPosition.DYNAMIC); + this.setPriority(OverlayPriority.MED); + } + + @Override + public Dimension render(Graphics2D graphics) { + if (!this.config.drawTiles()) { + return null; + } + this.FreezeTimersService.forEachPlayer((player, color) -> { + Polygon poly = player.getCanvasTilePoly(); + if (poly != null) { + OverlayUtil.renderPolygon(graphics, poly, color); + } + }); + return null; + } +} + diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/freezetimers/PlayerSpellEffect.java b/runelite-client/src/main/java/net/runelite/client/plugins/freezetimers/PlayerSpellEffect.java new file mode 100644 index 0000000000..8bc136fbb5 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/freezetimers/PlayerSpellEffect.java @@ -0,0 +1,35 @@ +package net.runelite.client.plugins.freezetimers; + +public enum PlayerSpellEffect { + BARRAGE("Ice Barrage", 20000, false), + BLITZ("Ice Blitz", 15000, false); + + private final String SPELL_NAME; + private long startTime; + private int duration; + private boolean halvable; + + private PlayerSpellEffect(String name, int duration, boolean halvable) { + this.SPELL_NAME = name; + this.duration = duration; + this.halvable = halvable; + this.startTime = System.currentTimeMillis(); + } + + public String getSPELL_NAME() { + return this.SPELL_NAME; + } + + public long getStartTime() { + return this.startTime; + } + + public int getDuration() { + return this.duration; + } + + public boolean isHalvable() { + return this.halvable; + } +} + diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/freezetimers/Spell.java b/runelite-client/src/main/java/net/runelite/client/plugins/freezetimers/Spell.java new file mode 100644 index 0000000000..d9033a7c0d --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/freezetimers/Spell.java @@ -0,0 +1,34 @@ +package net.runelite.client.plugins.freezetimers; + +import net.runelite.api.Actor; + +public abstract class Spell { + private final Actor affectedTarget; + private final Actor caster; + public final long startTime; + private long remainingTime; + private boolean isFinished; + + protected Spell(Actor affectedTarget, Actor caster) { + this.affectedTarget = affectedTarget; + this.caster = caster; + this.startTime = System.currentTimeMillis(); + } + + public Actor getAffectedTarget() { + return this.affectedTarget; + } + + public Actor getCaster() { + return this.caster; + } + + public long getStartTime() { + return this.startTime; + } + + public boolean isFinished() { + return this.isFinished; + } +} + diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grotesqueguardians/GrotesqueGuardiansOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/grotesqueguardians/GrotesqueGuardiansOverlay.java new file mode 100644 index 0000000000..8f90c59363 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/grotesqueguardians/GrotesqueGuardiansOverlay.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2018, Damen + * 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.grotesqueguardians; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.Polygon; +import javax.inject.Inject; +import net.runelite.api.Client; +import net.runelite.api.GraphicsObject; +import net.runelite.api.Perspective; +import net.runelite.api.coords.LocalPoint; +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.OverlayPriority; +import net.runelite.client.ui.overlay.OverlayUtil; + +class GrotesqueGuardiansOverlay extends Overlay +{ + private static final int GROTESQUE_GUARDIANS_REGION_ID = 6727; + private final Client client; + private static final int GROTESQUE_GUARDIANS_LIGHTNING_START = 1416; + private static final int GROTESQUE_GUARDIANS_LIGHTNING_END = 1431; + private static final int GROTESQUE_GUARDIANS_FALLING_ROCKS = 1436; + private static final int GROTESQUE_GUARDIANS_STONE_ORB = 160; + + @Inject + private GrotesqueGuardiansOverlay(Client client) + { + this.client = client; + setPosition(OverlayPosition.DYNAMIC); + setLayer(OverlayLayer.ABOVE_SCENE); + setPriority(OverlayPriority.LOW); + } + + @Override + public Dimension render(Graphics2D graphics) + { + if (!client.isInInstancedRegion() || client.getMapRegions()[0] != GROTESQUE_GUARDIANS_REGION_ID) + { + return null; + } + + // TODO: Awaiting GraphicsObjectDespawn event to be tracked to make this more efficient. + for (GraphicsObject graphicsObject : client.getGraphicsObjects()) + { + Color color = null; + + if (graphicsObject.getId() >= GROTESQUE_GUARDIANS_LIGHTNING_START && graphicsObject.getId() <= GROTESQUE_GUARDIANS_LIGHTNING_END) + { + color = Color.ORANGE; + } + else if (graphicsObject.getId() == GROTESQUE_GUARDIANS_STONE_ORB) + { + color = Color.GRAY; + } + else if (graphicsObject.getId() == GROTESQUE_GUARDIANS_FALLING_ROCKS) + { + color = Color.YELLOW; + } + else + { + continue; + } + + LocalPoint lp = graphicsObject.getLocation(); + Polygon poly = Perspective.getCanvasTilePoly(client, lp); + + if (poly != null) + { + OverlayUtil.renderPolygon(graphics, poly, color); + } + } + return null; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grotesqueguardians/GrotesqueGuardiansPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/grotesqueguardians/GrotesqueGuardiansPlugin.java new file mode 100644 index 0000000000..d6df9dc49f --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/grotesqueguardians/GrotesqueGuardiansPlugin.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2018, Damen + * 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.grotesqueguardians; + +import javax.inject.Inject; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.ui.overlay.OverlayManager; + +@PluginDescriptor( + name = "!Grotesque Guardians", + description = "Display tile indicators for the Grotesque Guardian special attacks", + tags = {"grotesque", "guardians", "gargoyle", "garg"} +) +public class GrotesqueGuardiansPlugin extends Plugin +{ + @Inject + private OverlayManager overlayManager; + + @Inject + private GrotesqueGuardiansOverlay overlay; + + @Override + protected void startUp() throws Exception + { + overlayManager.add(overlay); + } + + @Override + protected void shutDown() throws Exception + { + overlayManager.remove(overlay); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/hideprayers/HidePrayersConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/hideprayers/HidePrayersConfig.java new file mode 100644 index 0000000000..8a92bed779 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/hideprayers/HidePrayersConfig.java @@ -0,0 +1,35 @@ +package net.runelite.client.plugins.hideprayers; + +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("hideprayers") +public interface HidePrayersConfig extends Config +{ + @ConfigItem( + position = 0, + keyName = "pk prayers", + name = "Hides none pk prayers", + description = "Hides widget icons." + ) + default boolean showPrayers() { return false; } + + @ConfigItem( + position = 1, + keyName = "eagle/mystic", + name = "Shows eagle and mystic prayers", + description = "Hides widget icons." + ) + default boolean showEagleMystic() { return false; } + + @ConfigItem( + position = 1, + keyName = "ultstr", + name = "Shows ultimate strength", + description = "Hides widget icons." + ) + default boolean showUltStrength() { return false; } + +} + diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/hideprayers/HidePrayersPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/hideprayers/HidePrayersPlugin.java new file mode 100644 index 0000000000..df473a5d0d --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/hideprayers/HidePrayersPlugin.java @@ -0,0 +1,169 @@ +package net.runelite.client.plugins.hideprayers; + +import com.google.common.collect.ImmutableList; +import net.runelite.client.eventbus.Subscribe; +import com.google.inject.Provides; +import net.runelite.api.*; +import net.runelite.api.events.ConfigChanged; +import net.runelite.api.events.GameStateChanged; +import net.runelite.api.events.WidgetLoaded; +import net.runelite.api.widgets.Widget; +import net.runelite.api.widgets.WidgetID; +import net.runelite.api.widgets.WidgetInfo; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; + +import javax.inject.Inject; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +@PluginDescriptor( + name = "!Hide Prayers", + description = "Hides specific Prayers in the Prayer tab." +) +public class HidePrayersPlugin extends Plugin { + private static final int PRAYER_COUNT = Prayer.values().length; + + private static final List PRAYER_WIDGET_INFO_LIST = ImmutableList.of(WidgetInfo.PRAYER_THICK_SKIN, + WidgetInfo.PRAYER_BURST_OF_STRENGTH, WidgetInfo.PRAYER_CLARITY_OF_THOUGHT, WidgetInfo.PRAYER_SHARP_EYE, + WidgetInfo.PRAYER_MYSTIC_WILL, WidgetInfo.PRAYER_ROCK_SKIN, WidgetInfo.PRAYER_SUPERHUMAN_STRENGTH, + WidgetInfo.PRAYER_IMPROVED_REFLEXES, WidgetInfo.PRAYER_RAPID_RESTORE, WidgetInfo.PRAYER_RAPID_HEAL, + WidgetInfo.PRAYER_PROTECT_ITEM, WidgetInfo.PRAYER_HAWK_EYE, WidgetInfo.PRAYER_MYSTIC_LORE, + WidgetInfo.PRAYER_STEEL_SKIN, WidgetInfo.PRAYER_ULTIMATE_STRENGTH, WidgetInfo.PRAYER_INCREDIBLE_REFLEXES, + WidgetInfo.PRAYER_PROTECT_FROM_MAGIC, WidgetInfo.PRAYER_PROTECT_FROM_MISSILES, + WidgetInfo.PRAYER_PROTECT_FROM_MELEE, WidgetInfo.PRAYER_EAGLE_EYE, WidgetInfo.PRAYER_MYSTIC_MIGHT, + WidgetInfo.PRAYER_RETRIBUTION, WidgetInfo.PRAYER_REDEMPTION, WidgetInfo.PRAYER_SMITE, + WidgetInfo.PRAYER_PRESERVE, WidgetInfo.PRAYER_CHIVALRY, WidgetInfo.PRAYER_PIETY, WidgetInfo.PRAYER_RIGOUR, + WidgetInfo.PRAYER_AUGURY); + + @Inject + private Client client; + + @Inject + private HidePrayersConfig config; + + @Provides + HidePrayersConfig provideConfig(ConfigManager configManager) { + return configManager.getConfig(HidePrayersConfig.class); + } + + @Override + protected void startUp() throws Exception { + hidePrayers(); + } + + @Override + protected void shutDown() throws Exception { + restorePrayers(); + } + + @Subscribe + public void onGameStateChanged(GameStateChanged event) { + if (event.getGameState() == GameState.LOGGED_IN) { + hidePrayers(); + } + } + + @Subscribe + public void onConfigChanged(ConfigChanged event) { + if (event.getGroup().equals("hideprayers")) { + hidePrayers(); + } + } + + @Subscribe + public void onWidgetLoaded(WidgetLoaded event) { + if (event.getGroupId() == WidgetID.PRAYER_GROUP_ID || event.getGroupId() == WidgetID.QUICK_PRAYERS_GROUP_ID) { + hidePrayers(); + } + } + + private PrayerTabState getPrayerTabState() { + HashTable componentTable = client.getComponentTable(); + for (WidgetNode widgetNode : componentTable.getNodes()) { + if (widgetNode.getId() == WidgetID.PRAYER_GROUP_ID) { + return PrayerTabState.PRAYERS; + } else if (widgetNode.getId() == WidgetID.QUICK_PRAYERS_GROUP_ID) { + return PrayerTabState.QUICK_PRAYERS; + } + } + return PrayerTabState.NONE; + } + + private void restorePrayers() { + if (client.getGameState() != GameState.LOGGED_IN) + return; + + PrayerTabState prayerTabState = getPrayerTabState(); + + if (prayerTabState == PrayerTabState.PRAYERS) { + List prayerWidgets = PRAYER_WIDGET_INFO_LIST.stream().map(client::getWidget) + .filter(Objects::nonNull).collect(Collectors.toList()); + + if (prayerWidgets.size() != PRAYER_WIDGET_INFO_LIST.size()) + return; + + for (int index = 0; index < PRAYER_COUNT; index++) + prayerWidgets.get(Prayer.values()[index].ordinal()).setHidden(false); + } + } + + private void hidePrayers() { + if (client.getGameState() != GameState.LOGGED_IN) + return; + + PrayerTabState prayerTabState = getPrayerTabState(); + + if (prayerTabState == PrayerTabState.PRAYERS) { + List prayerWidgets = PRAYER_WIDGET_INFO_LIST.stream().map(client::getWidget) + .filter(Objects::nonNull).collect(Collectors.toList()); + + if (prayerWidgets.size() != PRAYER_WIDGET_INFO_LIST.size()) + return; + + for (int index = 0; index < PRAYER_COUNT; index++) { + Prayer prayer = Prayer.values()[index]; + Widget prayerWidget = prayerWidgets.get(prayer.ordinal()); + + if (!config.showPrayers() && !config.showEagleMystic()) + prayerWidget.setHidden(false); + + if (config.showPrayers()) { + prayerWidget.setHidden(true); + prayerWidgets.get(Prayer.values()[10].ordinal()).setHidden(false);// protect item + prayerWidgets.get(Prayer.values()[16].ordinal()).setHidden(false);// mage + prayerWidgets.get(Prayer.values()[17].ordinal()).setHidden(false);// range + prayerWidgets.get(Prayer.values()[18].ordinal()).setHidden(false);// melee + prayerWidgets.get(Prayer.values()[23].ordinal()).setHidden(false);// smite + if (config.showEagleMystic()) { + prayerWidgets.get(Prayer.values()[27].ordinal()).setHidden(true);// rigour + prayerWidgets.get(Prayer.values()[28].ordinal()).setHidden(true);// augury + } else { + prayerWidgets.get(Prayer.values()[27].ordinal()).setHidden(false);// rigour + prayerWidgets.get(Prayer.values()[28].ordinal()).setHidden(false);// augury + } + if (config.showUltStrength()) { + prayerWidgets.get(Prayer.values()[26].ordinal()).setHidden(true);// piety + } else { + prayerWidgets.get(Prayer.values()[26].ordinal()).setHidden(false);// piety + } + } + if (config.showEagleMystic()) { + prayerWidget.setHidden(true); + prayerWidgets.get(Prayer.values()[19].ordinal()).setHidden(false);// eagle + prayerWidgets.get(Prayer.values()[20].ordinal()).setHidden(false);// mystic + prayerWidgets.get(Prayer.values()[27].ordinal()).setHidden(true);// rigour + prayerWidgets.get(Prayer.values()[28].ordinal()).setHidden(true);// augury + } + if (config.showUltStrength()) { + prayerWidget.setHidden(true); + prayerWidgets.get(Prayer.values()[14].ordinal()).setHidden(false);// Ult Strength + prayerWidgets.get(Prayer.values()[26].ordinal()).setHidden(true);// piety + } + + } + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/hideprayers/PrayerTabState.java b/runelite-client/src/main/java/net/runelite/client/plugins/hideprayers/PrayerTabState.java new file mode 100644 index 0000000000..699300f8a9 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/hideprayers/PrayerTabState.java @@ -0,0 +1,8 @@ +package net.runelite.client.plugins.hideprayers; + +public enum PrayerTabState +{ + NONE, + PRAYERS, + QUICK_PRAYERS +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/hydra/HydraConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/hydra/HydraConfig.java new file mode 100644 index 0000000000..8c3d8467b8 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/hydra/HydraConfig.java @@ -0,0 +1,41 @@ +package net.runelite.client.plugins.hydra; + +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("hydra") +public interface HydraConfig extends Config { + @ConfigItem( + position = 0, + keyName = "hydraenable", + name = "Enable Hydra (194 cb) Helper", + description = "Configures whether or not to enable Hydra Helper. (For use on regular hydra's only, will not work with Alchemical Hydra)." + ) + default boolean EnableHydra() { return true; } + + @ConfigItem( + position = 1, + keyName = "textindicators", + name = "Text Indicator", + description = "Configures if text indicator is shown above hydra's or not." + ) + default boolean TextIndicator() { return true; } + + @ConfigItem( + position = 2, + keyName = "countersize", + name = "Bold indicator", + description = "Configures if text indicator is bold or not." + ) + default boolean BoldText() { return false; } + + @ConfigItem( + position = 3, + keyName = "prayerhelper", + name = "Prayer Helper", + description = "Configures if prayer helper is shown or not." + ) + default boolean PrayerHelper() { return true; } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/hydra/HydraIndicatorOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/hydra/HydraIndicatorOverlay.java new file mode 100644 index 0000000000..6c38c81b3f --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/hydra/HydraIndicatorOverlay.java @@ -0,0 +1,52 @@ +package net.runelite.client.plugins.hydra; + +import java.awt.*; +import java.awt.image.BufferedImage; +import javax.inject.Inject; + +import net.runelite.api.*; +import net.runelite.api.Point; +import net.runelite.client.game.SpriteManager; +import net.runelite.client.ui.overlay.*; +import net.runelite.client.ui.overlay.components.ComponentConstants; +import net.runelite.client.ui.overlay.components.ImageComponent; +import net.runelite.client.ui.overlay.components.LineComponent; +import net.runelite.client.ui.overlay.components.PanelComponent; + +import static net.runelite.api.MenuAction.RUNELITE_OVERLAY_CONFIG; +import static net.runelite.client.ui.overlay.OverlayManager.OPTION_CONFIGURE; + +public class HydraIndicatorOverlay extends Overlay { + private final HydraConfig config; + private final HydraPlugin plugin; + + private final PanelComponent panelComponent = new PanelComponent(); + + @Inject + private HydraIndicatorOverlay(HydraConfig config, HydraPlugin plugin) { + this.config = config; + this.plugin = plugin; + setPosition(OverlayPosition.BOTTOM_RIGHT); + setPriority(OverlayPriority.MED); + panelComponent.setPreferredSize(new Dimension(14, 0)); + } + + @Override + public Dimension render(Graphics2D graphics) { + if (!config.PrayerHelper()) { + return null; + } + + if (plugin.Hydra != null) { + if (plugin.hydras.containsKey(plugin.Hydra.getIndex())) { + int val = plugin.hydras.get(plugin.Hydra.getIndex()); + if (val != 0) { + panelComponent.getChildren().clear(); + panelComponent.getChildren().add(LineComponent.builder().right(Integer.toString(val)).build()); + return panelComponent.render(graphics); + } + } + } + return null; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/hydra/HydraOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/hydra/HydraOverlay.java new file mode 100644 index 0000000000..b499227ec2 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/hydra/HydraOverlay.java @@ -0,0 +1,76 @@ +package net.runelite.client.plugins.hydra; + +import java.awt.*; +import javax.inject.Inject; + +import net.runelite.api.*; +import net.runelite.api.Point; +import net.runelite.client.ui.FontManager; +import net.runelite.client.ui.overlay.*; +import net.runelite.client.ui.overlay.components.PanelComponent; + +public class HydraOverlay extends Overlay { + private final HydraConfig config; + private final HydraPlugin plugin; + private final PanelComponent panelComponent = new PanelComponent(); + + + @Inject + private Client client; + + @Inject + private HydraOverlay(HydraConfig config, HydraPlugin plugin) { + this.config = config; + this.plugin = plugin; + setLayer(OverlayLayer.ABOVE_SCENE); + setPosition(OverlayPosition.DYNAMIC); + setPriority(OverlayPriority.MED); + panelComponent.setPreferredSize(new Dimension(150, 0)); + } + + @Override + public Dimension render(Graphics2D graphics) { + if (!config.TextIndicator()) { + return null; + } + + for (NPC hydra : client.getNpcs()) { + if (hydra == null || hydra.getName() == null) { + continue; + } + if (hydra.getName().equalsIgnoreCase("Hydra")) { + if (plugin.hydras.containsKey(hydra.getIndex())) { + int val = plugin.hydras.get(hydra.getIndex()); + if (val != 0) { + if (config.BoldText()) { + graphics.setFont(FontManager.getRunescapeBoldFont()); + } + if (plugin.hydraattacks.containsKey(hydra.getIndex())) { + int attack = plugin.hydraattacks.get(hydra.getIndex()); + if (attack == 8261) { + if (val == 3) { + OverlayUtil.renderTextLocation(graphics, hydra.getCanvasTextLocation(graphics, "MAGE", hydra.getLogicalHeight() + 100), "MAGE", Color.BLUE); + } else { + OverlayUtil.renderTextLocation(graphics, hydra.getCanvasTextLocation(graphics, "RANGE", hydra.getLogicalHeight() + 100), "RANGE", Color.GREEN); + } + } else if (attack == 8262) { + if (val == 3) { + OverlayUtil.renderTextLocation(graphics, hydra.getCanvasTextLocation(graphics, "RANGE", hydra.getLogicalHeight() + 100), "RANGE", Color.GREEN); + } else { + OverlayUtil.renderTextLocation(graphics, hydra.getCanvasTextLocation(graphics, "MAGE", hydra.getLogicalHeight() + 100), "MAGE", Color.BLUE); + } + } + } + Point runelitepleaseexplainwhyineedtocheckthisfornullinsteadoftheentirehydravariablethisshitcostmelikeanhourofmylifeandiblameyouadam = hydra.getCanvasTextLocation(graphics, Integer.toString(val), hydra.getLogicalHeight() + 40); + if (runelitepleaseexplainwhyineedtocheckthisfornullinsteadoftheentirehydravariablethisshitcostmelikeanhourofmylifeandiblameyouadam != null) { + OverlayUtil.renderTextLocation(graphics, runelitepleaseexplainwhyineedtocheckthisfornullinsteadoftheentirehydravariablethisshitcostmelikeanhourofmylifeandiblameyouadam, Integer.toString(val), Color.WHITE); + } + } + } + } + + } + graphics.setFont(FontManager.getRunescapeFont()); + return null; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/hydra/HydraPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/hydra/HydraPlugin.java new file mode 100644 index 0000000000..ba8c0317a7 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/hydra/HydraPlugin.java @@ -0,0 +1,145 @@ +package net.runelite.client.plugins.hydra; + +import net.runelite.api.events.*; +import net.runelite.client.eventbus.Subscribe; +import com.google.inject.Provides; +import javax.inject.Inject; +import net.runelite.api.*; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.game.SpriteManager; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.ui.overlay.OverlayManager; + +import java.util.HashMap; +import java.util.Map; + +@PluginDescriptor( + name = "Hydra", + description = "Hydra Helper", + tags = {"Hydra", "Helper"} +) +public class HydraPlugin extends Plugin +{ + @Inject + private OverlayManager overlayManager; + + @Inject + private HydraConfig config; + + @Inject + private HydraOverlay HydraOverlay; + + @Inject + private HydraPrayOverlay HydraPrayOverlay; + + @Inject + private HydraIndicatorOverlay HydraIndicatorOverlay; + + @Inject + private Client client; + + @Inject + private SpriteManager spriteManager; + + @Provides + HydraConfig provideConfig(ConfigManager configManager) { + return configManager.getConfig(HydraConfig.class); + } + + Map hydras = new HashMap<>(); + Map hydraattacks = new HashMap<>(); + NPC Hydra; + + @Override + protected void startUp() throws Exception { + overlayManager.add(HydraOverlay); + overlayManager.add(HydraPrayOverlay); + overlayManager.add(HydraIndicatorOverlay); + } + + @Override + protected void shutDown() throws Exception { + overlayManager.remove(HydraOverlay); + overlayManager.remove(HydraPrayOverlay); + overlayManager.remove(HydraIndicatorOverlay); + hydras.clear(); + hydraattacks.clear(); + } + + @Subscribe + public void onNpcSpawned(NpcSpawned event) { + if (!config.EnableHydra()) { + return; + } + NPC hydra = event.getNpc(); + if (hydra.getCombatLevel() != 0 && hydra.getName() != null) { + if (hydra.getName().equalsIgnoreCase("Hydra")) { + if (!hydras.containsKey(hydra.getIndex())) { + hydras.put(hydra.getIndex(), 3); + } + } + } + } + + @Subscribe + public void onNpcDespawned(NpcDespawned event) { + if (!config.EnableHydra()) { + return; + } + NPC hydra = event.getNpc(); + if (hydra.getCombatLevel() != 0 && hydra.getName() != null) { + if (hydra.getName().equalsIgnoreCase("Hydra")) { + if (hydras.containsKey(hydra.getIndex())) { + hydras.remove(hydra.getIndex()); + } + if (hydraattacks.containsKey(hydra.getIndex())) { + hydraattacks.remove(hydra.getIndex()); + } + } + } + } + + @Subscribe + public void onAnimationChanged(AnimationChanged event) { + Actor monster = event.getActor(); + Actor local = client.getLocalPlayer(); + if (monster instanceof NPC) { + NPC hydra = (NPC) monster; + if (hydra.getCombatLevel() != 0 && hydra.getName() != null) { + if (hydra.getName().equalsIgnoreCase("Hydra")) { + if (hydras.containsKey(hydra.getIndex())) { + if (hydra.getAnimation() == 8261 || hydra.getAnimation() == 8262) { + if (hydra.getInteracting().equals(local)) { + Hydra = hydra; + } + if (hydraattacks.containsKey(hydra.getIndex())) { + int lastattack = hydraattacks.get(hydra.getIndex()); + hydraattacks.replace(hydra.getIndex(), hydra.getAnimation()); + + if (lastattack != hydra.getAnimation()) { + hydras.replace(hydra.getIndex(), 2); + } else { + int currval = hydras.get(hydra.getIndex()); + if (currval == 1) { + hydras.replace(hydra.getIndex(), 3); + } else { + hydras.replace(hydra.getIndex(), currval - 1); + } + } + } else { + hydraattacks.put(hydra.getIndex(), hydra.getAnimation()); + int currval = hydras.get(hydra.getIndex()); + if (currval == 1) { + hydras.replace(hydra.getIndex(), 3); + } else { + hydras.replace(hydra.getIndex(), currval - 1); + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/hydra/HydraPrayOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/hydra/HydraPrayOverlay.java new file mode 100644 index 0000000000..47a7657667 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/hydra/HydraPrayOverlay.java @@ -0,0 +1,100 @@ +package net.runelite.client.plugins.hydra; + +import java.awt.*; +import java.awt.image.BufferedImage; +import javax.inject.Inject; + +import net.runelite.api.*; +import net.runelite.api.Point; +import net.runelite.client.game.SpriteManager; +import net.runelite.client.ui.overlay.*; +import net.runelite.client.ui.overlay.components.ComponentConstants; +import net.runelite.client.ui.overlay.components.ImageComponent; +import net.runelite.client.ui.overlay.components.PanelComponent; + +public class HydraPrayOverlay extends Overlay { + private final HydraConfig config; + private final HydraPlugin plugin; + + private static final Color NOT_ACTIVATED_BACKGROUND_COLOR = new Color(150, 0, 0, 150); + + private final SpriteManager spriteManager; + private final PanelComponent imagePanelComponent = new PanelComponent(); + + + @Inject + private Client client; + + @Inject + private HydraPrayOverlay(HydraConfig config, HydraPlugin plugin, SpriteManager spriteManager) { + this.config = config; + this.plugin = plugin; + setPosition(OverlayPosition.BOTTOM_RIGHT); + setPriority(OverlayPriority.HIGH); + this.spriteManager = spriteManager; + } + + @Override + public Dimension render(Graphics2D graphics) { + if (!config.PrayerHelper()) { + return null; + } + + if (plugin.Hydra != null) { + if (plugin.hydras.containsKey(plugin.Hydra.getIndex())) { + int val = plugin.hydras.get(plugin.Hydra.getIndex()); + if (val != 0) { + if (plugin.hydraattacks.containsKey(plugin.Hydra.getIndex())) { + int attack = plugin.hydraattacks.get(plugin.Hydra.getIndex()); + if (attack == 8261) { + if (val == 3) { + final BufferedImage prayerImage = spriteManager.getSprite(SpriteID.PRAYER_PROTECT_FROM_MAGIC, 0); + + imagePanelComponent.getChildren().clear(); + imagePanelComponent.getChildren().add(new ImageComponent(prayerImage)); + imagePanelComponent.setBackgroundColor(client.isPrayerActive(Prayer.PROTECT_FROM_MAGIC) + ? ComponentConstants.STANDARD_BACKGROUND_COLOR + : NOT_ACTIVATED_BACKGROUND_COLOR); + + return imagePanelComponent.render(graphics); + } else { + final BufferedImage prayerImage = spriteManager.getSprite(SpriteID.PRAYER_PROTECT_FROM_MISSILES, 0); + + imagePanelComponent.getChildren().clear(); + imagePanelComponent.getChildren().add(new ImageComponent(prayerImage)); + imagePanelComponent.setBackgroundColor(client.isPrayerActive(Prayer.PROTECT_FROM_MISSILES) + ? ComponentConstants.STANDARD_BACKGROUND_COLOR + : NOT_ACTIVATED_BACKGROUND_COLOR); + + return imagePanelComponent.render(graphics); + } + } else if (attack == 8262) { + if (val == 3) { + final BufferedImage prayerImage = spriteManager.getSprite(SpriteID.PRAYER_PROTECT_FROM_MISSILES, 0); + + imagePanelComponent.getChildren().clear(); + imagePanelComponent.getChildren().add(new ImageComponent(prayerImage)); + imagePanelComponent.setBackgroundColor(client.isPrayerActive(Prayer.PROTECT_FROM_MISSILES) + ? ComponentConstants.STANDARD_BACKGROUND_COLOR + : NOT_ACTIVATED_BACKGROUND_COLOR); + + return imagePanelComponent.render(graphics); + } else { + final BufferedImage prayerImage = spriteManager.getSprite(SpriteID.PRAYER_PROTECT_FROM_MAGIC, 0); + + imagePanelComponent.getChildren().clear(); + imagePanelComponent.getChildren().add(new ImageComponent(prayerImage)); + imagePanelComponent.setBackgroundColor(client.isPrayerActive(Prayer.PROTECT_FROM_MAGIC) + ? ComponentConstants.STANDARD_BACKGROUND_COLOR + : NOT_ACTIVATED_BACKGROUND_COLOR); + + return imagePanelComponent.render(graphics); + } + } + } + } + } + } + return null; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/kittennotifier/KittenNotifierConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/kittennotifier/KittenNotifierConfig.java new file mode 100644 index 0000000000..e9d28f6b26 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/kittennotifier/KittenNotifierConfig.java @@ -0,0 +1,27 @@ +package net.runelite.client.plugins.kittennotifier; +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("kittennotifier") +public interface KittenNotifierConfig extends Config{ + @ConfigItem( + keyName = "absolutelyNeeded", + name = "Notify only on Absolute Need", + description = "Only notify when kitten absolutely needs food or attention." + ) + default boolean absolutelyNeeded() { return false; } + @ConfigItem( + keyName = "catOwned", + name = "", + description = "", + hidden = true + ) + default boolean catOwned() { return false; } + @ConfigItem( + keyName = "catOwned", + name = "", + description = "" + ) + void catOwned(Boolean bool); +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/kittennotifier/KittenNotifierPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/kittennotifier/KittenNotifierPlugin.java new file mode 100644 index 0000000000..921dcd4a46 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/kittennotifier/KittenNotifierPlugin.java @@ -0,0 +1,84 @@ +package net.runelite.client.plugins.kittennotifier; +import com.google.inject.Provides; +import net.runelite.api.ChatMessageType; +import net.runelite.api.events.ChatMessage; +import net.runelite.api.events.NpcActionChanged; +import net.runelite.api.events.NpcSpawned; +import net.runelite.client.Notifier; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.api.NPC; +import net.runelite.api.Client; +import javax.inject.Inject; + +@PluginDescriptor( + name = "!Kitten Notifier", + description = "Sends a notification when your kitten needs food, attention, or is grown.", + tags = {"kitten, notifications"} +) +public class KittenNotifierPlugin extends Plugin{ + @Inject + private Notifier notifier; + @Inject + private KittenNotifierConfig config; + @Inject + private Client client; + @Provides + KittenNotifierConfig getConfig(ConfigManager configManager) + { + return configManager.getConfig(KittenNotifierConfig.class); + } + @Subscribe + public void onChatMessage(ChatMessage event) { + if (event.getType() == ChatMessageType.ENGINE && !config.catOwned()) { + if (!config.absolutelyNeeded()) { + if (event.getMessage().contentEquals("Your kitten is hungry.")) { + notifier.notify("Your kitten is hungry."); + } + if (event.getMessage().contentEquals("Your kitten wants attention.")) { + notifier.notify("Your kitten wants attention."); + } + } + if (event.getMessage().contentEquals("Your kitten is very hungry.")) { + notifier.notify("Your kitten is very hungry."); + } + if (event.getMessage().contentEquals("Your kitten really wants attention.")) { + notifier.notify("Your kitten really wants attention."); + } + } + } + @Subscribe + public void onNpcActionChanged(NpcActionChanged event) { + if (!config.catOwned()) { + for (NPC npc : client.getNpcs()) { + if (npc.getInteracting() != null) { + if (npc.getName().contentEquals("Cat") && !config.catOwned()) { + // If this if statement is included in previous it could null. + if (npc.getInteracting().getName().contentEquals(client.getLocalPlayer().getName())) { + config.catOwned(true); + notifier.notify("Your kitten has grown into a cat."); + } + } + } + } + } + } + @Subscribe + public void onNpcSpawned(NpcSpawned event) { + NPC cat = event.getNpc(); + if (cat.getName() != null) { + if (cat.getName().equalsIgnoreCase("Kitten")) { + if (cat.getInteracting().getName().contentEquals(client.getLocalPlayer().getName())) { + config.catOwned(false); + } + } + else if (cat.getName().contentEquals("Cat")) { + if (cat.getInteracting().getName().equalsIgnoreCase(client.getLocalPlayer().getName())) { + config.catOwned(true); + } + } + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/lizardmenshaman/LizardmenShamanConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/lizardmenshaman/LizardmenShamanConfig.java new file mode 100644 index 0000000000..33e61c0d46 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/lizardmenshaman/LizardmenShamanConfig.java @@ -0,0 +1,32 @@ +package net.runelite.client.plugins.lizardmenshaman; + +import net.runelite.client.config.Config; +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("shaman") +public interface LizardmenShamanConfig extends Config +{ + @ConfigItem( + position = 1, + keyName = "showTimer", + name = "Show timer", + description = "Display timer till for lizardman shaman spawns." + ) + default boolean showTimer() + { + return true; + } + + @ConfigItem( + position = 2, + keyName = "notifyOnSpawn", + name = "Notify on spawn", + description = "Notify user when lizardman summons spawns." + ) + default boolean notifyOnSpawn() + { + return true; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/lizardmenshaman/LizardmenShamanPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/lizardmenshaman/LizardmenShamanPlugin.java new file mode 100644 index 0000000000..e6b3923fea --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/lizardmenshaman/LizardmenShamanPlugin.java @@ -0,0 +1,91 @@ +package net.runelite.client.plugins.lizardmenshaman; + +import com.google.common.eventbus.Subscribe; +import com.google.inject.Provides; +import javax.inject.Inject; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.Actor; +import net.runelite.api.Client; +import net.runelite.api.coords.LocalPoint; +import net.runelite.api.events.AnimationChanged; +import net.runelite.client.Notifier; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import java.util.HashMap; +import java.util.Map; +import net.runelite.client.ui.overlay.OverlayManager; + +@PluginDescriptor( + name = "!Lizard Shamans", + description = "Configures timer for lizardmen shaman spawns.", + enabledByDefault = false, + tags = {"shaman", "lizard", "lizardmen"} +) +@Slf4j +public class LizardmenShamanPlugin extends Plugin +{ + private static final String SHAMAN = "Lizardman shaman"; + private static final String MESSAGE = "A Lizardman shaman has summoned his spawn!"; + + @Getter(AccessLevel.PACKAGE) + private final Map spawns = new HashMap<>(); + + @Inject + private OverlayManager overlayManager; + + @Inject + private ShamanSpawnOverlay overlay; + + @Inject + private LizardmenShamanConfig config; + + @Inject + private Notifier notifier; + + @Inject + private Client client; + + @Provides + LizardmenShamanConfig getConfig(ConfigManager configManager) + { + return configManager.getConfig(LizardmenShamanConfig.class); + } + + @Override + protected void startUp() throws Exception + { + overlayManager.add(overlay); + } + + @Override + protected void shutDown() throws Exception + { + overlayManager.remove(overlay); + spawns.clear(); + } + + @Subscribe + public void onAnimationChanged(AnimationChanged event) + { + Actor actor = event.getActor(); + if (actor == null || actor.getName() == null) + { + return; + } + else if (actor.getName().equals(SHAMAN) && actor.getAnimation() == 7157) + { + if (config.showTimer()) + { + spawns.put(event.getActor().getLocalLocation(), new LizardmenShamanSpawn(8.4, null)); + } + + if (config.notifyOnSpawn()) + { + notifier.notify(MESSAGE); + } + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/lizardmenshaman/LizardmenShamanSpawn.java b/runelite-client/src/main/java/net/runelite/client/plugins/lizardmenshaman/LizardmenShamanSpawn.java new file mode 100644 index 0000000000..d4297ed624 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/lizardmenshaman/LizardmenShamanSpawn.java @@ -0,0 +1,18 @@ +package net.runelite.client.plugins.lizardmenshaman; + +import java.time.Instant; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@RequiredArgsConstructor +@AllArgsConstructor +class LizardmenShamanSpawn +{ + private final Instant start = Instant.now(); + private double countdownTimer; + private Instant end; +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/lizardmenshaman/ShamanSpawnOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/lizardmenshaman/ShamanSpawnOverlay.java new file mode 100644 index 0000000000..4d363e72c4 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/lizardmenshaman/ShamanSpawnOverlay.java @@ -0,0 +1,90 @@ +package net.runelite.client.plugins.lizardmenshaman; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.time.Duration; +import java.time.Instant; +import javax.inject.Inject; +import net.runelite.api.Client; +import net.runelite.api.Perspective; +import net.runelite.api.Point; +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.components.ProgressPieComponent; + +class ShamanSpawnOverlay extends Overlay +{ + private final Client client; + private final LizardmenShamanPlugin plugin; + + @Inject + private ShamanSpawnOverlay(Client client, LizardmenShamanPlugin plugin) + { + setPosition(OverlayPosition.DYNAMIC); + setLayer(OverlayLayer.ABOVE_SCENE); + this.client = client; + this.plugin = plugin; + } + @Override + public Dimension render(Graphics2D graphics) + { + plugin.getSpawns().forEach((localPoint, spawn) -> + { + final Instant now = Instant.now(); + final long startCountdown = Duration.between(spawn.getStart(), now).getSeconds(); + final double certainSec = spawn.getCountdownTimer() - startCountdown; + + if (certainSec <= 0) + { + if (spawn.getEnd() == null) + { + spawn.setEnd(Instant.now()); + } + } + + final ProgressPieComponent pieComponent = new ProgressPieComponent(); + final Point loc = Perspective.localToCanvas(client, localPoint, client.getPlane()); + + if (loc == null || certainSec < 0) + { + return; + } + + pieComponent.setPosition(loc); + pieComponent.setProgress(certainSec / spawn.getCountdownTimer()); + if (certainSec > 4.8) + { + pieComponent.setFill(Color.GREEN); + pieComponent.setBorderColor(Color.GREEN); + pieComponent.render(graphics); + } + else if (certainSec > 3.6) + { + pieComponent.setFill(Color.YELLOW); + pieComponent.setBorderColor(Color.YELLOW); + pieComponent.render(graphics); + } + else if (certainSec > 2.4) + { + pieComponent.setFill(Color.ORANGE); + pieComponent.setBorderColor(Color.ORANGE); + pieComponent.render(graphics); + } + else if (certainSec > 1.2) + { + pieComponent.setFill(new Color(255, 140, 0)); + pieComponent.setBorderColor(new Color(255, 140, 0)); + pieComponent.render(graphics); + } + else + { + pieComponent.setFill(Color.RED); + pieComponent.setBorderColor(Color.RED); + pieComponent.render(graphics); + } + }); + return null; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/menumodifier/MenuModifierConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/menumodifier/MenuModifierConfig.java new file mode 100644 index 0000000000..53f49012c2 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/menumodifier/MenuModifierConfig.java @@ -0,0 +1,24 @@ +package net.runelite.client.plugins.menumodifier; + +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("menumodifier") +public interface MenuModifierConfig extends Config +{ + @ConfigItem(position = 0, keyName = "hideCancel", name = "Hide Cancel", description = "Hides the 'cancel' option from the right click menu") + default boolean hideCancel() { return true; } + + @ConfigItem(position = 1, keyName = "hideExamine", name = "Hide Examine", description = "Hides the 'examine' option from the right click menu") + default boolean hideExamine() { return true; } + + @ConfigItem(position = 2, keyName = "hideTradeWith", name = "Hide Trade With", description = "Hides the 'trade with' option from the right click menu") + default boolean hideTradeWith() { return true; } + + @ConfigItem(position = 3, keyName = "hideReport", name = "Hide Report", description = "Hides the 'report' option from the right click menu") + default boolean hideReport() { return true; } + + @ConfigItem(position = 4, keyName = "hideLookup", name = "Hide Lookup", description = "Hides the 'lookup' option from the right click menu") + default boolean hideLookup() { return true; } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/menumodifier/MenuModifierInputListener.java b/runelite-client/src/main/java/net/runelite/client/plugins/menumodifier/MenuModifierInputListener.java new file mode 100644 index 0000000000..cbb15161f8 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/menumodifier/MenuModifierInputListener.java @@ -0,0 +1,39 @@ +package net.runelite.client.plugins.menumodifier; + +import net.runelite.client.input.KeyListener; +import net.runelite.client.input.MouseAdapter; + +import javax.inject.Inject; +import java.awt.event.KeyEvent; + +public class MenuModifierInputListener extends MouseAdapter implements KeyListener +{ + private static final int HOTKEY = KeyEvent.VK_CONTROL; + + @Override + public void keyTyped(KeyEvent e) + { + + } + + @Inject + private MenuModifierPlugin plugin; + + @Override + public void keyPressed(KeyEvent e) + { + if (e.getKeyCode() == HOTKEY) + { + plugin.setHotKeyPressed(true); + } + } + + @Override + public void keyReleased(KeyEvent e) + { + if (e.getKeyCode() == HOTKEY) + { + plugin.setHotKeyPressed(false); + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/menumodifier/MenuModifierPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/menumodifier/MenuModifierPlugin.java new file mode 100644 index 0000000000..dc5b9e7a27 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/menumodifier/MenuModifierPlugin.java @@ -0,0 +1,168 @@ +package net.runelite.client.plugins.menumodifier; + +import net.runelite.api.events.MenuOpened; +import net.runelite.client.eventbus.Subscribe; +import com.google.inject.Provides; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.Setter; +import net.runelite.api.Client; +import net.runelite.api.MenuEntry; +import net.runelite.api.Player; +import net.runelite.api.events.GameTick; +import net.runelite.api.events.MenuEntryAdded; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.input.KeyManager; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.util.MiscUtils; +import net.runelite.client.util.Text; + +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +@PluginDescriptor( + name = "!Menu Modifier", + description = "Changes right click menu for players", + tags = { "menu", "modifier", "right", "click", "pk", "bogla" }, + enabledByDefault = false +) +public class MenuModifierPlugin extends Plugin +{ + @Inject + private Client client; + + @Inject + private MenuModifierConfig config; + + @Inject + private MenuModifierInputListener inputListener; + + @Inject + private KeyManager keyManager; + + @Provides + MenuModifierConfig provideConfig(ConfigManager configManager) + { + return configManager.getConfig(MenuModifierConfig.class); + } + + @Override + protected void startUp() throws Exception + { + keyManager.registerKeyListener(inputListener); + } + + @Override + protected void shutDown() throws Exception + { + keyManager.unregisterKeyListener(inputListener); + } + + @Getter(AccessLevel.PACKAGE) + @Setter(AccessLevel.PACKAGE) + private boolean hotKeyPressed; + + @Subscribe + public void onMenuOpened(MenuOpened event) + { + Player localPlayer = client.getLocalPlayer(); + + if (localPlayer == null) + return; + + if (!(MiscUtils.getWildernessLevelFrom(client, localPlayer.getWorldLocation()) >= 0)) + return; + + if (hotKeyPressed) + return; + + List menu_entries = new ArrayList(); + + for (MenuEntry entry : event.getMenuEntries()) + { + String option = Text.removeTags(entry.getOption()).toLowerCase(); + + if (option.contains("trade with") && config.hideTradeWith()) + continue; + + if (option.contains("lookup") && config.hideLookup()) + continue; + + if (option.contains("report") && config.hideReport()) + continue; + + if (option.contains("examine") && config.hideExamine()) + continue; + + int identifier = entry.getIdentifier(); + + Player[] players = client.getCachedPlayers(); + Player player = null; + + if (identifier >= 0 && identifier < players.length) + player = players[identifier]; + + if (player == null) + { + menu_entries.add(entry); + continue; + } + + if ((option.contains("attack") || option.contains("cast")) && (player.isFriend() || player.isClanMember())) + continue; + + menu_entries.add(entry); + } + + MenuEntry[] updated_menu_entries = new MenuEntry[menu_entries.size()]; + updated_menu_entries = menu_entries.toArray(updated_menu_entries); + + client.setMenuEntries(updated_menu_entries); + } + + /*@Subscribe + public void onMenuEntryAdded(MenuEntryAdded menuEntryAdded) + { + if (true) + return; + + if (!inWilderness) + return; + + if (hotKeyPressed) + return; + + String option = Text.removeTags(menuEntryAdded.getOption()).toLowerCase(); + + if ((option.contains("trade with") && config.hideTradeWith()) + || (option.contains("lookup") && config.hideLookup()) + || (option.contains("report") && config.hideReport()) + || (option.contains("examine") && config.hideExamine()) + || (option.contains("cancel") && config.hideCancel())) + { + int identifier = menuEntryAdded.getIdentifier(); + + Player[] players = client.getCachedPlayers(); + Player player = null; + + if (identifier >= 0 && identifier < players.length) + player = players[identifier]; + + if (player == null) + return; + + //allow trading with friends/clanmates + if (option.contains("trade with") && (player.isFriend() || player.isClanMember())) + return; + + MenuEntry[] menuEntries = client.getMenuEntries(); + + if (menuEntries.length > 0) + client.setMenuEntries(Arrays.copyOf(menuEntries, menuEntries.length - 1)); + } + }*/ +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/musicmodifier/MidiFileAdjuster.java b/runelite-client/src/main/java/net/runelite/client/plugins/musicmodifier/MidiFileAdjuster.java new file mode 100644 index 0000000000..ee67b0a694 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/musicmodifier/MidiFileAdjuster.java @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2019, Rodolfo Ruiz-Velasco + * 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.musicmodifier; + +import javax.sound.midi.*; +import java.io.IOException; + +public class MidiFileAdjuster { + + private int bankLSBValue; + private int chPosition = -1; + + private boolean customBank = false; + + public Sequence reorderTracks(Sequence sequence) throws InvalidMidiDataException, IOException { + for (Track track : sequence.getTracks()) { + for (int i = 0; i < track.size(); i++) { + MidiEvent midiEvent = track.get(i); + MidiMessage midiMessage = midiEvent.getMessage(); + + if (midiMessage instanceof ShortMessage) { + ShortMessage sm = (ShortMessage) midiMessage; + + if (sm.getChannel() < 16) { + getBankLSB(sm); + + if (i == 0 & bankLSBValue != 1) { + chPosition++; + if (chPosition == 9) { + chPosition = 10; + } + } + + if (!customBank) { + + if (sm.getChannel() == 9) { + bankLSBValue = 1; + } + + if (sm.getChannel() != 9) { + bankLSBValue = 0; + } + } + } + + if (bankLSBValue == 1) { + + int drumChannel = 9; + if (sm.getCommand() == ShortMessage.PROGRAM_CHANGE) { + sm.setMessage(ShortMessage.PROGRAM_CHANGE, drumChannel, sm.getData1(), sm.getData2()); + } + + if (sm.getCommand() == ShortMessage.CONTROL_CHANGE) { + sm.setMessage(ShortMessage.CONTROL_CHANGE, drumChannel, sm.getData1(), sm.getData2()); + } + + if (sm.getCommand() == ShortMessage.NOTE_OFF) { + sm.setMessage(ShortMessage.NOTE_OFF, drumChannel, sm.getData1(), sm.getData2()); + } + + if (sm.getCommand() == ShortMessage.NOTE_ON) { + sm.setMessage(ShortMessage.NOTE_ON, drumChannel, sm.getData1(), sm.getData2()); + } + + if (sm.getCommand() == ShortMessage.PROGRAM_CHANGE) { + sm.setMessage(ShortMessage.PROGRAM_CHANGE, drumChannel, sm.getData1(), sm.getData2()); + } + + if (sm.getCommand() == ShortMessage.CONTROL_CHANGE) { + sm.setMessage(ShortMessage.CONTROL_CHANGE, drumChannel, sm.getData1(), sm.getData2()); + } + + if (sm.getCommand() == ShortMessage.PITCH_BEND) { + sm.setMessage(ShortMessage.PITCH_BEND, drumChannel, sm.getData1(), sm.getData2()); + } + + if (sm.getCommand() == ShortMessage.CHANNEL_PRESSURE) { + sm.setMessage(ShortMessage.CHANNEL_PRESSURE, drumChannel, sm.getData1(), sm.getData2()); + } + + if (sm.getCommand() == ShortMessage.POLY_PRESSURE) { + sm.setMessage(ShortMessage.POLY_PRESSURE, drumChannel, sm.getData1(), sm.getData2()); + } + } else { + + if (sm.getCommand() == ShortMessage.PROGRAM_CHANGE) { + sm.setMessage(ShortMessage.PROGRAM_CHANGE, chPosition, sm.getData1(), sm.getData2()); + } + + if (sm.getCommand() == ShortMessage.CONTROL_CHANGE) { + sm.setMessage(ShortMessage.CONTROL_CHANGE, chPosition, sm.getData1(), sm.getData2()); + } + + if (sm.getCommand() == ShortMessage.NOTE_OFF) { + sm.setMessage(ShortMessage.NOTE_OFF, chPosition, sm.getData1(), sm.getData2()); + } + + if (sm.getCommand() == ShortMessage.NOTE_ON) { + sm.setMessage(ShortMessage.NOTE_ON, chPosition, sm.getData1(), sm.getData2()); + } + + if (sm.getCommand() == ShortMessage.PROGRAM_CHANGE) { + sm.setMessage(ShortMessage.PROGRAM_CHANGE, chPosition, sm.getData1(), sm.getData2()); + } + + if (sm.getCommand() == ShortMessage.CONTROL_CHANGE) { + sm.setMessage(ShortMessage.CONTROL_CHANGE, chPosition, sm.getData1(), sm.getData2()); + } + + if (sm.getCommand() == ShortMessage.PITCH_BEND) { + sm.setMessage(ShortMessage.PITCH_BEND, chPosition, sm.getData1(), sm.getData2()); + } + + if (sm.getCommand() == ShortMessage.CHANNEL_PRESSURE) { + sm.setMessage(ShortMessage.CHANNEL_PRESSURE, chPosition, sm.getData1(), sm.getData2()); + } + + if (sm.getCommand() == ShortMessage.POLY_PRESSURE) { + sm.setMessage(ShortMessage.POLY_PRESSURE, chPosition, sm.getData1(), sm.getData2()); + } + } + } + } + } + return sequence; + } + + private void getBankLSB(ShortMessage sm) throws InvalidMidiDataException + { + if (sm.getCommand() == ShortMessage.CONTROL_CHANGE) + { + if (sm.getData1() == 32) + { + bankLSBValue = sm.getData2(); + customBank = true; + } + if (sm.getData1() == 0) + { + sm.setMessage(sm.getCommand(), sm.getChannel(), sm.getData1(), bankLSBValue); + } + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/musicmodifier/MusicCustomizerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/musicmodifier/MusicCustomizerPlugin.java new file mode 100644 index 0000000000..a1202750bb --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/musicmodifier/MusicCustomizerPlugin.java @@ -0,0 +1,355 @@ +/* + * Copyright (c) 2019, Rodolfo Ruiz-Velasco + * 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.musicmodifier; + +import net.runelite.api.*; +import net.runelite.api.events.GameTick; +import net.runelite.api.events.WidgetLoaded; +import net.runelite.api.widgets.*; +import net.runelite.client.callback.ClientThread; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.game.chatbox.ChatboxPanelManager; +import net.runelite.client.game.chatbox.ChatboxTextInput; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; + +import javax.inject.Inject; +import java.io.File; + +@PluginDescriptor( + name = "Music Track Customizer", + description = "Customize what track plays and how it sounds, with local files", + tags = {"music", "sound"}, + enabledByDefault = false +) + +public class MusicCustomizerPlugin extends Plugin +{ + + @Inject + private Client client; + + @Inject + private ChatboxPanelManager chatboxPanelManager; + + @Inject + private ClientThread clientThread; + + private RealTimeMIDIPlayer realTimeMIDIPlayer = new RealTimeMIDIPlayer(); + + private String songName = "Scape Main"; + + private ChatboxTextInput songInput; + + private Widget playlistModeButton; + + private Widget playlistBox; + + private Widget hidePlaylistButton; + + private Widget addPlaylistSongButton; + + private Widget playlistText; + + private Widget playlistSong; + + private String defaultUnlockedSongs; + + private boolean isLooping = true; + + private boolean playlistMode = false; + + private int newSongY = 34; + + private int playlistCount = 0; + + @Override + public void startUp() + { + playSong(songName); + } + + @Override + public void shutDown() + { + if (realTimeMIDIPlayer != null) + { + realTimeMIDIPlayer.stopSong(); + } + } + + @Subscribe + public void onGameTick(GameTick event) + { + try + { + if (!playlistMode) + { + String newSong = client.getWidget(WidgetInfo.MUSICTAB_CURRENT_SONG_NAME).getText(); + + if (!newSong.equals(songName)) + { + songName = newSong; + playSongFromList(songName); + } + } + } catch (NullPointerException ignored) + { + + } + + } + + private void playSong(String song) + { + File midiMusicFile = new File(System.getProperty("user.home") + "/RuneLiteAudio/MIDI Files/" + + "Music/" + song + ".mid/"); + if (realTimeMIDIPlayer.midi == null) + { + realTimeMIDIPlayer.midi = midiMusicFile; + realTimeMIDIPlayer.run(); + } + + else + { + if (realTimeMIDIPlayer.isPlaying()) + { + realTimeMIDIPlayer.stopSong(); + } + realTimeMIDIPlayer.midi = midiMusicFile; + realTimeMIDIPlayer.run(); + } + } + + @Subscribe + private void onWidgetLoaded(WidgetLoaded widgetLoaded) + { + if (widgetLoaded.getGroupId() == WidgetID.MUSICTAB_GROUP_ID) + { + Widget musicPlayerSongs = client.getWidget(WidgetInfo.MUSICTAB_ALL_SONGS); + if (musicPlayerSongs != null) + { + playlistModeButton = musicPlayerSongs.createChild(-1, WidgetType.GRAPHIC); + playlistModeButton.setSpriteId(SpriteID.RS2_TAB_MUSIC); + playlistModeButton.setOriginalWidth(32); + playlistModeButton.setOriginalHeight(32); + playlistModeButton.setXPositionMode(WidgetPositionMode.ABSOLUTE_RIGHT); + playlistModeButton.setOriginalX(0); + playlistModeButton.setOriginalY(0); + playlistModeButton.setHasListener(true); + playlistModeButton.setAction(1, "Open"); + playlistModeButton.setOnOpListener((JavaScriptCallback) e -> openPlaylist()); + playlistModeButton.setName("Playlist"); + playlistModeButton.setHidden(true); //Playlist is not enabled for this release (Unfinished). + playlistModeButton.revalidate(); + } + } + } + + private void openPlaylist() + { + playlistMode = true; + + Widget currentPlayingSong = client.getWidget(WidgetInfo.MUSICTAB_CURRENT_SONG_NAME); + Widget allInGameSongs = client.getWidget(WidgetInfo.MUSICTAB_ALL_SONGS); + Widget musicScrollbar = client.getWidget(WidgetInfo.MUSICTAB_SCROLLBAR); + allInGameSongs.setHidden(true); + musicScrollbar.setHidden(true); + + defaultUnlockedSongs = client.getWidget(WidgetInfo.MUSICTAB_UNLOCKED_SONGS).getText(); + + client.getWidget(WidgetInfo.MUSICTAB_UNLOCKED_SONGS).setText(playlistCount + " / 10"); + + playlistBox = client.getWidget(WidgetInfo.MUSICTAB_INTERFACE); + + playlistText = playlistBox.createChild(-1, WidgetType.TEXT); + playlistText.setText("Music Playlist"); + playlistText.setFontId(497); + playlistText.setXPositionMode(WidgetPositionMode.ABSOLUTE_TOP); + playlistText.setOriginalX(40); + playlistText.setOriginalY(14); + playlistText.setOriginalHeight(1); + playlistText.setOriginalWidth(1); + playlistText.setTextColor(currentPlayingSong.getTextColor()); + playlistText.revalidate(); + + hidePlaylistButton = playlistBox.createChild(-1, WidgetType.GRAPHIC); + hidePlaylistButton.setSpriteId(SpriteID.RS2_TAB_MUSIC); + hidePlaylistButton.setOriginalWidth(32); + hidePlaylistButton.setOriginalHeight(32); + hidePlaylistButton.setXPositionMode(WidgetPositionMode.ABSOLUTE_RIGHT); + hidePlaylistButton.setOriginalX(0); + hidePlaylistButton.setOriginalY(6); + hidePlaylistButton.setHasListener(true); + hidePlaylistButton.setAction(1, "Close"); + hidePlaylistButton.setOnOpListener((JavaScriptCallback) e -> closePlaylist()); + hidePlaylistButton.setName("Playlist"); + hidePlaylistButton.revalidate(); + + addPlaylistSongButton = playlistBox.createChild(-1, WidgetType.GRAPHIC); + addPlaylistSongButton.setSpriteId(SpriteID.BANK_ADD_TAB_ICON); + addPlaylistSongButton.setOriginalWidth(36); + addPlaylistSongButton.setOriginalHeight(32); + addPlaylistSongButton.setXPositionMode(WidgetPositionMode.ABSOLUTE_LEFT); + addPlaylistSongButton.setOriginalX(0); + addPlaylistSongButton.setOriginalY(6); + addPlaylistSongButton.setHasListener(true); + addPlaylistSongButton.setAction(1, "Add to"); + addPlaylistSongButton.setOnOpListener((JavaScriptCallback) e -> addSongFromInput()); + addPlaylistSongButton.setName("Playlist"); + addPlaylistSongButton.revalidate(); + + if (playlistSong != null) + { + playlistSong.setHidden(false); + } + } + + private void closePlaylist() + { + playlistMode = false; + + Widget allInGameSongs = client.getWidget(WidgetInfo.MUSICTAB_ALL_SONGS); + Widget musicScrollbar = client.getWidget(WidgetInfo.MUSICTAB_SCROLLBAR); + allInGameSongs.setHidden(false); + musicScrollbar.setHidden(false); + + client.getWidget(WidgetInfo.MUSICTAB_UNLOCKED_SONGS).setText(defaultUnlockedSongs); + playlistText.setHidden(true); + addPlaylistSongButton.setHidden(true); + hidePlaylistButton.setHidden(true); + + if (playlistSong != null) + { + playlistSong.setHidden(true); + } + } + + private void addSongFromInput() + { + addPlaylistSongButton.setAction(1, "Close search"); + addPlaylistSongButton.setOnOpListener((JavaScriptCallback) e -> closeInput()); + songInput = chatboxPanelManager.openTextInput("Please type a valid song name") + .onChanged(s -> clientThread.invokeLater(() -> updateSongs(s))) + .onClose(() -> + { + clientThread.invokeLater(() -> updateSongs(songInput.getValue())); + addPlaylistSongButton.setOnOpListener((JavaScriptCallback) e -> addSongFromInput()); + addPlaylistSongButton.setAction(1, "Add to"); + }) + .build(); + } + + private void updateSongs() + { + String song = ""; + if (chatboxIsOpen()) + { + song = songInput.getValue(); + } + updateSongs(song); + } + + private void updateSongs(String song) + { + if (playlistBox == null) + { + return; + } + + if (new File(System.getProperty("user.home") + "/RuneLiteAudio/MIDI Files/" + + "Music/" + song + ".mid/").exists()) + { + playListSongPlayer(song); + } + } + + private void playListSongPlayer(String song) + { + if (!song.equals(songName) && !chatboxIsOpen() && playlistCount < 10) + { + Widget playlistWidget = client.getWidget(WidgetInfo.MUSICTAB_INTERFACE); + playlistSong = playlistWidget.createChild(-1, WidgetType.TEXT); + playlistSong.setText(song); + playlistSong.setFontId(495); + playlistSong.setOriginalX(12); + playlistSong.setOriginalY(newSongY); + playlistSong.setOriginalWidth(120); + playlistSong.setOriginalHeight(16); + playlistSong.setTextColor(client.getWidget(WidgetInfo.MUSICTAB_CURRENT_SONG_NAME).getTextColor()); + playlistSong.setHasListener(true); + playlistSong.setAction(1, "Play"); + playlistSong.setOnOpListener((JavaScriptCallback) e -> playSongFromList(song)); + playlistSong.setName(song); + playlistSong.revalidate(); + + newSongY = newSongY + 15; + + songName = song; + + playlistCount++; + client.getWidget(WidgetInfo.MUSICTAB_UNLOCKED_SONGS).setText(playlistCount + " / 10"); + + if (playlistCount == 10) + { + addPlaylistSongButton.setHidden(true); + } + } + } + + private boolean chatboxIsOpen() + { + return songInput != null && chatboxPanelManager.getCurrentInput() == songInput; + } + + private void closeInput() + { + updateSongs(); + chatboxPanelManager.close(); + } + + private void playSongFromList(String song) + { + client.getWidget(WidgetInfo.MUSICTAB_CURRENT_SONG_NAME).setName(song); + File midiMusicFile = new File(System.getProperty("user.home") + "/RuneLiteAudio/MIDI Files/" + + "Music/" + song + ".mid/"); + + if (realTimeMIDIPlayer.midi == null) + { + realTimeMIDIPlayer.midi = midiMusicFile; + realTimeMIDIPlayer.run(); + } + + else + { + if (realTimeMIDIPlayer.isPlaying()) + { + realTimeMIDIPlayer.stopSong(); + } + realTimeMIDIPlayer.midi = midiMusicFile; + realTimeMIDIPlayer.run(); + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/musicmodifier/RealTimeMIDIPlayer.java b/runelite-client/src/main/java/net/runelite/client/plugins/musicmodifier/RealTimeMIDIPlayer.java new file mode 100644 index 0000000000..858c7465fd --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/musicmodifier/RealTimeMIDIPlayer.java @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2019, Rodolfo Ruiz-Velasco + * 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.musicmodifier; + +import com.sun.media.sound.AudioSynthesizer; + +import javax.sound.midi.*; +import javax.sound.sampled.*; +import java.io.*; +import java.util.HashMap; +import java.util.Map; + +public class RealTimeMIDIPlayer implements Runnable +{ + private AudioFormat format; + + private Sequence midiSequence; + + private Soundbank soundbank; + + private SourceDataLine sdl; + + private MusicCustomizerPlugin customMusicPlugin; + + private MidiFileAdjuster adjuster; + + private Clip clip; + + public boolean looping = true; + + public File soundFont = new File(System.getProperty("user.home") + "/RuneLiteAudio/SoundFonts/" + + "RuneScape 2.sf2/"); + + public File midi; + + @Override + public void run() { + + try { + + adjuster = new MidiFileAdjuster(); //Unfinished class + + midiSequence = MidiSystem.getSequence(midi); + soundbank = MidiSystem.getSoundbank(soundFont); + init(); + } + catch (IOException | InvalidMidiDataException e) + { + e.printStackTrace(); + } + } + + public void stopSong() + { + if (sdl.isRunning()) + { + sdl.stop(); + } + } + + public static AudioSynthesizer findAudioSynthesizer() throws MidiUnavailableException + { + Synthesizer synth = MidiSystem.getSynthesizer(); + if (synth instanceof AudioSynthesizer) + return (AudioSynthesizer) synth; + + double gain = 0.8D; + + MidiDevice.Info[] infos = MidiSystem.getMidiDeviceInfo(); + MidiChannel[] channels = synth.getChannels(); + + for (int i = 0; i < channels.length; i++) + { + channels[i].controlChange(7, ((int) (channels[i].getController(7) * gain))); + } + + for (int i = 0; i < infos.length; i++) + { + MidiDevice device = MidiSystem.getMidiDevice(infos[i]); + + if (device instanceof AudioSynthesizer) + return (AudioSynthesizer) device; + } + return null; + } + + public boolean isPlaying() + { + return sdl.isActive(); + } + + public static double send(Sequence sequence, Receiver receiver) { + float divtype = sequence.getDivisionType(); + assert (sequence.getDivisionType() == Sequence.PPQ); + Track[] tracks = sequence.getTracks(); + int[] trackspos = new int[tracks.length]; + int mpq = 500000; + int seqres = sequence.getResolution(); + long lasttick = 0; + long curtime = 0; + while (true) { + MidiEvent selevent = null; + int seltrack = -1; + for (int i = 0; i < tracks.length; i++) { + int trackpos = trackspos[i]; + Track track = tracks[i]; + if (trackpos < track.size()) { + MidiEvent event = track.get(trackpos); + if (selevent == null + || event.getTick() < selevent.getTick()) { + selevent = event; + seltrack = i; + } + } + } + if (seltrack == -1) + break; + trackspos[seltrack]++; + long tick = selevent.getTick(); + if (divtype == Sequence.PPQ) + curtime += ((tick - lasttick) * mpq) / seqres; + else + curtime = (long) ((tick * 1000000.0 * divtype) / seqres); + lasttick = tick; + MidiMessage msg = selevent.getMessage(); + if (msg instanceof MetaMessage) { + if (divtype == Sequence.PPQ) + if (((MetaMessage) msg).getType() == 0x51) { + byte[] data = ((MetaMessage) msg).getData(); + mpq = ((data[0] & 0xff) << 16) + | ((data[1] & 0xff) << 8) | (data[2] & 0xff); + } + } else { + if (receiver != null) + receiver.send(msg, curtime); + } + } + return curtime / 1000000.0; + } + + public void init() { + new Thread(new Runnable() { + @Override + public void run() { + + AudioSynthesizer synth = null; + try { + synth = findAudioSynthesizer(); + format = new AudioFormat(44100, 16, 2, true, false); + + Map info = new HashMap(); + info.put("resamplerType", "sinc"); + info.put("maxPolyphony", "8192"); + AudioInputStream ais = synth.openStream(format, info); + synth.unloadAllInstruments(synth.getDefaultSoundbank()); + synth.loadAllInstruments(soundbank); + double total = send(midiSequence, synth.getReceiver()); + long length = (long) (ais.getFormat().getFrameRate() * (total + 4)); + AudioInputStream stream = new AudioInputStream(ais, format, length); + sdl = AudioSystem.getSourceDataLine(format); + sdl.open(format); + sdl.start(); + writeAudio(sdl, stream); + } catch (LineUnavailableException | MidiUnavailableException e) { + e.printStackTrace(); + } + } + }).start(); + } + + public void writeAudio(SourceDataLine sdl, AudioInputStream stream) + { + new Thread(new Runnable() { + @Override + public void run() { + + byte[] sampledAudio = new byte[1024]; + + int numBytesRead = 0; + + while (numBytesRead != -1) { + try + { + numBytesRead = stream.read(sampledAudio, 0, sampledAudio.length); + + if (numBytesRead >= 0) { + sdl.write(sampledAudio, 0, numBytesRead); + } + } + + catch (IOException e) + { + e.printStackTrace(); + } + + finally { + + if (!isPlaying() && looping) + { + this.run(); + } + } + } + } + }).start(); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/nexthitnotifier/NextHitNotifierConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/nexthitnotifier/NextHitNotifierConfig.java new file mode 100644 index 0000000000..8bce5b84b7 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/nexthitnotifier/NextHitNotifierConfig.java @@ -0,0 +1,11 @@ +package net.runelite.client.plugins.nexthitnotifier; + + +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; + +@ConfigGroup("nexthitnotifier") +public interface NextHitNotifierConfig extends Config +{ + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/nexthitnotifier/NextHitNotifierOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/nexthitnotifier/NextHitNotifierOverlay.java new file mode 100644 index 0000000000..fe47f37307 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/nexthitnotifier/NextHitNotifierOverlay.java @@ -0,0 +1,59 @@ +package net.runelite.client.plugins.nexthitnotifier; + +import net.runelite.api.Client; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.components.PanelComponent; +import net.runelite.client.ui.overlay.components.TitleComponent; +import net.runelite.client.util.MiscUtils; + +import javax.inject.Inject; +import java.awt.*; + +public class NextHitNotifierOverlay extends Overlay +{ + private final Client client; + private final NextHitNotifierPlugin plugin; + private final NextHitNotifierConfig config; + + private final PanelComponent panelComponent = new PanelComponent(); + private final Dimension panelSize = new Dimension(48, 0); + + @Inject + private NextHitNotifierOverlay(Client client, NextHitNotifierPlugin plugin, NextHitNotifierConfig config) + { + setPosition(OverlayPosition.BOTTOM_LEFT); + //setPosition(OverlayPosition.DYNAMIC); + //setPosition(OverlayPosition.DETACHED); + + this.client = client; + this.plugin = plugin; + this.config = config; + } + + @Override + public Dimension render(Graphics2D graphics) + { + panelComponent.getChildren().clear(); + panelComponent.setPreferredSize(panelSize); + + String lastHitText = Integer.toString(plugin.lastHit); + int lastHit = plugin.lastHit; + + if (plugin.showTime < 0) + { + lastHitText = "0"; + lastHit = 0; + } + + int g = (int)MiscUtils.clamp((float)Math.floor(lastHit / 30.f) * 255.f, 0.f, 255.f); + int r = 255 - g; + + Color textColor = Color.getHSBColor(Color.RGBtoHSB(r, g, 0, null)[0], 1.f, 1.f); + + panelComponent.getChildren().add(TitleComponent.builder().text("Next hit:").color(Color.YELLOW).build()); + panelComponent.getChildren().add(TitleComponent.builder().text(lastHitText).color(textColor).build()); + + return panelComponent.render(graphics); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/nexthitnotifier/NextHitNotifierPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/nexthitnotifier/NextHitNotifierPlugin.java new file mode 100644 index 0000000000..ccece4d4a4 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/nexthitnotifier/NextHitNotifierPlugin.java @@ -0,0 +1,116 @@ +package net.runelite.client.plugins.nexthitnotifier; + +import net.runelite.client.eventbus.Subscribe; +import com.google.inject.Provides; +import net.runelite.api.Client; +import net.runelite.api.GameState; +import net.runelite.api.Skill; +import net.runelite.api.events.ExperienceChanged; +import net.runelite.api.events.GameStateChanged; +import net.runelite.api.events.GameTick; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.ui.overlay.OverlayManager; + +import javax.inject.Inject; + +@PluginDescriptor( + name = "!Next Hit Notifier", + description = "Shows estimated next hit based on xp drop.", + tags = { "experience", "damage", "overlay", "pking", "bogla" }, + enabledByDefault = false +) +public class NextHitNotifierPlugin extends Plugin +{ + @Inject + private Client client; + + @Inject + private OverlayManager overlayManager; + + @Inject + private NextHitNotifierOverlay overlay; + + private int lastHpXp = 0; + int lastHit = 0; + int showTime = 0; + + @Provides + NextHitNotifierConfig getConfig(ConfigManager configManager) + { + return configManager.getConfig(NextHitNotifierConfig.class); + } + + @Override + protected void startUp() throws Exception + { + overlayManager.add(overlay); + } + + @Override + protected void shutDown() throws Exception + { + overlayManager.remove(overlay); + } + + @Subscribe + public void onGameStateChanged(GameStateChanged event) + { + if (event.getGameState() == GameState.LOGGED_IN) + { + lastHpXp = client.getSkillExperience(Skill.HITPOINTS); + lastHit = 0; + showTime = 0; + } + else + { + lastHpXp = 0; + lastHit = 0; + showTime = 0; + } + } + + @Subscribe + public void onGameTick(GameTick event) + { + if (showTime > 0) + showTime--; + else + lastHit = 0; + } + + @Subscribe + public void onExperienceChanged(ExperienceChanged event) + { + if (client.getGameState() != GameState.LOGGED_IN) + { + lastHpXp = 0; + lastHit = 0; + showTime = 0; + return; + } + + final Skill skill = event.getSkill(); + + if (skill != Skill.HITPOINTS) + return; + + final int currentXp = client.getSkillExperience(skill); + + int gainedXp = currentXp - lastHpXp; + + //filter out big xp drops (such as login) + if (gainedXp > 1000) + { + lastHpXp = client.getSkillExperience(skill); + return; + } + + lastHit = (int)Math.rint(gainedXp / 1.33f); + lastHpXp = currentXp; + showTime = 3; + } + + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/pkvision/PKVisionConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/pkvision/PKVisionConfig.java new file mode 100644 index 0000000000..74a733a7eb --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/pkvision/PKVisionConfig.java @@ -0,0 +1,55 @@ +package net.runelite.client.plugins.pkvision; + +import java.awt.Color; +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("pkvision") +public interface PKVisionConfig extends Config +{ + @ConfigItem(position = 0, keyName = "drawOwnName", name = "Highlight own player", description = "Configures whether or not your own player should be highlighted") + default boolean highlightOwnPlayer() + { + return false; + } + + @ConfigItem(position = 1, keyName = "ownNameColor", name = "Own player color", description = "Color of your own player") + default Color getOwnPlayerColor() + { + return new Color(0, 184, 212); + } + + @ConfigItem(position = 2, keyName = "drawFriendNames", name = "Highlight friends", description = "Configures whether or not friends should be highlighted") + default boolean highlightFriends() + { + return true; + } + + @ConfigItem(position = 3, keyName = "friendNameColor", name = "Friend color", description = "Color of friend names" ) + default Color getFriendColor() + { + return new Color(0, 200, 80); + } + + @ConfigItem(position = 4, keyName = "drawPlayerTiles", name = "Draw tiles under players", description = "Configures whether or not tiles under highlighted players should be drawn") + default boolean drawTiles() + { + return false; + } + + @ConfigItem(position = 5, keyName = "drawPlayerNames", name = "Draw names above players", description = "Configures whether or not player names should be drawn above players") + default boolean drawPlayerNames() { return true; } + + @ConfigItem(position = 6, keyName = "drawPlayerLevels", name = "Draw levels above players", description = "Configures whether or not player levels should be drawn above players") + default boolean drawPlayerLevels() + { + return true; + } + + //@ConfigItem(position = 7, keyName = "drawPlayerHealth", name = "Draw health above players", description = "Configures whether or not player levels should be drawn above players") + //default boolean drawPlayerHealth() + //{ + // return true; + //} +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/pkvision/PKVisionMinimapOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/pkvision/PKVisionMinimapOverlay.java new file mode 100644 index 0000000000..fc844eb734 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/pkvision/PKVisionMinimapOverlay.java @@ -0,0 +1,43 @@ +package net.runelite.client.plugins.pkvision; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import javax.inject.Inject; +import javax.inject.Singleton; +import net.runelite.api.Player; +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.OverlayPriority; +import net.runelite.client.ui.overlay.OverlayUtil; + +@Singleton +public class PKVisionMinimapOverlay extends Overlay +{ + private final PKVisionService pkVisionService; + + @Inject + private PKVisionMinimapOverlay(PKVisionService pkVisionService) + { + this.pkVisionService = pkVisionService; + setLayer(OverlayLayer.ABOVE_WIDGETS); + setPosition(OverlayPosition.DYNAMIC); + setPriority(OverlayPriority.HIGH); + } + + @Override + public Dimension render(Graphics2D graphics) + { + pkVisionService.forEachPlayer((player, color) -> renderPlayerOverlay(graphics, player, color)); + return null; +} + + private void renderPlayerOverlay(Graphics2D graphics, Player actor, Color color) + { + final net.runelite.api.Point minimapLocation = actor.getMinimapLocation(); + + if (minimapLocation != null) + OverlayUtil.renderTextLocation(graphics, minimapLocation, Integer.toString(actor.getCombatLevel()), color); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/pkvision/PKVisionOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/pkvision/PKVisionOverlay.java new file mode 100644 index 0000000000..b36b91da7b --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/pkvision/PKVisionOverlay.java @@ -0,0 +1,55 @@ +package net.runelite.client.plugins.pkvision; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import javax.inject.Inject; +import javax.inject.Singleton; + +import net.runelite.api.Player; +import net.runelite.api.Point; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.OverlayPriority; +import net.runelite.client.ui.overlay.OverlayUtil; + +@Singleton +public class PKVisionOverlay extends Overlay +{ + private final PKVisionService pkVisionService; + private final PKVisionConfig config; + + @Inject + private PKVisionOverlay(PKVisionConfig config, PKVisionService pkVisionService, PKVisionPlugin pkVisionPlugin) + { + this.config = config; + this.pkVisionService = pkVisionService; + setPosition(OverlayPosition.DYNAMIC); + setPriority(OverlayPriority.MED); + } + + @Override + public Dimension render(Graphics2D graphics) + { + pkVisionService.forEachPlayer((player, color) -> renderPlayerOverlay(graphics, player, color)); + return null; + } + + private void renderPlayerOverlay(Graphics2D graphics, Player actor, Color color) + { + if (!config.drawPlayerNames() && !config.drawPlayerLevels()) + return; + + String text = ""; + if (config.drawPlayerLevels()) + text += "(" + actor.getCombatLevel() + ") "; + + if (config.drawPlayerNames()) + text += actor.getName().replace('\u00A0', ' '); + + Point textLocation = actor.getCanvasTextLocation(graphics, text, actor.getLogicalHeight() + 40); + + if (textLocation != null) + OverlayUtil.renderTextLocation(graphics, textLocation, text, color); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/pkvision/PKVisionPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/pkvision/PKVisionPlugin.java new file mode 100644 index 0000000000..6fc74fb2c2 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/pkvision/PKVisionPlugin.java @@ -0,0 +1,135 @@ +package net.runelite.client.plugins.pkvision; + +import net.runelite.client.eventbus.Subscribe; +import com.google.inject.Provides; +import java.awt.Color; +import javax.inject.Inject; +import net.runelite.api.Client; +import static net.runelite.api.MenuAction.*; +import net.runelite.api.MenuEntry; +import net.runelite.api.Player; +import net.runelite.api.events.MenuEntryAdded; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.ui.overlay.OverlayManager; +import net.runelite.client.util.ColorUtil; +import net.runelite.client.util.MiscUtils; +import net.runelite.client.util.Text; + +@PluginDescriptor( + name = "!PK Vision", + description = "Highlight players on-screen and/or on the minimap", + tags = {"highlight", "minimap", "overlay", "players", "pk", "helper", "vision", "bogla"}, + enabledByDefault = false +) +public class PKVisionPlugin extends Plugin +{ + @Inject + private OverlayManager overlayManager; + + @Inject + private PKVisionConfig config; + + @Inject + private PKVisionOverlay pkVisionOverlay; + + @Inject + private PKVisionTileOverlay pkVisionTileOverlay; + + @Inject + private PKVisionMinimapOverlay pkVisionMinimapOverlay; + + @Inject + private Client client; + + @Provides + PKVisionConfig provideConfig(ConfigManager configManager) + { + return configManager.getConfig(PKVisionConfig.class); + } + + @Override + protected void startUp() throws Exception + { + overlayManager.add(pkVisionOverlay); + overlayManager.add(pkVisionTileOverlay); + overlayManager.add(pkVisionMinimapOverlay); + } + + @Override + protected void shutDown() throws Exception + { + overlayManager.remove(pkVisionOverlay); + overlayManager.remove(pkVisionTileOverlay); + overlayManager.remove(pkVisionMinimapOverlay); + } + + @Subscribe + public void onMenuEntryAdded(MenuEntryAdded menuEntryAdded) + { + int type = menuEntryAdded.getType(); + String option = Text.removeTags(menuEntryAdded.getOption()).toLowerCase(); + + if (type >= 2000) + type -= 2000; + + int identifier = menuEntryAdded.getIdentifier(); + if (type == FOLLOW.getId() || type == TRADE.getId() + || type == ITEM_USE_ON_PLAYER.getId() || type == PLAYER_FIRST_OPTION.getId() + || type == PLAYER_SECOND_OPTION.getId() || type == PLAYER_THIRD_OPTION.getId() + || type == PLAYER_FOURTH_OPTION.getId() || type == PLAYER_FIFTH_OPTION.getId() + || type == PLAYER_SIXTH_OPTION.getId() || type == PLAYER_SEVENTH_OPTION.getId() + || type == PLAYER_EIGTH_OPTION.getId() || type == SPELL_CAST_ON_PLAYER.getId() + || type == RUNELITE.getId()) + { + final Player localPlayer = client.getLocalPlayer(); + Player[] players = client.getCachedPlayers(); + Player player = null; + + if (identifier >= 0 && identifier < players.length) + player = players[identifier]; + + if (player == null) + return; + + Color color = null; + + if (config.highlightFriends() && (player.isFriend() || player.isClanMember())) + { + color = config.getFriendColor(); + } + else if (!player.isFriend() && !player.isClanMember()) + { + int lvlDelta = player.getCombatLevel() - localPlayer.getCombatLevel(); + int wildyLvl = MiscUtils.getWildernessLevelFrom(client, player.getWorldLocation()); + + if (wildyLvl <= 0) + return; + + int R = MiscUtils.clamp((int)(((float)(lvlDelta + wildyLvl) / (float)(wildyLvl * 2)) * 255.f), 0, 255); + int G = MiscUtils.clamp(255 - R, 0, 255); + + if (Math.abs(lvlDelta) <= wildyLvl) + color = Color.getHSBColor(Color.RGBtoHSB(R, G, 0, null)[0], 1.f, 1.f); + } + + if (color != null) + { + MenuEntry[] menuEntries = client.getMenuEntries(); + MenuEntry lastEntry = menuEntries[menuEntries.length - 1]; + + // strip out existing '); + if (idx != -1) + target = target.substring(idx + 1); + + lastEntry.setTarget(ColorUtil.prependColorTag(target, color)); + + + client.setMenuEntries(menuEntries); + } + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/pkvision/PKVisionService.java b/runelite-client/src/main/java/net/runelite/client/plugins/pkvision/PKVisionService.java new file mode 100644 index 0000000000..770cbd6505 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/pkvision/PKVisionService.java @@ -0,0 +1,63 @@ +package net.runelite.client.plugins.pkvision; + +import java.awt.Color; +import java.util.function.BiConsumer; +import javax.inject.Inject; +import javax.inject.Singleton; +import net.runelite.api.Client; +import net.runelite.api.Player; +import net.runelite.client.util.MiscUtils; + +@Singleton +public class PKVisionService +{ + private final Client client; + private final PKVisionConfig config; + + @Inject + private PKVisionService(Client client, PKVisionConfig config) + { + this.config = config; + this.client = client; + } + + public void forEachPlayer(final BiConsumer consumer) + { + final Player localPlayer = client.getLocalPlayer(); + + for (Player player : client.getPlayers()) + { + if (player == null || player.getName() == null) + continue; + + if (player == localPlayer) + { + if (config.highlightOwnPlayer()) + consumer.accept(player, config.getOwnPlayerColor()); + + continue; + } + + if (config.highlightFriends() && (player.isFriend() || player.isClanMember())) + { + consumer.accept(player, config.getFriendColor()); + } + else if (player != localPlayer && !player.isFriend() && !player.isClanMember()) + { + int lvlDelta = player.getCombatLevel() - localPlayer.getCombatLevel(); + int wildyLvl = MiscUtils.getWildernessLevelFrom(client, player.getWorldLocation()); + + if (wildyLvl <= 0) + continue; + + if (Math.abs(lvlDelta) > wildyLvl) + continue; + + int R = MiscUtils.clamp((int)(((float)(lvlDelta + wildyLvl) / (float)(wildyLvl * 2)) * 255.f), 0, 255); + int G = MiscUtils.clamp(255 - R, 0, 255); + + consumer.accept(player, Color.getHSBColor(Color.RGBtoHSB(R, G, 0, null)[0], 1.f, 1.f)); + } + } + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/pkvision/PKVisionTileOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/pkvision/PKVisionTileOverlay.java new file mode 100644 index 0000000000..aba6c3fad6 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/pkvision/PKVisionTileOverlay.java @@ -0,0 +1,44 @@ +package net.runelite.client.plugins.pkvision; + +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.Polygon; +import javax.inject.Inject; +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.OverlayPriority; +import net.runelite.client.ui.overlay.OverlayUtil; + +public class PKVisionTileOverlay extends Overlay +{ + private final PKVisionService pkVisionService; + private final PKVisionConfig config; + + @Inject + private PKVisionTileOverlay(PKVisionConfig config, PKVisionService pkVisionService) + { + this.config = config; + this.pkVisionService = pkVisionService; + setLayer(OverlayLayer.ABOVE_SCENE); + setPosition(OverlayPosition.DYNAMIC); + setPriority(OverlayPriority.MED); + } + + @Override + public Dimension render(Graphics2D graphics) + { + if (!config.drawTiles()) + return null; + + pkVisionService.forEachPlayer((player, color) -> + { + final Polygon poly = player.getCanvasTilePoly(); + + if (poly != null) + OverlayUtil.renderPolygon(graphics, poly, color); + }); + + return null; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/plankmakehelper/PlankMakeOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/plankmakehelper/PlankMakeOverlay.java new file mode 100644 index 0000000000..8295f294b1 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/plankmakehelper/PlankMakeOverlay.java @@ -0,0 +1,88 @@ +package net.runelite.client.plugins.plankmakehelper; + +import net.runelite.api.*; +import net.runelite.api.widgets.Widget; +import net.runelite.api.widgets.WidgetInfo; +import net.runelite.api.widgets.WidgetItem; +import net.runelite.client.ui.overlay.*; + +import javax.inject.Inject; +import java.awt.*; + +public class PlankMakeOverlay extends Overlay { + + private final PlankMakePlugin plugin; + private final Client client; + + @Inject + public PlankMakeOverlay(final PlankMakePlugin plugin, final Client client) { + super(plugin); + this.plugin = plugin; + this.client = client; + + setPosition(OverlayPosition.DETACHED); + setLayer(OverlayLayer.ALWAYS_ON_TOP); + setPriority(OverlayPriority.MED); + } + + @Override + public Dimension render(Graphics2D graphics) { + if (hasPlankableItems()) { + renderInventory(graphics); + renderPlankMakeSpell(graphics); + } + return null; + } + + private void renderInventory(Graphics2D graphics) { + Widget inventory = client.getWidget(WidgetInfo.INVENTORY); + + int firstItemSeenIndex = -1; + + if (inventory != null) { + for (WidgetItem item : inventory.getWidgetItems()) { + if (PlankMakePlugin.isLogAndPlankable(item.getId())) { + if (firstItemSeenIndex == -1) { + firstItemSeenIndex = item.getIndex(); + } + if (!inventory.isHidden()) { + if (item.getIndex() != firstItemSeenIndex) { + OverlayUtil.renderPolygon(graphics, RectangleToPolygon(item.getCanvasBounds()), Color.BLUE); + } + } + } + } + if (firstItemSeenIndex != -1) { + OverlayUtil.renderPolygon(graphics, RectangleToPolygon(inventory.getWidgetItem(firstItemSeenIndex).getCanvasBounds()), Color.CYAN); + } + } + } + + private void renderPlankMakeSpell(Graphics2D graphics) { + Widget plankMakeSpell = client.getWidget(218,128); + if (plankMakeSpell != null && (plankMakeSpell.getCanvasLocation().getX() != 29 & plankMakeSpell.getCanvasLocation().getY() != 32)) { + OverlayUtil.renderPolygon(graphics, RectangleToPolygon(plankMakeSpell.getBounds()), Color.CYAN); + } + } + + private boolean hasPlankableItems() { + ItemContainer invo = client.getItemContainer(InventoryID.INVENTORY); + if (invo != null) { + if (invo.getItems().length > 0) { + for (Item item : invo.getItems()) { + if (PlankMakePlugin.isLogAndPlankable(item.getId())) { + return true; + } + } + } + } + return false; + } + + static Polygon RectangleToPolygon(Rectangle rect) { + int[] xpoints = {rect.x, rect.x + rect.width, rect.x + rect.width, rect.x}; + int[] ypoints = {rect.y, rect.y, rect.y + rect.height, rect.y + rect.height}; + return new Polygon(xpoints, ypoints, 4); + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/plankmakehelper/PlankMakePlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/plankmakehelper/PlankMakePlugin.java new file mode 100644 index 0000000000..4c5a72001e --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/plankmakehelper/PlankMakePlugin.java @@ -0,0 +1,49 @@ +package net.runelite.client.plugins.plankmakehelper; + +import net.runelite.api.Client; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.ui.overlay.OverlayManager; + +import javax.inject.Inject; + +@PluginDescriptor( + name = "Plank Make Helper", + description = "Highlights planks and plank make spell", + tags = {"overlay", "plankmaking", "lunar", "money", "moneymaking", "gp"} +) + +public class PlankMakePlugin extends Plugin { + + @Inject + private OverlayManager overlayManager; + + @Inject + private Client client; + + @Inject + private PlankMakeOverlay overlay; + + @Override + protected void startUp() { + overlayManager.add(overlay); + } + + @Override + protected void shutDown() { + overlayManager.remove(overlay); + } + + static boolean isLogAndPlankable(int itemID) { + switch (itemID) { + case 6332: //mahogany + case 1521: //oak + case 6333: //teak + case 1511: //plain + return true; + default: + return false; + } + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/prayagainstplayer/PlayerContainer.java b/runelite-client/src/main/java/net/runelite/client/plugins/prayagainstplayer/PlayerContainer.java new file mode 100644 index 0000000000..6c1370ef76 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/prayagainstplayer/PlayerContainer.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2019, gazivodag + * 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.prayagainstplayer; + +import net.runelite.api.Player; + +/** + * Contains a player object + * When they attacked me + * And (in milliseconds) when to expire the overlay around them + */ +public class PlayerContainer { + + private Player player; + private long whenTheyAttackedMe; + private int millisToExpireHighlight; + + public PlayerContainer(Player player, long whenTheyAttackedMe, int millisToExpireHighlight) { + this.player = player; + this.whenTheyAttackedMe = whenTheyAttackedMe; + this.millisToExpireHighlight = millisToExpireHighlight; + } + + + //getters + public Player getPlayer() { + return player; + } + public long getWhenTheyAttackedMe() { + return whenTheyAttackedMe; + } + public int getMillisToExpireHighlight() { return millisToExpireHighlight; }; + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/prayagainstplayer/PrayAgainstPlayerConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/prayagainstplayer/PrayAgainstPlayerConfig.java new file mode 100644 index 0000000000..ce453fd3d9 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/prayagainstplayer/PrayAgainstPlayerConfig.java @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2019, gazivodag + * 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.prayagainstplayer; + +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +import java.awt.*; + +@ConfigGroup("prayagainstplayer") +public interface PrayAgainstPlayerConfig extends Config { + @ConfigItem( + position = 0, + keyName = "attackerPlayerColor", + name = "Attacker color", + description = "This is the color that will be used to highlight attackers." + ) + default Color attackerPlayerColor() { return new Color(0xFF0006); } + + @ConfigItem( + position = 1, + keyName = "potentialPlayerColor", + name = "Potential Attacker color", + description = "This is the color that will be used to highlight potential attackers." + ) + default Color potentialPlayerColor() { return new Color(0xFFFF00); } + + //// + @ConfigItem( + position = 2, + keyName = "attackerTargetTimeout", + name = "Attacker Timeout", + description = "Seconds until attacker is no longer highlighted." + ) + default int attackerTargetTimeout() { return 10; } + + @ConfigItem( + position = 3, + keyName = "potentialTargetTimeout", + name = "Potential Attacker Timeout", + description = "Seconds until potential attacker is no longer highlighted." + ) + default int potentialTargetTimeout() { return 10; } + + @ConfigItem( + position = 4, + keyName = "newSpawnTimeout", + name = "New Player Timeout", + description = "Seconds until logged in/spawned player is no longer highlighted." + ) + default int newSpawnTimeout() { return 5; } + //// + + //// + @ConfigItem( + position = 5, + keyName = "ignoreFriends", + name = "Ignore Friends", + description = "This lets you decide whether you want friends to be highlighted by this plugin." + ) + default boolean ignoreFriends() { return true; } + + @ConfigItem( + position = 6, + keyName = "ignoreClanMates", + name = "Ignore Clan Mates", + description = "This lets you decide whether you want clan mates to be highlighted by this plugin." + ) + default boolean ignoreClanMates() { return true; } + //// + + @ConfigItem( + position = 7, + keyName = "markNewPlayer", + name = "Mark new player as potential attacker", + description = "Marks someone that logged in or teleported as a potential attacker for your safety\nDO NOT RUN THIS IN WORLD 1-2 GRAND EXCHANGE!" + ) + default boolean markNewPlayer() { return false; } + + @ConfigItem( + position = 8, + keyName = "drawTargetPrayAgainst", + name = "Draw what to pray on attacker", + description = "Tells you what to pray from what weapon the attacker is holding" + ) + default boolean drawTargetPrayAgainst() { return true; } + + @ConfigItem( + position = 9, + keyName = "drawPotentialTargetPrayAgainst", + name = "Draw what to pray on potential attacker", + description = "Tells you what to pray from what weapon the potential attacker is holding" + ) + default boolean drawPotentialTargetPrayAgainst() { return true; } + + @ConfigItem( + position = 10, + keyName = "drawTargetPrayAgainstPrayerTab", + name = "Draw what to pray from prayer tab", + description = "Tells you what to pray from what weapon the attacker is holding from the prayer tab" + ) + default boolean drawTargetPrayAgainstPrayerTab() { return false; } + + @ConfigItem( + position = 11, + keyName = "drawTargetsName", + name = "Draw name on attacker", + description = "Configures whether or not the attacker\'s name should be shown" + ) + default boolean drawTargetsName() { return true; } + + @ConfigItem( + position = 12, + keyName = "drawPotentialTargetsName", + name = "Draw name on potential attacker", + description = "Configures whether or not the potential attacker\'s name should be shown" + ) + default boolean drawPotentialTargetsName() { return true; } + + @ConfigItem( + position = 13, + keyName = "drawTargetHighlight", + name = "Draw highlight around attacker", + description = "Configures whether or not the attacker should be highlighted" + ) + default boolean drawTargetHighlight() { return true; } + + @ConfigItem( + position = 14, + keyName = "drawPotentialTargetHighlight", + name = "Draw highlight around potential attacker", + description = "Configures whether or not the potential attacker should be highlighted" + ) + default boolean drawPotentialTargetHighlight() { return true; } + + @ConfigItem( + position = 15, + keyName = "drawTargetTile", + name = "Draw tile under attacker", + description = "Configures whether or not the attacker\'s tile be highlighted" + ) + default boolean drawTargetTile() { return false; } + + @ConfigItem( + position = 16, + keyName = "drawPotentialTargetTile", + name = "Draw tile under potential attacker", + description = "Configures whether or not the potential attacker\'s tile be highlighted" + ) + default boolean drawPotentialTargetTile() { return false; } + + @ConfigItem( + position = 17, + keyName = "drawUnknownWeapons", + name = "Draw unknown weapons", + description = "Configures whether or not the unknown weapons should be shown when a player equips one" + ) + default boolean drawUnknownWeapons() { return false; } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/prayagainstplayer/PrayAgainstPlayerOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/prayagainstplayer/PrayAgainstPlayerOverlay.java new file mode 100644 index 0000000000..e54efd8127 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/prayagainstplayer/PrayAgainstPlayerOverlay.java @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2019, gazivodag + * 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.prayagainstplayer; + +import net.runelite.api.Client; +import net.runelite.api.ItemComposition; +import net.runelite.api.Player; +import net.runelite.api.kit.KitType; +import net.runelite.client.ui.overlay.*; +import net.runelite.client.util.Text; +import net.runelite.api.Point; + +import javax.inject.Inject; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.util.ConcurrentModificationException; + +class PrayAgainstPlayerOverlay extends Overlay { + + private final PrayAgainstPlayerPlugin plugin; + private final PrayAgainstPlayerConfig config; + private final Client client; + + @Inject + private PrayAgainstPlayerOverlay(PrayAgainstPlayerPlugin plugin, PrayAgainstPlayerConfig config, Client client) { + super(plugin); + this.plugin = plugin; + this.config = config; + this.client = client; + + setLayer(OverlayLayer.ABOVE_SCENE); + setPosition(OverlayPosition.DYNAMIC); + setPriority(OverlayPriority.HIGH); + } + + + @Override + public Dimension render(Graphics2D graphics) { + renderPotentialPlayers(graphics); + renderAttackingPlayers(graphics); + return null; + } + + private void renderPotentialPlayers(Graphics2D graphics) { + if (plugin.getPotentialPlayersAttackingMe() == null || !plugin.getPotentialPlayersAttackingMe().isEmpty()) { + try { + for (PlayerContainer container : plugin.getPotentialPlayersAttackingMe()) { + if ((System.currentTimeMillis() > (container.getWhenTheyAttackedMe() + container.getMillisToExpireHighlight())) && (container.getPlayer().getInteracting() != client.getLocalPlayer())) { + plugin.removePlayerFromPotentialContainer(container); + } + if (config.drawPotentialTargetsName()) renderNameAboveHead(graphics, container.getPlayer(), config.potentialPlayerColor()); + if (config.drawPotentialTargetHighlight()) renderHighlightedPlayer(graphics, container.getPlayer(), config.potentialPlayerColor()); + if (config.drawPotentialTargetTile()) renderTileUnderPlayer(graphics, container.getPlayer(), config.potentialPlayerColor()); + if (config.drawPotentialTargetPrayAgainst()) renderPrayAgainstOnPlayer(graphics, container.getPlayer(), config.potentialPlayerColor()); + } + } catch (ConcurrentModificationException e) { + } + } + } + + private void renderAttackingPlayers(Graphics2D graphics) { + if (plugin.getPlayersAttackingMe() == null || !plugin.getPlayersAttackingMe().isEmpty()) { + try { + for (PlayerContainer container : plugin.getPlayersAttackingMe()) { + if ((System.currentTimeMillis() > (container.getWhenTheyAttackedMe() + container.getMillisToExpireHighlight())) && (container.getPlayer().getInteracting() != client.getLocalPlayer())) { + plugin.removePlayerFromAttackerContainer(container); + } + + if (config.drawTargetsName()) renderNameAboveHead(graphics, container.getPlayer(), config.attackerPlayerColor()); + if (config.drawTargetHighlight()) renderHighlightedPlayer(graphics, container.getPlayer(), config.attackerPlayerColor()); + if (config.drawTargetTile()) renderTileUnderPlayer(graphics, container.getPlayer(), config.attackerPlayerColor()); + if (config.drawTargetPrayAgainst()) renderPrayAgainstOnPlayer(graphics, container.getPlayer(), config.attackerPlayerColor()); + } + } catch (ConcurrentModificationException e) { + } + } + } + + private void renderNameAboveHead(Graphics2D graphics, Player player, Color color) { + final String name = Text.sanitize(player.getName()); + final int offset = player.getLogicalHeight() + 40; + Point textLocation = player.getCanvasTextLocation(graphics, name, offset); + if (textLocation != null) { + OverlayUtil.renderTextLocation(graphics, textLocation, name, color); + } + } + + private void renderHighlightedPlayer(Graphics2D graphics, Player player, Color color) { + try { + OverlayUtil.renderPolygon(graphics, player.getConvexHull(), color); + } catch (NullPointerException e) { + } + } + + private void renderTileUnderPlayer(Graphics2D graphics, Player player, Color color) { + Polygon poly = player.getCanvasTilePoly(); + OverlayUtil.renderPolygon(graphics, poly, color); + } + + private void renderPrayAgainstOnPlayer(Graphics2D graphics, Player player, Color color) { + final int offset = (player.getLogicalHeight() / 2) + 75; + BufferedImage icon; + + switch (WeaponType.checkWeaponOnPlayer(client, player)) { + case WEAPON_MELEE: + icon = plugin.getProtectionIcon(WeaponType.WEAPON_MELEE); + break; + case WEAPON_MAGIC: + icon = plugin.getProtectionIcon(WeaponType.WEAPON_MAGIC); + break; + case WEAPON_RANGED: + icon = plugin.getProtectionIcon(WeaponType.WEAPON_RANGED); + break; + default: + icon = null; + break; + } + try { + if (icon != null) { + Point point = player.getCanvasImageLocation(icon, offset); + OverlayUtil.renderImageLocation(graphics, point, icon); + } else { + if (config.drawUnknownWeapons()) { + int itemId = player.getPlayerComposition().getEquipmentId(KitType.WEAPON); + ItemComposition itemComposition = client.getItemDefinition(itemId); + + final String str = itemComposition.getName().toUpperCase(); + Point point = player.getCanvasTextLocation(graphics, str, offset); + OverlayUtil.renderTextLocation(graphics, point, str, color); + } + } + } catch (Exception e) { + } + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/prayagainstplayer/PrayAgainstPlayerOverlayPrayerTab.java b/runelite-client/src/main/java/net/runelite/client/plugins/prayagainstplayer/PrayAgainstPlayerOverlayPrayerTab.java new file mode 100644 index 0000000000..4e505675f6 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/prayagainstplayer/PrayAgainstPlayerOverlayPrayerTab.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2019, gazivodag + * 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.prayagainstplayer; + +import net.runelite.api.Client; +import net.runelite.api.Player; +import net.runelite.api.widgets.Widget; +import net.runelite.api.widgets.WidgetInfo; +import net.runelite.client.ui.overlay.*; + +import javax.inject.Inject; +import java.awt.*; +import java.util.ConcurrentModificationException; + +class PrayAgainstPlayerOverlayPrayerTab extends Overlay { + + private final PrayAgainstPlayerPlugin plugin; + private final PrayAgainstPlayerConfig config; + private final Client client; + + @Inject + private PrayAgainstPlayerOverlayPrayerTab (PrayAgainstPlayerPlugin plugin, PrayAgainstPlayerConfig config, Client client) { + super(plugin); + this.plugin = plugin; + this.config = config; + this.client = client; + + setPosition(OverlayPosition.DETACHED); + setLayer(OverlayLayer.ALWAYS_ON_TOP); + setPriority(OverlayPriority.MED); + } + + + @Override + public Dimension render(Graphics2D graphics) { + if (plugin.getPlayersAttackingMe() == null || !plugin.getPlayersAttackingMe().isEmpty()) { + try { + for (PlayerContainer container : plugin.getPlayersAttackingMe()) { + if (plugin.getPlayersAttackingMe() != null && plugin.getPlayersAttackingMe().size() > 0) { + //no reason to show you what prayers to pray in your prayer tab if multiple people are attacking you + if ((plugin.getPlayersAttackingMe().size() == 1) && (config.drawTargetPrayAgainstPrayerTab())) { + renderPrayerToClick(graphics, container.getPlayer()); + } + } + } + } catch (ConcurrentModificationException e) { + } + } + return null; + } + + private void renderPrayerToClick(Graphics2D graphics, Player player) { + Widget PROTECT_FROM_MAGIC = client.getWidget(WidgetInfo.PRAYER_PROTECT_FROM_MAGIC); + Widget PROTECT_FROM_RANGED = client.getWidget(WidgetInfo.PRAYER_PROTECT_FROM_MISSILES); + Widget PROTECT_FROM_MELEE = client.getWidget(WidgetInfo.PRAYER_PROTECT_FROM_MELEE); + Color color = Color.RED; + if (PROTECT_FROM_MELEE.isHidden()) return; + switch (WeaponType.checkWeaponOnPlayer(client, player)) { + case WEAPON_MAGIC: + OverlayUtil.renderPolygon(graphics, rectangleToPolygon(PROTECT_FROM_MAGIC.getBounds()), color); + break; + case WEAPON_MELEE: + OverlayUtil.renderPolygon(graphics, rectangleToPolygon(PROTECT_FROM_MELEE.getBounds()), color); + break; + case WEAPON_RANGED: + OverlayUtil.renderPolygon(graphics, rectangleToPolygon(PROTECT_FROM_RANGED.getBounds()), color); + break; + default: + break; + } + } + + private static Polygon rectangleToPolygon(Rectangle rect) { + int[] xpoints = {rect.x, rect.x + rect.width, rect.x + rect.width, rect.x}; + int[] ypoints = {rect.y, rect.y, rect.y + rect.height, rect.y + rect.height}; + return new Polygon(xpoints, ypoints, 4); + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/prayagainstplayer/PrayAgainstPlayerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/prayagainstplayer/PrayAgainstPlayerPlugin.java new file mode 100644 index 0000000000..5664621b60 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/prayagainstplayer/PrayAgainstPlayerPlugin.java @@ -0,0 +1,327 @@ +/* + * Copyright (c) 2019, gazivodag + * 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.prayagainstplayer; + +import com.google.inject.Provides; +import net.runelite.api.*; +import net.runelite.api.events.*; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.game.SpriteManager; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.ui.overlay.OverlayManager; +import net.runelite.client.util.ImageUtil; + +import javax.inject.Inject; +import java.awt.*; +import java.awt.image.*; +import java.util.ArrayList; +import java.util.Arrays; + +@PluginDescriptor( + name = "!Pray Against Player", + description = "Use plugin in PvP situations for best results!!", + tags = {"highlight", "pvp", "overlay", "players"} +) + +/** + * I am fully aware that there is plenty of overhead and is a MESS! + * If you'd like to contribute please do! + */ +public class PrayAgainstPlayerPlugin extends Plugin { + + private static final int[] PROTECTION_ICONS = { + SpriteID.PRAYER_PROTECT_FROM_MISSILES, + SpriteID.PRAYER_PROTECT_FROM_MELEE, + SpriteID.PRAYER_PROTECT_FROM_MAGIC + }; + private static final Dimension PROTECTION_ICON_DIMENSION = new Dimension(33, 33); + private static final Color PROTECTION_ICON_OUTLINE_COLOR = new Color(33, 33, 33); + public final BufferedImage[] ProtectionIcons = new BufferedImage[PROTECTION_ICONS.length]; + + private ArrayList potentialPlayersAttackingMe; + private ArrayList playersAttackingMe; + + @Inject + private Client client; + + @Inject + private SpriteManager spriteManager; + + @Inject + private OverlayManager overlayManager; + + @Inject + private PrayAgainstPlayerOverlay overlay; + + @Inject + private PrayAgainstPlayerOverlayPrayerTab overlayPrayerTab; + + @Inject + private PrayAgainstPlayerConfig config; + + @Provides + PrayAgainstPlayerConfig provideConfig(ConfigManager configManager) { + return configManager.getConfig(PrayAgainstPlayerConfig.class); + } + + @Subscribe + public void onGameStateChanged(GameStateChanged gameStateChanged) { + if (gameStateChanged.getGameState() == GameState.LOGGED_IN) { + loadProtectionIcons(); + } + } + + @Override + protected void startUp() { + potentialPlayersAttackingMe = new ArrayList<>(); + playersAttackingMe = new ArrayList<>(); + overlayManager.add(overlay); + overlayManager.add(overlayPrayerTab); + } + + @Override + protected void shutDown() throws Exception { + overlayManager.remove(overlay); + overlayManager.remove(overlayPrayerTab); + } + + @Subscribe + protected void onAnimationChanged(AnimationChanged animationChanged) { + if ((animationChanged.getActor() instanceof Player) && (animationChanged.getActor().getInteracting() instanceof Player) && (animationChanged.getActor().getInteracting() == client.getLocalPlayer())) { + Player sourcePlayer = (Player) animationChanged.getActor(); + + //is the client is a friend/clan and the config is set to ignore friends/clan dont add them to list + if (client.isFriended(sourcePlayer.getName(), true) && config.ignoreFriends()) return; + if (client.isClanMember(sourcePlayer.getName()) && config.ignoreClanMates()) return; + + if ((sourcePlayer.getAnimation() != -1) && (!isBlockAnimation(sourcePlayer.getAnimation()))) { + //if attacker attacks again, reset his timer so overlay doesn't go away + if (findPlayerInAttackerList(sourcePlayer) != null) { + resetPlayerFromAttackerContainerTimer(findPlayerInAttackerList(sourcePlayer)); + } + //if he attacks and he was in the potential attackers list, remove him + if (!potentialPlayersAttackingMe.isEmpty() && potentialPlayersAttackingMe.contains(findPlayerInPotentialList(sourcePlayer))) { + removePlayerFromPotentialContainer(findPlayerInPotentialList(sourcePlayer)); + } + //if he's not in the attackers list, add him + if (findPlayerInAttackerList(sourcePlayer) == null) { + PlayerContainer container = new PlayerContainer(sourcePlayer, System.currentTimeMillis(), (config.attackerTargetTimeout() * 1000)); + playersAttackingMe.add(container); + } + } + } + } + + @Subscribe + protected void onInteractingChanged(InteractingChanged interactingChanged) { + //if someone interacts with you, add them to the potential attackers list + if ((interactingChanged.getSource() instanceof Player) && (interactingChanged.getTarget() instanceof Player)) { + Player sourcePlayer = (Player) interactingChanged.getSource(); + Player targetPlayer = (Player) interactingChanged.getTarget(); + if ((targetPlayer == client.getLocalPlayer()) && (findPlayerInPotentialList(sourcePlayer) == null)) { //we're being interacted with + + //is the client is a friend/clan and the config is set to ignore friends/clan dont add them to list + if (client.isFriended(sourcePlayer.getName(), true) && config.ignoreFriends()) return; + if (client.isClanMember(sourcePlayer.getName()) && config.ignoreClanMates()) return; + + PlayerContainer container = new PlayerContainer(sourcePlayer, System.currentTimeMillis(), (config.potentialTargetTimeout() * 1000)); + potentialPlayersAttackingMe.add(container); + } + } + } + + @Subscribe + protected void onPlayerDespawned(PlayerDespawned playerDespawned) { + PlayerContainer container = findPlayerInAttackerList(playerDespawned.getPlayer()); + PlayerContainer container2 = findPlayerInPotentialList(playerDespawned.getPlayer()); + if (container != null) { + playersAttackingMe.remove(container); + } + if (container2 != null) { + potentialPlayersAttackingMe.remove(container2); + } + } + + @Subscribe + protected void onPlayerSpawned(PlayerSpawned playerSpawned) { + if (config.markNewPlayer()) { + Player p = playerSpawned.getPlayer(); + + if (client.isFriended(p.getName(), true) && config.ignoreFriends()) return; + if (client.isClanMember(p.getName()) && config.ignoreClanMates()) return; + + PlayerContainer container = findPlayerInPotentialList(p); + if (container == null) { + container = new PlayerContainer(p, System.currentTimeMillis(), (config.newSpawnTimeout() * 1000)); + potentialPlayersAttackingMe.add(container); + } + } + } + + PlayerContainer findPlayerInAttackerList(Player player) { + if (playersAttackingMe.isEmpty()) { + return null; + } + for (int i = 0 ; i < playersAttackingMe.size() ; i++) { + PlayerContainer container = playersAttackingMe.get(i); + if (container.getPlayer() == player) { + return container; + } + } + return null; + } + + PlayerContainer findPlayerInPotentialList(Player player) { + if (potentialPlayersAttackingMe.isEmpty()) { + return null; + } + for (int i = 0 ; i < potentialPlayersAttackingMe.size() ; i++) { + PlayerContainer container = potentialPlayersAttackingMe.get(i); + if (container.getPlayer() == player) { + return container; + } + } + return null; + } + + /** + * Resets player timer in case he attacks again, so his highlight doesn't go away so easily + * @param container + */ + public void resetPlayerFromAttackerContainerTimer(PlayerContainer container) { + removePlayerFromAttackerContainer(container); + PlayerContainer newContainer = new PlayerContainer(container.getPlayer(), System.currentTimeMillis(), (config.attackerTargetTimeout() * 1000)); + playersAttackingMe.add(newContainer); + } + + + public void removePlayerFromPotentialContainer(PlayerContainer container) { + if ((potentialPlayersAttackingMe != null) && (!potentialPlayersAttackingMe.isEmpty()) && (potentialPlayersAttackingMe.contains(container))) { + potentialPlayersAttackingMe.remove(container); + } + } + + public void removePlayerFromAttackerContainer(PlayerContainer container) { + if ((playersAttackingMe != null) && (!playersAttackingMe.isEmpty()) && (playersAttackingMe.contains(container))) { + playersAttackingMe.remove(container); + } + } + + private boolean isBlockAnimation(int anim) { + switch (anim) { + case AnimationID.BLOCK_DEFENDER: + case AnimationID.BLOCK_NO_SHIELD: + case AnimationID.BLOCK_SHIELD: + case AnimationID.BLOCK_SWORD: + case AnimationID.BLOCK_UNARMED: + return true; + default: + return false; + } + } + + public ArrayList getPotentialPlayersAttackingMe() { return potentialPlayersAttackingMe; } + public ArrayList getPlayersAttackingMe() { return playersAttackingMe; } + + //All of the methods below are from the Zulrah plugin!!! Credits to it's respective owner + private void loadProtectionIcons() { + final IndexedSprite[] protectionIcons = {}; + final IndexedSprite[] newProtectionIcons = Arrays.copyOf(protectionIcons, PROTECTION_ICONS.length); + int curPosition = 0; + + for (int i = 0; i < PROTECTION_ICONS.length; i++, curPosition++) + { + final int resource = PROTECTION_ICONS[i]; + ProtectionIcons[i] = rgbaToIndexedBufferedImage(ProtectionIconFromSprite(spriteManager.getSprite(resource, 0))); + newProtectionIcons[curPosition] = createIndexedSprite(client, ProtectionIcons[i]); + } + } + + private static IndexedSprite createIndexedSprite(final Client client, final BufferedImage bufferedImage) { + final IndexColorModel indexedCM = (IndexColorModel) bufferedImage.getColorModel(); + + final int width = bufferedImage.getWidth(); + final int height = bufferedImage.getHeight(); + final byte[] pixels = ((DataBufferByte) bufferedImage.getRaster().getDataBuffer()).getData(); + final int[] palette = new int[indexedCM.getMapSize()]; + indexedCM.getRGBs(palette); + + final IndexedSprite newIndexedSprite = client.createIndexedSprite(); + newIndexedSprite.setPixels(pixels); + newIndexedSprite.setPalette(palette); + newIndexedSprite.setWidth(width); + newIndexedSprite.setHeight(height); + newIndexedSprite.setOriginalWidth(width); + newIndexedSprite.setOriginalHeight(height); + newIndexedSprite.setOffsetX(0); + newIndexedSprite.setOffsetY(0); + return newIndexedSprite; + } + + private static BufferedImage rgbaToIndexedBufferedImage(final BufferedImage sourceBufferedImage) { + final BufferedImage indexedImage = new BufferedImage( + sourceBufferedImage.getWidth(), + sourceBufferedImage.getHeight(), + BufferedImage.TYPE_BYTE_INDEXED); + + final ColorModel cm = indexedImage.getColorModel(); + final IndexColorModel icm = (IndexColorModel) cm; + + final int size = icm.getMapSize(); + final byte[] reds = new byte[size]; + final byte[] greens = new byte[size]; + final byte[] blues = new byte[size]; + icm.getReds(reds); + icm.getGreens(greens); + icm.getBlues(blues); + + final WritableRaster raster = indexedImage.getRaster(); + final int pixel = raster.getSample(0, 0, 0); + final IndexColorModel resultIcm = new IndexColorModel(8, size, reds, greens, blues, pixel); + final BufferedImage resultIndexedImage = new BufferedImage(resultIcm, raster, sourceBufferedImage.isAlphaPremultiplied(), null); + resultIndexedImage.getGraphics().drawImage(sourceBufferedImage, 0, 0, null); + return resultIndexedImage; + } + + private static BufferedImage ProtectionIconFromSprite(final BufferedImage freezeSprite) { + final BufferedImage freezeCanvas = ImageUtil.resizeCanvas(freezeSprite, PROTECTION_ICON_DIMENSION.width, PROTECTION_ICON_DIMENSION.height); + return ImageUtil.outlineImage(freezeCanvas, PROTECTION_ICON_OUTLINE_COLOR); + } + + BufferedImage getProtectionIcon(WeaponType weaponType) { + switch (weaponType) { + case WEAPON_RANGED: + return ProtectionIcons[0]; + case WEAPON_MELEE: + return ProtectionIcons[1]; + case WEAPON_MAGIC: + return ProtectionIcons[2]; + } + return null; + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/prayagainstplayer/WeaponType.java b/runelite-client/src/main/java/net/runelite/client/plugins/prayagainstplayer/WeaponType.java new file mode 100644 index 0000000000..1dc00c8311 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/prayagainstplayer/WeaponType.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2019, gazivodag + * 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.prayagainstplayer; + +import net.runelite.api.Client; +import net.runelite.api.ItemComposition; +import net.runelite.api.Player; +import net.runelite.api.kit.KitType; + +enum WeaponType { + + WEAPON_MELEE, + WEAPON_RANGED, + WEAPON_MAGIC, + WEAPON_UNKNOWN; + + /** + * im fully aware this could of been done better!!! + * @param client + * @param attacker + * @return + */ + public static WeaponType checkWeaponOnPlayer (Client client, Player attacker) { + int itemId = attacker.getPlayerComposition().getEquipmentId(KitType.WEAPON); + ItemComposition itemComposition = client.getItemDefinition(itemId); + String weaponNameGivenLowerCase = itemComposition.getName().toLowerCase(); + + if (itemId == -1) return WEAPON_MELEE; + if (weaponNameGivenLowerCase == null || weaponNameGivenLowerCase.toLowerCase().contains("null")) return WEAPON_MELEE; + + for (String meleeWeaponName : meleeWeaponNames) { + if (weaponNameGivenLowerCase.contains(meleeWeaponName) && !weaponNameGivenLowerCase.contains("thrownaxe")) { + return WEAPON_MELEE; + } + } + + for (String rangedWeaponName : rangedWeaponNames) { + if (weaponNameGivenLowerCase.contains(rangedWeaponName)) { + return WEAPON_RANGED; + } + } + + for (String magicWeaponName : magicWeaponNames) { + if (weaponNameGivenLowerCase.contains(magicWeaponName)) { + return WEAPON_MAGIC; + } + } + + return WEAPON_UNKNOWN; + + } + + private static String[] meleeWeaponNames = { + "sword", + "scimitar", + "dagger", + "spear", + "mace", + "axe", + "whip", + "tentacle", + "-ket-", + "-xil-", + "warhammer", + "halberd", + "claws", + "hasta", + "scythe", + "maul", + "anchor", + "sabre", + "excalibur", + "machete", + "dragon hunter lance", + "event rpg", + "silverlight", + "darklight", + "arclight", + "flail", + "granite hammer", + "rapier", + "bulwark" + }; + + private static String[] rangedWeaponNames = { + "bow", + "blowpipe", + "xil-ul", + "knife", + "dart", + "thrownaxe", + "chinchompa", + "ballista" + }; + + private static String[] magicWeaponNames = { + "staff", + "trident", + "wand", + "dawnbringer" + }; + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/profiles/ProfilePanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/profiles/ProfilePanel.java new file mode 100644 index 0000000000..cf27d2b78d --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/profiles/ProfilePanel.java @@ -0,0 +1,123 @@ +/* + * Decompiled with CFR 0.139. + */ +package net.runelite.client.plugins.profiles; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.image.BufferedImage; +import javax.swing.BorderFactory; +import javax.swing.ImageIcon; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import net.runelite.api.Client; +import net.runelite.api.GameState; +import net.runelite.client.ui.ColorScheme; +import net.runelite.client.util.ImageUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +class ProfilePanel +extends JPanel { + private static final Logger log = LoggerFactory.getLogger(ProfilePanel.class); + private static final ImageIcon DELETE_ICON; + private static final ImageIcon DELETE_HOVER_ICON; + private final String loginText; + private String password = null; + + ProfilePanel(final Client client, final String data, final ProfilesConfig config) { + String[] parts = data.split(":"); + this.loginText = parts[1]; + if (parts.length == 3) { + this.password = parts[2]; + } + final ProfilePanel panel = this; + this.setLayout(new BorderLayout()); + this.setBackground(ColorScheme.DARKER_GRAY_COLOR); + JPanel labelWrapper = new JPanel(new BorderLayout()); + labelWrapper.setBackground(ColorScheme.DARKER_GRAY_COLOR); + labelWrapper.setBorder(new CompoundBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, ColorScheme.DARK_GRAY_COLOR), BorderFactory.createLineBorder(ColorScheme.DARKER_GRAY_COLOR))); + JPanel panelActions = new JPanel(new BorderLayout(3, 0)); + panelActions.setBorder(new EmptyBorder(0, 0, 0, 8)); + panelActions.setBackground(ColorScheme.DARKER_GRAY_COLOR); + final JLabel delete = new JLabel(); + delete.setIcon(DELETE_ICON); + delete.setToolTipText("Delete account profile"); + delete.addMouseListener(new MouseAdapter(){ + + @Override + public void mousePressed(MouseEvent e) { + panel.getParent().remove(panel); + ProfilesPanel.removeProfile(data); + } + + @Override + public void mouseEntered(MouseEvent e) { + delete.setIcon(DELETE_HOVER_ICON); + } + + @Override + public void mouseExited(MouseEvent e) { + delete.setIcon(DELETE_ICON); + } + }); + panelActions.add((Component)delete, "East"); + JLabel label = new JLabel(); + label.setText(parts[0]); + label.setBorder(null); + label.setBackground(ColorScheme.DARKER_GRAY_COLOR); + label.setPreferredSize(new Dimension(0, 24)); + label.setForeground(Color.WHITE); + label.setBorder(new EmptyBorder(0, 8, 0, 0)); + labelWrapper.add((Component)label, "Center"); + labelWrapper.add((Component)panelActions, "East"); + label.addMouseListener(new MouseAdapter(){ + + @Override + public void mousePressed(MouseEvent e) { + if (SwingUtilities.isLeftMouseButton(e) && client.getGameState() == GameState.LOGIN_SCREEN) { + client.setUsername(ProfilePanel.this.loginText); + if (config.rememberPassword() && ProfilePanel.this.password != null) { + client.setPassword(ProfilePanel.this.password); + } + } + } + }); + JPanel bottomContainer = new JPanel(new BorderLayout()); + bottomContainer.setBorder(new EmptyBorder(8, 0, 8, 0)); + bottomContainer.setBackground(ColorScheme.DARKER_GRAY_COLOR); + bottomContainer.addMouseListener(new MouseAdapter(){ + + @Override + public void mousePressed(MouseEvent e) { + if (SwingUtilities.isLeftMouseButton(e) && client.getGameState() == GameState.LOGIN_SCREEN) { + client.setUsername(ProfilePanel.this.loginText); + } + } + }); + JLabel login = new JLabel(); + login.setText(config.isStreamerMode() ? "Hidden email" : this.loginText); + login.setBorder(null); + login.setPreferredSize(new Dimension(0, 24)); + login.setForeground(Color.WHITE); + login.setBorder(new EmptyBorder(0, 8, 0, 0)); + bottomContainer.add((Component)login, "Center"); + this.add((Component)labelWrapper, "North"); + this.add((Component)bottomContainer, "Center"); + } + + static { + BufferedImage deleteImg = ImageUtil.getResourceStreamFromClass(ProfilesPlugin.class, "net/runelite/client/plugins/profiles/delete_icon.png"); + DELETE_ICON = new ImageIcon(deleteImg); + DELETE_HOVER_ICON = new ImageIcon(ImageUtil.alphaOffset(deleteImg, -100)); + } + +} + diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/profiles/ProfilesConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/profiles/ProfilesConfig.java new file mode 100644 index 0000000000..2d6b171919 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/profiles/ProfilesConfig.java @@ -0,0 +1,36 @@ +/* + * Decompiled with CFR 0.139. + */ +package net.runelite.client.plugins.profiles; + +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup(value="profiles") +public interface ProfilesConfig +extends Config { + @ConfigItem(keyName="profilesData", name="", description="", hidden=true) + default public String profilesData() { + return ""; + } + + @ConfigItem(keyName="profilesData", name="", description="") + public void profilesData(String var1); + + @ConfigItem(keyName="rememberPassword", name="Remember Password", description="Remembers passwords for accounts") + default public boolean rememberPassword() { + return true; + } + + @ConfigItem(keyName="streamerMode", name="Hide email addresses", description="Hides your account emails") + default public boolean isStreamerMode() { + return false; + } + + @ConfigItem(keyName="switchPanel", name="Auto-open Panel", description="Automatically switch to the account switcher panel on the login screen") + default public boolean switchPanel() { + return true; + } +} + diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/profiles/ProfilesPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/profiles/ProfilesPanel.java new file mode 100644 index 0000000000..f3f6da6b67 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/profiles/ProfilesPanel.java @@ -0,0 +1,248 @@ +/* + * Decompiled with CFR 0.139. + */ +package net.runelite.client.plugins.profiles; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.LayoutManager; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.util.Arrays; +import java.util.function.Consumer; +import javax.inject.Inject; +import javax.swing.JButton; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JTextField; +import javax.swing.border.Border; +import javax.swing.border.EmptyBorder; +import net.runelite.api.Client; +import net.runelite.client.plugins.profiles.ProfilePanel; +import net.runelite.client.plugins.profiles.ProfilesConfig; +import net.runelite.client.ui.ColorScheme; +import net.runelite.client.ui.PluginPanel; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +class ProfilesPanel +extends PluginPanel { + private static final Logger log = LoggerFactory.getLogger(ProfilesPanel.class); + private static final String ACCOUNT_USERNAME = "Account Username"; + private static final String ACCOUNT_LABEL = "Account Label"; + private static final String PASSWORD_LABEL = "Account Password"; + private static final Dimension PREFERRED_SIZE = new Dimension(205, 30); + private static final Dimension MINIMUM_SIZE = new Dimension(0, 30); + private final Client client; + private static ProfilesConfig profilesConfig; + private final JTextField txtAccountLabel = new JTextField("Account Label"); + private final JPasswordField txtAccountLogin = new JPasswordField("Account Username"); + private final JPasswordField txtPasswordLogin = new JPasswordField("Account Password"); + private final JPanel profilesPanel = new JPanel(); + private GridBagConstraints c; + + @Inject + public ProfilesPanel(Client client, final ProfilesConfig config) { + this.client = client; + profilesConfig = config; + this.setBorder(new EmptyBorder(18, 10, 0, 10)); + this.setBackground(ColorScheme.DARK_GRAY_COLOR); + this.setLayout(new GridBagLayout()); + this.c = new GridBagConstraints(); + this.c.fill = 2; + this.c.gridx = 0; + this.c.gridy = 0; + this.c.weightx = 1.0; + this.c.weighty = 0.0; + this.c.insets = new Insets(0, 0, 4, 0); + this.txtAccountLabel.setPreferredSize(PREFERRED_SIZE); + this.txtAccountLabel.setForeground(ColorScheme.MEDIUM_GRAY_COLOR); + this.txtAccountLabel.setBackground(ColorScheme.DARKER_GRAY_COLOR); + this.txtAccountLabel.setMinimumSize(MINIMUM_SIZE); + this.txtAccountLabel.addFocusListener(new FocusListener(){ + + @Override + public void focusGained(FocusEvent e) { + if (ProfilesPanel.this.txtAccountLabel.getText().equals(ProfilesPanel.ACCOUNT_LABEL)) { + ProfilesPanel.this.txtAccountLabel.setText(""); + ProfilesPanel.this.txtAccountLabel.setForeground(ColorScheme.LIGHT_GRAY_COLOR); + } + } + + @Override + public void focusLost(FocusEvent e) { + if (ProfilesPanel.this.txtAccountLabel.getText().isEmpty()) { + ProfilesPanel.this.txtAccountLabel.setForeground(ColorScheme.MEDIUM_GRAY_COLOR); + ProfilesPanel.this.txtAccountLabel.setText(ProfilesPanel.ACCOUNT_LABEL); + } + } + }); + this.add((Component)this.txtAccountLabel, this.c); + ++this.c.gridy; + this.txtAccountLogin.setEchoChar('\u0000'); + this.txtAccountLogin.setPreferredSize(PREFERRED_SIZE); + this.txtAccountLogin.setForeground(ColorScheme.MEDIUM_GRAY_COLOR); + this.txtAccountLogin.setBackground(ColorScheme.DARKER_GRAY_COLOR); + this.txtAccountLogin.setMinimumSize(MINIMUM_SIZE); + this.txtAccountLogin.addFocusListener(new FocusListener(){ + + @Override + public void focusGained(FocusEvent e) { + if (ProfilesPanel.ACCOUNT_USERNAME.equals(String.valueOf(ProfilesPanel.this.txtAccountLogin.getPassword()))) { + ProfilesPanel.this.txtAccountLogin.setText(""); + if (config.isStreamerMode()) { + ProfilesPanel.this.txtAccountLogin.setEchoChar('*'); + } + ProfilesPanel.this.txtAccountLogin.setForeground(ColorScheme.LIGHT_GRAY_COLOR); + } + } + + @Override + public void focusLost(FocusEvent e) { + if (ProfilesPanel.this.txtAccountLogin.getPassword().length == 0) { + ProfilesPanel.this.txtAccountLogin.setForeground(ColorScheme.MEDIUM_GRAY_COLOR); + ProfilesPanel.this.txtAccountLogin.setText(ProfilesPanel.ACCOUNT_USERNAME); + ProfilesPanel.this.txtAccountLogin.setEchoChar('\u0000'); + } + } + }); + this.add((Component)this.txtAccountLogin, this.c); + ++this.c.gridy; + this.txtPasswordLogin.setEchoChar('\u0000'); + this.txtPasswordLogin.setPreferredSize(PREFERRED_SIZE); + this.txtPasswordLogin.setForeground(ColorScheme.MEDIUM_GRAY_COLOR); + this.txtPasswordLogin.setBackground(ColorScheme.DARKER_GRAY_COLOR); + this.txtPasswordLogin.setToolTipText("Account password"); + this.txtPasswordLogin.setMinimumSize(MINIMUM_SIZE); + this.txtPasswordLogin.addFocusListener(new FocusListener(){ + + @Override + public void focusGained(FocusEvent e) { + if (ProfilesPanel.PASSWORD_LABEL.equals(String.valueOf(ProfilesPanel.this.txtPasswordLogin.getPassword()))) { + ProfilesPanel.this.txtPasswordLogin.setText(""); + ProfilesPanel.this.txtPasswordLogin.setEchoChar('*'); + ProfilesPanel.this.txtPasswordLogin.setForeground(ColorScheme.LIGHT_GRAY_COLOR); + } + } + + @Override + public void focusLost(FocusEvent e) { + if (ProfilesPanel.this.txtPasswordLogin.getPassword().length == 0) { + ProfilesPanel.this.txtPasswordLogin.setForeground(ColorScheme.MEDIUM_GRAY_COLOR); + ProfilesPanel.this.txtPasswordLogin.setText(ProfilesPanel.PASSWORD_LABEL); + ProfilesPanel.this.txtPasswordLogin.setEchoChar('\u0000'); + } + } + }); + if (config.rememberPassword()) { + this.add((Component)this.txtPasswordLogin, this.c); + ++this.c.gridy; + } + this.c.insets = new Insets(0, 0, 15, 0); + final JButton btnAddAccount = new JButton("Add Account"); + btnAddAccount.setPreferredSize(PREFERRED_SIZE); + btnAddAccount.setBackground(ColorScheme.DARKER_GRAY_COLOR); + btnAddAccount.setMinimumSize(MINIMUM_SIZE); + btnAddAccount.addActionListener(e -> { + String labelText = String.valueOf(this.txtAccountLabel.getText()); + String loginText = String.valueOf(this.txtAccountLogin.getPassword()); + String passwordText = String.valueOf(this.txtPasswordLogin.getPassword()); + if (labelText.equals(ACCOUNT_LABEL) || loginText.equals(ACCOUNT_USERNAME)) { + return; + } + String data = config.rememberPassword() && this.txtPasswordLogin.getPassword() != null ? labelText + ":" + loginText + ":" + passwordText : labelText + ":" + loginText; + log.info(data); + this.addAccount(data); + ProfilesPanel.addProfile(data); + this.txtAccountLabel.setText(ACCOUNT_LABEL); + this.txtAccountLabel.setForeground(ColorScheme.MEDIUM_GRAY_COLOR); + this.txtAccountLogin.setText(ACCOUNT_USERNAME); + this.txtAccountLogin.setEchoChar('\u0000'); + this.txtAccountLogin.setForeground(ColorScheme.MEDIUM_GRAY_COLOR); + this.txtPasswordLogin.setText(PASSWORD_LABEL); + this.txtPasswordLogin.setEchoChar('\u0000'); + this.txtPasswordLogin.setForeground(ColorScheme.MEDIUM_GRAY_COLOR); + }); + this.txtAccountLogin.addKeyListener(new KeyAdapter(){ + + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == 10) { + btnAddAccount.doClick(); + btnAddAccount.requestFocus(); + } + } + }); + this.txtAccountLogin.addMouseListener(new MouseListener(){ + + @Override + public void mouseClicked(MouseEvent e) { + } + + @Override + public void mousePressed(MouseEvent e) { + } + + @Override + public void mouseReleased(MouseEvent e) { + } + + @Override + public void mouseEntered(MouseEvent e) { + } + + @Override + public void mouseExited(MouseEvent e) { + } + }); + this.add((Component)btnAddAccount, this.c); + ++this.c.gridy; + this.profilesPanel.setLayout(new GridBagLayout()); + this.add((Component)this.profilesPanel, this.c); + this.c.gridy = 0; + this.c.insets = new Insets(0, 0, 5, 0); + this.addAccounts(config.profilesData()); + } + + void redrawProfiles() { + this.profilesPanel.removeAll(); + this.c.gridy = 0; + this.addAccounts(profilesConfig.profilesData()); + } + + private void addAccount(String data) { + ProfilePanel profile = new ProfilePanel(this.client, data, profilesConfig); + ++this.c.gridy; + this.profilesPanel.add((Component)profile, this.c); + this.revalidate(); + this.repaint(); + } + + void addAccounts(String data) { + if (!(data = data.trim()).contains(":")) { + return; + } + Arrays.stream(data.split("\\n")).forEach(this::addAccount); + } + + static void addProfile(String data) { + profilesConfig.profilesData(profilesConfig.profilesData() + data + "\n"); + } + + static void removeProfile(String data) { + profilesConfig.profilesData(profilesConfig.profilesData().replaceAll(data + "\\n", "")); + } + +} + diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/profiles/ProfilesPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/profiles/ProfilesPlugin.java new file mode 100644 index 0000000000..4128adba44 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/profiles/ProfilesPlugin.java @@ -0,0 +1,128 @@ +/* + * Decompiled with CFR 0.139. + */ +package net.runelite.client.plugins.profiles; + +import com.google.inject.Provides; +import java.awt.image.BufferedImage; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.NoSuchAlgorithmException; +import java.util.logging.Logger; +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.SecretKeySpec; +import javax.inject.Inject; +import net.runelite.api.Client; +import net.runelite.api.events.ConfigChanged; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.ui.ClientToolbar; +import net.runelite.client.ui.NavigationButton; +import net.runelite.client.util.ImageUtil; + +@PluginDescriptor(name="Account Switcher", description="Allow for a allows you to easily switch between multiple OSRS Accounts", tags={"profile", "account", "login", "log in"}) +public class ProfilesPlugin +extends Plugin { + @Inject + private ClientToolbar clientToolbar; + @Inject + private Client client; + @Inject + private ProfilesConfig config; + private ProfilesPanel panel; + private NavigationButton navButton; + String text = "Hello World"; + private static String key = "Bar12345Bar12345"; + private static Key aesKey = new SecretKeySpec(key.getBytes(), "AES"); + + @Provides + ProfilesConfig getConfig(ConfigManager configManager) { + return configManager.getConfig(ProfilesConfig.class); + } + + @Override + protected void startUp() throws Exception { + this.panel = this.injector.getInstance(ProfilesPanel.class); + BufferedImage icon = ImageUtil.getResourceStreamFromClass(this.getClass(), "/net/runelite/client/plugins/profiles/profiles_icon.png"); + this.navButton = NavigationButton.builder().tooltip("Profiles").icon(icon).priority(8).panel(this.panel).build(); + this.clientToolbar.addNavigation(this.navButton); + } + + @Override + protected void shutDown() { + this.clientToolbar.removeNavigation(this.navButton); + } + + @Subscribe + private void onConfigChanged(ConfigChanged event) throws Exception { + if (event.getGroup().equals("profiles") && event.getKey().equals("rememberPassword")) { + this.panel = this.injector.getInstance(ProfilesPanel.class); + this.shutDown(); + this.startUp(); + } + } + + public static String decryptText(String text) { + byte[] bb = new byte[text.length()]; + for (int i = 0; i < text.length(); ++i) { + bb[i] = (byte)text.charAt(i); + } + Cipher cipher = null; + try { + cipher = Cipher.getInstance("AES"); + } + catch (NoSuchAlgorithmException | NoSuchPaddingException e) { + e.printStackTrace(); + } + try { + cipher.init(2, aesKey); + } + catch (InvalidKeyException e) { + e.printStackTrace(); + } + try { + Logger.getLogger("EncryptionLogger").info("Decrypted " + text + " to " + new String(cipher.doFinal(bb))); + return new String(cipher.doFinal(bb)); + } + catch (BadPaddingException | IllegalBlockSizeException e) { + e.printStackTrace(); + return ""; + } + } + + public static String encryptText(String text) { + try { + Cipher cipher = Cipher.getInstance("AES"); + cipher.init(1, aesKey); + byte[] encrypted = cipher.doFinal(text.getBytes()); + StringBuilder sb = new StringBuilder(); + for (byte b : encrypted) { + sb.append((char)b); + } + Logger.getLogger("EncryptionLogger").info("Encrypted " + text + " to " + sb.toString()); + return sb.toString(); + } + catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + catch (NoSuchPaddingException e) { + e.printStackTrace(); + } + catch (BadPaddingException e) { + e.printStackTrace(); + } + catch (IllegalBlockSizeException e) { + e.printStackTrace(); + } + catch (InvalidKeyException e) { + e.printStackTrace(); + } + return ""; + } +} + diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/pyramidplunder/Obstacles.java b/runelite-client/src/main/java/net/runelite/client/plugins/pyramidplunder/Obstacles.java new file mode 100644 index 0000000000..fff21802f0 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/pyramidplunder/Obstacles.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018, Steffen Hauge + * 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.pyramidplunder; + +import com.google.common.collect.ImmutableSet; +import java.util.Set; +import static net.runelite.api.ObjectID.SPEARTRAP_21280; + +public class Obstacles +{ + static final Set WALL_OBSTACLE_IDS = ImmutableSet.of( + 26618, 26619, 26620, 26621 + ); + + static final Set TRAP_OBSTACLE_IDS = ImmutableSet.of( + SPEARTRAP_21280 + ); +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/pyramidplunder/PyramidPlunderConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/pyramidplunder/PyramidPlunderConfig.java new file mode 100644 index 0000000000..4b3038762c --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/pyramidplunder/PyramidPlunderConfig.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2018, Steffen Hauge + * 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.pyramidplunder; + +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("pyramidplunder") +public interface PyramidPlunderConfig extends Config +{ + @ConfigItem( + position = 1, + keyName = "highlightDoors", + name = "Highlights doors", + description = "Highlights the four doors in each room" + ) + default boolean highlightDoors() + { + return true; + } + + @ConfigItem( + position = 2, + keyName = "highlightSpearTrap", + name = "Highlights spear traps", + description = "Highlights the spear traps in each room" + ) + default boolean highlightSpearTrap() + { + return false; + } + + @ConfigItem( + position = 3, + keyName = "showTimer", + name = "Display numerical timer", + description = "Displays a numerical timer instead of the default timer" + ) + default boolean showTimer() + { + return true; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/pyramidplunder/PyramidPlunderOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/pyramidplunder/PyramidPlunderOverlay.java new file mode 100644 index 0000000000..fd61a03229 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/pyramidplunder/PyramidPlunderOverlay.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2018, Steffen Hauge + * 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.pyramidplunder; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.geom.Area; +import javax.inject.Inject; +import net.runelite.api.Client; +import net.runelite.api.ObjectComposition; +import static net.runelite.api.ObjectID.SPEARTRAP_21280; +import static net.runelite.api.ObjectID.TOMB_DOOR_20948; +import static net.runelite.api.ObjectID.TOMB_DOOR_20949; +import net.runelite.api.Point; +import net.runelite.api.coords.LocalPoint; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayLayer; +import net.runelite.client.ui.overlay.OverlayPosition; + +public class PyramidPlunderOverlay extends Overlay +{ + private static final int MAX_DISTANCE = 2400; + private static final Color COLOR_DOOR = Color.GREEN; + private static final Color COLOR_SPEAR_TRAP = Color.ORANGE; + + private final Client client; + private final PyramidPlunderPlugin plugin; + private final PyramidPlunderConfig config; + + @Inject + private PyramidPlunderOverlay(Client client, PyramidPlunderPlugin plugin, PyramidPlunderConfig config) + { + this.client = client; + this.plugin = plugin; + this.config = config; + setPosition(OverlayPosition.DYNAMIC); + setLayer(OverlayLayer.ABOVE_SCENE); + } + @Override + public Dimension render(Graphics2D graphics) + { + if (!plugin.isInGame()) + { + return null; + } + + LocalPoint playerLocation = client.getLocalPlayer().getLocalLocation(); + Point mousePosition = client.getMouseCanvasPosition(); + + plugin.getObstacles().forEach((object, tile) -> + { + if (Obstacles.WALL_OBSTACLE_IDS.contains(object.getId()) && !config.highlightDoors() || + Obstacles.TRAP_OBSTACLE_IDS.contains(object.getId()) && !config.highlightSpearTrap()) + { + return; + } + + if (tile.getPlane() == client.getPlane() && + object.getLocalLocation().distanceTo(playerLocation) < MAX_DISTANCE) + { + int objectID = object.getId(); + if (Obstacles.WALL_OBSTACLE_IDS.contains(object.getId())) + { + //Impostor + ObjectComposition comp = client.getObjectDefinition(objectID); + ObjectComposition impostor = comp.getImpostor(); + + if (impostor == null) + { + return; + } + objectID = impostor.getId(); + } + + Area objectClickbox = object.getClickbox(); + if (objectClickbox != null) + { + Color configColor = Color.GREEN; + switch (objectID) + { + case SPEARTRAP_21280: + configColor = COLOR_SPEAR_TRAP; + break; + case TOMB_DOOR_20948: + case TOMB_DOOR_20949: + configColor = COLOR_DOOR; + break; + } + + if (objectClickbox.contains(mousePosition.getX(), mousePosition.getY())) + { + graphics.setColor(configColor.darker()); + } + else + { + graphics.setColor(configColor); + } + + graphics.draw(objectClickbox); + graphics.setColor(new Color(configColor.getRed(), configColor.getGreen(), configColor.getBlue(), 50)); + graphics.fill(objectClickbox); + } + } + }); + + return null; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/pyramidplunder/PyramidPlunderPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/pyramidplunder/PyramidPlunderPlugin.java new file mode 100644 index 0000000000..64a009f2cb --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/pyramidplunder/PyramidPlunderPlugin.java @@ -0,0 +1,271 @@ +/* + * Copyright (c) 2018, Steffen Hauge + * 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.pyramidplunder; + +import com.google.common.eventbus.Subscribe; +import com.google.inject.Provides; +import java.time.temporal.ChronoUnit; +import java.util.HashMap; +import java.util.Map; +import javax.inject.Inject; +import lombok.Getter; +import net.runelite.api.Client; +import static net.runelite.api.ItemID.PHARAOHS_SCEPTRE; +import net.runelite.api.Player; +import net.runelite.api.Tile; +import net.runelite.api.TileObject; +import net.runelite.api.Varbits; +import net.runelite.api.coords.WorldPoint; +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.VarbitChanged; +import net.runelite.api.events.WallObjectChanged; +import net.runelite.api.events.WallObjectDespawned; +import net.runelite.api.events.WallObjectSpawned; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.game.ItemManager; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.ui.overlay.OverlayManager; +import net.runelite.client.ui.overlay.infobox.InfoBoxManager; + +@PluginDescriptor( + name = "PyramidPlunder", + description = "Highlights doors and spear traps in pyramid plunder and adds a numerical timer", + tags = {"pyramidplunder", "pyramid", "plunder", "overlay", "skilling", "thieving"}, + enabledByDefault = false +) + +public class PyramidPlunderPlugin extends Plugin +{ + private static final int PYRAMIND_PLUNDER_REGION_ID = 7749; + private static final int PYRAMIND_PLUNDER_TIMER_MAX = 500; + private static final double GAMETICK_SECOND = 0.6; + + @Getter + private final Map obstacles = new HashMap<>(); + + @Inject + private Client client; + + @Inject + private PyramidPlunderConfig config; + + @Inject + private InfoBoxManager infoBoxManager; + + @Inject + private ItemManager itemManager; + + @Inject + private OverlayManager overlayManager; + + @Inject + private PyramidPlunderOverlay pyramidPlunderOverlay; + + @Getter + private boolean isInGame; + + private int pyramidTimer = 0; + + @Provides + PyramidPlunderConfig getConfig(ConfigManager configManager) + { + return configManager.getConfig(PyramidPlunderConfig.class); + } + + @Override + protected void startUp() throws Exception + { + overlayManager.add(pyramidPlunderOverlay); + } + + @Override + protected void shutDown() throws Exception + { + overlayManager.remove(pyramidPlunderOverlay); + obstacles.clear(); + reset(); + } + + @Subscribe + public void onConfigChanged(ConfigChanged event) + { + if (!config.showTimer()) + { + removeTimer(); + } + + if (config.showTimer() && isInGame) + { + int remainingTime = PYRAMIND_PLUNDER_TIMER_MAX - pyramidTimer; + + if (remainingTime >= 2) + { + double timeInSeconds = remainingTime * GAMETICK_SECOND; + showTimer((int)timeInSeconds, ChronoUnit.SECONDS); + } + } + } + + private void removeTimer() + { + infoBoxManager.removeIf(infoBox -> infoBox instanceof PyramidPlunderTimer); + } + + private void showTimer() + { + showTimer(5, ChronoUnit.MINUTES); + } + + private void showTimer(int period, ChronoUnit chronoUnit) + { + removeTimer(); + infoBoxManager.addInfoBox(new PyramidPlunderTimer(this, itemManager.getImage(PHARAOHS_SCEPTRE), period, chronoUnit)); + } + + @Subscribe + public void onGameStateChange(GameStateChanged event) + { + switch (event.getGameState()) + { + case HOPPING: + case LOGIN_SCREEN: + reset(); + break; + case LOADING: + obstacles.clear(); + case LOGGED_IN: + if (!isInRegion()) + { + reset(); + } + break; + } + } + + private boolean isInRegion() + { + Player local = client.getLocalPlayer(); + if (local == null) + { + return false; + } + + WorldPoint location = local.getWorldLocation(); + if (location.getRegionID() != PYRAMIND_PLUNDER_REGION_ID) + { + return false; + } + + return true; + } + + @Subscribe + public void onVarbitChanged(VarbitChanged event) + { + int lastValue = pyramidTimer; + pyramidTimer = client.getVar(Varbits.PYRAMID_PLUNDER_TIMER); + + if (lastValue == pyramidTimer) + { + return; + } + + if (pyramidTimer == 0) + { + reset(); + } + if (pyramidTimer == 1) + { + isInGame = true; + if (config.showTimer()) + { + showTimer(); + } + } + } + + private void reset() + { + isInGame = false; + removeTimer(); + } + + @Subscribe + public void onGameObjectSpawned(GameObjectSpawned event) + { + onTileObject(event.getTile(), null, event.getGameObject()); + } + + @Subscribe + public void onGameObjectChanged(GameObjectChanged event) + { + onTileObject(event.getTile(), event.getPrevious(), event.getGameObject()); + } + + @Subscribe + public void onGameObjectDeSpawned(GameObjectDespawned event) + { + onTileObject(event.getTile(), event.getGameObject(), null); + } + + @Subscribe + public void onWallObjectSpawned(WallObjectSpawned event) + { + onTileObject(event.getTile(), null, event.getWallObject()); + } + + @Subscribe + public void onWallObjectChanged(WallObjectChanged event) + { + onTileObject(event.getTile(), event.getPrevious(), event.getWallObject()); + } + + @Subscribe + public void onWallObjectDeSpawned(WallObjectDespawned event) + { + onTileObject(event.getTile(), event.getWallObject(), null); + } + + private void onTileObject(Tile tile, TileObject oldObject, TileObject newObject) + { + obstacles.remove(oldObject); + + if (newObject == null) + { + return; + } + + if (Obstacles.WALL_OBSTACLE_IDS.contains(newObject.getId()) || + Obstacles.TRAP_OBSTACLE_IDS.contains(newObject.getId())) + { + obstacles.put(newObject, tile); + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/pyramidplunder/PyramidPlunderTimer.java b/runelite-client/src/main/java/net/runelite/client/plugins/pyramidplunder/PyramidPlunderTimer.java new file mode 100644 index 0000000000..a9f73cf8c3 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/pyramidplunder/PyramidPlunderTimer.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2018, Steffen Hauge + * 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.pyramidplunder; + +import java.awt.image.BufferedImage; +import java.time.temporal.ChronoUnit; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.ui.overlay.infobox.Timer; + +public class PyramidPlunderTimer extends Timer +{ + PyramidPlunderTimer(Plugin plugin, BufferedImage image, int period, ChronoUnit chronoUnit) + { + super(period, chronoUnit, image, plugin); + setTooltip("Time left until minigame ends"); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ui/ScreenMarkerPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ui/ScreenMarkerPanel.java index d225410209..a0c1214a0d 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ui/ScreenMarkerPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ui/ScreenMarkerPanel.java @@ -139,7 +139,7 @@ class ScreenMarkerPanel extends JPanel INVISIBLE_ICON = new ImageIcon(invisibleImg); INVISIBLE_HOVER_ICON = new ImageIcon(ImageUtil.alphaOffset(invisibleImg, -100)); - final BufferedImage deleteImg = ImageUtil.getResourceStreamFromClass(ScreenMarkerPlugin.class, "delete_icon.png"); + final BufferedImage deleteImg = ImageUtil.getResourceStreamFromClass(ScreenMarkerPlugin.class, "net/runelite/client/plugins/profiles/delete_icon.png"); DELETE_ICON = new ImageIcon(deleteImg); DELETE_HOVER_ICON = new ImageIcon(ImageUtil.alphaOffset(deleteImg, -100)); } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/shayzieninfirmary/ShayzienInfirmaryOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/shayzieninfirmary/ShayzienInfirmaryOverlay.java new file mode 100644 index 0000000000..726702f8eb --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/shayzieninfirmary/ShayzienInfirmaryOverlay.java @@ -0,0 +1,100 @@ +/* + * 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.shayzieninfirmary; + +import java.awt.AlphaComposite; +import java.awt.Color; +import java.awt.Composite; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.Polygon; +import java.awt.image.BufferedImage; +import javax.inject.Inject; +import net.runelite.api.Client; +import net.runelite.api.ItemID; +import net.runelite.api.NPC; +import net.runelite.api.Point; +import net.runelite.client.game.ItemManager; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.OverlayUtil; + +public class ShayzienInfirmaryOverlay extends Overlay +{ + private final ShayzienInfirmaryPlugin plugin; + private final Client client; + + private BufferedImage medPackImage; + + @Inject + public ShayzienInfirmaryOverlay(ShayzienInfirmaryPlugin plugin, Client client, ItemManager itemManager) + { + setPosition(OverlayPosition.DYNAMIC); + this.plugin = plugin; + this.client = client; + + medPackImage = itemManager.getImage(ItemID.SHAYZIEN_MEDPACK); + } + + @Override + public Dimension render(Graphics2D graphics) + { + if (!plugin.isAtInfirmary()) + { + return null; + } + + for (NPC npc : plugin.getUnhealedSoldiers()) + { + + Polygon tilePoly = npc.getCanvasTilePoly(); + + if (tilePoly == null) + { + continue; + } + + OverlayUtil.renderPolygon(graphics, npc.getCanvasTilePoly(), Color.ORANGE); + + Point imageLocation = npc.getCanvasImageLocation(medPackImage, 25); + + if (imageLocation == null) + { + continue; + } + + Composite originalComposite = graphics.getComposite(); + Composite translucentComposite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.8f); + + graphics.setComposite(translucentComposite); + + OverlayUtil.renderImageLocation(graphics, imageLocation, medPackImage); + + graphics.setComposite(originalComposite); + } + + return null; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/shayzieninfirmary/ShayzienInfirmaryPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/shayzieninfirmary/ShayzienInfirmaryPlugin.java new file mode 100644 index 0000000000..f4ff6078f1 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/shayzieninfirmary/ShayzienInfirmaryPlugin.java @@ -0,0 +1,121 @@ +/* + * 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.shayzieninfirmary; + +import java.util.ArrayList; +import java.util.List; +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.events.GameTick; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.ui.overlay.OverlayManager; + +@Slf4j +@PluginDescriptor( + name = "Shayzien Infirmary", + description = "Shows the status of wounded soldiers", + tags = {"shayzien", "infirmary", "soldiers"} +) +public class ShayzienInfirmaryPlugin extends Plugin +{ + @Getter(AccessLevel.PACKAGE) + private List unhealedSoldiers = new ArrayList(); + + @Inject + private OverlayManager overlayManager; + + @Inject + private Client client; + + @Inject + private ShayzienInfirmaryOverlay overlay; + + @Override + protected void startUp() throws Exception + { + loadPlugin(); + } + + @Override + protected void shutDown() throws Exception + { + unloadPlugin(); + } + + private void loadPlugin() + { + overlayManager.add(overlay); + } + + private void unloadPlugin() + { + overlayManager.remove(overlay); + } + + @Subscribe + public void onGameTick(GameTick event) + { + if(!isAtInfirmary()) + { + return; + } + + unhealedSoldiers.clear(); + + for (NPC npc : client.getNpcs()) + { + if (isUnhealedSoldierId(npc.getId())) + { + unhealedSoldiers.add(npc); + } + } + } + + public boolean isSoldierId(int npcId) + { + return (npcId >= 6826 && npcId <= 6857); + } + + public boolean isUnhealedSoldierId(int npcId) + { + return (isSoldierId(npcId) && npcId % 2 == 0); + } + + public boolean isHealedSoldierId(int npcId) + { + return (isSoldierId(npcId) && npcId % 2 == 1); + } + + public boolean isAtInfirmary() + { + return client.getLocalPlayer().getWorldLocation().getRegionID() == 6200; + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/shiftwalker/ShiftWalkerConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/shiftwalker/ShiftWalkerConfig.java new file mode 100644 index 0000000000..dfd21986ba --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/shiftwalker/ShiftWalkerConfig.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2018, Plinko60 + * 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.shiftwalker; + +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("shiftwalkhere") +public interface ShiftWalkerConfig extends Config +{ + + @ConfigItem( + keyName = "shiftWalkEverything", + name = "Walk Under Everything", + description = "Enable this option when you do not want to interact with anything while Shift is pressed. " + + "If Walk Here is an option it will be the action taken." + ) + default boolean shiftWalkEverything() + { + return true; + } + + @ConfigItem( + keyName = "shiftWalkBoxTraps", + name = "Walk Under Box Traps", + description = "Press \"Shift\" to be able to walk under instead of picking up a Box Trap." + ) + default boolean shiftWalkBoxTraps() + { + return true; + } + + @ConfigItem( + keyName = "shiftWalkAttackOption", + name = "Walk Under Attack Options", + description = "Press \"Shift\" to be able to walk instead of attacking. Make sure Left Click Attack is on." + ) + default boolean shiftWalkAttackOption() + { + return true; + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/shiftwalker/ShiftWalkerGroups.java b/runelite-client/src/main/java/net/runelite/client/plugins/shiftwalker/ShiftWalkerGroups.java new file mode 100644 index 0000000000..aff9c0efdb --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/shiftwalker/ShiftWalkerGroups.java @@ -0,0 +1,32 @@ +package net.runelite.client.plugins.shiftwalker; + +import java.util.HashSet; + +public final class ShiftWalkerGroups +{ + //Specific Targets to limit the walking to + private static final String BOX_TRAP = "BOX TRAP"; + private static final String BOX_TRAP_SHAKING = "SHAKING BOX"; + + //Specific menu options to replace + private static final String BOX_TRAP_DISMANTLE = "DISMANTLE"; + private static final String BOX_TRAP_CHECK = "CHECK"; + + private static final String ATTACK_OPTIONS_ATTACK = "ATTACK"; + + public static final HashSet BOX_TRAP_TARGETS = new HashSet<>(); + public static final HashSet BOX_TRAP_KEYWORDS = new HashSet<>(); + public static final HashSet ATTACK_OPTIONS_KEYWORDS = new HashSet<>(); + + static + { + BOX_TRAP_TARGETS.add(BOX_TRAP); + BOX_TRAP_TARGETS.add(BOX_TRAP_SHAKING); + + BOX_TRAP_KEYWORDS.add(BOX_TRAP_DISMANTLE); + BOX_TRAP_KEYWORDS.add(BOX_TRAP_CHECK); + + ATTACK_OPTIONS_KEYWORDS.add(ATTACK_OPTIONS_ATTACK); + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/shiftwalker/ShiftWalkerInputListener.java b/runelite-client/src/main/java/net/runelite/client/plugins/shiftwalker/ShiftWalkerInputListener.java new file mode 100644 index 0000000000..677f30357d --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/shiftwalker/ShiftWalkerInputListener.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2018, Plinko60 + * 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.shiftwalker; + +import net.runelite.client.input.KeyListener; + +import javax.inject.Inject; +import java.awt.event.KeyEvent; + +public class ShiftWalkerInputListener implements KeyListener +{ + + @Inject + private ShiftWalkerPlugin plugin; + + @Override + public void keyTyped(KeyEvent event) + { + + } + + @Override + public void keyPressed(KeyEvent event) + { + if (event.getKeyCode() == KeyEvent.VK_SHIFT) + { + plugin.setHotKeyPressed(true); + } + } + + @Override + public void keyReleased(KeyEvent event) + { + if (event.getKeyCode() == KeyEvent.VK_SHIFT) + { + plugin.setHotKeyPressed(false); + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/shiftwalker/ShiftWalkerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/shiftwalker/ShiftWalkerPlugin.java new file mode 100644 index 0000000000..aef2adb45e --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/shiftwalker/ShiftWalkerPlugin.java @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2018, Plinko60 + * 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.shiftwalker; + +import com.google.inject.Provides; +import lombok.Setter; +import net.runelite.api.*; +import net.runelite.api.events.*; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.input.KeyManager; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.util.Text; + +import javax.inject.Inject; + +/** + * Shift Walker Plugin. Credit to MenuEntrySwapperPlugin for code some code structure used here. + */ +@PluginDescriptor( + name = "!Shift To Walk Here", + description = "Use Shift to toggle the Walk Here menu option. While pressed you will Walk rather than interact with objects.", + tags = {"npcs", "items", "objects"}, + enabledByDefault = false +) +public class ShiftWalkerPlugin extends Plugin +{ + + private static final String WALK_HERE = "WALK HERE"; + private static final String CANCEL = "CANCEL"; + + @Inject + private Client client; + + @Inject + private ShiftWalkerConfig config; + + @Inject + private ShiftWalkerInputListener inputListener; + + @Inject + private ConfigManager configManager; + + @Inject + private KeyManager keyManager; + + @Setter + private boolean hotKeyPressed = false; + + @Provides + ShiftWalkerConfig provideConfig(ConfigManager configManager) + { + return configManager.getConfig(ShiftWalkerConfig.class); + } + + @Override + public void startUp() + { + keyManager.registerKeyListener(inputListener); + } + + @Override + public void shutDown() + { + keyManager.unregisterKeyListener(inputListener); + } + + @Subscribe + public void onFocusChanged(FocusChanged event) + { + if (!event.isFocused()) + { + hotKeyPressed = false; + } + } + + /** + * Event when a new menu entry was added. + * @param event {@link MenuEntryAdded}. + */ + @Subscribe + public void onMenuEntryAdded(MenuEntryAdded event) + { + if (client.getGameState() != GameState.LOGGED_IN || !hotKeyPressed) + { + return; + } + + final String pOptionToReplace = Text.removeTags(event.getOption()).toUpperCase(); + + //If the option is already to walk there, or cancel we don't need to swap it with anything + if (pOptionToReplace.equals(CANCEL) || pOptionToReplace.equals(WALK_HERE)) + { + return; + } + + String target = Text.removeTags(event.getTarget().toUpperCase()); + + if (config.shiftWalkEverything()) + { + //swap(pOptionToReplace); //Swap everything with walk here + stripEntries(); + } + else if (config.shiftWalkBoxTraps() && ShiftWalkerGroups.BOX_TRAP_TARGETS.contains(target) + && ShiftWalkerGroups.BOX_TRAP_KEYWORDS.contains(pOptionToReplace)) + { + //swap(pOptionToReplace); //Swap only on box traps + stripEntries(); + } + else if (config.shiftWalkAttackOption() && ShiftWalkerGroups.ATTACK_OPTIONS_KEYWORDS.contains(pOptionToReplace)) + { + //swap(pOptionToReplace); //Swap on everything that has an attack keyword as the first option + stripEntries(); + } + } + + /** + * Strip everything except "Walk here" + * Other way was unconventional because if there was multiple targets in the menu entry it wouldn't swap correctly + */ + private void stripEntries() { + MenuEntry walkkHereEntry = null; + + for (MenuEntry entry : client.getMenuEntries()) { + switch (entry.getOption()) { + case "Walk here": + walkkHereEntry = entry; + break; + } + } + if (walkkHereEntry != null) { + MenuEntry[] newEntries = new MenuEntry[1]; + newEntries[0] = walkkHereEntry; + client.setMenuEntries(newEntries); + } + } + + /** + * Swaps menu entries if the entries could be found. This places Walk Here where the top level menu option was. + * @param pOptionToReplace The String containing the Menu Option that needs to be replaced. IE: "Attack", "Chop Down". + */ + private void swap(String pOptionToReplace) + { + MenuEntry[] entries = client.getMenuEntries(); + + Integer walkHereEntry = searchIndex(entries, WALK_HERE); + Integer entryToReplace = searchIndex(entries, pOptionToReplace); + + if (walkHereEntry != null + && entryToReplace != null) + { + MenuEntry walkHereMenuEntry = entries[walkHereEntry]; + entries[walkHereEntry] = entries[entryToReplace]; + entries[entryToReplace] = walkHereMenuEntry; + + client.setMenuEntries(entries); + } + } + + /** + * Finds the index of the menu that contains the verbiage we are looking for. + * @param pMenuEntries The list of {@link MenuEntry}s. + * @param pMenuEntryToSearchFor The Option in the menu to search for. + * @return The index location or null if it was not found. + */ + private Integer searchIndex(MenuEntry[] pMenuEntries, String pMenuEntryToSearchFor) + { + Integer indexLocation = 0; + + for (MenuEntry menuEntry : pMenuEntries) + { + String entryOption = Text.removeTags(menuEntry.getOption()).toUpperCase(); + + if (entryOption.equals(pMenuEntryToSearchFor)) + { + return indexLocation; + } + + indexLocation++; + } + + return null; + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/slayermusiq/QuestGuideLinks.java b/runelite-client/src/main/java/net/runelite/client/plugins/slayermusiq/QuestGuideLinks.java new file mode 100644 index 0000000000..078491bb4d --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/slayermusiq/QuestGuideLinks.java @@ -0,0 +1,207 @@ +package net.runelite.client.plugins.slayermusiq; + +import net.runelite.client.util.LinkBrowser; +import net.runelite.api.ChatMessageType; +import net.runelite.api.events.ChatMessage; +import net.runelite.client.chat.ChatColorType; +import net.runelite.client.chat.ChatMessageBuilder; +import net.runelite.client.chat.ChatMessageManager; +import net.runelite.client.chat.QueuedMessage; + +public class QuestGuideLinks { + private static final Link[] QUEST_GUIDE_LINKS = { + // Free Quests + new Link("Cook's Assistant", "https://www.youtube.com/watch?v=ehmtDRelj3c"), + new Link("Romeo & Juliet", "https://www.youtube.com/watch?v=rH_biWSNWVY"), + new Link("Demon Slayer", "https://www.youtube.com/watch?v=hgACrzJSiQk"), + new Link("Shield of Arrav", "https://www.youtube.com/watch?v=a_imLDKUdzg"), + new Link("Sheep Shearer", "https://www.youtube.com/watch?v=XFG3aNwK68s"), + new Link("The Restless Ghost", "https://www.youtube.com/watch?v=UkWNcsG_pXM"), + new Link("Ernest the Chicken", "https://www.youtube.com/watch?v=cq8NIVhSqh4"), + new Link("Vampire Slayer", "https://www.youtube.com/watch?v=FcEuxsDJWCU"), + new Link("Imp Catcher", "https://www.youtube.com/watch?v=LHgnl0FbOzk"), + new Link("Prince Ali Rescue", "https://www.youtube.com/watch?v=hrSPl1GfFaw"), + new Link("Doric's Quest", "https://www.youtube.com/watch?v=5TYyxHU27a4"), + new Link("Black Knights' Fortress", "https://www.youtube.com/watch?v=aekoZi3f9cU"), + new Link("Witch's Potion", "https://www.youtube.com/watch?v=XV4i5sPUvXo"), + new Link("The Knight's Sword", "https://www.youtube.com/watch?v=UkBWaI0rOqE"), + new Link("Goblin Diplomacy", "https://www.youtube.com/watch?v=P9BKOb_dLoY"), + new Link("Pirate's Treasure", "https://www.youtube.com/watch?v=zcD87PQW8Qk"), + new Link("Dragon Slayer", "https://www.youtube.com/watch?v=bMtCjlFOaBI"), + new Link("Rune Mysteries", "https://www.youtube.com/watch?v=l8ZhaN8uoS0"), + new Link("Misthalin Mystery", "https://www.youtube.com/watch?v=QlFqVAobAlQ"), + new Link("The Corsair Curse", "https://www.youtube.com/watch?v=wi7mUAHExz4"), + new Link("X Marks the Spot", "https://www.youtube.com/watch?v=GhRgvEG5jxQ"), + // Members Quests + new Link("Druidic Ritual", "https://www.youtube.com/watch?v=QIfU6HSmH4w"), + new Link("Lost City", "https://www.youtube.com/watch?v=T-kQNUSjFZI"), + new Link("Witch's House", "https://www.youtube.com/watch?v=TLsg7Wa-LUA"), + new Link("Merlin's Crystal", "https://www.youtube.com/watch?v=ESX-qriNtCE"), + new Link("Heroes' Quest", "https://www.youtube.com/watch?v=hK2N0WLKviE"), + new Link("Scorpion Catcher", "https://www.youtube.com/watch?v=xpqdec7_ZWg"), + new Link("Family Crest", "https://www.youtube.com/watch?v=0mk_Cgjr738"), + new Link("Monk's Friend", "https://www.youtube.com/watch?v=avi4y4G3Hcw"), + new Link("Temple of Ikov", "https://www.youtube.com/watch?v=5K7jDgr_4Z4"), + new Link("Clock Tower", "https://www.youtube.com/watch?v=GUCkkQFzyDw"), + new Link("Holy Grail", "https://www.youtube.com/watch?v=cgXoV1QlYco"), + new Link("Tree Gnome Village", "https://www.youtube.com/watch?v=T6Su__yuyRI"), + new Link("Fight Arena", "https://www.youtube.com/watch?v=4Nqjep2E5pw"), + new Link("Hazeel Cult", "https://www.youtube.com/watch?v=2_fhFJW6cNY"), + new Link("Sheep Herder", "https://www.youtube.com/watch?v=akC9FeYCG1Q"), + new Link("Plague City", "https://www.youtube.com/watch?v=Hf2wQQZL5CU"), + new Link("Waterfall Quest", "https://www.youtube.com/watch?v=xWBSnGkQTi4"), + new Link("Jungle Potion", "https://www.youtube.com/watch?v=xqLKsFz08As"), + new Link("The Grand Tree", "https://www.youtube.com/watch?v=N5e_Jus_E-Y"), + new Link("Underground Pass", "https://www.youtube.com/watch?v=5klGJg1wY8k"), + new Link("Observatory Quest", "https://www.youtube.com/watch?v=yxa9B6svv44"), + new Link("Watchtower", "https://www.youtube.com/watch?v=Vb10GoYP7FE"), + new Link("Dwarf Cannon", "https://www.youtube.com/watch?v=pROFg5jcCR0"), + new Link("Murder Mystery", "https://www.youtube.com/watch?v=P1IDGCA2f9o"), + new Link("The Dig Site", "https://www.youtube.com/watch?v=TOdcWV4MzuU"), + new Link("Gertrude's Cat", "https://www.youtube.com/watch?v=g7S09wA8EAY"), + new Link("Legends' Quest", "https://www.youtube.com/watch?v=Lid8enDEF_U"), + new Link("Death Plateau", "https://www.youtube.com/watch?v=SIQFmTvnb6w"), + new Link("Big Chompy Bird Hunting", "https://www.youtube.com/watch?v=s2fytMOHJXI"), + new Link("Elemental Workshop I", "https://www.youtube.com/watch?v=tbZD2RDqvfQ"), + new Link("Nature Spirit", "https://www.youtube.com/watch?v=Enf8vUWb5o0"), + new Link("Priest in Peril", "https://www.youtube.com/watch?v=fyYri6wUQIU"), + new Link("Regicide", "https://www.youtube.com/watch?v=KkWM-ok3C4Y"), + new Link("Tai Bwo Wannai Trio", "https://www.youtube.com/watch?v=Mdair5mvZL0"), + new Link("Troll Stronghold", "https://www.youtube.com/watch?v=zqmUs-f3AKA"), + new Link("Horror from the Deep", "https://www.youtube.com/watch?v=9htK8kb6DR8"), + new Link("Throne of Miscellania", "https://www.youtube.com/watch?v=fzGMnv2skBE"), + new Link("Monkey Madness I", "https://www.youtube.com/watch?v=VnoRfeBnPFA"), + new Link("Haunted Mine", "https://www.youtube.com/watch?v=cIc6loJHm9Q"), + new Link("Troll Romance", "https://www.youtube.com/watch?v=j2zifZVu7Gc"), + new Link("In Search of the Myreque", "https://www.youtube.com/watch?v=5nmYFHdAXAQ"), + new Link("Creature of Fenkenstrain", "https://www.youtube.com/watch?v=swqUVIs7B7M"), + new Link("Roving Elves", "https://www.youtube.com/watch?v=J3qf9DnT9cA"), + new Link("One Small Favour", "https://www.youtube.com/watch?v=ix_0-W3e9ps"), + new Link("Mountain Daughter", "https://www.youtube.com/watch?v=HETx_LX7aiY"), + new Link("Between a Rock...", "https://www.youtube.com/watch?v=cB11I45EGgA"), + new Link("The Golem", "https://www.youtube.com/watch?v=qpEHpiO6lLw"), + new Link("Desert Treasure", "https://www.youtube.com/watch?v=BuIqulIsICo"), + new Link("Icthlarin's Little Helper", "https://www.youtube.com/watch?v=wpNKm8_vUOM"), + new Link("Tears of Guthix", "https://www.youtube.com/watch?v=EMonDNI0uPk"), + new Link("The Lost Tribe", "https://www.youtube.com/watch?v=spZErjRnCdc"), + new Link("The Giant Dwarf", "https://www.youtube.com/watch?v=Z7PsGpOYgxY"), + new Link("Recruitment Drive", "https://www.youtube.com/watch?v=sOuzMpA_xtw"), + new Link("Mourning's Ends Part I", "https://www.youtube.com/watch?v=vuzAdk-h3c0"), + new Link("Garden of Tranquillity", "https://www.youtube.com/watch?v=7hbCzYnLCsQ"), + new Link("A Tail of Two Cats", "https://www.youtube.com/watch?v=SgN9Yw_YqHk"), + new Link("Wanted!", "https://www.youtube.com/watch?v=ZHZAKDCfXGs"), + new Link("Mourning's Ends Part II", "https://www.youtube.com/watch?v=FK5sLogGbU8"), + new Link("Rum Deal", "https://www.youtube.com/watch?v=I14CIu5x2S8"), + new Link("Shadow of the Storm", "https://www.youtube.com/watch?v=5ZvWd3XCQjI"), + new Link("Ratcatchers", "https://www.youtube.com/watch?v=s7G22fEuhTc"), + new Link("Spirits of the Elid", "https://www.youtube.com/watch?v=A1zAX55hZC0"), + new Link("Devious Minds", "https://www.youtube.com/watch?v=_UtlFmrWt1w"), + new Link("Enakhra's Lament", "https://www.youtube.com/watch?v=Y3kEIPYVaVE"), + new Link("Cabin Fever", "https://www.youtube.com/watch?v=k5DtxNXhOaw"), + new Link("Fairytale I - Growing Pains", "https://www.youtube.com/watch?v=cfGI9qFOmsg"), + new Link("Recipe for Disaster", "https://www.youtube.com/watch?v=hrAyyInJaTA"), + new Link("In Aid of the Myreque", "https://www.youtube.com/watch?v=O2Ru2NmuTaA"), + new Link("A Soul's Bane", "https://www.youtube.com/watch?v=dp8dp79qp6I"), + new Link("Rag and Bone Man", "https://www.youtube.com/watch?v=3owXSeN56W8"), + new Link("Swan Song", "https://www.youtube.com/watch?v=IpmERThXv2g"), + new Link("Royal Trouble", "https://www.youtube.com/watch?v=bVWUlKzNXEg"), + new Link("Death to the Dorgeshuun", "https://www.youtube.com/watch?v=2XJHuLhig98"), + new Link("Fairytale II - Cure a Queen", "https://www.youtube.com/watch?v=P6KkRk4_e3U"), + new Link("Lunar Diplomacy", "https://www.youtube.com/watch?v=vmeSKb7IBgQ"), + new Link("The Eyes of Glouphrie", "https://www.youtube.com/watch?v=0YCPwmZcxKA"), + new Link("Darkness of Hallowvale", "https://www.youtube.com/watch?v=QziKl99qdtU"), + new Link("Elemental Workshop II", "https://www.youtube.com/watch?v=Bb4E7ecIgv0"), + new Link("My Arm's Big Adventure", "https://www.youtube.com/watch?v=xa1KWOewgYA"), + new Link("Enlightened Journey", "https://www.youtube.com/watch?v=XAPthC8d7k0"), + new Link("Eagles' Peak", "https://www.youtube.com/watch?v=KDxIrrwXp7U"), + new Link("Animal Magnetism", "https://www.youtube.com/watch?v=kUyjXA7TaFU"), + new Link("Contact!", "https://www.youtube.com/watch?v=czn-yWABBWs"), + new Link("Cold War", "https://www.youtube.com/watch?v=0m1KpP-qKWI"), + new Link("The Fremennik Isles", "https://www.youtube.com/watch?v=EvxhiOWmraY"), + new Link("The Great Brain Robbery", "https://www.youtube.com/watch?v=ImHFASuNUN8"), + new Link("What Lies Below", "https://www.youtube.com/watch?v=f_9nVMGTtuo"), + new Link("Olaf's Quest", "https://www.youtube.com/watch?v=mXV5bM1NFMM"), + new Link("Dream Mentor", "https://www.youtube.com/watch?v=XDLUu0Kf0sE"), + new Link("Grim Tales", "https://www.youtube.com/watch?v=dFB0Q6v8Apw"), + new Link("King's Ransom", "https://www.youtube.com/watch?v=UJz9ZfF3uCY"), + new Link("Shilo Village", "https://www.youtube.com/watch?v=bDvBi8FT-QI"), + new Link("Biohazard", "https://www.youtube.com/watch?v=n9k87LwOGMk"), + new Link("Tower of Life", "https://www.youtube.com/watch?v=KReMcWpeY3k"), + new Link("Rag and Bone Man II", "https://www.youtube.com/watch?v=KGdHiDDUX_U"), + new Link("Zogre Flesh Eaters", "https://www.youtube.com/watch?v=vzm4949kXP4"), + new Link("Monkey Madness II", "https://www.youtube.com/watch?v=ykE5LbjABaI"), + new Link("Client of Kourend", "https://www.youtube.com/watch?v=Y-KIHF-cL9w"), + new Link("The Queen of Thieves", "https://www.youtube.com/watch?v=W94zFZVrHkQ"), + new Link("Bone Voyage", "https://www.youtube.com/watch?v=-VTR4p8kPmI"), + new Link("Dragon Slayer II", "https://www.youtube.com/watch?v=4BMb3Zwzk_U"), + new Link("The Depths of Despair", "https://www.youtube.com/watch?v=CaVUk2eAsKs"), + new Link("A Taste of Hope", "https://www.youtube.com/watch?v=VjdgEIizdSc"), + new Link("Tale of the Righteous", "https://www.youtube.com/watch?v=99yiv0tPl58"), + new Link("Making Friends with My Arm", "https://www.youtube.com/watch?v=DltzzhIsM_Q"), + new Link("The Ascent of Arceuus", "https://www.youtube.com/watch?v=4VQnfrv6S18"), + new Link("The Forsaken Tower", "https://www.youtube.com/watch?v=con0sXl5NBY"), + new Link("Fishing Contest", "https://www.youtube.com/watch?v=XYSv37A_l5w"), + new Link("Tribal Totem", "https://www.youtube.com/watch?v=XkUEIjr886M"), + new Link("Sea Slug", "https://www.youtube.com/watch?v=oOZVfa5SkVQ"), + new Link("The Tourist Trap", "https://www.youtube.com/watch?v=0bmSCCepMvo"), + new Link("Eadgar's Ruse", "https://www.youtube.com/watch?v=aVQ3DjTElXg"), + new Link("Shades of Mort'ton", "https://www.youtube.com/watch?v=eF05R8OMxgg"), + new Link("The Fremennik Trials", "https://www.youtube.com/watch?v=YUIvEgcvl5c"), + new Link("Ghosts Ahoy", "https://www.youtube.com/watch?v=aNBkLOywDfM"), + new Link("The Feud", "https://www.youtube.com/watch?v=nlBSc9IUklA"), + new Link("Forgettable Tale...", "https://www.youtube.com/watch?v=3HvFd6AxNU0"), + new Link("Making History", "https://www.youtube.com/watch?v=bOTGi2zAuhs"), + new Link("The Hand in the Sand", "https://www.youtube.com/watch?v=gdNLcZ-l1Lw"), + new Link("The Slug Menace", "https://www.youtube.com/watch?v=BRQbdr3JEZ8"), + new Link("Another Slice of H.A.M.", "https://www.youtube.com/watch?v=Yq3db7827Lk") + }; + + private static class Link { + + private String questName; + private String url; + + public Link(String questName, String url) { + this.questName = questName; + this.url = url; + } + + public String getQuestName() { + return questName; + } + + public void openURL() { + LinkBrowser.browse(this.url); + } + + } + + private static boolean openGuide(String questName) { + for (Link link : QUEST_GUIDE_LINKS) { + if (link.getQuestName().equals(questName)) { + link.openURL(); + return true; + } + } + return false; + } + + private static void logQuestNotFoundError(String questName, ChatMessageManager chatMessageManager) { + String chatMessage = new ChatMessageBuilder() + .append(ChatColorType.HIGHLIGHT) + .append("Could not find Slayermusiq1 guide for " + questName) + .build(); + + chatMessageManager.queue(QueuedMessage.builder() + .type(ChatMessageType.CONSOLE) + .runeLiteFormattedMessage(chatMessage) + .build()); + } + + public static void tryOpenGuide(String questName, ChatMessageManager chatMessageManager) { + boolean success = openGuide(questName); + if (!success) { + logQuestNotFoundError(questName, chatMessageManager); + } + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/slayermusiq/SlayermusiqPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/slayermusiq/SlayermusiqPlugin.java new file mode 100644 index 0000000000..7e07a0564d --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/slayermusiq/SlayermusiqPlugin.java @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2018, Jeremy Berchtold + * 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. + */ + + +// Based off RuneLite's Wiki Plugin +/* + * Copyright (c) 2018 Abex + * 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.slayermusiq; + +import com.google.inject.Provides; +import com.google.common.primitives.Ints; +import java.awt.Dimension; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.inject.Inject; +import javax.swing.SwingUtilities; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.Client; +import net.runelite.api.MenuAction; +import net.runelite.api.MenuEntry; +import net.runelite.api.widgets.WidgetInfo; +import net.runelite.api.events.MenuEntryAdded; +import net.runelite.api.events.MenuOptionClicked; +import net.runelite.client.util.Text; +import net.runelite.client.chat.ChatMessageManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; + +@PluginDescriptor( + name = "!Slayermusiq1 Guides", + description = "Adds a right-click option to go to Slayermusiq1's guides from the quest tab", + tags = {"quest", "guide", "slayermusiq"} +) +@Slf4j +public class SlayermusiqPlugin extends Plugin +{ + + private static final int[] QUESTLIST_WIDGET_IDS = new int[] + { + WidgetInfo.QUESTLIST_FREE_CONTAINER.getId(), + WidgetInfo.QUESTLIST_MEMBERS_CONTAINER.getId(), + WidgetInfo.QUESTLIST_MINIQUEST_CONTAINER.getId(), + }; + + private static final String MENUOP_SLAYERMUSIQ = "Slayermusiq"; + + @Inject + private Client client; + + @Inject + private ChatMessageManager chatMessageManager; + + @Override + protected void startUp() throws Exception + { + // + } + + @Override + protected void shutDown() throws Exception + { + // + } + + @Subscribe + public void onMenuEntryAdded(MenuEntryAdded event) + { + int widgetID = event.getActionParam1(); + if (Ints.contains(QUESTLIST_WIDGET_IDS, widgetID) && "Read Journal:".equals(event.getOption())) { + MenuEntry[] menuEntries = client.getMenuEntries(); + + MenuEntry newMenuEntry = createSlayermusiqOptionMenuEntry(event); + menuEntries = Arrays.copyOf(menuEntries, menuEntries.length + 1); + menuEntries[menuEntries.length - 1] = newMenuEntry; + + client.setMenuEntries(menuEntries); + } + } + + @Subscribe + private void onMenuOptionClicked(MenuOptionClicked ev) { + if (ev.getMenuAction() == MenuAction.RUNELITE && ev.getMenuOption().equals(MENUOP_SLAYERMUSIQ)) { + ev.consume(); + String quest = Text.removeTags(ev.getMenuTarget()); + QuestGuideLinks.tryOpenGuide(quest, chatMessageManager); + } + } + + private MenuEntry createSlayermusiqOptionMenuEntry(MenuEntryAdded event) { + int widgetIndex = event.getActionParam0(); + int widgetID = event.getActionParam1(); + + MenuEntry menuEntry = new MenuEntry(); + menuEntry.setTarget(event.getTarget()); + menuEntry.setOption(MENUOP_SLAYERMUSIQ); + menuEntry.setParam0(widgetIndex); + menuEntry.setParam1(widgetID); + menuEntry.setType(MenuAction.RUNELITE.getId()); + + return menuEntry; + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/spellbookfixer/SpellbookFixerConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/spellbookfixer/SpellbookFixerConfig.java new file mode 100644 index 0000000000..bb16cd3709 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/spellbookfixer/SpellbookFixerConfig.java @@ -0,0 +1,110 @@ +package net.runelite.client.plugins.spellbookfixer; + +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("spellbookfixer") +public interface SpellbookFixerConfig extends Config +{ + @ConfigItem(position = 0, keyName = "shouldHideOthers", name = "Hide Others", description = "Toggle on to hide spells not useful for pking that cannot be filtered otherwise.") + default boolean shouldHideOthers() + { + return false; + } + + //ice blitz + @ConfigItem(position = 1, keyName = "shouldModifyIceBlitz", name = "Ice Blitz", description = "Toggle on to enable Ice Blitz modifications.") + default boolean shouldModifyIceBlitz() { return false; } + @ConfigItem(position = 2, keyName = "getBlitzPositionX", name = "Ice Blitz Pos X", description = "Modifies the X-axis position of Ice Blitz.") + default int getBlitzPositionX() + { + return 0; + } + @ConfigItem(position = 3, keyName = "getBlitzPositionY", name = "Ice Blitz Pos Y", description = "Modifies the Y-axis position of Ice Blitz.") + default int getBlitzPositionY() + { + return 118; + } + @ConfigItem(position = 4, keyName = "getBlitzSize", name = "Ice Blitz Size", description = "Modifies the width of Ice Blitz.") + default int getBlitzSize() + { + return 80; + } + + //ice barrage + @ConfigItem(position = 5, keyName = "shouldModifyIceBarrage", name = "Ice Barrage", description = "Toggle on to enable Ice Barrage modifications.") + default boolean shouldModifyIceBarrage() { return false; } + @ConfigItem(position = 6, keyName = "getBarragePositionX", name = "Ice Barrage Pos X", description = "Modifies the X-axis position of Ice Barrage.") + default int getBarragePositionX() + { + return 0; + } + @ConfigItem(position = 7, keyName = "getBarragePositionY", name = "Ice Barrage Pos X", description = "Modifies the X-axis position of Ice Barrage.") + default int getBarragePositionY() + { + return 0; + } + @ConfigItem(position = 8, keyName = "getBarrageSize", name = "Ice Barrage Size", description = "Modifies the width position of Ice Barrage.") + default int getBarrageSize() + { + return 80; + } + + //vengeance + @ConfigItem(position = 9, keyName = "shouldModifyVengeance", name = "Vengeance", description = "Toggle on to enable Vengeance modifications.") + default boolean shouldModifyVengeance() { return false; } + @ConfigItem(position = 10, keyName = "getVengeancePositionX", name = "Vengeance Pos X", description = "Modifies the X-axis position of Vengeance.") + default int getVengeancePositionX() + { + return 0; + } + @ConfigItem(position = 11, keyName = "getVengeancePositionY", name = "Vengeance Pos X", description = "Modifies the X-axis position of Vengeance.") + default int getVengeancePositionY() + { + return 0; + } + @ConfigItem(position = 12, keyName = "getVengeanceSize", name = "Vengeance Size", description = "Modifies the width position of Vengeance.") + default int getVengeanceSize() + { + return 80; + } + + //teleblock + @ConfigItem(position = 13, keyName = "shouldModifyTeleBlock", name = "TeleBlock", description = "Toggle on to enable TeleBlock modifications.") + default boolean shouldModifyTeleBlock() { return false; } + @ConfigItem(position = 14, keyName = "getTeleBlockPositionX", name = "TeleBlock Pos X", description = "Modifies the X-axis position of TeleBlock.") + default int getTeleBlockPositionX() + { + return 0; + } + @ConfigItem(position = 15, keyName = "getTeleBlockPositionY", name = "TeleBlock Pos X", description = "Modifies the X-axis position of TeleBlock.") + default int getTeleBlockPositionY() + { + return 0; + } + @ConfigItem(position = 16, keyName = "getTeleBlockSize", name = "TeleBlock Size", description = "Modifies the width position of TeleBlock.") + default int getTeleBlockSize() + { + return 80; + } + + //entangle + @ConfigItem(position = 17, keyName = "shouldModifyEntangle", name = "Entangle", description = "Toggle on to enable Entangle modifications.") + default boolean shouldModifyEntangle() { return false; } + @ConfigItem(position = 18, keyName = "getEntanglePositionX", name = "Entangle Pos X", description = "Modifies the X-axis position of Entangle.") + default int getEntanglePositionX() + { + return 0; + } + @ConfigItem(position = 19, keyName = "getEntanglePositionY", name = "Entangle Pos X", description = "Modifies the X-axis position of Entangle.") + default int getEntanglePositionY() + { + return 118; + } + @ConfigItem(position = 20, keyName = "getEntangleSize", name = "Entangle Size", description = "Modifies the width position of Entangle.") + default int getEntangleSize() + { + return 80; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/spellbookfixer/SpellbookFixerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/spellbookfixer/SpellbookFixerPlugin.java new file mode 100644 index 0000000000..7d71cef371 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/spellbookfixer/SpellbookFixerPlugin.java @@ -0,0 +1,170 @@ +package net.runelite.client.plugins.spellbookfixer; + +import com.google.inject.Provides; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.Client; +import net.runelite.api.GameState; +import net.runelite.api.events.GameStateChanged; +import net.runelite.api.events.GameTick; +import net.runelite.api.events.WidgetLoaded; +import net.runelite.api.widgets.Widget; +import net.runelite.api.widgets.WidgetID; +import net.runelite.api.widgets.WidgetInfo; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; + +import javax.inject.Inject; + + +@PluginDescriptor( + name = "Spellbook Fixer", + description = "Resize and filter spellbook for PKing", + tags = {"resize", "spellbook", "magic", "spell", "pk", "book", "filter", "bogla"} +) +@Slf4j +public class SpellbookFixerPlugin extends Plugin +{ + @Inject + private Client client; + + @Inject + SpellbookFixerConfig config; + + @Provides + SpellbookFixerConfig provideConfig(ConfigManager configManager) { return configManager.getConfig(SpellbookFixerConfig.class); } + + @Override + protected void startUp() throws Exception + { + adjustSpellbook(); + } + + @Override + protected void shutDown() throws Exception + { + resetSpellbook(); + } + + @Subscribe + public void onGameStateChanged(GameStateChanged event) + { + if (event.getGameState() == GameState.LOGGED_IN) + adjustSpellbook(); + } + + @Subscribe + public void onWidgetLoaded(WidgetLoaded event) + { + if (event.getGroupId() == WidgetID.SPELLBOOK_GROUP_ID) + adjustSpellbook(); + } + + @Subscribe + public void onGameTick(GameTick event) + { + adjustSpellbook(); + } + + private void adjustSpellbook() + { + if (client.getGameState() != GameState.LOGGED_IN) + return; + + try + { + if (config.shouldModifyIceBarrage()) + modifySpell(WidgetInfo.SPELL_ICE_BARRAGE, config.getBarragePositionX(), config.getBarragePositionY(), config.getBarrageSize()); + + if (config.shouldModifyIceBlitz()) + modifySpell(WidgetInfo.SPELL_ICE_BLITZ, config.getBlitzPositionX(), config.getBlitzPositionY(), config.getBlitzSize()); + + if (config.shouldModifyVengeance()) + modifySpell(WidgetInfo.SPELL_VENGEANCE, config.getVengeancePositionX(), config.getVengeancePositionY(), config.getVengeanceSize()); + + if (config.shouldModifyTeleBlock()) + modifySpell(WidgetInfo.SPELL_TELE_BLOCK, config.getTeleBlockPositionX(), config.getTeleBlockPositionY(), config.getTeleBlockSize()); + + if (config.shouldModifyEntangle()) + modifySpell(WidgetInfo.SPELL_ENTANGLE, config.getEntanglePositionX(), config.getEntanglePositionY(), config.getEntangleSize()); + + setSpellHidden(WidgetInfo.SPELL_BLOOD_BLITZ, config.shouldHideOthers()); + setSpellHidden(WidgetInfo.SPELL_VENGEANCE_OTHER, config.shouldHideOthers()); + setSpellHidden(WidgetInfo.SPELL_BIND, config.shouldHideOthers()); + setSpellHidden(WidgetInfo.SPELL_SNARE, config.shouldHideOthers()); + } + catch (Exception e) + { + //swallow + } + + + } + + private void resetSpellbook() + { + if (client.getGameState() != GameState.LOGGED_IN) + return; + + try + { + if (config.shouldModifyIceBarrage()) + modifySpell(WidgetInfo.SPELL_ICE_BARRAGE, config.getBarragePositionX(), config.getBarragePositionY(), 24); + + if (config.shouldModifyIceBlitz()) + modifySpell(WidgetInfo.SPELL_ICE_BLITZ, config.getBlitzPositionX(), config.getBlitzPositionY(), 24); + + if (config.shouldModifyVengeance()) + modifySpell(WidgetInfo.SPELL_VENGEANCE, config.getVengeancePositionX(), config.getVengeancePositionY(), 24); + + if (config.shouldModifyTeleBlock()) + modifySpell(WidgetInfo.SPELL_TELE_BLOCK, config.getTeleBlockPositionX(), config.getTeleBlockPositionY(), 24); + + if (config.shouldModifyEntangle()) + modifySpell(WidgetInfo.SPELL_ENTANGLE, config.getEntanglePositionX(), config.getEntanglePositionY(), 24); + + setSpellHidden(WidgetInfo.SPELL_BLOOD_BLITZ, false); + setSpellHidden(WidgetInfo.SPELL_VENGEANCE_OTHER, false); + setSpellHidden(WidgetInfo.SPELL_BIND, false); + setSpellHidden(WidgetInfo.SPELL_SNARE, false); + } + catch (Exception e) + { + //swallow + } + } + + private void modifySpell(WidgetInfo widgetInfo, int posX, int posY, int size) + { + Widget widget = client.getWidget(widgetInfo); + + if (widget == null) + return; + + try + { + widget.setOriginalX(posX); + widget.setOriginalY(posY); + widget.setOriginalWidth(size); + widget.setOriginalHeight(size); + widget.revalidate(); + } + catch (Exception e) + { + //swallow + } + + } + + private void setSpellHidden(WidgetInfo widgetInfo, boolean hidden) + { + Widget widget = client.getWidget(widgetInfo); + + if (widget == null) + return; + + widget.setHidden(hidden); + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/ActionType.java b/runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/ActionType.java new file mode 100644 index 0000000000..b801e8b2a8 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/ActionType.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018, Davis Cook + * Copyright (c) 2018, Daddy Dozer + * 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.suppliestracker; + +/** + * Type of action performed in a menu + */ +public enum ActionType +{ + + CONSUMABLE, TELEPORT, CAST; + +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/BlowpipeDartType.java b/runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/BlowpipeDartType.java new file mode 100644 index 0000000000..09182d6cde --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/BlowpipeDartType.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2018, Davis Cook + * 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.suppliestracker; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import static net.runelite.api.ItemID.*; + +/** + * Type of darts that can be put into the blowpipe + */ +@AllArgsConstructor +public enum BlowpipeDartType +{ + BRONZE(BRONZE_DART), IRON(IRON_DART), + STEEL(STEEL_DART), MITHRIL(MITHRIL_DART), + ADAMANT(ADAMANT_DART), RUNE(RUNE_DART), + DRAGON(DRAGON_DART); + + @Getter + private int dartID; + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/ItemType.java b/runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/ItemType.java new file mode 100644 index 0000000000..8479937589 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/ItemType.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2018, Davis Cook + * Copyright (c) 2018, Daddy Dozer + * 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.suppliestracker; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * The potential types that supplies can be along with a categorization function + * that assigns the supplies to these categories + */ +@AllArgsConstructor +public enum ItemType +{ + FOOD("Food"), + POTION("Potions"), + RUNE("Runes"), + AMMO("Ammo"), + TELEPORT("Teleports"); + + @Getter + private String label; + + /** + * Takes an item and determines what ItemType it should categorize into + * @param item the item to determine category for + * @return our best guess for what category this item goes into + * note that if the guess is wrong (per say) it won't break anything because it will be + * consistently wrong but it could have an item that is clearly not food in the food section + */ + public static ItemType categorize(SuppliesTrackerItem item) + { + if (item.getName().contains("(4)")) + { + return ItemType.POTION; + } + if (item.getName().toLowerCase().contains("bolt") || item.getName().toLowerCase().contains("dart") + || item.getName().toLowerCase().contains("arrow") || item.getName().toLowerCase().contains("javelin") + || item.getName().toLowerCase().contains("knive") || item.getName().toLowerCase().contains("throwing") + || item.getName().toLowerCase().contains("zulrah's scale") || item.getName().toLowerCase().contains("cannonball")) + { + return ItemType.AMMO; + } + if (item.getName().contains("rune")) + { + return ItemType.RUNE; + } + if (item.getName().toLowerCase().contains("teleport")) + { + return ItemType.TELEPORT; + } + return ItemType.FOOD; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/MenuAction.java b/runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/MenuAction.java new file mode 100644 index 0000000000..42a942e74b --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/MenuAction.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2018, Davis Cook + * 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.suppliestracker; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import net.runelite.api.Item; + +/** + * Data class that tracks all info related to a menu click action + */ +@AllArgsConstructor +public class MenuAction +{ + + @Getter + private ActionType type; + @Getter + private Item[] oldInventory; + + public static class ItemAction extends MenuAction + { + + @Getter + private int itemID; + @Getter + private int slot; + + public ItemAction(ActionType type, Item[] oldInventory, int itemID, int slot) + { + super(type, oldInventory); + this.itemID = itemID; + this.slot = slot; + } + + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/SuppliesBox.java b/runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/SuppliesBox.java new file mode 100644 index 0000000000..76dc060a9f --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/SuppliesBox.java @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2018, Davis Cook + * Copyright (c) 2018, Daddy Dozer + * 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.suppliestracker; + +import lombok.AccessLevel; +import lombok.Getter; +import net.runelite.client.game.AsyncBufferedImage; +import net.runelite.client.game.ItemManager; +import net.runelite.client.ui.ColorScheme; +import net.runelite.client.ui.FontManager; +import net.runelite.client.util.StackFormatter; +import net.runelite.client.util.Text; +import net.runelite.http.api.item.ItemPrice; + +import javax.swing.*; +import javax.swing.border.EmptyBorder; +import java.awt.*; +import java.util.ArrayList; +import java.util.List; + +import static net.runelite.api.ItemID.*; +import static net.runelite.api.ItemID.HALF_A_MEAT_PIE; + +public class SuppliesBox extends JPanel +{ + private static final int ITEMS_PER_ROW = 5; + + private final JPanel itemContainer = new JPanel(); + private final JLabel priceLabel = new JLabel(); + private final JLabel subTitleLabel = new JLabel(); + private final ItemManager itemManager; + @Getter(AccessLevel.PACKAGE) + private final String id; + private final SuppliesTrackerPlugin plugin; + private final SuppliesTrackerPanel panel; + + @Getter + private final List trackedItems = new ArrayList<>(); + + private long totalPrice; + + @Getter + private final ItemType type; + + SuppliesBox(final ItemManager itemManager, final String id, + final SuppliesTrackerPlugin plugin, final SuppliesTrackerPanel panel, + final ItemType type) + { + this.id = id; + this.itemManager = itemManager; + this.plugin = plugin; + this.panel = panel; + this.type = type; + + setLayout(new BorderLayout(0, 1)); + setBorder(new EmptyBorder(5, 0, 0, 0)); + + final JPanel logTitle = new JPanel(new BorderLayout(5, 0)); + logTitle.setBorder(new EmptyBorder(7, 7, 7, 7)); + logTitle.setBackground(ColorScheme.DARKER_GRAY_COLOR.darker()); + + final JLabel titleLabel = new JLabel(Text.removeTags(id)); + titleLabel.setFont(FontManager.getRunescapeSmallFont()); + titleLabel.setForeground(Color.WHITE); + + logTitle.add(titleLabel, BorderLayout.WEST); + + subTitleLabel.setFont(FontManager.getRunescapeSmallFont()); + subTitleLabel.setForeground(ColorScheme.LIGHT_GRAY_COLOR); + logTitle.add(subTitleLabel, BorderLayout.CENTER); + + priceLabel.setFont(FontManager.getRunescapeSmallFont()); + priceLabel.setForeground(ColorScheme.LIGHT_GRAY_COLOR); + logTitle.add(priceLabel, BorderLayout.EAST); + + add(logTitle, BorderLayout.NORTH); + add(itemContainer, BorderLayout.CENTER); + + // Create popup menu + final JPopupMenu popupMenu = new JPopupMenu(); + popupMenu.setBorder(new EmptyBorder(5, 5, 5, 5)); + setComponentPopupMenu(popupMenu); + + // Create reset menu + final JMenuItem reset = new JMenuItem("Reset Category"); + reset.addActionListener(e -> + { + for (SuppliesTrackerItem item : trackedItems) + { + plugin.clearItem(item.getId()); + } + clearAll(); + rebuild(); + panel.updateOverall(); + }); + + popupMenu.add(reset); + + setVisible(false); + } + + void update(SuppliesTrackerItem item) + { + trackedItems.removeIf(r -> r.getId() == item.getId()); + trackedItems.add(item); + setVisible(trackedItems.size() > 0); + } + + void remove(SuppliesTrackerItem item) + { + trackedItems.removeIf(r -> r.getId() == item.getId()); + plugin.clearItem(item.getId()); + setVisible(trackedItems.size() > 0); + } + + void clearAll() + { + trackedItems.clear(); + setVisible(false); + } + + public long getTotalSupplies() + { + long totalSupplies = 0; + for (SuppliesTrackerItem item : trackedItems) + { + totalSupplies += item.getQuantity(); + } + return totalSupplies; + } + + public long getTotalPrice() + { + return totalPrice; + } + + void rebuild() + { + buildItems(); + + priceLabel.setText(StackFormatter.quantityToStackSize(totalPrice) + " gp"); + priceLabel.setToolTipText(StackFormatter.formatNumber(totalPrice) + " gp"); + + final long supplies = getTotalSupplies(); + if (supplies > 0) + { + subTitleLabel.setText("x " + supplies); + } + else + { + subTitleLabel.setText(""); + } + + validate(); + repaint(); + } + + private void buildItems() + { + final List items = new ArrayList<>(trackedItems); + totalPrice = 0; + + for (SuppliesTrackerItem item : items) + { + totalPrice += item.getPrice(); + } + + items.sort((i1, i2) -> Long.compare(i2.getPrice(), i1.getPrice())); + + // calculates how many rows need to be displayed to fit all item + final int rowSize = ((items.size() % ITEMS_PER_ROW == 0) ? 0 : 1) + items.size() / ITEMS_PER_ROW; + + itemContainer.removeAll(); + itemContainer.setLayout(new GridLayout(rowSize, ITEMS_PER_ROW, 1, 1)); + + for (int i = 0; i < rowSize * ITEMS_PER_ROW; i++) + { + final JPanel slotContainer = new JPanel(); + slotContainer.setBackground(ColorScheme.DARKER_GRAY_COLOR); + + if (i < items.size()) + { + final SuppliesTrackerItem item = items.get(i); + final JLabel imageLabel = new JLabel(); + imageLabel.setToolTipText(buildToolTip(item)); + imageLabel.setVerticalAlignment(SwingConstants.CENTER); + imageLabel.setHorizontalAlignment(SwingConstants.CENTER); + + AsyncBufferedImage itemImage = itemManager.getImage(getModifiedItemId(item.getName(), item.getId()), item.getQuantity(), item.getQuantity() > 1); + itemImage.addTo(imageLabel); + slotContainer.add(imageLabel); + + // create popup menu + final JPopupMenu popupMenu = new JPopupMenu(); + popupMenu.setBorder(new EmptyBorder(5, 5, 5, 5)); + slotContainer.setComponentPopupMenu(popupMenu); + + final JMenuItem reset = new JMenuItem("Reset"); + reset.addActionListener(e -> + { + remove(item); + rebuild(); + panel.updateOverall(); + }); + + popupMenu.add(reset); + } + itemContainer.add(slotContainer); + } + itemContainer.repaint(); + } + + private int getModifiedItemId(String name, int itemId) + { + if (SuppliesTrackerPlugin.isPotion(name)) + { + return getSingleDose(name); + } + if (SuppliesTrackerPlugin.isCake(name, itemId)) + { + return getSlice(itemId); + } + if (SuppliesTrackerPlugin.isPizzaPie(name)) + { + return getHalf(itemId); + } + + return itemId; + } + + //Switches full cake ids to get the image for slice + private int getSlice(int itemId) + { + switch (itemId) + { + case CAKE: + itemId = SLICE_OF_CAKE; + break; + case CHOCOLATE_CAKE: + itemId = CHOCOLATE_SLICE; + break; + } + return itemId; + } + + //Switches full pizza and pie ids to get the image for half + private int getHalf(int itemId) + { + switch (itemId) + { + case ANCHOVY_PIZZA: + itemId = _12_ANCHOVY_PIZZA; + break; + case MEAT_PIZZA: + itemId = _12_MEAT_PIZZA; + break; + case PINEAPPLE_PIZZA: + itemId = _12_PINEAPPLE_PIZZA; + break; + case PLAIN_PIZZA: + itemId = _12_PLAIN_PIZZA; + break; + case REDBERRY_PIE: + itemId = HALF_A_REDBERRY_PIE; + break; + case GARDEN_PIE: + itemId = HALF_A_GARDEN_PIE; + break; + case SUMMER_PIE: + itemId = HALF_A_SUMMER_PIE; + break; + case FISH_PIE: + itemId = HALF_A_FISH_PIE; + break; + case BOTANICAL_PIE: + itemId = HALF_A_BOTANICAL_PIE; + break; + case MUSHROOM_PIE: + itemId = HALF_A_MUSHROOM_PIE; + break; + case ADMIRAL_PIE: + itemId = HALF_AN_ADMIRAL_PIE; + break; + case WILD_PIE: + itemId = HALF_A_WILD_PIE; + break; + case APPLE_PIE: + itemId = HALF_AN_APPLE_PIE; + break; + case MEAT_PIE: + itemId = HALF_A_MEAT_PIE; + break; + + } + return itemId; + } + + private int getSingleDose(String name) + { + String nameModified = name.replace("(4)", "(1)"); + int itemId = 0; + List itemList = itemManager.search(nameModified); + for (ItemPrice item: itemList) + { + itemId = item.getId(); + } + return itemId; + } + + private static String buildToolTip(SuppliesTrackerItem item) + { + final String name = item.getName(); + final int quantity = item.getQuantity(); + final long price = item.getPrice(); + return name + " x " + quantity + " (" + StackFormatter.quantityToStackSize(price) + ") "; + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/SuppliesTrackerConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/SuppliesTrackerConfig.java new file mode 100644 index 0000000000..f14160db15 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/SuppliesTrackerConfig.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018, Davis Cook + * 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.suppliestracker; + +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("suppliestracker") +public interface SuppliesTrackerConfig extends Config +{ + @ConfigItem( + keyName = "blowpipeAmmo", + name = "Ammo used in your blowpipe", + description = "What type of dart are you using in your toxic blowpipe" + ) + default BlowpipeDartType blowpipeAmmo() + { + return BlowpipeDartType.MITHRIL; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/SuppliesTrackerItem.java b/runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/SuppliesTrackerItem.java new file mode 100644 index 0000000000..270d3b08b1 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/SuppliesTrackerItem.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2018, Daddy Dozer + * 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.suppliestracker; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor + +class SuppliesTrackerItem +{ + @Getter + private int id; + @Getter + private String name; + @Getter + private int quantity; + @Getter + private long price; +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/SuppliesTrackerPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/SuppliesTrackerPanel.java new file mode 100644 index 0000000000..6eea00105f --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/SuppliesTrackerPanel.java @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2018, Psikoi + * Copyright (c) 2018, Tomas Slusny + * Copyright (c) 2018, Daddy Dozer + * 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.suppliestracker; + +import net.runelite.client.game.ItemManager; +import net.runelite.client.ui.ColorScheme; +import net.runelite.client.ui.FontManager; +import net.runelite.client.ui.PluginPanel; +import net.runelite.client.ui.components.PluginErrorPanel; +import net.runelite.client.util.ColorUtil; +import net.runelite.client.util.StackFormatter; +import javax.swing.BoxLayout; +import javax.swing.ImageIcon; +import javax.swing.JLabel; +import javax.swing.JMenuItem; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.border.EmptyBorder; +import java.awt.BorderLayout; +import java.awt.GridLayout; +import java.awt.image.BufferedImage; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ScheduledExecutorService; + + +class SuppliesTrackerPanel extends PluginPanel +{ + private static final String HTML_LABEL_TEMPLATE = + "%s%s"; + + // Handle loot logs + private final JPanel logsContainer = new JPanel(); + + private final List boxList = new ArrayList<>(); + + private final PluginErrorPanel errorPanel = new PluginErrorPanel(); + + private final ScheduledExecutorService executor; + + // Handle overall session data + private final JPanel overallPanel = new JPanel(); + private final JLabel overallSuppliesUsedLabel = new JLabel(); + private final JLabel overallCostLabel = new JLabel(); + private final JLabel overallIcon = new JLabel(); + private final ItemManager itemManager; + private final SuppliesTrackerPlugin plugin; + private int overallSuppliesUsed; + private int overallCost; + + SuppliesTrackerPanel(final ItemManager itemManager, ScheduledExecutorService executor, SuppliesTrackerPlugin plugin) + { + this.executor = executor; + this.itemManager = itemManager; + this.plugin = plugin; + setBorder(new EmptyBorder(6, 6, 6, 6)); + setBackground(ColorScheme.DARK_GRAY_COLOR); + setLayout(new BorderLayout()); + + // Create layout panel for wrapping + final JPanel layoutPanel = new JPanel(); + layoutPanel.setLayout(new BoxLayout(layoutPanel, BoxLayout.Y_AXIS)); + add(layoutPanel, BorderLayout.NORTH); + + // Create panel that will contain overall data + overallPanel.setBorder(new EmptyBorder(10, 10, 10, 10)); + overallPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR); + overallPanel.setLayout(new BorderLayout()); + overallPanel.setVisible(true); + + // Add icon and contents + final JPanel overallInfo = new JPanel(); + overallInfo.setBackground(ColorScheme.DARKER_GRAY_COLOR); + overallInfo.setLayout(new GridLayout(2, 1)); + overallInfo.setBorder(new EmptyBorder(0, 10, 0, 0)); + overallSuppliesUsedLabel.setFont(FontManager.getRunescapeSmallFont()); + overallCostLabel.setFont(FontManager.getRunescapeSmallFont()); + overallInfo.add(overallSuppliesUsedLabel); + overallInfo.add(overallCostLabel); + overallPanel.add(overallIcon, BorderLayout.WEST); + overallPanel.add(overallInfo, BorderLayout.CENTER); + + for (ItemType type : ItemType.values()) + { + SuppliesBox newBox = new SuppliesBox(itemManager, type.getLabel(), plugin, this, type); + logsContainer.add(newBox); + boxList.add(newBox); + } + + // Create reset all menu + final JMenuItem reset = new JMenuItem("Reset All"); + reset.addActionListener(e -> + { + overallSuppliesUsed = 0; + overallCost = 0; + plugin.clearSupplies(); + for (SuppliesBox box : boxList) + { + box.clearAll(); + } + updateOverall(); + logsContainer.repaint(); + }); + + // Create popup menu + final JPopupMenu popupMenu = new JPopupMenu(); + popupMenu.setBorder(new EmptyBorder(5, 5, 5, 5)); + popupMenu.add(reset); + overallPanel.setComponentPopupMenu(popupMenu); + + // Create Supply Rows wrapper + logsContainer.setLayout(new BoxLayout(logsContainer, BoxLayout.Y_AXIS)); + layoutPanel.add(overallPanel); + layoutPanel.add(logsContainer); + + errorPanel.setContent("Supply trackers", "You have not used any supplies yet."); + add(errorPanel); + overallPanel.setVisible(false); + } + + /** + * loads an img to the icon on the header + * @param img the img for the header icon + */ + public void loadHeaderIcon(BufferedImage img) + { + overallIcon.setIcon(new ImageIcon(img)); + } + + /** + * convert key value pair to html formatting needed to display nicely + * @param key key + * @param value value + * @return key: value in html + */ + private static String htmlLabel(String key, long value) + { + final String valueStr = StackFormatter.quantityToStackSize(value); + return String.format(HTML_LABEL_TEMPLATE, ColorUtil.toHexColor(ColorScheme.LIGHT_GRAY_COLOR), key, valueStr); + } + + /** + * Add an item to the supply panel by placing it into the correct box + * @param item the item to add + */ + public void addItem(SuppliesTrackerItem item) + { + ItemType category = ItemType.categorize(item); + for (SuppliesBox box : boxList) + { + if (box.getType() == category) + { + box.update(item); + box.rebuild(); + break; + } + } + updateOverall(); + } + + /** + * Updates overall stats to calculate overall used and overall cost from + * the info in each box + */ + public void updateOverall() + { + overallSuppliesUsed = 0; + for (SuppliesBox box : boxList) + { + overallSuppliesUsed += box.getTotalSupplies(); + } + + overallCost = 0; + for (SuppliesBox box : boxList) + { + overallCost += box.getTotalPrice(); + } + + overallSuppliesUsedLabel.setText(htmlLabel("Total Supplies: ", overallSuppliesUsed)); + overallCostLabel.setText(htmlLabel("Total Cost: ", overallCost)); + + if (overallSuppliesUsed <= 0) + { + add(errorPanel); + overallPanel.setVisible(false); + } + else + { + remove(errorPanel); + overallPanel.setVisible(true); + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/SuppliesTrackerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/SuppliesTrackerPlugin.java new file mode 100644 index 0000000000..f7034af63f --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/SuppliesTrackerPlugin.java @@ -0,0 +1,781 @@ +/* + * Copyright (c) 2018, Psikoi + * Copyright (c) 2018, Adam + * Copyright (c) 2018, Sir Girion + * Copyright (c) 2018, Davis Cook + * Copyright (c) 2018, Daddy Dozer + * 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.suppliestracker; + + +import com.google.inject.Provides; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.*; +import net.runelite.api.events.*; +import net.runelite.client.callback.ClientThread; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.game.ItemManager; +import net.runelite.client.game.SpriteManager; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.ui.ClientToolbar; +import net.runelite.client.ui.NavigationButton; +import net.runelite.client.util.ImageUtil; +import net.runelite.http.api.item.ItemPrice; + +import static net.runelite.api.AnimationID.*; +import static net.runelite.api.ItemID.*; +import static net.runelite.client.plugins.suppliestracker.ActionType.CONSUMABLE; +import static net.runelite.client.plugins.suppliestracker.ActionType.TELEPORT; +import static net.runelite.client.plugins.suppliestracker.ActionType.CAST; + +import java.util.*; +import java.util.concurrent.ScheduledExecutorService; +import java.util.regex.Pattern; +import javax.inject.Inject; +import javax.swing.SwingUtilities; +import java.awt.image.BufferedImage; + + +@PluginDescriptor( + name = "Supplies Used Tracker", + description = "Tracks supplies used during the session", + tags = {"cost"}, + enabledByDefault = false +) +@Slf4j +public class SuppliesTrackerPlugin extends Plugin +{ + + private static final String POTION_PATTERN = "[(]\\d[)]"; + + private static final String EAT_PATTERN = "^eat"; + private static final String DRINK_PATTERN = "^drink"; + private static final String TELEPORT_PATTERN = "^teleport"; + private static final String TELETAB_PATTERN = "^break"; + private static final String SPELL_PATTERN = "^cast|^grand\\sexchange|^outside|^seers|^yanille"; + + private static final int EQUIPMENT_MAINHAND_SLOT = EquipmentInventorySlot.WEAPON.getSlotIdx(); + private static final int EQUIPMENT_AMMO_SLOT = EquipmentInventorySlot.AMMO.getSlotIdx(); + private static final int EQUIPMENT_CAPE_SLOT = EquipmentInventorySlot.CAPE.getSlotIdx(); + + private static final double NO_AVAS_PERCENT = 1.0; + private static final double ASSEMBLER_PERCENT = 0.20; + private static final double ACCUMULATOR_PERCENT = 0.28; + private static final double ATTRACTOR_PERCENT = 0.40; + + private static final int BLOWPIPE_TICKS_RAPID_PVM = 2; + private static final int BLOWPIPE_TICKS_RAPID_PVP = 3; + private static final int BLOWPIPE_TICKS_NORMAL_PVM = 3; + private static final int BLOWPIPE_TICKS_NORMAL_PVP = 4; + + private static final double SCALES_PERCENT = 0.66; + + private static final int POTION_DOSES = 4, CAKE_DOSES = 3, PIZZA_PIE_DOSES = 2; + + private static final Random random = new Random(); + + private static final int[] THROWING_IDS = new int[]{BRONZE_DART, IRON_DART, STEEL_DART, BLACK_DART, MITHRIL_DART, ADAMANT_DART, RUNE_DART, DRAGON_DART, BRONZE_KNIFE, IRON_KNIFE, STEEL_KNIFE, BLACK_KNIFE, MITHRIL_KNIFE, ADAMANT_KNIFE, RUNE_KNIFE, BRONZE_THROWNAXE, IRON_THROWNAXE, STEEL_THROWNAXE, MITHRIL_THROWNAXE, ADAMANT_THROWNAXE, RUNE_THROWNAXE, DRAGON_KNIFE, DRAGON_KNIFE_22812, DRAGON_KNIFE_22814, DRAGON_KNIFEP_22808, DRAGON_KNIFEP_22810, DRAGON_KNIFEP , DRAGON_THROWNAXE, CHINCHOMPA_10033, RED_CHINCHOMPA_10034, BLACK_CHINCHOMPA}; + private static final int[] RUNE_IDS = new int[]{AIR_RUNE, WATER_RUNE, EARTH_RUNE, MIND_RUNE, BODY_RUNE, COSMIC_RUNE, CHAOS_RUNE, NATURE_RUNE, LAW_RUNE, DEATH_RUNE, ASTRAL_RUNE, BLOOD_RUNE, SOUL_RUNE, WRATH_RUNE, MIST_RUNE, DUST_RUNE, MUD_RUNE, SMOKE_RUNE, STEAM_RUNE, LAVA_RUNE}; + + //Hold Supply Data + private static HashMap suppliesEntry = new HashMap<>(); + private ItemContainer old; + private Deque actionStack = new ArrayDeque<>(); + private int ammoId = 0; + private int ammoAmount = 0; + private int thrownId = 0; + private int thrownAmount = 0; + private boolean ammoLoaded = false; + private boolean throwingAmmoLoaded = false; + private boolean mainHandThrowing = false; + private int mainHand = 0; + private SuppliesTrackerPanel panel; + private NavigationButton navButton; + private String[] RAIDS_CONSUMABLES = new String[]{"xeric's", "elder", "twisted", "revitalisation", "overload", "prayer enhance", "pysk", "suphi", "leckish", "brawk", "mycil", "roqed", "kyren", "guanic", "prael", "giral", "phluxia", "kryket", "murng", "psykk"}; + + private int attackStyleVarbit = -1; + private int ticks = 0; + private int ticksInAnimation; + + @Inject + private ClientToolbar clientToolbar; + + @Inject + private ItemManager itemManager; + + @Inject + private SpriteManager spriteManager; + + @Inject + private SuppliesTrackerConfig config; + + @Inject + private Client client; + + @Inject + private ScheduledExecutorService executorService; + + @Inject + private ClientThread clientThread; + + + @Override + protected void startUp() throws Exception + { + panel = new SuppliesTrackerPanel(itemManager, executorService, this); + final BufferedImage header = ImageUtil.getResourceStreamFromClass(getClass(), "panel_icon.png"); + panel.loadHeaderIcon(header); + final BufferedImage icon = ImageUtil.getResourceStreamFromClass(getClass(), "panel_icon.png"); + + navButton = NavigationButton.builder() + .tooltip("Supplies Tracker") + .icon(icon) + .priority(5) + .panel(panel) + .build(); + + clientToolbar.addNavigation(navButton); + } + + @Override + protected void shutDown() + { + clientToolbar.removeNavigation(navButton); + } + + @Provides + SuppliesTrackerConfig provideConfig(ConfigManager configManager) + { + return configManager.getConfig(SuppliesTrackerConfig.class); + } + + @Subscribe + public void onGameTick(GameTick tick) + { + Player player = client.getLocalPlayer(); + if (player.getAnimation() == BLOWPIPE_ATTACK) + { + ticks++; + } + if (ticks == ticksInAnimation && (player.getAnimation() == BLOWPIPE_ATTACK)) + { + double ava_percent = getAccumulatorPercent(); + double scale_percent = SCALES_PERCENT; + // randomize the usage of supplies since we CANNOT actually get real supplies used + if (random.nextDouble() <= ava_percent) + { + buildEntries(config.blowpipeAmmo().getDartID()); + + } + if (random.nextDouble() <= scale_percent) + { + buildEntries(ZULRAHS_SCALES); + } + ticks = 0; + } + } + + /** + * checks the player's cape slot to determine what percent of their darts are lost + * - where lost means either break or drop to floor + * @return the percent lost + */ + private double getAccumulatorPercent() + { + double percent = NO_AVAS_PERCENT; + ItemContainer equipment = client.getItemContainer(InventoryID.EQUIPMENT); + if (equipment.getItems().length > EQUIPMENT_CAPE_SLOT) + { + int capeID = equipment.getItems()[EQUIPMENT_CAPE_SLOT].getId(); + switch (capeID) + { + case AVAS_ASSEMBLER: + case ASSEMBLER_MAX_CAPE: + percent = ASSEMBLER_PERCENT; + break; + case AVAS_ACCUMULATOR: + case ACCUMULATOR_MAX_CAPE: + // TODO: the ranging cape can be used as an attractor so this could be wrong + case RANGING_CAPE: + percent = ACCUMULATOR_PERCENT; + break; + case AVAS_ATTRACTOR: + percent = ATTRACTOR_PERCENT; + break; + } + } + return percent; + } + + @Subscribe + public void onVarbitChanged(VarbitChanged event) + { + if (attackStyleVarbit == -1 || attackStyleVarbit != client.getVar(VarPlayer.ATTACK_STYLE)) + { + attackStyleVarbit = client.getVar(VarPlayer.ATTACK_STYLE); + if (attackStyleVarbit == 0 || attackStyleVarbit == 3) + { + ticksInAnimation = BLOWPIPE_TICKS_NORMAL_PVM; + if (client.getLocalPlayer() != null && + client.getLocalPlayer().getInteracting() instanceof Player) { + ticksInAnimation = BLOWPIPE_TICKS_NORMAL_PVP; + } + } + else if (attackStyleVarbit == 1) + { + ticksInAnimation = BLOWPIPE_TICKS_RAPID_PVM; + if (client.getLocalPlayer() != null && + client.getLocalPlayer().getInteracting() instanceof Player) { + ticksInAnimation = BLOWPIPE_TICKS_RAPID_PVP; + } + } + } + } + + /** + * Checks for changes between the provided inventories in runes specifically to add those runes + * to the supply tracker + * + * we can't in general just check for when inventory slots change but this method is only run + * immediately after the player performs a cast animation or cast menu click/entry + * @param itemContainer the new inventory + * @param oldInv the old inventory + */ + private void checkUsedRunes(ItemContainer itemContainer, Item[] oldInv) + { + for (int i = 0; i < itemContainer.getItems().length; i++) + { + Item newItem = itemContainer.getItems()[i]; + Item oldItem = oldInv[i]; + boolean isRune = false; + for (int j = 0; j < RUNE_IDS.length; j++) + { + if (oldItem.getId() == RUNE_IDS[j]) + { + isRune = true; + } + } + if (isRune && (newItem.getId() != oldItem.getId() || newItem.getQuantity() != oldItem.getQuantity())) + { + int quantity = oldItem.getQuantity(); + if (newItem.getId() == oldItem.getId()) + { + quantity -= newItem.getQuantity(); + } + buildEntries(oldItem.getId(), quantity); + } + } + } + + @Subscribe + public void onCannonballFired(CannonballFired cannonballFired) + { + buildEntries(CANNONBALL); + } + + @Subscribe + public void onAnimationChanged(AnimationChanged animationChanged) + { + if (animationChanged.getActor() == client.getLocalPlayer()) + { + if (animationChanged.getActor().getAnimation() == HIGH_LEVEL_MAGIC_ATTACK) + { + //Trident of the seas + if (mainHand == TRIDENT_OF_THE_SEAS || mainHand == TRIDENT_OF_THE_SEAS_E || mainHand == TRIDENT_OF_THE_SEAS_FULL ) + { + buildEntries(CHAOS_RUNE); + buildEntries(DEATH_RUNE); + buildEntries(FIRE_RUNE, 5); + buildEntries(COINS_995, 10); + } + //Trident of the swamp + else if (mainHand == TRIDENT_OF_THE_SWAMP_E || mainHand == TRIDENT_OF_THE_SWAMP || mainHand == UNCHARGED_TOXIC_TRIDENT_E || mainHand == UNCHARGED_TOXIC_TRIDENT) + { + buildEntries(CHAOS_RUNE); + buildEntries(DEATH_RUNE); + buildEntries(FIRE_RUNE, 5); + buildEntries(ZULRAHS_SCALES); + } + //Sang Staff + else if (mainHand == SANGUINESTI_STAFF || mainHand == SANGUINESTI_STAFF_UNCHARGED) + { + buildEntries(BLOOD_RUNE, 3); + } + else + { + old = client.getItemContainer(InventoryID.INVENTORY); + + if (old.getItems() != null && !actionStack.stream().anyMatch(a -> + a.getType() == CAST)) + { + MenuAction newAction = new MenuAction(CAST, old.getItems()); + actionStack.push(newAction); + } + } + } + else if (animationChanged.getActor().getAnimation() == LOW_LEVEL_MAGIC_ATTACK) + { + old = client.getItemContainer(InventoryID.INVENTORY); + + if (old.getItems() != null && !actionStack.stream().anyMatch(a -> + a.getType() == CAST)) + { + MenuAction newAction = new MenuAction(CAST, old.getItems()); + actionStack.push(newAction); + } + } + } + } + + @Subscribe + public void onItemContainerChanged(ItemContainerChanged itemContainerChanged) + { + ItemContainer itemContainer = itemContainerChanged.getItemContainer(); + + for (MenuAction action : actionStack) + { + System.out.println(action.getType()); + } + + if (itemContainer == client.getItemContainer(InventoryID.INVENTORY) && old != null && !actionStack.isEmpty()) + { + while (!actionStack.isEmpty()) + { + MenuAction frame = actionStack.pop(); + ActionType type = frame.getType(); + MenuAction.ItemAction itemFrame; + Item[] oldInv = frame.getOldInventory(); + switch (type) + { + case CONSUMABLE: + itemFrame = (MenuAction.ItemAction) frame; + int nextItem = itemFrame.getItemID(); + int nextSlot = itemFrame.getSlot(); + if (itemContainer.getItems()[nextSlot].getId() != oldInv[nextSlot].getId()) + { + buildEntries(nextItem); + } + break; + case TELEPORT: + itemFrame = (MenuAction.ItemAction) frame; + int teleid = itemFrame.getItemID(); + int slot = itemFrame.getSlot(); + if (itemContainer.getItems()[slot].getId() != oldInv[slot].getId() || itemContainer.getItems()[slot].getQuantity() != oldInv[slot].getQuantity()) + { + buildEntries(teleid); + } + break; + case CAST: + checkUsedRunes(itemContainer, oldInv); + break; + } + } + } + + if (itemContainer == client.getItemContainer(InventoryID.EQUIPMENT)) + { + //set mainhand for trident tracking + if (itemContainer.getItems().length > EQUIPMENT_MAINHAND_SLOT) + { + mainHand = itemContainer.getItems()[EQUIPMENT_MAINHAND_SLOT].getId(); + net.runelite.api.Item mainHandItem = itemContainer.getItems()[EQUIPMENT_MAINHAND_SLOT]; + for (int throwingIDs: THROWING_IDS) + { + if (mainHand == throwingIDs) + { + mainHandThrowing = true; + break; + } + else + { + mainHandThrowing = false; + } + } + if (mainHandThrowing) + { + if (throwingAmmoLoaded) + { + if (thrownId == mainHandItem.getId()) + { + if (thrownAmount - 1 == mainHandItem.getQuantity()) + { + buildEntries(mainHandItem.getId()); + thrownAmount = mainHandItem.getQuantity(); + } + else + { + thrownAmount = mainHandItem.getQuantity(); + } + } + else + { + thrownId = mainHandItem.getId(); + thrownAmount = mainHandItem.getQuantity(); + } + } + else + { + thrownId = mainHandItem.getId(); + thrownAmount = mainHandItem.getQuantity(); + throwingAmmoLoaded = true; + } + } + } + //Ammo tracking + if (itemContainer.getItems().length > EQUIPMENT_AMMO_SLOT) + { + net.runelite.api.Item ammoSlot = itemContainer.getItems()[EQUIPMENT_AMMO_SLOT]; + if (ammoSlot != null) + { + if (ammoLoaded) + { + if (ammoId == ammoSlot.getId()) + { + if (ammoAmount - 1 == ammoSlot.getQuantity()) + { + buildEntries(ammoSlot.getId()); + ammoAmount = ammoSlot.getQuantity(); + } + else + { + ammoAmount = ammoSlot.getQuantity(); + } + } + else + { + ammoId = ammoSlot.getId(); + ammoAmount = ammoSlot.getQuantity(); + } + } + else + { + ammoId = ammoSlot.getId(); + ammoAmount = ammoSlot.getQuantity(); + ammoLoaded = true; + } + } + } + + } + } + + @Subscribe + public void onMenuOptionClicked(final MenuOptionClicked event) + { + System.out.println(event.getMenuAction().getId()); + System.out.println(event.getActionParam()); + System.out.println(event.getMenuOption()); + System.out.println(event.getMenuTarget()); + + // Uses stacks to push/pop for tick eating + // Create pattern to find eat/drink at beginning + Pattern eatPattern = Pattern.compile(EAT_PATTERN); + Pattern drinkPattern = Pattern.compile(DRINK_PATTERN); + if (eatPattern.matcher(event.getMenuTarget().toLowerCase()).find() || drinkPattern.matcher(event.getMenuTarget().toLowerCase()).find()) + { + if (!actionStack.stream().anyMatch(a -> + { + if (a instanceof MenuAction.ItemAction) + { + MenuAction.ItemAction i = (MenuAction.ItemAction) a; + return i.getItemID() == event.getId(); + } + return false; + })) + { + old = client.getItemContainer(InventoryID.INVENTORY); + int slot = event.getActionParam(); + if (old.getItems() != null) + { + int pushItem = old.getItems()[event.getActionParam()].getId(); + MenuAction newAction = new MenuAction.ItemAction(CONSUMABLE, old.getItems(), pushItem, slot); + actionStack.push(newAction); + } + } + } + + // Create pattern for teleport scrolls and tabs + Pattern teleportPattern = Pattern.compile(TELEPORT_PATTERN); + Pattern teletabPattern = Pattern.compile(TELETAB_PATTERN); + if (teleportPattern.matcher(event.getMenuTarget().toLowerCase()).find() || + teletabPattern.matcher(event.getMenuTarget().toLowerCase()).find()) + { + old = client.getItemContainer(InventoryID.INVENTORY); + + // Makes stack only contains one teleport type to stop from adding multiple of one teleport + if (old.getItems() != null && !actionStack.stream().anyMatch(a -> + a.getType() == TELEPORT)) + { + int teleid = event.getId(); + MenuAction newAction = new MenuAction.ItemAction(TELEPORT, old.getItems(), teleid, event.getActionParam()); + actionStack.push(newAction); + } + } + + // Create pattern for spell cast + Pattern spellPattern = Pattern.compile(SPELL_PATTERN); + // note that here we look at the menuOption not menuTarget b/c the option for all spells is cast + // but the target differs based on each spell name + if (spellPattern.matcher(event.getMenuOption().toLowerCase()).find()) + { + old = client.getItemContainer(InventoryID.INVENTORY); + + if (old.getItems() != null && !actionStack.stream().anyMatch(a -> + a.getType() == CAST)) + { + MenuAction newAction = new MenuAction(CAST, old.getItems()); + actionStack.push(newAction); + } + } + } + + /** + * Checks if item name is potion + * @param name the name of the item + * @return if the item is a potion - i.e. has a (1) (2) (3) or (4) in the name + */ + static boolean isPotion(String name) + { + return name.contains("(4)") || name.contains("(3)") || name.contains("(2)") || name.contains("(1)"); + } + + /** + * Checks if item name is pizza or pie + * @param name the name of the item + * @return if the item is a pizza or a pie - i.e. has pizza or pie in the name + */ + static boolean isPizzaPie(String name) + { + return name.toLowerCase().contains("pizza") || name.toLowerCase().contains(" pie"); + } + + static boolean isCake(String name, int itemId) + { + return name.toLowerCase().contains("cake") || itemId == ItemID.CHOCOLATE_SLICE; + } + + /** + * correct prices for potions, pizzas pies, and cakes + * tracker tracks each dose of a potion/pizza/pie/cake as an entire one + * so must divide price by total amount of doses in each + * this is necessary b/c the most correct/accurate price for these resources is the + * full price not the 1-dose price + * @param name the item name + * @param itemId the item id + * @param price the current calculated price + * @return the price modified by the number of doses + */ + private long scalePriceByDoses(String name, int itemId, long price) + { + if (isPotion(name)) + { + return price / POTION_DOSES; + } + if (isPizzaPie(name)) + { + return price / PIZZA_PIE_DOSES; + } + if (isCake(name, itemId)) + { + return price / CAKE_DOSES; + } + return price; + } + + /** + * Add an item to the supply tracker (with 1 count for that item) + * @param itemId the id of the item + */ + void buildEntries(int itemId) + { + buildEntries(itemId, 1); + } + + /** + * Add an item to the supply tracker + * @param itemId the id of the item + * @param count the amount of the item to add to the tracker + */ + void buildEntries(int itemId, int count) + { + final ItemComposition itemComposition = itemManager.getItemComposition(itemId); + String name = itemComposition.getName(); + long calculatedPrice; + + for (String raidsConsumables: RAIDS_CONSUMABLES) + { + if (name.toLowerCase().contains(raidsConsumables)) return; + } + + // convert potions, pizzas/pies, and cakes to their full equivalents + // e.g. a half pizza becomes full pizza, 3 dose potion becomes 4, etc... + if (isPotion(name)) + { + name = name.replaceAll(POTION_PATTERN, "(4)"); + itemId = getPotionID(name); + } + if (isPizzaPie(name)) + { + itemId = getFullVersionItemID(itemId); + name = itemManager.getItemComposition(itemId).getName(); + } + if (isCake(name, itemId)) + { + itemId = getFullVersionItemID(itemId); + name = itemManager.getItemComposition(itemId).getName(); + } + + int newQuantity; + if (suppliesEntry.containsKey(itemId)) + { + newQuantity = suppliesEntry.get(itemId).getQuantity() + count; + } + else + { + newQuantity = count; + } + + // calculate price for amount of doses used + calculatedPrice = ((long) itemManager.getItemPrice(itemId)) * ((long) newQuantity); + calculatedPrice = scalePriceByDoses(name, itemId, calculatedPrice); + + // write the new quantity and calculated price for this entry + SuppliesTrackerItem newEntry = new SuppliesTrackerItem( + itemId, + name, + newQuantity, + calculatedPrice); + + suppliesEntry.put(itemId, newEntry); + SwingUtilities.invokeLater(() -> + { + panel.addItem(newEntry); + }); + } + + /** + * reset all item stacks + */ + public void clearSupplies() + { + suppliesEntry.clear(); + } + + /** + * reset an individual item stack + * @param itemId the id of the item stack + */ + public void clearItem(int itemId) + { + suppliesEntry.remove(itemId); + } + + /** + * Gets the item id that matches the provided name within the itemManager + * @param name the given name + * @return the item id for this name + */ + private int getPotionID(String name) + { + int itemId = 0; + + List items = itemManager.search(name); + for (ItemPrice item: items) + { + if (item.getName().contains(name)) + { + itemId = item.getId(); + } + } + return itemId; + } + + /** + * Takes the item id of a partial item (e.g. 1 dose potion, 1/2 a pizza, etc...) and returns + * the corresponding full item + * @param itemId the partial item id + * @return the full item id + */ + private int getFullVersionItemID(int itemId) + { + switch (itemId) + { + case _12_ANCHOVY_PIZZA: + itemId = ANCHOVY_PIZZA; + break; + case _12_MEAT_PIZZA: + itemId = MEAT_PIZZA; + break; + case _12_PINEAPPLE_PIZZA: + itemId = PINEAPPLE_PIZZA; + break; + case _12_PLAIN_PIZZA: + itemId = PLAIN_PIZZA; + break; + case HALF_A_REDBERRY_PIE: + itemId = REDBERRY_PIE; + break; + case HALF_A_GARDEN_PIE: + itemId = GARDEN_PIE; + break; + case HALF_A_SUMMER_PIE: + itemId = SUMMER_PIE; + break; + case HALF_A_FISH_PIE: + itemId = FISH_PIE; + break; + case HALF_A_BOTANICAL_PIE: + itemId = BOTANICAL_PIE; + break; + case HALF_A_MUSHROOM_PIE: + itemId = MUSHROOM_PIE; + break; + case HALF_AN_ADMIRAL_PIE: + itemId = ADMIRAL_PIE; + break; + case HALF_A_WILD_PIE: + itemId = WILD_PIE; + break; + case HALF_AN_APPLE_PIE: + itemId = APPLE_PIE; + break; + case HALF_A_MEAT_PIE: + itemId = MEAT_PIE; + break; + // note behavior of case means both below cases return CAKE + case _23_CAKE: + case SLICE_OF_CAKE: + itemId = CAKE; + break; + case _23_CHOCOLATE_CAKE: + case CHOCOLATE_SLICE: + itemId = CHOCOLATE_CAKE; + break; + } + return itemId; + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/templetrek/TempleTrekBogOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/templetrek/TempleTrekBogOverlay.java new file mode 100644 index 0000000000..119a70f245 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/templetrek/TempleTrekBogOverlay.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2018, Frosty Fridge + * 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.templetrek; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.Polygon; +import javax.inject.Inject; +import net.runelite.api.GroundObject; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.OverlayPriority; +import net.runelite.client.ui.overlay.OverlayUtil; + +public class TempleTrekBogOverlay extends Overlay +{ + private final TempleTrekConfig config; + private final TempleTrekPlugin plugin; + + private static final Color GREEN = new Color(0, 200, 83); + + @Inject + private TempleTrekBogOverlay(TempleTrekConfig config, TempleTrekPlugin plugin) + { + super(plugin); + this.config = config; + this.plugin = plugin; + setPosition(OverlayPosition.DYNAMIC); + setPriority(OverlayPriority.LOW); + } + + @Override + public Dimension render(Graphics2D graphics) + { + if (config.bogMapActive()) + { + for (GroundObject bog : plugin.getBogList()) + { + Polygon bogPoly = bog.getCanvasTilePoly(); + OverlayUtil.renderPolygon(graphics, bogPoly, GREEN); + } + } + return null; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/templetrek/TempleTrekConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/templetrek/TempleTrekConfig.java new file mode 100644 index 0000000000..090d1a9cab --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/templetrek/TempleTrekConfig.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2018, Frosty Fridge + * 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.templetrek; + +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("templetrek") +public interface TempleTrekConfig extends Config +{ + @ConfigItem( + keyName = "bogMapActive", + name = "Bog Map", + description = "Marks out a safe route through the bog event", + position = 0 + ) + default boolean bogMapActive() + { + return true; + } + + @ConfigItem( + keyName = "pointTrackerActive", + name = "Point Tracker", + description = "Track your Temple Trek reward points, which determine the size of your reward.", + position = 1 + ) + default boolean pointTrackerActive() + { + return true; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/templetrek/TempleTrekOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/templetrek/TempleTrekOverlay.java new file mode 100644 index 0000000000..afbf4c88a5 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/templetrek/TempleTrekOverlay.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2018, Frosty Fridge + * 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.templetrek; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import javax.inject.Inject; +import net.runelite.client.ui.overlay.Overlay; +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 TempleTrekOverlay extends Overlay +{ + private final TempleTrekConfig config; + private final TempleTrekPlugin plugin; + + private final PanelComponent panelComponent = new PanelComponent(); + + @Inject + private TempleTrekOverlay(TempleTrekConfig config, TempleTrekPlugin plugin) + { + super(plugin); + this.config = config; + this.plugin = plugin; + setPosition(OverlayPosition.TOP_LEFT); + setPriority(OverlayPriority.LOW); + } + + @Override + public Dimension render(Graphics2D graphics) + { + if (config.pointTrackerActive() && plugin.isInTrek()) + { + int points = plugin.getRewardPoints(); + double percentage = plugin.getRewardPercentage() * 100; + panelComponent.getChildren().clear(); + panelComponent.getChildren().add(LineComponent.builder() + .left("Trek Points: ") + .right(Integer.toString(points)) + .rightColor(percentage < 25 ? Color.RED : percentage >= 25 && percentage < 50 ? Color.YELLOW : + percentage >= 50 && percentage < 75 ? Color.BLUE : Color.GREEN) + .build()); + panelComponent.getChildren().add(LineComponent.builder() + .left("Reward %: ") + .right(String.format("%.2f", percentage) + "%") + .rightColor(percentage < 25 ? Color.RED : percentage >= 25 && percentage < 50 ? Color.YELLOW : + percentage >= 50 && percentage < 75 ? Color.BLUE : Color.GREEN) + .build()); + return panelComponent.render(graphics); + } + return null; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/templetrek/TempleTrekPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/templetrek/TempleTrekPlugin.java new file mode 100644 index 0000000000..99fd1d4286 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/templetrek/TempleTrekPlugin.java @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2018, Frosty Fridge + * 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.templetrek; + +import com.google.inject.Provides; +import java.util.HashSet; +import java.util.Set; +import javax.inject.Inject; +import lombok.Getter; +import net.runelite.api.Client; +import net.runelite.api.GroundObject; +import net.runelite.api.ObjectID; +import net.runelite.api.Varbits; +import net.runelite.api.events.GroundObjectSpawned; +import net.runelite.api.events.VarbitChanged; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.ui.overlay.OverlayManager; + +@PluginDescriptor( + name = "Temple Trekking", + description = "Helpers for the Temple Trek minigame", + tags = {"minigame", "overlay", "temple trek"} +) +public class TempleTrekPlugin extends Plugin +{ + + @Inject + private Client client; + + @Inject + private OverlayManager overlayManager; + + @Inject + private TempleTrekOverlay overlay; + + @Inject + private TempleTrekBogOverlay bogOverlay; + + @Inject + private TempleTrekConfig config; + + @Getter + private final Set bogList = new HashSet(); + + @Getter + private boolean inTrek = false; + + @Provides + TempleTrekConfig getConfig(ConfigManager configManager) + { + return configManager.getConfig(TempleTrekConfig.class); + } + + @Override + protected void startUp() + { + overlayManager.add(overlay); + overlayManager.add(bogOverlay); + } + + @Override + protected void shutDown() + { + overlayManager.remove(overlay); + overlayManager.remove(bogOverlay); + bogList.clear(); + } + + @Subscribe + public void onGroundObjectSpawned(GroundObjectSpawned event) + { + GroundObject obj = event.getGroundObject(); + if (obj.getId() == ObjectID.BOG) + { + bogList.add(obj); + } + } + + //onGroundObjectDespawned is having issues handling this, so bogmap removal is here instead. + @Subscribe + public void onVarbitChanged(VarbitChanged event) + { + if (!bogList.isEmpty() && client.getVar(Varbits.TREK_EVENT) == 0) + { + bogList.clear(); + } + if (!inTrek && client.getVar(Varbits.TREK_STARTED) == 1) + { + inTrek = true; + } + else if (inTrek) + { + if (client.getVar(Varbits.TREK_STATUS) == 0 && client.getVar(Varbits.TREK_POINTS) == 0) + { + inTrek = false; + } + } + } + + protected int getRewardPoints() + { + return client.getVar(Varbits.TREK_POINTS); + } + + protected double getRewardPercentage() + { + double percentage = 0.000126945 * getRewardPoints() - 0.0357188951; + return Math.max(percentage, 0); + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/tickcounter/TickCounterConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/tickcounter/TickCounterConfig.java new file mode 100644 index 0000000000..79e90dc38d --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/tickcounter/TickCounterConfig.java @@ -0,0 +1,71 @@ +package net.runelite.client.plugins.tickcounter; + +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +import java.awt.*; + +@ConfigGroup("tickcounter") +public interface TickCounterConfig extends Config { + @ConfigItem( + keyName = "resetInstance", + name = "Reset on new instances", + description = "", + position = 1 + ) + default boolean instance() + { + return true; + } + @ConfigItem( + keyName = "selfColor", + name = "Your color", + description = "", + position = 4 + ) + default Color selfColor() + { + return Color.green; + } + @ConfigItem( + keyName = "totalColor", + name = "Total color", + description = "", + position = 6 + ) + default Color totalColor() + { + return Color.RED; + } + @ConfigItem( + keyName = "otherColor", + name = "Other players color", + description = "", + position = 5 + ) + default Color otherColor() + { + return Color.white; + } + @ConfigItem( + keyName = "bgColor", + name = "Background color", + description = "", + position = 3 + ) + default Color bgColor() + { + return new Color(70, 61, 50, 156); + } + @ConfigItem( + keyName = "titleColor", + name = "Title color", + description = "", + position = 2 + ) + default Color titleColor() + { + return Color.white; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/tickcounter/TickCounterOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/tickcounter/TickCounterOverlay.java new file mode 100644 index 0000000000..33e22fe1fa --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/tickcounter/TickCounterOverlay.java @@ -0,0 +1,69 @@ +package net.runelite.client.plugins.tickcounter; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Map.Entry; + +import javax.inject.Inject; + +import net.runelite.api.Client; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.components.LayoutableRenderableEntity; +import net.runelite.client.ui.overlay.components.LineComponent; +import net.runelite.client.ui.overlay.components.PanelComponent; +import net.runelite.client.ui.overlay.components.TitleComponent; + +public class TickCounterOverlay extends Overlay { + + private TickCounterPlugin plugin; + private TickCounterConfig config; + private Client client; + private PanelComponent panelComponent = new PanelComponent(); + + @Inject + public TickCounterOverlay(TickCounterPlugin plugin,Client client,TickCounterConfig config) { + super(plugin); + setPosition(OverlayPosition.DYNAMIC); + setPosition(OverlayPosition.DETACHED); + setPosition(OverlayPosition.BOTTOM_RIGHT); + this.plugin = plugin; + this.client = client; + this.config = config; + } + + @Override + public Dimension render(Graphics2D g) { + List elems = panelComponent.getChildren(); + elems.clear(); + panelComponent.setBackgroundColor(config.bgColor()); + elems.add(TitleComponent.builder().text("Combat counter").color(config.titleColor()).build()); + List> list = new ArrayList<>(plugin.activity.entrySet()); + list.sort(new Comparator>() { + @Override + public int compare(Entry o1, Entry o2) { + int value = -Integer.compare(o1.getValue(), o2.getValue()); + if (value == 0) + value = o1.getKey().compareTo(o2.getKey()); + return value; + } + }); + int total = 0; + for (Entry e : list) { + total += e.getValue(); + if(e.getKey().equals(client.getLocalPlayer().getName())){ + elems.add(LineComponent.builder().leftColor(config.selfColor()).rightColor(config.selfColor()).left(e.getKey()).right(e.getValue().toString()).build()); + }else{ + elems.add(LineComponent.builder().left(e.getKey()).right(e.getValue().toString()).leftColor(config.otherColor()).rightColor(config.otherColor()).build()); + + } + } + elems.add(LineComponent.builder().left("Total").leftColor(config.totalColor()).rightColor(config.totalColor()).right(String.valueOf(total)).build()); + return this.panelComponent.render(g); + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/tickcounter/TickCounterPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/tickcounter/TickCounterPlugin.java new file mode 100644 index 0000000000..fe2130285c --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/tickcounter/TickCounterPlugin.java @@ -0,0 +1,193 @@ +package net.runelite.client.plugins.tickcounter; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.inject.Inject; + +import com.google.inject.Provides; +import net.runelite.api.Actor; +import net.runelite.api.Client; +import net.runelite.api.Player; +import net.runelite.api.events.AnimationChanged; +import net.runelite.api.events.ClientTick; +import net.runelite.api.events.GameTick; +import net.runelite.api.kit.KitType; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.ui.overlay.OverlayManager; + +@PluginDescriptor(name = "!Tick Counter", + description = "Counts combat activity for nearby players", + enabledByDefault = false +) +public class TickCounterPlugin extends Plugin { + + @Inject + private OverlayManager overlayManager; + @Inject + private TickCounterConfig config; + @Inject + private Client client; + @Provides + TickCounterConfig provideConfig(ConfigManager configManager) + { + return configManager.getConfig(TickCounterConfig.class); + } + @Inject + private TickCounterOverlay overlay; + + Map activity = new HashMap<>(); + + private List blowpiping = new ArrayList<>(); + boolean instanced = false; + boolean prevInstance = false; + + @Override + protected void startUp() throws Exception { + overlayManager.add(overlay); + } + + @Override + protected void shutDown() throws Exception { + overlayManager.remove(overlay); + activity.clear(); + } + + @Subscribe + public void onAnimationChanged(AnimationChanged e) { + if (!(e.getActor() instanceof Player)) + return; + Player p = (Player) e.getActor(); + int weapon = -1; + if (p.getPlayerComposition() != null) + weapon = p.getPlayerComposition().getEquipmentId(KitType.WEAPON); + int delta = 0; + switch (p.getAnimation()) { + case 7617: // rune knife + case 8194: // dragon knife + case 8291: // dragon knife spec + case 5061: // blowpipe + if (weapon == 12926) + { + blowpiping.add(p); + } + else + { + delta = 2; + } + break; + case 2323: // rpg + case 7618: // chin + delta = 3; + break; + case 426: // bow shoot + if (weapon == 20997) // twisted bow + delta = 5; + else // shortbow + delta = 3; + break; + case 376: // dds poke + case 377: // dds slash + case 422: // punch + case 423: // kick + case 386: // lunge + case 390: // generic slash + case 1062: // dds spec + case 1067: // claw stab + case 1074: // msb spec + case 1167: // trident cast + case 1658: // whip + case 2890: // arclight spec + case 3294: // abby dagger slash + case 3297: // abby dagger poke + case 3298: // bludgeon attack + case 3299: // bludgeon spec + case 3300: // abby dagger spec + case 7514: // claw spec + case 7515: // d sword spec + case 8145: // rapier stab + case 8288: // dhl stab + case 8289: // dhl slash + case 8290: // dhl crush + delta = 4; + break; + case 393: // staff bash + if (weapon == 13652) { // claw scratch + delta = 4; + break; + } + case 395: // axe autos + case 400: // pick smash + case 1379: //burst or blitz + case 1979: // barrage spell cast + case 1162: // strike/bolt spells + case 7552: // generic crossbow + case 7855: // surge spells + case 8056: // scythe swing + delta = 5; + break; + case 401: + if (weapon == 13576) // dwh bop + delta = 6; + else // used by pickaxe and axe + delta = 5; + break; + case 1378: + case 7045: + case 7054: + case 7055: // godsword autos + case 7511: // dinh's attack + case 7516: // maul attack + case 7555: // ballista attack + case 7638: // zgs spec + case 7640: // sgs spec + case 7642: // bgs spec + case 7643: // bgs spec + case 7644: // ags spec + delta = 6; + break; + case 428: // chally swipe + case 440: // chally jab + case 1203: // chally spec + delta = 7; + break; + case -1: + blowpiping.remove(p); + break; + } + if (delta > 0) { + String name = p.getName(); + this.activity.put(name, this.activity.getOrDefault(name, 0) + delta); + } + } + + @Subscribe + public void onClientTick(ClientTick e) { + /* + * Hack for blowpipe since the AnimationChanged event doesn't fire when using a + * blowpipe because of its speed. If blowpipe animation restarts, then add 2 + */ + for (Player p : blowpiping) { + Actor rsp = p; + if (rsp.getActionFrame() == 0 && rsp.getActionFrameCycle() == 1) { + String name = p.getName(); + int activity = this.activity.getOrDefault(name, 0).intValue(); + this.activity.put(name, activity + 2); + } + } + } + @Subscribe + public void onGameTick(GameTick tick){ + if(!config.instance())return; + prevInstance = instanced; + instanced = client.isInInstancedRegion(); + if(!prevInstance && instanced){ + activity.clear(); + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/OverviewItemPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/OverviewItemPanel.java index 96cf62c6fc..520dcb0f3b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/OverviewItemPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/OverviewItemPanel.java @@ -50,7 +50,7 @@ class OverviewItemPanel extends JPanel static { - ARROW_RIGHT_ICON = new ImageIcon(ImageUtil.getResourceStreamFromClass(TimeTrackingPlugin.class, "/util/arrow_right.png")); + ARROW_RIGHT_ICON = new ImageIcon(ImageUtil.getResourceStreamFromClass(TimeTrackingPlugin.class, "/net/runelite/client/plugins/timetracking/arrow_right.png")); } OverviewItemPanel(ItemManager itemManager, TimeTrackingPanel pluginPanel, Tab tab, String title) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TimeTrackingPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TimeTrackingPlugin.java index b49e461025..9279883311 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TimeTrackingPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TimeTrackingPlugin.java @@ -111,7 +111,7 @@ public class TimeTrackingPlugin extends Plugin birdHouseTracker.loadFromConfig(); farmingTracker.loadCompletionTimes(); - final BufferedImage icon = ImageUtil.getResourceStreamFromClass(getClass(), "watch.png"); + final BufferedImage icon = ImageUtil.getResourceStreamFromClass(getClass(), "/net/runelite/client/plugins/timetracking/watch.png"); panel = new TimeTrackingPanel(itemManager, config, farmingTracker, birdHouseTracker, clockManager); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/tobdamagecount/DamageCounterPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/tobdamagecount/DamageCounterPlugin.java new file mode 100644 index 0000000000..d7cda5adff --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/tobdamagecount/DamageCounterPlugin.java @@ -0,0 +1,356 @@ +/* + * Copyright (c) 2018, Bryan Chau(RSN:Laura Brehm) + * 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.tobdamagecount; + +import javax.inject.Inject; +import java.text.DecimalFormat; + +import net.runelite.api.Client; +import net.runelite.api.GameState; +import net.runelite.api.NPC; +import net.runelite.api.NpcID; +import net.runelite.api.Skill; +import net.runelite.api.ChatMessageType; +import net.runelite.api.Actor; +import net.runelite.api.Player; +import net.runelite.api.coords.WorldPoint; +import net.runelite.api.events.HitsplatApplied; +import net.runelite.api.events.GameTick; +import net.runelite.api.events.GameStateChanged; +import net.runelite.api.events.NpcDespawned; +import net.runelite.api.events.LocalPlayerDeath; +import net.runelite.client.chat.ChatColorType; +import net.runelite.client.chat.ChatMessageBuilder; +import net.runelite.client.chat.ChatMessageManager; +import net.runelite.client.chat.QueuedMessage; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.eventbus.Subscribe; + + +@PluginDescriptor( + name = "ToB Damage Counter", + description = "Gives you an estimation damage on a boss and taken after the fight is done" + + "the damage will be posted in the chat", + tags = {"combat", "npcs", "tob", "damage"}, + enabledByDefault = false +) + +public class DamageCounterPlugin extends Plugin +{ + private int currentWorld = -1; + private int DamageCount = 0; + private int currenthpxp = -1; // checking the current hp xp so be easier to find + private String BossName = null; //to ID the boss to calculate the damage + private int DamageTaken = 0; + private boolean status = true; //default boolean alive = true, dead = false + //formatting the number for damage taken and dealt with to look beeter + private static final DecimalFormat DAMAGEFORMAT = new DecimalFormat("#,###"); + private static final double XP_RATIO = 1.3333; + private static final double BOSS_MODIFIER = 1.05; + private static final int MAIDENHP = 3500; + private static final int BLOATHP = 2000; + private static final int NYLOHP = 2500; + private static final int SOTHP = 4000; + private static final int XARPUSHP = 5080; + private static final int VERZIKHP = 8500; + private static final boolean ALIVE = true; // + private static final boolean DEAD = false; //if they're dead they cannot "recreate" the message of being alive + //locations at ToB + private static final int MAIDEN_REGION = 12613; + private static final int MAIDEN_REGION_1 = 12869; + private static final int BLOAT_REGION = 13125; + private static final int NYLOCAS_REGION = 13122; + private static final int SOTETSEG_REGION = 13123; + private static final int SOTETSEG_REGION2 = 13379; + private static final int XARPUS_REGION = 12612; + private static final int VERZIK_REGION = 12611; + private static final int[] ToB_Region = {MAIDEN_REGION, MAIDEN_REGION_1, BLOAT_REGION, NYLOCAS_REGION, + SOTETSEG_REGION, SOTETSEG_REGION2, XARPUS_REGION, VERZIK_REGION}; + //setting up the array for a check list + private static int[] NPCARRAY = {NpcID.THE_MAIDEN_OF_SUGADINTI, NpcID.THE_MAIDEN_OF_SUGADINTI_8361, + NpcID.THE_MAIDEN_OF_SUGADINTI_8362, NpcID.THE_MAIDEN_OF_SUGADINTI_8363, NpcID.THE_MAIDEN_OF_SUGADINTI_8364, + NpcID.THE_MAIDEN_OF_SUGADINTI_8365, NpcID.PESTILENT_BLOAT, NpcID.NYLOCAS_VASILIAS, + NpcID.NYLOCAS_VASILIAS_8355, NpcID.NYLOCAS_VASILIAS_8356, NpcID.NYLOCAS_VASILIAS_8357, NpcID.SOTETSEG, + NpcID.SOTETSEG_8388, NpcID.XARPUS, NpcID.XARPUS_8339, NpcID.XARPUS_8340, NpcID.XARPUS_8341, + NpcID.VERZIK_VITUR, NpcID.VERZIK_VITUR_8369, NpcID.VERZIK_VITUR_8370, NpcID.VERZIK_VITUR_8371, + NpcID.VERZIK_VITUR_8372, NpcID.VERZIK_VITUR_8373, NpcID.VERZIK_VITUR_8374, NpcID.VERZIK_VITUR_8375}; + + private int[] HEALTHARRAY = {MAIDENHP, NYLOHP, VERZIKHP}; + + @Inject + private Client client; + @Inject + private ChatMessageManager chatMessangerManager; + //every game tick it will go through methods + @Subscribe + private void onGameTick(GameTick tick) + { + if (client.getGameState() != GameState.LOGGED_IN) + { + ResetCounter(); + return; + } + checkInterAction(); + DamageCounting(); + currenthpxp = client.getSkillExperience(Skill.HITPOINTS); + } + //checks for npcID and put the boss name into a string be easier to ID it + //once the boss is found it will never check it + private void checkInterAction() + { + Player localPlayer = client.getLocalPlayer(); + Actor interacting = localPlayer.getInteracting(); + if (client.getGameState() == GameState.LOGGED_IN) + { + if (BossName == null) + { + if (interacting instanceof NPC) + { + int interactingId = ((NPC) interacting).getId(); + String interactingName = interacting.getName(); + for (int aNPCARRAY : NPCARRAY) + { + if (aNPCARRAY == interactingId) + { + BossName = interactingName; + } + } + } + } + } + } + + @Subscribe + //if you hop it will reset the counter + public void onGameStateChanged(GameStateChanged event) + { + if (event.getGameState() == GameState.LOGGED_IN) + { + if (currentWorld == -1) + { + currentWorld = client.getWorld(); + } + else if (currentWorld != client.getWorld()) + { + currentWorld = client.getWorld(); + ResetCounter(); + } + } + } + + //grabbing the xp and calculating the damage + private int XPtoDamage() + { + int NewXp; + double damageOutput = 0; + int XPdrop; + if (currenthpxp != -1) + { + XPdrop = client.getSkillExperience(Skill.HITPOINTS); + NewXp = XPdrop - currenthpxp; + currenthpxp = -1; + damageOutput = NewXp / XP_RATIO; + } + //returns the damage you have done + return (int) Math.floor(damageOutput); + } + + //adding up the damage for the print message checks every tick(aka attack tick) + private void DamageCounting() + { + Player localPlayer = client.getLocalPlayer(); + Actor interacting = localPlayer.getInteracting(); + if (client.getGameState() == GameState.LOGGED_IN) + { + if (interacting instanceof NPC) + { + String interactingName = interacting.getName(); + int NPC = ((NPC) interacting).getId(); + if (interactingName.equals(BossName)) + { + DamageCount += (XPtoDamage() * BOSS_MODIFIER); + + } + } + } + } + + + @Subscribe + //will add the damage that you have taken from the current boss fight + private void onHitsplatApplied(HitsplatApplied Hit) + { + if (Hit.getActor().equals(client.getLocalPlayer())) + { + DamageTaken += Hit.getHitsplat().getAmount(); + } + + } + + //will check for the monster if it died works only on ToB Bosses + /*Verzik has three phases so the program will add up all the damage and prints it into one message + because every time she phases she "dies" so making sure the counter doesn't print out the damage for phase 1, 2, + and 3. + */ + @Subscribe + public void onNpcDespawned(NpcDespawned npc) + { + NPC actor = npc.getNpc(); + double Percent = calculatePercent(WorldPoint.fromLocalInstance(client, + client.getLocalPlayer().getLocalLocation()).getRegionID()); + if (actor.isDead() && actor.getId() == NpcID.VERZIK_VITUR_8375 && status) + { + DamagePrint(actor, Percent); + ResetCounter(); + } + else if (actor.isDead() && actor.getName().equals(BossName) && actor.getId() != NpcID.VERZIK_VITUR_8374 && + actor.getId() != NpcID.VERZIK_VITUR_8372 && actor.getId() != NpcID.VERZIK_VITUR_8370 && + status) + { + DamagePrint(actor, Percent); + ResetCounter(); + } + //will reset the counter after the boss dies and if you died during the fight + else if (actor.isDead() && actor.getName().equals(BossName) && !status) + { + ResetCounter(); + } + } + + private double calculatePercent(int id) + { + double percent = 0; + if (DamageCount != 0) { + if (id == MAIDEN_REGION || id == MAIDEN_REGION_1) + { + percent = (DamageCount / (double) MAIDENHP) * 100; + } + else if (id == BLOAT_REGION) + { + percent = (DamageCount / (double) BLOATHP) * 100; + } + else if (id == NYLOCAS_REGION) + { + percent = (DamageCount / (double) NYLOHP) * 100; + } + else if (id == SOTETSEG_REGION || id == SOTETSEG_REGION2) + { + percent = (DamageCount / (double) SOTHP) * 100; + } + else if (id == XARPUS_REGION) + { + percent = (DamageCount / (double) XARPUSHP) * 100; + } + else if (id == VERZIK_REGION) + { + percent = (DamageCount / (double) VERZIKHP) * 100; + } + } + return percent; + } + + //just reset the counter for the next fight and status + private void ResetCounter() + { + DamageCount = 0; + DamageTaken = 0; + BossName = null; + status = ALIVE; + } + + //print out the damage after the boss have died + //prevent people from spectating to get the damage message, it is impossible for them to get damage + private void DamagePrint(NPC actor, double percent) + { + String MessageDamage; + if (percent >= 50) + { + MessageDamage = "Well done carrying the team!" + + "WOWIE!! You did" + DAMAGEFORMAT.format(DamageCount) + " damage to " + + actor.getName() + "! You did %" + String.format("%.2f", percent) + " of the damage"; + } + else if (percent >= 25) + { + MessageDamage = "Well done carrying some dead weight in your team! " + + "Awesome! You did " + DAMAGEFORMAT.format(DamageCount) + " damage to " + + actor.getName() + "! You did %" + String.format("%.2f", percent) + " of the damage"; + } + else if (percent >= 1) + { + MessageDamage = "Well done everyone is pulling their weight! " + + "You did " + DAMAGEFORMAT.format(DamageCount) + " damage to " + + actor.getName() + "! You did %" + String.format("%.2f", percent) + " of the damage"; + } + else + { + MessageDamage = "Didn't do much" + + "Fucking leech did " + DAMAGEFORMAT.format(DamageCount) + " damage to " + + actor.getName() + "! You did %" + String.format("%.2f", percent) + " of the damage"; + } + + sendChatMessage(MessageDamage); + String MessageTaken = "You have taken " + DAMAGEFORMAT.format(DamageTaken) + " damage from this fight!"; + sendChatMessage(MessageTaken); + } + + @Subscribe + //whenever you have died in tob you will get a death message with damage + // made sure the message works at ToB area or else it will message every where + private void onLocalPlayerDeath(LocalPlayerDeath death) + { + String DeathMessage = "You have died! You did " + DAMAGEFORMAT.format(DamageCount) + " damage to " + + BossName + "!"; + String MessageTaken = "You have taken " + DAMAGEFORMAT.format(DamageTaken) + " damage from this fight!"; + for (int i = 0; i < ToB_Region.length; i++) + { + if (WorldPoint.fromLocalInstance(client, + client.getLocalPlayer().getLocalLocation()).getRegionID() == ToB_Region[i]) + { + sendChatMessage(DeathMessage); + sendChatMessage(MessageTaken); + ResetCounter(); + //status will become "dead" after you died in the fight + status = DEAD; + } + } + } + + //sends a message saying this "You have done XYZ damage to boss name! or the death message + // "Well done! you have done your best, you have done XYZ damage to boss name + private void sendChatMessage(String chatMessage) + { + final String message = new ChatMessageBuilder() + .append(ChatColorType.HIGHLIGHT) + .append(chatMessage) + .build(); + chatMessangerManager.queue( + QueuedMessage.builder() + .type(ChatMessageType.CONSOLE) + .runeLiteFormattedMessage(message) + .build()); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/vetion/VetionConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/vetion/VetionConfig.java new file mode 100644 index 0000000000..b97e73b318 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/vetion/VetionConfig.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2019, Frosty Fridge + * 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.vetion; + +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("vetion") +public interface VetionConfig extends Config +{ + @ConfigItem( + keyName = "earthquakeTimerActive", + name = "Vet'ion Earthquake Timer", + description = "Configures whether or not a timer is shown to track the cooldown of Vet'ion's earthquake attack", + position = 0 + ) + default boolean eartquakeTimerActive() + { + return true; + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/vetion/VetionOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/vetion/VetionOverlay.java new file mode 100644 index 0000000000..dcc51b0929 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/vetion/VetionOverlay.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2019, Frosty Fridge + * 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.vetion; + +import net.runelite.api.Client; +import net.runelite.api.Perspective; +import net.runelite.api.Point; +import net.runelite.api.coords.LocalPoint; +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.components.ProgressPieComponent; + +import javax.inject.Inject; +import java.awt.*; +import java.time.Duration; +import java.time.Instant; + +public class VetionOverlay extends Overlay{ + + private static final Color RED_ALPHA = new Color(Color.RED.getRed(), Color.RED.getGreen(), Color.RED.getBlue(), 100); + private static final Duration MAX_TIME = Duration.ofSeconds(9); + private final VetionPlugin plugin; + private Client client; + + @Inject + private VetionOverlay(Client client, VetionPlugin plugin) + { + this.plugin = plugin; + this.client = client; + setPosition(OverlayPosition.DYNAMIC); + setLayer(OverlayLayer.ABOVE_SCENE); + } + + @Override + public Dimension render(Graphics2D graphics) + { + plugin.getVetions().forEach((actor, timer) -> + { + LocalPoint localPos = actor.getLocalLocation(); + if (localPos != null) + { + Point position = Perspective.localToCanvas(client, localPos, client.getPlane(), + actor.getLogicalHeight() + 96); + if (position != null) + { + position = new Point(position.getX(), position.getY()); + + final ProgressPieComponent progressPie = new ProgressPieComponent(); + progressPie.setDiameter(30); + progressPie.setFill(RED_ALPHA); + progressPie.setBorderColor(Color.RED); + progressPie.setPosition(position); + + final Duration duration = Duration.between(timer, Instant.now()); + progressPie.setProgress(1 - (duration.compareTo(MAX_TIME) < 0 + ? (double) duration.toMillis() / MAX_TIME.toMillis() + : 1)); + + progressPie.render(graphics); + if (1 - duration.compareTo(MAX_TIME) < 0) + { + plugin.getVetions().remove(actor); + } + } + } + }); + + return null; + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/vetion/VetionPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/vetion/VetionPlugin.java new file mode 100644 index 0000000000..9df9a4fa57 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/vetion/VetionPlugin.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2019, Frosty Fridge + * 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.vetion; + +import com.google.inject.Provides; +import lombok.Getter; +import net.runelite.api.*; +import net.runelite.api.events.AnimationChanged; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.ui.overlay.OverlayManager; + +import javax.inject.Inject; +import java.time.Instant; +import java.util.HashMap; +import java.util.Map; + +@PluginDescriptor( + name = "!Vetion", + description = "Tracks Vet'ion's special attacks", + tags = {"bosses", "combat", "pve", "overlay"} +) +public class VetionPlugin extends Plugin { + + @Inject + private Client client; + + @Inject + private VetionConfig config; + + @Inject + private OverlayManager overlayManager; + + @Inject + private VetionOverlay overlay; + + @Getter + private Map vetions; + + @Provides + VetionConfig getConfig(ConfigManager configManager) + { + return configManager.getConfig(VetionConfig.class); + } + + @Override + protected void startUp() + { + vetions = new HashMap<>(); + overlayManager.add(overlay); + } + + @Override + protected void shutDown() + { + overlayManager.remove(overlay); + vetions = null; + } + + + @Subscribe + public void onAnimationChanged(AnimationChanged event) + { + if (config.eartquakeTimerActive() && event.getActor().getAnimation() == AnimationID.VETION_EARTHQUAKE) + { + Actor vet = event.getActor(); + vetions.remove(vet, Instant.now()); + vetions.put(vet, Instant.now()); + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/vorkath/TileHighlight.java b/runelite-client/src/main/java/net/runelite/client/plugins/vorkath/TileHighlight.java new file mode 100644 index 0000000000..c6e5623450 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/vorkath/TileHighlight.java @@ -0,0 +1,8 @@ +package net.runelite.client.plugins.vorkath; + +public enum TileHighlight +{ + None, + Single, + All +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/vorkath/VorkathConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/vorkath/VorkathConfig.java new file mode 100644 index 0000000000..927b9b1997 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/vorkath/VorkathConfig.java @@ -0,0 +1,51 @@ +package net.runelite.client.plugins.vorkath; + +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +import java.awt.*; + +@ConfigGroup("vorkath") +public interface VorkathConfig extends Config { + @ConfigItem( + position = 0, + keyName = "Vorkathenable", + name = "Enable Vorkath Helper", + description = "Configures whether or not to enable Vorkath Helper." + ) + default boolean EnableVorkath() { return true; } + + @ConfigItem( + position = 1, + keyName = "countercolor", + name = "Indicator color", + description = "Configures color of text displaying Vorkath hits left to special attack." + ) + default Color CounterColor() { return Color.YELLOW; } + + @ConfigItem( + position = 2, + keyName = "countersize", + name = "Bold indicator", + description = "Configures if text indicator is bold or not." + ) + default boolean BoldText() { return true; } + + @ConfigItem( + position = 3, + keyName = "enumConfig", + name = "Fireball Tile Highlight", + description = "Select how to apply tile highlighting for Vorkath's fireball attack" + ) + default TileHighlight TileHighlight() { return TileHighlight.All; } + + @ConfigItem( + position = 4, + keyName = "overlayindicators", + name = "Overlay Indicators", + description = "Configures if an overlay box displaying vorkath information should be displayed." + ) + default boolean VorkathBox() { return false; } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/vorkath/VorkathIndicatorOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/vorkath/VorkathIndicatorOverlay.java new file mode 100644 index 0000000000..77403dab97 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/vorkath/VorkathIndicatorOverlay.java @@ -0,0 +1,53 @@ +package net.runelite.client.plugins.vorkath; + +import java.awt.*; +import javax.inject.Inject; + +import net.runelite.api.*; +import net.runelite.api.coords.LocalPoint; +import net.runelite.api.coords.WorldArea; +import net.runelite.client.ui.FontManager; +import net.runelite.client.ui.overlay.*; +import net.runelite.client.ui.overlay.components.LineComponent; +import net.runelite.client.ui.overlay.components.PanelComponent; + +public class VorkathIndicatorOverlay extends Overlay { + private final VorkathConfig config; + private final VorkathPlugin plugin; + private final PanelComponent panelComponent = new PanelComponent(); + + + @Inject + private Client client; + + @Inject + private VorkathIndicatorOverlay(VorkathConfig config, VorkathPlugin plugin) { + this.config = config; + this.plugin = plugin; + setPosition(OverlayPosition.BOTTOM_RIGHT); + setPriority(OverlayPriority.MED); + panelComponent.setPreferredSize(new Dimension(150, 0)); + } + + @Override + public Dimension render(Graphics2D graphics) { + if (!config.EnableVorkath()) { + return null; + } + + NPC Vorkath = plugin.Vorkath; + if (Vorkath != null) { + if (config.VorkathBox()) { + panelComponent.getChildren().clear(); + if (plugin.venomticks != 0) { + if (plugin.venomticks + 5 <= plugin.ticks) { + panelComponent.getChildren().add(LineComponent.builder().left("Quickfire Barrage:").right(Integer.toString(30 - (plugin.ticks - plugin.venomticks))).rightColor(Color.ORANGE).build()); + } + } + panelComponent.getChildren().add(LineComponent.builder().left("Special Attack:").right(Integer.toString(7 - plugin.hits)).rightColor(config.CounterColor()).build()); + return panelComponent.render(graphics); + } + } + return null; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/vorkath/VorkathOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/vorkath/VorkathOverlay.java new file mode 100644 index 0000000000..0773141574 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/vorkath/VorkathOverlay.java @@ -0,0 +1,84 @@ +package net.runelite.client.plugins.vorkath; + +import java.awt.*; +import javax.inject.Inject; + +import net.runelite.api.*; +import net.runelite.api.coords.LocalPoint; +import net.runelite.api.coords.WorldArea; +import net.runelite.client.ui.FontManager; +import net.runelite.client.ui.overlay.*; +import net.runelite.client.ui.overlay.components.PanelComponent; + +public class VorkathOverlay extends Overlay { + private final VorkathConfig config; + private final VorkathPlugin plugin; + private final PanelComponent panelComponent = new PanelComponent(); + + + @Inject + private Client client; + + @Inject + private VorkathOverlay(VorkathConfig config, VorkathPlugin plugin) { + this.config = config; + this.plugin = plugin; + setLayer(OverlayLayer.ABOVE_SCENE); + setPosition(OverlayPosition.DYNAMIC); + setPriority(OverlayPriority.MED); + panelComponent.setPreferredSize(new Dimension(150, 0)); + } + + @Override + public Dimension render(Graphics2D graphics) { + if (!config.EnableVorkath()) { + return null; + } + Actor local = client.getLocalPlayer(); + + WorldArea area = local.getWorldArea(); + if (area == null) + { + return null; + } + + NPC Vorkath = plugin.Vorkath; + if (Vorkath != null) { + if (plugin.fireball != null) { + if (config.TileHighlight() == TileHighlight.Single) { + final Polygon poly = Perspective.getCanvasTilePoly(client, plugin.fireball); + if (poly != null) { + OverlayUtil.renderPolygon(graphics, poly, Color.RED); + } + } else if (config.TileHighlight() == TileHighlight.All) { + for (int dx = -1; dx <= 1; dx++) { + for (int dy = -1; dy <= 1; dy++) { + if (dx == 0 && dy == 0) { + continue; + } + LocalPoint lp = new LocalPoint(plugin.fireball.getX() + dx * Perspective.LOCAL_TILE_SIZE + dx * Perspective.LOCAL_TILE_SIZE * (area.getWidth() - 1) / 2, plugin.fireball.getY() + dy * Perspective.LOCAL_TILE_SIZE + dy * Perspective.LOCAL_TILE_SIZE * (area.getHeight() - 1) / 2); + Polygon polyadj = Perspective.getCanvasTilePoly(client, lp); + if (polyadj != null) { + OverlayUtil.renderPolygon(graphics, polyadj, Color.ORANGE); + } + } + } + } + } + + if (config.BoldText()) { + graphics.setFont(FontManager.getRunescapeBoldFont()); + } + + if (plugin.venomticks != 0) { + if (plugin.venomticks + 5 <= plugin.ticks) { + OverlayUtil.renderTextLocation(graphics, Vorkath.getCanvasTextLocation(graphics, Integer.toString(30 - (plugin.ticks - plugin.venomticks)), Vorkath.getLogicalHeight() + 150), Integer.toString(30 - (plugin.ticks - plugin.venomticks)), Color.ORANGE); + } + } + + OverlayUtil.renderTextLocation(graphics, Vorkath.getCanvasTextLocation(graphics, Integer.toString(7 - plugin.hits), Vorkath.getLogicalHeight() + 40), Integer.toString(7 - plugin.hits), config.CounterColor()); + graphics.setFont(FontManager.getRunescapeFont()); + } + return null; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/vorkath/VorkathPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/vorkath/VorkathPlugin.java new file mode 100644 index 0000000000..1ba5e4b0e7 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/vorkath/VorkathPlugin.java @@ -0,0 +1,157 @@ +package net.runelite.client.plugins.vorkath; + +import net.runelite.api.events.*; +import net.runelite.client.eventbus.Subscribe; +import com.google.inject.Provides; +import javax.inject.Inject; +import net.runelite.api.*; +import net.runelite.api.coords.LocalPoint; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.game.SpriteManager; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.ui.overlay.OverlayManager; +import org.apache.commons.lang3.ArrayUtils; + +@PluginDescriptor( + name = "!Vorkath", + description = "Vorkath Helper", + tags = {"Vorkath", "Helper"} +) +public class VorkathPlugin extends Plugin +{ + @Inject + private OverlayManager overlayManager; + + @Inject + private VorkathConfig config; + + @Inject + private VorkathOverlay VorkathOverlay; + + @Inject + private VorkathIndicatorOverlay VorkathIndicatorOverlay; + + @Inject + private Client client; + + @Inject + private SpriteManager spriteManager; + + @Provides + VorkathConfig provideConfig(ConfigManager configManager) { + return configManager.getConfig(VorkathConfig.class); + } + + NPC Vorkath; + int hits; + int ticks; + Boolean ice = false; + LocalPoint fireball; + int fireballticks = 0; + int lastattack; + int venomticks; + + int[] VorkathIDs = {393, 395, 1470, 1471, 1477, 1479}; + + @Override + protected void startUp() throws Exception { + overlayManager.add(VorkathOverlay); + overlayManager.add(VorkathIndicatorOverlay); + } + + @Override + protected void shutDown() throws Exception { + overlayManager.remove(VorkathOverlay); + overlayManager.remove(VorkathIndicatorOverlay); + Vorkath = null; + hits = 0; + fireball = null; + fireballticks = 0; + ice = false; + lastattack = 0; + } + + @Subscribe + public void onGameTick(GameTick event) { + if (!config.EnableVorkath()) { + return; + } + ticks++; + if (ticks - fireballticks > 5) { + fireballticks = 0; + fireball = null; + } + + if (venomticks + 30 <= ticks) { + venomticks = 0; + } + + boolean foundVorkath = false; + for (NPC monster : client.getNpcs()) + { + if (monster == null || monster.getName() == null || monster.getCombatLevel() == 0) + { + continue; + } + if (monster.getName().equalsIgnoreCase("Vorkath")) { + foundVorkath = true; + Vorkath = monster; + break; + } + } + if (!foundVorkath) { + Vorkath = null; + hits = 0; + fireball = null; + fireballticks = 0; + ice = false; + lastattack = 0; + } + } + + @Subscribe + public void onProjectileMoved(ProjectileMoved event) { + if (Vorkath != null) { + Projectile ball = event.getProjectile(); + if (ArrayUtils.contains(VorkathIDs, ball.getId())) { + if (ticks - lastattack > 4) { + if (ball.getId() == 395) { + ice = true; + } + hits++; + lastattack = ticks; + if (hits == 7) { + hits = 0; + } + } + } + } + } + + @Subscribe + public void onAnimationChanged(AnimationChanged event) { + Actor vorki = event.getActor(); + Actor local = client.getLocalPlayer(); + if (vorki instanceof NPC) { + if (vorki.equals(Vorkath)) { + if (vorki.getAnimation() != -1 && vorki.getAnimation() != 7948 && vorki.getAnimation() != 7952) { + if (ice) { + ice = false; + } else { + hits++; + if (hits == 7) { + venomticks = ticks; + hits = 0; + } + if (vorki.getAnimation() == 7960) { + fireball = local.getLocalLocation(); + fireballticks = ticks; + } + } + } + + } + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/wildernesslocations/WildernessLocationsOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/wildernesslocations/WildernessLocationsOverlay.java new file mode 100644 index 0000000000..69668fcbd6 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/wildernesslocations/WildernessLocationsOverlay.java @@ -0,0 +1,38 @@ + +package net.runelite.client.plugins.wildernesslocations; + +import java.awt.Dimension; +import java.awt.Graphics2D; +import javax.inject.Inject; +import net.runelite.api.Client; +import net.runelite.client.plugins.wildernesslocations.WildernessLocationsPlugin; +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.OverlayPriority; +import net.runelite.client.ui.overlay.components.TextComponent; + +public class WildernessLocationsOverlay +extends Overlay { + private final WildernessLocationsPlugin plugin; + private TextComponent textComponent; + + @Inject + public WildernessLocationsOverlay(Client client, WildernessLocationsPlugin plugin) { + this.plugin = plugin; + setLayer(OverlayLayer.ABOVE_WIDGETS); + setPriority(OverlayPriority.HIGH); + setPosition(OverlayPosition.BOTTOM_RIGHT); + textComponent = new TextComponent(); + } + + @Override + public Dimension render(Graphics2D graphics) { + if (plugin.isRenderLocation()) { + textComponent.setText(plugin.getLocationString()); + return textComponent.render(graphics); + } + return null; + } +} + diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/wildernesslocations/WildernessLocationsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/wildernesslocations/WildernessLocationsPlugin.java new file mode 100644 index 0000000000..0020f384f4 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/wildernesslocations/WildernessLocationsPlugin.java @@ -0,0 +1,127 @@ + +package net.runelite.client.plugins.wildernesslocations; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.function.Consumer; +import javax.inject.Inject; +import net.runelite.api.Client; +import net.runelite.api.Player; +import net.runelite.api.Varbits; +import net.runelite.api.coords.WorldArea; +import net.runelite.api.coords.WorldPoint; +import net.runelite.api.events.GameTick; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.wildernesslocations.WildernessLocationsOverlay; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayManager; +import net.runelite.client.util.WildernessLocation; + +@PluginDescriptor(name="PvP Wild Locations", + description="Indicates the players current location in the wild", + tags={"Wildy,", "Wilderness Location", "location", "loc", "pvp", "pklite"}) + +public class WildernessLocationsPlugin extends Plugin { + @Inject + private Client client; + @Inject + OverlayManager overlayManager; + @Inject + private WildernessLocationsOverlay overlay; + private final HashMap wildLocs; + private boolean renderLocation; + private String locationString; + private WorldPoint worldPoint; + private static int UPDATE_INTERVAL = 3; + + public WildernessLocationsPlugin() { + overlay = new WildernessLocationsOverlay(client, this); + wildLocs = WildernessLocationsPlugin.getLocationMap(); + locationString = ""; + worldPoint = null; + } + + @Override + protected void startUp() throws Exception { + overlayManager.add(overlay); + } + + @Override + protected void shutDown() throws Exception { + overlayManager.add(overlay); + } + + @Subscribe + public void onGameTick(GameTick event) { + if (UPDATE_INTERVAL > 0) { + --UPDATE_INTERVAL; + return; + } + boolean bl = renderLocation = client.getVar(Varbits.IN_WILDERNESS) == 1; + if (renderLocation) { + if (client.getLocalPlayer().getWorldLocation() != worldPoint) { + locationString = location(); + worldPoint = client.getLocalPlayer().getWorldLocation(); + } + } else { + worldPoint = null; + locationString = ""; + } + UPDATE_INTERVAL = 3; + } + + private String location() { + int dist = 10000; + String s = ""; + WorldArea closestArea = null; + for (Map.Entry entry : wildLocs.entrySet()) { + WorldArea worldArea = entry.getKey(); + if (worldArea.toWorldPointList().contains(client.getLocalPlayer().getWorldLocation())) { + s = entry.getValue(); + return s; + } + int distTo = worldArea.distanceTo(client.getLocalPlayer().getWorldLocation()); + if (distTo >= dist) continue; + dist = distTo; + closestArea = worldArea; + } + if (client.getLocalPlayer().getWorldLocation().getY() > ((WorldArea)Objects.requireNonNull(closestArea)).toWorldPoint().getY() + closestArea.getHeight()) { + s = s + "N"; + } + if (client.getLocalPlayer().getWorldLocation().getY() < closestArea.toWorldPoint().getY()) { + s = s + "S"; + } + if (client.getLocalPlayer().getWorldLocation().getX() < closestArea.toWorldPoint().getX()) { + s = s + "W"; + } + if (client.getLocalPlayer().getWorldLocation().getX() > closestArea.toWorldPoint().getX() + closestArea.getWidth()) { + s = s + "E"; + } + s = s + " of "; + if ((s = s + wildLocs.get(closestArea)).startsWith(" of ")) { + s = s.substring(3); + } + return s; + } + + private static HashMap getLocationMap() { + HashMap hashMap = new HashMap(); + Arrays.stream(WildernessLocation.values()).forEach(wildernessLocation -> hashMap.put(wildernessLocation.getWorldArea(), wildernessLocation.getName())); + return hashMap; + } + + public boolean isRenderLocation() { + return renderLocation; + } + + public String getLocationString() { + return locationString; + } +} + diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/zoneIndicators/MapLocations.java b/runelite-client/src/main/java/net/runelite/client/plugins/zoneIndicators/MapLocations.java new file mode 100644 index 0000000000..81e8e0f527 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/zoneIndicators/MapLocations.java @@ -0,0 +1,3479 @@ +/* + * Copyright (c) 2018, Woox + * 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.zoneIndicators; + +import java.awt.Polygon; +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.geom.Area; +import java.util.ArrayList; +import java.util.List; +import net.runelite.api.Constants; + +public class MapLocations +{ + private static final List[] MULTICOMBAT = new List[Constants.MAX_Z]; + private static final List[] NOT_MULTICOMBAT = new List[Constants.MAX_Z]; + private static final List[] ROUGH_WILDERNESS = new List[Constants.MAX_Z]; + private static final List[] DEADMAN_SAFE_ZONES = new List[Constants.MAX_Z]; + private static final List[] PVP_WORLD_SAFE_ZONES = new List[Constants.MAX_Z]; + + private static Area getArea(List shapes) + { + Area area = new Area(); + for (Shape shape : shapes) + { + area.add(new Area(shape)); + } + return area; + } + + private static Area getArea(List shapes, Rectangle view) + { + Area area = new Area(); + for (Shape shape : shapes) + { + if (shape.intersects(view)) + { + area.add(new Area(shape)); + } + } + return area; + } + + public static Area getMulticombat(int plane) + { + Area area = getArea(MULTICOMBAT[plane]); + area.subtract(getArea(NOT_MULTICOMBAT[plane])); + return area; + } + + public static Area getMulticombat(Rectangle view, int plane) + { + Area area = getArea(MULTICOMBAT[plane], view); + area.subtract(getArea(NOT_MULTICOMBAT[plane], view)); + return area; + } + + public static Area getRoughWilderness(int plane) + { + return getArea(ROUGH_WILDERNESS[plane]); + } + + public static Area getRoughWilderness(Rectangle view, int plane) + { + return getArea(ROUGH_WILDERNESS[plane], view); + } + + public static Area getDeadmanSafeZones(int plane) + { + return getArea(DEADMAN_SAFE_ZONES[plane]); + } + + public static Area getDeadmanSafeZones(Rectangle view, int plane) + { + return getArea(DEADMAN_SAFE_ZONES[plane], view); + } + + public static Area getPvpSafeZones(int plane) + { + return getArea(PVP_WORLD_SAFE_ZONES[plane]); + } + + public static Area getPvpSafeZones(Rectangle view, int plane) + { + return getArea(PVP_WORLD_SAFE_ZONES[plane], view); + } + + static + { + for (int i = 0; i < MULTICOMBAT.length; i++) + { + MULTICOMBAT[i] = new ArrayList<>(); + } + for (int i = 0; i < NOT_MULTICOMBAT.length; i++) + { + NOT_MULTICOMBAT[i] = new ArrayList<>(); + } + for (int i = 0; i < ROUGH_WILDERNESS.length; i++) + { + ROUGH_WILDERNESS[i] = new ArrayList<>(); + } + for (int i = 0; i < DEADMAN_SAFE_ZONES.length; i++) + { + DEADMAN_SAFE_ZONES[i] = new ArrayList<>(); + } + for (int i = 0; i < PVP_WORLD_SAFE_ZONES.length; i++) + { + PVP_WORLD_SAFE_ZONES[i] = new ArrayList<>(); + } + + defineMulticombatAreas(); + defineDeadmanSafeZones(); + definePvpSafeZones(); + defineWilderness(); + } + + private static void defineMulticombatAreas() + { + // Main Wilderness + addPolygonOnPlane(MULTICOMBAT, 0, + 3200, 3968, + 3392, 3968, + 3392, 3840, + 3328, 3840, + 3328, 3520, + 3136, 3520, + 3136, 3648, + 3192, 3648, + 3192, 3752, + 3152, 3752, + 3152, 3840, + 3136, 3840, + 3136, 3872, + 3112, 3872, + 3112, 3880, + 3072, 3880, + 3072, 3896, + 3048, 3896, + 3048, 3872, + 3056, 3872, + 3056, 3864, + 3048, 3864, + 3048, 3856, + 3008, 3856, + 3008, 3904, + 3200, 3904); + + // South of wildy agility training arena + addPolygonOnPlane(MULTICOMBAT, 0, + 2984, 3928, + 3008, 3928, + 3008, 3912, + 2984, 3912); + + // Wildy zamorak temple + addPolygonOnPlane(MULTICOMBAT, 0, + 2944, 3832, + 2960, 3832, + 2960, 3816, + 2944, 3816); + + // Wildy bandit camp + addPolygonOnPlane(MULTICOMBAT, 0, + 3008, 3712, + 3072, 3712, + 3072, 3600, + 3008, 3600); + + // Chaos temple north of Falador + addPolygonOnPlane(MULTICOMBAT, 0, + 2928, 3520, + 2944, 3520, + 2944, 3512, + 2928, 3512); + + // Burthorpe + addPolygonOnPlane(MULTICOMBAT, 0, + 2880, 3544, + 2904, 3544, + 2904, 3520, + 2880, 3520); + + // White Wolf Mountain + addPolygonOnPlane(MULTICOMBAT, 0, + 2880, 3520, + 2816, 3520, + 2816, 3456, + 2880, 3456); + + // Death Plateu + addPolygonOnPlane(MULTICOMBAT, 0, + 2848, 3608, + 2880, 3608, + 2880, 3600, + 2848, 3600); + + // Trollheim/Godwars + addPolygonOnPlane(MULTICOMBAT, 0, + 2880, 3776, + 2912, 3776, + 2912, 3696, + 2920, 3696, + 2920, 3688, + 2896, 3688, + 2896, 3696, + 2880, 3696, + 2880, 3728, + 2888, 3728, + 2888, 3744, + 2880, 3744); + + // Northen Rellekka + addPolygonOnPlane(MULTICOMBAT, 0, + 2656, 3736, + 2704, 3736, + 2704, 3728, + 2712, 3728, + 2712, 3736, + 2736, 3736, + 2736, 3712, + 2656, 3712); + + // Northen Fremennik Isles + addPolygonOnPlane(MULTICOMBAT, 0, + 2304, 3904, + 2432, 3904, + 2432, 3840, + 2368, 3840, + 2368, 3816, + 2352, 3816, + 2352, 3824, + 2304, 3824); + + // Pirates Cove + addPolygonOnPlane(MULTICOMBAT, 0, + 2176, 3840, + 2240, 3840, + 2240, 3776, + 2176, 3776); + + // Lunar Isle + addPolygonOnPlane(MULTICOMBAT, 0, + 2048, 3968, + 2176, 3968, + 2176, 3840, + 2048, 3840); + + // Piscatoris Fishing Colony + addPolygonOnPlane(MULTICOMBAT, 0, + 2304, 3712, + 2368, 3712, + 2368, 3648, + 2304, 3648); + + // Ranging Guild + addPolygonOnPlane(MULTICOMBAT, 0, + 2656, 3448, + 2680, 3448, + 2680, 3440, + 2688, 3440, + 2688, 3416, + 2680, 3416, + 2680, 3408, + 2656, 3408, + 2656, 3416, + 2648, 3416, + 2648, 3440, + 2656, 3440); + + // Necromancer house, southeast of Ardy + addPolygonOnPlane(MULTICOMBAT, 0, + 2656, 3256, + 2680, 3256, + 2680, 3216, + 2664, 3216, + 2664, 3232, + 2656, 3232); + + // Battlefield noth of Tree Gnome Village + addPolygonOnPlane(MULTICOMBAT, 0, + 2504, 3248, + 2544, 3248, + 2544, 3232, + 2552, 3232, + 2552, 3208, + 2504, 3208); + + // Castle Wars + addPolygonOnPlane(MULTICOMBAT, 0, + 2368, 3136, + 2432, 3136, + 2432, 3072, + 2368, 3072); + + // Jiggig + addPolygonOnPlane(MULTICOMBAT, 0, + 2456, 3056, + 2496, 3056, + 2496, 3032, + 2456, 3032); + + // East feldip hills, near rantz + addPolygonOnPlane(MULTICOMBAT, 0, + 2648, 2976, + 2656, 2976, + 2656, 2952, + 2648, 2952); + + // Ape Atoll + addPolygonOnPlane(MULTICOMBAT, 0, + 2688, 2816, + 2816, 2816, + 2816, 2688, + 2688, 2688); + + // Pest Control + addPolygonOnPlane(MULTICOMBAT, 0, + 2624, 2624, + 2688, 2624, + 2688, 2560, + 2624, 2560); + + // Desert Bandit Camp + addPolygonOnPlane(MULTICOMBAT, 0, + 3152, 3000, + 3192, 3000, + 3192, 2960, + 3152, 2960); + + // Al Kharid + addPolygonOnPlane(MULTICOMBAT, 0, + 3264, 3200, + 3328, 3200, + 3328, 3136, + 3264, 3136); + + // Wizards Tower + addPolygonOnPlane(MULTICOMBAT, 0, + 3094, 3176, + 3126, 3176, + 3126, 3144, + 3094, 3144); + + // Draynor Village + addPolygonOnPlane(MULTICOMBAT, 0, + 3112, 3264, + 3136, 3264, + 3136, 3232, + 3104, 3232, + 3104, 3256, + 3112, 3256); + + // Falador + addPolygonOnPlane(MULTICOMBAT, 0, + 2944, 3456, + 3008, 3456, + 3008, 3328, + 3016, 3328, + 3016, 3304, + 2944, 3304); + + // Southwest fally castle isn't multicombat downstairs + addPolygonOnPlane(NOT_MULTICOMBAT, 0, + 2968, 3336, + 2968, 3328, + 2960, 3328, + 2960, 3336); + + // Barbarian Village + addPolygonOnPlane(MULTICOMBAT, 0, + 3072, 3456, + 3136, 3456, + 3136, 3392, + 3048, 3392, + 3048, 3408, + 3056, 3408, + 3056, 3440, + 3064, 3440, + 3064, 3448, + 3072, 3448); + + // Ammoniate crabs at northwest fossil island + addPolygonOnPlane(MULTICOMBAT, 0, + 3648, 3885, + 3663, 3885, + 3663, 3882, + 3664, 3882, + 3664, 3872, + 3663, 3872, + 3663, 3868, + 3648, 3868); + + // Ammoniate crabs at north fossil island + addPolygonOnPlane(MULTICOMBAT, 0, + 3680, 3904, + 3744, 3904, + 3744, 3856, + 3756, 3856, + 3756, 3852, + 3755, 3852, + 3755, 3851, + 3754, 3851, + 3754, 3850, + 3751, 3850, + 3751, 3849, + 3750, 3849, + 3750, 3848, + 3749, 3848, + 3749, 3847, + 3748, 3847, + 3748, 3846, + 3747, 3846, + 3747, 3845, + 3746, 3845, + 3746, 3844, + 3742, 3844, + 3742, 3845, + 3740, 3845, + 3740, 3844, + 3732, 3844, + 3732, 3843, + 3730, 3843, + 3730, 3842, + 3724, 3842, + 3724, 3843, + 3717, 3843, + 3717, 3842, + 3712, 3842, + 3712, 3846, + 3710, 3846, + 3710, 3847, + 3709, 3847, + 3709, 3848, + 3708, 3848, + 3708, 3859, + 3709, 3859, + 3709, 3860, + 3710, 3860, + 3710, 3861, + 3712, 3861, + 3712, 3866, + 3713, 3866, + 3713, 3870, + 3714, 3870, + 3714, 3873, + 3713, 3873, + 3713, 3876, + 3712, 3876, + 3712, 3881, + 3710, 3881, + 3710, 3888, + 3712, 3888, + 3712, 3890, + 3714, 3890, + 3714, 3891, + 3716, 3891, + 3716, 3892, + 3717, 3892, + 3717, 3893, + 3716, 3893, + 3716, 3894, + 3714, 3894, + 3714, 3895, + 3713, 3895, + 3713, 3896, + 3712, 3896, + 3712, 3897, + 3705, 3897, + 3705, 3898, + 3704, 3898, + 3704, 3899, + 3692, 3899, + 3692, 3898, + 3688, 3898, + 3688, 3897, + 3686, 3897, + 3686, 3896, + 3680, 3896); + + // Zeah, southwest of Wintertodt, snowy area with ice giants and wolves + addPolygonOnPlane(MULTICOMBAT, 0, + 1540, 3898, + 1543, 3898, + 1543, 3901, + 1546, 3901, + 1546, 3903, + 1547, 3903, + 1547, 3904, + 1550, 3904, + 1550, 3903, + 1553, 3903, + 1553, 3904, + 1559, 3904, + 1559, 3902, + 1564, 3902, + 1564, 3903, + 1565, 3903, + 1565, 3904, + 1568, 3904, + 1568, 3903, + 1569, 3903, + 1569, 3902, + 1570, 3902, + 1570, 3901, + 1573, 3901, + 1573, 3898, + 1577, 3898, + 1577, 3899, + 1578, 3899, + 1578, 3902, + 1579, 3902, + 1579, 3903, + 1584, 3903, + 1584, 3902, + 1586, 3902, + 1586, 3901, + 1590, 3901, + 1590, 3891, + 1588, 3891, + 1588, 3887, + 1572, 3887, + 1572, 3872, + 1567, 3872, + 1567, 3868, + 1563, 3868, + 1563, 3867, + 1558, 3867, + 1558, 3868, + 1557, 3868, + 1557, 3870, + 1549, 3870, + 1549, 3874, + 1545, 3874, + 1545, 3876, + 1543, 3876, + 1543, 3877, + 1542, 3877, + 1542, 3879, + 1541, 3879, + 1541, 3882, + 1539, 3882, + 1539, 3887, + 1540, 3887, + 1540, 3888, + 1539, 3888, + 1539, 3894, + 1540, 3894); + + // Zeah arceuus area + addPolygonOnPlane(MULTICOMBAT, 0, + 1664, 3776, + 1664, 3785, + 1667, 3785, + 1667, 3805, + 1671, 3805, + 1671, 3811, + 1675, 3811, + 1675, 3819, + 1690, 3819, + 1690, 3814, + 1695, 3814, + 1695, 3806, + 1719, 3806, + 1719, 3787, + 1725, 3787, + 1725, 3778, + 1711, 3778, + 1711, 3776); + + // Arceuus teletab-making house + addPolygonOnPlane(MULTICOMBAT, 0, + 1667, 3772, + 1679, 3772, + 1679, 3775, + 1691, 3775, + 1691, 3761, + 1679, 3761, + 1679, 3764, + 1667, 3764); + // Next house east + addPolygonOnPlane(MULTICOMBAT, 0, + 1696, 3775, + 1708, 3775, + 1708, 3763, + 1696, 3763); + // Next house east + addPolygonOnPlane(MULTICOMBAT, 0, + 1713, 3775, + 1727, 3775, + 1727, 3763, + 1724, 3763, + 1724, 3752, + 1716, 3752, + 1716, 3763, + 1713, 3763); + // Arceuus rune shop house + addPolygonOnPlane(MULTICOMBAT, 0, + 1716, 3750, + 1728, 3750, + 1728, 3736, + 1716, 3736); + // Arceuus general store house + addPolygonOnPlane(MULTICOMBAT, 0, + 1717, 3732, + 1725, 3732, + 1725, 3715, + 1715, 3715, + 1715, 3725, + 1717, 3725); + // Arceuus pub + addPolygonOnPlane(MULTICOMBAT, 0, + 1683, 3732, + 1691, 3732, + 1691, 3725, + 1697, 3725, + 1697, 3730, + 1703, 3730, + 1703, 3712, + 1683, 3712); + // Arceuus staff store + addPolygonOnPlane(MULTICOMBAT, 0, + 1664, 3732, + 1676, 3732, + 1676, 3720, + 1664, 3720); + // Next house to the west + addPolygonOnPlane(MULTICOMBAT, 0, + 1647, 3738, + 1655, 3738, + 1655, 3726, + 1658, 3726, + 1658, 3714, + 1644, 3714, + 1644, 3726, + 1647, 3726); + // Next house to the north + addPolygonOnPlane(MULTICOMBAT, 0, + 1647, 3762, + 1657, 3762, + 1657, 3752, + 1655, 3752, + 1655, 3745, + 1647, 3745); + + // Arceuus house magic trees + addPolygonOnPlane(MULTICOMBAT, 0, + 1682, 3755, + 1692, 3755, + 1692, 3745, + 1690, 3745, + 1690, 3738, + 1682, 3738); + // West of that ^ + addPolygonOnPlane(MULTICOMBAT, 0, + 1667, 3756, + 1675, 3756, + 1675, 3740, + 1665, 3740, + 1665, 3746, + 1667, 3746); + + // This one goes through western piscarilius, northen hosidius + // and southwestern arceuus + addPolygonOnPlane(MULTICOMBAT, 0, + 1728, 3808, + 1792, 3808, + 1792, 3764, + 1856, 3764, + 1856, 3712, + 1792, 3712, + 1792, 3648, + 1664, 3648, + 1664, 3706, + 1665, 3706, + 1665, 3705, + 1668, 3705, + 1668, 3706, + 1671, 3706, + 1671, 3705, + 1675, 3705, + 1675, 3704, + 1683, 3704, + 1683, 3701, + 1684, 3701, + 1684, 3700, + 1686, 3700, + 1686, 3702, + 1687, 3702, + 1687, 3700, + 1688, 3700, + 1688, 3701, + 1690, 3701, + 1690, 3703, + 1689, 3703, + 1689, 3704, + 1690, 3704, + 1690, 3705, + 1704, 3705, + 1704, 3707, + 1706, 3707, + 1706, 3712, + 1711, 3712, + 1711, 3711, + 1710, 3711, + 1710, 3710, + 1712, 3710, + 1712, 3707, + 1728, 3707); + + // Kourend castle + addPolygonOnPlane(MULTICOMBAT, 0, + 1614, 3691, + 1619, 3691, + 1619, 3690, + 1620, 3690, + 1620, 3689, + 1653, 3689, + 1653, 3690, + 1654, 3690, + 1654, 3691, + 1657, 3691, + 1657, 3690, + 1658, 3690, + 1658, 3689, + 1659, 3689, + 1659, 3686, + 1658, 3686, + 1658, 3685, + 1657, 3685, + 1657, 3662, + 1658, 3662, + 1658, 3661, + 1659, 3661, + 1659, 3658, + 1658, 3658, + 1658, 3657, + 1657, 3657, + 1657, 3656, + 1654, 3656, + 1654, 3657, + 1653, 3657, + 1653, 3658, + 1620, 3658, + 1620, 3657, + 1619, 3657, + 1619, 3656, + 1614, 3656, + 1614, 3657, + 1613, 3657, + 1613, 3661, + 1612, 3661, + 1612, 3662, + 1611, 3662, + 1611, 3663, + 1600, 3663, + 1600, 3662, + 1599, 3662, + 1599, 3661, + 1594, 3661, + 1594, 3662, + 1593, 3662, + 1593, 3685, + 1594, 3685, + 1594, 3686, + 1599, 3686, + 1599, 3685, + 1600, 3685, + 1600, 3684, + 1611, 3684, + 1611, 3685, + 1612, 3685, + 1612, 3686, + 1613, 3686, + 1613, 3690, + 1614, 3690); + + // Western hosidius area, including woodcutting guild and western sand crabs + addPolygonOnPlane(MULTICOMBAT, 0, + 1650, 3648, + 1664, 3648, + 1664, 3520, + 1689, 3520, + 1689, 3496, + 1707, 3496, + 1707, 3485, + 1708, 3485, + 1708, 3484, + 1710, 3484, + 1710, 3483, + 1713, 3483, + 1713, 3482, + 1720, 3482, + 1720, 3481, + 1721, 3481, + 1721, 3480, + 1722, 3480, + 1722, 3479, + 1723, 3479, + 1723, 3478, + 1724, 3478, + 1724, 3477, + 1726, 3477, + 1726, 3476, + 1728, 3476, + 1728, 3472, + 1708, 3472, + 1708, 3456, + 1600, 3456, + 1600, 3584, + 1608, 3584, + 1608, 3616, + 1650, 3616); + + // Hosidius sand crabs + addPolygonOnPlane(MULTICOMBAT, 0, + 1740, 3478, + 1741, 3478, + 1741, 3479, + 1745, 3479, + 1745, 3480, + 1751, 3480, + 1751, 3479, + 1752, 3479, + 1752, 3478, + 1753, 3478, + 1753, 3477, + 1755, 3477, + 1755, 3476, + 1757, 3476, + 1757, 3475, + 1758, 3475, + 1758, 3474, + 1759, 3474, + 1759, 3473, + 1779, 3473, + 1779, 3474, + 1781, 3474, + 1781, 3475, + 1786, 3475, + 1786, 3476, + 1800, 3476, + 1800, 3475, + 1805, 3475, + 1805, 3474, + 1807, 3474, + 1807, 3473, + 1808, 3473, + 1808, 3472, + 1810, 3472, + 1810, 3471, + 1833, 3471, + 1833, 3470, + 1834, 3470, + 1834, 3469, + 1852, 3469, + 1852, 3449, + 1792, 3449, + 1792, 3424, + 1800, 3424, + 1800, 3449, + 1800, 3400, + 1728, 3400, + 1728, 3462, + 1729, 3462, + 1729, 3466, + 1730, 3466, + 1730, 3469, + 1731, 3469, + 1731, 3470, + 1732, 3470, + 1732, 3471, + 1733, 3471, + 1733, 3473, + 1734, 3473, + 1734, 3474, + 1736, 3474, + 1736, 3475, + 1737, 3475, + 1737, 3476, + 1738, 3476, + 1738, 3477, + 1740, 3477); + + // Apparently there is a 1x1 single zone on the sand crab island + addPolygonOnPlane(NOT_MULTICOMBAT, 0, + 1777, 3416, + 1777, 3417, + 1778, 3417, + 1778, 3416); + + // Eastern hosidius area + addPolygonOnPlane(MULTICOMBAT, 0, + 1834, 3584, + 1888, 3584, + 1888, 3528, + 1856, 3528, + 1856, 3520, + 1834, 3520, + 1834, 3522, + 1833, 3522, + 1833, 3535, + 1834, 3535, + 1834, 3538, + 1835, 3538, + 1835, 3539, + 1836, 3539, + 1836, 3540, + 1837, 3540, + 1837, 3541, + 1838, 3541, + 1838, 3542, + 1840, 3542, + 1840, 3543, + 1841, 3543, + 1841, 3545, + 1842, 3545, + 1842, 3546, + 1844, 3546, + 1844, 3547, + 1845, 3547, + 1845, 3548, + 1851, 3548, + 1851, 3551, + 1853, 3551, + 1853, 3563, + 1851, 3563, + 1851, 3566, + 1847, 3566, + 1847, 3567, + 1845, 3567, + 1845, 3568, + 1844, 3568, + 1844, 3569, + 1843, 3569, + 1843, 3571, + 1842, 3571, + 1842, 3573, + 1841, 3573, + 1841, 3574, + 1840, 3574, + 1840, 3575, + 1839, 3575, + 1839, 3576, + 1838, 3576, + 1838, 3577, + 1837, 3577, + 1837, 3578, + 1836, 3578, + 1836, 3579, + 1835, 3579, + 1835, 3581, + 1834, 3581); + + // Eastern hosidius area also has a 1x1 multi area + addPolygonOnPlane(MULTICOMBAT, 0, + 1849, 3563, + 1849, 3564, + 1850, 3564, + 1850, 3563); + + // Hosidius cows/chickens/pigs + addPolygonOnPlane(MULTICOMBAT, 0, + 1792, 3513, + 1802, 3513, + 1802, 3520, + 1810, 3520, + 1810, 3513, + 1816, 3513, + 1816, 3512, + 1836, 3512, + 1836, 3494, + 1796, 3494, + 1796, 3495, + 1792, 3495); + + // Hosidius southeast of tithe farm + addPolygonOnPlane(MULTICOMBAT, 0, + 1777, 3597, + 1794, 3597, + 1794, 3561, + 1777, 3561, + 1777, 3591, + 1779, 3591, + 1779, 3592, + 1777, 3592); + + // West of shayzien house + addPolygonOnPlane(MULTICOMBAT, 0, + 1408, 3584, + 1408, 3582, + 1486, 3582, + 1486, 3568, + 1528, 3568, + 1528, 3520, + 1408, 3520, + 1408, 3464, + 1380, 3464, + 1380, 3486, + 1377, 3486, + 1377, 3488, + 1373, 3488, + 1373, 3492, + 1364, 3492, + 1364, 3512, + 1358, 3512, + 1358, 3520, + 1356, 3520, + 1356, 3532, + 1358, 3532, + 1358, 3540, + 1359, 3540, + 1359, 3542, + 1360, 3542, + 1360, 3557, + 1356, 3557, + 1356, 3560, + 1351, 3560, + 1351, 3570, + 1354, 3570, + 1354, 3581, + 1346, 3581, + 1346, 3584); + + // South of chambers of xeric + addPolygonOnPlane(MULTICOMBAT, 0, + 1261, 3489, + 1259, 3489, + 1259, 3488, + 1255, 3488, + 1255, 3487, + 1243, 3487, + 1243, 3490, + 1234, 3490, + 1234, 3480, + 1192, 3480, + 1192, 3568, + 1209, 3568, + 1209, 3548, + 1215, 3548, + 1215, 3544, + 1217, 3544, + 1217, 3536, + 1235, 3536, + 1235, 3532, + 1249, 3532, + 1249, 3525, + 1248, 3525, + 1248, 3517, + 1254, 3517, + 1254, 3513, + 1274, 3513, + 1274, 3510, + 1296, 3510, + 1296, 3511, + 1300, 3511, + 1300, 3501, + 1287, 3501, + 1287, 3490, + 1280, 3490, + 1280, 3489, + 1264, 3489, + 1264, 3490, + 1261, 3490); + + // Lizardman shamans + addPolygonOnPlane(MULTICOMBAT, 0, + 1416, 3728, + 1456, 3728, + 1456, 3688, + 1416, 3688); + + // Other lizardman area at shayzien (west side) + addPolygonOnPlane(MULTICOMBAT, 0, + 1472, 3712, + 1510, 3712, + 1510, 3702, + 1509, 3702, + 1509, 3701, + 1506, 3701, + 1506, 3696, + 1500, 3696, + 1500, 3680, + 1472, 3680); + + // Other lizardman area at shayzien (east side) + addPolygonOnPlane(MULTICOMBAT, 0, + 1538, 3704, + 1560, 3704, + 1560, 3672, + 1538, 3672); + + // Lovakengj house + addPolygonOnPlane(MULTICOMBAT, 0, + 1600, 3712, + 1472, 3712, + 1472, 3840, + 1547, 3840, + 1547, 3816, + 1556, 3816, + 1556, 3809, + 1562, 3809, + 1562, 3800, + 1568, 3800, + 1568, 3793, + 1571, 3793, + 1571, 3816, + 1571, 3776, + 1600, 3776); + + // Shayzien house + addPolygonOnPlane(MULTICOMBAT, 0, + 1475, 3587, + 1475, 3641, + 1534, 3641, + 1534, 3587); + + // Shayzien house bank is non-multi + addPolygonOnPlane(NOT_MULTICOMBAT, 0, + 1495, 3622, + 1515, 3622, + 1515, 3612, + 1495, 3612); + + // Shayzien house general store + addPolygonOnPlane(MULTICOMBAT, 0, + 1539, 3640, + 1551, 3640, + 1551, 3621, + 1539, 3621); + + // Kourend woodland barbarian area + addPolygonOnPlane(MULTICOMBAT, 0, + 1572, 3442, + 1591, 3442, + 1591, 3424, + 1572, 3424); + + // Catacombs + addPolygonTo(MULTICOMBAT, + 1600, 9984, + 1600, 10067, + 1628, 10067, + 1628, 10070, + 1639, 10070, + 1639, 10112, + 1730, 10112, + 1730, 9984); + + // Zeah dungeon with sand crabs + addPolygonTo(MULTICOMBAT, + 1632, 9792, + 1632, 9856, + 1728, 9856, + 1728, 9792); + + // Waterbirth island near the doors where people use rune throwing axes + addPolygonTo(MULTICOMBAT, + 2536, 10136, + 2536, 10152, + 2552, 10152, + 2552, 10136); + + // Waterbirth island dungeon, on the path to dks + addPolygonTo(MULTICOMBAT, + 1792, 4352, + 1792, 4416, + 1984, 4416, + 1984, 4352); + + // Dagannoths in lighthouse + addPolygonTo(MULTICOMBAT, + 2496, 10048, + 2560, 10048, + 2560, 9984, + 2496, 9984); + + // Dagannoth kings (DKs) including slayer only dks + addPolygonTo(MULTICOMBAT, + 2944, 4352, + 2944, 4480, + 2880, 4480, + 2880, 4352); + + // White wolf mountain dungeon at ice queen + addPolygonTo(MULTICOMBAT, + 2856, 9928, + 2856, 9968, + 2880, 9968, + 2880, 9928); + + // Kharazi jungle dungeon (in dragon slayer 2 quest) + addPolygonTo(MULTICOMBAT, + 2816, 9296, + 2880, 9296, + 2880, 9216, + 2816, 9216); + + // Tzhaar, fight pits and inferno area + addPolygonTo(MULTICOMBAT, + 2368, 5184, + 2560, 5184, + 2560, 5056, + 2368, 5056); + + // Smoke devils + addPolygonTo(MULTICOMBAT, + 2432, 9408, + 2344, 9408, + 2344, 9472, + 2432, 9472); + + // Kraken + addPolygonTo(MULTICOMBAT, + 2270, 10045, + 2291, 10045, + 2291, 10022, + 2270, 10022); + + // Giant mole + addPolygonTo(MULTICOMBAT, + 1728, 5240, + 1792, 5240, + 1792, 5120, + 1728, 5120); + + // Godwars dungeon + addPolygonTo(MULTICOMBAT, + 2816, 5376, + 2944, 5376, + 2944, 5248, + 2816, 5248); + + // Desert treasure shadow diamond area + addPolygonTo(MULTICOMBAT, + 2752, 5064, + 2728, 5064, + 2728, 5088, + 2720, 5088, + 2720, 5096, + 2712, 5096, + 2712, 5112, + 2736, 5112, + 2736, 5120, + 2752, 5120); + + // Kalphite slayer area + addPolygonTo(MULTICOMBAT, + 3264, 9544, + 3344, 9544, + 3344, 9472, + 3264, 9472); + + // Normal kalphite area including kalphite queen + addPolygonTo(MULTICOMBAT, + 3456, 9536, + 3520, 9536, + 3520, 9472, + 3456, 9472); + + // Tarns lair + addPolygonTo(MULTICOMBAT, + 3136, 4664, + 3200, 4664, + 3200, 4544, + 3136, 4544); + + // Haunted mine boss area + addPolygonTo(MULTICOMBAT, + 2752, 4416, + 2752, 4480, + 2816, 4480, + 2816, 4416); + + // Entrance to dorgesh kaan + addPolygonTo(MULTICOMBAT, + 3328, 9600, + 3312, 9600, + 3312, 9640, + 3304, 9640, + 3304, 9664, + 3328, 9664); + + // Hammerspikes hangout in dwarven mines + addPolygonTo(MULTICOMBAT, + 2960, 9824, + 2976, 9824, + 2976, 9800, + 2960, 9800); + + // Fremennik isles dungeon + addPolygonTo(MULTICOMBAT, + 2432, 10304, + 2432, 10240, + 2368, 10240, + 2368, 10304); + + // Varrock sewers + addPolygonTo(MULTICOMBAT, + 3152, 9920, + 3288, 9920, + 3288, 9856, + 3152, 9856); + + // Stronghold of security 1st floor + addPolygonTo(MULTICOMBAT, + 1856, 5248, + 1920, 5248, + 1920, 5184, + 1856, 5184); + + // Corp cave + addPolygonTo(MULTICOMBAT, + 2960, 4400, + 3000, 4400, + 3000, 4368, + 2960, 4368); + + // ZMI altar area + addPolygonTo(MULTICOMBAT, + 3008, 5632, + 3072, 5632, + 3072, 5568, + 3008, 5568); + + // Dragon slayer 2 zeah underground puzzle + addPolygonTo(MULTICOMBAT, + 1472, 9984, + 1536, 9984, + 1536, 9920, + 1472, 9920); + + // Wildy revenant caves + addPolygonTo(MULTICOMBAT, + 3136, 10062, + 3136, 10240, + 3236, 10240, + 3236, 10229, + 3264, 10229, + 3264, 10048, + 3208, 10048, + 3208, 10062); + + // King black dragon (Kbd) + addPolygonTo(MULTICOMBAT, + 2240, 4672, + 2240, 4736, + 2304, 4736, + 2304, 4672); + + // Scorpia + addPolygonTo(MULTICOMBAT, + 3248, 10352, + 3248, 10328, + 3216, 10328, + 3216, 10352); + + // Inside mage bank + addPolygonTo(MULTICOMBAT, + 2496, 4672, + 2496, 4736, + 2560, 4736, + 2560, 4672); + + // Wildy godwars dungeon + addPolygonTo(MULTICOMBAT, + 3072, 10112, + 3008, 10112, + 3008, 10176, + 3048, 10176, + 3048, 10152, + 3056, 10152, + 3056, 10144, + 3064, 10144, + 3064, 10136, + 3072, 10136); + + // Enchanted valley + addPolygonTo(MULTICOMBAT, + 3008, 4480, + 3008, 4544, + 3072, 4544, + 3072, 4480); + + // Zulrah + addPolygonTo(MULTICOMBAT, + 2256, 3080, + 2280, 3080, + 2280, 3064, + 2256, 3064); + + // Abyssal sire and abyss + addPolygonTo(MULTICOMBAT, + 3008, 4736, + 2944, 4736, + 2944, 4864, + 3136, 4864, + 3136, 4736, + 3072, 4736, + 3072, 4800, + 3008, 4800); + } + + private static void defineDeadmanSafeZones() + { + // Varrock + addPolygonOnPlane(DEADMAN_SAFE_ZONES, 0, + 3182, 3382, + 3182, 3399, + 3174, 3399, + 3174, 3448, + 3198, 3448, + 3198, 3449, + 3197, 3449, + 3197, 3450, + 3196, 3450, + 3196, 3451, + 3195, 3451, + 3195, 3452, + 3194, 3452, + 3194, 3453, + 3193, 3453, + 3193, 3454, + 3192, 3454, + 3192, 3455, + 3191, 3455, + 3191, 3456, + 3190, 3456, + 3190, 3457, + 3185, 3457, + 3185, 3463, + 3186, 3463, + 3186, 3464, + 3187, 3464, + 3187, 3467, + 3167, 3467, + 3167, 3468, + 3163, 3468, + 3163, 3467, + 3142, 3467, + 3142, 3468, + 3141, 3468, + 3141, 3469, + 3140, 3469, + 3140, 3470, + 3139, 3470, + 3139, 3471, + 3138, 3471, + 3138, 3484, + 3139, 3484, + 3139, 3485, + 3140, 3485, + 3140, 3486, + 3141, 3486, + 3141, 3491, + 3140, 3491, + 3140, 3492, + 3139, 3492, + 3139, 3493, + 3138, 3493, + 3138, 3515, + 3139, 3515, + 3139, 3516, + 3140, 3516, + 3140, 3517, + 3141, 3517, + 3141, 3518, + 3160, 3518, + 3160, 3517, + 3161, 3517, + 3161, 3516, + 3162, 3516, + 3162, 3515, + 3167, 3515, + 3167, 3516, + 3168, 3516, + 3168, 3517, + 3169, 3517, + 3169, 3518, + 3191, 3518, + 3191, 3517, + 3192, 3517, + 3192, 3516, + 3193, 3516, + 3193, 3515, + 3194, 3515, + 3194, 3514, + 3195, 3514, + 3195, 3513, + 3196, 3513, + 3196, 3512, + 3197, 3512, + 3197, 3511, + 3198, 3511, + 3198, 3510, + 3199, 3510, + 3199, 3509, + 3200, 3509, + 3200, 3508, + 3230, 3508, + 3230, 3507, + 3231, 3507, + 3231, 3506, + 3232, 3506, + 3232, 3505, + 3233, 3505, + 3233, 3504, + 3234, 3504, + 3234, 3503, + 3235, 3503, + 3235, 3502, + 3252, 3502, + 3252, 3496, + 3253, 3496, + 3253, 3495, + 3254, 3495, + 3254, 3494, + 3255, 3494, + 3255, 3493, + 3263, 3493, + 3263, 3472, + 3264, 3472, + 3264, 3471, + 3265, 3471, + 3265, 3470, + 3266, 3470, + 3266, 3469, + 3267, 3469, + 3267, 3468, + 3268, 3468, + 3268, 3467, + 3269, 3467, + 3269, 3466, + 3270, 3466, + 3270, 3465, + 3271, 3465, + 3271, 3437, + 3274, 3437, + 3274, 3424, + 3277, 3424, + 3277, 3420, + 3274, 3420, + 3274, 3411, + 3275, 3411, + 3275, 3410, + 3276, 3410, + 3276, 3409, + 3277, 3409, + 3277, 3408, + 3288, 3408, + 3288, 3391, + 3289, 3391, + 3289, 3385, + 3290, 3385, + 3290, 3378, + 3289, 3378, + 3289, 3377, + 3288, 3377, + 3288, 3376, + 3265, 3376, + 3265, 3380, + 3253, 3380, + 3253, 3382, + 3245, 3382, + 3245, 3380, + 3242, 3380, + 3242, 3382, + 3239, 3382, + 3239, 3381, + 3209, 3381, + 3209, 3382, + 3282, 3382); + + // Lumbridge + addPolygonOnPlane(DEADMAN_SAFE_ZONES, 0, + 3201, 3257, + 3213, 3257, + 3213, 3264, + 3233, 3264, + 3233, 3257, + 3235, 3257, + 3235, 3241, + 3237, 3241, + 3237, 3237, + 3239, 3237, + 3239, 3231, + 3243, 3231, + 3243, 3220, + 3253, 3220, + 3253, 3217, + 3256, 3217, + 3256, 3212, + 3259, 3212, + 3259, 3190, + 3247, 3190, + 3247, 3191, + 3238, 3191, + 3238, 3195, + 3230, 3195, + 3230, 3201, + 3228, 3201, + 3228, 3202, + 3227, 3202, + 3227, 3205, + 3228, 3205, + 3228, 3207, + 3225, 3207, + 3225, 3206, + 3224, 3206, + 3224, 3205, + 3223, 3205, + 3223, 3204, + 3222, 3204, + 3222, 3203, + 3215, 3203, + 3215, 3202, + 3214, 3202, + 3214, 3201, + 3203, 3201, + 3203, 3202, + 3202, 3202, + 3202, 3203, + 3201, 3203, + 3201, 3217, + 3199, 3217, + 3199, 3220, + 3201, 3220); + + // Falador + addPolygonOnPlane(DEADMAN_SAFE_ZONES, 0, + 2986, 3395, + 2986, 3394, + 2987, 3394, + 2987, 3393, + 2996, 3393, + 2996, 3394, + 3002, 3394, + 3002, 3395, + 3009, 3395, + 3009, 3394, + 3010, 3394, + 3010, 3393, + 3011, 3393, + 3011, 3392, + 3021, 3392, + 3021, 3391, + 3022, 3391, + 3022, 3390, + 3041, 3390, + 3041, 3389, + 3047, 3389, + 3047, 3390, + 3062, 3390, + 3062, 3389, + 3063, 3389, + 3063, 3388, + 3064, 3388, + 3064, 3387, + 3065, 3387, + 3065, 3386, + 3066, 3386, + 3066, 3368, + 3065, 3368, + 3065, 3367, + 3064, 3367, + 3064, 3366, + 3063, 3366, + 3063, 3365, + 3062, 3365, + 3062, 3364, + 3061, 3364, + 3061, 3363, + 3060, 3363, + 3060, 3331, + 3061, 3331, + 3061, 3328, + 3058, 3328, + 3058, 3329, + 3025, 3329, + 3025, 3328, + 3024, 3328, + 3024, 3327, + 3016, 3327, + 3016, 3326, + 3015, 3326, + 3015, 3325, + 3014, 3325, + 3014, 3324, + 3013, 3324, + 3013, 3323, + 3008, 3323, + 3008, 3324, + 3006, 3324, + 3006, 3323, + 3002, 3323, + 3002, 3322, + 3001, 3322, + 3001, 3321, + 3000, 3321, + 3000, 3320, + 2999, 3320, + 2999, 3319, + 2998, 3319, + 2998, 3318, + 2997, 3318, + 2997, 3317, + 2996, 3317, + 2996, 3316, + 2992, 3316, + 2992, 3315, + 2991, 3315, + 2991, 3314, + 2990, 3314, + 2990, 3313, + 2989, 3313, + 2989, 3312, + 2988, 3312, + 2988, 3311, + 2987, 3311, + 2987, 3310, + 2986, 3310, + 2986, 3309, + 2966, 3309, + 2966, 3310, + 2956, 3310, + 2956, 3311, + 2941, 3311, + 2941, 3312, + 2940, 3312, + 2940, 3320, + 2936, 3320, + 2936, 3354, + 2937, 3354, + 2937, 3357, + 2936, 3357, + 2936, 3389, + 2937, 3389, + 2937, 3390, + 2938, 3390, + 2938, 3391, + 2939, 3391, + 2939, 3392, + 2940, 3392, + 2940, 3393, + 2943, 3393, + 2943, 3394, + 2944, 3394, + 2944, 3395, + 2950, 3395, + 2950, 3394, + 2956, 3394, + 2956, 3395); + + // Port phasmatys + addPolygonOnPlane(DEADMAN_SAFE_ZONES, 0, + 3650, 3456, + 3650, 3472, + 3651, 3472, + 3651, 3473, + 3652, 3473, + 3652, 3474, + 3653, 3474, + 3653, 3507, + 3654, 3507, + 3654, 3508, + 3668, 3508, + 3668, 3509, + 3669, 3509, + 3669, 3510, + 3670, 3510, + 3670, 3511, + 3671, 3511, + 3671, 3512, + 3672, 3512, + 3672, 3513, + 3673, 3513, + 3673, 3514, + 3674, 3514, + 3674, 3515, + 3675, 3515, + 3675, 3516, + 3676, 3516, + 3676, 3517, + 3687, 3517, + 3687, 3494, + 3690, 3494, + 3690, 3493, + 3696, 3493, + 3696, 3482, + 3699, 3482, + 3699, 3481, + 3712, 3481, + 3712, 3456); + + // Sophanem + addPolygonOnPlane(DEADMAN_SAFE_ZONES, 0, + 3274, 2752, + 3274, 2784, + 3277, 2784, + 3277, 2786, + 3274, 2786, + 3274, 2789, + 3272, 2789, + 3272, 2810, + 3322, 2810, + 3322, 2752); + + // Ardy + addPolygonOnPlane(DEADMAN_SAFE_ZONES, 0, + 2560, 3256, + 2560, 3264, + 2559, 3264, + 2559, 3328, + 2560, 3328, + 2560, 3339, + 2561, 3339, + 2561, 3340, + 2562, 3340, + 2562, 3341, + 2563, 3341, + 2563, 3342, + 2616, 3342, + 2616, 3341, + 2617, 3341, + 2617, 3340, + 2669, 3340, + 2669, 3339, + 2670, 3339, + 2670, 3338, + 2671, 3338, + 2671, 3337, + 2672, 3337, + 2672, 3336, + 2673, 3336, + 2673, 3335, + 2674, 3335, + 2674, 3334, + 2683, 3334, + 2683, 3333, + 2684, 3333, + 2684, 3332, + 2685, 3332, + 2685, 3331, + 2686, 3331, + 2686, 3330, + 2687, 3330, + 2687, 3329, + 2688, 3329, + 2688, 3264, + 2638, 3264, + 2638, 3263, + 2625, 3263, + 2625, 3264, + 2611, 3264, + 2611, 3257, + 2602, 3257, + 2602, 3264, + 2587, 3264, + 2587, 3263, + 2586, 3263, + 2586, 3262, + 2584, 3262, + 2584, 3261, + 2583, 3261, + 2583, 3260, + 2582, 3260, + 2582, 3259, + 2581, 3259, + 2581, 3258, + 2572, 3258, + 2572, 3260, + 2571, 3260, + 2571, 3261, + 2566, 3261, + 2566, 3260, + 2565, 3260, + 2565, 3259, + 2564, 3259, + 2564, 3256); + + // Yanille + addPolygonOnPlane(DEADMAN_SAFE_ZONES, 0, + 2613, 3103, + 2614, 3103, + 2614, 3102, + 2615, 3102, + 2615, 3101, + 2616, 3101, + 2616, 3100, + 2617, 3100, + 2617, 3099, + 2618, 3099, + 2618, 3098, + 2619, 3098, + 2619, 3097, + 2620, 3097, + 2620, 3075, + 2590, 3075, + 2590, 3074, + 2589, 3074, + 2589, 3073, + 2584, 3073, + 2584, 3074, + 2583, 3074, + 2583, 3075, + 2543, 3075, + 2543, 3076, + 2542, 3076, + 2542, 3077, + 2539, 3077, + 2539, 3107, + 2542, 3107, + 2542, 3108, + 2543, 3108, + 2543, 3109, + 2608, 3109, + 2608, 3108, + 2609, 3108, + 2609, 3107, + 2610, 3107, + 2610, 3106, + 2611, 3106, + 2611, 3105, + 2612, 3105, + 2612, 3104, + 2613, 3104); + + // Gnome stronghold + addPolygonOnPlane(DEADMAN_SAFE_ZONES, 0, + 2495, 3439, + 2494, 3439, + 2494, 3432, + 2495, 3432, + 2495, 3431, + 2496, 3431, + 2496, 3430, + 2497, 3430, + 2497, 3429, + 2498, 3429, + 2498, 3417, + 2497, 3417, + 2497, 3416, + 2496, 3416, + 2496, 3412, + 2495, 3412, + 2495, 3408, + 2494, 3408, + 2494, 3404, + 2495, 3404, + 2495, 3403, + 2496, 3403, + 2496, 3402, + 2497, 3402, + 2497, 3401, + 2498, 3401, + 2498, 3400, + 2499, 3400, + 2499, 3399, + 2500, 3399, + 2500, 3398, + 2501, 3398, + 2501, 3397, + 2502, 3397, + 2502, 3396, + 2506, 3396, + 2506, 3391, + 2502, 3391, + 2502, 3390, + 2492, 3390, + 2492, 3391, + 2489, 3391, + 2489, 3390, + 2488, 3390, + 2488, 3389, + 2485, 3389, + 2485, 3390, + 2482, 3390, + 2482, 3389, + 2476, 3389, + 2476, 3390, + 2471, 3390, + 2471, 3391, + 2468, 3391, + 2468, 3390, + 2467, 3390, + 2467, 3389, + 2466, 3389, + 2466, 3385, + 2465, 3385, + 2465, 3384, + 2458, 3384, + 2458, 3385, + 2457, 3385, + 2457, 3389, + 2456, 3389, + 2456, 3390, + 2455, 3390, + 2455, 3391, + 2450, 3391, + 2450, 3390, + 2446, 3390, + 2446, 3391, + 2443, 3391, + 2443, 3390, + 2442, 3390, + 2442, 3389, + 2440, 3389, + 2440, 3388, + 2434, 3388, + 2434, 3389, + 2433, 3389, + 2433, 3390, + 2432, 3390, + 2432, 3391, + 2428, 3391, + 2428, 3392, + 2427, 3392, + 2427, 3393, + 2420, 3393, + 2420, 3394, + 2419, 3394, + 2419, 3395, + 2418, 3395, + 2418, 3396, + 2417, 3396, + 2417, 3397, + 2416, 3397, + 2416, 3399, + 2415, 3399, + 2415, 3400, + 2414, 3400, + 2414, 3408, + 2413, 3408, + 2413, 3409, + 2412, 3409, + 2412, 3410, + 2411, 3410, + 2411, 3411, + 2410, 3411, + 2410, 3412, + 2387, 3412, + 2387, 3407, + 2383, 3407, + 2383, 3408, + 2380, 3408, + 2380, 3409, + 2379, 3409, + 2379, 3410, + 2377, 3410, + 2377, 3411, + 2376, 3411, + 2376, 3413, + 2375, 3413, + 2375, 3417, + 2374, 3417, + 2374, 3418, + 2373, 3418, + 2373, 3419, + 2372, 3419, + 2372, 3420, + 2371, 3420, + 2371, 3421, + 2370, 3421, + 2370, 3422, + 2369, 3422, + 2369, 3433, + 2370, 3433, + 2370, 3434, + 2371, 3434, + 2371, 3444, + 2372, 3444, + 2372, 3445, + 2373, 3445, + 2373, 3446, + 2374, 3446, + 2374, 3447, + 2375, 3447, + 2375, 3459, + 2376, 3459, + 2376, 3460, + 2377, 3460, + 2377, 3461, + 2378, 3461, + 2378, 3462, + 2379, 3462, + 2379, 3463, + 2380, 3463, + 2380, 3464, + 2381, 3464, + 2381, 3476, + 2379, 3476, + 2379, 3477, + 2378, 3477, + 2378, 3478, + 2377, 3478, + 2377, 3485, + 2376, 3485, + 2376, 3486, + 2375, 3486, + 2375, 3499, + 2376, 3499, + 2376, 3500, + 2377, 3500, + 2377, 3507, + 2378, 3507, + 2378, 3508, + 2379, 3508, + 2379, 3509, + 2380, 3509, + 2380, 3521, + 2382, 3521, + 2382, 3522, + 2384, 3522, + 2384, 3523, + 2393, 3523, + 2393, 3524, + 2399, 3524, + 2399, 3525, + 2404, 3525, + 2404, 3524, + 2405, 3524, + 2405, 3523, + 2407, 3523, + 2407, 3522, + 2415, 3522, + 2415, 3521, + 2425, 3521, + 2425, 3522, + 2427, 3522, + 2427, 3523, + 2430, 3523, + 2430, 3522, + 2431, 3522, + 2431, 3521, + 2432, 3521, + 2432, 3520, + 2448, 3520, + 2448, 3517, + 2454, 3517, + 2454, 3516, + 2455, 3516, + 2455, 3515, + 2456, 3515, + 2456, 3514, + 2457, 3514, + 2457, 3513, + 2460, 3513, + 2460, 3512, + 2461, 3512, + 2461, 3511, + 2465, 3511, + 2465, 3510, + 2468, 3510, + 2468, 3511, + 2472, 3511, + 2472, 3512, + 2473, 3512, + 2473, 3513, + 2475, 3513, + 2475, 3514, + 2476, 3514, + 2476, 3515, + 2477, 3515, + 2477, 3516, + 2478, 3516, + 2478, 3517, + 2483, 3517, + 2483, 3516, + 2487, 3516, + 2487, 3515, + 2488, 3515, + 2488, 3512, + 2487, 3512, + 2487, 3509, + 2488, 3509, + 2488, 3508, + 2489, 3508, + 2489, 3507, + 2491, 3507, + 2491, 3506, + 2492, 3506, + 2492, 3505, + 2493, 3505, + 2493, 3499, + 2492, 3499, + 2492, 3498, + 2491, 3498, + 2491, 3497, + 2490, 3497, + 2490, 3495, + 2491, 3495, + 2491, 3494, + 2492, 3494, + 2492, 3493, + 2493, 3493, + 2493, 3485, + 2490, 3485, + 2490, 3484, + 2489, 3484, + 2489, 3483, + 2488, 3483, + 2488, 3482, + 2487, 3482, + 2487, 3481, + 2486, 3481, + 2486, 3474, + 2488, 3474, + 2488, 3471, + 2489, 3471, + 2489, 3470, + 2490, 3470, + 2490, 3460, + 2491, 3460, + 2491, 3456, + 2496, 3456, + 2496, 3440, + 2495, 3440); + + // Rellekka + addPolygonOnPlane(DEADMAN_SAFE_ZONES, 0, + 2620, 3682, + 2624, 3682, + 2624, 3683, + 2625, 3683, + 2625, 3687, + 2629, 3687, + 2629, 3686, + 2630, 3686, + 2630, 3685, + 2632, 3685, + 2632, 3686, + 2636, 3686, + 2636, 3692, + 2645, 3692, + 2645, 3695, + 2647, 3695, + 2647, 3696, + 2649, 3696, + 2649, 3702, + 2650, 3702, + 2650, 3703, + 2651, 3703, + 2651, 3704, + 2652, 3704, + 2652, 3711, + 2653, 3711, + 2653, 3712, + 2691, 3712, + 2691, 3709, + 2692, 3709, + 2692, 3707, + 2693, 3707, + 2693, 3703, + 2692, 3703, + 2692, 3701, + 2691, 3701, + 2691, 3699, + 2690, 3699, + 2690, 3695, + 2691, 3695, + 2691, 3693, + 2692, 3693, + 2692, 3691, + 2693, 3691, + 2693, 3685, + 2692, 3685, + 2692, 3683, + 2691, 3683, + 2691, 3681, + 2690, 3681, + 2690, 3680, + 2689, 3680, + 2689, 3672, + 2690, 3672, + 2690, 3671, + 2691, 3671, + 2691, 3666, + 2690, 3666, + 2690, 3664, + 2689, 3664, + 2689, 3660, + 2690, 3660, + 2690, 3658, + 2691, 3658, + 2691, 3656, + 2692, 3656, + 2692, 3654, + 2693, 3654, + 2693, 3651, + 2692, 3651, + 2692, 3649, + 2690, 3649, + 2690, 3648, + 2688, 3648, + 2688, 3647, + 2686, 3647, + 2686, 3646, + 2673, 3646, + 2673, 3645, + 2636, 3645, + 2636, 3647, + 2627, 3647, + 2627, 3648, + 2625, 3648, + 2625, 3649, + 2624, 3649, + 2624, 3650, + 2622, 3650, + 2622, 3651, + 2620, 3651, + 2620, 3652, + 2618, 3652, + 2618, 3653, + 2616, 3653, + 2616, 3654, + 2609, 3654, + 2609, 3655, + 2607, 3655, + 2607, 3656, + 2603, 3656, + 2603, 3657, + 2602, 3657, + 2602, 3658, + 2601, 3658, + 2601, 3663, + 2602, 3663, + 2602, 3664, + 2603, 3664, + 2603, 3665, + 2604, 3665, + 2604, 3666, + 2605, 3666, + 2605, 3667, + 2606, 3667, + 2606, 3671, + 2609, 3671, + 2609, 3672, + 2610, 3672, + 2610, 3673, + 2611, 3673, + 2611, 3675, + 2612, 3675, + 2612, 3676, + 2614, 3676, + 2614, 3677, + 2616, 3677, + 2616, 3679, + 2618, 3679, + 2618, 3681, + 2620, 3681); + + // Jatizo + addPolygonOnPlane(DEADMAN_SAFE_ZONES, 0, + 2407, 3797, + 2407, 3793, + 2399, 3793, + 2399, 3792, + 2391, 3792, + 2391, 3791, + 2386, 3791, + 2386, 3796, + 2388, 3796, + 2388, 3802, + 2386, 3802, + 2386, 3807, + 2388, 3807, + 2388, 3809, + 2402, 3809, + 2402, 3819, + 2406, 3819, + 2406, 3824, + 2408, 3824, + 2408, 3826, + 2413, 3826, + 2413, 3824, + 2419, 3824, + 2419, 3826, + 2424, 3826, + 2424, 3821, + 2423, 3821, + 2423, 3798, + 2422, 3798, + 2422, 3797); + + // Neitiznot + addPolygonOnPlane(DEADMAN_SAFE_ZONES, 0, + 2329, 3812, + 2333, 3812, + 2333, 3813, + 2334, 3813, + 2334, 3814, + 2335, 3814, + 2335, 3815, + 2338, 3815, + 2338, 3816, + 2339, 3816, + 2339, 3817, + 2368, 3817, + 2368, 3776, + 2352, 3776, + 2352, 3796, + 2344, 3796, + 2344, 3795, + 2331, 3795, + 2331, 3797, + 2330, 3797, + 2330, 3798, + 2329, 3798); + + // Pest control + addPolygonOnPlane(DEADMAN_SAFE_ZONES, 0, + 2624, 2688, + 2688, 2688, + 2688, 2624, + 2624, 2624); + + // Tutorial island + addPolygonOnPlane(DEADMAN_SAFE_ZONES, 0, + 3052, 3135, + 3156, 3135, + 3156, 3057, + 3052, 3057); + + // Camelot bank + addPolygonOnPlane(DEADMAN_SAFE_ZONES, 0, + 2724, 3487, + 2724, 3490, + 2721, 3490, + 2721, 3494, + 2719, 3494, + 2719, 3497, + 2721, 3497, + 2721, 3498, + 2731, 3498, + 2731, 3490, + 2728, 3490, + 2728, 3487); + + // Catherby bank + addPolygonOnPlane(DEADMAN_SAFE_ZONES, 0, + 2806, 3438, + 2806, 3446, + 2813, 3446, + 2813, 3438); + + // Kourend castle + addPolygonOnPlane(DEADMAN_SAFE_ZONES, 0, + 1627, 3658, + 1620, 3658, + 1620, 3657, + 1619, 3657, + 1619, 3656, + 1614, 3656, + 1614, 3657, + 1613, 3657, + 1613, 3661, + 1612, 3661, + 1612, 3662, + 1611, 3662, + 1611, 3663, + 1600, 3663, + 1600, 3662, + 1599, 3662, + 1599, 3661, + 1594, 3661, + 1594, 3662, + 1593, 3662, + 1593, 3685, + 1594, 3685, + 1594, 3686, + 1599, 3686, + 1599, 3685, + 1600, 3685, + 1600, 3684, + 1611, 3684, + 1611, 3685, + 1612, 3685, + 1612, 3686, + 1613, 3686, + 1613, 3690, + 1614, 3690, + 1614, 3691, + 1619, 3691, + 1619, 3690, + 1620, 3690, + 1620, 3689, + 1630, 3689, + 1630, 3686, + 1620, 3686, + 1620, 3685, + 1619, 3685, + 1619, 3683, + 1620, 3683, + 1620, 3682, + 1621, 3682, + 1621, 3681, + 1622, 3681, + 1622, 3680, + 1623, 3680, + 1623, 3679, + 1624, 3679, + 1624, 3668, + 1623, 3668, + 1623, 3667, + 1622, 3667, + 1622, 3666, + 1621, 3666, + 1621, 3665, + 1620, 3665, + 1620, 3664, + 1619, 3664, + 1619, 3662, + 1620, 3662, + 1620, 3661, + 1627, 3661); + } + + private static void definePvpSafeZones() + { + // Grand exchange + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 3159, 3473, + 3159, 3474, + 3157, 3474, + 3157, 3475, + 3155, 3475, + 3155, 3476, + 3153, 3476, + 3153, 3477, + 3152, 3477, + 3152, 3478, + 3151, 3478, + 3151, 3480, + 3150, 3480, + 3150, 3482, + 3149, 3482, + 3149, 3484, + 3148, 3484, + 3148, 3496, + 3149, 3496, + 3149, 3498, + 3150, 3498, + 3150, 3500, + 3151, 3500, + 3151, 3502, + 3152, 3502, + 3152, 3503, + 3153, 3503, + 3153, 3504, + 3155, 3504, + 3155, 3505, + 3157, 3505, + 3157, 3506, + 3159, 3506, + 3159, 3507, + 3171, 3507, + 3171, 3506, + 3173, 3506, + 3173, 3505, + 3175, 3505, + 3175, 3504, + 3177, 3504, + 3177, 3503, + 3178, 3503, + 3178, 3502, + 3179, 3502, + 3179, 3500, + 3180, 3500, + 3180, 3498, + 3181, 3498, + 3181, 3496, + 3182, 3496, + 3182, 3484, + 3181, 3484, + 3181, 3482, + 3180, 3482, + 3180, 3480, + 3179, 3480, + 3179, 3478, + 3178, 3478, + 3178, 3477, + 3177, 3477, + 3177, 3476, + 3175, 3476, + 3175, 3475, + 3173, 3475, + 3173, 3474, + 3171, 3474, + 3171, 3473); + + // Edgeville + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 3091, 3488, + 3091, 3493, + 3090, 3493, + 3090, 3498, + 3091, 3498, + 3091, 3500, + 3099, 3500, + 3099, 3488); + + // Fally west bank + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 2943, 3368, + 2943, 3374, + 2948, 3374, + 2948, 3370, + 2950, 3370, + 2950, 3366, + 2949, 3366, + 2949, 3359, + 2945, 3359, + 2945, 3362, + 2946, 3362, + 2946, 3366, + 2945, 3366, + 2945, 3368); + + // Fally east bank + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 3009, 3353, + 3009, 3359, + 3019, 3359, + 3019, 3357, + 3022, 3357, + 3022, 3353); + + // Fally castle + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 2964, 3354, + 2966, 3354, + 2966, 3352, + 2967, 3352, + 2967, 3349, + 2976, 3349, + 2976, 3348, + 2977, 3348, + 2977, 3347, + 2981, 3347, + 2981, 3343, + 2982, 3343, + 2982, 3339, + 2981, 3339, + 2981, 3337, + 2967, 3337, + 2967, 3330, + 2963, 3330, + 2963, 3331, + 2962, 3331, + 2962, 3332, + 2961, 3332, + 2961, 3334, + 2964, 3334, + 2964, 3335, + 2965, 3335, + 2965, 3343, + 2964, 3343, + 2964, 3344, + 2961, 3344, + 2961, 3350, + 2963, 3350, + 2963, 3352, + 2964, 3352); + + // Varrock east bank + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 3250, 3425, + 3258, 3425, + 3258, 3416, + 3250, 3416); + + // Varrock west bank + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 3180, 3433, + 3180, 3448, + 3191, 3448, + 3191, 3433); + + // Port phasmatys + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 3686, 3472, + 3700, 3472, + 3700, 3461, + 3686, 3461); + + // Yanille bank + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 2609, 3088, + 2609, 3098, + 2617, 3098, + 2617, 3088); + + // Ardy east bank + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 2649, 3280, + 2649, 3288, + 2659, 3288, + 2659, 3280); + + // Ardy west bank + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 2612, 3330, + 2612, 3336, + 2615, 3336, + 2615, 3335, + 2619, 3335, + 2619, 3336, + 2622, 3336, + 2622, 3330); + + // Fishing guild bank + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 2593, 3413, + 2588, 3413, + 2588, 3418, + 2583, 3418, + 2583, 3423, + 2590, 3423, + 2590, 3420, + 2593, 3420); + + // Gnome stronghold bank near slayer cave (2nd floor) + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 1, + 2444, 3431, + 2444, 3435, + 2448, 3435, + 2448, 3431, + 2447, 3431, + 2447, 3428, + 2449, 3428, + 2449, 3422, + 2447, 3422, + 2447, 3419, + 2448, 3419, + 2448, 3415, + 2444, 3415, + 2444, 3419, + 2445, 3419, + 2445, 3422, + 2443, 3422, + 2443, 3428, + 2445, 3428, + 2445, 3431); + + // Gnome stronghold bank in grand tree + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 1, + 2456, 3488, + 2452, 3488, + 2452, 3486, + 2450, 3486, + 2450, 3483, + 2451, 3483, + 2451, 3478, + 2448, 3478, + 2448, 3483, + 2449, 3483, + 2449, 3486, + 2447, 3486, + 2447, 3488, + 2443, 3488, + 2443, 3487, + 2438, 3487, + 2438, 3490, + 2443, 3490, + 2443, 3489, + 2447, 3489, + 2447, 3491, + 2449, 3491, + 2449, 3494, + 2448, 3494, + 2448, 3496, + 2451, 3496, + 2451, 3494, + 2450, 3494, + 2450, 3491, + 2452, 3491, + 2452, 3489, + 2456, 3489); + + // Al kharid bank + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 3265, 3161, + 3265, 3174, + 3273, 3174, + 3273, 3161); + + // Shantay pass bank + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 3308, 3119, + 3308, 3125, + 3310, 3125, + 3310, 3119); + + // Nardah bank + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 3431, 2891, + 3431, 2889, + 3427, 2889, + 3427, 2887, + 3424, 2887, + 3424, 2895, + 3431, 2895, + 3431, 2893, + 3432, 2893, + 3432, 2891); + + // Sophanem bank + addPolygonTo(PVP_WORLD_SAFE_ZONES, + 2807, 5158, + 2792, 5158, + 2792, 5175, + 2807, 5175); + + // Canifis bank + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 3509, 3474, + 3509, 3478, + 3508, 3478, + 3508, 3483, + 3509, 3483, + 3509, 3484, + 3517, 3484, + 3517, 3477, + 3516, 3477, + 3516, 3476, + 3513, 3476, + 3513, 3474); + + // Lumbridge castle outside + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 3216, 3209, + 3216, 3210, + 3217, 3210, + 3217, 3228, + 3216, 3228, + 3216, 3229, + 3227, 3229, + 3227, 3221, + 3230, 3221, + 3230, 3217, + 3227, 3217, + 3227, 3209); + + // Lumbridge bank upstairs + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 2, + 3211, 3223, + 3211, 3215, + 3207, 3215, + 3207, 3223); + + // Draynor bank + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 3098, 3240, + 3088, 3240, + 3088, 3247, + 3098, 3247); + + // Pest control bank + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 2665, 2656, + 2670, 2656, + 2670, 2651, + 2665, 2651); + + // Shilo village bank + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 2843, 2957, + 2846, 2957, + 2846, 2956, + 2849, 2956, + 2849, 2957, + 2850, 2957, + 2850, 2958, + 2855, 2958, + 2855, 2957, + 2856, 2957, + 2856, 2956, + 2858, 2956, + 2858, 2957, + 2862, 2957, + 2862, 2952, + 2858, 2952, + 2858, 2953, + 2856, 2953, + 2856, 2952, + 2855, 2952, + 2855, 2951, + 2850, 2951, + 2850, 2952, + 2849, 2952, + 2849, 2953, + 2847, 2953, + 2847, 2952, + 2843, 2952); + + // Legends guild bank + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 2, + 2731, 3374, + 2731, 3383, + 2734, 3383, + 2734, 3374); + + // Legends guild middle floor + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 1, + 2724, 3374, + 2724, 3383, + 2734, 3383, + 2734, 3382, + 2736, 3382, + 2736, 3375, + 2734, 3375, + 2734, 3374); + + // Warriors guild bank + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 2843, 3537, + 2843, 3540, + 2841, 3540, + 2841, 3546, + 2849, 3546, + 2849, 3537, + 2847, 3537, + 2847, 3536, + 2846, 3536, + 2846, 3537); + + // Camelot bank + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 2724, 3487, + 2724, 3490, + 2721, 3490, + 2721, 3494, + 2719, 3494, + 2719, 3497, + 2721, 3497, + 2721, 3498, + 2731, 3498, + 2731, 3490, + 2728, 3490, + 2728, 3487); + + // Camelot respawn point + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 2761, 3483, + 2761, 3476, + 2755, 3476, + 2755, 3483); + + // Catherby bank + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 2806, 3438, + 2806, 3446, + 2813, 3446, + 2813, 3438); + + // Barbarian outpost bank + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 2536, 3572, + 2536, 3575, + 2538, 3575, + 2538, 3572); + + // Piscatoris bank + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 2327, 3686, + 2327, 3694, + 2333, 3694, + 2333, 3686); + + // Lletya bank + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 2350, 3161, + 2350, 3165, + 2351, 3165, + 2351, 3167, + 2357, 3167, + 2357, 3165, + 2356, 3165, + 2356, 3164, + 2355, 3164, + 2355, 3161); + + // Castle wars bank + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 2446, 3087, + 2445, 3087, + 2445, 3085, + 2447, 3085, + 2447, 3081, + 2443, 3081, + 2443, 3082, + 2439, 3082, + 2439, 3081, + 2435, 3081, + 2435, 3099, + 2439, 3099, + 2439, 3098, + 2443, 3098, + 2443, 3099, + 2447, 3099, + 2447, 3095, + 2445, 3095, + 2445, 3093, + 2446, 3093); + + // Duel arena bank + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 3380, 3267, + 3380, 3273, + 3381, 3273, + 3381, 3274, + 3385, 3274, + 3385, 3267); + + // Clan wars bank + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 3375, 3165, + 3361, 3165, + 3361, 3173, + 3375, 3173); + + // Lumbridge cellar bank + addPolygonTo(PVP_WORLD_SAFE_ZONES, + 3218, 9622, + 3218, 9624, + 3220, 9624, + 3220, 9622); + + // Dorgesh kaan bank + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 2709, 5348, + 2707, 5348, + 2707, 5345, + 2701, 5345, + 2701, 5347, + 2697, 5347, + 2697, 5353, + 2701, 5353, + 2701, 5355, + 2707, 5355, + 2707, 5350, + 2709, 5350); + + // Keldagrim bank + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 2842, 10204, + 2834, 10204, + 2834, 10216, + 2842, 10216); + + // Tzhaar bank + addPolygonTo(PVP_WORLD_SAFE_ZONES, + 2438, 5176, + 2438, 5180, + 2441, 5180, + 2441, 5182, + 2449, 5182, + 2449, 5181, + 2450, 5181, + 2450, 5180, + 2452, 5180, + 2452, 5175, + 2441, 5175, + 2441, 5176); + + // Inferno bank + addPolygonTo(PVP_WORLD_SAFE_ZONES, + 2542, 5135, + 2542, 5139, + 2539, 5139, + 2539, 5140, + 2538, 5140, + 2538, 5141, + 2537, 5141, + 2537, 5144, + 2541, 5144, + 2541, 5145, + 2543, 5145, + 2543, 5144, + 2544, 5144, + 2544, 5142, + 2545, 5142, + 2545, 5135); + + // Port khazard bank + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 2661, 3160, + 2661, 3163, + 2666, 3163, + 2666, 3160); + + // Corsair cove bank + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 2569, 2863, + 2569, 2868, + 2572, 2868, + 2572, 2863); + + // Burgh de rott bank + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 3495, 3210, + 3495, 3214, + 3501, 3214, + 3501, 3210); + + // Edgeville respawn point + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 3092, 3468, + 3092, 3474, + 3098, 3474, + 3098, 3468); + + // Mage bank + addPolygonTo(PVP_WORLD_SAFE_ZONES, + 2529, 4711, + 2529, 4724, + 2548, 4724, + 2548, 4711); + + // Lunar bank + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 2097, 3917, + 2097, 3922, + 2105, 3922, + 2105, 3917); + + // Jatizo bank + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 2414, 3801, + 2414, 3804, + 2420, 3804, + 2420, 3801); + + // Neitiznot bank + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 2334, 3805, + 2334, 3809, + 2340, 3809, + 2340, 3805); + + // Hosidius bank + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 1671, 3558, + 1671, 3577, + 1682, 3577, + 1682, 3558); + + // Woodcutting guild bank + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 1589, 3475, + 1589, 3481, + 1594, 3481, + 1594, 3475); + + // Lands end bank + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 1508, 3415, + 1508, 3424, + 1514, 3424, + 1514, 3415); + + // Chambers of xeric bank + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 1252, 3570, + 1252, 3574, + 1257, 3574, + 1257, 3570); + + // Arceuus bank + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 1621, 3736, + 1621, 3754, + 1627, 3754, + 1627, 3751, + 1633, 3751, + 1633, 3754, + 1639, 3754, + 1639, 3736); + + // Piscarilius bank + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 1794, 3784, + 1794, 3794, + 1812, 3794, + 1812, 3784); + + // Lovakengj bank southeast + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 1518, 3735, + 1518, 3744, + 1535, 3744, + 1535, 3735); + + // Lovakenj bank west + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 1433, 3820, + 1433, 3837, + 1442, 3837, + 1442, 3820); + + // Lovakenj sulphur mine bank + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 1452, 3855, + 1452, 3860, + 1455, 3860, + 1455, 3855); + + // Blast mine bank southeast + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 1500, 3856, + 1500, 3858, + 1503, 3858, + 1503, 3856); + + // Wintertodt bank + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 1638, 3942, + 1638, 3947, + 1642, 3947, + 1642, 3942); + + // Shayzien bank + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 1495, 3612, + 1495, 3622, + 1515, 3622, + 1515, 3612); + + // Hosidius grape farm bank + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 1804, 3571, + 1804, 3572, + 1808, 3572, + 1808, 3571); + + // Hosidius cooking bank + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 1652, 3605, + 1652, 3615, + 1661, 3615, + 1661, 3605); + + // Ecteria bank + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 2618, 3893, + 2618, 3897, + 2622, 3897, + 2622, 3893); + + // Mining guild expanded area + addPolygonTo(PVP_WORLD_SAFE_ZONES, + 3018, 9733, + 3021, 9733, + 3021, 9729, + 3022, 9729, + 3022, 9728, + 3023, 9728, + 3023, 9727, + 3025, 9727, + 3025, 9726, + 3026, 9726, + 3026, 9725, + 3030, 9725, + 3030, 9726, + 3032, 9726, + 3032, 9727, + 3035, 9727, + 3035, 9726, + 3038, 9726, + 3038, 9727, + 3041, 9727, + 3041, 9728, + 3042, 9728, + 3042, 9730, + 3045, 9730, + 3045, 9727, + 3047, 9727, + 3047, 9726, + 3048, 9726, + 3048, 9724, + 3052, 9724, + 3052, 9725, + 3053, 9725, + 3053, 9726, + 3055, 9726, + 3055, 9725, + 3056, 9725, + 3056, 9723, + 3057, 9723, + 3057, 9720, + 3056, 9720, + 3056, 9719, + 3054, 9719, + 3054, 9718, + 3052, 9718, + 3052, 9717, + 3050, 9717, + 3050, 9718, + 3045, 9718, + 3045, 9716, + 3044, 9716, + 3044, 9715, + 3041, 9715, + 3041, 9714, + 3039, 9714, + 3039, 9713, + 3037, 9713, + 3037, 9714, + 3036, 9714, + 3036, 9715, + 3034, 9715, + 3034, 9716, + 3029, 9716, + 3029, 9715, + 3028, 9715, + 3028, 9714, + 3026, 9714, + 3026, 9709, + 3027, 9709, + 3027, 9708, + 3028, 9708, + 3028, 9705, + 3029, 9705, + 3029, 9701, + 3028, 9701, + 3028, 9700, + 3027, 9700, + 3027, 9699, + 3023, 9699, + 3023, 9700, + 3019, 9700, + 3019, 9701, + 3018, 9701, + 3018, 9705, + 3019, 9705, + 3019, 9707, + 3020, 9707, + 3020, 9708, + 3021, 9708, + 3021, 9709, + 3022, 9709, + 3022, 9713, + 3021, 9713, + 3021, 9714, + 3019, 9714, + 3019, 9715, + 3018, 9715, + 3018, 9717, + 3015, 9717, + 3015, 9716, + 3013, 9716, + 3013, 9717, + 3012, 9717, + 3012, 9720, + 3013, 9720, + 3013, 9721, + 3015, 9721, + 3015, 9723, + 3016, 9723, + 3016, 9727, + 3017, 9727, + 3017, 9730, + 3018, 9730); + + // Motherlode mine bank + addPolygonTo(PVP_WORLD_SAFE_ZONES, + 3760, 5671, + 3760, 5668, + 3761, 5668, + 3761, 5665, + 3760, 5665, + 3760, 5663, + 3758, 5663, + 3758, 5671); + + // Mos le harmles bank + addPolygonOnPlane(PVP_WORLD_SAFE_ZONES, 0, + 3679, 2980, + 3679, 2985, + 3681, 2985, + 3681, 2984, + 3682, 2984, + 3682, 2985, + 3684, 2985, + 3684, 2980, + 3682, 2980, + 3682, 2981, + 3681, 2981, + 3681, 2980); + + // Zanaris bank + addPolygonTo(PVP_WORLD_SAFE_ZONES, + 2388, 4454, + 2380, 4454, + 2380, 4463, + 2388, 4463); + + // Wodcuting guild bank underground + addPolygonTo(PVP_WORLD_SAFE_ZONES, + 1550, 9872, + 1550, 9874, + 1553, 9874, + 1553, 9872); + } + + private static void defineWilderness() + { + // Above ground + addPolygonTo(ROUGH_WILDERNESS, + 2944, 3523, + 3392, 3523, + 3392, 3971, + 2944, 3971); + + // Underground + addPolygonTo(ROUGH_WILDERNESS, + 2944, 9918, + 2944, 10360, + 3264, 10360, + 3264, 9918); + } + + private static void addPolygonTo(List[] shapes, int... coords) + { + Polygon poly = new Polygon(); + for (int i = 0; i < coords.length; i += 2) + { + poly.addPoint(coords[i], coords[i + 1]); + } + for (int i = 0; i < shapes.length; i++) + { + shapes[i].add(poly); + } + } + + private static void addPolygonOnPlane(List[] shapes, int plane, int... coords) + { + Polygon poly = new Polygon(); + for (int i = 0; i < coords.length; i += 2) + { + poly.addPoint(coords[i], coords[i + 1]); + } + shapes[plane].add(poly); + } + + private static void addPolygonOnPlanes(List[] shapes, int minPlane, int maxPlane, int... coords) + { + Polygon poly = new Polygon(); + for (int i = 0; i < coords.length; i += 2) + { + poly.addPoint(coords[i], coords[i + 1]); + } + for (int i = minPlane; i <= maxPlane; i++) + { + shapes[i].add(poly); + } + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/zoneIndicators/ZoneIndicatorsConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/zoneIndicators/ZoneIndicatorsConfig.java new file mode 100644 index 0000000000..370533dac7 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/zoneIndicators/ZoneIndicatorsConfig.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2018, Woox + * 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.zoneIndicators; + +import java.awt.Color; +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("zoneIndicators") +public interface ZoneIndicatorsConfig extends Config +{ + @ConfigItem( + keyName = "multicombatZoneVisibility", + name = "Multicombat zones", + description = "Determine where multicombat zones should be shown", + position = 1 + ) + default ZoneVisibility multicombatZoneVisibility() + { + return ZoneVisibility.SHOW_IN_PVP; + } + + @ConfigItem( + keyName = "pvpSafeZones", + name = "PvP safe zones", + description = "Show safe zones in PvP worlds", + position = 2 + ) + default boolean showPvpSafeZones() + { + return true; + } + + @ConfigItem( + keyName = "deadmanSafeZones", + name = "Deadman safe zones", + description = "Show safe zones in Deadman worlds", + position = 3 + ) + default boolean showDeadmanSafeZones() + { + return true; + } + + @ConfigItem( + keyName = "collisionDetection", + name = "Collision detection", + description = "Only show lines where they can be walked through", + position = 4 + ) + default boolean collisionDetection() + { + return false; + } + + @ConfigItem( + keyName = "showMinimapLines", + name = "Show on minimap", + description = "Show multicombat and safe zones on the minimap", + position = 5 + ) + default boolean showMinimapLines() + { + return true; + } + + @ConfigItem( + keyName = "multicombatColor", + name = "Multicombat zone color", + description = "Choose color to use for marking multicombat zones", + position = 6 + ) + default Color multicombatColor() + { + return Color.MAGENTA; + } + + @ConfigItem( + keyName = "safeZoneColor", + name = "Safe zone color", + description = "Choose color to use for marking safe zones in PvP/Deadman", + position = 7 + ) + default Color safeZoneColor() + { + return Color.GREEN; + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/zoneIndicators/ZoneIndicatorsMinimapOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/zoneIndicators/ZoneIndicatorsMinimapOverlay.java new file mode 100644 index 0000000000..699b0f0ddf --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/zoneIndicators/ZoneIndicatorsMinimapOverlay.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2018, Woox + * 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.zoneIndicators; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.geom.GeneralPath; +import javax.inject.Inject; +import net.runelite.api.Client; +import net.runelite.api.Perspective; +import net.runelite.api.Point; +import net.runelite.api.coords.LocalPoint; +import net.runelite.api.geometry.Geometry; +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.OverlayPriority; + +public class ZoneIndicatorsMinimapOverlay extends Overlay +{ + private final static int MAX_LOCAL_DRAW_LENGTH = 20 * Perspective.LOCAL_TILE_SIZE; + + @Inject + private Client client; + + @Inject + private ZoneIndicatorsPlugin plugin; + + @Inject + private ZoneIndicatorsConfig config; + + @Inject + public ZoneIndicatorsMinimapOverlay() + { + setPosition(OverlayPosition.DYNAMIC); + setLayer(OverlayLayer.ALWAYS_ON_TOP); + setPriority(OverlayPriority.LOW); + } + + private Color getTransparentColorVersion(Color c) + { + return new Color(c.getRed(), c.getGreen(), c.getBlue(), 192); + } + + private void renderPath(Graphics2D graphics, GeneralPath path, Color color) + { + LocalPoint playerLp = client.getLocalPlayer().getLocalLocation(); + Rectangle viewArea = new Rectangle( + playerLp.getX() - MAX_LOCAL_DRAW_LENGTH, + playerLp.getY() - MAX_LOCAL_DRAW_LENGTH, + MAX_LOCAL_DRAW_LENGTH * 2, + MAX_LOCAL_DRAW_LENGTH * 2); + + graphics.setColor(color); + + path = Geometry.clipPath(path, viewArea); + path = Geometry.filterPath(path, (p1, p2) -> + Perspective.localToMinimap(client, new LocalPoint((int)p1[0], (int)p1[1])) != null && + Perspective.localToMinimap(client, new LocalPoint((int)p2[0], (int)p2[1])) != null); + path = Geometry.transformPath(path, coords -> + { + Point point = Perspective.localToMinimap(client, new LocalPoint((int)coords[0], (int)coords[1])); + coords[0] = point.getX(); + coords[1] = point.getY(); + }); + + graphics.draw(path); + } + + @Override + public Dimension render(Graphics2D graphics) + { + if (!config.showMinimapLines()) + { + return null; + } + + GeneralPath multicombatPath = plugin.getMulticombatPathToDisplay()[client.getPlane()]; + GeneralPath pvpPath = plugin.getPvpPathToDisplay()[client.getPlane()]; + + if (config.multicombatZoneVisibility() != ZoneVisibility.HIDE && multicombatPath != null) + { + renderPath(graphics, multicombatPath, getTransparentColorVersion(config.multicombatColor())); + } + if ((config.showPvpSafeZones() || config.showDeadmanSafeZones()) && pvpPath != null) + { + renderPath(graphics, pvpPath, getTransparentColorVersion(config.safeZoneColor())); + } + + return null; + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/zoneIndicators/ZoneIndicatorsOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/zoneIndicators/ZoneIndicatorsOverlay.java new file mode 100644 index 0000000000..0f52222e8b --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/zoneIndicators/ZoneIndicatorsOverlay.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2018, Woox + * 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.zoneIndicators; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.geom.GeneralPath; +import javax.inject.Inject; +import net.runelite.api.Client; +import net.runelite.api.Perspective; +import net.runelite.api.Point; +import net.runelite.api.coords.LocalPoint; +import net.runelite.api.geometry.Geometry; +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.OverlayPriority; + +public class ZoneIndicatorsOverlay extends Overlay +{ + private final static int MAX_LOCAL_DRAW_LENGTH = 20 * Perspective.LOCAL_TILE_SIZE; + + @Inject + private Client client; + + @Inject + private ZoneIndicatorsPlugin plugin; + + @Inject + private ZoneIndicatorsConfig config; + + @Inject + public ZoneIndicatorsOverlay() + { + setPosition(OverlayPosition.DYNAMIC); + setLayer(OverlayLayer.ABOVE_SCENE); + setPriority(OverlayPriority.LOW); + } + + private Color getTransparentColorVersion(Color c) + { + return new Color(c.getRed(), c.getGreen(), c.getBlue(), 92); + } + + private void renderPath(Graphics2D graphics, GeneralPath path, Color color) + { + LocalPoint playerLp = client.getLocalPlayer().getLocalLocation(); + Rectangle viewArea = new Rectangle( + playerLp.getX() - MAX_LOCAL_DRAW_LENGTH, + playerLp.getY() - MAX_LOCAL_DRAW_LENGTH, + MAX_LOCAL_DRAW_LENGTH * 2, + MAX_LOCAL_DRAW_LENGTH * 2); + + graphics.setColor(color); + graphics.setStroke(new BasicStroke(2)); + + path = Geometry.clipPath(path, viewArea); + path = Geometry.filterPath(path, (p1, p2) -> + Perspective.localToCanvas(client, new LocalPoint((int)p1[0], (int)p1[1]), client.getPlane()) != null && + Perspective.localToCanvas(client, new LocalPoint((int)p2[0], (int)p2[1]), client.getPlane()) != null); + path = Geometry.transformPath(path, coords -> + { + Point point = Perspective.localToCanvas(client, new LocalPoint((int)coords[0], (int)coords[1]), client.getPlane()); + coords[0] = point.getX(); + coords[1] = point.getY(); + }); + + graphics.draw(path); + } + + @Override + public Dimension render(Graphics2D graphics) + { + GeneralPath multicombatPath = plugin.getMulticombatPathToDisplay()[client.getPlane()]; + GeneralPath pvpPath = plugin.getPvpPathToDisplay()[client.getPlane()]; + + if (config.multicombatZoneVisibility() != ZoneVisibility.HIDE && multicombatPath != null) + { + renderPath(graphics, multicombatPath, getTransparentColorVersion(config.multicombatColor())); + } + if ((config.showPvpSafeZones() || config.showDeadmanSafeZones()) && pvpPath != null) + { + renderPath(graphics, pvpPath, getTransparentColorVersion(config.safeZoneColor())); + } + + return null; + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/zoneIndicators/ZoneIndicatorsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/zoneIndicators/ZoneIndicatorsPlugin.java new file mode 100644 index 0000000000..5f3397e900 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/zoneIndicators/ZoneIndicatorsPlugin.java @@ -0,0 +1,297 @@ +/* + * Copyright (c) 2018, Woox + * 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.zoneIndicators; + +import net.runelite.client.eventbus.Subscribe; +import com.google.inject.Provides; +import java.awt.Rectangle; +import java.awt.geom.GeneralPath; +import java.util.Arrays; +import javax.inject.Inject; +import lombok.Getter; +import net.runelite.api.Client; +import net.runelite.api.Constants; +import net.runelite.api.GameState; +import net.runelite.api.ObjectComposition; +import net.runelite.api.Perspective; +import net.runelite.api.Tile; +import net.runelite.api.WallObject; +import net.runelite.api.WorldType; +import net.runelite.api.coords.LocalPoint; +import net.runelite.api.coords.WorldArea; +import net.runelite.api.coords.WorldPoint; +import net.runelite.api.events.ConfigChanged; +import net.runelite.api.events.GameStateChanged; +import net.runelite.api.geometry.Geometry; +import net.runelite.client.callback.ClientThread; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.ui.overlay.OverlayManager; + +@PluginDescriptor( + name = "!MultiLines", + description = "Show borders of multicombat and PvP safezones", + tags = {"multicombat", "lines", "pvp", "deadman", "safezones", "bogla"}, + enabledByDefault = false +) +public class ZoneIndicatorsPlugin extends Plugin +{ + @Inject + private Client client; + + @Inject + private ClientThread clientThread; + + @Inject + private ZoneIndicatorsConfig config; + + @Inject + private ZoneIndicatorsOverlay overlay; + + @Inject + private ZoneIndicatorsMinimapOverlay minimapOverlay; + + @Inject + private OverlayManager overlayManager; + + @Getter + private GeneralPath[] multicombatPathToDisplay; + + @Getter + private GeneralPath[] pvpPathToDisplay; + + @Getter + private boolean inPvp; + + @Getter + private boolean inDeadman; + + private int currentPlane; + + @Provides + ZoneIndicatorsConfig getConfig(ConfigManager configManager) + { + return configManager.getConfig(ZoneIndicatorsConfig.class); + } + + @Override + protected void startUp() throws Exception + { + overlayManager.add(overlay); + overlayManager.add(minimapOverlay); + + multicombatPathToDisplay = new GeneralPath[Constants.MAX_Z]; + pvpPathToDisplay = new GeneralPath[Constants.MAX_Z]; + + clientThread.invokeLater(() -> + { + if (client.getGameState() == GameState.LOGGED_IN) + { + findLinesInScene(); + } + }); + } + + @Override + protected void shutDown() throws Exception + { + overlayManager.remove(overlay); + overlayManager.remove(minimapOverlay); + + multicombatPathToDisplay = null; + pvpPathToDisplay = null; + } + + private void transformWorldToLocal(float[] coords) + { + LocalPoint lp = LocalPoint.fromWorld(client, (int)coords[0], (int)coords[1]); + coords[0] = lp.getX() - Perspective.LOCAL_TILE_SIZE / 2; + coords[1] = lp.getY() - Perspective.LOCAL_TILE_SIZE / 2; + } + + private boolean isOpenableAt(WorldPoint wp) + { + int sceneX = wp.getX() - client.getBaseX(); + int sceneY = wp.getY() - client.getBaseY(); + + Tile tile = client.getScene().getTiles()[wp.getPlane()][sceneX][sceneY]; + if (tile == null) + { + return false; + } + + WallObject wallObject = tile.getWallObject(); + if (wallObject == null) + { + return false; + } + + ObjectComposition objectComposition = client.getObjectDefinition(wallObject.getId()); + if (objectComposition == null) + { + return false; + } + + String[] actions = objectComposition.getActions(); + if (actions == null) + { + return false; + } + + return Arrays.stream(actions).anyMatch(x -> x != null && x.toLowerCase().equals("open")); + } + + private boolean collisionFilter(float[] p1, float[] p2) + { + int x1 = (int)p1[0]; + int y1 = (int)p1[1]; + int x2 = (int)p2[0]; + int y2 = (int)p2[1]; + + if (x1 > x2) + { + int temp = x1; + x1 = x2; + x2 = temp; + } + if (y1 > y2) + { + int temp = y1; + y1 = y2; + y2 = temp; + } + int dx = x2 - x1; + int dy = y2 - y1; + WorldArea wa1 = new WorldArea(new WorldPoint( + x1, y1, currentPlane), 1, 1); + WorldArea wa2 = new WorldArea(new WorldPoint( + x1 - dy, y1 - dx, currentPlane), 1, 1); + + if (isOpenableAt(wa1.toWorldPoint()) || isOpenableAt(wa2.toWorldPoint())) + { + // When there's something with the open option (e.g. a door) on the tile, + // we assume it can be opened and walked through afterwards. Without this + // check, the line for that tile wouldn't render with collision detection + // because the collision check isn't done if collision data changes. + return true; + } + + boolean b1 = wa1.canTravelInDirection(client, -dy, -dx); + boolean b2 = wa2.canTravelInDirection(client, dy, dx); + return b1 && b2; + } + + private void findLinesInScene() + { + inDeadman = client.getWorldType().stream().anyMatch(x -> + x == WorldType.DEADMAN || x == WorldType.SEASONAL_DEADMAN); + inPvp = client.getWorldType().stream().anyMatch(x -> + x == WorldType.PVP || x == WorldType.PVP_HIGH_RISK); + + Rectangle sceneRect = new Rectangle( + client.getBaseX() + 1, client.getBaseY() + 1, + Constants.SCENE_SIZE - 2, Constants.SCENE_SIZE - 2); + + // Generate lines for multicombat zones + if (config.multicombatZoneVisibility() == ZoneVisibility.HIDE) + { + for (int i = 0; i < multicombatPathToDisplay.length; i++) + { + multicombatPathToDisplay[i] = null; + } + } + else + { + for (int i = 0; i < multicombatPathToDisplay.length; i++) + { + currentPlane = i; + + GeneralPath lines = new GeneralPath(MapLocations.getMulticombat(sceneRect, i)); + lines = Geometry.clipPath(lines, sceneRect); + if (config.multicombatZoneVisibility() == ZoneVisibility.SHOW_IN_PVP && + !isInDeadman() && !isInPvp()) + { + lines = Geometry.clipPath(lines, MapLocations.getRoughWilderness(i)); + } + lines = Geometry.splitIntoSegments(lines, 1); + if (config.collisionDetection()) + { + lines = Geometry.filterPath(lines, this::collisionFilter); + } + lines = Geometry.transformPath(lines, this::transformWorldToLocal); + multicombatPathToDisplay[i] = lines; + } + } + + // Generate safezone lines for deadman/pvp worlds + for (int i = 0; i < pvpPathToDisplay.length; i++) + { + currentPlane = i; + + GeneralPath safeZonePath = null; + if (config.showDeadmanSafeZones() && isInDeadman()) + { + safeZonePath = new GeneralPath(MapLocations.getDeadmanSafeZones(sceneRect, i)); + } + else if (config.showPvpSafeZones() && isInPvp()) + { + safeZonePath = new GeneralPath(MapLocations.getPvpSafeZones(sceneRect, i)); + } + if (safeZonePath != null) + { + safeZonePath = Geometry.clipPath(safeZonePath, sceneRect); + safeZonePath = Geometry.splitIntoSegments(safeZonePath, 1); + if (config.collisionDetection()) + { + safeZonePath = Geometry.filterPath(safeZonePath, this::collisionFilter); + } + safeZonePath = Geometry.transformPath(safeZonePath, this::transformWorldToLocal); + } + pvpPathToDisplay[i] = safeZonePath; + } + } + + @Subscribe + public void onConfigChanged(ConfigChanged event) + { + if (event.getKey().equals("collisionDetection") || + event.getKey().equals("multicombatZoneVisibility") || + event.getKey().equals("deadmanSafeZones") || + event.getKey().equals("pvpSafeZones")) + { + findLinesInScene(); + } + } + + @Subscribe + public void onGameStateChanged(GameStateChanged event) + { + if (event.getGameState() == GameState.LOGGED_IN) + { + findLinesInScene(); + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/zoneIndicators/ZoneVisibility.java b/runelite-client/src/main/java/net/runelite/client/plugins/zoneIndicators/ZoneVisibility.java new file mode 100644 index 0000000000..9a457d9e50 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/zoneIndicators/ZoneVisibility.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018, Woox + * 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.zoneIndicators; + +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public enum ZoneVisibility +{ + HIDE("Hide"), + SHOW_IN_PVP("Show in PvP"), + SHOW_EVERYWHERE("Show everywhere"); + + private final String visibility; + + @Override + public String toString() + { + return visibility; + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/ztob/TheatreConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/ztob/TheatreConfig.java new file mode 100644 index 0000000000..6f63951785 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/ztob/TheatreConfig.java @@ -0,0 +1,138 @@ +/* + * THIS SOFTWARE WRITTEN BY A KEYBOARD-WIELDING MONKEY BOI + * No rights reserved. Use, redistribute, and modify at your own discretion, + * and in accordance with Yagex and RuneLite guidelines. + * However, aforementioned monkey would prefer if you don't sell this plugin for profit. + * Good luck on your raids! + */ + +package net.runelite.client.plugins.ztob; + +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("Theatre") + +public interface TheatreConfig extends Config +{ + @ConfigItem( + position = 0, + keyName = "MaidenBlood", + name = "Maiden blood attack", + description = "" + ) + default boolean MaidenBlood(){ return false; } + + @ConfigItem( + position = 1, + keyName = "MaidenSpawns", + name = "Maiden blood spawns", + description = "" + ) + default boolean MaidenSpawns(){ return false; } + + @ConfigItem( + position = 2, + keyName = "BloatIndicator", + name = "Bloat indicator", + description = "" + ) + default boolean BloatIndicator(){ return false; } + + @ConfigItem( + position = 3, + keyName = "BloatHands", + name = "Bloat Falling Hands", + description = "" + ) + default boolean BloatHands(){ return false; } + + @ConfigItem( + position = 4, + keyName = "NyloPillars", + name = "Nylocas pillar health", + description = "" + ) + default boolean NyloPillars(){ return false; } + + @ConfigItem( + position = 5, + keyName = "NyloBlasts", + name = "Nylocas explosions", + description = "" + ) + default boolean NyloBlasts(){ return false; } + + @ConfigItem( + position = 6, + keyName = "SotetsegMaze1", + name = "Sotetseg maze", + description = "" + ) + default boolean SotetsegMaze1(){ return false; } + + @ConfigItem( + position = 7, + keyName = "SotetsegMaze2", + name = "Sotetseg maze (solo mode)", + description = "" + ) + default boolean SotetsegMaze2(){ return false; } + + @ConfigItem( + position = 8, + keyName = "SotetsegTick", + name = "Sotetseg tick eat", + description = "" + ) + default boolean SotetsegTick(){ return false; } + + @ConfigItem( + position = 9, + keyName = "XarpusExhumed", + name = "Xarpus exhumed", + description = "" + ) + default boolean XarpusExhumed(){ return false; } + + @ConfigItem( + position = 10, + keyName = "XarpusTick", + name = "Xarpus tick", + description = "" + ) + default boolean XarpusTick(){ return false; } + + @ConfigItem( + position = 11, + keyName = "VerzikCupcakes", + name = "Verzik cupcakes", + description = " " + ) + default boolean VerzikCupcakes(){ return false; } + + @ConfigItem( + position = 12, + keyName = "VerzikTick", + name = "Verzik p3 tick", + description = "" + ) + default boolean VerzikTick(){ return false; } + + @ConfigItem( + position = 13, + keyName = "VerzikMelee", + name = "Verzik p3 melee range", + description = "" + ) + default boolean VerzikMelee(){ return false; } + + @ConfigItem( + position = 14, + keyName = "VerzikYellow", + name = "Verzik yellow timing", + description = "" + ) + default boolean VerzikYellow(){ return false; } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/ztob/TheatreOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/ztob/TheatreOverlay.java new file mode 100644 index 0000000000..07f8e2f7b4 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/ztob/TheatreOverlay.java @@ -0,0 +1,347 @@ +/* + * THIS SOFTWARE WRITTEN BY A KEYBOARD-WIELDING MONKEY BOI + * No rights reserved. Use, redistribute, and modify at your own discretion, + * and in accordance with Yagex and RuneLite guidelines. + * However, aforementioned monkey would prefer if you don't sell this plugin for profit. + * Good luck on your raids! + */ + +package net.runelite.client.plugins.ztob; + +import java.awt.*; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import javax.inject.Inject; + +import net.runelite.api.*; +import net.runelite.api.Point; +import net.runelite.api.coords.LocalPoint; +import net.runelite.api.coords.WorldArea; +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; +import net.runelite.client.ui.overlay.OverlayPriority; +import net.runelite.client.ui.overlay.OverlayUtil; + +public class TheatreOverlay extends Overlay { + private final Client client; + + + private final TheatrePlugin plugin; + private final TheatreConfig config; + + @Inject + private TheatreOverlay(Client client, TheatrePlugin plugin, TheatreConfig config) { + this.client = client; + this.plugin = plugin; + this.config = config; + setPosition(OverlayPosition.DYNAMIC); + setPriority(OverlayPriority.HIGH); + setLayer(OverlayLayer.ABOVE_SCENE); + } + + @Override + public Dimension render(Graphics2D graphics) + { + if (plugin.isRunMaiden()) + { + if (config.MaidenBlood()) + { + for (WorldPoint point : plugin.getMaiden_BloodSpatters()) + { + drawTile(graphics, point, new Color(0,150,200), 2, 150, 10); + } + } + + if (config.MaidenSpawns()) + { + for (WorldPoint point : plugin.getMaiden_SpawnLocations()) + { + drawTile(graphics, point, new Color(0,150,200), 2, 180, 20); + } + for (WorldPoint point : plugin.getMaiden_SpawnLocations2()) + { + drawTile(graphics, point, new Color(0,150,200), 1,120, 10); + } + } + } + + if (plugin.isRunBloat()) + { + + if (config.BloatHands()) + { + for (WorldPoint p : plugin.getBloat_Hands()) + { + drawTile(graphics, p, Color.BLACK,3,255,0); + } + } + if(config.BloatIndicator()) { + NPC bloat = plugin.getBloat_NPC(); + int state = plugin.getBloat_State(); + if (bloat == null) { + return null; + } + switch (state) { + case 2: + renderNpcOverlay(graphics, bloat, Color.GREEN, 3, 150, 0); + break; + case 3: + renderNpcOverlay(graphics, bloat, Color.YELLOW, 3, 150, 0); + break; + default: + renderNpcOverlay(graphics, bloat, new Color(223, 109, 255), 3, 150, 0); + break; + } + } + } + + if (plugin.isRunNylocas()) + { + if (config.NyloPillars()) + { + Map pillars = plugin.getNylocas_Pillars(); + for (NPC npc : pillars.keySet()) { + final int health = pillars.get(npc); + final String healthStr = String.valueOf(health) + "%"; + WorldPoint p = npc.getWorldLocation(); + LocalPoint lp = LocalPoint.fromWorld(client, p.getX() + 1, p.getY() + 1); + final double rMod = 130.0 * health / 100.0; + final double gMod = 255.0 * health / 100.0; + final double bMod = 125.0 * health / 100.0; + final Color c = new Color((int) (255 - rMod), (int) (0 + gMod), (int) (0 + bMod)); + Point canvasPoint = Perspective.localToCanvas(client, lp, client.getPlane(), + 65); + renderTextLocation(graphics, healthStr, 13, Font.BOLD, c, canvasPoint); + } + } + + if (config.NyloBlasts()) + { + final Map npcMap = plugin.getNylocas_Map(); + for (NPC npc : npcMap.keySet()) + { + int ticksLeft = npcMap.get(npc); + if (ticksLeft > -1) { + if (ticksLeft <= 6) { + Color color = new Color(255, 255,0 ,180); + int outlineWidth = 2; + int outlineAlpha = 150; + renderNpcOverlay(graphics, npc, color, outlineWidth, outlineAlpha, 0); + } + } + } + } + } + + if (plugin.isRunSotetseg()) + { + if (config.SotetsegMaze1()) + { + int i = 1; + for (GroundObject z : plugin.getRedTiles().keySet()) + { + Polygon poly = z.getCanvasTilePoly(); + if (poly != null) + { + graphics.setColor(Color.WHITE); + graphics.setStroke(new BasicStroke(2)); + graphics.draw(poly); + } + Point textLocation = z.getCanvasTextLocation(graphics, String.valueOf(i), 0); + if (textLocation != null) + { + OverlayUtil.renderTextLocation(graphics, textLocation, String.valueOf(i), Color.WHITE); + } + + i++; + } + } + + if (config.SotetsegMaze2()) + { + for (WorldPoint p : plugin.getRedTilesOverworld()) + { + drawTile(graphics, p, Color.WHITE, 2, 255, 10); + } + } + if (config.SotetsegTick()) { + NPC boss = plugin.getSotetseg_NPC(); + int eattick = plugin.getTickTillEat(); + if (eattick > -1) + { + final String eatTicksStr = String.valueOf(eattick); + Point canvasPoint = boss.getCanvasTextLocation(graphics, eatTicksStr, 130); + renderTextLocation(graphics, eatTicksStr, 12, Font.BOLD, Color.WHITE, canvasPoint); + + } + } + } + + + + if (plugin.isRunXarpus()) + { + NPC boss = plugin.getXarpus_NPC(); + + if (boss.getId() == NpcID.XARPUS_8340 && !plugin.isXarpus_Stare() && config.XarpusTick()) + { + int tick = plugin.getXarpus_TicksUntilShoot(); + if (tick < 1) + { + tick = tick % 4 + 4; + } + final String ticksLeftStr = String.valueOf(tick); + Point canvasPoint = boss.getCanvasTextLocation(graphics, ticksLeftStr, 130); + renderTextLocation(graphics, ticksLeftStr, 12, Font.BOLD, Color.WHITE, canvasPoint); + } + if (boss.getId() == NpcID.XARPUS_8339 && config.XarpusExhumed()) + { + for (GroundObject o : plugin.getXarpus_Exhumeds().keySet()) + { + Polygon poly = o.getCanvasTilePoly(); + if (poly != null) + { + graphics.setColor(new Color(0, 255, 0, 130)); + graphics.setStroke(new BasicStroke(1)); + graphics.draw(poly); + } + } + } + } + + if (plugin.isRunVerzik()) + { + if (config.VerzikCupcakes()) + { + for (WorldPoint p : plugin.getVerzik_RangeProjectiles().values()) + { + drawTile(graphics, p, Color.RED, 2, 180, 50); + } + } + + if (config.VerzikYellow()) + { + for (WorldPoint p : plugin.getVerzik_YellowTiles()) + { + drawTile(graphics, p, Color.YELLOW,3,255,0); + + Projectile yellowBall = plugin.getVerzik_YellowBall(); + if (yellowBall != null) + { + final int ticksToImpact = yellowBall.getRemainingCycles()/30; + final String countdownStr = String.valueOf(ticksToImpact); + Point canvasPoint = Perspective.getCanvasTextLocation(client, graphics, LocalPoint.fromWorld(client, p), countdownStr, 0); + renderTextLocation(graphics, countdownStr, 12, Font.BOLD, Color.WHITE, canvasPoint); + } + } + } + if (plugin.getVerzik_NPC_P3() != null) { + final NPC boss = plugin.getVerzik_NPC_P3(); + if (boss.getId() == NpcID.VERZIK_VITUR_8374) + { + if (config.VerzikTick()) + { + final int ticksLeft = plugin.getP3_TicksUntilAttack(); + if (ticksLeft > 0 && ticksLeft < 8) + { + final String ticksLeftStr = String.valueOf(ticksLeft); + Point canvasPoint = boss.getCanvasTextLocation(graphics, ticksLeftStr, 60); + renderTextLocation(graphics, ticksLeftStr, 15, Font.BOLD, Color.WHITE, canvasPoint); + } + } + + if (config.VerzikMelee() && boss.getAnimation() != 8127) + { + List meleeRange = getHitSquares(boss.getWorldLocation(), 7, 1, false); + + for (WorldPoint p : meleeRange) + { + drawTile(graphics, p, Color.WHITE, 1,155, 10); + } + } + } + } + } + return null; + } + + private void drawTile(Graphics2D graphics, WorldPoint point, Color color, int strokeWidth, int outlineAlpha, int fillAlpha) { + WorldPoint playerLocation = client.getLocalPlayer().getWorldLocation(); + if (point.distanceTo(playerLocation) >= 32) { + return; + } + LocalPoint lp = LocalPoint.fromWorld(client, point); + if (lp == null) { + return; + } + + Polygon poly = Perspective.getCanvasTilePoly(client, lp); + if (poly == null) { + return; + } + //OverlayUtil.renderPolygon(graphics, poly, color); + graphics.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), outlineAlpha)); + graphics.setStroke(new BasicStroke(strokeWidth)); + graphics.draw(poly); + graphics.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), fillAlpha)); + graphics.fill(poly); + } + + private void renderNpcOverlay(Graphics2D graphics, NPC actor, Color color, int outlineWidth, int outlineAlpha, int fillAlpha) + { + int size = 1; + NPCComposition composition = actor.getTransformedComposition(); + if (composition != null) + { + size = composition.getSize(); + } + LocalPoint lp = actor.getLocalLocation(); + Polygon tilePoly = Perspective.getCanvasTileAreaPoly(client, lp, size); + + if (tilePoly != null) + { + graphics.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), outlineAlpha)); + graphics.setStroke(new BasicStroke(outlineWidth)); + graphics.draw(tilePoly); + graphics.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), fillAlpha)); + graphics.fill(tilePoly); + } + } + + private void renderTextLocation(Graphics2D graphics, String txtString, int fontSize, int fontStyle, Color fontColor, Point canvasPoint) + { + graphics.setFont(new Font("Arial", fontStyle, fontSize)); + if (canvasPoint != null) + { + final Point canvasCenterPoint = new Point( + canvasPoint.getX(), + canvasPoint.getY()); + final Point canvasCenterPoint_shadow = new Point( + canvasPoint.getX() + 1, + canvasPoint.getY() + 1) ; + OverlayUtil.renderTextLocation(graphics, canvasCenterPoint_shadow, txtString, Color.BLACK); + OverlayUtil.renderTextLocation(graphics, canvasCenterPoint, txtString, fontColor); + } + } + + private List getHitSquares(WorldPoint npcLoc, int npcSize, int thickness, boolean includeUnder) + { + List little = new WorldArea(npcLoc, npcSize, npcSize).toWorldPointList(); + List big = new WorldArea(npcLoc.getX()-thickness, npcLoc.getY()-thickness, npcSize + (thickness * 2), npcSize + (thickness * 2), npcLoc.getPlane()).toWorldPointList(); + if (!includeUnder) + { + for (Iterator it = big.iterator(); it.hasNext();) + { + WorldPoint p = it.next(); + if (little.contains(p)) + { + it.remove(); + } + } + } + return big; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/ztob/TheatrePlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/ztob/TheatrePlugin.java new file mode 100644 index 0000000000..8fa2ee2481 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/ztob/TheatrePlugin.java @@ -0,0 +1,766 @@ +/* + * THIS SOFTWARE WRITTEN BY A KEYBOARD-WIELDING MONKEY BOI + * No rights reserved. Use, redistribute, and modify at your own discretion, + * and in accordance with Yagex and RuneLite guidelines. + * However, aforementioned monkey would prefer if you don't sell this plugin for profit. + * Good luck on your raids! + */ + +package net.runelite.client.plugins.ztob; + +import java.util.*; +import java.util.Iterator; +import javax.inject.Inject; + +import net.runelite.client.chat.ChatMessageManager; +import net.runelite.client.eventbus.Subscribe; +import com.google.inject.Provides; +import lombok.AccessLevel; +import lombok.Getter; +import net.runelite.api.*; +import net.runelite.api.coords.WorldPoint; +import net.runelite.api.events.*; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.ui.overlay.OverlayManager; + +@PluginDescriptor( + name = "!Theatre of Blood", + description = "All-in-one plugin for Theatre of Blood", + tags = {"ToB"}, + enabledByDefault = false +) + +public class TheatrePlugin extends Plugin { + private static final int GRAPHICSOBJECT_ID_MAIDEN = 1579; + private static final int NPCID_NYLOCAS_PILLAR = 8358; + private static final int GROUNDOBJECT_ID_BLACKMAZE = 33034; + private static final int GROUNDOBJECT_ID_REDMAZE = 33035; + private static final int GROUNDOBJECT_ID_EXHUMED = 32743; + private static final int ANIMATION_ID_XARPUS = 8059; + private static final int GRAPHICSOBJECT_ID_YELLOW = 1595; + private static final int GRAPHICSOBJECT_ID_BLOAT_HAND1 = 1570; + private static final int GRAPHICSOBJECT_ID_BLOAT_HAND2 = 1571; + private static final int GRAPHICSOBJECT_ID_BLOAT_HAND3 = 1572; + private static final int GRAPHICSOBJECT_ID_BLOAT_HAND4 = 1573; + private static final int GRAPHICSOBJECT_ID_BLOAT_HAND5 = 1576; + private static final int PROJECTILE_ID_P2RANGE = 1583; + private static final int PROJECTILE_ID_YELLOW = 1596; + private static final int ANIMATION_ID_P3_WEB = 8127; + private static final int ANIMATION_ID_P3_YELLOW = 8126; + private static final int ANIMATION_ID_P3_MELEE = 8123; + private static final int ANIMATION_ID_P3_MAGE = 8124; + private static final int ANIMATION_ID_P3_RANGE = 8125; + private static final int VERZIK_ID_P3 = NpcID.VERZIK_VITUR_8374; + private static final int NPC_ID_TORNADO = 8386; + private static final int PROJECTILE_ID_P3_GREEN = 1598; + private static final String sotmsg = "A large ball of energy is shot your way..."; + private static final String sotmsg1 = "A large ball of energy is shot your way..."; + + @Inject + private ChatMessageManager chatMessageManager; + + @Getter(AccessLevel.PACKAGE) + private boolean runMaiden; + + @Getter(AccessLevel.PACKAGE) + private List Maiden_BloodSpatters = new ArrayList<>(); + + private List Maiden_Spawns = new ArrayList<>(); + + @Getter(AccessLevel.PACKAGE) + private List Maiden_SpawnLocations = new ArrayList<>(); + + @Getter(AccessLevel.PACKAGE) + private List Maiden_SpawnLocations2 = new ArrayList<>(); + + + @Getter(AccessLevel.PACKAGE) + private boolean runBloat; + + @Getter(AccessLevel.PACKAGE) + private int TickTillEat = 20; + + @Getter(AccessLevel.PACKAGE) + private NPC Bloat_NPC; + + private int Bloat_downCount; + + @Getter(AccessLevel.PACKAGE) + private Integer Bloat_State; + + + @Getter(AccessLevel.PACKAGE) + private boolean runNylocas; + + @Getter(AccessLevel.PACKAGE) + private Map Nylocas_Pillars = new HashMap<>(); + + @Getter(AccessLevel.PACKAGE) + private Map Nylocas_Map = new HashMap<>(); + + + @Getter(AccessLevel.PACKAGE) + private boolean runSotetseg; + + @Getter(AccessLevel.PACKAGE) + private final Map RedTiles = new LinkedHashMap<>(); + + @Getter(AccessLevel.PACKAGE) + private List RedTilesOverworld = new ArrayList<>(); + + private List BlackTilesOverworld = new ArrayList<>(); + + private List BlackTilesUnderworld= new ArrayList<>(); + + private List RedTilesUnderworld= new ArrayList<>(); + + private List GridPath = new ArrayList<>(); + + @Getter(AccessLevel.PACKAGE) + private boolean runXarpus; + + private int Xarpus_previousAnimation; + + @Getter(AccessLevel.PACKAGE) + private boolean Xarpus_Stare; + + @Getter(AccessLevel.PACKAGE) + private final Map Xarpus_Exhumeds = new HashMap<>(); + + @Getter(AccessLevel.PACKAGE) + private int Xarpus_TicksUntilShoot = 9; + + @Getter(AccessLevel.PACKAGE) + private NPC Xarpus_NPC; + + @Getter(AccessLevel.PACKAGE) + private NPC Sotetseg_NPC; + + @Getter(AccessLevel.PACKAGE) + private boolean runVerzik; + + @Getter(AccessLevel.PACKAGE) + private final Map Verzik_RangeProjectiles = new HashMap<>(); + + @Getter(AccessLevel.PACKAGE) + private int P3_TicksUntilAttack = -1; + + @Getter(AccessLevel.PACKAGE) + private Projectile Verzik_YellowBall; + + @Getter(AccessLevel.PACKAGE) + private List Verzik_YellowTiles = new ArrayList<>(); + + @Getter(AccessLevel.PACKAGE) + private List Bloat_Hands = new ArrayList<>(); + + @Getter(AccessLevel.PACKAGE) + private NPC Verzik_NPC; + + @Getter(AccessLevel.PACKAGE) + private NPC Verzik_NPC_P3; + + @Getter(AccessLevel.PACKAGE) + private NPC Verzik_NPC_P2; + + private int P3_attacksLeft; + + @Inject + private Client client; + + @Inject + private OverlayManager overlayManager; + + @Inject + private TheatreOverlay overlay; + + @Inject + private TheatreConfig config; + + @Provides + TheatreConfig getConfig(ConfigManager configManager) { + return configManager.getConfig(TheatreConfig.class); + } + + @Override + protected void startUp() { + overlayManager.add(overlay); + } + + @Override + protected void shutDown() { + overlayManager.remove(overlay); + } + + @Subscribe + public void onNpcSpawned(NpcSpawned npcSpawned) + { + NPC npc = npcSpawned.getNpc(); + switch (npc.getId()) + { + case NpcID.THE_MAIDEN_OF_SUGADINTI: + case NpcID.THE_MAIDEN_OF_SUGADINTI_8361: + case NpcID.THE_MAIDEN_OF_SUGADINTI_8362: + case NpcID.THE_MAIDEN_OF_SUGADINTI_8363: + case NpcID.THE_MAIDEN_OF_SUGADINTI_8364: + case NpcID.THE_MAIDEN_OF_SUGADINTI_8365: + runMaiden = true; + break; + case NpcID.BLOOD_SPAWN: + Maiden_Spawns.add(npc); + break; + case NpcID.PESTILENT_BLOAT: + runBloat = true; + Bloat_NPC = npc; + break; + case NPCID_NYLOCAS_PILLAR: + runNylocas = true; + if (!Nylocas_Pillars.keySet().contains(npc)) + { + Nylocas_Pillars.put(npc, 100); + } + break; + case 8342: case 8343: case 8344: case 8345: case 8346: case 8347: + case 8348: case 8349: case 8350: case 8351: case 8352: case 8353: + if (runNylocas) + { + Nylocas_Map.put(npc, 52); + } + break; + case NpcID.SOTETSEG: + case NpcID.SOTETSEG_8388: + runSotetseg = true; + Sotetseg_NPC = npc; + RedTiles.clear(); + break; + case NpcID.XARPUS: + case NpcID.XARPUS_8339: + case NpcID.XARPUS_8340: + case NpcID.XARPUS_8341: + runXarpus = true; + Xarpus_NPC = npc; + Xarpus_Stare = false; + Xarpus_TicksUntilShoot = 9; + Xarpus_previousAnimation = -1; + break; + case NpcID.VERZIK_VITUR_8369: + case NpcID.VERZIK_VITUR_8370: + case NpcID.VERZIK_VITUR_8371: + case NpcID.VERZIK_VITUR_8373: + case NpcID.VERZIK_VITUR_8375: + Verzik_NPC = npc; + runVerzik = true; + break; + + case NpcID.VERZIK_VITUR_8372:/*p2 spider*/ + Verzik_NPC_P2 = npc; + runVerzik = true; + break; + + case NpcID.VERZIK_VITUR_8374:/*p3 spider*/ + P3_TicksUntilAttack = 0; + P3_attacksLeft = 9; + Verzik_NPC_P3 = npc; + runVerzik = true; + break; + } + } + + @Subscribe + public void onNpcDespawned(NpcDespawned npcDespawned) { + NPC npc = npcDespawned.getNpc(); + switch (npc.getId()) { + case NpcID.THE_MAIDEN_OF_SUGADINTI: + case NpcID.THE_MAIDEN_OF_SUGADINTI_8361: + case NpcID.THE_MAIDEN_OF_SUGADINTI_8362: + case NpcID.THE_MAIDEN_OF_SUGADINTI_8363: + case NpcID.THE_MAIDEN_OF_SUGADINTI_8364: + case NpcID.THE_MAIDEN_OF_SUGADINTI_8365: + runMaiden = false; + Maiden_Spawns.clear(); + break; + case NpcID.BLOOD_SPAWN: + Maiden_Spawns.remove(npc); + break; + case NpcID.PESTILENT_BLOAT: + runBloat = false; + Bloat_NPC = null; + break; + case NPCID_NYLOCAS_PILLAR: + if (Nylocas_Pillars.keySet().contains(npc)) + { + Nylocas_Pillars.remove(npc); + } + break; + case 8342: case 8343: case 8344: case 8345: case 8346: case 8347: + case 8348: case 8349: case 8350: case 8351: case 8352: case 8353: + if (Nylocas_Map.keySet().contains(npc)) + { + Nylocas_Map.remove(npc); + } + break; + case NpcID.SOTETSEG: + case NpcID.SOTETSEG_8388: + RedTiles.clear(); + if (client.getPlane() != 3) + { + runSotetseg = false; + } + Sotetseg_NPC = null; + break; + case NpcID.XARPUS: + case NpcID.XARPUS_8339: + case NpcID.XARPUS_8340: + case NpcID.XARPUS_8341: + runXarpus = false; + Xarpus_NPC = null; + Xarpus_Stare = false; + Xarpus_TicksUntilShoot = 9; + Xarpus_previousAnimation = -1; + Xarpus_Exhumeds.clear(); + break; + case NpcID.VERZIK_VITUR_8369: + case NpcID.VERZIK_VITUR_8370: + case NpcID.VERZIK_VITUR_8371:/*p2*/ + + case NpcID.VERZIK_VITUR_8373: + + case NpcID.VERZIK_VITUR_8375: + Verzik_NPC = null; + break; + case NpcID.VERZIK_VITUR_8372: + Verzik_NPC_P2 = null; + break; + case NpcID.VERZIK_VITUR_8374:/*p3 spider*/ + Verzik_NPC_P3 = null; + break; + + } + + } + + @Subscribe + public void onGroundObjectSpawned(GroundObjectSpawned event) + { + if (runSotetseg) + { + GroundObject o = event.getGroundObject(); + if (o.getId() == GROUNDOBJECT_ID_BLACKMAZE) + { + Tile t = event.getTile(); + WorldPoint p = t.getWorldLocation(); + if (t.getPlane() == 0) + { + if (!BlackTilesOverworld.contains(p)) + BlackTilesOverworld.add(p); + } + else + { + if (!BlackTilesUnderworld.contains(p)) + BlackTilesUnderworld.add(p); + } + } + + if (o.getId() == GROUNDOBJECT_ID_REDMAZE) + { + Tile t = event.getTile(); + WorldPoint p = t.getWorldLocation(); + if (p.getPlane() == 0) + { + if (!RedTiles.containsValue(t)) + { + RedTiles.put(o,t); + } + } + else + { + if (!RedTilesUnderworld.contains(p)) + RedTilesUnderworld.add(p); + } + } + } + + if (runXarpus) + { + GroundObject o = event.getGroundObject(); + if (o.getId() == GROUNDOBJECT_ID_EXHUMED) + { + Xarpus_Exhumeds.put(o, 18); + } + } + } + + @Subscribe + public void onProjectileMoved(ProjectileMoved event) + { + if (runVerzik) + { + Projectile projectile = event.getProjectile(); + if (projectile.getId() == PROJECTILE_ID_P2RANGE) + { + WorldPoint p = WorldPoint.fromLocal(client,event.getPosition()); + Verzik_RangeProjectiles.put(projectile, p); + } + } + } + + @Subscribe + public void onChatMessage(ChatMessage chatMessage) + { + MessageNode messageNode = chatMessage.getMessageNode(); + + if (messageNode.getValue().toLowerCase().contains(sotmsg.toLowerCase())) + { + TickTillEat = 20; + /*20 ticks*/ + + } + + + } + + @Subscribe + public void onGameTick(GameTick event) + { + if (runMaiden) + { + Maiden_BloodSpatters.clear(); + for (GraphicsObject o : client.getGraphicsObjects()) + { + if (o.getId() == GRAPHICSOBJECT_ID_MAIDEN) + { + Maiden_BloodSpatters.add(WorldPoint.fromLocal(client, o.getLocation())); + } + } + + Maiden_SpawnLocations2.clear(); + Maiden_SpawnLocations2.addAll(Maiden_SpawnLocations); + Maiden_SpawnLocations.clear(); + for (NPC spawn : Maiden_Spawns) + { + Maiden_SpawnLocations.add(spawn.getWorldLocation()); + } + } + + if (runBloat) + { + + Bloat_downCount++; + + Bloat_Hands.clear(); + for (GraphicsObject o : client.getGraphicsObjects()) + { + if (o.getId() == GRAPHICSOBJECT_ID_BLOAT_HAND1 || o.getId() == GRAPHICSOBJECT_ID_BLOAT_HAND2 || o.getId() == GRAPHICSOBJECT_ID_BLOAT_HAND3 || o.getId() == GRAPHICSOBJECT_ID_BLOAT_HAND4|| o.getId() == GRAPHICSOBJECT_ID_BLOAT_HAND5) + { + Bloat_Hands.add(WorldPoint.fromLocal(client, o.getLocation())); + } + } + + + if (Bloat_NPC.getAnimation() == -1) //1 = up; 2 = down; 3 = warn; + { + Bloat_downCount = 0; + if (Bloat_NPC.getHealth() == 0) + { + Bloat_State = 2; + } + else + Bloat_State = 1; + } + else + { + if (25 it = Nylocas_Map.keySet().iterator(); it.hasNext();) + { + NPC npc = it.next(); + int ticksLeft = Nylocas_Map.get(npc); + + if (ticksLeft < 0) + { + it.remove(); + continue; + } + Nylocas_Map.replace(npc, ticksLeft - 1); + } + + for (NPC pillar : Nylocas_Pillars.keySet()) + { + int healthPercent = pillar.getHealthRatio(); + if (healthPercent > -1) + { + Nylocas_Pillars.replace(pillar, healthPercent); + } + } + for (NPC npc : client.getNpcs()) + { + if (npc.getId() == 8358) + { + runNylocas = true; + break; + } + runNylocas = false; + } + } + + if (runSotetseg) + { + TickTillEat--; + boolean sotetsegFighting = false; + for (NPC npc : client.getNpcs()) + { + if (npc.getId() == NpcID.SOTETSEG_8388) + { + BlackTilesUnderworld.clear(); + BlackTilesOverworld.clear(); + RedTilesOverworld.clear(); + RedTilesUnderworld.clear(); + GridPath.clear(); + sotetsegFighting = true; + RedTiles.clear(); + break; + } + } + + if (!sotetsegFighting) + { + if (!BlackTilesUnderworld.isEmpty() && !RedTilesUnderworld.isEmpty() && GridPath.isEmpty()) + { + int minX = 99999; + int minY = 99999; + for (WorldPoint p : BlackTilesUnderworld) + { + int x = p.getX(); + int y = p.getY(); + if (x < minX) + { + minX = x; + } + if (y < minY) + { + minY = y; + } + } + + + + boolean messageSent = false; + for (WorldPoint p : RedTilesUnderworld) + { + WorldPoint pN = new WorldPoint(p.getX(), p.getY() + 1, p.getPlane()); + WorldPoint pS = new WorldPoint(p.getX(), p.getY() - 1, p.getPlane()); + WorldPoint pE = new WorldPoint(p.getX() + 1, p.getY(), p.getPlane()); + WorldPoint pW = new WorldPoint(p.getX() - 1, p.getY(), p.getPlane()); + + if ( !( (RedTilesUnderworld.contains(pN) && RedTilesUnderworld.contains(pS)) || + (RedTilesUnderworld.contains(pE) && RedTilesUnderworld.contains(pW)) ) ) + { + GridPath.add(new Point(p.getX() - minX, p.getY() - minY)); + if (!messageSent) + { + //client.addChatMessage(ChatMessageType.SERVER, "", "Maze path acquired.", null); + messageSent = true; + } + } + + } + } + + if (!BlackTilesOverworld.isEmpty() && !GridPath.isEmpty() && RedTilesOverworld.isEmpty()) + { + int minX = 99999; + int minY = 99999; + for (WorldPoint p : BlackTilesOverworld) + { + int x = p.getX(); + int y = p.getY(); + if (x < minX) + { + minX = x; + } + if (y < minY) + { + minY = y; + } + } + for (Point p : GridPath) + { + RedTilesOverworld.add(new WorldPoint(minX + p.getX(), minY + p.getY(), 0)); + } + } + } + } + + if (runXarpus) + { + for (Iterator it = Xarpus_Exhumeds.keySet().iterator(); it.hasNext();) + { + GroundObject key = it.next(); + Xarpus_Exhumeds.replace(key, Xarpus_Exhumeds.get(key) - 1); + if (Xarpus_Exhumeds.get(key) < 0) + { + it.remove(); + } + } + if (Xarpus_NPC.getOverheadText() != null ) + { + Xarpus_Stare = true; + } + if (Xarpus_Stare) + { + //dont hit xarpus if it looking at u + } + else if (Xarpus_NPC.getId() == NpcID.XARPUS_8340) + { + Xarpus_TicksUntilShoot--; + if (Xarpus_NPC.getAnimation() == ANIMATION_ID_XARPUS && Xarpus_previousAnimation != ANIMATION_ID_XARPUS) + { + Xarpus_TicksUntilShoot = 3; + } + Xarpus_previousAnimation = Xarpus_NPC.getAnimation(); + } + + } + + if (runVerzik) + { + if (!Verzik_RangeProjectiles.isEmpty()) + { + for (Iterator it = Verzik_RangeProjectiles.keySet().iterator(); it.hasNext();) + { + Projectile projectile = it.next(); + if (projectile.getRemainingCycles() < 1) + { + it.remove(); + } + } + } + + Verzik_YellowBall = null; + Verzik_YellowTiles.clear(); + + for (Projectile projectile : client.getProjectiles()) + { + if (projectile.getId() == PROJECTILE_ID_YELLOW) + { + Verzik_YellowBall = projectile; + break; + } + } + for (GraphicsObject o : client.getGraphicsObjects()) + { + if (o.getId() == GRAPHICSOBJECT_ID_YELLOW) + { + Verzik_YellowTiles.add(WorldPoint.fromLocal(client, o.getLocation())); + } + } + + for (NPC npc : client.getNpcs()) + { + if (npc.getId() == 8379) + { + runVerzik = true; + break; + } + runVerzik = false; + } + + + if (Verzik_NPC_P3 != null) { + boolean tornadosActive = false; + for (NPC npc : client.getNpcs()) + { + if (npc.getId() == NPC_ID_TORNADO) + { + tornadosActive = true; + break; + } + } + + boolean isGreenBall = false; + for (Projectile projectile : client.getProjectiles()) + { + if (projectile.getId() == PROJECTILE_ID_P3_GREEN) { + isGreenBall = projectile.getRemainingCycles() > 210; + break; + } + } + P3_TicksUntilAttack--; + + switch (Verzik_NPC_P3.getAnimation()) { + case ANIMATION_ID_P3_MAGE: + if (P3_TicksUntilAttack < 2) { + P3_attacksLeft--; + if (tornadosActive) { + P3_TicksUntilAttack = 5; + } else { + P3_TicksUntilAttack = 7; + } + if (P3_attacksLeft < 1) { + P3_TicksUntilAttack = 24; + } + } + break; + case ANIMATION_ID_P3_RANGE: + if (P3_TicksUntilAttack < 2) { + P3_attacksLeft--; + if (tornadosActive) { + P3_TicksUntilAttack = 5; + } else { + P3_TicksUntilAttack = 7; + } + if (P3_attacksLeft < 1) { + P3_TicksUntilAttack = 30; + } + if (isGreenBall) { + P3_TicksUntilAttack = 12; + } + } + break; + case ANIMATION_ID_P3_MELEE: + if (P3_TicksUntilAttack < 2) { + P3_attacksLeft--; + if (tornadosActive) { + P3_TicksUntilAttack = 5; + } else { + P3_TicksUntilAttack = 7; + } + if (P3_attacksLeft < 1) { + P3_TicksUntilAttack = 24; + } + } + break; + case ANIMATION_ID_P3_WEB: + P3_attacksLeft = 4; + P3_TicksUntilAttack = 11; // + break; + case ANIMATION_ID_P3_YELLOW: + P3_attacksLeft = 14; + P3_TicksUntilAttack = 11; + break; + } + } + + } + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/ZulrahConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/ZulrahConfig.java new file mode 100644 index 0000000000..5e18ef4fc3 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/ZulrahConfig.java @@ -0,0 +1,24 @@ +package net.runelite.client.plugins.zulrah; + +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("zulrah") +public interface ZulrahConfig extends Config { + @ConfigItem( + position = 0, + keyName = "zulrahenable", + name = "Enable Zulrah Helper", + description = "Configures whether or not to enable Zulrah Helper." + ) + default boolean EnableZulrah() { return true; } + + @ConfigItem( + position = 1, + keyName = "zulrahprayenable", + name = "Show Prayer Helper", + description = "Configures whether or not to show when to pray at Zulrah." + ) + default boolean EnableZulrahPrayerHelper() { return true; } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/ZulrahOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/ZulrahOverlay.java new file mode 100644 index 0000000000..ed2dd23991 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/ZulrahOverlay.java @@ -0,0 +1,72 @@ +package net.runelite.client.plugins.zulrah; + +import java.awt.*; +import javax.inject.Inject; + +import net.runelite.api.*; +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.OverlayPriority; +import net.runelite.client.ui.overlay.components.PanelComponent; +import net.runelite.client.ui.overlay.components.TitleComponent; + +public class ZulrahOverlay extends Overlay { + private final ZulrahConfig config; + private final ZulrahPlugin plugin; + private final PanelComponent panelComponent = new PanelComponent(); + + + @Inject + private Client client; + + @Inject + private ZulrahOverlay(ZulrahConfig config, ZulrahPlugin plugin) { + this.config = config; + this.plugin = plugin; + setLayer(OverlayLayer.ABOVE_SCENE); + setPosition(OverlayPosition.DYNAMIC); + setPriority(OverlayPriority.MED); + panelComponent.setPreferredSize(new Dimension(150, 0)); + } + + @Override + public Dimension render(Graphics2D graphics) { + if (!config.EnableZulrahPrayerHelper()) { + return null; + } + NPC Zulrah = plugin.Zulrah; + if (Zulrah != null) { + if (plugin.prayerconserve && plugin.nextprayerendticks == 0) { + Player player = client.getLocalPlayer(); + HeadIcon icon = player.getOverheadIcon(); + if (icon != null) { + final String text = "Disable Overhead Prayer"; + final int textWidth = graphics.getFontMetrics().stringWidth(text); + final int textHeight = graphics.getFontMetrics().getAscent() - graphics.getFontMetrics().getDescent(); + final int width = (int) client.getRealDimensions().getWidth(); + java.awt.Point jpoint = new java.awt.Point((width / 2) - textWidth, textHeight + 75); + panelComponent.getChildren().clear(); + panelComponent.getChildren().add(TitleComponent.builder().text(text).color(Color.RED).build()); + panelComponent.setPreferredLocation(jpoint); + panelComponent.render(graphics); + } + } else if (plugin.nextprayerendticks != 0) { + Player player = client.getLocalPlayer(); + HeadIcon icon = player.getOverheadIcon(); + if (icon == null) { + final String text = "Protect from MAGIC: " + (plugin.nextprayerendticks - plugin.ticks); + final int textWidth = graphics.getFontMetrics().stringWidth(text); + final int textHeight = graphics.getFontMetrics().getAscent() - graphics.getFontMetrics().getDescent(); + final int width = (int) client.getRealDimensions().getWidth(); + java.awt.Point jpoint = new java.awt.Point((width / 2) - textWidth, textHeight + 75); + panelComponent.getChildren().clear(); + panelComponent.getChildren().add(TitleComponent.builder().text(text).color(Color.GREEN).build()); + panelComponent.setPreferredLocation(jpoint); + panelComponent.render(graphics); + } + } + } + return null; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/ZulrahPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/ZulrahPlugin.java new file mode 100644 index 0000000000..10c9f89e74 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/ZulrahPlugin.java @@ -0,0 +1,640 @@ +package net.runelite.client.plugins.zulrah; + +import net.runelite.client.eventbus.Subscribe; +import com.google.inject.Provides; +import javax.inject.Inject; +import java.awt.*; +import java.awt.image.*; +import java.util.*; +import java.util.List; + +import net.runelite.api.*; + +import net.runelite.api.coords.LocalPoint; +import net.runelite.api.events.GameStateChanged; +import net.runelite.api.events.GameTick; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.game.SpriteManager; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.ui.overlay.OverlayManager; +import net.runelite.client.util.ImageUtil; + +@PluginDescriptor( + name = "!Zulrah", + description = "Zulrah Helper", + tags = {"Zulrah", "Helper"} +) +public class ZulrahPlugin extends Plugin +{ + @Inject + private OverlayManager overlayManager; + + @Inject + private ZulrahConfig config; + + @Inject + private ZulrahOverlay ZulrahOverlay; + + @Inject + private ZulrahTileOverlay ZulrahTileOverlay; + + @Inject + private Client client; + + @Inject + private SpriteManager spriteManager; + + @Provides + ZulrahConfig provideConfig(ConfigManager configManager) { + return configManager.getConfig(ZulrahConfig.class); + } + + private static final int[] PROTECTION_ICONS = { + SpriteID.PRAYER_PROTECT_FROM_MISSILES, + SpriteID.PRAYER_PROTECT_FROM_MELEE, + SpriteID.PRAYER_PROTECT_FROM_MAGIC + }; + + private static final Dimension PROTECTION_ICON_DIMENSION = new Dimension(33, 33); + private static final Color PROTECTION_ICON_OUTLINE_COLOR = new Color(33, 33, 33); + public final BufferedImage[] ProtectionIcons = new BufferedImage[PROTECTION_ICONS.length]; + int zulrahstart = 0; + NPC Zulrah; + + @Subscribe + public void onGameStateChanged(GameStateChanged gameStateChanged) + { + if (gameStateChanged.getGameState() == GameState.LOGGED_IN) { + loadProtectionIcons(); + } + } + + @Override + protected void startUp() throws Exception { + overlayManager.add(ZulrahOverlay); + overlayManager.add(ZulrahTileOverlay); + } + + @Override + protected void shutDown() throws Exception { + overlayManager.remove(ZulrahOverlay); + overlayManager.remove(ZulrahTileOverlay); + } + + LocalPoint ZulrahPosCenter = new LocalPoint(6720, 7616); + LocalPoint ZulrahPosWest = new LocalPoint(8000, 7360); + LocalPoint ZulrahPosEast = new LocalPoint(5440, 7360); + LocalPoint ZulrahPosNorth = new LocalPoint(6720, 6208); + + LocalPoint SWCornerTile = new LocalPoint(7488, 7872); + LocalPoint SWCornerTileMelee = new LocalPoint(7232, 8000); + LocalPoint WPillar = new LocalPoint(7232, 7232); + LocalPoint WPillarN = new LocalPoint(7232, 7104); + LocalPoint EPillar = new LocalPoint(6208, 7232); + LocalPoint EPillarN = new LocalPoint(6208, 7104); + LocalPoint SECornerTile = new LocalPoint(6208, 8000); + LocalPoint SECornerTileMelee = new LocalPoint(5952, 7744); + LocalPoint Middle = new LocalPoint(6720, 6848); + + int ticks; + int phaseticks; + int not; + int lastphase; + int phase; + int nextprayerendticks; + boolean phase1 = true; + boolean phase2 = true; + boolean phase3 = true; + boolean phase4 = true; + boolean restart = false; + boolean prayerconserve = false; + Color nztcolor; + LocalPoint nextzulrahtile; + LocalPoint nexttile; + LocalPoint currenttile; + LocalPoint lastloc; + LocalPoint MeleeTile; + List phases = new ArrayList<>(); + List locations = new ArrayList<>(); + + ArrayList Phase1types = new ArrayList<>(Arrays.asList(2042, 2043, 2044, 2042, 2044, 2043, 2042, 2044, 2042, 2043)); + ArrayList Phase1pos = new ArrayList<>(Arrays.asList(ZulrahPosCenter, ZulrahPosCenter, ZulrahPosCenter, ZulrahPosEast, ZulrahPosNorth, ZulrahPosCenter, ZulrahPosWest, ZulrahPosNorth, ZulrahPosEast, ZulrahPosCenter)); + ArrayList Phase1tiles = new ArrayList<>(Arrays.asList(SWCornerTile, SWCornerTile, SWCornerTile, EPillar, EPillarN, EPillar, Middle, EPillar, EPillar, SWCornerTile)); + ArrayList Phase1ticks = new ArrayList<>(Arrays.asList(28, 20, 18, 28, 39, 22, 20, 36, 48, 20)); + + ArrayList Phase2types = new ArrayList<>(Arrays.asList(2042, 2043, 2044, 2042, 2043, 2044, 2042, 2044, 2042, 2043)); + ArrayList Phase2pos = new ArrayList<>(Arrays.asList(ZulrahPosCenter, ZulrahPosCenter, ZulrahPosCenter, ZulrahPosNorth, ZulrahPosCenter, ZulrahPosEast, ZulrahPosNorth, ZulrahPosNorth, ZulrahPosEast, ZulrahPosCenter)); + ArrayList Phase2tiles = new ArrayList<>(Arrays.asList(SWCornerTile, SWCornerTile, SWCornerTile, EPillar, EPillar, EPillar, WPillar, WPillarN, EPillar, SWCornerTile)); + ArrayList Phase2ticks = new ArrayList<>(Arrays.asList(28, 20, 17, 39, 22, 20, 28, 36, 48, 21)); + + ArrayList Phase3types = new ArrayList<>(Arrays.asList(2042, 2042, 2043, 2044, 2042, 2044, 2042, 2042, 2044, 2042, 2044)); + ArrayList Phase3pos = new ArrayList<>(Arrays.asList(ZulrahPosCenter, ZulrahPosWest, ZulrahPosCenter, ZulrahPosEast, ZulrahPosNorth, ZulrahPosWest, ZulrahPosCenter, ZulrahPosEast, ZulrahPosCenter, ZulrahPosWest, ZulrahPosCenter)); + ArrayList Phase3tiles = new ArrayList<>(Arrays.asList(SWCornerTile, SWCornerTile, SECornerTile, EPillar, WPillar, WPillar, EPillar, EPillar, WPillar, WPillar, SWCornerTile)); + ArrayList Phase3ticks = new ArrayList<>(Arrays.asList(28, 30, 40, 20, 20, 20, 25, 20, 36, 35, 18)); + + ArrayList Phase4types = new ArrayList<>(Arrays.asList(2042, 2044, 2042, 2044, 2043, 2042, 2042, 2044, 2042, 2044, 2042, 2044)); + ArrayList Phase4pos = new ArrayList<>(Arrays.asList(ZulrahPosCenter, ZulrahPosWest, ZulrahPosNorth, ZulrahPosEast, ZulrahPosCenter, ZulrahPosWest, ZulrahPosNorth, ZulrahPosEast, ZulrahPosCenter, ZulrahPosCenter, ZulrahPosWest, ZulrahPosCenter)); + ArrayList Phase4tiles = new ArrayList<>(Arrays.asList(SWCornerTile, SWCornerTile, EPillar, EPillar, WPillar, WPillar, WPillar, EPillar, WPillar, WPillar, WPillar, SWCornerTile)); + ArrayList Phase4ticks = new ArrayList<>(Arrays.asList(28, 36, 24, 30, 28, 17, 34, 33, 20, 27, 29, 18)); + + @Subscribe + public void onGameTick(GameTick event) { + if (!config.EnableZulrah()) { + return; + } + + boolean foundzulrah = false; + for (NPC monster : client.getNpcs()) + { + if (monster == null || monster.getName() == null) + { + continue; + } + if (monster.getName().equalsIgnoreCase("zulrah")) { + foundzulrah = true; + Zulrah = monster; + break; + } + } + if (!foundzulrah) { + Zulrah = null; + } + + if (Zulrah != null) { + if (zulrahstart == 0) { + currenttile = SWCornerTile; + lastloc = Zulrah.getLocalLocation(); + lastphase = Zulrah.getId(); + zulrahstart = client.getTickCount(); + phases.add(lastphase); + locations.add(lastloc); + phaseticks = 28; + } else { + if (!Zulrah.getLocalLocation().equals(lastloc) || Zulrah.getId() != lastphase) { + if (restart) { + phases.clear(); + locations.clear(); + zulrahstart = client.getTickCount(); + lastphase = 0; + lastloc = null; + phase = 0; + phase1 = true; + phase2 = true; + phase3 = true; + phase4 = true; + nextzulrahtile = null; + nztcolor = null; + nexttile = null; + currenttile = SWCornerTile; + restart = false; + ticks = 0; + prayerconserve = false; + phaseticks = 34; + not = 0; + nextprayerendticks = 0; + } + lastloc = Zulrah.getLocalLocation(); + lastphase = Zulrah.getId(); + ticks = 0; + phases.add(lastphase); + locations.add(lastloc); + if (phase == 0) { + for (int i = 0; i < phases.size(); i++) { + if (phase1) { + if (!phases.get(i).equals(Phase1types.get(i)) || !locations.get(i).equals(Phase1pos.get(i))) { + phase1 = false; + not++; + } + } + if (phase2) { + if (!phases.get(i).equals(Phase2types.get(i)) || !locations.get(i).equals(Phase2pos.get(i))) { + phase2 = false; + not++; + } + } + if (phase3) { + if (!phases.get(i).equals(Phase3types.get(i)) || !locations.get(i).equals(Phase3pos.get(i))) { + phase3 = false; + not++; + } + } + if (phase4) { + if (!phases.get(i).equals(Phase4types.get(i)) || !locations.get(i).equals(Phase4pos.get(i))) { + phase4 = false; + not++; + } + } + } + + if (not == 2) { + if (lastphase == 2043) { + nztcolor = Color.BLUE; + nextzulrahtile = ZulrahPosCenter; + currenttile = SWCornerTile; + nexttile = SWCornerTile; + phaseticks = Phase2ticks.get(phases.size() - 1); + prayerconserve = true; + } else if (lastphase == 2044) { + nztcolor = Color.GREEN; + nextzulrahtile = ZulrahPosNorth; + currenttile = SWCornerTile; + nexttile = EPillar; + phaseticks = Phase2ticks.get(phases.size() - 1); + prayerconserve = false; + } + } else if (not == 3) { + if (phase1) { + nztcolor = zulrahtype(Phase1types.get(phases.size())); + nextzulrahtile = Phase1pos.get(phases.size()); + currenttile = Phase1tiles.get(phases.size() - 1); + nexttile = Phase1tiles.get(phases.size()); + phaseticks = Phase1ticks.get(phases.size() - 1); + prayerconserve = true; + phase = 1; + } else if (phase2) { + nztcolor = zulrahtype(Phase2types.get(phases.size())); + nextzulrahtile = Phase2pos.get(phases.size()); + currenttile = Phase2tiles.get(phases.size() - 1); + nexttile = Phase2tiles.get(phases.size()); + phaseticks = Phase2ticks.get(phases.size() - 1); + prayerconserve = false; + phase = 2; + } else if (phase3) { + nztcolor = zulrahtype(Phase3types.get(phases.size())); + nextzulrahtile = Phase3pos.get(phases.size()); + currenttile = Phase3tiles.get(phases.size() - 1); + nexttile = Phase3tiles.get(phases.size()); + phaseticks = Phase3ticks.get(phases.size() - 1); + prayerconserve = false; + phase = 3; + } else if (phase4) { + nztcolor = zulrahtype(Phase4types.get(phases.size())); + nextzulrahtile = Phase4pos.get(phases.size()); + currenttile = Phase4tiles.get(phases.size() - 1); + nexttile = Phase4tiles.get(phases.size()); + phaseticks = Phase4ticks.get(phases.size() - 1); + prayerconserve = true; + phase = 4; + } else { + System.out.println("ERROR: COULD NOT IDENTIFY ZULRAH PHASE!"); + } + not = 0; + } + } else { + if (phase == 1) { + if (Phase1types.size() == phases.size()) { + nztcolor = null; + nextzulrahtile = null; + nexttile = null; + restart = true; + } else { + nextzulrahtile = Phase1pos.get(phases.size()); + nexttile = Phase1tiles.get(phases.size()); + if (phases.size() == 8) { + nztcolor = Color.YELLOW; + } else { + nztcolor = zulrahtype(Phase1types.get(phases.size())); + } + } + currenttile = Phase1tiles.get(phases.size() - 1); + phaseticks = Phase1ticks.get(phases.size() - 1); + } else if (phase == 2) { + if (Phase2types.size() == phases.size()) { + nztcolor = null; + nextzulrahtile = null; + nexttile = null; + restart = true; + } else { + nextzulrahtile = Phase2pos.get(phases.size()); + nexttile = Phase2tiles.get(phases.size()); + if (phases.size() == 8) { + nztcolor = Color.YELLOW; + } else { + nztcolor = zulrahtype(Phase2types.get(phases.size())); + } + } + currenttile = Phase2tiles.get(phases.size() - 1); + phaseticks = Phase2ticks.get(phases.size() - 1); + } else if (phase == 3) { + if (Phase3types.size() == phases.size()) { + nztcolor = null; + nextzulrahtile = null; + nexttile = null; + restart = true; + } else { + nextzulrahtile = Phase3pos.get(phases.size()); + nexttile = Phase3tiles.get(phases.size()); + if (phases.size() == 9) { + nztcolor = Color.YELLOW; + } else { + nztcolor = zulrahtype(Phase3types.get(phases.size())); + } + } + currenttile = Phase3tiles.get(phases.size() - 1); + phaseticks = Phase3ticks.get(phases.size() - 1); + } else if (phase == 4) { + if (Phase4types.size() == phases.size()) { + nztcolor = null; + nextzulrahtile = null; + nexttile = null; + restart = true; + } else { + nextzulrahtile = Phase4pos.get(phases.size()); + nexttile = Phase4tiles.get(phases.size()); + if (phases.size() == 10) { + nztcolor = Color.YELLOW; + } else { + nztcolor = zulrahtype(Phase4types.get(phases.size())); + } + } + currenttile = Phase4tiles.get(phases.size() - 1); + phaseticks = Phase4ticks.get(phases.size() - 1); + } else { + System.out.println("ERROR: COULD NOT IDENTIFY ZULRAH PHASE!"); + } + } + } else { + ticks++; + if (phases.size() == 1 && phaseticks == 34) { + if (ticks >= 18) { + prayerconserve = true; + } else { + prayerconserve = false; + } + } + if (not == 2) { + if (lastphase == 2043) { + if (ticks >= 12 && ticks <= 13) { + MeleeTile = SWCornerTileMelee; + } else { + MeleeTile = null; + } + } + } else if (phase == 1) { + if (phases.size() == 5) { + if (ticks >= 19) { + prayerconserve = true; + } else { + prayerconserve = false; + } + } else if (phases.size() == 8) { + if (ticks >= 19) { + prayerconserve = true; + } else { + prayerconserve = false; + } + } else if (phases.size() == 9) { + if (ticks >= 34) { + prayerconserve = true; + } else { + prayerconserve = false; + } + } else if (phases.size() == 10) { + if (ticks >= 12 && ticks <= 13) { + MeleeTile = SWCornerTileMelee; + } else { + MeleeTile = null; + } + } else if (phases.size() == 4 || phases.size() == 6 || phases.size() == 10) { + prayerconserve = true; + } else { + prayerconserve = false; + } + } else if (phase == 2) { + if (phases.size() == 4) { + if (ticks >= 20) { + prayerconserve = true; + } else { + prayerconserve = false; + } + } else if (phases.size() == 8) { + if (ticks >= 18) { + prayerconserve = true; + } else { + prayerconserve = false; + } + } else if (phases.size() == 9) { + if (ticks >= 34) { + prayerconserve = true; + } else { + prayerconserve = false; + } + } else if (phases.size() == 5 || phases.size() == 7 || phases.size() == 10) { + if (phases.size() == 10) { + if (ticks >= 12 && ticks <= 13) { + MeleeTile = SWCornerTileMelee; + } else { + MeleeTile = null; + } + } + prayerconserve = true; + } else { + prayerconserve = false; + } + } else if (phase == 3) { + if (phases.size() == 2) { + if (ticks >= 20) { + prayerconserve = true; + } else { + prayerconserve = false; + } + } else if (phases.size() == 3) { + prayerconserve = true; + if (ticks >= 24 && ticks <= 25) { + MeleeTile = SECornerTileMelee; + } else if (ticks >= 32 && ticks <= 33) { + MeleeTile = SECornerTile; + } else { + MeleeTile = null; + } + } else if (phases.size() == 7 || phases.size() == 11) { + prayerconserve = true; + } else if (phases.size() == 9) { + if (ticks >= 16) { + prayerconserve = true; + } else { + prayerconserve = false; + } + } else { + prayerconserve = false; + } + } else if (phase == 4) { + if (phases.size() == 2) { + if (ticks >= 10 && ticks <= 16) { + nextprayerendticks = 16; + } else { + nextprayerendticks = 0; + } + + if (ticks >= 16) { + prayerconserve = false; + } else { + prayerconserve = true; + } + } else if (phases.size() == 3) { + if (ticks >= 16) { + prayerconserve = true; + } else { + prayerconserve = false; + } + } else if (phases.size() == 4) { + if (ticks >= 10 && ticks <= 16) { + nextprayerendticks = 16; + } else { + nextprayerendticks = 0; + } + + if (ticks <= 16) { + prayerconserve = true; + } else { + prayerconserve = false; + } + } else if (phases.size() == 5 || phases.size() == 7 || phases.size() == 12) { + prayerconserve = true; + } else if (phases.size() == 8) { + if (ticks >= 18) { + prayerconserve = true; + } else { + prayerconserve = false; + } + } else if (phases.size() == 10) { + if (ticks >= 14) { + prayerconserve = true; + } else { + prayerconserve = false; + } + } else { + prayerconserve = false; + } + } + } + } + } else { + if (zulrahstart > 0) { + phases.clear(); + locations.clear(); + zulrahstart = 0; + lastphase= 0; + lastloc = null; + phase = 0; + phase1 = true; + phase2 = true; + phase3 = true; + phase4 = true; + nextzulrahtile = null; + nztcolor = null; + nexttile = null; + currenttile = null; + restart = false; + ticks = 0; + prayerconserve = false; + not = 0; + nextprayerendticks = 0; + } + } + } + + public Color zulrahtype(int type) { + switch(type) { + case 2042: + return Color.GREEN; + case 2043: + return Color.RED; + case 2044: + return Color.BLUE; + } + return null; + } + + private void loadProtectionIcons() { + final IndexedSprite[] protectionIcons = {}; + final IndexedSprite[] newProtectionIcons = Arrays.copyOf(protectionIcons, PROTECTION_ICONS.length); + int curPosition = 0; + + for (int i = 0; i < PROTECTION_ICONS.length; i++, curPosition++) + { + final int resource = PROTECTION_ICONS[i]; + ProtectionIcons[i] = rgbaToIndexedBufferedImage(ProtectionIconFromSprite(spriteManager.getSprite(resource, 0))); + newProtectionIcons[curPosition] = createIndexedSprite(client, ProtectionIcons[i]); + } + } + + private static IndexedSprite createIndexedSprite(final Client client, final BufferedImage bufferedImage) { + final IndexColorModel indexedCM = (IndexColorModel) bufferedImage.getColorModel(); + + final int width = bufferedImage.getWidth(); + final int height = bufferedImage.getHeight(); + final byte[] pixels = ((DataBufferByte) bufferedImage.getRaster().getDataBuffer()).getData(); + final int[] palette = new int[indexedCM.getMapSize()]; + indexedCM.getRGBs(palette); + + final IndexedSprite newIndexedSprite = client.createIndexedSprite(); + newIndexedSprite.setPixels(pixels); + newIndexedSprite.setPalette(palette); + newIndexedSprite.setWidth(width); + newIndexedSprite.setHeight(height); + newIndexedSprite.setOriginalWidth(width); + newIndexedSprite.setOriginalHeight(height); + newIndexedSprite.setOffsetX(0); + newIndexedSprite.setOffsetY(0); + return newIndexedSprite; + } + + private static BufferedImage rgbaToIndexedBufferedImage(final BufferedImage sourceBufferedImage) { + final BufferedImage indexedImage = new BufferedImage( + sourceBufferedImage.getWidth(), + sourceBufferedImage.getHeight(), + BufferedImage.TYPE_BYTE_INDEXED); + + final ColorModel cm = indexedImage.getColorModel(); + final IndexColorModel icm = (IndexColorModel) cm; + + final int size = icm.getMapSize(); + final byte[] reds = new byte[size]; + final byte[] greens = new byte[size]; + final byte[] blues = new byte[size]; + icm.getReds(reds); + icm.getGreens(greens); + icm.getBlues(blues); + + final WritableRaster raster = indexedImage.getRaster(); + final int pixel = raster.getSample(0, 0, 0); + final IndexColorModel resultIcm = new IndexColorModel(8, size, reds, greens, blues, pixel); + final BufferedImage resultIndexedImage = new BufferedImage(resultIcm, raster, sourceBufferedImage.isAlphaPremultiplied(), null); + resultIndexedImage.getGraphics().drawImage(sourceBufferedImage, 0, 0, null); + return resultIndexedImage; + } + + private static BufferedImage ProtectionIconFromSprite(final BufferedImage freezeSprite) { + final BufferedImage freezeCanvas = ImageUtil.resizeCanvas(freezeSprite, PROTECTION_ICON_DIMENSION.width, PROTECTION_ICON_DIMENSION.height); + return ImageUtil.outlineImage(freezeCanvas, PROTECTION_ICON_OUTLINE_COLOR); + } + + BufferedImage getProtectionIcon() { + int type = 0; + if (phase1) { + type = Phase1types.get(phases.size()); + } else if (phase2) { + type = Phase2types.get(phases.size()); + } else if (phase3) { + type = Phase3types.get(phases.size()); + } else if (phase4) { + type = Phase4types.get(phases.size()); + } else { + System.out.println("ERROR: COULD NOT IDENTIFY ZULRAH PHASE!"); + } + + if (type > 0) { + switch (type) { + case 2042: + return ProtectionIcons[0]; + case 2043: + return ProtectionIcons[1]; + case 2044: + return ProtectionIcons[2]; + } + } + return null; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/ZulrahTileOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/ZulrahTileOverlay.java new file mode 100644 index 0000000000..28d14f026b --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/ZulrahTileOverlay.java @@ -0,0 +1,120 @@ +package net.runelite.client.plugins.zulrah; + +import java.awt.*; +import java.awt.image.BufferedImage; +import javax.inject.Inject; + +import net.runelite.api.*; +import net.runelite.api.Point; +import net.runelite.client.ui.FontManager; +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.OverlayPriority; +import net.runelite.client.ui.overlay.OverlayUtil; + +public class ZulrahTileOverlay extends Overlay +{ + private final ZulrahConfig config; + private final ZulrahPlugin plugin; + + @Inject + private Client client; + + @Inject + private ZulrahTileOverlay(ZulrahConfig config, ZulrahPlugin plugin) + { + this.config = config; + this.plugin = plugin; + setLayer(OverlayLayer.ABOVE_SCENE); + setPosition(OverlayPosition.DYNAMIC); + setPriority(OverlayPriority.MED); + } + + @Override + public Dimension render(Graphics2D graphics) + { + + NPC Zulrah = plugin.Zulrah; + if (Zulrah != null) { + OverlayUtil.renderTextLocation(graphics, Zulrah.getCanvasTextLocation(graphics, Integer.toString(plugin.phaseticks - plugin.ticks), Zulrah.getLogicalHeight() + 40), Integer.toString(plugin.phaseticks - plugin.ticks), Color.WHITE); + Player player = client.getLocalPlayer(); + if (plugin.currenttile != null) { + if (plugin.currenttile.equals(plugin.nexttile)) { + final Polygon poly = Perspective.getCanvasTilePoly(client, plugin.currenttile); + if (poly != null) { + Point textLocationtile = Perspective.getCanvasTextLocation(client, graphics, plugin.currenttile, "Current & Next", 50); + OverlayUtil.renderTextLocation(graphics, textLocationtile, "Current & Next", Color.WHITE); + OverlayUtil.renderPolygon(graphics, poly, Color.WHITE); + } + } else { + if (!player.getLocalLocation().equals(plugin.currenttile)) { + final Polygon poly = Perspective.getCanvasTilePoly(client, plugin.currenttile); + if (poly != null) { + Point textLocationtile = Perspective.getCanvasTextLocation(client, graphics, plugin.currenttile, "Current", 50); + OverlayUtil.renderTextLocation(graphics, textLocationtile, "Current", Color.WHITE); + OverlayUtil.renderPolygon(graphics, poly, Color.GREEN); + } + } + if (plugin.nexttile != null) { + final Polygon poly2 = Perspective.getCanvasTilePoly(client, plugin.nexttile); + if (poly2 != null) { + Point textLocationtile = Perspective.getCanvasTextLocation(client, graphics, plugin.nexttile, "Next", 50); + OverlayUtil.renderTextLocation(graphics, textLocationtile, "Next", Color.WHITE); + OverlayUtil.renderPolygon(graphics, poly2, Color.RED); + } + } + } + } + if (plugin.nextzulrahtile != null) { + String style = ""; + if (plugin.nztcolor.equals(Color.RED)) { + style = "MELEE"; + } else if (plugin.nztcolor.equals(Color.BLUE)) { + style = "MAGE"; + } else if (plugin.nztcolor.equals(Color.GREEN)) { + style = "RANGE"; + } else if (plugin.nztcolor.equals(Color.YELLOW)) { + style = "JAD"; + } + + final Polygon poly = Perspective.getCanvasTilePoly(client, plugin.nextzulrahtile); + Point textLocation = Perspective.getCanvasTextLocation(client, graphics, plugin.nextzulrahtile, style, 200); + if (poly != null) + { + BufferedImage clanchatImage = null; + if (style.equals("JAD")) { + if (plugin.phase4 && plugin.phases.size() == 10) { + clanchatImage = plugin.ProtectionIcons[2]; + } else if (plugin.phase3 && plugin.phases.size() == 9) { + clanchatImage = plugin.ProtectionIcons[2]; + } else { + clanchatImage = plugin.ProtectionIcons[0]; + } + } else { + clanchatImage = plugin.getProtectionIcon(); + } + + if (clanchatImage != null) { + Point imageLocation = new Point(textLocation.getX(), textLocation.getY() + 15); + OverlayUtil.renderImageLocation(graphics, imageLocation, clanchatImage); + } + + graphics.setFont(FontManager.getRunescapeBoldFont()); + OverlayUtil.renderTextLocation(graphics, textLocation, style, Color.WHITE); + OverlayUtil.renderPolygon(graphics, poly, plugin.nztcolor); + } + } + if (plugin.MeleeTile != null) { + final Polygon poly = Perspective.getCanvasTilePoly(client, plugin.MeleeTile); + if (poly != null) { + Point textLocationtile = Perspective.getCanvasTextLocation(client, graphics, plugin.MeleeTile, "MOVE HERE NOW!", 50); + graphics.setFont(FontManager.getRunescapeBoldFont()); + OverlayUtil.renderTextLocation(graphics, textLocationtile, "MOVE HERE NOW!", Color.WHITE); + OverlayUtil.renderPolygon(graphics, poly, Color.BLACK); + } + } + } + return null; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/util/MiscUtils.java b/runelite-client/src/main/java/net/runelite/client/util/MiscUtils.java new file mode 100644 index 0000000000..039c3cfa2b --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/util/MiscUtils.java @@ -0,0 +1,79 @@ +package net.runelite.client.util; + +import net.runelite.api.Client; +import net.runelite.api.Player; +import net.runelite.api.WorldType; +import net.runelite.api.coords.WorldPoint; + +import java.awt.*; + +public class MiscUtils +{ + private static int[] abovePointsX = { 2944, 3392, 3392, 2944 }; + private static int[] abovePointsY = { 3523, 3523, 3971, 3971 }; + private static int[] belowPointsX = { 2944, 2944, 3264, 3264 }; + private static int[] belowPointsY = { 9918, 10360, 10360, 9918 }; + + private static Polygon abovePoly = new Polygon(abovePointsX, abovePointsY, abovePointsX.length); + private static Polygon belowPoly = new Polygon(belowPointsX, belowPointsY, belowPointsX.length); + + //test replacement so private for now + private static boolean inWildy(WorldPoint point) + { + if (point == null) + return false; + + return abovePoly.contains(point.getX(), point.getY()) || belowPoly.contains(point.getX(), point.getY()); + } + + public static int getWildernessLevelFrom(Client client, WorldPoint point) + { + if (client == null) + return 0; + + if (point == null) + return 0; + + int x = point.getX(); + + if (point.getPlane() == 0 && (x < 2940 || x > 3391)) + return 0; + + int y = point.getY(); + //v underground //v above ground + int wildernessLevel = clamp(y > 6400 ? ((y - 9920) / 8) + 1 : ((y - 3520) / 8) + 1, 0, 56); + + if (point.getPlane() > 0) + if (y < 9920) + wildernessLevel = 0; + + if (client.getWorldType().stream().anyMatch(worldType -> worldType == WorldType.PVP || worldType == WorldType.PVP_HIGH_RISK)) + { + wildernessLevel += 15; + } + + return Math.max(0, wildernessLevel); + } + + public static int clamp(int val, int min, int max) + { + return Math.max(min, Math.min(max, val)); + } + + public static float clamp(float val, float min, float max) + { + return Math.max(min, Math.min(max, val)); + } + + public static boolean inWilderness(Client client) + { + Player localPlayer = client.getLocalPlayer(); + + if (localPlayer == null) + return false; + + return inWildy(localPlayer.getWorldLocation()); + + //return getWildernessLevelFrom(client, localPlayer.getWorldLocation()) > 0; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/util/WildernessLocation.java b/runelite-client/src/main/java/net/runelite/client/util/WildernessLocation.java new file mode 100644 index 0000000000..19e62c64a4 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/util/WildernessLocation.java @@ -0,0 +1,98 @@ + +package net.runelite.client.util; + +import net.runelite.api.coords.WorldArea; + +public enum WildernessLocation { + REV_CAVE_OTHER("Rev Cave", new Location(3128, 10232, 3225, 10059), 0), + REV_BLACK_DRAGS("Rev Black Drags", new Location(3223, 10216, 3254, 10190), 0), + REV_DARK_BEAST("Rev Dark Beast", new Location(3243, 10154, 3264, 10136), 0), + REV_MAIN_CHAMBER("Main Rev Chamber", new Location(3227, 10187, 3261, 10157), 0), + REV_ENTRANCE_INSIDE("Inside Rev Ent.", new Location(3238, 10236, 3243, 10231), 0), + ICE_ROCK("Ice Rock", new Location(2957, 3942, 2984, 3929), 0), + WILDY_AGILITY_COURSE("Wildy Agility Course", new Location(2988, 3967, 3008, 3906), 0), + FIRE_GIANT_ENTRANCE("Fire Giant Entrance", new Location(3042, 3929, 3051, 3920), 0), + PIRATE_HUT("Pirate Hut", new Location(3037, 3959, 3045, 3948), 0), + MAGE_BANK("Mage Bank", new Location(3082, 3960, 3103, 3952), 0), + MAGE_ARENA("Mage Arena", new Location(3088, 3949, 3123, 3919), 0), + LEVER("Lever", new Location(3149, 3933, 3162, 3917), 0), + WEB("Web", new Location(3153, 3961, 3163, 3948), 0), + RESOURCE_ARENA("Resource Arena", new Location(3174, 3946, 3195, 3923), 0), + AXE_HUT("Axe Hut", new Location(3187, 3962, 3194, 3957), 0), + SCORPIA("Scorpia", new Location(3216, 3949, 3248, 3935), 0), + ROGUE_CASTLE("Rogue Castle", new Location(3275, 3947, 3299, 3920), 0), + FIFTY_PORTS("50 ports", new Location(3301, 3923, 3315, 3909), 0), + VOLCANO("Volcano", new Location(3345, 3957, 3390, 3916), 0), + NEW_GATE("New Gate", new Location(3345, 3957, 3390, 3916), 0), + GLORY_HOLE("Glory Hole", new Location(3352, 3897, 3386, 3869), 0), + GLORY_HILL("Glory Hill", new Location(3331, 3890, 3348, 3866), 0), + GDZ("Gdz", new Location(3279, 3895, 3296, 3875), 0), + GAP("Gap", new Location(3238, 3855, 3258, 3841), 0), + OLD_GATE("Old Gate", new Location(3211, 3906, 3238, 3882), 0), + LAVA_DRAGS("Lava Drags", new Location(3175, 3857, 3221, 3805), 0), + SPIDER_HILL("Spider Hill", new Location(3156, 3896, 3182, 3871), 0), + RUNE_ROCKS("Rune Rocks", new Location(3055, 3890, 3072, 3876), 0), + ICE_GATE("Ice Gate", new Location(2945, 3913, 2978, 3878), 0), + VENENATIS("Venenatis", new Location(3298, 3759, 3353, 3722), 0), + SINGLE_STRIP("Single Strip", new Location(3333, 3842, 3348, 3774), 0), + CALLISTO("Callisto", new Location(3266, 3863, 3315, 3827), 0), + DWARVES("Dwarves", new Location(3230, 3805, 3264, 3779), 0), + VETTION("Vet'tion", new Location(3183, 3796, 3227, 3765), 0), + EAST_DRAGONS("East Drags", new Location(3326, 3704, 3365, 3671), 0), + HILL_GIANTS("Hill Giants", new Location(3282, 3687, 3300, 3674), 0), + ENTS("Ents", new Location(3300, 3627, 3320, 3584), 0), + CHAOS_TEMPLE("Chaos Temple", new Location(3220, 3632, 3255, 3593), 0), + NINETEEN_OBELISK("19s", new Location(3220, 3672, 3234, 3660), 0), + CORP_CAVE("Corp Cave", new Location(3201, 3684, 3219, 3672), 0), + THIRTEEN_OBELISK("13s", new Location(3145, 3628, 3168, 3609), 0), + SOUTH_REV_ENTRANCE("Lvl 18 Rev Ent", new Location(3071, 3660, 3092, 3645), 0), + GRAVES("Graves", new Location(3128, 3686, 3181, 3658), 0), + GRAVEYARD_DRAGS("Graveyard Drags", new Location(3129, 3717, 3172, 3691), 0), + CHINS("Chins", new Location(3128, 3792, 3160, 3754), 0), + REV_ENTRANCE("Rev Entrance", new Location(3118, 3837, 3142, 3818), 0), + HOB_OBELISK("35 Obelisk", new Location(3097, 3804, 3115, 3785), 0), + HOBGOBLINS("Hobgoblins", new Location(3073, 3775, 3104, 3745), 0), + GWD("God Wars Dungeon", new Location(3010, 3745, 3027, 3727), 0), + LAVA_MAZE_TELE("Lava Maze Tele", new Location(3019, 3842, 3044, 3812), 0), + KBD_CAGE("KBD CAGE", new Location(3007, 3855, 3021, 3839), 0), + GHORROCK("44s", new Location(2973, 3870, 2987, 3859), 0), + CHAOS_FANATIC("Chaos Fanatic", new Location(2971, 3854, 2992, 3834), 0), + HIGH_ALTAR("High Altar", new Location(2945, 3826, 2970, 3813), 0), + CEMETERY("Cemetery", new Location(2956, 3767, 2996, 3736), 0), + CRAZY_ARCHAEOLOGIST("Crazy Archaeologist", new Location(2952, 3709, 2985, 3678), 0), + DARK_WARRIOR_FORTRESS("Dark Warriors", new Location(3014, 3648, 3046, 3616), 0), + WEST_DRAGONS("West Drags", new Location(2960, 3627, 2992, 3598), 0), + BANDIT_CAMP("Bandit Camp", new Location(3017, 3712, 3059, 3681), 0); + + private final String name1; + private final WorldArea worldArea; + + private WildernessLocation(String name, Location location, int plane) { + name1 = name; + worldArea = new WorldArea(location.x, location.y, location.width, location.height, plane); + } + + public String getName() { + return name1; + } + + public WorldArea getWorldArea() { + return worldArea; + } + + public static class Location { + public int x; + public int y; + public int width; + public int height; + + Location(int x, int y, int x1, int y1) { + x = x; + y = y1; + width = x1 - x; + height = y - y1; + } + } + +} + diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/profiles/delete_icon.png b/runelite-client/src/main/resources/net/runelite/client/plugins/profiles/delete_icon.png new file mode 100644 index 0000000000..18b67f23f3 Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/profiles/delete_icon.png differ diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/profiles/profiles_icon.png b/runelite-client/src/main/resources/net/runelite/client/plugins/profiles/profiles_icon.png new file mode 100644 index 0000000000..a733eaf4a1 Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/profiles/profiles_icon.png differ