From 2d5d490b50d39bc53ba5ca6da257d3e1f2f9ff03 Mon Sep 17 00:00:00 2001 From: Brandt Hill Date: Mon, 2 Mar 2020 17:01:48 -0600 Subject: [PATCH] kingdom: add notification message for favor and coffer value This allows setting a configurable threshold where you will receive a message informing you your favor and/or coffer is low on login Co-authored-by: Adam --- .../main/java/net/runelite/api/Varbits.java | 5 +- .../kingdomofmiscellania/KingdomConfig.java | 77 ++++++++ .../kingdomofmiscellania/KingdomCounter.java | 6 +- .../kingdomofmiscellania/KingdomPlugin.java | 180 ++++++++++++++++-- 4 files changed, 252 insertions(+), 16 deletions(-) create mode 100755 runelite-client/src/main/java/net/runelite/client/plugins/kingdomofmiscellania/KingdomConfig.java diff --git a/runelite-api/src/main/java/net/runelite/api/Varbits.java b/runelite-api/src/main/java/net/runelite/api/Varbits.java index 418c56bb6c..df7c45eb3d 100644 --- a/runelite-api/src/main/java/net/runelite/api/Varbits.java +++ b/runelite-api/src/main/java/net/runelite/api/Varbits.java @@ -387,9 +387,10 @@ public enum Varbits MULTICOMBAT_AREA(4605), /** - * Kingdom Management + * Kingdom of Miscellania Management + * Kingdom Approval is represented as a 7-bit unsigned integer; 127 corresponds to 100% approval */ - KINGDOM_FAVOR(72), + KINGDOM_APPROVAL(72), KINGDOM_COFFER(74), /** diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/kingdomofmiscellania/KingdomConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/kingdomofmiscellania/KingdomConfig.java new file mode 100755 index 0000000000..064d260fd7 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/kingdomofmiscellania/KingdomConfig.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2020, Brandt Hill + * 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.kingdomofmiscellania; + +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; +import net.runelite.client.config.Range; + +@ConfigGroup(KingdomConfig.CONFIG_GROUP_NAME) +public interface KingdomConfig extends Config +{ + String CONFIG_GROUP_NAME = "kingdomofmiscellania"; + int MAX_COFFER = 7_500_000; + int MAX_APPROVAL_PERCENT = 100; + + @ConfigItem( + position = 1, + keyName = "sendNotifications", + name = "Send Notifications", + description = "Send chat notifications upon login showing current estimated coffer and approval" + ) + default boolean shouldSendNotifications() + { + return false; + } + + @Range( + max = MAX_COFFER + ) + @ConfigItem( + position = 2, + keyName = "cofferThreshold", + name = "Coffer Threshold", + description = "Send notifications if coffer is below this value" + ) + default int getCofferThreshold() + { + return MAX_COFFER; + } + + @Range( + max = MAX_APPROVAL_PERCENT + ) + @ConfigItem( + position = 3, + keyName = "approvalThreshold", + name = "Approval Threshold", + description = "Send notifications if approval percentage is below this value" + ) + default int getApprovalThreshold() + { + return MAX_APPROVAL_PERCENT; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/kingdomofmiscellania/KingdomCounter.java b/runelite-client/src/main/java/net/runelite/client/plugins/kingdomofmiscellania/KingdomCounter.java index aaaf0468b0..7653b57a53 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/kingdomofmiscellania/KingdomCounter.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/kingdomofmiscellania/KingdomCounter.java @@ -34,20 +34,20 @@ public class KingdomCounter extends Counter KingdomCounter(BufferedImage image, KingdomPlugin plugin) { - super(image, plugin, plugin.getFavor()); + super(image, plugin, plugin.getApproval()); this.plugin = plugin; } @Override public String getText() { - return KingdomPlugin.getFavorPercent(plugin.getFavor()) + "%"; + return KingdomPlugin.getApprovalPercent(plugin.getApproval()) + "%"; } @Override public String getTooltip() { - return "Favor: " + plugin.getFavor() + "/127" + "
" + return "Approval: " + plugin.getApproval() + "/" + KingdomPlugin.MAX_APPROVAL + "
" + "Coffer: " + QuantityFormatter.quantityToStackSize(plugin.getCoffer()); } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/kingdomofmiscellania/KingdomPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/kingdomofmiscellania/KingdomPlugin.java index 17347b3aac..bae9b177bf 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/kingdomofmiscellania/KingdomPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/kingdomofmiscellania/KingdomPlugin.java @@ -25,32 +25,55 @@ package net.runelite.client.plugins.kingdomofmiscellania; import com.google.common.collect.ImmutableSet; +import com.google.inject.Provides; +import java.time.Duration; +import java.time.Instant; import javax.inject.Inject; -import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import net.runelite.api.ChatMessageType; import net.runelite.api.Client; import net.runelite.api.GameState; import static net.runelite.api.ItemID.TEAK_CHEST; +import net.runelite.api.Quest; +import net.runelite.api.QuestState; import net.runelite.api.VarPlayer; import net.runelite.api.Varbits; import net.runelite.api.events.GameStateChanged; +import net.runelite.api.events.GameTick; import net.runelite.api.events.VarbitChanged; +import net.runelite.client.chat.ChatColorType; +import net.runelite.client.chat.ChatMessageBuilder; +import net.runelite.client.chat.ChatMessageManager; +import net.runelite.client.chat.QueuedMessage; +import net.runelite.client.config.ConfigManager; import net.runelite.client.eventbus.Subscribe; import net.runelite.client.game.ItemManager; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; import net.runelite.client.ui.overlay.infobox.InfoBoxManager; +import net.runelite.client.util.QuantityFormatter; @PluginDescriptor( name = "Kingdom of Miscellania", - description = "Show amount of favor when inside Miscellania", - tags = {"favor", "favour", "managing", "overlay"}, + description = "Show amount of approval when inside Miscellania", + tags = {"favor", "favour", "managing", "overlay", "approval", "coffer"}, enabledByDefault = false ) @Slf4j public class KingdomPlugin extends Plugin { private static final ImmutableSet KINGDOM_REGION = ImmutableSet.of(10044, 10300); + private static final String CONFIG_LAST_CHANGED_KEY = "lastChanged"; + private static final String CONFIG_COFFER_KEY = "coffer"; + private static final String CONFIG_APPROVAL_KEY = "approval"; + private static final String CHAT_MESSAGE_FORMAT = "Your Kingdom of Miscellania approval is %d%%, and your coffer has %s coins."; + private static final int MAX_WITHDRAWAL_BASE = 50_000; + private static final int MAX_WITHDRAWAL_ROYAL_TROUBLE = 75_000; + private static final float APPROVAL_DECREMENT_BASE = 0.025f; + private static final float APPROVAL_DECREMENT_ROYAL_TROUBLE = 0.010f; + static final int MAX_APPROVAL = 127; + + private boolean loggingIn; @Inject private Client client; @@ -61,8 +84,14 @@ public class KingdomPlugin extends Plugin @Inject private ItemManager itemManager; - @Getter - private int favor = 0, coffer = 0; + @Inject + private KingdomConfig config; + + @Inject + private ConfigManager configManager; + + @Inject + private ChatMessageManager chatMessageManager; private KingdomCounter counter; @@ -72,11 +101,28 @@ public class KingdomPlugin extends Plugin removeKingdomInfobox(); } + @Provides + KingdomConfig getConfig(ConfigManager configManager) + { + return configManager.getConfig(KingdomConfig.class); + } + @Subscribe public void onVarbitChanged(VarbitChanged event) { - favor = client.getVar(Varbits.KINGDOM_FAVOR); - coffer = client.getVar(Varbits.KINGDOM_COFFER); + final int coffer = client.getVar(Varbits.KINGDOM_COFFER); + final int approval = client.getVar(Varbits.KINGDOM_APPROVAL); + + if (client.getGameState() == GameState.LOGGED_IN + && isThroneOfMiscellaniaCompleted() + && (isInKingdom() || coffer > 0 && approval > 0) + && (getCoffer() != coffer || getApproval() != approval)) + { + setLastChanged(Instant.now()); + setCoffer(coffer); + setApproval(approval); + } + processInfobox(); } @@ -87,11 +133,25 @@ public class KingdomPlugin extends Plugin { processInfobox(); } + else if (event.getGameState() == GameState.LOGGING_IN) + { + loggingIn = true; + } + } + + @Subscribe + public void onGameTick(GameTick gameTick) + { + if (loggingIn) + { + loggingIn = false; + createNotification(); + } } private void processInfobox() { - if (client.getGameState() == GameState.LOGGED_IN && hasCompletedQuest() && isInKingdom()) + if (client.getGameState() == GameState.LOGGED_IN && isThroneOfMiscellaniaCompleted() && isInKingdom()) { addKingdomInfobox(); } @@ -99,7 +159,33 @@ public class KingdomPlugin extends Plugin { removeKingdomInfobox(); } + } + private void createNotification() + { + if (!config.shouldSendNotifications() || !isThroneOfMiscellaniaCompleted()) + { + return; + } + + if (getLastChanged() == null) + { + log.debug("Kingdom Of Miscellania values not yet set. Visit Miscellania to automatically set values."); + return; + } + + Instant lastChanged = getLastChanged(); + int lastCoffer = getCoffer(); + int lastApproval = getApproval(); + int estimatedCoffer = estimateCoffer(lastChanged, lastCoffer); + int estimatedApproval = estimateApproval(lastChanged, lastApproval); + if (estimatedCoffer < config.getCofferThreshold() || getApprovalPercent(estimatedApproval) < config.getApprovalThreshold()) + { + sendChatMessage(String.format( + CHAT_MESSAGE_FORMAT, + getApprovalPercent(estimatedApproval), + QuantityFormatter.quantityToStackSize(estimatedCoffer))); + } } private void addKingdomInfobox() @@ -122,20 +208,92 @@ public class KingdomPlugin extends Plugin } } + private int estimateCoffer(Instant lastChanged, int lastCoffer) + { + int daysSince = (int) Duration.between(lastChanged, Instant.now()).toDays(); + int maxDailyWithdrawal = isRoyalTroubleCompleted() ? MAX_WITHDRAWAL_ROYAL_TROUBLE : MAX_WITHDRAWAL_BASE; + int maxDailyThreshold = maxDailyWithdrawal * 10; + + for (int i = 0; i < daysSince; i++) + { + lastCoffer -= (lastCoffer > maxDailyThreshold) ? maxDailyWithdrawal : lastCoffer / 10; + } + return lastCoffer; + } + + private int estimateApproval(Instant lastChanged, int lastApproval) + { + int daysSince = (int) Duration.between(lastChanged, Instant.now()).toDays(); + float dailyPercentage = isRoyalTroubleCompleted() ? APPROVAL_DECREMENT_ROYAL_TROUBLE : APPROVAL_DECREMENT_BASE; + + lastApproval -= (int) (daysSince * dailyPercentage * MAX_APPROVAL); + return Math.max(lastApproval, 0); + } + private boolean isInKingdom() { return client.getLocalPlayer() != null && KINGDOM_REGION.contains(client.getLocalPlayer().getWorldLocation().getRegionID()); } - private boolean hasCompletedQuest() + private boolean isThroneOfMiscellaniaCompleted() { return client.getVar(VarPlayer.THRONE_OF_MISCELLANIA) > 0; } - static int getFavorPercent(int favor) + private boolean isRoyalTroubleCompleted() { - return (favor * 100) / 127; + return Quest.ROYAL_TROUBLE.getState(client) == QuestState.FINISHED; } + static int getApprovalPercent(int approval) + { + return (approval * 100) / MAX_APPROVAL; + } + + private void sendChatMessage(String chatMessage) + { + final String message = new ChatMessageBuilder() + .append(ChatColorType.HIGHLIGHT) + .append(chatMessage) + .build(); + + chatMessageManager.queue( + QueuedMessage.builder() + .type(ChatMessageType.CONSOLE) + .runeLiteFormattedMessage(message) + .build()); + } + + private Instant getLastChanged() + { + return configManager.getRSProfileConfiguration(KingdomConfig.CONFIG_GROUP_NAME, CONFIG_LAST_CHANGED_KEY, Instant.class); + } + + private void setLastChanged(Instant lastChanged) + { + configManager.setRSProfileConfiguration(KingdomConfig.CONFIG_GROUP_NAME, CONFIG_LAST_CHANGED_KEY, lastChanged); + } + + int getCoffer() + { + Integer coffer = configManager.getRSProfileConfiguration(KingdomConfig.CONFIG_GROUP_NAME, CONFIG_COFFER_KEY, int.class); + return coffer == null ? 0 : coffer; + } + + private void setCoffer(int coffer) + { + configManager.setRSProfileConfiguration(KingdomConfig.CONFIG_GROUP_NAME, CONFIG_COFFER_KEY, coffer); + } + + int getApproval() + { + Integer approval = configManager.getRSProfileConfiguration(KingdomConfig.CONFIG_GROUP_NAME, CONFIG_APPROVAL_KEY, int.class); + return approval == null ? 0 : approval; + } + + private void setApproval(int approval) + { + configManager.setRSProfileConfiguration(KingdomConfig.CONFIG_GROUP_NAME, CONFIG_APPROVAL_KEY, approval); + } }