From 343f83bc81866ca1b5cb63da7cf87e7ac570713d Mon Sep 17 00:00:00 2001 From: Kyle <48519776+xKylee@users.noreply.github.com> Date: Sat, 27 Feb 2021 00:39:38 +0000 Subject: [PATCH] add: hotkeypressed + thieving plugin --- .../client/plugins/thieving/Chest.java | 75 +++++++ .../client/plugins/thieving/ChestOverlay.java | 125 +++++++++++ .../client/plugins/thieving/ChestRespawn.java | 54 +++++ .../plugins/thieving/ThievingConfig.java | 107 +++++++++ .../plugins/thieving/ThievingOverlay.java | 138 ++++++++++++ .../plugins/thieving/ThievingPlugin.java | 208 ++++++++++++++++++ .../plugins/thieving/ThievingSession.java | 61 +++++ .../runelite/client/util/HotkeyListener.java | 4 + 8 files changed, 772 insertions(+) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/thieving/Chest.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/thieving/ChestOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/thieving/ChestRespawn.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/thieving/ThievingConfig.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/thieving/ThievingOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/thieving/ThievingPlugin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/thieving/ThievingSession.java diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/thieving/Chest.java b/runelite-client/src/main/java/net/runelite/client/plugins/thieving/Chest.java new file mode 100644 index 0000000000..ec2b374990 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/thieving/Chest.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2019, whs + * 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.thieving; + +import com.google.common.collect.ImmutableMap; +import java.time.Duration; +import java.util.Map; +import lombok.AccessLevel; +import lombok.Getter; +import net.runelite.api.ObjectID; + +enum Chest +{ + TEN_COIN(Duration.ofMillis(6000), ObjectID.CHEST_11735), + FIFTY_COIN(Duration.ofMillis(46000), ObjectID.CHEST_11737), + NATURE_RUNE(Duration.ofMillis(10000), ObjectID.CHEST_11736), + STEEL_ARROWTIPS(Duration.ofMillis(77000), ObjectID.CHEST_11742), + AVERAGE_CHEST(Duration.ofMillis(90000), ObjectID.CHEST_22697), + BLOOD_RUNE(Duration.ofMillis(120000), ObjectID.CHEST_11738), + ARDOUGNE_CASTLE(Duration.ofMillis(400000), ObjectID.CHEST_11739), // FIXME: Please time + RICH_CHEST(Duration.ofMillis(300000), ObjectID.CHEST_22681), // FIXME: Please time + ROGUE_CASTLE(Duration.ofMillis(10000), ObjectID.CHEST_26757); // FIXME: Please time + + private static final Map CHESTS; + + static + { + ImmutableMap.Builder builder = new ImmutableMap.Builder<>(); + for (Chest chest : values()) + { + for (int id : chest.ids) + { + builder.put(id, chest); + } + } + CHESTS = builder.build(); + } + + @Getter(AccessLevel.PACKAGE) + private final Duration respawnTime; + private final int[] ids; + + Chest(Duration respawnTime, int... ids) + { + this.respawnTime = respawnTime; + this.ids = ids; + } + + static Chest of(int id) + { + return CHESTS.get(id); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/thieving/ChestOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/thieving/ChestOverlay.java new file mode 100644 index 0000000000..20d084df2b --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/thieving/ChestOverlay.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2019, whs + * 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.thieving; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.Iterator; +import java.util.List; +import javax.inject.Inject; +import javax.inject.Singleton; +import lombok.AccessLevel; +import lombok.Setter; +import net.runelite.api.Client; +import net.runelite.api.Perspective; +import net.runelite.api.Point; +import net.runelite.api.coords.LocalPoint; +import net.runelite.api.coords.WorldPoint; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayLayer; +import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.components.ProgressPieComponent; + +@Singleton +class ChestOverlay extends Overlay +{ + private final Client client; + private final List respawns; + + @Setter(AccessLevel.PACKAGE) + private Color pieFillColor; + @Setter(AccessLevel.PACKAGE) + private Color pieBorderColor; + @Setter(AccessLevel.PACKAGE) + private boolean respawnPieInverted; + @Setter(AccessLevel.PACKAGE) + private int respawnPieDiameter; + + @Inject + private ChestOverlay(final Client client, final ThievingPlugin plugin) + { + setPosition(OverlayPosition.DYNAMIC); + setLayer(OverlayLayer.ABOVE_SCENE); + this.respawns = plugin.getRespawns(); + this.client = client; + } + + @Override + public Dimension render(Graphics2D graphics) + { + if (respawns.isEmpty()) + { + return null; + } + + Instant now = Instant.now(); + for (Iterator it = respawns.iterator(); it.hasNext(); ) + { + ChestRespawn chestRespawn = it.next(); + + float percent = 1.0f - (now.until(chestRespawn.getEndTime(), ChronoUnit.MILLIS) / (float) chestRespawn.getRespawnTime()); + if (percent > 1.0f) + { + it.remove(); + continue; + } + + if (chestRespawn.getWorld() != client.getWorld()) + { + continue; + } + + WorldPoint worldPoint = chestRespawn.getWorldPoint(); + LocalPoint loc = LocalPoint.fromWorld(client, worldPoint); + if (loc == null) + { + continue; + } + + Point point = Perspective.localToCanvas(client, loc, client.getPlane(), 0); + if (point == null) + { + continue; + } + + if (respawnPieInverted) + { + percent = 1.0f - percent; + } + + ProgressPieComponent ppc = new ProgressPieComponent(); + ppc.setDiameter(respawnPieDiameter); + ppc.setBorderColor(pieBorderColor); + ppc.setFill(pieFillColor); + ppc.setPosition(point); + ppc.setProgress(percent); + ppc.render(graphics); + } + return null; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/thieving/ChestRespawn.java b/runelite-client/src/main/java/net/runelite/client/plugins/thieving/ChestRespawn.java new file mode 100644 index 0000000000..f8231dd5c0 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/thieving/ChestRespawn.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2019, whs + * 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.thieving; + +import java.time.Instant; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import net.runelite.api.coords.WorldPoint; + +@RequiredArgsConstructor +@Getter(AccessLevel.PACKAGE) +class ChestRespawn +{ + private final Chest chest; + private final WorldPoint worldPoint; + private final Instant endTime; + private final int world; + + private long respawnTime = -1; + + long getRespawnTime() + { + if (respawnTime != -1) + { + return respawnTime; + } + + respawnTime = chest.getRespawnTime().toMillis(); + return respawnTime; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/thieving/ThievingConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/thieving/ThievingConfig.java new file mode 100644 index 0000000000..5ca9685773 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/thieving/ThievingConfig.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2018, Joris K + * Copyright (c) 2018, Lasse + * Copyright (c) 2019, ermalsh + * 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.thieving; + +import java.awt.Color; +import net.runelite.client.config.Alpha; +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; +import net.runelite.client.config.ConfigSection; +import net.runelite.client.config.Range; +import net.runelite.client.config.Units; + +@ConfigGroup("thieving") +public interface ThievingConfig extends Config +{ + @ConfigSection( + keyName = "sessionTitle", + name = "Session", + description = "", + position = 1 + ) + String sessionTitle = "Session"; + + @ConfigItem( + position = 2, + keyName = "statTimeout", + name = "Reset stats", + description = "Change the time until the thieving session is reset and the overlay is hidden", + section = sessionTitle + ) + @Units(Units.MINUTES) + default int statTimeout() + { + return 5; + } + + @ConfigSection( + name = "Chest", + description = "", + position = 2, + keyName = "chestTitle" + ) + String chestTitle = "Chest"; + + @Alpha + @ConfigItem( + keyName = "respawnColor", + name = "Respawn timer color", + description = "Configures the color of the respawn timer", + section = chestTitle + ) + default Color respawnColor() + { + return Color.YELLOW; + } + + @ConfigItem( + keyName = "respawnPieInverted", + name = "Invert respawn timer", + description = "Configures whether the respawn timer goes from empty to full or the other way around", + section = chestTitle + ) + default boolean respawnPieInverted() + { + return false; + } + + @Range( + min = 1, + max = 50 + ) + @ConfigItem( + keyName = "respawnPieDiameter", + name = "Respawn pie diameter", + description = "Configures how big the respawn timer pie is", + section = chestTitle + ) + default int respawnPieDiameter() + { + return 30; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/thieving/ThievingOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/thieving/ThievingOverlay.java new file mode 100644 index 0000000000..61589dabf9 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/thieving/ThievingOverlay.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2018, Joris K + * Copyright (c) 2018, Lasse + * Copyright (c) 2019, ermalsh + * 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.thieving; + +import com.openosrs.client.ui.overlay.components.table.TableAlignment; +import com.openosrs.client.ui.overlay.components.table.TableComponent; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.text.DecimalFormat; +import javax.inject.Inject; +import javax.inject.Singleton; +import net.runelite.api.Client; +import net.runelite.api.Skill; +import net.runelite.client.plugins.xptracker.XpTrackerService; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.components.PanelComponent; +import net.runelite.client.ui.overlay.components.TitleComponent; + +@Singleton +public class ThievingOverlay extends Overlay +{ + private static final DecimalFormat FORMAT = new DecimalFormat("#.#"); + + private final Client client; + private final ThievingPlugin plugin; + private final XpTrackerService xpTrackerService; + private final PanelComponent panelComponent = new PanelComponent(); + public static final int PICKPOCKET_FAIL = 424; + public static final int THIEVING_STALL = 832; + public static final int PICKPOCKET_SUCCESS = 881; + + @Inject + private ThievingOverlay(final Client client, final ThievingPlugin plugin, final XpTrackerService xpTrackerService) + { + setPosition(OverlayPosition.TOP_LEFT); + this.client = client; + this.plugin = plugin; + this.xpTrackerService = xpTrackerService; + } + + @Override + public Dimension render(Graphics2D graphics) + { + ThievingSession session = plugin.getSession(); + if (session == null) + { + return null; + } + + panelComponent.setPreferredSize(new Dimension(145, 0)); + panelComponent.getChildren().clear(); + + if (isThieving()) + { + panelComponent.getChildren().add(TitleComponent.builder() + .text("Thieving") + .color(Color.GREEN) + .build()); + } + else if (isStunned()) + { + panelComponent.getChildren().add(TitleComponent.builder() + .text("Stunned") + .color(Color.ORANGE) + .build()); + } + else + { + panelComponent.getChildren().add(TitleComponent.builder() + .text("NOT thieving") + .color(Color.RED) + .build()); + } + + TableComponent tableComponent = new TableComponent(); + tableComponent.setColumnAlignments(TableAlignment.LEFT, TableAlignment.RIGHT); + + tableComponent.addRow("Succeeded:", session.getSuccessful() + (session.getSuccessful() >= 1 ? " (" + xpTrackerService.getActionsHr(Skill.THIEVING) + "/hr)" : "")); + tableComponent.addRow("Failed:", session.getFailed() + (session.getFailed() >= 1 ? " (" + FORMAT.format(session.getSuccessRate()) + "%)" : "")); + + panelComponent.getChildren().add(tableComponent); + + return panelComponent.render(graphics); + } + + private boolean isThieving() + { + if (client.getLocalPlayer() == null) + { + return false; + } + + switch (client.getLocalPlayer().getAnimation()) + { + case THIEVING_STALL: + case PICKPOCKET_SUCCESS: + return true; + default: + return false; + } + } + + private boolean isStunned() + { + if (client.getLocalPlayer() == null) + { + return false; + } + + return client.getLocalPlayer().getAnimation() == PICKPOCKET_FAIL; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/thieving/ThievingPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/thieving/ThievingPlugin.java new file mode 100644 index 0000000000..ec93373fae --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/thieving/ThievingPlugin.java @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2018, Joris K + * Copyright (c) 2018, Lasse + * Copyright (c) 2019, ermalsh + * 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.thieving; + +import com.google.inject.Provides; +import java.time.Duration; +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; +import javax.inject.Inject; +import lombok.AccessLevel; +import lombok.Getter; +import net.runelite.api.ChatMessageType; +import net.runelite.api.Client; +import net.runelite.api.GameObject; +import net.runelite.api.GameState; +import net.runelite.api.events.ChatMessage; +import net.runelite.api.events.GameObjectDespawned; +import net.runelite.api.events.GameStateChanged; +import net.runelite.api.events.GameTick; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.events.ConfigChanged; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDependency; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.xptracker.XpTrackerPlugin; +import net.runelite.client.ui.overlay.OverlayManager; +import org.pf4j.Extension; + +@Extension +@PluginDescriptor( + name = "Thieving", + enabledByDefault = false, + description = "Show thieving overlay", + tags = {"overlay", "skilling", "thieving", "pickpocketing"} +) +@PluginDependency(XpTrackerPlugin.class) +public class ThievingPlugin extends Plugin +{ + @Inject + private Client client; + + @Inject + private ThievingConfig config; + + @Inject + private ThievingOverlay overlay; + + @Inject + private ChestOverlay chestOverlay; + + @Inject + private OverlayManager overlayManager; + + @Getter(AccessLevel.PACKAGE) + private ThievingSession session; + + @Getter(AccessLevel.PACKAGE) + private final List respawns = new ArrayList<>(); + + private boolean recentlyLoggedIn = false; + + @Provides + ThievingConfig getConfig(ConfigManager configManager) + { + return configManager.getConfig(ThievingConfig.class); + } + + @Override + protected void startUp() + { + chestOverlay.setPieFillColor(config.respawnColor()); + chestOverlay.setPieBorderColor(config.respawnColor().darker()); + chestOverlay.setRespawnPieInverted(config.respawnPieInverted()); + chestOverlay.setRespawnPieDiameter(config.respawnPieDiameter()); + + session = null; + overlayManager.add(overlay); + overlayManager.add(chestOverlay); + } + + @Override + protected void shutDown() + { + overlayManager.remove(overlay); + overlayManager.remove(chestOverlay); + session = null; + } + + @Subscribe + private void onGameStateChanged(GameStateChanged event) + { + if (event.getGameState() == GameState.LOGGED_IN) + { + recentlyLoggedIn = true; + } + } + + @Subscribe + private void onGameTick(GameTick gameTick) + { + recentlyLoggedIn = false; + + if (session == null || config.statTimeout() == 0) + { + return; + } + + Duration statTimeout = Duration.ofMinutes(config.statTimeout()); + Duration sinceCut = Duration.between(session.getLastThievingAction(), Instant.now()); + + if (sinceCut.compareTo(statTimeout) >= 0) + { + session = null; + } + } + + @Subscribe + private void onChatMessage(ChatMessage event) + { + if (event.getType() != ChatMessageType.SPAM) + { + return; + } + + final String message = event.getMessage(); + + if (message.startsWith("You pickpocket") || message.startsWith("You pick-pocket") || message.startsWith("You steal") + || message.startsWith("You successfully pick-pocket") || message.startsWith("You successfully pick") || message.startsWith("You successfully steal") + || message.startsWith("You pick the knight") || message.startsWith("You pick the Elf") || message.startsWith("You pick the vyre")) + { + if (session == null) + { + session = new ThievingSession(); + } + + session.updateLastThievingAction(); + session.hasSucceeded(); + } + else if (message.startsWith("You fail to pick") || message.startsWith("You fail to steal")) + { + if (session == null) + { + session = new ThievingSession(); + } + + session.updateLastThievingAction(); + session.hasFailed(); + } + } + + @Subscribe + private void onGameObjectDespawned(GameObjectDespawned event) + { + if (client.getGameState() != GameState.LOGGED_IN || recentlyLoggedIn) + { + return; + } + + final GameObject object = event.getGameObject(); + + Chest chest = Chest.of(object.getId()); + if (chest != null) + { + ChestRespawn chestRespawn = new ChestRespawn(chest, object.getWorldLocation(), Instant.now().plus(chest.getRespawnTime()), client.getWorld()); + respawns.add(chestRespawn); + } + } + + @Subscribe + private void onConfigChanged(ConfigChanged event) + { + if (!"thieving".equals(event.getGroup())) + { + return; + } + + chestOverlay.setPieFillColor(config.respawnColor()); + chestOverlay.setPieBorderColor(config.respawnColor().darker()); + chestOverlay.setRespawnPieInverted(config.respawnPieInverted()); + chestOverlay.setRespawnPieDiameter(config.respawnPieDiameter()); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/thieving/ThievingSession.java b/runelite-client/src/main/java/net/runelite/client/plugins/thieving/ThievingSession.java new file mode 100644 index 0000000000..56abcafd7f --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/thieving/ThievingSession.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2018, Joris K + * Copyright (c) 2018, Lasse + * Copyright (c) 2019, ermalsh + * 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.thieving; + +import java.time.Instant; +import lombok.AccessLevel; +import lombok.Getter; + +class ThievingSession +{ + @Getter(AccessLevel.PACKAGE) + private Instant lastThievingAction; + @Getter(AccessLevel.PACKAGE) + private int successful; + @Getter(AccessLevel.PACKAGE) + private int failed; + + void updateLastThievingAction() + { + this.lastThievingAction = Instant.now(); + } + + void hasSucceeded() + { + this.successful++; + } + + void hasFailed() + { + this.failed++; + } + + double getSuccessRate() + { + return ((double) getFailed() / (getSuccessful() + getFailed())) * 100; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/util/HotkeyListener.java b/runelite-client/src/main/java/net/runelite/client/util/HotkeyListener.java index 2099564846..6f5555bfe7 100644 --- a/runelite-client/src/main/java/net/runelite/client/util/HotkeyListener.java +++ b/runelite-client/src/main/java/net/runelite/client/util/HotkeyListener.java @@ -91,4 +91,8 @@ public abstract class HotkeyListener implements KeyListener public void hotkeyPressed() { } + + protected void hotkeyReleased() + { + } }