diff --git a/runelite-api/src/main/java/net/runelite/api/Constants.java b/runelite-api/src/main/java/net/runelite/api/Constants.java index 42ac6527bf..b988d21fb0 100644 --- a/runelite-api/src/main/java/net/runelite/api/Constants.java +++ b/runelite-api/src/main/java/net/runelite/api/Constants.java @@ -114,4 +114,9 @@ public class Constants * Height of a standard item sprite */ public static final int ITEM_SPRITE_HEIGHT = 32; + + /** + * The height of the overworld, in tiles. Coordinates above this are in caves and other such zones. + */ + public static final int OVERWORLD_MAX_Y = 4160; } diff --git a/runelite-api/src/main/java/net/runelite/api/ObjectID.java b/runelite-api/src/main/java/net/runelite/api/ObjectID.java index 0ad9e0d818..b632893cdc 100644 --- a/runelite-api/src/main/java/net/runelite/api/ObjectID.java +++ b/runelite-api/src/main/java/net/runelite/api/ObjectID.java @@ -915,7 +915,7 @@ public final class ObjectID public static final int GRAPEVINE = 1753; public static final int GRAPEVINE_1754 = 1754; public static final int GLASS_DOOR = 1762; - public static final int PUMP_AND_DRAIN = 1763; + public static final int SINK_1763 = 1763; public static final int DUMMY_1764 = 1764; public static final int LEAFLETS = 1767; public static final int CHEST_1768 = 1768; @@ -5504,11 +5504,11 @@ public final class ObjectID public static final int BIRD_SNARE_9347 = 9347; public static final int BIRD_SNARE_9348 = 9348; public static final int BIRD_SNARE_9349 = 9349; - public static final int GRAVE_9354 = 9354; - public static final int GRAVE_9355 = 9355; - public static final int GRAVE_9356 = 9356; - public static final int GRAVE_9357 = 9357; - public static final int GRAVE_9358 = 9358; + public static final int ORNATE_COMBAT_DUMMY = 9354; + public static final int ORNATE_UNDEAD_COMBAT_DUMMY = 9355; + public static final int ORNATE_WILDERNESS_COMBAT_DUMMY = 9356; + public static final int ORNATE_KALPHITE_COMBAT_DUMMY = 9357; + public static final int ORNATE_KURASK_COMBAT_DUMMY = 9358; public static final int GRAVESTONE_9359 = 9359; public static final int GRAVESTONE_9360 = 9360; public static final int GRAVESTONE_9361 = 9361; @@ -6988,19 +6988,11 @@ public final class ObjectID public static final int LADDER_12389 = 12389; public static final int LADDER_12390 = 12390; public static final int LADDER_12391 = 12391; - public static final int SACKS_12392 = 12392; - public static final int SACKS_12393 = 12393; - public static final int SACK_PILE = 12394; - public static final int SACK_PILE_12395 = 12395; public static final int SACK_12396 = 12396; public static final int SACK_12399 = 12399; public static final int CAULDRON_12400 = 12400; public static final int CAULDRON_12401 = 12401; public static final int EXPLOSION = 12402; - public static final int SHELVES_12403 = 12403; - public static final int SHELVES_12404 = 12404; - public static final int CUPBOARD_12405 = 12405; - public static final int CUPBOARD_12406 = 12406; public static final int STOOL_12407 = 12407; public static final int STOOL_12408 = 12408; public static final int STOOL_12409 = 12409; @@ -7673,7 +7665,7 @@ public final class ObjectID public static final int SHELVES_13556 = 13556; public static final int SHELVES_13557 = 13557; public static final int SHELVES_13558 = 13558; - public static final int PUMP_AND_DRAIN_13559 = 13559; + public static final int PUMP_AND_DRAIN = 13559; public static final int PUMP_AND_DRAIN_13560 = 13560; public static final int PUMP_AND_TUB = 13561; public static final int PUMP_AND_TUB_13562 = 13562; @@ -9032,7 +9024,7 @@ public final class ObjectID public static final int LARGE_DOOR_15758 = 15758; public static final int DOOR_15759 = 15759; public static final int SACK_15760 = 15760; - public static final int SACK_PILE_15761 = 15761; + public static final int SACK_PILE = 15761; public static final int SACKS_15762 = 15762; public static final int BED_15767 = 15767; public static final int CRATE_15768 = 15768; @@ -11465,6 +11457,16 @@ public final class ObjectID public static final int EXIT_PORTAL_20843 = 20843; public static final int TREE_20844 = 20844; public static final int TREE_20845 = 20845; + public static final int GATE_20847 = 20847; + public static final int GATE_20848 = 20848; + public static final int GATE_20849 = 20849; + public static final int GATE_20850 = 20850; + public static final int FLOUR = 20851; + public static final int CAVE_20852 = 20852; + public static final int CAVE_20853 = 20853; + public static final int CAVE_20854 = 20854; + public static final int SKELETON_20855 = 20855; + public static final int SKELETON_20856 = 20856; public static final int COMPOST_BIN_20870 = 20870; public static final int COMPOST_BIN_20871 = 20871; public static final int COMPOST_BIN_20872 = 20872; @@ -15414,8 +15416,8 @@ public final class ObjectID public static final int POOL_OF_RESTORATION = 29237; public static final int POOL_OF_REVITALISATION = 29238; public static final int POOL_OF_REJUVENATION = 29239; - public static final int FANCY_REJUVENATION_POOL = 29240; - public static final int ORNATE_REJUVENATION_POOL = 29241; + public static final int FANCY_POOL_OF_REJUVENATION = 29240; + public static final int ORNATE_POOL_OF_REJUVENATION = 29241; public static final int ZEN_GARDEN = 29242; public static final int ZEN_GARDEN_29243 = 29243; public static final int ZEN_GARDEN_29244 = 29244; @@ -20610,6 +20612,16 @@ public final class ObjectID public static final int LECTERN_40357 = 40357; public static final int LECTERN_40358 = 40358; public static final int PICTURE_40359 = 40359; + public static final int MOUNTED_HEAD_SPACE = 40360; + public static final int MOUNTED_HEAD = 40361; + public static final int SHELVES_40362 = 40362; + public static final int SHELVES_40363 = 40363; + public static final int WORKBENCH_40364 = 40364; + public static final int ROCKS_40365 = 40365; + public static final int ROCKS_40366 = 40366; + public static final int BARREL_OF_FLOUR_40367 = 40367; + public static final int STAIRCASE_40381 = 40381; + public static final int DOOR_40382 = 40382; public static final int MAGICAL_PUMPKIN = 40383; public static final int MAGICAL_PUMPKIN_40384 = 40384; public static final int MAGICAL_PUMPKIN_40385 = 40385; @@ -20646,5 +20658,237 @@ public final class ObjectID public static final int BIG_DOOR_40420 = 40420; public static final int TUNNEL_ENTRANCE_40421 = 40421; public static final int TUNNEL_ENTRANCE_40422 = 40422; + public static final int TABLE_40423 = 40423; + public static final int TABLE_40424 = 40424; + public static final int SHELVES_40425 = 40425; + public static final int WOOD_40426 = 40426; + public static final int ORNATE_VAMPYRE_COMBAT_DUMMY = 40430; + public static final int ORNATE_DRAGON_COMBAT_DUMMY = 40431; + public static final int TABLE_40432 = 40432; + public static final int TREE_40433 = 40433; + public static final int SUPPLIES_40434 = 40434; + public static final int SOUL_OBELISK = 40435; + public static final int SOUL_OBELISK_40436 = 40436; + public static final int BLUE_BARRIER = 40437; + public static final int RED_BARRIER = 40438; + public static final int NEUTRAL_BARRIER = 40439; + public static final int PORTAL_40440 = 40440; + public static final int PORTAL_40441 = 40441; + public static final int BANDAGE_TABLE = 40442; + public static final int BARRICADE_TABLE = 40443; + public static final int BARRICADE_TABLE_40444 = 40444; + public static final int EXPLOSIVE_POTION_TABLE = 40445; + public static final int POTION_OF_POWER_TABLE = 40446; + public static final int TENT_40447 = 40447; + public static final int SCOREBOARD_40448 = 40448; + public static final int SOUL_OBELISK_40449 = 40449; + public static final int SOUL_OBELISK_40450 = 40450; + public static final int SOUL_OBELISK_40451 = 40451; + public static final int BLUE_BARRIER_40452 = 40452; + public static final int BLUE_BARRIER_40453 = 40453; + public static final int BLUE_BARRIER_40454 = 40454; + public static final int RED_BARRIER_40455 = 40455; + public static final int RED_BARRIER_40456 = 40456; + public static final int RED_BARRIER_40457 = 40457; + public static final int NEUTRAL_BARRIER_40458 = 40458; + public static final int NEUTRAL_BARRIER_40459 = 40459; + public static final int PORTAL_40460 = 40460; + public static final int PORTAL_40461 = 40461; + public static final int BANDAGE_TABLE_40462 = 40462; + public static final int BANDAGE_TABLE_40463 = 40463; + public static final int BARRICADE_TABLE_40464 = 40464; + public static final int BARRICADE_TABLE_40465 = 40465; + public static final int EXPLOSIVE_POTION_TABLE_40466 = 40466; + public static final int EXPLOSIVE_POTION_TABLE_40467 = 40467; + public static final int POTION_OF_POWER_TABLE_40468 = 40468; + public static final int POTION_OF_POWER_TABLE_40469 = 40469; + public static final int BLUE_BARRIER_40470 = 40470; + public static final int RED_BARRIER_40471 = 40471; + public static final int BALANCE_PORTAL = 40472; + public static final int BANK_CHEST_40473 = 40473; + public static final int SOUL_WARS_PORTAL = 40474; + public static final int SOUL_WARS_PORTAL_40475 = 40475; + public static final int PORTAL_40476 = 40476; + public static final int GRAVESTONE_40491 = 40491; + public static final int GRAVESTONE_40492 = 40492; + public static final int GRAVESTONE_40493 = 40493; + public static final int GRAVESTONE_40494 = 40494; + public static final int GRAVESTONE_40495 = 40495; + public static final int GRAVESTONE_40496 = 40496; + public static final int GRAVESTONE_40497 = 40497; + public static final int GRAVESTONE_40498 = 40498; + public static final int GRAVESTONE_40499 = 40499; + public static final int CRATE_40544 = 40544; + public static final int CRATE_40545 = 40545; + public static final int CRATE_40546 = 40546; + public static final int CRATE_40547 = 40547; + public static final int BARREL_40548 = 40548; + public static final int BARREL_40549 = 40549; + public static final int TORCH_40550 = 40550; + public static final int TORCH_40551 = 40551; + public static final int CRATE_40588 = 40588; + public static final int CRATE_40589 = 40589; + public static final int BARREL_40590 = 40590; + public static final int TREE_40715 = 40715; + public static final int TREE_40716 = 40716; + public static final int FENCE_40723 = 40723; + public static final int ANVIL_40725 = 40725; + public static final int FIRE_40728 = 40728; + public static final int TENT_40731 = 40731; + public static final int BROKEN_POTTERS_WHEEL = 40732; + public static final int POTTERS_WHEEL_40733 = 40733; + public static final int BROKEN_SPINNING_WHEEL_40734 = 40734; + public static final int SPINNING_WHEEL_40735 = 40735; + public static final int CAVE_40736 = 40736; + public static final int OPENING_40737 = 40737; + public static final int BONES_40738 = 40738; + public static final int CHEST_40739 = 40739; + public static final int CHEST_40741 = 40741; + public static final int LOCKED_CHEST_40742 = 40742; + public static final int BARREL_40744 = 40744; + public static final int TREE_40750 = 40750; + public static final int TREE_STUMP_40751 = 40751; + public static final int TREE_40752 = 40752; + public static final int TREE_STUMP_40753 = 40753; + public static final int MAPLE_TREE_40754 = 40754; + public static final int MAPLE_TREE_40755 = 40755; + public static final int YEW_40756 = 40756; + public static final int TREE_STUMP_40757 = 40757; + public static final int TEAK_40758 = 40758; + public static final int TREE_STUMP_40759 = 40759; + public static final int MAHOGANY_40760 = 40760; + public static final int TREE_STUMP_40761 = 40761; + public static final int DOOR_HOTSPOT_40768 = 40768; + public static final int DOOR_HOTSPOT_40769 = 40769; + public static final int WINDOW_SPACE_40770 = 40770; + public static final int CHRISTMAS_CURTAINS = 40771; + public static final int CHRISTMAS_CURTAINS_40772 = 40772; + public static final int CHRISTMAS_CURTAINS_40773 = 40773; + public static final int DECORATED_LIMESTONE_FIREPLACE = 40774; + public static final int DECORATED_LIMESTONE_FIREPLACE_40775 = 40775; + public static final int DECORATED_MARBLE_FIREPLACE = 40776; + public static final int DECORATED_MARBLE_FIREPLACE_40777 = 40777; + public static final int CHRISTMASSPIRIT_TREE = 40778; + public static final int SPIRITUAL_FAIRY_TREE_40780 = 40780; + public static final int SPIRITUAL_FAIRY_TREE_40781 = 40781; + public static final int SPIRITUAL_FAIRY_TREE_40782 = 40782; + public static final int SPIRITUAL_FAIRY_TREE_40783 = 40783; + public static final int SPIRITUAL_FAIRY_TREE_40784 = 40784; + public static final int SPIRITUAL_FAIRY_TREE_40785 = 40785; + public static final int SPIRITUAL_FAIRY_TREE_40786 = 40786; + public static final int SPIRITUAL_FAIRY_TREE_40787 = 40787; + public static final int SPIRITUAL_FAIRY_TREE_40788 = 40788; + public static final int SPIRITUAL_FAIRY_TREE_40789 = 40789; + public static final int SPIRITUAL_FAIRY_TREE_40790 = 40790; + public static final int SPIRITUAL_FAIRY_TREE_40791 = 40791; + public static final int SPIRITUAL_FAIRY_TREE_40792 = 40792; + public static final int SPIRITUAL_FAIRY_TREE_40793 = 40793; + public static final int SPIRITUAL_FAIRY_TREE_40794 = 40794; + public static final int SPIRITUAL_FAIRY_TREE_40795 = 40795; + public static final int SPIRITUAL_FAIRY_TREE_40796 = 40796; + public static final int SPIRITUAL_FAIRY_TREE_40797 = 40797; + public static final int SPIRITUAL_FAIRY_TREE_40798 = 40798; + public static final int SPIRITUAL_FAIRY_TREE_40799 = 40799; + public static final int SPIRITUAL_FAIRY_TREE_40800 = 40800; + public static final int SPIRITUAL_FAIRY_TREE_40801 = 40801; + public static final int SPIRITUAL_FAIRY_TREE_40802 = 40802; + public static final int SPIRITUAL_FAIRY_TREE_40803 = 40803; + public static final int SPIRITUAL_FAIRY_TREE_40804 = 40804; + public static final int SPIRITUAL_FAIRY_TREE_40805 = 40805; + public static final int SPIRITUAL_FAIRY_TREE_40806 = 40806; + public static final int SPIRITUAL_FAIRY_TREE_40807 = 40807; + public static final int SPIRITUAL_FAIRY_TREE_40808 = 40808; + public static final int SPIRITUAL_FAIRY_TREE_40809 = 40809; + public static final int SPIRITUAL_FAIRY_TREE_40810 = 40810; + public static final int SPIRITUAL_FAIRY_TREE_40811 = 40811; + public static final int SPIRITUAL_FAIRY_TREE_40812 = 40812; + public static final int SPIRITUAL_FAIRY_TREE_40813 = 40813; + public static final int SPIRITUAL_FAIRY_TREE_40814 = 40814; + public static final int SPIRITUAL_FAIRY_TREE_40815 = 40815; + public static final int SPIRITUAL_FAIRY_TREE_40816 = 40816; + public static final int SPIRITUAL_FAIRY_TREE_40817 = 40817; + public static final int SPIRITUAL_FAIRY_TREE_40818 = 40818; + public static final int SPIRITUAL_FAIRY_TREE_40819 = 40819; + public static final int SPIRITUAL_FAIRY_TREE_40820 = 40820; + public static final int SPIRITUAL_FAIRY_TREE_40821 = 40821; + public static final int SPIRITUAL_FAIRY_TREE_40822 = 40822; + public static final int SPIRITUAL_FAIRY_TREE_40823 = 40823; + public static final int SPIRITUAL_FAIRY_TREE_40824 = 40824; + public static final int SPIRITUAL_FAIRY_TREE_40825 = 40825; + public static final int SPIRITUAL_FAIRY_TREE_40826 = 40826; + public static final int SPIRITUAL_FAIRY_TREE_40827 = 40827; + public static final int SPIRITUAL_FAIRY_TREE_40828 = 40828; + public static final int SPIRITUAL_FAIRY_TREE_40829 = 40829; + public static final int SPIRITUAL_FAIRY_TREE_40830 = 40830; + public static final int SPIRITUAL_FAIRY_TREE_40831 = 40831; + public static final int SPIRITUAL_FAIRY_TREE_40832 = 40832; + public static final int SPIRITUAL_FAIRY_TREE_40833 = 40833; + public static final int SPIRITUAL_FAIRY_TREE_40834 = 40834; + public static final int SPIRITUAL_FAIRY_TREE_40835 = 40835; + public static final int SPIRITUAL_FAIRY_TREE_40836 = 40836; + public static final int SPIRITUAL_FAIRY_TREE_40837 = 40837; + public static final int SPIRITUAL_FAIRY_TREE_40838 = 40838; + public static final int SPIRITUAL_FAIRY_TREE_40839 = 40839; + public static final int SPIRITUAL_FAIRY_TREE_40840 = 40840; + public static final int SPIRITUAL_FAIRY_TREE_40841 = 40841; + public static final int SPIRITUAL_FAIRY_TREE_40842 = 40842; + public static final int SPIRITUAL_FAIRY_TREE_40843 = 40843; + public static final int FROZEN_POOL_OF_RESTORATION = 40844; + public static final int FROZEN_POOL_OF_REVITALISATION = 40845; + public static final int FROZEN_POOL_OF_REJUVENATION = 40846; + public static final int FROZEN_FANCY_POOL_OF_REJUVENATION = 40847; + public static final int FROZEN_ORNATE_POOL_OF_REJUVENATION = 40848; + public static final int SHUTTERED_WINDOW_40849 = 40849; + public static final int DECORATIVE_WINDOW_40850 = 40850; + public static final int STAINEDGLASS_WINDOW_40851 = 40851; + public static final int DECORATIVE_WINDOW_40852 = 40852; + public static final int STAINEDGLASS_WINDOW_40853 = 40853; + public static final int DECORATIVE_WINDOW_40854 = 40854; + public static final int STAINEDGLASS_WINDOW_40855 = 40855; + public static final int DECORATIVE_WINDOW_40856 = 40856; + public static final int STAINEDGLASS_WINDOW_40857 = 40857; + public static final int DOOR_40858 = 40858; + public static final int DOOR_40859 = 40859; + public static final int DOOR_40860 = 40860; + public static final int DOOR_40861 = 40861; + public static final int SACKS_40871 = 40871; + public static final int SACKS_40872 = 40872; + public static final int SACKS_40873 = 40873; + public static final int SACKS_40874 = 40874; + public static final int SACK_PILE_40875 = 40875; + public static final int SACK_PILE_40876 = 40876; + public static final int SACK_PILE_40877 = 40877; + public static final int SACK_PILE_40878 = 40878; + public static final int SHELVES_40879 = 40879; + public static final int SHELVES_40880 = 40880; + public static final int SHELVES_40881 = 40881; + public static final int SHELVES_40882 = 40882; + public static final int CUPBOARD_40883 = 40883; + public static final int CUPBOARD_40884 = 40884; + public static final int CUPBOARD_40885 = 40885; + public static final int CUPBOARD_40886 = 40886; + public static final int CAVE_ENTRANCE_40887 = 40887; + public static final int CAVE_EXIT_40888 = 40888; + public static final int CREVICE_40889 = 40889; + public static final int FIRE_REMAINS_40891 = 40891; + public static final int SLED = 40892; + public static final int SLOPE_40893 = 40893; + public static final int SLOPE_END = 40894; + public static final int PEBBLES = 40896; + public static final int BOULDER_40897 = 40897; + public static final int BOULDER_40899 = 40899; + public static final int STICK_40901 = 40901; + public static final int STICK_40902 = 40902; + public static final int CAULDRON_40903 = 40903; + public static final int TREASURE_CHEST_40904 = 40904; + public static final int ROCKS_40905 = 40905; + public static final int ROCKS_40906 = 40906; + public static final int SOCKING = 40909; + public static final int SOCKING_40910 = 40910; + public static final int SOCKING_40911 = 40911; + public static final int EVERGREEN_40932 = 40932; + public static final int EVERGREEN_40933 = 40933; + public static final int TREE_40937 = 40937; + public static final int TREE_40938 = 40938; /* This file is automatically generated. Do not edit. */ -} +} \ No newline at end of file diff --git a/runelite-api/src/main/java/net/runelite/api/ProjectileID.java b/runelite-api/src/main/java/net/runelite/api/ProjectileID.java index 42f63193f8..a507b06925 100644 --- a/runelite-api/src/main/java/net/runelite/api/ProjectileID.java +++ b/runelite-api/src/main/java/net/runelite/api/ProjectileID.java @@ -118,7 +118,7 @@ public class ProjectileID public static final int HUNLLEF_PRAYER_ATTACK = 1713; public static final int HUNLLEF_CORRUPTED_PRAYER_ATTACK = 1714; - public static final int ZALCANO_PROJECTILE = 1728; - public static final int PORAZDIR_ENERGY_BALL = 1514; + + public static final int ZALCANO_PROJECTILE_FIREBALL = 1728; } 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 27d9a88942..656bc19f9e 100644 --- a/runelite-api/src/main/java/net/runelite/api/Quest.java +++ b/runelite-api/src/main/java/net/runelite/api/Quest.java @@ -179,6 +179,7 @@ public enum Quest THE_FREMENNIK_EXILES(718, "The Fremennik Exiles"), SINS_OF_THE_FATHER(1276, "Sins of the Father"), A_PORCINE_OF_INTEREST(1690, "A Porcine of Interest"), + GETTING_AHEAD(752, "Getting Ahead"), //Miniquests ENTER_THE_ABYSS(319, "Enter the Abyss"), diff --git a/runelite-api/src/main/java/net/runelite/api/VarClientInt.java b/runelite-api/src/main/java/net/runelite/api/VarClientInt.java index c9c5a49916..54dcc406e4 100644 --- a/runelite-api/src/main/java/net/runelite/api/VarClientInt.java +++ b/runelite-api/src/main/java/net/runelite/api/VarClientInt.java @@ -66,11 +66,7 @@ public enum VarClientInt MEMBERSHIP_STATUS(103), - /** - * The currently open interface tab - * @see net.runelite.api.vars.InterfaceTab - */ - INTERFACE_TAB(171), + INVENTORY_TAB(171), WORLD_MAP_SEARCH_FOCUSED(190); 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 241861dd42..231752d927 100644 --- a/runelite-api/src/main/java/net/runelite/api/VarPlayer.java +++ b/runelite-api/src/main/java/net/runelite/api/VarPlayer.java @@ -292,7 +292,9 @@ public enum VarPlayer /** * 0 = 2 buttons, 1 = 1 button */ - MOUSE_BUTTONS(170); + MOUSE_BUTTONS(170), + + ZALCANO_FORM(1683); public final int id; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/poh/BurnerOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/poh/BurnerOverlay.java new file mode 100644 index 0000000000..16c8d4edc3 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/poh/BurnerOverlay.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2018, Seth + * 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.poh; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.time.Duration; +import java.time.Instant; +import javax.inject.Inject; +import net.runelite.api.Client; +import net.runelite.api.Perspective; +import net.runelite.api.Point; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayLayer; +import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.components.ProgressPieComponent; + +class BurnerOverlay extends Overlay +{ + private final Client client; + private final PohConfig config; + private final PohPlugin plugin; + + @Inject + private BurnerOverlay(Client client, PohConfig config, PohPlugin plugin) + { + setPosition(OverlayPosition.DYNAMIC); + setLayer(OverlayLayer.ABOVE_SCENE); + this.client = client; + this.config = config; + this.plugin = plugin; + } + + @Override + public Dimension render(Graphics2D graphics) + { + if (!config.showBurner()) + { + return null; + } + + plugin.getIncenseBurners().forEach((tile, burner) -> + { + if (tile.getPlane() != client.getPlane()) + { + return; + } + + if (!PohPlugin.BURNER_LIT.contains(burner.getId())) + { + return; + } + + final Instant now = Instant.now(); + final long startCountdown = Duration.between(burner.getStart(), now).getSeconds(); + final double certainSec = burner.getCountdownTimer() - startCountdown; + + long endCountdown = 0; + + if (certainSec <= 0) + { + if (burner.getEnd() == null) + { + burner.setEnd(Instant.now()); + } + + endCountdown = Duration.between(burner.getEnd(), now).getSeconds(); + } + + final double randomSec = burner.getRandomTimer() - endCountdown; + final ProgressPieComponent pieComponent = new ProgressPieComponent(); + final Point loc = Perspective.localToCanvas(client, tile.getLocalLocation(), tile.getPlane()); + + if (loc == null) + { + return; + } + + pieComponent.setPosition(loc); + + if (certainSec > 0) + { + pieComponent.setProgress(certainSec / burner.getCountdownTimer()); + pieComponent.setFill(Color.GREEN); + pieComponent.setBorderColor(Color.GREEN); + pieComponent.render(graphics); + } + else if (randomSec > 0) + { + pieComponent.setProgress(randomSec / burner.getRandomTimer()); + pieComponent.setFill(Color.ORANGE); + pieComponent.setBorderColor(Color.ORANGE); + pieComponent.render(graphics); + } + }); + + return null; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/poh/IncenseBurner.java b/runelite-client/src/main/java/net/runelite/client/plugins/poh/IncenseBurner.java new file mode 100644 index 0000000000..c47f9bfe14 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/poh/IncenseBurner.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2018, Tomas Slusny + * 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.poh; + +import java.time.Instant; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@RequiredArgsConstructor +@AllArgsConstructor +class IncenseBurner +{ + private final Instant start = Instant.now(); + private final int id; + private double countdownTimer; + private double randomTimer; + private Instant end; +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/poh/PohConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/poh/PohConfig.java new file mode 100644 index 0000000000..317be3b2cf --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/poh/PohConfig.java @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2018, Seth + * 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.poh; + +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("poh") +public interface PohConfig extends Config +{ + @ConfigItem( + keyName = "showPortals", + name = "Show Portals", + description = "Configures whether to display teleport portals" + ) + default boolean showPortals() + { + return true; + } + + @ConfigItem( + keyName = "showAltar", + name = "Show Altar", + description = "Configures whether or not the altar is displayed" + ) + default boolean showAltar() + { + return true; + } + + @ConfigItem( + keyName = "showGlory", + name = "Show Glory mount", + description = "Configures whether or not the mounted glory is displayed" + ) + default boolean showGlory() + { + return true; + } + + @ConfigItem( + keyName = "showPools", + name = "Show Pools", + description = "Configures whether or not the pools are displayed" + ) + default boolean showPools() + { + return true; + } + + @ConfigItem( + keyName = "showRepairStand", + name = "Show Repair stand", + description = "Configures whether or not the repair stand is displayed" + ) + default boolean showRepairStand() + { + return true; + } + + @ConfigItem( + keyName = "showExitPortal", + name = "Show Exit portal", + description = "Configures whether or not the exit portal is displayed" + ) + default boolean showExitPortal() + { + return true; + } + + @ConfigItem( + keyName = "showBurner", + name = "Show Incense Burner timers", + description = "Configures whether or not unlit/lit burners are displayed" + ) + default boolean showBurner() + { + return true; + } + + @ConfigItem( + keyName = "showSpellbook", + name = "Show Spellbook altar", + description = "Configures whether or not the Spellbook altar is displayed" + ) + default boolean showSpellbook() + { + return true; + } + + @ConfigItem( + keyName = "showJewelleryBox", + name = "Show Jewellery Box", + description = "Configures whether or not the jewellery box is displayed" + ) + default boolean showJewelleryBox() + { + return true; + } + + @ConfigItem( + keyName = "showMagicTravel", + name = "Show Fairy/ Spirit Tree/ Obelisk", + description = "Configures whether or not the Fairy ring, Spirit tree or Obelisk is displayed" + ) + default boolean showMagicTravel() + { + return true; + } + + @ConfigItem( + keyName = "showPortalNexus", + name = "Show Portal Nexus", + description = "Configures whether or not the Portal Nexus is displayed" + ) + default boolean showPortalNexus() + { + return true; + } + + @ConfigItem( + keyName = "showDigsitePendant", + name = "Show Digsite Pendant", + description = "Configures whether or not the Digsite Pendant is displayed" + ) + default boolean showDigsitePendant() + { + return true; + } + + @ConfigItem( + keyName = "showXericsTalisman", + name = "Show Xeric's Talisman", + description = "Configures whether or not the Xeric's Talisman is displayed" + ) + default boolean showXericsTalisman() + { + return true; + } + + @ConfigItem( + keyName = "showMythicalCape", + name = "Show Mythical Cape", + description = "Configures whether or not the Mythical Cape is displayed" + ) + default boolean showMythicalCape() + { + return true; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/poh/PohIcons.java b/runelite-client/src/main/java/net/runelite/client/plugins/poh/PohIcons.java new file mode 100644 index 0000000000..01925a7aab --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/poh/PohIcons.java @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2018, Seth + * 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.poh; + +import com.google.common.collect.ImmutableMap; +import java.awt.image.BufferedImage; +import java.util.Map; +import lombok.Getter; +import static net.runelite.api.NullObjectID.*; +import static net.runelite.api.ObjectID.*; +import net.runelite.client.util.ImageUtil; + +public enum PohIcons +{ + EXITPORTAL("exitportal", PORTAL_4525), + VARROCK("varrock", NULL_13615, NULL_13622, NULL_13629), + FALADOR("falador", FALADOR_PORTAL, FALADOR_PORTAL_13624, FALADOR_PORTAL_13631), + LUMBRIDGE("lumbridge", LUMBRIDGE_PORTAL, LUMBRIDGE_PORTAL_13623, LUMBRIDGE_PORTAL_13630), + ARDOUGNE("ardougne", ARDOUGNE_PORTAL, ARDOUGNE_PORTAL_13626, ARDOUGNE_PORTAL_13633), + YANILLE("yanille", NULL_13620, NULL_13627, NULL_13634), + CAMELOT("camelot", NULL_13618, NULL_13625, NULL_13632), + LUNARISLE("lunarisle", LUNAR_ISLE_PORTAL, LUNAR_ISLE_PORTAL_29347, LUNAR_ISLE_PORTAL_29355), + WATERBIRTH("waterbirth", WATERBIRTH_ISLAND_PORTAL, WATERBIRTH_ISLAND_PORTAL_29350, WATERBIRTH_ISLAND_PORTAL_29358), + FISHINGGUILD("fishingguild", FISHING_GUILD_PORTAL, FISHING_GUILD_PORTAL_29351, FISHING_GUILD_PORTAL_29359), + SENNTISTEN("senntisten", SENNTISTEN_PORTAL, SENNTISTEN_PORTAL_29348, SENNTISTEN_PORTAL_29356), + KHARYLL("kharyll", KHARYRLL_PORTAL, KHARYRLL_PORTAL_29346, KHARYRLL_PORTAL_29354), + ANNAKARL("annakarl", ANNAKARL_PORTAL, ANNAKARL_PORTAL_29349, ANNAKARL_PORTAL_29357), + KOUREND("kourend", KOUREND_PORTAL, KOUREND_PORTAL_29353, KOUREND_PORTAL_29361), + MARIM("marim", MARIM_PORTAL, MARIM_PORTAL_29352, MARIM_PORTAL_29360), + TROLLSTRONGHOLD("trollstronghold", TROLL_STRONGHOLD_PORTAL, TROLL_STRONGHOLD_PORTAL_33180, TROLL_STRONGHOLD_PORTAL_33181), + GHORROCK("ghorrock", GHORROCK_PORTAL, GHORROCK_PORTAL_33436, GHORROCK_PORTAL_33439), + CARRALLANGAR("carrallangar", CARRALLANGAR_PORTAL, CARRALLANGAR_PORTAL_33437, CARRALLANGAR_PORTAL_33440), + CATHERBY("catherby", CATHERBY_PORTAL, CATHERBY_PORTAL_33435, CATHERBY_PORTAL_33438), + WEISS("weiss", WEISS_PORTAL, WEISS_PORTAL_37593, WEISS_PORTAL_37605), + APEATOLLDUNGEON("apeatolldungeon", APE_ATOLL_DUNGEON_PORTAL, APE_ATOLL_DUNGEON_PORTAL_37604, APE_ATOLL_DUNGEON_PORTAL_37616), + BARROWS("barrows", BARROWS_PORTAL, BARROWS_PORTAL_37603, BARROWS_PORTAL_37615), + BATTLEFRONT("battlefront", BATTLEFRONT_PORTAL, BATTLEFRONT_PORTAL_37596, BATTLEFRONT_PORTAL_37608), + CEMETERY("cemetery", CEMETERY_PORTAL, CEMETERY_PORTAL_37602, CEMETERY_PORTAL_37614), + DRAYNORMANOR("draynormanor", DRAYNOR_MANOR_PORTAL, DRAYNOR_MANOR_PORTAL_37595, DRAYNOR_MANOR_PORTAL_37607), + FENKENSTRAINSCASTLE("fenkenstrainscastle", FENKENSTRAINS_CASTLE_PORTAL, FENKENSTRAINS_CASTLE_PORTAL_37599, FENKENSTRAINS_CASTLE_PORTAL_37611), + HARMONYISLAND("harmonyisland", HARMONY_ISLAND_PORTAL, HARMONY_ISLAND_PORTAL_37601, HARMONY_ISLAND_PORTAL_37613), + LUMBRIDGEGRAVEYARD("lumbridgegraveyard", LUMBRIDGE_GRAVEYARD_PORTAL, LUMBRIDGE_GRAVEYARD_PORTAL_37594, LUMBRIDGE_GRAVEYARD_PORTAL_37606), + MINDALTAR("mindaltar", MIND_ALTAR_PORTAL, MIND_ALTAR_PORTAL_37597, MIND_ALTAR_PORTAL_37609), + SALVEGRAVEYARD("salvegraveyard", SALVE_GRAVEYARD_PORTAL, SALVE_GRAVEYARD_PORTAL_37598, SALVE_GRAVEYARD_PORTAL_37610), + WESTARDOUGNE("westardougne", WEST_ARDOUGNE_PORTAL, WEST_ARDOUGNE_PORTAL_37600, WEST_ARDOUGNE_PORTAL_37612), + ALTAR("altar", + ALTAR_13179, ALTAR_13180, ALTAR_13181, ALTAR_13182, ALTAR_13183, ALTAR_13184, ALTAR_13185, ALTAR_13186, + ALTAR_13187, ALTAR_13188, ALTAR_13189, ALTAR_13190, ALTAR_13191, ALTAR_13192, ALTAR_13193, ALTAR_13194, + ALTAR_13196, ALTAR_13197, ALTAR_13198, ALTAR_13199 + ), + POOLS("pool", POOL_OF_RESTORATION, POOL_OF_REVITALISATION, POOL_OF_REJUVENATION, FANCY_POOL_OF_REJUVENATION, ORNATE_POOL_OF_REJUVENATION), + GLORY("glory", AMULET_OF_GLORY), + REPAIR("repair", ARMOUR_REPAIR_STAND), + SPELLBOOKALTAR("spellbook", ANCIENT_ALTAR, LUNAR_ALTAR, DARK_ALTAR, NULL_29150), + JEWELLERYBOX("jewellery", NULL_29154, NULL_29155, NULL_29156), + MAGICTRAVEL("transportation", SPIRIT_TREE_29227, NULL_29228, NULL_29229, OBELISK_31554), + PORTALNEXUS("portalnexus", + PORTAL_NEXUS, PORTAL_NEXUS_33355, PORTAL_NEXUS_33356, PORTAL_NEXUS_33357, PORTAL_NEXUS_33358, PORTAL_NEXUS_33359, PORTAL_NEXUS_33360, + PORTAL_NEXUS_33361, PORTAL_NEXUS_33362, PORTAL_NEXUS_33363, PORTAL_NEXUS_33364, PORTAL_NEXUS_33365, PORTAL_NEXUS_33366, PORTAL_NEXUS_33367, + PORTAL_NEXUS_33368, PORTAL_NEXUS_33369, PORTAL_NEXUS_33370, PORTAL_NEXUS_33371, PORTAL_NEXUS_33372, PORTAL_NEXUS_33373, PORTAL_NEXUS_33374, + PORTAL_NEXUS_33375, PORTAL_NEXUS_33376, PORTAL_NEXUS_33377, PORTAL_NEXUS_33378, PORTAL_NEXUS_33379, PORTAL_NEXUS_33380, PORTAL_NEXUS_33381, + PORTAL_NEXUS_33382, PORTAL_NEXUS_33383, PORTAL_NEXUS_33384, PORTAL_NEXUS_33385, PORTAL_NEXUS_33386, PORTAL_NEXUS_33387, PORTAL_NEXUS_33388, + PORTAL_NEXUS_33389, PORTAL_NEXUS_33390, PORTAL_NEXUS_33391, PORTAL_NEXUS_33392, PORTAL_NEXUS_33393, PORTAL_NEXUS_33394, PORTAL_NEXUS_33395, + PORTAL_NEXUS_33396, PORTAL_NEXUS_33397, PORTAL_NEXUS_33398, PORTAL_NEXUS_33399, PORTAL_NEXUS_33400, PORTAL_NEXUS_33401, PORTAL_NEXUS_33402, + PORTAL_NEXUS_33403, PORTAL_NEXUS_33404, PORTAL_NEXUS_33405, PORTAL_NEXUS_33406, PORTAL_NEXUS_33407, PORTAL_NEXUS_33408, PORTAL_NEXUS_33409, + PORTAL_NEXUS_33410, PORTAL_NEXUS_33423, PORTAL_NEXUS_33424, PORTAL_NEXUS_33425, PORTAL_NEXUS_33426, PORTAL_NEXUS_33427, PORTAL_NEXUS_33428, + PORTAL_NEXUS_33429, PORTAL_NEXUS_33430, PORTAL_NEXUS_33431, PORTAL_NEXUS_37547, PORTAL_NEXUS_37548, PORTAL_NEXUS_37549, PORTAL_NEXUS_37550, + PORTAL_NEXUS_37551, PORTAL_NEXUS_37552, PORTAL_NEXUS_37553, PORTAL_NEXUS_37554, PORTAL_NEXUS_37555, PORTAL_NEXUS_37556, PORTAL_NEXUS_37557, + PORTAL_NEXUS_37558, PORTAL_NEXUS_37559, PORTAL_NEXUS_37560, PORTAL_NEXUS_37561, PORTAL_NEXUS_37562, PORTAL_NEXUS_37563, PORTAL_NEXUS_37564, + PORTAL_NEXUS_37565, PORTAL_NEXUS_37566, PORTAL_NEXUS_37567, PORTAL_NEXUS_37568, PORTAL_NEXUS_37569, PORTAL_NEXUS_37570, PORTAL_NEXUS_37571, + PORTAL_NEXUS_37572, PORTAL_NEXUS_37573, PORTAL_NEXUS_37574, PORTAL_NEXUS_37575, PORTAL_NEXUS_37576, PORTAL_NEXUS_37577, PORTAL_NEXUS_37578, + PORTAL_NEXUS_37579, PORTAL_NEXUS_37580 + ), + XERICSTALISMAN("xericstalisman", + XERICS_TALISMAN, XERICS_TALISMAN_33412, XERICS_TALISMAN_33413, XERICS_TALISMAN_33414, XERICS_TALISMAN_33415, XERICS_TALISMAN_33419 + ), + DIGSITEPENDANT("digsitependant", + DIGSITE_PENDANT, DIGSITE_PENDANT_33417, DIGSITE_PENDANT_33418, DIGSITE_PENDANT_33420 + ), + MYTHICALCAPE("mythicalcape", MYTHICAL_CAPE, MOUNTED_MYTHICAL_CAPE); + + private static final Map minimapIcons; + + @Getter + private final String imageResource; + @Getter + private final int[] Ids; + + private BufferedImage image; + + static + { + ImmutableMap.Builder builder = new ImmutableMap.Builder<>(); + + for (PohIcons icon : values()) + { + for (Integer spotId : icon.getIds()) + { + builder.put(spotId, icon); + } + } + + minimapIcons = builder.build(); + } + + PohIcons(String imageResource, int... ids) + { + this.imageResource = imageResource; + this.Ids = ids; + } + + public static PohIcons getIcon(int id) + { + return minimapIcons.get(id); + } + + public BufferedImage getImage() + { + if (image != null) + { + return image; + } + + image = ImageUtil.getResourceStreamFromClass(getClass(), getImageResource() + ".png"); + + return image; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/poh/PohOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/poh/PohOverlay.java new file mode 100644 index 0000000000..2eb39344b5 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/poh/PohOverlay.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2018, Seth + * 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.poh; + +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import javax.inject.Inject; +import lombok.Getter; +import net.runelite.api.Client; +import net.runelite.api.Perspective; +import net.runelite.api.coords.LocalPoint; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayLayer; +import net.runelite.client.ui.overlay.OverlayPosition; + +public class PohOverlay extends Overlay +{ + private static final PohIcons[] PORTALS = { + PohIcons.LUMBRIDGE, PohIcons.FALADOR, PohIcons.VARROCK, PohIcons.CAMELOT, PohIcons.ARDOUGNE, + PohIcons.YANILLE, PohIcons.LUNARISLE, PohIcons.WATERBIRTH, PohIcons.FISHINGGUILD, + PohIcons.SENNTISTEN, PohIcons.KHARYLL, PohIcons.ANNAKARL, PohIcons.KOUREND, PohIcons.MARIM, + PohIcons.TROLLSTRONGHOLD, PohIcons.CARRALLANGAR, PohIcons.CATHERBY, PohIcons.WEISS, PohIcons.GHORROCK, + PohIcons.APEATOLLDUNGEON, PohIcons.BARROWS, PohIcons.BATTLEFRONT, PohIcons.CEMETERY, PohIcons.DRAYNORMANOR, + PohIcons.FENKENSTRAINSCASTLE, PohIcons.HARMONYISLAND, PohIcons.LUMBRIDGEGRAVEYARD, PohIcons.MINDALTAR, PohIcons.SALVEGRAVEYARD, + PohIcons.WESTARDOUGNE, + }; + + private static final int MAX_DISTANCE = 2350; + + @Getter + private final List iconList = new ArrayList<>(); + + private final Client client; + private final PohConfig config; + private final PohPlugin plugin; + + @Inject + public PohOverlay(Client client, PohConfig config, PohPlugin plugin) + { + setPosition(OverlayPosition.DYNAMIC); + setLayer(OverlayLayer.ABOVE_WIDGETS); + this.client = client; + this.config = config; + this.plugin = plugin; + } + + @Override + public Dimension render(Graphics2D graphics) + { + LocalPoint localLocation = client.getLocalPlayer().getLocalLocation(); + plugin.getPohObjects().forEach((object, tile) -> + { + LocalPoint location = object.getLocalLocation(); + if (tile.getPlane() == client.getPlane() && localLocation.distanceTo(location) <= MAX_DISTANCE) + { + PohIcons icon = PohIcons.getIcon(object.getId()); + + if (icon != null && iconList.contains(icon)) + { + net.runelite.api.Point minimapLoc = Perspective.getMiniMapImageLocation(client, object.getLocalLocation(), icon.getImage()); + + if (minimapLoc != null) + { + graphics.drawImage(icon.getImage(), minimapLoc.getX(), minimapLoc.getY(), null); + } + } + } + }); + + return null; + } + + public void updateConfig() + { + iconList.clear(); + if (config.showPortals()) + { + Collections.addAll(iconList, PORTALS); + } + if (config.showAltar()) + { + iconList.add(PohIcons.ALTAR); + } + if (config.showGlory()) + { + iconList.add(PohIcons.GLORY); + } + if (config.showRepairStand()) + { + iconList.add(PohIcons.REPAIR); + } + if (config.showPools()) + { + iconList.add(PohIcons.POOLS); + } + if (config.showExitPortal()) + { + iconList.add(PohIcons.EXITPORTAL); + } + if (config.showSpellbook()) + { + iconList.add(PohIcons.SPELLBOOKALTAR); + } + if (config.showJewelleryBox()) + { + iconList.add(PohIcons.JEWELLERYBOX); + } + if (config.showMagicTravel()) + { + iconList.add(PohIcons.MAGICTRAVEL); + } + if (config.showPortalNexus()) + { + iconList.add(PohIcons.PORTALNEXUS); + } + if (config.showDigsitePendant()) + { + iconList.add(PohIcons.DIGSITEPENDANT); + } + if (config.showXericsTalisman()) + { + iconList.add(PohIcons.XERICSTALISMAN); + } + if (config.showMythicalCape()) + { + iconList.add(PohIcons.MYTHICALCAPE); + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/poh/PohPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/poh/PohPlugin.java new file mode 100644 index 0000000000..d7d7181060 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/poh/PohPlugin.java @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2018, Seth + * 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.poh; + +import com.google.common.collect.Sets; +import com.google.inject.Provides; +import java.io.IOException; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ScheduledExecutorService; +import javax.inject.Inject; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.Actor; +import net.runelite.api.AnimationID; +import net.runelite.api.Client; +import net.runelite.api.Constants; +import net.runelite.api.DecorativeObject; +import net.runelite.api.GameObject; +import net.runelite.api.GameState; +import net.runelite.api.ObjectID; +import net.runelite.api.Player; +import net.runelite.api.Tile; +import net.runelite.api.TileObject; +import net.runelite.api.coords.LocalPoint; +import net.runelite.api.events.AnimationChanged; +import net.runelite.client.events.ConfigChanged; +import net.runelite.api.events.DecorativeObjectDespawned; +import net.runelite.api.events.DecorativeObjectSpawned; +import net.runelite.api.events.GameObjectDespawned; +import net.runelite.api.events.GameObjectSpawned; +import net.runelite.api.events.GameStateChanged; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.game.HiscoreManager; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.ui.overlay.OverlayManager; +import net.runelite.http.api.hiscore.HiscoreEndpoint; +import net.runelite.http.api.hiscore.HiscoreResult; +import net.runelite.http.api.hiscore.Skill; + +@PluginDescriptor( + name = "Player-owned House", + description = "Show minimap icons and mark unlit/lit burners", + tags = {"construction", "poh", "minimap", "overlay"} +) +@Slf4j +public class PohPlugin extends Plugin +{ + static final Set BURNER_UNLIT = Sets.newHashSet(ObjectID.INCENSE_BURNER, ObjectID.INCENSE_BURNER_13210, ObjectID.INCENSE_BURNER_13212); + static final Set BURNER_LIT = Sets.newHashSet(ObjectID.INCENSE_BURNER_13209, ObjectID.INCENSE_BURNER_13211, ObjectID.INCENSE_BURNER_13213); + + @Getter(AccessLevel.PACKAGE) + private final Map pohObjects = new HashMap<>(); + + @Getter(AccessLevel.PACKAGE) + private final Map incenseBurners = new HashMap<>(); + + @Inject + private OverlayManager overlayManager; + + @Inject + private PohOverlay overlay; + + @Inject + private Client client; + + @Inject + private ScheduledExecutorService executor; + + @Inject + private HiscoreManager hiscoreManager; + + @Inject + private BurnerOverlay burnerOverlay; + + @Provides + PohConfig getConfig(ConfigManager configManager) + { + return configManager.getConfig(PohConfig.class); + } + + @Override + protected void startUp() throws Exception + { + overlayManager.add(overlay); + overlayManager.add(burnerOverlay); + overlay.updateConfig(); + } + + @Override + protected void shutDown() throws Exception + { + overlayManager.remove(overlay); + overlayManager.remove(burnerOverlay); + pohObjects.clear(); + incenseBurners.clear(); + } + + @Subscribe + public void onConfigChanged(ConfigChanged event) + { + overlay.updateConfig(); + } + + @Subscribe + public void onGameObjectSpawned(GameObjectSpawned event) + { + final GameObject gameObject = event.getGameObject(); + + if (!BURNER_LIT.contains(gameObject.getId()) && !BURNER_UNLIT.contains(gameObject.getId())) + { + if (PohIcons.getIcon(gameObject.getId()) != null) + { + pohObjects.put(gameObject, event.getTile()); + } + + return; + } + + final double countdownTimer = 130.0; // Minimum amount of seconds a burner will light + final double randomTimer = 30.0; // Minimum amount of seconds a burner will light + incenseBurners.put(event.getTile(), new IncenseBurner(gameObject.getId(), countdownTimer, randomTimer, null)); + } + + @Subscribe + public void onGameObjectDespawned(GameObjectDespawned event) + { + GameObject gameObject = event.getGameObject(); + pohObjects.remove(gameObject); + } + + @Subscribe + public void onDecorativeObjectSpawned(DecorativeObjectSpawned event) + { + DecorativeObject decorativeObject = event.getDecorativeObject(); + if (PohIcons.getIcon(decorativeObject.getId()) != null) + { + pohObjects.put(decorativeObject, event.getTile()); + } + } + + @Subscribe + public void onDecorativeObjectDespawned(DecorativeObjectDespawned event) + { + DecorativeObject decorativeObject = event.getDecorativeObject(); + pohObjects.remove(decorativeObject); + } + + @Subscribe + public void onGameStateChanged(GameStateChanged event) + { + if (event.getGameState() == GameState.LOADING) + { + pohObjects.clear(); + incenseBurners.clear(); + } + } + + @Subscribe + public void onAnimationChanged(AnimationChanged event) + { + final Actor actor = event.getActor(); + final String actorName = actor.getName(); + + if (!(actor instanceof Player) || actor.getAnimation() != AnimationID.INCENSE_BURNER) + { + return; + } + + final LocalPoint loc = actor.getLocalLocation(); + + // Find burner closest to player + incenseBurners.keySet() + .stream() + .min(Comparator.comparingInt(a -> loc.distanceTo(a.getLocalLocation()))) + .ifPresent(tile -> + { + final IncenseBurner incenseBurner = incenseBurners.get(tile); + + if (actor == client.getLocalPlayer()) + { + int level = client.getRealSkillLevel(net.runelite.api.Skill.FIREMAKING); + updateBurner(incenseBurner, level); + } + else if (actorName != null) + { + lookupPlayer(actorName, incenseBurner); + } + }); + + } + + private void lookupPlayer(String playerName, IncenseBurner incenseBurner) + { + executor.execute(() -> + { + try + { + final HiscoreResult playerStats = hiscoreManager.lookup(playerName, HiscoreEndpoint.NORMAL); + + if (playerStats == null) + { + return; + } + + final Skill fm = playerStats.getFiremaking(); + final int level = fm.getLevel(); + updateBurner(incenseBurner, Math.max(level, 1)); + } + catch (IOException e) + { + log.warn("Error fetching Hiscore data " + e.getMessage()); + } + }); + } + + private static void updateBurner(IncenseBurner incenseBurner, int fmLevel) + { + final double tickLengthSeconds = Constants.GAME_TICK_LENGTH / 1000.0; + incenseBurner.setCountdownTimer((200 + fmLevel) * tickLengthSeconds); + incenseBurner.setRandomTimer(fmLevel * tickLengthSeconds); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/questlist/QuestListPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/questlist/QuestListPlugin.java new file mode 100644 index 0000000000..166ef3f669 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/questlist/QuestListPlugin.java @@ -0,0 +1,431 @@ +/* + * Copyright (c) 2019 Spudjb + * 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.questlist; + +import com.google.common.collect.ImmutableList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.EnumMap; +import java.util.List; +import java.util.stream.Collectors; +import javax.inject.Inject; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.Getter; +import net.runelite.api.Client; +import net.runelite.api.GameState; +import net.runelite.api.ScriptID; +import net.runelite.api.SoundEffectID; +import net.runelite.api.SpriteID; +import net.runelite.api.VarClientInt; +import net.runelite.api.Varbits; +import net.runelite.api.events.GameStateChanged; +import net.runelite.api.events.ScriptPostFired; +import net.runelite.api.events.VarClientIntChanged; +import net.runelite.api.events.VarbitChanged; +import net.runelite.api.widgets.JavaScriptCallback; +import net.runelite.api.widgets.Widget; +import net.runelite.api.widgets.WidgetInfo; +import net.runelite.api.widgets.WidgetPositionMode; +import net.runelite.api.widgets.WidgetType; +import net.runelite.client.callback.ClientThread; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.game.chatbox.ChatboxPanelManager; +import net.runelite.client.game.chatbox.ChatboxTextInput; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.util.Text; + +@PluginDescriptor( + name = "Quest List", + description = "Adds searching and filtering to the quest list" +) +public class QuestListPlugin extends Plugin +{ + private static final int ENTRY_PADDING = 8; + private static final List QUEST_HEADERS = ImmutableList.of("Free Quests", "Members' Quests", "Miniquests"); + + private static final String MENU_OPEN = "Open"; + private static final String MENU_CLOSE = "Close"; + + private static final String MENU_TOGGLE = "Toggle"; + + private static final String MENU_SEARCH = "Search"; + private static final String MENU_SHOW = "Show"; + + @Inject + private Client client; + + @Inject + private ChatboxPanelManager chatboxPanelManager; + + @Inject + private ClientThread clientThread; + + private ChatboxTextInput searchInput; + private Widget questSearchButton; + private Widget questHideButton; + + private EnumMap> questSet; + + private QuestState currentFilterState; + + @Override + protected void startUp() + { + currentFilterState = QuestState.ALL; + clientThread.invoke(this::addQuestButtons); + } + + @Override + protected void shutDown() + { + currentFilterState = null; + Widget header = client.getWidget(WidgetInfo.QUESTLIST_BOX); + if (header != null) + { + header.deleteAllChildren(); + } + } + + @Subscribe + public void onGameStateChanged(GameStateChanged e) + { + if (e.getGameState() == GameState.LOGGING_IN) + { + currentFilterState = QuestState.ALL; + } + } + + @Subscribe + public void onScriptPostFired(ScriptPostFired event) + { + if (event.getScriptId() != ScriptID.QUESTLIST_PROGRESS_LIST_SHOW) + { + return; + } + addQuestButtons(); + } + + private void addQuestButtons() + { + Widget header = client.getWidget(WidgetInfo.QUESTLIST_BOX); + if (header != null) + { + header.deleteAllChildren(); + + questSearchButton = header.createChild(-1, WidgetType.GRAPHIC); + questSearchButton.setSpriteId(SpriteID.GE_SEARCH); + questSearchButton.setOriginalWidth(18); + questSearchButton.setOriginalHeight(17); + questSearchButton.setXPositionMode(WidgetPositionMode.ABSOLUTE_RIGHT); + questSearchButton.setOriginalX(5); + questSearchButton.setOriginalY(0); + questSearchButton.setHasListener(true); + questSearchButton.setAction(1, MENU_OPEN); + questSearchButton.setOnOpListener((JavaScriptCallback) e -> openSearch()); + questSearchButton.setName(MENU_SEARCH); + questSearchButton.revalidate(); + + questHideButton = header.createChild(-1, WidgetType.GRAPHIC); + redrawHideButton(); + + questHideButton.setOriginalWidth(13); + questHideButton.setOriginalHeight(13); + questHideButton.setXPositionMode(WidgetPositionMode.ABSOLUTE_RIGHT); + questHideButton.setOriginalX(24); + questHideButton.setOriginalY(2); + questHideButton.setHasListener(true); + questHideButton.setOnOpListener((JavaScriptCallback) e -> toggleHidden()); + questHideButton.setAction(1, MENU_TOGGLE); + questHideButton.revalidate(); + + questSet = new EnumMap<>(QuestContainer.class); + + updateFilter(); + } + } + + @Subscribe + public void onVarbitChanged(VarbitChanged varbitChanged) + { + if (isChatboxOpen() && !isOnQuestTab()) + { + chatboxPanelManager.close(); + } + } + + @Subscribe + public void onVarClientIntChanged(VarClientIntChanged varClientIntChanged) + { + if (varClientIntChanged.getIndex() == VarClientInt.INVENTORY_TAB.getIndex()) + { + if (isChatboxOpen() && !isOnQuestTab()) + { + chatboxPanelManager.close(); + } + } + } + + private void toggleHidden() + { + QuestState[] questStates = QuestState.values(); + int nextState = (currentFilterState.ordinal() + 1) % questStates.length; + currentFilterState = questStates[nextState]; + + redrawHideButton(); + + updateFilter(); + client.playSoundEffect(SoundEffectID.UI_BOOP); + } + + private void redrawHideButton() + { + questHideButton.setSpriteId(currentFilterState.getSpriteId()); + questHideButton.setName(MENU_SHOW + " " + currentFilterState.getName()); + } + + private boolean isOnQuestTab() + { + return client.getVar(Varbits.QUEST_TAB) == 0 && client.getVar(VarClientInt.INVENTORY_TAB) == 2; + } + + private boolean isChatboxOpen() + { + return searchInput != null && chatboxPanelManager.getCurrentInput() == searchInput; + } + + private void closeSearch() + { + updateFilter(""); + chatboxPanelManager.close(); + client.playSoundEffect(SoundEffectID.UI_BOOP); + } + + private void openSearch() + { + updateFilter(""); + client.playSoundEffect(SoundEffectID.UI_BOOP); + questSearchButton.setAction(1, MENU_CLOSE); + questSearchButton.setOnOpListener((JavaScriptCallback) e -> closeSearch()); + searchInput = chatboxPanelManager.openTextInput("Search quest list") + .onChanged(s -> clientThread.invokeLater(() -> updateFilter(s))) + .onDone(s -> false) + .onClose(() -> + { + clientThread.invokeLater(() -> updateFilter("")); + questSearchButton.setOnOpListener((JavaScriptCallback) e -> openSearch()); + questSearchButton.setAction(1, MENU_OPEN); + }) + .build(); + } + + private void updateFilter() + { + String filter = ""; + if (isChatboxOpen()) + { + filter = searchInput.getValue(); + } + + updateFilter(filter); + } + + private void updateFilter(String filter) + { + filter = filter.toLowerCase(); + final Widget container = client.getWidget(WidgetInfo.QUESTLIST_CONTAINER); + + final Widget freeList = client.getWidget(QuestContainer.FREE_QUESTS.widgetInfo); + final Widget memberList = client.getWidget(QuestContainer.MEMBER_QUESTS.widgetInfo); + final Widget miniList = client.getWidget(QuestContainer.MINI_QUESTS.widgetInfo); + + if (container == null || freeList == null || memberList == null || miniList == null) + { + return; + } + + updateList(QuestContainer.FREE_QUESTS, filter); + updateList(QuestContainer.MEMBER_QUESTS, filter); + updateList(QuestContainer.MINI_QUESTS, filter); + + memberList.setOriginalY(freeList.getOriginalY() + freeList.getOriginalHeight() + ENTRY_PADDING); + miniList.setOriginalY(memberList.getOriginalY() + memberList.getOriginalHeight() + ENTRY_PADDING); + + // originalHeight is changed within updateList so revalidate all lists + freeList.revalidate(); + memberList.revalidate(); + miniList.revalidate(); + + int y = miniList.getRelativeY() + miniList.getHeight() + 10; + + int newHeight; + if (container.getScrollHeight() > 0) + { + newHeight = (container.getScrollY() * y) / container.getScrollHeight(); + } + else + { + newHeight = 0; + } + + container.setScrollHeight(y); + container.revalidateScroll(); + + clientThread.invokeLater(() -> + client.runScript( + ScriptID.UPDATE_SCROLLBAR, + WidgetInfo.QUESTLIST_SCROLLBAR.getId(), + WidgetInfo.QUESTLIST_CONTAINER.getId(), + newHeight + )); + } + + private void updateList(QuestContainer questContainer, String filter) + { + Widget list = client.getWidget(questContainer.widgetInfo); + if (list == null) + { + return; + } + + Collection quests = questSet.get(questContainer); + + if (quests != null) + { + // Check to make sure the list hasn't been rebuild since we were last her + // Do this by making sure the list's dynamic children are the same as when we last saw them + if (quests.stream().noneMatch(w -> + { + Widget codeWidget = w.getQuest(); + if (codeWidget == null) + { + return false; + } + return list.getChild(codeWidget.getIndex()) == codeWidget; + })) + { + quests = null; + } + } + + if (quests == null) + { + // Find all of the widgets that we care about, sorting by their Y value + quests = Arrays.stream(list.getDynamicChildren()) + .sorted(Comparator.comparing(Widget::getRelativeY)) + .filter(w -> !QUEST_HEADERS.contains(w.getText())) + .map(w -> new QuestWidget(w, Text.removeTags(w.getText()).toLowerCase())) + .collect(Collectors.toList()); + questSet.put(questContainer, quests); + } + + // offset because of header + int y = 20; + for (QuestWidget questInfo : quests) + { + Widget quest = questInfo.getQuest(); + QuestState questState = QuestState.getByColor(quest.getTextColor()); + + boolean hidden; + if (!filter.isEmpty()) + { + // If searching, show result regardless of filtered state + hidden = !questInfo.getTitle().contains(filter); + } + else + { + // Otherwise hide if it doesn't match the filter state + if (currentFilterState == QuestState.NOT_COMPLETED) + { + hidden = questState == QuestState.COMPLETE; + } + else + { + hidden = currentFilterState != QuestState.ALL && questState != currentFilterState; + } + } + + quest.setHidden(hidden); + quest.setOriginalY(y); + quest.revalidate(); + + if (!hidden) + { + y += quest.getHeight(); + } + } + + list.setOriginalHeight(y); + } + + @AllArgsConstructor + @Getter + private enum QuestContainer + { + FREE_QUESTS(WidgetInfo.QUESTLIST_FREE_CONTAINER), + MEMBER_QUESTS(WidgetInfo.QUESTLIST_MEMBERS_CONTAINER), + MINI_QUESTS(WidgetInfo.QUESTLIST_MINIQUEST_CONTAINER); + + private final WidgetInfo widgetInfo; + } + + @AllArgsConstructor + @Getter + private enum QuestState + { + NOT_STARTED(0xff0000, "Not started", SpriteID.MINIMAP_ORB_HITPOINTS), + IN_PROGRESS(0xffff00, "In progress", SpriteID.MINIMAP_ORB_HITPOINTS_DISEASE), + COMPLETE(0xdc10d, "Completed", SpriteID.MINIMAP_ORB_HITPOINTS_POISON), + ALL(0, "All", SpriteID.MINIMAP_ORB_PRAYER), + NOT_COMPLETED(0, "Not Completed", SpriteID.MINIMAP_ORB_RUN); + + private final int color; + private final String name; + private final int spriteId; + + static QuestState getByColor(int color) + { + for (QuestState value : values()) + { + if (value.getColor() == color) + { + return value; + } + } + + return null; + } + } + + @Data + @AllArgsConstructor + private static class QuestWidget + { + private Widget quest; + private String title; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skybox/Skybox.java b/runelite-client/src/main/java/net/runelite/client/plugins/skybox/Skybox.java new file mode 100644 index 0000000000..91f9fada23 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skybox/Skybox.java @@ -0,0 +1,515 @@ +/* + * Copyright (c) 2019 Abex + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.skybox; + +import java.awt.Color; +import java.awt.image.BufferedImage; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import net.runelite.api.Client; + +class Skybox +{ + @FunctionalInterface + public interface ChunkMapper + { + /** + * Gets the instance template chunk data for the specified point + * + * @see Client#getInstanceTemplateChunks + */ + int getTemplateChunk(int cx, int cy, int plane); + } + + private static final double SQRT2 = Math.sqrt(2); + + // How many stddev per direction we need to stay visibly continuous + // 511/512 accuracy + private static final double BLEND_DISTRIBUTION = 3.075; + + // This has a worst case complexity of O((BLEND_RADUS*2)^2) + // BLEND_RADIUS is in chunks (8 tiles) + private static final int BLEND_RADIUS = 5; + + // The maximum number of tiles that can be blended before becoming visibly discontinuous + private static final int MAX_BLEND = (int) ((BLEND_RADIUS * 8) / BLEND_DISTRIBUTION); + + private static final int PLANE_ALL = 0b1111; + + private static final Pattern PATTERN = Pattern.compile("^[ \\t]*(?" + + "//.*$|" + // //comment + "m[ \\t]*(?[0-9]+)[ \\t]+(?[0-9]+)|" + // m + "r[ \\t]*(?[0-9]+)[ \\t]+(?[0-9]+)|" + // r + "R[ \\t]*(?[0-9]+)[ \\t]+(?[0-9]+)[ \\t]+(?[0-9]+)[ \\t]+(?[0-9]+)|" + // R + "c[ \\t]*(?[0-9-]+)[ \\t]+(?[0-9-]+)|" + // c + "C[ \\t]*(?[0-9-]+)[ \\t]+(?[0-9-]+)[ \\t]+(?[0-9-]+)[ \\t]+(?[0-9-]+)|" + // C + "#[ \\t]*(?[0-9a-fA-F]{6}|[0-9a-fA-F]{3})|" + // # or # + "p[ \\t]*(?all|0?[ \\t]*1?[ \\t]*2?[ \\t]*3?)|" + // p all or p<1><2><3><4> + "b[ \\t]*(?[0-9]+)|" + // b + "bounds[ \\t]+(?[0-9]+)[ \\t]+(?[0-9]+)[ \\t]+(?[0-9]+)[ \\t]+(?[0-9]+)" + // bounds + ")[ \\t]*"); + + private final int[] chunks; + private final int[] planeOverrides; + + private final int x1; + private final int y1; + private final int x2; + private final int y2; + private final int stride; + + public Skybox(InputStream is, String filename) throws IOException + { + this(new InputStreamReader(is, StandardCharsets.UTF_8), filename); + } + + public Skybox(Reader reader, String filename) throws IOException + { + int[] chunks = null; + int[] planeOverrides = new int[64]; + int planeOverrideEnd = 0; + int x1 = 0, y1 = 0, x2 = 0, y2 = 0, stride = 0; + BufferedReader br = new BufferedReader(reader); + int lineNo = 1; + int color = 0; + int plane = PLANE_ALL; + int rx1 = 0, ry1 = 0, rx2 = 0, ry2 = 0; + try + { + Matcher m = PATTERN.matcher(""); + for (String line; (line = br.readLine()) != null; lineNo++) + { + m.reset(line); + int end = 0; + while (end < line.length()) + { + m.region(end, line.length()); + if (!m.find()) + { + throw new IllegalArgumentException("Unexpected: \"" + line.substring(end) + "\" (" + filename + ":" + lineNo + ")"); + } + end = m.end(); + + String expr = m.group("expr"); + if (expr == null || expr.length() <= 0 || expr.startsWith("//")) + { + continue; + } + + if (chunks == null) + { + if (!expr.startsWith("bounds")) + { + throw new IllegalArgumentException("Expected bounds (" + filename + ":" + lineNo + ")"); + } + x1 = Integer.parseInt(m.group("bx1")) * 8; + y1 = Integer.parseInt(m.group("by1")) * 8; + x2 = (Integer.parseInt(m.group("bx2")) + 1) * 8; + y2 = (Integer.parseInt(m.group("by2")) + 1) * 8; + stride = (x2 - x1); + chunks = new int[stride * (y2 - y1)]; + Arrays.fill(chunks, -1); + continue; + } + + char cha = expr.charAt(0); + switch (cha) + { + case '#': + String sColor = m.group("color"); + int scolor = Integer.parseInt(sColor, 16); + int cr, cg, cb; + if (sColor.length() == 3) + { + // Expand #RGB to #RRGGBB + cr = scolor >> 8 & 0xF; + cr |= cr << 4; + cg = scolor >> 4 & 0xF; + cg |= cg << 4; + cb = scolor & 0xF; + cb |= cb << 4; + } + else + { + cr = scolor >> 16 & 0xFF; + cg = scolor >> 8 & 0xFF; + cb = scolor & 0xFF; + } + + // Convert to YCoCg24 because it produces less blending artifacts due + // to mismatched skew rates + // See: https://stackoverflow.com/questions/10566668/lossless-rgb-to-ycbcr-transformation + byte cco = (byte) (cb - cr); + byte tmp = (byte) (cr + (cco >> 1)); + byte ccg = (byte) (tmp - cg); + byte cy = (byte) (cg + (ccg >> 1)); + + color = color & 0xFF000000 | (cy & 0xFF) << 16 | (cco & 0xFF) << 8 | (ccg & 0xFF); + break; + case 'b': + int iblend = Integer.parseInt(m.group("blend")); + if (iblend < 0) + { + throw new IllegalArgumentException("Blend must be >=0 (" + filename + ":" + lineNo + ")"); + } + if (iblend > MAX_BLEND) + { + throw new IllegalArgumentException("Blend must be <= " + MAX_BLEND + " (" + filename + ":" + lineNo + ")"); + } + color = color & 0x00FFFFFF | iblend << 24; + break; + case 'm': + rx2 = rx1 = Integer.parseInt(m.group("mrx")); + ry2 = ry1 = Integer.parseInt(m.group("mry")); + break; + case 'p': + String planes = m.group("plane"); + if ("all".equals(planes)) + { + plane = PLANE_ALL; + } + else + { + plane = 0; + for (int i = 0; i < planes.length(); i++) + { + plane |= 1 << (planes.charAt(i) - '0'); + } + } + break; + case 'r': + case 'R': + if (cha == 'r') + { + rx2 = rx1 = Integer.parseInt(m.group("rx")); + ry2 = ry1 = Integer.parseInt(m.group("ry")); + } + else + { + rx1 = Integer.parseInt(m.group("rx1")); + ry1 = Integer.parseInt(m.group("ry1")); + rx2 = Integer.parseInt(m.group("rx2")); + ry2 = Integer.parseInt(m.group("ry2")); + } + // fallthrough + case 'c': + case 'C': + int cx1 = rx1 * 8; + int cy1 = ry1 * 8; + int cx2 = rx2 * 8 + 7; + int cy2 = ry2 * 8 + 7; + if (cha == 'c') + { + cx2 = cx1 = cx1 + Integer.parseInt(m.group("cx")); + cy2 = cy1 = cy1 + Integer.parseInt(m.group("cy")); + } + else if (cha == 'C') + { + cx2 = cx1 + Integer.parseInt(m.group("cx2")); + cy2 = cy1 + Integer.parseInt(m.group("cy2")); + cx1 = cx1 + Integer.parseInt(m.group("cx1")); + cy1 = cy1 + Integer.parseInt(m.group("cy1")); + } + + if (cx1 < x1 || cy1 < y1 || cx2 >= x2 || cy2 >= y2) + { + throw new IllegalArgumentException("Coordinate out of bounds (" + filename + ":" + lineNo + ")"); + } + if (cx1 > cx2 || cy1 > cy2) + { + throw new IllegalArgumentException("First coord must be before second (" + filename + ":" + lineNo + ")"); + } + + for (int y = cy1; y <= cy2; y++) + { + int yoffset = stride * (y - y1); + for (int x = cx1; x <= cx2; x++) + { + int offset = (x - x1) + yoffset; + + if (plane == PLANE_ALL) + { + chunks[offset] = color; + } + else + { + // We are not setting all planes in this chunk, so allocate a plane override section + // and add a pointer to it in the normal chunk's space. We do this because most chunks + // do not have plane-specific data + int ocv = chunks[offset]; + int poptr; + if ((ocv & 0x8000_0000) != 0 && ocv != -1) + { + // Existing plane override + poptr = ocv & 0x7FFF_FFFF; + } + else + { + poptr = planeOverrideEnd; + planeOverrideEnd += 4; + if (planeOverrideEnd > planeOverrides.length) + { + planeOverrides = Arrays.copyOf(planeOverrides, planeOverrideEnd + 64); + } + chunks[offset] = poptr | 0x8000_0000; + for (int i = 0; i < 4; i++) + { + planeOverrides[poptr + i] = ocv; + } + } + + for (int i = 0; i < 4; i++) + { + if ((plane & (1 << i)) != 0) + { + planeOverrides[poptr + i] = color; + } + } + } + } + } + break; + } + } + } + } + catch (NumberFormatException ex) + { + throw new IllegalArgumentException("Expected number (" + filename + ":" + lineNo + ")", ex); + } + if (chunks == null) + { + throw new IllegalArgumentException(filename + ": no data"); + } + + this.chunks = chunks; + this.planeOverrides = planeOverrides; + this.stride = stride; + this.x1 = x1; + this.y1 = y1; + this.x2 = x2; + this.y2 = y2; + } + + private int chunkData(int cx, int cy, int plane, ChunkMapper chunkMapper) + { + if (chunkMapper != null) + { + int itp = chunkMapper.getTemplateChunk(cx, cy, plane); + if (itp == -1) + { + return -1; + } + + cy = itp >> 3 & 0x7FF; + cx = itp >> 14 & 0x3FF; + plane = itp >> 24 & 0x3; + } + + if (cx < x1) + { + cx = x1; + } + if (cx >= x2) + { + cx = x2 - 1; + } + if (cy < y1) + { + cy = y1; + } + if (cy >= y2) + { + cy = y2 - 1; + } + + int cv = chunks[(stride * (cy - y1)) + (cx - x1)]; + if (cv == -1) + { + return -1; + } + if ((cv & 0x8000_0000) != 0) + { + cv = planeOverrides[(cv & 0x7FFF_FFFF) | plane]; + } + return cv; + } + + /** + * Calculates the RGB color for a specific world coordinate. Arguments are floats for sub-tile accuracy. + * + * @param x Sample X coordinate in tiles + * @param y Samlpe Y coordinate in tiles + * @param x Player X coordinate in tiles + * @param y Player Y coordinate in tiles + * @param chunkMapper maps chunks to their instance templates, or null if not in an instance + */ + public int getColorForPoint(double x, double y, int px, int py, int plane, double brightness, ChunkMapper chunkMapper) + { + x /= 8.d; + y /= 8.d; + + int centerChunkData = chunkData(px / 8, py / 8, plane, chunkMapper); + if (centerChunkData == -1) + { + // No data in the center chunk? + return 0; + } + + double t = 0; + double ty = 0; + double tco = 0; + double tcg = 0; + + int xmin = (int) (x - BLEND_RADIUS); + int xmax = (int) Math.ceil(x + BLEND_RADIUS); + int ymin = (int) (y - BLEND_RADIUS); + int ymax = (int) Math.ceil(y + BLEND_RADIUS); + + for (int ucx = xmin; ucx < xmax; ucx++) + { + for (int ucy = ymin; ucy <= ymax; ucy++) + { + int val = chunkData(ucx, ucy, plane, chunkMapper); + if (val == -1) + { + continue; + } + + // Get the blend value, add 1/8 tile to make sure we don't div/0, convert to chunks + double sigma = ((val >>> 24) + .125) / 8.d; + + // Calculate how far we have to be away before we can discard this value without + // becoming visibly discontinuous + double minDist = 1 + (sigma * BLEND_DISTRIBUTION); + + // Try to fast-fail + double dxl = ucx - x; + double dxh = dxl + 1.d; + if (dxl < -minDist || dxl > minDist) + { + continue; + } + + double dyl = ucy - y; + double dyh = dyl + 1.d; + if (dyl < -minDist || dyh > minDist) + { + continue; + } + + // Calculate integrate a gaussian distribution in each dimension for + // this chunk relative to the requested point + double erfdivc = sigma * SQRT2; + double m = (erf(dxl / erfdivc) - erf(dxh / erfdivc)) * (erf(dyl / erfdivc) - erf(dyh / erfdivc)); + + // Load our YCoCg24 values into floats + double vy = (val >>> 16 & 0xFF) / 255.d; + double vco = (byte) (val >>> 8) / 128.d; + double vcg = (byte) val / 128.d; + + // And multiply by the weight + ty += vy * m; + tco += vco * m; + tcg += vcg * m; + t += m; + } + } + + // Convert back to int range values, and bounds check while we are at it + byte ay = (byte) Math.min(Math.max(Math.round(ty / t * 255.d), 0), 255); + byte aco = (byte) Math.min(Math.max(Math.round(tco * 128.d / t), -128), 127); + byte acg = (byte) Math.min(Math.max(Math.round(tcg * 128.d / t), -128), 127); + + // convert back to rgb from YCoCg24 + int g = (ay - (acg >> 1)) & 0xFF; + int tmp = (g + acg) & 0xFF; + int r = (tmp - (aco >> 1)) & 0xFF; + int b = (r + aco) & 0xFF; + + // increase brightness with HSB + float[] hsb = Color.RGBtoHSB(r, g, b, null); + hsb[2] = (float) Math.pow(hsb[2], brightness); + + return 0xFFFFFF & Color.HSBtoRGB(hsb[0], hsb[1], hsb[2]); + } + + /** + * Approximation of erf 'Gauss error function' which is used to calculate + * the cumulative distribution of a gaussian distribution. + * This is used to simulate a large kernel gaussian blur without having + * to sample the same chunk multiple times. + */ + private double erf(double x) + { + double ax = Math.abs(x); + double t = 1.d / (1.d + (ax * .3275911d)); + double y = 1.d - ((((((1.061405429d * t) - 1.453152027d) * t) + 1.421413741d) * t - 0.284496736d) * t + 0.254829592d) * t * Math.exp(-ax * ax); + return Math.copySign(y, x); + } + + /** + * Draws the skybox map to an image + * + * @param resolution The number of pixels per tile + * @param line How many tiles to put a line + * @param plane the plane (0-4) to render + */ + BufferedImage render(double resolution, int line, int plane, ChunkMapper chunkMapper) + { + int w = (int) (((x2 - x1) * 8) * resolution); + int h = (int) (((y2 - y1) * 8) * resolution); + BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); + int lineEvery = line <= 0 ? Integer.MAX_VALUE : (int) (line * resolution); + + for (int y = 0; y < h; y++) + { + for (int x = 0; x < w; x++) + { + int color; + if (x % lineEvery == 0 || y % lineEvery == 0) + { + color = 0x00FFFFFF; + } + else + { + double fx = (x1 * 8) + (x / resolution); + double fy = (y1 * 8) + (y / resolution); + color = getColorForPoint(fx, fy, (int) fx, (int) fy, plane, .8, chunkMapper); + } + img.setRGB(x, h - 1 - y, color | 0xFF000000); + } + } + + return img; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skybox/SkyboxPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/skybox/SkyboxPlugin.java new file mode 100644 index 0000000000..468e23a201 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skybox/SkyboxPlugin.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2019 Abex + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.skybox; + +import com.google.inject.Inject; +import com.google.inject.Provides; +import java.awt.Color; +import java.io.IOException; +import net.runelite.api.Client; +import net.runelite.api.Constants; +import net.runelite.api.GameState; +import net.runelite.api.Player; +import net.runelite.api.coords.LocalPoint; +import net.runelite.api.events.BeforeRender; +import net.runelite.api.events.GameStateChanged; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; + +@PluginDescriptor( + name = "Skybox", + description = "Draws an oldschool styled skybox", + enabledByDefault = false, + tags = {"sky"} +) +public class SkyboxPlugin extends Plugin +{ + @Inject + private Client client; + + @Inject + private SkyboxPluginConfig config; + + private Skybox skybox; + + @Override + public void startUp() throws IOException + { + skybox = new Skybox(SkyboxPlugin.class.getResourceAsStream("skybox.txt"), "skybox.txt"); + } + + @Override + public void shutDown() + { + client.setSkyboxColor(0); + skybox = null; + } + + @Provides + SkyboxPluginConfig provideConfig(ConfigManager configManager) + { + return configManager.getConfig(SkyboxPluginConfig.class); + } + + private int mapChunk(int cx, int cy, int plane) + { + cx -= client.getBaseX() / 8; + cy -= client.getBaseY() / 8; + + int[][] instanceTemplateChunks = client.getInstanceTemplateChunks()[plane]; + // Blending can access this out of bounds, so do a range check + if (cx < 0 || cx >= instanceTemplateChunks.length || cy < 0 || cy >= instanceTemplateChunks[cx].length) + { + return -1; + } + + return instanceTemplateChunks[cx][cy]; + } + + @Subscribe + public void onBeforeRender(BeforeRender r) + { + if (skybox == null || client.getGameState() != GameState.LOGGED_IN) + { + return; + } + + Player player = client.getLocalPlayer(); + if (player == null) + { + return; + } + + Color overrideColor = player.getWorldLocation().getY() < Constants.OVERWORLD_MAX_Y + ? config.customOverworldColor() : config.customOtherColor(); + if (overrideColor != null) + { + client.setSkyboxColor(overrideColor.getRGB()); + return; + } + + int px, py; + if (client.getOculusOrbState() == 1) + { + px = client.getOculusOrbFocalPointX(); + py = client.getOculusOrbFocalPointY(); + } + else + { + LocalPoint p = player.getLocalLocation(); + px = p.getX(); + py = p.getY(); + } + + // Inverse of camera location / 2 + int spx = -((client.getCameraX() - px) >> 1); + int spy = -((client.getCameraY() - py) >> 1); + + int baseX = client.getBaseX(); + int baseY = client.getBaseY(); + + client.setSkyboxColor(skybox.getColorForPoint( + baseX + ((px + spx) / 128.f), + baseY + ((py + spy) / 128.f), + baseX + (px / 128), + baseY + (py / 128), + client.getPlane(), + client.getTextureProvider().getBrightness(), + client.isInInstancedRegion() ? this::mapChunk : null + )); + } + + @Subscribe + public void onGameStateChanged(GameStateChanged gameStateChanged) + { + if (gameStateChanged.getGameState() == GameState.LOGIN_SCREEN) + { + client.setSkyboxColor(0); + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skybox/SkyboxPluginConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/skybox/SkyboxPluginConfig.java new file mode 100644 index 0000000000..3968724b72 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skybox/SkyboxPluginConfig.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2019 logarrhytmic + * 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.skybox; + +import java.awt.Color; +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("skybox") +public interface SkyboxPluginConfig extends Config +{ + @ConfigItem( + keyName = "customOverworldColor", + name = "Overworld sky color", + description = "Sets the color to use for the sky in overworld areas.", + position = 1 + ) + Color customOverworldColor(); + + @ConfigItem( + keyName = "customOtherColor", + name = "Cave sky color", + description = "Sets the color to use for the sky in non-overworld areas.", + position = 2 + ) + Color customOtherColor(); +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/wiki/WikiConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/wiki/WikiConfig.java new file mode 100644 index 0000000000..5462023f14 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/wiki/WikiConfig.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2020, Henry Darnell + * 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.wiki; + +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup(WikiPlugin.CONFIG_GROUP_KEY) +public interface WikiConfig extends Config +{ + @ConfigItem( + keyName = "leftClickSearch", + name = "Left Click Search", + description = "Swap left-click on the Wiki button to Search", + position = 1 + ) + default boolean leftClickSearch() + { + return false; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/wiki/WikiPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/wiki/WikiPlugin.java new file mode 100644 index 0000000000..2f0609bcfe --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/wiki/WikiPlugin.java @@ -0,0 +1,426 @@ +/* + * Copyright (c) 2018 Abex + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.wiki; + +import com.google.inject.Provides; +import java.util.Arrays; +import java.util.stream.Stream; +import javax.inject.Inject; +import javax.inject.Provider; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.Client; +import net.runelite.api.MenuAction; +import net.runelite.api.MenuEntry; +import net.runelite.api.NPC; +import net.runelite.api.NPCComposition; +import net.runelite.api.ObjectComposition; +import net.runelite.api.SpriteID; +import net.runelite.api.coords.WorldPoint; +import net.runelite.api.events.MenuEntryAdded; +import net.runelite.api.events.MenuOptionClicked; +import net.runelite.api.events.WidgetHiddenChanged; +import net.runelite.api.events.WidgetLoaded; +import net.runelite.api.widgets.JavaScriptCallback; +import net.runelite.api.widgets.Widget; +import net.runelite.api.widgets.WidgetConfig; +import net.runelite.api.widgets.WidgetID; +import net.runelite.api.widgets.WidgetInfo; +import net.runelite.api.widgets.WidgetPositionMode; +import net.runelite.api.widgets.WidgetType; +import net.runelite.client.callback.ClientThread; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.events.ConfigChanged; +import net.runelite.client.game.ItemManager; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.ui.JagexColors; +import net.runelite.client.util.LinkBrowser; +import net.runelite.client.util.Text; +import okhttp3.HttpUrl; + +@Slf4j +@PluginDescriptor( + name = "Wiki", + description = "Adds a Wiki button that takes you to the OSRS Wiki" +) +public class WikiPlugin extends Plugin +{ + static final HttpUrl WIKI_BASE = HttpUrl.parse("https://oldschool.runescape.wiki"); + static final HttpUrl WIKI_API = WIKI_BASE.newBuilder().addPathSegments("api.php").build(); + static final String UTM_SORUCE_KEY = "utm_source"; + static final String UTM_SORUCE_VALUE = "runelite"; + + private static final String MENUOP_WIKI = "Wiki"; + + @Inject + private WikiConfig config; + + @Inject + private ClientThread clientThread; + + @Inject + private Client client; + + @Inject + private ItemManager itemManager; + + @Inject + private Provider wikiSearchChatboxTextInputProvider; + + private Widget icon; + + private boolean wikiSelected = false; + + static final String CONFIG_GROUP_KEY = "wiki"; + + @Provides + WikiConfig getConfig(ConfigManager configManager) + { + return configManager.getConfig(WikiConfig.class); + } + + @Override + public void startUp() + { + clientThread.invokeLater(this::addWidgets); + } + + @Override + public void shutDown() + { + clientThread.invokeLater(this::removeWidgets); + } + + private void removeWidgets() + { + + Widget minimapOrbs = client.getWidget(WidgetInfo.MINIMAP_ORBS); + if (minimapOrbs == null) + { + return; + } + Widget[] children = minimapOrbs.getChildren(); + if (children == null || children.length < 1) + { + return; + } + children[0] = null; + + Widget vanilla = client.getWidget(WidgetInfo.MINIMAP_WIKI_BANNER); + if (vanilla != null) + { + vanilla.setHidden(false); + } + + onDeselect(); + client.setSpellSelected(false); + } + + @Subscribe + private void onWidgetLoaded(WidgetLoaded l) + { + if (l.getGroupId() == WidgetID.MINIMAP_GROUP_ID) + { + addWidgets(); + } + } + + private void addWidgets() + { + Widget minimapOrbs = client.getWidget(WidgetInfo.MINIMAP_ORBS); + if (minimapOrbs == null) + { + return; + } + + Widget vanilla = client.getWidget(WidgetInfo.MINIMAP_WIKI_BANNER); + if (vanilla != null) + { + vanilla.setHidden(true); + } + + icon = minimapOrbs.createChild(0, WidgetType.GRAPHIC); + icon.setSpriteId(SpriteID.WIKI_DESELECTED); + icon.setOriginalX(0); + icon.setOriginalY(0); + icon.setXPositionMode(WidgetPositionMode.ABSOLUTE_RIGHT); + icon.setYPositionMode(WidgetPositionMode.ABSOLUTE_BOTTOM); + icon.setOriginalWidth(40); + icon.setOriginalHeight(14); + icon.setTargetVerb("Lookup"); + icon.setName("Wiki"); + icon.setClickMask(WidgetConfig.USE_GROUND_ITEM | WidgetConfig.USE_ITEM | WidgetConfig.USE_NPC + | WidgetConfig.USE_OBJECT | WidgetConfig.USE_WIDGET); + icon.setNoClickThrough(true); + icon.setOnTargetEnterListener((JavaScriptCallback) ev -> + { + wikiSelected = true; + icon.setSpriteId(SpriteID.WIKI_SELECTED); + client.setAllWidgetsAreOpTargetable(true); + }); + + final int searchIndex = config.leftClickSearch() ? 4 : 5; + icon.setAction(searchIndex, "Search"); + icon.setOnOpListener((JavaScriptCallback) ev -> + { + if (ev.getOp() == searchIndex + 1) + { + openSearchInput(); + } + }); + + // This doesn't always run because we cancel the menuop + icon.setOnTargetLeaveListener((JavaScriptCallback) ev -> onDeselect()); + icon.revalidate(); + } + + @Subscribe + private void onWidgetHiddenChanged(WidgetHiddenChanged ev) + { + if (ev.getWidget().getId() == WidgetInfo.MINIMAP_WIKI_BANNER.getId()) + { + ev.getWidget().setHidden(true); + } + } + + @Subscribe + public void onConfigChanged(ConfigChanged event) + { + if (event.getGroup().equals(CONFIG_GROUP_KEY)) + { + clientThread.invokeLater(() -> + { + removeWidgets(); + addWidgets(); + }); + } + } + + private void onDeselect() + { + client.setAllWidgetsAreOpTargetable(false); + + wikiSelected = false; + if (icon != null) + { + icon.setSpriteId(SpriteID.WIKI_DESELECTED); + } + } + + @Subscribe + private void onMenuOptionClicked(MenuOptionClicked ev) + { + optarget: + if (wikiSelected) + { + onDeselect(); + client.setSpellSelected(false); + ev.consume(); + + String type; + int id; + String name; + WorldPoint location; + + switch (ev.getMenuAction()) + { + case RUNELITE: + // This is a quest widget op + break optarget; + case CANCEL: + return; + case ITEM_USE_ON_WIDGET: + case SPELL_CAST_ON_GROUND_ITEM: + { + type = "item"; + id = itemManager.canonicalize(ev.getId()); + name = itemManager.getItemComposition(id).getName(); + location = null; + break; + } + case SPELL_CAST_ON_NPC: + { + type = "npc"; + NPC npc = client.getCachedNPCs()[ev.getId()]; + NPCComposition nc = npc.getTransformedComposition(); + id = nc.getId(); + name = nc.getName(); + location = npc.getWorldLocation(); + break; + } + case SPELL_CAST_ON_GAME_OBJECT: + { + type = "object"; + ObjectComposition lc = client.getObjectDefinition(ev.getId()); + if (lc.getImpostorIds() != null) + { + lc = lc.getImpostor(); + } + id = lc.getId(); + name = lc.getName(); + location = WorldPoint.fromScene(client, ev.getActionParam(), ev.getWidgetId(), client.getPlane()); + break; + } + case SPELL_CAST_ON_WIDGET: + Widget w = getWidget(ev.getWidgetId(), ev.getActionParam()); + + if (w.getType() == WidgetType.GRAPHIC && w.getItemId() != -1) + { + type = "item"; + id = itemManager.canonicalize(w.getItemId()); + name = itemManager.getItemComposition(id).getName(); + location = null; + break; + } + // fallthrough + default: + log.info("Unknown menu option: {} {} {}", ev, ev.getMenuAction(), ev.getMenuAction() == MenuAction.CANCEL); + return; + } + + name = Text.removeTags(name); + HttpUrl.Builder urlBuilder = WIKI_BASE.newBuilder(); + urlBuilder.addPathSegments("w/Special:Lookup") + .addQueryParameter("type", type) + .addQueryParameter("id", "" + id) + .addQueryParameter("name", name) + .addQueryParameter(UTM_SORUCE_KEY, UTM_SORUCE_VALUE); + + if (location != null) + { + urlBuilder.addQueryParameter("x", "" + location.getX()) + .addQueryParameter("y", "" + location.getY()) + .addQueryParameter("plane", "" + location.getPlane()); + } + + HttpUrl url = urlBuilder.build(); + + LinkBrowser.browse(url.toString()); + return; + } + + if (ev.getMenuAction() == MenuAction.RUNELITE) + { + switch (ev.getMenuOption()) + { + case MENUOP_WIKI: + LinkBrowser.browse(WIKI_BASE.newBuilder() + .addPathSegment("w") + .addPathSegment(Text.removeTags(ev.getMenuTarget())) + .addQueryParameter(UTM_SORUCE_KEY, UTM_SORUCE_VALUE) + .build().toString()); + + } + } + } + + private void openSearchInput() + { + wikiSearchChatboxTextInputProvider.get() + .build(); + } + + private Widget getWidget(int wid, int index) + { + Widget w = client.getWidget(wid); + if (index != -1) + { + w = w.getChild(index); + } + return w; + } + + @Subscribe + public void onMenuEntryAdded(MenuEntryAdded event) + { + int widgetIndex = event.getActionParam0(); + int widgetID = event.getActionParam1(); + MenuEntry[] menuEntries = client.getMenuEntries(); + + if (wikiSelected && event.getType() == MenuAction.SPELL_CAST_ON_WIDGET.getId()) + { + Widget w = getWidget(widgetID, widgetIndex); + if (w.getType() == WidgetType.GRAPHIC && w.getItemId() != -1) + { + for (int ourEntry = menuEntries.length - 1;ourEntry >= 0; ourEntry--) + { + MenuEntry entry = menuEntries[ourEntry]; + if (entry.getType() == MenuAction.SPELL_CAST_ON_WIDGET.getId()) + { + int id = itemManager.canonicalize(w.getItemId()); + String name = itemManager.getItemComposition(id).getName(); + entry.setTarget(JagexColors.MENU_TARGET_TAG + name); + break; + } + } + client.setMenuEntries(menuEntries); + } + else + { + // we don't support this widget + // remove the last SPELL_CAST_ON_WIDGET; we can't blindly remove the top action because some other + // plugin might have added something on this same event, and we probably shouldn't remove that instead + MenuEntry[] oldEntries = menuEntries; + menuEntries = Arrays.copyOf(menuEntries, menuEntries.length - 1); + for (int ourEntry = oldEntries.length - 1; + ourEntry >= 2 && oldEntries[oldEntries.length - 1].getType() != MenuAction.SPELL_CAST_ON_WIDGET.getId(); + ourEntry--) + { + menuEntries[ourEntry - 1] = oldEntries[ourEntry]; + } + client.setMenuEntries(menuEntries); + } + } + + if (WidgetInfo.TO_GROUP(widgetID) == WidgetInfo.SKILLS_CONTAINER.getGroupId()) + { + Widget w = getWidget(widgetID, widgetIndex); + if (w.getActions() == null || w.getParentId() != WidgetInfo.SKILLS_CONTAINER.getId()) + { + return; + } + + String action = Stream.of(w.getActions()) + .filter(s -> s != null && !s.isEmpty()) + .findFirst().orElse(null); + if (action == null) + { + return; + } + + menuEntries = Arrays.copyOf(menuEntries, menuEntries.length + 1); + + MenuEntry menuEntry = menuEntries[menuEntries.length - 1] = new MenuEntry(); + menuEntry.setTarget(action.replace("View ", "").replace(" guide", "")); + menuEntry.setOption(MENUOP_WIKI); + menuEntry.setParam0(widgetIndex); + menuEntry.setParam1(widgetID); + menuEntry.setType(MenuAction.RUNELITE.getId()); + + client.setMenuEntries(menuEntries); + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/wiki/WikiSearchChatboxTextInput.java b/runelite-client/src/main/java/net/runelite/client/plugins/wiki/WikiSearchChatboxTextInput.java new file mode 100644 index 0000000000..8552d90eef --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/wiki/WikiSearchChatboxTextInput.java @@ -0,0 +1,313 @@ +/* + * Copyright (c) 2018 Abex + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.wiki; + +import com.google.common.collect.ImmutableList; +import com.google.common.reflect.TypeToken; +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonParseException; +import com.google.gson.JsonParser; +import com.google.inject.Inject; +import java.awt.event.KeyEvent; +import java.io.IOException; +import java.util.List; +import java.util.concurrent.Future; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import javax.inject.Named; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.widgets.JavaScriptCallback; +import net.runelite.api.widgets.Widget; +import net.runelite.api.widgets.WidgetPositionMode; +import net.runelite.api.widgets.WidgetSizeMode; +import net.runelite.api.widgets.WidgetTextAlignment; +import net.runelite.api.widgets.WidgetType; +import net.runelite.client.callback.ClientThread; +import net.runelite.client.game.chatbox.ChatboxPanelManager; +import net.runelite.client.game.chatbox.ChatboxTextInput; +import net.runelite.client.ui.JagexColors; +import net.runelite.client.util.LinkBrowser; +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; + +@Slf4j +public class WikiSearchChatboxTextInput extends ChatboxTextInput +{ + private static final int LINE_HEIGHT = 20; + private static final int CHATBOX_HEIGHT = 120; + private static final int MAX_NUM_PREDICTIONS = (CHATBOX_HEIGHT / LINE_HEIGHT) - 2; // 1 title, 1 edit + + private static final int PREDICTION_DEBOUNCE_DELAY_MS = 200; + + private final ChatboxPanelManager chatboxPanelManager; + private final Gson gson = new Gson(); + + private Future runningRequest = null; + private List predictions = ImmutableList.of(); + + private int selectedPrediction = -1; + private String offPrediction = null; + + @Inject + public WikiSearchChatboxTextInput(ChatboxPanelManager chatboxPanelManager, ClientThread clientThread, + ScheduledExecutorService scheduledExecutorService, @Named("developerMode") final boolean developerMode, + OkHttpClient okHttpClient) + { + super(chatboxPanelManager, clientThread); + this.chatboxPanelManager = chatboxPanelManager; + + lines(1); + prompt("OSRS Wiki Search"); + onDone(string -> + { + if (string != null && string.length() > 0) + { + search(string); + } + }); + onChanged(searchString -> + { + selectedPrediction = -1; + Future rr = runningRequest; + if (rr != null) + { + rr.cancel(false); + } + if (searchString.length() <= 1) + { + runningRequest = null; + clientThread.invokeLater(() -> + { + predictions = ImmutableList.of(); + update(); + }); + return; + } + runningRequest = scheduledExecutorService.schedule(() -> + { + HttpUrl url = WikiPlugin.WIKI_API.newBuilder() + .addQueryParameter("action", "opensearch") + .addQueryParameter("search", searchString) + .addQueryParameter("redirects", "resolve") + .addQueryParameter("format", "json") + .addQueryParameter("warningsaserror", Boolean.toString(developerMode)) + .build(); + + Request req = new Request.Builder() + .url(url) + .build(); + + okHttpClient.newCall(req).enqueue(new Callback() + { + @Override + public void onFailure(Call call, IOException e) + { + log.warn("error searching wiki", e); + } + + @Override + public void onResponse(Call call, Response response) throws IOException + { + String body = response.body().string(); + try // NOPMD: UseTryWithResources + { + JsonArray jar = new JsonParser().parse(body).getAsJsonArray(); + List apredictions = gson.fromJson(jar.get(1), new TypeToken>() + { + }.getType()); + + if (apredictions.size() > MAX_NUM_PREDICTIONS) + { + apredictions = apredictions.subList(0, MAX_NUM_PREDICTIONS); + } + + final List bpredictions = apredictions; + + clientThread.invokeLater(() -> + { + predictions = bpredictions; + update(); + }); + } + catch (JsonParseException | IllegalStateException | IndexOutOfBoundsException e) + { + log.warn("error parsing wiki response {}", body, e); + } + finally + { + response.close(); + } + } + }); + + runningRequest = null; + }, PREDICTION_DEBOUNCE_DELAY_MS, TimeUnit.MILLISECONDS); + }); + } + + @Override + protected void update() + { + Widget container = chatboxPanelManager.getContainerWidget(); + container.deleteAllChildren(); + + Widget promptWidget = container.createChild(-1, WidgetType.TEXT); + promptWidget.setText(getPrompt()); + promptWidget.setTextColor(0x800000); + promptWidget.setFontId(getFontID()); + promptWidget.setXPositionMode(WidgetPositionMode.ABSOLUTE_CENTER); + promptWidget.setOriginalX(0); + promptWidget.setYPositionMode(WidgetPositionMode.ABSOLUTE_TOP); + promptWidget.setOriginalY(5); + promptWidget.setOriginalHeight(LINE_HEIGHT); + promptWidget.setXTextAlignment(WidgetTextAlignment.CENTER); + promptWidget.setYTextAlignment(WidgetTextAlignment.CENTER); + promptWidget.setWidthMode(WidgetSizeMode.MINUS); + promptWidget.revalidate(); + + buildEdit(0, 5 + LINE_HEIGHT, container.getWidth(), LINE_HEIGHT); + + Widget separator = container.createChild(-1, WidgetType.LINE); + separator.setXPositionMode(WidgetPositionMode.ABSOLUTE_CENTER); + separator.setOriginalX(0); + separator.setYPositionMode(WidgetPositionMode.ABSOLUTE_TOP); + separator.setOriginalY(4 + (LINE_HEIGHT * 2)); + separator.setOriginalHeight(0); + separator.setOriginalWidth(16); + separator.setWidthMode(WidgetSizeMode.MINUS); + separator.revalidate(); + + for (int i = 0; i < predictions.size(); i++) + { + String pred = predictions.get(i); + int y = 6 + (LINE_HEIGHT * (2 + i)); + + Widget bg = container.createChild(-1, WidgetType.RECTANGLE); + bg.setTextColor(0x4444DD); + bg.setFilled(true); + bg.setXPositionMode(WidgetPositionMode.ABSOLUTE_CENTER); + bg.setOriginalX(1); + bg.setYPositionMode(WidgetPositionMode.ABSOLUTE_TOP); + bg.setOriginalY(y); + bg.setOriginalHeight(LINE_HEIGHT); + bg.setOriginalWidth(16); + bg.setWidthMode(WidgetSizeMode.MINUS); + bg.revalidate(); + bg.setName(JagexColors.MENU_TARGET_TAG + pred); + bg.setAction(0, "Open"); + bg.setHasListener(true); + bg.setOnOpListener((JavaScriptCallback) ev -> search(pred)); + + Widget text = container.createChild(-1, WidgetType.TEXT); + text.setText(pred); + text.setFontId(getFontID()); + text.setXPositionMode(WidgetPositionMode.ABSOLUTE_CENTER); + text.setOriginalX(0); + text.setYPositionMode(WidgetPositionMode.ABSOLUTE_TOP); + text.setOriginalY(y); + text.setOriginalHeight(LINE_HEIGHT); + text.setXTextAlignment(WidgetTextAlignment.CENTER); + text.setYTextAlignment(WidgetTextAlignment.CENTER); + text.setWidthMode(WidgetSizeMode.MINUS); + text.revalidate(); + + if (i == selectedPrediction) + { + text.setTextColor(0xFFFFFF); + } + else + { + bg.setOpacity(255); + text.setTextColor(0x000000); + bg.setOnMouseRepeatListener((JavaScriptCallback) ev -> text.setTextColor(0xFFFFFF)); + bg.setOnMouseLeaveListener((JavaScriptCallback) ev -> text.setTextColor(0x000000)); + } + } + } + + @Override + public void keyPressed(KeyEvent ev) + { + if (!chatboxPanelManager.shouldTakeInput()) + { + return; + } + + switch (ev.getKeyCode()) + { + case KeyEvent.VK_UP: + ev.consume(); + if (selectedPrediction > -1) + { + selectedPrediction--; + if (selectedPrediction == -1) + { + value(offPrediction); + } + else + { + value(predictions.get(selectedPrediction)); + } + } + break; + case KeyEvent.VK_DOWN: + ev.consume(); + + if (selectedPrediction == -1) + { + offPrediction = getValue(); + } + + selectedPrediction++; + if (selectedPrediction >= predictions.size()) + { + selectedPrediction = predictions.size() - 1; + } + + if (selectedPrediction != -1) + { + value(predictions.get(selectedPrediction)); + } + break; + default: + super.keyPressed(ev); + } + } + + private void search(String search) + { + LinkBrowser.browse(WikiPlugin.WIKI_BASE.newBuilder() + .addQueryParameter("search", search) + .addQueryParameter(WikiPlugin.UTM_SORUCE_KEY, WikiPlugin.UTM_SORUCE_VALUE) + .build() + .toString()); + chatboxPanelManager.close(); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/RegionFilterMode.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/RegionFilterMode.java new file mode 100644 index 0000000000..2a1a244b89 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/RegionFilterMode.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2019, Liam Edwards + * 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.worldhopper; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import net.runelite.http.api.worlds.WorldRegion; + +@NoArgsConstructor +@AllArgsConstructor +public enum RegionFilterMode +{ + NONE, + AUSTRALIA(WorldRegion.AUSTRALIA), + GERMANY(WorldRegion.GERMANY), + UNITED_KINGDOM(WorldRegion.UNITED_KINGDOM) + { + @Override + public String toString() + { + return "U.K."; + } + }, + UNITED_STATES(WorldRegion.UNITED_STATES_OF_AMERICA) + { + @Override + public String toString() + { + return "USA"; + } + }; + + @Getter + private WorldRegion region; +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/SubscriptionFilterMode.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/SubscriptionFilterMode.java new file mode 100644 index 0000000000..b95a41a468 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/SubscriptionFilterMode.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2019, Shawn + * 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.worldhopper; + +public enum SubscriptionFilterMode +{ + BOTH, + FREE, + MEMBERS +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldHopperConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldHopperConfig.java new file mode 100644 index 0000000000..d1135ffe34 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldHopperConfig.java @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2018, Lotto + * Copyright (c) 2019, gregg1494 + * 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.worldhopper; + +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; +import net.runelite.client.config.Keybind; + +@ConfigGroup(WorldHopperConfig.GROUP) +public interface WorldHopperConfig extends Config +{ + String GROUP = "worldhopper"; + + @ConfigItem( + keyName = "previousKey", + name = "Quick-hop previous", + description = "When you press this key you'll hop to the previous world", + position = 0 + ) + default Keybind previousKey() + { + return new Keybind(KeyEvent.VK_LEFT, InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK); + } + + @ConfigItem( + keyName = "nextKey", + name = "Quick-hop next", + description = "When you press this key you'll hop to the next world", + position = 1 + ) + default Keybind nextKey() + { + return new Keybind(KeyEvent.VK_RIGHT, InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK); + } + + @ConfigItem( + keyName = "quickhopOutOfDanger", + name = "Quick-hop out of dangerous worlds", + description = "Don't hop to a PVP/high risk world when quick-hopping", + position = 2 + ) + default boolean quickhopOutOfDanger() + { + return true; + } + + @ConfigItem( + keyName = "quickHopRegionFilter", + name = "Quick-hop region", + description = "Limit quick-hopping to worlds of a specific region", + position = 3 + ) + default RegionFilterMode quickHopRegionFilter() + { + return RegionFilterMode.NONE; + } + + @ConfigItem( + keyName = "showSidebar", + name = "Show world switcher sidebar", + description = "Show sidebar containing all worlds that mimics in-game interface", + position = 4 + ) + default boolean showSidebar() + { + return true; + } + + @ConfigItem( + keyName = "ping", + name = "Show world ping", + description = "Shows ping to each game world", + position = 5 + ) + default boolean ping() + { + return true; + } + + @ConfigItem( + keyName = "showMessage", + name = "Show world hop message in chat", + description = "Shows what world is being hopped to in the chat", + position = 6 + ) + default boolean showWorldHopMessage() + { + return true; + } + + @ConfigItem( + keyName = "menuOption", + name = "Show Hop-to menu option", + description = "Adds Hop-to menu option to the friends list and friends chat members list", + position = 7 + ) + default boolean menuOption() + { + return true; + } + + @ConfigItem( + keyName = "subscriptionFilter", + name = "Show subscription types", + description = "Only show free worlds, member worlds, or both types of worlds in sidebar", + position = 8 + ) + default SubscriptionFilterMode subscriptionFilter() + { + return SubscriptionFilterMode.BOTH; + } + + @ConfigItem( + keyName = "displayPing", + name = "Display current ping", + description = "Displays ping to current game world", + position = 9 + ) + default boolean displayPing() + { + return false; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldHopperPingOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldHopperPingOverlay.java new file mode 100644 index 0000000000..cf4d1b924c --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldHopperPingOverlay.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2019, gregg1494 + * 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.worldhopper; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import javax.inject.Inject; +import net.runelite.api.Client; +import net.runelite.api.Point; +import net.runelite.api.widgets.Widget; +import net.runelite.api.widgets.WidgetInfo; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayLayer; +import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.OverlayPriority; +import net.runelite.client.ui.overlay.OverlayUtil; + +class WorldHopperPingOverlay extends Overlay +{ + private static final int Y_OFFSET = 11; + private static final int X_OFFSET = 1; + + private final Client client; + private final WorldHopperPlugin worldHopperPlugin; + private final WorldHopperConfig worldHopperConfig; + + @Inject + private WorldHopperPingOverlay(Client client, WorldHopperPlugin worldHopperPlugin, WorldHopperConfig worldHopperConfig) + { + this.client = client; + this.worldHopperPlugin = worldHopperPlugin; + this.worldHopperConfig = worldHopperConfig; + setLayer(OverlayLayer.ABOVE_WIDGETS); + setPriority(OverlayPriority.HIGH); + setPosition(OverlayPosition.DYNAMIC); + } + + @Override + public Dimension render(Graphics2D graphics) + { + if (!worldHopperConfig.displayPing()) + { + return null; + } + + final int ping = worldHopperPlugin.getCurrentPing(); + if (ping < 0) + { + return null; + } + + final String text = ping + " ms"; + final int textWidth = graphics.getFontMetrics().stringWidth(text); + final int textHeight = graphics.getFontMetrics().getAscent() - graphics.getFontMetrics().getDescent(); + + // Adjust ping offset for logout button + Widget logoutButton = client.getWidget(WidgetInfo.RESIZABLE_MINIMAP_LOGOUT_BUTTON); + int xOffset = X_OFFSET; + if (logoutButton != null && !logoutButton.isHidden()) + { + xOffset += logoutButton.getWidth(); + } + + final int width = (int) client.getRealDimensions().getWidth(); + final Point point = new Point(width - textWidth - xOffset, textHeight + Y_OFFSET); + OverlayUtil.renderTextLocation(graphics, point, text, Color.YELLOW); + + return null; + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldHopperPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldHopperPlugin.java new file mode 100644 index 0000000000..8f9b9060b5 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldHopperPlugin.java @@ -0,0 +1,852 @@ +/* + * Copyright (c) 2017, Adam + * Copyright (c) 2018, Lotto + * Copyright (c) 2019, gregg1494 + * 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.worldhopper; + +import com.google.common.base.Stopwatch; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ObjectArrays; +import com.google.inject.Provides; +import java.awt.image.BufferedImage; +import java.time.Instant; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import javax.imageio.ImageIO; +import javax.inject.Inject; +import javax.swing.SwingUtilities; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.ChatMessageType; +import net.runelite.api.ChatPlayer; +import net.runelite.api.FriendsChatMember; +import net.runelite.api.FriendsChatManager; +import net.runelite.api.Client; +import net.runelite.api.Friend; +import net.runelite.api.GameState; +import net.runelite.api.MenuAction; +import net.runelite.api.MenuEntry; +import net.runelite.api.NameableContainer; +import net.runelite.api.Varbits; +import net.runelite.api.events.ChatMessage; +import net.runelite.api.events.GameStateChanged; +import net.runelite.api.events.GameTick; +import net.runelite.api.events.MenuEntryAdded; +import net.runelite.api.events.PlayerMenuOptionClicked; +import net.runelite.api.events.VarbitChanged; +import net.runelite.api.events.WorldListLoad; +import net.runelite.api.widgets.WidgetInfo; +import net.runelite.client.chat.ChatColorType; +import net.runelite.client.chat.ChatMessageBuilder; +import net.runelite.client.chat.ChatMessageManager; +import net.runelite.client.chat.QueuedMessage; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.events.ConfigChanged; +import net.runelite.client.events.WorldsFetch; +import net.runelite.client.game.WorldService; +import net.runelite.client.input.KeyManager; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.worldhopper.ping.Ping; +import net.runelite.client.ui.ClientToolbar; +import net.runelite.client.ui.NavigationButton; +import net.runelite.client.ui.overlay.OverlayManager; +import net.runelite.client.util.ExecutorServiceExceptionLogger; +import net.runelite.client.util.HotkeyListener; +import net.runelite.client.util.Text; +import net.runelite.client.util.WorldUtil; +import net.runelite.http.api.worlds.World; +import net.runelite.http.api.worlds.WorldResult; +import net.runelite.http.api.worlds.WorldType; +import org.apache.commons.lang3.ArrayUtils; + +@PluginDescriptor( + name = "World Hopper", + description = "Allows you to quickly hop worlds", + tags = {"ping", "switcher"} +) +@Slf4j +public class WorldHopperPlugin extends Plugin +{ + private static final int REFRESH_THROTTLE = 60_000; // ms + private static final int MAX_PLAYER_COUNT = 1950; + + private static final int DISPLAY_SWITCHER_MAX_ATTEMPTS = 3; + + private static final String HOP_TO = "Hop-to"; + private static final String KICK_OPTION = "Kick"; + private static final ImmutableList BEFORE_OPTIONS = ImmutableList.of("Add friend", "Remove friend", KICK_OPTION); + private static final ImmutableList AFTER_OPTIONS = ImmutableList.of("Message"); + + @Inject + private Client client; + + @Inject + private ConfigManager configManager; + + @Inject + private ClientToolbar clientToolbar; + + @Inject + private KeyManager keyManager; + + @Inject + private ChatMessageManager chatMessageManager; + + @Inject + private WorldHopperConfig config; + + @Inject + private OverlayManager overlayManager; + + @Inject + private WorldHopperPingOverlay worldHopperOverlay; + + @Inject + private WorldService worldService; + + private ScheduledExecutorService hopperExecutorService; + + private NavigationButton navButton; + private WorldSwitcherPanel panel; + + private net.runelite.api.World quickHopTargetWorld; + private int displaySwitcherAttempts = 0; + + @Getter + private int lastWorld; + + private int favoriteWorld1, favoriteWorld2; + + private ScheduledFuture pingFuture, currPingFuture; + private int currentWorld; + private Instant lastFetch; + + @Getter(AccessLevel.PACKAGE) + private int currentPing; + + private final Map storedPings = new HashMap<>(); + + private final HotkeyListener previousKeyListener = new HotkeyListener(() -> config.previousKey()) + { + @Override + public void hotkeyPressed() + { + hop(true); + } + }; + private final HotkeyListener nextKeyListener = new HotkeyListener(() -> config.nextKey()) + { + @Override + public void hotkeyPressed() + { + hop(false); + } + }; + + @Provides + WorldHopperConfig getConfig(ConfigManager configManager) + { + return configManager.getConfig(WorldHopperConfig.class); + } + + @Override + protected void startUp() throws Exception + { + currentPing = -1; + + keyManager.registerKeyListener(previousKeyListener); + keyManager.registerKeyListener(nextKeyListener); + + panel = new WorldSwitcherPanel(this); + + final BufferedImage icon; + synchronized (ImageIO.class) + { + icon = ImageIO.read(getClass().getResourceAsStream("icon.png")); + } + + navButton = NavigationButton.builder() + .tooltip("World Switcher") + .icon(icon) + .priority(3) + .panel(panel) + .build(); + + if (config.showSidebar()) + { + clientToolbar.addNavigation(navButton); + } + + overlayManager.add(worldHopperOverlay); + + panel.setFilterMode(config.subscriptionFilter()); + + // The plugin has its own executor for pings, as it blocks for a long time + hopperExecutorService = new ExecutorServiceExceptionLogger(Executors.newSingleThreadScheduledExecutor()); + // Run the first-run ping + hopperExecutorService.execute(this::pingInitialWorlds); + + // Give some initial delay - this won't run until after pingInitialWorlds finishes from tick() anyway + pingFuture = hopperExecutorService.scheduleWithFixedDelay(this::pingNextWorld, 15, 3, TimeUnit.SECONDS); + currPingFuture = hopperExecutorService.scheduleWithFixedDelay(this::pingCurrentWorld, 15, 1, TimeUnit.SECONDS); + + // populate initial world list + updateList(); + } + + @Override + protected void shutDown() throws Exception + { + pingFuture.cancel(true); + pingFuture = null; + + currPingFuture.cancel(true); + currPingFuture = null; + + overlayManager.remove(worldHopperOverlay); + + keyManager.unregisterKeyListener(previousKeyListener); + keyManager.unregisterKeyListener(nextKeyListener); + + clientToolbar.removeNavigation(navButton); + + hopperExecutorService.shutdown(); + hopperExecutorService = null; + } + + @Subscribe + public void onConfigChanged(final ConfigChanged event) + { + if (event.getGroup().equals(WorldHopperConfig.GROUP)) + { + switch (event.getKey()) + { + case "showSidebar": + if (config.showSidebar()) + { + clientToolbar.addNavigation(navButton); + } + else + { + clientToolbar.removeNavigation(navButton); + } + break; + case "ping": + if (config.ping()) + { + SwingUtilities.invokeLater(() -> panel.showPing()); + } + else + { + SwingUtilities.invokeLater(() -> panel.hidePing()); + } + break; + case "subscriptionFilter": + panel.setFilterMode(config.subscriptionFilter()); + updateList(); + break; + } + } + } + + private void setFavoriteConfig(int world) + { + configManager.setConfiguration(WorldHopperConfig.GROUP, "favorite_" + world, true); + } + + private boolean isFavoriteConfig(int world) + { + Boolean favorite = configManager.getConfiguration(WorldHopperConfig.GROUP, "favorite_" + world, Boolean.class); + return favorite != null && favorite; + } + + private void clearFavoriteConfig(int world) + { + configManager.unsetConfiguration(WorldHopperConfig.GROUP, "favorite_" + world); + } + + boolean isFavorite(World world) + { + int id = world.getId(); + return id == favoriteWorld1 || id == favoriteWorld2 || isFavoriteConfig(id); + } + + int getCurrentWorld() + { + return client.getWorld(); + } + + void hopTo(World world) + { + hop(world.getId()); + } + + void addToFavorites(World world) + { + log.debug("Adding world {} to favorites", world.getId()); + setFavoriteConfig(world.getId()); + panel.updateFavoriteMenu(world.getId(), true); + } + + void removeFromFavorites(World world) + { + log.debug("Removing world {} from favorites", world.getId()); + clearFavoriteConfig(world.getId()); + panel.updateFavoriteMenu(world.getId(), false); + } + + @Subscribe + public void onVarbitChanged(VarbitChanged varbitChanged) + { + int old1 = favoriteWorld1; + int old2 = favoriteWorld2; + + favoriteWorld1 = client.getVar(Varbits.WORLDHOPPER_FAVROITE_1); + favoriteWorld2 = client.getVar(Varbits.WORLDHOPPER_FAVROITE_2); + + if (old1 != favoriteWorld1 || old2 != favoriteWorld2) + { + SwingUtilities.invokeLater(panel::updateList); + } + } + + @Subscribe + public void onMenuEntryAdded(MenuEntryAdded event) + { + if (!config.menuOption()) + { + return; + } + + int groupId = WidgetInfo.TO_GROUP(event.getActionParam1()); + String option = event.getOption(); + + if (groupId == WidgetInfo.FRIENDS_LIST.getGroupId() || groupId == WidgetInfo.FRIENDS_CHAT.getGroupId()) + { + boolean after; + + if (AFTER_OPTIONS.contains(option)) + { + after = true; + } + else if (BEFORE_OPTIONS.contains(option)) + { + after = false; + } + else + { + return; + } + + // Don't add entry if user is offline + ChatPlayer player = getChatPlayerFromName(event.getTarget()); + WorldResult worldResult = worldService.getWorlds(); + + if (player == null || player.getWorld() == 0 || player.getWorld() == client.getWorld() + || worldResult == null) + { + return; + } + + World currentWorld = worldResult.findWorld(client.getWorld()); + World targetWorld = worldResult.findWorld(player.getWorld()); + if (targetWorld == null || currentWorld == null + || (!currentWorld.getTypes().contains(WorldType.PVP) && targetWorld.getTypes().contains(WorldType.PVP))) + { + // Disable Hop-to a PVP world from a regular world + return; + } + + final MenuEntry hopTo = new MenuEntry(); + hopTo.setOption(HOP_TO); + hopTo.setTarget(event.getTarget()); + hopTo.setType(MenuAction.RUNELITE.getId()); + hopTo.setParam0(event.getActionParam0()); + hopTo.setParam1(event.getActionParam1()); + + insertMenuEntry(hopTo, client.getMenuEntries(), after); + } + } + + private void insertMenuEntry(MenuEntry newEntry, MenuEntry[] entries, boolean after) + { + MenuEntry[] newMenu = ObjectArrays.concat(entries, newEntry); + + if (after) + { + int menuEntryCount = newMenu.length; + ArrayUtils.swap(newMenu, menuEntryCount - 1, menuEntryCount - 2); + } + + client.setMenuEntries(newMenu); + } + + @Subscribe + public void onPlayerMenuOptionClicked(PlayerMenuOptionClicked event) + { + if (!event.getMenuOption().equals(HOP_TO)) + { + return; + } + + ChatPlayer player = getChatPlayerFromName(event.getMenuTarget()); + + if (player != null) + { + hop(player.getWorld()); + } + } + + @Subscribe + public void onGameStateChanged(GameStateChanged gameStateChanged) + { + // If the player has disabled the side bar plugin panel, do not update the UI + if (config.showSidebar() && gameStateChanged.getGameState() == GameState.LOGGED_IN) + { + if (lastWorld != client.getWorld()) + { + int newWorld = client.getWorld(); + panel.switchCurrentHighlight(newWorld, lastWorld); + lastWorld = newWorld; + } + } + } + + @Subscribe + public void onWorldListLoad(WorldListLoad worldListLoad) + { + if (!config.showSidebar()) + { + return; + } + + Map worldData = new HashMap<>(); + + for (net.runelite.api.World w : worldListLoad.getWorlds()) + { + worldData.put(w.getId(), w.getPlayerCount()); + } + + panel.updateListData(worldData); + this.lastFetch = Instant.now(); // This counts as a fetch as it updates populations + } + + void refresh() + { + Instant now = Instant.now(); + if (lastFetch != null && now.toEpochMilli() - lastFetch.toEpochMilli() < REFRESH_THROTTLE) + { + log.debug("Throttling world refresh"); + return; + } + + lastFetch = now; + worldService.refresh(); + } + + @Subscribe + public void onWorldsFetch(WorldsFetch worldsFetch) + { + updateList(); + } + + /** + * This method ONLY updates the list's UI, not the actual world list and data it displays. + */ + private void updateList() + { + WorldResult worldResult = worldService.getWorlds(); + if (worldResult != null) + { + SwingUtilities.invokeLater(() -> panel.populate(worldResult.getWorlds())); + } + } + + private void hop(boolean previous) + { + WorldResult worldResult = worldService.getWorlds(); + if (worldResult == null || client.getGameState() != GameState.LOGGED_IN) + { + return; + } + + World currentWorld = worldResult.findWorld(client.getWorld()); + + if (currentWorld == null) + { + return; + } + + EnumSet currentWorldTypes = currentWorld.getTypes().clone(); + // Make it so you always hop out of PVP and high risk worlds + if (config.quickhopOutOfDanger()) + { + currentWorldTypes.remove(WorldType.PVP); + currentWorldTypes.remove(WorldType.HIGH_RISK); + } + // Don't regard these worlds as a type that must be hopped between + currentWorldTypes.remove(WorldType.BOUNTY); + currentWorldTypes.remove(WorldType.SKILL_TOTAL); + currentWorldTypes.remove(WorldType.LAST_MAN_STANDING); + + List worlds = worldResult.getWorlds(); + + int worldIdx = worlds.indexOf(currentWorld); + int totalLevel = client.getTotalLevel(); + + World world; + do + { + /* + Get the previous or next world in the list, + starting over at the other end of the list + if there are no more elements in the + current direction of iteration. + */ + if (previous) + { + worldIdx--; + + if (worldIdx < 0) + { + worldIdx = worlds.size() - 1; + } + } + else + { + worldIdx++; + + if (worldIdx >= worlds.size()) + { + worldIdx = 0; + } + } + + world = worlds.get(worldIdx); + + // Check world region if filter is enabled + if (config.quickHopRegionFilter() != RegionFilterMode.NONE && world.getRegion() != config.quickHopRegionFilter().getRegion()) + { + continue; + } + + EnumSet types = world.getTypes().clone(); + + types.remove(WorldType.BOUNTY); + // Treat LMS world like casual world + types.remove(WorldType.LAST_MAN_STANDING); + + if (types.contains(WorldType.SKILL_TOTAL)) + { + try + { + int totalRequirement = Integer.parseInt(world.getActivity().substring(0, world.getActivity().indexOf(" "))); + + if (totalLevel >= totalRequirement) + { + types.remove(WorldType.SKILL_TOTAL); + } + } + catch (NumberFormatException ex) + { + log.warn("Failed to parse total level requirement for target world", ex); + } + } + + // Avoid switching to near-max population worlds, as it will refuse to allow the hop if the world is full + if (world.getPlayers() >= MAX_PLAYER_COUNT) + { + continue; + } + + // Break out if we've found a good world to hop to + if (currentWorldTypes.equals(types)) + { + break; + } + } + while (world != currentWorld); + + if (world == currentWorld) + { + String chatMessage = new ChatMessageBuilder() + .append(ChatColorType.NORMAL) + .append("Couldn't find a world to quick-hop to.") + .build(); + + chatMessageManager.queue(QueuedMessage.builder() + .type(ChatMessageType.CONSOLE) + .runeLiteFormattedMessage(chatMessage) + .build()); + } + else + { + hop(world.getId()); + } + } + + private void hop(int worldId) + { + WorldResult worldResult = worldService.getWorlds(); + // Don't try to hop if the world doesn't exist + World world = worldResult.findWorld(worldId); + if (world == null) + { + return; + } + + final net.runelite.api.World rsWorld = client.createWorld(); + rsWorld.setActivity(world.getActivity()); + rsWorld.setAddress(world.getAddress()); + rsWorld.setId(world.getId()); + rsWorld.setPlayerCount(world.getPlayers()); + rsWorld.setLocation(world.getLocation()); + rsWorld.setTypes(WorldUtil.toWorldTypes(world.getTypes())); + + if (client.getGameState() == GameState.LOGIN_SCREEN) + { + // on the login screen we can just change the world by ourselves + client.changeWorld(rsWorld); + return; + } + + if (config.showWorldHopMessage()) + { + String chatMessage = new ChatMessageBuilder() + .append(ChatColorType.NORMAL) + .append("Quick-hopping to World ") + .append(ChatColorType.HIGHLIGHT) + .append(Integer.toString(world.getId())) + .append(ChatColorType.NORMAL) + .append("..") + .build(); + + chatMessageManager + .queue(QueuedMessage.builder() + .type(ChatMessageType.CONSOLE) + .runeLiteFormattedMessage(chatMessage) + .build()); + } + + quickHopTargetWorld = rsWorld; + displaySwitcherAttempts = 0; + } + + @Subscribe + public void onGameTick(GameTick event) + { + if (quickHopTargetWorld == null) + { + return; + } + + if (client.getWidget(WidgetInfo.WORLD_SWITCHER_LIST) == null) + { + client.openWorldHopper(); + + if (++displaySwitcherAttempts >= DISPLAY_SWITCHER_MAX_ATTEMPTS) + { + String chatMessage = new ChatMessageBuilder() + .append(ChatColorType.NORMAL) + .append("Failed to quick-hop after ") + .append(ChatColorType.HIGHLIGHT) + .append(Integer.toString(displaySwitcherAttempts)) + .append(ChatColorType.NORMAL) + .append(" attempts.") + .build(); + + chatMessageManager + .queue(QueuedMessage.builder() + .type(ChatMessageType.CONSOLE) + .runeLiteFormattedMessage(chatMessage) + .build()); + + resetQuickHopper(); + } + } + else + { + client.hopToWorld(quickHopTargetWorld); + resetQuickHopper(); + } + } + + @Subscribe + public void onChatMessage(ChatMessage event) + { + if (event.getType() != ChatMessageType.GAMEMESSAGE) + { + return; + } + + if (event.getMessage().equals("Please finish what you're doing before using the World Switcher.")) + { + resetQuickHopper(); + } + } + + private void resetQuickHopper() + { + displaySwitcherAttempts = 0; + quickHopTargetWorld = null; + } + + private ChatPlayer getChatPlayerFromName(String name) + { + String cleanName = Text.removeTags(name); + + // Search friends chat members first, because we can always get their world; + // friends worlds may be hidden if they have private off. (#5679) + FriendsChatManager friendsChatManager = client.getFriendsChatManager(); + if (friendsChatManager != null) + { + FriendsChatMember member = friendsChatManager.findByName(cleanName); + if (member != null) + { + return member; + } + } + + NameableContainer friendContainer = client.getFriendContainer(); + if (friendContainer != null) + { + return friendContainer.findByName(cleanName); + } + + return null; + } + + /** + * Ping all worlds. This takes a long time and is only run on first run. + */ + private void pingInitialWorlds() + { + WorldResult worldResult = worldService.getWorlds(); + if (worldResult == null || !config.showSidebar() || !config.ping()) + { + return; + } + + Stopwatch stopwatch = Stopwatch.createStarted(); + + for (World world : worldResult.getWorlds()) + { + int ping = ping(world); + SwingUtilities.invokeLater(() -> panel.updatePing(world.getId(), ping)); + } + + stopwatch.stop(); + + log.debug("Done pinging worlds in {}", stopwatch.elapsed()); + } + + /** + * Ping the next world + */ + private void pingNextWorld() + { + WorldResult worldResult = worldService.getWorlds(); + if (worldResult == null || !config.showSidebar() || !config.ping()) + { + return; + } + + List worlds = worldResult.getWorlds(); + if (worlds.isEmpty()) + { + return; + } + + if (currentWorld >= worlds.size()) + { + // Wrap back around + currentWorld = 0; + } + + World world = worlds.get(currentWorld++); + + // If we are displaying the ping overlay, there is a separate scheduled task for the current world + boolean displayPing = config.displayPing() && client.getGameState() == GameState.LOGGED_IN; + if (displayPing && client.getWorld() == world.getId()) + { + return; + } + + int ping = ping(world); + log.trace("Ping for world {} is: {}", world.getId(), ping); + SwingUtilities.invokeLater(() -> panel.updatePing(world.getId(), ping)); + } + + /** + * Ping the current world for the ping overlay + */ + private void pingCurrentWorld() + { + WorldResult worldResult = worldService.getWorlds(); + // There is no reason to ping the current world if not logged in, as the overlay doesn't draw + if (worldResult == null || !config.displayPing() || client.getGameState() != GameState.LOGGED_IN) + { + return; + } + + final World currentWorld = worldResult.findWorld(client.getWorld()); + if (currentWorld == null) + { + log.debug("unable to find current world: {}", client.getWorld()); + return; + } + + currentPing = ping(currentWorld); + log.trace("Ping for current world is: {}", currentPing); + + SwingUtilities.invokeLater(() -> panel.updatePing(currentWorld.getId(), currentPing)); + } + + Integer getStoredPing(World world) + { + if (!config.ping()) + { + return null; + } + + return storedPings.get(world.getId()); + } + + private int ping(World world) + { + int ping = Ping.ping(world); + storedPings.put(world.getId(), ping); + return ping; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldSwitcherPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldSwitcherPanel.java new file mode 100644 index 0000000000..9948a856f5 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldSwitcherPanel.java @@ -0,0 +1,405 @@ +/* + * Copyright (c) 2018, Psikoi + * 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.worldhopper; + +import com.google.common.collect.Ordering; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.GridLayout; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; +import lombok.AccessLevel; +import lombok.Setter; +import net.runelite.client.ui.ColorScheme; +import net.runelite.client.ui.DynamicGridLayout; +import net.runelite.client.ui.PluginPanel; +import net.runelite.http.api.worlds.World; +import net.runelite.http.api.worlds.WorldType; + +class WorldSwitcherPanel extends PluginPanel +{ + private static final Color ODD_ROW = new Color(44, 44, 44); + + private static final int WORLD_COLUMN_WIDTH = 60; + private static final int PLAYERS_COLUMN_WIDTH = 40; + private static final int PING_COLUMN_WIDTH = 47; + + private final JPanel listContainer = new JPanel(); + + private WorldTableHeader worldHeader; + private WorldTableHeader playersHeader; + private WorldTableHeader activityHeader; + private WorldTableHeader pingHeader; + + private WorldOrder orderIndex = WorldOrder.WORLD; + private boolean ascendingOrder = true; + + private final ArrayList rows = new ArrayList<>(); + private final WorldHopperPlugin plugin; + @Setter(AccessLevel.PACKAGE) + private SubscriptionFilterMode filterMode; + + WorldSwitcherPanel(WorldHopperPlugin plugin) + { + this.plugin = plugin; + + setBorder(null); + setLayout(new DynamicGridLayout(0, 1)); + + JPanel headerContainer = buildHeader(); + + listContainer.setLayout(new GridLayout(0, 1)); + + add(headerContainer); + add(listContainer); + } + + void switchCurrentHighlight(int newWorld, int lastWorld) + { + for (WorldTableRow row : rows) + { + if (row.getWorld().getId() == newWorld) + { + row.recolour(true); + } + else if (row.getWorld().getId() == lastWorld) + { + row.recolour(false); + } + } + } + + void updateListData(Map worldData) + { + for (WorldTableRow worldTableRow : rows) + { + World world = worldTableRow.getWorld(); + Integer playerCount = worldData.get(world.getId()); + if (playerCount != null) + { + worldTableRow.updatePlayerCount(playerCount); + } + } + + // If the list is being ordered by player count, then it has to be re-painted + // to properly display the new data + if (orderIndex == WorldOrder.PLAYERS) + { + updateList(); + } + } + + void updatePing(int world, int ping) + { + for (WorldTableRow worldTableRow : rows) + { + if (worldTableRow.getWorld().getId() == world) + { + worldTableRow.setPing(ping); + + // If the panel is sorted by ping, re-sort it + if (orderIndex == WorldOrder.PING) + { + updateList(); + } + break; + } + } + } + + void hidePing() + { + for (WorldTableRow worldTableRow : rows) + { + worldTableRow.hidePing(); + } + } + + void showPing() + { + for (WorldTableRow worldTableRow : rows) + { + worldTableRow.showPing(); + } + } + + void updateList() + { + rows.sort((r1, r2) -> + { + switch (orderIndex) + { + case PING: + // Leave worlds with unknown ping at the bottom + return getCompareValue(r1, r2, row -> + { + int ping = row.getPing(); + return ping > 0 ? ping : null; + }); + case WORLD: + return getCompareValue(r1, r2, row -> row.getWorld().getId()); + case PLAYERS: + return getCompareValue(r1, r2, WorldTableRow::getUpdatedPlayerCount); + case ACTIVITY: + // Leave empty activity worlds on the bottom of the list + return getCompareValue(r1, r2, row -> + { + String activity = row.getWorld().getActivity(); + return !activity.equals("-") ? activity : null; + }); + default: + return 0; + } + }); + + rows.sort((r1, r2) -> + { + boolean b1 = plugin.isFavorite(r1.getWorld()); + boolean b2 = plugin.isFavorite(r2.getWorld()); + return Boolean.compare(b2, b1); + }); + + listContainer.removeAll(); + + for (int i = 0; i < rows.size(); i++) + { + WorldTableRow row = rows.get(i); + row.setBackground(i % 2 == 0 ? ODD_ROW : ColorScheme.DARK_GRAY_COLOR); + listContainer.add(row); + } + + listContainer.revalidate(); + listContainer.repaint(); + } + + private int getCompareValue(WorldTableRow row1, WorldTableRow row2, Function compareByFn) + { + Ordering ordering = Ordering.natural(); + if (!ascendingOrder) + { + ordering = ordering.reverse(); + } + ordering = ordering.nullsLast(); + return ordering.compare(compareByFn.apply(row1), compareByFn.apply(row2)); + } + + void updateFavoriteMenu(int world, boolean favorite) + { + for (WorldTableRow row : rows) + { + if (row.getWorld().getId() == world) + { + row.setFavoriteMenu(favorite); + } + } + } + + void populate(List worlds) + { + rows.clear(); + + for (int i = 0; i < worlds.size(); i++) + { + World world = worlds.get(i); + + switch (filterMode) + { + case FREE: + if (world.getTypes().contains(WorldType.MEMBERS)) + { + continue; + } + break; + case MEMBERS: + if (!world.getTypes().contains(WorldType.MEMBERS)) + { + continue; + } + break; + } + + rows.add(buildRow(world, i % 2 == 0, world.getId() == plugin.getCurrentWorld() && plugin.getLastWorld() != 0, plugin.isFavorite(world))); + } + + updateList(); + } + + private void orderBy(WorldOrder order) + { + pingHeader.highlight(false, ascendingOrder); + worldHeader.highlight(false, ascendingOrder); + playersHeader.highlight(false, ascendingOrder); + activityHeader.highlight(false, ascendingOrder); + + switch (order) + { + case PING: + pingHeader.highlight(true, ascendingOrder); + break; + case WORLD: + worldHeader.highlight(true, ascendingOrder); + break; + case PLAYERS: + playersHeader.highlight(true, ascendingOrder); + break; + case ACTIVITY: + activityHeader.highlight(true, ascendingOrder); + break; + } + + orderIndex = order; + updateList(); + } + + /** + * Builds the entire table header. + */ + private JPanel buildHeader() + { + JPanel header = new JPanel(new BorderLayout()); + JPanel leftSide = new JPanel(new BorderLayout()); + JPanel rightSide = new JPanel(new BorderLayout()); + + pingHeader = new WorldTableHeader("Ping", orderIndex == WorldOrder.PING, ascendingOrder, plugin::refresh); + pingHeader.setPreferredSize(new Dimension(PING_COLUMN_WIDTH, 0)); + pingHeader.addMouseListener(new MouseAdapter() + { + @Override + public void mousePressed(MouseEvent mouseEvent) + { + if (SwingUtilities.isRightMouseButton(mouseEvent)) + { + return; + } + ascendingOrder = orderIndex != WorldOrder.PING || !ascendingOrder; + orderBy(WorldOrder.PING); + } + }); + + worldHeader = new WorldTableHeader("World", orderIndex == WorldOrder.WORLD, ascendingOrder, plugin::refresh); + worldHeader.setPreferredSize(new Dimension(WORLD_COLUMN_WIDTH, 0)); + worldHeader.addMouseListener(new MouseAdapter() + { + @Override + public void mousePressed(MouseEvent mouseEvent) + { + if (SwingUtilities.isRightMouseButton(mouseEvent)) + { + return; + } + ascendingOrder = orderIndex != WorldOrder.WORLD || !ascendingOrder; + orderBy(WorldOrder.WORLD); + } + }); + + playersHeader = new WorldTableHeader("#", orderIndex == WorldOrder.PLAYERS, ascendingOrder, plugin::refresh); + playersHeader.setPreferredSize(new Dimension(PLAYERS_COLUMN_WIDTH, 0)); + playersHeader.addMouseListener(new MouseAdapter() + { + @Override + public void mousePressed(MouseEvent mouseEvent) + { + if (SwingUtilities.isRightMouseButton(mouseEvent)) + { + return; + } + ascendingOrder = orderIndex != WorldOrder.PLAYERS || !ascendingOrder; + orderBy(WorldOrder.PLAYERS); + } + }); + + activityHeader = new WorldTableHeader("Activity", orderIndex == WorldOrder.ACTIVITY, ascendingOrder, plugin::refresh); + activityHeader.addMouseListener(new MouseAdapter() + { + @Override + public void mousePressed(MouseEvent mouseEvent) + { + if (SwingUtilities.isRightMouseButton(mouseEvent)) + { + return; + } + ascendingOrder = orderIndex != WorldOrder.ACTIVITY || !ascendingOrder; + orderBy(WorldOrder.ACTIVITY); + } + }); + + leftSide.add(worldHeader, BorderLayout.WEST); + leftSide.add(playersHeader, BorderLayout.CENTER); + + rightSide.add(activityHeader, BorderLayout.CENTER); + rightSide.add(pingHeader, BorderLayout.EAST); + + header.add(leftSide, BorderLayout.WEST); + header.add(rightSide, BorderLayout.CENTER); + + return header; + } + + /** + * Builds a table row, that displays the world's information. + */ + private WorldTableRow buildRow(World world, boolean stripe, boolean current, boolean favorite) + { + WorldTableRow row = new WorldTableRow(world, current, favorite, plugin.getStoredPing(world), + world1 -> + { + plugin.hopTo(world1); + }, + (world12, add) -> + { + if (add) + { + plugin.addToFavorites(world12); + } + else + { + plugin.removeFromFavorites(world12); + } + + updateList(); + } + ); + row.setBackground(stripe ? ODD_ROW : ColorScheme.DARK_GRAY_COLOR); + return row; + } + + /** + * Enumerates the multiple ordering options for the world list. + */ + private enum WorldOrder + { + WORLD, + PLAYERS, + ACTIVITY, + PING + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldTableHeader.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldTableHeader.java new file mode 100644 index 0000000000..1004bc7016 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldTableHeader.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2018, Psikoi + * 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.worldhopper; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.image.BufferedImage; +import javax.annotation.Nonnull; +import javax.swing.BorderFactory; +import javax.swing.ImageIcon; +import javax.swing.JLabel; +import javax.swing.JMenuItem; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import net.runelite.client.ui.ColorScheme; +import net.runelite.client.ui.FontManager; +import net.runelite.client.util.ImageUtil; + +class WorldTableHeader extends JPanel +{ + private static final ImageIcon ARROW_UP; + private static final ImageIcon HIGHLIGHT_ARROW_DOWN; + private static final ImageIcon HIGHLIGHT_ARROW_UP; + + private static final Color ARROW_COLOR = ColorScheme.LIGHT_GRAY_COLOR; + private static final Color HIGHLIGHT_COLOR = ColorScheme.BRAND_ORANGE; + + static + { + final BufferedImage arrowDown = ImageUtil.getResourceStreamFromClass(WorldHopperPlugin.class, "arrow_down.png"); + final BufferedImage arrowUp = ImageUtil.rotateImage(arrowDown, Math.PI); + final BufferedImage arrowUpFaded = ImageUtil.luminanceOffset(arrowUp, -80); + ARROW_UP = new ImageIcon(arrowUpFaded); + + final BufferedImage highlightArrowDown = ImageUtil.fillImage(arrowDown, HIGHLIGHT_COLOR); + final BufferedImage highlightArrowUp = ImageUtil.fillImage(arrowUp, HIGHLIGHT_COLOR); + HIGHLIGHT_ARROW_DOWN = new ImageIcon(highlightArrowDown); + HIGHLIGHT_ARROW_UP = new ImageIcon(highlightArrowUp); + } + + private final JLabel textLabel = new JLabel(); + private final JLabel arrowLabel = new JLabel(); + // Determines if this header column is being used to order the list + private boolean ordering = false; + + WorldTableHeader(String title, boolean ordered, boolean ascending, @Nonnull Runnable onRefresh) + { + setLayout(new BorderLayout(5, 0)); + setBorder(new CompoundBorder( + BorderFactory.createMatteBorder(0, 0, 0, 1, ColorScheme.MEDIUM_GRAY_COLOR), + new EmptyBorder(0, 5, 0, 2))); + setBackground(ColorScheme.SCROLL_TRACK_COLOR); + + addMouseListener(new MouseAdapter() + { + @Override + public void mouseEntered(MouseEvent mouseEvent) + { + textLabel.setForeground(HIGHLIGHT_COLOR); + if (!ordering) + { + arrowLabel.setIcon(HIGHLIGHT_ARROW_UP); + } + } + + @Override + public void mouseExited(MouseEvent mouseEvent) + { + if (!ordering) + { + textLabel.setForeground(ARROW_COLOR); + arrowLabel.setIcon(ARROW_UP); + } + } + }); + + textLabel.setText(title); + textLabel.setFont(FontManager.getRunescapeSmallFont()); + + final JMenuItem refresh = new JMenuItem("Refresh worlds"); + refresh.addActionListener(e -> + { + onRefresh.run(); + }); + + final JPopupMenu popupMenu = new JPopupMenu(); + popupMenu.setBorder(new EmptyBorder(5, 5, 5, 5)); + popupMenu.add(refresh); + + textLabel.setComponentPopupMenu(popupMenu); + setComponentPopupMenu(popupMenu); + + highlight(ordered, ascending); + + add(textLabel, BorderLayout.WEST); + add(arrowLabel, BorderLayout.EAST); + } + + /** + * The labels inherit the parent's mouse listeners. + */ + @Override + public void addMouseListener(MouseListener mouseListener) + { + super.addMouseListener(mouseListener); + textLabel.addMouseListener(mouseListener); + arrowLabel.addMouseListener(mouseListener); + } + + /** + * If this column header is being used to order, then it should be + * highlighted, changing it's font color and icon. + */ + public void highlight(boolean on, boolean ascending) + { + ordering = on; + arrowLabel.setIcon(on ? (ascending ? HIGHLIGHT_ARROW_DOWN : HIGHLIGHT_ARROW_UP) : ARROW_UP); + textLabel.setForeground(on ? HIGHLIGHT_COLOR : ARROW_COLOR); + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldTableRow.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldTableRow.java new file mode 100644 index 0000000000..6361c123c8 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldTableRow.java @@ -0,0 +1,364 @@ +/* + * Copyright (c) 2018, Psikoi + * 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.worldhopper; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import javax.swing.ImageIcon; +import javax.swing.JLabel; +import javax.swing.JMenuItem; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.border.EmptyBorder; +import lombok.AccessLevel; +import lombok.Getter; +import net.runelite.client.ui.FontManager; +import net.runelite.client.util.ImageUtil; +import net.runelite.http.api.worlds.World; +import net.runelite.http.api.worlds.WorldRegion; +import net.runelite.http.api.worlds.WorldType; + +class WorldTableRow extends JPanel +{ + private static final ImageIcon FLAG_AUS; + private static final ImageIcon FLAG_UK; + private static final ImageIcon FLAG_US; + private static final ImageIcon FLAG_GER; + + private static final int WORLD_COLUMN_WIDTH = 60; + private static final int PLAYERS_COLUMN_WIDTH = 40; + private static final int PING_COLUMN_WIDTH = 35; + + private static final Color CURRENT_WORLD = new Color(66, 227, 17); + private static final Color DANGEROUS_WORLD = new Color(251, 62, 62); + private static final Color TOURNAMENT_WORLD = new Color(79, 145, 255); + private static final Color MEMBERS_WORLD = new Color(210, 193, 53); + private static final Color FREE_WORLD = new Color(200, 200, 200); + private static final Color LEAGUE_WORLD = new Color(133, 177, 178); + + static + { + FLAG_AUS = new ImageIcon(ImageUtil.getResourceStreamFromClass(WorldHopperPlugin.class, "flag_aus.png")); + FLAG_UK = new ImageIcon(ImageUtil.getResourceStreamFromClass(WorldHopperPlugin.class, "flag_uk.png")); + FLAG_US = new ImageIcon(ImageUtil.getResourceStreamFromClass(WorldHopperPlugin.class, "flag_us.png")); + FLAG_GER = new ImageIcon(ImageUtil.getResourceStreamFromClass(WorldHopperPlugin.class, "flag_ger.png")); + } + + private final JMenuItem favoriteMenuOption = new JMenuItem(); + + private JLabel worldField; + private JLabel playerCountField; + private JLabel activityField; + private JLabel pingField; + private final BiConsumer onFavorite; + + @Getter + private final World world; + + @Getter(AccessLevel.PACKAGE) + private int updatedPlayerCount; + + private int ping; + + private Color lastBackground; + + WorldTableRow(World world, boolean current, boolean favorite, Integer ping, Consumer onSelect, BiConsumer onFavorite) + { + this.world = world; + this.onFavorite = onFavorite; + this.updatedPlayerCount = world.getPlayers(); + + setLayout(new BorderLayout()); + setBorder(new EmptyBorder(2, 0, 2, 0)); + + addMouseListener(new MouseAdapter() + { + @Override + public void mouseClicked(MouseEvent mouseEvent) + { + if (mouseEvent.getClickCount() == 2) + { + if (onSelect != null) + { + onSelect.accept(world); + } + } + } + + @Override + public void mousePressed(MouseEvent mouseEvent) + { + if (mouseEvent.getClickCount() == 2) + { + setBackground(getBackground().brighter()); + } + } + + @Override + public void mouseReleased(MouseEvent mouseEvent) + { + if (mouseEvent.getClickCount() == 2) + { + setBackground(getBackground().darker()); + } + } + + @Override + public void mouseEntered(MouseEvent mouseEvent) + { + WorldTableRow.this.lastBackground = getBackground(); + setBackground(getBackground().brighter()); + } + + @Override + public void mouseExited(MouseEvent mouseEvent) + { + setBackground(lastBackground); + } + }); + + setFavoriteMenu(favorite); + + final JPopupMenu popupMenu = new JPopupMenu(); + popupMenu.setBorder(new EmptyBorder(5, 5, 5, 5)); + popupMenu.add(favoriteMenuOption); + + setComponentPopupMenu(popupMenu); + + JPanel leftSide = new JPanel(new BorderLayout()); + JPanel rightSide = new JPanel(new BorderLayout()); + leftSide.setOpaque(false); + rightSide.setOpaque(false); + + JPanel worldField = buildWorldField(); + worldField.setPreferredSize(new Dimension(WORLD_COLUMN_WIDTH, 0)); + worldField.setOpaque(false); + + JPanel pingField = buildPingField(ping); + pingField.setPreferredSize(new Dimension(PING_COLUMN_WIDTH, 0)); + pingField.setOpaque(false); + + JPanel playersField = buildPlayersField(); + playersField.setPreferredSize(new Dimension(PLAYERS_COLUMN_WIDTH, 0)); + playersField.setOpaque(false); + + JPanel activityField = buildActivityField(); + activityField.setBorder(new EmptyBorder(5, 5, 5, 5)); + activityField.setOpaque(false); + + recolour(current); + + leftSide.add(worldField, BorderLayout.WEST); + leftSide.add(playersField, BorderLayout.CENTER); + rightSide.add(activityField, BorderLayout.CENTER); + rightSide.add(pingField, BorderLayout.EAST); + + add(leftSide, BorderLayout.WEST); + add(rightSide, BorderLayout.CENTER); + } + + void setFavoriteMenu(boolean favorite) + { + String favoriteAction = favorite ? + "Remove " + world.getId() + " from favorites" : + "Add " + world.getId() + " to favorites"; + + favoriteMenuOption.setText(favoriteAction); + + for (ActionListener listener : favoriteMenuOption.getActionListeners()) + { + favoriteMenuOption.removeActionListener(listener); + } + + favoriteMenuOption.addActionListener(e -> + { + onFavorite.accept(world, !favorite); + }); + } + + void updatePlayerCount(int playerCount) + { + this.updatedPlayerCount = playerCount; + playerCountField.setText(playerCountString(playerCount)); + } + + private static String playerCountString(int playerCount) + { + return playerCount < 0 ? "OFF" : Integer.toString(playerCount); + } + + void setPing(int ping) + { + this.ping = ping; + pingField.setText(ping <= 0 ? "-" : Integer.toString(ping)); + } + + void hidePing() + { + pingField.setText("-"); + } + + void showPing() + { + setPing(ping); // to update pingField + } + + int getPing() + { + return ping; + } + + public void recolour(boolean current) + { + playerCountField.setForeground(current ? CURRENT_WORLD : Color.WHITE); + pingField.setForeground(current ? CURRENT_WORLD : Color.WHITE); + + if (current) + { + activityField.setForeground(CURRENT_WORLD); + worldField.setForeground(CURRENT_WORLD); + return; + } + else if (world.getTypes().contains(WorldType.PVP) + || world.getTypes().contains(WorldType.HIGH_RISK) + || world.getTypes().contains(WorldType.DEADMAN)) + { + activityField.setForeground(DANGEROUS_WORLD); + } + else if (world.getTypes().contains(WorldType.LEAGUE)) + { + activityField.setForeground(LEAGUE_WORLD); + } + else if (world.getTypes().contains(WorldType.TOURNAMENT)) + { + activityField.setForeground(TOURNAMENT_WORLD); + } + else + { + activityField.setForeground(Color.WHITE); + } + + worldField.setForeground(world.getTypes().contains(WorldType.MEMBERS) ? MEMBERS_WORLD : FREE_WORLD); + } + + /** + * Builds the players list field (containing the amount of players logged in that world). + */ + private JPanel buildPlayersField() + { + JPanel column = new JPanel(new BorderLayout()); + column.setBorder(new EmptyBorder(0, 5, 0, 5)); + + playerCountField = new JLabel(playerCountString(world.getPlayers())); + playerCountField.setFont(FontManager.getRunescapeSmallFont()); + + column.add(playerCountField, BorderLayout.WEST); + + return column; + } + + private JPanel buildPingField(Integer ping) + { + JPanel column = new JPanel(new BorderLayout()); + column.setBorder(new EmptyBorder(0, 5, 0, 5)); + + pingField = new JLabel("-"); + pingField.setFont(FontManager.getRunescapeSmallFont()); + + column.add(pingField, BorderLayout.EAST); + + if (ping != null) + { + setPing(ping); + } + + return column; + } + + /** + * Builds the activity list field (containing that world's activity/theme). + */ + private JPanel buildActivityField() + { + JPanel column = new JPanel(new BorderLayout()); + column.setBorder(new EmptyBorder(0, 5, 0, 5)); + + activityField = new JLabel(world.getActivity()); + activityField.setFont(FontManager.getRunescapeSmallFont()); + + column.add(activityField, BorderLayout.WEST); + + return column; + } + + /** + * Builds the world list field (containing the country's flag and the world index). + */ + private JPanel buildWorldField() + { + JPanel column = new JPanel(new BorderLayout(7, 0)); + column.setBorder(new EmptyBorder(0, 5, 0, 5)); + + worldField = new JLabel(world.getId() + ""); + + ImageIcon flagIcon = getFlag(world.getRegion()); + if (flagIcon != null) + { + JLabel flag = new JLabel(flagIcon); + column.add(flag, BorderLayout.WEST); + } + column.add(worldField, BorderLayout.CENTER); + + return column; + } + + private static ImageIcon getFlag(WorldRegion region) + { + if (region == null) + { + return null; + } + + switch (region) + { + case UNITED_STATES_OF_AMERICA: + return FLAG_US; + case UNITED_KINGDOM: + return FLAG_UK; + case AUSTRALIA: + return FLAG_AUS; + case GERMANY: + return FLAG_GER; + default: + return null; + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/ping/IPHlpAPI.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/ping/IPHlpAPI.java new file mode 100644 index 0000000000..109ea36f77 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/ping/IPHlpAPI.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.worldhopper.ping; + +import com.sun.jna.Library; +import com.sun.jna.Native; +import com.sun.jna.Pointer; + +interface IPHlpAPI extends Library +{ + IPHlpAPI INSTANCE = Native.loadLibrary("IPHlpAPI", IPHlpAPI.class); + + Pointer IcmpCreateFile(); + + boolean IcmpCloseHandle(Pointer handle); + + int IcmpSendEcho(Pointer IcmpHandle, int DestinationAddress, Pointer RequestData, short RequestSize, Pointer RequestOptions, IcmpEchoReply ReplyBuffer, int ReplySize, int Timeout); +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/ping/IcmpEchoReply.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/ping/IcmpEchoReply.java new file mode 100644 index 0000000000..038022f44d --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/ping/IcmpEchoReply.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.worldhopper.ping; + +import com.sun.jna.Native; +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinDef; +import java.util.Arrays; +import java.util.List; + +public class IcmpEchoReply extends Structure +{ + private static final int IP_OPTION_INFO_SIZE = 1 + 1 + 1 + 1 + (Native.POINTER_SIZE == 8 ? 12 : 4); // on 64bit vms add 4 byte padding + public static final int SIZE = 4 + 4 + 4 + 2 + 2 + Native.POINTER_SIZE + IP_OPTION_INFO_SIZE; + + public WinDef.ULONG address; + public WinDef.ULONG status; + public WinDef.ULONG roundTripTime; + public WinDef.USHORT dataSize; + public WinDef.USHORT reserved; + public WinDef.PVOID data; + public WinDef.UCHAR ttl; + public WinDef.UCHAR tos; + public WinDef.UCHAR flags; + public WinDef.UCHAR optionsSize; + public WinDef.PVOID optionsData; + + IcmpEchoReply(Pointer p) + { + super(p); + } + + @Override + protected List getFieldOrder() + { + return Arrays.asList("address", "status", "roundTripTime", "dataSize", "reserved", "data", "ttl", "tos", "flags", "optionsSize", "optionsData"); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/ping/Ping.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/ping/Ping.java new file mode 100644 index 0000000000..fd9a84fa57 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/ping/Ping.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.worldhopper.ping; + +import com.sun.jna.Memory; +import com.sun.jna.Pointer; +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.UnknownHostException; +import lombok.extern.slf4j.Slf4j; +import net.runelite.client.util.OSType; +import net.runelite.http.api.worlds.World; + +@Slf4j +public class Ping +{ + private static final String RUNELITE_PING = "RuneLitePing"; + + private static final int TIMEOUT = 2000; + private static final int PORT = 43594; + + public static int ping(World world) + { + try + { + switch (OSType.getOSType()) + { + case Windows: + return windowsPing(world); + default: + return tcpPing(world); + } + } + catch (IOException ex) + { + log.warn("error pinging", ex); + return -1; + } + } + + private static int windowsPing(World world) throws UnknownHostException + { + IPHlpAPI ipHlpAPI = IPHlpAPI.INSTANCE; + Pointer ptr = ipHlpAPI.IcmpCreateFile(); + InetAddress inetAddress = InetAddress.getByName(world.getAddress()); + byte[] address = inetAddress.getAddress(); + String dataStr = RUNELITE_PING; + int dataLength = dataStr.length() + 1; + Pointer data = new Memory(dataLength); + data.setString(0L, dataStr); + IcmpEchoReply icmpEchoReply = new IcmpEchoReply(new Memory(IcmpEchoReply.SIZE + dataLength)); + assert icmpEchoReply.size() == IcmpEchoReply.SIZE; + int packed = (address[0] & 0xff) | ((address[1] & 0xff) << 8) | ((address[2] & 0xff) << 16) | ((address[3] & 0xff) << 24); + int ret = ipHlpAPI.IcmpSendEcho(ptr, packed, data, (short) (dataLength), Pointer.NULL, icmpEchoReply, IcmpEchoReply.SIZE + dataLength, TIMEOUT); + if (ret != 1) + { + ipHlpAPI.IcmpCloseHandle(ptr); + return -1; + } + + int rtt = Math.toIntExact(icmpEchoReply.roundTripTime.longValue()); + ipHlpAPI.IcmpCloseHandle(ptr); + + return rtt; + } + + private static int tcpPing(World world) throws IOException + { + try (Socket socket = new Socket()) + { + socket.setSoTimeout(TIMEOUT); + InetAddress inetAddress = InetAddress.getByName(world.getAddress()); + long start = System.nanoTime(); + socket.connect(new InetSocketAddress(inetAddress, PORT)); + long end = System.nanoTime(); + return (int) ((end - start) / 1000000L); + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/AgilityCourseLocation.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/AgilityCourseLocation.java new file mode 100644 index 0000000000..ee0c61db88 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/AgilityCourseLocation.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2020, melky + * 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.worldmap; + +import lombok.Getter; +import net.runelite.api.coords.WorldPoint; + +@Getter +enum AgilityCourseLocation +{ + AGILITY_PYRAMID("Agility Pyramid", new WorldPoint(3347, 2827, 0)), + AL_KHARID_ROOFTOP_COURSE("Al Kharid Rooftop Course", new WorldPoint(3272, 3195, 0)), + APE_ATOLL_AGILITY_COURSE("Ape Atoll Agility Course", new WorldPoint(2752, 2742, 0)), + ARDOUGNE_ROOFTOP_COURSE("Ardougne Rooftop Course", new WorldPoint(2673, 3298, 0)), + BARBARIAN_OUTPOST_AGILITY_COURSE("Barbarian Outpost Agility Course", new WorldPoint(2544, 3569, 0)), + BRIMHAVEN_AGILITY_ARENA("Brimhaven Agility Arena", new WorldPoint(2806, 3193, 0)), + CANIFIS_ROOFTOP_COURSE("Canifis Rooftop Course", new WorldPoint(3506, 3490, 0)), + DRAYNOR_VILLAGE_ROOFTOP_COURSE("Draynor Village Rooftop Course", new WorldPoint(3103, 3279, 0)), + FALADOR_ROOFTOP_COURSE("Falador Rooftop Course", new WorldPoint(3035, 3342, 0)), + GNOME_STRONGHOLD_AGILITY_COURSE("Gnome Stronghold Agility Course", new WorldPoint(2474, 3436, 0)), + PENGUIN_AGILITY_COURSE("Penguin Agility Course", new WorldPoint(2638, 4041, 0)), + POLLNIVNEACH_ROOFTOP_COURSE("Pollnivneach Rooftop Course", new WorldPoint(3350, 2963, 0)), + PRIFDDINAS_AGILITY_COURSE("Prifddinas Agility Course", new WorldPoint(3253, 6109, 0)), + RELLEKKA_ROOFTOP_COURSE("Rellekka Rooftop Course", new WorldPoint(2624, 3677, 0)), + SEERS_VILLAGE_ROOFTOP_COURSE("Seers' Village Rooftop Course", new WorldPoint(2728, 3488, 0)), + VARROCK_ROOFTOP_COURSE("Varrock Rooftop Course", new WorldPoint(3219, 3414, 0)), + WEREWOLF_AGILITY_COURSE("Werewolf Agility Course", new WorldPoint(3542, 3463, 0)), + WILDERNESS_AGILITY_COURSE("Wilderness Agility Course", new WorldPoint(2997, 3916, 0)); + + private final String tooltip; + private final WorldPoint location; + private final boolean rooftopCourse; + + AgilityCourseLocation(String tooltip, WorldPoint location) + { + this.tooltip = tooltip; + this.location = location; + this.rooftopCourse = this.name().contains("ROOFTOP_COURSE"); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/AgilityCoursePoint.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/AgilityCoursePoint.java new file mode 100644 index 0000000000..982f8db975 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/AgilityCoursePoint.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2020, melky + * 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.worldmap; + +import java.awt.image.BufferedImage; +import net.runelite.client.ui.overlay.worldmap.WorldMapPoint; + +class AgilityCoursePoint extends WorldMapPoint +{ + AgilityCoursePoint(AgilityCourseLocation data, BufferedImage icon, boolean showTooltip) + { + super(data.getLocation(), icon); + + if (showTooltip) + { + setTooltip(data.getTooltip()); + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/AgilityShortcutPoint.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/AgilityShortcutPoint.java new file mode 100644 index 0000000000..c99979f103 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/AgilityShortcutPoint.java @@ -0,0 +1,43 @@ +/* + * 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 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.worldmap; + +import java.awt.image.BufferedImage; +import net.runelite.client.ui.overlay.worldmap.WorldMapPoint; +import net.runelite.client.game.AgilityShortcut; + +class AgilityShortcutPoint extends WorldMapPoint +{ + AgilityShortcutPoint(AgilityShortcut data, BufferedImage icon, boolean showTooltip) + { + super(data.getWorldMapLocation(), icon); + + if (showTooltip) + { + setTooltip(data.getTooltip()); + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/DungeonLocation.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/DungeonLocation.java new file mode 100644 index 0000000000..1f5ee3bb0e --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/DungeonLocation.java @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2020, Arman S + * 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.worldmap; + +import lombok.Getter; +import net.runelite.api.coords.WorldPoint; + +@Getter +enum DungeonLocation +{ + ABANDONED_MINE("Abandoned Mine", new WorldPoint(3439, 3232, 0)), + ABANDONED_MINE_SECRET("Abandoned Mine (secret entrance)", new WorldPoint(3452, 3244, 0)), + ANCIENT_CAVERN("Ancient Cavern", new WorldPoint(2511, 3508, 0)), + APE_ATOLL("Ape Atoll Dungeon", new WorldPoint(2762, 2703, 0)), + ARDOUGNE_CASTLE("Ardougne Castle basement", new WorldPoint(2569, 3296, 0)), + ARDOUGNE_RAT_PITS("Ardougne Rat Pits", new WorldPoint(2560, 3320, 0)), + ARDOUGNE_SEWERS_N("Ardougne Sewers", new WorldPoint(2631, 3294, 0)), + ARDOUGNE_SEWERS_S("Ardougne Sewers", new WorldPoint(2586, 3235, 0)), + ARDOUGNE_SEWERS_W("Ardougne Sewers", new WorldPoint(2528, 3303, 0)), + ASGARNIAN_ICE("Asgarnian Ice Dungeon", new WorldPoint(3007, 3150, 0)), + BRIMHAVEN_AGILITY("Brimhaven Agility Arena", new WorldPoint(2808, 3194, 0)), + BRIMHAVEN_N("Brimhaven Dungeon", new WorldPoint(2743, 3154, 0)), + BRIMHAVEN_S("Brimhaven Dungeon", new WorldPoint(2759, 3062, 0)), + BRIMSTAILS_CAVE("Brimstail's Cave", new WorldPoint(2402, 3419, 0)), + CATACOMBS_OF_KOUREND("Catacombs of Kourend", new WorldPoint(1636, 3673, 0)), + CHAMBERS_OF_XERIC("Chambers of Xeric", new WorldPoint(1232, 3573, 0)), + CHAMPIONS_CHALLENGE("Champions' Challenge", new WorldPoint(3188, 3355, 0)), + CHAOS_DRUID_TOWER("Chaos Druid Tower dungeon", new WorldPoint(2561, 3356, 0)), + CHASM_OF_FIRE("Chasm of Fire", new WorldPoint(1432, 3670, 0)), + CLOCK_TOWER("Clock Tower Dungeon", new WorldPoint(2568, 3229, 0)), + CORPOREAL_BEAST("Corporeal Beast cave", new WorldPoint(3202, 3681, 0)), + CORSAIR_COVE_E("Corsair Cove Dungeon", new WorldPoint(2522, 2861, 0)), + CORSAIR_COVE_N("Corsair Cove Dungeon", new WorldPoint(2482, 2891, 0)), + CRABCLAW_CAVES("Crabclaw Caves", new WorldPoint(1643, 3449, 0)), + CRABCLAW_CAVES_TUNNEL("Crabclaw Caves Tunnel (quest)", new WorldPoint(1643, 3449, 0)), + CRANDOR("Crandor Dungeon", new WorldPoint(2833, 3256, 0)), + CRASH_ISLAND("Crash Island Dungeon", new WorldPoint(2920, 2721, 0)), + DEEP_WILDERNESS("Deep Wilderness Dungeon", new WorldPoint(3044, 3924, 0)), + DRAYNOR_MANOR_E("Draynor Manor basement", new WorldPoint(3114, 3357, 0)), + DRAYNOR_MANOR_W("Draynor Manor basement", new WorldPoint(3091, 3362, 0)), + DRAYNOR_SEWERS_NW("Draynor Sewers", new WorldPoint(3083, 3272, 0)), + DRAYNOR_SEWERS_SE("Draynor Sewers", new WorldPoint(3117, 3244, 0)), + DWARVEN_MINE("Dwarven Mine", new WorldPoint(3017, 3450, 0)), + DWARVEN_MINE_STAIRS("Dwarven Mine", new WorldPoint(3058, 3377, 0)), + ECTOFUNTUS("Ectofuntus dungeon", new WorldPoint(3651, 3519, 0)), + EDGEVILLE("Edgeville Dungeon", new WorldPoint(3096, 3469, 0)), + EDGEVILLE_SHED("Edgeville Dungeon", new WorldPoint(3115, 3452, 0)), + EDGEVILLE_WILDERNESS("Edgeville Dungeon", new WorldPoint(3087, 3571, 0)), + ENTRANA("Entrana Dungeon", new WorldPoint(2819, 3374, 0)), + ETCETERIA("Miscellania and Etceteria Dungeon", new WorldPoint(2619, 3865, 0)), + FALADOR_MOLE_NW("Mole Hole", new WorldPoint(2985, 3387, 0)), + FALADOR_MOLE_SE("Mole Hole", new WorldPoint(2997, 3376, 0)), + FORSAKEN_TOWER("Forsaken Tower basement", new WorldPoint(1381, 3825, 0)), + FORTHOS_E("Forthos Dungeon", new WorldPoint(1701, 3574, 0)), + FORTHOS_W("Forthos Dungeon", new WorldPoint(1669, 3567, 0)), + FREMENNIK_SLAYER("Fremennik Slayer Dungeon", new WorldPoint(2796, 3615, 0)), + GOBLIN_CAVE("Goblin Cave", new WorldPoint(2622, 3393, 0)), + GOD_WARS("God Wars Dungeon", new WorldPoint(2917, 3747, 0)), + GRAND_TREE("Grand Tree Tunnels", new WorldPoint(2464, 3496, 0)), + GAUNTLET("The Gauntlet", new WorldPoint(3227, 6116, 0)), + HAM_HIDEOUT("H.A.M. Hideout", new WorldPoint(3164, 3252, 0)), + HEROES_GUILD("Heroes' Guild mine", new WorldPoint(2891, 3507, 0)), + HESPORI("Hespori Patch", new WorldPoint(1231, 3729, 0)), + ICE_QUEEN("Ice Queen's Lair", new WorldPoint(2846, 3516, 0)), + ICE_QUEEN_E("Ice Queen's Lair", new WorldPoint(2856, 3519, 0)), + ICE_QUEEN_W("Ice Queen's Lair", new WorldPoint(2822, 3510, 0)), + ICE_TROLL_E("Ice Troll Caves", new WorldPoint(2400, 3889, 0)), + ICE_TROLL_W("Ice Troll Caves", new WorldPoint(2315, 3894, 0)), + IORWERTH("Iorwerth Dungeon", new WorldPoint(3224, 6044, 0)), + IORWERTH_CAMP_CAVE("Iorwerth Camp cave", new WorldPoint(2200, 3262, 0)), + IORWERTH_CAMP_CAVE_PRIF("Iorwerth Camp cave", new WorldPoint(3224, 6014, 0)), + JOGRE("Jogre Dungeon", new WorldPoint(2824, 3118, 0)), + JORMUNGANDS_PRISON("Jormungand's Prison", new WorldPoint(2464, 4012, 0)), + KALPHITE_CAVE("Kalphite Cave", new WorldPoint(3319, 3122, 0)), + KALPHITE_LAIR("Kalphite Lair", new WorldPoint(3226, 3108, 0)), + KARAMJA_VOLCANO("Karamja Dungeon", new WorldPoint(2855, 3168, 0)), + KARUULM_SLAYER("Karuulm Slayer Dungeon", new WorldPoint(1308, 3807, 0)), + KING_BLACK_DRAGON("King Black Dragon Lair", new WorldPoint(3016, 3849, 0)), + KELDAGRIM("Keldagrim Entrance", new WorldPoint(2730, 3713, 0)), + KELDAGRIM_ENTRANCE_MOUNTAIN("Keldagrim Entrance", new WorldPoint(2795, 3718, 0)), + KRAKEN_BOSS("Kraken (boss)", new WorldPoint(2279, 10017, 0)), + KRAKEN_COVE("Kraken Cove", new WorldPoint(2277, 3611, 0)), + LAVA_MAZE("Lava Maze Dungeon", new WorldPoint(3068, 3856, 0)), + LEGENDS_GUILD("Legends' Guild dungeon", new WorldPoint(2723, 3375, 0)), + LIGHTHOUSE("Lighthouse basement", new WorldPoint(2508, 3644, 0)), + LIZARDMAN_CAVES("Lizardman Caves", new WorldPoint(1306, 3574, 0)), + LIZARDMAN_TEMPLE_E("Lizardman Temple", new WorldPoint(1329, 3669, 0)), + LIZARDMAN_TEMPLE_N("Lizardman Temple", new WorldPoint(1311, 3686, 0)), + LIZARDMAN_TEMPLE_S("Lizardman Temple", new WorldPoint(1313, 3663, 0)), + LIZARDMAN_TEMPLE_W("Lizardman Temple", new WorldPoint(1291, 3657, 0)), + LUMBRIDGE_CASTLE("Lumbridge Castle cellar", new WorldPoint(3208, 3218, 0)), + LUMBRIDGE_SWAMP("Lumbridge Swamp Caves", new WorldPoint(3168, 3172, 0)), + LUNAR_ISLE("Lunar Isle Mine", new WorldPoint(2141, 3944, 0)), + MAGE_ARENA("Mage Arena Bank", new WorldPoint(3089, 3956, 0)), + MELZARS_MAZE("Melzar's Maze basement", new WorldPoint(2923, 3250, 0)), + MINING_GUILD("Mining Guild basement", new WorldPoint(3018, 3339, 0)), + MISCELLANIA("Miscellania and Etceteria Dungeon", new WorldPoint(2508, 3846, 0)), + MOS_LE_HARMLESS("Mos Le'Harmless Cave", new WorldPoint(3747, 2973, 0)), + MOS_LE_HARMLESS_ISLAND_E("Mos Le'Harmless Cave", new WorldPoint(3829, 3062, 0)), + MOS_LE_HARMLESS_ISLAND_W("Mos Le'Harmless Cave", new WorldPoint(3814, 3062, 0)), + MOTHERLODE_MINE("Motherlode Mine", new WorldPoint(3059, 9764, 0)), + MOTHERLODE_MINE_GUILD("Motherlode Mine", new WorldPoint(3055, 9744, 0)), + MYREQUE_HIDEOUT_CANAFIS("Myreque Hideout", new WorldPoint(3494, 3464, 0)), + MYREQUE_HIDEOUT_SWAMP("Myreque Hideout", new WorldPoint(3508, 3448, 0)), + MYTH_GUILD("Myths' Guild dungeon", new WorldPoint(2456, 2847, 0)), + MYTH_GUILD_WRATH("Myths' Guild dungeon", new WorldPoint(2444, 2819, 0)), + OBSERVATORY_NE("Observatory Dungeon", new WorldPoint(2458, 3186, 0)), + OBSERVATORY_SW("Observatory Dungeon", new WorldPoint(2436, 3163, 0)), + OGRE_ENCLAVE("Ogre Enclave", new WorldPoint(2505, 3039, 0)), + OURANIA_CAVE("Ourania Cave", new WorldPoint(2451, 3231, 0)), + PATERDOMUS("Paterdomus basement", new WorldPoint(3404, 3506, 0)), + PORT_PHASMATYS_BREWERY("Port Phasmatys Brewery", new WorldPoint(3679, 3498, 0)), + PORT_SARIM_RAT_PITS("Port Sarim Rat Pits", new WorldPoint(3017, 3232, 0)), + QUIDAMORTEM_CAVE("Quidamortem Cave", new WorldPoint(1213, 3559, 0)), + RED_CHIN_HUNTING("Red chinchompa hunting ground", new WorldPoint(2525, 2894, 0)), + REVENANT_CAVES_N("Revenant Caves", new WorldPoint(3124, 3832, 0)), + REVENANT_CAVES_S("Revenant Caves", new WorldPoint(3074, 3655, 0)), + RIVER_ELID("River Elid Dungeon", new WorldPoint(3370, 3132, 0)), + RIVER_KELDA("River Kelda", new WorldPoint(2835, 10112, 0)), + SALT_MINE("Salt Mine", new WorldPoint(2866, 3941, 0)), + SCAVID_CAVES_N("Scavid Caves", new WorldPoint(2521, 3070, 0)), + SCAVID_CAVES_NE("Scavid Caves", new WorldPoint(2540, 3054, 0)), + SCAVID_CAVES_NEE("Scavid Caves", new WorldPoint(2552, 3054, 0)), + SCAVID_CAVES_E("Scavid Caves", new WorldPoint(2551, 3035, 0)), + SCAVID_CAVES_ISLAND_E("Scavid Caves (island)", new WorldPoint(2574, 3028, 0)), + SCAVID_CAVES_ISLAND_W("Scavid Caves (island)", new WorldPoint(2500, 2991, 0)), + SCAVID_CAVES_S("Scavid Caves", new WorldPoint(2528, 3014, 0)), + SCAVID_CAVES_SEE("Scavid Caves", new WorldPoint(2561, 3025, 0)), + SCORPIA_E("Scorpia cave", new WorldPoint(3243, 3949, 0)), + SCORPIA_N("Scorpia cave", new WorldPoint(3230, 3952, 0)), + SCORPIA_S("Scorpia cave", new WorldPoint(3231, 3936, 0)), + SHADE_CATACOMBS("Shade Catacombs", new WorldPoint(3484, 3321, 0)), + SHAYZIEN_CRYPTS("Shayzien Crypts", new WorldPoint(1482, 3549, 0)), + SHILO_VILLAGE_MINE("Shilo Village mine", new WorldPoint(2823, 3001, 0)), + SISTERHOOD_SANCTUARY("Sisterhood Sanctuary", new WorldPoint(3727, 3300, 0)), + SISTERHOOD_SANCTUARY_LAB("Sisterhood Sanctuary", new WorldPoint(3724, 3356, 0)), + SLAYER_TOWER("Slayer Tower basement", new WorldPoint(3416, 3535, 0)), + SLEPE_BASEMENT("Slepe basement", new WorldPoint(3718, 3307, 0)), + SMOKE_DEVIL("Smoke Devil Dungeon", new WorldPoint(2411, 3061, 0)), + SMOKE_DEVIL_BOSS("Thermonuclear Smoke Devil (boss)", new WorldPoint(2377, 9452, 0)), + SMOKE_DUNGEON("Smoke Dungeon", new WorldPoint(3309, 2962, 0)), + SOPHANEM("Sophanem Dungeon", new WorldPoint(3314, 2797, 0)), + SOURHOG_CAVE("Sourhog Cave", new WorldPoint(3149, 3347, 0)), + STRONGHOLD_OF_SECURITY("Stronghold of Security", new WorldPoint(3080, 3420, 0)), + STRONGHOLD_SLAYER("Stronghold Slayer Dungeon", new WorldPoint(2427, 3424, 0)), + TAVERLEY("Taverley Dungeon", new WorldPoint(2883, 3397, 0)), + TAVERLEY_ISLAND("Taverley Dungeon", new WorldPoint(2841, 3424, 0)), + TEMPLE_OF_IKOV("Temple of Ikov", new WorldPoint(2676, 3404, 0)), + THEATRE_OF_BLOOD("Theatre of Blood", new WorldPoint(3676, 3219, 0)), + TOLNAS_RIFT("Tolna's rift", new WorldPoint(3308, 3450, 0)), + TOWER_OF_LIFE("Tower of Life dungeon", new WorldPoint(2649, 3213, 0)), + TRAHAEARN_MINE("Trahaearn Mine", new WorldPoint(3270, 6049, 0)), + TREE_GNOME_VILLAGE("Tree Gnome Village Dungeon", new WorldPoint(2532, 3155, 0)), + TROLL_STRONGHOLD("Troll Stronghold", new WorldPoint(2838, 3690, 0)), + TROLL_STRONGHOLD_ROOF("Troll Stronghold", new WorldPoint(2830, 3677, 0)), + TROLL_STRONGHOLD_MOUNTAIN("Troll Stronghold", new WorldPoint(2826, 3647, 0)), + TUTORIAL_ISLAND_E("Tutorial Island mine", new WorldPoint(3110, 3126, 0)), + TUTORIAL_ISLAND_W("Tutorial Island mine", new WorldPoint(3087, 3119, 0)), + TZHAAR_CITY("TzHaar City (Mor Ul Rek)", new WorldPoint(2862, 9572, 0)), + UNDERGROUND_PASS_KANDARIN("Underground Pass", new WorldPoint(2312, 3215, 0)), + UNDERGROUND_PASS_TIRANNWN("Underground Pass", new WorldPoint(2433, 3315, 0)), + UNDERGROUND_PASS_TIRANNWN_PRIF("Underground Pass", new WorldPoint(3336, 5967, 0)), + UNDERWATER("Underwater", new WorldPoint(3765, 3898, 0)), + UZER("Ruins of Uzer basement", new WorldPoint(3492, 3090, 0)), + VARROCK_SEWERS("Varrock Sewers", new WorldPoint(3236, 3458, 0)), + VARROCK_SEWERS_ZOO("Varrock Sewers", new WorldPoint(3229, 3504, 0)), + VOLCANIC_MINE("Volcanic Mine", new WorldPoint(3815, 3808, 0)), + VTAM_CORP("VTAM Corporation", new WorldPoint(3243, 3383, 0)), + WARRENS("The Warrens", new WorldPoint(1812, 3745, 0)), + WARRIORS_GUILD("Warriors' Guild basement", new WorldPoint(2832, 3542, 0)), + WATERBIRTH("Waterbirth Dungeon", new WorldPoint(2520, 3740, 0)), + WATERBIRTH_MOUNTAIN("Waterbirth Dungeon", new WorldPoint(2542, 3741, 0)), + WATERBIRTH_SUBLEVELS("Waterbirth Dungeon (sub-levels)", new WorldPoint(2545, 10143, 0)), + WEISS_HOLE("Hole (shortcut to boat)", new WorldPoint(2853, 3944, 0)), + WEREWOLF_AGILITY("Werewolf Agility Course", new WorldPoint(3542, 3461, 0)), + WHITE_WOLF_TUNNEL_E("White Wolf Tunnel", new WorldPoint(2876, 3480, 0)), + WHITE_WOLF_TUNNEL_W("White Wolf Tunnel", new WorldPoint(2819, 3484, 0)), + WILDERNESS_AGILITY("Wilderness Agility Course Dungeon", new WorldPoint(3004, 3963, 0)), + WILDERNESS_GOD_WARS("Wilderness God Wars Dungeon", new WorldPoint(3016, 3739, 0)), + WILDERNESS_SLAYER_CAVE_NORTH("Wilderness Slayer Cave", new WorldPoint(3292, 3746, 0)), + WILDERNESS_SLAYER_CAVE_SOUTH("Wilderness Slayer Cave", new WorldPoint(3259, 3666, 0)), + WITCHAVEN("Witchaven Dungeon", new WorldPoint(2695, 3283, 0)), + WIZARDS_GUILD("Wizards' Guild basement", new WorldPoint(2593, 3085, 0)), + WIZARDS_TOWER("Wizards' Tower basement", new WorldPoint(3103, 3162, 0)), + WOODCUTTING_GUILD("Woodcutting Guild dungeon", new WorldPoint(1603, 3508, 0)), + WYVERN_CAVE("Wyvern Cave", new WorldPoint(3745, 3779, 0)), + WYVERN_CAVE_TASK("Wyvern Cave (task only)", new WorldPoint(3677, 3854, 0)), + YANILLE_AGILITY("Yanille Agility Dungeon", new WorldPoint(2568, 3122, 0)), + YANILLE_AGILITY_CITY("Yanille Agility Dungeon", new WorldPoint(2603, 3078, 0)), + ZALCANO("Zalcano's Prison", new WorldPoint(3280, 6059, 0)), + ZOGRE_CAVE("Zogre cave", new WorldPoint(2484, 3043, 0)); + + private final String tooltip; + private final WorldPoint location; + + DungeonLocation(String tooltip, WorldPoint location) + { + this.tooltip = tooltip; + this.location = location; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/DungeonPoint.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/DungeonPoint.java new file mode 100644 index 0000000000..9d50ec322f --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/DungeonPoint.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2020, Arman S + * 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.worldmap; + +import java.awt.image.BufferedImage; +import net.runelite.client.ui.overlay.worldmap.WorldMapPoint; + +class DungeonPoint extends WorldMapPoint +{ + DungeonPoint(DungeonLocation data, BufferedImage icon) + { + super(data.getLocation(), icon); + setTooltip(data.getTooltip()); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/FairyRingLocation.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/FairyRingLocation.java new file mode 100644 index 0000000000..25ee1addcd --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/FairyRingLocation.java @@ -0,0 +1,80 @@ +/* + * 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 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.worldmap; + +import lombok.Getter; +import net.runelite.api.coords.WorldPoint; + +@Getter +enum FairyRingLocation +{ + AIR("AIR", new WorldPoint(2699, 3249, 0)), + AIQ("AIQ", new WorldPoint(2995, 3112, 0)), + AJR("AJR", new WorldPoint(2779, 3615, 0)), + AJS("AJS", new WorldPoint(2499, 3898, 0)), + AKQ("AKQ", new WorldPoint(2318, 3617, 0)), + AKS("AKS", new WorldPoint(2570, 2958, 0)), + ALP("ALP", new WorldPoint(2502, 3638, 0)), + ALQ("ALQ", new WorldPoint(3598, 3496, 0)), + ALS("ALS", new WorldPoint(2643, 3497, 0)), + BIP("BIP", new WorldPoint(3409, 3326, 0)), + BIQ("BIQ", new WorldPoint(3248, 3095, 0)), + BIS("BIS", new WorldPoint(2635, 3268, 0)), + BJS("BJS", new WorldPoint(2147, 3069, 0)), + BKP("BKP", new WorldPoint(2384, 3037, 0)), + BKR("BKR", new WorldPoint(3468, 3433, 0)), + BLP("BLP", new WorldPoint(2432, 5127, 0)), + BLR("BLR", new WorldPoint(2739, 3353, 0)), + CIP("CIP", new WorldPoint(2512, 3886, 0)), + CIR("CIR", new WorldPoint(1303, 3762, 0)), + CIQ("CIQ", new WorldPoint(2527, 3129, 0)), + CJR("CJR", new WorldPoint(2704, 3578, 0)), + CKR("CKR", new WorldPoint(2800, 3005, 0)), + CKS("CKS", new WorldPoint(3446, 3472, 0)), + CLP("CLP", new WorldPoint(3081, 3208, 0)), + CLS("CLS", new WorldPoint(2681, 3083, 0)), + DIP("DIP", new WorldPoint(3039, 4757, 0)), + DIS("DIS", new WorldPoint(3109, 3149, 0)), + DJP("DJP", new WorldPoint(2657, 3232, 0)), + DJR("DJR", new WorldPoint(1452, 3659, 0)), + DKP("DKP", new WorldPoint(2899, 3113, 0)), + DKR("DKR", new WorldPoint(3126, 3496, 0)), + DKS("DKS", new WorldPoint(2743, 3721, 0)), + DLQ("DLQ", new WorldPoint(3422, 3018, 0)), + DLR("DLR", new WorldPoint(2212, 3101, 0)), + CIS("CIS", new WorldPoint(1638, 3868, 0)), + CLR("CLR", new WorldPoint(2737, 2739, 0)), + ZANARIS("Zanaris", new WorldPoint(2411, 4436, 0)); + + private final String code; + private final WorldPoint location; + + FairyRingLocation(String code, WorldPoint location) + { + this.code = code; + this.location = location; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/FairyRingPoint.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/FairyRingPoint.java new file mode 100644 index 0000000000..ef4e55da92 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/FairyRingPoint.java @@ -0,0 +1,42 @@ +/* + * 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 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.worldmap; + +import java.awt.image.BufferedImage; +import net.runelite.client.ui.overlay.worldmap.WorldMapPoint; + +class FairyRingPoint extends WorldMapPoint +{ + FairyRingPoint(FairyRingLocation data, BufferedImage icon, boolean showTooltip) + { + super(data.getLocation(), icon); + + if (showTooltip) + { + setTooltip("Fairy Ring - " + data.getCode()); + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/FarmingPatchLocation.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/FarmingPatchLocation.java new file mode 100644 index 0000000000..cc3546e706 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/FarmingPatchLocation.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2018, Torkel Velure + * 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.worldmap; + +import lombok.Getter; +import net.runelite.api.coords.WorldPoint; + +@Getter +enum FarmingPatchLocation +{ + ALLOTMENT("Allotment", + new WorldPoint(3793, 2836, 0), + new WorldPoint(1269, 3730, 0) + ), + ALLOTMENT_FLOWER("Allotment/Flower", new WorldPoint(3289, 6100, 0)), + ALLOTMENT_HERB_FLOWER("Allotment/Herb/Flower", + new WorldPoint(1729, 3558, 0), + new WorldPoint(3598, 3524, 0), + new WorldPoint(3052, 3309, 0), + new WorldPoint(2810, 3462, 0), + new WorldPoint(2663, 3375, 0) + ), + ANIMA_HERB("Anima/Herb", new WorldPoint(1235, 3724, 0)), + BELLADONNA("Belladonna", new WorldPoint(3084, 3356, 0)), + BUSH("Bush", + new WorldPoint(2938, 3223, 0), + new WorldPoint(2589, 3862, 0), + new WorldPoint(3182, 3356, 0), + new WorldPoint(2615, 3224, 0) + ), + BUSH_FLOWER("Bush/Flower", new WorldPoint(1259, 3729, 0)), + CACTUS("Cactus", + new WorldPoint(3313, 3201, 0), + new WorldPoint(1264, 3745, 0) + ), + CALQUAT("Calquat", new WorldPoint(2793, 3099, 0)), + CELASTRUS_FRUIT_TREE("Celastrus/Fruit Tree", + new WorldPoint(1242, 3755, 0) + ), + CRYSTAL_TREE("Crystal Tree", + new WorldPoint(3292, 6120, 0) + ), + FRUIT_TREE("Fruit Tree", + new WorldPoint(2487, 3181, 0), + new WorldPoint(2343, 3160, 0), + new WorldPoint(2472, 3445, 0), + new WorldPoint(2858, 3432, 0), + new WorldPoint(2765, 3211, 0) + ), + GRAPES("Grapes", new WorldPoint(1807, 3555, 0)), + HARDWOOD("Hardwood", + new WorldPoint(3707, 3838, 0) + ), + HERB("Herb", + new WorldPoint(3789, 2840, 0), + new WorldPoint(2847, 3933, 0), + new WorldPoint(2828, 3696, 0) + ), + HESPORI("Hespori", new WorldPoint(1182, 10068, 0)), + HOPS("Hops", + new WorldPoint(2572, 3102, 0), + new WorldPoint(2661, 3523, 0), + new WorldPoint(3224, 3313, 0), + new WorldPoint(3812, 3334, 0) + ), + MUSHROOM("Mushroom", new WorldPoint(3449, 3471, 0)), + REDWOOD("Redwood", new WorldPoint(1233, 3754, 0)), + SEAWEED("Seaweed", new WorldPoint(3730, 10271, 0)), + SPIRIT_TREE("Spirit Tree", + new WorldPoint(3056, 3259, 0), + new WorldPoint(1690, 3540, 0), + new WorldPoint(3614, 3856, 0), + new WorldPoint(2799, 3205, 0), + new WorldPoint(1254, 3753, 0) + ), + TREE("Tree", + new WorldPoint(3226, 3457, 0), + new WorldPoint(2933, 3436, 0), + new WorldPoint(3189, 3233, 0), + new WorldPoint(2434, 3418, 0), + new WorldPoint(3005, 3375, 0), + new WorldPoint(1234, 3736, 0) + ); + + private final String tooltip; + private final WorldPoint[] locations; + + FarmingPatchLocation(String description, WorldPoint... locations) + { + this.tooltip = "Farming patch - " + description; + this.locations = locations; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/FarmingPatchPoint.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/FarmingPatchPoint.java new file mode 100644 index 0000000000..79b380c220 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/FarmingPatchPoint.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2018, Torkel Velure + * 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.worldmap; + +import java.awt.image.BufferedImage; +import net.runelite.api.coords.WorldPoint; +import net.runelite.client.ui.overlay.worldmap.WorldMapPoint; + +class FarmingPatchPoint extends WorldMapPoint +{ + FarmingPatchPoint(WorldPoint point, String tooltip, BufferedImage icon) + { + super(point, icon); + setTooltip(tooltip); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/FishingSpotLocation.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/FishingSpotLocation.java new file mode 100644 index 0000000000..3180ca0973 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/FishingSpotLocation.java @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2020, melky + * 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.worldmap; + +import java.util.Arrays; +import java.util.stream.Collectors; +import lombok.Getter; +import net.runelite.api.coords.WorldPoint; +import net.runelite.client.game.FishingSpot; + +@Getter +enum FishingSpotLocation +{ + AL_KHARID(FishingSpot.SHRIMP, + new WorldPoint(3274, 3140, 0), new WorldPoint(3266, 3148, 0)), + APE_ATOLL_SOUTH_WEST(FishingSpot.SHARK, new WorldPoint(2698, 2703, 0)), + BARBARIAN_OUTPOST(FishingSpot.SHRIMP, + new WorldPoint(2497, 3548, 0), new WorldPoint(2509, 3562, 0), + new WorldPoint(2514, 3575, 0)), + BARBARIAN_VILLAGE(FishingSpot.SALMON, + new WorldPoint(3103, 3424, 0), new WorldPoint(3109, 3433, 0)), + BRAINDEATH_ISLAND(FishingSpot.QUEST_RUM_DEAL, + new WorldPoint(2112, 5074, 0), new WorldPoint(2161, 5061, 0), + new WorldPoint(2172, 5074, 0)), + BRIMHAVEN_QUEST(FishingSpot.QUEST_TAI_BWO_WANNAI_TRIO, new WorldPoint(2767, 3165, 0)), + BURGH_DE_ROTT_SOUTH(FishingSpot.SHARK, + new WorldPoint(3472, 3192, 0), new WorldPoint(3486, 3182, 0), + new WorldPoint(3497, 3174, 0), new WorldPoint(3513, 3177, 0), + new WorldPoint(3528, 3164, 0), new WorldPoint(3537, 3177, 0), + new WorldPoint(3545, 3179, 0), new WorldPoint(3553, 3177, 0), + new WorldPoint(3559, 3173, 0), new WorldPoint(3564, 3174, 0)), + CATHERBY(new FishingSpot[]{FishingSpot.SHARK, FishingSpot.LOBSTER, FishingSpot.SHRIMP}, + new WorldPoint(2836, 3431, 0), new WorldPoint(2844, 3429, 0), + new WorldPoint(2853, 3423, 0), new WorldPoint(2859, 3426, 0)), + DRAYNOR_VILLAGE(FishingSpot.SHRIMP, new WorldPoint(3084, 3228, 0)), + ENTRANA_CENTER(FishingSpot.SALMON, + new WorldPoint(2841, 3356, 0), new WorldPoint(2842, 3359, 0), + new WorldPoint(2847, 3361, 0)), + ENTRANA_DOCK(FishingSpot.SHRIMP, + new WorldPoint(2875, 3331, 0), new WorldPoint(2878, 3334, 0), + new WorldPoint(2878, 3339, 0), new WorldPoint(2875, 3342, 0)), + ETCETERIA_DOCK(FishingSpot.ETCETERIA_LOBSTER, new WorldPoint(2577, 3854, 0)), + FAIRY_RING_CKR(FishingSpot.KARAMBWANJI, new WorldPoint(2806, 3014, 0)), + FAIRY_RING_DKP(FishingSpot.KARAMBWAN, + new WorldPoint(2898, 3119, 0), new WorldPoint(2911, 3119, 0)), + FARMING_GUILD_SOUTH_EAST(FishingSpot.SALMON, new WorldPoint(1269, 3707, 0)), + FARMING_GUILD_SOUTH_WEST1(FishingSpot.SHARK, new WorldPoint(1209, 3687, 0)), + FARMING_GUILD_SOUTH_WEST2(FishingSpot.SHARK, new WorldPoint(1221, 3714, 0)), + FARMING_GUILD_WEST(FishingSpot.SHARK, + new WorldPoint(1199, 3736, 0), new WorldPoint(1208, 3749, 0)), + FELDIP_HILLS_SOUTH(FishingSpot.SHRIMP, new WorldPoint(2511, 2838, 0)), + FISHING_CONTEST(FishingSpot.QUEST_FISHING_CONTEST, + new WorldPoint(2626, 3415, 0), new WorldPoint(2631, 3425, 0), + new WorldPoint(2629, 3435, 0), new WorldPoint(2636, 3444, 0)), + FISHING_GUILD(new FishingSpot[]{FishingSpot.SHARK, FishingSpot.LOBSTER}, + new WorldPoint(2604, 3423, 0), new WorldPoint(2605, 3417, 0), + new WorldPoint(2611, 3413, 0)), + FISHING_GUILD_PLATFORM(FishingSpot.MINNOW, + new WorldPoint(2609, 3444, 0), new WorldPoint(2617, 3444, 0)), + FISHING_PLATFORM(FishingSpot.SHRIMP, + new WorldPoint(2788, 3273, 0), new WorldPoint(2794, 3279, 0), + new WorldPoint(2793, 3283, 0)), + HOSIDIUS_CENTER(FishingSpot.SALMON, new WorldPoint(1715, 3612, 0)), + HOSIDIUS_EAST(new FishingSpot[]{FishingSpot.SHARK, FishingSpot.LOBSTER, FishingSpot.SHRIMP}, + new WorldPoint(1817, 3603, 0), new WorldPoint(1827, 3605, 0), + new WorldPoint(1828, 3614, 0), new WorldPoint(1840, 3619, 0), + new WorldPoint(1838, 3595, 0)), + INFIRMARY_SOUTH(FishingSpot.SALMON, new WorldPoint(1584, 3566, 0)), + IORWERTH_CAMP_INSIDE(FishingSpot.SALMON, new WorldPoint(3239, 5997, 0)), + IORWERTH_CAMP_NORTH_INSIDE(FishingSpot.SHARK, new WorldPoint(3185, 6027, 0)), + IORWERTH_CAMP_NORTH_OUTSIDE(FishingSpot.SHARK, new WorldPoint(2161, 3275, 0)), + IORWERTH_CAMP_OUTSIDE(FishingSpot.SALMON, new WorldPoint(2215, 3245, 0)), + ISAFDAR_NORTH_EAST_INSIDE(FishingSpot.SALMON, new WorldPoint(3293, 6005, 0)), + ISAFDAR_NORTH_EAST_OUTSIDE(FishingSpot.SALMON, new WorldPoint(2269, 3253, 0)), + JATISZO(new FishingSpot[]{FishingSpot.SHARK, FishingSpot.LOBSTER}, + new WorldPoint(2400, 3780, 0), new WorldPoint(2412, 3780, 0), + new WorldPoint(2419, 3789, 0)), + KINGSTOWN_EAST(FishingSpot.SALMON, new WorldPoint(1723, 3685, 0)), + LANDS_END_EAST(FishingSpot.SHRIMP, new WorldPoint(1534, 3414, 0)), + LANDS_END_WEST(new FishingSpot[]{FishingSpot.SHARK, FishingSpot.LOBSTER, FishingSpot.SHRIMP}, + new WorldPoint(1484, 3432, 0)), + LUMBRIDGE_RIVER(FishingSpot.SALMON, + new WorldPoint(3238, 3241, 0), new WorldPoint(3237, 3253, 0)), + LUMBRIDGE_SWAMP_CAVE_EAST(FishingSpot.CAVE_EEL, new WorldPoint(3244, 9570, 0)), + LUMBRIDGE_SWAMP_CAVE_WEST(FishingSpot.CAVE_EEL, new WorldPoint(3153, 9544, 0)), + LUMBRIDGE_SWAMP_SOUTH_EAST(FishingSpot.SHRIMP, new WorldPoint(3244, 3153, 0)), + MARIM(FishingSpot.SHARK, new WorldPoint(2774, 2740, 0)), + MOLCH_ISLAND(FishingSpot.COMMON_TENCH, new WorldPoint(1370, 3632, 0)), + MORTTON(FishingSpot.SLIMY_EEL, new WorldPoint(3439, 3273, 0)), + MORT_MYRE_SWAMP_NORTH(FishingSpot.SLIMY_EEL, + new WorldPoint(3480, 3433, 0), new WorldPoint(3485, 3448, 0)), + MORT_MYRE_SWAMP_WEST(FishingSpot.SLIMY_EEL, + new WorldPoint(3425, 3409, 0), new WorldPoint(3432, 3415, 0)), + MOR_UL_REK(FishingSpot.INFERNAL_EEL, + new WorldPoint(2443, 5104, 0), new WorldPoint(2476, 5077, 0), + new WorldPoint(2537, 5086, 0)), + MOUNT_QUIDAMORTEM(FishingSpot.BARB_FISH, + new WorldPoint(1271, 3546, 0), new WorldPoint(1265, 3541, 0), + new WorldPoint(1253, 3542, 0)), + MUDSKIPPER_POINT(FishingSpot.SHRIMP, + new WorldPoint(2995, 3158, 0), new WorldPoint(2985, 3176, 0)), + MUSA_POINT(new FishingSpot[]{FishingSpot.LOBSTER, FishingSpot.SHRIMP}, new WorldPoint(2925, 3179, 0)), + MYTHS_GUILD_NORTH(FishingSpot.LOBSTER, new WorldPoint(2456, 2893, 0)), + OBSERVATORY_EAST(FishingSpot.SALMON, new WorldPoint(2466, 3151, 0)), + OTTOS_GROTTO(FishingSpot.BARB_FISH, + new WorldPoint(2500, 3509, 0), new WorldPoint(2504, 3495, 0), + new WorldPoint(2505, 3515, 0), new WorldPoint(2520, 3518, 0)), + PISCATORIS(FishingSpot.MONKFISH, new WorldPoint(2308, 3700, 0)), + PORT_PISCARILIUS_EAST(FishingSpot.ANGLERFISH, new WorldPoint(1831, 3773, 0)), + PORT_PISCARILIUS_WEST(new FishingSpot[]{FishingSpot.LOBSTER, FishingSpot.SHRIMP}, + new WorldPoint(1762, 3796, 0), new WorldPoint(1745, 3802, 0)), + PRIFFDINAS_INSIDE_EAST(new FishingSpot[]{FishingSpot.SHARK, FishingSpot.LOBSTER}, + new WorldPoint(3186, 6102, 0), new WorldPoint(3187, 6123, 0)), + PRIFFDINAS_INSIDE_NORTH(new FishingSpot[]{FishingSpot.SHARK, FishingSpot.LOBSTER}, + new WorldPoint(3250, 6182, 0), new WorldPoint(3258, 6180, 0), + new WorldPoint(3258, 6197, 0)), + PRIFFDINAS_OUTSIDE_EAST(new FishingSpot[]{FishingSpot.SHARK, FishingSpot.LOBSTER}, + new WorldPoint(2162, 3350, 0), new WorldPoint(2163, 3371, 0)), + PRIFFDINAS_OUTSIDE_NORTH(new FishingSpot[]{FishingSpot.SHARK, FishingSpot.LOBSTER}, + new WorldPoint(2226, 3430, 0), new WorldPoint(2234, 3428, 0), + new WorldPoint(2234, 3445, 0)), + RELLEKKA_CENTER(FishingSpot.LOBSTER, new WorldPoint(2641, 3696, 0)), + RELLEKKA_NORTH_EAST(FishingSpot.SHARK, new WorldPoint(2649, 3708, 0)), + RELLEKKA_WEST(FishingSpot.SHRIMP, new WorldPoint(2632, 3694, 0)), + RIVER_ARDOUGNE(FishingSpot.SALMON, + new WorldPoint(2560, 3374, 0), new WorldPoint(2565, 3370, 0), + new WorldPoint(2526, 3412, 0), new WorldPoint(2536, 3405, 0), + new WorldPoint(2507, 3420, 0)), + SAND_CRAB_PENINSULA_NORTH(FishingSpot.SHARK, new WorldPoint(1675, 3490, 0)), + SAND_CRAB_PENINSULA_SOUTH(FishingSpot.SHRIMP, new WorldPoint(1676, 3469, 0)), + SAND_CRAB_PENINSULA_WEST(FishingSpot.LOBSTER, new WorldPoint(1668, 3479, 0)), + SEERS_VILLAGE(FishingSpot.SALMON, + new WorldPoint(2725, 3524, 0), new WorldPoint(2714, 3531, 0)), + SHILO_VILLAGE(FishingSpot.SALMON, + new WorldPoint(2854, 2977, 0), new WorldPoint(2858, 2973, 0)), + TAVERLEY_DUNGEON(FishingSpot.LAVA_EEL, + new WorldPoint(2893, 9764, 0), new WorldPoint(2889, 9766, 0), + new WorldPoint(2883, 9765, 0)), + TREE_GNOME_STRONGHOLD(FishingSpot.SALMON, + new WorldPoint(2389, 3422, 0), new WorldPoint(2382, 3415, 0)), + TUTORIAL_ISLAND(FishingSpot.TUTORIAL_SHRIMP, new WorldPoint(3100, 3091, 0)), + WATSON_HOUSE_SOUTH(FishingSpot.SALMON, new WorldPoint(1646, 3558, 0)), + WILDERNESS_BANDIT_CAMP(FishingSpot.SHRIMP, new WorldPoint(3049, 3704, 0)), + WILDERNESS_DARK_CRABS(FishingSpot.DARK_CRAB, + new WorldPoint(3362, 3802, 0), new WorldPoint(3347, 3813, 0)), + WILDERNESS_LAVA_MAZE(FishingSpot.LAVA_EEL, new WorldPoint(3071, 3840, 0)), + WILDERNESS_RESOURCE_AREA(FishingSpot.DARK_CRAB, new WorldPoint(3186, 3925, 0)), + ZUL_ANDRA(FishingSpot.SACRED_EEL, + new WorldPoint(2183, 3068, 0), new WorldPoint(2195, 3067, 0)), + ; + + private final WorldPoint[] locations; + private final String tooltip; + + FishingSpotLocation(FishingSpot fishingSpot, WorldPoint... locations) + { + this.tooltip = fishingSpot.getWorldMapTooltip(); + this.locations = locations; + } + + FishingSpotLocation(FishingSpot[] fishingSpot, WorldPoint... locations) + { + this.tooltip = Arrays.stream(fishingSpot).map(FishingSpot::getWorldMapTooltip).collect(Collectors.joining(" / ")); + this.locations = locations; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/FishingSpotPoint.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/FishingSpotPoint.java new file mode 100644 index 0000000000..fa32c86ffe --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/FishingSpotPoint.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2020, melky + * 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.worldmap; + +import java.awt.image.BufferedImage; +import net.runelite.api.coords.WorldPoint; +import net.runelite.client.ui.overlay.worldmap.WorldMapPoint; + +class FishingSpotPoint extends WorldMapPoint +{ + FishingSpotPoint(WorldPoint point, String tooltip, BufferedImage icon) + { + super(point, icon); + setTooltip(tooltip); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/HunterAreaLocation.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/HunterAreaLocation.java new file mode 100644 index 0000000000..3f9ee919b8 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/HunterAreaLocation.java @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2020, melky + * 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.worldmap; + +import com.google.common.base.Joiner; +import lombok.AllArgsConstructor; +import lombok.Getter; +import net.runelite.api.coords.WorldPoint; + +@Getter +enum HunterAreaLocation +{ + BONEYARD_HUNTER_AREA(new WorldPoint(3294, 3673, 0), HunterCreature.BLACK_SALAMANDER), + CANIFIS_HUNTER_AREA1(new WorldPoint(3553, 3438, 0), HunterCreature.SWAMP_LIZARD), + CANIFIS_HUNTER_AREA2(new WorldPoint(3535, 3445, 0), HunterCreature.SWAMP_LIZARD), + FALCONRY(new WorldPoint(2379, 3599, 0), HunterCreature.SPOTTED_KEBBIT, HunterCreature.DARK_KEBBIT, + HunterCreature.DASHING_KEBBIT, HunterCreature.GREATER_SIREN), + FELDIP_HUNTER_AREA(new WorldPoint(2557, 2912, 0), HunterCreature.CRIMSON_SWIFT, HunterCreature.FELDIP_WEASEL, + HunterCreature.TROPICAL_WAGTAIL, HunterCreature.SPINED_LARUPIA, HunterCreature.BARB_TAILED_KEBBIT, + HunterCreature.BLACK_WARLOCK, HunterCreature.CARNIVOROUS_CHINCHOMPA), + FOSSIL_ISLAND1(new WorldPoint(3693, 3800, 0), HunterCreature.HERBIBOAR), + FOSSIL_ISLAND2(new WorldPoint(3701, 3809, 0), HunterCreature.HERBIBOAR), + FOSSIL_ISLAND3(new WorldPoint(3703, 3829, 0), HunterCreature.HERBIBOAR), + FOSSIL_ISLAND4(new WorldPoint(3749, 3850, 0), HunterCreature.HERBIBOAR), + FOSSIL_ISLAND5(new WorldPoint(3684, 3870, 0), HunterCreature.HERBIBOAR), + FOSSIL_ISLAND_UNDERWATER(new WorldPoint(3743, 10295, 0), HunterCreature.FISH_SHOAL), + GWENITH_HUNTER_AREA_OUTSIDE(new WorldPoint(2269, 3408, 0), HunterCreature.CARNIVOROUS_CHINCHOMPA), + GWENITH_HUNTER_AREA_INSIDE(new WorldPoint(3293, 6160, 0), HunterCreature.CARNIVOROUS_CHINCHOMPA), + KARAMJA_HUNTER_AREA(new WorldPoint(2786, 3001, 0), HunterCreature.HORNED_GRAAHK), + KEBOS_SWAMP(new WorldPoint(1184, 3595, 0), HunterCreature.CRIMSON_SWIFT), + KOUREND_WOODLAND_CENTER(new WorldPoint(1512, 3478, 0), HunterCreature.RUBY_HARVEST), + KOUREND_WOODLAND_NORTH_WEST(new WorldPoint(1481, 3504, 0), HunterCreature.CHINCHOMPA), + KOUREND_WOODLAND_SOUTH(new WorldPoint(1556, 3436, 0), HunterCreature.COPPER_LONGTAIL), + LAKE_MOLCH(new WorldPoint(1363, 3632, 0), HunterCreature.BLUEGILL, HunterCreature.COMMON_TENCH, + HunterCreature.MOTTLED_EEL, HunterCreature.GREATER_SIREN), + OURANIA_HUNTER_AREA_EAST(new WorldPoint(2447, 3219, 0), HunterCreature.RED_SALAMANDER), + OURANIA_HUNTER_AREA_SOUTH(new WorldPoint(2475, 3240, 0), HunterCreature.RED_SALAMANDER), + PISCATORIS_HUNTER_AREA(new WorldPoint(2335, 3584, 0), HunterCreature.COMMON_KEBBIT, HunterCreature.COPPER_LONGTAIL, + HunterCreature.RUBY_HARVEST, HunterCreature.WILD_KEBBIT, HunterCreature.FERRET, HunterCreature.WHITE_RABBIT, + HunterCreature.PRICKLY_KEBBIT, HunterCreature.RAZOR_BACKED_KEBBIT, HunterCreature.CHINCHOMPA), + RELLEKA_HUNTER_AREA(new WorldPoint(2719, 3780, 0), HunterCreature.POLAR_KEBBIT, HunterCreature.CERULEAN_TWITCH, + HunterCreature.SAPPHIRE_GLACIALIS, HunterCreature.SNOWY_KNIGHT, + HunterCreature.SABRE_TOOTHED_KEBBIT, HunterCreature.SABRE_TOOTHED_KYATT), + SLEPE_NORTH(new WorldPoint(3677, 3405, 0), HunterCreature.SWAMP_LIZARD), + UZER_HUNTER_AREA(new WorldPoint(3401, 3104, 0), HunterCreature.GOLDEN_WARBLER, HunterCreature.DESERT_DEVIL, + HunterCreature.ORANGE_SALAMANDER), + WILDERNESS(new WorldPoint(3142, 3771, 0), HunterCreature.BLACK_CHINCHOMPA), + ; + + private final WorldPoint location; + private final String tooltip; + + HunterAreaLocation(WorldPoint location, HunterCreature... creatures) + { + this.location = location; + this.tooltip = Joiner.on("
").join(creatures); + } + + @AllArgsConstructor + private enum HunterCreature + { + BARB_TAILED_KEBBIT("Barb-tailed kebbit", 33), + BLACK_CHINCHOMPA("Black chinchompa", 73), + BLACK_SALAMANDER("Black salamander", 67), + BLACK_WARLOCK("Black warlock", 45), + BLUEGILL("Bluegill", 35), + CARNIVOROUS_CHINCHOMPA("Carnivorous chinchompa", 63), + CERULEAN_TWITCH("Cerulean twitch", 11), + CHINCHOMPA("Chinchompa", 53), + COMMON_KEBBIT("Common kebbit", 3), + COMMON_TENCH("Common tench", 51), + COPPER_LONGTAIL("Copper longtail", 9), + CRIMSON_SWIFT("Crimson swift", 1), + DARK_KEBBIT("Dark kebbit", 57), + DASHING_KEBBIT("Dashing kebbit", 69), + DESERT_DEVIL("Desert devil", 13), + FELDIP_WEASEL("Feldip Weasel", 7), + FERRET("Ferret", 27), + FISH_SHOAL("Fish shoal", 44), + GOLDEN_WARBLER("Golden warbler", 5), + GREATER_SIREN("Greater siren", 87), + HERBIBOAR("Herbiboar", 80), + HORNED_GRAAHK("Horned graahk", 41), + // IMP and MANIACAL_MONKEY do not have a specific hunter area + MOTTLED_EEL("Mottled eel", 68), + ORANGE_SALAMANDER("Orange salamander", 47), + POLAR_KEBBIT("Polar kebbit", 1), + PRICKLY_KEBBIT("Prickly kebbit", 37), + RAZOR_BACKED_KEBBIT("Razor-backed kebbit", 49), + RED_SALAMANDER("Red salamander", 59), + RUBY_HARVEST("Ruby harvest", 15), + SABRE_TOOTHED_KEBBIT("Sabre-toothed kebbit", 51), + SABRE_TOOTHED_KYATT("Sabre-toothed kyatt", 55), + SAPPHIRE_GLACIALIS("Sapphire glacialis", 25), + SNOWY_KNIGHT("Snowy knight", 35), + SPINED_LARUPIA("Spined larupia", 31), + SPOTTED_KEBBIT("Spotted kebbit", 43), + SWAMP_LIZARD("Swamp lizard", 29), + TROPICAL_WAGTAIL("Tropical wagtail", 19), + WHITE_RABBIT("White rabbit", 27), + WILD_KEBBIT("Wild kebbit", 23), + ; + + private String name; + private int level; + + @Override + public String toString() + { + return name + " (" + level + ")"; + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/HunterAreaPoint.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/HunterAreaPoint.java new file mode 100644 index 0000000000..2725f3de11 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/HunterAreaPoint.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2020, melky + * 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.worldmap; + +import java.awt.image.BufferedImage; +import net.runelite.client.ui.overlay.worldmap.WorldMapPoint; + +class HunterAreaPoint extends WorldMapPoint +{ + HunterAreaPoint(HunterAreaLocation data, BufferedImage icon) + { + super(data.getLocation(), icon); + setTooltip(data.getTooltip()); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/KourendTaskLocation.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/KourendTaskLocation.java new file mode 100644 index 0000000000..3b3890ffed --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/KourendTaskLocation.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2020, Brooklyn + * 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.worldmap; + +import lombok.Getter; +import net.runelite.api.coords.WorldPoint; + +@Getter +enum KourendTaskLocation +{ + ARCEUUS_LIBRARY_SOUTH("Arceuus Task (0%) - Library Assistance", new WorldPoint(1622, 3801, 0)), + ARCEUUS_LIBRARY_NORTH("Arceuus Task (0%) - Library Assistance", new WorldPoint(1638, 3817, 0)), + ARCEUUS_DENSE_ESSENCE("Arceuus Task (25%) - Dense Essence Mining", new WorldPoint(1757, 3852, 0)), + ARCEUUS_REANIMATE_CREATURES("Arceuus Task (60%) - Creature Reanimation", new WorldPoint(1714, 3883, 0)), + ARCEUUS_BLOOD_ALTAR("Arceuus Unlock (100%) - Blood Rune Crafting", new WorldPoint(1716, 3829, 0)), + ARCEUUS_SOUL_ALTAR("Arceuus Unlock (100%) - Soul Rune Crafting", new WorldPoint(1814, 3854, 0)), + + HOSIDIUS_PLOUGH_NORTH("Hosidius Task (0%) - Ploughs", new WorldPoint(1770, 3550, 0)), + HOSIDIUS_PLOUGH_SOUTH("Hosidius Task (0%) - Ploughs", new WorldPoint(1769, 3528, 0)), + HOSIDUIS_FERTILISER("Hosidius Task (5%) - Making Fertilizer", new WorldPoint(1701, 3527, 0)), + HOSIDIUS_SPIRIT_TREE("Hosidius Unlock (35%) - Spirit Tree Patch", new WorldPoint(1695, 3545, 0)), + HOSIDIUS_MESS("Hosidius Task (45%) - Mess", new WorldPoint(1639, 3629, 0)), + HOSIDIUS_FARMING_PATCH("Hosidius Unlock (50%) - Disease Protection", new WorldPoint(1740, 3552, 0)), + HOSIDIUS_VINERY("Hosidius Task (65%) - Grape Planting", new WorldPoint(1804, 3564, 0)), + HOSIDIUS_TITHE_FARM("Hosidius Unlock (100%) - Tithe Farm", new WorldPoint(1802, 3499, 0)), + HOSIDIUS_KITCHEN("Hosidius Unlock (100%) - Kitchen", new WorldPoint(1678, 3618, 0)), + + LOVAKENGJ_DYNAMITE("Lovakengj Task (0%) - Making Juniper Charcoal", new WorldPoint(1718, 3472, 0)), + LOVAKENGJ_SULFUR("Lovakengj Task (0%) - Mining Volcanic Sulphur", new WorldPoint(1437, 3861, 0)), + LOVAKENGJ_ARMOURER_T1("Lovakengj Task (30%) - Armourer (Tier 1)", new WorldPoint(1453, 3750, 0)), + LOVAKENGJ_ARMOURER_T2("Lovakengj Task (40%) - Armourer (Tier 2)", new WorldPoint(1436, 3784, 0)), + LOVAKENGJ_ARMOURER_T3("Lovakengj Task (50%) - Armourer (Tier 3)", new WorldPoint(1430, 3797, 0)), + LOVAKENGJ_ARMOURER_T4("Lovakengj Task (60%) - Armourer (Tier 4)", new WorldPoint(1460, 3763, 0)), + LOVAKENGJ_ARMOURER_T5("Lovakengj Task (70%) - Armourer (Tier 5)", new WorldPoint(1524, 3755, 0)), + LOVAKENGJ_BLAST_MINE("Lovakengj Unlock (100%) - Blast Mine", new WorldPoint(1488, 3865, 0)), + + PISCARILIUS_CRANE_REPAIR("Piscarilius Task (0%) - Fishing Crane Repair", new WorldPoint(1820, 3742, 0)), + PISCARILIUS_FRESH_FISH("Piscarilius Task (15%) - Fish Delivery", new WorldPoint(1831, 3716, 0)), + PISCARILIUS_SANDWORMS("Piscarilius Task (30%) - Hunting Sandworms", new WorldPoint(1841, 3789, 0)), + PISCARILIUS_STEALING_ARTEFACTS("Piscarilius Task (75%) - Stealing Artefacts", new WorldPoint(1849, 3753, 0)), + + SHAYZIEN_HEALING_SOLDIERS("Shayzien Task (0%) - Healing Wounded Soldiers", new WorldPoint(1572, 3581, 0)), + SHAYZIEN_LIZARDMEN_EAST("Shayzien Task (5%) - Killing Lizardmen", new WorldPoint(1570, 3668, 0)), + SHAYZIEN_LIZARDMEN_WEST("Shayzien Task (5%) - Killing Lizardmen", new WorldPoint(1471, 3686, 0)), + SHAYZIEN_ORGANIZED_CRIME("Shayzien Task (40%) - Organized Crime", new WorldPoint(1565, 3605, 0)), + SHAYZIEN_COMBAT_RING("Shayzien Task (60%) - Combat Ring", new WorldPoint(1539, 3589, 0)), + SHAYZIEN_LIZARDMAN_SHAMANS("Shayzien Unlock (100%) - Lizardman Shamans", new WorldPoint(1455, 3693, 0)); + + private final String tooltip; + private final WorldPoint location; + + KourendTaskLocation(String tooltip, WorldPoint location) + { + this.tooltip = tooltip; + this.location = location; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/KourendTaskPoint.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/KourendTaskPoint.java new file mode 100644 index 0000000000..21170e0789 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/KourendTaskPoint.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020, Brooklyn + * 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.worldmap; + +import net.runelite.client.ui.overlay.worldmap.WorldMapPoint; + +public class KourendTaskPoint extends WorldMapPoint +{ + KourendTaskPoint(KourendTaskLocation data) + { + super(data.getLocation(), WorldMapPlugin.BLANK_ICON); + setTooltip(data.getTooltip()); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/MinigameLocation.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/MinigameLocation.java new file mode 100644 index 0000000000..a7145c8ee9 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/MinigameLocation.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2018, Magic fTail + * 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.worldmap; + +import lombok.Getter; +import net.runelite.api.coords.WorldPoint; + +@Getter +enum MinigameLocation +{ + BARBARIAN_ASSAULT("Barbarian Assault", new WorldPoint(2531, 3569, 0)), + BURGH_DE_ROTT_RAMBLE("Burgh de Rott Ramble", new WorldPoint(3434, 3487, 0)), + CASTLE_WARS("Castle Wars", new WorldPoint(2439, 3092, 0)), + CASTLE_WARS_PORTAL("Castle Wars Portal", new WorldPoint(3140, 3626, 0)), + DUEL_ARENA("Duel Arena", new WorldPoint(3313, 3238, 0)), + MAGE_ARENA("Mage Arena", new WorldPoint(3095, 3957, 0)), + NIGHTMARE_ZONE("Nightmare Zone", new WorldPoint(2606, 3115, 0)), + PEST_CONTROL_NOVICE("Pest Control Novice", new WorldPoint(2660, 2637, 0)), + PEST_CONTROL_INTERMEDIATE("Pest Control Intermediate", new WorldPoint(2638, 2641, 0)), + PEST_CONTROL_VETERAN("Pest Control Veteran", new WorldPoint(2632, 2648, 0)), + TEMPLE_TREKKING("Temple Trekking", new WorldPoint(3479, 3240, 0)), + TZHAAR_FIGHT_CAVE("TzHaar Fight Cave", new WorldPoint(2437, 5168, 0)), + TZHAAR_FIGHT_PIT("TzHaar Fight Pit", new WorldPoint(2398, 5177, 0)), + LAST_MAN_STANDING("Last Man Standing", new WorldPoint(3138, 3635, 0)), + INFERNO("Inferno", new WorldPoint(2495, 5118, 0)), + BRIMHAVEN_AGILITY_ARENA("Brimhaven Agility Arena", new WorldPoint(2809, 3191, 0)), + FISHING_TRAWLER("Fishing Trawler", new WorldPoint(2667, 3163, 0)), + GNOME_BALL("Gnome Ball", new WorldPoint(2381, 3488, 0)), + GNOME_RESTAURANT("Gnome Restaurant", new WorldPoint(2436, 3502, 0)), + IMPETUOUS_IMPULSES("Impetuous Impulses", new WorldPoint(2425, 4445, 0)), + MAGE_TRAINING_ARENA("Mage Training Arena", new WorldPoint(3362, 3318, 0)), + PYRAMID_PLUNDER("Pyramid Plunder", new WorldPoint(3288, 2787, 0)), + RANGING_GUILD("Ranging Guild", new WorldPoint(2671, 3419, 0)), + ROGUES_DEN("Rogues' Den", new WorldPoint(2905, 3537, 0)), + SORCERESSS_GARDEN("Sorceress's Garden", new WorldPoint(3285, 3180, 0)), + TROUBLE_BREWING("Trouble Brewing", new WorldPoint(3811, 3021, 0)), + VOLCANIC_MINE("Volcanic Mine", new WorldPoint(3812, 3810, 0)), + TAI_BWO_WANNAI_CLEANUP("Tai Bwo Wannai Cleanup", new WorldPoint(2795, 3066, 0)), + BURTHORPE_GAMES_ROOM("Burthorpe Games Room", new WorldPoint(2900, 3565, 0)), + RAT_PITS_PORT_SARIM("Rat Pits", new WorldPoint(3015, 3232, 0)), + RAT_PITS_VARROCK("Rat Pits", new WorldPoint(3266, 3400, 0)), + RAT_PITS_ARDOUGNE("Rat Pits", new WorldPoint(2561, 3318, 0)), + RAT_PITS_KELDAGRIM("Rat Pits", new WorldPoint(2913, 10188, 0)), + TEARS_OF_GUTHIX("Tears of Guthix", new WorldPoint(3257, 9517, 0)), + CLAN_WARS("Clan Wars", new WorldPoint(3133, 3621, 0)), + ANIMATION_ROOM("Animation Room", new WorldPoint(2853, 3537, 0)), + DUMMY_ROOM("Dummy Room", new WorldPoint(2857, 3551, 0)), + CATAPULT_ROOM("Catapult Room", new WorldPoint(2842, 3545, 0)), + SHOT_PUT_ROOM("Shot Put Room", new WorldPoint(2863, 3550, 0)), + HALLOWED_SEPULCHRE("Hallowed Sepulchre", new WorldPoint(3653, 3386, 1)), + THE_GAUNTLET("The Gauntlet", new WorldPoint(3223, 12505, 1)); + + private final String tooltip; + private final WorldPoint location; + + MinigameLocation(String tooltip, WorldPoint location) + { + this.tooltip = tooltip; + this.location = location; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/MinigamePoint.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/MinigamePoint.java new file mode 100644 index 0000000000..bd670aa023 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/MinigamePoint.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018, Magic fTail + * 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.worldmap; + +import java.awt.image.BufferedImage; +import net.runelite.client.ui.overlay.worldmap.WorldMapPoint; + +class MinigamePoint extends WorldMapPoint +{ + MinigamePoint(MinigameLocation data, BufferedImage icon) + { + super(data.getLocation(), icon); + setTooltip(data.getTooltip()); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/MiningSiteLocation.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/MiningSiteLocation.java new file mode 100644 index 0000000000..21358d1e0f --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/MiningSiteLocation.java @@ -0,0 +1,278 @@ +/* + * Copyright (c) 2020, dekvall + * 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.worldmap; + +import com.google.common.base.Joiner; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Value; +import net.runelite.api.coords.WorldPoint; + +@Getter +enum MiningSiteLocation +{ + AGILITY_PYRAMID(new WorldPoint(3322, 2875, 0), new Rock(5, Ore.GOLD)), + // ABANDONED_MINE -- NOT AVAILABLE ON WORLDMAP + AL_KHARID_MINE_NORTH(new WorldPoint(3298, 3312, 0), + new Rock(3, Ore.COPPER), new Rock(1, Ore.TIN), new Rock(7, Ore.IRON), new Rock(5, Ore.SILVER), + new Rock(3, Ore.COAL), new Rock(3, Ore.MITHRIL), new Rock(2, Ore.ADAMANTITE)), + AL_KHARID_MINE_SOUTH(new WorldPoint(3298, 3282, 0), new Rock(2, Ore.IRON), new Rock(2, Ore.GOLD)), + ARANDAR(new WorldPoint(2322, 3269, 0), new Rock(8, Ore.LIMESTONE)), + ARANDAR_PRIFDDINAS_MAP(new WorldPoint(3346, 6021, 0), new Rock(8, Ore.LIMESTONE)), + ARCEUUS_NORTH(new WorldPoint(1763, 3860, 0), new Rock(1, Ore.DENSE_ESSENCE)), + ARCEUUS_SOUTH(new WorldPoint(1763, 3844, 0), new Rock(1, Ore.DENSE_ESSENCE)), + ARDOUGNE_SEWERS(new WorldPoint(2670, 9680, 0), new Rock(5, Ore.IRON), new Rock(5, Ore.COAL)), + ARDOUGNE_SOUTH_EAST(new WorldPoint(2599, 3232, 0), new Rock(6, Ore.IRON), new Rock(4, Ore.COAL)), + // ARZINIAN_MINE -- NOT AVAILABLE ON WORLD MAP + ASGARNIA_ICE_DUNGEON_EAST(new WorldPoint(3063, 9582, 0), new Rock(2, Ore.BLURITE)), + ASGARNIA_ICE_DUNGEON_WEST(new WorldPoint(3049, 9568, 0), new Rock(2, Ore.BLURITE)), + BANDIT_CAMP_MINE(new WorldPoint(3086, 3763, 0), new Rock(16, Ore.IRON), new Rock(20, Ore.COAL), new Rock(22, Ore.MITHRIL), new Rock(8, Ore.ADAMANTITE)), + BANDIT_CAMP_QUARRY(new WorldPoint(3171, 2912, 0), new Rock(4, Ore.CLAY), new Rock(2, Ore.COAL), new Rock(32, Ore.SANDSTONE), new Rock(28, Ore.GRANITE)), + BARBARIAN_VILLAGE(new WorldPoint(3078, 3421, 0), new Rock(5, Ore.TIN), new Rock(4, Ore.COAL)), + BATTLEFIELD(new WorldPoint(2471, 3255, 0), new Rock(2, Ore.COPPER), new Rock(1, Ore.TIN)), + BLAST_MINE_EAST(new WorldPoint(1502, 3869, 0), new Rock(20, Ore.HARD_ROCK)), + BLAST_MINE_NORTH(new WorldPoint(1485, 3882, 0), new Rock(17, Ore.HARD_ROCK)), + BLAST_MINE_WEST(new WorldPoint(1471, 3865, 0), new Rock(22, Ore.HARD_ROCK)), + BRIMHAVEN_NORTH(new WorldPoint(2732, 3225, 0), new Rock(10, Ore.GOLD)), + BRIMHAVEN_SOUTH_(new WorldPoint(2743, 3150, 0), new Rock(6, Ore.GOLD)), + CENTRAL_FREMENIK_ISLES(new WorldPoint(2374, 3850, 0), new Rock(7, Ore.COAL), new Rock(1, Ore.RUNITE)), + CITHAREDE_ABBEY(new WorldPoint(3400, 3170, 0), new Rock(3, Ore.IRON), new Rock (3, Ore.COAL)), + COAL_TRUCKS(new WorldPoint(2580, 3484, 0), new Rock(18, Ore.COAL)), + CRAFTING_GUILD(new WorldPoint(2939, 3283, 0), new Rock(6, Ore.CLAY), new Rock(6, Ore.SILVER), new Rock(7, Ore.GOLD)), + CRANDOR_NORTH_EAST(new WorldPoint(2860, 3287, 0), new Rock(3, Ore.GOLD)), + CRANDOR_NORTH_WEST(new WorldPoint(2831, 3296, 0), new Rock(7, Ore.COAL), new Rock(1, Ore.MITHRIL)), + CRANDOR_SOUTH_EAST(new WorldPoint(2835, 3245, 0), new Rock(3, Ore.COAL), new Rock(3, Ore.ADAMANTITE)), + CRANDOR_SOUTH_WEST(new WorldPoint(2819, 3247, 0), new Rock(7, Ore.MITHRIL)), + DAEYALT_ESSENCE_MINE(new WorldPoint(3631, 3340, 0), new Rock(3, Ore.DAEYALT_ESSENCE)), + DESERT_MINING_CAMP_SURFACE(new WorldPoint(3299, 3021, 0), true, new Rock(4, Ore.COPPER), new Rock(4, Ore.TIN), new Rock(3, Ore.IRON), new Rock(4, Ore.COAL)), + // DESERT_MINING_CAMP_UNDERGROUND -- NOT AVAILABLE ON WORLDMAP + DORGESH_KAAN_NORTH(new WorldPoint(3309, 9645, 0), new Rock(1, Ore.IRON), new Rock(9, Ore.SILVER)), + DORGESH_KAAN_SOUTH_EAST(new WorldPoint(3322, 9616, 0), new Rock(3, Ore.IRON)), + DORGESH_KAAN_SOUTH_WEST(new WorldPoint(3312, 9621, 0), new Rock(3, Ore.IRON)), + DORGESH_KAAN_WEST(new WorldPoint(3311, 9628, 0), new Rock(3, Ore.IRON), new Rock(2, Ore.SILVER)), + DWARVEN_EAST_BOTTOM(new WorldPoint(3039, 9763, 0), + new Rock(5, Ore.TIN), new Rock(2, Ore.IRON), new Rock(8, Ore.COAL), new Rock(2, Ore.GOLD), new Rock(1, Ore.ADAMANTITE)), + DWARVEN_EAST_MIDDLE(new WorldPoint(3037, 9775, 0), + new Rock(4, Ore.COPPER), new Rock(3, Ore.IRON), new Rock(3, Ore.COAL), new Rock(2, Ore.MITHRIL), new Rock(2, Ore.ADAMANTITE)), + DWARVEN_EAST_TOP(new WorldPoint(3051, 9820, 0), new Rock(2, Ore.CLAY), new Rock(3, Ore.TIN), new Rock(2, Ore.IRON)), + DWARVEN_WEST_BOTTOM(new WorldPoint(3028, 9809, 0), new Rock(3, Ore.CLAY), new Rock(4, Ore.COPPER)), + DWARVEN_WEST_TOP(new WorldPoint(3031, 9828, 0), new Rock(3, Ore.COPPER), new Rock(2, Ore.TIN), new Rock(2, Ore.IRON)), + FREMENIK_ISLES_EAST(new WorldPoint(2405, 3867, 0), new Rock(3, Ore.COPPER), new Rock(3, Ore.TIN), new Rock(4, Ore.COAL)), + EAGLES_OUTPOST(new WorldPoint(3424, 3164, 0), new Rock(7, Ore.CLAY)), + EDGEVILLE_DUNGEON(new WorldPoint(3138, 9874, 0), + new Rock(2, Ore.COPPER), new Rock(2, Ore.TIN), new Rock(3, Ore.IRON), new Rock(3, Ore.SILVER), + new Rock(6, Ore.COAL), new Rock(1, Ore.MITHRIL), new Rock(2, Ore.ADAMANTITE)), + // EVIL_CHICKEN_LAIR -- NOT AVAILABLE ON WORLD MAP + FALADOR_WEST(new WorldPoint(2907, 3362, 0), + new Rock(2, Ore.COPPER), new Rock(6, Ore.TIN), new Rock(3, Ore.IRON), new Rock(2, Ore.COAL)), + FELDIP_HILLS_EAST(new WorldPoint(2638, 2996, 0), new Rock(3, Ore.ROCK)), + FELDIP_HILLS_MIDDLE(new WorldPoint(2579, 2998, 0), new Rock(4, Ore.ROCK)), + FELDIP_HILLS_WEST(new WorldPoint(2567, 2961, 0), new Rock(3, Ore.ROCK)), + FIGHT_ARENA(new WorldPoint(2630, 3142, 0), + new Rock(4, Ore.CLAY), new Rock(2, Ore.COPPER), new Rock(7, Ore.TIN), new Rock(9, Ore.IRON), + new Rock(1, Ore.COAL), new Rock(2, Ore.MITHRIL)), + FOSSIL_ISLAND(new WorldPoint(3770, 3815, 0), + new Rock(7, Ore.IRON), new Rock(20, Ore.COAL), new Rock(3, Ore.MITHRIL), new Rock(5, Ore.ADAMANTITE), + new Rock(2, Ore.RUNITE)), + FREMENNIK_ISLES_WEST(new WorldPoint(2310, 3853, 0), new Rock(3, Ore.COPPER)), + FROZEN_WASTE_PLATEU_CENTER(new WorldPoint(2963, 3933, 0), new Rock(1, Ore.RUNITE)), + FROZEN_WASTE_PLATEU_NORTH(new WorldPoint(2975, 3937, 0), new Rock(1, Ore.RUNITE)), + FROZEN_WASTE_PLATEU_SOUTH(new WorldPoint(2947, 3914, 0), new Rock(1, Ore.RUNITE)), + GRAND_TREE(new WorldPoint(2489, 9916, 0), + new Rock(9, Ore.CLAY), new Rock(8, Ore.IRON), new Rock(4, Ore.SILVER), new Rock(11, Ore.COAL), + new Rock(4, Ore.GOLD), new Rock(4, Ore.MITHRIL), new Rock(3, Ore.ADAMANTITE)), + GWENITH(new WorldPoint(2163, 3415, 0), new Rock(10, Ore.GOLD)), + GWENITH_PRIFDDINAS_MAP(new WorldPoint(3187, 6167, 0), new Rock(10, Ore.GOLD)), + HEROES_GUILD_EAST_BOTTOM(new WorldPoint(2939, 9898, 0), new Rock(3, Ore.COAL), new Rock(2, Ore.RUNITE)), + HEROES_GUILD_EAST_TOP(new WorldPoint(2940, 9884, 0), new Rock(5, Ore.COAL)), + HEROES_GUILD_WEST_BOTTOM(new WorldPoint(2921, 9904, 0), new Rock(3, Ore.COAL)), + HEROES_GUILD_WEST_TOP(new WorldPoint(2914, 9916, 0), new Rock(2, Ore.MITHRIL), new Rock(2, Ore.ADAMANTITE)), + HOSIDIUS_MINE(new WorldPoint(1777, 3489, 0), + new Rock(10, Ore.CLAY), new Rock(11, Ore.COPPER), new Rock(4, Ore.TIN), new Rock(9, Ore.IRON), + new Rock(2, Ore.SILVER)), + ISAFDAR(new WorldPoint(2277, 3159, 0), new Rock(4, Ore.ADAMANTITE), new Rock(2, Ore.RUNITE)), + JATIZSO(new WorldPoint(2396, 3812, 0), + new Rock(11, Ore.TIN), new Rock(7, Ore.IRON), new Rock(8, Ore.COAL), new Rock(15, Ore.MITHRIL), + new Rock(11, Ore.ADAMANTITE)), + KARAMJA_JUNGLE(new WorldPoint(2848, 3033, 0), + new Rock(1, Ore.IRON), new Rock(1, Ore.SILVER), new Rock(1, Ore.COAL), new Rock(2, Ore.MITHRIL), + new Rock(2, Ore.ADAMANTITE)), + KARAMJA_VOLCANO(new WorldPoint(2856, 9579, 0), new Rock(4, Ore.GOLD)), + KEBOS_LOWLANDS(new WorldPoint(1211, 3657, 0), new Rock(4, Ore.ADAMANTITE), new Rock(5, Ore.MITHRIL)), + KELDAGRIM_ENTRANCE(new WorldPoint(2724, 3693, 0), new Rock(9, Ore.IRON), new Rock(2, Ore.MITHRIL)), + KELDAGRIM_NORTH_EAST(new WorldPoint(2937, 10232, 0), new Rock(9, Ore.COAL)), + KELDAGRIM_SOUTH_WEST_BOTTOM(new WorldPoint(2872, 10119, 0), new Rock(2, Ore.COPPER), new Rock(5, Ore.COAL)), + KELDAGRIM_SOUTH_WEST_MIDDLE(new WorldPoint(2818, 10156, 0), new Rock(4, Ore.IRON), new Rock(1, Ore.GOLD)), + KELDAGRIM_SOUTH_WEST_TOP(new WorldPoint(2864, 10170, 0), new Rock(5, Ore.TIN)), + LAVA_MAZE_DUNGEON(new WorldPoint(3045, 10263, 0), true, new Rock(1, Ore.RUNITE)), + LAVA_MAZE_NORTH(new WorldPoint(3059, 3884, 0), new Rock(2, Ore.RUNITE)), + LEGENDS_GUILD_EAST(new WorldPoint(2709, 3331, 0), new Rock(6, Ore.IRON), new Rock(8, Ore.COAL)), + LEGENDS_GUILD_WEST(new WorldPoint(2694, 3332, 0), new Rock(5, Ore.IRON), new Rock(5, Ore.COAL)), + LOVAKENGJ_SOUTH(new WorldPoint(1476, 3779, 0), new Rock(4, Ore.IRON), new Rock(6, Ore.COAL), new Rock(1, Ore.MITHRIL)), + LOVAKENGJ_SULPHUR_EAST(new WorldPoint(1445, 3870, 0), new Rock(3, Ore.VOLCANIC_SULPHUR)), + LOVAKENGJ_SULPHUR_WEST(new WorldPoint(1427, 3870, 0), new Rock(2, Ore.VOLCANIC_SULPHUR)), + LOVAKENGJ_WEST(new WorldPoint(1432, 3845, 0), true, new Rock(45, Ore.COAL), new Rock(80, Ore.LOVAKITE)), + LUMBRIDGE_SWAMP_EAST(new WorldPoint(3226, 3146, 0), new Rock(5, Ore.COPPER), new Rock(5, Ore.TIN)), + LUMBRIDGE_SWAMP_WEST(new WorldPoint(3148, 3149, 0), + new Rock(7, Ore.COAL), new Rock(5, Ore.MITHRIL), new Rock(2, Ore.ADAMANTITE)), + // Lunar Isle Dungeon: Starting north-east and going clockwise + LUNAR_ISLE_1(new WorldPoint(2163, 10347, 0), + new Rock(2, Ore.SILVER), new Rock(1, Ore.GOLD), new Rock(1, Ore.GEM_ROCK), new Rock(6, Ore.LUNAR)), + LUNAR_ISLE_2(new WorldPoint(2165, 10325, 0), new Rock(3, Ore.GEM_ROCK), new Rock(4, Ore.LUNAR)), + LUNAR_ISLE_3(new WorldPoint(2140, 10318, 0), new Rock(3, Ore.SILVER), new Rock(3, Ore.LUNAR)), + LUNAR_ISLE_4(new WorldPoint(2125, 10327, 0), new Rock(4, Ore.GOLD), new Rock(1, Ore.LUNAR)), + LUNAR_ISLE_5(new WorldPoint(2124, 10342, 0), new Rock(2, Ore.GOLD), new Rock(5, Ore.LUNAR)), + MINING_GUILD_AMETHYST(new WorldPoint(3022, 9704, 0), new Rock(26, Ore.AMETHYST)), + MINING_GUILD_NORTH(new WorldPoint(3040, 9740, 0), + new Rock(4, Ore.IRON), new Rock(37, Ore.COAL), new Rock(5, Ore.MITHRIL), new Rock(2, Ore.ADAMANTITE)), + MINING_GUILD_SOUTH(new WorldPoint(3032, 9720, 0), + new Rock(8, Ore.IRON), new Rock(20, Ore.COAL), new Rock(10, Ore.MITHRIL), new Rock(8, Ore.ADAMANTITE), + new Rock(2, Ore.RUNITE)), + MISCELLANIA(new WorldPoint(2526, 3891, 0), new Rock(9, Ore.COAL)), + MISCELLANIA_DUNGEON(new WorldPoint(2504, 10287, 0), new Rock(4, Ore.COAL)), + MOR_UL_REK_NORTH(new WorldPoint(2458, 5167, 0), new Rock(3, Ore.SILVER), new Rock(3, Ore.GOLD)), + MOR_UL_REK_SOUTH_EAST(new WorldPoint(2513, 5074, 0), new Rock(6, Ore.SILVER), new Rock(7, Ore.GOLD)), + MOR_UL_REK_SOUTH_WEST(new WorldPoint(2499, 5062, 0), + new Rock(3, Ore.IRON), new Rock(4, Ore.COAL), new Rock(2, Ore.ADAMANTITE), new Rock(3, Ore.RUNITE)), + MOUNT_KARUULM(new WorldPoint(1278, 3814, 0), new Rock(6, Ore.IRON), new Rock(5, Ore.COAL)), + // MOURNER_TUNNELS -- NOT AVAILABLE ON WORLD MAP + MYTHS_GUILD(new WorldPoint(1936, 9020, 0), new Rock(4, Ore.ADAMANTITE), new Rock(2, Ore.RUNITE)), + OGRESS_SETTLEMENT(new WorldPoint(1977, 9041, 0), + new Rock(5, Ore.COAL), new Rock(2, Ore.MITHRIL), new Rock(1, Ore.ADAMANTITE)), + PIRATES_HIDEOUT(new WorldPoint(3056, 3945, 0), + new Rock(1, Ore.IRON), new Rock(4, Ore.COAL), new Rock(4, Ore.MITHRIL), new Rock(1, Ore.ADAMANTITE)), + PISCARILLIUS(new WorldPoint(1759, 3718, 0), + new Rock(5, Ore.COPPER), new Rock(6, Ore.TIN), new Rock(5, Ore.IRON), new Rock(2, Ore.SILVER), + new Rock(1, Ore.MITHRIL)), + PISCATORIS(new WorldPoint(2337, 3640, 0), + new Rock(2, Ore.CLAY), new Rock(2, Ore.COPPER), new Rock(2, Ore.TIN), new Rock(3, Ore.IRON)), + PORT_KHAZARD(new WorldPoint(2651, 3172, 0), + new Rock(2, Ore.COPPER), new Rock(2, Ore.TIN), new Rock(2, Ore.MITHRIL)), + RELLEKKA(new WorldPoint(2682, 3704, 0), new Rock(4, Ore.CLAY), new Rock(3, Ore.SILVER), new Rock(7, Ore.COAL)), + RIMMINGTON(new WorldPoint(2977, 3240, 0), + new Rock(2, Ore.CLAY), new Rock(5, Ore.COPPER), new Rock(2, Ore.TIN), new Rock(6, Ore.IRON), + new Rock(2, Ore.GOLD)), + SALT_MINE(new WorldPoint(2835, 10334, 0), + new Rock(7, Ore.BASALT), new Rock(15, Ore.TE_SALT), new Rock(12, Ore.EFH_SALT), new Rock(12, Ore.URT_SALT)), + SHAYZIEN_EAST(new WorldPoint(1597, 3653, 0), new Rock(3, Ore.CLAY), new Rock(1, Ore.MITHRIL), new Rock(1, Ore.ADAMANTITE)), + SHAYZIEN_WEST(new WorldPoint(1586, 3650, 0), + new Rock(4, Ore.IRON), new Rock(4, Ore.COAL), new Rock(1, Ore.MITHRIL), new Rock(1, Ore.ADAMANTITE)), + SHILO_VILLAGE_SURFACE(new WorldPoint(2822, 3001, 0), new Rock(7, Ore.GEM_ROCK)), + SILVAREA(new WorldPoint(3371, 3498, 0), new Rock(7, Ore.LIMESTONE)), + SLEPE_UNDERGROUND(new WorldPoint(3888, 9749, 0), new Rock(6, Ore.IRON), new Rock(14, Ore.COAL)), + TRAHEARN(new WorldPoint(3295, 12387, 0), + new Rock(26, Ore.IRON), new Rock(8, Ore.SILVER), new Rock(19, Ore.COAL), new Rock(14, Ore.GOLD), + new Rock(7, Ore.MITHRIL), new Rock(10, Ore.SOFT_CLAY), new Rock(7, Ore.ADAMANTITE), new Rock(4, Ore.RUNITE)), + // TUTORIAL ISLAND -- NOT AVAILABLE ON WORLD MAP + UZER(new WorldPoint(3415, 3160, 0), new Rock(10, Ore.CLAY)), + VARROCK_SOUTH_EAST(new WorldPoint(3286, 3365, 0), + new Rock(9, Ore.COPPER), new Rock(6, Ore.TIN), new Rock(4, Ore.IRON)), + VARROCK_SOUTH_WEST(new WorldPoint(3176, 3370, 0), + new Rock(3, Ore.CLAY), new Rock(8, Ore.TIN), new Rock(3, Ore.IRON), new Rock(3, Ore.SILVER)), + VERDANT_VALLEY(new WorldPoint(3766, 3757, 0), true, new Rock(3, Ore.IRON)), + WILDERNESS_RESOURCE_AREA(new WorldPoint(3192, 3930, 0), + new Rock(6, Ore.IRON), new Rock(11, Ore.COAL), new Rock(4, Ore.GOLD), new Rock(1, Ore.MITHRIL), new Rock(6, Ore.ADAMANTITE)), + WILDERNESS_SOUTH(new WorldPoint(3104, 3569, 0), new Rock(2, Ore.IRON), new Rock(3, Ore.COAL)), + WILDERNESS_SOUTH_WEST(new WorldPoint(3013, 3589, 0), new Rock(34, Ore.COAL)), + ; + + private final WorldPoint location; + private final String tooltip; + private final boolean iconRequired; + + MiningSiteLocation(WorldPoint location, Rock... rocks) + { + this(location, false, rocks); + } + + MiningSiteLocation(WorldPoint location, boolean iconRequired, Rock... rocks) + { + this.location = location; + this.iconRequired = iconRequired; + this.tooltip = createTooltip(rocks); + } + + private String createTooltip(Rock[] rocks) + { + return Joiner.on("
").join(rocks); + } + + @RequiredArgsConstructor + private enum Ore + { + ROCK("Rock"), + CLAY("Clay"), + COPPER("Copper"), + TIN("Tin"), + LIMESTONE("Limestone"), + BLURITE("Blurite"), + IRON("Iron"), + ELEMENTAL("Elemental"), + SILVER("Silver"), + COAL("Coal"), + SANDSTONE("Sandstone"), + DENSE_ESSENCE("Dense essence"), + DAEYALT_ESSENCE("Daeyalt essence"), + GOLD("Gold"), + GEM_ROCK("Gem rock"), + HARD_ROCK("Hard rock"), + VOLCANIC_SULPHUR("Volcanic sulphur"), + GRANITE("Granite"), + MITHRIL("Mithril"), + LUNAR("Lunar"), + LOVAKITE("Lovakite"), + ADAMANTITE("Adamantite"), + SOFT_CLAY("Soft clay"), + BASALT("Basalt"), + TE_SALT("Te salt"), + EFH_SALT("Efh salt"), + URT_SALT("Urt salt"), + RUNITE("Runite"), + AMETHYST("Amethyst"), + ; + + private final String name; + + @Override + public String toString() + { + return name; + } + } + + @Value + private static class Rock + { + private final int count; + private final Ore ore; + + @Override + public String toString() + { + return count + " " + ore; + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/MiningSitePoint.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/MiningSitePoint.java new file mode 100644 index 0000000000..3a09a1b298 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/MiningSitePoint.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2020, dekvall + * 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.worldmap; + +import java.awt.image.BufferedImage; +import net.runelite.client.ui.overlay.worldmap.WorldMapPoint; + +class MiningSitePoint extends WorldMapPoint +{ + MiningSitePoint(MiningSiteLocation point, BufferedImage icon) + { + super(point.getLocation(), icon); + setTooltip(point.getTooltip()); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/QuestStartLocation.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/QuestStartLocation.java new file mode 100644 index 0000000000..bf4c69b756 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/QuestStartLocation.java @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2018, John James Hamilton + * 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.worldmap; + +import lombok.Getter; +import net.runelite.api.coords.WorldPoint; +import net.runelite.api.Quest; + +// Some quests are in the same spot, but they are done in order. If multiple +// quests start in the same location, an array of quests is expected. +enum QuestStartLocation +{ + //Free Quests + COOKS_ASSISTANT_RFD(Quest.COOKS_ASSISTANT, new WorldPoint(3211, 3216, 0)), + THE_CORSAIR_CURSE(Quest.THE_CORSAIR_CURSE, new WorldPoint(3029, 3273, 0)), + DEMON_SLAYER(Quest.DEMON_SLAYER, new WorldPoint(3204, 3424, 0)), + DORICS_QUEST(Quest.DORICS_QUEST, new WorldPoint(2952, 3450, 0)), + DRAGON_SLAYER(Quest.DRAGON_SLAYER, new WorldPoint(3190, 3362, 0)), + ERNEST_THE_CHICKEN(Quest.ERNEST_THE_CHICKEN, new WorldPoint(3109, 3330, 0)), + GOBLIN_DIPLOMACY(Quest.GOBLIN_DIPLOMACY, new WorldPoint(2957, 3509, 0)), + IMP_CATCHER(Quest.IMP_CATCHER, new WorldPoint(3108, 3160, 0)), + THE_KNIGHTS_SWORD(Quest.THE_KNIGHTS_SWORD, new WorldPoint(2976, 3342, 0)), + MISTHALIN_MYSTERY(Quest.MISTHALIN_MYSTERY, new WorldPoint(3235, 3155, 0)), + PIRATES_TREASURE(Quest.PIRATES_TREASURE, new WorldPoint(3051, 3252, 0)), + PRINCE_ALI_RESCUE(Quest.PRINCE_ALI_RESCUE, new WorldPoint(3301, 3163, 0)), + THE_RESTLESS_GHOST(Quest.THE_RESTLESS_GHOST, new WorldPoint(3240, 3210, 0)), + RUNE_MYSTERIES(Quest.RUNE_MYSTERIES, new WorldPoint(3210, 3220, 0)), + SHEEP_SHEARER(Quest.SHEEP_SHEARER, new WorldPoint(3190, 3272, 0)), + SHIELD_OF_ARRAV_PHOENIX_GANG(Quest.SHIELD_OF_ARRAV, new WorldPoint(3208, 3495, 0)), + SHIELD_OF_ARRAV_BLACK_ARM_GANG(Quest.SHIELD_OF_ARRAV, new WorldPoint(3208, 3392, 0)), + VAMPYRE_SLAYER(Quest.VAMPYRE_SLAYER, new WorldPoint(3096, 3266, 0)), + WITCHS_POTION(Quest.WITCHS_POTION, new WorldPoint(2967, 3203, 0)), + X_MARKS_THE_SPOT(Quest.X_MARKS_THE_SPOT, new WorldPoint(3227, 3242, 0)), + + //Members' Quests + ANIMAL_MAGNETISM(Quest.ANIMAL_MAGNETISM, new WorldPoint(3094, 3360, 0)), + ANOTHER_SLICE_OF_HAM(Quest.ANOTHER_SLICE_OF_HAM, new WorldPoint(2799, 5428, 0)), + THE_ASCENT_OF_ARCEUUS(Quest.THE_ASCENT_OF_ARCEUUS, new WorldPoint(1699, 3742, 0)), + BETWEEN_A_ROCK(Quest.BETWEEN_A_ROCK, new WorldPoint(2823, 10168, 0)), + BIG_CHOMPY_BIRD_HUNTING(Quest.BIG_CHOMPY_BIRD_HUNTING, new WorldPoint(2629, 2981, 0)), + BIOHAZARD(Quest.BIOHAZARD, new WorldPoint(2591, 3335, 0)), + BONE_VOYAGE(Quest.BONE_VOYAGE, new WorldPoint(3259, 3450, 0)), + CABIN_FEVER(Quest.CABIN_FEVER, new WorldPoint(3674, 3496, 0)), + CLIENT_OF_KOUREND(Quest.CLIENT_OF_KOUREND, new WorldPoint(1823, 3690, 0)), + CLOCK_TOWER(Quest.CLOCK_TOWER, new WorldPoint(2568, 3249, 0)), + COLD_WAR(Quest.COLD_WAR, new WorldPoint(2593, 3265, 0)), + CONTACT(Quest.CONTACT, new WorldPoint(3280, 2770, 0)), + CREATURE_OF_FENKENSTRAIN(Quest.CREATURE_OF_FENKENSTRAIN, new WorldPoint(3487, 3485, 0)), + DARKNESS_OF_HALLOWVALE(Quest.DARKNESS_OF_HALLOWVALE, new WorldPoint(3494, 9628, 0)), + DEATH_PLATEAU_TROLL_STRONGHOLD(new Quest[]{Quest.DEATH_PLATEAU, Quest.TROLL_STRONGHOLD}, new WorldPoint(2895, 3528, 0)), + DEATH_TO_THE_DORGESHUUN(Quest.DEATH_TO_THE_DORGESHUUN, new WorldPoint(3316, 9613, 0)), + THE_DEPTHS_OF_DESPAIR(Quest.THE_DEPTHS_OF_DESPAIR, new WorldPoint(1781, 3570, 0)), + DESERT_TREASURE(Quest.DESERT_TREASURE, new WorldPoint(3177, 3043, 0)), + DEVIOUS_MINDS(Quest.DEVIOUS_MINDS, new WorldPoint(3405, 3492, 0)), + THE_DIG_SITE(Quest.THE_DIG_SITE, new WorldPoint(3363, 3337, 0)), + DRAGON_SLAYER_II(Quest.DRAGON_SLAYER_II, new WorldPoint(2456, 2868, 0)), + DREAM_MENTOR(Quest.DREAM_MENTOR, new WorldPoint(2144, 10346, 0)), + DRUIDIC_RITUAL(Quest.DRUIDIC_RITUAL, new WorldPoint(2916, 3484, 0)), + DWARF_CANNON(Quest.DWARF_CANNON, new WorldPoint(2566, 3461, 0)), + EADGARS_RUSE(Quest.EADGARS_RUSE, new WorldPoint(2896, 3426, 0)), + EAGLES_PEAK(Quest.EAGLES_PEAK, new WorldPoint(2605, 3264, 0)), + ELEMENTAL_WORKSHOP(new Quest[]{Quest.ELEMENTAL_WORKSHOP_I, Quest.ELEMENTAL_WORKSHOP_II}, new WorldPoint(2714, 3482, 0)), + ENAKHRAS_LAMENT(Quest.ENAKHRAS_LAMENT, new WorldPoint(3190, 2926, 0)), + ENLIGHTENED_JOURNEY(Quest.ENLIGHTENED_JOURNEY, new WorldPoint(2809, 3356, 0)), + THE_EYES_OF_GLOUPHRIE(Quest.THE_EYES_OF_GLOUPHRIE, new WorldPoint(2400, 3419, 0)), + FAIRYTALE(new Quest[]{Quest.FAIRYTALE_I__GROWING_PAINS, Quest.FAIRYTALE_II__CURE_A_QUEEN}, new WorldPoint(3077, 3258, 0)), + FAMILY_CREST(Quest.FAMILY_CREST, new WorldPoint(3278, 3404, 0)), + THE_FEUD(Quest.THE_FEUD, new WorldPoint(3301, 3211, 0)), + FIGHT_ARENA(Quest.FIGHT_ARENA, new WorldPoint(2565, 3199, 0)), + FISHING_CONTEST_1(Quest.FISHING_CONTEST, new WorldPoint(2875, 3483, 0)), + FISHING_CONTEST_2(Quest.FISHING_CONTEST, new WorldPoint(2820, 3487, 0)), + FORGETTABLE_TALE(Quest.FORGETTABLE_TALE, new WorldPoint(2826, 10215, 0)), + THE_FORSAKEN_TOWER(Quest.THE_FORSAKEN_TOWER, new WorldPoint(1482, 3748, 0)), + THE_FREMENNIK_ISLES(Quest.THE_FREMENNIK_ISLES, new WorldPoint(2645, 3711, 0)), + THE_FREMENNIK_TRIALS(Quest.THE_FREMENNIK_TRIALS, new WorldPoint(2657, 3669, 0)), + THE_FREMENNIK_EXILES(Quest.THE_FREMENNIK_EXILES, new WorldPoint(2658, 3669, 0)), + GARDEN_OF_TRANQUILLITY(Quest.GARDEN_OF_TRANQUILLITY, new WorldPoint(3227, 3477, 0)), + GERTRUDES_CAT_RATCATCHERS(Quest.GERTRUDES_CAT, new WorldPoint(3150, 3411, 0)), + GETTING_AHEAD(Quest.GETTING_AHEAD, new WorldPoint(1247, 3686, 0)), + GHOSTS_AHOY(Quest.GHOSTS_AHOY, new WorldPoint(3677, 3510, 0)), + THE_GIANT_DWARF(Quest.THE_GIANT_DWARF, new WorldPoint(2841, 10129, 0)), + THE_GOLEM(Quest.THE_GOLEM, new WorldPoint(3487, 3089, 0)), + THE_GRAND_TREE_MONKEY_MADNESS(new Quest[]{Quest.THE_GRAND_TREE, Quest.MONKEY_MADNESS_I, Quest.MONKEY_MADNESS_II}, new WorldPoint(2466, 3497, 0)), + THE_GREAT_BRAIN_ROBBERY(Quest.THE_GREAT_BRAIN_ROBBERY, new WorldPoint(3681, 2963, 0)), + GRIM_TALES(Quest.GRIM_TALES, new WorldPoint(2890, 3454, 0)), + THE_HAND_IN_THE_SAND(Quest.THE_HAND_IN_THE_SAND, new WorldPoint(2552, 3101, 0)), + HAUNTED_MINE(Quest.HAUNTED_MINE, new WorldPoint(3443, 3258, 0)), + HAZEEL_CULT(Quest.HAZEEL_CULT, new WorldPoint(2565, 3271, 0)), + HEROES_QUEST(Quest.HEROES_QUEST, new WorldPoint(2903, 3511, 0)), + HOLY_GRAIL(new Quest[]{Quest.MERLINS_CRYSTAL, Quest.HOLY_GRAIL}, new WorldPoint(2763, 3515, 0)), + HORROR_FROM_THE_DEEP(Quest.HORROR_FROM_THE_DEEP, new WorldPoint(2507, 3635, 0)), + ICTHLARINS_LITTLE_HELPER(Quest.ICTHLARINS_LITTLE_HELPER, new WorldPoint(3314, 2849, 0)), + IN_SEARCH_OF_THE_MYREQUE(Quest.IN_SEARCH_OF_THE_MYREQUE, new WorldPoint(3502, 3477, 0)), + JUNGLE_POTION(Quest.JUNGLE_POTION, new WorldPoint(2809, 3086, 0)), + KINGS_RANSOM(Quest.KINGS_RANSOM, new WorldPoint(2741, 3554, 0)), + LEGENDS_QUEST(Quest.LEGENDS_QUEST, new WorldPoint(2725, 3367, 0)), + LOST_CITY(Quest.LOST_CITY, new WorldPoint(3149, 3205, 0)), + THE_LOST_TRIBE(Quest.THE_LOST_TRIBE, new WorldPoint(3211, 3224, 0)), + LUNAR_DIPLOMACY(Quest.LUNAR_DIPLOMACY, new WorldPoint(2618, 3691, 0)), + MAKING_FRIENDS_WITH_MY_ARM(Quest.MAKING_FRIENDS_WITH_MY_ARM, new WorldPoint(2904, 10092, 0)), + MAKING_HISTORY(Quest.MAKING_HISTORY, new WorldPoint(2435, 3346, 0)), + MONKS_FRIEND(Quest.MONKS_FRIEND, new WorldPoint(2605, 3209, 0)), + MOUNTAIN_DAUGHTER(Quest.MOUNTAIN_DAUGHTER, new WorldPoint(2810, 3672, 0)), + MOURNINGS_ENDS_PART_I(Quest.MOURNINGS_END_PART_I, new WorldPoint(2289, 3149, 0)), + MOURNINGS_ENDS_PART_II(Quest.MOURNINGS_END_PART_II, new WorldPoint(2352, 3172, 0)), + MURDER_MYSTERY(Quest.MURDER_MYSTERY, new WorldPoint(2740, 3562, 0)), + MY_ARMS_BIG_ADVENTURE(Quest.MY_ARMS_BIG_ADVENTURE, new WorldPoint(2908, 10088, 0)), + NATURE_SPIRIT(Quest.NATURE_SPIRIT, new WorldPoint(3440, 9894, 0)), + OBSERVATORY_QUEST(Quest.OBSERVATORY_QUEST, new WorldPoint(2438, 3185, 0)), + OLAFS_QUEST(Quest.OLAFS_QUEST, new WorldPoint(2723, 3729, 0)), + ONE_SMALL_FAVOUR(Quest.ONE_SMALL_FAVOUR, new WorldPoint(2834, 2985, 0)), + PLAGUE_CITY_SONG_OF_THE_ELVES(new Quest[]{Quest.PLAGUE_CITY, Quest.SONG_OF_THE_ELVES}, new WorldPoint(2567, 3334, 0)), + A_PORCINE_OF_INTEREST(Quest.A_PORCINE_OF_INTEREST, new WorldPoint(3085, 3251, 0)), + PRIEST_IN_PERIL(Quest.PRIEST_IN_PERIL, new WorldPoint(3219, 3473, 0)), + THE_QUEEN_OF_THIEVES(Quest.THE_QUEEN_OF_THIEVES, new WorldPoint(1795, 3782, 0)), + RAG_AND_BONE_MAN(new Quest[]{Quest.RAG_AND_BONE_MAN, Quest.RAG_AND_BONE_MAN_II}, new WorldPoint(3359, 3504, 0)), + RECRUITMENT_DRIVE_BLACK_KNIGHTS_FORTRESS(new Quest[]{Quest.BLACK_KNIGHTS_FORTRESS, Quest.RECRUITMENT_DRIVE}, new WorldPoint(2959, 3336, 0)), + ROVING_ELVES(Quest.ROVING_ELVES, new WorldPoint(2288, 3146, 0)), + RUM_DEAL(Quest.RUM_DEAL, new WorldPoint(3679, 3535, 0)), + SCORPION_CATCHER(Quest.SCORPION_CATCHER, new WorldPoint(2701, 3399, 0)), + SEA_SLUG(Quest.SEA_SLUG, new WorldPoint(2715, 3302, 0)), + SHADES_OF_MORTTON(Quest.SHADES_OF_MORTTON, new WorldPoint(3463, 3308, 0)), + SHADOW_OF_THE_STORM(Quest.SHADOW_OF_THE_STORM, new WorldPoint(3270, 3159, 0)), + SHEEP_HERDER(Quest.SHEEP_HERDER, new WorldPoint(2616, 3299, 0)), + SHILO_VILLAGE(Quest.SHILO_VILLAGE, new WorldPoint(2882, 2951, 0)), + SINS_OF_THE_FATHER(Quest.SINS_OF_THE_FATHER, new WorldPoint(3728, 3319, 0)), + A_SOULS_BANE(Quest.A_SOULS_BANE, new WorldPoint(3307, 3454, 0)), + SPIRITS_OF_THE_ELID(Quest.SPIRITS_OF_THE_ELID, new WorldPoint(3441, 2911, 0)), + SWAN_SONG(Quest.SWAN_SONG, new WorldPoint(2345, 3652, 0)), + TAI_BWO_WANNAI_TRIO(Quest.TAI_BWO_WANNAI_TRIO, new WorldPoint(2779, 3087, 0)), + A_TAIL_OF_TWO_CATS(Quest.A_TAIL_OF_TWO_CATS, new WorldPoint(2917, 3557, 0)), + TALE_OF_THE_RIGHTEOUS(Quest.TALE_OF_THE_RIGHTEOUS, new WorldPoint(1511, 3631, 0)), + A_TASTE_OF_HOPE(Quest.A_TASTE_OF_HOPE, new WorldPoint(3668, 3216, 0)), + TEARS_OF_GUTHIX(Quest.TEARS_OF_GUTHIX, new WorldPoint(3251, 9517, 0)), + TEMPLE_OF_IKOV(Quest.TEMPLE_OF_IKOV, new WorldPoint(2574, 3320, 0)), + THRONE_OF_MISCELLANIA_ROYAL_TROUBLE(new Quest[]{Quest.THRONE_OF_MISCELLANIA, Quest.ROYAL_TROUBLE}, new WorldPoint(2497, 3859, 0)), + THE_TOURIST_TRAP(Quest.THE_TOURIST_TRAP, new WorldPoint(3302, 3113, 0)), + TOWER_OF_LIFE(Quest.TOWER_OF_LIFE, new WorldPoint(2640, 3218, 0)), + TREE_GNOME_VILLAGE(Quest.TREE_GNOME_VILLAGE, new WorldPoint(2541, 3169, 0)), + TRIBAL_TOTEM(Quest.TRIBAL_TOTEM, new WorldPoint(2790, 3182, 0)), + TROLL_ROMANCE(Quest.TROLL_ROMANCE, new WorldPoint(2890, 10097, 0)), + UNDERGROUND_PASS_REGICIDE(new Quest[]{Quest.REGICIDE, Quest.UNDERGROUND_PASS}, new WorldPoint(2575, 3293, 0)), + WANTED_SLUG_MENACE(new Quest[]{Quest.WANTED, Quest.THE_SLUG_MENACE}, new WorldPoint(2996, 3373, 0)), + WATCHTOWER(Quest.WATCHTOWER, new WorldPoint(2545, 3112, 0)), + WATERFALL_QUEST(Quest.WATERFALL_QUEST, new WorldPoint(2521, 3498, 0)), + WHAT_LIES_BELOW(Quest.WHAT_LIES_BELOW, new WorldPoint(3265, 3333, 0)), + WITCHS_HOUSE(Quest.WITCHS_HOUSE, new WorldPoint(2927, 3456, 0)), + ZOGRE_FLESH_EATERS(Quest.ZOGRE_FLESH_EATERS, new WorldPoint(2442, 3051, 0)); + + @Getter + private final WorldPoint location; + + @Getter + private final Quest[] quests; + + QuestStartLocation(Quest[] quests, WorldPoint location) + { + this.location = location; + this.quests = quests; + } + + QuestStartLocation(Quest quest, WorldPoint location) + { + this.location = location; + this.quests = new Quest[]{quest}; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/QuestStartPoint.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/QuestStartPoint.java new file mode 100644 index 0000000000..681015b98e --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/QuestStartPoint.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2018, John James Hamilton + * 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.worldmap; + +import net.runelite.api.coords.WorldPoint; +import net.runelite.client.ui.overlay.worldmap.WorldMapPoint; +import java.awt.image.BufferedImage; + +class QuestStartPoint extends WorldMapPoint +{ + QuestStartPoint(WorldPoint location, BufferedImage icon, String tooltip) + { + super(location, icon); + setTooltip(tooltip); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/RareTreeLocation.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/RareTreeLocation.java new file mode 100644 index 0000000000..addcda5af7 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/RareTreeLocation.java @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2018, Spedwards + * 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.worldmap; + +import lombok.Getter; +import net.runelite.api.coords.WorldPoint; + +@Getter +enum RareTreeLocation +{ + WILLOW("Willow tree", 30, + // Kandarin + new WorldPoint(2710, 3510, 0)), + + TEAK("Teak tree", 35, + // Ape Atoll + new WorldPoint(2774, 2697, 0), + + // Desert + new WorldPoint(3510, 3073, 0), + + // Mos Le'Harmless + new WorldPoint(3832, 3067, 0), + + // Karamja + new WorldPoint(2837, 2907, 0), + new WorldPoint(2899, 2897, 0), + + // Feldip Hills + new WorldPoint(2333, 3049, 0), + + // Prifddinas + new WorldPoint(3309, 6123, 0)), + + SWAYING("Swaying tree", 40, + // The Fremennik Trials + new WorldPoint(2738, 3639, 0)), + + MATURE_JUNIPER("Mature juniper tree", 42, + // Zeah + new WorldPoint(1690, 3524, 0)), + + MAPLE("Maple tree", 45, + // Zeah + new WorldPoint(1231, 3704, 0), + new WorldPoint(1293, 3756, 0), + new WorldPoint(1640, 3496, 0), + new WorldPoint(1613, 3494, 0), + new WorldPoint(1560, 3636, 0), + new WorldPoint(1646, 3590, 0), + + // Miscellania + new WorldPoint(2550, 3869, 0), + + // Kandarin + new WorldPoint(2712, 3382, 0), + new WorldPoint(2720, 3465, 0), + new WorldPoint(2726, 3501, 0), + new WorldPoint(2728, 3481, 0), + new WorldPoint(2748, 3466, 0), + new WorldPoint(2710, 3570, 0), + + // Prifddinas + new WorldPoint(2209, 3427, 0), + new WorldPoint(3233, 6179, 0)), + + MAHOGANY("Mahogany tree", 50, + // Zeah + new WorldPoint(1237, 3770, 0), + + // Ape Atoll + new WorldPoint(2716, 2710, 0), + new WorldPoint(2725, 2735, 0), + + // Mos Le'Harmless + new WorldPoint(3824, 3053, 0), + + // Karamja + new WorldPoint(2946, 2908, 0), + + // Prifddinas + new WorldPoint(3301, 6129, 0)), + + TEAK_MAHOGANY("Teak/Mahogany trees", 50, + // Miscellania + new WorldPoint(2602, 3895, 0), + + // Mos Le'Harmless + new WorldPoint(3810, 3058, 0), + + // Karamja + new WorldPoint(2821, 3084, 0)), + + YEW("Yew tree", 60, + // Zeah + new WorldPoint(1217, 3688, 0), + new WorldPoint(1353, 3731, 0), + new WorldPoint(1529, 3452, 0), + new WorldPoint(1591, 3421, 0), + new WorldPoint(1647, 3508, 0), + new WorldPoint(1621, 3512, 0), + new WorldPoint(1593, 3491, 0), + new WorldPoint(1583, 3499, 0), + new WorldPoint(1696, 3554, 0), + new WorldPoint(1625, 3677, 0), + new WorldPoint(1625, 3669, 0), + new WorldPoint(1642, 3683, 0), + new WorldPoint(1642, 3663, 0), + new WorldPoint(1642, 3533, 0), + new WorldPoint(1671, 3657, 0), + new WorldPoint(1680, 3657, 0), + + // Tirannwn + new WorldPoint(2217, 3141, 0), + + // Prifddinas + new WorldPoint(3288, 6066, 0), + new WorldPoint(3305, 6032, 0), + + // Kandarin + new WorldPoint(2315, 3610, 0), + new WorldPoint(2331, 3514, 0), + new WorldPoint(2358, 3510, 0), + new WorldPoint(2360, 3471, 0), + new WorldPoint(2434, 3432, 0), + new WorldPoint(2493, 3396, 0), + new WorldPoint(2495, 3490, 0), + new WorldPoint(2733, 3334, 0), + new WorldPoint(2757, 3431, 0), + new WorldPoint(2714, 3460, 0), + + // Asgarnia + new WorldPoint(2995, 3312, 0), + new WorldPoint(3018, 3316, 0), + new WorldPoint(3041, 3320, 0), + new WorldPoint(3052, 3272, 0), + new WorldPoint(2931, 3231, 0), + + // Misthalin + new WorldPoint(3085, 3481, 0), + new WorldPoint(3085, 3469, 0), + new WorldPoint(3146, 3255, 0), + new WorldPoint(3151, 3231, 0), + new WorldPoint(3165, 3220, 0), + new WorldPoint(3184, 3227, 0), + new WorldPoint(3251, 3364, 0), + new WorldPoint(3204, 3504, 0), + new WorldPoint(3208, 3500, 0), + new WorldPoint(3221, 3503, 0), + new WorldPoint(3248, 3473, 0), + new WorldPoint(3266, 3494, 0), + new WorldPoint(3270, 3471, 0), + new WorldPoint(3304, 3470, 0), + new WorldPoint(3249, 3202, 0), + + // Morytania + new WorldPoint(3674, 3447, 0), + new WorldPoint(3684, 3385, 0), + + // Zanaris + new WorldPoint(2412, 4464, 0), + new WorldPoint(2465, 4427, 0), + new WorldPoint(2491, 4426, 0)), + + MAPLE_YEW("Maple/Yew trees", 60, + // Feldip Hills + new WorldPoint(2476, 2893, 0)), + + SULLIUSCEP("Sulliuscep", 65, + // Fossil Island + new WorldPoint(3662, 3802, 0), + new WorldPoint(3662, 3781, 0), + new WorldPoint(3677, 3806, 0), + new WorldPoint(3677, 3733, 0), + new WorldPoint(3682, 3775, 0), + new WorldPoint(3682, 3758, 0)), + + MAGIC("Magic tree", 75, + // Zeah + new WorldPoint(1373, 3821, 0), + new WorldPoint(1389, 3821, 0), + new WorldPoint(1610, 3443, 0), + new WorldPoint(1578, 3488, 0), + new WorldPoint(1685, 3740, 0), + new WorldPoint(1681, 3689, 0), + new WorldPoint(1751, 3564, 0), + new WorldPoint(1796, 3600, 0), + + // Misthalin + new WorldPoint(3355, 3312, 0), + new WorldPoint(3368, 3312, 0), + + // Tirannwn + new WorldPoint(2284, 3141, 0), + + // Prifddinas + new WorldPoint(3229, 6101, 0), + + // Kandarin + new WorldPoint(2371, 3427, 0), + new WorldPoint(2432, 3411, 0), + new WorldPoint(2490, 3414, 0), + new WorldPoint(2704, 3397, 0), + new WorldPoint(2695, 3423, 0), + + // Feldip Hills + new WorldPoint(2443, 2845, 0), + + // Wilderness + new WorldPoint(3175, 3931, 0)), + + REDWOOD("Redwood tree", 90, + // Zeah + new WorldPoint(1569, 3493, 0), + new WorldPoint(1569, 3483, 0)); + + private final String tooltip; + private final WorldPoint[] locations; + private final int levelReq; + + RareTreeLocation(String description, int level, WorldPoint... locations) + { + this.tooltip = description + " - Level " + level; + this.locations = locations; + this.levelReq = level; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/RareTreePoint.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/RareTreePoint.java new file mode 100644 index 0000000000..0dd42013bd --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/RareTreePoint.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2018, Spedwards + * 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.worldmap; + +import net.runelite.api.coords.WorldPoint; +import net.runelite.client.ui.overlay.worldmap.WorldMapPoint; + +import java.awt.image.BufferedImage; + +class RareTreePoint extends WorldMapPoint +{ + RareTreePoint(WorldPoint point, String tooltip, BufferedImage icon, boolean showTooltip) + { + super(point, icon); + + if (showTooltip) + { + setTooltip(tooltip); + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/RunecraftingAltarLocation.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/RunecraftingAltarLocation.java new file mode 100644 index 0000000000..c2f8319635 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/RunecraftingAltarLocation.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2019, Dava96 + * 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.worldmap; + +import lombok.Getter; +import net.runelite.api.coords.WorldPoint; + +@Getter +enum RunecraftingAltarLocation +{ + AIR_ALTAR("Air Altar", 1, new WorldPoint(2985, 3293, 0), "air_altar_icon.png"), + MIND_ALTAR("Mind Altar", 2, new WorldPoint(2982, 3514, 0), "mind_altar_icon.png"), + WATER_ALTAR("Water Altar", 5, new WorldPoint(3185, 3165, 0), "water_altar_icon.png"), + EARTH_ALTAR("Earth Altar", 9, new WorldPoint(3306, 3474, 0), "earth_altar_icon.png"), + FIRE_ALTAR("Fire Altar", 14, new WorldPoint(3313, 3255, 0), "fire_altar_icon.png"), + BODY_ALTAR("Body Altar", 20, new WorldPoint(3053, 3445, 0), "body_altar_icon.png"), + COSMIC_ALTAR("Cosmic Altar", 27, new WorldPoint(2408, 4377, 0), "cosmic_altar_icon.png"), + CHAOS_ALTAR("Chaos Altar", 35, new WorldPoint(3060, 3591, 0), "chaos_altar_icon.png"), + ASTRAL_ALTAR("Astral Altar", 40, new WorldPoint(2158, 3864, 0), "astral_altar_icon.png"), + NATURE_ALTAR("Nature Altar", 44, new WorldPoint(2869, 3019, 0), "nature_altar_icon.png"), + LAW_ALTAR("Law Altar", 54, new WorldPoint(2858, 3381, 0), "law_altar_icon.png"), + DEATH_ALTAR("Death Altar", 65, new WorldPoint(1860, 4639, 0), "death_altar_icon.png"), + BLOOD_ALTAR("Blood Altar", 77, new WorldPoint(1716, 3827, 0), "blood_altar_icon.png"), + SOUL_ALTAR("Soul Altar", 90, new WorldPoint(1814, 3856, 0), "soul_altar_icon.png"), + WRATH_ALTAR("Wrath Altar", 95, new WorldPoint(2446, 2825, 0), "wrath_altar_icon.png"); + + private final String tooltip; + private final WorldPoint location; + private final int levelReq; + private final String iconPath; + + RunecraftingAltarLocation(String description, int level, WorldPoint location, String iconPath) + { + this.tooltip = description + " - Level " + level; + this.location = location; + this.levelReq = level; + this.iconPath = iconPath; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/RunecraftingAltarPoint.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/RunecraftingAltarPoint.java new file mode 100644 index 0000000000..11cdad457f --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/RunecraftingAltarPoint.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2019, Dava96 + * 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.worldmap; + +import net.runelite.client.ui.overlay.worldmap.WorldMapPoint; +import net.runelite.client.util.ImageUtil; + +class RunecraftingAltarPoint extends WorldMapPoint +{ + RunecraftingAltarPoint(RunecraftingAltarLocation point) + { + super(point.getLocation(), WorldMapPlugin.BLANK_ICON); + setImage(ImageUtil.getResourceStreamFromClass(WorldMapPlugin.class, point.getIconPath())); + setTooltip(point.getTooltip()); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/TeleportLocationData.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/TeleportLocationData.java new file mode 100644 index 0000000000..d2753e8010 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/TeleportLocationData.java @@ -0,0 +1,220 @@ +/* + * 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 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.worldmap; + +import lombok.Getter; +import net.runelite.api.coords.WorldPoint; + +@Getter +enum TeleportLocationData +{ + VARROCK(TeleportType.NORMAL_MAGIC, "Varrock", 25, new WorldPoint(3213, 3424, 0), "varrock_teleport_icon.png"), + VARROCK_GE(TeleportType.NORMAL_MAGIC, "Varrock GE", 25, new WorldPoint(3164, 3478, 0), "varrock_teleport_icon.png"), + LUMBRIDGE(TeleportType.NORMAL_MAGIC, "Lumbridge", 31, new WorldPoint(3222, 3218, 0), "lumbridge_teleport_icon.png"), + FALADOR(TeleportType.NORMAL_MAGIC, "Falador", 37, new WorldPoint(2965, 3381, 0), "falador_teleport_icon.png"), + CAMELOT(TeleportType.NORMAL_MAGIC, "Camelot", 45, new WorldPoint(2757, 3477, 0), "camelot_teleport_icon.png"), + CAMELOT_BANK(TeleportType.NORMAL_MAGIC, "Camelot Bank", 45, new WorldPoint(2726, 3485, 0), "camelot_teleport_icon.png"), + ARDOUGNE(TeleportType.NORMAL_MAGIC, "Ardougne", 51, new WorldPoint(2664, 3306, 0), "ardougne_teleport_icon.png"), + WATCHTOWER(TeleportType.NORMAL_MAGIC, "Watchtower", 58, new WorldPoint(2547, 3114, 0), "watchtower_teleport_icon.png"), + WATCHTOWER_YANILLE(TeleportType.NORMAL_MAGIC, "Watchtower Yanille", 58, new WorldPoint(2584, 3097, 0), "watchtower_teleport_icon.png"), + TROLLHEIM(TeleportType.NORMAL_MAGIC, "Trollheim", 61, new WorldPoint(2891, 3678, 0), "trollheim_teleport_icon.png"), + APE_ATOLL(TeleportType.NORMAL_MAGIC, "Ape Atoll", 64, new WorldPoint(2796, 2791, 0), "ape_atoll_teleport_icon.png"), + KOUREND(TeleportType.NORMAL_MAGIC, "Kourend", 69, new WorldPoint(1643, 3672, 0), "kourend_teleport_icon.png"), + PADDEWWA(TeleportType.ANCIENT_MAGICKS, "Paddewwa", 54, new WorldPoint(3097, 9880, 0), "paddewwa_teleport_icon.png"), + SENNTISTEN(TeleportType.ANCIENT_MAGICKS, "Senntisten", 60, new WorldPoint(3319, 3336, 0), "senntisten_teleport_icon.png"), + KHARYRLL(TeleportType.ANCIENT_MAGICKS, "Kharyrll", 66, new WorldPoint(3494, 3473, 0), "kharyrll_teleport_icon.png"), + LASSAR(TeleportType.ANCIENT_MAGICKS, "Lassar", 72, new WorldPoint(3002, 3472, 0), "lassar_teleport_icon.png"), + DAREEYAK(TeleportType.ANCIENT_MAGICKS, "Dareeyak", 78, new WorldPoint(2969, 3695, 0), "dareeyak_teleport_icon.png"), + CARRALLANGAR(TeleportType.ANCIENT_MAGICKS, "Carrallangar", 84, new WorldPoint(3157, 3667, 0), "carrallangar_teleport_icon.png"), + ANNAKARL(TeleportType.ANCIENT_MAGICKS, "Annakarl", 90, new WorldPoint(3288, 3888, 0), "annakarl_teleport_icon.png"), + GHORROCK(TeleportType.ANCIENT_MAGICKS, "Ghorrock", 96, new WorldPoint(2977, 3872, 0), "ghorrock_teleport_icon.png"), + MOONCLAN(TeleportType.LUNAR_MAGIC, "Moonclan", 69, new WorldPoint(2113, 3915, 0), "moonclan_teleport_icon.png"), + OURANIA(TeleportType.LUNAR_MAGIC, "Ourania", 71, new WorldPoint(2468, 3246, 0), "ourania_teleport_icon.png"), + WATERBIRTH(TeleportType.LUNAR_MAGIC, "Waterbirth", 72, new WorldPoint(2546, 3755, 0), "waterbirth_teleport_icon.png"), + BARBARIAN(TeleportType.LUNAR_MAGIC, "Barbarian", 75, new WorldPoint(2543, 3568, 0), "barbarian_teleport_icon.png"), + KHAZARD(TeleportType.LUNAR_MAGIC, "Khazard", 78, new WorldPoint(2636, 3167, 0), "khazard_teleport_icon.png"), + FISHING_GUILD(TeleportType.LUNAR_MAGIC, "Fishing Guild", 85, new WorldPoint(2612, 3391, 0), "fishing_guild_teleport_icon.png"), + CATHERBY(TeleportType.LUNAR_MAGIC, "Catherby", 87, new WorldPoint(2802, 3449, 0), "catherby_teleport_icon.png"), + ICE_PLATEAU(TeleportType.LUNAR_MAGIC, "Ice Plateau", 89, new WorldPoint(2973, 3939, 0), "ice_plateau_teleport_icon.png"), + LUMBRIDGE_GRAVEYARD(TeleportType.ARCEUUS_MAGIC, "Lumbridge Graveyard", 6, new WorldPoint(3241, 3194, 0), "lumbridge_graveyard_teleport_icon.png"), + DRAYNOR_MANOR(TeleportType.ARCEUUS_MAGIC, "Draynor Manor", 17, new WorldPoint(3108, 3352, 0), "draynor_manor_teleport_icon.png"), + BATTLEFRONT(TeleportType.ARCEUUS_MAGIC, "Battlefront", 23, new WorldPoint(1349, 3739, 0), "battlefront_teleport_icon.png"), + MIND_ALTAR(TeleportType.ARCEUUS_MAGIC, "Mind Altar", 28, new WorldPoint(2979, 3509, 0), "mind_altar_teleport_icon.png"), + SALVE_GRAVEYARD(TeleportType.ARCEUUS_MAGIC, "Salve Graveyard", 40, new WorldPoint(3433, 3461, 0), "salve_graveyard_teleport_icon.png"), + FENKENSTRAINS_CASTLE(TeleportType.ARCEUUS_MAGIC, "Fenkenstrain's Castle", 48, new WorldPoint(3548, 3528, 0), "fenkenstrains_castle_teleport_icon.png"), + WEST_ARDOUGNE(TeleportType.ARCEUUS_MAGIC, "West Ardougne", 61, new WorldPoint(2500, 3291, 0), "west_ardougne_teleport_icon.png"), + HARMONY_ISLAND(TeleportType.ARCEUUS_MAGIC, "Harmony Island", 65, new WorldPoint(3797, 2866, 0), "harmony_island_teleport_icon.png"), + CEMETERY(TeleportType.ARCEUUS_MAGIC, "Cemetery", 71, new WorldPoint(2978, 3763, 0), "cemetery_teleport_icon.png"), + BARROWS(TeleportType.ARCEUUS_MAGIC, "Barrows", 83, new WorldPoint(3565, 3315, 0), "barrows_teleport_icon.png"), + APE_ATOLL_ARCEUUS(TeleportType.ARCEUUS_MAGIC, "Ape Atoll", 90, new WorldPoint(2770, 2703, 0), "ape_atoll_teleport_icon_arceuus.png"), + + // Jewellery + BARBARIAN_ASSAULT(TeleportType.JEWELLERY, "Games Necklace" , "Barbarian Assault", new WorldPoint(2520, 3571, 0), "games_necklace_teleport_icon.png"), + BURTHORPE_GAMES_ROOM(TeleportType.JEWELLERY, "Games Necklace" , "Burthorpe Games Room", new WorldPoint(2898, 3554, 0), "games_necklace_teleport_icon.png"), + TEARS_OF_GUTHIX(TeleportType.JEWELLERY, "Games Necklace" , "Tears of Guthix", new WorldPoint(3245, 9500, 0), "games_necklace_teleport_icon.png"), + CORPOREAL_BEAST(TeleportType.JEWELLERY, "Games Necklace" , "Corporeal Beast", new WorldPoint(2967, 4384, 0), "games_necklace_teleport_icon.png"), + WINTERTODT_CAMP(TeleportType.JEWELLERY, "Games Necklace" , "Wintertodt Camp", new WorldPoint(1624, 3938, 0), "games_necklace_teleport_icon.png"), + DUEL_ARENA(TeleportType.JEWELLERY, "Ring of Dueling" , "Duel Arena", new WorldPoint(3315, 3235, 0), "ring_of_dueling_teleport_icon.png"), + FEROX_ENCLAVE(TeleportType.JEWELLERY, "Ring of Dueling" , "Ferox Enclave", new WorldPoint(3151, 3636, 0), "ring_of_dueling_teleport_icon.png"), + CASTLE_WARS(TeleportType.JEWELLERY, "Ring of Dueling" , "Castle Wars", new WorldPoint(2441, 3091, 0), "ring_of_dueling_teleport_icon.png"), + WARRIORS_GUILD(TeleportType.JEWELLERY, "Combat Bracelet" , "Warriors' Guild", new WorldPoint(2883, 3549, 0), "combat_bracelet_teleport_icon.png"), + CHAMPIONS_GUILD(TeleportType.JEWELLERY, "Combat Bracelet" , "Champions' Guild", new WorldPoint(3189, 3368, 0), "combat_bracelet_teleport_icon.png"), + EDGEVILLE_MONASTERY(TeleportType.JEWELLERY, "Combat Bracelet" , "Edgeville Monastery", new WorldPoint(3053, 3487, 0), "combat_bracelet_teleport_icon.png"), + RANGING_GUILD(TeleportType.JEWELLERY, "Combat Bracelet" , "Ranging Guild", new WorldPoint(2654, 3441, 0), "combat_bracelet_teleport_icon.png"), + FISHING_GUILD_NECK(TeleportType.JEWELLERY, "Skills Necklace" , "Fishing Guild", new WorldPoint(2613, 3390, 0), "skills_necklace_teleport_icon.png"), + MINING_GUILD(TeleportType.JEWELLERY, "Skills Necklace" , "Mining Guild", new WorldPoint(3049, 9762, 0), "skills_necklace_teleport_icon.png"), + CRAFTING_GUILD(TeleportType.JEWELLERY, "Skills Necklace" , "Crafting Guild", new WorldPoint(2934, 3294, 0), "skills_necklace_teleport_icon.png"), + COOKING_GUILD(TeleportType.JEWELLERY, "Skills Necklace" , "Cooking Guild", new WorldPoint(3145, 3438, 0), "skills_necklace_teleport_icon.png"), + WOODCUTTING_GUILD(TeleportType.JEWELLERY, "Skills Necklace" , "Woodcutting Guild", new WorldPoint(1662, 3505, 0), "skills_necklace_teleport_icon.png"), + FARMING_GUILD(TeleportType.JEWELLERY, "Skills Necklace" , "Farming Guild", new WorldPoint(1249, 3717, 0), "skills_necklace_teleport_icon.png"), + EDGEVILLE(TeleportType.JEWELLERY, "Amulet of Glory" , "Edgeville", new WorldPoint(3087, 3496, 0), "amulet_of_glory_teleport_icon.png"), + KARAMJA(TeleportType.JEWELLERY, "Amulet of Glory" , "Karamja", new WorldPoint(2918, 3176, 0), "amulet_of_glory_teleport_icon.png"), + DRAYNOR_VILLAGE(TeleportType.JEWELLERY, "Amulet of Glory" , "Draynor Village", new WorldPoint(3105, 3251, 0), "amulet_of_glory_teleport_icon.png"), + AL_KHARID(TeleportType.JEWELLERY, "Amulet of Glory" , "Al-Kharid", new WorldPoint(3293, 3163, 0), "amulet_of_glory_teleport_icon.png"), + MISCELLANIA(TeleportType.JEWELLERY, "Ring of Wealth" , "Miscellania", new WorldPoint(2535, 3862, 0), "ring_of_wealth_teleport_icon.png"), + GRAND_EXCHANGE(TeleportType.JEWELLERY, "Ring of Wealth" , "Grand Exchange", new WorldPoint(3162, 3480, 0), "ring_of_wealth_teleport_icon.png"), + FALADOR_PARK(TeleportType.JEWELLERY, "Ring of Wealth" , "Falador Park", new WorldPoint(2995, 3375, 0), "ring_of_wealth_teleport_icon.png"), + DONDAKAN(TeleportType.JEWELLERY, "Ring of Wealth" , "Dondakan", new WorldPoint(2831, 10165, 0), "ring_of_wealth_teleport_icon.png"), + SLAYER_TOWER(TeleportType.JEWELLERY, "Slayer Ring" , "Slayer Tower", new WorldPoint(3423, 3536, 0), "slayer_ring_teleport_icon.png"), + FREMENNIK_SLAYER_DUNGEON(TeleportType.JEWELLERY, "Slayer Ring" , "Fremennik Slayer Dungeon", new WorldPoint(2800, 9998, 0), "slayer_ring_teleport_icon.png"), + FREMENNIK_SLAYER_DUNGEON_OUTSIDE(TeleportType.JEWELLERY, "Slayer Ring" , "Fremennik Slayer Dungeon (inside)", new WorldPoint(2800, 3615, 0), "slayer_ring_teleport_icon.png"), + TARNS_LAIR(TeleportType.JEWELLERY, "Slayer Ring" , "Tarn's Lair", new WorldPoint(3187, 4601, 0), "slayer_ring_teleport_icon.png"), + STRONGHOLD_SLAYER_CAVE(TeleportType.JEWELLERY, "Slayer Ring" , "Stronghold Slayer Cave", new WorldPoint(2433, 3421, 0), "slayer_ring_teleport_icon.png"), + DARK_BEASTS(TeleportType.JEWELLERY, "Slayer Ring" , "Dark Beasts", new WorldPoint(2028, 4638, 0), "slayer_ring_teleport_icon.png"), + DIGSITE(TeleportType.JEWELLERY, "Digsite Pendant" , "Digsite", new WorldPoint(3339, 3445, 0), "digsite_pendant_teleport_icon.png"), + HOUSE_ON_THE_HILL(TeleportType.JEWELLERY, "Digsite Pendant" , "House on the Hill", new WorldPoint(3763, 3869, 0), "digsite_pendant_teleport_icon.png"), + LITHKREN(TeleportType.JEWELLERY, "Digsite Pendant" , "Lithkren", new WorldPoint(3547, 10456, 0), "digsite_pendant_teleport_icon.png"), + WIZARDS_TOWER(TeleportType.JEWELLERY, "Necklace of Passage" , "Wizards' Tower", new WorldPoint(3114, 3181, 0), "necklace_of_passage_teleport_icon.png"), + JORRALS_OUTPOST(TeleportType.JEWELLERY, "Necklace of Passage" , "Jorral's Outpost", new WorldPoint(2431, 3348, 0), "necklace_of_passage_teleport_icon.png"), + DESERT_EAGLE_STATION(TeleportType.JEWELLERY, "Necklace of Passage" , "Desert eagle station of the eagle transport system", new WorldPoint(3406, 3157, 0), "necklace_of_passage_teleport_icon.png"), + CHAOS_TEMPLE(TeleportType.JEWELLERY, "Burning Amulet" , "Chaos Temple (lvl 15)", new WorldPoint(3234, 3637, 0), "burning_amulet_teleport_icon.png"), + BANDIT_CAMP(TeleportType.JEWELLERY, "Burning Amulet" , "Bandit Camp (lvl 17)", new WorldPoint(3038, 3651, 0), "burning_amulet_teleport_icon.png"), + LAVA_MAZE(TeleportType.JEWELLERY, "Burning Amulet" , "Lava Maze (lvl 41)", new WorldPoint(3028, 3840, 0), "burning_amulet_teleport_icon.png"), + + // Misc + XERICS_LOOKOUT(TeleportType.OTHER, "Xeric's Talisman", "Xeric's Lookout", new WorldPoint(1576, 3528, 0), "xerics_talisman_teleport_icon.png"), + XERICS_GLADE(TeleportType.OTHER, "Xeric's Talisman", "Xeric's Glade", new WorldPoint(1754, 3564, 0), "xerics_talisman_teleport_icon.png"), + XERICS_INFERNO(TeleportType.OTHER, "Xeric's Talisman", "Xeric's Inferno", new WorldPoint(1504, 3819, 0), "xerics_talisman_teleport_icon.png"), + XERICS_HEART(TeleportType.OTHER, "Xeric's Talisman", "Xeric's Heart", new WorldPoint(1641, 3670, 0), "xerics_talisman_teleport_icon.png"), + XERICS_HONOUR(TeleportType.OTHER, "Xeric's Talisman", "Xeric's Honour", new WorldPoint(1254, 3559, 0), "xerics_talisman_teleport_icon.png"), + STRONGHOLD_OF_SECURITY(TeleportType.OTHER, "Skull Sceptre", "Stronghold of Security", new WorldPoint(3081, 3421, 0), "skull_sceptre_teleport_icon.png"), + MYTHS_GUILD(TeleportType.OTHER, "Mythical Cape", "Myth's Guild", new WorldPoint(2458, 2851, 0), "mythical_cape_teleport_icon.png"), + ECTOFUNTUS(TeleportType.OTHER, "Ectophial", "Ectofuntus", new WorldPoint(3660, 3522, 0), "ectophial_teleport_icon.png"), + CHAMPIONS_GUILD_CHRONICLE(TeleportType.OTHER, "Chronicle", "Champions' Guild", new WorldPoint(3202, 3357, 0), "chronicle_teleport_icon.png"), + GRAND_TREE(TeleportType.OTHER, "Royal Seed Pod", "Grand Tree", new WorldPoint(2465, 3495, 0), "royal_seed_pod_teleport_icon.png"), + RELLEKKKA_LYRE(TeleportType.OTHER, "Enchanted Lyre", "Rellekka", new WorldPoint(2664, 3643, 0), "enchanted_lyre_teleport_icon.png"), + WATERBIRTH_ISLAND_LYRE(TeleportType.OTHER, "Enchanted Lyre", "Waterbirth Island", new WorldPoint(2550, 3756, 0), "enchanted_lyre_teleport_icon.png"), + NEITIZNOT_LYRE(TeleportType.OTHER, "Enchanted Lyre", "Neitiznot", new WorldPoint(2336, 3801, 0), "enchanted_lyre_teleport_icon.png"), + JATIZSO_LYRE(TeleportType.OTHER, "Enchanted Lyre", "Jatizso", new WorldPoint(2409, 3809, 0), "enchanted_lyre_teleport_icon.png"), + WEISS_ICY_BASALT(TeleportType.OTHER, "Icy Basalt", "Weiss", new WorldPoint(2846, 3940, 0), "icy_basalt_teleport_icon.png"), + TROLL_STRONGHOLD_STONY_BASALT(TeleportType.OTHER, "Stony Basalt", "Troll Stronghold (with 73 Agility)", new WorldPoint(2838, 3693, 0), "stony_basalt_teleport_icon.png"), + TROLL_STRONGHOLD_STONY_BASALT_OUTSIDE(TeleportType.OTHER, "Stony Basalt", "Troll Stronghold", new WorldPoint(2844, 3693, 0), "stony_basalt_teleport_icon.png"), + KHAREDSTS_MEMOIRS_HOSIDIUS(TeleportType.OTHER, "Kharedst's Memoirs", "Lunch by the Lancalliums (Hosidius)", new WorldPoint(1713, 3612, 0), "kharedsts_memoirs_teleport_icon.png"), + KHAREDSTS_MEMOIRS_PISCARILIUS(TeleportType.OTHER, "Kharedst's Memoirs", "The Fisher's Flute (Piscarilius)", new WorldPoint(1802, 3748, 0), "kharedsts_memoirs_teleport_icon.png"), + KHAREDSTS_MEMOIRS_SHAYZIEN(TeleportType.OTHER, "Kharedst's Memoirs", "History and Hearsay (Shayzien)", new WorldPoint(1476, 3580, 0), "kharedsts_memoirs_teleport_icon.png"), + KHAREDSTS_MEMOIRS_LOVAKENGJ(TeleportType.OTHER, "Kharedst's Memoirs", "Jewelry of Jubilation (Lovakengj)", new WorldPoint(1544, 3762, 0), "kharedsts_memoirs_teleport_icon.png"), + KHAREDSTS_MEMOIRS_ARCEUUS(TeleportType.OTHER, "Kharedst's Memoirs", "A Dark Disposition (Arceuus)", new WorldPoint(1680, 3746, 0), "kharedsts_memoirs_teleport_icon.png"), + PHARAOHS_SCEPTRE_JALSAVRAH(TeleportType.OTHER, "Pharaoh's Sceptre", "Jalsavrah (Pyramid Plunder)", new WorldPoint(3288, 2795, 0), "pharaohs_sceptre_teleport_icon.png"), + PHARAOHS_SCEPTRE_JALEUSTROPHOS(TeleportType.OTHER, "Pharaoh's Sceptre", "Jaleustrophos (Agility Pyramid)", new WorldPoint(3341, 2827, 0), "pharaohs_sceptre_teleport_icon.png"), + PHARAOHS_SCEPTRE_JALDRAOCHT(TeleportType.OTHER, "Pharaoh's Sceptre", "Jaldraocht (Desert Treasure Pyramid)", new WorldPoint(3232, 2897, 0), "pharaohs_sceptre_teleport_icon.png"), + CAMULET_TEMPLE(TeleportType.OTHER, "Camulet", "Enakhra's Temple", new WorldPoint(3190, 2923, 0), "camulet_teleport_icon.png"), + TELEPORT_CRYSTAL_LLETYA(TeleportType.OTHER, "Teleport crystal", "Lletya", new WorldPoint(2330, 3172, 0), "teleport_crystal_icon.png"), + TELEPORT_CRYSTAL_PRIFDDINAS(TeleportType.OTHER, "Teleport crystal", "Prifddinas", new WorldPoint(3264, 6065, 0), "teleport_crystal_icon.png"), + DRAKANS_MEDALLION_VER_SINHAZA(TeleportType.OTHER, "Drakan's medallion", "Ver Sinhaza", new WorldPoint(3649, 3230, 0), "drakans_medallion_teleport_icon.png"), + DRAKANS_MEDALLION_DARKMEYER(TeleportType.OTHER, "Drakan's medallion", "Darkmeyer", new WorldPoint(3592, 3337, 0), "drakans_medallion_teleport_icon.png"), + + // Wilderness + OBELISK_13(TeleportType.OTHER, "Obelisk", "13", new WorldPoint(3156, 3620, 0), "obelisk_icon.png"), + OBELISK_19(TeleportType.OTHER, "Obelisk", "19", new WorldPoint(3227, 3667, 0), "obelisk_icon.png"), + OBELISK_27(TeleportType.OTHER, "Obelisk", "27", new WorldPoint(3035, 3732, 0), "obelisk_icon.png"), + OBELISK_35(TeleportType.OTHER, "Obelisk", "35", new WorldPoint(3106, 3794, 0), "obelisk_icon.png"), + OBELISK_44(TeleportType.OTHER, "Obelisk", "44", new WorldPoint(2980, 3866, 0), "obelisk_icon.png"), + OBELISK_50(TeleportType.OTHER, "Obelisk", "50", new WorldPoint(3307, 3916, 0), "obelisk_icon.png"), + WILDERNESS_CRABS_TELEPORT(TeleportType.OTHER, "Wilderness crabs teleport", new WorldPoint(3348, 3783, 0), "wilderness_crabs_teleport_icon.png"), + CANOE_WILDERNESS(TeleportType.OTHER, "Canoe (No departure)", "35", new WorldPoint(3141, 3796, 0), "transportation_icon.png"), + + // Achievement Diary + ARDOUGNE_CLOAK_MONASTERY(TeleportType.OTHER, "Ardougne Cloak", "Monastery", new WorldPoint(2606, 3222, 0), "ardougne_cloak_icon.png"), + ARDOUGNE_CLOAK_FARM(TeleportType.OTHER, "Ardougne Cloak", "Farm", new WorldPoint(2673, 3375, 0), "ardougne_cloak_icon.png"), + EXPLORERS_RING(TeleportType.OTHER, "Explorer's Ring", new WorldPoint(3052, 3292, 0), "explorers_ring_icon.png"), + KARAMJA_GLOVES_GEM_MINE(TeleportType.OTHER, "Karamja Gloves", "Gem Mine (Underground)", new WorldPoint(2827, 2997, 0), "karamja_gloves_icon.png"), + KARAMJA_GLOVES_DURADEL(TeleportType.OTHER, "Karamja Gloves", "Duradel", new WorldPoint(2870, 2981, 0), "karamja_gloves_icon.png"), + DESERT_AMULET_NARDAH(TeleportType.OTHER, "Desert Amulet", "Nardah", new WorldPoint(3425, 2928, 0), "desert_amulet_icon.png"), + DESERT_AMULKT_KALPHITE_CAVE(TeleportType.OTHER, "Desert Amulet", "Kalphite Cave", new WorldPoint(3322, 3122, 0), "desert_amulet_icon.png"), + MORYTANIA_LEGS_SLIME_PIT(TeleportType.OTHER, "Morytania Legs", "Slime Pit (Underground)", new WorldPoint(3654, 3516, 0), "morytania_legs_icon.png"), + MORYTANIA_LEGS_BURGH_DE_ROTT(TeleportType.OTHER, "Morytania Legs", "Burgh de Rott", new WorldPoint(3482, 3231, 0), "morytania_legs_icon.png"), + FREMENNIK_SEA_BOOTS(TeleportType.OTHER, "Fremennik Sea Boots", new WorldPoint(2640, 3675, 0), "fremennik_boots_icon.png"), + KANDARIN_HEADGEAR(TeleportType.OTHER, "Kandarin Headgear", new WorldPoint(2729, 3411, 0), "kandarin_headgear_icon.png"), + WILDERNESS_SWORD(TeleportType.OTHER, "Wilderness Sword", new WorldPoint(3377, 3891, 0), "wilderness_sword_icon.png"), + WESTERN_BANNER(TeleportType.OTHER, "Western Banner", new WorldPoint(2329, 3685, 0), "western_banner_icon.png"), + RADAS_BLESSING_MOUNT_KARUULM(TeleportType.OTHER, "Rada's Blessing", new WorldPoint(1311, 3795, 0), "radas_blessing_icon.png"), + RADAS_BLESSING_WOODLANG(TeleportType.OTHER, "Rada's Blessing", new WorldPoint(1553, 3454, 0), "radas_blessing_icon.png"), + + // Scrolls + DIGSITE_SCROLL(TeleportType.SCROLL, "Digsite Teleport", new WorldPoint(3324, 3412, 0), "scroll_teleport_icon.png"), + IORWERTH_CAMP_SCROLL(TeleportType.SCROLL, "Iorwerth Camp Teleport", new WorldPoint(2193, 3257, 0), "scroll_teleport_icon.png"), + FELDIP_HILLS_SCROLL(TeleportType.SCROLL, "Feldip Hills Teleport", new WorldPoint(2542, 2925, 0), "scroll_teleport_icon.png"), + LUMBERYARD_SCROLL(TeleportType.SCROLL, "Lumberyard Teleport", new WorldPoint(3303, 3487, 0), "scroll_teleport_icon.png"), + LUNAR_ISLE_SCROLL(TeleportType.SCROLL, "Lunar Isle Teleport", new WorldPoint(2093, 3912, 0), "scroll_teleport_icon.png"), + MORTTON_SCROLL(TeleportType.SCROLL, "Mort'ton", new WorldPoint(3489, 3288, 0), "scroll_teleport_icon.png"), + MOS_LEHARMLESS_SCROLL(TeleportType.SCROLL, "Mos Le'Harmless Teleport", new WorldPoint(3701, 2996, 0), "scroll_teleport_icon.png"), + NARDAH_SCROLL(TeleportType.SCROLL, "Nardah Teleport", new WorldPoint(3421, 2917, 0), "scroll_teleport_icon.png"), + PEST_CONTROL_SCROLL(TeleportType.SCROLL, "Pest Control Teleport", new WorldPoint(2657, 2660, 0), "scroll_teleport_icon.png"), + PISCATORIS_SCROLL(TeleportType.SCROLL, "Piscatoris Teleport", new WorldPoint(2339, 3648, 0), "scroll_teleport_icon.png"), + TAI_BWO_WANNAI_SCROLL(TeleportType.SCROLL, "Tai Bwo Wannai Teleport", new WorldPoint(2788, 3066, 0), "scroll_teleport_icon.png"), + ZULANDRA_SCROLL(TeleportType.SCROLL, "Zul-Andra Teleport", new WorldPoint(2197, 3056, 0), "scroll_teleport_icon.png"), + KEY_MASTER_SCROLL(TeleportType.SCROLL, "Key Master Teleport", new WorldPoint(2686, 9882, 0), "scroll_teleport_icon.png"), + REVENANT_CAVE_SCROLL(TeleportType.SCROLL, "Revenant Cave Teleport", new WorldPoint(3127, 3833, 0), "scroll_teleport_icon.png"); + + private final TeleportType type; + private final String tooltip; + private final WorldPoint location; + private final String iconPath; + + TeleportLocationData(TeleportType type, String destination, int magicLevel, WorldPoint location, String iconPath) + { + this.type = type; + this.tooltip = type.getPrefix() + " " + destination + " - lvl " + magicLevel; + this.location = location; + this.iconPath = iconPath; + } + + TeleportLocationData(TeleportType type, String item, String destination, WorldPoint location, String iconPath) + { + this.type = type; + this.tooltip = item + " - " + destination; + this.location = location; + this.iconPath = iconPath; + } + + TeleportLocationData(TeleportType type, String item, WorldPoint location, String iconPath) + { + this.type = type; + this.tooltip = item; + this.location = location; + this.iconPath = iconPath; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/TeleportPoint.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/TeleportPoint.java new file mode 100644 index 0000000000..c8c2a778cb --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/TeleportPoint.java @@ -0,0 +1,39 @@ +/* + * 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 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.worldmap; + +import net.runelite.client.ui.overlay.worldmap.WorldMapPoint; +import net.runelite.client.util.ImageUtil; + +class TeleportPoint extends WorldMapPoint +{ + TeleportPoint(TeleportLocationData data) + { + super(data.getLocation(), WorldMapPlugin.BLANK_ICON); + setTooltip(data.getTooltip()); + setImage(ImageUtil.getResourceStreamFromClass(WorldMapPlugin.class, data.getIconPath())); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/TeleportType.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/TeleportType.java new file mode 100644 index 0000000000..a51e8f9c6e --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/TeleportType.java @@ -0,0 +1,47 @@ +/* + * 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 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.worldmap; + +import lombok.Getter; + +@Getter +public enum TeleportType +{ + NORMAL_MAGIC(""), + ANCIENT_MAGICKS("Ancient - "), + LUNAR_MAGIC("Lunar - "), + ARCEUUS_MAGIC("Arceuus - "), + JEWELLERY("Jewellery - "), + SCROLL(""), + OTHER(""); + + private String prefix; + + TeleportType(String prefix) + { + this.prefix = prefix; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/TransportationPoint.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/TransportationPoint.java new file mode 100644 index 0000000000..2996794212 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/TransportationPoint.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019, Kyle Sergio + * Copyright (c) 2019, Bryce Altomare + * Copyright (c) 2019, Kyle Stead + * 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.worldmap; + +import net.runelite.api.coords.WorldPoint; +import net.runelite.client.ui.overlay.worldmap.WorldMapPoint; +import java.awt.image.BufferedImage; + +class TransportationPoint extends WorldMapPoint +{ + TransportationPoint(TransportationPointLocation data, BufferedImage icon) + { + super(data.getLocation(), icon); + final WorldPoint target = data.getTarget(); + if (target != null) + { + setTarget(target); + setJumpOnClick(true); + } + setTooltip(data.getTooltip()); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/TransportationPointLocation.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/TransportationPointLocation.java new file mode 100644 index 0000000000..d7a683076e --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/TransportationPointLocation.java @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2019, Kyle Sergio + * Copyright (c) 2019, Bryce Altomare + * Copyright (c) 2019, Kyle Stead + * 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.worldmap; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import net.runelite.api.coords.WorldPoint; +import javax.annotation.Nullable; + +@Getter +@AllArgsConstructor +enum TransportationPointLocation +{ + //Ships + ARDOUGNE_TO_BRIMHAVEN("Ship to Brimhaven / Rimmington", new WorldPoint(2675, 3275, 0)), + ARDOUGNE_TO_FISHINGPLAT("Ship to Fishing Platform", new WorldPoint(2722, 3304, 0), new WorldPoint(2779, 3271, 0)), + BRIMHAVEN_TO_ARDOUGNE("Ship to Ardougne / Rimmington", new WorldPoint(2772, 3234, 0)), + RIMMINGTON_TO_ARDOUGNE("Ship to Ardougne / Brimhaven", new WorldPoint(2915, 3224, 0)), + CATHERBY_TO_KEEP_LE_FAYE("Ship to Keep Le Faye", new WorldPoint(2804, 3421, 0), new WorldPoint(2769, 3402, 0)), + CORSAIR_TO_RIMMINGTON("Ship to Rimmington", new WorldPoint(2577, 2839, 0), new WorldPoint(2909, 3227, 0 )), + DRAGONTOOTH_TO_PHASMATYS("Ship to Port Phasmatys", new WorldPoint(3791, 3561, 0), new WorldPoint(3703, 3487, 0)), + DIGSITE_TO_FOSSIL("Ship to Fossil Island", new WorldPoint(3361, 3448, 0), new WorldPoint(3723, 3807, 0)), + ENTRANA_TO_PORTSARIM("Ship to Port Sarim", new WorldPoint(2833, 3334, 0), new WorldPoint(3046, 3233, 0)), + FISHINGPLAT_TO_ARDOUGNE("Ship to Ardougne", new WorldPoint(2779, 3271, 0), new WorldPoint(2722, 3304, 0)), + HARMLESS_TO_PORT_PHASMATYS("Ship to Port Phasmatys", new WorldPoint(3682, 2951, 0), new WorldPoint(3709, 3497, 0)), + ICEBERG_TO_RELLEKKA("Ship to Rellekka", new WorldPoint(2657, 3988, 0), new WorldPoint(2707, 3735, 0)), + ISLAND_OF_STONE_TO_RELLEKKA("Ship to Rellekka", new WorldPoint(2470, 3994, 0), new WorldPoint(2621, 3692, 0)), + ISLAND_TO_APE_ATOLL("Ship to Ape Atoll", new WorldPoint(2891, 2726, 0), new WorldPoint(2802, 2706, 0)), + JATIZSO_TO_RELLEKKA("Ship to Rellekka", new WorldPoint(2420, 3780, 0), new WorldPoint(2639, 3710, 0)), + KARAMJA_TO_PORT_SARIM("Ship to Port Sarim", new WorldPoint(2955, 3145, 0), new WorldPoint(3029, 3218, 0)), + KARAMJA_TO_PORT_KHAZARD("Ship to Port Khazard", new WorldPoint(2763, 2957, 0), new WorldPoint(2653, 3166, 0)), + LANDSEND_TO_PORTSARIM_PORTPISCARILIUS("Ship to Port Sarim/Port Piscarilius", new WorldPoint(1503, 3398, 0)), + LUNAR_ISLE_TO_PIRATES_COVE("Ship to Pirates' Cove", new WorldPoint(2137, 3899, 0), new WorldPoint(2223, 3796, 0)), + MISCELLANIA_TO_RELLEKKA("Ship to Rellekka", new WorldPoint(2579, 3846, 0), new WorldPoint(2627, 3692, 0)), + NEITIZNOT_TO_RELLEKKA("Ship to Rellekka", new WorldPoint(2310, 3779, 0), new WorldPoint(2639, 3710, 0)), + PESTCONTROL_TO_PORTSARIM("Ship to Port Sarim", new WorldPoint(2659, 2675, 0), new WorldPoint(3039, 3201, 0)), + PIRATES_COVE_TO_LUNAR_ISLE("Ship to Lunar Isle", new WorldPoint(2223, 3796, 0), new WorldPoint(2137, 3899, 0)), + PIRATES_COVE_TO_RELLEKKA("Ship to Rellekka", new WorldPoint(2212, 3794, 0), new WorldPoint(2620, 3695, 0)), + PORT_PHASMATYS_TO_DRAGONTOOTH("Ship to Dragontooth Island", new WorldPoint(3703, 3487, 0), new WorldPoint(3791, 3561, 0)), + PORT_PHASMATYS_TO_HARMLESS("Ship to Mos Le'Harmless", new WorldPoint(3709, 3497, 0), new WorldPoint(3682, 2951, 0)), + PORT_PISCARILIUS_TO_PORTSARIM_LANDSEND("Ship to Port Sarim/Land's End", new WorldPoint(1823, 3692, 0)), + PORTSARIM_TO_GREAT_KOUREND("Ship to Great Kourend", new WorldPoint(3054, 3244, 0), new WorldPoint(1823, 3692, 0)), + PORTSARIM_TO_ENTRANA("Ship to Entrana", new WorldPoint(3046, 3233, 0), new WorldPoint(2833, 3334, 0)), + PORTSARIM_TO_KARAMJA("Ship to Karamja", new WorldPoint(3029, 3218, 0), new WorldPoint(2955, 3144, 0)), + PORTSARIM_TO_CRANDOR("Ship to Crandor", new WorldPoint(3045, 3205, 0), new WorldPoint(2839, 3261, 0)), + PORTSARIM_TO_PEST_CONTROL("Ship to Pest Control", new WorldPoint(3039, 3201, 0), new WorldPoint(2659, 2675, 0)), + RELLEKKA_TO_JATIZSO_NEITIZNOT("Ship to Jatizso/Neitiznot", new WorldPoint(2639, 3710, 0)), + RELLEKKA_TO_MISCELLANIA("Ship to Miscellania", new WorldPoint(2627, 3692, 0), new WorldPoint(2579, 3846, 0)), + RELLEKKA_TO_PIRATES_COVE("Ship to Pirates' Cove", new WorldPoint(2620, 3695, 0), new WorldPoint(2212, 3794, 0)), + RELLEKKA_TO_WATERBIRTH("Ship to Waterbirth", new WorldPoint(2618, 3685, 0), new WorldPoint(2549, 3758, 0)), + RELLEKKA_TO_WEISS_ICEBERG("Ship to Weiss/Iceberg", new WorldPoint(2707, 3735, 0)), + RELLEKKA_TO_UNGAEL("Ship to Ungael", new WorldPoint(2638, 3698, 0), new WorldPoint(2276, 4034, 0)), + RIMMINGTON_TO_CORSAIR_COVE("Ship to Corsair Cove", new WorldPoint(2909, 3227, 0 ), new WorldPoint(2577, 2839, 0)), + WATERBIRTH_TO_RELLEKKA("Ship to Rellekka", new WorldPoint(2549, 3758, 0), new WorldPoint(2618, 3685, 0)), + WEISS_TO_RELLEKKA("Ship to Rellekka", new WorldPoint(2847, 3967, 0), new WorldPoint(2707, 3735, 0)), + UNGAEL_TO_RELLEKKA("Ship to Rellekka", new WorldPoint(2276, 4034, 0), new WorldPoint(2638, 3698, 0)), + + //Row Boats + ROW_BOAT_BATTLEFRONT("Rowboat to Molch/Molch Island/Shayzien", new WorldPoint(1383, 3663, 0)), + ROW_BOAT_BRAIN_DEATH("Rowboat to Port Phasmatys", new WorldPoint(2161, 5117, 0), new WorldPoint(3680, 3538, 0)), + ROW_BOAT_BURGH_DE_ROTT("Rowboat to Meiyerditch", new WorldPoint(3522, 3168, 0), new WorldPoint(3589, 3172, 0)), + ROW_BOAT_CRABCLAW("Rowboat to Hosidius", new WorldPoint(1780, 3417, 0), new WorldPoint(1779, 3457, 0)), + ROW_BOAT_DIVING_ISLAND("Rowboat to Barge/Camp/North of Island", new WorldPoint(3764, 3901, 0)), + ROW_BOAT_FISHING_GUILD("Rowboat to Hemenster", new WorldPoint(2598, 3426, 0), new WorldPoint(2613, 3439, 0)), + ROW_BOAT_GNOME_STRONGHOLD("Rowboat to Fishing Colony", new WorldPoint(2368, 3487, 0), new WorldPoint(2356, 3641, 0)), + ROW_BOAT_FISHING_COLONY("Rowboat to Gnome Stronghold", new WorldPoint(2356, 3641, 0), new WorldPoint(2368, 3487, 0)), + ROW_BOAT_HEMENSTER("Rowboat to Fishing Guild", new WorldPoint(2613, 3439, 0), new WorldPoint(2598, 3426, 0)), + ROW_BOAT_HOSIDIUS("Rowboat to Crabclaw Isle", new WorldPoint(1779, 3457, 0), new WorldPoint(1780, 3417, 0)), + ROW_BOAT_LITHKREN("Rowboat to Mushroom Forest", new WorldPoint(3582, 3973, 0), new WorldPoint(3659, 3849, 0)), + ROW_BOAT_LUMBRIDGE("Rowboat to Misthalin Mystery", new WorldPoint(3238, 3141, 0)), + ROW_BOAT_MOLCH("Rowboat to Molch Island/Shayzien/Battlefront", new WorldPoint(1343, 3646, 0)), + ROW_BOAT_MOLCH_ISLAND("Rowboat to Molch/Shayzien/Battlefront", new WorldPoint(1368, 3641, 0)), + ROW_BOAT_MORT("Rowboat to Mort Myre", new WorldPoint(3518, 3284, 0), new WorldPoint(3498, 3380, 0)), + ROW_BOAT_MORT_SWAMP("Rowboat to Mort'ton", new WorldPoint(3498, 3380, 0), new WorldPoint(3518, 3284, 0)), + ROW_BOAT_MUSEUM_CAMP("Rowboat to Barge/Digsite/North of Island", new WorldPoint(3723, 3807, 0)), + ROW_BOAT_MUSHROOM_FOREST_WEST("Rowboat to Lithkren", new WorldPoint(3659, 3849, 0), new WorldPoint(3582, 3973, 0)), + ROW_BOAT_MUSHROOM_FOREST_NE("Rowboat to Barge/Camp/Sea", new WorldPoint(3733, 3894, 0)), + ROW_BOAT_PORT_PHASMATYS_NORTH("Rowboat to Slepe", new WorldPoint(3670, 3545, 0), new WorldPoint(3661, 3279, 0)), + ROW_BOAT_PORT_PHASMATYS_EAST("Rowboat to Braindeath Island", new WorldPoint(3680, 3538, 0), new WorldPoint(2161, 5117, 0)), + ROW_BOAT_SHAYZIEN("Rowboat to Molch/Molch Island/Battlefront", new WorldPoint(1405, 3612, 0)), + ROW_BOAT_SLEPE("Rowboat to Port Phasmatys", new WorldPoint(3661, 3279, 0), new WorldPoint(3670, 3545, 0)), + OGRE_BOAT_FELDIP("Ogre Boat to Karamja", new WorldPoint(2653, 2964, 0), new WorldPoint(2757, 3085, 0)), + OGRE_BOAT_KARAMJA("Ogre Boat to Feldip", new WorldPoint(2757, 3085, 0), new WorldPoint(2653, 2964, 0)), + + //Charter ships + CHARTER_BRIMHAVEN("Charter Ship", new WorldPoint(2760, 3238, 0)), + CHARTER_CATHERBY("Charter Ship", new WorldPoint(2791, 3415, 0)), + CHARTER_CORSAIR_("Charter Ship", new WorldPoint(2589, 2851, 0)), + CHARTER_KARAMJA_NORTH("Charter Ship", new WorldPoint(2954, 3158, 0)), + CHARTER_KARAMJA_EAST("Charter Ship", new WorldPoint(2999, 3032, 0)), + CHARTER_KHAZARD("Charter Ship", new WorldPoint(2673, 3143, 0)), + CHARTER_MOSLE_HARMLESS("Charter Ship", new WorldPoint(3669, 2931, 0)), + CHARTER_PORT_PHASMATYS("Charter Ship", new WorldPoint(3702, 3503, 0)), + CHARTER_PORTSARIM("Charter Ship", new WorldPoint(3037, 3191, 0)), + CHARTER_TYRAS("Charter Ship", new WorldPoint(2141, 3123, 0)), + CHARTER_PRIFDDINAS("Charter Ship", new WorldPoint(2156, 3331, 0)), + CHARTER_PRIFDDINAS_INSTANCE("Charter Ship", new WorldPoint(3180, 6083, 0)), + + //Minecarts/Carts + MINE_CART_ARCEUUS("Minecart", new WorldPoint(1673, 3832, 0)), + MINE_CART_GRANDEXCHANGE("Minecart to Keldagrim", new WorldPoint(3139, 3504, 0)), + MINE_CART_HOSIDIUS("Minecart", new WorldPoint(1656, 3542, 0)), + MINE_CART_KELDAGRIM("Minecart", new WorldPoint(2908, 10170, 0)), + MINE_CART_LOVAKENGJ("Minecart", new WorldPoint(1524, 3721, 0)), + MINE_CART_PORT_PISCARILIUS("Minecart", new WorldPoint(1760, 3708, 0)), + MINE_CART_QUIDAMORTEM("Minecart", new WorldPoint(1253, 3550, 0)), + MINE_CART_SHAYZIEN("Minecart", new WorldPoint(1586, 3622, 0)), + MINE_CART_TAVERLEY_UNDERGROUND("Minecart", new WorldPoint(2874, 9870, 0)), + CART_TO_BRIMHAVEN("Cart to Brimhaven", new WorldPoint(2833, 2958, 0), new WorldPoint(2780, 3214, 0)), + CART_TO_SHILO("Cart to Shilo", new WorldPoint(2780, 3214, 0), new WorldPoint(2833, 2958, 0)), + + //Canoes + CANOE_BARBVILLAGE("Canoe", new WorldPoint(3111, 3409, 0)), + CANOE_CHAMPIONSGUILD("Canoe", new WorldPoint(3202, 3344, 0)), + CANOE_EDGEVILLE("Canoe", new WorldPoint(3130, 3509, 0)), + CANOE_LUMBRIDGE("Canoe", new WorldPoint(3241, 3238, 0)), + CANOE_FEROXENCLAVE("Canoe", new WorldPoint(3155, 3630, 0)), + + //Gnome Gliders + GNOME_GLIDER_KHARID("Gnome Glider", new WorldPoint(3278, 3213, 0)), + GNOME_GLIDER_APE_ATOLL("Gnome Glider", new WorldPoint(2712, 2804, 0)), + GNOME_GLIDER_KARAMJA("Gnome Glider", new WorldPoint(2971, 2974, 0)), + GNOME_GLIDER_FELDIP("Gnome Glider", new WorldPoint(2540, 2969, 0)), + GNOME_GLIDER_GNOMESTRONGHOLD("Gnome Glider", new WorldPoint(2460, 3502, 0)), + GNOME_GLIDER_WHITEWOLF("Gnome Glider", new WorldPoint(2845, 3501, 0)), + + //Balloons + BALLOON_VARROCK("Hot Air Balloon", new WorldPoint(3298, 3480, 0)), + BALLOON_YANILLE("Hot Air Balloon", new WorldPoint(2458, 3108, 0)), + BALLOON_GNOMESTRONGHOLD("Hot Air Balloon", new WorldPoint(2478, 3459, 0)), + BALLOON_TAVERLEY("Hot Air Balloon", new WorldPoint(2936, 3422, 0)), + BALLOON_FALADOR("Hot Air Balloon", new WorldPoint(2921, 3301, 0)), + + //Spirit Tree + SPIRITTREE_ARDOUGNE("Spirit Tree", new WorldPoint(2554, 3259, 0)), + SPIRITTREE_CORSAIR("Spirit Tree", new WorldPoint(2485, 2850, 0)), + SPIRITTREE_GNOMESTRONGHOLD("Spirit Tree", new WorldPoint(2459, 3446, 0)), + SPIRITTREE_GNOMEVILLAGE("Spirit Tree", new WorldPoint(2538, 3166, 0)), + SPIRITTREE_GRANDEXCHANGE("Spirit Tree", new WorldPoint(3184, 3510, 0)), + SPIRITTREE_PRIFDDINAS("Spirit Tree", new WorldPoint(3274, 6124, 0)), + + //Carpets + CARPET_KHARID("Carpet to Bedabin/Pollnivneach/Uzer", new WorldPoint(3311, 3107, 0)), + CARPET_BEDABIN("Carpet to Shantay Pass", new WorldPoint(3183, 3042, 0), new WorldPoint(3311, 3107, 0)), + CARPET_POLLNIVNEACH_NORTH("Carpet to Shantay Pass", new WorldPoint(3351, 3001, 0), new WorldPoint(3311, 3107, 0)), + CARPET_POLLNIVNEACH_SOUTH("Carpet to Nardah/Sophanem/Menaphos", new WorldPoint(3345, 2943, 0)), + CARPET_NARDAH("Carpet to Pollnivneach", new WorldPoint(3399, 2916, 0), new WorldPoint(3345, 2943, 0)), + CARPET_SOPHANEM("Carpet to Pollnivneach", new WorldPoint(3288, 2814, 0), new WorldPoint(3345, 2943, 0)), + CARPET_MENAPHOS("Carpet to Pollnivneach", new WorldPoint(3244, 2812, 0), new WorldPoint(3345, 2943, 0)), + CARPET_UZER("Carpet to Shantay Pass", new WorldPoint(3468, 3111, 0), new WorldPoint(3311, 3107, 0)), + + //Teleports + TELEPORT_ARCHIVE_FROM_ARCEUUS("Teleport to Library Archive", new WorldPoint(1623, 3808, 0)), + TELEPORT_HARMLESS_FROM_HARMONY("Teleport to Mos Le'Harmless", new WorldPoint(3784, 2828, 0)), + TELEPORT_RUNE_ARDOUGNE("Teleport to Rune Essence", new WorldPoint(2681, 3325, 0)), + TELEPORT_RUNE_YANILLE("Teleport to Rune Essence", new WorldPoint(2592, 3089, 0)), + TELEPORT_SORCERESS_GARDEN("Teleport to Sorceress's Garden", new WorldPoint(3320, 3141, 0)), + TELEPORT_PRIFDDINAS_LIBRARY("Teleport to Prifddinas Library", new WorldPoint(3254, 6082, 2)), + + //Other + ALTER_KOUREND_UNDERGROUND("Altar to Skotizo", new WorldPoint(1662, 10047, 0)), + FAIRY_RING_ZANARIS_TO_KHARID("Fairy Ring to Al Kharid", new WorldPoint(2483, 4471, 0)), + FAIRY_RING_ZANARIS_TO_SHACK("Fairy Ring to Shack", new WorldPoint(2451, 4471, 0)), + MOUNTAIN_GUIDE_QUIDAMORTEM("Mountain Guide", new WorldPoint(1275, 3559, 0)), + MOUNTAIN_GUIDE_WALL("Mountain Guide", new WorldPoint(1400, 3538, 0)), + MUSHTREE_MUSHROOM_FOREST("Mushtree", new WorldPoint(3674, 3871, 0)), + MUSHTREE_TAR_SWAMP("Mushtree", new WorldPoint(3676, 3755, 0)), + MUSHTREE_VERDANT_VALLEY("Mushtree", new WorldPoint(3757, 3756, 0)), + MYTHS_GUILD_PORTAL("Portal to Guilds", new WorldPoint(2456, 2856, 0)), + TRAIN_KELDAGRIM("Railway Station", new WorldPoint(2941, 10179, 0)), + WILDERNESS_LEVER_ARDOUGNE("Wilderness Lever to Deserted Keep", new WorldPoint(2559, 3309, 0), new WorldPoint(3154, 3924, 0)), + WILDERNESS_LEVER_EDGEVILLE("Wilderness Lever to Deserted Keep", new WorldPoint(3088, 3474, 0), new WorldPoint(3154, 3924, 0)), + WILDERNESS_LEVER_WILDERNESS("Wilderness Lever to Ardougne/Edgeville", new WorldPoint(3154, 3924, 0)); + + private final String tooltip; + private final WorldPoint location; + @Nullable + private final WorldPoint target; + + TransportationPointLocation(String tooltip, WorldPoint worldPoint) + { + this(tooltip, worldPoint, null); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/WorldMapConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/WorldMapConfig.java new file mode 100644 index 0000000000..8dddb1ad60 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/WorldMapConfig.java @@ -0,0 +1,309 @@ +/* + * 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 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.worldmap; + +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup(WorldMapPlugin.CONFIG_KEY) +public interface WorldMapConfig extends Config +{ + @ConfigItem( + keyName = WorldMapPlugin.CONFIG_KEY_FAIRY_RING_TOOLTIPS, + name = "Show fairy ring codes in tooltip", + description = "Display the code for fairy rings in the icon tooltip", + position = 1 + ) + default boolean fairyRingTooltips() + { + return true; + } + + @ConfigItem( + keyName = WorldMapPlugin.CONFIG_KEY_FAIRY_RING_ICON, + name = "Show fairy ring travel icon", + description = "Override the travel icon for fairy rings", + position = 2 + ) + default boolean fairyRingIcon() + { + return true; + } + + @ConfigItem( + keyName = WorldMapPlugin.CONFIG_KEY_AGILITY_SHORTCUT_TOOLTIPS, + name = "Show agility level requirement", + description = "Display the required Agility level in the icon tooltip", + position = 3 + ) + default boolean agilityShortcutTooltips() + { + return true; + } + + @ConfigItem( + keyName = WorldMapPlugin.CONFIG_KEY_AGILITY_SHORTCUT_LEVEL_ICON, + name = "Indicate inaccessible shortcuts", + description = "Indicate shortcuts you do not have the level to use on the icon", + position = 4 + ) + default boolean agilityShortcutLevelIcon() + { + return true; + } + + @ConfigItem( + keyName = WorldMapPlugin.CONFIG_KEY_AGILITY_COURSE_TOOLTIPS, + name = "Show agility course in tooltip", + description = "Displays the name of the agility course in the tooltip", + position = 5 + ) + default boolean agilityCourseTooltip() + { + return true; + } + + @ConfigItem( + keyName = WorldMapPlugin.CONFIG_KEY_AGILITY_COURSE_ROOFTOP_ICON, + name = "Indicate rooftop courses", + description = "Replace the agility icon with a mark of grace for rooftop courses", + position = 6 + ) + default boolean agilityCourseRooftop() + { + return true; + } + + @ConfigItem( + keyName = WorldMapPlugin.CONFIG_KEY_NORMAL_TELEPORT_ICON, + name = "Show Standard Spellbook destinations", + description = "Show icons at the destinations for teleports in the Standard Spellbook", + position = 7 + ) + default boolean normalTeleportIcon() + { + return true; + } + + @ConfigItem( + keyName = WorldMapPlugin.CONFIG_KEY_MINIGAME_TOOLTIP, + name = "Show minigame name in tooltip", + description = "Display the name of the minigame in the icon tooltip", + position = 8 + ) + default boolean minigameTooltip() + { + return true; + } + + @ConfigItem( + keyName = WorldMapPlugin.CONFIG_KEY_ANCIENT_TELEPORT_ICON, + name = "Show Ancient Magicks destinations", + description = "Show icons at the destinations for teleports in the Ancient Spellbook", + position = 9 + ) + default boolean ancientTeleportIcon() + { + return true; + } + + @ConfigItem( + keyName = WorldMapPlugin.CONFIG_KEY_LUNAR_TELEPORT_ICON, + name = "Show Lunar Spellbook destinations", + description = "Show icons at the destinations for teleports in the Lunar Spellbook", + position = 10 + ) + default boolean lunarTeleportIcon() + { + return true; + } + + @ConfigItem( + keyName = WorldMapPlugin.CONFIG_KEY_ARCEUUS_TELEPORT_ICON, + name = "Show Arceuus Spellbook destinations", + description = "Show icons at the destinations for teleports in the Arceuus Spellbook", + position = 11 + ) + default boolean arceuusTeleportIcon() + { + return true; + } + + @ConfigItem( + keyName = WorldMapPlugin.CONFIG_KEY_JEWELLERY_TELEPORT_ICON, + name = "Show jewellery teleport locations", + description = "Show icons at the destinations for teleports from jewellery", + position = 12 + ) + default boolean jewelleryTeleportIcon() + { + return true; + } + + @ConfigItem( + keyName = WorldMapPlugin.CONFIG_KEY_SCROLL_TELEPORT_ICON, + name = "Show teleport scroll locations", + description = "Show icons at the destinations for teleports from scrolls", + position = 13 + ) + default boolean scrollTeleportIcon() + { + return true; + } + + @ConfigItem( + keyName = WorldMapPlugin.CONFIG_KEY_MISC_TELEPORT_ICON, + name = "Show misc teleport locations", + description = "Show icons at the destinations for miscellaneous teleport items", + position = 14 + ) + default boolean miscellaneousTeleportIcon() + { + return true; + } + + @ConfigItem( + keyName = WorldMapPlugin.CONFIG_KEY_QUEST_START_TOOLTIPS, + name = "Show quest names and status", + description = "Indicates the names of quests and shows completion status", + position = 15 + ) + default boolean questStartTooltips() + { + return true; + } + + @ConfigItem( + keyName = WorldMapPlugin.CONFIG_KEY_FARMING_PATCH_TOOLTIPS, + name = "Show farming patch type", + description = "Display the type of farming patches in the icon tooltip", + position = 16 + ) + default boolean farmingPatchTooltips() + { + return true; + } + + @ConfigItem( + keyName = WorldMapPlugin.CONFIG_KEY_RARE_TREE_TOOLTIPS, + name = "Show rare tree type", + description = "Display the type of rare tree in the icon tooltip", + position = 17 + ) + default boolean rareTreeTooltips() + { + return true; + } + + @ConfigItem( + keyName = WorldMapPlugin.CONFIG_KEY_RARE_TREE_LEVEL_ICON, + name = "Indicate unavailable trees", + description = "Indicate rare trees you do not have the level to cut on the icon", + position = 18 + ) + default boolean rareTreeLevelIcon() + { + return true; + } + + @ConfigItem( + keyName = WorldMapPlugin.CONFIG_KEY_TRANSPORTATION_TELEPORT_TOOLTIPS, + name = "Show transportation tooltips", + description = "Indicates types and destinations of Transportation", + position = 19 + ) + default boolean transportationTeleportTooltips() + { + return true; + } + + @ConfigItem( + keyName = WorldMapPlugin.CONFIG_KEY_RUNECRAFTING_ALTAR_ICON, + name = "Show runecrafting altar locations", + description = "Show the icons of runecrafting altars", + position = 20 + ) + default boolean runecraftingAltarIcon() + { + return true; + } + + @ConfigItem( + keyName = WorldMapPlugin.CONFIG_KEY_MINING_SITE_TOOLTIPS, + name = "Show mining site tooltips", + description = "Indicates the ore available at mining sites", + position = 21 + ) + default boolean miningSiteTooltips() + { + return true; + } + + @ConfigItem( + keyName = WorldMapPlugin.CONFIG_KEY_DUNGEON_TOOLTIPS, + name = "Show dungeon tooltips", + description = "Indicates the names of dungeons", + position = 22 + ) + default boolean dungeonTooltips() + { + return true; + } + + @ConfigItem( + keyName = WorldMapPlugin.CONFIG_KEY_HUNTER_AREA_TOOLTIPS, + name = "Show hunter area tooltips", + description = "Indicates the creatures inside a hunting area", + position = 23 + ) + default boolean hunterAreaTooltips() + { + return true; + } + + @ConfigItem( + keyName = WorldMapPlugin.CONFIG_KEY_FISHING_SPOT_TOOLTIPS, + name = "Show fishing spot tooltips", + description = "Indicates the type of fish fishable at the fishing spot", + position = 24 + ) + default boolean fishingSpotTooltips() + { + return true; + } + + @ConfigItem( + keyName = WorldMapPlugin.CONFIG_KEY_KOUREND_TASK_TOOLTIPS, + name = "Show Kourend task tooltips", + description = "Indicates the task or unlock for Kourend Favour locations", + position = 25 + ) + default boolean kourendTaskTooltips() + { + return true; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/WorldMapPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/WorldMapPlugin.java new file mode 100644 index 0000000000..dc0d25a722 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/WorldMapPlugin.java @@ -0,0 +1,464 @@ +/* + * 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 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.worldmap; + +import com.google.inject.Inject; +import com.google.inject.Provides; +import java.awt.image.BufferedImage; +import java.util.Arrays; +import net.runelite.api.Client; +import net.runelite.api.GameState; +import net.runelite.api.Quest; +import net.runelite.api.QuestState; +import net.runelite.api.Skill; +import net.runelite.client.events.ConfigChanged; +import net.runelite.api.events.StatChanged; +import net.runelite.api.events.WidgetLoaded; +import net.runelite.api.widgets.WidgetID; +import net.runelite.client.callback.ClientThread; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.game.AgilityShortcut; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.ui.overlay.worldmap.WorldMapPointManager; +import net.runelite.client.util.ImageUtil; + +@PluginDescriptor( + name = "World Map", + description = "Enhance the world map to display additional information", + tags = {"agility", "dungeon", "fairy", "farming", "rings", "teleports"} +) +public class WorldMapPlugin extends Plugin +{ + static final BufferedImage BLANK_ICON; + private static final BufferedImage FAIRY_TRAVEL_ICON; + private static final BufferedImage NOPE_ICON; + private static final BufferedImage NOT_STARTED_ICON; + private static final BufferedImage STARTED_ICON; + private static final BufferedImage FINISHED_ICON; + private static final BufferedImage MINING_SITE_ICON; + private static final BufferedImage ROOFTOP_COURSE_ICON; + + static final String CONFIG_KEY = "worldmap"; + static final String CONFIG_KEY_FAIRY_RING_TOOLTIPS = "fairyRingTooltips"; + static final String CONFIG_KEY_FAIRY_RING_ICON = "fairyRingIcon"; + static final String CONFIG_KEY_AGILITY_SHORTCUT_TOOLTIPS = "agilityShortcutTooltips"; + static final String CONFIG_KEY_AGILITY_SHORTCUT_LEVEL_ICON = "agilityShortcutIcon"; + static final String CONFIG_KEY_AGILITY_COURSE_TOOLTIPS = "agilityCourseTooltips"; + static final String CONFIG_KEY_AGILITY_COURSE_ROOFTOP_ICON = "agilityCourseRooftopIcon"; + static final String CONFIG_KEY_NORMAL_TELEPORT_ICON = "standardSpellbookIcon"; + static final String CONFIG_KEY_ANCIENT_TELEPORT_ICON = "ancientSpellbookIcon"; + static final String CONFIG_KEY_LUNAR_TELEPORT_ICON = "lunarSpellbookIcon"; + static final String CONFIG_KEY_ARCEUUS_TELEPORT_ICON = "arceuusSpellbookIcon"; + static final String CONFIG_KEY_JEWELLERY_TELEPORT_ICON = "jewelleryIcon"; + static final String CONFIG_KEY_SCROLL_TELEPORT_ICON = "scrollIcon"; + static final String CONFIG_KEY_MISC_TELEPORT_ICON = "miscellaneousTeleportIcon"; + static final String CONFIG_KEY_QUEST_START_TOOLTIPS = "questStartTooltips"; + static final String CONFIG_KEY_MINIGAME_TOOLTIP = "minigameTooltip"; + static final String CONFIG_KEY_FARMING_PATCH_TOOLTIPS = "farmingpatchTooltips"; + static final String CONFIG_KEY_RARE_TREE_TOOLTIPS = "rareTreeTooltips"; + static final String CONFIG_KEY_RARE_TREE_LEVEL_ICON = "rareTreeIcon"; + static final String CONFIG_KEY_TRANSPORTATION_TELEPORT_TOOLTIPS = "transportationTooltips"; + static final String CONFIG_KEY_RUNECRAFTING_ALTAR_ICON = "runecraftingAltarIcon"; + static final String CONFIG_KEY_MINING_SITE_TOOLTIPS = "miningSiteTooltips"; + static final String CONFIG_KEY_DUNGEON_TOOLTIPS = "dungeonTooltips"; + static final String CONFIG_KEY_HUNTER_AREA_TOOLTIPS = "hunterAreaTooltips"; + static final String CONFIG_KEY_FISHING_SPOT_TOOLTIPS = "fishingSpotTooltips"; + static final String CONFIG_KEY_KOUREND_TASK_TOOLTIPS = "kourendTaskTooltips"; + + static + { + //A size of 17 gives us a buffer when triggering tooltips + final int iconBufferSize = 17; + + //Quest icons are a bit bigger. + final int questIconBufferSize = 22; + + BLANK_ICON = new BufferedImage(iconBufferSize, iconBufferSize, BufferedImage.TYPE_INT_ARGB); + + FAIRY_TRAVEL_ICON = new BufferedImage(iconBufferSize, iconBufferSize, BufferedImage.TYPE_INT_ARGB); + final BufferedImage fairyTravelIcon = ImageUtil.getResourceStreamFromClass(WorldMapPlugin.class, "fairy_ring_travel.png"); + FAIRY_TRAVEL_ICON.getGraphics().drawImage(fairyTravelIcon, 1, 1, null); + + NOPE_ICON = new BufferedImage(iconBufferSize, iconBufferSize, BufferedImage.TYPE_INT_ARGB); + final BufferedImage nopeImage = ImageUtil.getResourceStreamFromClass(WorldMapPlugin.class, "nope_icon.png"); + NOPE_ICON.getGraphics().drawImage(nopeImage, 1, 1, null); + + NOT_STARTED_ICON = new BufferedImage(questIconBufferSize, questIconBufferSize, BufferedImage.TYPE_INT_ARGB); + final BufferedImage notStartedIcon = ImageUtil.getResourceStreamFromClass(WorldMapPlugin.class, "quest_not_started_icon.png"); + NOT_STARTED_ICON.getGraphics().drawImage(notStartedIcon, 4, 4, null); + + STARTED_ICON = new BufferedImage(questIconBufferSize, questIconBufferSize, BufferedImage.TYPE_INT_ARGB); + final BufferedImage startedIcon = ImageUtil.getResourceStreamFromClass(WorldMapPlugin.class, "quest_started_icon.png"); + STARTED_ICON.getGraphics().drawImage(startedIcon, 4, 4, null); + + FINISHED_ICON = new BufferedImage(questIconBufferSize, questIconBufferSize, BufferedImage.TYPE_INT_ARGB); + final BufferedImage finishedIcon = ImageUtil.getResourceStreamFromClass(WorldMapPlugin.class, "quest_completed_icon.png"); + FINISHED_ICON.getGraphics().drawImage(finishedIcon, 4, 4, null); + + MINING_SITE_ICON = new BufferedImage(iconBufferSize, iconBufferSize, BufferedImage.TYPE_INT_ARGB); + final BufferedImage miningSiteIcon = ImageUtil.getResourceStreamFromClass(WorldMapPlugin.class, "mining_site_icon.png"); + MINING_SITE_ICON.getGraphics().drawImage(miningSiteIcon, 1, 1, null); + + ROOFTOP_COURSE_ICON = new BufferedImage(iconBufferSize, iconBufferSize, BufferedImage.TYPE_INT_ARGB); + final BufferedImage rooftopCourseIcon = ImageUtil.getResourceStreamFromClass(WorldMapPlugin.class, "rooftop_course_icon.png"); + ROOFTOP_COURSE_ICON.getGraphics().drawImage(rooftopCourseIcon, 1, 1, null); + } + + @Inject + private Client client; + + @Inject + private ClientThread clientThread; + + @Inject + private WorldMapConfig config; + + @Inject + private WorldMapPointManager worldMapPointManager; + + private int agilityLevel = 0; + private int woodcuttingLevel = 0; + + @Provides + WorldMapConfig provideConfig(ConfigManager configManager) + { + return configManager.getConfig(WorldMapConfig.class); + } + + @Override + protected void startUp() throws Exception + { + agilityLevel = client.getRealSkillLevel(Skill.AGILITY); + woodcuttingLevel = client.getRealSkillLevel(Skill.WOODCUTTING); + updateShownIcons(); + } + + @Override + protected void shutDown() throws Exception + { + worldMapPointManager.removeIf(FairyRingPoint.class::isInstance); + worldMapPointManager.removeIf(AgilityShortcutPoint.class::isInstance); + worldMapPointManager.removeIf(QuestStartPoint.class::isInstance); + worldMapPointManager.removeIf(TeleportPoint.class::isInstance); + worldMapPointManager.removeIf(TransportationPoint.class::isInstance); + worldMapPointManager.removeIf(MinigamePoint.class::isInstance); + worldMapPointManager.removeIf(FarmingPatchPoint.class::isInstance); + worldMapPointManager.removeIf(RareTreePoint.class::isInstance); + worldMapPointManager.removeIf(RunecraftingAltarPoint.class::isInstance); + worldMapPointManager.removeIf(DungeonPoint.class::isInstance); + worldMapPointManager.removeIf(FishingSpotPoint.class::isInstance); + worldMapPointManager.removeIf(AgilityCoursePoint.class::isInstance); + agilityLevel = 0; + woodcuttingLevel = 0; + } + + @Subscribe + public void onConfigChanged(ConfigChanged event) + { + if (!event.getGroup().equals(CONFIG_KEY)) + { + return; + } + + updateShownIcons(); + } + + @Subscribe + public void onStatChanged(StatChanged statChanged) + { + switch (statChanged.getSkill()) + { + case AGILITY: + { + int newAgilityLevel = statChanged.getLevel(); + if (newAgilityLevel != agilityLevel) + { + agilityLevel = newAgilityLevel; + updateAgilityIcons(); + } + break; + } + case WOODCUTTING: + { + int newWoodcutLevel = statChanged.getLevel(); + if (newWoodcutLevel != woodcuttingLevel) + { + woodcuttingLevel = newWoodcutLevel; + updateRareTreeIcons(); + } + break; + } + } + } + + @Subscribe + public void onWidgetLoaded(WidgetLoaded widgetLoaded) + { + if (widgetLoaded.getGroupId() == WidgetID.WORLD_MAP_GROUP_ID) + { + // Quest icons are per-account due to showing quest status, + // so we recreate them each time the map is loaded + updateQuestStartPointIcons(); + } + } + + private void updateAgilityIcons() + { + worldMapPointManager.removeIf(AgilityShortcutPoint.class::isInstance); + + if (config.agilityShortcutLevelIcon() || config.agilityShortcutTooltips()) + { + Arrays.stream(AgilityShortcut.values()) + .filter(value -> value.getWorldMapLocation() != null) + .map(value -> new AgilityShortcutPoint(value, + agilityLevel > 0 && config.agilityShortcutLevelIcon() && value.getLevel() > agilityLevel ? NOPE_ICON : BLANK_ICON, + config.agilityShortcutTooltips())) + .forEach(worldMapPointManager::add); + } + } + + private void updateAgilityCourseIcons() + { + worldMapPointManager.removeIf(AgilityCoursePoint.class::isInstance); + + if (config.agilityCourseTooltip() || config.agilityCourseRooftop()) + { + Arrays.stream(AgilityCourseLocation.values()) + .filter(value -> value.getLocation() != null) + .map(value -> new AgilityCoursePoint(value, + config.agilityCourseRooftop() && value.isRooftopCourse() ? ROOFTOP_COURSE_ICON : BLANK_ICON, + config.agilityCourseTooltip())) + .forEach(worldMapPointManager::add); + } + } + + private void updateRareTreeIcons() + { + worldMapPointManager.removeIf(RareTreePoint.class::isInstance); + + if (config.rareTreeLevelIcon() || config.rareTreeTooltips()) + { + Arrays.stream(RareTreeLocation.values()).forEach(rareTree -> + Arrays.stream(rareTree.getLocations()) + .map(point -> new RareTreePoint(point, + rareTree.getTooltip(), + woodcuttingLevel > 0 && config.rareTreeLevelIcon() && + rareTree.getLevelReq() > woodcuttingLevel ? NOPE_ICON : BLANK_ICON, + config.rareTreeTooltips())) + .forEach(worldMapPointManager::add)); + } + } + + private void updateShownIcons() + { + updateAgilityIcons(); + updateAgilityCourseIcons(); + updateRareTreeIcons(); + updateQuestStartPointIcons(); + + worldMapPointManager.removeIf(FairyRingPoint.class::isInstance); + if (config.fairyRingIcon() || config.fairyRingTooltips()) + { + Arrays.stream(FairyRingLocation.values()) + .map(value -> new FairyRingPoint(value, + config.fairyRingIcon() ? FAIRY_TRAVEL_ICON : BLANK_ICON, + config.fairyRingTooltips())) + .forEach(worldMapPointManager::add); + } + + worldMapPointManager.removeIf(MinigamePoint.class::isInstance); + if (config.minigameTooltip()) + { + Arrays.stream(MinigameLocation.values()) + .map(value -> new MinigamePoint(value, BLANK_ICON)) + .forEach(worldMapPointManager::add); + } + + worldMapPointManager.removeIf(TransportationPoint.class::isInstance); + if (config.transportationTeleportTooltips()) + { + Arrays.stream(TransportationPointLocation.values()) + .map(value -> new TransportationPoint(value, BLANK_ICON)) + .forEach((worldMapPointManager::add)); + } + + worldMapPointManager.removeIf(FarmingPatchPoint.class::isInstance); + if (config.farmingPatchTooltips()) + { + Arrays.stream(FarmingPatchLocation.values()).forEach(location -> + Arrays.stream(location.getLocations()) + .map(point -> new FarmingPatchPoint(point, location.getTooltip(), BLANK_ICON)) + .forEach(worldMapPointManager::add) + ); + } + + worldMapPointManager.removeIf(TeleportPoint.class::isInstance); + Arrays.stream(TeleportLocationData.values()) + .filter(data -> + { + switch (data.getType()) + { + case NORMAL_MAGIC: + return config.normalTeleportIcon(); + case ANCIENT_MAGICKS: + return config.ancientTeleportIcon(); + case LUNAR_MAGIC: + return config.lunarTeleportIcon(); + case ARCEUUS_MAGIC: + return config.arceuusTeleportIcon(); + case JEWELLERY: + return config.jewelleryTeleportIcon(); + case SCROLL: + return config.scrollTeleportIcon(); + case OTHER: + return config.miscellaneousTeleportIcon(); + default: + return false; + } + }).map(TeleportPoint::new) + .forEach(worldMapPointManager::add); + + worldMapPointManager.removeIf(RunecraftingAltarPoint.class::isInstance); + if (config.runecraftingAltarIcon()) + { + Arrays.stream(RunecraftingAltarLocation.values()) + .map(RunecraftingAltarPoint::new) + .forEach(worldMapPointManager::add); + } + + worldMapPointManager.removeIf(MiningSitePoint.class::isInstance); + if (config.miningSiteTooltips()) + { + Arrays.stream(MiningSiteLocation.values()) + .map(value -> new MiningSitePoint(value, value.isIconRequired() ? MINING_SITE_ICON : BLANK_ICON)) + .forEach(worldMapPointManager::add); + } + + worldMapPointManager.removeIf(DungeonPoint.class::isInstance); + if (config.dungeonTooltips()) + { + Arrays.stream(DungeonLocation.values()) + .map(value -> new DungeonPoint(value, BLANK_ICON)) + .forEach(worldMapPointManager::add); + } + + worldMapPointManager.removeIf(HunterAreaPoint.class::isInstance); + if (config.hunterAreaTooltips()) + { + Arrays.stream(HunterAreaLocation.values()) + .map(value -> new HunterAreaPoint(value, BLANK_ICON)) + .forEach(worldMapPointManager::add); + } + + worldMapPointManager.removeIf(FishingSpotPoint.class::isInstance); + if (config.fishingSpotTooltips()) + { + Arrays.stream(FishingSpotLocation.values()).forEach(location -> + Arrays.stream(location.getLocations()) + .map(point -> new FishingSpotPoint(point, location.getTooltip(), BLANK_ICON)) + .forEach(worldMapPointManager::add) + ); + } + + worldMapPointManager.removeIf(KourendTaskPoint.class::isInstance); + if (config.kourendTaskTooltips()) + { + Arrays.stream(KourendTaskLocation.values()) + .map(KourendTaskPoint::new) + .forEach(worldMapPointManager::add); + } + } + + private void updateQuestStartPointIcons() + { + worldMapPointManager.removeIf(QuestStartPoint.class::isInstance); + + if (!config.questStartTooltips()) + { + return; + } + + // Must setup the quest icons on the client thread, after the player has logged in. + clientThread.invokeLater(() -> + { + if (client.getGameState() != GameState.LOGGED_IN) + { + return false; + } + + Arrays.stream(QuestStartLocation.values()) + .map(this::createQuestStartPoint) + .forEach(worldMapPointManager::add); + return true; + }); + } + + private QuestStartPoint createQuestStartPoint(QuestStartLocation data) + { + Quest[] quests = data.getQuests(); + + // Get first uncompleted quest. Else, return the last quest. + Quest quest = null; + for (Quest q : quests) + { + if (q.getState(client) != QuestState.FINISHED) + { + quest = q; + break; + } + } + if (quest == null) + { + quest = quests[quests.length - 1]; + } + + BufferedImage icon = BLANK_ICON; + String tooltip = ""; + if (quest != null) + { + tooltip = quest.getName(); + switch (quest.getState(client)) + { + case FINISHED: + icon = FINISHED_ICON; + tooltip += " - Finished"; + break; + case IN_PROGRESS: + icon = STARTED_ICON; + tooltip += " - Started"; + break; + case NOT_STARTED: + icon = NOT_STARTED_ICON; + tooltip += " - Not Started"; + break; + } + } + + return new QuestStartPoint(data.getLocation(), icon, tooltip); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/zalcano/ZalcanoOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/zalcano/ZalcanoOverlay.java new file mode 100644 index 0000000000..e9541d9473 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/zalcano/ZalcanoOverlay.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2019, Hexagon + * 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.zalcano; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.Polygon; +import java.util.List; +import javax.inject.Inject; +import net.runelite.api.Client; +import net.runelite.api.GraphicsObject; +import net.runelite.api.Perspective; +import net.runelite.api.Player; +import net.runelite.api.coords.LocalPoint; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayLayer; +import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.OverlayPriority; +import net.runelite.client.ui.overlay.OverlayUtil; + +class ZalcanoOverlay extends Overlay +{ + private final Client client; + private final ZalcanoPlugin zalcanoPlugin; + + @Inject + private ZalcanoOverlay(Client client, ZalcanoPlugin zalcanoPlugin) + { + this.client = client; + this.zalcanoPlugin = zalcanoPlugin; + setPosition(OverlayPosition.DYNAMIC); + setLayer(OverlayLayer.ABOVE_SCENE); + setPriority(OverlayPriority.MED); + } + + @Override + public Dimension render(Graphics2D graphics) + { + List rocks = zalcanoPlugin.getRocks(); + if (!rocks.isEmpty()) + { + rocks.removeIf(GraphicsObject::finished); + + for (GraphicsObject graphicsObject : rocks) + { + final Player localPlayer = client.getLocalPlayer(); + final LocalPoint graphicsObjectLocation = graphicsObject.getLocation(); + final Polygon polygon = Perspective.getCanvasTilePoly(client, graphicsObjectLocation); + + if (polygon != null) + { + OverlayUtil.renderPolygon(graphics, polygon, localPlayer.getLocalLocation().equals(graphicsObjectLocation) ? Color.RED : Color.ORANGE); + } + } + } + + LocalPoint targetedGlowingRock = zalcanoPlugin.getTargetedGlowingRock(); + if (targetedGlowingRock != null && client.getGameCycle() < zalcanoPlugin.getTargetedGlowingRockEndCycle()) + { + final Polygon polygon = Perspective.getCanvasTileAreaPoly(client, targetedGlowingRock, 3); + + if (polygon != null) + { + OverlayUtil.renderPolygon(graphics, polygon, Color.RED); + } + } + return null; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/zalcano/ZalcanoPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/zalcano/ZalcanoPanel.java new file mode 100644 index 0000000000..b30b29ca7e --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/zalcano/ZalcanoPanel.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2020, dekvall + * 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.zalcano; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import javax.inject.Inject; +import net.runelite.client.ui.overlay.OverlayPanel; +import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.components.LineComponent; + +class ZalcanoPanel extends OverlayPanel +{ + private final ZalcanoPlugin plugin; + + @Inject + public ZalcanoPanel(ZalcanoPlugin plugin) + { + super(plugin); + setPosition(OverlayPosition.ABOVE_CHATBOX_RIGHT); + this.plugin = plugin; + } + + @Override + public Dimension render(Graphics2D g) + { + if (!plugin.isInCavern()) + { + return null; + } + + panelComponent.getChildren().add(LineComponent.builder() + .left("Health damage:") + .leftColor(colorFromCount(plugin.getHealthDamage())) + .right(Integer.toString(plugin.getHealthDamage())) + .build()); + + panelComponent.getChildren().add(LineComponent.builder() + .left("Shield damage:") + .leftColor(colorFromCount(plugin.getShieldDamage())) + .right(Integer.toString(plugin.getShieldDamage())) + .build()); + + return super.render(g); + } + + private static Color colorFromCount(int damage) + { + if (damage >= 50) + { + // Eligible for uniques/pet + return Color.GREEN; + } + if (damage >= 30) + { + // Eligible for drops + return Color.YELLOW; + } + return Color.RED; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/zalcano/ZalcanoPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/zalcano/ZalcanoPlugin.java new file mode 100644 index 0000000000..fdaa151b29 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/zalcano/ZalcanoPlugin.java @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2019, Hexagon + * 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.zalcano; + +import java.util.ArrayList; +import java.util.List; +import javax.inject.Inject; +import lombok.Getter; +import net.runelite.api.Actor; +import net.runelite.api.Client; +import net.runelite.api.GameObject; +import net.runelite.api.GameState; +import static net.runelite.api.GraphicID.GRAPHICS_OBJECT_ROCKFALL; +import net.runelite.api.GraphicsObject; +import net.runelite.api.Hitsplat; +import net.runelite.api.NPC; +import net.runelite.api.NpcID; +import static net.runelite.api.NpcID.ZALCANO; +import net.runelite.api.ObjectID; +import net.runelite.api.Projectile; +import static net.runelite.api.ProjectileID.ZALCANO_PROJECTILE_FIREBALL; +import net.runelite.api.VarPlayer; +import net.runelite.api.coords.LocalPoint; +import net.runelite.api.coords.WorldPoint; +import net.runelite.api.events.GameObjectSpawned; +import net.runelite.api.events.GameStateChanged; +import net.runelite.api.events.GraphicsObjectCreated; +import net.runelite.api.events.HitsplatApplied; +import net.runelite.api.events.NpcChanged; +import net.runelite.api.events.NpcDespawned; +import net.runelite.api.events.NpcSpawned; +import net.runelite.api.events.ProjectileMoved; +import net.runelite.api.events.VarbitChanged; +import net.runelite.client.callback.ClientThread; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.ui.overlay.OverlayManager; + +@PluginDescriptor( + name = "Zalcano", + description = "Assistance for the Zalcano fight", + enabledByDefault = false +) +public class ZalcanoPlugin extends Plugin +{ + private static final int ZALCANO_WEAKENED = NpcID.ZALCANO_9050; + private static final int GOLEM = NpcID.GOLEM_9051; + + @Inject + private Client client; + + @Inject + private OverlayManager overlayManager; + + @Inject + private ZalcanoOverlay overlay; + + @Inject + private ZalcanoPanel panel; + + @Inject + private ClientThread clientThread; + + @Getter + private LocalPoint targetedGlowingRock; + @Getter + private int targetedGlowingRockEndCycle; + private WorldPoint lastGlowingRock; + + @Getter + private final List rocks = new ArrayList<>(); + + @Getter + private int healthDamage; + @Getter + private int shieldDamage; + @Getter + private boolean inCavern; + + @Override + protected void startUp() + { + overlayManager.add(overlay); + overlayManager.add(panel); + rocks.clear(); + + resetDamageCounter(); + clientThread.invokeLater(() -> + { + if (client.getGameState() == GameState.LOGGED_IN) + { + inCavern = isHealthbarActive(); + } + }); + } + + @Override + protected void shutDown() + { + overlayManager.remove(overlay); + overlayManager.remove(panel); + } + + @Subscribe + public void onGraphicsObjectCreated(GraphicsObjectCreated graphicsObjectCreated) + { + GraphicsObject graphicsObject = graphicsObjectCreated.getGraphicsObject(); + if (graphicsObject.getId() == GRAPHICS_OBJECT_ROCKFALL) + { + rocks.add(graphicsObject); + } + } + + @Subscribe + public void onGameStateChanged(GameStateChanged event) + { + GameState gameState = event.getGameState(); + if (gameState == GameState.LOADING) + { + rocks.clear(); + } + } + + @Subscribe + public void onVarbitChanged(VarbitChanged event) + { + boolean wasInCavern = inCavern; + inCavern = isHealthbarActive(); + + if (!inCavern && wasInCavern) + { + resetDamageCounter(); + } + } + + @Subscribe + public void onNpcSpawned(NpcSpawned event) + { + final NPC npc = event.getNpc(); + + if (npc.getId() == GOLEM) + { + client.setHintArrow(npc); + } + } + + @Subscribe + public void onNpcDespawned(NpcDespawned event) + { + final NPC npc = event.getNpc(); + + if (npc.getId() == ZALCANO_WEAKENED) + { + client.clearHintArrow(); + } + else if (npc.getId() == GOLEM) + { + if (lastGlowingRock != null) + { + client.setHintArrow(lastGlowingRock); + } + } + } + + private void resetDamageCounter() + { + healthDamage = 0; + shieldDamage = 0; + } + + @Subscribe + public void onGameObjectSpawned(GameObjectSpawned event) + { + final GameObject gameObject = event.getGameObject(); + + if (gameObject.getId() == ObjectID.ROCK_FORMATION_GLOWING) + { + WorldPoint worldLocation = lastGlowingRock = gameObject.getWorldLocation(); + client.setHintArrow(worldLocation); + } + } + + @Subscribe + public void onNpcChanged(NpcChanged event) + { + final NPC npc = event.getNpc(); + + if (npc.getId() == ZALCANO_WEAKENED) + { + client.setHintArrow(npc); + } + else if (npc.getId() == ZALCANO) + { + if (lastGlowingRock != null) + { + client.setHintArrow(lastGlowingRock); + } + } + } + + @Subscribe + public void onProjectileMoved(ProjectileMoved event) + { + final Projectile projectile = event.getProjectile(); + + if (projectile.getId() == ZALCANO_PROJECTILE_FIREBALL) + { + targetedGlowingRock = event.getPosition(); + targetedGlowingRockEndCycle = projectile.getEndCycle(); + } + } + + @Subscribe + public void onHitsplatApplied(HitsplatApplied event) + { + final Actor actor = event.getActor(); + + if (!(actor instanceof NPC)) + { + return; + } + + int npcId = ((NPC) actor).getId(); + if (!(npcId == ZALCANO_WEAKENED || npcId == ZALCANO)) + { + return; + } + + Hitsplat hitsplat = event.getHitsplat(); + int damage = hitsplat.getAmount(); + + switch (hitsplat.getHitsplatType()) + { + case DAMAGE_ME: + healthDamage += damage; + break; + case DAMAGE_ME_ORANGE: + shieldDamage += damage; + break; + } + } + + private boolean isHealthbarActive() + { + return client.getVar(VarPlayer.ZALCANO_FORM) != -1; + } +} diff --git a/runelite-client/src/main/resources/net/runelite/client/runelite.properties b/runelite-client/src/main/resources/net/runelite/client/runelite.properties index 6b3d275c81..526d0cc0d6 100644 --- a/runelite-client/src/main/resources/net/runelite/client/runelite.properties +++ b/runelite-client/src/main/resources/net/runelite/client/runelite.properties @@ -1,7 +1,7 @@ runelite.title=RuneLite runelite.version=@project.version@ runescape.version=@rs.version@ -runelite.discord.appid=409416265891971072 +runelite.discord.appid=627741263881568257 runelite.discord.invite=https://discord.gg/R4BQ8tU runelite.github.link=https://github.com/runelite runelite.wiki.link=https://github.com/runelite/runelite/wiki