time tracking: add overview tab
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Daniel Teo <https://github.com/takuyakanbr>
|
||||
* 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;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Cursor;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.GridLayout;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import net.runelite.client.game.ItemManager;
|
||||
import net.runelite.client.ui.ColorScheme;
|
||||
import net.runelite.client.ui.FontManager;
|
||||
import net.runelite.client.util.ImageUtil;
|
||||
|
||||
class OverviewItemPanel extends JPanel
|
||||
{
|
||||
private static final ImageIcon ARROW_RIGHT_ICON;
|
||||
|
||||
private static final Color HOVER_COLOR = ColorScheme.DARKER_GRAY_HOVER_COLOR;
|
||||
|
||||
private final JLabel statusLabel;
|
||||
|
||||
static
|
||||
{
|
||||
ARROW_RIGHT_ICON = new ImageIcon(ImageUtil.getResourceStreamFromClass(TimeTrackingPlugin.class, "/util/arrow_right.png"));
|
||||
}
|
||||
|
||||
OverviewItemPanel(ItemManager itemManager, TimeTrackingPanel pluginPanel, Tab tab, String title)
|
||||
{
|
||||
setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||
setLayout(new BorderLayout());
|
||||
setBorder(new EmptyBorder(7, 7, 7, 7));
|
||||
|
||||
JLabel iconLabel = new JLabel();
|
||||
iconLabel.setMinimumSize(new Dimension(36, 32));
|
||||
itemManager.getImage(tab.getItemID()).addTo(iconLabel);
|
||||
add(iconLabel, BorderLayout.WEST);
|
||||
|
||||
JPanel textContainer = new JPanel();
|
||||
textContainer.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||
textContainer.setLayout(new GridLayout(2, 1));
|
||||
textContainer.setBorder(new EmptyBorder(5, 7, 5, 7));
|
||||
|
||||
addMouseListener(new MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mousePressed(MouseEvent mouseEvent)
|
||||
{
|
||||
pluginPanel.switchTab(tab);
|
||||
setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||
textContainer.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased(MouseEvent e)
|
||||
{
|
||||
setBackground(HOVER_COLOR);
|
||||
textContainer.setBackground(HOVER_COLOR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseEntered(MouseEvent e)
|
||||
{
|
||||
setBackground(HOVER_COLOR);
|
||||
textContainer.setBackground(HOVER_COLOR);
|
||||
setCursor(new Cursor(Cursor.HAND_CURSOR));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseExited(MouseEvent e)
|
||||
{
|
||||
setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||
textContainer.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||
setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
|
||||
}
|
||||
});
|
||||
|
||||
JLabel titleLabel = new JLabel(title);
|
||||
titleLabel.setForeground(Color.WHITE);
|
||||
titleLabel.setFont(FontManager.getRunescapeSmallFont());
|
||||
|
||||
statusLabel = new JLabel();
|
||||
statusLabel.setForeground(Color.GRAY);
|
||||
statusLabel.setFont(FontManager.getRunescapeSmallFont());
|
||||
|
||||
textContainer.add(titleLabel);
|
||||
textContainer.add(statusLabel);
|
||||
|
||||
add(textContainer, BorderLayout.CENTER);
|
||||
|
||||
JLabel arrowLabel = new JLabel(ARROW_RIGHT_ICON);
|
||||
add(arrowLabel, BorderLayout.EAST);
|
||||
}
|
||||
|
||||
void updateStatus(String text, Color color)
|
||||
{
|
||||
statusLabel.setText(text);
|
||||
statusLabel.setForeground(color);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Daniel Teo <https://github.com/takuyakanbr>
|
||||
* 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;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.GridLayout;
|
||||
import java.time.Instant;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import net.runelite.client.game.ItemManager;
|
||||
import net.runelite.client.plugins.timetracking.clocks.ClockManager;
|
||||
import net.runelite.client.plugins.timetracking.farming.FarmingTracker;
|
||||
import net.runelite.client.plugins.timetracking.farming.PatchImplementation;
|
||||
import net.runelite.client.plugins.timetracking.hunter.BirdHouseTracker;
|
||||
import net.runelite.client.ui.ColorScheme;
|
||||
|
||||
class OverviewTabPanel extends TabContentPanel
|
||||
{
|
||||
private final TimeTrackingConfig config;
|
||||
private final FarmingTracker farmingTracker;
|
||||
private final BirdHouseTracker birdHouseTracker;
|
||||
private final ClockManager clockManager;
|
||||
|
||||
private final OverviewItemPanel timerOverview;
|
||||
private final OverviewItemPanel stopwatchOverview;
|
||||
private final Map<PatchImplementation, OverviewItemPanel> farmingOverviews;
|
||||
private final OverviewItemPanel birdHouseOverview;
|
||||
|
||||
OverviewTabPanel(ItemManager itemManager, TimeTrackingConfig config, TimeTrackingPanel pluginPanel,
|
||||
FarmingTracker farmingTracker, BirdHouseTracker birdHouseTracker, ClockManager clockManager)
|
||||
{
|
||||
this.config = config;
|
||||
this.farmingTracker = farmingTracker;
|
||||
this.birdHouseTracker = birdHouseTracker;
|
||||
this.clockManager = clockManager;
|
||||
|
||||
setLayout(new GridLayout(0, 1, 0, 8));
|
||||
setBackground(ColorScheme.DARK_GRAY_COLOR);
|
||||
|
||||
timerOverview = new OverviewItemPanel(itemManager, pluginPanel, Tab.CLOCK, "Timers");
|
||||
add(timerOverview);
|
||||
|
||||
stopwatchOverview = new OverviewItemPanel(itemManager, pluginPanel, Tab.CLOCK, "Stopwatches");
|
||||
add(stopwatchOverview);
|
||||
|
||||
birdHouseOverview = new OverviewItemPanel(itemManager, pluginPanel, Tab.BIRD_HOUSE, "Bird Houses");
|
||||
add(birdHouseOverview);
|
||||
|
||||
farmingOverviews = new LinkedHashMap<>();
|
||||
farmingOverviews.put(PatchImplementation.ALLOTMENT, new OverviewItemPanel(itemManager, pluginPanel, Tab.ALLOTMENT, "Allotment Patches"));
|
||||
farmingOverviews.put(PatchImplementation.FLOWER, new OverviewItemPanel(itemManager, pluginPanel, Tab.FLOWER, "Flower Patches"));
|
||||
farmingOverviews.put(PatchImplementation.HERB, new OverviewItemPanel(itemManager, pluginPanel, Tab.HERB, "Herb Patches"));
|
||||
farmingOverviews.put(PatchImplementation.TREE, new OverviewItemPanel(itemManager, pluginPanel, Tab.TREE, "Tree Patches"));
|
||||
farmingOverviews.put(PatchImplementation.FRUIT_TREE, new OverviewItemPanel(itemManager, pluginPanel, Tab.FRUIT_TREE, "Fruit Tree Patches"));
|
||||
farmingOverviews.put(PatchImplementation.HOPS, new OverviewItemPanel(itemManager, pluginPanel, Tab.HOPS, "Hops Patches"));
|
||||
farmingOverviews.put(PatchImplementation.BUSH, new OverviewItemPanel(itemManager, pluginPanel, Tab.BUSH, "Bush Patches"));
|
||||
farmingOverviews.put(PatchImplementation.GRAPES, new OverviewItemPanel(itemManager, pluginPanel, Tab.GRAPE, "Grape Patches"));
|
||||
|
||||
for (OverviewItemPanel panel : farmingOverviews.values())
|
||||
{
|
||||
add(panel);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getUpdateInterval()
|
||||
{
|
||||
return 50; // 10 seconds
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update()
|
||||
{
|
||||
final long timers = clockManager.getActiveTimerCount();
|
||||
final long stopwatches = clockManager.getActiveStopwatchCount();
|
||||
|
||||
if (timers == 0)
|
||||
{
|
||||
timerOverview.updateStatus("No active timers", Color.GRAY);
|
||||
}
|
||||
else
|
||||
{
|
||||
timerOverview.updateStatus(timers + " active timer" + (timers == 1 ? "" : "s"), ColorScheme.PROGRESS_COMPLETE_COLOR);
|
||||
}
|
||||
|
||||
if (stopwatches == 0)
|
||||
{
|
||||
stopwatchOverview.updateStatus("No active stopwatches", Color.GRAY);
|
||||
}
|
||||
else
|
||||
{
|
||||
stopwatchOverview.updateStatus(stopwatches + " active stopwatch" + (stopwatches == 1 ? "" : "es"), ColorScheme.PROGRESS_COMPLETE_COLOR);
|
||||
}
|
||||
|
||||
farmingOverviews.forEach((patchType, panel) -> updateItemPanel(panel, farmingTracker.getCompletionTime(patchType)));
|
||||
updateItemPanel(birdHouseOverview, birdHouseTracker.getCompletionTime());
|
||||
}
|
||||
|
||||
private void updateItemPanel(OverviewItemPanel panel, long completionTime)
|
||||
{
|
||||
long duration = completionTime - Instant.now().getEpochSecond();
|
||||
|
||||
if (completionTime < 0)
|
||||
{
|
||||
panel.updateStatus("Unknown", Color.GRAY);
|
||||
}
|
||||
else if (duration <= 0)
|
||||
{
|
||||
panel.updateStatus("Ready", ColorScheme.PROGRESS_COMPLETE_COLOR);
|
||||
}
|
||||
else
|
||||
{
|
||||
panel.updateStatus("Ready " + getFormattedEstimate(duration, config.estimateRelative()), Color.GRAY);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,7 @@ import net.runelite.api.ItemID;
|
||||
@Getter
|
||||
public enum Tab
|
||||
{
|
||||
OVERVIEW("Overview", ItemID.OLD_NOTES),
|
||||
CLOCK("Timers & Stopwatches", ItemID.WATCH),
|
||||
BIRD_HOUSE("Bird Houses", ItemID.OAK_BIRD_HOUSE),
|
||||
ALLOTMENT("Allotment Patches", ItemID.CABBAGE),
|
||||
|
||||
@@ -25,6 +25,10 @@
|
||||
package net.runelite.client.plugins.timetracking;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.TextStyle;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Locale;
|
||||
import javax.swing.JPanel;
|
||||
|
||||
public abstract class TabContentPanel extends JPanel
|
||||
@@ -43,4 +47,41 @@ public abstract class TabContentPanel extends JPanel
|
||||
{
|
||||
return super.getPreferredSize();
|
||||
}
|
||||
|
||||
protected static String getFormattedEstimate(long remainingSeconds, boolean useRelativeTime)
|
||||
{
|
||||
if (useRelativeTime)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder("in ");
|
||||
long duration = (remainingSeconds + 59) / 60;
|
||||
long minutes = duration % 60;
|
||||
long hours = (duration / 60) % 24;
|
||||
long days = duration / (60 * 24);
|
||||
if (days > 0)
|
||||
{
|
||||
sb.append(days).append("d ");
|
||||
}
|
||||
if (hours > 0)
|
||||
{
|
||||
sb.append(hours).append("h ");
|
||||
}
|
||||
if (minutes > 0)
|
||||
{
|
||||
sb.append(minutes).append("m ");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
else
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
LocalDateTime endTime = LocalDateTime.now().plus(remainingSeconds, ChronoUnit.SECONDS);
|
||||
LocalDateTime currentTime = LocalDateTime.now();
|
||||
if (endTime.getDayOfWeek() != currentTime.getDayOfWeek())
|
||||
{
|
||||
sb.append(endTime.getDayOfWeek().getDisplayName(TextStyle.FULL, Locale.getDefault())).append(" ");
|
||||
}
|
||||
sb.append(String.format("at %d:%02d", endTime.getHour(), endTime.getMinute()));
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,8 @@ import java.awt.Dimension;
|
||||
import java.awt.GridLayout;
|
||||
import java.awt.Image;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JPanel;
|
||||
@@ -55,6 +57,7 @@ class TimeTrackingPanel extends PluginPanel
|
||||
|
||||
/* This is the panel the tabs' respective panels will be displayed on. */
|
||||
private final JPanel display = new JPanel();
|
||||
private final Map<Tab, MaterialTab> uiTabs = new HashMap<>();
|
||||
private final MaterialTabGroup tabGroup = new MaterialTabGroup(display);
|
||||
|
||||
private boolean active;
|
||||
@@ -81,6 +84,7 @@ 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.CLOCK, clockManager.getClockTabPanel());
|
||||
addTab(Tab.BIRD_HOUSE, birdHouseTracker.createBirdHouseTabPanel());
|
||||
|
||||
@@ -126,13 +130,20 @@ class TimeTrackingPanel extends PluginPanel
|
||||
return true;
|
||||
});
|
||||
|
||||
uiTabs.put(tab, materialTab);
|
||||
tabGroup.addTab(materialTab);
|
||||
|
||||
if (config.activeTab() == tab)
|
||||
{
|
||||
tabGroup.select(materialTab);
|
||||
}
|
||||
}
|
||||
|
||||
void switchTab(Tab tab)
|
||||
{
|
||||
tabGroup.select(uiTabs.get(tab));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the update interval of the active tab panel, in units of 200 milliseconds.
|
||||
*/
|
||||
|
||||
@@ -111,6 +111,7 @@ public class TimeTrackingPlugin extends Plugin
|
||||
clockManager.loadTimers();
|
||||
clockManager.loadStopwatches();
|
||||
birdHouseTracker.loadFromConfig();
|
||||
farmingTracker.loadCompletionTimes();
|
||||
|
||||
final BufferedImage icon = ImageUtil.getResourceStreamFromClass(getClass(), "watch.png");
|
||||
|
||||
@@ -205,6 +206,7 @@ public class TimeTrackingPlugin extends Plugin
|
||||
public void onUsernameChanged(UsernameChanged e)
|
||||
{
|
||||
farmingTracker.migrateConfiguration();
|
||||
farmingTracker.loadCompletionTimes();
|
||||
birdHouseTracker.loadFromConfig();
|
||||
panel.update();
|
||||
}
|
||||
|
||||
@@ -93,6 +93,16 @@ public class ClockManager
|
||||
SwingUtilities.invokeLater(clockTabPanel::rebuild);
|
||||
}
|
||||
|
||||
public long getActiveTimerCount()
|
||||
{
|
||||
return timers.stream().filter(Timer::isActive).count();
|
||||
}
|
||||
|
||||
public long getActiveStopwatchCount()
|
||||
{
|
||||
return stopwatches.stream().filter(Stopwatch::isActive).count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if any timers have completed, and send notifications if required.
|
||||
*/
|
||||
|
||||
@@ -29,12 +29,8 @@ import com.google.common.base.Strings;
|
||||
import java.awt.GridBagConstraints;
|
||||
import java.awt.GridBagLayout;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.format.TextStyle;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
@@ -215,40 +211,9 @@ public class FarmingTabPanel extends TabContentPanel
|
||||
{
|
||||
panel.getEstimate().setText("Done");
|
||||
}
|
||||
else if (config.estimateRelative())
|
||||
{
|
||||
int remaining = (int) (59 + doneEstimate - unixNow) / 60;
|
||||
StringBuilder f = new StringBuilder();
|
||||
f.append("Done in ");
|
||||
int min = remaining % 60;
|
||||
int hours = (remaining / 60) % 24;
|
||||
int days = remaining / (60 * 24);
|
||||
if (days > 0)
|
||||
{
|
||||
f.append(days).append("d ");
|
||||
}
|
||||
if (hours > 0)
|
||||
{
|
||||
f.append(hours).append("h ");
|
||||
}
|
||||
if (min > 0)
|
||||
{
|
||||
f.append(min).append("m ");
|
||||
}
|
||||
panel.getEstimate().setText(f.toString());
|
||||
}
|
||||
else
|
||||
{
|
||||
StringBuilder f = new StringBuilder();
|
||||
LocalDateTime ldtTime = LocalDateTime.ofEpochSecond(doneEstimate, 0, OffsetDateTime.now().getOffset());
|
||||
LocalDateTime ldtNow = LocalDateTime.now();
|
||||
f.append("Done ");
|
||||
if (ldtTime.getDayOfWeek() != ldtNow.getDayOfWeek())
|
||||
{
|
||||
f.append(ldtTime.getDayOfWeek().getDisplayName(TextStyle.FULL, Locale.getDefault())).append(" ");
|
||||
}
|
||||
f.append(String.format("at %d:%02d", ldtTime.getHour(), ldtTime.getMinute()));
|
||||
panel.getEstimate().setText(f.toString());
|
||||
panel.getEstimate().setText("Done " + getFormattedEstimate(doneEstimate - unixNow, config.estimateRelative()));
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -27,6 +27,8 @@ package net.runelite.client.plugins.timetracking.farming;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
import java.time.Instant;
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.Varbits;
|
||||
import net.runelite.api.coords.WorldPoint;
|
||||
@@ -59,6 +61,15 @@ public class FarmingTracker
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The time at which all patches of a particular type will be ready to be harvested,
|
||||
* or {@code -1} if we have no data about any patch of the given type.
|
||||
*
|
||||
* Each value is set to {@code 0} if all patches of that type have already completed
|
||||
* when updating the value.
|
||||
*/
|
||||
private Map<PatchImplementation, Long> completionTimes = new EnumMap<>(PatchImplementation.class);
|
||||
|
||||
public FarmingTabPanel createTabPanel(Tab tab)
|
||||
{
|
||||
return new FarmingTabPanel(client, itemManager, configManager, config, farmingWorld.getTabs().get(tab));
|
||||
@@ -88,9 +99,10 @@ public class FarmingTracker
|
||||
// timetracking.<login-username>.<regionID>.<VarbitID>=<varbitValue>:<unix time>
|
||||
String group = TimeTrackingConfig.CONFIG_GROUP + "." + client.getUsername() + "." + region.getRegionID();
|
||||
long unixNow = Instant.now().getEpochSecond();
|
||||
for (Varbits varbit : region.getVarbits())
|
||||
for (FarmingPatch patch : region.getPatches())
|
||||
{
|
||||
// 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 = Integer.toString(varbit.getId());
|
||||
String strVarbit = Integer.toString(client.getVar(varbit));
|
||||
String storedValue = configManager.getConfiguration(group, key);
|
||||
@@ -118,6 +130,7 @@ public class FarmingTracker
|
||||
|
||||
String value = strVarbit + ":" + unixNow;
|
||||
configManager.setConfiguration(group, key, value);
|
||||
updateCompletionTime(patch.getImplementation());
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
@@ -125,6 +138,97 @@ public class FarmingTracker
|
||||
return changed;
|
||||
}
|
||||
|
||||
public void loadCompletionTimes()
|
||||
{
|
||||
completionTimes.clear();
|
||||
|
||||
for (PatchImplementation patchType : PatchImplementation.values())
|
||||
{
|
||||
updateCompletionTime(patchType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the overall completion time for the given patch type.
|
||||
* @see #completionTimes
|
||||
*/
|
||||
public long getCompletionTime(PatchImplementation patchType)
|
||||
{
|
||||
Long completionTime = completionTimes.get(patchType);
|
||||
return completionTime == null ? -1 : completionTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the overall completion time for the given patch type.
|
||||
* @see #completionTimes
|
||||
*/
|
||||
private void updateCompletionTime(PatchImplementation patchType)
|
||||
{
|
||||
long maxCompletionTime = 0;
|
||||
boolean allUnknown = true;
|
||||
|
||||
for (FarmingPatch patch : farmingWorld.getPatchTypes().get(patchType))
|
||||
{
|
||||
String group = TimeTrackingConfig.CONFIG_GROUP + "." + client.getUsername() + "." + patch.getRegion().getRegionID();
|
||||
String key = Integer.toString(patch.getVarbit().getId());
|
||||
String storedValue = configManager.getConfiguration(group, key);
|
||||
long unixTime = 0;
|
||||
int value = 0;
|
||||
|
||||
if (storedValue != null)
|
||||
{
|
||||
String[] parts = storedValue.split(":");
|
||||
if (parts.length == 2)
|
||||
{
|
||||
try
|
||||
{
|
||||
value = Integer.parseInt(parts[0]);
|
||||
unixTime = Long.parseLong(parts[1]);
|
||||
}
|
||||
catch (NumberFormatException e)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PatchState state = unixTime <= 0 ? null : patch.getImplementation().forVarbitValue(value);
|
||||
if (state == null || state.getProduce().getItemID() < 0)
|
||||
{
|
||||
continue; // unknown state
|
||||
}
|
||||
|
||||
int tickrate = state.getTickRate() * 60;
|
||||
int stage = state.getStage();
|
||||
int stages = state.getStages();
|
||||
|
||||
if (state.getProduce() != Produce.WEEDS && state.getProduce() != Produce.SCARECROW)
|
||||
{
|
||||
// update max duration if this patch takes longer to grow
|
||||
if (tickrate > 0)
|
||||
{
|
||||
long tickTime = unixTime / tickrate;
|
||||
long doneEstimate = ((stages - 1 - stage) + tickTime) * tickrate;
|
||||
maxCompletionTime = Math.max(maxCompletionTime, doneEstimate);
|
||||
}
|
||||
else if (state.getCropState() == CropState.GROWING && stage != stages - 1)
|
||||
{
|
||||
continue; // unknown state
|
||||
}
|
||||
}
|
||||
|
||||
allUnknown = false;
|
||||
}
|
||||
|
||||
if (allUnknown)
|
||||
{
|
||||
completionTimes.put(patchType, -1L);
|
||||
return;
|
||||
}
|
||||
|
||||
completionTimes.put(patchType, (maxCompletionTime <= Instant.now().getEpochSecond()) ? 0 : maxCompletionTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates configuration data from {@code "farmingTracker"} key to {@code "timetracking"} key.
|
||||
* This method should be removed after a reasonable amount of time.
|
||||
|
||||
@@ -26,9 +26,12 @@
|
||||
package net.runelite.client.plugins.timetracking.farming;
|
||||
|
||||
import com.google.inject.Singleton;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.EnumMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
@@ -47,6 +50,9 @@ class FarmingWorld
|
||||
@Getter
|
||||
private Map<Tab, Set<FarmingPatch>> tabs = new HashMap<>();
|
||||
|
||||
@Getter
|
||||
private Map<PatchImplementation, List<FarmingPatch>> patchTypes = new EnumMap<>(PatchImplementation.class);
|
||||
|
||||
private final Comparator<FarmingPatch> tabSorter = Comparator
|
||||
.comparing(FarmingPatch::getImplementation)
|
||||
.thenComparing((FarmingPatch p) -> p.getRegion().getName())
|
||||
@@ -243,6 +249,10 @@ class FarmingWorld
|
||||
tabs
|
||||
.computeIfAbsent(p.getImplementation().getTab(), k -> new TreeSet<>(tabSorter))
|
||||
.add(p);
|
||||
|
||||
patchTypes
|
||||
.computeIfAbsent(p.getImplementation(), k -> new ArrayList<>())
|
||||
.add(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,12 +28,8 @@ package net.runelite.client.plugins.timetracking.hunter;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.format.TextStyle;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import net.runelite.api.ItemID;
|
||||
import net.runelite.client.game.ItemManager;
|
||||
import net.runelite.client.plugins.timetracking.TabContentPanel;
|
||||
@@ -130,48 +126,17 @@ public class BirdHouseTabPanel extends TabContentPanel
|
||||
panel.getEstimate().setText("Built");
|
||||
break;
|
||||
case SEEDED:
|
||||
long doneEstimate = startTime + BirdHouseTracker.BIRD_HOUSE_DURATION;
|
||||
if (doneEstimate < unixNow)
|
||||
long remainingTime = startTime + BirdHouseTracker.BIRD_HOUSE_DURATION - unixNow;
|
||||
if (remainingTime <= 0)
|
||||
{
|
||||
panel.getProgress().setValue(BirdHouseTracker.BIRD_HOUSE_DURATION);
|
||||
panel.getProgress().setForeground(COMPLETED_COLOR);
|
||||
panel.getEstimate().setText("Done");
|
||||
}
|
||||
else if (config.estimateRelative())
|
||||
{
|
||||
int remainingSeconds = (int) (59 + doneEstimate - unixNow);
|
||||
int remaining = remainingSeconds / 60;
|
||||
panel.getProgress().setValue(BirdHouseTracker.BIRD_HOUSE_DURATION - remainingSeconds);
|
||||
StringBuilder f = new StringBuilder("Done in ");
|
||||
int min = remaining % 60;
|
||||
int hours = (remaining / 60) % 24;
|
||||
int days = remaining / (60 * 24);
|
||||
if (days > 0)
|
||||
{
|
||||
f.append(days).append("d ");
|
||||
}
|
||||
if (hours > 0)
|
||||
{
|
||||
f.append(hours).append("h ");
|
||||
}
|
||||
if (min > 0)
|
||||
{
|
||||
f.append(min).append("m ");
|
||||
}
|
||||
panel.getEstimate().setText(f.toString());
|
||||
}
|
||||
else
|
||||
{
|
||||
StringBuilder f = new StringBuilder();
|
||||
LocalDateTime ldtTime = LocalDateTime.ofEpochSecond(doneEstimate, 0, OffsetDateTime.now().getOffset());
|
||||
LocalDateTime ldtNow = LocalDateTime.now();
|
||||
f.append("Done ");
|
||||
if (ldtTime.getDayOfWeek() != ldtNow.getDayOfWeek())
|
||||
{
|
||||
f.append(ldtTime.getDayOfWeek().getDisplayName(TextStyle.FULL, Locale.getDefault())).append(" ");
|
||||
}
|
||||
f.append(String.format("at %d:%02d", ldtTime.getHour(), ldtTime.getMinute()));
|
||||
panel.getEstimate().setText(f.toString());
|
||||
panel.getProgress().setValue((int) (BirdHouseTracker.BIRD_HOUSE_DURATION - remainingTime));
|
||||
panel.getEstimate().setText("Done " + getFormattedEstimate(remainingTime, config.estimateRelative()));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -78,6 +78,7 @@ public class BirdHouseTracker
|
||||
* This is set to {@code 0} if the bird houses have already completed
|
||||
* when updating it.
|
||||
*/
|
||||
@Getter
|
||||
private long completionTime = -1;
|
||||
|
||||
public BirdHouseTabPanel createBirdHouseTabPanel()
|
||||
|
||||
Reference in New Issue
Block a user