diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/smelting/SmeltingConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/smelting/SmeltingConfig.java new file mode 100644 index 0000000000..9671f29e1a --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/smelting/SmeltingConfig.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2019, Stephen + * 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.smelting; + +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("smelting") +public interface SmeltingConfig extends Config +{ + @ConfigItem( + position = 1, + keyName = "statTimeout", + name = "Reset stats (minutes)", + description = "The time it takes for the current smelting session to be reset" + ) + default int statTimeout() + { + return 5; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/smelting/SmeltingOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/smelting/SmeltingOverlay.java new file mode 100644 index 0000000000..7a49c00144 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/smelting/SmeltingOverlay.java @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2019, Stephen + * 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.smelting; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.time.Duration; +import java.time.Instant; +import javax.inject.Inject; +import static net.runelite.api.AnimationID.SMITHING_CANNONBALL; +import static net.runelite.api.AnimationID.SMITHING_SMELTING; +import net.runelite.api.Client; +import static net.runelite.api.MenuAction.RUNELITE_OVERLAY_CONFIG; +import net.runelite.api.Skill; +import net.runelite.client.plugins.xptracker.XpTrackerService; +import net.runelite.client.ui.overlay.Overlay; +import static net.runelite.client.ui.overlay.OverlayManager.OPTION_CONFIGURE; +import net.runelite.client.ui.overlay.OverlayMenuEntry; +import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.components.LineComponent; +import net.runelite.client.ui.overlay.components.PanelComponent; +import net.runelite.client.ui.overlay.components.TitleComponent; + +class SmeltingOverlay extends Overlay +{ + private static final int SMELT_TIMEOUT = 5; + + private final Client client; + private final SmeltingPlugin plugin; + private final XpTrackerService xpTrackerService; + + private final PanelComponent panelComponent = new PanelComponent(); + + @Inject + SmeltingOverlay(Client client, SmeltingPlugin plugin, XpTrackerService xpTrackerService) + { + super(plugin); + this.client = client; + this.plugin = plugin; + this.xpTrackerService = xpTrackerService; + setPosition(OverlayPosition.TOP_LEFT); + getMenuEntries().add(new OverlayMenuEntry(RUNELITE_OVERLAY_CONFIG, OPTION_CONFIGURE, "Smelting overlay")); + } + + @Override + public Dimension render(Graphics2D graphics) + { + SmeltingSession session = plugin.getSession(); + if (session == null) + { + return null; + } + + panelComponent.getChildren().clear(); + + if (isSmelting() || Duration.between(session.getLastItemSmelted(), Instant.now()).getSeconds() < SMELT_TIMEOUT) + { + panelComponent.getChildren().add(TitleComponent.builder() + .text("Smelting") + .color(Color.GREEN) + .build()); + } + else + { + panelComponent.getChildren().add(TitleComponent.builder() + .text("NOT smelting") + .color(Color.RED) + .build()); + } + + int actions = xpTrackerService.getActions(Skill.SMITHING); + if (actions > 0) + { + if (plugin.getSession().getBarsSmelted() > 0) + { + panelComponent.getChildren().add(LineComponent.builder() + .left("Bars:") + .right(Integer.toString(session.getBarsSmelted())) + .build()); + } + if (plugin.getSession().getCannonBallsSmelted() > 0) + { + panelComponent.getChildren().add(LineComponent.builder() + .left("Cannonballs:") + .right(Integer.toString(session.getCannonBallsSmelted())) + .build()); + } + if (actions > 2) + { + panelComponent.getChildren().add(LineComponent.builder() + .left("Actions/hr:") + .right(Integer.toString(xpTrackerService.getActionsHr(Skill.SMITHING))) + .build()); + } + } + + return panelComponent.render(graphics); + + } + + private boolean isSmelting() + { + switch (client.getLocalPlayer().getAnimation()) + { + case SMITHING_SMELTING: + case SMITHING_CANNONBALL: + return true; + default: + return false; + } + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/smelting/SmeltingPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/smelting/SmeltingPlugin.java new file mode 100644 index 0000000000..21a3b5440b --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/smelting/SmeltingPlugin.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2019, Stephen + * 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.smelting; + +import com.google.inject.Provides; +import java.time.Duration; +import java.time.Instant; +import javax.inject.Inject; +import lombok.AccessLevel; +import lombok.Getter; +import net.runelite.api.ChatMessageType; +import net.runelite.api.events.ChatMessage; +import net.runelite.api.events.GameTick; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +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; + +@PluginDescriptor( + name = "Smelting", + description = "Show Smelting stats", + tags = {"overlay", "skilling"} +) +@PluginDependency(XpTrackerPlugin.class) +public class SmeltingPlugin extends Plugin +{ + @Inject + private SmeltingConfig config; + + @Inject + private SmeltingOverlay overlay; + + @Inject + private OverlayManager overlayManager; + + @Getter(AccessLevel.PACKAGE) + private SmeltingSession session; + + @Provides + SmeltingConfig getConfig(ConfigManager configManager) + { + return configManager.getConfig(SmeltingConfig.class); + } + + @Override + protected void startUp() + { + session = null; + overlayManager.add(overlay); + } + + @Override + protected void shutDown() + { + overlayManager.remove(overlay); + session = null; + } + + @Subscribe + public void onChatMessage(ChatMessage event) + { + if (event.getType() != ChatMessageType.SPAM) + { + return; + } + + if (event.getMessage().startsWith("You retrieve a bar of")) + { + if (session == null) + { + session = new SmeltingSession(); + } + session.increaseBarsSmelted(); + } + else if (event.getMessage().startsWith("You remove the cannonballs from the mould")) + { + if (session == null) + { + session = new SmeltingSession(); + } + session.increaseCannonBallsSmelted(); + } + } + + @Subscribe + public void onGameTick(GameTick event) + { + if (session != null) + { + final Duration statTimeout = Duration.ofMinutes(config.statTimeout()); + final Duration sinceCaught = Duration.between(session.getLastItemSmelted(), Instant.now()); + + if (sinceCaught.compareTo(statTimeout) >= 0) + { + session = null; + } + } + } +} + diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/smelting/SmeltingSession.java b/runelite-client/src/main/java/net/runelite/client/plugins/smelting/SmeltingSession.java new file mode 100644 index 0000000000..b49cfff6c0 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/smelting/SmeltingSession.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2019, Stephen + * 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.smelting; + +import java.time.Instant; +import lombok.AccessLevel; +import lombok.Getter; + +class SmeltingSession +{ + @Getter(AccessLevel.PACKAGE) + private int barsSmelted; + + @Getter(AccessLevel.PACKAGE) + private int cannonBallsSmelted; + + @Getter(AccessLevel.PACKAGE) + private Instant lastItemSmelted; + + void increaseBarsSmelted() + { + barsSmelted++; + lastItemSmelted = Instant.now(); + } + + void increaseCannonBallsSmelted() + { + cannonBallsSmelted += 4; + lastItemSmelted = Instant.now(); + } +} \ No newline at end of file diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/smelting/SmeltingPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/smelting/SmeltingPluginTest.java new file mode 100644 index 0000000000..1c8573f26c --- /dev/null +++ b/runelite-client/src/test/java/net/runelite/client/plugins/smelting/SmeltingPluginTest.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2019, Stephen + * 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.smelting; + +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.events.ChatMessage; +import net.runelite.client.ui.overlay.OverlayManager; +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 org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class SmeltingPluginTest +{ + private static final String SMELT_CANNONBALL = "You remove the cannonballs from the mould"; + private static final String SMELT_BAR = "You retrieve a bar of steel."; + + @Inject + SmeltingPlugin smeltingPlugin; + + @Mock + @Bind + SmeltingConfig config; + + @Mock + @Bind + SmeltingOverlay smeltingOverlay; + + @Mock + @Bind + OverlayManager overlayManager; + + @Before + public void before() + { + Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this); + } + + @Test + public void testCannonballs() + { + ChatMessage chatMessage = new ChatMessage(null, ChatMessageType.SPAM, "", SMELT_CANNONBALL, "", 0); + smeltingPlugin.onChatMessage(chatMessage); + + SmeltingSession smeltingSession = smeltingPlugin.getSession(); + assertNotNull(smeltingSession); + assertEquals(4, smeltingSession.getCannonBallsSmelted()); + } + + @Test + public void testBars() + { + ChatMessage chatMessage = new ChatMessage(null, ChatMessageType.SPAM, "", SMELT_BAR, "", 0); + smeltingPlugin.onChatMessage(chatMessage); + + SmeltingSession smeltingSession = smeltingPlugin.getSession(); + assertNotNull(smeltingSession); + assertEquals(1, smeltingSession.getBarsSmelted()); + } +}