diff --git a/runelite-api/src/main/java/net/runelite/api/Quest.java b/runelite-api/src/main/java/net/runelite/api/Quest.java index a1489a0ba8..2a2d5d13fd 100644 --- a/runelite-api/src/main/java/net/runelite/api/Quest.java +++ b/runelite-api/src/main/java/net/runelite/api/Quest.java @@ -25,169 +25,168 @@ package net.runelite.api; import lombok.Getter; -import lombok.RequiredArgsConstructor; -@RequiredArgsConstructor public enum Quest { //Free Quests - BLACK_KNIGHTS_FORTRESS(299, "Black Knights' Fortress"), - COOKS_ASSISTANT(300, "Cook's Assistant"), - THE_CORSAIR_CURSE(301, "The Corsair Curse"), - DEMON_SLAYER(302, "Demon Slayer"), - DORICS_QUEST(303, "Doric's Quest"), - DRAGON_SLAYER(304, "Dragon Slayer"), - ERNEST_THE_CHICKEN(305, "Ernest the Chicken"), - GOBLIN_DIPLOMACY(306, "Goblin Diplomacy"), - IMP_CATCHER(307, "Imp Catcher"), - THE_KNIGHTS_SWORD(308, "The Knight's Sword"), - MISTHALIN_MYSTERY(309, "Misthalin Mystery"), - PIRATES_TREASURE(310, "Pirate's Treasure"), - PRINCE_ALI_RESCUE(311, "Prince Ali Rescue"), - THE_RESTLESS_GHOST(312, "The Restless Ghost"), - ROMEO__JULIET(313, "Romeo & Juliet"), - RUNE_MYSTERIES(314, "Rune Mysteries"), - SHEEP_SHEARER(315, "Sheep Shearer"), - SHIELD_OF_ARRAV(316, "Shield of Arrav"), - VAMPIRE_SLAYER(317, "Vampire Slayer"), - WITCHS_POTION(318, "Witch's Potion"), + BLACK_KNIGHTS_FORTRESS(299, "Black Knights' Fortress", VarPlayer.QUEST_BLACK_KNIGHTS_FORTRESS), + COOKS_ASSISTANT(300, "Cook's Assistant", VarPlayer.QUEST_COOKS_ASSISTANT), + THE_CORSAIR_CURSE(301, "The Corsair Curse", Varbits.QUEST_THE_CORSAIR_CURSE), + DEMON_SLAYER(302, "Demon Slayer", Varbits.QUEST_DEMON_SLAYER), + DORICS_QUEST(303, "Doric's Quest", VarPlayer.QUEST_DORICS_QUEST), + DRAGON_SLAYER(304, "Dragon Slayer", VarPlayer.QUEST_DRAGON_SLAYER), + ERNEST_THE_CHICKEN(305, "Ernest the Chicken", VarPlayer.QUEST_ERNEST_THE_CHICKEN), + GOBLIN_DIPLOMACY(306, "Goblin Diplomacy", Varbits.QUEST_GOBLIN_DIPLOMACY), + IMP_CATCHER(307, "Imp Catcher", VarPlayer.QUEST_IMP_CATCHER), + THE_KNIGHTS_SWORD(308, "The Knight's Sword", VarPlayer.QUEST_THE_KNIGHTS_SWORD), + MISTHALIN_MYSTERY(309, "Misthalin Mystery", Varbits.QUEST_MISTHALIN_MYSTERY), + PIRATES_TREASURE(310, "Pirate's Treasure", VarPlayer.QUEST_PIRATES_TREASURE), + PRINCE_ALI_RESCUE(311, "Prince Ali Rescue", VarPlayer.QUEST_PRINCE_ALI_RESCUE), + THE_RESTLESS_GHOST(312, "The Restless Ghost", VarPlayer.QUEST_THE_RESTLESS_GHOST), + ROMEO__JULIET(313, "Romeo & Juliet", VarPlayer.QUEST_ROMEO_AND_JULIET), + RUNE_MYSTERIES(314, "Rune Mysteries", VarPlayer.QUEST_RUNE_MYSTERIES), + SHEEP_SHEARER(315, "Sheep Shearer", VarPlayer.QUEST_SHEEP_SHEARER), + SHIELD_OF_ARRAV(316, "Shield of Arrav", VarPlayer.QUEST_SHIELD_OF_ARRAV), + VAMPIRE_SLAYER(317, "Vampire Slayer", VarPlayer.QUEST_VAMPIRE_SLAYER), + 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 - ANIMAL_MAGNETISM(331, "Animal Magnetism"), - ANOTHER_SLICE_OF_HAM(332, "Another Slice of H.A.M."), - BETWEEN_A_ROCK(333, "Between a Rock..."), - BIG_CHOMPY_BIRD_HUNTING(334, "Big Chompy Bird Hunting"), - BIOHAZARD(335, "Biohazard"), - CABIN_FEVER(336, "Cabin Fever"), - CLOCK_TOWER(337, "Clock Tower"), - COLD_WAR(338, "Cold War"), - CONTACT(339, "Contact!"), - CREATURE_OF_FENKENSTRAIN(340, "Creature of Fenkenstrain"), - DARKNESS_OF_HALLOWVALE(341, "Darkness of Hallowvale"), - DEATH_PLATEAU(342, "Death Plateau"), - DEATH_TO_THE_DORGESHUUN(343, "Death to the Dorgeshuun"), - THE_DEPTHS_OF_DESPAIR(344, "The Depths of Despair"), - DESERT_TREASURE(345, "Desert Treasure"), - DEVIOUS_MINDS(346, "Devious Minds"), - THE_DIG_SITE(347, "The Dig Site"), - DRAGON_SLAYER_II(348, "Dragon Slayer II"), - DREAM_MENTOR(349, "Dream Mentor"), - DRUIDIC_RITUAL(350, "Druidic Ritual"), - DWARF_CANNON(351, "Dwarf Cannon"), - EADGARS_RUSE(352, "Eadgar's Ruse"), - EAGLES_PEAK(353, "Eagles' Peak"), - ELEMENTAL_WORKSHOP_I(354, "Elemental Workshop I"), - ELEMENTAL_WORKSHOP_II(355, "Elemental Workshop II"), - ENAKHRAS_LAMENT(356, "Enakhra's Lament"), - ENLIGHTENED_JOURNEY(357, "Enlightened Journey"), - THE_EYES_OF_GLOUPHRIE(358, "The Eyes of Glouphrie"), - FAIRYTALE_I__GROWING_PAINS(359, "Fairytale I - Growing Pains"), - FAIRYTALE_II__CURE_A_QUEEN(360, "Fairytale II - Cure a Queen"), - FAMILY_CREST(361, "Family Crest"), - THE_FEUD(362, "The Feud"), - FIGHT_ARENA(363, "Fight Arena"), - FISHING_CONTEST(364, "Fishing Contest"), - FORGETTABLE_TALE(365, "Forgettable Tale..."), - BONE_VOYAGE(366, "Bone Voyage"), - THE_FREMENNIK_ISLES(367, "The Fremennik Isles"), - THE_FREMENNIK_TRIALS(368, "The Fremennik Trials"), - GARDEN_OF_TRANQUILLITY(369, "Garden of Tranquillity"), - GERTRUDES_CAT(370, "Gertrude's Cat"), - GHOSTS_AHOY(371, "Ghosts Ahoy"), - THE_GIANT_DWARF(372, "The Giant Dwarf"), - THE_GOLEM(373, "The Golem"), - THE_GRAND_TREE(374, "The Grand Tree"), - THE_GREAT_BRAIN_ROBBERY(375, "The Great Brain Robbery"), - GRIM_TALES(376, "Grim Tales"), - THE_HAND_IN_THE_SAND(377, "The Hand in the Sand"), - HAUNTED_MINE(378, "Haunted Mine"), - HAZEEL_CULT(379, "Hazeel Cult"), - HEROES_QUEST(380, "Heroes' Quest"), - HOLY_GRAIL(381, "Holy Grail"), - HORROR_FROM_THE_DEEP(382, "Horror from the Deep"), - ICTHLARINS_LITTLE_HELPER(383, "Icthlarin's Little Helper"), - IN_AID_OF_THE_MYREQUE(384, "In Aid of the Myreque"), - IN_SEARCH_OF_THE_MYREQUE(385, "In Search of the Myreque"), - JUNGLE_POTION(386, "Jungle Potion"), - KINGS_RANSOM(387, "King's Ransom"), - LEGENDS_QUEST(388, "Legends' Quest"), - LOST_CITY(389, "Lost City"), - THE_LOST_TRIBE(390, "The Lost Tribe"), - LUNAR_DIPLOMACY(391, "Lunar Diplomacy"), - MAKING_FRIENDS_WITH_MY_ARM(392, "Making Friends with My Arm"), - MAKING_HISTORY(393, "Making History"), - MERLINS_CRYSTAL(394, "Merlin's Crystal"), - MONKEY_MADNESS_I(395, "Monkey Madness I"), - MONKEY_MADNESS_II(396, "Monkey Madness II"), - MONKS_FRIEND(397, "Monk's Friend"), - MOUNTAIN_DAUGHTER(398, "Mountain Daughter"), - MOURNINGS_ENDS_PART_I(399, "Mourning's Ends Part I"), - MOURNINGS_ENDS_PART_II(400, "Mourning's Ends Part II"), - MURDER_MYSTERY(401, "Murder Mystery"), - MY_ARMS_BIG_ADVENTURE(402, "My Arm's Big Adventure"), - NATURE_SPIRIT(403, "Nature Spirit"), - OBSERVATORY_QUEST(404, "Observatory Quest"), - OLAFS_QUEST(405, "Olaf's Quest"), - ONE_SMALL_FAVOUR(406, "One Small Favour"), - PLAGUE_CITY(407, "Plague City"), - PRIEST_IN_PERIL(408, "Priest in Peril"), - THE_QUEEN_OF_THIEVES(409, "The Queen of Thieves"), - RAG_AND_BONE_MAN(410, "Rag and Bone Man"), - RAG_AND_BONE_MAN_II(411, "Rag and Bone Man II"), - RATCATCHERS(412, "Ratcatchers"), - RECIPE_FOR_DISASTER(413, "Recipe for Disaster"), - RECRUITMENT_DRIVE(414, "Recruitment Drive"), - REGICIDE(415, "Regicide"), - ROVING_ELVES(416, "Roving Elves"), - ROYAL_TROUBLE(417, "Royal Trouble"), - RUM_DEAL(418, "Rum Deal"), - SCORPION_CATCHER(419, "Scorpion Catcher"), - SEA_SLUG(420, "Sea Slug"), - SHADES_OF_MORTTON(421, "Shades of Mort'ton"), - SHADOW_OF_THE_STORM(422, "Shadow of the Storm"), - SHEEP_HERDER(423, "Sheep Herder"), - SHILO_VILLAGE(424, "Shilo Village"), - THE_SLUG_MENACE(425, "The Slug Menace"), - A_SOULS_BANE(426, "A Soul's Bane"), - SPIRITS_OF_THE_ELID(427, "Spirits of the Elid"), - SWAN_SONG(428, "Swan Song"), - TAI_BWO_WANNAI_TRIO(429, "Tai Bwo Wannai Trio"), - A_TAIL_OF_TWO_CATS(430, "A Tail of Two Cats"), - TALE_OF_THE_RIGHTEOUS(431, "Tale of the Righteous"), - A_TASTE_OF_HOPE(432, "A Taste of Hope"), - TEARS_OF_GUTHIX(433, "Tears of Guthix"), - TEMPLE_OF_IKOV(434, "Temple of Ikov"), - THRONE_OF_MISCELLANIA(435, "Throne of Miscellania"), - THE_TOURIST_TRAP(436, "The Tourist Trap"), - TOWER_OF_LIFE(437, "Tower of Life"), - TREE_GNOME_VILLAGE(438, "Tree Gnome Village"), - TRIBAL_TOTEM(439, "Tribal Totem"), - TROLL_ROMANCE(440, "Troll Romance"), - TROLL_STRONGHOLD(441, "Troll Stronghold"), - UNDERGROUND_PASS(442, "Underground Pass"), - CLIENT_OF_KOUREND(443, "Client of Kourend"), - WANTED(444, "Wanted!"), - WATCHTOWER(445, "Watchtower"), - WATERFALL_QUEST(446, "Waterfall Quest"), - WHAT_LIES_BELOW(447, "What Lies Below"), - WITCHS_HOUSE(448, "Witch's House"), - ZOGRE_FLESH_EATERS(449, "Zogre Flesh Eaters"), - THE_ASCENT_OF_ARCEUUS(542, "The Ascent of Arceuus"), - THE_FORSAKEN_TOWER(543, "The Forsaken Tower"), + ANIMAL_MAGNETISM(331, "Animal Magnetism", Varbits.QUEST_ANIMAL_MAGNETISM), + ANOTHER_SLICE_OF_HAM(332, "Another Slice of H.A.M.", Varbits.QUEST_ANOTHER_SLICE_OF_HAM), + BETWEEN_A_ROCK(333, "Between a Rock...", Varbits.QUEST_BETWEEN_A_ROCK), + BIG_CHOMPY_BIRD_HUNTING(334, "Big Chompy Bird Hunting", VarPlayer.QUEST_BIG_CHOMPY_BIRD_HUNTING), + BIOHAZARD(335, "Biohazard", VarPlayer.QUEST_BIOHAZARD), + CABIN_FEVER(336, "Cabin Fever", VarPlayer.QUEST_CABIN_FEVER), + CLOCK_TOWER(337, "Clock Tower", VarPlayer.QUEST_CLOCK_TOWER), + COLD_WAR(338, "Cold War", Varbits.QUEST_COLD_WAR), + CONTACT(339, "Contact!", Varbits.QUEST_CONTACT), + CREATURE_OF_FENKENSTRAIN(340, "Creature of Fenkenstrain", VarPlayer.QUEST_CREATURE_OF_FENKENSTRAIN), + DARKNESS_OF_HALLOWVALE(341, "Darkness of Hallowvale", Varbits.QUEST_DARKNESS_OF_HALLOWVALE), + DEATH_PLATEAU(342, "Death Plateau", VarPlayer.QUEST_DEATH_PLATEAU), + DEATH_TO_THE_DORGESHUUN(343, "Death to the Dorgeshuun", Varbits.QUEST_DEATH_TO_THE_DORGESHUUN), + THE_DEPTHS_OF_DESPAIR(344, "The Depths of Despair", Varbits.QUEST_THE_DEPTHS_OF_DESPAIR), + DESERT_TREASURE(345, "Desert Treasure", Varbits.QUEST_DESERT_TREASURE), + DEVIOUS_MINDS(346, "Devious Minds", Varbits.QUEST_DEVIOUS_MINDS), + THE_DIG_SITE(347, "The Dig Site", VarPlayer.QUEST_THE_DIG_SITE), + DRAGON_SLAYER_II(348, "Dragon Slayer II", Varbits.QUEST_DRAGON_SLAYER_II), + DREAM_MENTOR(349, "Dream Mentor", Varbits.QUEST_DREAM_MENTOR), + DRUIDIC_RITUAL(350, "Druidic Ritual", VarPlayer.QUEST_DRUIDIC_RITUAL), + DWARF_CANNON(351, "Dwarf Cannon", Varbits.QUEST_THE_GIANT_DWARF), + EADGARS_RUSE(352, "Eadgar's Ruse", VarPlayer.QUEST_EADGARS_RUSE), + EAGLES_PEAK(353, "Eagles' Peak", Varbits.QUEST_EAGLES_PEAK), + ELEMENTAL_WORKSHOP_I(354, "Elemental Workshop I", VarPlayer.QUEST_ELEMENTAL_WORKSHOP_I), + ELEMENTAL_WORKSHOP_II(355, "Elemental Workshop II", Varbits.QUEST_ELEMENTAL_WORKSHOP_II), + ENAKHRAS_LAMENT(356, "Enakhra's Lament", Varbits.QUEST_ENAKHRAS_LAMENT), + ENLIGHTENED_JOURNEY(357, "Enlightened Journey", Varbits.QUEST_ENLIGHTENED_JOURNEY), + THE_EYES_OF_GLOUPHRIE(358, "The Eyes of Glouphrie", Varbits.QUEST_THE_EYES_OF_GLOUPHRIE), + 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", Varbits.QUEST_FAIRYTALE_II_CURE_A_QUEEN), + FAMILY_CREST(361, "Family Crest", VarPlayer.QUEST_FAMILY_CREST), + THE_FEUD(362, "The Feud", Varbits.QUEST_THE_FEUD), + FIGHT_ARENA(363, "Fight Arena", VarPlayer.QUEST_FIGHT_ARENA), + FISHING_CONTEST(364, "Fishing Contest", VarPlayer.QUEST_FISHING_CONTEST), + FORGETTABLE_TALE(365, "Forgettable Tale...", Varbits.QUEST_FORGETTABLE_TALE), + BONE_VOYAGE(366, "Bone Voyage", Varbits.QUEST_BONE_VOYAGE), + THE_FREMENNIK_ISLES(367, "The Fremennik Isles", Varbits.QUEST_THE_FREMENNIK_ISLES), + THE_FREMENNIK_TRIALS(368, "The Fremennik Trials", VarPlayer.QUEST_THE_FREMENNIK_TRIALS), + GARDEN_OF_TRANQUILLITY(369, "Garden of Tranquillity", Varbits.QUEST_GARDEN_OF_TRANQUILLITY), + GERTRUDES_CAT(370, "Gertrude's Cat", VarPlayer.QUEST_GERTRUDES_CAT), + GHOSTS_AHOY(371, "Ghosts Ahoy", Varbits.QUEST_GHOSTS_AHOY), + THE_GIANT_DWARF(372, "The Giant Dwarf", Varbits.QUEST_THE_GIANT_DWARF), + THE_GOLEM(373, "The Golem", Varbits.QUEST_THE_GOLEM), + THE_GRAND_TREE(374, "The Grand Tree", VarPlayer.QUEST_THE_GRAND_TREE), + THE_GREAT_BRAIN_ROBBERY(375, "The Great Brain Robbery", VarPlayer.QUEST_THE_GREAT_BRAIN_ROBBERY), + GRIM_TALES(376, "Grim Tales", Varbits.QUEST_GRIM_TALES), + THE_HAND_IN_THE_SAND(377, "The Hand in the Sand", Varbits.QUEST_THE_HAND_IN_THE_SAND), + HAUNTED_MINE(378, "Haunted Mine", VarPlayer.QUEST_HAUNTED_MINE), + HAZEEL_CULT(379, "Hazeel Cult", VarPlayer.QUEST_HAZEEL_CULT), + HEROES_QUEST(380, "Heroes' Quest", VarPlayer.QUEST_HEROES_QUEST), + HOLY_GRAIL(381, "Holy Grail", VarPlayer.QUEST_HOLY_GRAIL), + HORROR_FROM_THE_DEEP(382, "Horror from the Deep", Varbits.QUEST_HORROR_FROM_THE_DEEP), + ICTHLARINS_LITTLE_HELPER(383, "Icthlarin's Little Helper", Varbits.QUEST_ICTHLARINS_LITTLE_HELPER), + 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", VarPlayer.QUEST_IN_SEARCH_OF_THE_MYREQUE), + JUNGLE_POTION(386, "Jungle Potion", VarPlayer.QUEST_JUNGLE_POTION), + KINGS_RANSOM(387, "King's Ransom", Varbits.QUEST_KINGS_RANSOM), + LEGENDS_QUEST(388, "Legends' Quest", VarPlayer.QUEST_LEGENDS_QUEST), + LOST_CITY(389, "Lost City", VarPlayer.QUEST_LOST_CITY), + THE_LOST_TRIBE(390, "The Lost Tribe", Varbits.QUEST_THE_LOST_TRIBE), + LUNAR_DIPLOMACY(391, "Lunar Diplomacy", Varbits.QUEST_LUNAR_DIPLOMACY), + MAKING_FRIENDS_WITH_MY_ARM(392, "Making Friends with My Arm", Varbits.QUEST_MAKING_FRIENDS_WITH_MY_ARM), + MAKING_HISTORY(393, "Making History", Varbits.QUEST_MAKING_HISTORY), + MERLINS_CRYSTAL(394, "Merlin's Crystal", VarPlayer.QUEST_MERLINS_CRYSTAL), + MONKEY_MADNESS_I(395, "Monkey Madness I", VarPlayer.QUEST_MONKEY_MADNESS_I), + MONKEY_MADNESS_II(396, "Monkey Madness II", Varbits.QUEST_MONKEY_MADNESS_II), + MONKS_FRIEND(397, "Monk's Friend", VarPlayer.QUEST_MONKS_FRIEND), + MOUNTAIN_DAUGHTER(398, "Mountain Daughter", Varbits.QUEST_MOUNTAIN_DAUGHTER), + 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", Varbits.QUEST_MOURNINGS_ENDS_PART_II), + MURDER_MYSTERY(401, "Murder Mystery", VarPlayer.QUEST_MURDER_MYSTERY), + MY_ARMS_BIG_ADVENTURE(402, "My Arm's Big Adventure", Varbits.QUEST_MY_ARMS_BIG_ADVENTURE), + NATURE_SPIRIT(403, "Nature Spirit", VarPlayer.QUEST_NATURE_SPIRIT), + OBSERVATORY_QUEST(404, "Observatory Quest", VarPlayer.QUEST_OBSERVATORY_QUEST), + OLAFS_QUEST(405, "Olaf's Quest", Varbits.QUEST_OLAFS_QUEST), + ONE_SMALL_FAVOUR(406, "One Small Favour", VarPlayer.QUEST_ONE_SMALL_FAVOUR), + PLAGUE_CITY(407, "Plague City", VarPlayer.QUEST_PLAGUE_CITY), + PRIEST_IN_PERIL(408, "Priest in Peril", VarPlayer.QUEST_PRIEST_IN_PERIL), + THE_QUEEN_OF_THIEVES(409, "The Queen of Thieves", Varbits.QUEST_THE_QUEEN_OF_THIEVES), + 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", VarPlayer.QUEST_RAG_AND_BONE_MAN_II), + RATCATCHERS(412, "Ratcatchers", Varbits.QUEST_RATCATCHERS), + RECIPE_FOR_DISASTER(413, "Recipe for Disaster", Varbits.QUEST_RECIPE_FOR_DISASTER), + RECRUITMENT_DRIVE(414, "Recruitment Drive", Varbits.QUEST_RECRUITMENT_DRIVE), + REGICIDE(415, "Regicide", VarPlayer.QUEST_REGICIDE), + ROVING_ELVES(416, "Roving Elves", VarPlayer.QUEST_ROVING_ELVES), + ROYAL_TROUBLE(417, "Royal Trouble", Varbits.QUEST_ROYAL_TROUBLE), + RUM_DEAL(418, "Rum Deal", VarPlayer.QUEST_RUM_DEAL), + SCORPION_CATCHER(419, "Scorpion Catcher", VarPlayer.QUEST_SCORPION_CATCHER), + SEA_SLUG(420, "Sea Slug", VarPlayer.QUEST_SEA_SLUG), + SHADES_OF_MORTTON(421, "Shades of Mort'ton", VarPlayer.QUEST_SHADES_OF_MORTTON), + SHADOW_OF_THE_STORM(422, "Shadow of the Storm", Varbits.QUEST_SHADOW_OF_THE_STORM), + SHEEP_HERDER(423, "Sheep Herder", VarPlayer.QUEST_SHEEP_HERDER), + SHILO_VILLAGE(424, "Shilo Village", VarPlayer.QUEST_SHILO_VILLAGE), + THE_SLUG_MENACE(425, "The Slug Menace", Varbits.QUEST_THE_SLUG_MENACE), + A_SOULS_BANE(426, "A Soul's Bane", Varbits.QUEST_A_SOULS_BANE), + SPIRITS_OF_THE_ELID(427, "Spirits of the Elid", Varbits.QUEST_SPIRITS_OF_THE_ELID), + SWAN_SONG(428, "Swan Song", Varbits.QUEST_SWAN_SONG), + 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", Varbits.QUEST_A_TAIL_OF_TWO_CATS), + TALE_OF_THE_RIGHTEOUS(431, "Tale of the Righteous", Varbits.QUEST_TALE_OF_THE_RIGHTEOUS), + A_TASTE_OF_HOPE(432, "A Taste of Hope", Varbits.QUEST_A_TASTE_OF_HOPE), + TEARS_OF_GUTHIX(433, "Tears of Guthix", Varbits.QUEST_TEARS_OF_GUTHIX), + TEMPLE_OF_IKOV(434, "Temple of Ikov", VarPlayer.QUEST_TEMPLE_OF_IKOV), + THRONE_OF_MISCELLANIA(435, "Throne of Miscellania", VarPlayer.QUEST_THRONE_OF_MISCELLANIA), + THE_TOURIST_TRAP(436, "The Tourist Trap", VarPlayer.QUEST_THE_TOURIST_TRAP), + TOWER_OF_LIFE(437, "Tower of Life", Varbits.QUEST_TOWER_OF_LIFE), + TREE_GNOME_VILLAGE(438, "Tree Gnome Village", VarPlayer.QUEST_TREE_GNOME_VILLAGE), + TRIBAL_TOTEM(439, "Tribal Totem", VarPlayer.QUEST_TRIBAL_TOTEM), + TROLL_ROMANCE(440, "Troll Romance", VarPlayer.QUEST_TROLL_ROMANCE), + TROLL_STRONGHOLD(441, "Troll Stronghold", VarPlayer.QUEST_TROLL_STRONGHOLD), + UNDERGROUND_PASS(442, "Underground Pass", VarPlayer.QUEST_UNDERGROUND_PASS), + CLIENT_OF_KOUREND(443, "Client of Kourend", Varbits.QUEST_CLIENT_OF_KOUREND), + WANTED(444, "Wanted!", Varbits.QUEST_WANTED), + WATCHTOWER(445, "Watchtower", VarPlayer.QUEST_WATCHTOWER), + WATERFALL_QUEST(446, "Waterfall Quest", VarPlayer.QUEST_WATERFALL_QUEST), + WHAT_LIES_BELOW(447, "What Lies Below", Varbits.QUEST_WHAT_LIES_BELOW), + WITCHS_HOUSE(448, "Witch's House", VarPlayer.QUEST_WITCHS_HOUSE), + ZOGRE_FLESH_EATERS(449, "Zogre Flesh Eaters", Varbits.QUEST_ZOGRE_FLESH_EATERS), + THE_ASCENT_OF_ARCEUUS(542, "The Ascent of Arceuus", Varbits.QUEST_THE_ASCENT_OF_ARCEUUS), + THE_FORSAKEN_TOWER(543, "The Forsaken Tower", Varbits.QUEST_THE_FORSAKEN_TOWER), //Miniquests - ENTER_THE_ABYSS(319, "Enter the Abyss"), - ARCHITECTURAL_ALLIANCE(320, "Architectural Alliance"), - BEAR_YOUR_SOUL(321, "Bear your Soul"), - ALFRED_GRIMHANDS_BARCRAWL(322, "Alfred Grimhand's Barcrawl"), - CURSE_OF_THE_EMPTY_LORD(323, "Curse of the Empty Lord"), - ENCHANTED_KEY(324, "Enchanted Key"), - THE_GENERALS_SHADOW(325, "The General's Shadow"), - SKIPPY_AND_THE_MOGRES(326, "Skippy and the Mogres"), - THE_MAGE_ARENA(327, "The Mage Arena"), - LAIR_OF_TARN_RAZORLOR(328, "Lair of Tarn Razorlor"), - FAMILY_PEST(329, "Family Pest"), - THE_MAGE_ARENA_II(330, "The Mage Arena II"); + ENTER_THE_ABYSS(319, "Enter the Abyss", VarPlayer.QUEST_ENTER_THE_ABYSS), + ARCHITECTURAL_ALLIANCE(320, "Architectural Alliance", Varbits.QUEST_ARCHITECTURAL_ALLIANCE), + BEAR_YOUR_SOUL(321, "Bear your Soul", Varbits.QUEST_BEAR_YOUR_SOUL), + ALFRED_GRIMHANDS_BARCRAWL(322, "Alfred Grimhand's Barcrawl", VarPlayer.QUEST_ALFRED_GRIMHANDS_BARCRAWL), + CURSE_OF_THE_EMPTY_LORD(323, "Curse of the Empty Lord", Varbits.QUEST_CURSE_OF_THE_EMPTY_LORD), + ENCHANTED_KEY(324, "Enchanted Key", Varbits.QUEST_ENCHANTED_KEY), + THE_GENERALS_SHADOW(325, "The General's Shadow", Varbits.QUEST_THE_GENERALS_SHADOW), + SKIPPY_AND_THE_MOGRES(326, "Skippy and the Mogres", Varbits.QUEST_SKIPPY_AND_THE_MOGRES), + THE_MAGE_ARENA(327, "The Mage Arena", VarPlayer.QUEST_THE_MAGE_ARENA), + LAIR_OF_TARN_RAZORLOR(328, "Lair of Tarn Razorlor", Varbits.QUEST_LAIR_OF_TARN_RAZORLOR), + FAMILY_PEST(329, "Family Pest", Varbits.QUEST_FAMILY_PEST), + THE_MAGE_ARENA_II(330, "The Mage Arena II", Varbits.QUEST_THE_MAGE_ARENA_II); @Getter private final int id; @@ -195,6 +194,26 @@ public enum Quest @Getter 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) { client.runScript(ScriptID.QUESTLIST_PROGRESS, id); @@ -208,4 +227,16 @@ public enum Quest return QuestState.IN_PROGRESS; } } + + public int getVar(Client client) + { + if (varbit != null) + { + return client.getVar(varbit); + } + else + { + return client.getVar(varPlayer); + } + } } diff --git a/runelite-api/src/main/java/net/runelite/api/VarPlayer.java b/runelite-api/src/main/java/net/runelite/api/VarPlayer.java index 36ff1a18f2..ccee29f4b8 100644 --- a/runelite-api/src/main/java/net/runelite/api/VarPlayer.java +++ b/runelite-api/src/main/java/net/runelite/api/VarPlayer.java @@ -163,7 +163,101 @@ public enum VarPlayer MUSIC_TRACKS_UNLOCKED_16(1009), MUSIC_TRACKS_UNLOCKED_17(1338), 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; } diff --git a/runelite-api/src/main/java/net/runelite/api/Varbits.java b/runelite-api/src/main/java/net/runelite/api/Varbits.java index f7223a237d..711f4c53e1 100644 --- a/runelite-api/src/main/java/net/runelite/api/Varbits.java +++ b/runelite-api/src/main/java/net/runelite/api/Varbits.java @@ -503,7 +503,95 @@ public enum Varbits TREK_STARTED(1956), TREK_EVENT(1958), 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. diff --git a/runelite-client/src/main/java/net/runelite/client/RuneLite.java b/runelite-client/src/main/java/net/runelite/client/RuneLite.java index 9aa7760168..b45e4ca0e4 100644 --- a/runelite-client/src/main/java/net/runelite/client/RuneLite.java +++ b/runelite-client/src/main/java/net/runelite/client/RuneLite.java @@ -66,6 +66,8 @@ import net.runelite.client.ui.RuneLiteSplashScreen; import net.runelite.client.ui.overlay.OverlayManager; import net.runelite.client.ui.overlay.OverlayRenderer; 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.InfoBoxOverlay; import net.runelite.client.ui.overlay.tooltip.TooltipOverlay; @@ -148,6 +150,12 @@ public class RuneLite @Inject private Provider worldMapOverlay; + @Inject + private Provider arrowWorldOverlay; + + @Inject + private Provider arrowMinimapOverlay; + @Inject private Provider lootManager; @@ -323,6 +331,8 @@ public class RuneLite overlayManager.add(infoBoxOverlay.get()); overlayManager.add(worldMapOverlay.get()); overlayManager.add(tooltipOverlay.get()); + overlayManager.add(arrowWorldOverlay.get()); + overlayManager.add(arrowMinimapOverlay.get()); } // Start plugins diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/example/ExamplePlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/example/ExamplePlugin.java new file mode 100644 index 0000000000..a608699885 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/example/ExamplePlugin.java @@ -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 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 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 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); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifierConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifierConfig.java index ff1337b73f..93efd764bc 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifierConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifierConfig.java @@ -64,11 +64,33 @@ public interface IdleNotifierConfig extends Config 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( keyName = "timeout", name = "Idle Notification Delay (ms)", description = "The notification delay after the player is idle", - position = 4 + position = 6 ) default int getIdleNotificationDelay() { @@ -79,7 +101,7 @@ public interface IdleNotifierConfig extends Config keyName = "hitpoints", name = "Hitpoints Notification Threshold", description = "The amount of hitpoints to send a notification at. A value of 0 will disable notification.", - position = 5 + position = 7 ) default int getHitpointsThreshold() { @@ -90,7 +112,7 @@ public interface IdleNotifierConfig extends Config keyName = "prayer", name = "Prayer Notification Threshold", 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() { @@ -100,7 +122,7 @@ public interface IdleNotifierConfig extends Config @ConfigItem( keyName = "oxygen", 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." ) default int getOxygenThreshold() @@ -111,7 +133,7 @@ public interface IdleNotifierConfig extends Config @ConfigItem( keyName = "spec", 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." ) default int getSpecEnergyThreshold() diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPlugin.java index d806888047..94495ab434 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPlugin.java @@ -31,6 +31,7 @@ import java.awt.*; import java.time.Duration; import java.time.Instant; import java.util.Arrays; +import java.util.EnumSet; import java.util.List; import javax.inject.Inject; import net.runelite.api.Actor; @@ -44,8 +45,10 @@ import net.runelite.api.NPC; import net.runelite.api.NPCComposition; import net.runelite.api.Player; import net.runelite.api.Skill; +import net.runelite.api.SkullIcon; import net.runelite.api.VarPlayer; import net.runelite.api.Varbits; +import net.runelite.api.WorldType; import net.runelite.api.events.AnimationChanged; import net.runelite.api.events.GameStateChanged; import net.runelite.api.events.GameTick; @@ -100,6 +103,8 @@ public class IdleNotifierPlugin extends Plugin private Instant sixHourWarningTime; private boolean ready; private boolean lastInteractWasCombat; + private SkullIcon lastTickSkull = null; + private boolean isFirstTick = true; @Provides IdleNotifierConfig provideConfig(ConfigManager configManager) @@ -327,9 +332,13 @@ public class IdleNotifierPlugin extends Plugin { case LOGIN_SCREEN: resetTimers(); + isFirstTick = true; + break; + case HOPPING: + isFirstTick = true; + ready = true; break; case LOGGING_IN: - case HOPPING: case CONNECTION_LOST: ready = true; break; @@ -381,6 +390,8 @@ public class IdleNotifierPlugin extends Plugin @Subscribe public void onGameTick(GameTick event) { + skullNotifier(); + final Player local = client.getLocalPlayer(); final Duration waitDuration = Duration.ofMillis(config.getIdleNotificationDelay()); lastCombatCountdown = Math.max(lastCombatCountdown - 1, 0); @@ -666,4 +677,31 @@ public class IdleNotifierPlugin extends Plugin 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; + } + } } \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/questhelper/ItemRequirement.java b/runelite-client/src/main/java/net/runelite/client/plugins/questhelper/ItemRequirement.java new file mode 100644 index 0000000000..0d68251ce1 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/questhelper/ItemRequirement.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2019, Trevor + * 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; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/questhelper/QuestDescriptor.java b/runelite-client/src/main/java/net/runelite/client/plugins/questhelper/QuestDescriptor.java new file mode 100644 index 0000000000..f952fbce88 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/questhelper/QuestDescriptor.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2019, Trevor + * 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(); +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/questhelper/QuestHelper.java b/runelite-client/src/main/java/net/runelite/client/plugins/questhelper/QuestHelper.java new file mode 100644 index 0000000000..b3f3d5096c --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/questhelper/QuestHelper.java @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2019, Trevor + * 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 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 loadSteps(); +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/questhelper/QuestHelperOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/questhelper/QuestHelperOverlay.java new file mode 100644 index 0000000000..0796f8a27c --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/questhelper/QuestHelperOverlay.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2016-2017, Seth + * Copyright (c) 2018, Lotto + * Copyright (c) 2019, Trevor + * 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); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/questhelper/QuestHelperPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/questhelper/QuestHelperPlugin.java new file mode 100644 index 0000000000..b2ab1f85dc --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/questhelper/QuestHelperPlugin.java @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2019, Trevor + * 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 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 scanAndInstantiate(ClassLoader classLoader, String packageName) throws IOException + { + Map> quests = new HashMap<>(); + + Map scannedQuests = new HashMap<>(); + ClassPath classPath = ClassPath.from(classLoader); + + ImmutableSet 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 questClass = (Class) clazz; + quests.put(questDescriptor.quest(), questClass); + } + + for (Map.Entry> questClazz : quests.entrySet()) + { + QuestHelper questHelper; + try + { + questHelper = instantiate((Class) 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 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; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/questhelper/QuestHelperWorldMapPoint.java b/runelite-client/src/main/java/net/runelite/client/plugins/questhelper/QuestHelperWorldMapPoint.java new file mode 100644 index 0000000000..be6e3c122e --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/questhelper/QuestHelperWorldMapPoint.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2018, Morgan Lewis + * Copyright (c) 2019, Trevor + * 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); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/questhelper/QuestHelperWorldOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/questhelper/QuestHelperWorldOverlay.java new file mode 100644 index 0000000000..9aafbfb7a5 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/questhelper/QuestHelperWorldOverlay.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2018, Lotto + * Copyright (c) 2019, Trevor + * 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; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/questhelper/QuestInstantiationException.java b/runelite-client/src/main/java/net/runelite/client/plugins/questhelper/QuestInstantiationException.java new file mode 100644 index 0000000000..1f43af8218 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/questhelper/QuestInstantiationException.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2016-2017, Adam + * Copyright (c) 2019, Trevor + * 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); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/questhelper/quests/cooksassistant/CooksAssistant.java b/runelite-client/src/main/java/net/runelite/client/plugins/questhelper/quests/cooksassistant/CooksAssistant.java new file mode 100644 index 0000000000..da55e5662e --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/questhelper/quests/cooksassistant/CooksAssistant.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2019, Trevor + * 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 loadSteps() + { + Map 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; + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/questhelper/quests/impcatcher/ImpCatcher.java b/runelite-client/src/main/java/net/runelite/client/plugins/questhelper/quests/impcatcher/ImpCatcher.java new file mode 100644 index 0000000000..666fe7ea5f --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/questhelper/quests/impcatcher/ImpCatcher.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2019, Trevor + * 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 loadSteps() + { + Map 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; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/questhelper/quests/xmarksthespot/XMarksTheSpot.java b/runelite-client/src/main/java/net/runelite/client/plugins/questhelper/quests/xmarksthespot/XMarksTheSpot.java new file mode 100644 index 0000000000..e18fab85e0 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/questhelper/quests/xmarksthespot/XMarksTheSpot.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2019, Trevor + * 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 loadSteps() + { + Map 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; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/questhelper/steps/DigStep.java b/runelite-client/src/main/java/net/runelite/client/plugins/questhelper/steps/DigStep.java new file mode 100644 index 0000000000..2fd845b939 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/questhelper/steps/DigStep.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2019, Trevor + * 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 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); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/questhelper/steps/NpcTalkStep.java b/runelite-client/src/main/java/net/runelite/client/plugins/questhelper/steps/NpcTalkStep.java new file mode 100644 index 0000000000..a42b4c7bf9 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/questhelper/steps/NpcTalkStep.java @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2019, Trevor + * 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 npcsToHighlight = new ArrayList<>(); + List 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); + } + + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/questhelper/steps/QuestStep.java b/runelite-client/src/main/java/net/runelite/client/plugins/questhelper/steps/QuestStep.java new file mode 100644 index 0000000000..36bd00a28e --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/questhelper/steps/QuestStep.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2019, Trevor + * 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); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsConfig.java index c54ac589ab..c8bd785eac 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsConfig.java @@ -38,8 +38,8 @@ public interface RaidsConfig extends Config @ConfigItem( position = 0, keyName = "raidsTimer", - name = "Display elapsed raid time", - description = "Display elapsed raid time" + name = "Level time tooltip", + description = "Displays your level times as a tooltip on the points overlay" ) default boolean raidsTimer() { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsPlugin.java index 89180b7bcf..79c4055264 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsPlugin.java @@ -31,7 +31,6 @@ import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.image.BufferedImage; import java.text.DecimalFormat; -import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -57,11 +56,13 @@ import net.runelite.api.Tile; import net.runelite.api.VarPlayer; import net.runelite.api.Varbits; import net.runelite.api.events.ChatMessage; +import net.runelite.api.events.ClientTick; import net.runelite.api.events.ConfigChanged; import net.runelite.api.events.VarbitChanged; import net.runelite.api.events.WidgetHiddenChanged; import net.runelite.api.widgets.Widget; import net.runelite.api.widgets.WidgetInfo; +import net.runelite.api.widgets.WidgetInfo; import net.runelite.client.callback.ClientThread; import net.runelite.client.chat.ChatColorType; 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.FontManager; 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.tooltip.Tooltip; +import net.runelite.client.ui.overlay.tooltip.TooltipManager; import net.runelite.client.util.Text; import net.runelite.client.util.HotkeyListener; +import org.apache.commons.lang3.StringUtils; @PluginDescriptor( name = "Chambers Of Xeric", @@ -95,9 +100,6 @@ import net.runelite.client.util.HotkeyListener; public class RaidsPlugin extends Plugin { 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("###.##"); static final DecimalFormat POINTS_FORMAT = new DecimalFormat("#,###"); private static final String SPLIT_REGEX = "\\s*,\\s*"; @@ -106,6 +108,8 @@ public class RaidsPlugin extends Plugin @Inject 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 private ChatMessageManager chatMessageManager; @@ -146,6 +150,9 @@ public class RaidsPlugin extends Plugin @Inject private KeyManager keyManager; + @Inject + private TooltipManager tooltipManager; + @Getter private final ArrayList roomWhitelist = new ArrayList<>(); @@ -167,7 +174,12 @@ public class RaidsPlugin extends Plugin @Getter 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 RaidsConfig provideConfig(ConfigManager configManager) @@ -188,6 +200,7 @@ public class RaidsPlugin extends Plugin overlayManager.add(pointsOverlay); updateLists(); clientThread.invokeLater(() -> checkRaidPresence(true)); + widgetOverlay = overlayManager.getWidgetOverlay(WidgetInfo.RAIDS_POINTS_INFOBOX); } @Override @@ -195,16 +208,16 @@ public class RaidsPlugin extends Plugin { overlayManager.remove(overlay); overlayManager.remove(pointsOverlay); - infoBoxManager.removeInfoBox(timer); inRaidChambers = false; + widgetOverlay = null; raid = null; - timer = null; final Widget widget = client.getWidget(WidgetInfo.RAIDS_POINTS_INFOBOX); if (widget != null) { widget.setHidden(false); } + reset(); } @Subscribe @@ -215,12 +228,6 @@ public class RaidsPlugin extends Plugin return; } - if (event.getKey().equals("raidsTimer")) - { - updateInfoBoxState(); - return; - } - updateLists(); clientThread.invokeLater(() -> checkRaidPresence(true)); } @@ -253,25 +260,33 @@ public class RaidsPlugin extends Plugin if (inRaidChambers && event.getType() == ChatMessageType.FRIENDSCHATNOTIFICATION) { 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()); - infoBoxManager.addInfoBox(timer); - } - - if (timer != null && message.contains(LEVEL_COMPLETE_MESSAGE)) - { - timer.timeFloor(); - } - - if (message.startsWith(RAID_COMPLETE_MESSAGE)) - { - if (timer != null) + String floor = matcher.group(1); + int time = timeToSeconds(matcher.group(2)); + if (floor.equals("Upper")) { - timer.timeOlm(); - timer.setStopped(true); + upperTime = time; } + 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()) { @@ -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) { if (client.getGameState() != GameState.LOGGED_IN) @@ -318,7 +350,6 @@ public class RaidsPlugin extends Plugin if (force || inRaidChambers != setting) { inRaidChambers = setting; - updateInfoBoxState(); if (inRaidChambers) { @@ -343,9 +374,14 @@ public class RaidsPlugin extends Plugin overlay.setScoutOverlayShown(true); 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()); } - 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() { @@ -700,4 +712,94 @@ public class RaidsPlugin extends Plugin 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("
Lower level: " + secondsToTime(lowerTime - upperTime)); + } + } + else + { + builder.append("
Middle level: " + secondsToTime(middleTime - upperTime)); + if (lowerTime == -1) + { + tooltip = builder.toString(); + return; + } + else + { + builder.append("
Lower level: " + secondsToTime(lowerTime - middleTime)); + } + } + if (raidTime == -1) + { + tooltip = builder.toString(); + return; + } + builder.append("
Olm: " + secondsToTime(raidTime - lowerTime)); + tooltip = builder.toString(); + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsTimer.java b/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsTimer.java deleted file mode 100644 index 8df3087054..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsTimer.java +++ /dev/null @@ -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("
First floor: "); - builder.append(firstFloorTime.format(DateTimeFormatter.ofPattern("mm:ss"))); - } - - if (secondFloorTime != null) - { - builder.append("
Second floor: "); - builder.append(secondFloorTime.format(DateTimeFormatter.ofPattern("mm:ss"))); - } - - if (thirdFloorTime != null) - { - builder.append("
Third floor: "); - builder.append(thirdFloorTime.format(DateTimeFormatter.ofPattern("mm:ss"))); - } - - if (olmTime != null) - { - builder.append("
Olm: "); - builder.append(olmTime.format(DateTimeFormatter.ofPattern("mm:ss"))); - } - - return builder.toString(); - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayManager.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayManager.java index 5a6e240f90..bb21e1e3f3 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayManager.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayManager.java @@ -42,6 +42,7 @@ import lombok.Getter; import net.runelite.api.MenuAction; import net.runelite.api.events.MenuOptionClicked; import net.runelite.api.widgets.WidgetItem; +import net.runelite.api.widgets.WidgetInfo; import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigManager; import net.runelite.client.config.RuneLiteConfig; @@ -364,4 +365,21 @@ public class OverlayManager final String locationKey = overlay.getName() + OVERLAY_CONFIG_PREFERRED_POSITION; 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; + } } \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/WidgetOverlay.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/WidgetOverlay.java index 87a4de12ac..efc4262c41 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/WidgetOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/WidgetOverlay.java @@ -32,6 +32,7 @@ import java.util.Collection; import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; +import lombok.Getter; import net.runelite.api.Client; import net.runelite.api.widgets.Widget; import net.runelite.api.widgets.WidgetInfo; @@ -64,6 +65,7 @@ public class WidgetOverlay extends Overlay } private final Client client; + @Getter private final WidgetInfo widgetInfo; private final Rectangle parentBounds = new Rectangle(); diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/arrow/ArrowMinimapOverlay.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/arrow/ArrowMinimapOverlay.java new file mode 100644 index 0000000000..2dccd96dc7 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/arrow/ArrowMinimapOverlay.java @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2018, Hydrox6 + * 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 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 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); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/arrow/ArrowPoint.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/arrow/ArrowPoint.java new file mode 100644 index 0000000000..9969869356 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/arrow/ArrowPoint.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2018, Morgan Lewis + * 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 npcIDs; + + private HashSet 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 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 types; +} diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/arrow/ArrowPointManager.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/arrow/ArrowPointManager.java new file mode 100644 index 0000000000..36e547d389 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/arrow/ArrowPointManager.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2018, Morgan Lewis + * 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 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(); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/arrow/ArrowType.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/arrow/ArrowType.java new file mode 100644 index 0000000000..b4e2abc076 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/arrow/ArrowType.java @@ -0,0 +1,10 @@ +package net.runelite.client.ui.overlay.arrow; + +public enum ArrowType +{ + MINIMAP, + NPC, + OBJECT, + WORLD_MAP, + WORLD_POINT +} diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/arrow/ArrowUtil.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/arrow/ArrowUtil.java new file mode 100644 index 0000000000..39c05d87f1 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/arrow/ArrowUtil.java @@ -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 getObjects(final Client client, HashSet objectIDs) + { + final Scene scene = client.getScene(); + final Tile[][] tiles = scene.getTiles()[client.getPlane()]; + final ArrayList 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; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/arrow/ArrowWorldOverlay.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/arrow/ArrowWorldOverlay.java new file mode 100644 index 0000000000..72301360f6 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/arrow/ArrowWorldOverlay.java @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2018, Hydrox6 + * 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 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 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); + } + } +} diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPluginTest.java index 39af7a8e28..625d59ac19 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPluginTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPluginTest.java @@ -28,6 +28,7 @@ import com.google.inject.Guice; import com.google.inject.Inject; import com.google.inject.testing.fieldbinder.Bind; import com.google.inject.testing.fieldbinder.BoundFieldModule; +import java.util.EnumSet; import net.runelite.api.AnimationID; import net.runelite.api.Client; import net.runelite.api.GameState; @@ -36,6 +37,7 @@ import net.runelite.api.NPC; import net.runelite.api.NPCComposition; import net.runelite.api.Player; import net.runelite.api.VarPlayer; +import net.runelite.api.WorldType; import net.runelite.api.events.AnimationChanged; import net.runelite.api.events.GameStateChanged; import net.runelite.api.events.GameTick; @@ -118,6 +120,7 @@ public class IdleNotifierPluginTest when(client.getGameState()).thenReturn(GameState.LOGGED_IN); when(client.getKeyboardIdleTicks()).thenReturn(42); when(client.getMouseLastPressedMillis()).thenReturn(System.currentTimeMillis() - 100_000L); + when(client.getWorldType()).thenReturn(EnumSet.of(WorldType.DEADMAN)); } @Test