From 9e4969b598741e1ac178420b1797df4df9575e22 Mon Sep 17 00:00:00 2001 From: Seth Date: Sun, 25 Mar 2018 00:50:22 -0500 Subject: [PATCH] agility plugin: Add lap counter --- .../plugins/agilityplugin/AgilityConfig.java | 25 ++++- .../plugins/agilityplugin/AgilityPlugin.java | 74 ++++++++++++++- .../plugins/agilityplugin/AgilitySession.java | 67 ++++++++++++++ .../client/plugins/agilityplugin/Courses.java | 74 +++++++++++++++ .../agilityplugin/LapCounterOverlay.java | 91 +++++++++++++++++++ 5 files changed, 329 insertions(+), 2 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/agilityplugin/AgilitySession.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/agilityplugin/Courses.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/agilityplugin/LapCounterOverlay.java diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/agilityplugin/AgilityConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/agilityplugin/AgilityConfig.java index 7e2239d688..20b87d2c07 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/agilityplugin/AgilityConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/agilityplugin/AgilityConfig.java @@ -36,10 +36,33 @@ import net.runelite.client.config.ConfigItem; ) public interface AgilityConfig extends Config { + @ConfigItem( + keyName = "showLapCount", + name = "Show Lap count", + description = "Enable/disable the lap counter", + position = 1 + ) + default boolean showLapCount() + { + return true; + } + + @ConfigItem( + keyName = "lapTimeout", + name = "Hide Lap Count (minutes)", + description = "Time until the lap counter hides/resets", + position = 2 + ) + default int lapTimeout() + { + return 5; + } + @ConfigItem( keyName = "overlayColor", name = "Overlay Color", - description = "Color of Agility overlay" + description = "Color of Agility overlay", + position = 3 ) default Color getOverlayColor() { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/agilityplugin/AgilityPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/agilityplugin/AgilityPlugin.java index b9f265927a..385a4762c6 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/agilityplugin/AgilityPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/agilityplugin/AgilityPlugin.java @@ -26,16 +26,22 @@ package net.runelite.client.plugins.agilityplugin; import com.google.common.eventbus.Subscribe; import com.google.inject.Provides; +import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; +import java.util.Map; import javax.inject.Inject; import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import net.runelite.api.Client; import net.runelite.api.GameState; +import static net.runelite.api.Skill.AGILITY; import net.runelite.api.Tile; import net.runelite.api.TileObject; import net.runelite.api.events.DecorativeObjectChanged; import net.runelite.api.events.DecorativeObjectDespawned; import net.runelite.api.events.DecorativeObjectSpawned; +import net.runelite.api.events.ExperienceChanged; import net.runelite.api.events.GameObjectChanged; import net.runelite.api.events.GameObjectDespawned; import net.runelite.api.events.GameObjectSpawned; @@ -49,6 +55,7 @@ import net.runelite.api.events.WallObjectSpawned; import net.runelite.client.config.ConfigManager; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.ui.overlay.Overlay; @PluginDescriptor( name = "Agility" @@ -57,22 +64,87 @@ import net.runelite.client.plugins.PluginDescriptor; public class AgilityPlugin extends Plugin { @Getter - private final HashMap obstacles = new HashMap<>(); + private final Map obstacles = new HashMap<>(); @Inject @Getter private AgilityOverlay overlay; + @Inject + private LapCounterOverlay lapOverlay; + + @Inject + private Client client; + + @Inject + private AgilityConfig config; + + @Getter + private AgilitySession session; + + private int lastAgilityXp; + @Provides AgilityConfig getConfig(ConfigManager configManager) { return configManager.getConfig(AgilityConfig.class); } + @Override + public Collection getOverlays() + { + return Arrays.asList(overlay, lapOverlay); + } + @Override protected void shutDown() throws Exception { obstacles.clear(); + session = null; + } + + @Subscribe + public void onGameStateChange(GameStateChanged event) + { + switch (event.getGameState()) + { + case HOPPING: + case LOGIN_SCREEN: + session = null; + } + } + + @Subscribe + public void onExperienceChanged(ExperienceChanged event) + { + if (event.getSkill() != AGILITY || !config.showLapCount()) + { + return; + } + + // Determine how much EXP was actually gained + int agilityXp = client.getSkillExperience(AGILITY); + int skillGained = agilityXp - lastAgilityXp; + lastAgilityXp = agilityXp; + + // Get course + Courses course = Courses.getCourse(skillGained); + if (course == null) + { + return; + } + + if (session != null && session.getCourse() == course) + { + session.incrementLapCount(client); + } + else + { + session = new AgilitySession(course); + // New course found, reset lap count and set new course + session.resetLapCount(); + session.incrementLapCount(client); + } } // This code, brought to you, in part, by the letters C and V diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/agilityplugin/AgilitySession.java b/runelite-client/src/main/java/net/runelite/client/plugins/agilityplugin/AgilitySession.java new file mode 100644 index 0000000000..64d84a145c --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/agilityplugin/AgilitySession.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2018, Seth + * 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.agilityplugin; + +import java.time.Instant; +import lombok.Getter; +import lombok.Setter; +import net.runelite.api.Client; +import net.runelite.api.Experience; +import net.runelite.api.Skill; + +@Getter +@Setter +public class AgilitySession +{ + private final Courses course; + private Instant lastLapCompleted; + private int totalLaps; + private int lapsTillLevel; + + public AgilitySession(Courses course) + { + this.course = course; + } + + public void incrementLapCount(Client client) + { + Instant now = Instant.now(); + + lastLapCompleted = now; + ++totalLaps; + + int currentExp = client.getSkillExperience(Skill.AGILITY); + int nextLevel = client.getRealSkillLevel(Skill.AGILITY) + 1; + int remainingXp = nextLevel <= Experience.MAX_VIRT_LEVEL ? Experience.getXpForLevel(nextLevel) - currentExp : 0; + + lapsTillLevel = remainingXp > 0 ? (int) Math.ceil(remainingXp / course.getTotalXp()) : 0; + } + + public void resetLapCount() + { + totalLaps = 0; + lapsTillLevel = 0; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/agilityplugin/Courses.java b/runelite-client/src/main/java/net/runelite/client/plugins/agilityplugin/Courses.java new file mode 100644 index 0000000000..2aa3c1fad6 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/agilityplugin/Courses.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2018, Seth + * 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.agilityplugin; + +import java.util.HashMap; +import java.util.Map; +import lombok.Getter; + +public enum Courses +{ + GNOME(86.5, 46), + DRAYNOR(120.0, 79), + AL_KARID(180.0, 30), + PYRAMID(722.0, 300), + VARROCK(238.0, 125), + PENGUIN(540.0, 65), + BARBARIAN(139.5, 60), + CANIFIS(240.0, 175), + APE_ATOLL(580.0, 300), + FALADOR(440, 180), + WILDERNESS(571.0, 499), + SEERS(570.0, 435), + POLLNIVEACH(890.0, 540), + RELLEKA(780.0, 475), + ARDOUGNE(793.0, 529); + + private final static Map courseXps = new HashMap<>(); + + @Getter + private final double totalXp; + + private final int lastObstacleXp; + + static + { + for (Courses course : values()) + { + courseXps.put(course.lastObstacleXp, course); + } + } + + Courses(double totalXp, int lastObstacleXp) + { + this.totalXp = totalXp; + this.lastObstacleXp = lastObstacleXp; + } + + public static Courses getCourse(int exp) + { + return courseXps.get(exp); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/agilityplugin/LapCounterOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/agilityplugin/LapCounterOverlay.java new file mode 100644 index 0000000000..392a9bd2a1 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/agilityplugin/LapCounterOverlay.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2018, Seth + * 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.agilityplugin; + +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.time.Duration; +import java.time.Instant; +import javax.inject.Inject; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.components.PanelComponent; + +public class LapCounterOverlay extends Overlay +{ + private final AgilityPlugin plugin; + private final AgilityConfig config; + + private final PanelComponent panelComponent = new PanelComponent(); + + @Inject + LapCounterOverlay(AgilityPlugin plugin, AgilityConfig config) + { + setPosition(OverlayPosition.TOP_LEFT); + this.plugin = plugin; + this.config = config; + } + + @Override + public Dimension render(Graphics2D graphics) + { + AgilitySession session = plugin.getSession(); + + if (!config.showLapCount() || + session == null || + session.getLastLapCompleted() == null || + session.getCourse() == null) + { + return null; + } + + Duration lapTimeout = Duration.ofMinutes(config.lapTimeout()); + Duration sinceLap = Duration.between(session.getLastLapCompleted(), Instant.now()); + + if (sinceLap.compareTo(lapTimeout) >= 0) + { + // timeout session + session.setLastLapCompleted(null); + return null; + } + + panelComponent.getLines().clear(); + + panelComponent.getLines().add(new PanelComponent.Line( + "Total Laps", + Integer.toString(session.getTotalLaps()) + )); + + if (session.getLapsTillLevel() > 0) + { + panelComponent.getLines().add(new PanelComponent.Line( + "Laps till level", + Integer.toString(session.getLapsTillLevel()) + )); + } + + return panelComponent.render(graphics); + } +}