Addeds arrow api, quest helper plugin, changed raids etc (#81)

* add a skull timer

* fix the feature and check edge cases

* Add config option and a check for deadman worlds

* add support for NPE

* Add reskulling on players and the BH shop skull option

* Add skull notifier plugin

* move to idle notification plugin

* remove old plugin

* fix plugin and change default config option to false

* fix to try and fix travis

* should fix travis

* indentation fix and adds a method for the logic

* fix config

* fix config #2

* Stop a NPE from happening on log in

* actually fix NPE

* fix notifications on first tick

* Remove raids timer infobox and add tooltip to points widget

* Add get widget overlay

* client: add custom arrow API

Currently supports:
Minimap
Arbitrary World Point
NPCs (by ID, and multiple per arrow)
Objects (by ID, and multiple per arrow)

TODO: Add world map point support

* Add quest helper

* Add Npc Talk Step to quest helper

* Add Cooks Assistant quest helper

* Add Imp Catcher to quest helper

* Add Dig Step to quest helper

* Add X Marks The Spot to quest helper

* Adds back skull timer
This commit is contained in:
James
2019-04-23 17:36:42 -07:00
committed by Kyleeld
parent 90e5025acd
commit f8363b9c23
33 changed files with 2705 additions and 370 deletions

View File

@@ -25,169 +25,168 @@
package net.runelite.api; package net.runelite.api;
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public enum Quest public enum Quest
{ {
//Free Quests //Free Quests
BLACK_KNIGHTS_FORTRESS(299, "Black Knights' Fortress"), BLACK_KNIGHTS_FORTRESS(299, "Black Knights' Fortress", VarPlayer.QUEST_BLACK_KNIGHTS_FORTRESS),
COOKS_ASSISTANT(300, "Cook's Assistant"), COOKS_ASSISTANT(300, "Cook's Assistant", VarPlayer.QUEST_COOKS_ASSISTANT),
THE_CORSAIR_CURSE(301, "The Corsair Curse"), THE_CORSAIR_CURSE(301, "The Corsair Curse", Varbits.QUEST_THE_CORSAIR_CURSE),
DEMON_SLAYER(302, "Demon Slayer"), DEMON_SLAYER(302, "Demon Slayer", Varbits.QUEST_DEMON_SLAYER),
DORICS_QUEST(303, "Doric's Quest"), DORICS_QUEST(303, "Doric's Quest", VarPlayer.QUEST_DORICS_QUEST),
DRAGON_SLAYER(304, "Dragon Slayer"), DRAGON_SLAYER(304, "Dragon Slayer", VarPlayer.QUEST_DRAGON_SLAYER),
ERNEST_THE_CHICKEN(305, "Ernest the Chicken"), ERNEST_THE_CHICKEN(305, "Ernest the Chicken", VarPlayer.QUEST_ERNEST_THE_CHICKEN),
GOBLIN_DIPLOMACY(306, "Goblin Diplomacy"), GOBLIN_DIPLOMACY(306, "Goblin Diplomacy", Varbits.QUEST_GOBLIN_DIPLOMACY),
IMP_CATCHER(307, "Imp Catcher"), IMP_CATCHER(307, "Imp Catcher", VarPlayer.QUEST_IMP_CATCHER),
THE_KNIGHTS_SWORD(308, "The Knight's Sword"), THE_KNIGHTS_SWORD(308, "The Knight's Sword", VarPlayer.QUEST_THE_KNIGHTS_SWORD),
MISTHALIN_MYSTERY(309, "Misthalin Mystery"), MISTHALIN_MYSTERY(309, "Misthalin Mystery", Varbits.QUEST_MISTHALIN_MYSTERY),
PIRATES_TREASURE(310, "Pirate's Treasure"), PIRATES_TREASURE(310, "Pirate's Treasure", VarPlayer.QUEST_PIRATES_TREASURE),
PRINCE_ALI_RESCUE(311, "Prince Ali Rescue"), PRINCE_ALI_RESCUE(311, "Prince Ali Rescue", VarPlayer.QUEST_PRINCE_ALI_RESCUE),
THE_RESTLESS_GHOST(312, "The Restless Ghost"), THE_RESTLESS_GHOST(312, "The Restless Ghost", VarPlayer.QUEST_THE_RESTLESS_GHOST),
ROMEO__JULIET(313, "Romeo & Juliet"), ROMEO__JULIET(313, "Romeo & Juliet", VarPlayer.QUEST_ROMEO_AND_JULIET),
RUNE_MYSTERIES(314, "Rune Mysteries"), RUNE_MYSTERIES(314, "Rune Mysteries", VarPlayer.QUEST_RUNE_MYSTERIES),
SHEEP_SHEARER(315, "Sheep Shearer"), SHEEP_SHEARER(315, "Sheep Shearer", VarPlayer.QUEST_SHEEP_SHEARER),
SHIELD_OF_ARRAV(316, "Shield of Arrav"), SHIELD_OF_ARRAV(316, "Shield of Arrav", VarPlayer.QUEST_SHIELD_OF_ARRAV),
VAMPIRE_SLAYER(317, "Vampire Slayer"), VAMPIRE_SLAYER(317, "Vampire Slayer", VarPlayer.QUEST_VAMPIRE_SLAYER),
WITCHS_POTION(318, "Witch's Potion"), WITCHS_POTION(318, "Witch's Potion", VarPlayer.QUEST_WITCHS_POTION),
X_MARKS_THE_SPOT(550, "X Marks the Spot", Varbits.QUEST_X_MARKS_THE_SPOT),
//Members' Quests //Members' Quests
ANIMAL_MAGNETISM(331, "Animal Magnetism"), ANIMAL_MAGNETISM(331, "Animal Magnetism", Varbits.QUEST_ANIMAL_MAGNETISM),
ANOTHER_SLICE_OF_HAM(332, "Another Slice of H.A.M."), ANOTHER_SLICE_OF_HAM(332, "Another Slice of H.A.M.", Varbits.QUEST_ANOTHER_SLICE_OF_HAM),
BETWEEN_A_ROCK(333, "Between a Rock..."), BETWEEN_A_ROCK(333, "Between a Rock...", Varbits.QUEST_BETWEEN_A_ROCK),
BIG_CHOMPY_BIRD_HUNTING(334, "Big Chompy Bird Hunting"), BIG_CHOMPY_BIRD_HUNTING(334, "Big Chompy Bird Hunting", VarPlayer.QUEST_BIG_CHOMPY_BIRD_HUNTING),
BIOHAZARD(335, "Biohazard"), BIOHAZARD(335, "Biohazard", VarPlayer.QUEST_BIOHAZARD),
CABIN_FEVER(336, "Cabin Fever"), CABIN_FEVER(336, "Cabin Fever", VarPlayer.QUEST_CABIN_FEVER),
CLOCK_TOWER(337, "Clock Tower"), CLOCK_TOWER(337, "Clock Tower", VarPlayer.QUEST_CLOCK_TOWER),
COLD_WAR(338, "Cold War"), COLD_WAR(338, "Cold War", Varbits.QUEST_COLD_WAR),
CONTACT(339, "Contact!"), CONTACT(339, "Contact!", Varbits.QUEST_CONTACT),
CREATURE_OF_FENKENSTRAIN(340, "Creature of Fenkenstrain"), CREATURE_OF_FENKENSTRAIN(340, "Creature of Fenkenstrain", VarPlayer.QUEST_CREATURE_OF_FENKENSTRAIN),
DARKNESS_OF_HALLOWVALE(341, "Darkness of Hallowvale"), DARKNESS_OF_HALLOWVALE(341, "Darkness of Hallowvale", Varbits.QUEST_DARKNESS_OF_HALLOWVALE),
DEATH_PLATEAU(342, "Death Plateau"), DEATH_PLATEAU(342, "Death Plateau", VarPlayer.QUEST_DEATH_PLATEAU),
DEATH_TO_THE_DORGESHUUN(343, "Death to the Dorgeshuun"), DEATH_TO_THE_DORGESHUUN(343, "Death to the Dorgeshuun", Varbits.QUEST_DEATH_TO_THE_DORGESHUUN),
THE_DEPTHS_OF_DESPAIR(344, "The Depths of Despair"), THE_DEPTHS_OF_DESPAIR(344, "The Depths of Despair", Varbits.QUEST_THE_DEPTHS_OF_DESPAIR),
DESERT_TREASURE(345, "Desert Treasure"), DESERT_TREASURE(345, "Desert Treasure", Varbits.QUEST_DESERT_TREASURE),
DEVIOUS_MINDS(346, "Devious Minds"), DEVIOUS_MINDS(346, "Devious Minds", Varbits.QUEST_DEVIOUS_MINDS),
THE_DIG_SITE(347, "The Dig Site"), THE_DIG_SITE(347, "The Dig Site", VarPlayer.QUEST_THE_DIG_SITE),
DRAGON_SLAYER_II(348, "Dragon Slayer II"), DRAGON_SLAYER_II(348, "Dragon Slayer II", Varbits.QUEST_DRAGON_SLAYER_II),
DREAM_MENTOR(349, "Dream Mentor"), DREAM_MENTOR(349, "Dream Mentor", Varbits.QUEST_DREAM_MENTOR),
DRUIDIC_RITUAL(350, "Druidic Ritual"), DRUIDIC_RITUAL(350, "Druidic Ritual", VarPlayer.QUEST_DRUIDIC_RITUAL),
DWARF_CANNON(351, "Dwarf Cannon"), DWARF_CANNON(351, "Dwarf Cannon", Varbits.QUEST_THE_GIANT_DWARF),
EADGARS_RUSE(352, "Eadgar's Ruse"), EADGARS_RUSE(352, "Eadgar's Ruse", VarPlayer.QUEST_EADGARS_RUSE),
EAGLES_PEAK(353, "Eagles' Peak"), EAGLES_PEAK(353, "Eagles' Peak", Varbits.QUEST_EAGLES_PEAK),
ELEMENTAL_WORKSHOP_I(354, "Elemental Workshop I"), ELEMENTAL_WORKSHOP_I(354, "Elemental Workshop I", VarPlayer.QUEST_ELEMENTAL_WORKSHOP_I),
ELEMENTAL_WORKSHOP_II(355, "Elemental Workshop II"), ELEMENTAL_WORKSHOP_II(355, "Elemental Workshop II", Varbits.QUEST_ELEMENTAL_WORKSHOP_II),
ENAKHRAS_LAMENT(356, "Enakhra's Lament"), ENAKHRAS_LAMENT(356, "Enakhra's Lament", Varbits.QUEST_ENAKHRAS_LAMENT),
ENLIGHTENED_JOURNEY(357, "Enlightened Journey"), ENLIGHTENED_JOURNEY(357, "Enlightened Journey", Varbits.QUEST_ENLIGHTENED_JOURNEY),
THE_EYES_OF_GLOUPHRIE(358, "The Eyes of Glouphrie"), THE_EYES_OF_GLOUPHRIE(358, "The Eyes of Glouphrie", Varbits.QUEST_THE_EYES_OF_GLOUPHRIE),
FAIRYTALE_I__GROWING_PAINS(359, "Fairytale I - Growing Pains"), FAIRYTALE_I__GROWING_PAINS(359, "Fairytale I - Growing Pains", Varbits.QUEST_FAIRYTALE_I_GROWING_PAINS),
FAIRYTALE_II__CURE_A_QUEEN(360, "Fairytale II - Cure a Queen"), FAIRYTALE_II__CURE_A_QUEEN(360, "Fairytale II - Cure a Queen", Varbits.QUEST_FAIRYTALE_II_CURE_A_QUEEN),
FAMILY_CREST(361, "Family Crest"), FAMILY_CREST(361, "Family Crest", VarPlayer.QUEST_FAMILY_CREST),
THE_FEUD(362, "The Feud"), THE_FEUD(362, "The Feud", Varbits.QUEST_THE_FEUD),
FIGHT_ARENA(363, "Fight Arena"), FIGHT_ARENA(363, "Fight Arena", VarPlayer.QUEST_FIGHT_ARENA),
FISHING_CONTEST(364, "Fishing Contest"), FISHING_CONTEST(364, "Fishing Contest", VarPlayer.QUEST_FISHING_CONTEST),
FORGETTABLE_TALE(365, "Forgettable Tale..."), FORGETTABLE_TALE(365, "Forgettable Tale...", Varbits.QUEST_FORGETTABLE_TALE),
BONE_VOYAGE(366, "Bone Voyage"), BONE_VOYAGE(366, "Bone Voyage", Varbits.QUEST_BONE_VOYAGE),
THE_FREMENNIK_ISLES(367, "The Fremennik Isles"), THE_FREMENNIK_ISLES(367, "The Fremennik Isles", Varbits.QUEST_THE_FREMENNIK_ISLES),
THE_FREMENNIK_TRIALS(368, "The Fremennik Trials"), THE_FREMENNIK_TRIALS(368, "The Fremennik Trials", VarPlayer.QUEST_THE_FREMENNIK_TRIALS),
GARDEN_OF_TRANQUILLITY(369, "Garden of Tranquillity"), GARDEN_OF_TRANQUILLITY(369, "Garden of Tranquillity", Varbits.QUEST_GARDEN_OF_TRANQUILLITY),
GERTRUDES_CAT(370, "Gertrude's Cat"), GERTRUDES_CAT(370, "Gertrude's Cat", VarPlayer.QUEST_GERTRUDES_CAT),
GHOSTS_AHOY(371, "Ghosts Ahoy"), GHOSTS_AHOY(371, "Ghosts Ahoy", Varbits.QUEST_GHOSTS_AHOY),
THE_GIANT_DWARF(372, "The Giant Dwarf"), THE_GIANT_DWARF(372, "The Giant Dwarf", Varbits.QUEST_THE_GIANT_DWARF),
THE_GOLEM(373, "The Golem"), THE_GOLEM(373, "The Golem", Varbits.QUEST_THE_GOLEM),
THE_GRAND_TREE(374, "The Grand Tree"), THE_GRAND_TREE(374, "The Grand Tree", VarPlayer.QUEST_THE_GRAND_TREE),
THE_GREAT_BRAIN_ROBBERY(375, "The Great Brain Robbery"), THE_GREAT_BRAIN_ROBBERY(375, "The Great Brain Robbery", VarPlayer.QUEST_THE_GREAT_BRAIN_ROBBERY),
GRIM_TALES(376, "Grim Tales"), GRIM_TALES(376, "Grim Tales", Varbits.QUEST_GRIM_TALES),
THE_HAND_IN_THE_SAND(377, "The Hand in the Sand"), THE_HAND_IN_THE_SAND(377, "The Hand in the Sand", Varbits.QUEST_THE_HAND_IN_THE_SAND),
HAUNTED_MINE(378, "Haunted Mine"), HAUNTED_MINE(378, "Haunted Mine", VarPlayer.QUEST_HAUNTED_MINE),
HAZEEL_CULT(379, "Hazeel Cult"), HAZEEL_CULT(379, "Hazeel Cult", VarPlayer.QUEST_HAZEEL_CULT),
HEROES_QUEST(380, "Heroes' Quest"), HEROES_QUEST(380, "Heroes' Quest", VarPlayer.QUEST_HEROES_QUEST),
HOLY_GRAIL(381, "Holy Grail"), HOLY_GRAIL(381, "Holy Grail", VarPlayer.QUEST_HOLY_GRAIL),
HORROR_FROM_THE_DEEP(382, "Horror from the Deep"), HORROR_FROM_THE_DEEP(382, "Horror from the Deep", Varbits.QUEST_HORROR_FROM_THE_DEEP),
ICTHLARINS_LITTLE_HELPER(383, "Icthlarin's Little Helper"), ICTHLARINS_LITTLE_HELPER(383, "Icthlarin's Little Helper", Varbits.QUEST_ICTHLARINS_LITTLE_HELPER),
IN_AID_OF_THE_MYREQUE(384, "In Aid of the Myreque"), IN_AID_OF_THE_MYREQUE(384, "In Aid of the Myreque", Varbits.QUEST_IN_AID_OF_THE_MYREQUE),
IN_SEARCH_OF_THE_MYREQUE(385, "In Search of the Myreque"), IN_SEARCH_OF_THE_MYREQUE(385, "In Search of the Myreque", VarPlayer.QUEST_IN_SEARCH_OF_THE_MYREQUE),
JUNGLE_POTION(386, "Jungle Potion"), JUNGLE_POTION(386, "Jungle Potion", VarPlayer.QUEST_JUNGLE_POTION),
KINGS_RANSOM(387, "King's Ransom"), KINGS_RANSOM(387, "King's Ransom", Varbits.QUEST_KINGS_RANSOM),
LEGENDS_QUEST(388, "Legends' Quest"), LEGENDS_QUEST(388, "Legends' Quest", VarPlayer.QUEST_LEGENDS_QUEST),
LOST_CITY(389, "Lost City"), LOST_CITY(389, "Lost City", VarPlayer.QUEST_LOST_CITY),
THE_LOST_TRIBE(390, "The Lost Tribe"), THE_LOST_TRIBE(390, "The Lost Tribe", Varbits.QUEST_THE_LOST_TRIBE),
LUNAR_DIPLOMACY(391, "Lunar Diplomacy"), LUNAR_DIPLOMACY(391, "Lunar Diplomacy", Varbits.QUEST_LUNAR_DIPLOMACY),
MAKING_FRIENDS_WITH_MY_ARM(392, "Making Friends with My Arm"), MAKING_FRIENDS_WITH_MY_ARM(392, "Making Friends with My Arm", Varbits.QUEST_MAKING_FRIENDS_WITH_MY_ARM),
MAKING_HISTORY(393, "Making History"), MAKING_HISTORY(393, "Making History", Varbits.QUEST_MAKING_HISTORY),
MERLINS_CRYSTAL(394, "Merlin's Crystal"), MERLINS_CRYSTAL(394, "Merlin's Crystal", VarPlayer.QUEST_MERLINS_CRYSTAL),
MONKEY_MADNESS_I(395, "Monkey Madness I"), MONKEY_MADNESS_I(395, "Monkey Madness I", VarPlayer.QUEST_MONKEY_MADNESS_I),
MONKEY_MADNESS_II(396, "Monkey Madness II"), MONKEY_MADNESS_II(396, "Monkey Madness II", Varbits.QUEST_MONKEY_MADNESS_II),
MONKS_FRIEND(397, "Monk's Friend"), MONKS_FRIEND(397, "Monk's Friend", VarPlayer.QUEST_MONKS_FRIEND),
MOUNTAIN_DAUGHTER(398, "Mountain Daughter"), MOUNTAIN_DAUGHTER(398, "Mountain Daughter", Varbits.QUEST_MOUNTAIN_DAUGHTER),
MOURNINGS_ENDS_PART_I(399, "Mourning's Ends Part I"), MOURNINGS_ENDS_PART_I(399, "Mourning's Ends Part I", VarPlayer.QUEST_MOURNINGS_ENDS_PART_I),
MOURNINGS_ENDS_PART_II(400, "Mourning's Ends Part II"), MOURNINGS_ENDS_PART_II(400, "Mourning's Ends Part II", Varbits.QUEST_MOURNINGS_ENDS_PART_II),
MURDER_MYSTERY(401, "Murder Mystery"), MURDER_MYSTERY(401, "Murder Mystery", VarPlayer.QUEST_MURDER_MYSTERY),
MY_ARMS_BIG_ADVENTURE(402, "My Arm's Big Adventure"), MY_ARMS_BIG_ADVENTURE(402, "My Arm's Big Adventure", Varbits.QUEST_MY_ARMS_BIG_ADVENTURE),
NATURE_SPIRIT(403, "Nature Spirit"), NATURE_SPIRIT(403, "Nature Spirit", VarPlayer.QUEST_NATURE_SPIRIT),
OBSERVATORY_QUEST(404, "Observatory Quest"), OBSERVATORY_QUEST(404, "Observatory Quest", VarPlayer.QUEST_OBSERVATORY_QUEST),
OLAFS_QUEST(405, "Olaf's Quest"), OLAFS_QUEST(405, "Olaf's Quest", Varbits.QUEST_OLAFS_QUEST),
ONE_SMALL_FAVOUR(406, "One Small Favour"), ONE_SMALL_FAVOUR(406, "One Small Favour", VarPlayer.QUEST_ONE_SMALL_FAVOUR),
PLAGUE_CITY(407, "Plague City"), PLAGUE_CITY(407, "Plague City", VarPlayer.QUEST_PLAGUE_CITY),
PRIEST_IN_PERIL(408, "Priest in Peril"), PRIEST_IN_PERIL(408, "Priest in Peril", VarPlayer.QUEST_PRIEST_IN_PERIL),
THE_QUEEN_OF_THIEVES(409, "The Queen of Thieves"), THE_QUEEN_OF_THIEVES(409, "The Queen of Thieves", Varbits.QUEST_THE_QUEEN_OF_THIEVES),
RAG_AND_BONE_MAN(410, "Rag and Bone Man"), RAG_AND_BONE_MAN(410, "Rag and Bone Man", VarPlayer.QUEST_RAG_AND_BONE_MAN),
RAG_AND_BONE_MAN_II(411, "Rag and Bone Man II"), RAG_AND_BONE_MAN_II(411, "Rag and Bone Man II", VarPlayer.QUEST_RAG_AND_BONE_MAN_II),
RATCATCHERS(412, "Ratcatchers"), RATCATCHERS(412, "Ratcatchers", Varbits.QUEST_RATCATCHERS),
RECIPE_FOR_DISASTER(413, "Recipe for Disaster"), RECIPE_FOR_DISASTER(413, "Recipe for Disaster", Varbits.QUEST_RECIPE_FOR_DISASTER),
RECRUITMENT_DRIVE(414, "Recruitment Drive"), RECRUITMENT_DRIVE(414, "Recruitment Drive", Varbits.QUEST_RECRUITMENT_DRIVE),
REGICIDE(415, "Regicide"), REGICIDE(415, "Regicide", VarPlayer.QUEST_REGICIDE),
ROVING_ELVES(416, "Roving Elves"), ROVING_ELVES(416, "Roving Elves", VarPlayer.QUEST_ROVING_ELVES),
ROYAL_TROUBLE(417, "Royal Trouble"), ROYAL_TROUBLE(417, "Royal Trouble", Varbits.QUEST_ROYAL_TROUBLE),
RUM_DEAL(418, "Rum Deal"), RUM_DEAL(418, "Rum Deal", VarPlayer.QUEST_RUM_DEAL),
SCORPION_CATCHER(419, "Scorpion Catcher"), SCORPION_CATCHER(419, "Scorpion Catcher", VarPlayer.QUEST_SCORPION_CATCHER),
SEA_SLUG(420, "Sea Slug"), SEA_SLUG(420, "Sea Slug", VarPlayer.QUEST_SEA_SLUG),
SHADES_OF_MORTTON(421, "Shades of Mort'ton"), SHADES_OF_MORTTON(421, "Shades of Mort'ton", VarPlayer.QUEST_SHADES_OF_MORTTON),
SHADOW_OF_THE_STORM(422, "Shadow of the Storm"), SHADOW_OF_THE_STORM(422, "Shadow of the Storm", Varbits.QUEST_SHADOW_OF_THE_STORM),
SHEEP_HERDER(423, "Sheep Herder"), SHEEP_HERDER(423, "Sheep Herder", VarPlayer.QUEST_SHEEP_HERDER),
SHILO_VILLAGE(424, "Shilo Village"), SHILO_VILLAGE(424, "Shilo Village", VarPlayer.QUEST_SHILO_VILLAGE),
THE_SLUG_MENACE(425, "The Slug Menace"), THE_SLUG_MENACE(425, "The Slug Menace", Varbits.QUEST_THE_SLUG_MENACE),
A_SOULS_BANE(426, "A Soul's Bane"), A_SOULS_BANE(426, "A Soul's Bane", Varbits.QUEST_A_SOULS_BANE),
SPIRITS_OF_THE_ELID(427, "Spirits of the Elid"), SPIRITS_OF_THE_ELID(427, "Spirits of the Elid", Varbits.QUEST_SPIRITS_OF_THE_ELID),
SWAN_SONG(428, "Swan Song"), SWAN_SONG(428, "Swan Song", Varbits.QUEST_SWAN_SONG),
TAI_BWO_WANNAI_TRIO(429, "Tai Bwo Wannai Trio"), TAI_BWO_WANNAI_TRIO(429, "Tai Bwo Wannai Trio", VarPlayer.QUEST_TAI_BWO_WANNAI_TRIO),
A_TAIL_OF_TWO_CATS(430, "A Tail of Two Cats"), A_TAIL_OF_TWO_CATS(430, "A Tail of Two Cats", Varbits.QUEST_A_TAIL_OF_TWO_CATS),
TALE_OF_THE_RIGHTEOUS(431, "Tale of the Righteous"), TALE_OF_THE_RIGHTEOUS(431, "Tale of the Righteous", Varbits.QUEST_TALE_OF_THE_RIGHTEOUS),
A_TASTE_OF_HOPE(432, "A Taste of Hope"), A_TASTE_OF_HOPE(432, "A Taste of Hope", Varbits.QUEST_A_TASTE_OF_HOPE),
TEARS_OF_GUTHIX(433, "Tears of Guthix"), TEARS_OF_GUTHIX(433, "Tears of Guthix", Varbits.QUEST_TEARS_OF_GUTHIX),
TEMPLE_OF_IKOV(434, "Temple of Ikov"), TEMPLE_OF_IKOV(434, "Temple of Ikov", VarPlayer.QUEST_TEMPLE_OF_IKOV),
THRONE_OF_MISCELLANIA(435, "Throne of Miscellania"), THRONE_OF_MISCELLANIA(435, "Throne of Miscellania", VarPlayer.QUEST_THRONE_OF_MISCELLANIA),
THE_TOURIST_TRAP(436, "The Tourist Trap"), THE_TOURIST_TRAP(436, "The Tourist Trap", VarPlayer.QUEST_THE_TOURIST_TRAP),
TOWER_OF_LIFE(437, "Tower of Life"), TOWER_OF_LIFE(437, "Tower of Life", Varbits.QUEST_TOWER_OF_LIFE),
TREE_GNOME_VILLAGE(438, "Tree Gnome Village"), TREE_GNOME_VILLAGE(438, "Tree Gnome Village", VarPlayer.QUEST_TREE_GNOME_VILLAGE),
TRIBAL_TOTEM(439, "Tribal Totem"), TRIBAL_TOTEM(439, "Tribal Totem", VarPlayer.QUEST_TRIBAL_TOTEM),
TROLL_ROMANCE(440, "Troll Romance"), TROLL_ROMANCE(440, "Troll Romance", VarPlayer.QUEST_TROLL_ROMANCE),
TROLL_STRONGHOLD(441, "Troll Stronghold"), TROLL_STRONGHOLD(441, "Troll Stronghold", VarPlayer.QUEST_TROLL_STRONGHOLD),
UNDERGROUND_PASS(442, "Underground Pass"), UNDERGROUND_PASS(442, "Underground Pass", VarPlayer.QUEST_UNDERGROUND_PASS),
CLIENT_OF_KOUREND(443, "Client of Kourend"), CLIENT_OF_KOUREND(443, "Client of Kourend", Varbits.QUEST_CLIENT_OF_KOUREND),
WANTED(444, "Wanted!"), WANTED(444, "Wanted!", Varbits.QUEST_WANTED),
WATCHTOWER(445, "Watchtower"), WATCHTOWER(445, "Watchtower", VarPlayer.QUEST_WATCHTOWER),
WATERFALL_QUEST(446, "Waterfall Quest"), WATERFALL_QUEST(446, "Waterfall Quest", VarPlayer.QUEST_WATERFALL_QUEST),
WHAT_LIES_BELOW(447, "What Lies Below"), WHAT_LIES_BELOW(447, "What Lies Below", Varbits.QUEST_WHAT_LIES_BELOW),
WITCHS_HOUSE(448, "Witch's House"), WITCHS_HOUSE(448, "Witch's House", VarPlayer.QUEST_WITCHS_HOUSE),
ZOGRE_FLESH_EATERS(449, "Zogre Flesh Eaters"), ZOGRE_FLESH_EATERS(449, "Zogre Flesh Eaters", Varbits.QUEST_ZOGRE_FLESH_EATERS),
THE_ASCENT_OF_ARCEUUS(542, "The Ascent of Arceuus"), THE_ASCENT_OF_ARCEUUS(542, "The Ascent of Arceuus", Varbits.QUEST_THE_ASCENT_OF_ARCEUUS),
THE_FORSAKEN_TOWER(543, "The Forsaken Tower"), THE_FORSAKEN_TOWER(543, "The Forsaken Tower", Varbits.QUEST_THE_FORSAKEN_TOWER),
//Miniquests //Miniquests
ENTER_THE_ABYSS(319, "Enter the Abyss"), ENTER_THE_ABYSS(319, "Enter the Abyss", VarPlayer.QUEST_ENTER_THE_ABYSS),
ARCHITECTURAL_ALLIANCE(320, "Architectural Alliance"), ARCHITECTURAL_ALLIANCE(320, "Architectural Alliance", Varbits.QUEST_ARCHITECTURAL_ALLIANCE),
BEAR_YOUR_SOUL(321, "Bear your Soul"), BEAR_YOUR_SOUL(321, "Bear your Soul", Varbits.QUEST_BEAR_YOUR_SOUL),
ALFRED_GRIMHANDS_BARCRAWL(322, "Alfred Grimhand's Barcrawl"), ALFRED_GRIMHANDS_BARCRAWL(322, "Alfred Grimhand's Barcrawl", VarPlayer.QUEST_ALFRED_GRIMHANDS_BARCRAWL),
CURSE_OF_THE_EMPTY_LORD(323, "Curse of the Empty Lord"), CURSE_OF_THE_EMPTY_LORD(323, "Curse of the Empty Lord", Varbits.QUEST_CURSE_OF_THE_EMPTY_LORD),
ENCHANTED_KEY(324, "Enchanted Key"), ENCHANTED_KEY(324, "Enchanted Key", Varbits.QUEST_ENCHANTED_KEY),
THE_GENERALS_SHADOW(325, "The General's Shadow"), THE_GENERALS_SHADOW(325, "The General's Shadow", Varbits.QUEST_THE_GENERALS_SHADOW),
SKIPPY_AND_THE_MOGRES(326, "Skippy and the Mogres"), SKIPPY_AND_THE_MOGRES(326, "Skippy and the Mogres", Varbits.QUEST_SKIPPY_AND_THE_MOGRES),
THE_MAGE_ARENA(327, "The Mage Arena"), THE_MAGE_ARENA(327, "The Mage Arena", VarPlayer.QUEST_THE_MAGE_ARENA),
LAIR_OF_TARN_RAZORLOR(328, "Lair of Tarn Razorlor"), LAIR_OF_TARN_RAZORLOR(328, "Lair of Tarn Razorlor", Varbits.QUEST_LAIR_OF_TARN_RAZORLOR),
FAMILY_PEST(329, "Family Pest"), FAMILY_PEST(329, "Family Pest", Varbits.QUEST_FAMILY_PEST),
THE_MAGE_ARENA_II(330, "The Mage Arena II"); THE_MAGE_ARENA_II(330, "The Mage Arena II", Varbits.QUEST_THE_MAGE_ARENA_II);
@Getter @Getter
private final int id; private final int id;
@@ -195,6 +194,26 @@ public enum Quest
@Getter @Getter
private final String name; private final String name;
private final Varbits varbit;
private final VarPlayer varPlayer;
Quest(int id, String name, Varbits varbit)
{
this.id = id;
this.name = name;
this.varbit = varbit;
this.varPlayer = null;
}
Quest(int id, String name, VarPlayer varPlayer)
{
this.id = id;
this.name = name;
this.varbit = null;
this.varPlayer = varPlayer;
}
public QuestState getState(Client client) public QuestState getState(Client client)
{ {
client.runScript(ScriptID.QUESTLIST_PROGRESS, id); client.runScript(ScriptID.QUESTLIST_PROGRESS, id);
@@ -208,4 +227,16 @@ public enum Quest
return QuestState.IN_PROGRESS; return QuestState.IN_PROGRESS;
} }
} }
public int getVar(Client client)
{
if (varbit != null)
{
return client.getVar(varbit);
}
else
{
return client.getVar(varPlayer);
}
}
} }

View File

@@ -163,7 +163,101 @@ public enum VarPlayer
MUSIC_TRACKS_UNLOCKED_16(1009), MUSIC_TRACKS_UNLOCKED_16(1009),
MUSIC_TRACKS_UNLOCKED_17(1338), MUSIC_TRACKS_UNLOCKED_17(1338),
MUSIC_TRACKS_UNLOCKED_18(1681), MUSIC_TRACKS_UNLOCKED_18(1681),
MUSIC_TRACKS_UNLOCKED_19(2065); MUSIC_TRACKS_UNLOCKED_19(2065),
/**
* f2p Quest varbits, these don't hold the completion value.
*/
QUEST_BLACK_KNIGHTS_FORTRESS(130),
QUEST_COOKS_ASSISTANT(29),
QUEST_DORICS_QUEST(31),
QUEST_DRAGON_SLAYER(176),
QUEST_ERNEST_THE_CHICKEN(32),
QUEST_IMP_CATCHER(160),
QUEST_THE_KNIGHTS_SWORD(122),
QUEST_PIRATES_TREASURE(71),
QUEST_PRINCE_ALI_RESCUE(273),
QUEST_THE_RESTLESS_GHOST(107),
QUEST_ROMEO_AND_JULIET(144),
QUEST_RUNE_MYSTERIES(63),
QUEST_SHEEP_SHEARER(179),
QUEST_SHIELD_OF_ARRAV(145),
QUEST_SHIELD_OF_ARRAV_STATE_146(146),
QUEST_VAMPIRE_SLAYER(178),
QUEST_WITCHS_POTION(67),
/**
* member Quest varbits, these don't hold the completion value.
*/
QUEST_BIG_CHOMPY_BIRD_HUNTING(293),
QUEST_BIOHAZARD(68),
QUEST_CABIN_FEVER(655),
QUEST_CLOCK_TOWER(10),
QUEST_CREATURE_OF_FENKENSTRAIN(399),
QUEST_DEATH_PLATEAU(314),
QUEST_THE_DIG_SITE(131),
QUEST_DRUIDIC_RITUAL(80),
QUEST_DWARF_CANNON(0),
QUEST_EADGARS_RUSE(335),
QUEST_ELEMENTAL_WORKSHOP_I(299),
QUEST_FAMILY_CREST(148),
QUEST_FIGHT_ARENA(17),
QUEST_FISHING_CONTEST(11),
QUEST_THE_FREMENNIK_TRIALS(347),
QUEST_WATERFALL_QUEST(65),
QUEST_GERTRUDES_CAT(180),
QUEST_THE_GRAND_TREE(150),
QUEST_HAUNTED_MINE(382),
QUEST_HAZEEL_CULT(223),
QUEST_HEROES_QUEST(188),
QUEST_HOLY_GRAIL(5),
QUEST_IN_SEARCH_OF_THE_MYREQUE(387),
QUEST_JUNGLE_POTION(175),
QUEST_LEGENDS_QUEST(139),
QUEST_LOST_CITY(147),
QUEST_MERLINS_CRYSTAL(14),
QUEST_MONKEY_MADNESS_I(365),
QUEST_MONKS_FRIEND(30),
QUEST_MOURNINGS_ENDS_PART_I(517),
QUEST_MURDER_MYSTERY(192),
QUEST_NATURE_SPIRIT(307),
QUEST_OBSERVATORY_QUEST(112),
QUEST_ONE_SMALL_FAVOUR(416),
QUEST_PLAGUE_CITY(165),
QUEST_PRIEST_IN_PERIL(302),
QUEST_RAG_AND_BONE_MAN(714),
QUEST_REGICIDE(328),
QUEST_ROVING_ELVES(402),
QUEST_RUM_DEAL(600),
QUEST_SCORPION_CATCHER(76),
QUEST_SEA_SLUG(159),
QUEST_SHADES_OF_MORTTON(339),
QUEST_SHEEP_HERDER(60),
QUEST_SHILO_VILLAGE(116),
QUEST_TAI_BWO_WANNAI_TRIO(320),
QUEST_TEMPLE_OF_IKOV(26),
QUEST_THRONE_OF_MISCELLANIA(359),
QUEST_THE_TOURIST_TRAP(197),
QUEST_WITCHS_HOUSE(226),
QUEST_TREE_GNOME_VILLAGE(111),
QUEST_TRIBAL_TOTEM(200),
QUEST_TROLL_ROMANCE(385),
QUEST_TROLL_STRONGHOLD(317),
QUEST_UNDERGROUND_PASS(161),
QUEST_UNDERGROUND_PASS_STATE_162(162),
QUEST_WATCHTOWER(212),
QUEST_THE_GREAT_BRAIN_ROBBERY(980),
QUEST_RAG_AND_BONE_MAN_II(714),
/**
* mini-quest varbits, these don't hold the completion value.
*/
QUEST_ENTER_THE_ABYSS(492),
QUEST_ALFRED_GRIMHANDS_BARCRAWL(77),
QUEST_ALFRED_GRIMHANDS_BARCRAWL_STATE_76(76),
QUEST_THE_MAGE_ARENA(267);
private final int id; private final int id;
} }

View File

@@ -503,7 +503,95 @@ public enum Varbits
TREK_STARTED(1956), TREK_STARTED(1956),
TREK_EVENT(1958), TREK_EVENT(1958),
TREK_STATUS(6719), TREK_STATUS(6719),
BLOAT_ENTERED_ROOM(6447); BLOAT_ENTERED_ROOM(6447),
/**
* f2p Quest varbits, these don't hold the completion value.
*/
QUEST_DEMON_SLAYER(2561),
QUEST_GOBLIN_DIPLOMACY(2378),
QUEST_MISTHALIN_MYSTERY(3468),
QUEST_THE_CORSAIR_CURSE(6071),
QUEST_X_MARKS_THE_SPOT(8063),
/**
* member Quest varbits, these don't hold the completion value.
*/
QUEST_ANIMAL_MAGNETISM(3185),
QUEST_BETWEEN_A_ROCK(299),
QUEST_CONTACT(3274),
QUEST_ZOGRE_FLESH_EATERS(487),
QUEST_DARKNESS_OF_HALLOWVALE(2573),
QUEST_DEATH_TO_THE_DORGESHUUN(2258),
QUEST_DESERT_TREASURE(358),
QUEST_DEVIOUS_MINDS(1465),
QUEST_EAGLES_PEAK(2780),
QUEST_ELEMENTAL_WORKSHOP_II(2639),
QUEST_ENAKHRAS_LAMENT(1560),
QUEST_ENLIGHTENED_JOURNEY(2866),
QUEST_THE_EYES_OF_GLOUPHRIE(2497),
QUEST_FAIRYTALE_I_GROWING_PAINS(1803),
QUEST_FAIRYTALE_II_CURE_A_QUEEN(2326),
QUEST_THE_FEUD(334),
QUEST_FORGETTABLE_TALE(822),
QUEST_GARDEN_OF_TRANQUILLITY(961),
QUEST_GHOSTS_AHOY(217),
QUEST_THE_GIANT_DWARF(571),
QUEST_THE_GOLEM(346),
QUEST_HORROR_FROM_THE_DEEP(34),
QUEST_ICTHLARINS_LITTLE_HELPER(418),
QUEST_IN_AID_OF_THE_MYREQUE(1990),
QUEST_THE_LOST_TRIBE(532),
QUEST_LUNAR_DIPLOMACY(2448),
QUEST_MAKING_HISTORY(1383),
QUEST_MOUNTAIN_DAUGHTER(260),
QUEST_MOURNINGS_ENDS_PART_II(1103),
QUEST_MY_ARMS_BIG_ADVENTURE(2790),
QUEST_RATCATCHERS(1404),
QUEST_RECIPE_FOR_DISASTER(1850),
QUEST_RECRUITMENT_DRIVE(657),
QUEST_ROYAL_TROUBLE(2140),
QUEST_THE_SLUG_MENACE(2610),
QUEST_SHADOW_OF_THE_STORM(1372),
QUEST_A_SOULS_BANE(2011),
QUEST_SPIRITS_OF_THE_ELID(1444),
QUEST_SWAN_SONG(2098),
QUEST_A_TAIL_OF_TWO_CATS(1028),
QUEST_TEARS_OF_GUTHIX(451),
QUEST_WANTED(1051),
QUEST_COLD_WAR(3293),
QUEST_THE_FREMENNIK_ISLES(3311),
QUEST_TOWER_OF_LIFE(3337),
QUEST_WHAT_LIES_BELOW(3523),
QUEST_OLAFS_QUEST(3534),
QUEST_ANOTHER_SLICE_OF_HAM(3550),
QUEST_DREAM_MENTOR(3618),
QUEST_GRIM_TALES(2783),
QUEST_KINGS_RANSOM(3888),
QUEST_MONKEY_MADNESS_II(5027),
QUEST_CLIENT_OF_KOUREND(5619),
QUEST_BONE_VOYAGE(5795),
QUEST_THE_QUEEN_OF_THIEVES(6037),
QUEST_THE_DEPTHS_OF_DESPAIR(6027),
QUEST_DRAGON_SLAYER_II(6104),
QUEST_TALE_OF_THE_RIGHTEOUS(6358),
QUEST_A_TASTE_OF_HOPE(6396),
QUEST_MAKING_FRIENDS_WITH_MY_ARM(6528),
QUEST_THE_ASCENT_OF_ARCEUUS(7856),
QUEST_THE_FORSAKEN_TOWER(7796),
/**
* mini-quest varbits, these don't hold the completion value.
*/
QUEST_ARCHITECTURAL_ALLIANCE(4982),
QUEST_BEAR_YOUR_SOUL(5078),
QUEST_CURSE_OF_THE_EMPTY_LORD(821),
QUEST_ENCHANTED_KEY(1391),
QUEST_THE_GENERALS_SHADOW(3330),
QUEST_SKIPPY_AND_THE_MOGRES(1344),
QUEST_LAIR_OF_TARN_RAZORLOR(3290),
QUEST_FAMILY_PEST(5347),
QUEST_THE_MAGE_ARENA_II(6067);
/** /**
* The raw varbit ID. * The raw varbit ID.

View File

@@ -66,6 +66,8 @@ import net.runelite.client.ui.RuneLiteSplashScreen;
import net.runelite.client.ui.overlay.OverlayManager; import net.runelite.client.ui.overlay.OverlayManager;
import net.runelite.client.ui.overlay.OverlayRenderer; import net.runelite.client.ui.overlay.OverlayRenderer;
import net.runelite.client.ui.overlay.WidgetOverlay; import net.runelite.client.ui.overlay.WidgetOverlay;
import net.runelite.client.ui.overlay.arrow.ArrowMinimapOverlay;
import net.runelite.client.ui.overlay.arrow.ArrowWorldOverlay;
import net.runelite.client.ui.overlay.infobox.InfoBoxManager; import net.runelite.client.ui.overlay.infobox.InfoBoxManager;
import net.runelite.client.ui.overlay.infobox.InfoBoxOverlay; import net.runelite.client.ui.overlay.infobox.InfoBoxOverlay;
import net.runelite.client.ui.overlay.tooltip.TooltipOverlay; import net.runelite.client.ui.overlay.tooltip.TooltipOverlay;
@@ -148,6 +150,12 @@ public class RuneLite
@Inject @Inject
private Provider<WorldMapOverlay> worldMapOverlay; private Provider<WorldMapOverlay> worldMapOverlay;
@Inject
private Provider<ArrowWorldOverlay> arrowWorldOverlay;
@Inject
private Provider<ArrowMinimapOverlay> arrowMinimapOverlay;
@Inject @Inject
private Provider<LootManager> lootManager; private Provider<LootManager> lootManager;
@@ -323,6 +331,8 @@ public class RuneLite
overlayManager.add(infoBoxOverlay.get()); overlayManager.add(infoBoxOverlay.get());
overlayManager.add(worldMapOverlay.get()); overlayManager.add(worldMapOverlay.get());
overlayManager.add(tooltipOverlay.get()); overlayManager.add(tooltipOverlay.get());
overlayManager.add(arrowWorldOverlay.get());
overlayManager.add(arrowMinimapOverlay.get());
} }
// Start plugins // Start plugins

View File

@@ -0,0 +1,162 @@
package net.runelite.client.plugins.example;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.util.EnumSet;
import java.util.HashSet;
import javax.inject.Inject;
import net.runelite.api.Client;
import net.runelite.api.GameState;
import net.runelite.api.ItemID;
import net.runelite.api.Point;
import net.runelite.api.SpriteID;
import net.runelite.api.coords.WorldPoint;
import net.runelite.client.callback.ClientThread;
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.plugins.cluescrolls.clues.emote.STASHUnit;
import net.runelite.client.ui.overlay.arrow.ArrowPoint;
import net.runelite.client.ui.overlay.arrow.ArrowPointManager;
import net.runelite.client.ui.overlay.arrow.ArrowType;
@PluginDescriptor(
name = "ArrowTest",
developerPlugin = true
)
public class ExamplePlugin extends Plugin
{
@Inject
private ArrowPointManager arrowPointManager;
@Inject
private SpriteManager spriteManager;
@Inject
private ItemManager itemManager;
@Inject
private Client client;
@Inject
private ClientThread clientThread;
private boolean firstLogin = true;
@Override
public void startUp() throws Exception
{
clientThread.invokeLater(this::loadArrows);
}
@Override
public void shutDown()
{
arrowPointManager.clear();
}
public void loadArrows()
{
if (client.getGameState() != GameState.LOGGED_IN)
{
return;
}
BufferedImage i = spriteManager.getSprite(SpriteID.MINIMAP_GUIDE_ARROW_YELLOW, 0);
BufferedImage i3 = spriteManager.getSprite(SpriteID.RED_GUIDE_ARROW, 0);
AffineTransform at = new AffineTransform();
at.concatenate(AffineTransform.getScaleInstance(1, -1));
at.concatenate(AffineTransform.getTranslateInstance(0, -i3.getHeight()));
BufferedImage i4 = new BufferedImage(i3.getWidth(), i3.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g = i4.createGraphics();
g.transform(at);
g.drawImage(i3, 0, 0, null);
g.dispose();
Point i3Offset = new Point(0, i3.getHeight() / -2);
ArrowPoint one = ArrowPoint.builder()
.worldPoint(new WorldPoint(1554, 3551, 0))
.minimapImage(i4)
.worldImage(i)
.minimapImageOffset(i3Offset)
.types(EnumSet.of(ArrowType.MINIMAP, ArrowType.WORLD_POINT))
.build();
//arrowPointManager.add(this, one);
ArrowPoint two = ArrowPoint.builder()
.worldPoint(new WorldPoint(1544, 3580, 0))
.minimapImage(i4)
.worldImage(i)
.minimapImageOffset(i3Offset)
.types(EnumSet.of(ArrowType.MINIMAP, ArrowType.WORLD_POINT))
.build();
//arrowPointManager.add(this, two);
ArrowPoint three = ArrowPoint.builder()
.worldPoint(new WorldPoint(1571, 3541, 0))
.minimapImage(i4)
.worldImage(i)
.minimapImageOffset(i3Offset)
.types(EnumSet.of(ArrowType.MINIMAP, ArrowType.WORLD_POINT))
.build();
//arrowPointManager.add(this, three);
HashSet<Integer> NPCs = new HashSet<>();
NPCs.add(6910);
ArrowPoint npcone = ArrowPoint.builder()
.worldPoint(new WorldPoint(1545, 3595, 0))
.minimapImage(i4)
.worldImage(i)
.minimapImageOffset(i3Offset)
.npcIDs(NPCs)
.types(EnumSet.of(ArrowType.MINIMAP, ArrowType.NPC))
.build();
//arrowPointManager.add(this, npcone);
HashSet<Integer> NPCs2 = new HashSet<>();
NPCs2.add(6889);
NPCs2.add(6883);
NPCs2.add(6885);
ArrowPoint npctwo = ArrowPoint.builder()
.worldPoint(new WorldPoint(1551, 3561, 0))
.minimapImage(i4)
.worldImage(i)
.minimapImageOffset(i3Offset)
.worldImageOffset(new Point(0, -i4.getHeight()))
.npcIDs(NPCs2)
.types(EnumSet.of(ArrowType.MINIMAP, ArrowType.NPC))
.build();
//arrowPointManager.add(this, npctwo);
HashSet<Integer> OBJs = new HashSet<>();
OBJs.add(STASHUnit.SHAYZIEN_WAR_TENT);
ArrowPoint objone = ArrowPoint.builder()
.worldPoint(new WorldPoint(1550, 3541, 0))
.minimapImage(i4)
.worldImage(i)
.minimapImageOffset(i3Offset)
.worldImageOffset(new Point(0, -i4.getHeight()))
.objectIDs(OBJs)
.types(EnumSet.of(ArrowType.MINIMAP, ArrowType.OBJECT))
.build();
arrowPointManager.add(this, objone);
BufferedImage i5 = itemManager.getImage(ItemID.BONES);
ArrowPoint four = ArrowPoint.builder()
.worldPoint(new WorldPoint(1517, 3553, 0))
.minimapImage(i5)
.worldImage(i5)
.minimapImagePointToTarget(false)
.types(EnumSet.of(ArrowType.MINIMAP, ArrowType.WORLD_POINT))
.build();
//arrowPointManager.add(this, four);
}
}

View File

@@ -64,11 +64,33 @@ public interface IdleNotifierConfig extends Config
return true; return true;
} }
@ConfigItem(
position = 4,
keyName = "skullNotification",
name = "Skull Notification",
description = "Receive a notification when you skull."
)
default boolean showSkullNotification()
{
return false;
}
@ConfigItem(
position = 5,
keyName = "unskullNotification",
name = "Unskull Notification",
description = "Receive a notification when you unskull."
)
default boolean showUnskullNotification()
{
return false;
}
@ConfigItem( @ConfigItem(
keyName = "timeout", keyName = "timeout",
name = "Idle Notification Delay (ms)", name = "Idle Notification Delay (ms)",
description = "The notification delay after the player is idle", description = "The notification delay after the player is idle",
position = 4 position = 6
) )
default int getIdleNotificationDelay() default int getIdleNotificationDelay()
{ {
@@ -79,7 +101,7 @@ public interface IdleNotifierConfig extends Config
keyName = "hitpoints", keyName = "hitpoints",
name = "Hitpoints Notification Threshold", name = "Hitpoints Notification Threshold",
description = "The amount of hitpoints to send a notification at. A value of 0 will disable notification.", description = "The amount of hitpoints to send a notification at. A value of 0 will disable notification.",
position = 5 position = 7
) )
default int getHitpointsThreshold() default int getHitpointsThreshold()
{ {
@@ -90,7 +112,7 @@ public interface IdleNotifierConfig extends Config
keyName = "prayer", keyName = "prayer",
name = "Prayer Notification Threshold", name = "Prayer Notification Threshold",
description = "The amount of prayer points to send a notification at. A value of 0 will disable notification.", description = "The amount of prayer points to send a notification at. A value of 0 will disable notification.",
position = 6 position = 8
) )
default int getPrayerThreshold() default int getPrayerThreshold()
{ {
@@ -100,7 +122,7 @@ public interface IdleNotifierConfig extends Config
@ConfigItem( @ConfigItem(
keyName = "oxygen", keyName = "oxygen",
name = "Oxygen Notification Threshold", name = "Oxygen Notification Threshold",
position = 7, position = 9,
description = "The amount of remaining oxygen to send a notification at. A value of 0 will disable notification." description = "The amount of remaining oxygen to send a notification at. A value of 0 will disable notification."
) )
default int getOxygenThreshold() default int getOxygenThreshold()
@@ -111,7 +133,7 @@ public interface IdleNotifierConfig extends Config
@ConfigItem( @ConfigItem(
keyName = "spec", keyName = "spec",
name = "Special Attack Energy Notification Threshold", name = "Special Attack Energy Notification Threshold",
position = 8, position = 10,
description = "The amount of spec energy reached to send a notification at. A value of 0 will disable notification." description = "The amount of spec energy reached to send a notification at. A value of 0 will disable notification."
) )
default int getSpecEnergyThreshold() default int getSpecEnergyThreshold()

View File

@@ -31,6 +31,7 @@ import java.awt.*;
import java.time.Duration; import java.time.Duration;
import java.time.Instant; import java.time.Instant;
import java.util.Arrays; import java.util.Arrays;
import java.util.EnumSet;
import java.util.List; import java.util.List;
import javax.inject.Inject; import javax.inject.Inject;
import net.runelite.api.Actor; import net.runelite.api.Actor;
@@ -44,8 +45,10 @@ import net.runelite.api.NPC;
import net.runelite.api.NPCComposition; import net.runelite.api.NPCComposition;
import net.runelite.api.Player; import net.runelite.api.Player;
import net.runelite.api.Skill; import net.runelite.api.Skill;
import net.runelite.api.SkullIcon;
import net.runelite.api.VarPlayer; import net.runelite.api.VarPlayer;
import net.runelite.api.Varbits; import net.runelite.api.Varbits;
import net.runelite.api.WorldType;
import net.runelite.api.events.AnimationChanged; import net.runelite.api.events.AnimationChanged;
import net.runelite.api.events.GameStateChanged; import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.GameTick; import net.runelite.api.events.GameTick;
@@ -100,6 +103,8 @@ public class IdleNotifierPlugin extends Plugin
private Instant sixHourWarningTime; private Instant sixHourWarningTime;
private boolean ready; private boolean ready;
private boolean lastInteractWasCombat; private boolean lastInteractWasCombat;
private SkullIcon lastTickSkull = null;
private boolean isFirstTick = true;
@Provides @Provides
IdleNotifierConfig provideConfig(ConfigManager configManager) IdleNotifierConfig provideConfig(ConfigManager configManager)
@@ -327,9 +332,13 @@ public class IdleNotifierPlugin extends Plugin
{ {
case LOGIN_SCREEN: case LOGIN_SCREEN:
resetTimers(); resetTimers();
isFirstTick = true;
break;
case HOPPING:
isFirstTick = true;
ready = true;
break; break;
case LOGGING_IN: case LOGGING_IN:
case HOPPING:
case CONNECTION_LOST: case CONNECTION_LOST:
ready = true; ready = true;
break; break;
@@ -381,6 +390,8 @@ public class IdleNotifierPlugin extends Plugin
@Subscribe @Subscribe
public void onGameTick(GameTick event) public void onGameTick(GameTick event)
{ {
skullNotifier();
final Player local = client.getLocalPlayer(); final Player local = client.getLocalPlayer();
final Duration waitDuration = Duration.ofMillis(config.getIdleNotificationDelay()); final Duration waitDuration = Duration.ofMillis(config.getIdleNotificationDelay());
lastCombatCountdown = Math.max(lastCombatCountdown - 1, 0); lastCombatCountdown = Math.max(lastCombatCountdown - 1, 0);
@@ -666,4 +677,31 @@ public class IdleNotifierPlugin extends Plugin
lastInteract = null; lastInteract = null;
} }
} }
private void skullNotifier()
{
final Player local = client.getLocalPlayer();
SkullIcon currentTickSkull = local.getSkullIcon();
EnumSet worldTypes = client.getWorldType();
if (!(worldTypes.contains(WorldType.DEADMAN) || worldTypes.contains(WorldType.SEASONAL_DEADMAN)))
{
if (!isFirstTick)
{
if (config.showSkullNotification() && lastTickSkull == null && currentTickSkull == SkullIcon.SKULL)
{
notifier.notify("[" + local.getName() + "] is now skulled!");
}
else if (config.showUnskullNotification() && lastTickSkull == SkullIcon.SKULL && currentTickSkull == null)
{
notifier.notify("[" + local.getName() + "] is now unskulled!");
}
}
else
{
isFirstTick = false;
}
lastTickSkull = currentTickSkull;
}
}
} }

View File

@@ -0,0 +1,87 @@
/*
* Copyright (c) 2019, Trevor <https://github.com/Trevor159>
* 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.questhelper;
import lombok.Getter;
import net.runelite.api.Client;
import net.runelite.api.InventoryID;
import net.runelite.api.Item;
public class ItemRequirement
{
@Getter
private int id;
@Getter
private int quantity;
private boolean equip;
public ItemRequirement(int id)
{
this(id, 1);
}
public ItemRequirement(int id, int quantity)
{
this.id = id;
this.quantity = quantity;
equip = false;
}
public ItemRequirement(int id, int quantity, boolean equip)
{
this(id, quantity);
this.equip = equip;
}
public boolean check(Client client)
{
Item[] items;
if (equip)
{
items = client.getItemContainer(InventoryID.EQUIPMENT).getItems();
}
else
{
items = client.getItemContainer(InventoryID.INVENTORY).getItems();
}
int tempQuantity = quantity;
for (Item item : items)
{
if (item.getId() == id)
{
if (item.getQuantity() >= tempQuantity)
{
return true;
}
else
{
tempQuantity -= item.getQuantity();
}
}
}
return false;
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2019, Trevor <https://github.com/Trevor159>
* 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.questhelper;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import net.runelite.api.Quest;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface QuestDescriptor
{
Quest quest();
}

View File

@@ -0,0 +1,143 @@
/*
* Copyright (c) 2019, Trevor <https://github.com/Trevor159>
* 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.questhelper;
import com.google.inject.Binder;
import com.google.inject.CreationException;
import com.google.inject.Injector;
import com.google.inject.Module;
import java.util.Map;
import javax.inject.Inject;
import lombok.Getter;
import lombok.Setter;
import net.runelite.api.Client;
import net.runelite.api.Quest;
import net.runelite.api.QuestState;
import net.runelite.client.eventbus.EventBus;
import net.runelite.client.plugins.questhelper.steps.QuestStep;
public abstract class QuestHelper implements Module
{
@Inject
private Client client;
@Inject
private EventBus eventBus;
@Getter
protected QuestStep currentStep;
protected Map<Integer, QuestStep> steps;
protected Injector injector;
@Getter
@Setter
private Quest quest;
protected int var;
@Override
public void configure(Binder binder)
{
}
protected void startUp() throws Exception
{
steps = loadSteps();
instantiateSteps();
var = getVar();
startUpStep(var);
}
protected void shutDown() throws Exception
{
steps = null;
shutDownStep();
}
protected void startUpStep(int i) throws Exception
{
if (steps.containsKey(i))
{
QuestStep step = steps.get(i);
currentStep = step;
eventBus.register(currentStep);
currentStep.startUp();
}
else
{
currentStep = null;
}
}
protected void shutDownStep() throws Exception
{
if (currentStep != null)
{
eventBus.unregister(currentStep);
currentStep.shutDown();
currentStep = null;
}
}
protected void updateQuest() throws Exception
{
shutDownStep();
if (!isCompleted())
{
currentStep = steps.get(getVar());
currentStep.startUp();
}
else
{
currentStep = null;
}
}
protected void instantiateSteps()
{
for (QuestStep step : steps.values())
{
try
{
injector.injectMembers(step);
}
catch (CreationException ex)
{
ex.printStackTrace();
}
}
}
protected boolean isCompleted()
{
return (quest.getState(client) == QuestState.FINISHED);
}
protected int getVar()
{
return quest.getVar(client);
}
protected abstract Map<Integer, QuestStep> loadSteps();
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright (c) 2016-2017, Seth <Sethtroll3@gmail.com>
* Copyright (c) 2018, Lotto <https://github.com/devLotto>
* Copyright (c) 2019, Trevor <https://github.com/Trevor159>
* 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 HOLDER 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.questhelper;
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.OverlayPriority;
import net.runelite.client.ui.overlay.components.ComponentConstants;
import net.runelite.client.ui.overlay.components.PanelComponent;
public class QuestHelperOverlay extends Overlay
{
public static final Color TITLED_CONTENT_COLOR = new Color(190, 190, 190);
private final QuestHelperPlugin plugin;
private final PanelComponent panelComponent = new PanelComponent();
@Inject
public QuestHelperOverlay(QuestHelperPlugin plugin)
{
this.plugin = plugin;
setPriority(OverlayPriority.LOW);
}
@Override
public Dimension render(Graphics2D graphics)
{
QuestHelper questHelper = plugin.getSelectedQuest();
if (questHelper == null || questHelper.getCurrentStep() == null)
{
return null;
}
panelComponent.getChildren().clear();
panelComponent.setPreferredSize(new Dimension(ComponentConstants.STANDARD_WIDTH, 0));
questHelper.getCurrentStep().makeOverlayHint(panelComponent, plugin);
return panelComponent.render(graphics);
}
}

View File

@@ -0,0 +1,263 @@
/*
* Copyright (c) 2019, Trevor <https://github.com/Trevor159>
* 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.questhelper;
import com.google.common.collect.ImmutableSet;
import com.google.common.reflect.ClassPath;
import com.google.inject.Binder;
import com.google.inject.CreationException;
import com.google.inject.Injector;
import com.google.inject.Module;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.inject.Inject;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
import net.runelite.api.GameState;
import net.runelite.api.Quest;
import net.runelite.api.events.GameStateChanged;
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.RuneLite;
import net.runelite.client.eventbus.EventBus;
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 net.runelite.client.util.Text;
@PluginDescriptor(
name = "Quest Helper",
description = "Helps you with your quests"
)
@Slf4j
public class QuestHelperPlugin extends Plugin
{
@Inject
private Client client;
@Inject
private EventBus eventBus;
@Inject
private OverlayManager overlayManager;
@Inject
private QuestHelperOverlay questHelperOverlay;
@Inject
private QuestHelperWorldOverlay questHelperWorldOverlay;
private static final String QUEST_PACKAGE = "net.runelite.client.plugins.questhelper.quests";
private Map<String, QuestHelper> quests;
@Getter
private QuestHelper selectedQuest = null;
@Override
protected void startUp() throws IOException
{
if (client.getGameState() == GameState.LOGGED_IN)
{
if (quests == null)
{
quests = scanAndInstantiate(getClass().getClassLoader(), QUEST_PACKAGE);
}
}
overlayManager.add(questHelperOverlay);
overlayManager.add(questHelperWorldOverlay);
}
@Override
protected void shutDown() throws Exception
{
overlayManager.remove(questHelperOverlay);
overlayManager.remove(questHelperWorldOverlay);
quests = null;
shutDownQuest();
}
@Subscribe
public void onWidgetLoaded(WidgetLoaded event) throws Exception
{
int groupId = event.getGroupId();
if (groupId == WidgetID.DIARY_QUEST_GROUP_ID)
{
Widget widget = client.getWidget(WidgetInfo.DIARY_QUEST_WIDGET_TITLE);
String questname = Text.removeTags(widget.getText());
if (quests.containsKey(questname))
{
QuestHelper widgetQuest = quests.get(questname);
if (selectedQuest == null || !selectedQuest.equals(widgetQuest))
{
shutDownQuest();
startUpQuest(widgetQuest);
}
}
}
}
@Subscribe
public void onGameStateChanged(GameStateChanged event) throws IOException
{
if (event.getGameState() == GameState.LOGGED_IN)
{
if (quests == null)
{
quests = scanAndInstantiate(getClass().getClassLoader(), QUEST_PACKAGE);
}
}
}
@Subscribe
public void onVarbitChanged(VarbitChanged event) throws Exception
{
if (selectedQuest != null && selectedQuest.var != selectedQuest.getVar())
{
selectedQuest.var = selectedQuest.getVar();
selectedQuest.updateQuest();
if (selectedQuest.getCurrentStep() == null)
{
shutDownQuest();
}
}
}
private void startUpQuest(QuestHelper questHelper) throws Exception
{
if (!questHelper.isCompleted())
{
selectedQuest = questHelper;
eventBus.register(selectedQuest);
selectedQuest.startUp();
}
else
{
selectedQuest = null;
}
}
private void shutDownQuest() throws Exception
{
if (selectedQuest != null)
{
selectedQuest.shutDown();
eventBus.unregister(selectedQuest);
selectedQuest = null;
}
}
Map<String, QuestHelper> scanAndInstantiate(ClassLoader classLoader, String packageName) throws IOException
{
Map<Quest, Class<? extends QuestHelper>> quests = new HashMap<>();
Map<String, QuestHelper> scannedQuests = new HashMap<>();
ClassPath classPath = ClassPath.from(classLoader);
ImmutableSet<ClassPath.ClassInfo> classes = packageName == null ? classPath.getAllClasses()
: classPath.getTopLevelClassesRecursive(packageName);
for (ClassPath.ClassInfo classInfo : classes)
{
Class<?> clazz = classInfo.load();
QuestDescriptor questDescriptor = clazz.getAnnotation(QuestDescriptor.class);
if (questDescriptor == null)
{
if (clazz.getSuperclass() == QuestHelper.class)
{
log.warn("Class {} is a quest helper, but has no quest descriptor",
clazz);
}
continue;
}
if (clazz.getSuperclass() != QuestHelper.class)
{
log.warn("Class {} has quest descriptor, but is not a quest helper",
clazz);
continue;
}
Class<QuestHelper> questClass = (Class<QuestHelper>) clazz;
quests.put(questDescriptor.quest(), questClass);
}
for (Map.Entry<Quest, Class<? extends QuestHelper>> questClazz : quests.entrySet())
{
QuestHelper questHelper;
try
{
questHelper = instantiate((Class<QuestHelper>) questClazz.getValue(), questClazz.getKey());
}
catch (QuestInstantiationException ex)
{
log.warn("Error instantiating quest helper!", ex);
continue;
}
scannedQuests.put(questClazz.getKey().getName(), questHelper);
}
return scannedQuests;
}
private QuestHelper instantiate(Class<QuestHelper> clazz, Quest quest) throws QuestInstantiationException
{
QuestHelper questHelper;
try
{
questHelper = clazz.newInstance();
questHelper.setQuest(quest);
}
catch (InstantiationException | IllegalAccessException ex)
{
throw new QuestInstantiationException(ex);
}
try
{
Module questModule = (Binder binder) ->
{
binder.bind(clazz).toInstance(questHelper);
binder.install(questHelper);
};
Injector questInjector = RuneLite.getInjector().createChildInjector(questModule);
questInjector.injectMembers(questHelper);
questHelper.injector = questInjector;
}
catch (CreationException ex)
{
throw new QuestInstantiationException(ex);
}
log.debug("Loaded quest helper {}", clazz.getSimpleName());
return questHelper;
}
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright (c) 2018, Morgan Lewis <https://github.com/MESLewis>
* Copyright (c) 2019, Trevor <https://github.com/Trevor159>
* 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.questhelper;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import net.runelite.api.Point;
import net.runelite.api.coords.WorldPoint;
import net.runelite.client.ui.overlay.worldmap.WorldMapPoint;
import net.runelite.client.util.ImageUtil;
public class QuestHelperWorldMapPoint extends WorldMapPoint
{
private final BufferedImage questWorldImage;
private final Point questWorldImagePoint;
private final BufferedImage questImage;
public QuestHelperWorldMapPoint(final WorldPoint worldPoint, BufferedImage image)
{
super(worldPoint, null);
BufferedImage mapArrow = ImageUtil.getResourceStreamFromClass(getClass(), "/util/clue_arrow.png");
questWorldImage = new BufferedImage(mapArrow.getWidth(), mapArrow.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics graphics = questWorldImage.getGraphics();
graphics.drawImage(mapArrow, 0, 0, null);
int buffer = mapArrow.getWidth() / 2 - image.getWidth() / 2;
buffer = buffer < 0 ? 0 : buffer;
graphics.drawImage(image, buffer, buffer, null);
questWorldImagePoint = new Point(questWorldImage.getWidth() / 2, questWorldImage.getHeight());
this.questImage = image;
this.setSnapToEdge(true);
this.setJumpOnClick(true);
this.setImage(questWorldImage);
this.setImagePoint(questWorldImagePoint);
}
@Override
public void onEdgeSnap()
{
this.setImage(questImage);
this.setImagePoint(null);
}
@Override
public void onEdgeUnsnap()
{
this.setImage(questWorldImage);
this.setImagePoint(questWorldImagePoint);
}
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright (c) 2018, Lotto <https://github.com/devLotto>
* Copyright (c) 2019, Trevor <https://github.com/Trevor159>
* 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.questhelper;
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.OverlayLayer;
import net.runelite.client.ui.overlay.OverlayPosition;
public class QuestHelperWorldOverlay extends Overlay
{
public static final int IMAGE_Z_OFFSET = 30;
public static final Color CLICKBOX_BORDER_COLOR = Color.CYAN;
public static final Color CLICKBOX_HOVER_BORDER_COLOR = CLICKBOX_BORDER_COLOR.darker();
public static final Color CLICKBOX_FILL_COLOR = new Color(0, 255, 0, 20);
private final QuestHelperPlugin plugin;
@Inject
public QuestHelperWorldOverlay(QuestHelperPlugin plugin)
{
setPosition(OverlayPosition.DYNAMIC);
setLayer(OverlayLayer.ABOVE_SCENE);
this.plugin = plugin;
}
@Override
public Dimension render(Graphics2D graphics)
{
QuestHelper quest = plugin.getSelectedQuest();
if (quest != null && quest.getCurrentStep() != null)
{
quest.getCurrentStep().makeWorldOverlayHint(graphics, plugin);
}
return null;
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (c) 2016-2017, Adam <Adam@sigterm.info>
* Copyright (c) 2019, Trevor <https://github.com/Trevor159>
* 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.questhelper;
public class QuestInstantiationException extends Exception
{
public QuestInstantiationException(Throwable cause)
{
super(cause);
}
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright (c) 2019, Trevor <https://github.com/Trevor159>
* 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.questhelper.quests.cooksassistant;
import java.util.HashMap;
import java.util.Map;
import net.runelite.api.ItemID;
import net.runelite.api.NpcID;
import net.runelite.api.Quest;
import net.runelite.api.coords.WorldPoint;
import net.runelite.client.plugins.questhelper.ItemRequirement;
import net.runelite.client.plugins.questhelper.QuestHelper;
import net.runelite.client.plugins.questhelper.steps.NpcTalkStep;
import net.runelite.client.plugins.questhelper.steps.QuestStep;
import net.runelite.client.plugins.questhelper.QuestDescriptor;
@QuestDescriptor(
quest = Quest.COOKS_ASSISTANT
)
public class CooksAssistant extends QuestHelper
{
@Override
protected Map<Integer, QuestStep> loadSteps()
{
Map<Integer, QuestStep> steps = new HashMap<>();
steps.put(0, new NpcTalkStep(this, NpcID.COOK_4626, new WorldPoint(3206, 3214, 0),
"Give the Cook in Lumbridge Castle's kitchen the required items to finish the quest.",
new ItemRequirement(ItemID.BUCKET_OF_MILK), new ItemRequirement(ItemID.POT_OF_FLOUR),
new ItemRequirement(ItemID.EGG)));
steps.put(1, steps.get(0));
return steps;
}
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright (c) 2019, Trevor <https://github.com/Trevor159>
* 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.questhelper.quests.impcatcher;
import java.util.HashMap;
import java.util.Map;
import net.runelite.api.ItemID;
import net.runelite.api.NpcID;
import net.runelite.api.Quest;
import net.runelite.api.coords.WorldPoint;
import net.runelite.client.plugins.questhelper.ItemRequirement;
import net.runelite.client.plugins.questhelper.QuestHelper;
import net.runelite.client.plugins.questhelper.steps.NpcTalkStep;
import net.runelite.client.plugins.questhelper.steps.QuestStep;
import net.runelite.client.plugins.questhelper.QuestDescriptor;
@QuestDescriptor(
quest = Quest.IMP_CATCHER
)
public class ImpCatcher extends QuestHelper
{
@Override
protected Map<Integer, QuestStep> loadSteps()
{
Map<Integer, QuestStep> steps = new HashMap<>();
steps.put(0, new NpcTalkStep(this, NpcID.WIZARD_MIZGOG, new WorldPoint(3103, 3163, 2),
"Talk to Wizard Mizgog on the top floor of the Wizards' Tower with the required items to finish the quest.",
new ItemRequirement(ItemID.BLACK_BEAD), new ItemRequirement(ItemID.WHITE_BEAD),
new ItemRequirement(ItemID.RED_BEAD), new ItemRequirement(ItemID.YELLOW_BEAD)));
steps.put(1, steps.get(0));
return steps;
}
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright (c) 2019, Trevor <https://github.com/Trevor159>
* 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.questhelper.quests.xmarksthespot;
import java.util.HashMap;
import java.util.Map;
import net.runelite.api.ItemID;
import net.runelite.api.NpcID;
import net.runelite.api.Quest;
import net.runelite.api.coords.WorldPoint;
import net.runelite.client.plugins.questhelper.ItemRequirement;
import net.runelite.client.plugins.questhelper.QuestHelper;
import net.runelite.client.plugins.questhelper.steps.DigStep;
import net.runelite.client.plugins.questhelper.steps.NpcTalkStep;
import net.runelite.client.plugins.questhelper.steps.QuestStep;
import net.runelite.client.plugins.questhelper.QuestDescriptor;
@QuestDescriptor(
quest = Quest.X_MARKS_THE_SPOT
)
public class XMarksTheSpot extends QuestHelper
{
@Override
protected Map<Integer, QuestStep> loadSteps()
{
Map<Integer, QuestStep> steps = new HashMap<>();
steps.put(0, new NpcTalkStep(this, NpcID.VEOS_8484, new WorldPoint(3228, 3242, 0),
"Talk to Veos in The Sheared Ram pub in Lumbridge to start the quest."));
steps.put(1, steps.get(0));
steps.put(2, new DigStep(this, new WorldPoint(3230, 3209, 0),
"Dig north of Bob's Brilliant Axes, on the west side of the plant against the wall of his house.",
new ItemRequirement(ItemID.TREASURE_SCROLL)));
steps.put(3, new DigStep(this, new WorldPoint(3203, 3212, 0),
"Dig behind Lumbridge Castle, just outside the kitchen door.",
new ItemRequirement(ItemID.TREASURE_SCROLL_23068)));
steps.put(4, new DigStep(this, new WorldPoint(3109, 3264, 0),
"Dig north-west of the Draynor Village jail, just by the wheat farm.",
new ItemRequirement(ItemID.MYSTERIOUS_ORB_23069)));
steps.put(5, new DigStep(this, new WorldPoint(3078, 3259, 0),
"Dig in the pig pen just west where Martin the Master Gardener is.",
new ItemRequirement(ItemID.TREASURE_SCROLL_23070)));
steps.put(6, new NpcTalkStep(this, NpcID.VEOS_8484, new WorldPoint(3054, 3245, 0),
"Talk to Veos directly south of the Rusty Anchor Inn in Port Sarim to finish the quest.",
new ItemRequirement(ItemID.ANCIENT_CASKET)));
steps.put(7, new NpcTalkStep(this, NpcID.VEOS_8484, new WorldPoint(3054, 3245, 0),
"Talk to Veos directly south of the Rusty Anchor Inn in Port Sarim to finish the quest."));
return steps;
}
}

View File

@@ -0,0 +1,124 @@
/*
* Copyright (c) 2019, Trevor <https://github.com/Trevor159>
* 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.questhelper.steps;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.inject.Inject;
import net.runelite.api.Client;
import net.runelite.api.ItemID;
import net.runelite.api.coords.LocalPoint;
import net.runelite.api.coords.WorldPoint;
import net.runelite.client.game.ItemManager;
import net.runelite.client.plugins.questhelper.ItemRequirement;
import net.runelite.client.plugins.questhelper.QuestHelper;
import net.runelite.client.plugins.questhelper.QuestHelperPlugin;
import net.runelite.client.plugins.questhelper.QuestHelperWorldMapPoint;
import net.runelite.client.ui.overlay.OverlayUtil;
import net.runelite.client.ui.overlay.components.LineComponent;
import net.runelite.client.ui.overlay.components.PanelComponent;
import net.runelite.client.ui.overlay.worldmap.WorldMapPointManager;
public class DigStep extends QuestStep
{
@Inject
Client client;
@Inject
ItemManager itemManager;
@Inject
WorldMapPointManager worldMapPointManager;
private final WorldPoint worldPoint;
private final List<ItemRequirement> itemRequirements = new ArrayList<>();
public DigStep(QuestHelper questHelper, WorldPoint worldPoint, String text, ItemRequirement... itemRequirements)
{
super(questHelper, text);
this.worldPoint = worldPoint;
this.itemRequirements.add(0, new ItemRequirement(ItemID.SPADE));
Collections.addAll(this.itemRequirements, itemRequirements);
}
@Override
public void startUp() throws Exception
{
worldMapPointManager.add(new QuestHelperWorldMapPoint(worldPoint, getQuestImage()));
}
@Override
public void shutDown() throws Exception
{
worldMapPointManager.removeIf(QuestHelperWorldMapPoint.class::isInstance);
}
@Override
public void makeOverlayHint(PanelComponent panelComponent, QuestHelperPlugin plugin)
{
super.makeOverlayHint(panelComponent, plugin);
panelComponent.getChildren().add(LineComponent.builder().left("Required Items:").build());
for (ItemRequirement itemRequirement : itemRequirements)
{
String text = itemRequirement.getQuantity() + " x " + itemManager.getItemComposition(itemRequirement.getId()).getName();
Color color;
if (itemRequirement.check(client))
{
color = Color.GREEN;
}
else
{
color = Color.RED;
}
panelComponent.getChildren().add(LineComponent.builder()
.left(text)
.leftColor(color)
.build());
}
}
@Override
public void makeWorldOverlayHint(Graphics2D graphics, QuestHelperPlugin plugin)
{
LocalPoint localLocation = LocalPoint.fromWorld(client, worldPoint);
if (localLocation == null)
{
return;
}
OverlayUtil.renderTileOverlay(client, graphics, localLocation, getSpadeImage(), Color.ORANGE);
}
private BufferedImage getSpadeImage()
{
return itemManager.getImage(ItemID.SPADE);
}
}

View File

@@ -0,0 +1,161 @@
/*
* Copyright (c) 2019, Trevor <https://github.com/Trevor159>
* 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.questhelper.steps;
import java.awt.Color;
import java.awt.Graphics2D;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.inject.Inject;
import net.runelite.api.Client;
import net.runelite.api.NPC;
import net.runelite.api.coords.WorldPoint;
import net.runelite.api.events.NpcDespawned;
import net.runelite.api.events.NpcSpawned;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.game.ItemManager;
import net.runelite.client.plugins.questhelper.ItemRequirement;
import net.runelite.client.plugins.questhelper.QuestHelper;
import net.runelite.client.plugins.questhelper.QuestHelperPlugin;
import net.runelite.client.plugins.questhelper.QuestHelperWorldMapPoint;
import static net.runelite.client.plugins.questhelper.QuestHelperWorldOverlay.IMAGE_Z_OFFSET;
import net.runelite.client.ui.overlay.OverlayUtil;
import net.runelite.client.ui.overlay.components.LineComponent;
import net.runelite.client.ui.overlay.components.PanelComponent;
import net.runelite.client.ui.overlay.worldmap.WorldMapPointManager;
public class NpcTalkStep extends QuestStep
{
@Inject
protected Client client;
@Inject
protected ItemManager itemManager;
@Inject
protected WorldMapPointManager worldMapPointManager;
private int npcID;
private WorldPoint worldPoint;
private List<NPC> npcsToHighlight = new ArrayList<>();
List<ItemRequirement> itemRequirements;
public NpcTalkStep(QuestHelper questHelper, int npcID, WorldPoint worldPoint, String text, ItemRequirement... itemRequirements)
{
super(questHelper, text);
this.npcID = npcID;
this.worldPoint = worldPoint;
this.itemRequirements = Arrays.asList(itemRequirements);
}
@Override
public void startUp() throws Exception
{
for (NPC npc : client.getNpcs())
{
if (npcID == npc.getId())
{
npcsToHighlight.add(npc);
}
}
worldMapPointManager.add(new QuestHelperWorldMapPoint(worldPoint, getQuestImage()));
}
@Override
public void shutDown() throws Exception
{
npcsToHighlight.clear();
worldMapPointManager.removeIf(QuestHelperWorldMapPoint.class::isInstance);
}
@Subscribe
public void onNpcSpawned(NpcSpawned event)
{
if (event.getNpc().getId() == npcID)
{
npcsToHighlight.add(event.getNpc());
}
}
@Subscribe
public void onNpcDespawned(NpcDespawned event)
{
if (npcsToHighlight.contains(event.getNpc()))
{
npcsToHighlight.remove(event.getNpc());
}
}
@Override
public void makeOverlayHint(PanelComponent panelComponent, QuestHelperPlugin plugin)
{
super.makeOverlayHint(panelComponent, plugin);
if (itemRequirements.isEmpty())
{
return;
}
panelComponent.getChildren().add(LineComponent.builder().left("Required Items:").build());
for (ItemRequirement itemRequirement : itemRequirements)
{
String text = itemRequirement.getQuantity() + " x " + itemManager.getItemComposition(itemRequirement.getId()).getName();
Color color;
if (itemRequirement.check(client))
{
color = Color.GREEN;
}
else
{
color = Color.RED;
}
panelComponent.getChildren().add(LineComponent.builder()
.left(text)
.leftColor(color)
.build());
}
}
@Override
public void makeWorldOverlayHint(Graphics2D graphics, QuestHelperPlugin plugin)
{
if (!worldPoint.isInScene(client))
{
return;
}
if (npcsToHighlight.isEmpty())
{
return;
}
for (NPC npc : npcsToHighlight)
{
OverlayUtil.renderActorOverlayImage(graphics, npc, getQuestImage(), Color.CYAN, IMAGE_Z_OFFSET);
}
}
}

View File

@@ -0,0 +1,85 @@
/*
* Copyright (c) 2019, Trevor <https://github.com/Trevor159>
* 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.questhelper.steps;
import com.google.inject.Binder;
import com.google.inject.Inject;
import com.google.inject.Module;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import net.runelite.api.SpriteID;
import net.runelite.client.game.SpriteManager;
import net.runelite.client.plugins.questhelper.QuestHelper;
import static net.runelite.client.plugins.questhelper.QuestHelperOverlay.TITLED_CONTENT_COLOR;
import net.runelite.client.plugins.questhelper.QuestHelperPlugin;
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 abstract class QuestStep implements Module
{
@Inject
SpriteManager spriteManager;
private final String text;
private final QuestHelper questHelper;
public QuestStep(QuestHelper questHelper, String text)
{
this.text = text;
this.questHelper = questHelper;
}
@Override
public void configure(Binder binder)
{
}
public void startUp() throws Exception
{
}
public void shutDown() throws Exception
{
}
public void makeOverlayHint(PanelComponent panelComponent, QuestHelperPlugin plugin)
{
panelComponent.getChildren().add(TitleComponent.builder().text(questHelper.getQuest().getName()).build());
panelComponent.getChildren().add(LineComponent.builder().left("Step:").build());
panelComponent.getChildren().add(LineComponent.builder()
.left(text)
.leftColor(TITLED_CONTENT_COLOR)
.build());
}
public abstract void makeWorldOverlayHint(Graphics2D graphics, QuestHelperPlugin plugin);
public BufferedImage getQuestImage()
{
return spriteManager.getSprite(SpriteID.TAB_QUESTS, 0);
}
}

View File

@@ -38,8 +38,8 @@ public interface RaidsConfig extends Config
@ConfigItem( @ConfigItem(
position = 0, position = 0,
keyName = "raidsTimer", keyName = "raidsTimer",
name = "Display elapsed raid time", name = "Level time tooltip",
description = "Display elapsed raid time" description = "Displays your level times as a tooltip on the points overlay"
) )
default boolean raidsTimer() default boolean raidsTimer()
{ {

View File

@@ -31,7 +31,6 @@ import java.awt.Graphics2D;
import java.awt.Rectangle; import java.awt.Rectangle;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.time.Instant;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
@@ -57,11 +56,13 @@ import net.runelite.api.Tile;
import net.runelite.api.VarPlayer; import net.runelite.api.VarPlayer;
import net.runelite.api.Varbits; import net.runelite.api.Varbits;
import net.runelite.api.events.ChatMessage; import net.runelite.api.events.ChatMessage;
import net.runelite.api.events.ClientTick;
import net.runelite.api.events.ConfigChanged; import net.runelite.api.events.ConfigChanged;
import net.runelite.api.events.VarbitChanged; import net.runelite.api.events.VarbitChanged;
import net.runelite.api.events.WidgetHiddenChanged; import net.runelite.api.events.WidgetHiddenChanged;
import net.runelite.api.widgets.Widget; import net.runelite.api.widgets.Widget;
import net.runelite.api.widgets.WidgetInfo; import net.runelite.api.widgets.WidgetInfo;
import net.runelite.api.widgets.WidgetInfo;
import net.runelite.client.callback.ClientThread; import net.runelite.client.callback.ClientThread;
import net.runelite.client.chat.ChatColorType; import net.runelite.client.chat.ChatColorType;
import net.runelite.client.chat.ChatMessageBuilder; import net.runelite.client.chat.ChatMessageBuilder;
@@ -81,9 +82,13 @@ import net.runelite.client.plugins.raids.solver.RotationSolver;
import net.runelite.client.ui.DrawManager; import net.runelite.client.ui.DrawManager;
import net.runelite.client.ui.FontManager; import net.runelite.client.ui.FontManager;
import net.runelite.client.ui.overlay.OverlayManager; import net.runelite.client.ui.overlay.OverlayManager;
import net.runelite.client.ui.overlay.WidgetOverlay;
import net.runelite.client.ui.overlay.infobox.InfoBoxManager; import net.runelite.client.ui.overlay.infobox.InfoBoxManager;
import net.runelite.client.ui.overlay.tooltip.Tooltip;
import net.runelite.client.ui.overlay.tooltip.TooltipManager;
import net.runelite.client.util.Text; import net.runelite.client.util.Text;
import net.runelite.client.util.HotkeyListener; import net.runelite.client.util.HotkeyListener;
import org.apache.commons.lang3.StringUtils;
@PluginDescriptor( @PluginDescriptor(
name = "Chambers Of Xeric", name = "Chambers Of Xeric",
@@ -95,9 +100,6 @@ import net.runelite.client.util.HotkeyListener;
public class RaidsPlugin extends Plugin public class RaidsPlugin extends Plugin
{ {
private static final int LOBBY_PLANE = 3; private static final int LOBBY_PLANE = 3;
private static final String RAID_START_MESSAGE = "The raid has begun!";
private static final String LEVEL_COMPLETE_MESSAGE = "level complete!";
private static final String RAID_COMPLETE_MESSAGE = "Congratulations - your raid is complete!";
private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("###.##"); private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("###.##");
static final DecimalFormat POINTS_FORMAT = new DecimalFormat("#,###"); static final DecimalFormat POINTS_FORMAT = new DecimalFormat("#,###");
private static final String SPLIT_REGEX = "\\s*,\\s*"; private static final String SPLIT_REGEX = "\\s*,\\s*";
@@ -106,6 +108,8 @@ public class RaidsPlugin extends Plugin
@Inject @Inject
private ItemManager itemManager; private ItemManager itemManager;
private static final Pattern LEVEL_COMPLETE_REGEX = Pattern.compile("(.+) level complete! Duration: ([0-9:]+)");
private static final Pattern RAID_COMPLETE_REGEX = Pattern.compile("Congratulations - your raid is complete! Duration: ([0-9:]+)");
@Inject @Inject
private ChatMessageManager chatMessageManager; private ChatMessageManager chatMessageManager;
@@ -146,6 +150,9 @@ public class RaidsPlugin extends Plugin
@Inject @Inject
private KeyManager keyManager; private KeyManager keyManager;
@Inject
private TooltipManager tooltipManager;
@Getter @Getter
private final ArrayList<String> roomWhitelist = new ArrayList<>(); private final ArrayList<String> roomWhitelist = new ArrayList<>();
@@ -167,7 +174,12 @@ public class RaidsPlugin extends Plugin
@Getter @Getter
private boolean inRaidChambers; private boolean inRaidChambers;
private RaidsTimer timer; private int upperTime = -1;
private int middleTime = -1;
private int lowerTime = -1;
private int raidTime = -1;
private WidgetOverlay widgetOverlay;
private String tooltip;
@Provides @Provides
RaidsConfig provideConfig(ConfigManager configManager) RaidsConfig provideConfig(ConfigManager configManager)
@@ -188,6 +200,7 @@ public class RaidsPlugin extends Plugin
overlayManager.add(pointsOverlay); overlayManager.add(pointsOverlay);
updateLists(); updateLists();
clientThread.invokeLater(() -> checkRaidPresence(true)); clientThread.invokeLater(() -> checkRaidPresence(true));
widgetOverlay = overlayManager.getWidgetOverlay(WidgetInfo.RAIDS_POINTS_INFOBOX);
} }
@Override @Override
@@ -195,16 +208,16 @@ public class RaidsPlugin extends Plugin
{ {
overlayManager.remove(overlay); overlayManager.remove(overlay);
overlayManager.remove(pointsOverlay); overlayManager.remove(pointsOverlay);
infoBoxManager.removeInfoBox(timer);
inRaidChambers = false; inRaidChambers = false;
widgetOverlay = null;
raid = null; raid = null;
timer = null;
final Widget widget = client.getWidget(WidgetInfo.RAIDS_POINTS_INFOBOX); final Widget widget = client.getWidget(WidgetInfo.RAIDS_POINTS_INFOBOX);
if (widget != null) if (widget != null)
{ {
widget.setHidden(false); widget.setHidden(false);
} }
reset();
} }
@Subscribe @Subscribe
@@ -215,12 +228,6 @@ public class RaidsPlugin extends Plugin
return; return;
} }
if (event.getKey().equals("raidsTimer"))
{
updateInfoBoxState();
return;
}
updateLists(); updateLists();
clientThread.invokeLater(() -> checkRaidPresence(true)); clientThread.invokeLater(() -> checkRaidPresence(true));
} }
@@ -253,25 +260,33 @@ public class RaidsPlugin extends Plugin
if (inRaidChambers && event.getType() == ChatMessageType.FRIENDSCHATNOTIFICATION) if (inRaidChambers && event.getType() == ChatMessageType.FRIENDSCHATNOTIFICATION)
{ {
String message = Text.removeTags(event.getMessage()); String message = Text.removeTags(event.getMessage());
Matcher matcher;
if (config.raidsTimer() && message.startsWith(RAID_START_MESSAGE)) matcher = LEVEL_COMPLETE_REGEX.matcher(message);
if (matcher.find())
{ {
timer = new RaidsTimer(spriteManager.getSprite(TAB_QUESTS_BROWN_RAIDING_PARTY, 0), this, Instant.now()); String floor = matcher.group(1);
infoBoxManager.addInfoBox(timer); int time = timeToSeconds(matcher.group(2));
} if (floor.equals("Upper"))
if (timer != null && message.contains(LEVEL_COMPLETE_MESSAGE))
{
timer.timeFloor();
}
if (message.startsWith(RAID_COMPLETE_MESSAGE))
{
if (timer != null)
{ {
timer.timeOlm(); upperTime = time;
timer.setStopped(true);
} }
else if (floor.equals("Middle"))
{
middleTime = time;
}
else if (floor.equals("Lower"))
{
lowerTime = time;
}
updateTooltip();
}
matcher = RAID_COMPLETE_REGEX.matcher(message);
if (matcher.find())
{
raidTime = timeToSeconds(matcher.group(1));
updateTooltip();
if (config.pointsMessage()) if (config.pointsMessage())
{ {
@@ -306,6 +321,23 @@ public class RaidsPlugin extends Plugin
} }
} }
@Subscribe
public void onClientTick(ClientTick event)
{
if (!config.raidsTimer()
|| !client.getGameState().equals(GameState.LOGGED_IN)
|| tooltip == null)
{
return;
}
final Point mousePosition = client.getMouseCanvasPosition();
if (widgetOverlay.getBounds().contains(mousePosition.getX(), mousePosition.getY()))
{
tooltipManager.add(new Tooltip(tooltip));
}
}
private void checkRaidPresence(boolean force) private void checkRaidPresence(boolean force)
{ {
if (client.getGameState() != GameState.LOGGED_IN) if (client.getGameState() != GameState.LOGGED_IN)
@@ -318,7 +350,6 @@ public class RaidsPlugin extends Plugin
if (force || inRaidChambers != setting) if (force || inRaidChambers != setting)
{ {
inRaidChambers = setting; inRaidChambers = setting;
updateInfoBoxState();
if (inRaidChambers) if (inRaidChambers)
{ {
@@ -343,9 +374,14 @@ public class RaidsPlugin extends Plugin
overlay.setScoutOverlayShown(true); overlay.setScoutOverlayShown(true);
sendRaidLayoutMessage(); sendRaidLayoutMessage();
} }
else if (!config.scoutOverlayAtBank()) else
{ {
overlay.setScoutOverlayShown(false); if (!config.scoutOverlayAtBank())
{
overlay.setScoutOverlayShown(false);
}
reset();
} }
} }
@@ -378,30 +414,6 @@ public class RaidsPlugin extends Plugin
.build()); .build());
} }
private void updateInfoBoxState()
{
if (timer == null)
{
return;
}
if (inRaidChambers && config.raidsTimer())
{
if (!infoBoxManager.getInfoBoxes().contains(timer))
{
infoBoxManager.addInfoBox(timer);
}
}
else
{
infoBoxManager.removeInfoBox(timer);
}
if (!inRaidChambers)
{
timer = null;
}
}
private void updateLists() private void updateLists()
{ {
@@ -700,4 +712,94 @@ public class RaidsPlugin extends Plugin
return room; return room;
} }
public void reset()
{
raid = null;
upperTime = -1;
middleTime = -1;
lowerTime = -1;
raidTime = -1;
tooltip = null;
}
private int timeToSeconds(String s)
{
int seconds = -1;
String[] split = s.split(":");
if (split.length == 2)
{
seconds = Integer.parseInt(split[0]) * 60 + Integer.parseInt(split[1]);
}
if (split.length == 3)
{
seconds = Integer.parseInt(split[0]) * 3600 + Integer.parseInt(split[1]) * 60 + Integer.parseInt(split[2]);
}
return seconds;
}
private String secondsToTime(int seconds)
{
StringBuilder builder = new StringBuilder();
if (seconds >= 3600)
{
builder.append((int)Math.floor(seconds / 3600) + ";");
}
seconds %= 3600;
if (builder.toString().equals(""))
{
builder.append((int)Math.floor(seconds / 60));
}
else
{
builder.append(StringUtils.leftPad(String.valueOf((int)Math.floor(seconds / 60)), 2, '0'));
}
builder.append(":");
seconds %= 60;
builder.append(StringUtils.leftPad(String.valueOf(seconds), 2, '0'));
return builder.toString();
}
private void updateTooltip()
{
StringBuilder builder = new StringBuilder();
if (upperTime == -1)
{
tooltip = null;
return;
}
builder.append("Upper level: " + secondsToTime(upperTime));
if (middleTime == -1)
{
if (lowerTime == -1)
{
tooltip = builder.toString();
return;
}
else
{
builder.append("</br>Lower level: " + secondsToTime(lowerTime - upperTime));
}
}
else
{
builder.append("</br>Middle level: " + secondsToTime(middleTime - upperTime));
if (lowerTime == -1)
{
tooltip = builder.toString();
return;
}
else
{
builder.append("</br>Lower level: " + secondsToTime(lowerTime - middleTime));
}
}
if (raidTime == -1)
{
tooltip = builder.toString();
return;
}
builder.append("</br>Olm: " + secondsToTime(raidTime - lowerTime));
tooltip = builder.toString();
}
} }

View File

@@ -1,150 +0,0 @@
/*
* Copyright (c) 2018, Kamiel
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.client.plugins.raids;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import lombok.Setter;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.ui.overlay.infobox.InfoBox;
public class RaidsTimer extends InfoBox
{
private final Instant startTime;
private Instant floorTime;
private LocalTime time;
private LocalTime firstFloorTime;
private LocalTime secondFloorTime;
private LocalTime thirdFloorTime;
private LocalTime olmTime;
@Setter
private boolean stopped;
public RaidsTimer(BufferedImage image, Plugin plugin, Instant startTime)
{
super(image, plugin);
this.startTime = startTime;
floorTime = startTime;
stopped = false;
}
public void timeFloor()
{
Duration elapsed = Duration.between(floorTime, Instant.now());
if (firstFloorTime == null)
{
firstFloorTime = LocalTime.ofSecondOfDay(elapsed.getSeconds());
}
else if (secondFloorTime == null)
{
secondFloorTime = LocalTime.ofSecondOfDay(elapsed.getSeconds());
}
else if (thirdFloorTime == null)
{
thirdFloorTime = LocalTime.ofSecondOfDay(elapsed.getSeconds());
}
floorTime = Instant.now();
}
public void timeOlm()
{
Duration elapsed = Duration.between(floorTime, Instant.now());
olmTime = LocalTime.ofSecondOfDay(elapsed.getSeconds());
}
@Override
public String getText()
{
if (startTime == null)
{
return "";
}
if (!stopped)
{
Duration elapsed = Duration.between(startTime, Instant.now());
time = LocalTime.ofSecondOfDay(elapsed.getSeconds());
}
if (time.getHour() > 0)
{
return time.format(DateTimeFormatter.ofPattern("HH:mm"));
}
return time.format(DateTimeFormatter.ofPattern("mm:ss"));
}
@Override
public Color getTextColor()
{
if (stopped)
{
return Color.GREEN;
}
return Color.WHITE;
}
@Override
public String getTooltip()
{
StringBuilder builder = new StringBuilder();
builder.append("Elapsed raid time: ");
builder.append(time.format(DateTimeFormatter.ofPattern("HH:mm:ss")));
if (firstFloorTime != null)
{
builder.append("</br>First floor: ");
builder.append(firstFloorTime.format(DateTimeFormatter.ofPattern("mm:ss")));
}
if (secondFloorTime != null)
{
builder.append("</br>Second floor: ");
builder.append(secondFloorTime.format(DateTimeFormatter.ofPattern("mm:ss")));
}
if (thirdFloorTime != null)
{
builder.append("</br>Third floor: ");
builder.append(thirdFloorTime.format(DateTimeFormatter.ofPattern("mm:ss")));
}
if (olmTime != null)
{
builder.append("</br>Olm: ");
builder.append(olmTime.format(DateTimeFormatter.ofPattern("mm:ss")));
}
return builder.toString();
}
}

View File

@@ -42,6 +42,7 @@ import lombok.Getter;
import net.runelite.api.MenuAction; import net.runelite.api.MenuAction;
import net.runelite.api.events.MenuOptionClicked; import net.runelite.api.events.MenuOptionClicked;
import net.runelite.api.widgets.WidgetItem; import net.runelite.api.widgets.WidgetItem;
import net.runelite.api.widgets.WidgetInfo;
import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigGroup;
import net.runelite.client.config.ConfigManager; import net.runelite.client.config.ConfigManager;
import net.runelite.client.config.RuneLiteConfig; import net.runelite.client.config.RuneLiteConfig;
@@ -364,4 +365,21 @@ public class OverlayManager
final String locationKey = overlay.getName() + OVERLAY_CONFIG_PREFERRED_POSITION; final String locationKey = overlay.getName() + OVERLAY_CONFIG_PREFERRED_POSITION;
return configManager.getConfiguration(RUNELITE_CONFIG_GROUP_NAME, locationKey, OverlayPosition.class); return configManager.getConfiguration(RUNELITE_CONFIG_GROUP_NAME, locationKey, OverlayPosition.class);
} }
public WidgetOverlay getWidgetOverlay(final WidgetInfo info)
{
for (Overlay o : overlays)
{
if (o instanceof WidgetOverlay)
{
WidgetOverlay overlay = (WidgetOverlay) o;
if (overlay.getWidgetInfo().equals(info))
{
return overlay;
}
}
}
return null;
}
} }

View File

@@ -32,6 +32,7 @@ import java.util.Collection;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import lombok.Getter;
import net.runelite.api.Client; import net.runelite.api.Client;
import net.runelite.api.widgets.Widget; import net.runelite.api.widgets.Widget;
import net.runelite.api.widgets.WidgetInfo; import net.runelite.api.widgets.WidgetInfo;
@@ -64,6 +65,7 @@ public class WidgetOverlay extends Overlay
} }
private final Client client; private final Client client;
@Getter
private final WidgetInfo widgetInfo; private final WidgetInfo widgetInfo;
private final Rectangle parentBounds = new Rectangle(); private final Rectangle parentBounds = new Rectangle();

View File

@@ -0,0 +1,210 @@
/*
* Copyright (c) 2018, Hydrox6 <ikada@protonmail.ch>
* 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.ui.overlay.arrow;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Collection;
import javax.inject.Inject;
import javax.inject.Singleton;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
import net.runelite.api.GameObject;
import net.runelite.api.NPC;
import net.runelite.api.Perspective;
import net.runelite.api.Point;
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 net.runelite.client.ui.overlay.OverlayPriority;
@Singleton
@Slf4j
public class ArrowMinimapOverlay extends Overlay
{
private static final int MINIMAP_VISIBLE_RANGE = 17 * 128;
private final ArrowPointManager arrowPointManager;
private final Client client;
@Inject
private ArrowMinimapOverlay(Client client, ArrowPointManager arrowPointManager)
{
this.client = client;
this.arrowPointManager = arrowPointManager;
setPosition(OverlayPosition.DYNAMIC);
setPriority(OverlayPriority.HIGH);
setLayer(OverlayLayer.ALWAYS_ON_TOP);
}
@Override
public Dimension render(Graphics2D graphics)
{
final Collection<ArrowPoint> points = arrowPointManager.getArrowPoints().values();
if (points.isEmpty())
{
return null;
}
final LocalPoint localPlayerPos = client.getLocalPlayer().getLocalLocation();
final WorldPoint worldPlayerPos = WorldPoint.fromLocal(client, localPlayerPos);
for (ArrowPoint arrowPoint : points)
{
final WorldPoint worldPoint = arrowPoint.getWorldPoint();
if (worldPoint.distanceTo(worldPlayerPos) < arrowPoint.getVisibleRange())
{
LocalPoint fallBackPoint = LocalPoint.fromWorld(client, worldPoint);
if (arrowPoint.types.contains(ArrowType.NPC))
{
boolean found = false;
for (NPC npc : client.getCachedNPCs())
{
if (npc != null && arrowPoint.getNpcIDs().contains(npc.getId()))
{
found = true;
renderMinimapArrow(graphics, arrowPoint, npc.getLocalLocation(), localPlayerPos, worldPlayerPos);
}
}
if (found || fallBackPoint == null)
{
continue;
}
renderMinimapArrow(graphics, arrowPoint, null, localPlayerPos, worldPlayerPos);
}
else if (arrowPoint.types.contains(ArrowType.OBJECT))
{
ArrayList<GameObject> objects = ArrowUtil.getObjects(client, arrowPoint.getObjectIDs());
if (objects.isEmpty() && fallBackPoint != null)
{
renderMinimapArrow(graphics, arrowPoint, null, localPlayerPos, worldPlayerPos);
continue;
}
for (GameObject object : objects)
{
renderMinimapArrow(graphics, arrowPoint, object.getLocalLocation(), localPlayerPos, worldPlayerPos);
}
}
else if (arrowPoint.types.contains(ArrowType.WORLD_POINT))
{
renderMinimapArrow(graphics, arrowPoint, null, localPlayerPos, worldPlayerPos);
}
}
}
return null;
}
private void renderMinimapArrow(Graphics2D graphics, ArrowPoint arrowPoint, LocalPoint localPoint, LocalPoint localPlayerPos, WorldPoint worldPlayerPos)
{
final BufferedImage minimapImage = arrowPoint.getMinimapImage();
final WorldPoint worldPoint = arrowPoint.getWorldPoint();
final Point minimapImageOffset = arrowPoint.getMinimapImageOffset();
if (localPoint != null && localPlayerPos.distanceTo(localPoint) < MINIMAP_VISIBLE_RANGE)
{
final Point minimapLoc = Perspective.getMiniMapImageLocation(client, localPoint, minimapImage);
if (minimapLoc != null)
{
graphics.drawImage(minimapImage, minimapLoc.getX() + minimapImageOffset.getX(), minimapLoc.getY() + minimapImageOffset.getY(), null);
}
}
else
{
if (!arrowPoint.isMinimapUseFallback())
{
return;
}
final Point minimapPlayerPos = Perspective.localToMinimap(client, localPlayerPos);
double cameraAngle = ((client.getCameraYaw()) / 2048.0) * 2 * Math.PI;
//Use localPoint if it's available for a smoother rotation
double theta;
if (localPoint != null)
{
theta = Math.atan2(localPoint.getX() - localPlayerPos.getX(), localPlayerPos.getY() - localPoint.getY());
}
else
{
theta = Math.atan2(worldPoint.getX() - worldPlayerPos.getX(), worldPlayerPos.getY() - worldPoint.getY());
}
AffineTransform at = new AffineTransform();
if (arrowPoint.isMinimapImagePointToTarget())
{
at.translate(minimapPlayerPos.getX(), minimapPlayerPos.getY());
at.rotate(cameraAngle - theta);
at.translate(0, 66);
at.translate(minimapImageOffset.getX() - minimapImage.getWidth() / 2, minimapImageOffset.getY() - minimapImage.getHeight() / 2);
}
else
{
//Get the correct position as if it were rotated
at.rotate(cameraAngle - theta);
at.translate(0, 66);
double dX = at.getTranslateX();
double dY = at.getTranslateY();
//Then apply that position to an un-rotated transform
at = new AffineTransform();
at.translate(minimapPlayerPos.getX(), minimapPlayerPos.getY());
at.translate(dX, dY);
at.translate(minimapImageOffset.getX() - minimapImage.getWidth() / 2, minimapImageOffset.getY() - minimapImage.getHeight() / 2);
}
graphics.drawImage(minimapImage, at, null);
}
}
private void renderMinimapArrowNPC(Graphics2D graphics, ArrowPoint arrowPoint, NPC npc, LocalPoint localPlayerPos, WorldPoint worldPlayerPos)
{
final BufferedImage minimapImage = arrowPoint.getMinimapImage();
final WorldPoint worldPoint = arrowPoint.getWorldPoint();
LocalPoint localPoint;
if (npc != null)
{
localPoint = LocalPoint.fromWorld(client, worldPoint);
if (localPoint != null)
{
//For whatever reason, LocalPoint.fromWorld returns a point (-1, -1) from the actual point
localPoint = new LocalPoint(localPoint.getX() + 1, localPoint.getY() + 1);
}
}
else
{
localPoint = npc.getLocalLocation();
}
renderMinimapArrow(graphics, arrowPoint, localPoint, localPlayerPos, worldPlayerPos);
}
}

View File

@@ -0,0 +1,85 @@
/*
* Copyright (c) 2018, Morgan Lewis <https://github.com/MESLewis>
* 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.ui.overlay.arrow;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.util.EnumSet;
import java.util.HashSet;
import lombok.Builder;
import lombok.Data;
import lombok.NonNull;
import net.runelite.api.Point;
import net.runelite.api.coords.WorldPoint;
@Data
@Builder
public class ArrowPoint
{
/**
* Can define a point to mark, or be used as a fallback when an object or an NPC is outside of the scene
*/
@NonNull
private WorldPoint worldPoint;
private HashSet<Integer> npcIDs;
private HashSet<Integer> objectIDs;
/**
* The regions that an NPC or Object has to be in to be marked (eg. Exam Centre locked chest Hard Clue requires
* killing a Barbarian in Barbarian Village, or Konar Slayer requires killing monsters in a certain area)
*/
private HashSet<Integer> region;
private BufferedImage worldImage;
@Builder.Default
private Point worldImageOffset = new Point(0, 0);
@Builder.Default
private Color tileColor = Color.RED;
private BufferedImage minimapImage;
@Builder.Default
private Point minimapImageOffset = new Point(0, 0);
/**
* Whether the minimap arrow should rotate. Set to false if not using an arrow
*/
@Builder.Default
private boolean minimapImagePointToTarget = true;
@Builder.Default
private int visibleRange = 128;
/**
* Whether the minimap arrow should use the fallback point. Useful when there are multiple locations that could be
* used as a fallback (eg. Seers' Village locked draw Medium Clue, where you need to kill any chicken for a key)
*/
@Builder.Default
private boolean minimapUseFallback = true;
/**
* A Set of what arrows should be rendered
*/
@NonNull
EnumSet<ArrowType> types;
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright (c) 2018, Morgan Lewis <https://github.com/MESLewis>
* 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.ui.overlay.arrow;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import javax.inject.Singleton;
import lombok.AccessLevel;
import lombok.Getter;
import net.runelite.api.events.NpcSpawned;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.plugins.Plugin;
@Singleton
public class ArrowPointManager
{
@Getter(AccessLevel.PACKAGE)
private final Multimap<Plugin, ArrowPoint> arrowPoints = HashMultimap.create();
public void add(Plugin plugin, ArrowPoint arrowPoint)
{
arrowPoints.put(plugin, arrowPoint);
}
public void remove(Plugin plugin, ArrowPoint arrowPoint)
{
arrowPoints.remove(plugin, arrowPoint);
}
public void clear(Plugin plugin)
{
arrowPoints.removeAll(plugin);
}
public void clear()
{
arrowPoints.clear();
}
}

View File

@@ -0,0 +1,10 @@
package net.runelite.client.ui.overlay.arrow;
public enum ArrowType
{
MINIMAP,
NPC,
OBJECT,
WORLD_MAP,
WORLD_POINT
}

View File

@@ -0,0 +1,50 @@
package net.runelite.client.ui.overlay.arrow;
import java.util.ArrayList;
import java.util.HashSet;
import net.runelite.api.Client;
import net.runelite.api.GameObject;
import net.runelite.api.ObjectComposition;
import net.runelite.api.Scene;
import net.runelite.api.Tile;
class ArrowUtil
{
static ArrayList<GameObject> getObjects(final Client client, HashSet<Integer> objectIDs)
{
final Scene scene = client.getScene();
final Tile[][] tiles = scene.getTiles()[client.getPlane()];
final ArrayList<GameObject> found = new ArrayList<>();
for (Tile[] tiles2 : tiles)
{
for (Tile tile : tiles2)
{
for (GameObject object : tile.getGameObjects())
{
if (object == null)
{
continue;
}
if (objectIDs.contains(object.getId()))
{
found.add(object);
continue;
}
// Check impostors
final ObjectComposition comp = client.getObjectDefinition(object.getId());
final ObjectComposition impostor = comp.getImpostorIds() != null ? comp.getImpostor() : comp;
if (impostor != null && objectIDs.contains(impostor.getId()))
{
found.add(object);
}
}
}
}
return found;
}
}

View File

@@ -0,0 +1,158 @@
/*
* Copyright (c) 2018, Hydrox6 <ikada@protonmail.ch>
* 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.ui.overlay.arrow;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Collection;
import javax.inject.Inject;
import javax.inject.Singleton;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
import net.runelite.api.GameObject;
import net.runelite.api.NPC;
import net.runelite.api.Perspective;
import net.runelite.api.Point;
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 net.runelite.client.ui.overlay.OverlayPriority;
import net.runelite.client.ui.overlay.OverlayUtil;
@Singleton
@Slf4j
public class ArrowWorldOverlay extends Overlay
{
private static final int Z_OFFSET = 20;
private final ArrowPointManager arrowPointManager;
private final Client client;
@Inject
private ArrowWorldOverlay(Client client, ArrowPointManager arrowPointManager)
{
this.client = client;
this.arrowPointManager = arrowPointManager;
setPosition(OverlayPosition.DYNAMIC);
setPriority(OverlayPriority.HIGHEST);
setLayer(OverlayLayer.UNDER_WIDGETS);
}
@Override
public Dimension render(Graphics2D graphics)
{
final Collection<ArrowPoint> points = arrowPointManager.getArrowPoints().values();
if (points.isEmpty())
{
return null;
}
WorldPoint playerLocation = client.getLocalPlayer().getWorldLocation();
for (ArrowPoint arrowPoint : points)
{
WorldPoint point = arrowPoint.getWorldPoint();
if (point.distanceTo(playerLocation) < client.getScene().getDrawDistance())
{
LocalPoint fallBackPoint = LocalPoint.fromWorld(client, point);
if (arrowPoint.types.contains(ArrowType.NPC))
{
boolean found = false;
for (NPC npc : client.getCachedNPCs())
{
if (npc != null && arrowPoint.getNpcIDs().contains(npc.getId()))
{
found = true;
renderWorldArrow(graphics, arrowPoint, npc.getLocalLocation(), npc.getLogicalHeight() + Z_OFFSET);
}
}
if (found || fallBackPoint == null)
{
continue;
}
renderWorldArrow(graphics, arrowPoint, fallBackPoint);
}
else if (arrowPoint.types.contains(ArrowType.OBJECT))
{
ArrayList<GameObject> objects = ArrowUtil.getObjects(client, arrowPoint.getObjectIDs());
if (objects.isEmpty() && fallBackPoint != null)
{
renderWorldArrow(graphics, arrowPoint, fallBackPoint);
continue;
}
for (GameObject object : objects)
{
if (object.getRenderable().getModel() == null)
{
renderWorldArrow(graphics, arrowPoint, object.getLocalLocation(), 0);
}
else
{
renderWorldArrow(graphics, arrowPoint, object.getLocalLocation(), object.getRenderable().getModel().getModelHeight() + Z_OFFSET);
}
}
}
else if (arrowPoint.types.contains(ArrowType.WORLD_POINT))
{
renderWorldArrow(graphics, arrowPoint, fallBackPoint);
}
}
}
return null;
}
private void renderWorldArrow(Graphics2D graphics, ArrowPoint arrowPoint, LocalPoint localPoint)
{
renderWorldArrow(graphics, arrowPoint, localPoint, 0);
}
private void renderWorldArrow(Graphics2D graphics, ArrowPoint arrowPoint, LocalPoint localPoint, int zOffset)
{
final BufferedImage worldImage = arrowPoint.getWorldImage();
//Draw Tile
Polygon poly = Perspective.getCanvasTilePoly(client, localPoint);
if (poly != null)
{
OverlayUtil.renderPolygon(graphics, poly, arrowPoint.getTileColor());
}
Point worldIconOffset = arrowPoint.getWorldImageOffset();
//Draw World Arrow
Point canvasPoint = Perspective.getCanvasImageLocation(client, localPoint, worldImage, zOffset);
if (canvasPoint != null)
{
graphics.drawImage(worldImage, canvasPoint.getX() + worldIconOffset.getX(), canvasPoint.getY() + worldIconOffset.getY(), null);
}
}
}

View File

@@ -28,6 +28,7 @@ import com.google.inject.Guice;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.testing.fieldbinder.Bind; import com.google.inject.testing.fieldbinder.Bind;
import com.google.inject.testing.fieldbinder.BoundFieldModule; import com.google.inject.testing.fieldbinder.BoundFieldModule;
import java.util.EnumSet;
import net.runelite.api.AnimationID; import net.runelite.api.AnimationID;
import net.runelite.api.Client; import net.runelite.api.Client;
import net.runelite.api.GameState; import net.runelite.api.GameState;
@@ -36,6 +37,7 @@ import net.runelite.api.NPC;
import net.runelite.api.NPCComposition; import net.runelite.api.NPCComposition;
import net.runelite.api.Player; import net.runelite.api.Player;
import net.runelite.api.VarPlayer; import net.runelite.api.VarPlayer;
import net.runelite.api.WorldType;
import net.runelite.api.events.AnimationChanged; import net.runelite.api.events.AnimationChanged;
import net.runelite.api.events.GameStateChanged; import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.GameTick; import net.runelite.api.events.GameTick;
@@ -118,6 +120,7 @@ public class IdleNotifierPluginTest
when(client.getGameState()).thenReturn(GameState.LOGGED_IN); when(client.getGameState()).thenReturn(GameState.LOGGED_IN);
when(client.getKeyboardIdleTicks()).thenReturn(42); when(client.getKeyboardIdleTicks()).thenReturn(42);
when(client.getMouseLastPressedMillis()).thenReturn(System.currentTimeMillis() - 100_000L); when(client.getMouseLastPressedMillis()).thenReturn(System.currentTimeMillis() - 100_000L);
when(client.getWorldType()).thenReturn(EnumSet.of(WorldType.DEADMAN));
} }
@Test @Test