From 1a0c2eb03ba22422d36316bcabefa4bbbff75f00 Mon Sep 17 00:00:00 2001 From: zeruth Date: Fri, 19 Apr 2019 22:59:29 -0400 Subject: [PATCH] Plugins Bringup Adds all? non broken widget plugins: AoeWarnings BATools ClanmanMode Equipment Inspector Fight Cave Wave Helper Fight Cave Jad Helper Freeze Timers Grotesque Guardians Hide Prayers Hydra Lizardmen Shaman Menu Modifier Music Modifier Next Hit Notifier PK Vision Plank make helper Pray against player Profiles Pyramid Plunder Screen markers Shayzien Infirmary Shift Walker Slayermusiq Spellbook fixer Supplies tracker Temple Trek Time Tracking Theatre of Blood damage counter Vetion Vorkath Wilderness Locations Zone Indicators ZTOB Zulrah --- .../src/main/java/net/runelite/api/Actor.java | 7 + .../java/net/runelite/api/AnimationID.java | 21 +- .../main/java/net/runelite/api/VarPlayer.java | 3 +- .../main/java/net/runelite/api/Varbits.java | 11 +- .../runelite/api/events/CannonballFired.java | 32 + .../java/net/runelite/api/kit/KitType.java | 5 +- .../net/runelite/api/widgets/WidgetID.java | 145 +- .../net/runelite/api/widgets/WidgetInfo.java | 62 +- .../plugins/aoewarnings/AoeProjectile.java | 58 + .../aoewarnings/AoeProjectileInfo.java | 128 + .../plugins/aoewarnings/AoeWarningConfig.java | 213 + .../aoewarnings/AoeWarningOverlay.java | 166 + .../plugins/aoewarnings/AoeWarningPlugin.java | 307 ++ .../plugins/aoewarnings/BombOverlay.java | 178 + .../plugins/aoewarnings/CrystalBomb.java | 64 + .../client/plugins/batools/BAToolsConfig.java | 124 + .../plugins/batools/BAToolsOverlay.java | 126 + .../client/plugins/batools/BAToolsPlugin.java | 643 +++ .../client/plugins/batools/Calls.java | 84 + .../client/plugins/batools/CycleCounter.java | 14 + .../client/plugins/batools/Healer.java | 84 + .../client/plugins/batools/HealerCode.java | 34 + .../clanmanmode/ClanManModeConfig.java | 168 + .../ClanManModeMinimapOverlay.java | 52 + .../clanmanmode/ClanManModeOverlay.java | 63 + .../clanmanmode/ClanManModePlugin.java | 137 + .../clanmanmode/ClanManModeService.java | 157 + .../clanmanmode/ClanManModeTileOverlay.java | 48 + .../EquipmentInspectorConfig.java | 65 + .../EquipmentInspectorPanel.java | 98 + .../EquipmentInspectorPlugin.java | 240 ++ .../plugins/equipmentinspector/ItemPanel.java | 52 + .../plugins/equipmentinspector/normal.png | Bin 0 -> 624 bytes .../FightCaveJadHelperOverlay.java | 63 + .../FightCaveJadHelperPlugin.java | 89 + .../plugins/fightcavejadhelper/JadAttack.java | 29 + .../FightCaveWaveHelperConfig.java | 43 + .../FightCaveWaveHelperPlugin.java | 173 + .../fightcavewavehelper/WaveDisplayMode.java | 43 + .../fightcavewavehelper/WaveMonster.java | 47 + .../fightcavewavehelper/WaveOverlay.java | 125 + .../client/plugins/freezetimers/Barrage.java | 43 + .../freezetimers/FreezeTimersConfig.java | 51 + .../freezetimers/FreezeTimersOverlay.java | 157 + .../freezetimers/FreezeTimersPlugin.java | 402 ++ .../freezetimers/FreezeTimersService.java | 81 + .../freezetimers/FreezeTimersTileOverlay.java | 46 + .../freezetimers/PlayerSpellEffect.java | 35 + .../client/plugins/freezetimers/Spell.java | 34 + .../GrotesqueGuardiansOverlay.java | 100 + .../GrotesqueGuardiansPlugin.java | 56 + .../hideprayers/HidePrayersConfig.java | 35 + .../hideprayers/HidePrayersPlugin.java | 169 + .../plugins/hideprayers/PrayerTabState.java | 8 + .../client/plugins/hydra/HydraConfig.java | 41 + .../plugins/hydra/HydraIndicatorOverlay.java | 52 + .../client/plugins/hydra/HydraOverlay.java | 76 + .../client/plugins/hydra/HydraPlugin.java | 145 + .../plugins/hydra/HydraPrayOverlay.java | 100 + .../kittennotifier/KittenNotifierConfig.java | 27 + .../kittennotifier/KittenNotifierPlugin.java | 84 + .../LizardmenShamanConfig.java | 32 + .../LizardmenShamanPlugin.java | 91 + .../lizardmenshaman/LizardmenShamanSpawn.java | 18 + .../lizardmenshaman/ShamanSpawnOverlay.java | 90 + .../menumodifier/MenuModifierConfig.java | 24 + .../MenuModifierInputListener.java | 39 + .../menumodifier/MenuModifierPlugin.java | 168 + .../musicmodifier/MidiFileAdjuster.java | 165 + .../musicmodifier/MusicCustomizerPlugin.java | 355 ++ .../musicmodifier/RealTimeMIDIPlayer.java | 229 ++ .../NextHitNotifierConfig.java | 11 + .../NextHitNotifierOverlay.java | 59 + .../NextHitNotifierPlugin.java | 116 + .../plugins/pkvision/PKVisionConfig.java | 55 + .../pkvision/PKVisionMinimapOverlay.java | 43 + .../plugins/pkvision/PKVisionOverlay.java | 55 + .../plugins/pkvision/PKVisionPlugin.java | 135 + .../plugins/pkvision/PKVisionService.java | 63 + .../plugins/pkvision/PKVisionTileOverlay.java | 44 + .../plankmakehelper/PlankMakeOverlay.java | 88 + .../plankmakehelper/PlankMakePlugin.java | 49 + .../prayagainstplayer/PlayerContainer.java | 56 + .../PrayAgainstPlayerConfig.java | 183 + .../PrayAgainstPlayerOverlay.java | 158 + .../PrayAgainstPlayerOverlayPrayerTab.java | 101 + .../PrayAgainstPlayerPlugin.java | 327 ++ .../plugins/prayagainstplayer/WeaponType.java | 125 + .../client/plugins/profiles/ProfilePanel.java | 123 + .../plugins/profiles/ProfilesConfig.java | 36 + .../plugins/profiles/ProfilesPanel.java | 248 ++ .../plugins/profiles/ProfilesPlugin.java | 128 + .../plugins/pyramidplunder/Obstacles.java | 40 + .../pyramidplunder/PyramidPlunderConfig.java | 66 + .../pyramidplunder/PyramidPlunderOverlay.java | 131 + .../pyramidplunder/PyramidPlunderPlugin.java | 271 ++ .../pyramidplunder/PyramidPlunderTimer.java | 39 + .../screenmarkers/ui/ScreenMarkerPanel.java | 2 +- .../ShayzienInfirmaryOverlay.java | 100 + .../ShayzienInfirmaryPlugin.java | 121 + .../shiftwalker/ShiftWalkerConfig.java | 66 + .../shiftwalker/ShiftWalkerGroups.java | 32 + .../shiftwalker/ShiftWalkerInputListener.java | 61 + .../shiftwalker/ShiftWalkerPlugin.java | 208 + .../plugins/slayermusiq/QuestGuideLinks.java | 207 + .../slayermusiq/SlayermusiqPlugin.java | 152 + .../spellbookfixer/SpellbookFixerConfig.java | 110 + .../spellbookfixer/SpellbookFixerPlugin.java | 170 + .../plugins/suppliestracker/ActionType.java | 36 + .../suppliestracker/BlowpipeDartType.java | 46 + .../plugins/suppliestracker/ItemType.java | 77 + .../plugins/suppliestracker/MenuAction.java | 60 + .../plugins/suppliestracker/SuppliesBox.java | 341 ++ .../SuppliesTrackerConfig.java | 43 + .../suppliestracker/SuppliesTrackerItem.java | 42 + .../suppliestracker/SuppliesTrackerPanel.java | 217 + .../SuppliesTrackerPlugin.java | 781 ++++ .../templetrek/TempleTrekBogOverlay.java | 68 + .../plugins/templetrek/TempleTrekConfig.java | 55 + .../plugins/templetrek/TempleTrekOverlay.java | 78 + .../plugins/templetrek/TempleTrekPlugin.java | 136 + .../tickcounter/TickCounterConfig.java | 71 + .../tickcounter/TickCounterOverlay.java | 69 + .../tickcounter/TickCounterPlugin.java | 193 + .../timetracking/OverviewItemPanel.java | 2 +- .../timetracking/TimeTrackingPlugin.java | 2 +- .../tobdamagecount/DamageCounterPlugin.java | 356 ++ .../client/plugins/vetion/VetionConfig.java | 44 + .../client/plugins/vetion/VetionOverlay.java | 93 + .../client/plugins/vetion/VetionPlugin.java | 95 + .../client/plugins/vorkath/TileHighlight.java | 8 + .../client/plugins/vorkath/VorkathConfig.java | 51 + .../vorkath/VorkathIndicatorOverlay.java | 53 + .../plugins/vorkath/VorkathOverlay.java | 84 + .../client/plugins/vorkath/VorkathPlugin.java | 157 + .../WildernessLocationsOverlay.java | 38 + .../WildernessLocationsPlugin.java | 127 + .../plugins/zoneIndicators/MapLocations.java | 3479 +++++++++++++++++ .../zoneIndicators/ZoneIndicatorsConfig.java | 111 + .../ZoneIndicatorsMinimapOverlay.java | 116 + .../zoneIndicators/ZoneIndicatorsOverlay.java | 113 + .../zoneIndicators/ZoneIndicatorsPlugin.java | 297 ++ .../zoneIndicators/ZoneVisibility.java | 43 + .../client/plugins/ztob/TheatreConfig.java | 138 + .../client/plugins/ztob/TheatreOverlay.java | 347 ++ .../client/plugins/ztob/TheatrePlugin.java | 766 ++++ .../client/plugins/zulrah/ZulrahConfig.java | 24 + .../client/plugins/zulrah/ZulrahOverlay.java | 72 + .../client/plugins/zulrah/ZulrahPlugin.java | 640 +++ .../plugins/zulrah/ZulrahTileOverlay.java | 120 + .../net/runelite/client/util/MiscUtils.java | 79 + .../client/util/WildernessLocation.java | 98 + .../client/plugins/profiles/delete_icon.png | Bin 0 -> 208 bytes .../client/plugins/profiles/profiles_icon.png | Bin 0 -> 77 bytes 154 files changed, 20987 insertions(+), 12 deletions(-) create mode 100644 runelite-api/src/main/java/net/runelite/api/events/CannonballFired.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/aoewarnings/AoeProjectile.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/aoewarnings/AoeProjectileInfo.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/aoewarnings/AoeWarningConfig.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/aoewarnings/AoeWarningOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/aoewarnings/AoeWarningPlugin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/aoewarnings/BombOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/aoewarnings/CrystalBomb.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/batools/BAToolsConfig.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/batools/BAToolsOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/batools/BAToolsPlugin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/batools/Calls.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/batools/CycleCounter.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/batools/Healer.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/batools/HealerCode.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/clanmanmode/ClanManModeConfig.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/clanmanmode/ClanManModeMinimapOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/clanmanmode/ClanManModeOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/clanmanmode/ClanManModePlugin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/clanmanmode/ClanManModeService.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/clanmanmode/ClanManModeTileOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/equipmentinspector/EquipmentInspectorConfig.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/equipmentinspector/EquipmentInspectorPanel.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/equipmentinspector/EquipmentInspectorPlugin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/equipmentinspector/ItemPanel.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/equipmentinspector/normal.png create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/fightcavejadhelper/FightCaveJadHelperOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/fightcavejadhelper/FightCaveJadHelperPlugin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/fightcavejadhelper/JadAttack.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/fightcavewavehelper/FightCaveWaveHelperConfig.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/fightcavewavehelper/FightCaveWaveHelperPlugin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/fightcavewavehelper/WaveDisplayMode.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/fightcavewavehelper/WaveMonster.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/fightcavewavehelper/WaveOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/freezetimers/Barrage.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/freezetimers/FreezeTimersConfig.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/freezetimers/FreezeTimersOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/freezetimers/FreezeTimersPlugin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/freezetimers/FreezeTimersService.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/freezetimers/FreezeTimersTileOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/freezetimers/PlayerSpellEffect.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/freezetimers/Spell.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/grotesqueguardians/GrotesqueGuardiansOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/grotesqueguardians/GrotesqueGuardiansPlugin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/hideprayers/HidePrayersConfig.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/hideprayers/HidePrayersPlugin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/hideprayers/PrayerTabState.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/hydra/HydraConfig.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/hydra/HydraIndicatorOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/hydra/HydraOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/hydra/HydraPlugin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/hydra/HydraPrayOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/kittennotifier/KittenNotifierConfig.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/kittennotifier/KittenNotifierPlugin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/lizardmenshaman/LizardmenShamanConfig.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/lizardmenshaman/LizardmenShamanPlugin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/lizardmenshaman/LizardmenShamanSpawn.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/lizardmenshaman/ShamanSpawnOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/menumodifier/MenuModifierConfig.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/menumodifier/MenuModifierInputListener.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/menumodifier/MenuModifierPlugin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/musicmodifier/MidiFileAdjuster.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/musicmodifier/MusicCustomizerPlugin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/musicmodifier/RealTimeMIDIPlayer.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/nexthitnotifier/NextHitNotifierConfig.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/nexthitnotifier/NextHitNotifierOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/nexthitnotifier/NextHitNotifierPlugin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/pkvision/PKVisionConfig.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/pkvision/PKVisionMinimapOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/pkvision/PKVisionOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/pkvision/PKVisionPlugin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/pkvision/PKVisionService.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/pkvision/PKVisionTileOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/plankmakehelper/PlankMakeOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/plankmakehelper/PlankMakePlugin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/prayagainstplayer/PlayerContainer.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/prayagainstplayer/PrayAgainstPlayerConfig.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/prayagainstplayer/PrayAgainstPlayerOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/prayagainstplayer/PrayAgainstPlayerOverlayPrayerTab.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/prayagainstplayer/PrayAgainstPlayerPlugin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/prayagainstplayer/WeaponType.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/profiles/ProfilePanel.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/profiles/ProfilesConfig.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/profiles/ProfilesPanel.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/profiles/ProfilesPlugin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/pyramidplunder/Obstacles.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/pyramidplunder/PyramidPlunderConfig.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/pyramidplunder/PyramidPlunderOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/pyramidplunder/PyramidPlunderPlugin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/pyramidplunder/PyramidPlunderTimer.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/shayzieninfirmary/ShayzienInfirmaryOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/shayzieninfirmary/ShayzienInfirmaryPlugin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/shiftwalker/ShiftWalkerConfig.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/shiftwalker/ShiftWalkerGroups.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/shiftwalker/ShiftWalkerInputListener.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/shiftwalker/ShiftWalkerPlugin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/slayermusiq/QuestGuideLinks.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/slayermusiq/SlayermusiqPlugin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/spellbookfixer/SpellbookFixerConfig.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/spellbookfixer/SpellbookFixerPlugin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/ActionType.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/BlowpipeDartType.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/ItemType.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/MenuAction.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/SuppliesBox.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/SuppliesTrackerConfig.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/SuppliesTrackerItem.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/SuppliesTrackerPanel.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/SuppliesTrackerPlugin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/templetrek/TempleTrekBogOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/templetrek/TempleTrekConfig.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/templetrek/TempleTrekOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/templetrek/TempleTrekPlugin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/tickcounter/TickCounterConfig.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/tickcounter/TickCounterOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/tickcounter/TickCounterPlugin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/tobdamagecount/DamageCounterPlugin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/vetion/VetionConfig.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/vetion/VetionOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/vetion/VetionPlugin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/vorkath/TileHighlight.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/vorkath/VorkathConfig.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/vorkath/VorkathIndicatorOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/vorkath/VorkathOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/vorkath/VorkathPlugin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/wildernesslocations/WildernessLocationsOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/wildernesslocations/WildernessLocationsPlugin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/zoneIndicators/MapLocations.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/zoneIndicators/ZoneIndicatorsConfig.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/zoneIndicators/ZoneIndicatorsMinimapOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/zoneIndicators/ZoneIndicatorsOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/zoneIndicators/ZoneIndicatorsPlugin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/zoneIndicators/ZoneVisibility.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/ztob/TheatreConfig.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/ztob/TheatreOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/ztob/TheatrePlugin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/zulrah/ZulrahConfig.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/zulrah/ZulrahOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/zulrah/ZulrahPlugin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/zulrah/ZulrahTileOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/util/MiscUtils.java create mode 100644 runelite-client/src/main/java/net/runelite/client/util/WildernessLocation.java create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/profiles/delete_icon.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/profiles/profiles_icon.png 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 0000000000000000000000000000000000000000..613f95e46d5235a49ec0843faaa35384e9b0896b GIT binary patch literal 624 zcmV-$0+0QPP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0tHD#K~z{r?Uuhw z!$1_rUmcyLn~P8fmrQ~BGyq5#M;t!sDe|AI9PCT z5L;*0n(yTB<#Ji>Y`9Ti3&7jKIc7LRzA=EB!@v>HuhtrJNg@rXeB>M9M-je0Kgy>A zylh?sDMIn)l}ej(`+H8v(E%7b5cc-@C3nN&w2Fwp-R+IopcXQj3?*h0Vy~0lOgpUz zIPDlH0yrEs;AVPiQm$;Ucia462~d@01* literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..18b67f23f3454d38f3758f3f42009f5a1fe03214 GIT binary patch literal 208 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+0wn(&ce?|mI14-?iy0WWg+Z8+Vb&Z8pkR}y zi(`n!#I+Y4xegf!uwHm1$rYQjwENzFCcRY^?Kw^_ZCjo@DfSu)EZIE!{CxZW=Xnk{ zrk0t8y?F6#(rwm@ApvTF-fT_0nwNcq%o6RzRhF1Xlr8;}zU|uD*fra3CC*fD`Z4$G zm*&4YE8bjNd$v^en6=-9QlIbhwx5yfWuHIW^8Xs~u!i*|FD#yH0lJ34)78&qol`;+ E08pz??f?J) literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..a733eaf4a19343c3416d4b12cea60d735d9f7170 GIT binary patch literal 77 zcmZ?wbhEHb6k!l#n8*ME|NsBLFxLl2D*j|)WME)s&;f~p