diff --git a/runelite-api/src/main/java/net/runelite/api/AnimationID.java b/runelite-api/src/main/java/net/runelite/api/AnimationID.java index 7c4d40e61c..92e852c7fa 100644 --- a/runelite-api/src/main/java/net/runelite/api/AnimationID.java +++ b/runelite-api/src/main/java/net/runelite/api/AnimationID.java @@ -101,6 +101,7 @@ public final class AnimationID public static final int SMITHING_SMELTING = 899; public static final int SMITHING_CANNONBALL = 827; //cball smithing uses this and SMITHING_SMELTING public static final int SMITHING_ANVIL = 898; + public static final int SMITHING_IMCANDO_HAMMER = 8911; public static final int FISHING_BIG_NET = 620; public static final int FISHING_NET = 621; public static final int FISHING_POLE_CAST = 623; // pole is in the water @@ -205,6 +206,7 @@ public final class AnimationID public static final int LEAGUE_HOME_TELEPORT_6 = 8807; public static final int CONSTRUCTION = 3676; + public static final int CONSTRUCTION_IMCANDO = 8192; public static final int SAND_COLLECTION = 895; public static final int PISCARILIUS_CRANE_REPAIR = 7199; public static final int HOME_MAKE_TABLET = 4067; diff --git a/runelite-api/src/main/java/net/runelite/api/Client.java b/runelite-api/src/main/java/net/runelite/api/Client.java index 2a7a6ca2fe..095af54819 100644 --- a/runelite-api/src/main/java/net/runelite/api/Client.java +++ b/runelite-api/src/main/java/net/runelite/api/Client.java @@ -2174,4 +2174,19 @@ public interface Client extends GameEngine * use createBuffer to create a new byte buffer */ Buffer createBuffer(byte[] initialBytes); + + /** + * Get the list of message ids for the recently received cross-world messages. The upper 32 bits of the + * id is the world id, the lower is a sequence number per-world. + * + * @return + */ + long[] getCrossWorldMessageIds(); + + /** + * Get the index of the next message to be inserted in the cross world message id list + * + * @return + */ + int getCrossWorldMessageIdsIndex(); } diff --git a/runelite-api/src/main/java/net/runelite/api/PlayerComposition.java b/runelite-api/src/main/java/net/runelite/api/PlayerComposition.java index 1512b02f33..37b04762a1 100644 --- a/runelite-api/src/main/java/net/runelite/api/PlayerComposition.java +++ b/runelite-api/src/main/java/net/runelite/api/PlayerComposition.java @@ -38,6 +38,13 @@ public interface PlayerComposition */ boolean isFemale(); + /** + * Get the body part colors for this player composition. + * + * @return an array of the colors, always size 5 + */ + int[] getColors(); + /** * Gets an array of IDs related to equipment slots. *

diff --git a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java index a6a4b1af61..d1fb730f86 100644 --- a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java +++ b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java @@ -433,7 +433,8 @@ public enum WidgetInfo EXPERIENCE_TRACKER_WIDGET(WidgetID.EXPERIENCE_TRACKER_GROUP_ID, WidgetID.ExperienceTracker.WIDGET), EXPERIENCE_TRACKER_BOTTOM_BAR(WidgetID.EXPERIENCE_TRACKER_GROUP_ID, WidgetID.ExperienceTracker.BOTTOM_BAR), - FISHING_TRAWLER_TIMER(WidgetID.FISHING_TRAWLER_GROUP_ID, 14), + FISHING_TRAWLER_CONTRIBUTION(WidgetID.FISHING_TRAWLER_GROUP_ID, 14), + FISHING_TRAWLER_TIMER(WidgetID.FISHING_TRAWLER_GROUP_ID, 15), TITHE_FARM(WidgetID.TITHE_FARM_GROUP_ID, 3), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/achievementdiary/diaries/ArdougneDiaryRequirement.java b/runelite-client/src/main/java/net/runelite/client/plugins/achievementdiary/diaries/ArdougneDiaryRequirement.java index ef9754d6ff..147b0427bb 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/achievementdiary/diaries/ArdougneDiaryRequirement.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/achievementdiary/diaries/ArdougneDiaryRequirement.java @@ -105,7 +105,7 @@ public class ArdougneDiaryRequirement extends GenericDiaryRequirement add("Smith a Dragon sq shield in West Ardougne.", new SkillRequirement(Skill.SMITHING, 60), new QuestRequirement(Quest.LEGENDS_QUEST)); - add("Craft some Death runes.", + add("Craft some Death runes from Essence.", new SkillRequirement(Skill.RUNECRAFT, 65), new QuestRequirement(Quest.MOURNINGS_END_PART_II)); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/achievementdiary/diaries/FaladorDiaryRequirement.java b/runelite-client/src/main/java/net/runelite/client/plugins/achievementdiary/diaries/FaladorDiaryRequirement.java index 30f7843199..fc4efddea2 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/achievementdiary/diaries/FaladorDiaryRequirement.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/achievementdiary/diaries/FaladorDiaryRequirement.java @@ -84,7 +84,7 @@ public class FaladorDiaryRequirement extends GenericDiaryRequirement new SkillRequirement(Skill.MAGIC, 37)); // HARD - add("Craft 140 Mind runes simultaneously.", + add("Craft 140 Mind runes simultaneously from Essence.", new SkillRequirement(Skill.RUNECRAFT, 56)); add("Change your family crest to the Saradomin symbol.", new SkillRequirement(Skill.PRAYER, 70)); @@ -106,7 +106,7 @@ public class FaladorDiaryRequirement extends GenericDiaryRequirement new QuestRequirement(Quest.GRIM_TALES)); // ELITE - add("Craft 252 Air Runes simultaneously.", + add("Craft 252 Air Runes simultaneously from Essence.", new SkillRequirement(Skill.RUNECRAFT, 88)); add("Purchase a White 2h Sword from Sir Vyvin.", new QuestRequirement(Quest.WANTED)); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/achievementdiary/diaries/FremennikDiaryRequirement.java b/runelite-client/src/main/java/net/runelite/client/plugins/achievementdiary/diaries/FremennikDiaryRequirement.java index 46c708efbb..f4a8808322 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/achievementdiary/diaries/FremennikDiaryRequirement.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/achievementdiary/diaries/FremennikDiaryRequirement.java @@ -111,7 +111,7 @@ public class FremennikDiaryRequirement extends GenericDiaryRequirement new QuestRequirement(Quest.THE_GIANT_DWARF, true)); // ELITE - add("Craft 56 astral runes at once.", + add("Craft 56 astral runes at once from Essence.", new SkillRequirement(Skill.RUNECRAFT, 82), new QuestRequirement(Quest.LUNAR_DIPLOMACY)); add("Create a dragonstone amulet in the Neitiznot furnace.", diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/achievementdiary/diaries/KaramjaDiaryRequirement.java b/runelite-client/src/main/java/net/runelite/client/plugins/achievementdiary/diaries/KaramjaDiaryRequirement.java index acdae10ce3..db04602d53 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/achievementdiary/diaries/KaramjaDiaryRequirement.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/achievementdiary/diaries/KaramjaDiaryRequirement.java @@ -96,7 +96,7 @@ public class KaramjaDiaryRequirement extends GenericDiaryRequirement ); // HARD - add("Craft some nature runes.", + add("Craft some nature runes from Essence.", new SkillRequirement(Skill.RUNECRAFT, 44), new QuestRequirement(Quest.RUNE_MYSTERIES)); add("Cook a karambwan thoroughly.", @@ -122,7 +122,7 @@ public class KaramjaDiaryRequirement extends GenericDiaryRequirement new QuestRequirement(Quest.SHILO_VILLAGE)); // ELITE - add("Craft 56 Nature runes at once.", + add("Craft 56 Nature runes at once from Essence.", new SkillRequirement(Skill.RUNECRAFT, 91)); add("Check the health of a palm tree in Brimhaven.", new SkillRequirement(Skill.FARMING, 68)); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/achievementdiary/diaries/KourendDiaryRequirement.java b/runelite-client/src/main/java/net/runelite/client/plugins/achievementdiary/diaries/KourendDiaryRequirement.java index 182b288a3b..2cde458c8e 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/achievementdiary/diaries/KourendDiaryRequirement.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/achievementdiary/diaries/KourendDiaryRequirement.java @@ -110,7 +110,7 @@ public class KourendDiaryRequirement extends GenericDiaryRequirement new QuestRequirement(Quest.DREAM_MENTOR)); //ELITE - add("Craft one or more Blood runes.", + add("Craft one or more Blood runes from Essence.", new SkillRequirement(Skill.RUNECRAFT, 77), new SkillRequirement(Skill.MINING, 38), new SkillRequirement(Skill.CRAFTING, 38), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/achievementdiary/diaries/LumbridgeDiaryRequirement.java b/runelite-client/src/main/java/net/runelite/client/plugins/achievementdiary/diaries/LumbridgeDiaryRequirement.java index 7238e18a55..f729082db2 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/achievementdiary/diaries/LumbridgeDiaryRequirement.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/achievementdiary/diaries/LumbridgeDiaryRequirement.java @@ -43,7 +43,7 @@ public class LumbridgeDiaryRequirement extends GenericDiaryRequirement new SkillRequirement(Skill.SLAYER, 7)); add("Have Sedridor teleport you to the Essence Mine.", new QuestRequirement(Quest.RUNE_MYSTERIES)); - add("Craft some water runes.", + add("Craft some water runes from Essence.", new SkillRequirement(Skill.RUNECRAFT, 5), new QuestRequirement(Quest.RUNE_MYSTERIES)); add("Chop and burn some oak logs in Lumbridge.", @@ -94,7 +94,7 @@ public class LumbridgeDiaryRequirement extends GenericDiaryRequirement add("Squeeze past the jutting wall on your way to the cosmic altar.", new SkillRequirement(Skill.AGILITY, 46), new QuestRequirement(Quest.LOST_CITY)); - add("Craft 56 Cosmic runes simultaneously.", + add("Craft 56 Cosmic runes simultaneously from Essence.", new SkillRequirement(Skill.RUNECRAFT, 59), new QuestRequirement(Quest.LOST_CITY)); add("Travel from Lumbridge to Edgeville on a Waka Canoe.", @@ -128,7 +128,7 @@ public class LumbridgeDiaryRequirement extends GenericDiaryRequirement new SkillRequirement(Skill.WOODCUTTING, 75)); add("Smith an Adamant platebody down Draynor sewer.", new SkillRequirement(Skill.SMITHING, 88)); - add("Craft 140 or more Water runes at once.", + add("Craft 140 or more Water runes at once from Essence.", new SkillRequirement(Skill.RUNECRAFT, 76), new QuestRequirement(Quest.RUNE_MYSTERIES)); } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/achievementdiary/diaries/VarrockDiaryRequirement.java b/runelite-client/src/main/java/net/runelite/client/plugins/achievementdiary/diaries/VarrockDiaryRequirement.java index bff800b4c2..b5c8aeb2db 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/achievementdiary/diaries/VarrockDiaryRequirement.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/achievementdiary/diaries/VarrockDiaryRequirement.java @@ -46,7 +46,7 @@ public class VarrockDiaryRequirement extends GenericDiaryRequirement new SkillRequirement(Skill.AGILITY, 13)); add("Spin a bowl on the pottery wheel and fire it in the oven in Barb Village.", new SkillRequirement(Skill.CRAFTING, 8)); - add("Craft some Earth runes.", + add("Craft some Earth runes from Essence.", new SkillRequirement(Skill.RUNECRAFT, 9)); add("Catch some trout in the River Lum at Barbarian Village.", new SkillRequirement(Skill.FISHING, 20)); @@ -112,7 +112,7 @@ public class VarrockDiaryRequirement extends GenericDiaryRequirement new SkillRequirement(Skill.SMITHING, 89), new SkillRequirement(Skill.FLETCHING, 81), new QuestRequirement(Quest.THE_TOURIST_TRAP)); - add("Craft 100 or more earth runes simultaneously.", + add("Craft 100 or more earth runes simultaneously from Essence.", new SkillRequirement(Skill.RUNECRAFT, 78), new QuestRequirement(Quest.RUNE_MYSTERIES)); } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonPlugin.java index 1f9aeeaa25..535a50b19a 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonPlugin.java @@ -39,6 +39,7 @@ import net.runelite.api.GameObject; import net.runelite.api.GameState; import net.runelite.api.InventoryID; import net.runelite.api.Item; +import net.runelite.api.ItemContainer; import net.runelite.api.ItemID; import static net.runelite.api.ObjectID.CANNON_BASE; import net.runelite.api.Player; @@ -301,6 +302,23 @@ public class CannonPlugin extends Plugin cannonPlaced = true; addCounter(); cballsLeft = 0; + + final ItemContainer inventory = client.getItemContainer(InventoryID.INVENTORY); + if (inventory != null) + { + int invCballs = inventory.count(ItemID.GRANITE_CANNONBALL) > 0 + ? inventory.count(ItemID.GRANITE_CANNONBALL) + : inventory.count(ItemID.CANNONBALL); + // Cannonballs are always forcibly loaded after the furnace is added. If the player has more than + // the max number of cannon balls in their inventory, the cannon will always be fully filled. + // This is preferable to using the proceeding "You load the cannon with x cannon balls" message + // since it will show a lower number of cannon balls if the cannon is already partially-filled + // prior to being placed. + if (invCballs >= MAX_CBALLS) + { + cballsLeft = MAX_CBALLS; + } + } } if (event.getMessage().contains("You pick up the cannon") @@ -366,6 +384,15 @@ public class CannonPlugin extends Plugin } } + if (event.getMessage().startsWith("Your cannon contains")) + { + Matcher m = NUMBER_PATTERN.matcher(event.getMessage()); + if (m.find()) + { + cballsLeft = Integer.parseInt(m.group()); + } + } + if (event.getMessage().startsWith("You unload your cannon and receive Cannonball") || event.getMessage().startsWith("You unload your cannon and receive Granite cannonball")) { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/ClueScrollPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/ClueScrollPlugin.java index d33a0e6b29..8fc616fcde 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/ClueScrollPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/ClueScrollPlugin.java @@ -1156,6 +1156,18 @@ public class ClueScrollPlugin extends Plugin return mapClue.getObjectId() == -1 && itemId == ItemID.SPADE; } + else if (c instanceof SkillChallengeClue) + { + SkillChallengeClue challengeClue = (SkillChallengeClue) c; + + for (ItemRequirement ir : challengeClue.getItemRequirements()) + { + if (ir.fulfilledBy(itemId)) + { + return true; + } + } + } return false; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/SkillChallengeClue.java b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/SkillChallengeClue.java index a36b967fae..e76e588cc5 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/SkillChallengeClue.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/SkillChallengeClue.java @@ -179,7 +179,12 @@ public class SkillChallengeClue extends ClueScroll implements NpcClueScroll, Nam any("", item(ItemID.GRACEFUL_GLOVES), item(ItemID.GRACEFUL_GLOVES_11859), item(ItemID.GRACEFUL_GLOVES_13587), item(ItemID.GRACEFUL_GLOVES_13588), item(ItemID.GRACEFUL_GLOVES_13599), item(ItemID.GRACEFUL_GLOVES_13600), item(ItemID.GRACEFUL_GLOVES_13611), item(ItemID.GRACEFUL_GLOVES_13612), item(ItemID.GRACEFUL_GLOVES_13623), item(ItemID.GRACEFUL_GLOVES_13624), item(ItemID.GRACEFUL_GLOVES_13635), item(ItemID.GRACEFUL_GLOVES_13636), item(ItemID.GRACEFUL_GLOVES_13675), item(ItemID.GRACEFUL_GLOVES_13676), item(ItemID.GRACEFUL_GLOVES_21073), item(ItemID.GRACEFUL_GLOVES_21075), item(ItemID.GRACEFUL_GLOVES_24755), item(ItemID.GRACEFUL_GLOVES_24757), item(ItemID.GRACEFUL_GLOVES_25081), item(ItemID.GRACEFUL_GLOVES_25083)), any("", item(ItemID.GRACEFUL_BOOTS), item(ItemID.GRACEFUL_BOOTS_11861), item(ItemID.GRACEFUL_BOOTS_13589), item(ItemID.GRACEFUL_BOOTS_13590), item(ItemID.GRACEFUL_BOOTS_13601), item(ItemID.GRACEFUL_BOOTS_13602), item(ItemID.GRACEFUL_BOOTS_13613), item(ItemID.GRACEFUL_BOOTS_13614), item(ItemID.GRACEFUL_BOOTS_13625), item(ItemID.GRACEFUL_BOOTS_13626), item(ItemID.GRACEFUL_BOOTS_13637), item(ItemID.GRACEFUL_BOOTS_13638), item(ItemID.GRACEFUL_BOOTS_13677), item(ItemID.GRACEFUL_BOOTS_13678), item(ItemID.GRACEFUL_BOOTS_21076), item(ItemID.GRACEFUL_BOOTS_21078), item(ItemID.GRACEFUL_BOOTS_24758), item(ItemID.GRACEFUL_BOOTS_24760), item(ItemID.GRACEFUL_BOOTS_25084), item(ItemID.GRACEFUL_BOOTS_25086)))), new SkillChallengeClue("Mix an anti-venom potion.", item(ItemID.ANTIDOTE4_5952), xOfItem(ItemID.ZULRAHS_SCALES, 20)), - new SkillChallengeClue("Mine a piece of Runite ore", "mine a piece of runite ore whilst sporting the finest mining gear.", true, ANY_PICKAXE, all("Prospector kit", item(ItemID.PROSPECTOR_HELMET), any("", item(ItemID.PROSPECTOR_JACKET), item(ItemID.VARROCK_ARMOUR_4)), item(ItemID.PROSPECTOR_LEGS), item(ItemID.PROSPECTOR_BOOTS))), + new SkillChallengeClue("Mine a piece of Runite ore", "mine a piece of runite ore whilst sporting the finest mining gear.", true, ANY_PICKAXE, + all("Prospector kit", + any("", item(ItemID.PROSPECTOR_HELMET), item(ItemID.GOLDEN_PROSPECTOR_HELMET)), + any("", item(ItemID.PROSPECTOR_JACKET), item(ItemID.VARROCK_ARMOUR_4), item(ItemID.GOLDEN_PROSPECTOR_JACKET)), + any("", item(ItemID.PROSPECTOR_LEGS), item(ItemID.GOLDEN_PROSPECTOR_LEGS)), + any("", item(ItemID.PROSPECTOR_BOOTS), item(ItemID.GOLDEN_PROSPECTOR_BOOTS)))), new SkillChallengeClue("Steal a gem from the Ardougne market."), new SkillChallengeClue("Pickpocket an elf."), new SkillChallengeClue("Bind a blood rune at the blood altar.", item(ItemID.DARK_ESSENCE_FRAGMENTS)), @@ -188,7 +193,12 @@ public class SkillChallengeClue extends ClueScroll implements NpcClueScroll, Nam new SkillChallengeClue("Cremate a set of fiyr remains.", any("Magic or Redwood Pyre Logs", item(ItemID.MAGIC_PYRE_LOGS), item(ItemID.REDWOOD_PYRE_LOGS)), item(ItemID.TINDERBOX), item(ItemID.FIYR_REMAINS)), new SkillChallengeClue("Dissect a sacred eel.", item(ItemID.KNIFE), any("Fishing rod", item(ItemID.FISHING_ROD), item(ItemID.PEARL_FISHING_ROD)), item(ItemID.FISHING_BAIT)), new SkillChallengeClue("Kill a lizardman shaman."), - new SkillChallengeClue("Catch an Anglerfish.", "angle for an anglerfish whilst sporting the finest fishing gear.", true, any("Fishing rod", item(ItemID.FISHING_ROD), item(ItemID.PEARL_FISHING_ROD)), item(ItemID.SANDWORMS), all("Angler's outfit", item(ItemID.ANGLER_HAT), item(ItemID.ANGLER_TOP), item(ItemID.ANGLER_WADERS), item(ItemID.ANGLER_BOOTS))), + new SkillChallengeClue("Catch an Anglerfish.", "angle for an anglerfish whilst sporting the finest fishing gear.", true, any("Fishing rod", item(ItemID.FISHING_ROD), item(ItemID.PEARL_FISHING_ROD)), item(ItemID.SANDWORMS), + all("Angler's outfit", + any("", item(ItemID.ANGLER_HAT), item(ItemID.SPIRIT_ANGLER_HEADBAND)), + any("", item(ItemID.ANGLER_TOP), item(ItemID.SPIRIT_ANGLER_TOP)), + any("", item(ItemID.ANGLER_WADERS), item(ItemID.SPIRIT_ANGLER_WADERS)), + any("", item(ItemID.ANGLER_BOOTS), item(ItemID.SPIRIT_ANGLER_BOOTS)))), new SkillChallengeClue("Chop a redwood log.", "chop a redwood log whilst sporting the finest lumberjack gear.", true, ANY_AXE, all("Lumberjack outfit", item(ItemID.LUMBERJACK_HAT), item(ItemID.LUMBERJACK_TOP), item(ItemID.LUMBERJACK_LEGS), item(ItemID.LUMBERJACK_BOOTS))), new SkillChallengeClue("Craft a light orb in the Dorgesh-Kaan bank.", item(ItemID.CAVE_GOBLIN_WIRE), item(ItemID.EMPTY_LIGHT_ORB)), new SkillChallengeClue("Kill a reanimated Abyssal Demon.", "kill a reanimated abyssal.", xOfItem(ItemID.SOUL_RUNE, 4), xOfItem(ItemID.BLOOD_RUNE, 1), any("Nature Rune x4", xOfItem(ItemID.NATURE_RUNE, 4), item(ItemID.BRYOPHYTAS_STAFF)), range("Ensouled abyssal head", ItemID.ENSOULED_ABYSSAL_HEAD, ItemID.ENSOULED_ABYSSAL_HEAD_13508)), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingConfig.java index 4aed364903..f0f37fdbc5 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingConfig.java @@ -173,22 +173,22 @@ public interface FishingConfig extends Config @ConfigItem( position = 11, - keyName = "trawlerNotification", - name = "Trawler activity notification", - description = "Send a notification when fishing trawler activity drops below 15%." + keyName = "trawlerTimer", + name = "Trawler timer in M:SS", + description = "Trawler timer will display a more accurate timer in M:SS format." ) - default boolean trawlerNotification() + default boolean trawlerTimer() { return true; } @ConfigItem( position = 12, - keyName = "trawlerTimer", - name = "Trawler timer in MM:SS", - description = "Trawler Timer will display a more accurate timer in MM:SS format." + keyName = "trawlerContribution", + name = "Trawler contribution", + description = "Display the exact number of trawler contribution points gained." ) - default boolean trawlerTimer() + default boolean trawlerContribution() { return true; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingPlugin.java index e782b81428..66e57e7d52 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingPlugin.java @@ -57,7 +57,6 @@ import net.runelite.api.events.InteractingChanged; import net.runelite.api.events.ItemContainerChanged; import net.runelite.api.events.NpcDespawned; import net.runelite.api.events.NpcSpawned; -import net.runelite.api.events.VarbitChanged; import net.runelite.api.events.WidgetLoaded; import net.runelite.api.widgets.Widget; import net.runelite.api.widgets.WidgetID; @@ -86,8 +85,7 @@ public class FishingPlugin extends Plugin { private static final int TRAWLER_SHIP_REGION_NORMAL = 7499; private static final int TRAWLER_SHIP_REGION_SINKING = 8011; - private static final int TRAWLER_TIME_LIMIT_IN_SECONDS = 614; - private static final int TRAWLER_ACTIVITY_THRESHOLD = Math.round(0.15f * 255); + private static final int TRAWLER_TIME_LIMIT_IN_SECONDS = 314; private Instant trawlerStartTime; @@ -124,8 +122,6 @@ public class FishingPlugin extends Plugin @Inject private FishingSpotMinimapOverlay fishingSpotMinimapOverlay; - private boolean trawlerNotificationSent; - @Provides FishingConfig provideConfig(ConfigManager configManager) { @@ -150,7 +146,6 @@ public class FishingPlugin extends Plugin overlayManager.remove(fishingSpotMinimapOverlay); fishingSpots.clear(); minnowSpots.clear(); - trawlerNotificationSent = false; currentSpot = null; trawlerStartTime = null; } @@ -328,10 +323,8 @@ public class FishingPlugin extends Plugin } } - if (config.trawlerTimer()) - { - updateTrawlerTimer(); - } + updateTrawlerTimer(); + updateTrawlerContribution(); } @Subscribe @@ -362,40 +355,42 @@ public class FishingPlugin extends Plugin } } - @Subscribe - public void onVarbitChanged(VarbitChanged event) - { - if (!config.trawlerNotification() || client.getGameState() != GameState.LOGGED_IN) - { - return; - } - - int regionID = client.getLocalPlayer().getWorldLocation().getRegionID(); - - if ((regionID == TRAWLER_SHIP_REGION_NORMAL || regionID == TRAWLER_SHIP_REGION_SINKING) - && client.getVar(Varbits.FISHING_TRAWLER_ACTIVITY) <= TRAWLER_ACTIVITY_THRESHOLD) - { - if (!trawlerNotificationSent) - { - notifier.notify("You have low Fishing Trawler activity!"); - trawlerNotificationSent = true; - } - } - else - { - trawlerNotificationSent = false; - } - } - @Subscribe public void onWidgetLoaded(WidgetLoaded event) { if (event.getGroupId() == WidgetID.FISHING_TRAWLER_GROUP_ID) { trawlerStartTime = Instant.now(); + log.debug("Trawler session started"); } } + /** + * Updates the trawler contribution value + */ + private void updateTrawlerContribution() + { + int regionID = client.getLocalPlayer().getWorldLocation().getRegionID(); + if (regionID != TRAWLER_SHIP_REGION_NORMAL && regionID != TRAWLER_SHIP_REGION_SINKING) + { + return; + } + + if (!config.trawlerContribution()) + { + return; + } + + Widget trawlerContributionWidget = client.getWidget(WidgetInfo.FISHING_TRAWLER_CONTRIBUTION); + if (trawlerContributionWidget == null) + { + return; + } + + int trawlerContribution = client.getVar(Varbits.FISHING_TRAWLER_ACTIVITY); + trawlerContributionWidget.setText("Contribution: " + trawlerContribution); + } + /** * Changes the Fishing Trawler timer widget from minutes to minutes and seconds */ @@ -414,6 +409,11 @@ public class FishingPlugin extends Plugin return; } + if (!config.trawlerTimer()) + { + return; + } + Widget trawlerTimerWidget = client.getWidget(WidgetInfo.FISHING_TRAWLER_TIMER); if (trawlerTimerWidget == null) { @@ -438,7 +438,7 @@ public class FishingPlugin extends Plugin } else { - trawlerText.append("00"); + trawlerText.append('0'); } trawlerText.append(':'); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/friendlist/FriendListConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/friendlist/FriendListConfig.java new file mode 100644 index 0000000000..441820c465 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/friendlist/FriendListConfig.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2021, Maciej + * 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.friendlist; + +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("friendlist") +public interface FriendListConfig extends Config +{ + @ConfigItem( + keyName = "showWorldOnLogin", + name = "Show world on login", + description = "Shows world number on friend login notifications" + ) + default boolean showWorldOnLogin() + { + return false; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/friendlist/FriendListPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/friendlist/FriendListPlugin.java index da491c4c76..da5ebda38b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/friendlist/FriendListPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/friendlist/FriendListPlugin.java @@ -25,19 +25,26 @@ */ package net.runelite.client.plugins.friendlist; +import com.google.inject.Provides; import javax.inject.Inject; +import net.runelite.api.ChatMessageType; +import net.runelite.api.ChatPlayer; import net.runelite.api.Client; import net.runelite.api.Friend; import net.runelite.api.Ignore; +import net.runelite.api.MessageNode; import net.runelite.api.NameableContainer; import net.runelite.api.ScriptID; import net.runelite.api.VarPlayer; +import net.runelite.api.events.ChatMessage; import net.runelite.api.events.ScriptPostFired; import net.runelite.api.widgets.Widget; import net.runelite.api.widgets.WidgetInfo; +import net.runelite.client.config.ConfigManager; import net.runelite.client.eventbus.Subscribe; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.util.Text; @PluginDescriptor( name = "Friend List", @@ -54,6 +61,15 @@ public class FriendListPlugin extends Plugin @Inject private Client client; + @Inject + private FriendListConfig config; + + @Provides + FriendListConfig getConfig(ConfigManager configManager) + { + return configManager.getConfig(FriendListConfig.class); + } + @Override protected void shutDown() { @@ -109,6 +125,25 @@ public class FriendListPlugin extends Plugin } } + @Subscribe + public void onChatMessage(ChatMessage message) + { + if (message.getType() == ChatMessageType.LOGINLOGOUTNOTIFICATION && config.showWorldOnLogin()) + { + MessageNode messageNode = message.getMessageNode(); + // get the player name out of the notification + String name = messageNode.getValue() + .substring(0, messageNode.getValue().indexOf(" ")); + ChatPlayer player = findFriend(name); + + if (player != null && player.getWorld() > 0) + { + messageNode + .setValue(messageNode.getValue() + String.format(" (World %d)", player.getWorld())); + } + } + } + private void setFriendsListTitle(final String title) { Widget friendListTitleWidget = client.getWidget(WidgetInfo.FRIEND_CHAT_TITLE); @@ -126,4 +161,16 @@ public class FriendListPlugin extends Plugin ignoreTitleWidget.setText(title); } } + + private ChatPlayer findFriend(String name) + { + NameableContainer friendContainer = client.getFriendContainer(); + if (friendContainer != null) + { + String cleanName = Text.removeTags(name); + return friendContainer.findByName(cleanName); + } + + return null; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPlugin.java index 6d7dcec442..f25c479d77 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPlugin.java @@ -189,6 +189,7 @@ public class IdleNotifierPlugin extends Plugin case FLETCHING_ATTACH_BOLT_TIPS_TO_DRAGON_BOLT: /* Smithing(Anvil, Furnace, Cannonballs */ case SMITHING_ANVIL: + case SMITHING_IMCANDO_HAMMER: case SMITHING_SMELTING: case SMITHING_CANNONBALL: /* Fishing */ diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/wintertodt/WintertodtPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/wintertodt/WintertodtPlugin.java index 343b55f458..398ee8eb94 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/wintertodt/WintertodtPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/wintertodt/WintertodtPlugin.java @@ -34,6 +34,7 @@ import lombok.AccessLevel; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import static net.runelite.api.AnimationID.CONSTRUCTION; +import static net.runelite.api.AnimationID.CONSTRUCTION_IMCANDO; import static net.runelite.api.AnimationID.FIREMAKING; import static net.runelite.api.AnimationID.FLETCHING_BOW_CUTTING; import static net.runelite.api.AnimationID.IDLE; @@ -436,6 +437,7 @@ public class WintertodtPlugin extends Plugin break; case CONSTRUCTION: + case CONSTRUCTION_IMCANDO: setActivity(WintertodtActivity.FIXING_BRAZIER); break; } 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 index d1135ffe34..44c33c22ab 100644 --- 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 @@ -136,6 +136,17 @@ public interface WorldHopperConfig extends Config return SubscriptionFilterMode.BOTH; } + @ConfigItem( + keyName = "regionFilter", + name = "Filter worlds by region", + description = "Restrict sidebar worlds to one region", + position = 8 + ) + default RegionFilterMode regionFilter() + { + return RegionFilterMode.NONE; + } + @ConfigItem( keyName = "displayPing", name = "Display current ping", 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 index f0d4f88185..6fbd1a4c09 100644 --- 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 @@ -209,7 +209,8 @@ public class WorldHopperPlugin extends Plugin overlayManager.add(worldHopperOverlay); - panel.setFilterMode(config.subscriptionFilter()); + panel.setSubscriptionFilterMode(config.subscriptionFilter()); + panel.setRegionFilterMode(config.regionFilter()); // The plugin has its own executor for pings, as it blocks for a long time hopperExecutorService = new ExecutorServiceExceptionLogger(Executors.newSingleThreadScheduledExecutor()); @@ -272,7 +273,11 @@ public class WorldHopperPlugin extends Plugin } break; case "subscriptionFilter": - panel.setFilterMode(config.subscriptionFilter()); + panel.setSubscriptionFilterMode(config.subscriptionFilter()); + updateList(); + break; + case "regionFilter": + panel.setRegionFilterMode(config.regionFilter()); updateList(); break; } 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 index 12d0a724c3..6f62551cc9 100644 --- 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 @@ -66,7 +66,9 @@ class WorldSwitcherPanel extends PluginPanel private final ArrayList rows = new ArrayList<>(); private final WorldHopperPlugin plugin; @Setter(AccessLevel.PACKAGE) - private SubscriptionFilterMode filterMode; + private SubscriptionFilterMode subscriptionFilterMode; + @Setter(AccessLevel.PACKAGE) + private RegionFilterMode regionFilterMode; WorldSwitcherPanel(WorldHopperPlugin plugin) { @@ -231,7 +233,7 @@ class WorldSwitcherPanel extends PluginPanel { World world = worlds.get(i); - switch (filterMode) + switch (subscriptionFilterMode) { case FREE: if (world.getTypes().contains(WorldType.MEMBERS)) @@ -247,6 +249,11 @@ class WorldSwitcherPanel extends PluginPanel break; } + if (regionFilterMode.getRegion() != null && !regionFilterMode.getRegion().equals(world.getRegion())) + { + continue; + } + rows.add(buildRow(world, i % 2 == 0, world.getId() == plugin.getCurrentWorld() && plugin.getLastWorld() != 0, plugin.isFavorite(world))); } diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/cannon/CannonPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/cannon/CannonPluginTest.java new file mode 100644 index 0000000000..12bc19ead8 --- /dev/null +++ b/runelite-client/src/test/java/net/runelite/client/plugins/cannon/CannonPluginTest.java @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2021, Alexsuperfly + * Copyright (c) 2021, Jordan Atwood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.cannon; + +import com.google.inject.Guice; +import com.google.inject.testing.fieldbinder.Bind; +import com.google.inject.testing.fieldbinder.BoundFieldModule; +import javax.inject.Inject; +import net.runelite.api.ChatMessageType; +import net.runelite.api.Client; +import net.runelite.api.InventoryID; +import net.runelite.api.ItemContainer; +import net.runelite.api.ItemID; +import net.runelite.api.events.ChatMessage; +import net.runelite.client.Notifier; +import net.runelite.client.game.ItemManager; +import net.runelite.client.ui.overlay.OverlayManager; +import net.runelite.client.ui.overlay.infobox.InfoBoxManager; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class CannonPluginTest +{ + + @Inject + private CannonPlugin plugin; + + @Mock + @Bind + private CannonConfig config; + + @Mock + @Bind + private CannonOverlay cannonOverlay; + + @Mock + @Bind + private CannonSpotOverlay cannonSpotOverlay; + + @Mock + @Bind + private InfoBoxManager infoBoxManager; + + @Mock + @Bind + private Notifier notifier; + + @Mock + @Bind + private ItemManager itemManager; + + @Mock + @Bind + private Client client; + + @Mock + @Bind + private OverlayManager overlayManager; + + private static final ChatMessage ADD_FURNACE = new ChatMessage(); + + @BeforeClass + public static void chatMessageSetup() + { + ADD_FURNACE.setType(ChatMessageType.SPAM); + ADD_FURNACE.setMessage("You add the furnace."); + } + + @Before + public void before() + { + Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this); + } + + @Test + public void addWrongTypeOfCannonballs() + { + final ChatMessage message = new ChatMessage(); + message.setType(ChatMessageType.GAMEMESSAGE); + message.setMessage("Your cannon contains 20 x Cannonball.
You can only add cannonballs of the same kind."); + + plugin.onChatMessage(message); + + assertEquals(20, plugin.getCballsLeft()); + } + + @Test + public void addMaxCannonballs() + { + final ItemContainer inventory = mock(ItemContainer.class); + when(client.getItemContainer(InventoryID.INVENTORY)).thenReturn(inventory); + when(inventory.count(ItemID.CANNONBALL)).thenReturn(100); + when(inventory.count(ItemID.GRANITE_CANNONBALL)).thenReturn(0); + + plugin.onChatMessage(ADD_FURNACE); + assertTrue(plugin.isCannonPlaced()); + + assertEquals(30, plugin.getCballsLeft()); + + plugin.onChatMessage(loadCannonballs(30)); + assertEquals(30, plugin.getCballsLeft()); + } + + @Test + public void addNotMaxCannonballs() + { + final ItemContainer inventory = mock(ItemContainer.class); + when(client.getItemContainer(InventoryID.INVENTORY)).thenReturn(inventory); + when(inventory.count(ItemID.GRANITE_CANNONBALL)).thenReturn(12); + + plugin.onChatMessage(ADD_FURNACE); + assertTrue(plugin.isCannonPlaced()); + + assertEquals(0, plugin.getCballsLeft()); + + plugin.onChatMessage(loadCannonballs(12)); + assertEquals(12, plugin.getCballsLeft()); + } + + @Test + public void addReclaimedCannonballs() + { + final ItemContainer inventory = mock(ItemContainer.class); + when(client.getItemContainer(InventoryID.INVENTORY)).thenReturn(inventory); + when(inventory.count(ItemID.CANNONBALL)).thenReturn(1250); + + plugin.onChatMessage(ADD_FURNACE); + assertTrue(plugin.isCannonPlaced()); + + assertEquals(30, plugin.getCballsLeft()); + + plugin.onChatMessage(loadCannonballs(18)); + assertEquals(30, plugin.getCballsLeft()); + } + + private static ChatMessage loadCannonballs(final int numCannonballs) + { + final ChatMessage message = new ChatMessage(); + message.setType(ChatMessageType.GAMEMESSAGE); + + // Cannons use the same chat message for loading cannonballs regardless of whether they're normal or granite. + if (numCannonballs == 1) + { + message.setMessage("You load the cannon with one cannonball."); + } + else + { + message.setMessage(String.format("You load the cannon with %s cannonballs.", numCannonballs)); + } + + return message; + } + +} diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/friendlist/FriendListPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/friendlist/FriendListPluginTest.java new file mode 100644 index 0000000000..4b3fd12f32 --- /dev/null +++ b/runelite-client/src/test/java/net/runelite/client/plugins/friendlist/FriendListPluginTest.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2021, Maciej + * 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.friendlist; + +import com.google.inject.Guice; +import com.google.inject.Inject; +import com.google.inject.testing.fieldbinder.Bind; +import com.google.inject.testing.fieldbinder.BoundFieldModule; +import net.runelite.api.ChatMessageType; +import net.runelite.api.Client; +import net.runelite.api.Friend; +import net.runelite.api.MessageNode; +import net.runelite.api.NameableContainer; +import net.runelite.api.events.ChatMessage; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class FriendListPluginTest +{ + @Mock + @Bind + private Client client; + + @Mock + @Bind + private FriendListConfig config; + + @Inject + private FriendListPlugin friendListPlugin; + + @Before + public void before() + { + Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this); + } + + @Test + public void onChatMessage() + { + when(config.showWorldOnLogin()).thenReturn(true); + + MessageNode messageNode = mock(MessageNode.class); + when(messageNode.getValue()).thenReturn("test\u00a0rsn has logged in."); + + ChatMessage chatMessage = new ChatMessage(); + chatMessage.setType(ChatMessageType.LOGINLOGOUTNOTIFICATION); + chatMessage.setMessageNode(messageNode); + + Friend friend = mock(Friend.class); + when(friend.getWorld()).thenReturn(311); + + NameableContainer friendContainer = mock(NameableContainer.class); + when(friendContainer.findByName("test\u00a0rsn")).thenReturn(friend); + when(client.getFriendContainer()).thenReturn(friendContainer); + + friendListPlugin.onChatMessage(chatMessage); + + verify(messageNode).setValue("test\u00a0rsn has logged in. (World 311)"); + } +} diff --git a/runescape-api/src/main/java/net/runelite/rs/api/RSClient.java b/runescape-api/src/main/java/net/runelite/rs/api/RSClient.java index f2263d79b6..c992630680 100644 --- a/runescape-api/src/main/java/net/runelite/rs/api/RSClient.java +++ b/runescape-api/src/main/java/net/runelite/rs/api/RSClient.java @@ -1406,4 +1406,10 @@ public interface RSClient extends RSGameEngine, Client @Construct RSSceneTilePaint createSceneTilePaint(int swColor, int seColor, int neColor, int nwColor, int texture, int rgb, boolean isFlat); + + @Import("crossWorldMessageIds") + long[] getCrossWorldMessageIds(); + + @Import("crossWorldMessageIdsIndex") + int getCrossWorldMessageIdsIndex(); } diff --git a/runescape-api/src/main/java/net/runelite/rs/api/RSPlayerComposition.java b/runescape-api/src/main/java/net/runelite/rs/api/RSPlayerComposition.java index 88b11cd233..28c0214141 100644 --- a/runescape-api/src/main/java/net/runelite/rs/api/RSPlayerComposition.java +++ b/runescape-api/src/main/java/net/runelite/rs/api/RSPlayerComposition.java @@ -9,7 +9,7 @@ public interface RSPlayerComposition extends PlayerComposition boolean isFemale(); @Import("bodyColors") - int[] getBodyPartColours(); + int[] getColors(); @Import("hash") long getHash(); diff --git a/runescape-client/src/main/java/Client.java b/runescape-client/src/main/java/Client.java index 10466adaf5..839a70eaec 100644 --- a/runescape-client/src/main/java/Client.java +++ b/runescape-client/src/main/java/Client.java @@ -180,7 +180,8 @@ public final class Client extends GameEngine implements Usernamed { @Export("minimapState") static int minimapState; @ObfuscatedName("pt") - static long[] field689; + @Export("crossWorldMessageIds") + static long[] crossWorldMessageIds; @ObfuscatedName("qt") @ObfuscatedGetter( intValue = -163319865 @@ -195,7 +196,8 @@ public final class Client extends GameEngine implements Usernamed { @ObfuscatedGetter( intValue = 1846796199 ) - static int field760; + @Export("crossWorldMessageIdsIndex") + static int crossWorldMessageIdsIndex; @ObfuscatedName("st") @ObfuscatedSignature( descriptor = "Lmi;" @@ -1534,8 +1536,8 @@ public final class Client extends GameEngine implements Usernamed { publicChatMode = 0; // L: 573 tradeChatMode = 0; // L: 575 field824 = ""; // L: 576 - field689 = new long[100]; // L: 578 - field760 = 0; // L: 579 + crossWorldMessageIds = new long[100]; // L: 578 + crossWorldMessageIdsIndex = 0; // L: 579 field827 = 0; // L: 581 field828 = new int[128]; // L: 582 field792 = new int[128]; // L: 583 @@ -3836,7 +3838,7 @@ public final class Client extends GameEngine implements Usernamed { boolean var12 = false; // L: 5460 for (var53 = 0; var53 < 100; ++var53) { // L: 5461 - if (field689[var53] == var10) { // L: 5462 + if (crossWorldMessageIds[var53] == var10) { // L: 5462 var12 = true; // L: 5463 break; // L: 5464 } @@ -3847,8 +3849,8 @@ public final class Client extends GameEngine implements Usernamed { } if (!var12 && field716 == 0) { // L: 5468 - field689[field760] = var10; // L: 5469 - field760 = (field760 + 1) % 100; // L: 5470 + crossWorldMessageIds[crossWorldMessageIdsIndex] = var10; // L: 5469 + crossWorldMessageIdsIndex = (crossWorldMessageIdsIndex + 1) % 100; // L: 5470 var22 = AbstractFont.escapeBrackets(GrandExchangeOfferAgeComparator.method4607(class16.method258(var3))); // L: 5471 byte var55; if (var9.isPrivileged) { // L: 5473 @@ -4379,7 +4381,7 @@ public final class Client extends GameEngine implements Usernamed { var11 = true; } else { for (var53 = 0; var53 < 100; ++var53) { // L: 5866 - if (field689[var53] == var31) { // L: 5867 + if (crossWorldMessageIds[var53] == var31) { // L: 5867 var11 = true; // L: 5868 break; // L: 5869 } @@ -4387,8 +4389,8 @@ public final class Client extends GameEngine implements Usernamed { } if (!var11) { // L: 5873 - field689[field760] = var31; // L: 5874 - field760 = (field760 + 1) % 100; // L: 5875 + crossWorldMessageIds[crossWorldMessageIdsIndex] = var31; // L: 5874 + crossWorldMessageIdsIndex = (crossWorldMessageIdsIndex + 1) % 100; // L: 5875 var22 = class16.method258(var3); // L: 5876 int var54 = var56 >= 0 ? 43 : 46; // L: 5877 Projectile.addChatMessage(var54, "", var22, var33.field25); // L: 5878 @@ -4659,7 +4661,7 @@ public final class Client extends GameEngine implements Usernamed { break; } - if (var39 == field689[var15]) { // L: 6076 + if (var39 == crossWorldMessageIds[var15]) { // L: 6076 var13 = true; // L: 6077 break; // L: 6078 } @@ -4669,8 +4671,8 @@ public final class Client extends GameEngine implements Usernamed { } if (!var13) { // L: 6085 - field689[field760] = var39; // L: 6086 - field760 = (field760 + 1) % 100; // L: 6087 + crossWorldMessageIds[crossWorldMessageIdsIndex] = var39; // L: 6086 + crossWorldMessageIdsIndex = (crossWorldMessageIdsIndex + 1) % 100; // L: 6087 var42 = AbstractFont.escapeBrackets(class16.method258(var3)); // L: 6088 int var16 = var56 >= 0 ? 41 : 44; // L: 6089 if (var74.modIcon != -1) { // L: 6090 @@ -5017,7 +5019,7 @@ public final class Client extends GameEngine implements Usernamed { boolean var14 = false; // L: 6349 for (var15 = 0; var15 < 100; ++var15) { // L: 6350 - if (var29 == field689[var15]) { // L: 6351 + if (var29 == crossWorldMessageIds[var15]) { // L: 6351 var14 = true; // L: 6352 break; // L: 6353 } @@ -5028,8 +5030,8 @@ public final class Client extends GameEngine implements Usernamed { } if (!var14 && field716 == 0) { // L: 6359 - field689[field760] = var29; // L: 6360 - field760 = (field760 + 1) % 100; // L: 6361 + crossWorldMessageIds[crossWorldMessageIdsIndex] = var29; // L: 6360 + crossWorldMessageIdsIndex = (crossWorldMessageIdsIndex + 1) % 100; // L: 6361 var42 = AbstractFont.escapeBrackets(GrandExchangeOfferAgeComparator.method4607(class16.method258(var3))); // L: 6362 if (var34.modIcon != -1) { // L: 6363 Projectile.addChatMessage(9, class337.method5986(var34.modIcon) + var45, var42, Varcs.base37DecodeLong(var20));