From 0eb197d7b2a3aed5486a7b8f28a2b25f89ddb355 Mon Sep 17 00:00:00 2001 From: Seth Date: Tue, 18 Jul 2017 17:48:46 -0500 Subject: [PATCH] runelite-client: add infobox and timers plugin --- .../java/net/runelite/client/RuneLite.java | 7 + .../net/runelite/client/callback/Hooks.java | 6 + .../net/runelite/client/plugins/Plugin.java | 3 +- .../client/plugins/PluginManager.java | 2 + .../client/plugins/timers/GameTimer.java | 91 +++++++++ .../client/plugins/timers/TimerTimer.java | 45 +++++ .../client/plugins/timers/Timers.java | 189 ++++++++++++++++++ .../client/plugins/timers/TimersConfig.java | 126 ++++++++++++ .../client/ui/overlay/OverlayRenderer.java | 6 +- .../client/ui/overlay/infobox/Counter.java | 63 ++++++ .../client/ui/overlay/infobox/InfoBox.java | 57 ++++++ .../ui/overlay/infobox/InfoBoxManager.java | 77 +++++++ .../ui/overlay/infobox/InfoBoxOverlay.java | 110 ++++++++++ .../client/ui/overlay/infobox/Timer.java | 98 +++++++++ .../client/plugins/timers/antifire.png | Bin 0 -> 3456 bytes .../client/plugins/timers/antivenom.png | Bin 0 -> 693 bytes .../runelite/client/plugins/timers/cannon.png | Bin 0 -> 402 bytes .../client/plugins/timers/exantifire.png | Bin 0 -> 811 bytes .../client/plugins/timers/magicimbue.png | Bin 0 -> 826 bytes .../client/plugins/timers/overload.png | Bin 0 -> 525 bytes .../client/plugins/timers/stamina.png | Bin 0 -> 818 bytes .../client/plugins/timers/teleblock.png | Bin 0 -> 313 bytes 22 files changed, 877 insertions(+), 3 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/timers/GameTimer.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/timers/TimerTimer.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/timers/Timers.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/timers/TimersConfig.java create mode 100644 runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/Counter.java create mode 100644 runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBox.java create mode 100644 runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBoxManager.java create mode 100644 runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBoxOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/Timer.java create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/timers/antifire.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/timers/antivenom.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/timers/cannon.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/timers/exantifire.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/timers/magicimbue.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/timers/overload.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/timers/stamina.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/timers/teleblock.png diff --git a/runelite-client/src/main/java/net/runelite/client/RuneLite.java b/runelite-client/src/main/java/net/runelite/client/RuneLite.java index 5c33240e16..0f1fb47b6a 100644 --- a/runelite-client/src/main/java/net/runelite/client/RuneLite.java +++ b/runelite-client/src/main/java/net/runelite/client/RuneLite.java @@ -53,6 +53,7 @@ import net.runelite.client.plugins.PluginManager; import net.runelite.client.task.Scheduler; import net.runelite.client.ui.ClientUI; import net.runelite.client.ui.overlay.OverlayRenderer; +import net.runelite.client.ui.overlay.infobox.InfoBoxManager; import net.runelite.http.api.account.AccountClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -84,6 +85,7 @@ public class RuneLite private AccountSession accountSession; private final ConfigManager configManager = new ConfigManager(eventBus); private final ItemManager itemManager = new ItemManager(this); + private final InfoBoxManager infoBoxManager = new InfoBoxManager(); static { @@ -365,4 +367,9 @@ public class RuneLite { return itemManager; } + + public InfoBoxManager getInfoBoxManager() + { + return infoBoxManager; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/callback/Hooks.java b/runelite-client/src/main/java/net/runelite/client/callback/Hooks.java index af1e347a4c..41182b6637 100644 --- a/runelite-client/src/main/java/net/runelite/client/callback/Hooks.java +++ b/runelite-client/src/main/java/net/runelite/client/callback/Hooks.java @@ -34,6 +34,7 @@ import net.runelite.client.events.*; import net.runelite.client.game.DeathChecker; import net.runelite.client.task.Scheduler; import net.runelite.client.ui.overlay.OverlayRenderer; +import net.runelite.client.ui.overlay.infobox.InfoBoxManager; import net.runelite.rs.api.MainBufferProvider; import net.runelite.rs.api.MessageNode; import org.slf4j.Logger; @@ -70,8 +71,13 @@ public class Hooks logger.warn("error during death check", ex); } + // tick pending scheduled tasks Scheduler scheduler = runelite.getScheduler(); scheduler.tick(); + + // cull infoboxes + InfoBoxManager infoBoxManager = runelite.getInfoBoxManager(); + infoBoxManager.cull(); } public static void draw(Object provider, Graphics graphics, int x, int y) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/Plugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/Plugin.java index 60d940fe0c..2dcfdab658 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/Plugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/Plugin.java @@ -25,7 +25,6 @@ package net.runelite.client.plugins; import com.google.common.util.concurrent.AbstractIdleService; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.concurrent.Executor; @@ -41,7 +40,7 @@ public abstract class Plugin extends AbstractIdleService public Collection getOverlays() { Overlay overlay = getOverlay(); - return overlay != null ? Arrays.asList(overlay) : Collections.EMPTY_LIST; + return overlay != null ? Collections.singletonList(overlay) : Collections.EMPTY_LIST; } /** diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java index f19d362641..9d0440aac9 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java @@ -55,6 +55,7 @@ import net.runelite.client.plugins.pestcontrol.PestControl; import net.runelite.client.plugins.pricecommands.PriceCommands; import net.runelite.client.plugins.rememberusername.RememberUsername; import net.runelite.client.plugins.runecraft.Runecraft; +import net.runelite.client.plugins.timers.Timers; import net.runelite.client.plugins.woodcutting.WoodcuttingPlugin; import net.runelite.client.plugins.xpglobes.XpGlobes; import net.runelite.client.plugins.xptracker.XPTracker; @@ -106,6 +107,7 @@ public class PluginManager plugins.add(new RememberUsername()); plugins.add(new PriceCommands()); plugins.add(new ClueScrollPlugin()); + plugins.add(new Timers()); if (RuneLite.getOptions().has("developer-mode")) { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timers/GameTimer.java b/runelite-client/src/main/java/net/runelite/client/plugins/timers/GameTimer.java new file mode 100644 index 0000000000..431b934df0 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timers/GameTimer.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2017, Seth + * Copyright (c) 2017, Adam + * 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.timers; + +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.io.InputStream; +import java.time.Duration; +import java.time.temporal.ChronoUnit; +import javax.imageio.ImageIO; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public enum GameTimer +{ + STAMINA("stamina", 2, ChronoUnit.MINUTES), + ANTIFIRE("antifire", 6, ChronoUnit.MINUTES), + EXANTIFIRE("exantifire", 12, ChronoUnit.MINUTES), + OVERLOAD("overload", 5, ChronoUnit.MINUTES), + CANNON("cannon", 25, ChronoUnit.MINUTES), + MAGICIMBUE("magicimbue", 15, ChronoUnit.SECONDS), + FULLTB("teleblock", 5, ChronoUnit.MINUTES), + HALFTB("teleblock", 150, ChronoUnit.SECONDS), + SUPERANTIVENOM("antivenom", 3, ChronoUnit.MINUTES); + + private static final Logger logger = LoggerFactory.getLogger(GameTimer.class); + + private final String imageResource; + private final Duration duration; + + private BufferedImage image; + + GameTimer(String imageResource, long time, ChronoUnit unit) + { + this.imageResource = imageResource; + this.duration = Duration.of(time, unit); + } + + public String getImageResource() + { + return imageResource; + } + + public Duration getDuration() + { + return duration; + } + + public BufferedImage getImage() + { + if (image != null) + { + return image; + } + + InputStream in = GameTimer.class.getResourceAsStream(imageResource + ".png"); + try + { + image = ImageIO.read(in); + } + catch (IOException ex) + { + logger.warn("unable to load image", ex); + } + + return image; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimerTimer.java b/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimerTimer.java new file mode 100644 index 0000000000..4b7e77a693 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimerTimer.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2017, Adam + * 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.timers; + +import java.time.temporal.ChronoUnit; +import net.runelite.client.ui.overlay.infobox.Timer; + +public class TimerTimer extends Timer +{ + private final GameTimer timer; + + public TimerTimer(GameTimer timer) + { + super(timer.getDuration().toMillis(), ChronoUnit.MILLIS, timer.getImage()); + this.timer = timer; + } + + public GameTimer getTimer() + { + return timer; + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timers/Timers.java b/runelite-client/src/main/java/net/runelite/client/plugins/timers/Timers.java new file mode 100644 index 0000000000..e61c10fa53 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timers/Timers.java @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2017, 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.timers; + +import com.google.common.eventbus.Subscribe; +import net.runelite.api.ChatMessageType; +import net.runelite.client.RuneLite; +import net.runelite.client.events.ChatMessage; +import net.runelite.client.events.ConfigChanged; +import net.runelite.client.plugins.Plugin; +import static net.runelite.client.plugins.timers.GameTimer.*; +import net.runelite.client.ui.overlay.infobox.InfoBoxManager; + +public class Timers extends Plugin +{ + private final TimersConfig config = RuneLite.getRunelite().getConfigManager().getConfig(TimersConfig.class); + + private final InfoBoxManager infoBoxManager = RuneLite.getRunelite().getInfoBoxManager(); + + @Override + protected void startUp() throws Exception + { + } + + @Override + protected void shutDown() throws Exception + { + } + + public TimersConfig getConfig() + { + return config; + } + + @Subscribe + public void updateConfig(ConfigChanged event) + { + if (!config.showStamina()) + { + removeGameTimer(STAMINA); + } + + if (!config.showAntiFire()) + { + removeGameTimer(ANTIFIRE); + } + + if (!config.showExAntiFire()) + { + removeGameTimer(EXANTIFIRE); + } + + if (!config.showOverload()) + { + removeGameTimer(OVERLOAD); + } + + if (!config.showCannon()) + { + removeGameTimer(CANNON); + } + + if (!config.showMagicImbue()) + { + removeGameTimer(MAGICIMBUE); + } + + if (!config.showAntiVenom()) + { + removeGameTimer(SUPERANTIVENOM); + } + if (!config.showTeleblock()) + { + removeGameTimer(FULLTB); + removeGameTimer(HALFTB); + } + } + + @Subscribe + public void onChatMessage(ChatMessage event) + { + if (event.getType() != ChatMessageType.FILTERED && event.getType() != ChatMessageType.SERVER) + { + return; + } + + if (event.getMessage().equals("You drink some of your stamina potion.") && config.showStamina()) + { + createGameTimer(STAMINA); + } + + if (event.getMessage().equals("Your stamina potion has expired.")) + { + removeGameTimer(STAMINA); + } + + if (event.getMessage().equals("You drink some of your antifire potion.") && config.showAntiFire()) + { + createGameTimer(ANTIFIRE); + } + + if (event.getMessage().equals("You drink some of your extended antifire potion.") && config.showExAntiFire()) + { + createGameTimer(EXANTIFIRE); + } + + if (event.getMessage().equals("Your antifire potion has expired.")) + { + //they have the same expired message + removeGameTimer(ANTIFIRE); + removeGameTimer(EXANTIFIRE); + } + + if (event.getMessage().contains("You drink some of your overload potion") && config.showOverload()) + { + createGameTimer(OVERLOAD); + } + + if ((event.getMessage().equals("You add the furnace.") || event.getMessage().contains("You repair your cannon, restoring it to working order.")) && config.showCannon()) + { + createGameTimer(CANNON); + } + + if (event.getMessage().equals("You pick up the cannon. It's really heavy.")) + { + removeGameTimer(CANNON); + } + + if (event.getMessage().contains("You drink some of your super antivenom potion") && config.showAntiVenom()) + { + createGameTimer(SUPERANTIVENOM); + } + + if (event.getMessage().equals("You are charged to combine runes!") && config.showMagicImbue()) + { + createGameTimer(MAGICIMBUE); + } + + if (event.getMessage().equals("Your Magic Imbue charge has ended.")) + { + removeGameTimer(MAGICIMBUE); + } + + if (event.getMessage().equals("A teleblock spell has been cast on you. It will expire in 5 minutes, 0 seconds.") && config.showTeleblock()) + { + createGameTimer(FULLTB); + } + + if (event.getMessage().equals("A teleblock spell has been cast on you. It will expire in 2 minutes, 30 seconds.") && config.showTeleblock()) + { + createGameTimer(HALFTB); + } + } + + public void createGameTimer(GameTimer timer) + { + removeGameTimer(timer); + + TimerTimer t = new TimerTimer(timer); + infoBoxManager.addInfoBox(t); + } + + public void removeGameTimer(GameTimer timer) + { + infoBoxManager.removeIf(t -> t instanceof TimerTimer && ((TimerTimer) t).getTimer() == timer); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimersConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimersConfig.java new file mode 100644 index 0000000000..d31ecbd5b8 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimersConfig.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2017, 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.timers; + +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup( + keyName = "timers", + name = "Timers", + description = "Configuration for the timers plugin" +) +public interface TimersConfig +{ + @ConfigItem( + keyName = "enabled", + name = "Enable", + description = "Configures whether the timer plugin is displayed" + ) + default boolean enabled() + { + return true; + } + + @ConfigItem( + keyName = "showStamina", + name = "Stamina timer", + description = "Configures whether stamina timer is displayed" + ) + default boolean showStamina() + { + return true; + } + + @ConfigItem( + keyName = "showAntiFire", + name = "AntiFire timer", + description = "Configures whether antifire timer is displayed" + ) + default boolean showAntiFire() + { + return true; + } + + @ConfigItem( + keyName = "showExAntiFire", + name = "Extended Antifire timer", + description = "Configures whether extended antifire timer is displayed" + ) + default boolean showExAntiFire() + { + return true; + } + + @ConfigItem( + keyName = "showOverload", + name = "Overload timer", + description = "Configures whether overload timer is displayed" + ) + default boolean showOverload() + { + return true; + } + + @ConfigItem( + keyName = "showCannon", + name = "Cannon timer", + description = "Configures whether cannon timer is displayed" + ) + default boolean showCannon() + { + return true; + } + + @ConfigItem( + keyName = "showMagicImbue", + name = "Magic Imbue timer", + description = "Configures whether magic imbue timer is displayed" + ) + default boolean showMagicImbue() + { + return true; + } + + @ConfigItem( + keyName = "showTeleblock", + name = "Teleblock timer", + description = "Configures whether teleblock timer is displayed" + ) + default boolean showTeleblock() + { + return true; + } + + @ConfigItem( + keyName = "showAntiVenom", + name = "Anti Venom+ timer", + description = "Configures whether anti venom+ timer is displayed" + ) + default boolean showAntiVenom() + { + return true; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayRenderer.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayRenderer.java index 7e8e6b7d74..f5bdbce3a0 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayRenderer.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayRenderer.java @@ -25,12 +25,14 @@ package net.runelite.client.ui.overlay; import java.awt.image.BufferedImage; - import net.runelite.client.RuneLite; import net.runelite.client.plugins.Plugin; +import net.runelite.client.ui.overlay.infobox.InfoBoxOverlay; public class OverlayRenderer { + private final InfoBoxOverlay infoBoxOverlay = new InfoBoxOverlay(); + public void render(BufferedImage clientBuffer) { TopDownRendererLeft tdl = new TopDownRendererLeft(); @@ -56,6 +58,8 @@ public class OverlayRenderer } } + tdl.add(infoBoxOverlay); + tdl.render(clientBuffer); tdr.render(clientBuffer); dr.render(clientBuffer); diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/Counter.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/Counter.java new file mode 100644 index 0000000000..be9e026105 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/Counter.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2017, Adam + * 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.ui.overlay.infobox; + +import java.awt.Color; +import java.awt.image.BufferedImage; + +public class Counter extends InfoBox +{ + private String text; + + public Counter(BufferedImage image, String text) + { + super(image); + this.text = text; + } + + @Override + public String toString() + { + return "Counter{" + "text=" + text + '}'; + } + + @Override + public String getText() + { + return text; + } + + public void setText(String text) + { + this.text = text; + } + + @Override + public Color getTextColor() + { + return Color.WHITE; + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBox.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBox.java new file mode 100644 index 0000000000..347bd5d5fb --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBox.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2017, Adam + * 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.ui.overlay.infobox; + +import java.awt.Color; +import java.awt.image.BufferedImage; + +public abstract class InfoBox +{ + private final BufferedImage image; + + public InfoBox(BufferedImage image) + { + this.image = image; + } + + public BufferedImage getImage() + { + return image; + } + + public abstract String getText(); + + public abstract Color getTextColor(); + + public boolean render() + { + return true; + } + + public boolean cull() + { + return false; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBoxManager.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBoxManager.java new file mode 100644 index 0000000000..ef841c383c --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBoxManager.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2017, Adam + * 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.ui.overlay.infobox; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.function.Predicate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class InfoBoxManager +{ + private static final Logger logger = LoggerFactory.getLogger(InfoBoxManager.class); + + private final List infoBoxes = new ArrayList<>(); + + public void addInfoBox(InfoBox infoBox) + { + logger.debug("Adding InfoBox {}", infoBox); + infoBoxes.add(infoBox); + } + + public void removeInfoBox(InfoBox infoBox) + { + logger.debug("Removing InfoBox {}", infoBox); + infoBoxes.remove(infoBox); + } + + public void removeIf(Predicate filter) + { + logger.debug("Removing InfoBoxs for filter {}", filter); + infoBoxes.removeIf(filter); + } + + public List getInfoBoxes() + { + return Collections.unmodifiableList(infoBoxes); + } + + public void cull() + { + for (Iterator it = infoBoxes.iterator(); it.hasNext();) + { + InfoBox box = it.next(); + + if (box.cull()) + { + logger.debug("Culling InfoBox {}", box); + it.remove(); + } + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBoxOverlay.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBoxOverlay.java new file mode 100644 index 0000000000..abbdcf9e46 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBoxOverlay.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2017, Seth + * Copyright (c) 2017, Adam + * 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.ui.overlay.infobox; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.FontMetrics; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; +import java.util.List; +import net.runelite.api.Client; +import net.runelite.api.GameState; +import net.runelite.client.RuneLite; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.OverlayPriority; + +public class InfoBoxOverlay extends Overlay +{ + private static final int BOXSIZE = 35; + private static final int SEPARATOR = 2; + private static final Color BACKGROUND = new Color(Color.gray.getRed(), Color.gray.getGreen(), Color.gray.getBlue(), 127); + + private final RuneLite runelite = RuneLite.getRunelite(); + private final Client client = RuneLite.getClient(); + + public InfoBoxOverlay() + { + super(OverlayPosition.TOP_LEFT, OverlayPriority.LOW); + } + + @Override + public Dimension render(Graphics2D graphics) + { + if (client.getGameState() != GameState.LOGGED_IN) + { + return null; + } + + List infoBoxes = runelite.getInfoBoxManager().getInfoBoxes(); + + if (infoBoxes.isEmpty()) + { + return null; + } + + FontMetrics metrics = graphics.getFontMetrics(); + + int width = infoBoxes.size() * BOXSIZE; + int x = 0; + + for (InfoBox box : infoBoxes) + { + if (!box.render()) + { + continue; + } + + graphics.setColor(BACKGROUND); + graphics.fillRect(x, 0, BOXSIZE, BOXSIZE); + + graphics.setColor(Color.darkGray); + graphics.drawRect(x, 0, BOXSIZE, BOXSIZE); + + String str = box.getText(); + Color color = box.getTextColor(); + + BufferedImage image = box.getImage(); + if (image != null) + { + graphics.drawImage(image, x + (BOXSIZE - image.getWidth()) / 2, SEPARATOR, null); + } + + // text shaddow + graphics.setColor(Color.black); + graphics.drawString(str, x + ((BOXSIZE - metrics.stringWidth(str)) / 2) + 1, BOXSIZE - SEPARATOR + 1); + + graphics.setColor(color); + graphics.drawString(str, x + ((BOXSIZE - metrics.stringWidth(str)) / 2), BOXSIZE - SEPARATOR); + + x += BOXSIZE + SEPARATOR; + } + + return new Dimension(width, BOXSIZE); + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/Timer.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/Timer.java new file mode 100644 index 0000000000..197fd53597 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/Timer.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2017, Adam + * 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.ui.overlay.infobox; + +import com.google.common.base.Preconditions; +import java.awt.Color; +import java.awt.image.BufferedImage; +import java.time.Duration; +import java.time.Instant; +import java.time.temporal.ChronoUnit; + +public class Timer extends InfoBox +{ + private final Instant startTime; + private final Instant endTime; + private final Duration duration; + + public Timer(long period, ChronoUnit unit, BufferedImage image) + { + super(image); + + Preconditions.checkArgument(period > 0, "negative period!"); + + startTime = Instant.now(); + duration = Duration.of(period, unit); + endTime = startTime.plus(duration); + } + + @Override + public String toString() + { + return "Timer{" + "startTime=" + startTime + ", endTime=" + endTime + ", duration=" + duration + '}'; + } + + @Override + public String getText() + { + Duration timeLeft = Duration.between(Instant.now(), endTime); + + int seconds = (int) (timeLeft.toMillis() / 1000L); + + int minutes = (seconds % 3600) / 60; + int secs = seconds % 60; + + return String.format("%d:%02d", minutes, secs); + } + + @Override + public Color getTextColor() + { + Duration timeLeft = Duration.between(Instant.now(), endTime); + + //check if timer has 10% of time left + if (timeLeft.getSeconds() < (duration.getSeconds() * .10)) + { + return Color.RED.brighter(); + } + + return Color.WHITE; + } + + @Override + public boolean render() + { + Duration timeLeft = Duration.between(Instant.now(), endTime); + return !timeLeft.isNegative(); + } + + @Override + public boolean cull() + { + Duration timeLeft = Duration.between(Instant.now(), endTime); + return timeLeft.isZero() || timeLeft.isNegative(); + } + +} diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/timers/antifire.png b/runelite-client/src/main/resources/net/runelite/client/plugins/timers/antifire.png new file mode 100644 index 0000000000000000000000000000000000000000..cc1d3d25483ef4567f37abcf0507697320ed6801 GIT binary patch literal 3456 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 literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/timers/exantifire.png b/runelite-client/src/main/resources/net/runelite/client/plugins/timers/exantifire.png new file mode 100644 index 0000000000000000000000000000000000000000..b6007c0d41b12deeafe9904ef3b93ab215bba320 GIT binary patch literal 811 zcmV+`1JwM9P)Px%q6th2O|bK%OvMq%G{>g?wqP?iz)&iy4m`tT4x{CF$W z32T$+reIMRWoe`5x~02~rI}%6pfsz9b))+d=H`y!oB}yTVGIhRG@~qxFTVT=?-MRx zy0BvyDq}jeuMNQY`POK!(>cJPKOoEctgf!Ud7|3wY@$5Y*1A|QdcB-H@9aS|%f{n5 zv|g`A-tE%uboNrG63(4F&(u^#2muR$Uw`?X7t1fWaecnF5o+u7>b0x9`_9|LgnDaCfKvklkdVB-z1Zms7hjU=X_JUSa5}c$cX)Ud8r964$3W?v+ zNyTdu9HNTT1gFV}c4qp-1nn4U%k9Uvp)9M~PfJZ^{#lQpEQ zp&gNW&2WjJbzttqEG)%($0GY5ko|L=hCh;U!XfO>rbRm-6ZK=@G30 zcxmH(+Awyb7ov)40%&XmO>ok}sT+@JGK{5_)wW3~Xlw$Blv`;(nf@A(pb+Gzzh>Fh z8YasT%SzRwnqri!ZY=Wmv)_OqqYI_nR_(g2FH{r_FrjmH20 literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/timers/magicimbue.png b/runelite-client/src/main/resources/net/runelite/client/plugins/timers/magicimbue.png new file mode 100644 index 0000000000000000000000000000000000000000..e79369b21b17f52d6d37c57464064eb6ed5b5a81 GIT binary patch literal 826 zcmV-A1I7G_P)WFU8GbZ8()Nlj2>E@cM*00N##L_t(I%dM16NK{c2 z$A9;}H<=S61BHU3K??~{LY;<*QKo^R28niVid@tpSFNI{XeCG#6hcv((uBfQr6`$_ z$Ryf`+QLF2H%TDkqAv@b_vYRfulJlm3$hM8xF6^K?m7SS?h)}wJpiUDkaJ^7hVE0E!z=^X26|FCtlS5Vbf3KqA^8Y+HGI zrVBDmA492WUEP)h;MnyJKE7E2;K-F*Ja30@ALalUO^*``MF4m*T7|#f@il%kY|R8D7)1y_S|0Aw9Na>9HkC^XjYj$%>E$p%c*{iD+;G z=)#QE?;fnS*UE$Cg0_M-#||OchKZhsTdpf*IU45LfyWCh{2Kq59@r9OceY}ld*GD+ z)QLPFX_|iy^5pSo-rVkN;+)g`zNfV2fe-3R1adH8-Sg!KNGln12NEzZoi$Ma$c1R_p^I%#l{7t0d8J84?tm{ zjH1dKeESYln7&U(-#kT?HI#&!@*>_;Wn)hnFatnAaXAIW<=zYNmj>{cHqp^HkH0jq zr3Bm3@OLm^qd5$YPU`BmBqhNx05e_v1gdI0$Hn1Co6-YVPN%#^(Hsj4akMGu^*9#_ zP!bF?)71|^psI#X@8-PJOF~U7r&EsZi&MlSb;6JmB|(6oITrSAbbH<3v@flkU&gl8 zW?(M3ITjYV`ES)GVB2)D!AwsK{2fdA8e0*V_Px#9cffpbVF}#ZDnqB0000007G(RVRU6=Aa`kWXdp*PO;BVmWd{HN0iH=jK~zY` zwU)7N(@+$KzjG7Rn21ynV(X5?9)TD^;uSjZ7JYy|L1(5GDs_us1PP>Mple44R`!-g zt`m!xdk%xuHbL>di51_?w)~&({^viw7NSdR`PwAKus?oY^6cpoj^15ZNZ7?hZv~6OBW=A>!5d83k~ z6cf1zNpCXLlsum?n`L!GJIj8mqN?P1&TKkmnq_rzDmEN`BuP4$DF%Faf5z4CE6%@< zgM$#v>3BS*-`~Z#G9+q>a}u4bU`~@{2NA){fHFb11#0FLPEU?8GYmt`aN=;{7z|zn zb1GGS_Irn^RWNHc48aausbOq;4CO0FQTjcT=`C zQZ;KPQss`tw$`Y{W^IjhvHSA>kz8*~mp|GPI`2aTR*9|sFID}l`Z)OqtW?Hs(LRA{ P00000NkvXXu0mjf>B8zC literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/timers/stamina.png b/runelite-client/src/main/resources/net/runelite/client/plugins/timers/stamina.png new file mode 100644 index 0000000000000000000000000000000000000000..77aadd9784c79a3d852d8696324a9280ed49023a GIT binary patch literal 818 zcmV-21I_%2P)Px%>`6pHR7l6AmQAZ1MHGggbE>L)=7z)%a2Lc~KzBkextky+@#7z4kv|Z17qfRM zL3c`)D!2;SC6I{8l>}VqLU3U~goL1ALNdv9zWO@Teac18^vv9znNF?^#V|F~Z$0&% z_jI+;cu8FR9;NY)Cc#fW-eRXWV&~~DkDm^?`u)8|Vc)ds*3rWhMG5=c?~i%?$SYj< z;^(+dSerzrf_ZKfMLC)-eO52c3@ajq#T8yRZeQZ*(`Rr_fgyQrjB}$fqsWaDC*Q^U zz<1ZLF12B$ryB>_09?MAR&)LSLB^vot=5QcxBL8sYPVapc=UP$EE-uhWH{{aL$uXe z^m8(@EMqtrFzEO9lT(S8E?s70qeYAni;=It`j*a<4!3XJT-yk1bGrS*HZQ&S!lYne z(NLAf&RQ$lZl#C_W)Yc4sOta8DI7iY2Qfw><@Qw+6=idCYkxVtc>WX?jW@!t)ok8= zL}!%INq3)@RN|*+kF(JV3$ynhc2O0AcLWvQKla*6+NGo~A4+R;PL<#l=Y%9t#^#@u zRN{pXjuU)&LlI|VTBO7YD!P`ow*7+#(z)2o{ZS|dRY`(F9eAfrq!K^-=q(cOFpHQi z0;npYLI@63A@QEnJKouRwMM(JR6F$xl*`2hr=_>*APs&MX-OIU@+L8hGcgsCP^KX9 z9#yT;R?7shICT}-GG76LQ=H6b(;(QMBz@RLai~JV_{py;D?`_m0hI z6(9)?uUhkOUR-5XELFkDq!A{~AamtWfOL*>S#fF>_>#`Kk~T?{BzWF_^DrP$A3+tXJN!0|IMf$rrJKbIWeAMHo zI}fU7tn59KzdU!cs{C{TLCMV+jSK!Am$j^cw80E^JhQCvhjfhLY3aROn0^8u{Ot_Id|^=|hFYl{!tLAy|YF z{mx6nd1)9Ru5}l@_G$`Y>JPThs8j;3+FG}eN(|7k+GKjgG0Xz?dWZM8&G){cbyF$< zsl)^t<3269IR}zTz?Ywd-1Nah?vvYEy?nqNidmQoeb2Y%Brm6!%%MP*rCxyhp0#e_ z%b%^kT6fQyv%PF%<64X!9IqmF3wx=b|G$?hX0Xn=`O_hbZ+-(0DLcCh5g0@{00000 LNkvXXu0mjfU#yDz literal 0 HcmV?d00001