Merge remote-tracking branch 'runelite/master'

This commit is contained in:
Owain van Brakel
2019-12-17 10:21:02 +01:00
22 changed files with 509 additions and 87 deletions

View File

@@ -32,6 +32,9 @@ import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.TrayIcon;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
@@ -42,6 +45,13 @@ import java.util.List;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.inject.Singleton;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.ChatMessageType;
import net.runelite.api.Client;
@@ -60,6 +70,23 @@ import net.runelite.client.util.OSType;
@Slf4j
public class Notifier
{
@Getter
@RequiredArgsConstructor
public enum NativeCustomOff
{
NATIVE("Native"),
CUSTOM("Custom"),
OFF("Off");
private final String name;
@Override
public String toString()
{
return name;
}
}
// Default timeout of notification in milliseconds
private static final int DEFAULT_TIMEOUT = 10000;
private static final String DOUBLE_QUOTE = "\"";
@@ -127,9 +154,13 @@ public class Notifier
sendNotification(appName, message, type);
}
if (runeLiteConfig.enableNotificationSound())
switch (runeLiteConfig.notificationSound())
{
Toolkit.getDefaultToolkit().beep();
case NATIVE:
Toolkit.getDefaultToolkit().beep();
break;
case CUSTOM:
executorService.submit(this::playCustomSound);
}
if (runeLiteConfig.enableGameMessageNotification() && client.getGameState() == GameState.LOGGED_IN)
@@ -366,4 +397,48 @@ public class Notifier
return "normal";
}
}
private void playCustomSound()
{
Clip clip = null;
// Try to load the user sound from ~/.runelite/notification.wav
File file = new File(RuneLite.RUNELITE_DIR, "notification.wav");
if (file.exists())
{
try
{
InputStream fileStream = new BufferedInputStream(new FileInputStream(file));
try (AudioInputStream sound = AudioSystem.getAudioInputStream(fileStream))
{
clip = AudioSystem.getClip();
clip.open(sound);
}
}
catch (UnsupportedAudioFileException | IOException | LineUnavailableException e)
{
clip = null;
log.warn("Unable to play notification sound", e);
}
}
if (clip == null)
{
// Otherwise load from the classpath
InputStream fileStream = new BufferedInputStream(Notifier.class.getResourceAsStream("notification.wav"));
try (AudioInputStream sound = AudioSystem.getAudioInputStream(fileStream))
{
clip = AudioSystem.getClip();
clip.open(sound);
}
catch (UnsupportedAudioFileException | IOException | LineUnavailableException e)
{
log.warn("Unable to play builtin notification sound", e);
Toolkit.getDefaultToolkit().beep();
return;
}
}
clip.start();
}
}

View File

@@ -26,6 +26,7 @@ package net.runelite.client.config;
import java.awt.Dimension;
import net.runelite.api.Constants;
import net.runelite.client.Notifier;
import net.runelite.client.ui.ContainableFrame;
@ConfigGroup("runelite")
@@ -213,14 +214,14 @@ public interface RuneLiteConfig extends Config
@ConfigItem(
keyName = "notificationSound",
name = "Enable sound on notifications",
name = "Notification sound",
description = "Enables the playing of a beep sound when notifications are displayed",
position = 16,
titleSection = "notificationsTitle"
)
default boolean enableNotificationSound()
default Notifier.NativeCustomOff notificationSound()
{
return true;
return Notifier.NativeCustomOff.NATIVE;
}
@ConfigItem(

View File

@@ -148,4 +148,15 @@ public interface ClanChatConfig extends Config
{
return "";
}
@ConfigItem(
keyName = "confirmKicks",
name = "Confirm Kicks",
description = "Shows a chat prompt to confirm kicks",
position = 10
)
default boolean confirmKicks()
{
return false;
}
}

View File

@@ -28,6 +28,7 @@ package net.runelite.client.plugins.clanchat;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.Runnables;
import com.google.inject.Provides;
import java.awt.Color;
import java.awt.image.BufferedImage;
@@ -74,6 +75,7 @@ import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.events.ConfigChanged;
import net.runelite.client.game.ClanManager;
import net.runelite.client.game.SpriteManager;
import net.runelite.client.game.chatbox.ChatboxPanelManager;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import static net.runelite.client.ui.JagexColors.CHAT_CLAN_NAME_OPAQUE_BACKGROUND;
@@ -120,6 +122,9 @@ public class ClanChatPlugin extends Plugin
@Inject
private ClientThread clientThread;
@Inject
private ChatboxPanelManager chatboxPanelManager;
private List<String> chats = new ArrayList<>();
private ClanChatIndicator clanMemberCounter;
private int clanJoinedTick;
@@ -134,6 +139,8 @@ public class ClanChatPlugin extends Plugin
private boolean clanTabChat;
private String clanname;
private boolean kickConfirmed = false;
@SuppressWarnings("unchecked")
public static CopyOnWriteArrayList<Player> getClanMembers()
{
@@ -521,14 +528,37 @@ public class ClanChatPlugin extends Plugin
@Subscribe
private void onScriptCallbackEvent(ScriptCallbackEvent scriptCallbackEvent)
{
if (!scriptCallbackEvent.getEventName().equalsIgnoreCase("clanchatInput"))
switch (scriptCallbackEvent.getEventName())
{
return;
}
case "clanchatInput":
{
final int[] intStack = client.getIntStack();
final int size = client.getIntStackSize();
intStack[size - 1] = config.clanTabChat() ? 1 : 0;
break;
}
case "confirmClanKick":
{
if (!config.confirmKicks() || kickConfirmed)
{
break;
}
final int[] intStack = client.getIntStack();
final int size = client.getIntStackSize();
intStack[size - 1] = this.clanTabChat ? 1 : 0;
// Set a flag so the script doesn't instantly kick them
final int[] intStack = client.getIntStack();
final int size = client.getIntStackSize();
intStack[size - 1] = 1;
// Get name of player we are trying to kick
final String[] stringStack = client.getStringStack();
final int stringSize = client.getStringStackSize();
final String kickPlayerName = stringStack[stringSize - 1];
// Show a chatbox panel confirming the kick
clientThread.invokeLater(() -> confirmKickPlayer(kickPlayerName));
break;
}
}
}
int getClanAmount()
@@ -642,6 +672,21 @@ public class ClanChatPlugin extends Plugin
clanMemberCounter = null;
}
private void confirmKickPlayer(final String kickPlayerName)
{
chatboxPanelManager.openTextMenuInput("Attempting to kick: " + kickPlayerName)
.option("1. Confirm kick", () ->
clientThread.invoke(() ->
{
kickConfirmed = true;
client.runScript(ScriptID.CLAN_SEND_KICK, kickPlayerName);
kickConfirmed = false;
})
)
.option("2. Cancel", Runnables::doNothing)
.build();
}
private void addClanCounter()
{
if (!this.showClanCounter || clanMemberCounter != null || clanMembers.isEmpty())

View File

@@ -26,28 +26,33 @@
package net.runelite.client.plugins.devtools;
import java.awt.GridLayout;
import java.awt.TrayIcon;
import javax.inject.Inject;
import javax.swing.JButton;
import javax.swing.JPanel;
import net.runelite.api.Client;
import net.runelite.client.Notifier;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.PluginPanel;
class DevToolsPanel extends PluginPanel
{
private final Client client;
private final Notifier notifier;
private final DevToolsPlugin plugin;
private final WidgetInspector widgetInspector;
private final VarInspector varInspector;
@Inject
private DevToolsPanel(Client client, DevToolsPlugin plugin, WidgetInspector widgetInspector, VarInspector varInspector)
private DevToolsPanel(Client client, DevToolsPlugin plugin, WidgetInspector widgetInspector, VarInspector varInspector, Notifier notifier)
{
super();
this.client = client;
this.plugin = plugin;
this.widgetInspector = widgetInspector;
this.varInspector = varInspector;
this.notifier = notifier;
setBackground(ColorScheme.DARK_GRAY_COLOR);
@@ -135,6 +140,13 @@ class DevToolsPanel extends PluginPanel
container.add(plugin.getSoundEffects());
final JButton notificationBtn = new JButton("Notification");
notificationBtn.addActionListener(e ->
{
notifier.notify("Wow!", TrayIcon.MessageType.ERROR);
});
container.add(notificationBtn);
return container;
}
}

View File

@@ -38,7 +38,7 @@ enum ItemIdentification
RANARR_SEED(Type.SEED, "Ranarr", "R", ItemID.RANARR_SEED),
TOADFLAX_SEED(Type.SEED, "Toad", "TOA", ItemID.TOADFLAX_SEED),
IRIT_SEED(Type.SEED, "Irit", "I", ItemID.IRIT_SEED),
AVANTOE_SEED(Type.SEED, "Avantoe", "A", ItemID.AVANTOE_SEED),
AVANTOE_SEED(Type.SEED, "Avan", "A", ItemID.AVANTOE_SEED),
KWUARM_SEED(Type.SEED, "Kwuarm", "K", ItemID.KWUARM_SEED),
SNAPDRAGON_SEED(Type.SEED, "Snap", "S", ItemID.SNAPDRAGON_SEED),
CADANTINE_SEED(Type.SEED, "Cadan", "C", ItemID.CADANTINE_SEED),
@@ -56,7 +56,7 @@ enum ItemIdentification
RANARR(Type.HERB, "Ranarr", "R", ItemID.RANARR_WEED, ItemID.GRIMY_RANARR_WEED),
TOADFLAX(Type.HERB, "Toad", "TOA", ItemID.TOADFLAX, ItemID.GRIMY_TOADFLAX),
IRIT(Type.HERB, "Irit", "I", ItemID.IRIT_LEAF, ItemID.GRIMY_IRIT_LEAF),
AVANTOE(Type.HERB, "Avantoe", "A", ItemID.AVANTOE, ItemID.GRIMY_AVANTOE),
AVANTOE(Type.HERB, "Avan", "A", ItemID.AVANTOE, ItemID.GRIMY_AVANTOE),
KWUARM(Type.HERB, "Kwuarm", "K", ItemID.KWUARM, ItemID.GRIMY_KWUARM),
SNAPDRAGON(Type.HERB, "Snap", "S", ItemID.SNAPDRAGON, ItemID.GRIMY_SNAPDRAGON),
CADANTINE(Type.HERB, "Cadan", "C", ItemID.CADANTINE, ItemID.GRIMY_CADANTINE),

View File

@@ -129,7 +129,7 @@ public class SlayerPlugin extends Plugin
//NPC messages
private static final Pattern NPC_ASSIGN_MESSAGE = Pattern.compile(".*(?:Your new task is to kill|You are to bring balance to)\\s*(?<amount>\\d+) (?<name>.+?)(?: (?:in|on|south of) (?:the )?(?<location>.+))?\\.");
private static final Pattern NPC_ASSIGN_BOSS_MESSAGE = Pattern.compile("^Excellent. You're now assigned to kill (?:the )?(.*) (\\d+) times.*Your reward point tally is (.*)\\.$");
private static final Pattern NPC_ASSIGN_FIRST_MESSAGE = Pattern.compile("^We'll start you off hunting (.*), you'll need to kill (\\d*) of them.");
private static final Pattern NPC_ASSIGN_FIRST_MESSAGE = Pattern.compile("^We'll start you off (?:hunting|bringing balance to) (.*), you'll need to kill (\\d*) of them\\.$");
private static final Pattern NPC_CURRENT_MESSAGE = Pattern.compile("^You're still (?:hunting|bringing balance to) (?<name>.+)(?: (?:in|on|south of) (?:the )?(?<location>.+), with|; you have) (?<amount>\\d+) to go\\..*");
private static final int GROTESQUE_GUARDIANS_REGION = 6727;

View File

@@ -172,6 +172,7 @@ public class TwitchPlugin extends Plugin implements TwitchListener, ChatboxInput
.sender("Twitch")
.name(sender)
.runeLiteFormattedMessage(chatMessage)
.timestamp((int) (System.currentTimeMillis() / 1000))
.build());
}

View File

@@ -28,14 +28,16 @@ package net.runelite.client.plugins.woodcutting;
import java.time.Instant;
import lombok.AllArgsConstructor;
import lombok.Getter;
import net.runelite.api.coords.LocalPoint;
import net.runelite.api.coords.WorldPoint;
@AllArgsConstructor
@Getter
class TreeRespawn
{
private final Tree tree;
private final LocalPoint location;
private final int lenX;
private final int lenY;
private final WorldPoint worldLocation;
private final Instant startTime;
private final int respawnTime;

View File

@@ -42,6 +42,8 @@ import net.runelite.api.GameObject;
import net.runelite.api.ItemID;
import net.runelite.api.MenuOpcode;
import net.runelite.api.Player;
import net.runelite.api.Point;
import net.runelite.api.coords.WorldPoint;
import net.runelite.api.events.AnimationChanged;
import net.runelite.api.events.ChatMessage;
import net.runelite.api.events.GameObjectChanged;
@@ -217,8 +219,12 @@ public class WoodcuttingPlugin extends Plugin
{
if (tree.getRespawnTime() != null && !recentlyLoggedIn)
{
Point max = object.getSceneMaxLocation();
Point min = object.getSceneMinLocation();
int lenX = max.getX() - min.getX();
int lenY = max.getY() - min.getY();
log.debug("Adding respawn timer for {} tree at {}", tree, object.getLocalLocation());
TreeRespawn treeRespawn = new TreeRespawn(tree, object.getLocalLocation(), Instant.now(), (int) tree.getRespawnTime().toMillis());
TreeRespawn treeRespawn = new TreeRespawn(tree, lenX, lenY, WorldPoint.fromScene(client, min.getX(), min.getY(), client.getPlane()), Instant.now(), (int) tree.getRespawnTime().toMillis());
respawns.add(treeRespawn);
}
@@ -240,9 +246,9 @@ public class WoodcuttingPlugin extends Plugin
{
switch (event.getGameState())
{
case LOADING:
case HOPPING:
respawns.clear();
case LOADING:
treeObjects.clear();
break;
case LOGGED_IN:

View File

@@ -103,10 +103,17 @@ class WoodcuttingTreesOverlay extends Overlay
Instant now = Instant.now();
for (TreeRespawn treeRespawn : respawns)
{
LocalPoint loc = treeRespawn.getLocation();
LocalPoint minLocation = LocalPoint.fromWorld(client, treeRespawn.getWorldLocation());
if (minLocation == null)
{
continue;
}
LocalPoint centeredLocation = new LocalPoint(
minLocation.getX() + treeRespawn.getLenX() * Perspective.LOCAL_HALF_TILE_SIZE,
minLocation.getY() + treeRespawn.getLenY() * Perspective.LOCAL_HALF_TILE_SIZE);
float percent = (now.toEpochMilli() - treeRespawn.getStartTime().toEpochMilli()) / (float) treeRespawn.getRespawnTime();
Point point = Perspective.localToCanvas(client, loc, client.getPlane());
Point point = Perspective.localToCanvas(client, centeredLocation, client.getPlane());
if (point == null || percent > 1.0f)
{
continue;