diff --git a/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java b/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java index a1cc3ee8b5..549ef0b123 100644 --- a/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java +++ b/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java @@ -152,6 +152,11 @@ public class ConfigManager scheduledExecutorService.scheduleWithFixedDelay(this::sendConfig, 30, 30, TimeUnit.SECONDS); } + public String getRSProfileKey() + { + return rsProfileKey; + } + public final void switchSession(AccountSession session) { // Ensure existing config is saved diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TimeTrackingConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TimeTrackingConfig.java index 28704ff3ce..aa284ee86c 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TimeTrackingConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TimeTrackingConfig.java @@ -41,6 +41,7 @@ public interface TimeTrackingConfig extends Config String TIMERS = "timers"; String STOPWATCHES = "stopwatches"; String PREFER_SOONEST = "preferSoonest"; + String NOTIFY = "notify"; @ConfigItem( keyName = "timeFormatMode", diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TimeTrackingPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TimeTrackingPlugin.java index 310c2e4e4a..7eee98df8c 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TimeTrackingPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TimeTrackingPlugin.java @@ -29,7 +29,6 @@ import com.google.inject.Inject; import com.google.inject.Provides; import java.awt.image.BufferedImage; import java.time.Instant; -import java.time.temporal.ChronoUnit; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -58,7 +57,6 @@ import net.runelite.client.plugins.timetracking.clocks.ClockManager; import net.runelite.client.plugins.timetracking.farming.FarmingContractManager; import net.runelite.client.plugins.timetracking.farming.FarmingTracker; import net.runelite.client.plugins.timetracking.hunter.BirdHouseTracker; -import net.runelite.client.task.Schedule; import net.runelite.client.ui.ClientToolbar; import net.runelite.client.ui.NavigationButton; import net.runelite.client.ui.overlay.infobox.InfoBoxManager; @@ -102,6 +100,8 @@ public class TimeTrackingPlugin extends Plugin private ScheduledFuture panelUpdateFuture; + private ScheduledFuture notifierFuture; + private TimeTrackingPanel panel; private NavigationButton navButton; @@ -139,6 +139,7 @@ public class TimeTrackingPlugin extends Plugin clientToolbar.addNavigation(navButton); panelUpdateFuture = executorService.scheduleAtFixedRate(this::updatePanel, 200, 200, TimeUnit.MILLISECONDS); + notifierFuture = executorService.scheduleAtFixedRate(this::checkCompletion, 10, 10, TimeUnit.SECONDS); } @Override @@ -153,6 +154,7 @@ public class TimeTrackingPlugin extends Plugin panelUpdateFuture = null; } + notifierFuture.cancel(true); clientToolbar.removeNavigation(navButton); infoBoxManager.removeInfoBox(farmingContractManager.getInfoBox()); farmingContractManager.setInfoBox(null); @@ -260,8 +262,7 @@ public class TimeTrackingPlugin extends Plugin } } - @Schedule(period = 10, unit = ChronoUnit.SECONDS) - public void checkCompletion() + private void checkCompletion() { boolean birdHouseDataChanged = birdHouseTracker.checkCompletion(); @@ -269,6 +270,8 @@ public class TimeTrackingPlugin extends Plugin { panel.update(); } + + farmingTracker.checkCompletion(); } private void updatePanel() diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TimeablePanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TimeablePanel.java index e7584a6e27..9796146500 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TimeablePanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TimeablePanel.java @@ -29,22 +29,29 @@ import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.GridLayout; +import javax.swing.ImageIcon; import javax.swing.JLabel; import javax.swing.JPanel; +import javax.swing.JToggleButton; import javax.swing.border.EmptyBorder; import lombok.Getter; +import lombok.extern.slf4j.Slf4j; import net.runelite.api.Constants; import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.FontManager; import net.runelite.client.ui.components.ThinProgressBar; import net.runelite.client.ui.components.shadowlabel.JShadowedLabel; +import net.runelite.client.util.ImageUtil; +import net.runelite.client.util.SwingUtil; +@Slf4j @Getter public class TimeablePanel extends JPanel { private final T timeable; private final JLabel icon = new JLabel(); private final JLabel farmingContractIcon = new JLabel(); + private final JToggleButton notifyButton = new JToggleButton(); private final JLabel estimate = new JLabel(); private final ThinProgressBar progress = new ThinProgressBar(); private final JLabel text; @@ -79,8 +86,29 @@ public class TimeablePanel extends JPanel infoPanel.add(text); infoPanel.add(estimate); + ImageIcon notifyIcon = new ImageIcon(ImageUtil.loadImageResource(TimeTrackingPlugin.class, "notify_icon.png")); + ImageIcon notifySelectedIcon = new ImageIcon(ImageUtil.loadImageResource(TimeTrackingPlugin.class, "notify_selected_icon.png")); + + notifyButton.setPreferredSize(new Dimension(30, 16)); + notifyButton.setBorder(new EmptyBorder(0, 0, 0, 10)); + notifyButton.setIcon(notifyIcon); + notifyButton.setSelectedIcon(notifySelectedIcon); + SwingUtil.removeButtonDecorations(notifyButton); + SwingUtil.addModalTooltip(notifyButton, "Disable notifications", "Enable notifications"); + + JPanel notifyPanel = new JPanel(); + notifyPanel.setLayout(new BorderLayout()); + notifyPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR); + notifyPanel.add(notifyButton, BorderLayout.CENTER); + + JPanel iconPanel = new JPanel(); + iconPanel.setLayout(new BorderLayout()); + iconPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR); + iconPanel.add(notifyPanel, BorderLayout.EAST); + iconPanel.add(farmingContractIcon, BorderLayout.WEST); + + topContainer.add(iconPanel, BorderLayout.EAST); topContainer.add(icon, BorderLayout.WEST); - topContainer.add(farmingContractIcon, BorderLayout.EAST); topContainer.add(infoPanel, BorderLayout.CENTER); progress.setValue(0); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingPatch.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingPatch.java index 8c24588917..25b29ba01f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingPatch.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingPatch.java @@ -29,6 +29,7 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.Setter; import net.runelite.api.Varbits; +import net.runelite.client.plugins.timetracking.TimeTrackingConfig; @RequiredArgsConstructor( access = AccessLevel.PACKAGE @@ -41,4 +42,14 @@ class FarmingPatch private final String name; private final Varbits varbit; private final PatchImplementation implementation; -} + + String configKey() + { + return region.getRegionID() + "." + varbit.getId(); + } + + String notifyConfigKey() + { + return TimeTrackingConfig.NOTIFY + "." + region.getRegionID() + "." + varbit.getId(); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingRegion.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingRegion.java index a759d2c31b..bb149af373 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingRegion.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingRegion.java @@ -33,13 +33,15 @@ public class FarmingRegion { private final String name; private final int regionID; + private final boolean definite; private final FarmingPatch[] patches; private final Varbits[] varbits; - FarmingRegion(String name, int regionID, FarmingPatch... patches) + FarmingRegion(String name, int regionID, boolean definite, FarmingPatch... patches) { this.name = name; this.regionID = regionID; + this.definite = definite; this.patches = patches; this.varbits = new Varbits[patches.length]; for (int i = 0; i < patches.length; i++) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingTabPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingTabPanel.java index 1c068790c8..18d525c1e0 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingTabPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingTabPanel.java @@ -33,8 +33,11 @@ import java.util.ArrayList; import java.util.List; import java.util.Set; import javax.swing.JLabel; +import javax.swing.JToggleButton; import javax.swing.border.EmptyBorder; +import lombok.extern.slf4j.Slf4j; import net.runelite.api.ItemID; +import net.runelite.client.config.ConfigManager; import net.runelite.client.game.ItemManager; import net.runelite.client.plugins.timetracking.TabContentPanel; import net.runelite.client.plugins.timetracking.TimeTrackingConfig; @@ -42,10 +45,12 @@ import net.runelite.client.plugins.timetracking.TimeablePanel; import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.FontManager; +@Slf4j public class FarmingTabPanel extends TabContentPanel { private final FarmingTracker farmingTracker; private final ItemManager itemManager; + private final ConfigManager configManager; private final TimeTrackingConfig config; private final List> patchPanels; private final FarmingContractManager farmingContractManager; @@ -53,6 +58,7 @@ public class FarmingTabPanel extends TabContentPanel FarmingTabPanel( FarmingTracker farmingTracker, ItemManager itemManager, + ConfigManager configManager, TimeTrackingConfig config, Set patches, FarmingContractManager farmingContractManager @@ -60,6 +66,7 @@ public class FarmingTabPanel extends TabContentPanel { this.farmingTracker = farmingTracker; this.itemManager = itemManager; + this.configManager = configManager; this.config = config; this.patchPanels = new ArrayList<>(); this.farmingContractManager = farmingContractManager; @@ -103,6 +110,18 @@ public class FarmingTabPanel extends TabContentPanel lastImpl = patch.getImplementation(); } + // Set toggle state of notification menu on icon click; + JToggleButton toggleNotify = p.getNotifyButton(); + String configKey = patch.notifyConfigKey(); + + toggleNotify.addActionListener(e -> + { + if (configManager.getRSProfileKey() != null) + { + configManager.setRSProfileConfiguration(TimeTrackingConfig.CONFIG_GROUP, configKey, toggleNotify.isSelected()); + } + }); + patchPanels.add(p); add(p, c); c.gridy++; @@ -197,18 +216,26 @@ public class FarmingTabPanel extends TabContentPanel { panel.getProgress().setVisible(false); } - JLabel farmingContractIcon = panel.getFarmingContractIcon(); - if (farmingContractManager.shouldHighlightFarmingTabPanel(patch)) - { - itemManager.getImage(ItemID.SEED_PACK).addTo(farmingContractIcon); - farmingContractIcon.setToolTipText(farmingContractManager.getContract().getName()); - } - else - { - farmingContractIcon.setIcon(null); - farmingContractIcon.setToolTipText(""); - } } + + JLabel farmingContractIcon = panel.getFarmingContractIcon(); + if (farmingContractManager.shouldHighlightFarmingTabPanel(patch)) + { + itemManager.getImage(ItemID.SEED_PACK).addTo(farmingContractIcon); + farmingContractIcon.setToolTipText(farmingContractManager.getContract().getName()); + } + else + { + farmingContractIcon.setIcon(null); + farmingContractIcon.setToolTipText(""); + } + + String configKey = patch.notifyConfigKey(); + JToggleButton toggleNotify = panel.getNotifyButton(); + boolean notifyEnabled = Boolean.TRUE + .equals(configManager.getRSProfileConfiguration(TimeTrackingConfig.CONFIG_GROUP, configKey, Boolean.class)); + + toggleNotify.setSelected(notifyEnabled); } } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingTracker.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingTracker.java index a5c953f324..e01c451b8d 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingTracker.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingTracker.java @@ -29,20 +29,28 @@ import com.google.inject.Singleton; import java.time.Instant; import java.util.Collection; import java.util.EnumMap; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Set; import javax.annotation.Nullable; import lombok.extern.slf4j.Slf4j; import net.runelite.api.Client; +import net.runelite.api.GameState; import net.runelite.api.Varbits; import net.runelite.api.coords.WorldPoint; import net.runelite.api.vars.Autoweed; import net.runelite.api.widgets.WidgetModalMode; +import net.runelite.client.Notifier; import net.runelite.client.config.ConfigManager; +import net.runelite.client.config.RuneScapeProfile; +import net.runelite.client.config.RuneScapeProfileType; import net.runelite.client.game.ItemManager; import net.runelite.client.plugins.timetracking.SummaryState; import net.runelite.client.plugins.timetracking.Tab; import net.runelite.client.plugins.timetracking.TimeTrackingConfig; +import net.runelite.client.util.Text; @Slf4j @Singleton @@ -53,6 +61,7 @@ public class FarmingTracker private final ConfigManager configManager; private final TimeTrackingConfig config; private final FarmingWorld farmingWorld; + private final Notifier notifier; private final Map summaries = new EnumMap<>(Tab.class); @@ -61,23 +70,26 @@ public class FarmingTracker * or {@code -1} if we have no data about any patch of the given type. */ private final Map completionTimes = new EnumMap<>(Tab.class); + Map wasNotified = new HashMap<>(); private boolean newRegionLoaded; private Collection lastRegions; + private boolean firstNotifyCheck = true; @Inject - private FarmingTracker(Client client, ItemManager itemManager, ConfigManager configManager, TimeTrackingConfig config, FarmingWorld farmingWorld) + private FarmingTracker(Client client, ItemManager itemManager, ConfigManager configManager, TimeTrackingConfig config, FarmingWorld farmingWorld, Notifier notifier) { this.client = client; this.itemManager = itemManager; this.configManager = configManager; this.config = config; this.farmingWorld = farmingWorld; + this.notifier = notifier; } public FarmingTabPanel createTabPanel(Tab tab, FarmingContractManager farmingContractManager) { - return new FarmingTabPanel(this, itemManager, config, farmingWorld.getTabs().get(tab), farmingContractManager); + return new FarmingTabPanel(this, itemManager, configManager, config, farmingWorld.getTabs().get(tab), farmingContractManager); } /** @@ -130,7 +142,7 @@ public class FarmingTracker { // Write the config value if it doesn't match what is current, or it is more than 5 minutes old Varbits varbit = patch.getVarbit(); - String key = region.getRegionID() + "." + varbit.getId(); + String key = patch.configKey(); String strVarbit = Integer.toString(client.getVar(varbit)); String storedValue = configManager.getRSProfileConfiguration(TimeTrackingConfig.CONFIG_GROUP, key); @@ -189,6 +201,12 @@ public class FarmingTracker configManager.setRSProfileConfiguration(TimeTrackingConfig.CONFIG_GROUP, TimeTrackingConfig.FARM_TICK_OFFSET, offsetMins); } } + if (currentPatchState.getTickRate() != 0 + // Don't set wasNotified to false if witnessing a check-health action + && !(previousPatchState.getCropState() == CropState.GROWING && currentPatchState.getCropState() == CropState.HARVESTABLE && currentPatchState.getProduce().getPatchImplementation().isHealthCheckRequired())) + { + wasNotified.put(new ProfilePatch(patch, configManager.getRSProfileKey()), false); + } } else { @@ -258,17 +276,23 @@ public class FarmingTracker @Nullable public PatchPrediction predictPatch(FarmingPatch patch) + { + return predictPatch(patch, configManager.getRSProfileKey()); + } + + @Nullable + public PatchPrediction predictPatch(FarmingPatch patch, String profile) { long unixNow = Instant.now().getEpochSecond(); boolean autoweed = Integer.toString(Autoweed.ON.ordinal()) - .equals(configManager.getRSProfileConfiguration(TimeTrackingConfig.CONFIG_GROUP, TimeTrackingConfig.AUTOWEED)); + .equals(configManager.getConfiguration(TimeTrackingConfig.CONFIG_GROUP, profile, TimeTrackingConfig.AUTOWEED)); boolean botanist = Boolean.TRUE - .equals(configManager.getRSProfileConfiguration(TimeTrackingConfig.CONFIG_GROUP, TimeTrackingConfig.BOTANIST, Boolean.class)); + .equals(configManager.getConfiguration(TimeTrackingConfig.CONFIG_GROUP, profile, TimeTrackingConfig.BOTANIST, Boolean.class)); - String key = patch.getRegion().getRegionID() + "." + patch.getVarbit().getId(); - String storedValue = configManager.getRSProfileConfiguration(TimeTrackingConfig.CONFIG_GROUP, key); + String key = patch.configKey(); + String storedValue = configManager.getConfiguration(TimeTrackingConfig.CONFIG_GROUP, profile, key); if (storedValue == null) { @@ -323,11 +347,11 @@ public class FarmingTracker long doneEstimate = 0; if (tickrate > 0) { - long tickNow = getTickTime(tickrate, 0, unixNow); - long tickTime = getTickTime(tickrate, 0, unixTime); + long tickNow = getTickTime(tickrate, 0, unixNow, profile); + long tickTime = getTickTime(tickrate, 0, unixTime, profile); int delta = (int) (tickNow - tickTime) / (tickrate * 60); - doneEstimate = getTickTime(tickrate, stages - 1 - stage, tickTime); + doneEstimate = getTickTime(tickrate, stages - 1 - stage, tickTime, profile); stage += delta; if (stage >= stages) @@ -347,13 +371,13 @@ public class FarmingTracker public long getTickTime(int tickRate, int ticks) { - return getTickTime(tickRate, ticks, Instant.now().getEpochSecond()); + return getTickTime(tickRate, ticks, Instant.now().getEpochSecond(), configManager.getRSProfileKey()); } - public long getTickTime(int tickRate, int ticks, long requestedTime) + public long getTickTime(int tickRate, int ticks, long requestedTime, String profile) { - Integer offsetPrecisionMins = configManager.getRSProfileConfiguration(TimeTrackingConfig.CONFIG_GROUP, TimeTrackingConfig.FARM_TICK_OFFSET_PRECISION, int.class); - Integer offsetTimeMins = configManager.getRSProfileConfiguration(TimeTrackingConfig.CONFIG_GROUP, TimeTrackingConfig.FARM_TICK_OFFSET, int.class); + Integer offsetPrecisionMins = configManager.getConfiguration(TimeTrackingConfig.CONFIG_GROUP, profile, TimeTrackingConfig.FARM_TICK_OFFSET_PRECISION, int.class); + Integer offsetTimeMins = configManager.getConfiguration(TimeTrackingConfig.CONFIG_GROUP, profile, TimeTrackingConfig.FARM_TICK_OFFSET, int.class); //All offsets are negative but are stored as positive long calculatedOffsetTime = 0L; @@ -465,4 +489,124 @@ public class FarmingTracker completionTimes.put(tab.getKey(), completionTime); } } + + public void checkCompletion() + { + List rsProfiles = configManager.getRSProfiles(); + long unixNow = Instant.now().getEpochSecond(); + + for (RuneScapeProfile profile : rsProfiles) + { + Integer offsetPrecisionMins = configManager.getConfiguration(TimeTrackingConfig.CONFIG_GROUP, profile.getKey(), TimeTrackingConfig.FARM_TICK_OFFSET_PRECISION, int.class); + Integer offsetTimeMins = configManager.getConfiguration(TimeTrackingConfig.CONFIG_GROUP, profile.getKey(), TimeTrackingConfig.FARM_TICK_OFFSET, int.class); + + RuneScapeProfileType profileType = profile.getType(); + + for (Map.Entry> tab : farmingWorld.getTabs().entrySet()) + { + for (FarmingPatch patch : tab.getValue()) + { + ProfilePatch profilePatch = new ProfilePatch(patch, profile.getKey()); + boolean patchNotified = (wasNotified.get(profilePatch) != null ? wasNotified.get(profilePatch) : false); + String configKey = patch.notifyConfigKey(); + boolean shouldNotify = Boolean.TRUE + .equals(configManager.getConfiguration(TimeTrackingConfig.CONFIG_GROUP, profile.getKey(), configKey, Boolean.class)); + PatchPrediction prediction = predictPatch(patch, profile.getKey()); + + if (prediction == null) + { + continue; + } + + int tickRate = prediction.getProduce().getTickrate(); + + if (offsetPrecisionMins == null || offsetTimeMins == null || (offsetPrecisionMins < tickRate && offsetPrecisionMins < 40) || prediction.getProduce() == Produce.WEEDS + || unixNow <= prediction.getDoneEstimate() || patchNotified || prediction.getCropState() == CropState.FILLING) + { + continue; + } + + wasNotified.put(profilePatch, true); + + if (!firstNotifyCheck && shouldNotify) + { + final StringBuilder notificationStringBuilder = new StringBuilder(); + // Same RS account + if (client.getGameState() == GameState.LOGGED_IN && profile.getDisplayName().equals(client.getLocalPlayer().getName())) + { + // Same RS account but different profile type + if (profileType != RuneScapeProfileType.getCurrent(client)) + { + notificationStringBuilder.append("(") + .append(Text.titleCase(profile.getType())) + .append(") "); + } + // Same RS account AND profile falls through here so no bracketed prefix is added + } + else + { + // Different RS account AND profile type + if (profileType != RuneScapeProfileType.getCurrent(client) || client.getGameState() == GameState.LOGIN_SCREEN) + { + //Don't print profile type when logged out if is STANDARD + if (client.getGameState() == GameState.LOGIN_SCREEN && profileType == RuneScapeProfileType.STANDARD) + { + notificationStringBuilder.append("(") + .append(profile.getDisplayName()) + .append(") "); + } + else + { + notificationStringBuilder.append("(") + .append(profile.getDisplayName()) + .append(" - ") + .append(Text.titleCase(profile.getType())) + .append(") "); + } + } + // Different RS account but same profile type + else + { + notificationStringBuilder.append("(") + .append(profile.getDisplayName()) + .append(") "); + } + } + + notificationStringBuilder + .append("Your ") + .append(prediction.getProduce().getName()); + + switch (prediction.getCropState()) + { + case HARVESTABLE: + case GROWING: + if (prediction.getProduce().getName().toLowerCase(Locale.ENGLISH).contains("compost")) + { + notificationStringBuilder.append(" is ready to collect in "); + } + else + { + notificationStringBuilder.append(" is ready to harvest in "); + } + break; + case DISEASED: + notificationStringBuilder.append(" has become diseased in "); + break; + case DEAD: + notificationStringBuilder.append(" has died in "); + break; + } + + notificationStringBuilder.append(patch.getRegion().isDefinite() ? "the " : "") + .append(patch.getRegion().getName()) + .append("."); + + notifier.notify(notificationStringBuilder.toString()); + } + } + } + } + firstNotifyCheck = false; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingWorld.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingWorld.java index 7ca46e130c..5429240775 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingWorld.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingWorld.java @@ -64,14 +64,14 @@ class FarmingWorld { // Some of these patches get updated in multiple regions. // It may be worth it to add a specialization for these patches - add(new FarmingRegion("Al Kharid", 13106, + add(new FarmingRegion("Al Kharid", 13106, false, new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.CACTUS) ), 13362, 13105); - add(new FarmingRegion("Ardougne", 10290, + add(new FarmingRegion("Ardougne", 10290, false, new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.BUSH) ), 10546); - add(new FarmingRegion("Ardougne", 10548, + add(new FarmingRegion("Ardougne", 10548, false, new FarmingPatch("North", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT), new FarmingPatch("South", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT), new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER), @@ -79,12 +79,12 @@ class FarmingWorld new FarmingPatch("", Varbits.FARMING_4775, PatchImplementation.COMPOST) )); - add(new FarmingRegion("Brimhaven", 11058, + add(new FarmingRegion("Brimhaven", 11058, false, new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.FRUIT_TREE), new FarmingPatch("", Varbits.FARMING_4772, PatchImplementation.SPIRIT_TREE) ), 11057); - add(new FarmingRegion("Catherby", 11062, + add(new FarmingRegion("Catherby", 11062, false, new FarmingPatch("North", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT), new FarmingPatch("South", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT), new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER), @@ -103,7 +103,7 @@ class FarmingWorld return true; } }, 11061, 11318, 11317); - add(new FarmingRegion("Catherby", 11317, + add(new FarmingRegion("Catherby", 11317, false, new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.FRUIT_TREE) ) { @@ -115,27 +115,27 @@ class FarmingWorld } }); - add(new FarmingRegion("Champions' Guild", 12596, + add(new FarmingRegion("Champions' Guild", 12596, true, new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.BUSH) )); - add(new FarmingRegion("Draynor Manor", 12340, + add(new FarmingRegion("Draynor Manor", 12340, false, new FarmingPatch("Belladonna", Varbits.FARMING_4771, PatchImplementation.BELLADONNA) )); - add(new FarmingRegion("Entrana", 11060, + add(new FarmingRegion("Entrana", 11060, false, new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.HOPS) ), 11316); - add(new FarmingRegion("Etceteria", 10300, + add(new FarmingRegion("Etceteria", 10300, false, new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.BUSH), new FarmingPatch("", Varbits.FARMING_4772, PatchImplementation.SPIRIT_TREE) )); - add(new FarmingRegion("Falador", 11828, + add(new FarmingRegion("Falador", 11828, false, new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.TREE) ), 12084); - add(new FarmingRegion("Falador", 12083, + add(new FarmingRegion("Falador", 12083, false, new FarmingPatch("North West", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT), new FarmingPatch("South East", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT), new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER), @@ -151,7 +151,7 @@ class FarmingWorld } }); - add(new FarmingRegion("Fossil Island", 14651, + add(new FarmingRegion("Fossil Island", 14651, false, new FarmingPatch("East", Varbits.FARMING_4771, PatchImplementation.HARDWOOD_TREE), new FarmingPatch("Middle", Varbits.FARMING_4772, PatchImplementation.HARDWOOD_TREE), new FarmingPatch("West", Varbits.FARMING_4773, PatchImplementation.HARDWOOD_TREE) @@ -179,22 +179,22 @@ class FarmingWorld return loc.getPlane() == 0; } }, 14907, 14908, 15164, 14652, 14906, 14650, 15162, 15163); - add(new FarmingRegion("Seaweed", 15008, + add(new FarmingRegion("Seaweed", 15008, false, new FarmingPatch("North", Varbits.FARMING_4771, PatchImplementation.SEAWEED), new FarmingPatch("South", Varbits.FARMING_4772, PatchImplementation.SEAWEED) )); - add(new FarmingRegion("Gnome Stronghold", 9781, + add(new FarmingRegion("Gnome Stronghold", 9781, true, new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.TREE), new FarmingPatch("", Varbits.FARMING_4772, PatchImplementation.FRUIT_TREE) ), 9782, 9526, 9525); - add(new FarmingRegion("Harmony", 15148, + add(new FarmingRegion("Harmony", 15148, false, new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT), new FarmingPatch("", Varbits.FARMING_4772, PatchImplementation.HERB) )); - add(new FarmingRegion("Kourend", 6967, + add(new FarmingRegion("Kourend", 6967, false, new FarmingPatch("North East", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT), new FarmingPatch("South West", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT), new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER), @@ -202,7 +202,7 @@ class FarmingWorld new FarmingPatch("", Varbits.FARMING_4775, PatchImplementation.COMPOST), new FarmingPatch("", Varbits.FARMING_7904, PatchImplementation.SPIRIT_TREE) ), 6711); - add(new FarmingRegion("Kourend", 7223, + add(new FarmingRegion("Kourend", 7223, false, new FarmingPatch("East 1", Varbits.GRAPES_4953, PatchImplementation.GRAPES), new FarmingPatch("East 2", Varbits.GRAPES_4954, PatchImplementation.GRAPES), new FarmingPatch("East 3", Varbits.GRAPES_4955, PatchImplementation.GRAPES), @@ -217,21 +217,21 @@ class FarmingWorld new FarmingPatch("West 6", Varbits.GRAPES_4964, PatchImplementation.GRAPES) )); - add(new FarmingRegion("Lletya", 9265, + add(new FarmingRegion("Lletya", 9265, false, new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.FRUIT_TREE) ), 11103); - add(new FarmingRegion("Lumbridge", 12851, + add(new FarmingRegion("Lumbridge", 12851, false, new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.HOPS) )); - add(new FarmingRegion("Lumbridge", 12594, + add(new FarmingRegion("Lumbridge", 12594, false, new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.TREE) ), 12850); - add(new FarmingRegion("Morytania", 13622, + add(new FarmingRegion("Morytania", 13622, false, new FarmingPatch("Mushroom", Varbits.FARMING_4771, PatchImplementation.MUSHROOM) ), 13878); - add(new FarmingRegion("Morytania", 14391, + add(new FarmingRegion("Morytania", 14391, false, new FarmingPatch("North West", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT), new FarmingPatch("South East", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT), new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER), @@ -239,7 +239,7 @@ class FarmingWorld new FarmingPatch("", Varbits.FARMING_4775, PatchImplementation.COMPOST) ), 14390); - add(new FarmingRegion("Port Sarim", 12082, + add(new FarmingRegion("Port Sarim", 12082, false, new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.SPIRIT_TREE) ) { @@ -250,48 +250,48 @@ class FarmingWorld } }, 12083); - add(new FarmingRegion("Rimmington", 11570, + add(new FarmingRegion("Rimmington", 11570, false, new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.BUSH) ), 11826); - add(new FarmingRegion("Seers' Village", 10551, + add(new FarmingRegion("Seers' Village", 10551, false, new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.HOPS) ), 10550); - add(new FarmingRegion("Tai Bwo Wannai", 11056, + add(new FarmingRegion("Tai Bwo Wannai", 11056, false, new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.CALQUAT) )); - add(new FarmingRegion("Taverley", 11573, + add(new FarmingRegion("Taverley", 11573, false, new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.TREE) ), 11829); - add(new FarmingRegion("Tree Gnome Village", 9777, + add(new FarmingRegion("Tree Gnome Village", 9777, true, new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.FRUIT_TREE) ), 10033); - add(new FarmingRegion("Troll Stronghold", 11321, + add(new FarmingRegion("Troll Stronghold", 11321, true, new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.HERB) )); - add(new FarmingRegion("Varrock", 12854, + add(new FarmingRegion("Varrock", 12854, false, new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.TREE) ), 12853); - add(new FarmingRegion("Yanille", 10288, + add(new FarmingRegion("Yanille", 10288, false, new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.HOPS) )); - add(new FarmingRegion("Weiss", 11325, + add(new FarmingRegion("Weiss", 11325, false, new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.HERB) )); - add(new FarmingRegion("Farming Guild", 5021, + add(new FarmingRegion("Farming Guild", 5021, true, new FarmingPatch("Hespori", Varbits.FARMING_7908, PatchImplementation.HESPORI) )); //Full 3x3 region area centered on farming guild - add(farmingGuildRegion = new FarmingRegion("Farming Guild", 4922, + add(farmingGuildRegion = new FarmingRegion("Farming Guild", 4922, true, new FarmingPatch("", Varbits.FARMING_7905, PatchImplementation.TREE), new FarmingPatch("", Varbits.FARMING_4775, PatchImplementation.HERB), new FarmingPatch("", Varbits.FARMING_4772, PatchImplementation.BUSH), @@ -308,7 +308,7 @@ class FarmingWorld ), 5177, 5178, 5179, 4921, 4923, 4665, 4666, 4667); //All of Prifddinas, and all of Prifddinas Underground - add(new FarmingRegion("Prifddinas", 13151, + add(new FarmingRegion("Prifddinas", 13151, false, new FarmingPatch("North", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT), new FarmingPatch("South", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT), new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/ProfilePatch.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/ProfilePatch.java new file mode 100644 index 0000000000..d928c1f8e0 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/ProfilePatch.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2021 Hannah Ryan + * 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.timetracking.farming; + +import lombok.Value; + +@Value +class ProfilePatch +{ + FarmingPatch patch; + String rsProfileKey; +} diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/timetracking/notify_icon.png b/runelite-client/src/main/resources/net/runelite/client/plugins/timetracking/notify_icon.png new file mode 100644 index 0000000000..4a4667bedb Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/timetracking/notify_icon.png differ diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/timetracking/notify_selected_icon.png b/runelite-client/src/main/resources/net/runelite/client/plugins/timetracking/notify_selected_icon.png new file mode 100644 index 0000000000..1fee9d1f8d Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/timetracking/notify_selected_icon.png differ