Merge remote-tracking branch 'upstream/master' into master

This commit is contained in:
ThatGamerBlue
2021-04-26 17:36:22 +01:00
28 changed files with 553 additions and 80 deletions

View File

@@ -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;

View File

@@ -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();
}

View File

@@ -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.
* <p>

View File

@@ -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),

View File

@@ -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));

View File

@@ -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));

View File

@@ -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.",

View File

@@ -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));

View File

@@ -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),

View File

@@ -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));
}

View File

@@ -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));
}

View File

@@ -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"))
{

View File

@@ -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;
}

View File

@@ -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)),

View File

@@ -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;
}

View File

@@ -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(':');

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) 2021, Maciej <https://github.com/mlewicki12>
* 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;
}
}

View File

@@ -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<Friend> friendContainer = client.getFriendContainer();
if (friendContainer != null)
{
String cleanName = Text.removeTags(name);
return friendContainer.findByName(cleanName);
}
return null;
}
}

View File

@@ -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 */

View File

@@ -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;
}

View File

@@ -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",

View File

@@ -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;
}

View File

@@ -66,7 +66,9 @@ class WorldSwitcherPanel extends PluginPanel
private final ArrayList<WorldTableRow> 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)));
}

View File

@@ -0,0 +1,186 @@
/*
* Copyright (c) 2021, Alexsuperfly <alexsuperfly@users.noreply.github.com>
* Copyright (c) 2021, Jordan Atwood <nightfirecat@protonmail.com>
* 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.<br>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;
}
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright (c) 2021, Maciej <https://github.com/mlewicki12>
* 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<Friend> 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)");
}
}

View File

@@ -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();
}

View File

@@ -9,7 +9,7 @@ public interface RSPlayerComposition extends PlayerComposition
boolean isFemale();
@Import("bodyColors")
int[] getBodyPartColours();
int[] getColors();
@Import("hash")
long getHash();

View File

@@ -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));