From db3b32c09380d2d7b197e4f468d068312243f05e Mon Sep 17 00:00:00 2001 From: melkypie <5113962+melkypie@users.noreply.github.com> Date: Thu, 7 May 2020 23:25:08 +0300 Subject: [PATCH] timetracking: add farming contract functionality Co-authored-by: Koekkruimels --- .../timetracking/OverviewItemPanel.java | 58 +++- .../timetracking/OverviewTabPanel.java | 27 +- .../plugins/timetracking/SummaryState.java | 1 + .../plugins/timetracking/TabContentPanel.java | 2 +- .../timetracking/TimeTrackingPanel.java | 9 +- .../timetracking/TimeTrackingPlugin.java | 31 +- .../plugins/timetracking/TimeablePanel.java | 12 +- .../farming/FarmingContractInfoBox.java | 110 ++++++ .../farming/FarmingContractManager.java | 326 ++++++++++++++++++ .../timetracking/farming/FarmingTabPanel.java | 17 +- .../timetracking/farming/FarmingTracker.java | 4 +- .../timetracking/farming/FarmingWorld.java | 5 +- .../plugins/timetracking/farming/Produce.java | 177 +++++++--- 13 files changed, 691 insertions(+), 88 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingContractInfoBox.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingContractManager.java diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/OverviewItemPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/OverviewItemPanel.java index 47484a669d..a029d0b5d9 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/OverviewItemPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/OverviewItemPanel.java @@ -31,6 +31,7 @@ import java.awt.Dimension; import java.awt.GridLayout; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.util.function.BooleanSupplier; import javax.swing.ImageIcon; import javax.swing.JLabel; import javax.swing.JPanel; @@ -47,7 +48,12 @@ class OverviewItemPanel extends JPanel private static final Color HOVER_COLOR = ColorScheme.DARKER_GRAY_HOVER_COLOR; + private final JPanel textContainer; private final JLabel statusLabel; + private final JLabel arrowLabel; + private final BooleanSupplier isSelectable; + + private boolean isHighlighted; static { @@ -56,16 +62,23 @@ class OverviewItemPanel extends JPanel OverviewItemPanel(ItemManager itemManager, TimeTrackingPanel pluginPanel, Tab tab, String title) { + this(itemManager, () -> pluginPanel.switchTab(tab), () -> true, tab.getItemID(), title); + } + + OverviewItemPanel(ItemManager itemManager, Runnable onTabSwitched, BooleanSupplier isSelectable, int iconItemID, String title) + { + this.isSelectable = isSelectable; + setBackground(ColorScheme.DARKER_GRAY_COLOR); setLayout(new BorderLayout()); setBorder(new EmptyBorder(7, 7, 7, 7)); JLabel iconLabel = new JLabel(); iconLabel.setMinimumSize(new Dimension(Constants.ITEM_SPRITE_WIDTH, Constants.ITEM_SPRITE_HEIGHT)); - itemManager.getImage(tab.getItemID()).addTo(iconLabel); + itemManager.getImage(iconItemID).addTo(iconLabel); add(iconLabel, BorderLayout.WEST); - JPanel textContainer = new JPanel(); + textContainer = new JPanel(); textContainer.setBackground(ColorScheme.DARKER_GRAY_COLOR); textContainer.setLayout(new GridLayout(2, 1)); textContainer.setBorder(new EmptyBorder(5, 7, 5, 7)); @@ -75,32 +88,27 @@ class OverviewItemPanel extends JPanel @Override public void mousePressed(MouseEvent mouseEvent) { - pluginPanel.switchTab(tab); - setBackground(ColorScheme.DARKER_GRAY_COLOR); - textContainer.setBackground(ColorScheme.DARKER_GRAY_COLOR); + onTabSwitched.run(); + + setHighlighted(false); } @Override public void mouseReleased(MouseEvent e) { - setBackground(HOVER_COLOR); - textContainer.setBackground(HOVER_COLOR); + setHighlighted(true); } @Override public void mouseEntered(MouseEvent e) { - setBackground(HOVER_COLOR); - textContainer.setBackground(HOVER_COLOR); - setCursor(new Cursor(Cursor.HAND_CURSOR)); + setHighlighted(true); } @Override public void mouseExited(MouseEvent e) { - setBackground(ColorScheme.DARKER_GRAY_COLOR); - textContainer.setBackground(ColorScheme.DARKER_GRAY_COLOR); - setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); + setHighlighted(false); } }); @@ -117,7 +125,8 @@ class OverviewItemPanel extends JPanel add(textContainer, BorderLayout.CENTER); - JLabel arrowLabel = new JLabel(ARROW_RIGHT_ICON); + arrowLabel = new JLabel(ARROW_RIGHT_ICON); + arrowLabel.setVisible(isSelectable.getAsBoolean()); add(arrowLabel, BorderLayout.EAST); } @@ -125,5 +134,26 @@ class OverviewItemPanel extends JPanel { statusLabel.setText(text); statusLabel.setForeground(color); + + arrowLabel.setVisible(isSelectable.getAsBoolean()); + + if (isHighlighted && !isSelectable.getAsBoolean()) + { + setHighlighted(false); + } + } + + private void setHighlighted(boolean highlighted) + { + if (highlighted && !isSelectable.getAsBoolean()) + { + return; + } + + setBackground(highlighted ? HOVER_COLOR : ColorScheme.DARKER_GRAY_COLOR); + setCursor(new Cursor(highlighted && getMousePosition(true) != null ? Cursor.HAND_CURSOR : Cursor.DEFAULT_CURSOR)); + textContainer.setBackground(highlighted ? HOVER_COLOR : ColorScheme.DARKER_GRAY_COLOR); + + isHighlighted = highlighted; } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/OverviewTabPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/OverviewTabPanel.java index f736c49fef..432bf3b87c 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/OverviewTabPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/OverviewTabPanel.java @@ -31,8 +31,11 @@ import java.time.Instant; import java.util.Map; import java.util.function.Function; import java.util.stream.Stream; +import javax.annotation.Nullable; +import net.runelite.api.ItemID; import net.runelite.client.game.ItemManager; 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.ui.ColorScheme; @@ -43,19 +46,23 @@ class OverviewTabPanel extends TabContentPanel private final FarmingTracker farmingTracker; private final BirdHouseTracker birdHouseTracker; private final ClockManager clockManager; + private final FarmingContractManager farmingContractManager; private final OverviewItemPanel timerOverview; private final OverviewItemPanel stopwatchOverview; private final Map farmingOverviews; private final OverviewItemPanel birdHouseOverview; + private final OverviewItemPanel farmingContractOverview; OverviewTabPanel(ItemManager itemManager, TimeTrackingConfig config, TimeTrackingPanel pluginPanel, - FarmingTracker farmingTracker, BirdHouseTracker birdHouseTracker, ClockManager clockManager) + FarmingTracker farmingTracker, BirdHouseTracker birdHouseTracker, ClockManager clockManager, + FarmingContractManager farmingContractManager) { this.config = config; this.farmingTracker = farmingTracker; this.birdHouseTracker = birdHouseTracker; this.clockManager = clockManager; + this.farmingContractManager = farmingContractManager; setLayout(new GridLayout(0, 1, 0, 8)); setBackground(ColorScheme.DARK_GRAY_COLOR); @@ -80,6 +87,10 @@ class OverviewTabPanel extends TabContentPanel return p; } )); + + farmingContractOverview = new OverviewItemPanel(itemManager, () -> pluginPanel.switchTab(farmingContractManager.getContractTab()), + farmingContractManager::hasContract, ItemID.SEED_PACK, "Farming Contract"); + add(farmingContractOverview); } @Override @@ -113,12 +124,14 @@ class OverviewTabPanel extends TabContentPanel } farmingOverviews.forEach((patchType, panel) -> - updateItemPanel(panel, farmingTracker.getSummary(patchType), farmingTracker.getCompletionTime(patchType))); + updateItemPanel(panel, farmingTracker.getSummary(patchType), farmingTracker.getCompletionTime(patchType), null)); - updateItemPanel(birdHouseOverview, birdHouseTracker.getSummary(), birdHouseTracker.getCompletionTime()); + updateItemPanel(birdHouseOverview, birdHouseTracker.getSummary(), birdHouseTracker.getCompletionTime(), null); + updateItemPanel(farmingContractOverview, farmingContractManager.getSummary(), farmingContractManager.getCompletionTime(), + farmingContractManager.getContractName()); } - private void updateItemPanel(OverviewItemPanel panel, SummaryState summary, long completionTime) + private void updateItemPanel(OverviewItemPanel panel, SummaryState summary, long completionTime, @Nullable String farmingContract) { switch (summary) { @@ -135,11 +148,13 @@ class OverviewTabPanel extends TabContentPanel { panel.updateStatus("Ready " + getFormattedEstimate(duration, config.estimateRelative()), Color.GRAY); } - break; } case EMPTY: - panel.updateStatus("Empty", Color.GRAY); + panel.updateStatus(farmingContract == null ? "Empty" : farmingContract, Color.GRAY); + break; + case OCCUPIED: + panel.updateStatus(farmingContract == null ? "" : farmingContract, Color.RED); break; case UNKNOWN: default: diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/SummaryState.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/SummaryState.java index 3dc659f53e..96c4ecdae4 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/SummaryState.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/SummaryState.java @@ -28,6 +28,7 @@ public enum SummaryState { COMPLETED, IN_PROGRESS, + OCCUPIED, EMPTY, UNKNOWN } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TabContentPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TabContentPanel.java index 47740a8b17..f10a00594b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TabContentPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TabContentPanel.java @@ -48,7 +48,7 @@ public abstract class TabContentPanel extends JPanel return super.getPreferredSize(); } - protected static String getFormattedEstimate(long remainingSeconds, boolean useRelativeTime) + public static String getFormattedEstimate(long remainingSeconds, boolean useRelativeTime) { if (useRelativeTime) { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TimeTrackingPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TimeTrackingPanel.java index b984927202..9c99ed27cd 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TimeTrackingPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TimeTrackingPanel.java @@ -41,6 +41,7 @@ import javax.swing.border.EmptyBorder; import net.runelite.client.util.AsyncBufferedImage; import net.runelite.client.game.ItemManager; 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.ui.ColorScheme; @@ -64,7 +65,8 @@ class TimeTrackingPanel extends PluginPanel private TabContentPanel activeTabPanel = null; TimeTrackingPanel(ItemManager itemManager, TimeTrackingConfig config, - FarmingTracker farmingTracker, BirdHouseTracker birdHouseTracker, ClockManager clockManager) + FarmingTracker farmingTracker, BirdHouseTracker birdHouseTracker, ClockManager clockManager, + FarmingContractManager farmingContractManager) { super(false); @@ -82,13 +84,14 @@ class TimeTrackingPanel extends PluginPanel add(tabGroup, BorderLayout.NORTH); add(display, BorderLayout.CENTER); - addTab(Tab.OVERVIEW, new OverviewTabPanel(itemManager, config, this, farmingTracker, birdHouseTracker, clockManager)); + addTab(Tab.OVERVIEW, new OverviewTabPanel(itemManager, config, this, farmingTracker, birdHouseTracker, clockManager, + farmingContractManager)); addTab(Tab.CLOCK, clockManager.getClockTabPanel()); addTab(Tab.BIRD_HOUSE, birdHouseTracker.createBirdHouseTabPanel()); for (Tab tab : Tab.FARMING_TABS) { - addTab(tab, farmingTracker.createTabPanel(tab)); + addTab(tab, farmingTracker.createTabPanel(tab, farmingContractManager)); } } 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 c3def0d822..93eef61337 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 @@ -33,10 +33,12 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import javax.inject.Inject; +import net.runelite.api.ChatMessageType; import net.runelite.api.Client; import net.runelite.api.GameState; import net.runelite.api.coords.WorldPoint; import net.runelite.client.events.ConfigChanged; +import net.runelite.api.events.ChatMessage; import net.runelite.api.events.GameTick; import net.runelite.api.events.UsernameChanged; import net.runelite.api.widgets.Widget; @@ -50,11 +52,13 @@ import static net.runelite.client.plugins.timetracking.TimeTrackingConfig.CONFIG import static net.runelite.client.plugins.timetracking.TimeTrackingConfig.STOPWATCHES; import static net.runelite.client.plugins.timetracking.TimeTrackingConfig.TIMERS; 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; import net.runelite.client.util.ImageUtil; @PluginDescriptor( @@ -64,6 +68,8 @@ import net.runelite.client.util.ImageUtil; ) public class TimeTrackingPlugin extends Plugin { + private static final String CONTRACT_COMPLETED = "You've completed a Farming Guild Contract. You should return to Guildmaster Jane."; + @Inject private ClientToolbar clientToolbar; @@ -76,6 +82,9 @@ public class TimeTrackingPlugin extends Plugin @Inject private BirdHouseTracker birdHouseTracker; + @Inject + private FarmingContractManager farmingContractManager; + @Inject private ClockManager clockManager; @@ -85,6 +94,9 @@ public class TimeTrackingPlugin extends Plugin @Inject private TimeTrackingConfig config; + @Inject + private InfoBoxManager infoBoxManager; + @Inject private ScheduledExecutorService executorService; @@ -113,7 +125,7 @@ public class TimeTrackingPlugin extends Plugin final BufferedImage icon = ImageUtil.getResourceStreamFromClass(getClass(), "watch.png"); - panel = new TimeTrackingPanel(itemManager, config, farmingTracker, birdHouseTracker, clockManager); + panel = new TimeTrackingPanel(itemManager, config, farmingTracker, birdHouseTracker, clockManager, farmingContractManager); navButton = NavigationButton.builder() .tooltip("Time Tracking") @@ -140,6 +152,8 @@ public class TimeTrackingPlugin extends Plugin } clientToolbar.removeNavigation(navButton); + infoBoxManager.removeInfoBox(farmingContractManager.getInfoBox()); + farmingContractManager.setInfoBox(null); } @Subscribe @@ -193,8 +207,9 @@ public class TimeTrackingPlugin extends Plugin boolean birdHouseDataChanged = birdHouseTracker.updateData(loc); boolean farmingDataChanged = farmingTracker.updateData(loc); + boolean farmingContractDataChanged = farmingContractManager.updateData(loc); - if (birdHouseDataChanged || farmingDataChanged) + if (birdHouseDataChanged || farmingDataChanged || farmingContractDataChanged) { panel.update(); } @@ -205,9 +220,21 @@ public class TimeTrackingPlugin extends Plugin { farmingTracker.loadCompletionTimes(); birdHouseTracker.loadFromConfig(); + farmingContractManager.loadContractFromConfig(); panel.update(); } + @Subscribe + public void onChatMessage(ChatMessage event) + { + if (event.getType() != ChatMessageType.GAMEMESSAGE || !event.getMessage().equals(CONTRACT_COMPLETED)) + { + return; + } + + farmingContractManager.setContract(null); + } + @Schedule(period = 10, unit = ChronoUnit.SECONDS) public void checkCompletion() { 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 c01f770063..e7584a6e27 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 @@ -44,8 +44,10 @@ public class TimeablePanel extends JPanel { private final T timeable; private final JLabel icon = new JLabel(); + private final JLabel farmingContractIcon = new JLabel(); private final JLabel estimate = new JLabel(); private final ThinProgressBar progress = new ThinProgressBar(); + private final JLabel text; public TimeablePanel(T timeable, String title, int maximumProgressValue) { @@ -60,23 +62,25 @@ public class TimeablePanel extends JPanel topContainer.setBackground(ColorScheme.DARKER_GRAY_COLOR); icon.setMinimumSize(new Dimension(Constants.ITEM_SPRITE_WIDTH, Constants.ITEM_SPRITE_HEIGHT)); + farmingContractIcon.setMinimumSize(new Dimension(Constants.ITEM_SPRITE_WIDTH, Constants.ITEM_SPRITE_HEIGHT)); JPanel infoPanel = new JPanel(); infoPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR); infoPanel.setLayout(new GridLayout(2, 1)); infoPanel.setBorder(new EmptyBorder(4, 4, 4, 0)); - final JLabel location = new JShadowedLabel(title); - location.setFont(FontManager.getRunescapeSmallFont()); - location.setForeground(Color.WHITE); + text = new JShadowedLabel(title); + text.setFont(FontManager.getRunescapeSmallFont()); + text.setForeground(Color.WHITE); estimate.setFont(FontManager.getRunescapeSmallFont()); estimate.setForeground(Color.GRAY); - infoPanel.add(location); + infoPanel.add(text); infoPanel.add(estimate); 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/FarmingContractInfoBox.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingContractInfoBox.java new file mode 100644 index 0000000000..1c6fb1a19d --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingContractInfoBox.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2019, Koekkruimels + * Copyright (c) 2020, melky + * 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 java.awt.Color; +import java.awt.image.BufferedImage; +import java.time.Instant; +import lombok.Getter; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.timetracking.SummaryState; +import net.runelite.client.plugins.timetracking.TabContentPanel; +import net.runelite.client.plugins.timetracking.TimeTrackingConfig; +import net.runelite.client.ui.ColorScheme; +import net.runelite.client.ui.overlay.infobox.InfoBox; +import net.runelite.client.util.ColorUtil; + +class FarmingContractInfoBox extends InfoBox +{ + @Getter + private final Produce contract; + private final FarmingContractManager manager; + private final TimeTrackingConfig config; + + FarmingContractInfoBox(BufferedImage image, Plugin plugin, Produce contract, TimeTrackingConfig config, FarmingContractManager manager) + { + super(image, plugin); + this.contract = contract; + this.config = config; + this.manager = manager; + } + + @Override + public String getText() + { + return null; + } + + @Override + public Color getTextColor() + { + return null; + } + + @Override + public String getTooltip() + { + SummaryState summary = manager.getSummary(); + + Color contractColor; + String contractDescription; + switch (summary) + { + case COMPLETED: + contractDescription = "Ready"; + contractColor = ColorScheme.PROGRESS_COMPLETE_COLOR; + break; + case OCCUPIED: + contractDescription = "Occupied"; + contractColor = ColorScheme.PROGRESS_ERROR_COLOR; + break; + case IN_PROGRESS: + contractDescription = "Ready " + TabContentPanel.getFormattedEstimate(manager.getCompletionTime() - Instant.now().getEpochSecond(), + config.estimateRelative()); + contractColor = Color.GRAY; + break; + case EMPTY: + case UNKNOWN: + default: + contractDescription = null; + contractColor = Color.GRAY; + break; + } + + StringBuilder sb = new StringBuilder(); + sb.append(ColorUtil.wrapWithColorTag("Farming Contract", Color.WHITE)); + sb.append("
"); + sb.append(ColorUtil.wrapWithColorTag(contract.getName(), contractColor)); + + if (contractDescription != null) + { + sb.append("
"); + sb.append(ColorUtil.wrapWithColorTag(contractDescription, contractColor)); + } + + return sb.toString(); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingContractManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingContractManager.java new file mode 100644 index 0000000000..77ab730ef8 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingContractManager.java @@ -0,0 +1,326 @@ +/* + * Copyright (c) 2019, Koekkruimels + * Copyright (c) 2020, melky + * 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 java.time.Instant; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.inject.Inject; +import lombok.Getter; +import lombok.Setter; +import net.runelite.api.Client; +import net.runelite.api.NullNpcID; +import net.runelite.api.coords.WorldPoint; +import net.runelite.api.widgets.Widget; +import net.runelite.api.widgets.WidgetInfo; +import net.runelite.client.config.ConfigManager; +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.plugins.timetracking.TimeTrackingPlugin; +import net.runelite.client.ui.overlay.infobox.InfoBoxManager; +import net.runelite.client.util.Text; + +public class FarmingContractManager +{ + private static final int GUILDMASTER_JANE_NPC_ID = NullNpcID.NULL_8628; + private static final int FARMING_GUILD_REGION_ID = 4922; + private static final Pattern CONTRACT_ASSIGN_PATTERN = Pattern.compile("(?:We need you to grow|Please could you grow) (?:some|a|an) ([a-zA-Z ]+)(?: for us\\?|\\.)"); + private static final String CONTRACT_REWARDED = "You'll be wanting a reward then. Here you go."; + private static final String CONFIG_KEY_CONTRACT = "contract"; + + @Getter + private SummaryState summary = SummaryState.UNKNOWN; + + @Inject + private Client client; + + @Inject + private ItemManager itemManager; + + @Inject + private TimeTrackingConfig config; + + @Inject + private TimeTrackingPlugin plugin; + + @Inject + private FarmingWorld farmingWorld; + + @Inject + private FarmingTracker farmingTracker; + + @Inject + private InfoBoxManager infoBoxManager; + + @Inject + private ConfigManager configManager; + + @Getter + private Produce contract = null; + + @Getter + @Setter + private FarmingContractInfoBox infoBox; + + @Getter + private long completionTime; + + public void setContract(@Nullable Produce contract) + { + this.contract = contract; + setStoredContract(contract); + handleContractState(); + } + + public boolean hasContract() + { + return contract != null; + } + + @Nullable + public Tab getContractTab() + { + return hasContract() ? contract.getPatchImplementation().getTab() : null; + } + + @Nullable + public String getContractName() + { + return hasContract() ? contract.getContractName() : null; + } + + public boolean shouldHighlightFarmingTabPanel(@Nonnull FarmingPatch patch) + { + PatchPrediction patchPrediction = farmingTracker.predictPatch(patch); + if (contract != null && + patch.getRegion().getRegionID() == FARMING_GUILD_REGION_ID && + contract.getPatchImplementation() == patch.getImplementation() && + patchPrediction != null && + (summary == SummaryState.EMPTY && + (patchPrediction.getProduce() == null || patchPrediction.getProduce() == Produce.WEEDS) + || patchPrediction.getProduce().equals(contract))) + { + return true; + } + return false; + } + + public void loadContractFromConfig() + { + contract = getStoredContract(); + handleContractState(); + } + + public boolean updateData(WorldPoint loc) + { + SummaryState oldSummary = summary; + + handleContractState(); + if (loc.getRegionID() == FARMING_GUILD_REGION_ID) + { + handleGuildmasterJaneWidgetDialog(); + handleInfoBox(); + } + else + { + if (infoBox != null) + { + infoBoxManager.removeInfoBox(infoBox); + infoBox = null; + } + } + return oldSummary != summary; + } + + private void handleInfoBox() + { + if (infoBox != null) + { + if (contract == null) + { + infoBoxManager.removeInfoBox(infoBox); + infoBox = null; + } + else if (infoBox.getContract() != contract) + { + infoBoxManager.removeInfoBox(infoBox); + infoBox = new FarmingContractInfoBox(itemManager.getImage(contract.getItemID()), plugin, contract, config, this); + infoBoxManager.addInfoBox(infoBox); + } + } + else if (contract != null) + { + infoBox = new FarmingContractInfoBox(itemManager.getImage(contract.getItemID()), plugin, contract, config, this); + infoBoxManager.addInfoBox(infoBox); + } + } + + private void handleGuildmasterJaneWidgetDialog() + { + Widget npcDialog = client.getWidget(WidgetInfo.DIALOG_NPC_HEAD_MODEL); + + if (npcDialog == null || npcDialog.getModelId() != GUILDMASTER_JANE_NPC_ID) + { + return; + } + + String dialogText = Text.removeTags(client.getWidget(WidgetInfo.DIALOG_NPC_TEXT).getText()); + + if (dialogText.equals(CONTRACT_REWARDED)) + { + setContract(null); + } + + Matcher matcher = CONTRACT_ASSIGN_PATTERN.matcher(dialogText); + + if (!matcher.find()) + { + return; + } + + String name = matcher.group(1); + + Produce farmingContract = Produce.getByContractName(name); + + if (farmingContract == null) + { + return; + } + + Produce currentFarmingContract = contract; + + if (farmingContract == currentFarmingContract) + { + return; + } + + setContract(farmingContract); + } + + private void handleContractState() + { + if (contract == null) + { + summary = SummaryState.UNKNOWN; + return; + } + + PatchImplementation patchImplementation = contract.getPatchImplementation(); + + boolean hasEmptyPatch = false; + completionTime = Long.MAX_VALUE; + for (FarmingPatch patch : farmingWorld.getFarmingGuildRegion().getPatches()) + { + if (patch.getImplementation() != patchImplementation) + { + continue; + } + + PatchPrediction prediction = farmingTracker.predictPatch(patch); + if (prediction == null) + { + continue; + } + + Produce produce = prediction.getProduce(); + if (completionTime == Long.MAX_VALUE) + { + if (produce == null || produce == Produce.WEEDS) + { + summary = SummaryState.EMPTY; + hasEmptyPatch = true; + continue; + } + + if ((contract.requiresHealthCheck() && prediction.getCropState() == CropState.HARVESTABLE) + && !hasEmptyPatch) + { + summary = SummaryState.OCCUPIED; + } + } + + if (produce != contract) + { + if (!hasEmptyPatch && completionTime == Long.MAX_VALUE) + { + summary = SummaryState.OCCUPIED; + } + } + else + { + long estimatedTime = Math.min(prediction.getDoneEstimate(), completionTime); + + if (estimatedTime <= Instant.now().getEpochSecond()) + { + summary = SummaryState.COMPLETED; + completionTime = 0; + break; + } + else + { + summary = SummaryState.IN_PROGRESS; + completionTime = estimatedTime; + } + } + } + } + + + @Nullable + private Produce getStoredContract() + { + try + { + return Produce.getByItemID(Integer.parseInt(configManager.getConfiguration(getConfigGroup(), CONFIG_KEY_CONTRACT))); + } + catch (NumberFormatException ignored) + { + return null; + } + } + + private void setStoredContract(@Nullable Produce contract) + { + if (contract != null) + { + configManager.setConfiguration(getConfigGroup(), CONFIG_KEY_CONTRACT, String.valueOf(contract.getItemID())); + } + else + { + configManager.unsetConfiguration(getConfigGroup(), CONFIG_KEY_CONTRACT); + } + } + + @Nonnull + private String getConfigGroup() + { + return TimeTrackingConfig.CONFIG_GROUP + "." + client.getUsername(); + } +} 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 fa6852082d..f51bc9af8e 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 @@ -34,6 +34,7 @@ import java.util.List; import java.util.Set; import javax.swing.JLabel; import javax.swing.border.EmptyBorder; +import net.runelite.api.ItemID; import net.runelite.client.game.ItemManager; import net.runelite.client.plugins.timetracking.TabContentPanel; import net.runelite.client.plugins.timetracking.TimeTrackingConfig; @@ -47,18 +48,21 @@ public class FarmingTabPanel extends TabContentPanel private final ItemManager itemManager; private final TimeTrackingConfig config; private final List> patchPanels; + private final FarmingContractManager farmingContractManager; FarmingTabPanel( FarmingTracker farmingTracker, ItemManager itemManager, TimeTrackingConfig config, - Set patches + Set patches, + FarmingContractManager farmingContractManager ) { this.farmingTracker = farmingTracker; this.itemManager = itemManager; this.config = config; this.patchPanels = new ArrayList<>(); + this.farmingContractManager = farmingContractManager; setLayout(new GridBagLayout()); setBackground(ColorScheme.DARK_GRAY_COLOR); @@ -187,6 +191,17 @@ 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(""); + } } } } 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 ec32d63379..c45a9309d7 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 @@ -70,9 +70,9 @@ public class FarmingTracker } - public FarmingTabPanel createTabPanel(Tab tab) + public FarmingTabPanel createTabPanel(Tab tab, FarmingContractManager farmingContractManager) { - return new FarmingTabPanel(this, itemManager, config, farmingWorld.getTabs().get(tab)); + return new FarmingTabPanel(this, itemManager, config, farmingWorld.getTabs().get(tab), farmingContractManager); } /** 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 4683f2feb9..51a08292fb 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 @@ -52,6 +52,9 @@ class FarmingWorld .thenComparing((FarmingPatch p) -> p.getRegion().getName()) .thenComparing(FarmingPatch::getName); + @Getter + private final FarmingRegion farmingGuildRegion; + FarmingWorld() { // Some of these patches get updated in multiple regions. @@ -229,7 +232,7 @@ class FarmingWorld new FarmingPatch("Hespori", Varbits.FARMING_7908, PatchImplementation.HESPORI) )); - add(new FarmingRegion("Farming Guild", 4922, + add(farmingGuildRegion = new FarmingRegion("Farming Guild", 4922, new FarmingPatch("", Varbits.FARMING_7905, PatchImplementation.TREE), new FarmingPatch("", Varbits.FARMING_4775, PatchImplementation.HERB), new FarmingPatch("", Varbits.FARMING_4772, PatchImplementation.BUSH), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/Produce.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/Produce.java index bcb4e5ab0b..e80912552c 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/Produce.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/Produce.java @@ -25,6 +25,7 @@ */ package net.runelite.client.plugins.timetracking.farming; +import javax.annotation.Nullable; import lombok.Getter; import lombok.RequiredArgsConstructor; import net.runelite.api.ItemID; @@ -38,30 +39,30 @@ public enum Produce SCARECROW("Scarecrow", ItemID.SCARECROW, 5, 4), // Allotment crops - POTATO("Potato", ItemID.POTATO, 10, 5, 0, 3), - ONION("Onion", ItemID.ONION, 10, 5, 0, 3), - CABBAGE("Cabbage", ItemID.CABBAGE, 10, 5, 0, 3), - TOMATO("Tomato", ItemID.TOMATO, 10, 5, 0, 3), - SWEETCORN("Sweetcorn", ItemID.SWEETCORN, 10, 6, 0, 3), - STRAWBERRY("Strawberry", ItemID.STRAWBERRY, 10, 7, 0, 3), - WATERMELON("Watermelon", ItemID.WATERMELON, 10, 8, 0, 3), - SNAPE_GRASS("Snape grass", ItemID.SNAPE_GRASS, 10, 8, 0, 3), + POTATO("Potato", "Potatoes", PatchImplementation.ALLOTMENT, ItemID.POTATO, 10, 5, 0, 3), + ONION("Onion", "Onions", PatchImplementation.ALLOTMENT, ItemID.ONION, 10, 5, 0, 3), + CABBAGE("Cabbage", "Cabbages", PatchImplementation.ALLOTMENT, ItemID.CABBAGE, 10, 5, 0, 3), + TOMATO("Tomato", "Tomatoes", PatchImplementation.ALLOTMENT, ItemID.TOMATO, 10, 5, 0, 3), + SWEETCORN("Sweetcorn", PatchImplementation.ALLOTMENT, ItemID.SWEETCORN, 10, 6, 0, 3), + STRAWBERRY("Strawberry", "Strawberries", PatchImplementation.ALLOTMENT, ItemID.STRAWBERRY, 10, 7, 0, 3), + WATERMELON("Watermelon", "Watermelons", PatchImplementation.ALLOTMENT, ItemID.WATERMELON, 10, 8, 0, 3), + SNAPE_GRASS("Snape grass", PatchImplementation.ALLOTMENT, ItemID.SNAPE_GRASS, 10, 8, 0, 3), // Flower crops - MARIGOLD("Marigold", ItemID.MARIGOLDS, 5, 5), - ROSEMARY("Rosemary", ItemID.ROSEMARY, 5, 5), - NASTURTIUM("Nasturtium", ItemID.NASTURTIUMS, 5, 5), - WOAD("Woad", ItemID.WOAD_LEAF, 5, 5), - LIMPWURT("Limpwurt", ItemID.LIMPWURT_ROOT, 5, 5), - WHITE_LILY("While lily", ItemID.WHITE_LILY, 5, 5), + MARIGOLD("Marigold", "Marigolds", PatchImplementation.FLOWER, ItemID.MARIGOLDS, 5, 5), + ROSEMARY("Rosemary", PatchImplementation.FLOWER, ItemID.ROSEMARY, 5, 5), + NASTURTIUM("Nasturtium", "Nasturtiums", PatchImplementation.FLOWER, ItemID.NASTURTIUMS, 5, 5), + WOAD("Woad", PatchImplementation.FLOWER, ItemID.WOAD_LEAF, 5, 5), + LIMPWURT("Limpwurt", "Limpwurt roots", PatchImplementation.FLOWER, ItemID.LIMPWURT_ROOT, 5, 5), + WHITE_LILY("White lily", "White lillies", PatchImplementation.FLOWER, ItemID.WHITE_LILY, 5, 5), // Bush crops - REDBERRIES("Redberry", ItemID.REDBERRIES, 20, 6, 20, 5), - CADAVABERRIES("Cadavaberry", ItemID.CADAVA_BERRIES, 20, 7, 20, 5), - DWELLBERRIES("Dwellberry", ItemID.DWELLBERRIES, 20, 8, 20, 5), - JANGERBERRIES("Jangerberry", ItemID.JANGERBERRIES, 20, 9, 20, 5), - WHITEBERRIES("Whiteberry", ItemID.WHITE_BERRIES, 20, 9, 20, 5), - POISON_IVY("Poison", ItemID.POISON_IVY_BERRIES, 20, 9, 20, 5), + REDBERRIES("Redberry", "Redberries", PatchImplementation.BUSH, ItemID.REDBERRIES, 20, 6, 20, 5), + CADAVABERRIES("Cadavaberry", "Cadava berries", PatchImplementation.BUSH, ItemID.CADAVA_BERRIES, 20, 7, 20, 5), + DWELLBERRIES("Dwellberry", "Dwellberries", PatchImplementation.BUSH, ItemID.DWELLBERRIES, 20, 8, 20, 5), + JANGERBERRIES("Jangerberry", "Jangerberries", PatchImplementation.BUSH, ItemID.JANGERBERRIES, 20, 9, 20, 5), + WHITEBERRIES("Whiteberry", "White berries", PatchImplementation.BUSH, ItemID.WHITE_BERRIES, 20, 9, 20, 5), + POISON_IVY("Poison", "Poison ivy berries", PatchImplementation.BUSH, ItemID.POISON_IVY_BERRIES, 20, 9, 20, 5), // Hop crops BARLEY("Barley", ItemID.BARLEY, 10, 5, 0, 3), @@ -73,43 +74,43 @@ public enum Produce WILDBLOOD("Wildblood", ItemID.WILDBLOOD_HOPS, 10, 9, 0, 3), // Herb crops - GUAM("Guam", ItemID.GUAM_LEAF, 20, 5, 0, 3), - MARRENTILL("Marrentill", ItemID.MARRENTILL, 20, 5, 0, 3), - TARROMIN("Tarromin", ItemID.TARROMIN, 20, 5, 0, 3), - HARRALANDER("Harralander", ItemID.HARRALANDER, 20, 5, 0, 3), - RANARR("Ranarr", ItemID.RANARR_WEED, 20, 5, 0, 3), - TOADFLAX("Toadflax", ItemID.TOADFLAX, 20, 5, 0, 3), - IRIT("Irit", ItemID.IRIT_LEAF, 20, 5, 0, 3), - AVANTOE("Avantoe", ItemID.AVANTOE, 20, 5, 0, 3), - KWUARM("Kwuarm", ItemID.KWUARM, 20, 5, 0, 3), - SNAPDRAGON("Snapdragon", ItemID.SNAPDRAGON, 20, 5, 0, 3), - CADANTINE("Cadantine", ItemID.CADANTINE, 20, 5, 0, 3), - LANTADYME("Lantadyme", ItemID.LANTADYME, 20, 5, 0, 3), - DWARF_WEED("Dwarf Weed", ItemID.DWARF_WEED, 20, 5, 0, 3), - TORSTOL("Torstol", ItemID.TORSTOL, 20, 5, 0, 3), - GOUTWEED("Goutweed", ItemID.GOUTWEED, 20, 5, 0, 2), - ANYHERB("Any herb", ItemID.GUAM_LEAF, 20, 5, 0, 3), + GUAM("Guam", PatchImplementation.HERB, ItemID.GUAM_LEAF, 20, 5, 0, 3), + MARRENTILL("Marrentill", PatchImplementation.HERB, ItemID.MARRENTILL, 20, 5, 0, 3), + TARROMIN("Tarromin", PatchImplementation.HERB, ItemID.TARROMIN, 20, 5, 0, 3), + HARRALANDER("Harralander", PatchImplementation.HERB, ItemID.HARRALANDER, 20, 5, 0, 3), + RANARR("Ranarr", PatchImplementation.HERB, ItemID.RANARR_WEED, 20, 5, 0, 3), + TOADFLAX("Toadflax", PatchImplementation.HERB, ItemID.TOADFLAX, 20, 5, 0, 3), + IRIT("Irit", PatchImplementation.HERB, ItemID.IRIT_LEAF, 20, 5, 0, 3), + AVANTOE("Avantoe", PatchImplementation.HERB, ItemID.AVANTOE, 20, 5, 0, 3), + KWUARM("Kwuarm", PatchImplementation.HERB, ItemID.KWUARM, 20, 5, 0, 3), + SNAPDRAGON("Snapdragon", PatchImplementation.HERB, ItemID.SNAPDRAGON, 20, 5, 0, 3), + CADANTINE("Cadantine", PatchImplementation.HERB, ItemID.CADANTINE, 20, 5, 0, 3), + LANTADYME("Lantadyme", PatchImplementation.HERB, ItemID.LANTADYME, 20, 5, 0, 3), + DWARF_WEED("Dwarf Weed", PatchImplementation.HERB, ItemID.DWARF_WEED, 20, 5, 0, 3), + TORSTOL("Torstol", PatchImplementation.HERB, ItemID.TORSTOL, 20, 5, 0, 3), + GOUTWEED("Goutweed", PatchImplementation.HERB, ItemID.GOUTWEED, 20, 5, 0, 2), + ANYHERB("Any herb", PatchImplementation.HERB, ItemID.GUAM_LEAF, 20, 5, 0, 3), // Tree crops - OAK("Oak", ItemID.OAK_LOGS, 40, 5), - WILLOW("Willow", ItemID.WILLOW_LOGS, 40, 7), - MAPLE("Maple", ItemID.MAPLE_LOGS, 40, 9), - YEW("Yew", ItemID.YEW_LOGS, 40, 11), - MAGIC("Magic", ItemID.MAGIC_LOGS, 40, 13), + OAK("Oak", "Oak tree", PatchImplementation.TREE, ItemID.OAK_LOGS, 40, 5), + WILLOW("Willow", "Willow tree", PatchImplementation.TREE, ItemID.WILLOW_LOGS, 40, 7), + MAPLE("Maple", "Maple tree", PatchImplementation.TREE, ItemID.MAPLE_LOGS, 40, 9), + YEW("Yew", "Yew tree", PatchImplementation.TREE, ItemID.YEW_LOGS, 40, 11), + MAGIC("Magic", "Magic tree", PatchImplementation.TREE, ItemID.MAGIC_LOGS, 40, 13), // Fruit tree crops - APPLE("Apple", ItemID.COOKING_APPLE, 160, 7, 45, 7), - BANANA("Banana", ItemID.BANANA, 160, 7, 45, 7), - ORANGE("Orange", ItemID.ORANGE, 160, 7, 45, 7), - CURRY("Curry", ItemID.CURRY_LEAF, 160, 7, 45, 7), - PINEAPPLE("Pineapple", ItemID.PINEAPPLE, 160, 7, 45, 7), - PAPAYA("Papaya", ItemID.PAPAYA_FRUIT, 160, 7, 45, 7), - PALM("Palm", ItemID.COCONUT, 160, 7, 45, 7), - DRAGONFRUIT("Dragonfruit", ItemID.DRAGONFRUIT, 160, 7, 45, 7), + APPLE("Apple", "Apple tree", PatchImplementation.FRUIT_TREE, ItemID.COOKING_APPLE, 160, 7, 45, 7), + BANANA("Banana", "Banana tree", PatchImplementation.FRUIT_TREE, ItemID.BANANA, 160, 7, 45, 7), + ORANGE("Orange", "Orange tree", PatchImplementation.FRUIT_TREE, ItemID.ORANGE, 160, 7, 45, 7), + CURRY("Curry", "Curry tree", PatchImplementation.FRUIT_TREE, ItemID.CURRY_LEAF, 160, 7, 45, 7), + PINEAPPLE("Pineapple", "Pineapple plant", PatchImplementation.FRUIT_TREE, ItemID.PINEAPPLE, 160, 7, 45, 7), + PAPAYA("Papaya", "Papaya tree", PatchImplementation.FRUIT_TREE, ItemID.PAPAYA_FRUIT, 160, 7, 45, 7), + PALM("Palm", "Palm tree", PatchImplementation.FRUIT_TREE, ItemID.COCONUT, 160, 7, 45, 7), + DRAGONFRUIT("Dragonfruit", "Dragonfruit tree", PatchImplementation.FRUIT_TREE, ItemID.DRAGONFRUIT, 160, 7, 45, 7), // Cactus - CACTUS("Cactus", ItemID.CACTUS_SPINE, 80, 8, 20, 4), - POTATO_CACTUS("Potato cactus", ItemID.POTATO_CACTUS, 10, 8, 5, 7), + CACTUS("Cactus", PatchImplementation.CACTUS, ItemID.CACTUS_SPINE, 80, 8, 20, 4), + POTATO_CACTUS("Potato cactus", "Potato cacti", PatchImplementation.CACTUS, ItemID.POTATO_CACTUS, 10, 8, 5, 7), // Hardwood TEAK("Teak", ItemID.TEAK_LOGS, 560, 8), @@ -127,8 +128,8 @@ public enum Produce BELLADONNA("Belladonna", ItemID.CAVE_NIGHTSHADE, 80, 5), CALQUAT("Calquat", ItemID.CALQUAT_FRUIT, 160, 9, 0, 7), SPIRIT_TREE("Spirit tree", ItemID.SPIRIT_TREE, 320, 13), - CELASTRUS("Celastrus", ItemID.BATTLESTAFF, 160, 6, 0, 4), - REDWOOD("Redwood", ItemID.REDWOOD_LOGS, 640, 11), + CELASTRUS("Celastrus", "Celastrus tree", PatchImplementation.CELASTRUS, ItemID.BATTLESTAFF, 160, 6, 0, 4), + REDWOOD("Redwood", "Redwood tree", PatchImplementation.REDWOOD, ItemID.REDWOOD_LOGS, 640, 11), HESPORI("Hespori", NullItemID.NULL_23044, 640, 4, 0, 2), CRYSTAL_TREE("Crystal tree", ItemID.CRYSTAL_SHARDS, 80, 7); @@ -136,6 +137,14 @@ public enum Produce * User-visible name */ private final String name; + /** + * Farming contract names + */ + private final String contractName; + /** + * Patch type for the crop + */ + private final PatchImplementation patchImplementation; /** * User-visible item icon */ @@ -158,8 +167,68 @@ public enum Produce */ private final int harvestStages; + Produce(String name, int itemID, int tickrate, int stages, int regrowTickrate, int harvestStages) + { + this(name, name, null, itemID, tickrate, stages, regrowTickrate, harvestStages); + } + + Produce(String name, PatchImplementation patchImplementation, int itemID, int tickrate, int stages, int regrowTickrate, int harvestStages) + { + this(name, name, patchImplementation, itemID, tickrate, stages, regrowTickrate, harvestStages); + } + + Produce(String name, String contractName, PatchImplementation patchImplementation, int itemID, int tickrate, int stages) + { + this(name, contractName, patchImplementation, itemID, tickrate, stages, 0, 1); + } + + Produce(String name, PatchImplementation patchImplementation, int itemID, int tickrate, int stages) + { + this(name, name, patchImplementation, itemID, tickrate, stages, 0, 1); + } + Produce(String name, int itemID, int tickrate, int stages) { - this(name, itemID, tickrate, stages, 0, 1); + this(name, name, null, itemID, tickrate, stages, 0, 1); + } + + boolean requiresHealthCheck() + { + switch (this.patchImplementation) + { + case BUSH: + case TREE: + case CACTUS: + case REDWOOD: + case CELASTRUS: + return true; + } + return false; + } + + @Nullable + static Produce getByItemID(int itemId) + { + for (Produce produce : Produce.values()) + { + if (produce.getItemID() == itemId) + { + return produce; + } + } + return null; + } + + @Nullable + static Produce getByContractName(String contractName) + { + for (Produce produce : Produce.values()) + { + if (produce.getContractName().equalsIgnoreCase(contractName)) + { + return produce; + } + } + return null; } }