Convert farming tracker to time tracking plugin

This commit is contained in:
takuyakanbr
2018-07-28 23:11:12 +08:00
parent 46c2745374
commit b502964142
19 changed files with 509 additions and 273 deletions

View File

@@ -22,7 +22,7 @@
* (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.farmingtracker;
package net.runelite.client.plugins.timetracking;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@@ -32,12 +32,17 @@ import net.runelite.api.ItemID;
@Getter
public enum Tab
{
ALLOTMENT("Allotments", ItemID.CABBAGE),
HERB("Herbs", ItemID.GRIMY_RANARR_WEED),
TREE("Trees", ItemID.MAHOGANY_LOGS),
FRUIT_TREE("Fruit Trees", ItemID.PINEAPPLE),
BUSH("Bushes", ItemID.REDBERRIES),
SPECIAL("Special", ItemID.MUSHROOM);
ALLOTMENT("Allotment Patches", ItemID.CABBAGE),
FLOWER("Flower Patches", ItemID.RED_FLOWERS),
HERB("Herb Patches", ItemID.GRIMY_RANARR_WEED),
TREE("Tree Patches", ItemID.YEW_LOGS),
FRUIT_TREE("Fruit Tree Patches", ItemID.PINEAPPLE),
HOPS("Hops Patches", ItemID.BARLEY),
BUSH("Bush Patches", ItemID.POISON_IVY_BERRIES),
GRAPE("Grape Patches", ItemID.GRAPES),
SPECIAL("Special Patches", ItemID.MUSHROOM);
public static final Tab[] FARMING_TABS = {ALLOTMENT, FLOWER, HERB, TREE, FRUIT_TREE, HOPS, BUSH, GRAPE, SPECIAL};
private final String name;
private final int itemID;

View File

@@ -0,0 +1,40 @@
/*
* 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.Dimension;
import javax.swing.JPanel;
import net.runelite.client.ui.PluginPanel;
public abstract class TabContentPanel extends JPanel
{
public abstract void update();
@Override
public Dimension getPreferredSize()
{
return new Dimension(PluginPanel.PANEL_WIDTH, super.getPreferredSize().height);
}
}

View File

@@ -22,22 +22,22 @@
* (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.farmingtracker;
package net.runelite.client.plugins.timetracking;
import net.runelite.client.config.Config;
import net.runelite.client.config.ConfigGroup;
import net.runelite.client.config.ConfigItem;
@ConfigGroup("farmingTracker")
public interface FarmingTrackerConfig extends Config
@ConfigGroup("timetracking")
public interface TimeTrackingConfig extends Config
{
String KEY_NAME = "farmingTracker";
String KEY_NAME = "timetracking";
String AUTOWEED = "autoweed";
@ConfigItem(
keyName = "estimateRelative",
name = "Show relative time",
description = "Show amount of time remaining for a patch, opposed to when the patch is finished"
description = "Show amount of time remaining instead of completion time"
)
default boolean estimateRelative()
{
@@ -45,21 +45,21 @@ public interface FarmingTrackerConfig extends Config
}
@ConfigItem(
keyName = "patch",
name = "Default patch",
description = "Default patch on opening the panel",
keyName = "activeTab",
name = "Active Tab",
description = "The currently selected tab",
hidden = true
)
default Tab patch()
default Tab activeTab()
{
return Tab.ALLOTMENT;
}
@ConfigItem(
keyName = "patch",
keyName = "activeTab",
name = "",
description = "",
hidden = true
)
void setPatch(Tab t);
}
void setActiveTab(Tab t);
}

View File

@@ -0,0 +1,151 @@
/*
* Copyright (c) 2018 Abex
* Copyright (c) 2018, Psikoi <https://github.com/psikoi>
* 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.Dimension;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.image.BufferedImage;
import javax.annotation.Nullable;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.border.EmptyBorder;
import lombok.extern.slf4j.Slf4j;
import net.runelite.client.game.AsyncBufferedImage;
import net.runelite.client.game.ItemManager;
import net.runelite.client.plugins.timetracking.farming.FarmingTracker;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.PluginPanel;
import net.runelite.client.ui.components.materialtabs.MaterialTab;
import net.runelite.client.ui.components.materialtabs.MaterialTabGroup;
@Slf4j
class TimeTrackingPanel extends PluginPanel
{
private final ItemManager itemManager;
private final TimeTrackingConfig config;
/* This is the panel the tabs' respective panels will be displayed on. */
private final JPanel display = new JPanel();
private final MaterialTabGroup tabGroup = new MaterialTabGroup(display);
private boolean active;
@Nullable
private TabContentPanel activeTabPanel = null;
TimeTrackingPanel(ItemManager itemManager, TimeTrackingConfig config, FarmingTracker farmingTracker)
{
super(false);
this.itemManager = itemManager;
this.config = config;
setLayout(new BorderLayout());
setBackground(ColorScheme.DARK_GRAY_COLOR);
display.setBorder(new EmptyBorder(10, 10, 8, 10));
tabGroup.setLayout(new GridLayout(0, 6, 7, 7));
tabGroup.setBorder(new EmptyBorder(10, 10, 0, 10));
add(tabGroup, BorderLayout.NORTH);
add(display, BorderLayout.CENTER);
for (Tab tab : Tab.FARMING_TABS)
{
addTab(tab, farmingTracker.createTabPanel(tab));
}
}
private void addTab(Tab tab, TabContentPanel tabContentPanel)
{
JPanel wrapped = new JPanel(new BorderLayout());
wrapped.add(tabContentPanel, BorderLayout.NORTH);
wrapped.setBackground(ColorScheme.DARK_GRAY_COLOR);
JScrollPane scroller = new JScrollPane(wrapped);
scroller.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
scroller.getVerticalScrollBar().setPreferredSize(new Dimension(16, 0));
scroller.getVerticalScrollBar().setBorder(new EmptyBorder(0, 9, 0, 0));
scroller.setBackground(ColorScheme.DARK_GRAY_COLOR);
// Use a placeholder icon until the async image gets loaded
MaterialTab materialTab = new MaterialTab(new ImageIcon(), tabGroup, scroller);
materialTab.setPreferredSize(new Dimension(30, 27));
materialTab.setName(tab.getName());
materialTab.setToolTipText(tab.getName());
AsyncBufferedImage icon = itemManager.getImage(tab.getItemID());
Runnable resize = () ->
{
BufferedImage subIcon = icon.getSubimage(0, 0, 32, 32);
materialTab.setIcon(new ImageIcon(subIcon.getScaledInstance(24, 24, Image.SCALE_SMOOTH)));
};
icon.onChanged(resize);
resize.run();
materialTab.setOnSelectEvent(() ->
{
config.setActiveTab(tab);
activeTabPanel = tabContentPanel;
tabContentPanel.update();
return true;
});
tabGroup.addTab(materialTab);
if (config.activeTab() == tab)
{
tabGroup.select(materialTab);
}
}
void update()
{
if (!active || activeTabPanel == null)
{
return;
}
activeTabPanel.update();
}
@Override
public void onActivate()
{
active = true;
update();
}
@Override
public void onDeactivate()
{
active = false;
}
}

View File

@@ -1,5 +1,6 @@
/*
* Copyright (c) 2018 Abex
* Copyright (c) 2018, Daniel Teo <https://github.com/takuyakanbr>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -22,19 +23,17 @@
* (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.farmingtracker;
package net.runelite.client.plugins.timetracking;
import com.google.common.eventbus.Subscribe;
import com.google.inject.Provides;
import java.awt.image.BufferedImage;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import javax.inject.Inject;
import javax.swing.SwingUtilities;
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.events.GameTick;
import net.runelite.api.events.UsernameChanged;
@@ -42,58 +41,56 @@ import net.runelite.client.config.ConfigManager;
import net.runelite.client.game.ItemManager;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.plugins.timetracking.farming.FarmingTracker;
import net.runelite.client.task.Schedule;
import net.runelite.client.ui.NavigationButton;
import net.runelite.client.ui.ClientToolbar;
import net.runelite.client.util.ImageUtil;
@PluginDescriptor(
name = "Farming Tracker",
description = "Show when your farming plots would be fully grown",
tags = {"skilling", "panel", "timers"}
name = "Time Tracking",
description = "Enable the Time Tracking panel, which contains farming trackers",
tags = {"farming", "skilling", "panel"}
)
@Slf4j
public class FarmingTrackerPlugin extends Plugin
public class TimeTrackingPlugin extends Plugin
{
@Inject
private ClientToolbar clientToolbar;
@Inject
private ConfigManager configManager;
@Inject
private Client client;
@Inject
private FarmingWorld farmingWorld;
private FarmingTracker farmingTracker;
@Inject
private ItemManager itemManager;
@Inject
private FarmingTrackerConfig config;
private TimeTrackingConfig config;
private FarmingTrackerPanel panel;
private TimeTrackingPanel panel;
private NavigationButton navButton;
private WorldPoint lastTickLoc;
private WorldPoint lastTickLocation;
@Provides
FarmingTrackerConfig provideConfig(ConfigManager configManager)
TimeTrackingConfig provideConfig(ConfigManager configManager)
{
return configManager.getConfig(FarmingTrackerConfig.class);
return configManager.getConfig(TimeTrackingConfig.class);
}
@Override
protected void startUp() throws Exception
{
final BufferedImage icon = ImageUtil.getResourceStreamFromClass(getClass(), "farming.png");
final BufferedImage icon = ImageUtil.getResourceStreamFromClass(getClass(), "watch.png");
panel = new FarmingTrackerPanel(client, itemManager, configManager, config, farmingWorld);
panel = new TimeTrackingPanel(itemManager, config, farmingTracker);
navButton = NavigationButton.builder()
.tooltip("Farming Tracker")
.tooltip("Time Tracking")
.icon(icon)
.panel(panel)
.priority(4)
@@ -104,15 +101,10 @@ public class FarmingTrackerPlugin extends Plugin
updatePanel();
}
@Subscribe
public void onUsernameChanged(UsernameChanged e)
{
updatePanel();
}
@Override
protected void shutDown() throws Exception
{
lastTickLocation = null;
clientToolbar.removeNavigation(navButton);
}
@@ -121,73 +113,33 @@ public class FarmingTrackerPlugin extends Plugin
{
if (client.getGameState() != GameState.LOGGED_IN)
{
lastTickLoc = null;
lastTickLocation = null;
return;
}
WorldPoint loc = lastTickLoc;
lastTickLoc = client.getLocalPlayer().getWorldLocation();
if (loc == null || loc.getRegionID() != lastTickLoc.getRegionID())
WorldPoint loc = lastTickLocation;
lastTickLocation = client.getLocalPlayer().getWorldLocation();
if (loc == null || loc.getRegionID() != lastTickLocation.getRegionID())
{
return;
}
boolean changed = false;
boolean farmingDataChanged = farmingTracker.updateData(loc);
{
String group = FarmingTrackerConfig.KEY_NAME + "." + client.getUsername();
String autoweed = Integer.toString(client.getVar(Varbits.AUTOWEED));
if (!autoweed.equals(configManager.getConfiguration(group, FarmingTrackerConfig.AUTOWEED)))
{
configManager.setConfiguration(group, FarmingTrackerConfig.AUTOWEED, autoweed);
changed = true;
}
}
FarmingRegion region = farmingWorld.getRegions().get(loc.getRegionID());
if (region != null && region.isInBounds(loc))
{
// Write config with new varbits
// farmingTracker.<login-username>.<regionID>.<VarbitID>=<varbitValue>:<unix time>
String group = FarmingTrackerConfig.KEY_NAME + "." + client.getUsername() + "." + region.getRegionID();
long unixNow = Instant.now().getEpochSecond();
for (Varbits varbit : region.getVarbits())
{
// Write the config value if it doesn't match what is current, or it is more than 5 minutes old
String key = Integer.toString(varbit.getId());
String strVarbit = Integer.toString(client.getVar(varbit));
String storedValue = configManager.getConfiguration(group, key);
if (storedValue != null)
{
String[] parts = storedValue.split(":");
if (parts.length == 2 && parts[0].equals(strVarbit))
{
long unixTime = 0;
try
{
unixTime = Long.parseLong(parts[1]);
}
catch (NumberFormatException e)
{
}
if (unixTime + (5 * 60) > unixNow && unixNow + 30 > unixTime)
{
continue;
}
}
}
String value = strVarbit + ":" + unixNow;
configManager.setConfiguration(group, key, value);
changed = true;
}
}
if (changed)
if (farmingDataChanged)
{
updatePanel();
}
}
@Subscribe
public void onUsernameChanged(UsernameChanged e)
{
farmingTracker.migrateConfiguration();
updatePanel();
}
@Schedule(period = 10, unit = ChronoUnit.SECONDS)
public void updatePanel()
{

View File

@@ -22,7 +22,7 @@
* (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.farmingtracker;
package net.runelite.client.plugins.timetracking.farming;
import java.awt.Color;
import lombok.Getter;

View File

@@ -22,7 +22,7 @@
* (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.farmingtracker;
package net.runelite.client.plugins.timetracking.farming;
import lombok.AccessLevel;
import lombok.Getter;
@@ -34,7 +34,7 @@ import net.runelite.api.Varbits;
access = AccessLevel.PACKAGE
)
@Getter
public class FarmingPatch
class FarmingPatch
{
@Setter(AccessLevel.PACKAGE)
private FarmingRegion region;

View File

@@ -23,7 +23,7 @@
* (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.farmingtracker;
package net.runelite.client.plugins.timetracking.farming;
import com.google.common.base.Strings;
import java.awt.BorderLayout;

View File

@@ -22,7 +22,7 @@
* (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.farmingtracker;
package net.runelite.client.plugins.timetracking.farming;
import lombok.Getter;
import net.runelite.api.Varbits;

View File

@@ -23,15 +23,11 @@
* (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.farmingtracker;
package net.runelite.client.plugins.timetracking.farming;
import com.google.common.base.Strings;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
@@ -39,180 +35,106 @@ import java.time.format.TextStyle;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import javax.swing.ImageIcon;
import java.util.Set;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.border.EmptyBorder;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
import net.runelite.api.vars.Autoweed;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.game.AsyncBufferedImage;
import net.runelite.client.game.ItemManager;
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.FontManager;
import net.runelite.client.ui.PluginPanel;
import net.runelite.client.ui.components.materialtabs.MaterialTab;
import net.runelite.client.ui.components.materialtabs.MaterialTabGroup;
@Slf4j
class FarmingTrackerPanel extends PluginPanel
public class FarmingTabPanel extends TabContentPanel
{
private final Client client;
private final ItemManager itemManager;
private final ConfigManager configManager;
private final FarmingTrackerConfig config;
private final TimeTrackingConfig config;
private final List<FarmingPatchPanel> patchPanels;
private boolean active;
private List<FarmingPatchPanel> patchPanels = new ArrayList<>();
/* This is the panel the tabs' respective panels will be displayed on. */
private final JPanel display = new JPanel();
private final MaterialTabGroup tabGroup = new MaterialTabGroup(display);
FarmingTrackerPanel(
Client client,
ItemManager itemManager,
ConfigManager configManager,
FarmingTrackerConfig config,
FarmingWorld farmingWorld
)
FarmingTabPanel(Client client, ItemManager itemManager, ConfigManager configManager,
TimeTrackingConfig config, Set<FarmingPatch> patches)
{
super(false);
this.client = client;
this.itemManager = itemManager;
this.configManager = configManager;
this.config = config;
this.patchPanels = new ArrayList<>();
setLayout(new BorderLayout());
setLayout(new GridBagLayout());
setBackground(ColorScheme.DARK_GRAY_COLOR);
display.setBorder(new EmptyBorder(10, 10, 8, 10));
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 1;
c.gridx = 0;
c.gridy = 0;
tabGroup.setBorder(new EmptyBorder(10, 1, 0, 0));
PatchImplementation lastImpl = null;
add(tabGroup, BorderLayout.NORTH);
add(display, BorderLayout.CENTER);
farmingWorld.getTabs().forEach((tab, patches) ->
boolean first = true;
for (FarmingPatch patch : patches)
{
JPanel container = new JPanel(new GridBagLayout())
FarmingPatchPanel p = new FarmingPatchPanel(patch);
/* Show labels to subdivide tabs into sections */
if (patch.getImplementation() != lastImpl && !Strings.isNullOrEmpty(patch.getImplementation().getName()))
{
@Override
public Dimension getPreferredSize()
{
return new Dimension(PluginPanel.PANEL_WIDTH, super.getPreferredSize().height);
}
};
container.setBackground(ColorScheme.DARK_GRAY_COLOR);
JLabel groupLabel = new JLabel(patch.getImplementation().getName());
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 1;
c.gridx = 0;
c.gridy = 0;
PatchImplementation lastImpl = null;
boolean first = true;
for (FarmingPatch patch : patches)
{
FarmingPatchPanel p = new FarmingPatchPanel(patch);
/* Show labels to subdivide tabs into sections */
if (patch.getImplementation() != lastImpl && !Strings.isNullOrEmpty(patch.getImplementation().getName()))
{
JLabel groupLabel = new JLabel(patch.getImplementation().getName());
if (first)
{
first = false;
groupLabel.setBorder(new EmptyBorder(4, 0, 0, 0));
}
else
{
groupLabel.setBorder(new EmptyBorder(15, 0, 0, 0));
}
groupLabel.setFont(FontManager.getRunescapeSmallFont());
container.add(groupLabel, c);
c.gridy++;
lastImpl = patch.getImplementation();
}
patchPanels.add(p);
container.add(p, c);
c.gridy++;
/* This is a weird hack to remove the top border on the first tracker of every tab */
if (first)
{
first = false;
p.setBorder(null);
groupLabel.setBorder(new EmptyBorder(4, 0, 0, 0));
}
else
{
groupLabel.setBorder(new EmptyBorder(15, 0, 0, 0));
}
groupLabel.setFont(FontManager.getRunescapeSmallFont());
add(groupLabel, c);
c.gridy++;
lastImpl = patch.getImplementation();
}
JPanel wrapped = new JPanel(new BorderLayout());
wrapped.add(container, BorderLayout.NORTH);
wrapped.setBackground(ColorScheme.DARK_GRAY_COLOR);
patchPanels.add(p);
add(p, c);
c.gridy++;
JScrollPane scroller = new JScrollPane(wrapped);
scroller.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
scroller.getVerticalScrollBar().setPreferredSize(new Dimension(16, 0));
scroller.getVerticalScrollBar().setBorder(new EmptyBorder(0, 9, 0, 0));
scroller.setBackground(ColorScheme.DARK_GRAY_COLOR);
//Use a placeholder icon until the async image gets loaded
MaterialTab materialTab = new MaterialTab(new ImageIcon(), tabGroup, scroller);
materialTab.setPreferredSize(new Dimension(30, 27));
materialTab.setName(tab.getName());
AsyncBufferedImage icon = itemManager.getImage(tab.getItemID());
Runnable resize = () ->
/* This is a weird hack to remove the top border on the first tracker of every tab */
if (first)
{
BufferedImage subIcon = icon.getSubimage(0, 0, 32, 32);
materialTab.setIcon(new ImageIcon(subIcon.getScaledInstance(24, 24, Image.SCALE_SMOOTH)));
};
icon.onChanged(resize);
resize.run();
materialTab.setOnSelectEvent(() ->
{
config.setPatch(tab);
return true;
});
tabGroup.addTab(materialTab);
if (config.patch() == tab)
{
tabGroup.select(materialTab);
first = false;
p.setBorder(null);
}
});
}
}
void update()
@Override
public void update()
{
if (!active)
{
return;
}
long unixNow = Instant.now().getEpochSecond();
log.debug("Updating panel with username {}", client.getUsername());
boolean autoweed = false;
boolean autoweed;
{
String group = FarmingTrackerConfig.KEY_NAME + "." + client.getUsername();
String group = TimeTrackingConfig.KEY_NAME + "." + client.getUsername();
autoweed = Integer.toString(Autoweed.ON.ordinal())
.equals(configManager.getConfiguration(group, FarmingTrackerConfig.AUTOWEED));
.equals(configManager.getConfiguration(group, TimeTrackingConfig.AUTOWEED));
}
for (FarmingPatchPanel panel : patchPanels)
{
FarmingPatch patch = panel.getPatch();
String group = FarmingTrackerConfig.KEY_NAME + "." + client.getUsername() + "." + patch.getRegion().getRegionID();
String group = TimeTrackingConfig.KEY_NAME + "." + client.getUsername() + "." + patch.getRegion().getRegionID();
String key = Integer.toString(patch.getVarbit().getId());
String storedValue = configManager.getConfiguration(group, key);
long unixTime = 0;
@@ -258,19 +180,9 @@ class FarmingTrackerPanel extends PluginPanel
}
int stage = state.getStage();
int stages = state.getCropState() == CropState.HARVESTABLE ?
state.getProduce().getHarvestStages() :
state.getProduce().getStages();
int tickrate = 0;
switch (state.getCropState())
{
case HARVESTABLE:
tickrate = state.getProduce().getRegrowTickrate() * 60;
break;
case GROWING:
tickrate = state.getProduce().getTickrate() * 60;
break;
}
int stages = state.getStages();
int tickrate = state.getTickRate() * 60;
if (autoweed && state.getProduce() == Produce.WEEDS)
{
stage = 0;
@@ -374,17 +286,4 @@ class FarmingTrackerPanel extends PluginPanel
}
}
}
@Override
public void onActivate()
{
active = true;
update();
}
@Override
public void onDeactivate()
{
active = false;
}
}

View File

@@ -0,0 +1,169 @@
/*
* Copyright (c) 2018 Abex
* 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 com.google.inject.Inject;
import com.google.inject.Singleton;
import java.time.Instant;
import net.runelite.api.Client;
import net.runelite.api.Varbits;
import net.runelite.api.coords.WorldPoint;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.game.ItemManager;
import net.runelite.client.plugins.timetracking.Tab;
import net.runelite.client.plugins.timetracking.TimeTrackingConfig;
@Singleton
public class FarmingTracker
{
@Deprecated
private static final String OLD_KEY_NAME = "farmingTracker";
private final Client client;
private final ItemManager itemManager;
private final ConfigManager configManager;
private final TimeTrackingConfig config;
private final FarmingWorld farmingWorld;
@Inject
private FarmingTracker(Client client, ItemManager itemManager, ConfigManager configManager,
TimeTrackingConfig config, FarmingWorld farmingWorld)
{
this.client = client;
this.itemManager = itemManager;
this.configManager = configManager;
this.config = config;
this.farmingWorld = farmingWorld;
}
public FarmingTabPanel createTabPanel(Tab tab)
{
return new FarmingTabPanel(client, itemManager, configManager, config, farmingWorld.getTabs().get(tab));
}
/**
* Updates tracker data for the current region. Returns true if any data was changed.
*/
public boolean updateData(WorldPoint location)
{
boolean changed = false;
{
String group = TimeTrackingConfig.KEY_NAME + "." + client.getUsername();
String autoweed = Integer.toString(client.getVar(Varbits.AUTOWEED));
if (!autoweed.equals(configManager.getConfiguration(group, TimeTrackingConfig.AUTOWEED)))
{
configManager.setConfiguration(group, TimeTrackingConfig.AUTOWEED, autoweed);
changed = true;
}
}
FarmingRegion region = farmingWorld.getRegions().get(location.getRegionID());
if (region != null && region.isInBounds(location))
{
// Write config with new varbits
// timetracking.<login-username>.<regionID>.<VarbitID>=<varbitValue>:<unix time>
String group = TimeTrackingConfig.KEY_NAME + "." + client.getUsername() + "." + region.getRegionID();
long unixNow = Instant.now().getEpochSecond();
for (Varbits varbit : region.getVarbits())
{
// Write the config value if it doesn't match what is current, or it is more than 5 minutes old
String key = Integer.toString(varbit.getId());
String strVarbit = Integer.toString(client.getVar(varbit));
String storedValue = configManager.getConfiguration(group, key);
if (storedValue != null)
{
String[] parts = storedValue.split(":");
if (parts.length == 2 && parts[0].equals(strVarbit))
{
long unixTime = 0;
try
{
unixTime = Long.parseLong(parts[1]);
}
catch (NumberFormatException e)
{
// ignored
}
if (unixTime + (5 * 60) > unixNow && unixNow + 30 > unixTime)
{
continue;
}
}
}
String value = strVarbit + ":" + unixNow;
configManager.setConfiguration(group, key, value);
changed = true;
}
}
return changed;
}
/**
* Migrates configuration data from {@code "farmingTracker"} key to {@code "timetracking"} key.
* This method should be removed after a reasonable amount of time.
*/
@Deprecated
public void migrateConfiguration()
{
String username = client.getUsername();
// migrate autoweed config
{
String oldGroup = OLD_KEY_NAME + "." + username;
String newGroup = TimeTrackingConfig.KEY_NAME + "." + username;
String storedValue = configManager.getConfiguration(oldGroup, TimeTrackingConfig.AUTOWEED);
if (storedValue != null)
{
configManager.setConfiguration(newGroup, TimeTrackingConfig.AUTOWEED, storedValue);
configManager.unsetConfiguration(oldGroup, TimeTrackingConfig.AUTOWEED);
}
}
// migrate all saved data in all regions
for (FarmingRegion region : farmingWorld.getRegions().values())
{
String oldGroup = OLD_KEY_NAME + "." + username + "." + region.getRegionID();
String newGroup = TimeTrackingConfig.KEY_NAME + "." + username + "." + region.getRegionID();
for (Varbits varbit : region.getVarbits())
{
String key = Integer.toString(varbit.getId());
String storedValue = configManager.getConfiguration(oldGroup, key);
if (storedValue != null)
{
configManager.setConfiguration(newGroup, key, storedValue);
configManager.unsetConfiguration(oldGroup, key);
}
}
}
}
}

View File

@@ -23,7 +23,7 @@
* (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.farmingtracker;
package net.runelite.client.plugins.timetracking.farming;
import com.google.inject.Singleton;
import java.util.Collections;
@@ -36,9 +36,10 @@ import java.util.TreeSet;
import lombok.Getter;
import net.runelite.api.Varbits;
import net.runelite.api.coords.WorldPoint;
import net.runelite.client.plugins.timetracking.Tab;
@Singleton
public class FarmingWorld
class FarmingWorld
{
@Getter
private Map<Integer, FarmingRegion> regions = new HashMap<>();
@@ -51,7 +52,7 @@ public class FarmingWorld
.thenComparing((FarmingPatch p) -> p.getRegion().getName())
.thenComparing(FarmingPatch::getName);
public FarmingWorld()
FarmingWorld()
{
// Some of these patches get updated in multiple regions.
// It may be worth it to add a specialization for these patches

View File

@@ -22,10 +22,11 @@
* (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.farmingtracker;
package net.runelite.client.plugins.timetracking.farming;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import net.runelite.client.plugins.timetracking.Tab;
@RequiredArgsConstructor
@Getter
@@ -733,7 +734,7 @@ public enum PatchImplementation
return null;
}
},
FLOWER(Tab.HERB, "Flowers")
FLOWER(Tab.FLOWER, "")
{
@Override
PatchState forVarbitValue(int value)
@@ -1205,7 +1206,7 @@ public enum PatchImplementation
return null;
}
},
FRUIT_TREE(Tab.FRUIT_TREE, "Fruit trees")
FRUIT_TREE(Tab.FRUIT_TREE, "")
{
@Override
PatchState forVarbitValue(int value)
@@ -1454,7 +1455,7 @@ public enum PatchImplementation
return null;
}
},
HOPS(Tab.SPECIAL, "Hops")
HOPS(Tab.HOPS, "")
{
@Override
PatchState forVarbitValue(int value)
@@ -2267,7 +2268,7 @@ public enum PatchImplementation
return null;
}
},
GRAPES(Tab.BUSH, "Grapes")
GRAPES(Tab.GRAPE, "")
{
@Override
PatchState forVarbitValue(int value)

View File

@@ -22,14 +22,32 @@
* (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.farmingtracker;
package net.runelite.client.plugins.timetracking.farming;
import lombok.Value;
@Value
public class PatchState
class PatchState
{
private final Produce produce;
private final CropState cropState;
private final int stage;
int getStages()
{
return cropState == CropState.HARVESTABLE ? produce.getHarvestStages() : produce.getStages();
}
int getTickRate()
{
switch (cropState)
{
case HARVESTABLE:
return produce.getRegrowTickrate();
case GROWING:
return produce.getTickrate();
default:
return 0;
}
}
}

View File

@@ -23,7 +23,7 @@
* (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.farmingtracker;
package net.runelite.client.plugins.timetracking.farming;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@@ -127,7 +127,7 @@ public enum Produce
*/
private final int tickrate;
/**
* How many states this crop has during groth. Typically tickcount+1
* How many states this crop has during growth. Typically tickcount+1
*/
private final int stages;
/**

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -21,7 +21,7 @@
* 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.farmingtracker;
*/package net.runelite.client.plugins.timetracking.farming;
import org.junit.Test;

View File

@@ -22,7 +22,7 @@
* (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.farmingtracker;
package net.runelite.client.plugins.timetracking.farming;
import java.util.HashMap;
import java.util.Map;