Merge remote-tracking branch 'runelite/master'

This commit is contained in:
Owain van Brakel
2021-03-02 13:16:12 +01:00
51 changed files with 2748 additions and 414 deletions

View File

@@ -29,6 +29,7 @@ import java.awt.Polygon;
import java.awt.Shape;
import java.awt.image.BufferedImage;
import javax.annotation.Nullable;
import net.runelite.api.annotations.VisibleForDevtools;
import net.runelite.api.coords.LocalPoint;
import net.runelite.api.coords.WorldArea;
import net.runelite.api.coords.WorldPoint;
@@ -105,10 +106,6 @@ public interface Actor extends Renderable, Locatable
*/
LocalPoint getLocalLocation();
void setIdlePoseAnimation(int animation);
void setPoseAnimation(int animation);
/**
* Get the index of the PoseFrame (the index as it appears in the sequenceDefinition "frames" array).
*/
@@ -138,22 +135,84 @@ public interface Actor extends Renderable, Locatable
int getAnimation();
/**
* Gets the secondary animation the actor is performing.
* Gets the secondary animation the actor is performing. Usually an idle animation, or one of the walking ones.
*
* @return the animation ID
* @see AnimationID
*/
int getPoseAnimation();
@VisibleForDevtools
void setPoseAnimation(int animation);
/**
* If this is equal to the pose animation, the pose animation is ignored when
* you are doing another action.
* The idle pose animation. If the actor is not walking or otherwise animating, this will be used
* for their pose animation.
*
* @return the animation ID
* @see AnimationID
*/
int getIdlePoseAnimation();
@VisibleForDevtools
void setIdlePoseAnimation(int animation);
/**
* Animation used for rotating left if the actor is also not walking
*
* @return the animation ID
* @see AnimationID
*/
int getIdleRotateLeft();
/**
* Animation used for rotating right if the actor is also not walking
*
* @return the animation ID
* @see AnimationID
*/
int getIdleRotateRight();
/**
* Animation used for walking
*
* @return the animation ID
* @see AnimationID
*/
int getWalkAnimation();
/**
* Animation used for rotating left while walking
*
* @return the animation ID
* @see AnimationID
*/
int getWalkRotateLeft();
/**
* Animation used for rotating right while walking
*
* @return the animation ID
* @see AnimationID
*/
int getWalkRotateRight();
/**
* Animation used for an about-face while walking
*
* @return the animation ID
* @see AnimationID
*/
int getWalkRotate180();
/**
* Animation used for running
*
* @return the animation ID
* @see AnimationID
*/
int getRunAnimation();
/**
* Sets an animation for the actor to perform.
*
@@ -291,16 +350,18 @@ public interface Actor extends Renderable, Locatable
int getTurnRightAnimation();
int getWalkAnimation();
// TODO: Remove next major
@Deprecated
int getWalkBackAnimation();
// TODO: Remove next major
@Deprecated
int getWalkLeftAnimation();
// TODO: Remove next major
@Deprecated
int getWalkRightAnimation();
int getRunAnimation();
/**
* Returns true if this NPC has died
*

View File

@@ -395,11 +395,16 @@ public enum Varbits
MULTICOMBAT_AREA(4605),
/**
* Kingdom Management
* Kingdom of Miscellania Management
* Kingdom Approval is represented as a 7-bit unsigned integer; 127 corresponds to 100% approval
*/
KINGDOM_FAVOR(72),
KINGDOM_APPROVAL(72),
KINGDOM_COFFER(74),
// TODO: Remove next major
@Deprecated
KINGDOM_FAVOR(72),
/**
* The Hand in the Sand quest status
*/

View File

@@ -26,6 +26,7 @@ package net.runelite.api.widgets;
import java.awt.Rectangle;
import java.util.Collection;
import javax.annotation.Nullable;
import net.runelite.api.FontTypeFace;
import net.runelite.api.Point;
import net.runelite.api.SpritePixels;
@@ -117,6 +118,7 @@ public interface Widget
/**
* Gets the dynamic children of this widget in a sparse array
*/
@Nullable
Widget[] getChildren();
/**

View File

@@ -53,6 +53,7 @@ dependencies {
compileOnly(group = "net.runelite", name = "orange-extensions", version = "1.0")
implementation(project(":http-api"))
implementation(project(":runelite-jshell"))
implementation(group = "ch.qos.logback", name = "logback-classic", version = "1.2.3")
implementation(group = "com.google.code.gson", name = "gson", version = "2.8.5")
implementation(group = "com.google.guava", name = "guava", version = "23.2-jre")

View File

@@ -51,26 +51,20 @@ import net.runelite.client.util.ReflectUtil;
@ThreadSafe
public class EventBus
{
@FunctionalInterface
public interface SubscriberMethod
{
void invoke(Object event);
}
@Value
private static class Subscriber
public static class Subscriber
{
private final Object object;
private final Method method;
private final float priority;
@EqualsAndHashCode.Exclude
private final SubscriberMethod lamda;
private final Consumer<Object> lambda;
void invoke(final Object arg) throws Exception
{
if (lamda != null)
if (lambda != null)
{
lamda.invoke(arg);
lambda.accept(arg);
}
else
{
@@ -80,7 +74,9 @@ public class EventBus
}
private final Consumer<Throwable> exceptionHandler;
private ImmutableMultimap<Class, Subscriber> subscribers = ImmutableMultimap.of();
@Nonnull
private ImmutableMultimap<Class<?>, Subscriber> subscribers = ImmutableMultimap.of();
/**
* Instantiates EventBus with default exception handler
@@ -99,13 +95,8 @@ public class EventBus
*/
public synchronized void register(@Nonnull final Object object)
{
final ImmutableMultimap.Builder<Class, Subscriber> builder = ImmutableMultimap.builder();
if (subscribers != null)
{
final ImmutableMultimap.Builder<Class<?>, Subscriber> builder = ImmutableMultimap.builder();
builder.putAll(subscribers);
}
builder.orderValuesBy(Comparator.comparing(Subscriber::getPriority).reversed()
.thenComparing(s -> s.object.getClass().getName()));
@@ -138,7 +129,7 @@ public class EventBus
}
method.setAccessible(true);
SubscriberMethod lambda = null;
Consumer<Object> lambda = null;
try
{
@@ -147,14 +138,14 @@ public class EventBus
final MethodHandle target = caller.findVirtual(clazz, method.getName(), subscription);
final CallSite site = LambdaMetafactory.metafactory(
caller,
"invoke",
MethodType.methodType(SubscriberMethod.class, clazz),
"accept",
MethodType.methodType(Consumer.class, clazz),
subscription.changeParameterType(0, Object.class),
target,
subscription);
final MethodHandle factory = site.getTarget();
lambda = (SubscriberMethod) factory.bindTo(object).invokeExact();
lambda = (Consumer<Object>) factory.bindTo(object).invokeExact();
}
catch (Throwable e)
{
@@ -170,6 +161,21 @@ public class EventBus
subscribers = builder.build();
}
public synchronized <T> Subscriber register(Class<T> clazz, Consumer<T> subFn, float priority)
{
final ImmutableMultimap.Builder<Class<?>, Subscriber> builder = ImmutableMultimap.builder();
builder.putAll(subscribers);
builder.orderValuesBy(Comparator.comparing(Subscriber::getPriority).reversed()
.thenComparing(s -> s.object.getClass().getName()));
Subscriber sub = new Subscriber(subFn, null, priority, (Consumer<Object>) subFn);
builder.put(clazz, sub);
subscribers = builder.build();
return sub;
}
/**
* Unregisters all subscribed methods from provided subscriber object.
*
@@ -177,12 +183,7 @@ public class EventBus
*/
public synchronized void unregister(@Nonnull final Object object)
{
if (subscribers == null)
{
return;
}
final Multimap<Class, Subscriber> map = HashMultimap.create();
final Multimap<Class<?>, Subscriber> map = HashMultimap.create();
map.putAll(subscribers);
for (Class<?> clazz = object.getClass(); clazz != null; clazz = clazz.getSuperclass())
@@ -204,6 +205,21 @@ public class EventBus
subscribers = ImmutableMultimap.copyOf(map);
}
public synchronized void unregister(Subscriber sub)
{
if (sub == null)
{
return;
}
final Multimap<Class<?>, Subscriber> map = HashMultimap.create();
map.putAll(subscribers);
map.values().remove(sub);
subscribers = ImmutableMultimap.copyOf(map);
}
/**
* Posts provided event to all registered subscribers. Subscriber calls are invoked immediately,
* ordered by priority then their declaring class' name.

View File

@@ -253,6 +253,22 @@ public enum ItemMapping
ITEM_VOLATILE_ORB(VOLATILE_ORB, VOLATILE_NIGHTMARE_STAFF),
ITEM_NIGHTMARE_STAFF(NIGHTMARE_STAFF, ELDRITCH_NIGHTMARE_STAFF, HARMONISED_NIGHTMARE_STAFF, VOLATILE_NIGHTMARE_STAFF),
// Trouver Parchment
ITEM_TROUVER_PARCHMENT(
TROUVER_PARCHMENT, INFERNAL_MAX_CAPE_L, FIRE_MAX_CAPE_L, ASSEMBLER_MAX_CAPE_L, BRONZE_DEFENDER_L, IRON_DEFENDER_L, STEEL_DEFENDER_L, BLACK_DEFENDER_L, MITHRIL_DEFENDER_L, ADAMANT_DEFENDER_L,
RUNE_DEFENDER_L, DRAGON_DEFENDER_L, DECORATIVE_SWORD_L, DECORATIVE_ARMOUR_L, DECORATIVE_ARMOUR_L_24159, DECORATIVE_HELM_L, DECORATIVE_SHIELD_L, DECORATIVE_ARMOUR_L_24162, DECORATIVE_ARMOUR_L_24163, DECORATIVE_ARMOUR_L_24164,
DECORATIVE_ARMOUR_L_24165, DECORATIVE_ARMOUR_L_24166, DECORATIVE_ARMOUR_L_24167, DECORATIVE_ARMOUR_L_24168, SARADOMIN_HALO_L, ZAMORAK_HALO_L, GUTHIX_HALO_L, HEALER_HAT_L, FIGHTER_HAT_L, RANGER_HAT_L,
FIGHTER_TORSO_L, PENANCE_SKIRT_L, VOID_KNIGHT_TOP_L, ELITE_VOID_TOP_L, VOID_KNIGHT_ROBE_L, ELITE_VOID_ROBE_L, VOID_KNIGHT_MACE_L, VOID_KNIGHT_GLOVES_L, VOID_MAGE_HELM_L, VOID_RANGER_HELM_L,
VOID_MELEE_HELM_L, AVERNIC_DEFENDER_L, ARMADYL_HALO_L, BANDOS_HALO_L, SEREN_HALO_L, ANCIENT_HALO_L, BRASSICA_HALO_L, AVAS_ASSEMBLER_L, FIRE_CAPE_L, INFERNAL_CAPE_L, IMBUED_SARADOMIN_MAX_CAPE_L,
IMBUED_ZAMORAK_MAX_CAPE_L, IMBUED_GUTHIX_MAX_CAPE_L, IMBUED_SARADOMIN_CAPE_L, IMBUED_ZAMORAK_CAPE_L, IMBUED_GUTHIX_CAPE_L, RUNE_POUCH_L, RUNNER_HAT_L, DECORATIVE_BOOTS_L, DECORATIVE_FULL_HELM_L),
ITEM_TROUVER_PARCHMENT_REFUND_LARGE(
COINS_995, 475000L, INFERNAL_MAX_CAPE_L, FIRE_MAX_CAPE_L, ASSEMBLER_MAX_CAPE_L, RUNE_DEFENDER_L, DRAGON_DEFENDER_L, DECORATIVE_SWORD_L, DECORATIVE_ARMOUR_L, DECORATIVE_ARMOUR_L_24159, DECORATIVE_HELM_L, DECORATIVE_SHIELD_L,
DECORATIVE_ARMOUR_L_24162, DECORATIVE_ARMOUR_L_24163, DECORATIVE_ARMOUR_L_24164, DECORATIVE_ARMOUR_L_24165, DECORATIVE_ARMOUR_L_24166, DECORATIVE_ARMOUR_L_24167, DECORATIVE_ARMOUR_L_24168, SARADOMIN_HALO_L,
ZAMORAK_HALO_L, GUTHIX_HALO_L, HEALER_HAT_L, FIGHTER_HAT_L, RANGER_HAT_L, FIGHTER_TORSO_L, PENANCE_SKIRT_L, VOID_KNIGHT_TOP_L, ELITE_VOID_TOP_L, VOID_KNIGHT_ROBE_L, ELITE_VOID_ROBE_L, VOID_KNIGHT_MACE_L,
VOID_KNIGHT_GLOVES_L, VOID_MAGE_HELM_L, VOID_RANGER_HELM_L, VOID_MELEE_HELM_L, AVERNIC_DEFENDER_L, ARMADYL_HALO_L, BANDOS_HALO_L, SEREN_HALO_L, ANCIENT_HALO_L, BRASSICA_HALO_L, AVAS_ASSEMBLER_L,
FIRE_CAPE_L, INFERNAL_CAPE_L, IMBUED_SARADOMIN_MAX_CAPE_L, IMBUED_ZAMORAK_MAX_CAPE_L, IMBUED_GUTHIX_MAX_CAPE_L, IMBUED_SARADOMIN_CAPE_L, IMBUED_ZAMORAK_CAPE_L, IMBUED_GUTHIX_CAPE_L, RUNE_POUCH_L, RUNNER_HAT_L, DECORATIVE_BOOTS_L, DECORATIVE_FULL_HELM_L),
ITEM_TROUVER_PARCHMENT_REFUND_SMALL(COINS_995, 47500L, BRONZE_DEFENDER_L, IRON_DEFENDER_L, STEEL_DEFENDER_L, BLACK_DEFENDER_L, MITHRIL_DEFENDER_L, ADAMANT_DEFENDER_L),
// Crystal items
ITEM_CRYSTAL_TOOL_SEED(CRYSTAL_TOOL_SEED, CRYSTAL_AXE, CRYSTAL_AXE_INACTIVE, CRYSTAL_HARPOON, CRYSTAL_HARPOON_INACTIVE, CRYSTAL_PICKAXE, CRYSTAL_PICKAXE_INACTIVE),
ITEM_CRYSTAL_AXE(DRAGON_AXE, CRYSTAL_AXE, CRYSTAL_AXE_INACTIVE),

View File

@@ -70,7 +70,7 @@ import net.runelite.client.util.QuantityFormatter;
@PluginDescriptor(
name = "Bank",
description = "Modifications to the banking interface",
tags = {"grand", "exchange", "high", "alchemy", "prices", "deposit"}
tags = {"grand", "exchange", "high", "alchemy", "prices", "deposit", "pin"}
)
@Slf4j
public class BankPlugin extends Plugin
@@ -238,12 +238,15 @@ public class BankPlugin extends Plugin
log.debug("Bank pin keypress");
final String input = client.getVar(VarClientStr.CHATBOX_TYPED_TEXT);
final String chatboxTypedText = client.getVar(VarClientStr.CHATBOX_TYPED_TEXT);
final String inputText = client.getVar(VarClientStr.INPUT_TEXT);
clientThread.invokeLater(() ->
{
// reset chatbox input to avoid pin going to chatbox..
client.setVar(VarClientStr.CHATBOX_TYPED_TEXT, input);
client.setVar(VarClientStr.CHATBOX_TYPED_TEXT, chatboxTypedText);
client.runScript(ScriptID.CHAT_PROMPT_INIT);
client.setVar(VarClientStr.INPUT_TEXT, inputText);
client.runScript(ScriptID.CHAT_TEXT_INPUT_REBUILD, "");
client.runScript(onOpListener);
});

View File

@@ -481,12 +481,16 @@ public class ChatCommandsPlugin extends Plugin
advLogLoaded = false;
Widget adventureLog = client.getWidget(WidgetInfo.ADVENTURE_LOG);
if (adventureLog != null)
{
Matcher advLogExploitsText = ADVENTURE_LOG_TITLE_PATTERN.matcher(adventureLog.getChild(ADV_LOG_EXPLOITS_TEXT_INDEX).getText());
if (advLogExploitsText.find())
{
pohOwner = advLogExploitsText.group(1);
}
}
}
if (bossLogLoaded && (pohOwner == null || pohOwner.equals(client.getLocalPlayer().getName())))
{

View File

@@ -172,6 +172,18 @@ public class ChatNotificationsPlugin extends Plugin
notifier.notify(Text.removeTags(chatMessage.getName()) + ": " + chatMessage.getMessage());
}
break;
case PRIVATECHATOUT:
return;
case MODCHAT:
case PUBLICCHAT:
case FRIENDSCHAT:
case AUTOTYPER:
case MODAUTOTYPER:
if (client.getLocalPlayer() != null && Text.toJagexName(Text.removeTags(chatMessage.getName())).equals(client.getLocalPlayer().getName()))
{
return;
}
break;
case CONSOLE:
// Don't notify for notification messages
if (chatMessage.getName().equals(runeliteTitle))

View File

@@ -159,7 +159,7 @@ public class SkillChallengeClue extends ClueScroll implements NpcClueScroll, Nam
new SkillChallengeClue("Craft multiple cosmic runes from a single essence.", item(ItemID.PURE_ESSENCE)),
new SkillChallengeClue("Plant a watermelon seed.", item(ItemID.RAKE), item(ItemID.SEED_DIBBER), xOfItem(ItemID.WATERMELON_SEED, 3)),
new SkillChallengeClue("Activate the Chivalry prayer."),
new SkillChallengeClue("Hand in a Tier 2 or higher set of Shayzien supply armour", "take the lovakengj armourers a boxed set of shayzien supply armour at tier 2 or above.", any("Shayzien Supply Set (Tier 2 or higher)", item(ItemID.SHAYZIEN_SUPPLY_SET_2), item(ItemID.SHAYZIEN_SUPPLY_SET_3), item(ItemID.SHAYZIEN_SUPPLY_SET_4), item(ItemID.SHAYZIEN_SUPPLY_SET_5))),
new SkillChallengeClue("Hand in a Tier 2 or higher set of Shayzien supply armour. (Requires 11 lovakite bars)", "take the lovakengj armourers a boxed set of shayzien supply armour at tier 2 or above.", any("Shayzien Supply Set (Tier 2 or higher)", item(ItemID.SHAYZIEN_SUPPLY_SET_2), item(ItemID.SHAYZIEN_SUPPLY_SET_3), item(ItemID.SHAYZIEN_SUPPLY_SET_4), item(ItemID.SHAYZIEN_SUPPLY_SET_5))),
// Master Sherlock Tasks
new SkillChallengeClue("Equip an abyssal whip in front of the abyssal demons of the Slayer Tower.", true, any("Abyssal Whip", item(ItemID.ABYSSAL_WHIP), item(ItemID.FROZEN_ABYSSAL_WHIP), item(ItemID.VOLCANIC_ABYSSAL_WHIP))),
new SkillChallengeClue("Smith a runite med helm.", item(ItemID.HAMMER), item(ItemID.RUNITE_BAR)),

View File

@@ -62,7 +62,7 @@ public enum HotColdLocation
ASGARNIA_CRAFT_GUILD(new WorldPoint(2917, 3295, 0), ASGARNIA, "Outside the Crafting Guild cow pen.", BRASSICAN_MAGE),
ASGARNIA_RIMMINGTON(new WorldPoint(2976, 3239, 0), ASGARNIA, "In the centre of the Rimmington mine.", BRASSICAN_MAGE),
ASGARNIA_MUDSKIPPER(new WorldPoint(2987, 3110, 0), ASGARNIA, "Mudskipper Point, near the starfish in the south-west corner.", BRASSICAN_MAGE),
ASGARNIA_TROLL(new WorldPoint(2910, 3616, 0), ASGARNIA, "The Troll arena, where the player fights Dad during the Troll Stronghold quest. Bring climbing boots if travelling from Burthorpe.", BRASSICAN_MAGE),
ASGARNIA_TROLL(new WorldPoint(2910, 3615, 0), ASGARNIA, "The Troll arena, where the player fights Dad during the Troll Stronghold quest. Bring climbing boots if travelling from Burthorpe.", BRASSICAN_MAGE),
DESERT_GENIE(new WorldPoint(3359, 2912, 0), DESERT, "West of Nardah genie cave.", BRASSICAN_MAGE),
DESERT_ALKHARID_MINE(new WorldPoint(3279, 3263, 0), DESERT, "West of Al Kharid mine.", BRASSICAN_MAGE),
DESERT_MENAPHOS_GATE(new WorldPoint(3223, 2820, 0), DESERT, "North of Menaphos gate.", BRASSICAN_MAGE),
@@ -117,7 +117,7 @@ public enum HotColdLocation
KARAMJA_MUSA_POINT(new WorldPoint(2913, 3169, 0), KARAMJA, "Musa Point, banana plantation.", BRASSICAN_MAGE),
KARAMJA_BRIMHAVEN_FRUIT_TREE(new WorldPoint(2782, 3215, 0), KARAMJA, "Brimhaven, east of the fruit tree patch.", BRASSICAN_MAGE),
KARAMJA_WEST_BRIMHAVEN(new WorldPoint(2718, 3167, 0), KARAMJA, "West of Brimhaven.", BRASSICAN_MAGE),
KARAMJA_GLIDER(new WorldPoint(2966, 2975, 0), KARAMJA, "West of the gnome glider.", BRASSICAN_MAGE),
KARAMJA_GLIDER(new WorldPoint(2966, 2976, 0), KARAMJA, "West of the gnome glider.", BRASSICAN_MAGE),
KARAMJA_KHARAZI_NE(new WorldPoint(2904, 2925, 0), KARAMJA, "North-eastern part of Kharazi Jungle.", BRASSICAN_MAGE),
KARAMJA_KHARAZI_SW(new WorldPoint(2786, 2899, 0), KARAMJA, "South-western part of Kharazi Jungle.", BRASSICAN_MAGE),
KARAMJA_CRASH_ISLAND(new WorldPoint(2909, 2737, 0), KARAMJA, "Northern part of Crash Island.", BRASSICAN_MAGE),
@@ -146,7 +146,7 @@ public enum HotColdLocation
WESTERN_PROVINCE_PISCATORIS_HUNTER_AREA(new WorldPoint(2359, 3564, 0), WESTERN_PROVINCE, "Eastern part of Piscatoris Hunter area, south-west of the Falconry.", BRASSICAN_MAGE),
WESTERN_PROVINCE_ARANDAR(new WorldPoint(2370, 3319, 0), WESTERN_PROVINCE, "South-west of the crystal gate to Arandar.", ANCIENT_WIZARDS),
WESTERN_PROVINCE_ELF_CAMP_EAST(new WorldPoint(2268, 3242, 0), WESTERN_PROVINCE, "East of Iorwerth Camp.", BRASSICAN_MAGE),
WESTERN_PROVINCE_ELF_CAMP_NW(new WorldPoint(2174, 3280, 0), WESTERN_PROVINCE, "North-west of Iorwerth Camp.", BRASSICAN_MAGE),
WESTERN_PROVINCE_ELF_CAMP_NW(new WorldPoint(2177, 3282, 0), WESTERN_PROVINCE, "North-west of Iorwerth Camp.", BRASSICAN_MAGE),
WESTERN_PROVINCE_LLETYA(new WorldPoint(2337, 3166, 0), WESTERN_PROVINCE, "In Lletya.", BRASSICAN_MAGE),
WESTERN_PROVINCE_TYRAS(new WorldPoint(2206, 3158, 0), WESTERN_PROVINCE, "Near Tyras Camp.", BRASSICAN_MAGE),
WESTERN_PROVINCE_ZULANDRA(new WorldPoint(2196, 3057, 0), WESTERN_PROVINCE, "The northern house at Zul-Andra.", BRASSICAN_MAGE),

View File

@@ -196,7 +196,7 @@ public class CookingPlugin extends Plugin
}
else if (message.startsWith("You accidentally burn")
|| message.startsWith("You burn")
|| message.equals("You burn the mushroom in the fire.")
|| message.startsWith("Unfortunately the Jubbly")
|| message.startsWith("You accidentally spoil"))
{

View File

@@ -80,7 +80,7 @@ public class CrowdsourcingCooking
|| message.startsWith("You roast a")
|| message.startsWith("You spit-roast")
|| message.startsWith("You cook")
|| message.startsWith("You burn")
|| message.equals("You burn the mushroom in the fire.")
|| message.startsWith("Eventually the Jubbly")
|| message.startsWith("Unfortunately the Jubbly")
|| message.startsWith("You accidentally burn")

View File

@@ -28,7 +28,7 @@ import java.awt.Color;
import javax.swing.JButton;
import lombok.Getter;
class DevToolsButton extends JButton
public class DevToolsButton extends JButton
{
@Getter
private boolean active;
@@ -53,4 +53,20 @@ class DevToolsButton extends JButton
setBackground(null);
}
}
void addFrame(DevToolsFrame frame)
{
frame.setDevToolsButton(this);
addActionListener(ev ->
{
if (isActive())
{
frame.close();
}
else
{
frame.open();
}
});
}
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright (c) 2021 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.devtools;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import lombok.AccessLevel;
import lombok.Setter;
import net.runelite.client.ui.ClientUI;
public class DevToolsFrame extends JFrame
{
@Setter(AccessLevel.PACKAGE)
protected DevToolsButton devToolsButton;
public DevToolsFrame()
{
setIconImage(ClientUI.ICON);
setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
addWindowListener(new WindowAdapter()
{
@Override
public void windowClosing(WindowEvent e)
{
close();
devToolsButton.setActive(false);
}
});
}
public void open()
{
setVisible(true);
toFront();
repaint();
}
public void close()
{
setVisible(false);
}
}

View File

@@ -25,6 +25,7 @@
*/
package net.runelite.client.plugins.devtools;
import com.google.inject.ProvisionException;
import java.awt.GridLayout;
import java.awt.TrayIcon;
import java.util.concurrent.ScheduledExecutorService;
@@ -32,6 +33,7 @@ import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import javax.swing.JButton;
import javax.swing.JPanel;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
import net.runelite.api.GameState;
import net.runelite.api.MenuAction;
@@ -44,6 +46,7 @@ import net.runelite.client.ui.overlay.infobox.Counter;
import net.runelite.client.ui.overlay.infobox.InfoBoxManager;
import net.runelite.client.util.ImageUtil;
@Slf4j
class DevToolsPanel extends PluginPanel
{
private final Client client;
@@ -129,30 +132,10 @@ class DevToolsPanel extends PluginPanel
});
container.add(plugin.getWidgetInspector());
plugin.getWidgetInspector().addActionListener((ev) ->
{
if (plugin.getWidgetInspector().isActive())
{
widgetInspector.close();
}
else
{
widgetInspector.open();
}
});
plugin.getWidgetInspector().addFrame(widgetInspector);
container.add(plugin.getVarInspector());
plugin.getVarInspector().addActionListener((ev) ->
{
if (plugin.getVarInspector().isActive())
{
varInspector.close();
}
else
{
varInspector.open();
}
});
plugin.getVarInspector().addFrame(varInspector);
container.add(plugin.getSoundEffects());
@@ -164,17 +147,7 @@ class DevToolsPanel extends PluginPanel
container.add(notificationBtn);
container.add(plugin.getScriptInspector());
plugin.getScriptInspector().addActionListener((ev) ->
{
if (plugin.getScriptInspector().isActive())
{
scriptInspector.close();
}
else
{
scriptInspector.open();
}
});
plugin.getScriptInspector().addFrame(scriptInspector);
final JButton newInfoboxBtn = new JButton("Infobox");
newInfoboxBtn.addActionListener(e ->
@@ -198,22 +171,27 @@ class DevToolsPanel extends PluginPanel
container.add(clearInfoboxBtn);
container.add(plugin.getInventoryInspector());
plugin.getInventoryInspector().addActionListener((ev) ->
{
if (plugin.getInventoryInspector().isActive())
{
inventoryInspector.close();
}
else
{
inventoryInspector.open();
}
});
plugin.getInventoryInspector().addFrame(inventoryInspector);
final JButton disconnectBtn = new JButton("Disconnect");
disconnectBtn.addActionListener(e -> clientThread.invoke(() -> client.setGameState(GameState.CONNECTION_LOST)));
container.add(disconnectBtn);
try
{
ShellFrame sf = plugin.getInjector().getInstance(ShellFrame.class);
container.add(plugin.getShell());
plugin.getShell().addFrame(sf);
}
catch (LinkageError | ProvisionException e)
{
log.debug("Shell is not supported", e);
}
catch (Exception e)
{
log.info("Shell couldn't be loaded", e);
}
return container;
}
}

View File

@@ -143,6 +143,7 @@ public class DevToolsPlugin extends Plugin
private DevToolsButton soundEffects;
private DevToolsButton scriptInspector;
private DevToolsButton inventoryInspector;
private DevToolsButton shell;
private NavigationButton navButton;
@Provides
@@ -187,6 +188,7 @@ public class DevToolsPlugin extends Plugin
soundEffects = new DevToolsButton("Sound Effects");
scriptInspector = new DevToolsButton("Script Inspector");
inventoryInspector = new DevToolsButton("Inventory Inspector");
shell = new DevToolsButton("Shell");
overlayManager.add(overlay);
overlayManager.add(locationOverlay);

View File

@@ -28,8 +28,6 @@ import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
@@ -39,7 +37,6 @@ import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
@@ -65,7 +62,7 @@ import net.runelite.client.ui.ColorScheme;
@Slf4j
@Singleton
class InventoryInspector extends JFrame
class InventoryInspector extends DevToolsFrame
{
private static final int MAX_LOG_ENTRIES = 25;
@@ -80,7 +77,7 @@ class InventoryInspector extends JFrame
private final InventoryDeltaPanel deltaPanel;
@Inject
InventoryInspector(Client client, EventBus eventBus, DevToolsPlugin plugin, ItemManager itemManager, ClientThread clientThread)
InventoryInspector(Client client, EventBus eventBus, ItemManager itemManager, ClientThread clientThread)
{
this.client = client;
this.eventBus = eventBus;
@@ -92,18 +89,6 @@ class InventoryInspector extends JFrame
setTitle("OpenOSRS Inventory Inspector");
setIconImage(ClientUI.ICON);
setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
// Reset highlight on close
addWindowListener(new WindowAdapter()
{
@Override
public void windowClosing(WindowEvent e)
{
close();
plugin.getInventoryInspector().setActive(false);
}
});
tree.setBorder(new EmptyBorder(2, 2, 2, 2));
tree.setRootVisible(false);
tree.setShowsRootHandles(true);
@@ -175,19 +160,19 @@ class InventoryInspector extends JFrame
pack();
}
@Override
public void open()
{
eventBus.register(this);
setVisible(true);
toFront();
repaint();
super.open();
}
@Override
public void close()
{
eventBus.unregister(this);
clearTracker();
setVisible(false);
super.close();
}
@Subscribe

View File

@@ -31,8 +31,6 @@ import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
@@ -42,7 +40,6 @@ import javax.swing.BorderFactory;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
@@ -68,14 +65,13 @@ import static net.runelite.api.widgets.WidgetInfo.TO_GROUP;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.EventBus;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.ui.ClientUI;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.DynamicGridLayout;
import net.runelite.client.ui.FontManager;
import net.runelite.client.util.Text;
@Slf4j
public class ScriptInspector extends JFrame
public class ScriptInspector extends DevToolsFrame
{
// These scripts are the only ones that fire every client tick regardless of location.
private final static String DEFAULT_BLACKLIST = "3174,1004";
@@ -139,28 +135,16 @@ public class ScriptInspector extends JFrame
}
@Inject
ScriptInspector(Client client, EventBus eventBus, DevToolsPlugin plugin, ConfigManager configManager)
ScriptInspector(Client client, EventBus eventBus, ConfigManager configManager)
{
this.eventBus = eventBus;
this.client = client;
this.configManager = configManager;
setTitle("OpenOSRS Script Inspector");
setIconImage(ClientUI.ICON);
setLayout(new BorderLayout());
setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
addWindowListener(new WindowAdapter()
{
@Override
public void windowClosing(WindowEvent e)
{
close();
plugin.getScriptInspector().setActive(false);
}
});
tracker.setLayout(new DynamicGridLayout(0, 1, 0, 3));
final JPanel leftSide = new JPanel();
@@ -344,14 +328,14 @@ public class ScriptInspector extends JFrame
}
}
@Override
public void open()
{
eventBus.register(this);
setVisible(true);
toFront();
repaint();
super.open();
}
@Override
public void close()
{
configManager.setConfiguration("devtools", "highlights",
@@ -360,7 +344,7 @@ public class ScriptInspector extends JFrame
Text.toCSV(Lists.transform(new ArrayList<>(blacklist), String::valueOf)));
currentNode = null;
eventBus.unregister(this);
setVisible(false);
super.close();
}
private void addScriptLog(ScriptTreeNode treeNode)

View File

@@ -0,0 +1,70 @@
/*
* Copyright (c) 2021 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.devtools;
import java.util.concurrent.ScheduledExecutorService;
import javax.inject.Inject;
import javax.inject.Singleton;
import net.runelite.client.RuneLite;
import net.runelite.client.callback.ClientThread;
import net.runelite.jshell.ShellPanel;
@Singleton
class ShellFrame extends DevToolsFrame
{
private final ShellPanel shellPanel;
@Inject
ShellFrame(ClientThread clientThread, ScheduledExecutorService executor)
{
this.shellPanel = new ShellPanel(executor)
{
@Override
protected void invokeOnClientThread(Runnable r)
{
clientThread.invoke(r);
}
};
setContentPane(shellPanel);
setTitle("OpenOSRS Shell");
pack();
}
@Override
public void open()
{
shellPanel.switchContext(RuneLite.getInjector());
super.open();
}
@Override
public void close()
{
super.close();
shellPanel.freeContext();
}
}

View File

@@ -32,15 +32,12 @@ import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
@@ -62,13 +59,12 @@ import net.runelite.api.events.VarbitChanged;
import net.runelite.client.callback.ClientThread;
import net.runelite.client.eventbus.EventBus;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.ui.ClientUI;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.DynamicGridLayout;
import net.runelite.client.ui.FontManager;
@Slf4j
class VarInspector extends JFrame
class VarInspector extends DevToolsFrame
{
@Getter
private enum VarType
@@ -106,28 +102,16 @@ class VarInspector extends JFrame
private Map<Integer, Object> varcs = null;
@Inject
VarInspector(Client client, ClientThread clientThread, EventBus eventBus, DevToolsPlugin plugin)
VarInspector(Client client, ClientThread clientThread, EventBus eventBus)
{
this.client = client;
this.clientThread = clientThread;
this.eventBus = eventBus;
setTitle("OpenOSRS Var Inspector");
setIconImage(ClientUI.ICON);
setLayout(new BorderLayout());
setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
addWindowListener(new WindowAdapter()
{
@Override
public void windowClosing(WindowEvent e)
{
close();
plugin.getVarInspector().setActive(false);
}
});
tracker.setLayout(new DynamicGridLayout(0, 1, 0, 3));
final JPanel trackerWrapper = new JPanel();
@@ -332,6 +316,7 @@ class VarInspector extends JFrame
}
}
@Override
public void open()
{
if (oldVarps == null)
@@ -361,16 +346,15 @@ class VarInspector extends JFrame
});
eventBus.register(this);
setVisible(true);
toFront();
repaint();
super.open();
}
@Override
public void close()
{
super.close();
tracker.removeAll();
eventBus.unregister(this);
setVisible(false);
varcs = null;
varbits = null;
}

View File

@@ -32,8 +32,6 @@ import com.google.inject.Singleton;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Collection;
import java.util.Comparator;
import java.util.Enumeration;
@@ -43,7 +41,6 @@ import java.util.Stack;
import java.util.stream.Stream;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
@@ -73,13 +70,12 @@ import net.runelite.api.widgets.WidgetType;
import net.runelite.client.callback.ClientThread;
import net.runelite.client.eventbus.EventBus;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.ui.ClientUI;
import net.runelite.client.ui.overlay.OverlayManager;
import net.runelite.client.util.ColorUtil;
@Slf4j
@Singleton
class WidgetInspector extends JFrame
class WidgetInspector extends DevToolsFrame
{
private static final Map<Integer, WidgetInfo> widgetIdMap = new HashMap<>();
@@ -123,7 +119,6 @@ class WidgetInspector extends JFrame
ClientThread clientThread,
WidgetInfoTableModel infoTableModel,
DevToolsConfig config,
DevToolsPlugin plugin,
EventBus eventBus,
Provider<WidgetInspectorOverlay> overlay,
OverlayManager overlayManager)
@@ -138,18 +133,6 @@ class WidgetInspector extends JFrame
eventBus.register(this);
setTitle("OpenOSRS Widget Inspector");
setIconImage(ClientUI.ICON);
// Reset highlight on close
addWindowListener(new WindowAdapter()
{
@Override
public void windowClosing(WindowEvent e)
{
close();
plugin.getWidgetInspector().setActive(false);
}
});
setLayout(new BorderLayout());
@@ -402,21 +385,21 @@ class WidgetInspector extends JFrame
return widgetIdMap.get(packedId);
}
@Override
public void open()
{
setVisible(true);
toFront();
repaint();
super.open();
overlayManager.add(this.overlay.get());
clientThread.invokeLater(this::addPickerWidget);
}
@Override
public void close()
{
overlayManager.remove(this.overlay.get());
clientThread.invokeLater(this::removePickerWidget);
setSelectedWidget(null, -1, false);
setVisible(false);
super.close();
}
private void removePickerWidget()

View File

@@ -72,11 +72,22 @@ public interface DiscordConfig extends Config
return 5;
}
@ConfigItem(
keyName = "showMainMenu",
name = "Main Menu",
description = "Show status when in main menu",
position = 3
)
default boolean showMainMenu()
{
return true;
}
@ConfigItem(
keyName = "showSkillActivity",
name = "Skilling",
description = "Show your activity while training skills",
position = 3
position = 4
)
default boolean showSkillingActivity()
{
@@ -87,7 +98,7 @@ public interface DiscordConfig extends Config
keyName = "showBossActivity",
name = "Bosses",
description = "Show your activity and location while at bosses",
position = 4
position = 5
)
default boolean showBossActivity()
{
@@ -98,7 +109,7 @@ public interface DiscordConfig extends Config
keyName = "showCityActivity",
name = "Cities",
description = "Show your activity and location while in cities",
position = 5
position = 6
)
default boolean showCityActivity()
{
@@ -109,7 +120,7 @@ public interface DiscordConfig extends Config
keyName = "showDungeonActivity",
name = "Dungeons",
description = "Show your activity and location while in dungeons",
position = 6
position = 7
)
default boolean showDungeonActivity()
{
@@ -120,7 +131,7 @@ public interface DiscordConfig extends Config
keyName = "showMinigameActivity",
name = "Minigames",
description = "Show your activity and location while in minigames",
position = 7
position = 8
)
default boolean showMinigameActivity()
{
@@ -131,7 +142,7 @@ public interface DiscordConfig extends Config
keyName = "showRaidingActivity",
name = "Raids",
description = "Show your activity and location while in Raids",
position = 8
position = 9
)
default boolean showRaidingActivity()
{
@@ -142,7 +153,7 @@ public interface DiscordConfig extends Config
keyName = "showRegionsActivity",
name = "Regions",
description = "Show your activity and location while in other regions",
position = 9
position = 10
)
default boolean showRegionsActivity()
{

View File

@@ -358,10 +358,15 @@ public class DiscordPlugin extends Plugin
private void checkForGameStateUpdate()
{
discordState.triggerEvent(client.getGameState() == GameState.LOGGED_IN
final boolean isLoggedIn = client.getGameState() == GameState.LOGGED_IN;
if (config.showMainMenu() || isLoggedIn)
{
discordState.triggerEvent(isLoggedIn
? DiscordGameEventType.IN_GAME
: DiscordGameEventType.IN_MENU);
}
}
private void checkForAreaUpdate()
{

View File

@@ -289,12 +289,18 @@ public class FriendsChatPlugin extends Plugin
{
chatTitleWidget.setText(TITLE + " (" + friendsChatManager.getCount() + "/100)");
}
else if (config.recentChats() && chatList.getChildren() == null && !Strings.isNullOrEmpty(owner.getText()))
else if (chatList.getChildren() == null && !Strings.isNullOrEmpty(owner.getText()))
{
if (config.recentChats())
{
chatTitleWidget.setText(RECENT_TITLE);
loadFriendsChats();
}
else
{
chatTitleWidget.setText(TITLE);
}
}
}
if (!config.showJoinLeave())

View File

@@ -31,9 +31,13 @@ import net.runelite.client.config.Config;
import net.runelite.client.config.ConfigGroup;
import net.runelite.client.config.ConfigItem;
@ConfigGroup("groundMarker")
@ConfigGroup(GroundMarkerConfig.GROUND_MARKER_CONFIG_GROUP)
public interface GroundMarkerConfig extends Config
{
String GROUND_MARKER_CONFIG_GROUP = "groundMarker";
String SHOW_IMPORT_EXPORT_KEY_NAME = "showImportExport";
String SHOW_CLEAR_KEY_NAME = "showClear";
@Alpha
@ConfigItem(
keyName = "markerColor",
@@ -64,4 +68,24 @@ public interface GroundMarkerConfig extends Config
{
return false;
}
@ConfigItem(
keyName = SHOW_IMPORT_EXPORT_KEY_NAME,
name = "Show Import/Export options",
description = "Show the Import/Export options on the minimap right-click menu"
)
default boolean showImportExport()
{
return true;
}
@ConfigItem(
keyName = SHOW_CLEAR_KEY_NAME,
name = "Show Clear option",
description = "Show the Clear option on the minimap right-click menu, which deletes all currently loaded markers"
)
default boolean showClear()
{
return false;
}
}

View File

@@ -54,6 +54,7 @@ import net.runelite.api.events.MenuOptionClicked;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.EventBus;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.events.ConfigChanged;
import net.runelite.client.game.chatbox.ChatboxPanelManager;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
@@ -190,7 +191,14 @@ public class GroundMarkerPlugin extends Plugin
{
overlayManager.add(overlay);
overlayManager.add(minimapOverlay);
sharingManager.addMenuOptions();
if (config.showImportExport())
{
sharingManager.addImportExportMenuOptions();
}
if (config.showClear())
{
sharingManager.addClearMenuOption();
}
loadPoints();
eventBus.register(sharingManager);
}
@@ -280,6 +288,27 @@ public class GroundMarkerPlugin extends Plugin
}
}
@Subscribe
public void onConfigChanged(ConfigChanged event)
{
if (event.getGroup().equals(GroundMarkerConfig.GROUND_MARKER_CONFIG_GROUP)
&& (event.getKey().equals(GroundMarkerConfig.SHOW_IMPORT_EXPORT_KEY_NAME)
|| event.getKey().equals(GroundMarkerConfig.SHOW_CLEAR_KEY_NAME)))
{
// Maintain consistent menu option order by removing everything then adding according to config
sharingManager.removeMenuOptions();
if (config.showImportExport())
{
sharingManager.addImportExportMenuOptions();
}
if (config.showClear())
{
sharingManager.addClearMenuOption();
}
}
}
private void markTile(LocalPoint localPoint)
{
if (localPoint == null)

View File

@@ -59,6 +59,7 @@ class GroundMarkerSharingManager
{
private static final WidgetMenuOption EXPORT_MARKERS_OPTION = new WidgetMenuOption("Export", "Ground Markers", WORLD_MAP_OPTION);
private static final WidgetMenuOption IMPORT_MARKERS_OPTION = new WidgetMenuOption("Import", "Ground Markers", WORLD_MAP_OPTION);
private static final WidgetMenuOption CLEAR_MARKERS_OPTION = new WidgetMenuOption("Clear", "Ground Markers", WORLD_MAP_OPTION);
private final GroundMarkerPlugin plugin;
private final Client client;
@@ -79,16 +80,22 @@ class GroundMarkerSharingManager
this.gson = gson;
}
void addMenuOptions()
void addImportExportMenuOptions()
{
menuManager.addManagedCustomMenu(EXPORT_MARKERS_OPTION);
menuManager.addManagedCustomMenu(IMPORT_MARKERS_OPTION);
}
void addClearMenuOption()
{
menuManager.addManagedCustomMenu(CLEAR_MARKERS_OPTION);
}
void removeMenuOptions()
{
menuManager.removeManagedCustomMenu(EXPORT_MARKERS_OPTION);
menuManager.removeManagedCustomMenu(IMPORT_MARKERS_OPTION);
menuManager.removeManagedCustomMenu(CLEAR_MARKERS_OPTION);
}
private boolean widgetMenuClickedEquals(final WidgetMenuOptionClicked event, final WidgetMenuOption target)
@@ -114,6 +121,10 @@ class GroundMarkerSharingManager
{
promptForImport();
}
else if (widgetMenuClickedEquals(event, CLEAR_MARKERS_OPTION))
{
promptForClear();
}
}
private void exportGroundMarkers()
@@ -233,6 +244,41 @@ class GroundMarkerSharingManager
sendChatMessage(importPoints.size() + " ground markers were imported from the clipboard.");
}
private void promptForClear()
{
int[] regions = client.getMapRegions();
if (regions == null)
{
return;
}
long numActivePoints = Arrays.stream(regions)
.mapToLong(regionId -> plugin.getPoints(regionId).size())
.sum();
if (numActivePoints == 0)
{
sendChatMessage("You have no ground markers to clear.");
return;
}
chatboxPanelManager.openTextMenuInput("Are you sure you want to clear the<br>" + numActivePoints + " currently loaded ground markers?")
.option("Yes", () ->
{
for (int regionId : regions)
{
plugin.savePoints(regionId, null);
}
plugin.loadPoints();
sendChatMessage(numActivePoints + " ground marker"
+ (numActivePoints == 1 ? " was cleared." : "s were cleared."));
})
.option("No", Runnables::doNothing)
.build();
}
private void sendChatMessage(final String message)
{
chatMessageManager.queue(QueuedMessage.builder()

View File

@@ -34,6 +34,7 @@ import net.runelite.client.ui.overlay.OverlayManager;
@PluginDescriptor(
name = "Item Identification",
description = "Show identifying text over items with difficult to distinguish sprites",
tags = {"abbreviations", "labels", "seeds", "herbs", "saplings", "seedlings"},
enabledByDefault = false
)
public class ItemIdentificationPlugin extends Plugin

View File

@@ -0,0 +1,77 @@
/*
* Copyright (c) 2020, Brandt Hill <https://github.com/BrandtHill>
* 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.kingdomofmiscellania;
import net.runelite.client.config.Config;
import net.runelite.client.config.ConfigGroup;
import net.runelite.client.config.ConfigItem;
import net.runelite.client.config.Range;
@ConfigGroup(KingdomConfig.CONFIG_GROUP_NAME)
public interface KingdomConfig extends Config
{
String CONFIG_GROUP_NAME = "kingdomofmiscellania";
int MAX_COFFER = 7_500_000;
int MAX_APPROVAL_PERCENT = 100;
@ConfigItem(
position = 1,
keyName = "sendNotifications",
name = "Send Notifications",
description = "Send chat notifications upon login showing current estimated coffer and approval"
)
default boolean shouldSendNotifications()
{
return false;
}
@Range(
max = MAX_COFFER
)
@ConfigItem(
position = 2,
keyName = "cofferThreshold",
name = "Coffer Threshold",
description = "Send notifications if coffer is below this value"
)
default int getCofferThreshold()
{
return MAX_COFFER;
}
@Range(
max = MAX_APPROVAL_PERCENT
)
@ConfigItem(
position = 3,
keyName = "approvalThreshold",
name = "Approval Threshold",
description = "Send notifications if approval percentage is below this value"
)
default int getApprovalThreshold()
{
return MAX_APPROVAL_PERCENT;
}
}

View File

@@ -34,20 +34,20 @@ public class KingdomCounter extends Counter
KingdomCounter(BufferedImage image, KingdomPlugin plugin)
{
super(image, plugin, plugin.getFavor());
super(image, plugin, plugin.getApproval());
this.plugin = plugin;
}
@Override
public String getText()
{
return KingdomPlugin.getFavorPercent(plugin.getFavor()) + "%";
return KingdomPlugin.getApprovalPercent(plugin.getApproval()) + "%";
}
@Override
public String getTooltip()
{
return "Favor: " + plugin.getFavor() + "/127" + "</br>"
return "Approval: " + plugin.getApproval() + "/" + KingdomPlugin.MAX_APPROVAL + "</br>"
+ "Coffer: " + QuantityFormatter.quantityToStackSize(plugin.getCoffer());
}
}

View File

@@ -25,32 +25,55 @@
package net.runelite.client.plugins.kingdomofmiscellania;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Provides;
import java.time.Duration;
import java.time.Instant;
import javax.inject.Inject;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.ChatMessageType;
import net.runelite.api.Client;
import net.runelite.api.GameState;
import static net.runelite.api.ItemID.TEAK_CHEST;
import net.runelite.api.Quest;
import net.runelite.api.QuestState;
import net.runelite.api.VarPlayer;
import net.runelite.api.Varbits;
import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.GameTick;
import net.runelite.api.events.VarbitChanged;
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.game.ItemManager;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.ui.overlay.infobox.InfoBoxManager;
import net.runelite.client.util.QuantityFormatter;
@PluginDescriptor(
name = "Kingdom of Miscellania",
description = "Show amount of favor when inside Miscellania",
tags = {"favor", "favour", "managing", "overlay"},
description = "Show amount of approval when inside Miscellania",
tags = {"favor", "favour", "managing", "overlay", "approval", "coffer"},
enabledByDefault = false
)
@Slf4j
public class KingdomPlugin extends Plugin
{
private static final ImmutableSet<Integer> KINGDOM_REGION = ImmutableSet.of(10044, 10300);
private static final String CONFIG_LAST_CHANGED_KEY = "lastChanged";
private static final String CONFIG_COFFER_KEY = "coffer";
private static final String CONFIG_APPROVAL_KEY = "approval";
private static final String CHAT_MESSAGE_FORMAT = "Your Kingdom of Miscellania approval is %d%%, and your coffer has %s coins.";
private static final int MAX_WITHDRAWAL_BASE = 50_000;
private static final int MAX_WITHDRAWAL_ROYAL_TROUBLE = 75_000;
private static final float APPROVAL_DECREMENT_BASE = 0.025f;
private static final float APPROVAL_DECREMENT_ROYAL_TROUBLE = 0.010f;
static final int MAX_APPROVAL = 127;
private boolean loggingIn;
@Inject
private Client client;
@@ -61,8 +84,14 @@ public class KingdomPlugin extends Plugin
@Inject
private ItemManager itemManager;
@Getter
private int favor = 0, coffer = 0;
@Inject
private KingdomConfig config;
@Inject
private ConfigManager configManager;
@Inject
private ChatMessageManager chatMessageManager;
private KingdomCounter counter;
@@ -72,11 +101,28 @@ public class KingdomPlugin extends Plugin
removeKingdomInfobox();
}
@Provides
KingdomConfig getConfig(ConfigManager configManager)
{
return configManager.getConfig(KingdomConfig.class);
}
@Subscribe
public void onVarbitChanged(VarbitChanged event)
{
favor = client.getVar(Varbits.KINGDOM_FAVOR);
coffer = client.getVar(Varbits.KINGDOM_COFFER);
final int coffer = client.getVar(Varbits.KINGDOM_COFFER);
final int approval = client.getVar(Varbits.KINGDOM_APPROVAL);
if (client.getGameState() == GameState.LOGGED_IN
&& isThroneOfMiscellaniaCompleted()
&& (isInKingdom() || coffer > 0 && approval > 0)
&& (getCoffer() != coffer || getApproval() != approval))
{
setLastChanged(Instant.now());
setCoffer(coffer);
setApproval(approval);
}
processInfobox();
}
@@ -87,11 +133,25 @@ public class KingdomPlugin extends Plugin
{
processInfobox();
}
else if (event.getGameState() == GameState.LOGGING_IN)
{
loggingIn = true;
}
}
@Subscribe
public void onGameTick(GameTick gameTick)
{
if (loggingIn)
{
loggingIn = false;
createNotification();
}
}
private void processInfobox()
{
if (client.getGameState() == GameState.LOGGED_IN && hasCompletedQuest() && isInKingdom())
if (client.getGameState() == GameState.LOGGED_IN && isThroneOfMiscellaniaCompleted() && isInKingdom())
{
addKingdomInfobox();
}
@@ -99,7 +159,33 @@ public class KingdomPlugin extends Plugin
{
removeKingdomInfobox();
}
}
private void createNotification()
{
if (!config.shouldSendNotifications() || !isThroneOfMiscellaniaCompleted())
{
return;
}
if (getLastChanged() == null)
{
log.debug("Kingdom Of Miscellania values not yet set. Visit Miscellania to automatically set values.");
return;
}
Instant lastChanged = getLastChanged();
int lastCoffer = getCoffer();
int lastApproval = getApproval();
int estimatedCoffer = estimateCoffer(lastChanged, lastCoffer);
int estimatedApproval = estimateApproval(lastChanged, lastApproval);
if (estimatedCoffer < config.getCofferThreshold() || getApprovalPercent(estimatedApproval) < config.getApprovalThreshold())
{
sendChatMessage(String.format(
CHAT_MESSAGE_FORMAT,
getApprovalPercent(estimatedApproval),
QuantityFormatter.quantityToStackSize(estimatedCoffer)));
}
}
private void addKingdomInfobox()
@@ -122,20 +208,92 @@ public class KingdomPlugin extends Plugin
}
}
private int estimateCoffer(Instant lastChanged, int lastCoffer)
{
int daysSince = (int) Duration.between(lastChanged, Instant.now()).toDays();
int maxDailyWithdrawal = isRoyalTroubleCompleted() ? MAX_WITHDRAWAL_ROYAL_TROUBLE : MAX_WITHDRAWAL_BASE;
int maxDailyThreshold = maxDailyWithdrawal * 10;
for (int i = 0; i < daysSince; i++)
{
lastCoffer -= (lastCoffer > maxDailyThreshold) ? maxDailyWithdrawal : lastCoffer / 10;
}
return lastCoffer;
}
private int estimateApproval(Instant lastChanged, int lastApproval)
{
int daysSince = (int) Duration.between(lastChanged, Instant.now()).toDays();
float dailyPercentage = isRoyalTroubleCompleted() ? APPROVAL_DECREMENT_ROYAL_TROUBLE : APPROVAL_DECREMENT_BASE;
lastApproval -= (int) (daysSince * dailyPercentage * MAX_APPROVAL);
return Math.max(lastApproval, 0);
}
private boolean isInKingdom()
{
return client.getLocalPlayer() != null
&& KINGDOM_REGION.contains(client.getLocalPlayer().getWorldLocation().getRegionID());
}
private boolean hasCompletedQuest()
private boolean isThroneOfMiscellaniaCompleted()
{
return client.getVar(VarPlayer.THRONE_OF_MISCELLANIA) > 0;
}
static int getFavorPercent(int favor)
private boolean isRoyalTroubleCompleted()
{
return (favor * 100) / 127;
return Quest.ROYAL_TROUBLE.getState(client) == QuestState.FINISHED;
}
static int getApprovalPercent(int approval)
{
return (approval * 100) / MAX_APPROVAL;
}
private void sendChatMessage(String chatMessage)
{
final String message = new ChatMessageBuilder()
.append(ChatColorType.HIGHLIGHT)
.append(chatMessage)
.build();
chatMessageManager.queue(
QueuedMessage.builder()
.type(ChatMessageType.CONSOLE)
.runeLiteFormattedMessage(message)
.build());
}
private Instant getLastChanged()
{
return configManager.getRSProfileConfiguration(KingdomConfig.CONFIG_GROUP_NAME, CONFIG_LAST_CHANGED_KEY, Instant.class);
}
private void setLastChanged(Instant lastChanged)
{
configManager.setRSProfileConfiguration(KingdomConfig.CONFIG_GROUP_NAME, CONFIG_LAST_CHANGED_KEY, lastChanged);
}
int getCoffer()
{
Integer coffer = configManager.getRSProfileConfiguration(KingdomConfig.CONFIG_GROUP_NAME, CONFIG_COFFER_KEY, int.class);
return coffer == null ? 0 : coffer;
}
private void setCoffer(int coffer)
{
configManager.setRSProfileConfiguration(KingdomConfig.CONFIG_GROUP_NAME, CONFIG_COFFER_KEY, coffer);
}
int getApproval()
{
Integer approval = configManager.getRSProfileConfiguration(KingdomConfig.CONFIG_GROUP_NAME, CONFIG_APPROVAL_KEY, int.class);
return approval == null ? 0 : approval;
}
private void setApproval(int approval)
{
configManager.setRSProfileConfiguration(KingdomConfig.CONFIG_GROUP_NAME, CONFIG_APPROVAL_KEY, approval);
}
}

View File

@@ -557,13 +557,18 @@ public class MusicPlugin extends Plugin
return;
}
for (Widget w : track.getChildren())
Widget[] trackChildren = track.getChildren();
if (trackChildren != null)
{
for (Widget w : trackChildren)
{
if (w != null)
{
w.setAction(0, null);
}
}
}
handle.setOnVarTransmitListener((Object[]) null);
handle.setDragParent(track);
@@ -588,7 +593,11 @@ public class MusicPlugin extends Plugin
public void shutDown()
{
super.shutDown();
if (this.handle != null)
{
handle.setSpriteId(SpriteID.SETTINGS_SLIDER_HANDLE_BLUE);
}
this.icon.setOnOpListener((Object[]) null);

View File

@@ -57,6 +57,7 @@ import net.runelite.api.events.GameTick;
import net.runelite.api.events.GraphicsObjectCreated;
import net.runelite.api.events.MenuEntryAdded;
import net.runelite.api.events.MenuOptionClicked;
import net.runelite.api.events.NpcChanged;
import net.runelite.api.events.NpcDespawned;
import net.runelite.api.events.NpcSpawned;
import net.runelite.client.callback.ClientThread;
@@ -409,6 +410,26 @@ public class NpcIndicatorsPlugin extends Plugin
highlightedNpcs.remove(npc);
}
@Subscribe
public void onNpcChanged(NpcChanged event)
{
final NPC npc = event.getNpc();
final String npcName = npc.getName();
highlightedNpcs.remove(npc);
if (npcName == null)
{
return;
}
if (npcTags.contains(npc.getIndex())
|| highlightMatchesNPCName(npcName))
{
highlightedNpcs.add(npc);
}
}
@Subscribe
public void onGraphicsObjectCreated(GraphicsObjectCreated event)
{

View File

@@ -32,9 +32,21 @@ import net.runelite.client.config.ConfigGroup;
import net.runelite.client.config.ConfigItem;
import net.runelite.client.config.Units;
@ConfigGroup("slayer")
@ConfigGroup(SlayerConfig.GROUP_NAME)
public interface SlayerConfig extends Config
{
String GROUP_NAME = "slayer";
// Key names for stored task values
String TASK_NAME_KEY = "taskName";
String AMOUNT_KEY = "amount";
String INIT_AMOUNT_KEY = "initialAmount";
String TASK_LOC_KEY = "taskLocation";
String STREAK_KEY = "streak";
String POINTS_KEY = "points";
String EXPEDITIOUS_CHARGES_KEY = "expeditious";
String SLAUGHTER_CHARGES_KEY = "slaughter";
@ConfigItem(
position = 1,
keyName = "infobox",
@@ -124,148 +136,4 @@ public interface SlayerConfig extends Config
{
return true;
}
// Stored data
@ConfigItem(
keyName = "taskName",
name = "",
description = "",
hidden = true
)
default String taskName()
{
return "";
}
@ConfigItem(
keyName = "taskName",
name = "",
description = ""
)
void taskName(String key);
@ConfigItem(
keyName = "amount",
name = "",
description = "",
hidden = true
)
default int amount()
{
return -1;
}
@ConfigItem(
keyName = "amount",
name = "",
description = ""
)
void amount(int amt);
@ConfigItem(
keyName = "initialAmount",
name = "",
description = "",
hidden = true
)
default int initialAmount()
{
return -1;
}
@ConfigItem(
keyName = "initialAmount",
name = "",
description = ""
)
void initialAmount(int initialAmount);
@ConfigItem(
keyName = "taskLocation",
name = "",
description = "",
hidden = true
)
default String taskLocation()
{
return "";
}
@ConfigItem(
keyName = "taskLocation",
name = "",
description = ""
)
void taskLocation(String key);
@ConfigItem(
keyName = "streak",
name = "",
description = "",
hidden = true
)
default int streak()
{
return -1;
}
@ConfigItem(
keyName = "streak",
name = "",
description = ""
)
void streak(int streak);
@ConfigItem(
keyName = "points",
name = "",
description = "",
hidden = true
)
default int points()
{
return -1;
}
@ConfigItem(
keyName = "points",
name = "",
description = ""
)
void points(int points);
@ConfigItem(
keyName = "expeditious",
name = "",
description = "",
hidden = true
)
default int expeditious()
{
return -1;
}
@ConfigItem(
keyName = "expeditious",
name = "",
description = ""
)
void expeditious(int expeditious);
@ConfigItem(
keyName = "slaughter",
name = "",
description = "",
hidden = true
)
default int slaughter()
{
return -1;
}
@ConfigItem(
keyName = "slaughter",
name = "",
description = ""
)
void slaughter(int slaughter);
}

View File

@@ -139,6 +139,9 @@ public class SlayerPlugin extends Plugin
@Inject
private SlayerConfig config;
@Inject
private ConfigManager configManager;
@Inject
private OverlayManager overlayManager;
@@ -227,12 +230,17 @@ public class SlayerPlugin extends Plugin
{
cachedXp = client.getSkillExperience(SLAYER);
if (config.amount() != -1
&& !config.taskName().isEmpty())
migrateConfig();
if (getIntProfileConfig(SlayerConfig.AMOUNT_KEY) != -1
&& !getStringProfileConfig(SlayerConfig.TASK_NAME_KEY).isEmpty())
{
setExpeditiousChargeCount(config.expeditious());
setSlaughterChargeCount(config.slaughter());
clientThread.invoke(() -> setTask(config.taskName(), config.amount(), config.initialAmount(), config.taskLocation(), false));
setExpeditiousChargeCount(getIntProfileConfig(SlayerConfig.EXPEDITIOUS_CHARGES_KEY));
setSlaughterChargeCount(getIntProfileConfig(SlayerConfig.SLAUGHTER_CHARGES_KEY));
clientThread.invoke(() -> setTask(getStringProfileConfig(SlayerConfig.TASK_NAME_KEY),
getIntProfileConfig(SlayerConfig.AMOUNT_KEY),
getIntProfileConfig(SlayerConfig.INIT_AMOUNT_KEY),
getStringProfileConfig(SlayerConfig.TASK_LOC_KEY), false));
}
}
@@ -275,27 +283,50 @@ public class SlayerPlugin extends Plugin
taggedNpcs.clear();
break;
case LOGGED_IN:
if (config.amount() != -1
&& !config.taskName().isEmpty()
migrateConfig();
if (getIntProfileConfig(SlayerConfig.AMOUNT_KEY) != -1
&& !getStringProfileConfig(SlayerConfig.TASK_NAME_KEY).isEmpty()
&& loginFlag)
{
setExpeditiousChargeCount(config.expeditious());
setSlaughterChargeCount(config.slaughter());
setTask(config.taskName(), config.amount(), config.initialAmount(), config.taskLocation(), false);
setExpeditiousChargeCount(getIntProfileConfig(SlayerConfig.EXPEDITIOUS_CHARGES_KEY));
setSlaughterChargeCount(getIntProfileConfig(SlayerConfig.SLAUGHTER_CHARGES_KEY));
setTask(getStringProfileConfig(SlayerConfig.TASK_NAME_KEY),
getIntProfileConfig(SlayerConfig.AMOUNT_KEY),
getIntProfileConfig(SlayerConfig.INIT_AMOUNT_KEY),
getStringProfileConfig(SlayerConfig.TASK_LOC_KEY), false);
loginFlag = false;
}
break;
}
}
@VisibleForTesting
int getIntProfileConfig(String key)
{
Integer value = configManager.getRSProfileConfiguration(SlayerConfig.GROUP_NAME, key, int.class);
return value == null ? -1 : value;
}
@VisibleForTesting
String getStringProfileConfig(String key)
{
String value = configManager.getRSProfileConfiguration(SlayerConfig.GROUP_NAME, key, String.class);
return value == null ? "" : value;
}
private void setProfileConfig(String key, Object value)
{
configManager.setRSProfileConfiguration(SlayerConfig.GROUP_NAME, key, value);
}
private void save()
{
config.amount(amount);
config.initialAmount(initialAmount);
config.taskName(taskName);
config.taskLocation(taskLocation);
config.expeditious(expeditiousChargeCount);
config.slaughter(slaughterChargeCount);
setProfileConfig(SlayerConfig.AMOUNT_KEY, amount);
setProfileConfig(SlayerConfig.INIT_AMOUNT_KEY, initialAmount);
setProfileConfig(SlayerConfig.TASK_NAME_KEY, taskName);
setProfileConfig(SlayerConfig.TASK_LOC_KEY, taskLocation);
setProfileConfig(SlayerConfig.EXPEDITIOUS_CHARGES_KEY, expeditiousChargeCount);
setProfileConfig(SlayerConfig.SLAUGHTER_CHARGES_KEY, slaughterChargeCount);
}
@Subscribe
@@ -345,7 +376,7 @@ public class SlayerPlugin extends Plugin
int amount = Integer.parseInt(mAssignBoss.group(2));
setTask(mAssignBoss.group(1), amount, amount);
int points = Integer.parseInt(mAssignBoss.group(3).replaceAll(",", ""));
config.points(points);
setProfileConfig(SlayerConfig.POINTS_KEY, points);
}
else if (mCurrent.find())
{
@@ -363,12 +394,12 @@ public class SlayerPlugin extends Plugin
if (braceletText.contains("bracelet of slaughter"))
{
slaughterChargeCount = SLAUGHTER_CHARGE;
config.slaughter(slaughterChargeCount);
setProfileConfig(SlayerConfig.SLAUGHTER_CHARGES_KEY, slaughterChargeCount);
}
else if (braceletText.contains("expeditious bracelet"))
{
expeditiousChargeCount = EXPEDITIOUS_CHARGE;
config.expeditious(expeditiousChargeCount);
setProfileConfig(SlayerConfig.EXPEDITIOUS_CHARGES_KEY, expeditiousChargeCount);
}
}
@@ -380,12 +411,12 @@ public class SlayerPlugin extends Plugin
Matcher mPoints = REWARD_POINTS.matcher(w.getText());
if (mPoints.find())
{
final int prevPoints = config.points();
final int prevPoints = getIntProfileConfig(SlayerConfig.POINTS_KEY);
int points = Integer.parseInt(mPoints.group(1).replaceAll(",", ""));
if (prevPoints != points)
{
config.points(points);
setProfileConfig(SlayerConfig.POINTS_KEY, points);
removeCounter();
addCounter();
}
@@ -426,7 +457,7 @@ public class SlayerPlugin extends Plugin
amount++;
slaughterChargeCount = mSlaughter.find() ? Integer.parseInt(mSlaughter.group(1)) : SLAUGHTER_CHARGE;
config.slaughter(slaughterChargeCount);
setProfileConfig(SlayerConfig.SLAUGHTER_CHARGES_KEY, slaughterChargeCount);
}
if (chatMsg.startsWith(CHAT_BRACELET_EXPEDITIOUS))
@@ -435,7 +466,7 @@ public class SlayerPlugin extends Plugin
amount--;
expeditiousChargeCount = mExpeditious.find() ? Integer.parseInt(mExpeditious.group(1)) : EXPEDITIOUS_CHARGE;
config.expeditious(expeditiousChargeCount);
setProfileConfig(SlayerConfig.EXPEDITIOUS_CHARGES_KEY, expeditiousChargeCount);
}
if (chatMsg.startsWith(CHAT_BRACELET_EXPEDITIOUS_CHARGE))
@@ -448,7 +479,7 @@ public class SlayerPlugin extends Plugin
}
expeditiousChargeCount = Integer.parseInt(mExpeditious.group(1));
config.expeditious(expeditiousChargeCount);
setProfileConfig(SlayerConfig.EXPEDITIOUS_CHARGES_KEY, expeditiousChargeCount);
}
if (chatMsg.startsWith(CHAT_BRACELET_SLAUGHTER_CHARGE))
@@ -460,7 +491,7 @@ public class SlayerPlugin extends Plugin
}
slaughterChargeCount = Integer.parseInt(mSlaughter.group(1));
config.slaughter(slaughterChargeCount);
setProfileConfig(SlayerConfig.SLAUGHTER_CHARGES_KEY, slaughterChargeCount);
}
if (chatMsg.startsWith("You've completed") && (chatMsg.contains("Slayer master") || chatMsg.contains("Slayer Master")))
@@ -479,12 +510,12 @@ public class SlayerPlugin extends Plugin
if (mTasks != null)
{
int streak = Integer.parseInt(mTasks.replace(",", ""));
config.streak(streak);
setProfileConfig(SlayerConfig.STREAK_KEY, streak);
}
if (mPoints != null)
{
int points = Integer.parseInt(mPoints.replace(",", ""));
config.points(points);
setProfileConfig(SlayerConfig.POINTS_KEY, points);
}
}
@@ -597,7 +628,7 @@ public class SlayerPlugin extends Plugin
@Subscribe
private void onConfigChanged(ConfigChanged event)
{
if (!event.getGroup().equals("slayer") || !event.getKey().equals("infobox"))
if (!event.getGroup().equals(SlayerConfig.GROUP_NAME) || !event.getKey().equals("infobox"))
{
return;
}
@@ -627,7 +658,8 @@ public class SlayerPlugin extends Plugin
amount--;
}
config.amount(amount); // save changed value
// save changed value
setProfileConfig(SlayerConfig.AMOUNT_KEY, amount);
if (!config.showInfobox())
{
@@ -772,7 +804,7 @@ public class SlayerPlugin extends Plugin
}
counter = new TaskCounter(taskImg, this, amount);
counter.setTooltip(String.format(taskTooltip, capsString(taskName), config.points(), config.streak()));
counter.setTooltip(String.format(taskTooltip, capsString(taskName), getIntProfileConfig(SlayerConfig.POINTS_KEY), getIntProfileConfig(SlayerConfig.STREAK_KEY)));
infoBoxManager.addInfoBox(counter);
}
@@ -891,4 +923,26 @@ public class SlayerPlugin extends Plugin
{
return str.substring(0, 1).toUpperCase() + str.substring(1);
}
private void migrateConfig()
{
migrateConfigKey(SlayerConfig.TASK_NAME_KEY);
migrateConfigKey(SlayerConfig.AMOUNT_KEY);
migrateConfigKey(SlayerConfig.INIT_AMOUNT_KEY);
migrateConfigKey(SlayerConfig.TASK_LOC_KEY);
migrateConfigKey(SlayerConfig.STREAK_KEY);
migrateConfigKey(SlayerConfig.POINTS_KEY);
migrateConfigKey(SlayerConfig.EXPEDITIOUS_CHARGES_KEY);
migrateConfigKey(SlayerConfig.SLAUGHTER_CHARGES_KEY);
}
private void migrateConfigKey(String key)
{
Object value = configManager.getConfiguration(SlayerConfig.GROUP_NAME, key);
if (value != null)
{
configManager.unsetConfiguration(SlayerConfig.GROUP_NAME, key);
configManager.setRSProfileConfiguration(SlayerConfig.GROUP_NAME, key, value);
}
}
}

View File

@@ -44,7 +44,10 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.mockito.junit.MockitoJUnitRunner;
@@ -286,4 +289,46 @@ public class ChatNotificationsPluginTest
// set value uses our player name, which has nbsp replaced
verify(messageNode).setValue("<col=005f00><colHIGHLIGHT><u>Logic Knot</u><col=005f00> received a drop: Adamant longsword</col>");
}
@Test
public void testLocalPlayerSelfMention()
{
final String localPlayerName = "Broo klyn";
MessageNode messageNode = mock(MessageNode.class);
Player localPlayer = mock(Player.class);
when(client.getLocalPlayer()).thenReturn(localPlayer);
when(localPlayer.getName()).thenReturn(localPlayerName);
lenient().when(config.highlightOwnName()).thenReturn(true);
lenient().when(messageNode.getValue()).thenReturn("Spread love it's the Broo klyn way");
ChatMessage chatMessage = new ChatMessage();
chatMessage.setType(ChatMessageType.PUBLICCHAT);
chatMessage.setName("Broo\u00a0klyn");
chatMessage.setMessageNode(messageNode);
chatNotificationsPlugin.onChatMessage(chatMessage);
verify(messageNode, times(0)).setValue(any());
}
@Test
public void testPrivateChatOutReturn()
{
MessageNode messageNode = mock(MessageNode.class);
lenient().when(config.highlightWordsString()).thenReturn("Brooklyn");
lenient().when(messageNode.getValue()).thenReturn("Spread love it's the Brooklyn way");
ChatMessage chatMessage = new ChatMessage();
chatMessage.setType(ChatMessageType.PRIVATECHATOUT);
chatMessage.setMessageNode(messageNode);
chatNotificationsPlugin.startUp();
chatNotificationsPlugin.onChatMessage(chatMessage);
verify(messageNode, times(0)).setValue(any());
}
}

View File

@@ -0,0 +1,134 @@
/*
* Copyright (c) 2019, Adam <Adam@sigterm.info>
* 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.cooking;
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.GraphicID;
import net.runelite.api.Player;
import net.runelite.api.events.ChatMessage;
import net.runelite.api.events.GraphicChanged;
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.assertNotNull;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.mockito.ArgumentMatchers.any;
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 CookingPluginTest
{
private static final String[] COOKING_MESSAGES = {
"You successfully cook a shark.",
"You successfully cook an anglerfish.",
"You manage to cook a tuna.",
"You cook the karambwan. It looks delicious.",
"You roast a lobster.",
"You cook a bass.",
"You successfully bake a tasty garden pie.",
"You dry a piece of meat and extract the sinew."
};
private static final String incenseBurnerMessage = "You burn some marrentill in the incense burner.";
@Inject
CookingPlugin cookingPlugin;
@Mock
@Bind
Client client;
@Mock
@Bind
InfoBoxManager infoBoxManager;
@Mock
@Bind
ItemManager itemManager;
@Mock
@Bind
CookingConfig config;
@Mock
@Bind
CookingOverlay cookingOverlay;
@Mock
@Bind
OverlayManager overlayManager;
@Before
public void before()
{
Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this);
}
@Test
public void testOnChatMessage()
{
for (String message : COOKING_MESSAGES)
{
ChatMessage chatMessage = new ChatMessage(null, ChatMessageType.SPAM, "", message, "", 0);
cookingPlugin.onChatMessage(chatMessage);
}
ChatMessage chatMessage = new ChatMessage(null, ChatMessageType.SPAM, "", incenseBurnerMessage, "", 0);
cookingPlugin.onChatMessage(chatMessage);
CookingSession cookingSession = cookingPlugin.getSession();
assertNotNull(cookingSession);
assertEquals(COOKING_MESSAGES.length, cookingSession.getCookAmount());
assertEquals(0, cookingSession.getBurnAmount());
}
@Test
public void testOnGraphicChanged()
{
Player player = mock(Player.class);
when(player.getGraphic()).thenReturn(GraphicID.WINE_MAKE);
when(config.fermentTimer()).thenReturn(true);
when(client.getLocalPlayer()).thenReturn(player);
GraphicChanged graphicChanged = new GraphicChanged();
graphicChanged.setActor(player);
cookingPlugin.onGraphicChanged(graphicChanged);
verify(infoBoxManager).addInfoBox(any(FermentTimer.class));
}
}

View File

@@ -0,0 +1,184 @@
/*
* Copyright (c) 2018, Tomas Slusny <slusnucky@gmail.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.npchighlight;
import com.google.inject.Guice;
import com.google.inject.testing.fieldbinder.Bind;
import com.google.inject.testing.fieldbinder.BoundFieldModule;
import java.awt.Color;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ScheduledExecutorService;
import javax.inject.Inject;
import net.runelite.api.Client;
import net.runelite.api.MenuAction;
import net.runelite.api.MenuEntry;
import net.runelite.api.NPC;
import net.runelite.api.events.MenuEntryAdded;
import net.runelite.api.events.NpcChanged;
import net.runelite.api.events.NpcSpawned;
import net.runelite.client.ui.overlay.OverlayManager;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.Before;
import org.junit.Ignore;
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 NpcIndicatorsPluginTest
{
@Mock
@Bind
private Client client;
@Mock
@Bind
private ScheduledExecutorService executorService;
@Mock
@Bind
private NpcIndicatorsConfig npcIndicatorsConfig;
@Inject
private NpcIndicatorsPlugin npcIndicatorsPlugin;
@Mock
@Bind
private OverlayManager overlayManager;
@Before
public void setUp()
{
Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this);
}
@Test
public void getHighlights()
{
when(npcIndicatorsConfig.getNpcToHighlight()).thenReturn("goblin, , zulrah , *wyvern, ,");
final List<String> highlightedNpcs = npcIndicatorsPlugin.getHighlights();
assertEquals("Length of parsed NPCs is incorrect", 3, highlightedNpcs.size());
final Iterator<String> iterator = highlightedNpcs.iterator();
assertEquals("goblin", iterator.next());
assertEquals("zulrah", iterator.next());
assertEquals("*wyvern", iterator.next());
}
@Test
@Ignore
public void testDeadNpcMenuHighlight()
{
when(npcIndicatorsConfig.getNpcToHighlight()).thenReturn("goblin");
when(npcIndicatorsConfig.deadNpcMenuColor()).thenReturn(Color.RED);
npcIndicatorsPlugin.rebuildAllNpcs();
NPC npc = mock(NPC.class);
when(npc.getName()).thenReturn("Goblin");
when(npc.isDead()).thenReturn(true);
npcIndicatorsPlugin.onNpcSpawned(new NpcSpawned(npc));
when(client.getCachedNPCs()).thenReturn(new NPC[]{npc}); // id 0
when(client.getMenuEntries()).thenReturn(new MenuEntry[]{new MenuEntry()});
MenuEntryAdded menuEntryAdded = new MenuEntryAdded("", "Goblin", MenuAction.NPC_FIRST_OPTION.getId(), 0, -1, -1, false);
npcIndicatorsPlugin.onMenuEntryAdded(menuEntryAdded);
MenuEntry target = new MenuEntry();
target.setTarget("<col=ff0000>Goblin"); // red
verify(client).setMenuEntries(new MenuEntry[]{target});
}
@Test
@Ignore
public void testAliveNpcMenuHighlight()
{
when(npcIndicatorsConfig.getNpcToHighlight()).thenReturn("goblin");
when(npcIndicatorsConfig.highlightMenuNames()).thenReturn(true);
when(npcIndicatorsConfig.getHighlightColor()).thenReturn(Color.BLUE);
npcIndicatorsPlugin.rebuildAllNpcs();
NPC npc = mock(NPC.class);
when(npc.getName()).thenReturn("Goblin");
npcIndicatorsPlugin.onNpcSpawned(new NpcSpawned(npc));
when(client.getCachedNPCs()).thenReturn(new NPC[]{npc}); // id 0
when(client.getMenuEntries()).thenReturn(new MenuEntry[]{new MenuEntry()});
MenuEntryAdded menuEntryAdded = new MenuEntryAdded("", "Goblin", MenuAction.NPC_FIRST_OPTION.getId(), 0, -1, -1, false);
npcIndicatorsPlugin.onMenuEntryAdded(menuEntryAdded);
MenuEntry target = new MenuEntry();
target.setTarget("<col=0000ff>Goblin"); // blue
verify(client).setMenuEntries(new MenuEntry[]{target});
}
@Test
public void taggedNpcChanges()
{
when(npcIndicatorsConfig.getNpcToHighlight()).thenReturn("Joseph");
npcIndicatorsPlugin.rebuildAllNpcs();
NPC npc = mock(NPC.class);
when(npc.getName()).thenReturn("Joseph");
npcIndicatorsPlugin.onNpcSpawned(new NpcSpawned(npc));
assertTrue(npcIndicatorsPlugin.getHighlightedNpcs().contains(npc));
when(npc.getName()).thenReturn("Werewolf");
npcIndicatorsPlugin.onNpcChanged(new NpcChanged(npc, null));
assertFalse(npcIndicatorsPlugin.getHighlightedNpcs().contains(npc));
}
@Test
public void npcChangesToTagged()
{
when(npcIndicatorsConfig.getNpcToHighlight()).thenReturn("Werewolf");
npcIndicatorsPlugin.rebuildAllNpcs();
NPC npc = mock(NPC.class);
when(npc.getName()).thenReturn("Joseph");
npcIndicatorsPlugin.onNpcSpawned(new NpcSpawned(npc));
assertFalse(npcIndicatorsPlugin.getHighlightedNpcs().contains(npc));
when(npc.getName()).thenReturn("Werewolf");
npcIndicatorsPlugin.onNpcChanged(new NpcChanged(npc, null));
assertTrue(npcIndicatorsPlugin.getHighlightedNpcs().contains(npc));
}
}

View File

@@ -53,6 +53,7 @@ import net.runelite.api.widgets.WidgetInfo;
import net.runelite.client.Notifier;
import net.runelite.client.chat.ChatCommandManager;
import net.runelite.client.chat.ChatMessageManager;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.game.ItemManager;
import net.runelite.client.ui.overlay.OverlayManager;
import net.runelite.client.ui.overlay.infobox.InfoBoxManager;
@@ -64,6 +65,7 @@ import org.junit.runner.RunWith;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import org.mockito.Mock;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -137,6 +139,10 @@ public class SlayerPluginTest
@Bind
Client client;
@Mock
@Bind
ConfigManager configManager;
@Mock
@Bind
SlayerConfig slayerConfig;
@@ -283,7 +289,7 @@ public class SlayerPluginTest
assertEquals("Vet'ion", slayerPlugin.getTaskName());
assertEquals(3, slayerPlugin.getAmount());
verify(slayerConfig).points(914);
verify(configManager).setRSProfileConfiguration(SlayerConfig.GROUP_NAME, SlayerConfig.POINTS_KEY, 914);
}
@Test
@@ -296,7 +302,7 @@ public class SlayerPluginTest
assertEquals("Chaos Elemental", slayerPlugin.getTaskName());
assertEquals(3, slayerPlugin.getAmount());
verify(slayerConfig).points(914);
verify(configManager).setRSProfileConfiguration(SlayerConfig.GROUP_NAME, SlayerConfig.POINTS_KEY, 914);
}
@Test
@@ -309,7 +315,7 @@ public class SlayerPluginTest
assertEquals("Alchemical Hydra", slayerPlugin.getTaskName());
assertEquals(35, slayerPlugin.getAmount());
verify(slayerConfig).points(724);
verify(configManager).setRSProfileConfiguration(SlayerConfig.GROUP_NAME, SlayerConfig.POINTS_KEY, 724);
}
@Test
@@ -440,7 +446,7 @@ public class SlayerPluginTest
when(client.getWidget(WidgetInfo.SLAYER_REWARDS_TOPBAR)).thenReturn(rewardBar);
slayerPlugin.onGameTick(new GameTick());
verify(slayerConfig).points(17566);
verify(configManager).setRSProfileConfiguration(SlayerConfig.GROUP_NAME, SlayerConfig.POINTS_KEY, 17566);
}
@Test
@@ -449,7 +455,7 @@ public class SlayerPluginTest
ChatMessage chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "Perterter", TASK_ONE, null, 0);
slayerPlugin.onChatMessage(chatMessageEvent);
verify(slayerConfig).streak(1);
verify(configManager).setRSProfileConfiguration(SlayerConfig.GROUP_NAME, SlayerConfig.STREAK_KEY, 1);
assertEquals("", slayerPlugin.getTaskName());
assertEquals(0, slayerPlugin.getAmount());
}
@@ -460,7 +466,7 @@ public class SlayerPluginTest
ChatMessage chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "Perterter", TASK_COMPLETE_NO_POINTS, null, 0);
slayerPlugin.onChatMessage(chatMessageEvent);
verify(slayerConfig).streak(3);
verify(configManager).setRSProfileConfiguration(SlayerConfig.GROUP_NAME, SlayerConfig.STREAK_KEY, 3);
assertEquals("", slayerPlugin.getTaskName());
assertEquals(0, slayerPlugin.getAmount());
}
@@ -471,10 +477,10 @@ public class SlayerPluginTest
ChatMessage chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "Perterter", TASK_POINTS, null, 0);
slayerPlugin.onChatMessage(chatMessageEvent);
verify(slayerConfig).streak(9);
verify(configManager).setRSProfileConfiguration(SlayerConfig.GROUP_NAME, SlayerConfig.STREAK_KEY, 9);
assertEquals("", slayerPlugin.getTaskName());
assertEquals(0, slayerPlugin.getAmount());
verify(slayerConfig).points(18_000);
verify(configManager).setRSProfileConfiguration(SlayerConfig.GROUP_NAME, SlayerConfig.POINTS_KEY, 18_000);
}
@Test
@@ -483,10 +489,10 @@ public class SlayerPluginTest
ChatMessage chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "Perterter", TASK_LARGE_STREAK, null, 0);
slayerPlugin.onChatMessage(chatMessageEvent);
verify(slayerConfig).streak(2465);
verify(configManager).setRSProfileConfiguration(SlayerConfig.GROUP_NAME, SlayerConfig.STREAK_KEY, 2465);
assertEquals("", slayerPlugin.getTaskName());
assertEquals(0, slayerPlugin.getAmount());
verify(slayerConfig).points(131_071);
verify(configManager).setRSProfileConfiguration(SlayerConfig.GROUP_NAME, SlayerConfig.POINTS_KEY, 131_071);
}
@Test
@@ -495,7 +501,7 @@ public class SlayerPluginTest
ChatMessage chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "Perterter", TASK_COMPETE_TURAEL, null, 0);
slayerPlugin.onChatMessage(chatMessageEvent);
verify(slayerConfig).streak(104);
verify(configManager).setRSProfileConfiguration(SlayerConfig.GROUP_NAME, SlayerConfig.STREAK_KEY, 104);
assertEquals("", slayerPlugin.getTaskName());
assertEquals(0, slayerPlugin.getAmount());
}
@@ -506,8 +512,8 @@ public class SlayerPluginTest
ChatMessage chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "", TASK_MAX_STREAK, null, 0);
slayerPlugin.onChatMessage(chatMessageEvent);
verify(slayerConfig).streak(16_000);
verify(slayerConfig).points(131_071);
verify(configManager).setRSProfileConfiguration(SlayerConfig.GROUP_NAME, SlayerConfig.STREAK_KEY, 16000);
verify(configManager).setRSProfileConfiguration(SlayerConfig.GROUP_NAME, SlayerConfig.POINTS_KEY, 131_071);
assertEquals("", slayerPlugin.getTaskName());
assertEquals(0, slayerPlugin.getAmount());
}
@@ -518,8 +524,8 @@ public class SlayerPluginTest
ChatMessage chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "", TASK_MAX_POINTS, null, 0);
slayerPlugin.onChatMessage(chatMessageEvent);
verify(slayerConfig).streak(9);
verify(slayerConfig).points(131_071);
verify(configManager).setRSProfileConfiguration(SlayerConfig.GROUP_NAME, SlayerConfig.STREAK_KEY, 9);
verify(configManager).setRSProfileConfiguration(SlayerConfig.GROUP_NAME, SlayerConfig.POINTS_KEY, 131_071);
assertEquals("", slayerPlugin.getTaskName());
assertEquals(0, slayerPlugin.getAmount());
}
@@ -530,8 +536,8 @@ public class SlayerPluginTest
ChatMessage chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "", TASK_WILDERNESS, null, 0);
slayerPlugin.onChatMessage(chatMessageEvent);
verify(slayerConfig).streak(9);
verify(slayerConfig).points(18_000);
verify(configManager).setRSProfileConfiguration(SlayerConfig.GROUP_NAME, SlayerConfig.STREAK_KEY, 9);
verify(configManager).setRSProfileConfiguration(SlayerConfig.GROUP_NAME, SlayerConfig.POINTS_KEY, 18_000);
assertEquals("", slayerPlugin.getTaskName());
assertEquals(0, slayerPlugin.getAmount());
}
@@ -919,7 +925,10 @@ public class SlayerPluginTest
@Test
public void infoboxNotAddedOnLogin()
{
when(slayerConfig.taskName()).thenReturn(Task.BLOODVELD.getName());
when(slayerPlugin.getStringProfileConfig(SlayerConfig.TASK_NAME_KEY)).thenReturn(Task.BLOODVELD.getName());
when(slayerPlugin.getIntProfileConfig(SlayerConfig.AMOUNT_KEY)).thenReturn(50);
// Lenient required as this is not called assuming correct plugin logic
lenient().when(slayerConfig.showInfobox()).thenReturn(true);
GameStateChanged loggingIn = new GameStateChanged();
loggingIn.setGameState(GameState.LOGGING_IN);

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2019 Owain van Brakel <https://github.com/Owain94>
* 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.
*/
description = "RuneLite JShell"
dependencies {
annotationProcessor(group = "org.projectlombok", name = "lombok", version = "1.18.4")
compileOnly(group = "org.projectlombok", name = "lombok", version = "1.18.4")
implementation(group = "com.google.code.findbugs", name = "jsr305", version = "3.0.2")
implementation(group = "com.google.inject", name = "guice", version = "4.1.0", classifier = "no_aop")
implementation(group = "com.fifesoft", name = "rsyntaxtextarea", version = "3.1.2")
implementation(group = "com.fifesoft", name = "autocomplete", version = "3.1.1")
implementation(group = "org.slf4j", name = "slf4j-api", version = "1.7.12")
}

View File

@@ -0,0 +1,137 @@
/*
* Copyright (c) 2021 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.jshell;
import java.awt.Point;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import javax.swing.text.JTextComponent;
import jdk.jshell.JShell;
import jdk.jshell.SourceCodeAnalysis;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.fife.ui.autocomplete.BasicCompletion;
import org.fife.ui.autocomplete.Completion;
import org.fife.ui.autocomplete.CompletionProviderBase;
import org.fife.ui.autocomplete.ParameterizedCompletion;
@Slf4j
@RequiredArgsConstructor
public class JShellAutocompleteProvider extends CompletionProviderBase
{
private final JShell shell;
private String anchorText;
private List<Completion> completions;
@Override
protected List<Completion> getCompletionsImpl(JTextComponent comp)
{
return completions;
}
@Override
public String getAlreadyEnteredText(JTextComponent comp)
{
complete(comp);
return anchorText;
}
private void complete(JTextComponent comp)
{
completions = Collections.emptyList();
String src = comp.getText();
int cursor = comp.getCaretPosition();
for (int offset = 0; offset < src.length() && cursor >= offset; )
{
var snipSrc = src.substring(offset);
int thisOffset = offset;
var ci = shell.sourceCodeAnalysis().analyzeCompletion(snipSrc);
offset = src.length() - ci.remaining().length();
boolean mayHaveMore = ci.completeness() == SourceCodeAnalysis.Completeness.COMPLETE_WITH_SEMI
|| ci.completeness() == SourceCodeAnalysis.Completeness.COMPLETE;
if (cursor <= offset || !mayHaveMore)
{
var anchor = new int[1];
completions = shell.sourceCodeAnalysis()
.completionSuggestions(snipSrc, cursor - thisOffset, anchor)
.stream()
.filter(v -> !v.continuation().startsWith("$"))
.map(s ->
{
return new BasicCompletion(this, s.continuation());
})
.collect(Collectors.toList());
anchorText = snipSrc.substring(anchor[0], cursor - thisOffset);
break;
}
}
if (completions.isEmpty())
{
anchorText = null;
}
}
@Override
public List<Completion> getCompletionsAt(JTextComponent comp, Point p)
{
return Collections.emptyList();
}
@Override
public boolean isAutoActivateOkay(JTextComponent comp)
{
// try not to start autocomplete when it has no useful context
String text = comp.getText();
for (int i = comp.getCaretPosition(); i >= 0; i--)
{
char c = text.charAt(i);
if (Character.isJavaIdentifierPart(c) || c == '.' || c == '(')
{
return true;
}
if (Character.isWhitespace(c))
{
continue;
}
return false;
}
return false;
}
@Override
public List<ParameterizedCompletion> getParameterizedCompletions(JTextComponent tc)
{
return Collections.emptyList();
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright (c) 2021 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.jshell;
import java.util.Map;
import jdk.jshell.execution.DirectExecutionControl;
import jdk.jshell.spi.ExecutionControl;
import jdk.jshell.spi.ExecutionControlProvider;
import jdk.jshell.spi.ExecutionEnv;
public class RLShellExecutionControl extends DirectExecutionControl implements ExecutionControlProvider
{
public RLShellExecutionControl()
{
}
@Override
public String name()
{
return getClass().getName();
}
@Override
public ExecutionControl generate(ExecutionEnv env, Map<String, String> parameters) throws Throwable
{
return this;
}
}

View File

@@ -0,0 +1,142 @@
/*
* Copyright (c) 2021 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.jshell;
import com.google.common.base.Strings;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;
import jdk.jshell.EvalException;
class RemappingThrowable extends Throwable
{
private final String source;
private final Map<String, Integer> offsets;
private final Throwable wrapped;
private final Map<Throwable, Throwable> dejaVu;
public RemappingThrowable(String source, Map<String, Integer> offsets, Throwable other)
{
this(source, offsets, other, new HashMap<>());
}
private RemappingThrowable(String source, Map<String, Integer> offsets, Throwable other, Map<Throwable, Throwable> dejaVu)
{
super();
this.source = source;
this.offsets = offsets;
this.wrapped = other;
this.dejaVu = dejaVu;
dejaVu.put(wrapped, this);
setStackTrace(Stream.of(wrapped.getStackTrace())
.map(e ->
{
Integer boxOffset = offsets.get(e.getFileName());
if (boxOffset == null)
{
return e;
}
int offset = boxOffset;
int line = e.getLineNumber();
for (int i = 0; i <= offset && i < source.length(); i++)
{
if (source.charAt(i) == '\n')
{
line++;
}
}
return new StackTraceElement(
Strings.isNullOrEmpty(e.getClassName()) ? "Shell" : e.getClassName(),
Strings.isNullOrEmpty(e.getMethodName()) ? "global" : e.getMethodName(),
"",
line);
})
.toArray(StackTraceElement[]::new));
if (wrapped.getCause() != null)
{
initCause(remap(wrapped.getCause()));
}
for (Throwable suppressed : wrapped.getSuppressed())
{
addSuppressed(remap(suppressed));
}
}
private Throwable remap(Throwable other)
{
Throwable remap = dejaVu.get(other);
if (remap == null)
{
remap = new RemappingThrowable(source, offsets, other, dejaVu);
// ctor inserts into the map
}
return remap;
}
@Override
public String getMessage()
{
return wrapped.getMessage();
}
@Override
public String getLocalizedMessage()
{
return wrapped.getLocalizedMessage();
}
@Override
public synchronized Throwable fillInStackTrace()
{
return this;
}
@Override
public String toString()
{
String className;
if (wrapped instanceof EvalException)
{
className = ((EvalException) wrapped).getExceptionClassName();
}
else
{
className = wrapped.getClass().getName();
}
String message = wrapped.getLocalizedMessage();
if (message == null)
{
return className;
}
return className + ": " + message;
}
}

View File

@@ -0,0 +1,447 @@
/*
* Copyright (c) 2021 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.jshell;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Injector;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.RenderingHints;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.io.IOException;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.text.BadLocationException;
import javax.swing.text.Segment;
import jdk.jshell.Diag;
import jdk.jshell.JShell;
import jdk.jshell.Snippet;
import jdk.jshell.SnippetEvent;
import jdk.jshell.SourceCodeAnalysis;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.fife.ui.autocomplete.AutoCompletion;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
import org.fife.ui.rsyntaxtextarea.Theme;
import org.fife.ui.rtextarea.RTextScrollPane;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Slf4j
public abstract class ShellPanel extends JPanel
{
private final ScheduledExecutorService executor;
private final RSyntaxTextArea textArea;
private final JTextArea console = new JTextArea();
@Getter
private final Logger shellLogger;
private final List<Runnable> cleanup = new ArrayList<>();
private RLShellExecutionControl exec;
private JShell shell;
private Set<Snippet> prelude;
private Injector injector;
private AutoCompletion autoCompletion;
public static ShellPanel INSTANCE;
public ShellPanel(ScheduledExecutorService executor)
{
this.executor = executor;
Font codeFont = Stream.of(
"Source code pro",
"DejaVu Sans Code",
"Consolas",
Font.MONOSPACED)
.map(name -> new Font(name, Font.PLAIN, 12))
.filter(f -> !"Dialog.plain".equals(f.getFontName()))
.findFirst()
.get();
setLayout(new BorderLayout());
JPanel topPanel = new JPanel();
topPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
JButton run = new JButton("");
run.setToolTipText("Run");
run.addActionListener(ev -> run());
topPanel.add(run);
JButton clear = new JButton("🗑");
run.setToolTipText("Clear console");
clear.addActionListener(ev -> console.setText(""));
topPanel.add(clear);
add(topPanel, BorderLayout.NORTH);
textArea = new RSyntaxTextArea();
try
{
// RSyntaxTextArea::setAntiAliasingEnabled actually forces it to match the platform's
// default, which is pointless
var map = new HashMap<RenderingHints.Key, Object>();
map.put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
var f = RSyntaxTextArea.class.getDeclaredField("aaHints");
f.setAccessible(true);
f.set(textArea, map);
}
catch (ReflectiveOperationException e)
{
throw new RuntimeException(e);
}
textArea.setFont(codeFont);
textArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVA);
textArea.setAutoIndentEnabled(true);
textArea.setPaintTabLines(true);
textArea.setShowMatchedBracketPopup(true);
textArea.setCloseCurlyBraces(false);
textArea.setTabSize(2);
textArea.setMarkOccurrences(true);
textArea.setMarkOccurrencesDelay(200);
textArea.addKeyListener(new KeyAdapter()
{
@Override
public void keyPressed(KeyEvent e)
{
if (e.getKeyCode() == KeyEvent.VK_R && (e.getModifiersEx() & KeyEvent.CTRL_DOWN_MASK) != 0)
{
run();
e.consume();
}
if (e.getKeyCode() == KeyEvent.VK_F10)
{
run();
e.consume();
}
}
});
var textScrollArea = new RTextScrollPane(textArea);
try
{
Theme.load(ShellPanel.class.getResourceAsStream("darcula.xml"), codeFont)
.apply(textArea);
try (var is = ShellPanel.class.getResourceAsStream("default.jsh"))
{
textArea.setText(new String(is.readAllBytes(), StandardCharsets.UTF_8));
}
}
catch (IOException e)
{
throw new RuntimeException(e);
}
console.setFont(codeFont);
console.setFocusable(false);
console.setEditable(false);
console.setOpaque(false); // this turns off the hover effect for some reason
var split = new JSplitPane(JSplitPane.VERTICAL_SPLIT, textScrollArea, new JScrollPane(console));
split.setResizeWeight(.8);
split.setPreferredSize(new Dimension(800, 800));
add(split, BorderLayout.CENTER);
shellLogger = new TeeLogger(LoggerFactory.getLogger("Shell"), this::logToConsole);
INSTANCE = this;
// make sure jshell is on the classpath
JShell.builder();
}
public void switchContext(Injector injector)
{
freeContext();
this.injector = injector;
exec = new RLShellExecutionControl()
{
@Override
protected String invoke(Method doitMethod) throws Exception
{
var result = new AtomicReference<>();
var sema = new Semaphore(0);
invokeOnClientThread(() ->
{
try
{
result.set(super.invoke(doitMethod));
}
catch (Exception e)
{
result.set(e);
}
finally
{
sema.release();
}
});
sema.acquire();
if (result.get() instanceof String)
{
return (String) result.get();
}
throw (Exception) result.get();
}
};
shell = JShell.builder()
.executionEngine(exec, null)
.build();
String preludeStr;
try (var is = ShellPanel.class.getResourceAsStream("prelude.jsh"))
{
preludeStr = new String(is.readAllBytes(), StandardCharsets.UTF_8);
}
catch (IOException e)
{
throw new RuntimeException(e);
}
prelude = ImmutableSet.copyOf(eval(preludeStr, false));
var cp = new JShellAutocompleteProvider(shell);
autoCompletion = new AutoCompletion(cp);
autoCompletion.setAutoActivationDelay(200);
autoCompletion.setAutoActivationEnabled(true);
autoCompletion.setAutoCompleteSingleChoices(false);
autoCompletion.install(this.textArea);
}
public void logToConsole(String message)
{
SwingUtilities.invokeLater(() ->
{
try
{
var doc = console.getDocument();
if (doc.getLength() > 100_000)
{
Segment seg = new Segment();
int i = doc.getLength() - 75_000;
for (; i < doc.getLength(); i++)
{
doc.getText(i, 1, seg);
if (seg.array[0] == '\n')
{
break;
}
}
doc.remove(0, i);
}
doc.insertString(doc.getLength(), message + "\n", null);
console.setCaretPosition(doc.getLength());
}
catch (BadLocationException e)
{
throw new RuntimeException(e);
}
});
}
private List<Snippet> eval(String src, boolean isUserCode)
{
var out = new ArrayList<Snippet>();
var offsets = new HashMap<String, Integer>();
String output = null;
evaluation:
for (int offset = 0; offset < src.length(); )
{
// Workaround a jdk bug
for (; src.charAt(offset) == '\n'; offset++);
var ci = shell.sourceCodeAnalysis().analyzeCompletion(src.substring(offset));
int thisOffset = offset;
offset = src.length() - ci.remaining().length();
if (ci.completeness() == SourceCodeAnalysis.Completeness.EMPTY)
{
break;
}
List<SnippetEvent> evs = shell.eval(ci.source());
for (var ev : evs)
{
Snippet snip = ev.snippet();
offsets.put("#" + snip.id(), thisOffset);
if (ev.status() != Snippet.Status.VALID && ev.status() != Snippet.Status.RECOVERABLE_DEFINED)
{
var diags = shell.diagnostics(snip).collect(Collectors.toList());
for (var diag : diags)
{
String msg = toStringDiagnostic(src, thisOffset, diag);
if (isUserCode)
{
logToConsole(msg);
// It might be nice to highlight stuff here
}
else
{
throw new RuntimeException("prelude error: " + msg);
}
}
if (diags.isEmpty())
{
logToConsole("bad snippet" + ev.status());
}
break evaluation;
}
if (ev.exception() != null)
{
if (isUserCode)
{
shellLogger.error("", new RemappingThrowable(src, offsets, ev.exception()));
}
else
{
throw new RuntimeException("prelude error", ev.exception());
}
}
output = ev.value();
out.add(snip);
}
}
if (isUserCode && !Strings.isNullOrEmpty(output))
{
logToConsole("[OUTPUT] " + output);
}
return out;
}
private String toStringDiagnostic(String source, int offset, Diag diag)
{
int line = 1;
int column = 1;
offset += (int) diag.getPosition();
for (int i = 0; i < offset && i < source.length(); i++)
{
if (source.charAt(i) == '\n')
{
line++;
column = 1;
}
else
{
column++;
}
}
return line + ":" + column + ": " + diag.getMessage(Locale.getDefault());
}
protected void run()
{
String text = textArea.getText();
executor.submit(() ->
{
shell.snippets()
.filter(v -> !prelude.contains(v))
.forEach(shell::drop);
cleanup();
eval(text, true);
});
}
public void freeContext()
{
cleanup();
exec = null;
shell = null;
prelude = null;
injector = null;
if (autoCompletion != null)
{
autoCompletion.uninstall();
}
autoCompletion = null;
console.setText("");
}
private void cleanup()
{
for (var c : cleanup)
{
try
{
c.run();
}
catch (Exception e)
{
shellLogger.error("Cleanup threw:", e);
}
}
cleanup.clear();
}
protected abstract void invokeOnClientThread(Runnable r);
public <T> T inject(Class<T> clazz)
{
return injector.getInstance(clazz);
}
public void cleanup(Runnable r)
{
cleanup.add(r);
}
}

View File

@@ -0,0 +1,483 @@
/*
* Copyright (c) 2021 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.jshell;
import java.io.CharArrayWriter;
import java.io.PrintWriter;
import java.util.function.Consumer;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.Marker;
import org.slf4j.helpers.FormattingTuple;
import org.slf4j.helpers.MessageFormatter;
@SuppressWarnings("PlaceholderCountMatchesArgumentCount")
@RequiredArgsConstructor
public class TeeLogger implements Logger
{
private static final String TRACE = "[TRACE] ";
private static final String DEBUG = "[DEBUG] ";
private static final String INFO = "[INFO] ";
private static final String WARN = "[WARN] ";
private static final String ERROR = "[ERROR] ";
private final Logger delegate;
private final Consumer<String> messageConsumer;
@Override
public String getName()
{
return "OPRSShell";
}
@Override
public boolean isTraceEnabled()
{
return true;
}
private void log(String level, String message, Object... format)
{
FormattingTuple fmt = MessageFormatter.arrayFormat(message, format);
StringBuilder msg = new StringBuilder();
msg.append(level).append(fmt.getMessage());
Throwable throwable = fmt.getThrowable();
if (throwable != null)
{
msg.append("\n");
var caw = new CharArrayWriter();
try (PrintWriter pw = new PrintWriter(caw))
{
throwable.printStackTrace(pw);
}
msg.append(caw.toString());
}
messageConsumer.accept(msg.toString());
}
@Override
public void trace(String msg)
{
delegate.trace(msg);
log(TRACE, msg);
}
@Override
public void trace(String format, Object arg)
{
delegate.trace(format, arg);
log(TRACE, format, arg);
}
@Override
public void trace(String format, Object arg1, Object arg2)
{
delegate.trace(format, arg1, arg2);
log(TRACE, format, arg1, arg2);
}
@Override
public void trace(String format, Object... arguments)
{
delegate.trace(format, arguments);
log(TRACE, format, arguments);
}
@Override
public void trace(String msg, Throwable t)
{
delegate.trace(msg, t);
log(TRACE, msg, t);
}
@Override
public boolean isTraceEnabled(Marker marker)
{
return true;
}
@Override
public void trace(Marker marker, String msg)
{
delegate.trace(marker, msg);
log(TRACE, msg);
}
@Override
public void trace(Marker marker, String format, Object arg)
{
delegate.trace(marker, format, arg);
log(TRACE, format, arg);
}
@Override
public void trace(Marker marker, String format, Object arg1, Object arg2)
{
delegate.trace(marker, format, arg1, arg2);
log(TRACE, format, arg1, arg2);
}
@Override
public void trace(Marker marker, String format, Object... argArray)
{
delegate.trace(marker, format, argArray);
log(TRACE, format, argArray);
}
@Override
public void trace(Marker marker, String msg, Throwable t)
{
delegate.trace(marker, msg, t);
log(TRACE, msg, t);
}
@Override
public boolean isDebugEnabled()
{
return true;
}
@Override
public void debug(String msg)
{
delegate.debug(msg);
log(DEBUG, msg);
}
@Override
public void debug(String format, Object arg)
{
delegate.debug(format, arg);
log(DEBUG, format, arg);
}
@Override
public void debug(String format, Object arg1, Object arg2)
{
delegate.debug(format, arg1, arg2);
log(DEBUG, format, arg1, arg2);
}
@Override
public void debug(String format, Object... arguments)
{
delegate.debug(format, arguments);
log(DEBUG, format, arguments);
}
@Override
public void debug(String msg, Throwable t)
{
delegate.debug(msg, t);
log(DEBUG, msg, t);
}
@Override
public boolean isDebugEnabled(Marker marker)
{
return true;
}
@Override
public void debug(Marker marker, String msg)
{
delegate.debug(marker, msg);
log(DEBUG, msg);
}
@Override
public void debug(Marker marker, String format, Object arg)
{
delegate.debug(marker, format, arg);
log(DEBUG, format, arg);
}
@Override
public void debug(Marker marker, String format, Object arg1, Object arg2)
{
delegate.debug(marker, format, arg1, arg2);
log(DEBUG, format, arg1, arg2);
}
@Override
public void debug(Marker marker, String format, Object... arguments)
{
delegate.debug(marker, format, arguments);
log(DEBUG, format, arguments);
}
@Override
public void debug(Marker marker, String msg, Throwable t)
{
delegate.debug(marker, msg, t);
log(DEBUG, msg, t);
}
@Override
public boolean isInfoEnabled()
{
return true;
}
@Override
public void info(String msg)
{
delegate.info(msg);
log(INFO, msg);
}
@Override
public void info(String format, Object arg)
{
delegate.info(format, arg);
log(INFO, format, arg);
}
@Override
public void info(String format, Object arg1, Object arg2)
{
delegate.info(format, arg1, arg2);
log(INFO, format, arg1, arg2);
}
@Override
public void info(String format, Object... arguments)
{
delegate.info(format, arguments);
log(INFO, format, arguments);
}
@Override
public void info(String msg, Throwable t)
{
delegate.info(msg, t);
log(INFO, msg, t);
}
@Override
public boolean isInfoEnabled(Marker marker)
{
return true;
}
@Override
public void info(Marker marker, String msg)
{
delegate.info(marker, msg);
log(INFO, msg);
}
@Override
public void info(Marker marker, String format, Object arg)
{
delegate.info(marker, format, arg);
log(INFO, format, arg);
}
@Override
public void info(Marker marker, String format, Object arg1, Object arg2)
{
delegate.info(marker, format, arg1, arg2);
log(INFO, format, arg1, arg2);
}
@Override
public void info(Marker marker, String format, Object... arguments)
{
delegate.info(marker, format, arguments);
log(INFO, format, arguments);
}
@Override
public void info(Marker marker, String msg, Throwable t)
{
delegate.info(marker, msg, t);
log(INFO, msg, t);
}
@Override
public boolean isWarnEnabled()
{
return true;
}
@Override
public void warn(String msg)
{
delegate.warn(msg);
log(WARN, msg);
}
@Override
public void warn(String format, Object arg)
{
delegate.warn(format, arg);
log(WARN, format, arg);
}
@Override
public void warn(String format, Object... arguments)
{
delegate.warn(format, arguments);
log(WARN, format, arguments);
}
@Override
public void warn(String format, Object arg1, Object arg2)
{
delegate.warn(format, arg1, arg2);
log(WARN, format, arg1, arg2);
}
@Override
public void warn(String msg, Throwable t)
{
delegate.warn(msg, t);
log(WARN, msg, t);
}
@Override
public boolean isWarnEnabled(Marker marker)
{
return true;
}
@Override
public void warn(Marker marker, String msg)
{
delegate.warn(marker, msg);
log(WARN, msg);
}
@Override
public void warn(Marker marker, String format, Object arg)
{
delegate.warn(marker, format, arg);
log(WARN, format, arg);
}
@Override
public void warn(Marker marker, String format, Object arg1, Object arg2)
{
delegate.warn(marker, format, arg1, arg2);
log(WARN, format, arg1, arg2);
}
@Override
public void warn(Marker marker, String format, Object... arguments)
{
delegate.warn(marker, format, arguments);
log(WARN, format, arguments);
}
@Override
public void warn(Marker marker, String msg, Throwable t)
{
delegate.warn(marker, msg, t);
log(WARN, msg, t);
}
@Override
public boolean isErrorEnabled()
{
return true;
}
@Override
public void error(String msg)
{
delegate.error(msg);
log(ERROR, msg);
}
@Override
public void error(String format, Object arg)
{
delegate.error(format, arg);
log(ERROR, format, arg);
}
@Override
public void error(String format, Object arg1, Object arg2)
{
delegate.error(format, arg1, arg2);
log(ERROR, format, arg1, arg2);
}
@Override
public void error(String format, Object... arguments)
{
delegate.error(format, arguments);
log(ERROR, format, arguments);
}
@Override
public void error(String msg, Throwable t)
{
delegate.error(msg, t);
log(ERROR, msg, t);
}
@Override
public boolean isErrorEnabled(Marker marker)
{
return true;
}
@Override
public void error(Marker marker, String msg)
{
delegate.error(marker, msg);
log(ERROR, msg);
}
@Override
public void error(Marker marker, String format, Object arg)
{
delegate.error(marker, format, arg);
log(ERROR, format, arg);
}
@Override
public void error(Marker marker, String format, Object arg1, Object arg2)
{
delegate.error(marker, format, arg1, arg2);
log(ERROR, format, arg1, arg2);
}
@Override
public void error(Marker marker, String format, Object... arguments)
{
delegate.error(marker, format, arguments);
log(ERROR, format, arguments);
}
@Override
public void error(Marker marker, String msg, Throwable t)
{
delegate.error(marker, msg, t);
log(ERROR, msg, t);
}
}

View File

@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE RSyntaxTheme SYSTEM "theme.dtd">
<RSyntaxTheme version="1.0">
<!-- General editor colors. -->
<background color="2B2B2B"/>
<caret color="BBBBBB"/>
<selection useFG="false" bg="214283" roundedEdges="false"/>
<currentLineHighlight color="323232" fade="false"/>
<marginLine fg="394448"/>
<markAllHighlight color="155221"/>
<markOccurrencesHighlight color="32593D" border="true"/>
<matchedBracket fg="A9B7C6" bg="2E2E2E" highlightBoth="true" animate="true"/><!--ij draws a border-->
<hyperlinks fg="287BDE"/>
<secondaryLanguages>
<language index="1" bg="333344"/>
<language index="2" bg="223322"/>
<language index="3" bg="332222"/>
</secondaryLanguages>
<!-- Gutter styling. -->
<gutterBorder color="606366"/>
<lineNumbers fg="606366"/>
<foldIndicator fg="6A8088" iconBg="2f383c" iconArmedBg="3f484c"/>
<iconRowHeader activeLineRange="878787"/>
<!-- Syntax tokens. -->
<tokenStyles>
<style token="IDENTIFIER" fg="A9B7C6"/>
<style token="RESERVED_WORD" fg="CC7832" bold="false"/>
<style token="RESERVED_WORD_2" fg="CC7832" bold="false"/>
<style token="ANNOTATION" fg="BBB529"/>
<style token="COMMENT_DOCUMENTATION" fg="629755"/>
<style token="COMMENT_EOL" fg="808080"/>
<style token="COMMENT_MULTILINE" fg="808080"/>
<style token="COMMENT_KEYWORD" fg="629755"/>
<style token="COMMENT_MARKUP" fg="77B767"/>
<style token="FUNCTION" fg="A9B7C6"/><!-- any identifier magically known -->
<style token="DATA_TYPE" fg="CC7832" bold="false"/>
<style token="LITERAL_BOOLEAN" fg="CC7832" bold="false"/>
<style token="LITERAL_NUMBER_DECIMAL_INT" fg="6897BB"/>
<style token="LITERAL_NUMBER_FLOAT" fg="6897BB"/>
<style token="LITERAL_NUMBER_HEXADECIMAL" fg="6897BB"/>
<style token="LITERAL_STRING_DOUBLE_QUOTE" fg="6A8759"/>
<style token="LITERAL_CHAR" fg="6A8759"/>
<style token="LITERAL_BACKQUOTE" fg="6A8759"/>
<!-- all wrong but nobody will write xml in this -->
<style token="MARKUP_TAG_DELIMITER" fg="F92672"/>
<style token="MARKUP_TAG_NAME" fg="ABBFD3" bold="true"/>
<style token="MARKUP_TAG_ATTRIBUTE" fg="B3B689"/>
<style token="MARKUP_TAG_ATTRIBUTE_VALUE" fg="e1e2cf"/>
<style token="MARKUP_COMMENT" fg="878787"/>
<style token="MARKUP_DTD" fg="A082BD"/>
<style token="MARKUP_PROCESSING_INSTRUCTION" fg="A082BD"/>
<style token="MARKUP_CDATA" fg="d5e6f0"/>
<style token="MARKUP_CDATA_DELIMITER" fg="FD971F"/>
<style token="MARKUP_ENTITY_REFERENCE" fg="F92672"/>
<style token="OPERATOR" fg="A9B7C6"/>
<style token="PREPROCESSOR" fg="A082BD"/>
<style token="REGEX" fg="6A8759"/>
<style token="SEPARATOR" fg="A9B7C6"/>
<style token="VARIABLE" fg="A9B7C6" bold="false"/>
<style token="WHITESPACE" fg="606060"/>
<style token="ERROR_IDENTIFIER" fg="F9F9F9" bg="d82323"/>
<style token="ERROR_NUMBER_FORMAT" fg="F9F9F9" bg="d82323"/>
<style token="ERROR_STRING_DOUBLE" fg="F9F9F9" bg="d82323"/>
<style token="ERROR_CHAR" fg="F9F9F9" bg="d82323"/>
</tokenStyles>
</RSyntaxTheme>

View File

@@ -0,0 +1,8 @@
// Welcome to the OpenOSRS Development Shell
// Everything executed here runs on the client thread by default.
// By default client, clientThread, configManager and log are in scope
// You can subscribe to the Event Bus by using subscribe(Event.class, ev -> handler);
// and you can access things in the global injector module with var thing = inject(Thing.class);
// Press Ctrl+R or F10 to execute the contents of this editor
log.info("Hello {}", client.getGameState());

View File

@@ -0,0 +1,68 @@
/*
* Copyright (c) 2021 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.
*/
import java.util.function.Consumer;
import net.runelite.client.callback.ClientThread;
import net.runelite.client.config.ConfigManager;
import org.slf4j.Logger;
import java.util.*;
import java.util.stream.*;
import net.runelite.api.*;
import net.runelite.api.coords.*;
import net.runelite.api.events.*;
import net.runelite.api.widgets.*;
import net.runelite.client.events.*;
import net.runelite.client.game.*;
var $PANEL = net.runelite.jshell.ShellPanel.INSTANCE;
Logger log = $PANEL.getShellLogger();
static <T> T inject(Class<T> clazz)
{
return $PANEL.inject(clazz);
}
static void cleanup(Runnable r)
{
$PANEL.cleanup(r);
}
var $EVENT_BUS = inject(net.runelite.client.eventbus.EventBus.class);
static <T> void subscribe(Class<T> eventType, Consumer<T> subscriber, float priority)
{
var sub = $EVENT_BUS.register(eventType, subscriber, priority);
cleanup(() -> $EVENT_BUS.unregister(sub));
}
static <T> void subscribe(Class<T> eventType, Consumer<T> subscriber)
{
var sub = $EVENT_BUS.register(eventType, subscriber, 0.f);
cleanup(() -> $EVENT_BUS.unregister(sub));
}
var client = inject(Client.class);
var clientThread = inject(ClientThread.class);
var configManager = inject(ConfigManager.class);

View File

@@ -167,18 +167,36 @@ public interface RSActor extends RSRenderable, Actor
@Override
int getWalkAnimation();
// TODO: Remove next major
@Deprecated
@Import("walkBackSequence")
@Override
int getWalkBackAnimation();
@Import("walkBackSequence")
@Override
int getWalkRotate180();
// TODO: Remove next major
@Deprecated
@Import("walkLeftSequence")
@Override
int getWalkLeftAnimation();
@Import("walkLeftSequence")
@Override
int getWalkRotateLeft();
// TODO: Remove next major
@Deprecated
@Import("walkRightSequence")
@Override
int getWalkRightAnimation();
@Import("walkRightSequence")
@Override
int getWalkRotateRight();
@Import("runSequence")
@Override
int getRunAnimation();

View File

@@ -42,6 +42,7 @@ include(":injection-annotations")
include(":injector")
include(":runelite-api")
include(":runelite-client")
include(":runelite-jshell")
include(":runelite-mixins")
include(":runelite-script-assembler-plugin")
include(":runescape-api")