diff --git a/.gitignore b/.gitignore index 4c7b9a18..aecbd17d 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,15 @@ group-ironmen-tracker-master/bin/ group-ironmen-tracker-master/spotless/ -tasks-tracker-plugin-master/* - - +tasks-tracker-plugin-master/.gradle +tasks-tracker-plugin-master/build +tasks-tracker-plugin-master/.idea/ +tasks-tracker-plugin-master/.project +tasks-tracker-plugin-master/.settings/ +tasks-tracker-plugin-master/.classpath +tasks-tracker-plugin-master/nbactions.xml +tasks-tracker-plugin-master/nb-configuration.xml +tasks-tracker-plugin-master/nbproject/ +tasks-tracker-plugin-master/.run +tasks-tracker-plugin-master/bin +tasks-tracker-plugin-master/**/FileDataStoreReader.java diff --git a/tasks-tracker-plugin-master/.gitignore b/tasks-tracker-plugin-master/.gitignore new file mode 100644 index 00000000..fa54513f --- /dev/null +++ b/tasks-tracker-plugin-master/.gitignore @@ -0,0 +1,12 @@ +.gradle +build +.idea/ +.project +.settings/ +.classpath +nbactions.xml +nb-configuration.xml +nbproject/ +.run +bin +**/FileDataStoreReader.java \ No newline at end of file diff --git a/tasks-tracker-plugin-master/LICENSE b/tasks-tracker-plugin-master/LICENSE new file mode 100644 index 00000000..b4d4a96c --- /dev/null +++ b/tasks-tracker-plugin-master/LICENSE @@ -0,0 +1,25 @@ +BSD 2-Clause License + +Copyright (c) 2021, Tyler Hardy +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 HOLDER 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. diff --git a/tasks-tracker-plugin-master/README.md b/tasks-tracker-plugin-master/README.md new file mode 100644 index 00000000..9e3f1570 --- /dev/null +++ b/tasks-tracker-plugin-master/README.md @@ -0,0 +1,18 @@ +# Tasks Tracker +![image](https://img.shields.io/endpoint?url=https://api.runelite.net/pluginhub/shields/installs/plugin/tasks-tracker) +![image](https://img.shields.io/endpoint?url=https://api.runelite.net/pluginhub/shields/rank/plugin/tasks-tracker) + + +Provides a RuneLite panel to track & filter tasks for Combat Achievements and Leagues. + +## Support +Visit [our Discord](https://discord.gg/eCeKwhEzyK) for live support + +## Features +* View tasks & progress in RuneLite panel without opening game UI +* Search tasks by text or filters +* Track tasks in a tracked list tab +* Export for external services + +![image](https://user-images.githubusercontent.com/17709869/146846182-de573b11-ee53-482e-b3d2-a103f86d6089.png) +![image](https://user-images.githubusercontent.com/17709869/152713557-4f153acf-ff68-4299-852a-8898b5edb34c.gif) diff --git a/tasks-tracker-plugin-master/build.gradle b/tasks-tracker-plugin-master/build.gradle new file mode 100644 index 00000000..b6a006d1 --- /dev/null +++ b/tasks-tracker-plugin-master/build.gradle @@ -0,0 +1,33 @@ +plugins { + id 'java' +} + +repositories { + mavenLocal() + maven { + url = 'https://repo.runelite.net' + } + mavenCentral() +} + +def runeLiteVersion = 'latest.release' + +dependencies { + compileOnly group: 'net.runelite', name:'client', version: runeLiteVersion + + compileOnly 'org.projectlombok:lombok:1.18.30' + annotationProcessor 'org.projectlombok:lombok:1.18.30' + + testImplementation 'junit:junit:4.12' + testImplementation group: 'net.runelite', name:'client', version: runeLiteVersion + testImplementation group: 'net.runelite', name:'jshell', version: runeLiteVersion + testImplementation group: 'com.squareup.okhttp3', name: 'mockwebserver', version: '3.14.9' +} + +group = 'net.reldo' +version = '1.0-SNAPSHOT' + +tasks.withType(JavaCompile) { + options.encoding = 'UTF-8' + options.release.set(11) +} diff --git a/tasks-tracker-plugin-master/gradle/wrapper/gradle-wrapper.jar b/tasks-tracker-plugin-master/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..e708b1c0 Binary files /dev/null and b/tasks-tracker-plugin-master/gradle/wrapper/gradle-wrapper.jar differ diff --git a/tasks-tracker-plugin-master/gradle/wrapper/gradle-wrapper.properties b/tasks-tracker-plugin-master/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..12d38de6 --- /dev/null +++ b/tasks-tracker-plugin-master/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/tasks-tracker-plugin-master/gradlew b/tasks-tracker-plugin-master/gradlew new file mode 100644 index 00000000..f5feea6d --- /dev/null +++ b/tasks-tracker-plugin-master/gradlew @@ -0,0 +1,252 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/tasks-tracker-plugin-master/gradlew.bat b/tasks-tracker-plugin-master/gradlew.bat new file mode 100644 index 00000000..9d21a218 --- /dev/null +++ b/tasks-tracker-plugin-master/gradlew.bat @@ -0,0 +1,94 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/tasks-tracker-plugin-master/icon.png b/tasks-tracker-plugin-master/icon.png new file mode 100644 index 00000000..7dc707fe Binary files /dev/null and b/tasks-tracker-plugin-master/icon.png differ diff --git a/tasks-tracker-plugin-master/runelite-plugin.properties b/tasks-tracker-plugin-master/runelite-plugin.properties new file mode 100644 index 00000000..532e58a2 --- /dev/null +++ b/tasks-tracker-plugin-master/runelite-plugin.properties @@ -0,0 +1,6 @@ +displayName=Tasks Tracker +author=Reldo.net +support=https://github.com/osrs-reldo/tasks-tracker-plugin +description=Provides a panel to track league/combat tasks & export for external services +tags=combat,league,leagues,achievements,tasks,league3,leagues3,relic,fragment,unlock,unlocks +plugins=net.reldo.taskstracker.TasksTrackerPlugin \ No newline at end of file diff --git a/tasks-tracker-plugin-master/settings.gradle b/tasks-tracker-plugin-master/settings.gradle new file mode 100644 index 00000000..fd8cdcfa --- /dev/null +++ b/tasks-tracker-plugin-master/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'tasks-tracker' diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/HtmlUtil.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/HtmlUtil.java new file mode 100644 index 00000000..75efb406 --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/HtmlUtil.java @@ -0,0 +1,41 @@ +package net.reldo.taskstracker; + +import java.awt.Color; +import java.net.URL; + +public class HtmlUtil +{ + public static String HTML_LINE_BREAK = "
"; + + public static String wrapWithHtml(String text) + { + return "" + text + ""; + } + + public static String wrapWithWrappingParagraph(String text, int width) + { + return "

" + text + "

"; + } + + public static String wrapWithBold(String text) + { + return "" + text + ""; + } + + public static String imageTag(URL url) + { + return ""; + } + + public static String colorTag(String color, String text) + { + return "" + text + ""; + } + + public static String colorTag(Color color, String text) + { + String buf = Integer.toHexString(color.getRGB()); + String hex = "#" + buf.substring(buf.length() - 6); + return colorTag(hex, text); + } +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/TasksTrackerConfig.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/TasksTrackerConfig.java new file mode 100644 index 00000000..fbaa4a2c --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/TasksTrackerConfig.java @@ -0,0 +1,140 @@ +package net.reldo.taskstracker; + +import net.reldo.taskstracker.config.ConfigValues; +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup(TasksTrackerPlugin.CONFIG_GROUP_NAME) +public interface TasksTrackerConfig extends Config +{ + @ConfigItem( + position = 10, + keyName = "untrackUponCompletion", + name = "Untrack Tasks Upon Completion", + description = "Configures whether completed tasks should also automatically untracked when the task is completed." + ) + default boolean untrackUponCompletion() + { + return false; + } + + @ConfigItem( + position = 11, + keyName = "filterPanelCollapsible", + name = "Filter Panels Collapsible", + description = "Shows button that allows filter panels to be hidden." + ) + default boolean filterPanelCollapsible() + { + return true; + } + + @ConfigItem( + position = 12, + keyName = "saveSubFilterState", //@todo generalise this to all sub-filters + name = "Save Filter State", + description = "Configures whether the state of area filters should be saved and recalled when switching task type or restarting the plugin.", + hidden = true + ) + default boolean saveSubFilterState() + { + return true; + } + + @ConfigItem( + position = 100, + keyName = "completedFilter", + name = "Completed Tasks Filter", + description = "Configures whether completed tasks should be displayed.", + hidden = true + ) + default ConfigValues.CompletedFilterValues completedFilter() + { + return ConfigValues.CompletedFilterValues.COMPLETE_AND_INCOMPLETE; + } + + @ConfigItem( + position = 101, + keyName = "trackedFilter", + name = "Tracked Tasks Filter", + description = "Configures whether tracked tasks should be displayed.", + hidden = true + ) + default ConfigValues.TrackedFilterValues trackedFilter() + { + return ConfigValues.TrackedFilterValues.TRACKED_AND_UNTRACKED; + } + + @ConfigItem( + position = 102, + keyName = "ignoredFilter", + name = "Ignored Tasks Filter", + description = "Configures whether ignored tasks should be displayed.", + hidden = true + ) + default ConfigValues.IgnoredFilterValues ignoredFilter() + { + return ConfigValues.IgnoredFilterValues.NOT_IGNORED; + } + + @ConfigItem( + position = 103, + keyName = "taskListTab", + name = "Selected Task List Tab", + description = "Configures the currently selected tab on the task list.", + hidden = true + ) + default ConfigValues.TaskListTabs taskListTab() + { + return ConfigValues.TaskListTabs.ALL; + } + + @ConfigItem( + position = 106, + keyName = "taskTypeJsonName", + name = "Task Type", + description = "Configures the task type which is displayed in the panel.", + hidden = true + ) + default String taskTypeJsonName() + { + return "COMBAT"; + } + + @ConfigItem( + position = 109, + keyName = "dropdownFilter", + name = "Dropdown Filter", + description = "Configures the dropdown to filter tasks on.", + hidden = true + ) + default String dropdownFilter() + { + return ""; + } + + @ConfigItem( + position = 110, + keyName = "sortCriteria", + name = "Sort Criteria", + description = "Configures the criteria to sort tasks on.", + hidden = true + ) + default String sortCriteria() + { + return "Default"; + } + + @ConfigItem( + position = 111, + keyName = "sortDirection", + name = "Sort Direction", + description = "Configures the direction to sort tasks.", + hidden = true + ) + default ConfigValues.SortDirections sortDirection() + { + return ConfigValues.SortDirections.ASCENDING; + } +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/TasksTrackerPlugin.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/TasksTrackerPlugin.java new file mode 100644 index 00000000..94ca9f31 --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/TasksTrackerPlugin.java @@ -0,0 +1,584 @@ +package net.reldo.taskstracker; + +import com.google.gson.Gson; +import com.google.inject.Binder; +import com.google.inject.Provides; +import java.awt.Color; +import java.awt.Toolkit; +import java.awt.datatransfer.StringSelection; +import java.awt.image.BufferedImage; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import javax.annotation.Nullable; +import javax.inject.Inject; +import javax.inject.Named; +import javax.swing.JDialog; +import javax.swing.JOptionPane; +import javax.swing.SwingUtilities; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import net.reldo.taskstracker.data.Export; +import net.reldo.taskstracker.data.LongSerializer; +import net.reldo.taskstracker.data.TasksSummary; +import net.reldo.taskstracker.data.TrackerConfigStore; +import net.reldo.taskstracker.data.jsondatastore.reader.DataStoreReader; +import net.reldo.taskstracker.data.jsondatastore.reader.HttpDataStoreReader; +import net.reldo.taskstracker.data.reldo.ReldoImport; +import net.reldo.taskstracker.data.task.TaskFromStruct; +import net.reldo.taskstracker.data.task.TaskService; +import net.reldo.taskstracker.data.task.TaskType; +import net.reldo.taskstracker.data.task.filters.FilterService; +import net.reldo.taskstracker.panel.TasksTrackerPluginPanel; +import net.runelite.api.ChatMessageType; +import net.runelite.api.Client; +import net.runelite.api.Experience; +import net.runelite.api.GameState; +import net.runelite.api.Skill; +import net.runelite.api.events.CommandExecuted; +import net.runelite.api.events.GameStateChanged; +import net.runelite.api.events.GameTick; +import net.runelite.api.events.StatChanged; +import net.runelite.api.events.VarbitChanged; +import net.runelite.client.callback.ClientThread; +import net.runelite.client.chat.ChatMessageBuilder; +import net.runelite.client.chat.ChatMessageManager; +import net.runelite.client.chat.QueuedMessage; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.config.RuneScapeProfileType; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.events.ConfigChanged; +import net.runelite.client.events.ProfileChanged; +import net.runelite.client.game.SpriteManager; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.PluginManager; +import net.runelite.client.ui.ClientToolbar; +import net.runelite.client.ui.NavigationButton; +import net.runelite.client.util.ImageUtil; +import net.runelite.client.util.LinkBrowser; + +@Slf4j +@PluginDescriptor( + name = "Tasks Tracker" +) +public class TasksTrackerPlugin extends Plugin +{ + public static final String CONFIG_GROUP_NAME = "tasks-tracker"; + + public int[] playerSkills; + + public String taskTextFilter; + + public TasksTrackerPluginPanel pluginPanel; + + private static final long VARP_UPDATE_THROTTLE_DELAY_MS = 7 * 1000; + + private boolean forceUpdateVarpsFlag = false; + private Set varpIdsToUpdate = new HashSet<>(); + private long lastVarpUpdate = 0; + private NavigationButton navButton; + private RuneScapeProfileType currentProfileType; + private final Map oldExperience = new EnumMap<>(Skill.class); + + @Inject @Named("runelite.version") private String runeliteVersion; + @Inject private Gson gson; + @Inject private Client client; + @Inject private SpriteManager spriteManager; + @Inject private PluginManager pluginManager; + @Inject private ClientToolbar clientToolbar; + @Inject private ClientThread clientThread; + @Inject private ChatMessageManager chatMessageManager; + @Getter @Inject private ConfigManager configManager; + @Getter @Inject private TasksTrackerConfig config; + + @Inject private TrackerConfigStore trackerConfigStore; + @Inject private TaskService taskService; + @Inject private FilterService filterService; + + @Override + public void configure(Binder binder) + { + binder.bind(DataStoreReader.class).to(HttpDataStoreReader.class); + super.configure(binder); + } + + @Provides + TasksTrackerConfig getConfig(ConfigManager configManager) + { + return configManager.getConfig(TasksTrackerConfig.class); + } + + @Override + protected void startUp() + { + try + { + String taskTypeJsonName = config.taskTypeJsonName(); + taskService.setTaskType(taskTypeJsonName); + } + catch (Exception ex) + { + log.error("error setting task type in startUp", ex); + } + + forceUpdateVarpsFlag = false; + + pluginPanel = new TasksTrackerPluginPanel(this, config, spriteManager, taskService); + + boolean isLoggedIn = isLoggedInState(client.getGameState()); + pluginPanel.setLoggedIn(isLoggedIn); + if (isLoggedIn) + { + forceUpdateVarpsFlag = true; + } + + final BufferedImage icon = ImageUtil.loadImageResource(getClass(), "panel_icon.png"); + navButton = NavigationButton.builder() + .tooltip("Task Tracker") + .icon(icon) + .priority(5) + .panel(pluginPanel) + .build(); + clientToolbar.addNavigation(navButton); + + log.info("Tasks Tracker started!"); + } + + @Override + protected void shutDown() + { + pluginPanel.hideLoggedInPanel(); + pluginPanel = null; + taskService.clearTaskTypes(); + clientToolbar.removeNavigation(navButton); + log.info("Tasks Tracker stopped!"); + } + + @Subscribe + public void onCommandExecuted(CommandExecuted commandExecuted) + { + if (!commandExecuted.getCommand().startsWith("tt")) return; + + if (commandExecuted.getCommand().equalsIgnoreCase("tt-process-varp")) + { + String[] args = commandExecuted.getArguments(); + if (args.length == 0) return; + + try + { + int varpId = Integer.parseInt(args[0]); + log.debug("Processing varpId " + varpId); + processVarpAndUpdateTasks(varpId); + } + catch (NumberFormatException e) + { + log.debug("Invalid varpId, provide a valid integer"); + } + } + } + + @Subscribe + public void onVarbitChanged(VarbitChanged varbitChanged) + { + if (forceUpdateVarpsFlag || taskService.isTaskTypeChanged()) + { + // Force update is coming on next game tick, so ignore varbit change events + return; + } + int varpId = varbitChanged.getVarpId(); + if (!taskService.isVarpInCurrentTaskType(varpId)) + { + return; + } + varpIdsToUpdate.add(varbitChanged.getVarpId()); + } + + @Subscribe + public void onConfigChanged(ConfigChanged configChanged) + { + if (!configChanged.getGroup().equals(CONFIG_GROUP_NAME)) + { + return; + } + log.debug("onConfigChanged {} {}", configChanged.getKey(), configChanged.getNewValue()); + if (configChanged.getKey().equals("untrackUponCompletion") && config.untrackUponCompletion()) + { + forceVarpUpdate(); + } + + if (configChanged.getKey().equals("filterPanelCollapsible")) + { + SwingUtilities.invokeLater(pluginPanel::redraw); + } + } + + @Subscribe + public void onGameStateChanged(GameStateChanged gameStateChanged) + { + log.debug("onGameStateChanged {}", gameStateChanged.getGameState().toString()); + GameState newGameState = gameStateChanged.getGameState(); + RuneScapeProfileType newProfileType = RuneScapeProfileType.getCurrent(client); + + SwingUtilities.invokeLater(() -> pluginPanel.setLoggedIn(isLoggedInState(newGameState))); + + // Logged in + if (newGameState == GameState.LOGGING_IN) + { + forceUpdateVarpsFlag = true; + } + // Changed game mode + if (isLoggedInState(newGameState) && currentProfileType != null && currentProfileType != newProfileType) + { + forceUpdateVarpsFlag = true; + } + + currentProfileType = newProfileType; + } + + private boolean isLoggedInState(GameState gameState) + { + return gameState == GameState.LOGGED_IN || gameState == GameState.HOPPING || gameState == GameState.LOADING; + } + + @Subscribe + public void onGameTick(GameTick gameTick) + { + if (forceUpdateVarpsFlag || taskService.isTaskTypeChanged()) + { + log.debug("forceUpdateVarpsFlag game tick {} {}", forceUpdateVarpsFlag, taskService.isTaskTypeChanged()); + trackerConfigStore.loadCurrentTaskTypeFromConfig(); + forceVarpUpdate(); + SwingUtilities.invokeLater(() -> pluginPanel.redraw()); + forceUpdateVarpsFlag = false; + taskService.setTaskTypeChanged(false); + } + + // Flush throttled varp updates + long currentTimeEpoch = System.currentTimeMillis(); + if (currentTimeEpoch - lastVarpUpdate > VARP_UPDATE_THROTTLE_DELAY_MS) + { + flushVarpUpdates(varpIdsToUpdate); + varpIdsToUpdate = new HashSet<>(); + lastVarpUpdate = currentTimeEpoch; + } + } + + @Subscribe + public void onStatChanged(StatChanged statChanged) + { + // @todo deprecate one of these, we don't need to track player skills twice. + // Cache current player skills + int[] newSkills = client.getRealSkillLevels(); + boolean changed = !Arrays.equals(playerSkills, newSkills); + if (changed) + { + playerSkills = client.getRealSkillLevels(); + } + + final Skill skill = statChanged.getSkill(); + + // Modified from m0bilebtw's modification from Nightfirecat's virtual level ups plugin + final int xpAfter = client.getSkillExperience(skill); + final int levelAfter = Experience.getLevelForXp(xpAfter); + final int xpBefore = oldExperience.getOrDefault(skill, -1); + final int levelBefore = xpBefore == -1 ? -1 : Experience.getLevelForXp(xpBefore); + + oldExperience.put(skill, xpAfter); + + // Do not proceed if any of the following are true: + // * xpBefore == -1 (don't fire when first setting new known value) + // * xpAfter <= xpBefore (do not allow 200m -> 200m exp drops) + // * levelBefore >= levelAfter (stop if we're not actually reaching a new level) + // * levelAfter > MAX_REAL_LEVEL (stop if above 99) + if (xpBefore == -1 || xpAfter <= xpBefore || levelBefore >= levelAfter || levelAfter > Experience.MAX_REAL_LEVEL) + { + return; + } + + // If we get here, 'skill' was leveled up! + SwingUtilities.invokeLater(() -> pluginPanel.taskListPanel.refreshTaskPanelsWithSkill(skill)); + } + + @Subscribe + public void onProfileChanged(ProfileChanged profileChanged) + { + final Optional taskTrackerPlugin = pluginManager.getPlugins().stream().filter(p -> p.getName().equals("Tasks Tracker")).findFirst(); + if (taskTrackerPlugin.isPresent() && pluginManager.isPluginEnabled(taskTrackerPlugin.get())) + { + reloadTaskType(); + } + } + + public void refreshAllTasks() + { + SwingUtilities.invokeLater(() -> pluginPanel.refreshAllTasks()); + } + + public void reloadTaskType() { + taskService.clearTaskTypes(); + filterService.clearFilterConfigs(); + try { + String taskTypeJsonName = config.taskTypeJsonName(); + taskService.setTaskType(taskTypeJsonName).thenAccept(isSet -> { + if (!isSet) { + return; + } + SwingUtilities.invokeLater(() -> + { + pluginPanel.redraw(); + pluginPanel.refreshAllTasks(); + }); + }); + } catch (Exception ex) { + log.error("error setting task type in reload", ex); + } + + } + + public void saveCurrentTaskTypeData() + { + log.debug("saveCurrentTaskTypeData"); + trackerConfigStore.saveCurrentTaskTypeData(); + } + + public void openImportJsonDialog() + { + JOptionPane optionPane = new JOptionPane("Paste import data into the text field below to import task tracker data.", JOptionPane.INFORMATION_MESSAGE); + optionPane.setWantsInput(true); + JDialog inputDialog = optionPane.createDialog(this.pluginPanel, "Import Tasks Input"); + inputDialog.setAlwaysOnTop(true); + inputDialog.setVisible(true); + + if (optionPane.getInputValue().equals("") || optionPane.getInputValue().equals("uninitializedValue")) + { + this.showMessageBox("Import Tasks Error", "Input was empty so no data has been imported.", JOptionPane.ERROR_MESSAGE, false); + return; + } + + String json = ""; + ReldoImport reldoImport; + try + { + json = (String) optionPane.getInputValue(); + reldoImport = this.gson.fromJson(json, ReldoImport.class); + } + catch (Exception ex) + { + this.showMessageBox("Import Tasks Error", "There was an issue importing task tracker data. " + ex.getMessage(), JOptionPane.ERROR_MESSAGE, false); + log.error("There was an issue importing task tracker data.", ex); + log.debug("reldoImport json: {}", json); + return; + } + + if (!reldoImport.taskTypeName.equalsIgnoreCase(config.taskTypeJsonName())) + { + this.showMessageBox("Import Tasks Error", String.format("Wrong task type. Select the %s task type to import this data.", reldoImport.taskTypeName), JOptionPane.ERROR_MESSAGE, false); + return; + } + + optionPane = new JOptionPane("Importing tasks will overwrite task tracker settings and cannot be undone. Are you sure you want to import tasks?", JOptionPane.WARNING_MESSAGE, JOptionPane.YES_NO_OPTION); + JDialog confirmDialog = optionPane.createDialog(this.pluginPanel, "Import Tasks Overwrite Confirmation"); + confirmDialog.setAlwaysOnTop(true); + confirmDialog.setVisible(true); + + Object selectedValue = optionPane.getValue(); + if (selectedValue == null) + { + return; + } + + if (selectedValue.equals(JOptionPane.YES_OPTION)) + { + HashMap tasksById = new HashMap<>(); + taskService.getTasks().forEach((task) -> tasksById.put(task.getIntParam("id"), task)); + + reldoImport.getTasks().forEach((id, reldoTaskSave) -> { + TaskFromStruct task = tasksById.get(id); + task.loadReldoSave(reldoTaskSave); + }); + + trackerConfigStore.saveCurrentTaskTypeData(); + pluginPanel.redraw(); + } + } + + public void sendTotalsToChat() + { + TasksSummary summary = new TasksSummary(taskService.getTasks()); + int trackedTasks = summary.trackedTasksCount; + int trackedPoints = summary.trackedTasksPoints; + + final String message = new ChatMessageBuilder() + .append(Color.BLACK, String.format("Task Tracker - Tracked Tasks: %s | Tracked Points: %s", trackedTasks, trackedPoints)) + .build(); + + chatMessageManager.queue( + QueuedMessage.builder() + .type(ChatMessageType.CONSOLE) + .runeLiteFormattedMessage(message) + .build()); + } + + public void copyJsonToClipboard() + { + clientThread.invokeLater(() -> { + // Not worried with this complexity on the client thread because it's from an infrequent button press + String json = getCurrentTaskTypeExportJson(); + final StringSelection stringSelection = new StringSelection(json); + Toolkit.getDefaultToolkit().getSystemClipboard().setContents(stringSelection, null); + + String message = "Copied " + taskService.getCurrentTaskType().getName() + " data to clipboard!"; + showMessageBox("Data Exported!", message, JOptionPane.INFORMATION_MESSAGE, true); + }); + } + + private void forceVarpUpdate() + { + log.debug("forceVarpUpdate"); + processVarpAndUpdateTasks(null).thenAccept((processed) -> { + if (processed) + { + log.debug("forceVarpUpdate processed complete, saving"); + saveCurrentTaskTypeData(); + } + }); + } + + private void flushVarpUpdates(Set varpIds) + { + log.debug("Flushing throttled varp updates {}", varpIds); + varpIds.forEach((id) -> processVarpAndUpdateTasks(id).thenAccept(processed -> { + if (processed) + { + log.debug("flushVarpUpdates processed complete, saving"); + saveCurrentTaskTypeData(); + } + })); + } + + private CompletableFuture processTaskStatus(TaskFromStruct task) + { + CompletableFuture future = new CompletableFuture<>(); + clientThread.invoke(() -> { + int taskId = task.getIntParam("id"); + int varbitIndex = taskId / 32; + int bitIndex = taskId % 32; + try + { + int varpId = task.getTaskType().getTaskVarps().get(varbitIndex); + BigInteger varpValue = BigInteger.valueOf(client.getVarpValue(varpId)); + boolean isTaskCompleted = varpValue.testBit(bitIndex); + task.setCompleted(isTaskCompleted); + if (isTaskCompleted && config.untrackUponCompletion()) + { + task.setTracked(false); + } + log.debug("process taskFromStruct {} ({}) {}", task.getStringParam("name"), task.getIntParam("id"), isTaskCompleted); + future.complete(isTaskCompleted); + } + catch (Exception ex) + { + log.error("Error processing task status {}", taskId, ex); + future.completeExceptionally(ex); + } + }); + return future; + } + + /** + * Update task completion status. If no varpId is specified, it updates all tasks in the current task type + * @param varpId varp id to update (optional) + * @return An observable that emits true if all tasks were processed + */ + private CompletableFuture processVarpAndUpdateTasks(@Nullable Integer varpId) + { + log.info("processVarpAndUpdateTasks: " + (varpId != null ? varpId : "all")); + + List tasks = varpId != null ? + taskService.getTasksFromVarpId(varpId) : + taskService.getTasks(); + + List> taskFutures = new ArrayList<>(); + for (TaskFromStruct task : tasks) + { + CompletableFuture taskFuture = processTaskStatus(task); + taskFutures.add(taskFuture); + } + + CompletableFuture allTasksFuture = CompletableFuture.allOf(taskFutures.toArray(new CompletableFuture[0])); + return allTasksFuture + .thenRun(() -> { + if (varpId != null) + { + SwingUtilities.invokeLater(() -> pluginPanel.taskListPanel.refreshMultipleTasks(tasks)); + } else { + SwingUtilities.invokeLater(() -> pluginPanel.refreshAllTasks()); + } + }) + .thenApply(v -> true); + } + + private String getCurrentTaskTypeExportJson() + { + TaskType taskType = taskService.getCurrentTaskType(); + Gson gson = this.gson.newBuilder() + .excludeFieldsWithoutExposeAnnotation() + .registerTypeAdapter(float.class, new LongSerializer()) + .create(); + + if (taskType == null) + { + String error = "Cannot export to JSON; no task type selected."; + log.error(error); + return error; + } + else + { + Export export = new Export(taskType, taskService.getTasks(), runeliteVersion, client); + return gson.toJson(export); + } + } + + private void showMessageBox(final String title, final String message, int messageType, boolean showOpenLeagueTools) + { + SwingUtilities.invokeLater(() -> { + JOptionPane optionPane; + JDialog dialog; + + if (showOpenLeagueTools) + { + String[] options = {"Open OS League Tools", "Ok"}; + + optionPane = new JOptionPane(message, messageType, JOptionPane.YES_NO_OPTION, null, options, options[1]); + } + else + { + optionPane = new JOptionPane(message, messageType); + } + + dialog = optionPane.createDialog(pluginPanel, title); + dialog.setAlwaysOnTop(true); + dialog.setVisible(true); + + Object selectedValue = optionPane.getValue(); + if (selectedValue == null) + { + return; + } + + if (selectedValue.equals("Open OS League Tools")) + { + LinkBrowser.browse("https://www.osleague.tools/tracker?open=import&tab=tasks"); + } + }); + } +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/config/ConfigValues.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/config/ConfigValues.java new file mode 100644 index 00000000..21c7ac2f --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/config/ConfigValues.java @@ -0,0 +1,40 @@ +package net.reldo.taskstracker.config; + + +public class ConfigValues { + + public enum CompletedFilterValues + { + COMPLETE_AND_INCOMPLETE, + COMPLETE, + INCOMPLETE; + } + + public enum TrackedFilterValues + { + TRACKED_AND_UNTRACKED, + TRACKED, + UNTRACKED; + } + + public enum IgnoredFilterValues + { + NOT_IGNORED, + IGNORED_AND_NOT_IGNORED, + IGNORED; + } + + public enum TaskListTabs + { + TRACKED, + ALL, + CUSTOM; + } + + public enum SortDirections + { + ASCENDING, + DESCENDING; + } + +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/Export.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/Export.java new file mode 100644 index 00000000..1dcc0c5a --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/Export.java @@ -0,0 +1,94 @@ +package net.reldo.taskstracker.data; + +import com.google.gson.annotations.Expose; +import java.time.Instant; +import java.util.HashMap; +import java.util.List; +import lombok.Getter; +import net.reldo.taskstracker.data.task.ConfigTaskSave; +import net.reldo.taskstracker.data.task.TaskFromStruct; +import net.reldo.taskstracker.data.task.TaskType; +import net.reldo.taskstracker.quests.DiaryData; +import net.reldo.taskstracker.quests.QuestData; +import net.runelite.api.Actor; +import net.runelite.api.Client; + +@Getter +public class Export +{ + private final Client client; + + @Expose private final QuestData quests; + @Expose private final DiaryData diaries; + @Expose private String displayName; + @Expose private final int runescapeVersion; + @Expose private final String runeliteVersion; + @Expose private final long timestamp; + @Expose private final String taskType; + @Expose private final HashMap varbits; + @Expose private final HashMap varps; + @Expose private final HashMap tasks; + + public Export(TaskType taskType, List tasks, String runeliteVersion, Client client) + { + this.client = client; + Actor localPlayer = client.getLocalPlayer(); + if (localPlayer != null) + { + this.displayName = localPlayer.getName(); + } + quests = new QuestData(client); + diaries = new DiaryData(client); + runescapeVersion = client.getRevision(); + this.runeliteVersion = runeliteVersion; + timestamp = Instant.now().toEpochMilli(); + this.taskType = taskType.getTaskJsonName(); + varbits = getVarbits(taskType); + varps = getVarps(taskType); + this.tasks = getTaskSavesById(tasks); + } + + private HashMap getVarbits(TaskType taskType) + { + assert client.isClientThread(); + + HashMap varbitValueMap = new HashMap<>(); + for (int varbitId : taskType.getVarbits()) + { + varbitValueMap.put(varbitId, client.getVarbitValue(varbitId)); + } + + return varbitValueMap; + } + + public HashMap getVarps(TaskType taskType) + { + assert client.isClientThread(); + + HashMap varpValueMap = new HashMap<>(); + for (int varpId : taskType.getTaskVarps()) + { + varpValueMap.put(varpId, client.getVarpValue(varpId)); + } + for (int varpId : taskType.getOtherVarps()) + { + varpValueMap.put(varpId, client.getVarpValue(varpId)); + } + + return varpValueMap; + } + + public HashMap getTaskSavesById(List tasks) + { + HashMap taskSavesById = new HashMap<>(); + for (TaskFromStruct task : tasks) + { + if (task.getCompletedOn() == 0 && task.getIgnoredOn() == 0 && task.getTrackedOn() == 0) + { + continue; + } + taskSavesById.put(String.valueOf(task.getIntParam("id")), task.getSaveData()); + } + return taskSavesById; + } +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/LongSerializer.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/LongSerializer.java new file mode 100644 index 00000000..9f7b27ca --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/LongSerializer.java @@ -0,0 +1,17 @@ +package net.reldo.taskstracker.data; + +import com.google.gson.JsonElement; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import java.lang.reflect.Type; +import java.text.DecimalFormat; + +public class LongSerializer implements JsonSerializer +{ + @Override + public JsonElement serialize(Long value, Type type, JsonSerializationContext jsonSerializationContext) + { + return new JsonPrimitive(new DecimalFormat("#").format(value)); + } +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/TasksSummary.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/TasksSummary.java new file mode 100644 index 00000000..2c201ac7 --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/TasksSummary.java @@ -0,0 +1,24 @@ +package net.reldo.taskstracker.data; + +import java.util.Collection; +import lombok.extern.slf4j.Slf4j; +import net.reldo.taskstracker.data.task.TaskFromStruct; + +@Slf4j +public class TasksSummary +{ + public int trackedTasksCount = 0; + public int trackedTasksPoints = 0; + + public TasksSummary(Collection tasks) + { + tasks.forEach(task -> { + if (task.isTracked()) { + trackedTasksCount++; + int points = task.getPoints(); + log.debug("TasksSummary {} {}", task.getName(), points); + trackedTasksPoints += points; + } + }); + } +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/TrackerConfigStore.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/TrackerConfigStore.java new file mode 100644 index 00000000..9a267e81 --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/TrackerConfigStore.java @@ -0,0 +1,94 @@ +package net.reldo.taskstracker.data; + +import com.google.gson.Gson; +import com.google.gson.JsonParseException; +import com.google.gson.reflect.TypeToken; +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; +import javax.inject.Inject; +import javax.inject.Singleton; +import lombok.extern.slf4j.Slf4j; +import net.reldo.taskstracker.TasksTrackerPlugin; +import net.reldo.taskstracker.data.task.TaskFromStruct; +import net.reldo.taskstracker.data.task.ConfigTaskSave; +import net.reldo.taskstracker.data.task.TaskService; +import net.reldo.taskstracker.data.task.TaskType; +import net.runelite.client.config.ConfigManager; + +@Singleton +@Slf4j +public class TrackerConfigStore +{ + public static final String CONFIG_TASKS_PREFIX = "tasks"; + public static final String CONFIG_GROUP_PREFIX_SEPARATOR = "-"; + public static final String CONFIG_GROUP_NAME = TasksTrackerPlugin.CONFIG_GROUP_NAME; + + private final Gson customGson; + @Inject + private TaskService taskService; + @Inject + private ConfigManager configManager; + + @Inject + public TrackerConfigStore(Gson gson) + { + this.customGson = gson.newBuilder() + .excludeFieldsWithoutExposeAnnotation() + .registerTypeAdapter(float.class, new LongSerializer()) + .create(); + } + + public void loadCurrentTaskTypeFromConfig() + { + TaskType currentTaskType = taskService.getCurrentTaskType(); + if (currentTaskType == null) + { + log.debug("loadTaskTypeFromConfig type is null, skipping"); + return; + } + log.debug("loadTaskTypeFromConfig {}", currentTaskType.getName()); + String configKey = getCurrentTaskTypeConfigKey(); + String configJson = configManager.getRSProfileConfiguration(CONFIG_GROUP_NAME, configKey); + if (configJson == null) + { + log.debug("No save information for task type {}, not applying save", currentTaskType.getName()); + return; + } + + Type deserializeType = TypeToken.getParameterized(HashMap.class, Integer.class, ConfigTaskSave.class).getType(); + try + { + HashMap saveData = customGson.fromJson(configJson, deserializeType); + taskService.applySave(currentTaskType, saveData); + } + catch (JsonParseException ex) + { + log.error("{} {} json invalid. wiping saved data", CONFIG_GROUP_NAME, configKey, ex); + configManager.unsetRSProfileConfiguration(CONFIG_GROUP_NAME, configKey); + } + } + + public void saveCurrentTaskTypeData() + { + log.debug("saveTaskTypeToConfig"); + Map saveDataByStructId = taskService.getTasks().stream() + .filter(task -> task.getCompletedOn() != 0 || task.getIgnoredOn() != 0 || task.getTrackedOn() != 0) + .collect(Collectors.toMap( + TaskFromStruct::getStructId, + TaskFromStruct::getSaveData, + (existing, replacement) -> existing, + HashMap::new + )); + + String configValue = this.customGson.toJson(saveDataByStructId); + String configKey = CONFIG_TASKS_PREFIX + CONFIG_GROUP_PREFIX_SEPARATOR + taskService.getCurrentTaskType().getTaskJsonName(); + configManager.setRSProfileConfiguration(CONFIG_GROUP_NAME, configKey, configValue); + } + + private String getCurrentTaskTypeConfigKey() + { + return CONFIG_TASKS_PREFIX + CONFIG_GROUP_PREFIX_SEPARATOR + taskService.getCurrentTaskType().getTaskJsonName(); + } +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/jsondatastore/FilterDataClient.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/jsondatastore/FilterDataClient.java new file mode 100644 index 00000000..633818af --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/jsondatastore/FilterDataClient.java @@ -0,0 +1,50 @@ +package net.reldo.taskstracker.data.jsondatastore; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import javax.inject.Inject; +import javax.inject.Singleton; +import lombok.extern.slf4j.Slf4j; +import net.reldo.taskstracker.data.jsondatastore.reader.DataStoreReader; +import net.reldo.taskstracker.data.jsondatastore.types.FilterConfig; +import okhttp3.OkHttpClient; + +@Singleton +@Slf4j +public class FilterDataClient +{ + @Inject private ManifestClient manifestClient; + @Inject private OkHttpClient okHttpClient; + @Inject private Gson gson; + @Inject private DataStoreReader dataStoreReader; + + public FilterDataClient() + { + log.debug("init filter data client"); + } + + public HashMap getFilterConfigs() throws Exception + { + log.debug("get filter configs"); + try(InputStream stream = this.dataStoreReader.readFilterConfigs(this.manifestClient.getManifest().filterMetadata); + InputStreamReader responseReader = new InputStreamReader(stream, StandardCharsets.UTF_8)) + { + Type listType = TypeToken.getParameterized(ArrayList.class, FilterConfig.class).getType(); + + List filterConfigs = this.gson.fromJson(responseReader, listType); + HashMap filterConfigsByConfigKey = new HashMap<>(); + for (FilterConfig filterConfig : filterConfigs) + { + filterConfigsByConfigKey.put(filterConfig.getConfigKey(), filterConfig); + } + return filterConfigsByConfigKey; + } + } +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/jsondatastore/JsonDataStore.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/jsondatastore/JsonDataStore.java new file mode 100644 index 00000000..3c6ec83f --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/jsondatastore/JsonDataStore.java @@ -0,0 +1,6 @@ +package net.reldo.taskstracker.data.jsondatastore; + +public class JsonDataStore +{ + public static String baseUrl = "https://raw.githubusercontent.com/osrs-reldo/task-json-store/refs/heads/main"; +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/jsondatastore/ManifestClient.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/jsondatastore/ManifestClient.java new file mode 100644 index 00000000..3b03a11d --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/jsondatastore/ManifestClient.java @@ -0,0 +1,44 @@ +package net.reldo.taskstracker.data.jsondatastore; + +import com.google.common.io.CharStreams; +import com.google.gson.Gson; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import javax.inject.Inject; +import javax.inject.Singleton; +import lombok.extern.slf4j.Slf4j; +import net.reldo.taskstracker.data.jsondatastore.reader.DataStoreReader; +import net.reldo.taskstracker.data.jsondatastore.types.Manifest; +import okhttp3.OkHttpClient; + +@Singleton +@Slf4j +public class ManifestClient +{ + @Inject private OkHttpClient okHttpClient; + @Inject private Gson gson; + @Inject private DataStoreReader dataStoreReader; + + private static Manifest _manifest = null; + + public ManifestClient() + { + log.debug("init manifestclient"); + } + + public Manifest getManifest() throws Exception + { + if (_manifest != null) { + return _manifest; + } + try(InputStream stream = this.dataStoreReader.readManifestData(); + InputStreamReader responseReader = new InputStreamReader(stream, StandardCharsets.UTF_8)) + { + String manifestJson = CharStreams.toString(responseReader); // ew, why not a stream? not working... + _manifest = this.gson.fromJson(manifestJson, Manifest.class); + log.debug("_manifest = " + _manifest); + return _manifest; + } + } +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/jsondatastore/TaskDataClient.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/jsondatastore/TaskDataClient.java new file mode 100644 index 00000000..32bff48d --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/jsondatastore/TaskDataClient.java @@ -0,0 +1,67 @@ +package net.reldo.taskstracker.data.jsondatastore; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import javax.inject.Inject; +import javax.inject.Singleton; +import lombok.extern.slf4j.Slf4j; +import net.reldo.taskstracker.data.jsondatastore.reader.DataStoreReader; +import net.reldo.taskstracker.data.jsondatastore.types.TaskDefinition; +import net.reldo.taskstracker.data.jsondatastore.types.TaskTypeDefinition; +import net.reldo.taskstracker.data.task.TaskType; +import net.runelite.api.Client; +import net.runelite.client.callback.ClientThread; +import net.runelite.client.game.SpriteManager; +import okhttp3.OkHttpClient; + +@Singleton +@Slf4j +public class TaskDataClient +{ + @Inject private ManifestClient manifestClient; + @Inject private OkHttpClient okHttpClient; + @Inject private Gson gson; + @Inject private DataStoreReader dataStoreReader; + @Inject private Client client; + @Inject private ClientThread clientThread; + @Inject private SpriteManager spriteManager; + + public TaskDataClient() + { + log.debug("init task data client"); + } + + public HashMap getTaskTypes() throws Exception { + try (InputStream stream = this.dataStoreReader.readTaskTypes(this.manifestClient.getManifest().taskTypeMetadata); + InputStreamReader responseReader = new InputStreamReader(stream, StandardCharsets.UTF_8)) + { + Type listType = TypeToken.getParameterized(ArrayList.class, TaskTypeDefinition.class).getType(); + + List taskTypeDefinitions = this.gson.fromJson(responseReader, listType); + + HashMap taskTypes = new HashMap<>(); + for (TaskTypeDefinition taskTypeDefinition : taskTypeDefinitions) + { + taskTypes.put(taskTypeDefinition.getTaskJsonName(), new TaskType(client, clientThread, spriteManager, taskTypeDefinition)); + } + return taskTypes; + } + } + + public List getTaskDefinitions(String jsonFilename) throws Exception + { + try(InputStream stream = this.dataStoreReader.readTasks(jsonFilename); + InputStreamReader responseReader = new InputStreamReader(stream, StandardCharsets.UTF_8)) + { + Type listType = TypeToken.getParameterized(ArrayList.class, TaskDefinition.class).getType(); + return this.gson.fromJson(responseReader, listType); + } + } +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/jsondatastore/reader/DataStoreReader.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/jsondatastore/reader/DataStoreReader.java new file mode 100644 index 00000000..74dd601e --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/jsondatastore/reader/DataStoreReader.java @@ -0,0 +1,11 @@ +package net.reldo.taskstracker.data.jsondatastore.reader; + +import java.io.InputStream; + +public interface DataStoreReader +{ + InputStream readManifestData() throws Exception; + InputStream readTaskTypes(String taskTypeFilename) throws Exception; + InputStream readTasks(String jsonFilename) throws Exception; + InputStream readFilterConfigs(String filterFilename) throws Exception; +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/jsondatastore/reader/HttpDataStoreReader.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/jsondatastore/reader/HttpDataStoreReader.java new file mode 100644 index 00000000..34f43245 --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/jsondatastore/reader/HttpDataStoreReader.java @@ -0,0 +1,130 @@ +package net.reldo.taskstracker.data.jsondatastore.reader; + +import java.io.InputStream; +import javax.inject.Inject; +import javax.inject.Singleton; +import lombok.extern.slf4j.Slf4j; +import net.reldo.taskstracker.data.jsondatastore.JsonDataStore; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; + +@Singleton +@Slf4j +public class HttpDataStoreReader implements DataStoreReader +{ + @Inject + private OkHttpClient okHttpClient; + + @Override + public InputStream readManifestData() throws Exception + { + String manifestUrl = JsonDataStore.baseUrl + "/manifest.json"; + log.debug("getManifest json from {} ...", manifestUrl); + Request request = new Request.Builder() + .url(manifestUrl) + .build(); + Response response = this.okHttpClient.newCall(request).execute(); + if (!response.isSuccessful()) + { + String unsuccessful = "getManifest json request unsuccessful with status " + response.code(); + if (response.body() != null) + { + unsuccessful += " and body \n" + response.body(); + } + log.error(unsuccessful); + throw new Exception(unsuccessful); + } + if (response.body() == null) + { + log.error("getManifest returned without body"); + throw new Exception("getManifest returned without body"); + } + log.debug("getManifest json fetched successfully, deserializing result"); + return response.body().byteStream(); + } + + @Override + public InputStream readTaskTypes(String taskTypeFilename) throws Exception + { + String taskJsonUrl = JsonDataStore.baseUrl + "/" + taskTypeFilename; + log.debug("getTaskTypes json from {} ...", taskJsonUrl); + Request request = new Request.Builder() + .url(taskJsonUrl) + .build(); + Response response = this.okHttpClient.newCall(request).execute(); + if (!response.isSuccessful()) + { + String unsuccessful = "getTaskTypes json request unsuccessful with status " + response.code(); + if (response.body() != null) + { + unsuccessful += " and body \n" + response.body(); + } + log.error(unsuccessful); + throw new Exception(unsuccessful); + } + if (response.body() == null) + { + log.error("getTaskTypes returned without body"); + throw new Exception("getTaskTypes returned without body"); + } + log.debug("getTaskTypes json fetched successfully, deserializing result"); + return response.body().byteStream(); + } + + @Override + public InputStream readTasks(String jsonFilename) throws Exception + { + String taskJsonUrl = String.format("%s/tasks/%s.min.json", JsonDataStore.baseUrl, jsonFilename); + log.debug("getTasks json from {} ...", taskJsonUrl); + Request request = new Request.Builder() + .url(taskJsonUrl) + .build(); + Response response = this.okHttpClient.newCall(request).execute(); + if (!response.isSuccessful()) + { + String unsuccessful = "getTasks json request unsuccessful with status " + response.code(); + if (response.body() != null) + { + unsuccessful += " and body \n" + response.body(); + } + log.error(unsuccessful); + throw new Exception(unsuccessful); + } + if (response.body() == null) + { + log.error("getTasks returned without body"); + throw new Exception("getTasks returned without body"); + } + log.debug("getTasks json fetched successfully, deserializing result"); + return response.body().byteStream(); + } + + @Override + public InputStream readFilterConfigs(String filterFilename) throws Exception + { + String filterJsonUrl = JsonDataStore.baseUrl + "/" + filterFilename; + log.debug("getTaskTypes json from {} ...", filterJsonUrl); + Request request = new Request.Builder() + .url(filterJsonUrl) + .build(); + Response response = this.okHttpClient.newCall(request).execute(); + if (!response.isSuccessful()) + { + String unsuccessful = "getFilters json request unsuccessful with status " + response.code(); + if (response.body() != null) + { + unsuccessful += " and body \n" + response.body(); + } + log.error(unsuccessful); + throw new Exception(unsuccessful); + } + if (response.body() == null) + { + log.error("getFilters returned without body"); + throw new Exception("getFilters returned without body"); + } + log.debug("getFilters json fetched successfully, deserializing result"); + return response.body().byteStream(); + } +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/jsondatastore/types/FilterConfig.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/jsondatastore/types/FilterConfig.java new file mode 100644 index 00000000..2be17d79 --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/jsondatastore/types/FilterConfig.java @@ -0,0 +1,51 @@ +package net.reldo.taskstracker.data.jsondatastore.types; + +import java.util.ArrayList; +import lombok.AllArgsConstructor; +import lombok.Data; + +/** + * Represents the configuration for a filter + */ +@Data +@AllArgsConstructor +public class FilterConfig { + /** + * Key under which to store the filter's selected values, generally prefixed by task type + */ + private String configKey; + + /** + * The label displayed in the UI with the filter. + */ + private String label; + + /** + * The filter type, see enum for types of filters supported. + */ + private FilterType filterType; + + /** + * The source of the value(s) to use for the filter, see enum for types of values supported. + * If global is specified then configKey must match a filter config defined in filters.json + */ + private FilterValueType valueType; + + /** + * The name of the param or metadata property to use for the filter. + * Can be left null for SKILL value type + */ + private String valueName; + + /** + * Name of an enum specified in `TaskTypeDefinition.stringEnumMap` to provide labels for the filter + * Specifying this property will override the displayed integer value of `valueName` + */ + private String optionLabelEnum; + + /** + * Item values in a button filter (dropdown not yet supported) + */ + private ArrayList customItems; + +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/jsondatastore/types/FilterCustomItem.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/jsondatastore/types/FilterCustomItem.java new file mode 100644 index 00000000..63d766a9 --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/jsondatastore/types/FilterCustomItem.java @@ -0,0 +1,11 @@ +package net.reldo.taskstracker.data.jsondatastore.types; + +import lombok.Data; + +@Data +public class FilterCustomItem +{ + private Integer value; + private String tooltip; + private Integer spriteId; +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/jsondatastore/types/FilterType.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/jsondatastore/types/FilterType.java new file mode 100644 index 00000000..7f7d1f00 --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/jsondatastore/types/FilterType.java @@ -0,0 +1,7 @@ +package net.reldo.taskstracker.data.jsondatastore.types; + +public enum FilterType +{ + BUTTON_FILTER, + DROPDOWN_FILTER +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/jsondatastore/types/FilterValueType.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/jsondatastore/types/FilterValueType.java new file mode 100644 index 00000000..9b8998a7 --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/jsondatastore/types/FilterValueType.java @@ -0,0 +1,10 @@ +package net.reldo.taskstracker.data.jsondatastore.types; + +public enum FilterValueType +{ + PARAM_INTEGER, + PARAM_STRING, + SKILL, + METADATA, + GLOBAL +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/jsondatastore/types/Manifest.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/jsondatastore/types/Manifest.java new file mode 100644 index 00000000..c5ae8c91 --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/jsondatastore/types/Manifest.java @@ -0,0 +1,10 @@ +package net.reldo.taskstracker.data.jsondatastore.types; + +import lombok.Data; + +@Data +public class Manifest +{ + public String taskTypeMetadata; + public String filterMetadata; +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/jsondatastore/types/TaskDefinition.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/jsondatastore/types/TaskDefinition.java new file mode 100644 index 00000000..bb90ce89 --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/jsondatastore/types/TaskDefinition.java @@ -0,0 +1,46 @@ +package net.reldo.taskstracker.data.jsondatastore.types; + +import java.util.List; +import java.util.Map; +import lombok.Data; + +/** + * Represents a task with various attributes + */ +@Data +public class TaskDefinition +{ + /** + * Struct id for task data + */ + private Integer structId; + + /** + * Sort id based on the sort order in the game's UI + */ + private Integer sortId; + + /** + * Skills required for the task. + */ + private List skills; + + /** + * Metadata related to the task that isn't represented in the Struct/params + * May or may not be used for task filters + * Examples: + * - notes = extra description like "a magic cabbage is a cabbage picked at Draynor Manor" + * - category = an extra category type that isn't a param + */ + private Map metadata; + + /** + * Notes from the OSRS wiki + */ + private String wikiNotes; + + /** + * Completion percent from the OSRS wiki + */ + private Float completionPercent; +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/jsondatastore/types/TaskDefinitionSkill.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/jsondatastore/types/TaskDefinitionSkill.java new file mode 100644 index 00000000..a96739bf --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/jsondatastore/types/TaskDefinitionSkill.java @@ -0,0 +1,20 @@ +package net.reldo.taskstracker.data.jsondatastore.types; + +import lombok.Data; + +/** + * Represents a skill required for a task. + */ +@Data +public class TaskDefinitionSkill +{ + /** + * The skill + */ + private String skill; + + /** + * The level required + */ + private Integer level; +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/jsondatastore/types/TaskTypeDefinition.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/jsondatastore/types/TaskTypeDefinition.java new file mode 100644 index 00000000..d8c2172a --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/jsondatastore/types/TaskTypeDefinition.java @@ -0,0 +1,105 @@ +package net.reldo.taskstracker.data.jsondatastore.types; + +import java.util.ArrayList; +import java.util.HashMap; +import lombok.Data; + +/** + * Represents a task type with relevant configuration for UI display and task management + */ +@Data +public class TaskTypeDefinition +{ + + /** + * Name of the task type for UI display + */ + private String name; + + /** + * Description of the task type + */ + private String description; + + /** + * Is the task type enabled? + */ + private boolean isEnabled; + + /** + * Filename for the task JSON found in the tasks directory of task-json-store + * Extension not included. + */ + private String taskJsonName; + + /** + * Filters for the task type + */ + private ArrayList filters; + + /** + * A dictionary of parameters relevant to the task, with required id, name, description, tier + * The key is the plain English name for the parameter + * The value is an array of OSRS cache Struct ParamIDs that match with the plain English parameter + * Generally, there is only 1 value in the array, but multiple are available for fallback + */ + private HashMap intParamMap; + + /** + * A dictionary of parameters relevant to the task, with required id, name, description, tier + * The key is the plain English name for the parameter + * The value is an array of OSRS cache Struct ParamIDs that match with the plain English parameter + * Generally, there is only 1 value in the array, but multiple are available for fallback + */ + private HashMap stringParamMap; + + /** + * A dictionary of integer enums relevant to the task type + * The key is the plain English name describing the enum + * The value is an integer representing the enum id + * e.g. "tierSprites": 3213 (tier id maps to a sprite id) + */ + private HashMap intEnumMap; + + /** + * A dictionary of string enums relevant to the task type + * The key is the plain English name describing the enum + * The value is an integer representing the enum id + * e.g. "tierNames": 4757 (tier id maps to a sprite id) + */ + private HashMap stringEnumMap; + + /** + * A dictionary of tier sprite ids + * The key is a string representation of the tier id integer + * The value is an integer representing the sprite id + */ + private HashMap tierSpriteIdMap = new HashMap<>(); + + /** + * Varps used to store task progress + * Used for exports from the plugin + */ + private ArrayList taskVarps; + + /** + * Other varps used for the task type + * Used for exports from the plugin + * Examples in the past: League Points, Sage Renown + */ + private int[] otherVarps; + + /** + * Varbits used for the task type + * Used for exports from the plugin + * Examples in the past: Relics chosen, Tasks completed, unlocks, Fragment xp + */ + private int[] varbits; + + /** + * The script id used to parse the completion of a task + * This is a rs2asm script + * Example: Combat achievements = script 4834 + */ + private int taskCompletedScriptId; +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/reldo/ReldoImport.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/reldo/ReldoImport.java new file mode 100644 index 00000000..a2e2baac --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/reldo/ReldoImport.java @@ -0,0 +1,13 @@ +package net.reldo.taskstracker.data.reldo; + +import java.util.HashMap; +import lombok.Data; + +@Data +public class ReldoImport +{ + public String taskTypeName; + public int version; + public String rsn; + private HashMap tasks; +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/reldo/ReldoTaskSave.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/reldo/ReldoTaskSave.java new file mode 100644 index 00000000..7254f819 --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/reldo/ReldoTaskSave.java @@ -0,0 +1,14 @@ +package net.reldo.taskstracker.data.reldo; + +import lombok.Data; + +@Data +public class ReldoTaskSave +{ + long completed; + long todo; + long ignored; + int order; + String notes; + long lastUpdated; +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/task/ConfigTaskSave.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/task/ConfigTaskSave.java new file mode 100644 index 00000000..ae4fcdf9 --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/task/ConfigTaskSave.java @@ -0,0 +1,19 @@ +package net.reldo.taskstracker.data.task; + +import com.google.gson.annotations.Expose; + +public class ConfigTaskSave +{ + @Expose public final long completed; + @Expose public final long tracked; + @Expose public final Integer structId; + @Expose public final long ignored; + + public ConfigTaskSave(TaskFromStruct task) + { + completed = task.getCompletedOn(); + tracked = task.getTrackedOn(); + ignored = task.getIgnoredOn(); + structId = task.getStructId(); + } +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/task/TaskFromStruct.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/task/TaskFromStruct.java new file mode 100644 index 00000000..06963d31 --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/task/TaskFromStruct.java @@ -0,0 +1,208 @@ +package net.reldo.taskstracker.data.task; + +import java.time.Instant; +import java.util.HashMap; +import java.util.Map; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import net.reldo.taskstracker.data.jsondatastore.types.TaskDefinition; +import net.reldo.taskstracker.data.reldo.ReldoTaskSave; +import net.runelite.api.Client; +import net.runelite.api.StructComposition; + +@Slf4j +public class TaskFromStruct +{ + @Getter + private final Integer structId; + @Getter + private final Integer sortId; + @Getter + private TaskType taskType; + @Getter + private final TaskDefinition taskDefinition; + @Getter + private boolean structLoaded; + @Getter @Setter + private long completedOn; + @Getter @Setter + private long trackedOn; + @Getter @Setter + private long ignoredOn; + + private StructComposition _struct; + private final Map _stringParams = new HashMap<>(); + private final Map _intParams = new HashMap<>(); + + public TaskFromStruct(TaskType taskType, TaskDefinition taskDefinition) + { + this.taskType = taskType; + this.taskDefinition = taskDefinition; + this.structId = taskDefinition.getStructId(); + this.sortId = taskDefinition.getSortId(); + } + + public String getStringParam(String paramName) + { + return this._stringParams.get(paramName); + } + + public Integer getIntParam(String paramName) + { + return this._intParams.get(paramName); + } + + // TODO: Remove client from params + public boolean loadStructData(Client client) + { + assert client.isClientThread(); + + if (structLoaded) { + return true; + } + try + { + // log.debug("LOADING STRUCT DATA " + structId); + _struct = client.getStructComposition(structId); + taskType.getIntParamMap().forEach((paramName, paramId) -> { + int value = _struct.getIntValue(paramId); + // log.debug("{} {}", paramName, value); + _intParams.put(paramName, value); + }); + taskType.getStringParamMap().forEach((paramName, paramId) -> { + String value = _struct.getStringValue(paramId); + // log.debug("{} {}", paramName, value); + _stringParams.put(paramName, value); + }); + } + catch (Exception ex) + { + log.error("error loading struct data {}", ex, ex); + return false; + } + + structLoaded = true; + return true; + } + + public boolean isCompleted() + { + return completedOn > 0; + } + + public int getPoints() + { + if (taskType.getTierPoints().size() == 0) + { + return 0; + } + Integer points = taskType.getTierPoints().get(getTier()); + if (points == null) + { + return 0; + } + return points; + } + + public void setCompleted(boolean completed) + { + long now = Instant.now().toEpochMilli(); + if (completed && completedOn > 0 && completedOn <= now) + { + return; + } + completedOn = completed ? now : 0; + } + + public boolean isTracked() + { + return trackedOn > 0; + } + + public void setTracked(boolean state) + { + long now = Instant.now().toEpochMilli(); + if (state && trackedOn > 0 && trackedOn <= now) + { + return; + } + trackedOn = state ? now : 0; + } + + public boolean isIgnored() + { + return ignoredOn > 0; + } + + public void setIgnored(boolean state) + { + long now = Instant.now().toEpochMilli(); + if (state && ignoredOn > 0 && ignoredOn <= now) + { + return; + } + ignoredOn = state ? now : 0; + } + + public void loadConfigSave(ConfigTaskSave loadedData) + { + setDates(loadedData.completed, loadedData.ignored, loadedData.tracked); + } + + public void loadReldoSave(ReldoTaskSave loadedData) + { + setMostRecentDates(loadedData.getCompleted(), loadedData.getIgnored(), loadedData.getTodo()); + } + + private void setDates(long completedOn, long ignoredOn, long trackedOn) + { + // Set all dates regardless of how they compare + this.setCompletedOn(completedOn); + this.setIgnoredOn(ignoredOn); + this.setTrackedOn(trackedOn); + } + + private void setMostRecentDates(long completedOn, long ignoredOn, long trackedOn) + { + // Older completions take priority; incomplete (0) also takes priority + if (completedOn < this.getCompletedOn()) + { + this.setCompletedOn(completedOn); + } + // Newer ignores take priority + if (ignoredOn > this.getIgnoredOn()) + { + this.setIgnoredOn(ignoredOn); + } + // Newer tracks take priority + if (trackedOn > this.getTrackedOn()) + { + this.setTrackedOn(trackedOn); + } + } + + public String getName() + { + return getStringParam("name"); + } + + public int getTier() + { + return getIntParam("tier"); + } + + public String getDescription() + { + return getStringParam("description"); + } + + public ConfigTaskSave getSaveData() + { + return new ConfigTaskSave(this); + } + + public Float getCompletionPercent() { + return getTaskDefinition().getCompletionPercent(); + } +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/task/TaskService.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/task/TaskService.java new file mode 100644 index 00000000..d86a8f02 --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/task/TaskService.java @@ -0,0 +1,306 @@ +package net.reldo.taskstracker.data.task; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.stream.Collectors; +import javax.inject.Inject; +import javax.inject.Singleton; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import net.reldo.taskstracker.TasksTrackerPlugin; +import net.reldo.taskstracker.data.jsondatastore.ManifestClient; +import net.reldo.taskstracker.data.jsondatastore.TaskDataClient; +import net.reldo.taskstracker.data.jsondatastore.types.FilterConfig; +import net.reldo.taskstracker.data.jsondatastore.types.FilterValueType; +import net.reldo.taskstracker.data.jsondatastore.types.TaskDefinition; +import net.reldo.taskstracker.data.task.filters.FilterService; +import net.runelite.api.Client; +import net.runelite.api.EnumComposition; +import net.runelite.client.callback.ClientThread; +import net.runelite.client.config.ConfigManager; + +@Singleton +@Slf4j +public class TaskService +{ + @Inject private ManifestClient manifestClient; + @Inject private TaskDataClient taskDataClient; + @Inject private ClientThread clientThread; + @Inject private Client client; + @Inject private FilterService filterService; + @Inject private ConfigManager configManager; + + @Getter + @Setter + private boolean taskTypeChanged = false; + @Getter + private TaskType currentTaskType; + @Getter + private final List tasks = new ArrayList<>(); + @Getter + private final HashMap sortedIndexes = new HashMap<>(); + private HashMap _taskTypes = new HashMap<>(); + private HashSet currentTaskTypeVarps = new HashSet<>(); + private final ExecutorService futureExecutor = Executors.newSingleThreadExecutor(); + + public CompletableFuture setTaskType(String taskTypeJsonName) { + return getTaskTypesByJsonName().thenCompose(taskTypes -> + { + TaskType newTaskType = taskTypes.get(taskTypeJsonName); + if (newTaskType == null) + { + log.error("unsupported task type {}, falling back to COMBAT", taskTypeJsonName); + newTaskType = taskTypes.get("COMBAT"); + } + return this.setTaskType(newTaskType); + }); + } + + private CompletableFuture loadAllTasksStructData(Collection tasks) { + Collection> taskFutures = new ArrayList<>(); + for (TaskFromStruct task : tasks) { + CompletableFuture taskFuture = new CompletableFuture<>(); + clientThread.invoke(() -> { + boolean isTaskLoaded = task.loadStructData(client); + taskFuture.complete(isTaskLoaded); + }); + taskFutures.add(taskFuture); + } + return CompletableFuture.allOf(taskFutures.toArray(new CompletableFuture[0])).thenApply(v -> { + for (CompletableFuture future : taskFutures) { + if (!future.join()) { + return false; + } + } + return true; + }); + } + + public CompletableFuture setTaskType(TaskType newTaskType) { + log.debug("setTaskType {}", newTaskType.getTaskJsonName()); + if (newTaskType.equals(currentTaskType)) { + log.debug("Skipping setTaskType, same task type selected"); + return CompletableFuture.completedFuture(false); + } + currentTaskType = newTaskType; + configManager.setConfiguration(TasksTrackerPlugin.CONFIG_GROUP_NAME, "taskTypeJsonName", newTaskType.getTaskJsonName()); + + // Complete creation of any GLOBAL value type filterConfigs + for (FilterConfig filterConfig : currentTaskType.getFilters()) { + if (filterConfig.getValueType().equals(FilterValueType.GLOBAL)) { + // Set valueType to the one required by the global filter + FilterConfig globalFilterConfig = filterService.getGlobalFilterByKey(filterConfig.getConfigKey()); + filterConfig.setValueType(globalFilterConfig.getValueType()); + + // Set any filterConfig fields not already specified + Optional.ofNullable(filterConfig.getLabel()).ifPresentOrElse(val -> {}, () -> filterConfig.setLabel(globalFilterConfig.getLabel())); + Optional.ofNullable(filterConfig.getFilterType()).ifPresentOrElse(val -> {}, () -> filterConfig.setFilterType(globalFilterConfig.getFilterType())); + Optional.ofNullable(filterConfig.getValueName()).ifPresentOrElse(val -> {}, () -> filterConfig.setValueName(globalFilterConfig.getValueName())); + Optional.ofNullable(filterConfig.getOptionLabelEnum()).ifPresentOrElse(val -> {}, () -> filterConfig.setOptionLabelEnum(globalFilterConfig.getOptionLabelEnum())); + Optional.ofNullable(filterConfig.getCustomItems()).ifPresentOrElse(val -> {}, () -> filterConfig.setCustomItems(globalFilterConfig.getCustomItems())); + } + } + + List newTasks = new ArrayList<>(); + return newTaskType.loadTaskTypeDataAsync().thenCompose((isTaskTypeLoaded) -> { + if (!isTaskTypeLoaded) { + log.error("Error loading task type during setTaskType"); + return CompletableFuture.completedFuture(false); + } + + CompletableFuture future = new CompletableFuture<>(); + futureExecutor.submit(() -> { + try { + Collection taskDefinitions = taskDataClient.getTaskDefinitions(currentTaskType.getTaskJsonName()); + for (TaskDefinition definition : taskDefinitions) { + TaskFromStruct task = new TaskFromStruct(currentTaskType, definition); + newTasks.add(task); + } + loadAllTasksStructData(newTasks).thenApply(future::complete); + } catch (Exception e3) { + future.completeExceptionally(e3); + } + }); + return future; + }).thenCompose(areTasksLoaded -> { + if (!areTasksLoaded) { + return CompletableFuture.completedFuture(false); + } + + tasks.clear(); + tasks.addAll(newTasks); + + // Index task list for each property + sortedIndexes.clear(); + currentTaskType.getIntParamMap().keySet().forEach(paramName -> { + sortedIndexes.put(paramName, null); + addSortedIndex(paramName, Comparator.comparingInt((TaskFromStruct task) -> task.getIntParam(paramName))); + }); + currentTaskType.getStringParamMap().keySet().forEach(paramName -> { + sortedIndexes.put(paramName, null); + addSortedIndex(paramName, Comparator.comparing((TaskFromStruct task) -> task.getStringParam(paramName))); + }); + // todo: make this less of a special case. + if (tasks.stream().anyMatch(task -> task.getCompletionPercent() != null)) { + sortedIndexes.put("completion %", null); + addSortedIndex("completion %", + (TaskFromStruct task1, TaskFromStruct task2) -> + { + Float comp1 = task1.getTaskDefinition().getCompletionPercent() != null ? task1.getTaskDefinition().getCompletionPercent() : 0; + Float comp2 = task2.getTaskDefinition().getCompletionPercent() != null ? task2.getTaskDefinition().getCompletionPercent() : 0; + return comp1.compareTo(comp2); + }); + } + + currentTaskTypeVarps.clear(); + currentTaskTypeVarps = new HashSet<>(currentTaskType.getTaskVarps()); + + taskTypeChanged = true; + return CompletableFuture.completedFuture(true); + }); + } + + private void addSortedIndex(String paramName, Comparator comparator) + { + List sortedTasks = tasks.stream() + .sorted(comparator) + .collect(Collectors.toCollection(ArrayList::new)); + int[] sortedIndex = new int[tasks.size()]; + for(int i = 0; i < sortedTasks.size(); i++) + { + sortedIndex[i] = tasks.indexOf(sortedTasks.get(i)); + } + sortedIndexes.put(paramName, sortedIndex); + } + + public int getSortedTaskIndex(String sortCriteria, int position) + { + if(sortedIndexes.containsKey(sortCriteria)) + { + return sortedIndexes.get(sortCriteria)[position]; + } + else + { + return position; + } + } + + public boolean isVarpInCurrentTaskType(int varpId) + { + return currentTaskTypeVarps.contains(varpId); + } + + public void clearTaskTypes() + { + this._taskTypes.clear(); + } + + /** + * Get a map of task type json names to task type + * + * @return Hashmap of TaskType indexed by task type json name + */ + public CompletableFuture> getTaskTypesByJsonName() + { + if (_taskTypes.size() > 0) + { + return CompletableFuture.completedFuture(_taskTypes); + } + + try + { + CompletableFuture> future = new CompletableFuture<>(); + futureExecutor.submit(() -> + { + try + { + _taskTypes = taskDataClient.getTaskTypes(); + future.complete(_taskTypes); + } + catch (Exception e) { + future.completeExceptionally(e); + } + }); + + return future; + } + catch (Exception ex) + { + log.error("Unable to populate task types from data client", ex); + return CompletableFuture.completedFuture(new HashMap<>()); + } + } + + public CompletableFuture> getStringEnumValuesAsync(String enumName) + { + Integer enumId = currentTaskType.getStringEnumMap().get(enumName); + if (enumId == null) + { + return CompletableFuture.completedFuture(new HashMap<>()); + } + + CompletableFuture> future = new CompletableFuture<>(); + clientThread.invoke(() -> { + try + { + EnumComposition enumComposition = client.getEnum(enumId); + int[] keys = enumComposition.getKeys(); + HashMap map = new HashMap<>(); + for (int key : keys) + { + map.put(key, enumComposition.getStringValue(key)); + } + future.complete(map); + } + catch (Exception ex) + { + log.error("Error getting string enum values", ex); + future.completeExceptionally(ex); + } + }); + return future; + } + + public void applySave(TaskType saveTaskType, HashMap saveData) + { + String currentTaskTypeName = currentTaskType.getTaskJsonName(); + String saveTaskTypeName = saveTaskType.getTaskJsonName(); + if (!currentTaskTypeName.equals(saveTaskTypeName)) + { + log.warn("Cannot apply save, task types do not match current={} save={}", currentTaskTypeName, saveTaskTypeName); + return; + } + + for (TaskFromStruct task : getTasks()) + { + ConfigTaskSave configTaskSave = saveData.get(task.getStructId()); + if (configTaskSave == null) + { + continue; + } + task.loadConfigSave(configTaskSave); + } + } + + public List getTasksFromVarpId(Integer varpId) + { + int varpIndex = getCurrentTaskType().getTaskVarps().indexOf(varpId); + int minTaskId = varpIndex * 32; + int maxTaskId = minTaskId + 32; + + return getTasks().stream().filter(t -> { + int taskId = t.getIntParam("id"); + return taskId >= minTaskId && taskId <= maxTaskId; + }).collect(Collectors.toList()); + } +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/task/TaskType.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/task/TaskType.java new file mode 100644 index 00000000..684a276a --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/task/TaskType.java @@ -0,0 +1,147 @@ +package net.reldo.taskstracker.data.task; + +import java.awt.image.BufferedImage; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.concurrent.CompletableFuture; +import lombok.Getter; +import net.reldo.taskstracker.data.jsondatastore.types.FilterConfig; +import net.reldo.taskstracker.data.jsondatastore.types.FilterType; +import net.reldo.taskstracker.data.jsondatastore.types.TaskTypeDefinition; +import net.runelite.api.Client; +import net.runelite.api.EnumComposition; +import net.runelite.client.callback.ClientThread; +import net.runelite.client.game.SpriteManager; + +public class TaskType +{ + @Getter + private final HashMap spritesById = new HashMap<>(); + @Getter + private final HashMap tierSprites = new HashMap<>(); + @Getter + private final HashMap tierPoints = new HashMap<>(); + + private final Client client; + private final ClientThread clientThread; + private final SpriteManager spriteManager; + private final TaskTypeDefinition _taskTypeDefinition; + + public TaskType(Client client, ClientThread clientThread, SpriteManager spriteManager, TaskTypeDefinition taskTypeDefinition) + { + this.client = client; + this.clientThread = clientThread; + this.spriteManager = spriteManager; + this._taskTypeDefinition = taskTypeDefinition; + } + + public CompletableFuture loadTaskTypeDataAsync() + { + CompletableFuture future = new CompletableFuture<>(); + clientThread.invoke(() -> { + try { + getButtonFiltersSpriteIds().forEach((spriteId) -> { + BufferedImage spriteImage = spriteManager.getSprite(spriteId, 0); + spritesById.put(spriteId, spriteImage); + }); + _taskTypeDefinition.getTierSpriteIdMap().forEach((idKey, spriteId) -> { + Integer tierId = Integer.parseInt(idKey); + BufferedImage spriteImage = spriteManager.getSprite(spriteId, 0); + tierSprites.put(tierId, spriteImage); + }); + if (_taskTypeDefinition.getIntEnumMap().containsKey("tierPoints")) + { + int enumId = _taskTypeDefinition.getIntEnumMap().get("tierPoints"); + EnumComposition enumComposition = client.getEnum(enumId); + int[] keys = enumComposition.getKeys(); + for (int key : keys) + { + tierPoints.put(key, enumComposition.getIntValue(key)); + } + } + future.complete(true); + } catch (Exception e) { + future.completeExceptionally(e); + } + }); + + return future; + } + + public String getFilterConfigPrefix() + { + return _taskTypeDefinition.getTaskJsonName() + "."; + } + + private HashSet getButtonFiltersSpriteIds() + { + HashSet sprites = new HashSet<>(); + _taskTypeDefinition.getFilters().stream().filter( + (filterConfig) -> filterConfig.getFilterType().equals(FilterType.BUTTON_FILTER) + ).forEach((filterConfig) -> { + if (filterConfig.getCustomItems() != null) + { + filterConfig.getCustomItems().forEach((customSprite) -> { + Integer spriteId = customSprite.getSpriteId(); + if (spriteId == null) + { + return; + } + sprites.add(spriteId); + }); + } + }); + return sprites; + } + + public ArrayList getTaskVarps() + { + return _taskTypeDefinition.getTaskVarps(); + } + + public String getTaskJsonName() + { + return _taskTypeDefinition.getTaskJsonName(); + } + + public HashMap getIntParamMap() + { + return _taskTypeDefinition.getIntParamMap(); + } + + public HashMap getStringParamMap() + { + return _taskTypeDefinition.getStringParamMap(); + } + + public HashMap getStringEnumMap() + { + return _taskTypeDefinition.getStringEnumMap(); + } + + public String getName() + { + return _taskTypeDefinition.getName(); + } + + public ArrayList getFilters() + { + return _taskTypeDefinition.getFilters(); + } + + public int[] getOtherVarps() + { + return _taskTypeDefinition.getOtherVarps(); + } + + public int[] getVarbits() + { + return _taskTypeDefinition.getVarbits(); + } + + public int getTaskCompletedScriptId() + { + return _taskTypeDefinition.getTaskCompletedScriptId(); + } +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/task/filters/Filter.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/task/filters/Filter.java new file mode 100644 index 00000000..db4fdbeb --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/task/filters/Filter.java @@ -0,0 +1,8 @@ +package net.reldo.taskstracker.data.task.filters; + +import net.reldo.taskstracker.data.task.TaskFromStruct; + +public abstract class Filter +{ + public abstract boolean meetsCriteria(TaskFromStruct task); +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/task/filters/FilterService.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/task/filters/FilterService.java new file mode 100644 index 00000000..eb6190a1 --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/task/filters/FilterService.java @@ -0,0 +1,47 @@ +package net.reldo.taskstracker.data.task.filters; + +import java.util.HashMap; +import javax.inject.Inject; +import javax.inject.Singleton; +import lombok.extern.slf4j.Slf4j; +import net.reldo.taskstracker.data.jsondatastore.FilterDataClient; +import net.reldo.taskstracker.data.jsondatastore.types.FilterConfig; + +@Singleton +@Slf4j +public class FilterService +{ + @Inject + private FilterDataClient filterDataClient; + + // Filter config cache + private HashMap _filterConfigs = new HashMap<>(); + + public FilterConfig getGlobalFilterByKey(String filterKey) + { + // Instantiate filterConfigs if not already + if (_filterConfigs == null || _filterConfigs.isEmpty()) + { + try + { + _filterConfigs = filterDataClient.getFilterConfigs(); + return _filterConfigs.get(filterKey); + } + catch (Exception ex) + { + log.error("Unable to get filter configs", ex); + } + } + else + { + return _filterConfigs.get(filterKey); + } + + return null; + } + + public void clearFilterConfigs() + { + this._filterConfigs.clear(); + } +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/task/filters/ParamButtonFilter.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/task/filters/ParamButtonFilter.java new file mode 100644 index 00000000..d91dd017 --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/task/filters/ParamButtonFilter.java @@ -0,0 +1,34 @@ +package net.reldo.taskstracker.data.task.filters; + +import lombok.extern.slf4j.Slf4j; +import net.reldo.taskstracker.TasksTrackerPlugin; +import net.reldo.taskstracker.data.task.TaskFromStruct; +import net.runelite.client.config.ConfigManager; + +@Slf4j +public class ParamButtonFilter extends Filter +{ + private final String paramName; + private final String filterConfigKey; + private final ConfigManager configManager; + + public ParamButtonFilter(ConfigManager configManager, String paramName, String filterConfigKey) + { + this.configManager = configManager; + this.paramName = paramName; + this.filterConfigKey = filterConfigKey; + } + + @Override + public boolean meetsCriteria(TaskFromStruct task) + { + String configValue = configManager.getConfiguration(TasksTrackerPlugin.CONFIG_GROUP_NAME, filterConfigKey); + boolean isEmptyFilterSelection = configValue == null || configValue.isEmpty() || configValue.equals("-1"); + if (isEmptyFilterSelection) + { + return false; + } + + return configValue.contains("f-" + task.getIntParam(paramName) + "-f"); + } +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/task/filters/ParamDropdownFilter.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/task/filters/ParamDropdownFilter.java new file mode 100644 index 00000000..77256ab4 --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/data/task/filters/ParamDropdownFilter.java @@ -0,0 +1,46 @@ +package net.reldo.taskstracker.data.task.filters; + +import lombok.extern.slf4j.Slf4j; +import net.reldo.taskstracker.TasksTrackerPlugin; +import net.reldo.taskstracker.data.task.TaskFromStruct; +import net.runelite.client.config.ConfigManager; + +@Slf4j +public class ParamDropdownFilter extends Filter +{ + private final String paramName; + private final String filterConfigKey; + private final ConfigManager configManager; + + public ParamDropdownFilter(ConfigManager configManager, String paramName, String filterConfigKey) + { + this.configManager = configManager; + this.paramName = paramName; + this.filterConfigKey = filterConfigKey; + } + + @Override + public boolean meetsCriteria(TaskFromStruct task) + { + String configValue = configManager.getConfiguration(TasksTrackerPlugin.CONFIG_GROUP_NAME, filterConfigKey); + boolean isEmptyFilterSelection = configValue == null || configValue.isEmpty() || configValue.equals("-1"); + if (isEmptyFilterSelection) + { + return true; + } + if (task.getIntParam(paramName) != null) + { + try + { + Integer parsedConfigValue = Integer.parseInt(configValue); + return parsedConfigValue.equals(task.getIntParam(paramName)); + } + catch (Exception ex) + { + log.warn("meetsCriteria error parsing config value for {}", configValue); + return true; + } + } + return true; + } +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/Colors.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/Colors.java new file mode 100644 index 00000000..8b51eb34 --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/Colors.java @@ -0,0 +1,10 @@ +package net.reldo.taskstracker.panel; + +import java.awt.Color; + +public class Colors { + public static Color QUALIFIED_TEXT_COLOR = new Color(34, 177, 77); + public static Color UNQUALIFIED_BACKGROUND_COLOR = new Color(70, 30, 0); + public static Color UNQUALIFIED_TEXT_COLOR = new Color(251, 93, 93); + public static Color COMPLETED_BACKGROUND_COLOR = new Color(0, 50, 0); +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/Icons.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/Icons.java new file mode 100644 index 00000000..c8e90a05 --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/Icons.java @@ -0,0 +1,41 @@ +package net.reldo.taskstracker.panel; + +import java.awt.image.BufferedImage; +import javax.swing.Icon; +import javax.swing.ImageIcon; +import net.reldo.taskstracker.TasksTrackerPlugin; +import net.runelite.client.util.ImageUtil; + +public class Icons +{ + private static final String completeBtnPath = "panel/components/complete_button/"; + public static final Icon INCOMPLETE_ONLY_ICON = new ImageIcon(ImageUtil.loadImageResource(TasksTrackerPlugin.class, completeBtnPath + "incomplete_only_icon.png")); + public static final Icon COMPLETE_ONLY_ICON = new ImageIcon(ImageUtil.loadImageResource(TasksTrackerPlugin.class, completeBtnPath + "complete_only_icon.png")); + public static final Icon COMPLETE_INCOMPLETE_ICON = new ImageIcon(ImageUtil.loadImageResource(TasksTrackerPlugin.class, completeBtnPath + "complete_and_incomplete_icon.png")); + + private static final String ignoredBtnPath = "panel/components/ignored_button/"; + public static final BufferedImage semivisibleimg = ImageUtil.loadImageResource(TasksTrackerPlugin.class, ignoredBtnPath + "semivisible_icon.png"); + public static final Icon SEMIVISIBLE_ICON = new ImageIcon(ImageUtil.alphaOffset(semivisibleimg, -180)); + public static final Icon INVISIBLE_ICON = new ImageIcon(ImageUtil.loadImageResource(TasksTrackerPlugin.class, ignoredBtnPath + "invisible_icon.png")); + public static final Icon VISIBLE_ICON = new ImageIcon(ImageUtil.loadImageResource(TasksTrackerPlugin.class, ignoredBtnPath + "visible_icon.png")); + + private static final String trackedBtnPath = "panel/components/tracked_button/"; + public static final Icon UNTRACKED_ONLY_ICON = new ImageIcon(ImageUtil.loadImageResource(TasksTrackerPlugin.class, trackedBtnPath + "untracked_icon.png")); + public static final Icon TRACKED_ONLY_ICON = new ImageIcon(ImageUtil.loadImageResource(TasksTrackerPlugin.class, trackedBtnPath + "tracked_icon.png")); + public static final Icon TRACKED_UNTRACKED_ICON = new ImageIcon(ImageUtil.loadImageResource(TasksTrackerPlugin.class, trackedBtnPath + "tracked_and_untracked_icon.png")); + + private static final String sortBtnPath = "panel/components/sort_button/"; + public static final Icon ASCENDING_ICON = new ImageIcon(ImageUtil.loadImageResource(TasksTrackerPlugin.class, sortBtnPath + "ascending_icon.png")); + public static final Icon DESCENDING_ICON = new ImageIcon(ImageUtil.loadImageResource(TasksTrackerPlugin.class, sortBtnPath + "descending_icon.png")); + + private static final String expandBtnPath = "panel/components/"; + public static final Icon MENU_EXPANDED_ICON = new ImageIcon(ImageUtil.loadImageResource(TasksTrackerPlugin.class, expandBtnPath + "filter_menu_expanded.png")); + public static final BufferedImage collapseImg = ImageUtil.loadImageResource(TasksTrackerPlugin.class, expandBtnPath + "filter_menu_collapsed.png"); + public static final Icon MENU_ICON_HOVER = new ImageIcon(collapseImg); + + public static final Icon MENU_COLLAPSED_ICON = new ImageIcon(ImageUtil.alphaOffset(collapseImg, -180)); + public static final ImageIcon PLUS_ICON = new ImageIcon(ImageUtil.loadImageResource(TasksTrackerPlugin.class, "plus.png")); + public static final ImageIcon MINUS_ICON = new ImageIcon(ImageUtil.loadImageResource(TasksTrackerPlugin.class, "minus.png")); + public static final ImageIcon EYE_ICON = new ImageIcon(ImageUtil.loadImageResource(TasksTrackerPlugin.class, "eye.png")); + public static final ImageIcon EYE_CROSS_GREY = new ImageIcon(ImageUtil.loadImageResource(TasksTrackerPlugin.class, "eye-cross-grey.png")); +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/LoggedInPanel.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/LoggedInPanel.java new file mode 100644 index 00000000..1d6abd20 --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/LoggedInPanel.java @@ -0,0 +1,543 @@ +package net.reldo.taskstracker.panel; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.ButtonGroup; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JMenuItem; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.JToggleButton; +import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; +import javax.swing.border.EmptyBorder; +import javax.swing.border.MatteBorder; +import javax.swing.plaf.basic.BasicButtonUI; +import lombok.extern.slf4j.Slf4j; +import net.reldo.taskstracker.TasksTrackerConfig; +import net.reldo.taskstracker.TasksTrackerPlugin; +import net.reldo.taskstracker.config.ConfigValues; +import net.reldo.taskstracker.data.jsondatastore.types.FilterConfig; +import net.reldo.taskstracker.data.jsondatastore.types.FilterType; +import net.reldo.taskstracker.data.task.TaskService; +import net.reldo.taskstracker.data.task.TaskType; +import net.reldo.taskstracker.panel.components.SearchBox; +import net.reldo.taskstracker.panel.components.TriToggleButton; +import net.reldo.taskstracker.panel.filters.ComboItem; +import net.runelite.client.ui.ColorScheme; +import net.runelite.client.ui.FontManager; +import net.runelite.client.ui.PluginPanel; +import net.runelite.client.util.SwingUtil; + +@Slf4j +public class LoggedInPanel extends JPanel +{ + public TaskListPanel taskListPanel; + private JComboBox> taskTypeDropdown; + + private final TaskService taskService; + private final TasksTrackerPlugin plugin; + private final TasksTrackerConfig config; + + // Filter buttons + private final TriToggleButton completedFilterBtn = new TriToggleButton(); + private final TriToggleButton trackedFilterBtn = new TriToggleButton(); + private final TriToggleButton ignoredFilterBtn = new TriToggleButton(); + private final JPanel titlePanel = new JPanel(); + + // Task list tabs + private final JPanel tabPane = new JPanel(); + + // sub-filter panel + private SubFilterPanel subFilterPanel; + private SortPanel sortPanel; + private final JToggleButton collapseBtn = new JToggleButton(); + + public LoggedInPanel(TasksTrackerPlugin plugin, TasksTrackerConfig config, TaskService taskService) + { + super(false); + this.plugin = plugin; + this.taskService = taskService; + this.config = config; + + createPanel(); + } + + @Override + public Dimension getPreferredSize() + { + return getParent().getSize(); + } + + public void redraw() + { + // taskTypeDropdown may become de-synced after profile change + String selectedTaskTypeJsonName = taskTypeDropdown.getItemAt(taskTypeDropdown.getSelectedIndex()).getValue().getTaskJsonName(); + if(!selectedTaskTypeJsonName.equals(config.taskTypeJsonName())) + { + log.debug("Task type dropdown de-synced, attempting to find current task type"); + for(int i = 0; i < taskTypeDropdown.getItemCount(); i++) + { + ComboItem item = taskTypeDropdown.getItemAt(i); + if(item.getValue().getTaskJsonName().equals(config.taskTypeJsonName())) + { + log.debug("Current task type found, setting selected task type"); + taskTypeDropdown.setSelectedIndex(i); + break; + } + } + } + subFilterPanel.redraw(); + sortPanel.redraw(); + updateCollapseButtonText(); + + taskListPanel.redraw(); + } + + public void refreshAllTasks() + { + updateCollapseButtonText(); + taskListPanel.refreshAllTasks(); + } + + private void createPanel() + { + setLayout(new BorderLayout()); + setBackground(ColorScheme.DARK_GRAY_COLOR); + + taskListPanel = new TaskListPanel(plugin, taskService); + + add(getNorthPanel(), BorderLayout.NORTH); + add(getCenterPanel(), BorderLayout.CENTER); + add(getSouthPanel(), BorderLayout.SOUTH); + + loadAndApplyFilters(config.taskListTab()); + if(config.taskListTab().equals(ConfigValues.TaskListTabs.TRACKED)) + { + trackedFilterBtn.setState(1); + trackedFilterBtn.setEnabled(false); + plugin.getConfigManager().setConfiguration(TasksTrackerPlugin.CONFIG_GROUP_NAME, "trackedFilter", ConfigValues.TrackedFilterValues.TRACKED); + } + } + + private JPanel getCenterPanel() { + // wrapper for the task list and tab buttons + final JPanel taskListPanel = new JPanel(new BorderLayout()); + taskListPanel.setBackground(ColorScheme.DARK_GRAY_COLOR); + taskListPanel.setBorder(new MatteBorder(0, 0, 1, 0, ColorScheme.MEDIUM_GRAY_COLOR)); + taskListPanel.setAlignmentX(JPanel.CENTER_ALIGNMENT); + + tabPane.setLayout(new BoxLayout(tabPane, BoxLayout.X_AXIS)); + tabPane.setBorder(new EmptyBorder(0,0,0,0)); + tabPane.setPreferredSize(new Dimension(PluginPanel.PANEL_WIDTH,24)); + + JToggleButton trackedTab = tabButton("Tracked Tasks", ConfigValues.TaskListTabs.TRACKED); + JToggleButton allTab = tabButton("All Tasks", ConfigValues.TaskListTabs.ALL); + JToggleButton customTab = tabButton("Custom", ConfigValues.TaskListTabs.CUSTOM); + + ButtonGroup tabGroup = new ButtonGroup(); + + tabGroup.add(trackedTab); + tabGroup.add(allTab); + tabGroup.add(customTab); + + tabPane.add(Box.createHorizontalGlue()); + tabPane.add(trackedTab); + tabPane.add(Box.createHorizontalGlue()); + tabPane.add(allTab); + tabPane.add(Box.createHorizontalGlue()); + tabPane.add(customTab); + tabPane.add(Box.createHorizontalGlue()); + + taskListPanel.add(tabPane, BorderLayout.NORTH); + taskListPanel.add(this.taskListPanel, BorderLayout.CENTER); + + // set initial filter states to "complete and incomplete", "tracked and untracked", "not ignored" + Map filterStates = new HashMap<>(); + filterStates.put("completed",0); + filterStates.put("tracked",0); + filterStates.put("ignored",0); + for(ConfigValues.TaskListTabs tab : ConfigValues.TaskListTabs.values()) + { + filterStore.put(tab, filterStates); + } + + switch (config.taskListTab()) + { + case TRACKED: + trackedTab.setSelected(true); + break; + case ALL: + allTab.setSelected(true); + break; + case CUSTOM: + customTab.setSelected(true); + break; + } + tabChanged(config.taskListTab()); + + return taskListPanel; + } + + public void tabChanged(ConfigValues.TaskListTabs newTab) + { + if(newTab != null) { + changeTab(newTab); + + switch (newTab) { + case TRACKED: + trackedFilterBtn.setState(1); + trackedFilterBtn.setEnabled(false); + plugin.getConfigManager().setConfiguration(TasksTrackerPlugin.CONFIG_GROUP_NAME, "taskListTab", newTab); + filterButtonAction("tracked"); + break; + case ALL: + trackedFilterBtn.setState(0); + trackedFilterBtn.setEnabled(false); + completedFilterBtn.setState(0); + completedFilterBtn.setEnabled(false); + ignoredFilterBtn.setState(1); + ignoredFilterBtn.setEnabled(false); + plugin.getConfigManager().setConfiguration(TasksTrackerPlugin.CONFIG_GROUP_NAME, "taskListTab", newTab); + actionAllFilterButtons(); + break; + case CUSTOM: + plugin.getConfigManager().setConfiguration(TasksTrackerPlugin.CONFIG_GROUP_NAME, "taskListTab", newTab); + plugin.refreshAllTasks(); + break; + default: + plugin.refreshAllTasks(); + break; + } + } + } + + private JToggleButton tabButton(String label, ConfigValues.TaskListTabs tab) + { + JToggleButton button = new JToggleButton(label); + + button.setBorder(new EmptyBorder(2,5,2,5)); + button.setBackground(ColorScheme.DARK_GRAY_COLOR); + button.setForeground(ColorScheme.LIGHT_GRAY_COLOR); + button.addActionListener(e -> tabChanged(tab)); + + return button; + } + + private void changeTab(ConfigValues.TaskListTabs newTab) + { + saveFilters(); + resetFilters(); + loadAndApplyFilters(newTab); + } + + private final Map> filterStore = new HashMap<>(); + + private void saveFilters() + { + ConfigValues.TaskListTabs tab = config.taskListTab(); + + Map filterStates = new HashMap<>(); + filterStates.put("completed", config.completedFilter().ordinal()); + filterStates.put("tracked", config.trackedFilter().ordinal()); + filterStates.put("ignored", config.ignoredFilter().ordinal()); + + filterStore.put(tab, filterStates); + } + + private void resetFilters() + { + completedFilterBtn.setEnabled(true); + trackedFilterBtn.setEnabled(true); + ignoredFilterBtn.setEnabled(true); + } + + private void loadAndApplyFilters(ConfigValues.TaskListTabs tab) + { + Map filterStates = filterStore.get(tab); + + if(filterStates == null) return; + + Enum configValue; + + completedFilterBtn.setState(filterStates.get("completed")); + trackedFilterBtn.setState(filterStates.get("tracked")); + ignoredFilterBtn.setState(filterStates.get("ignored")); + + configValue = ConfigValues.CompletedFilterValues.values()[completedFilterBtn.getState()]; + plugin.getConfigManager().setConfiguration(TasksTrackerPlugin.CONFIG_GROUP_NAME, "completedFilter", configValue); + + configValue = ConfigValues.TrackedFilterValues.values()[trackedFilterBtn.getState()]; + plugin.getConfigManager().setConfiguration(TasksTrackerPlugin.CONFIG_GROUP_NAME, "trackedFilter", configValue); + + configValue = ConfigValues.IgnoredFilterValues.values()[ignoredFilterBtn.getState()]; + plugin.getConfigManager().setConfiguration(TasksTrackerPlugin.CONFIG_GROUP_NAME, "ignoredFilter", configValue); + } + + private JPanel getSouthPanel() + { + JPanel southPanel = new JPanel(new BorderLayout()); + southPanel.setBorder(new EmptyBorder(5,0,2,0)); + + JButton importButton = new JButton("Import"); + importButton.setBorder(new EmptyBorder(5, 5, 5, 5)); + importButton.setLayout(new BorderLayout(0, PluginPanel.BORDER_OFFSET)); + importButton.addActionListener(e -> plugin.openImportJsonDialog()); + southPanel.add(importButton, BorderLayout.WEST); + + JButton totalsButton = new JButton("Totals"); + totalsButton.setBorder(new EmptyBorder(5, 5, 5, 5)); + totalsButton.setLayout(new BorderLayout(0, PluginPanel.BORDER_OFFSET)); + totalsButton.addActionListener(e -> plugin.sendTotalsToChat()); + southPanel.add(totalsButton, BorderLayout.CENTER); + + JButton exportButton = new JButton("Export"); + exportButton.setBorder(new EmptyBorder(5, 5, 5, 5)); + exportButton.setLayout(new BorderLayout(0, PluginPanel.BORDER_OFFSET)); + exportButton.addActionListener(e -> plugin.copyJsonToClipboard()); + southPanel.add(exportButton, BorderLayout.EAST); + + return southPanel; + } + + private JPanel getNorthPanel() + { + JPanel northPanel = new JPanel(); + BoxLayout layout = new BoxLayout(northPanel, BoxLayout.Y_AXIS); + northPanel.setLayout(layout); + northPanel.setBorder(new EmptyBorder(10, 10, 10, 10)); + + taskTypeDropdown = new JComboBox<>(); + taskTypeDropdown.setAlignmentX(LEFT_ALIGNMENT); + taskTypeDropdown.setFocusable(false); + initTaskTypeDropdownAsync(); + + // Wrapper for collapsible sub-filter menu + JPanel subFilterWrapper = new JPanel(); + subFilterWrapper.setLayout(new BorderLayout()); + subFilterWrapper.setBorder(new MatteBorder(1, 0, 1, 0, ColorScheme.MEDIUM_GRAY_COLOR)); + subFilterWrapper.setAlignmentX(LEFT_ALIGNMENT); + subFilterWrapper.setBackground(ColorScheme.DARKER_GRAY_COLOR); + + // collapse button + SwingUtil.removeButtonDecorations(collapseBtn); + collapseBtn.setIcon(Icons.MENU_COLLAPSED_ICON); + collapseBtn.setSelectedIcon(Icons.MENU_EXPANDED_ICON); + collapseBtn.setRolloverIcon(Icons.MENU_ICON_HOVER); + SwingUtil.addModalTooltip(collapseBtn, "Collapse filters", "Expand filters"); + collapseBtn.setBackground(ColorScheme.DARKER_GRAY_COLOR); + collapseBtn.setAlignmentX(LEFT_ALIGNMENT); + collapseBtn.setUI(new BasicButtonUI()); // substance breaks the layout + collapseBtn.addActionListener(ev -> subFilterPanel.setVisible(!subFilterPanel.isVisible())); + collapseBtn.setHorizontalTextPosition(JButton.CENTER); + collapseBtn.setForeground(ColorScheme.LIGHT_GRAY_COLOR); + collapseBtn.setFont(FontManager.getRunescapeSmallFont()); + collapseBtn.setBorder(new EmptyBorder(2, 0, 2, 0)); + collapseBtn.setFocusable(false); + + // panel to hold sub-filters + subFilterPanel = new SubFilterPanel(plugin, taskService); + + subFilterWrapper.add(collapseBtn, BorderLayout.NORTH); + subFilterWrapper.add(subFilterPanel, BorderLayout.CENTER); + + sortPanel = new SortPanel(plugin, taskService, taskListPanel); + + northPanel.add(getTitleAndButtonPanel()); + northPanel.add(Box.createVerticalStrut(10)); + northPanel.add(taskTypeDropdown); + northPanel.add(Box.createVerticalStrut(2)); + northPanel.add(getSearchPanel()); + northPanel.add(Box.createVerticalStrut(2)); + northPanel.add(sortPanel); + northPanel.add(Box.createVerticalStrut(2)); + northPanel.add(subFilterWrapper); + + return northPanel; + } + + private JPanel getTitleAndButtonPanel() + { + titlePanel.setLayout(new BorderLayout()); + titlePanel.setBackground(ColorScheme.DARK_GRAY_COLOR); + titlePanel.setPreferredSize(new Dimension(0, 30)); + titlePanel.setBorder(new EmptyBorder(5, 5, 5, 10)); + + JLabel title = new JLabel("Tasks Tracker"); + title.setHorizontalAlignment(SwingConstants.LEFT); + title.setForeground(Color.WHITE); + + JPopupMenu reloadPluginPopup = new JPopupMenu(); + reloadPluginPopup.setBorder(new EmptyBorder(5, 5, 5, 5)); + JMenuItem reloadPluginMenuItem = new JMenuItem("Reload plugin"); + reloadPluginMenuItem.addActionListener(e -> plugin.reloadTaskType()); + reloadPluginPopup.add(reloadPluginMenuItem); + title.setComponentPopupMenu(reloadPluginPopup); + + // Filter button bar + final JPanel viewControls = new JPanel(); + viewControls.setLayout(new BoxLayout(viewControls, BoxLayout.X_AXIS)); + viewControls.setBackground(ColorScheme.DARK_GRAY_COLOR); + + // Completed tasks filter button + SwingUtil.removeButtonDecorations(completedFilterBtn); + completedFilterBtn.setIcons(Icons.COMPLETE_INCOMPLETE_ICON, Icons.COMPLETE_ONLY_ICON, Icons.INCOMPLETE_ONLY_ICON); + completedFilterBtn.setToolTips("All tasks", "Completed tasks only", "Incomplete tasks only"); + completedFilterBtn.setBackground(ColorScheme.DARK_GRAY_COLOR); + completedFilterBtn.setStateChangedAction(e -> filterButtonAction("completed")); + completedFilterBtn.popupMenuEnabled(true); + completedFilterBtn.setState(config.completedFilter().ordinal()); + + viewControls.add(completedFilterBtn); + + // Tracked tasks filter button + SwingUtil.removeButtonDecorations(trackedFilterBtn); + trackedFilterBtn.setIcons(Icons.TRACKED_UNTRACKED_ICON, Icons.TRACKED_ONLY_ICON, Icons.UNTRACKED_ONLY_ICON); + trackedFilterBtn.setToolTips("All tasks", "Tracked tasks only", "Untracked tasks only"); + trackedFilterBtn.setBackground(ColorScheme.DARK_GRAY_COLOR); + trackedFilterBtn.setStateChangedAction(e -> filterButtonAction("tracked")); + trackedFilterBtn.popupMenuEnabled(true); + trackedFilterBtn.setState(config.trackedFilter().ordinal()); + + viewControls.add(trackedFilterBtn); + + // Ignored tasks filter button + SwingUtil.removeButtonDecorations(ignoredFilterBtn); + ignoredFilterBtn.setIcons(Icons.SEMIVISIBLE_ICON, Icons.VISIBLE_ICON, Icons.INVISIBLE_ICON); + ignoredFilterBtn.setToolTips("Hide ignored tasks", "All tasks", "Ignored tasks only"); + ignoredFilterBtn.setBackground(ColorScheme.DARK_GRAY_COLOR); + ignoredFilterBtn.setStateChangedAction(e -> filterButtonAction("ignored")); + ignoredFilterBtn.popupMenuEnabled(true); + ignoredFilterBtn.setState(config.ignoredFilter().ordinal()); + + viewControls.add(ignoredFilterBtn); + + titlePanel.add(viewControls, BorderLayout.EAST); + titlePanel.add(title, BorderLayout.WEST); + titlePanel.setAlignmentX(LEFT_ALIGNMENT); + + return titlePanel; + } + + private void filterButtonActionNoRefresh(String filter) + { + int state; + Enum configValue; + + switch (filter) + { + case "completed": + state = completedFilterBtn.getState(); + configValue = ConfigValues.CompletedFilterValues.values()[state]; + break; + case "tracked": + state = trackedFilterBtn.getState(); + configValue = ConfigValues.TrackedFilterValues.values()[state]; + break; + case "ignored": + state = ignoredFilterBtn.getState(); + configValue = ConfigValues.IgnoredFilterValues.values()[state]; + break; + default: + log.debug("Filter button action failed due to unrecognised filter."); + return; + } + + plugin.getConfigManager().setConfiguration(TasksTrackerPlugin.CONFIG_GROUP_NAME, filter + "Filter", configValue); + } + + private void filterButtonAction(String filter) + { + filterButtonActionNoRefresh(filter); + plugin.refreshAllTasks(); + } + + private void actionAllFilterButtons() + { + filterButtonActionNoRefresh("tracked"); + filterButtonActionNoRefresh("ignored"); + filterButtonActionNoRefresh("completed"); + plugin.refreshAllTasks(); + } + + private JPanel getSearchPanel() + { + JPanel filtersPanel = new JPanel(); + filtersPanel.setAlignmentX(LEFT_ALIGNMENT); + filtersPanel.setLayout(new BoxLayout(filtersPanel, BoxLayout.Y_AXIS)); + + SearchBox textSearch = new SearchBox(); + textSearch.addTextChangedListener(() -> { + plugin.taskTextFilter = textSearch.getText().toLowerCase(); + plugin.refreshAllTasks(); + }); + + filtersPanel.add(textSearch); + + return filtersPanel; + } + + private void updateCollapseButtonText() + { + if (taskService.getCurrentTaskType() == null) return; + + ArrayList filters = taskService.getCurrentTaskType().getFilters(); + + int countInclusive = 0; + int countExclusive = 0; + + for (FilterConfig filterConfig : filters) + { + String filterText = Optional.ofNullable(plugin.getConfigManager() + .getConfiguration(TasksTrackerPlugin.CONFIG_GROUP_NAME, + taskService.getCurrentTaskType().getFilterConfigPrefix() + filterConfig.getConfigKey())) + .orElse(""); + + int count = (filterText.isEmpty() || filterText.equals("-1")) ? 0 : filterText.split(",").length; + + if (filterConfig.getFilterType().equals(FilterType.BUTTON_FILTER)) countInclusive += count; + if (filterConfig.getFilterType().equals(FilterType.DROPDOWN_FILTER)) countExclusive += count; + } + + collapseBtn.setText(countInclusive + " inclusive, " + countExclusive + " exclusive filters"); + } + + private void initTaskTypeDropdownAsync() { + TaskType currentTaskType = taskService.getCurrentTaskType(); + taskService.getTaskTypesByJsonName().thenAccept(taskTypes -> { + ArrayList> taskTypeItems = new ArrayList<>(); + taskTypes.forEach((taskTypeJsonName, taskType) -> { + ComboItem item = new ComboItem<>(taskType, taskType.getName()); + taskTypeItems.add(item); + taskTypeDropdown.addItem(item); + }); + + ComboItem currentTaskTypeComboItem = taskTypeItems.stream() + .filter(item -> item.getValue().equals(currentTaskType)) + .findFirst().orElseGet(() -> taskTypeItems.get(0)); + taskTypeDropdown.setSelectedItem(currentTaskTypeComboItem); + taskTypeDropdown.addActionListener(e -> { + TaskType taskType = taskTypeDropdown.getItemAt(taskTypeDropdown.getSelectedIndex()).getValue(); + taskService.setTaskType(taskType).thenAccept(wasTaskTypeChanged ->{ + if (wasTaskTypeChanged) { + SwingUtilities.invokeLater(() -> + { + redraw(); + plugin.refreshAllTasks(); + }); + } + }); + }); + }); + } +} \ No newline at end of file diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/LoggedOutPanel.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/LoggedOutPanel.java new file mode 100644 index 00000000..16331bc4 --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/LoggedOutPanel.java @@ -0,0 +1,12 @@ +package net.reldo.taskstracker.panel; + +import javax.swing.JLabel; +import javax.swing.JPanel; + +public class LoggedOutPanel extends JPanel +{ + public LoggedOutPanel() + { + this.add(new JLabel("Log into an account to track tasks.")); + } +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/SortPanel.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/SortPanel.java new file mode 100644 index 00000000..dec703eb --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/SortPanel.java @@ -0,0 +1,85 @@ +package net.reldo.taskstracker.panel; + +import java.util.List; +import java.util.stream.Collectors; +import javax.swing.BoxLayout; +import javax.swing.Icon; +import javax.swing.JComboBox; +import javax.swing.SwingUtilities; +import lombok.extern.slf4j.Slf4j; +import net.reldo.taskstracker.TasksTrackerPlugin; +import net.reldo.taskstracker.config.ConfigValues; +import net.reldo.taskstracker.data.task.TaskService; +import net.reldo.taskstracker.panel.components.FixedWidthPanel; +import net.reldo.taskstracker.panel.components.MultiToggleButton; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.ui.ColorScheme; +import net.runelite.client.util.SwingUtil; + +@Slf4j +public class SortPanel extends FixedWidthPanel +{ + + private final TasksTrackerPlugin plugin; + private final TaskService taskService; + private final TaskListPanel taskListPanel; + private final ConfigManager configManager; + private JComboBox sortDropdown; + private MultiToggleButton directionButton; + + public SortPanel(TasksTrackerPlugin plugin, TaskService taskService, TaskListPanel taskListPanel) + { + this.plugin = plugin; + this.configManager = plugin.getConfigManager(); + this.taskService = taskService; + this.taskListPanel = taskListPanel; + + setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); + setAlignmentX(LEFT_ALIGNMENT); + } + + public void redraw() + { + removeAll(); + + List criteriaList = taskService.getSortedIndexes().keySet().stream() + .sorted() + .map((str) -> str.substring(0, 1).toUpperCase() + str.substring(1)) + .collect(Collectors.toList()); + criteriaList.add(0,"Default"); + + String[] criteriaArray = criteriaList.toArray(new String[0]); + sortDropdown = new JComboBox<>(criteriaArray); + sortDropdown.setAlignmentX(LEFT_ALIGNMENT); + sortDropdown.setSelectedIndex(0); + sortDropdown.addActionListener(e -> { + updateConfig(); + SwingUtilities.invokeLater(taskListPanel::redraw); + }); + sortDropdown.setFocusable(false); + + directionButton = new MultiToggleButton(2); + SwingUtil.removeButtonDecorations(directionButton); + directionButton.setIcons(new Icon[]{Icons.ASCENDING_ICON, Icons.DESCENDING_ICON}); + directionButton.setToolTips(new String[]{"Ascending", "Descending"}); + directionButton.setBackground(ColorScheme.DARK_GRAY_COLOR); + directionButton.setStateChangedAction(e -> { + updateConfig(); + SwingUtilities.invokeLater(taskListPanel::redraw); + }); + + add(sortDropdown); + add(directionButton); + updateConfig(); + } + + protected void updateConfig() + { + log.debug("updateConfig {}, {}, {}", TasksTrackerPlugin.CONFIG_GROUP_NAME, "sortCriteria", sortDropdown.getItemAt(sortDropdown.getSelectedIndex()).toLowerCase()); + configManager.setConfiguration(TasksTrackerPlugin.CONFIG_GROUP_NAME, "sortCriteria", sortDropdown.getItemAt(sortDropdown.getSelectedIndex()).toLowerCase()); + + ConfigValues.SortDirections configValue = ConfigValues.SortDirections.values()[directionButton.getState()]; + log.debug("updateConfig {}, {}, {}", TasksTrackerPlugin.CONFIG_GROUP_NAME, "sortDirection", configValue); + configManager.setConfiguration(TasksTrackerPlugin.CONFIG_GROUP_NAME, "sortDirection", configValue); + } +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/SubFilterPanel.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/SubFilterPanel.java new file mode 100644 index 00000000..16b69261 --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/SubFilterPanel.java @@ -0,0 +1,122 @@ +package net.reldo.taskstracker.panel; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import javax.swing.BoxLayout; +import javax.swing.border.EmptyBorder; +import lombok.extern.slf4j.Slf4j; +import net.reldo.taskstracker.TasksTrackerPlugin; +import net.reldo.taskstracker.data.jsondatastore.types.FilterConfig; +import net.reldo.taskstracker.data.jsondatastore.types.FilterValueType; +import net.reldo.taskstracker.data.task.TaskService; +import net.reldo.taskstracker.panel.components.FixedWidthPanel; +import net.reldo.taskstracker.panel.filters.ComboItem; +import net.reldo.taskstracker.panel.filters.DynamicButtonFilterPanel; +import net.reldo.taskstracker.panel.filters.DynamicDropdownFilterPanel; +import net.reldo.taskstracker.panel.filters.FilterPanel; +import net.runelite.client.ui.ColorScheme; + +@Slf4j +public class SubFilterPanel extends FixedWidthPanel +{ + private final List filterPanels = new ArrayList<>(); + private TasksTrackerPlugin plugin; + private TaskService taskService; + + public SubFilterPanel(TasksTrackerPlugin plugin, TaskService taskService) + { + this.plugin = plugin; + this.taskService = taskService; + log.debug("SubFilterPanel.constructor"); + setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + setBorder(new EmptyBorder(0, 0, 0, 0)); + setBackground(ColorScheme.DARKER_GRAY_COLOR); + setVisible(false); + } + + public void redraw() + { + log.debug("SubFilterPanel.redraw"); // TODO: figure out why this calls multiple times upon switching task type + + removeAll(); + + filterPanels.clear(); + filterPanels.addAll(getFilterPanels(taskService.getCurrentTaskType().getFilters())); + filterPanels.forEach((filterPanel) -> { + add(filterPanel); + filterPanel.redraw(); + }); + } + + private List getFilterPanels(ArrayList filterConfigs) + { + List filterPanels = new ArrayList<>(); + for (FilterConfig filterConfig : filterConfigs) + { + try + { + FilterPanel filterPanel = createDynamicFilterPanel(filterConfig); + if (filterPanel == null) + { + continue; + } + filterPanels.add(filterPanel); + } + catch (Exception ex) + { + log.error("error creating filter panel {} {}", filterConfig.getConfigKey(), ex); + } + } + return filterPanels; + } + + private FilterPanel createDynamicFilterPanel(FilterConfig filterConfig) throws Exception + { + switch (filterConfig.getFilterType()) + { + case BUTTON_FILTER: + return new DynamicButtonFilterPanel(plugin, filterConfig, taskService.getCurrentTaskType()); + case DROPDOWN_FILTER: + ComboItem[] dropdownItems = getDropdownItems(filterConfig); + return new DynamicDropdownFilterPanel(plugin, filterConfig, taskService.getCurrentTaskType(), dropdownItems); + default: + log.error("invalid filter type " + filterConfig.getFilterType()); + return null; + } + } + + private ComboItem[] getDropdownItems(FilterConfig filterConfig) throws ExecutionException, InterruptedException + { + if (filterConfig.getValueType() == null) + { + throw new Error("invalid filterConfig for dropdown items"); + } + if (filterConfig.getValueType().equals(FilterValueType.PARAM_INTEGER)) + { + String enumName = filterConfig.getOptionLabelEnum(); + if (!enumName.isEmpty()) + { + HashMap enumEntries = taskService.getStringEnumValuesAsync(enumName).get(); // TODO: blocking call + ArrayList> options = new ArrayList<>(); + options.add(new ComboItem<>(-1, "")); + for (Map.Entry entry : enumEntries.entrySet()) + { + if (filterConfig.getValueName().equals("tier")) + { + if (entry.getValue().equals("All") || entry.getValue().equals("Tier")) + { + continue; + } + } + options.add(new ComboItem<>(entry.getKey(), entry.getValue())); + } + return options.toArray(new ComboItem[0]); + } + } + + return new ComboItem[0]; + } +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/TaskListPanel.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/TaskListPanel.java new file mode 100644 index 00000000..0fffbd9d --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/TaskListPanel.java @@ -0,0 +1,202 @@ +package net.reldo.taskstracker.panel; + +import java.awt.Component; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import javax.swing.BoxLayout; +import javax.swing.JLabel; +import javax.swing.JScrollPane; +import javax.swing.ScrollPaneConstants; +import javax.swing.SwingUtilities; +import javax.swing.border.EmptyBorder; +import lombok.extern.slf4j.Slf4j; +import net.reldo.taskstracker.TasksTrackerPlugin; +import net.reldo.taskstracker.config.ConfigValues; +import net.reldo.taskstracker.data.jsondatastore.types.TaskDefinitionSkill; +import net.reldo.taskstracker.data.task.TaskFromStruct; +import net.reldo.taskstracker.data.task.TaskService; +import net.reldo.taskstracker.panel.components.FixedWidthPanel; +import net.runelite.api.Skill; +import net.runelite.client.ui.FontManager; + +@Slf4j +public class TaskListPanel extends JScrollPane +{ + public TasksTrackerPlugin plugin; + private final HashMap taskPanelsByStructId = new HashMap<>(); + private final TaskListListPanel taskList; + private final TaskService taskService; + private final JLabel emptyTasks = new JLabel(); + + public TaskListPanel(TasksTrackerPlugin plugin, TaskService taskService) + { + this.plugin = plugin; + + taskList = new TaskListListPanel(plugin); + this.taskService = taskService; + + setViewportView(taskList); + setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + } + + public String getEmptyTaskListMessage() + { + return "No tasks match the current filters."; + } + + public void redraw() + { + taskList.redraw(); + } + + public void refreshAllTasks() + { + log.debug("TaskListPanel.refreshAllTasks"); + if (!SwingUtilities.isEventDispatchThread()) + { + log.error("Task list panel refresh failed - not event dispatch thread."); + return; + } + for (TaskPanel taskPanel : taskPanelsByStructId.values()) + { + taskPanel.refresh(); + } + } + + public void refreshMultipleTasks(Collection tasks) + { + log.debug("TaskListPanel.refreshMultipleTasks {}", tasks.size()); + if (!SwingUtilities.isEventDispatchThread()) + { + log.error("Task list panel refresh failed - not event dispatch thread."); + return; + } + for (TaskFromStruct task : tasks) + { + refresh(task); + } + } + + public void refreshTask(TaskFromStruct task) + { + log.debug("TaskListPanel.refreshMultipleTasks {}", task.getName()); + refresh(task); + } + + private void refresh(TaskFromStruct task) + { + if (!SwingUtilities.isEventDispatchThread()) + { + log.error("Task list panel refresh failed - not event dispatch thread."); + return; + } + if (task == null) + { + log.debug("Attempted to refresh null task"); + return; + } + + emptyTasks.setVisible(false); + + TaskPanel panel = taskPanelsByStructId.get(task.getStructId()); + if (panel != null) + { + panel.refresh(); + } + + boolean isAnyTaskPanelVisible = taskPanelsByStructId.values().stream() + .anyMatch(TaskPanel::isVisible); + + if (!isAnyTaskPanelVisible) + { + emptyTasks.setVisible(true); + } + } + + public void refreshTaskPanelsWithSkill(Skill skill) + { + // Refresh all task panels for tasks with 'skill' or + // 'SKILLS' (any skill) or 'TOTAL LEVEL' as a requirement. + taskPanelsByStructId.values().stream() + .filter(tp -> + { + List skillsList = tp.task.getTaskDefinition().getSkills(); + if (skillsList == null || skillsList.isEmpty()) + { + return false; + } + + return skillsList.stream() + .map(TaskDefinitionSkill::getSkill) + .anyMatch(s -> s.equalsIgnoreCase(skill.getName()) || + s.equalsIgnoreCase("SKILLS") || + s.equalsIgnoreCase("TOTAL LEVEL") + ); + }) + .forEach(TaskPanel::refresh); + } + + private class TaskListListPanel extends FixedWidthPanel + { + private final TasksTrackerPlugin plugin; + + public TaskListListPanel(TasksTrackerPlugin plugin) + { + this.plugin = plugin; + + setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + setBorder(new EmptyBorder(0, 10, 10, 10)); + setAlignmentX(Component.LEFT_ALIGNMENT); + + emptyTasks.setBorder(new EmptyBorder(10,0,10,0)); + emptyTasks.setText("
" + getEmptyTaskListMessage() + "
"); + emptyTasks.setFont(FontManager.getRunescapeSmallFont()); + emptyTasks.setHorizontalAlignment(JLabel.CENTER); + emptyTasks.setVerticalAlignment(JLabel.CENTER); + add(emptyTasks); + emptyTasks.setVisible(false); + } + + public void redraw() + { + log.debug("TaskListPanel.redraw"); + if(SwingUtilities.isEventDispatchThread()) + { + removeAll(); + taskPanelsByStructId.clear(); + add(emptyTasks); + emptyTasks.setVisible(false); + + log.debug("TaskListPanel creating panels"); + List tasks = taskService.getTasks(); + if (tasks == null || tasks.isEmpty()) + { + emptyTasks.setVisible(true); + return; + } + + for (int indexPosition = 0; indexPosition < tasks.size(); indexPosition++) + { + int adjustedIndexPosition = indexPosition; + if (plugin.getConfig().sortDirection().equals(ConfigValues.SortDirections.DESCENDING)) + { + adjustedIndexPosition = tasks.size() - (adjustedIndexPosition + 1); + } + TaskFromStruct task = tasks.get(taskService.getSortedTaskIndex(plugin.getConfig().sortCriteria(), adjustedIndexPosition)); + TaskPanel taskPanel = new TaskPanel(plugin, task); + add(taskPanel); + taskPanelsByStructId.put(task.getStructId(), taskPanel); + } + + log.debug("TaskListPanel validate and repaint"); + validate(); + repaint(); + } + else + { + log.error("Task list panel redraw failed - not event dispatch thread."); + } + } + } +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/TaskPanel.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/TaskPanel.java new file mode 100644 index 00000000..b17fda6c --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/TaskPanel.java @@ -0,0 +1,429 @@ +package net.reldo.taskstracker.panel; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Desktop; +import java.awt.Dimension; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import javax.swing.BoxLayout; +import javax.swing.ImageIcon; +import javax.swing.JLabel; +import javax.swing.JMenuItem; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.JToggleButton; +import javax.swing.JToolTip; +import javax.swing.ToolTipManager; +import javax.swing.border.EmptyBorder; +import lombok.extern.slf4j.Slf4j; +import net.reldo.taskstracker.HtmlUtil; +import net.reldo.taskstracker.TasksTrackerConfig; +import net.reldo.taskstracker.TasksTrackerPlugin; +import net.reldo.taskstracker.config.ConfigValues.CompletedFilterValues; +import net.reldo.taskstracker.config.ConfigValues.IgnoredFilterValues; +import net.reldo.taskstracker.config.ConfigValues.TrackedFilterValues; +import net.reldo.taskstracker.data.jsondatastore.types.FilterType; +import net.reldo.taskstracker.data.jsondatastore.types.TaskDefinitionSkill; +import net.reldo.taskstracker.data.task.TaskFromStruct; +import net.reldo.taskstracker.data.task.filters.Filter; +import net.reldo.taskstracker.data.task.filters.ParamButtonFilter; +import net.reldo.taskstracker.data.task.filters.ParamDropdownFilter; +import net.runelite.api.Constants; +import net.runelite.api.Skill; +import net.runelite.client.game.SkillIconManager; +import net.runelite.client.ui.ColorScheme; +import net.runelite.client.ui.FontManager; +import net.runelite.client.ui.PluginPanel; +import net.runelite.client.util.SwingUtil; + +@Slf4j +public class TaskPanel extends JPanel +{ + public final TaskFromStruct task; + + private final JLabel tierIcon = new JLabel(); + private final JPanel container = new JPanel(new BorderLayout()); + private final JPanel body = new JPanel(new BorderLayout()); + private final JLabel name = new JLabel("task"); + private final JLabel description = new JLabel("description"); + private final JPanel buttons = new JPanel(); + private final JToggleButton toggleTrack = new JToggleButton(); + private final JToggleButton toggleIgnore = new JToggleButton(); + + protected final ArrayList filters = new ArrayList<>(); + + protected TasksTrackerPlugin plugin; + + public TaskPanel(TasksTrackerPlugin plugin, TaskFromStruct task) + { + super(new BorderLayout()); + this.plugin = plugin; + this.task = task; + createPanel(); + setComponentPopupMenu(getPopupMenu()); + ToolTipManager.sharedInstance().registerComponent(this); + + task.getTaskType().getFilters().forEach((filterConfig) -> { + String paramName = filterConfig.getValueName(); + if (filterConfig.getFilterType().equals(FilterType.BUTTON_FILTER)) + { + Filter filter = new ParamButtonFilter(plugin.getConfigManager(), paramName, task.getTaskType().getTaskJsonName() + "." + filterConfig.getConfigKey()); + filters.add(filter); + } + else if (filterConfig.getFilterType().equals(FilterType.DROPDOWN_FILTER)) + { + Filter filter = new ParamDropdownFilter(plugin.getConfigManager(), paramName, task.getTaskType().getTaskJsonName() + "." + filterConfig.getConfigKey()); + filters.add(filter); + } + }); + + refresh(); + } + + public JPopupMenu getPopupMenu() + { + return null; + } + + public String getTaskTooltip() + { + StringBuilder tooltipText = new StringBuilder(); + tooltipText.append(HtmlUtil.wrapWithBold(task.getName())).append(HtmlUtil.HTML_LINE_BREAK); + tooltipText.append(task.getDescription()).append(HtmlUtil.HTML_LINE_BREAK); + + String skillSection = getSkillSectionHtml(); + if (skillSection != null) + { + tooltipText.append(skillSection).append(HtmlUtil.HTML_LINE_BREAK); + } + + String wikiNotes = task.getTaskDefinition().getWikiNotes(); + if (wikiNotes != null) + { + tooltipText.append(HtmlUtil.HTML_LINE_BREAK).append(wikiNotes).append(HtmlUtil.HTML_LINE_BREAK); + } + + if (task.isCompleted()) + { + tooltipText.append(HtmlUtil.HTML_LINE_BREAK); + String datePattern = "MM-dd-yyyy hh:mma"; + SimpleDateFormat simpleDateFormat = new SimpleDateFormat(datePattern); + tooltipText.append("✔ ").append(simpleDateFormat.format(new Date(task.getCompletedOn()))); + } + + Float completionPercent = task.getTaskDefinition().getCompletionPercent(); + if (completionPercent != null) + { + tooltipText.append(HtmlUtil.HTML_LINE_BREAK).append("Players Completed: ").append(completionPercent).append('%'); + } + + return HtmlUtil.wrapWithHtml( + HtmlUtil.wrapWithWrappingParagraph(tooltipText.toString(), 200) + ); + } + + public Color getTaskBackgroundColor() + { + if (plugin.playerSkills == null) + { + return ColorScheme.DARKER_GRAY_COLOR; + } + + if (task.isCompleted()) + { + return Colors.COMPLETED_BACKGROUND_COLOR; + } + + if (task.getTaskDefinition().getSkills() == null || task.getTaskDefinition().getSkills().size() == 0) + { + return ColorScheme.DARKER_GRAY_COLOR; + } + + for (TaskDefinitionSkill requiredSkill : task.getTaskDefinition().getSkills()) + { + Skill skill; + String requiredSkillName = requiredSkill.getSkill().toUpperCase(); + try + { + skill = Skill.valueOf(requiredSkillName); + } + catch (IllegalArgumentException ex) + { + log.error("invalid skill name " + requiredSkillName); + continue; + } + + if (plugin.playerSkills[skill.ordinal()] < requiredSkill.getLevel()) + { + return Colors.UNQUALIFIED_BACKGROUND_COLOR; + } + } + + return ColorScheme.DARKER_GRAY_COLOR; + } + + public void createPanel() + { + setLayout(new BorderLayout()); + setBorder(new EmptyBorder(0, 0, 7, 0)); + + container.setBorder(new EmptyBorder(7, 7, 6, 0)); + + // Body + + name.setFont(FontManager.getRunescapeSmallFont()); + name.setForeground(Color.WHITE); + body.add(name, BorderLayout.NORTH); + + description.setFont(FontManager.getRunescapeSmallFont()); + description.setForeground(Color.GRAY); + body.add(description, BorderLayout.CENTER); + + // Buttons + buttons.setLayout(new BoxLayout(buttons, BoxLayout.Y_AXIS)); + buttons.setBorder(new EmptyBorder(0, 0, 0, 7)); + + toggleTrack.setPreferredSize(new Dimension(8, 8)); + toggleTrack.setIcon(Icons.PLUS_ICON); + toggleTrack.setSelectedIcon(Icons.MINUS_ICON); + toggleTrack.setBorder(new EmptyBorder(5, 0, 5, 0)); + toggleTrack.addActionListener(e -> { + task.setTracked(toggleTrack.isSelected()); + plugin.pluginPanel.taskListPanel.refreshTask(task); + plugin.saveCurrentTaskTypeData(); + }); + SwingUtil.removeButtonDecorations(toggleTrack); + + toggleIgnore.setPreferredSize(new Dimension(8, 8)); + toggleIgnore.setIcon(Icons.EYE_CROSS_GREY); + toggleIgnore.setSelectedIcon(Icons.EYE_ICON); + SwingUtil.addModalTooltip(toggleIgnore, "Unignore", "Ignore"); + toggleIgnore.setBorder(new EmptyBorder(5, 0, 5, 0)); + toggleIgnore.addActionListener(e -> { + task.setIgnored(!task.isIgnored()); + plugin.pluginPanel.taskListPanel.refreshTask(task); + plugin.saveCurrentTaskTypeData(); + }); + SwingUtil.removeButtonDecorations(toggleIgnore); + + buttons.add(toggleTrack); + buttons.add(toggleIgnore); + + // Full + container.add(tierIcon, BorderLayout.WEST); + container.add(body, BorderLayout.CENTER); + container.add(buttons, BorderLayout.EAST); + + BufferedImage tierSprite = task.getTaskType().getTierSprites().get(task.getTier()); + if (tierSprite != null) + { + tierIcon.setMinimumSize(new Dimension(Constants.ITEM_SPRITE_WIDTH, Constants.ITEM_SPRITE_HEIGHT)); + tierIcon.setIcon(new ImageIcon(tierSprite)); + tierIcon.setBorder(new EmptyBorder(0, 0, 0, 5)); + } + else + { + tierIcon.setBorder(new EmptyBorder(0, 0, 0, 0)); + } + + add(container, BorderLayout.NORTH); + + addMouseListener(new MouseAdapter() + { + @Override + public void mouseReleased(MouseEvent e) + { + if (e.isPopupTrigger()) + { + JPopupMenu menu = createWikiPopupMenu(); + menu.show(e.getComponent(), e.getX(), e.getY()); + } + } + }); + } + + public JPopupMenu createWikiPopupMenu() + { + JPopupMenu popupMenu = new JPopupMenu(); + JMenuItem openWikiItem = new JMenuItem("Wiki"); + openWikiItem.addActionListener(e -> openRuneScapeWiki()); + popupMenu.add(openWikiItem); + return popupMenu; + } + + private void openRuneScapeWiki() + { + String wikiUrl = String.format("https://oldschool.runescape.wiki/%s", URLEncoder.encode(task.getName().replace(' ', '_'), StandardCharsets.UTF_8)); + if (Desktop.isDesktopSupported()) + { + try + { + Desktop.getDesktop().browse(new URI(wikiUrl)); + } + catch (IOException | URISyntaxException ex) + { + ex.printStackTrace(); + } + } + else + { + log.warn("Desktop browsing is not supported on this system."); + } + } + + public void refresh() + { + setBackgroundColor(getTaskBackgroundColor()); + name.setText(HtmlUtil.wrapWithHtml(task.getName())); + description.setText(HtmlUtil.wrapWithHtml(task.getDescription())); + toggleTrack.setSelected(task.isTracked()); + toggleIgnore.setSelected(task.isIgnored()); + + setVisible(meetsFilterCriteria()); + + revalidate(); + } + + protected boolean meetsFilterCriteria() + { + String nameLowercase = task.getName().toLowerCase(); + String descriptionLowercase = task.getDescription().toLowerCase(); + if (plugin.taskTextFilter != null && + !nameLowercase.contains(plugin.taskTextFilter) && + !descriptionLowercase.contains(plugin.taskTextFilter)) + { + return false; + } + + TasksTrackerConfig config = plugin.getConfig(); + + for (Filter filter : filters) + { + if (!filter.meetsCriteria(task)) + { + return false; + } + } + + if (config.completedFilter().equals(CompletedFilterValues.INCOMPLETE) && task.isCompleted()) + { + return false; + } + if (config.completedFilter().equals(CompletedFilterValues.COMPLETE) && !task.isCompleted()) + { + return false; + } + + if (config.ignoredFilter().equals(IgnoredFilterValues.NOT_IGNORED) && task.isIgnored()) + { + return false; + } + if (config.ignoredFilter().equals(IgnoredFilterValues.IGNORED) && !task.isIgnored()) + { + return false; + } + + if (config.trackedFilter().equals(TrackedFilterValues.UNTRACKED) && task.isTracked()) + { + return false; + } + return !config.trackedFilter().equals(TrackedFilterValues.TRACKED) || task.isTracked(); + } + + private void setBackgroundColor(Color color) + { + container.setBackground(color); + body.setBackground(color); + buttons.setBackground(color); + } + + @Override + public Dimension getMaximumSize() + { + return new Dimension(PluginPanel.PANEL_WIDTH, getPreferredSize().height); + } + + @Override + public JToolTip createToolTip() + { + JToolTip customTooltip = new JToolTip(); + customTooltip.setFont(FontManager.getRunescapeSmallFont()); + return customTooltip; + } + + @Override + public String getToolTipText(MouseEvent mouseEvent) + { + return getTaskTooltip(); + } + + private String getSkillSectionHtml() + { + List requiredSkills = task.getTaskDefinition().getSkills(); + if (requiredSkills == null) + { + return null; + } + StringBuilder skillSection = new StringBuilder(); + skillSection.append(HtmlUtil.HTML_LINE_BREAK); + for (TaskDefinitionSkill requiredSkill : requiredSkills) + { + Skill skill; + try + { + skill = Skill.valueOf(requiredSkill.getSkill().toUpperCase()); + } + catch (IllegalArgumentException ex) + { + log.warn("unknown skill: {}", requiredSkill.getSkill().toUpperCase(), ex); + continue; + } + + + Integer requiredLevel = requiredSkill.getLevel(); + int playerLevel = -1; + if (requiredLevel == null) + { + continue; + } + if (plugin.playerSkills != null) + { + playerLevel = plugin.playerSkills[skill.ordinal()]; + } + String skillMessage = getSkillRequirementHtml(requiredSkill.getSkill().toLowerCase(), playerLevel, requiredLevel); + skillSection.append(skillMessage).append(" "); + } + + return skillSection.toString(); + } + + private String getSkillRequirementHtml(String skillName, Integer playerLevel, int requiredLevel) + { + String skillIconPath = "/skill_icons_small/" + skillName + ".png"; + URL url = SkillIconManager.class.getResource(skillIconPath); + Color color = playerLevel >= requiredLevel ? Colors.QUALIFIED_TEXT_COLOR : Colors.UNQUALIFIED_TEXT_COLOR; + return HtmlUtil.imageTag(url) + " " + HtmlUtil.colorTag(color, playerLevel + "/" + requiredLevel); + } + + private String getPointsTooltipText() + { + int points = this.task.getPoints(); + if (points == 0) + { + return ""; + } + return " - " + points + " points"; + } +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/TaskPanelFactory.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/TaskPanelFactory.java new file mode 100644 index 00000000..df1b6fda --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/TaskPanelFactory.java @@ -0,0 +1,8 @@ +package net.reldo.taskstracker.panel; + +import net.reldo.taskstracker.data.task.TaskFromStruct; + +public interface TaskPanelFactory +{ + TaskPanel create(TaskFromStruct task); +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/TasksTrackerPluginPanel.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/TasksTrackerPluginPanel.java new file mode 100644 index 00000000..9fd6a485 --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/TasksTrackerPluginPanel.java @@ -0,0 +1,108 @@ +package net.reldo.taskstracker.panel; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import javax.swing.SwingUtilities; +import javax.swing.border.EmptyBorder; +import lombok.extern.slf4j.Slf4j; +import net.reldo.taskstracker.TasksTrackerConfig; +import net.reldo.taskstracker.TasksTrackerPlugin; +import net.reldo.taskstracker.data.task.TaskService; +import net.runelite.client.game.SpriteManager; +import net.runelite.client.ui.ColorScheme; +import net.runelite.client.ui.PluginPanel; + +@Slf4j +public class TasksTrackerPluginPanel extends PluginPanel +{ + private final LoggedInPanel loggedInPanel; + private final LoggedOutPanel loggedOutPanel = new LoggedOutPanel(); + + public TaskListPanel taskListPanel; + + private boolean loggedInPanelVisible = false; + + public TasksTrackerPluginPanel(TasksTrackerPlugin plugin, TasksTrackerConfig config, SpriteManager spriteManager, TaskService taskService) + { + super(false); + + setBorder(new EmptyBorder(6, 6, 6, 6)); + setBackground(ColorScheme.DARK_GRAY_COLOR); + setLayout(new BorderLayout()); + + loggedInPanel = new LoggedInPanel(plugin, config, taskService); + taskListPanel = loggedInPanel.taskListPanel; + add(loggedInPanel, BorderLayout.NORTH); + loggedInPanel.setVisible(false); + + // Add error pane + add(loggedOutPanel); + } + + @Override + public Dimension getPreferredSize() + { + return new Dimension(PANEL_WIDTH + SCROLLBAR_WIDTH, super.getPreferredSize().height); + } + + public void redraw() + { + if (loggedInPanelVisible) + { + loggedInPanel.redraw(); + } + } + + public void refreshAllTasks() + { + if (loggedInPanelVisible) + { + loggedInPanel.refreshAllTasks(); + } + } + + public void setLoggedIn(boolean loggedIn) + { + if(SwingUtilities.isEventDispatchThread()) + { + updateVisiblePanel(loggedIn); + } + else + { + log.error("Failed to update loggedIn state - not event dispatch thread."); + } + } + + public void hideLoggedInPanel() + { + if(SwingUtilities.isEventDispatchThread()) + { + updateVisiblePanel(false); + } + else + { + log.error("Failed to update logged in panel visibility - not event dispatch thread."); + } + } + + private void updateVisiblePanel(boolean loggedInPanelVisible) + { + if (loggedInPanelVisible != this.loggedInPanelVisible) + { + if (loggedInPanelVisible) + { + loggedOutPanel.setVisible(false); + loggedInPanel.setVisible(true); + } + else + { + loggedInPanel.setVisible(false); + loggedOutPanel.setVisible(true); + } + + validate(); + repaint(); + } + this.loggedInPanelVisible = loggedInPanelVisible; + } +} \ No newline at end of file diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/components/CheckBox.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/components/CheckBox.java new file mode 100644 index 00000000..2905ce84 --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/components/CheckBox.java @@ -0,0 +1,42 @@ +package net.reldo.taskstracker.panel.components; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.event.ActionListener; +import javax.swing.JCheckBox; +import javax.swing.JLabel; +import javax.swing.JPanel; +import net.runelite.client.ui.ColorScheme; +import static net.runelite.client.ui.PluginPanel.PANEL_WIDTH; + +public class CheckBox extends JPanel +{ + private final JCheckBox jCheckBox = new JCheckBox(); + + public CheckBox(String name) + { + setLayout(new BorderLayout()); + setMinimumSize(new Dimension(PANEL_WIDTH, 0)); + + JLabel label = new JLabel(name); + add(label, BorderLayout.CENTER); + + jCheckBox.setBackground(ColorScheme.LIGHT_GRAY_COLOR); + add(jCheckBox, BorderLayout.EAST); + } + + public boolean isSelected() + { + return jCheckBox.isSelected(); + } + + public void setSelected(boolean selected) + { + jCheckBox.setSelected(selected); + } + + public void addActionListener(ActionListener l) + { + jCheckBox.addActionListener(l); + } +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/components/FixedWidthPanel.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/components/FixedWidthPanel.java new file mode 100644 index 00000000..d2803d8b --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/components/FixedWidthPanel.java @@ -0,0 +1,15 @@ +package net.reldo.taskstracker.panel.components; + +import java.awt.Dimension; +import javax.swing.JPanel; +import net.runelite.client.ui.PluginPanel; + +public class FixedWidthPanel extends JPanel +{ + @Override + public Dimension getPreferredSize() + { + return new Dimension(PluginPanel.PANEL_WIDTH, super.getPreferredSize().height); + } + +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/components/MultiToggleButton.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/components/MultiToggleButton.java new file mode 100644 index 00000000..6c4fc47f --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/components/MultiToggleButton.java @@ -0,0 +1,148 @@ +package net.reldo.taskstracker.panel.components; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.Icon; +import javax.swing.JButton; +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; +import javax.swing.border.EmptyBorder; +import lombok.Getter; +import lombok.Setter; + +public class MultiToggleButton extends JButton +{ + + // Icons + private final Icon[] icons; + // Tooltips + private final String[] tooltips; + private final int stateCount; + @Setter + private ActionListener stateChangedAction = null; + @Getter + private int state = 0; + final JPopupMenu popupMenu = new JPopupMenu(); + private boolean popupMenuEnabled = false; + + public MultiToggleButton(int stateCount) + { + super(); + this.stateCount = stateCount; + icons = new Icon[stateCount]; + tooltips = new String[stateCount]; + popupMenu.setBorder(new EmptyBorder(5, 5, 5, 5)); + addActionListener(e -> changeStateThenAction()); + } + + public void popupMenuEnabled(boolean enabled) + { + if(popupMenuEnabled != enabled) + { + popupMenuEnabled = enabled; + if(popupMenuEnabled) + { + this.setComponentPopupMenu(popupMenu); + } + else + { + this.remove(popupMenu); + } + } + } + + public void setIcon(Icon icon, int state) + { + if (state < 0 || state > stateCount || icon == null) + { + return; + } + + icons[state] = icon; + + if (state == this.state) setIconState(); + } + + public boolean setIcons(Icon[] icons) + { + if (icons == null || icons.length == 0) + { + return false; + } + + for (int i = 0; i < icons.length; i++) + { + setIcon(icons[i], i); + } + + return true; + } + + public void setToolTip(String tooltip, int state) + { + if (state < 0 || state > 3 || tooltip == null) + { + return; + } + + tooltips[state] = tooltip; + addPopupMenuItem(tooltip, state); + + if (state == this.state) setTooltipState(); + } + + public boolean setToolTips(String[] tooltips) + { + if (tooltips == null || tooltips.length == 0) + { + return false; + } + + for (int i = 0; i < tooltips.length; i++) + { + setToolTip(tooltips[i], i); + } + + return true; + } + + public void changeState() + { + setState((++state) % stateCount); + } + + public void changeStateThenAction() + { + setStateThenAction((++state) % stateCount); + } + + private void setIconState() + { + super.setIcon(icons[state]); + } + + private void setTooltipState() + { + super.setToolTipText(tooltips[state]); + } + + public void setState(int state) + { + this.state = state; + setIconState(); + setTooltipState(); + } + + public void setStateThenAction(int state) + { + setState(state); + if(stateChangedAction != null) this.stateChangedAction.actionPerformed(new ActionEvent(this, 0, "")); + } + + private void addPopupMenuItem(String text, int state) + { + JMenuItem menuItem = new JMenuItem(text); + menuItem.addActionListener(e -> {if(isEnabled())setState(state);}); + popupMenu.add(menuItem); + } +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/components/SearchBox.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/components/SearchBox.java new file mode 100644 index 00000000..68529959 --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/components/SearchBox.java @@ -0,0 +1,53 @@ +package net.reldo.taskstracker.panel.components; + +import java.awt.Dimension; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import net.runelite.client.ui.ColorScheme; +import net.runelite.client.ui.PluginPanel; +import net.runelite.client.ui.components.IconTextField; + +public class SearchBox extends IconTextField +{ + private SearchBoxCallback fn; + + public SearchBox() + { + this.setIcon(IconTextField.Icon.SEARCH); + this.setPreferredSize(new Dimension(PluginPanel.PANEL_WIDTH - 20, 30)); + this.setBackground(ColorScheme.DARKER_GRAY_COLOR); + this.setHoverBackgroundColor(ColorScheme.DARK_GRAY_HOVER_COLOR); + this.getDocument().addDocumentListener(new DocumentListener() + { + @Override + public void insertUpdate(DocumentEvent documentEvent) + { + fn.call(); + } + + @Override + public void removeUpdate(DocumentEvent documentEvent) + { + fn.call(); + } + + @Override + public void changedUpdate(DocumentEvent documentEvent) + { + } + }); + + this.addActionListener(e -> fn.call() + ); + } + + public void addTextChangedListener(SearchBoxCallback fn) + { + this.fn = fn; + } + + public interface SearchBoxCallback + { + void call(); + } +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/components/TriToggleButton.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/components/TriToggleButton.java new file mode 100644 index 00000000..603d57e2 --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/components/TriToggleButton.java @@ -0,0 +1,39 @@ +package net.reldo.taskstracker.panel.components; + +import javax.swing.Icon; + +public class TriToggleButton extends MultiToggleButton +{ + public TriToggleButton() + { + super(3); + } + + public boolean setIcons(Icon icon0, Icon icon1, Icon icon2) + { + if (icon0 == null || icon1 == null || icon2 == null) + { + return false; + } + + setIcon(icon0, 0); + setIcon(icon1, 1); + setIcon(icon2, 2); + + return true; + } + + public boolean setToolTips(String tooltip0, String tooltip1, String tooltip2) + { + if (tooltip0 == null || tooltip1 == null || tooltip2 == null) + { + return false; + } + + setToolTip(tooltip0, 0); + setToolTip(tooltip1, 1); + setToolTip(tooltip2, 2); + + return true; + } +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/filters/ComboItem.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/filters/ComboItem.java new file mode 100644 index 00000000..6cdac2a1 --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/filters/ComboItem.java @@ -0,0 +1,29 @@ +package net.reldo.taskstracker.panel.filters; + +public class ComboItem +{ + private T value; + private String label; + + public ComboItem(T value, String label) + { + this.value = value; + this.label = label; + } + + public T getValue() + { + return this.value; + } + + public String getLabel() + { + return this.label; + } + + @Override + public String toString() + { + return label; + } +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/filters/DynamicButtonFilterPanel.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/filters/DynamicButtonFilterPanel.java new file mode 100644 index 00000000..1e1881bc --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/filters/DynamicButtonFilterPanel.java @@ -0,0 +1,131 @@ +package net.reldo.taskstracker.panel.filters; + +import com.google.common.collect.ImmutableList; +import java.awt.BorderLayout; +import java.awt.GridLayout; +import java.awt.image.BufferedImage; +import java.util.LinkedHashMap; +import java.util.List; +import javax.swing.JPanel; +import javax.swing.JToggleButton; +import javax.swing.border.EmptyBorder; +import net.reldo.taskstracker.TasksTrackerPlugin; +import net.reldo.taskstracker.data.jsondatastore.types.FilterConfig; +import net.reldo.taskstracker.data.jsondatastore.types.FilterCustomItem; +import net.reldo.taskstracker.data.task.TaskType; +import net.runelite.client.hiscore.HiscoreSkill; +import static net.runelite.client.hiscore.HiscoreSkill.*; +import net.runelite.client.ui.ColorScheme; +import net.runelite.client.util.ImageUtil; + +public class DynamicButtonFilterPanel extends FilterButtonPanel +{ + private final FilterConfig filterConfig; + private final TaskType taskType; + + /** + * Real skills, ordered in the way they should be displayed in the panel. + */ + private static final List SKILLS = ImmutableList.of( + ATTACK, HITPOINTS, MINING, + STRENGTH, AGILITY, SMITHING, + DEFENCE, HERBLORE, FISHING, + RANGED, THIEVING, COOKING, + PRAYER, CRAFTING, FIREMAKING, + MAGIC, FLETCHING, WOODCUTTING, + RUNECRAFT, SLAYER, FARMING, + CONSTRUCTION, HUNTER + ); + + public DynamicButtonFilterPanel(TasksTrackerPlugin plugin, FilterConfig filterConfig, TaskType taskType) + { + super(plugin, filterConfig.getLabel()); + this.filterConfig = filterConfig; + this.taskType = taskType; + this.configKey = taskType.getFilterConfigPrefix() + filterConfig.getConfigKey(); + + setLayout(new BorderLayout()); + setBackground(ColorScheme.DARKER_GRAY_COLOR); + setBorder(new EmptyBorder(10, 10, 10, 10)); + + redraw(); + } + + @Override + protected JPanel makeButtonPanel() + { + // Panel that holds tier icons + JPanel buttonPanel = new JPanel(); + buttonPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR); + + LinkedHashMap buttonImages = getIconImages(); + LinkedHashMap buttonTooltips = getTooltips(); + + buttonPanel.setLayout(new GridLayout(buttonImages.size() / 3, 3)); + + // For each filter value create a button and add it to the UI + buttonImages.forEach((key, image) -> { + String tooltip = buttonTooltips.get(key); + JToggleButton button = makeButton(tooltip, image); + button.setSelected(getConfigButtonState(key)); + buttons.put(key, button); + buttonPanel.add(button); + }); + + return buttonPanel; + } + + @Override + protected LinkedHashMap getIconImages() + { + LinkedHashMap images = new LinkedHashMap<>(); + + if (filterConfig.getConfigKey().equals("skill")) + { + String skillName; + BufferedImage skillImage; + int index = 0; + + for (FilterCustomItem customItem : filterConfig.getCustomItems()) + { + if (customItem.getValue() != 0) + { + skillName = SKILLS.get(index).name().toLowerCase(); + + String directory = "/skill_icons_small/"; + String skillIcon = directory + skillName + ".png"; + + skillImage = ImageUtil.loadImageResource(getClass(), skillIcon); + } + else + { + skillImage = ImageUtil.loadImageResource(TasksTrackerPlugin.class, "panel/components/no_skill.png"); + } + + String key = customItem.getValue().toString(); + images.put(key, skillImage); + index++; + } + } + else + { + for (FilterCustomItem customItem : filterConfig.getCustomItems()) + { + String key = customItem.getValue().toString(); + images.put(key, taskType.getSpritesById().get(customItem.getSpriteId())); + } + } + return images; + } + + private LinkedHashMap getTooltips() + { + LinkedHashMap tooltips = new LinkedHashMap<>(); + for (FilterCustomItem customItem : filterConfig.getCustomItems()) + { + String key = customItem.getValue().toString(); + tooltips.put(key, customItem.getTooltip()); + } + return tooltips; + } +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/filters/DynamicDropdownFilterPanel.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/filters/DynamicDropdownFilterPanel.java new file mode 100644 index 00000000..6277b505 --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/filters/DynamicDropdownFilterPanel.java @@ -0,0 +1,92 @@ +package net.reldo.taskstracker.panel.filters; + +import java.awt.GridLayout; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.SwingUtilities; +import javax.swing.border.EmptyBorder; +import lombok.extern.slf4j.Slf4j; +import net.reldo.taskstracker.TasksTrackerPlugin; +import net.reldo.taskstracker.data.jsondatastore.types.FilterConfig; +import net.reldo.taskstracker.data.task.TaskType; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.ui.ColorScheme; +import net.runelite.client.ui.FontManager; + +@Slf4j +public class DynamicDropdownFilterPanel extends FilterPanel +{ + private final String configKey; + private final FilterConfig filterConfig; + private final ConfigManager configManager; + private final TasksTrackerPlugin plugin; + private final ComboItem[] items; + private JComboBox dropdown; + + public DynamicDropdownFilterPanel(TasksTrackerPlugin plugin, FilterConfig filterConfig, TaskType taskType, ComboItem[] items) + { + this.configManager = plugin.getConfigManager(); + this.plugin = plugin; + this.filterConfig = filterConfig; + this.items = items; + this.configKey = taskType.getFilterConfigPrefix() + filterConfig.getConfigKey(); + + setLayout(new GridLayout(1,2)); + setBackground(ColorScheme.DARKER_GRAY_COLOR); + setBorder(new EmptyBorder(5, 10, 5, 10)); + } + + private JComboBox makeDropdownPanel() + { + JComboBox dropdown = new JComboBox<>(items); + dropdown.setFont(FontManager.getRunescapeSmallFont()); + dropdown.setAlignmentX(LEFT_ALIGNMENT); + dropdown.setSelectedItem(items[0]); + dropdown.setFocusable(false); + dropdown.setBackground(ColorScheme.DARK_GRAY_COLOR.brighter()); + dropdown.addActionListener(e -> { + ComboItem selection = dropdown.getItemAt(dropdown.getSelectedIndex()); + updateFilterConfig(); + plugin.refreshAllTasks(); + log.debug("selected: {} {}", selection.getLabel(), selection.getValue()); + }); + + return dropdown; + } + + private JLabel makeLabel() + { + JLabel label = new JLabel(filterConfig.getLabel() + ":"); + label.setFont(FontManager.getRunescapeSmallFont()); + label.setForeground(ColorScheme.LIGHT_GRAY_COLOR); + return label; + } + + protected void updateFilterConfig() + { + log.debug("updateFilterConfig {}, {}, {}", TasksTrackerPlugin.CONFIG_GROUP_NAME, configKey, dropdown.getItemAt(dropdown.getSelectedIndex()).getValue()); + configManager.setConfiguration(TasksTrackerPlugin.CONFIG_GROUP_NAME, configKey, dropdown.getItemAt(dropdown.getSelectedIndex()).getValue()); + } + + public void redraw() + { + if(SwingUtilities.isEventDispatchThread()) + { + removeAll(); + + dropdown = makeDropdownPanel(); + + add(makeLabel()); + add(dropdown); + + updateFilterConfig(); + + validate(); + repaint(); + } + else + { + log.error("Dropdown filter panel redraw failed - not event dispatch thread."); + } + } +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/filters/FilterButtonPanel.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/filters/FilterButtonPanel.java new file mode 100644 index 00000000..8395bf99 --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/filters/FilterButtonPanel.java @@ -0,0 +1,223 @@ +package net.reldo.taskstracker.panel.filters; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.image.BufferedImage; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.stream.Collectors; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.Icon; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JToggleButton; +import javax.swing.SwingUtilities; +import javax.swing.border.EmptyBorder; +import javax.swing.plaf.basic.BasicBorders; +import javax.swing.plaf.basic.BasicButtonUI; +import lombok.extern.slf4j.Slf4j; +import net.reldo.taskstracker.TasksTrackerPlugin; +import net.runelite.client.ui.ColorScheme; +import net.runelite.client.ui.FontManager; +import net.runelite.client.util.ImageUtil; +import net.runelite.client.util.SwingUtil; + +@Slf4j +public abstract class FilterButtonPanel extends FilterPanel +{ + protected final TasksTrackerPlugin plugin; + private final String label; + + protected final Map buttons = new HashMap<>(); + protected String configKey; + protected JPanel buttonPanel; + + protected JToggleButton collapseBtn; + private final String expandBtnPath = "panel/components/"; + private final BufferedImage collapseImg = ImageUtil.loadImageResource(TasksTrackerPlugin.class, expandBtnPath + "filter_buttons_collapsed.png"); + private final Icon MENU_COLLAPSED_ICON = new ImageIcon(ImageUtil.alphaOffset(collapseImg, -180)); + private final Icon MENU_ICON_HOVER = new ImageIcon(collapseImg); + private final BufferedImage expandedImg = ImageUtil.loadImageResource(TasksTrackerPlugin.class, expandBtnPath + "filter_buttons_expanded.png"); + private final Icon MENU_EXPANDED_ICON = new ImageIcon(ImageUtil.alphaOffset(expandedImg, -180)); + private final Icon MENU_ICON_HOVER_SELECTED = new ImageIcon(expandedImg); + + + public FilterButtonPanel(TasksTrackerPlugin plugin, String label) + { + this.plugin = plugin; + + this.label = label; + } + + protected abstract LinkedHashMap getIconImages(); + + protected abstract JPanel makeButtonPanel(); + + protected JToggleButton makeButton(String tooltip, BufferedImage image) + { + JToggleButton button = new JToggleButton(); + button.setBackground(ColorScheme.DARKER_GRAY_COLOR); + button.setBorder(new BasicBorders.ToggleButtonBorder(ColorScheme.DARKER_GRAY_COLOR, + ColorScheme.DARKER_GRAY_COLOR.darker(), + ColorScheme.MEDIUM_GRAY_COLOR.darker(), + ColorScheme.MEDIUM_GRAY_COLOR)); + button.setFocusable(false); + + if (image != null) { + ImageIcon selectedIcon = new ImageIcon(image); + ImageIcon deselectedIcon = new ImageIcon(ImageUtil.alphaOffset(image, -180)); + + button.setIcon(deselectedIcon); + button.setSelectedIcon(selectedIcon); + button.setPreferredSize(new Dimension(image.getWidth(), image.getHeight() + 10)); + } else { + button.setPreferredSize(new Dimension(button.getPreferredSize().width, 50)); + } + button.setToolTipText(tooltip.substring(0,1).toUpperCase() + tooltip.substring(1).toLowerCase()); + + button.addActionListener(e -> { + updateFilterText(); + updateCollapseButtonText(); + plugin.refreshAllTasks(); + }); + + button.setSelected(true); + + return button; + } + + protected JPanel allOrNoneButtons() + { + JPanel buttonWrapper = new JPanel(); + buttonWrapper.setLayout(new BoxLayout(buttonWrapper, BoxLayout.X_AXIS)); + buttonWrapper.setBackground(ColorScheme.DARKER_GRAY_COLOR); + buttonWrapper.setAlignmentX(JPanel.CENTER_ALIGNMENT); + + JButton all = new JButton("all"); + SwingUtil.removeButtonDecorations(all); + all.setFocusable(false); + all.setForeground(ColorScheme.MEDIUM_GRAY_COLOR); + all.setFont(FontManager.getRunescapeSmallFont()); + all.setPreferredSize(new Dimension(50, 0)); + all.addActionListener(e -> { + setAllSelected(true); + updateFilterText(); + updateCollapseButtonText(); + plugin.refreshAllTasks(); + }); + + JButton none = new JButton("none"); + SwingUtil.removeButtonDecorations(none); + none.setFocusable(false); + none.setForeground(ColorScheme.MEDIUM_GRAY_COLOR); + none.setFont(FontManager.getRunescapeSmallFont()); + none.setPreferredSize(new Dimension(50, 0)); + none.addActionListener(e -> { + setAllSelected(false); + updateFilterText(); + updateCollapseButtonText(); + plugin.refreshAllTasks(); + }); + + JLabel separator = new JLabel("|"); + separator.setForeground(ColorScheme.MEDIUM_GRAY_COLOR); + + buttonWrapper.add(Box.createHorizontalGlue()); + buttonWrapper.add(all); + buttonWrapper.add(Box.createHorizontalGlue()); + buttonWrapper.add(separator); + buttonWrapper.add(Box.createHorizontalGlue()); + buttonWrapper.add(none); + buttonWrapper.add(Box.createHorizontalGlue()); + + return buttonWrapper; + } + + public JToggleButton makeCollapseButton() + { + JToggleButton collapseBtn = new JToggleButton(); + + // collapse button + SwingUtil.removeButtonDecorations(collapseBtn); + collapseBtn.setIcon(MENU_COLLAPSED_ICON); + collapseBtn.setSelectedIcon(MENU_EXPANDED_ICON); + collapseBtn.setRolloverIcon(MENU_ICON_HOVER); + collapseBtn.setRolloverSelectedIcon(MENU_ICON_HOVER_SELECTED); + SwingUtil.addModalTooltip(collapseBtn, "Collapse filters", "Expand filters"); + collapseBtn.setBackground(ColorScheme.DARKER_GRAY_COLOR); + collapseBtn.setAlignmentX(LEFT_ALIGNMENT); + collapseBtn.setUI(new BasicButtonUI()); // substance breaks the layout + collapseBtn.addActionListener(ev -> buttonPanel.setVisible(!buttonPanel.isVisible())); + collapseBtn.setHorizontalTextPosition(JButton.CENTER); + collapseBtn.setForeground(ColorScheme.LIGHT_GRAY_COLOR); + collapseBtn.setFont(FontManager.getRunescapeSmallFont()); + collapseBtn.setBorder(new EmptyBorder(2, 0, 2, 0)); + collapseBtn.setFocusable(false); + collapseBtn.setSelected(true); + + return collapseBtn; + } + + protected void updateFilterText() + { + String filterText = buttons.entrySet().stream() + .filter(e -> e.getValue().isSelected()) + .map(e -> "f-" + e.getKey() + "-f") // prefix included to cover cases where one key name is contained in another (e.g. "Master" -> "Grandmaster") + .collect(Collectors.joining(",")); + + plugin.getConfigManager().setConfiguration(TasksTrackerPlugin.CONFIG_GROUP_NAME, configKey, filterText); + } + + protected boolean getConfigButtonState(String buttonKey) + { + String configValue = plugin.getConfigManager().getConfiguration(TasksTrackerPlugin.CONFIG_GROUP_NAME, configKey); + boolean isEmptyFilterSelection = configValue == null || configValue.isEmpty() || configValue.equals("-1"); + if (isEmptyFilterSelection) + { + return true; + } + + return configValue.contains("f-" + buttonKey + "-f"); + } + + protected void setAllSelected(boolean state) + { + buttons.values().forEach(button -> button.setSelected(state)); + } + + protected void updateCollapseButtonText() + { + collapseBtn.setText(label + " - " + buttons.values().stream().filter(JToggleButton::isSelected).count() + " / " + buttons.size()); + } + + public void redraw() + { + if(SwingUtilities.isEventDispatchThread()) + { + buttons.clear(); + removeAll(); + + collapseBtn = makeCollapseButton(); + buttonPanel = makeButtonPanel(); + + add(collapseBtn, BorderLayout.NORTH); + add(buttonPanel, BorderLayout.CENTER); + add(allOrNoneButtons(), BorderLayout.SOUTH); + updateFilterText(); + updateCollapseButtonText(); + + collapseBtn.setVisible(plugin.getConfig().filterPanelCollapsible()); + + validate(); + repaint(); + } + else + { + log.error("Filter button panel redraw failed - not event dispatch thread."); + } + } +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/filters/FilterPanel.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/filters/FilterPanel.java new file mode 100644 index 00000000..ce76e853 --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/panel/filters/FilterPanel.java @@ -0,0 +1,8 @@ +package net.reldo.taskstracker.panel.filters; + +import net.reldo.taskstracker.panel.components.FixedWidthPanel; + +public abstract class FilterPanel extends FixedWidthPanel +{ + public abstract void redraw(); +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/quests/DiaryData.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/quests/DiaryData.java new file mode 100644 index 00000000..e867414b --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/quests/DiaryData.java @@ -0,0 +1,15 @@ +package net.reldo.taskstracker.quests; + +import java.util.HashMap; +import net.runelite.api.Client; + +public class DiaryData extends HashMap +{ + public DiaryData(Client client) + { + for (DiaryVarbits diary : DiaryVarbits.values()) + { + this.put(diary.id, diary.getProgress(client)); + } + } +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/quests/DiaryVarbits.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/quests/DiaryVarbits.java new file mode 100644 index 00000000..732f9c96 --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/quests/DiaryVarbits.java @@ -0,0 +1,66 @@ +package net.reldo.taskstracker.quests; + +import net.runelite.api.Client; + +public enum DiaryVarbits +{ + DIARY_ARDOUGNE_EASY(4458), + DIARY_ARDOUGNE_MEDIUM(4459), + DIARY_ARDOUGNE_HARD(4460), + DIARY_ARDOUGNE_ELITE(4461), + DIARY_DESERT_EASY(4483), + DIARY_DESERT_MEDIUM(4484), + DIARY_DESERT_HARD(4485), + DIARY_DESERT_ELITE(4486), + DIARY_FALADOR_EASY(4462), + DIARY_FALADOR_MEDIUM(4463), + DIARY_FALADOR_HARD(4464), + DIARY_FALADOR_ELITE(4465), + DIARY_FREMENNIK_EASY(4491), + DIARY_FREMENNIK_MEDIUM(4492), + DIARY_FREMENNIK_HARD(4493), + DIARY_FREMENNIK_ELITE(4494), + DIARY_KANDARIN_EASY(4475), + DIARY_KANDARIN_MEDIUM(4476), + DIARY_KANDARIN_HARD(4477), + DIARY_KANDARIN_ELITE(4478), + DIARY_KARAMJA_EASY(3578), + DIARY_KARAMJA_MEDIUM(3599), + DIARY_KARAMJA_HARD(3611), + DIARY_KARAMJA_ELITE(4566), + DIARY_KOUREND_EASY(7925), + DIARY_KOUREND_MEDIUM(7926), + DIARY_KOUREND_HARD(7927), + DIARY_KOUREND_ELITE(7928), + DIARY_LUMBRIDGE_EASY(4495), + DIARY_LUMBRIDGE_MEDIUM(4496), + DIARY_LUMBRIDGE_HARD(4497), + DIARY_LUMBRIDGE_ELITE(4498), + DIARY_MORYTANIA_EASY(4487), + DIARY_MORYTANIA_MEDIUM(4488), + DIARY_MORYTANIA_HARD(4489), + DIARY_MORYTANIA_ELITE(4490), + DIARY_VARROCK_EASY(4479), + DIARY_VARROCK_MEDIUM(4480), + DIARY_VARROCK_HARD(4481), + DIARY_VARROCK_ELITE(4482), + DIARY_WESTERN_EASY(4471), + DIARY_WESTERN_MEDIUM(4472), + DIARY_WESTERN_HARD(4473), + DIARY_WESTERN_ELITE(4474), + DIARY_WILDERNESS_EASY(4466), + DIARY_WILDERNESS_MEDIUM(4467), + DIARY_WILDERNESS_HARD(4468), + DIARY_WILDERNESS_ELITE(4469); + + public int id; + + DiaryVarbits(int id) { + this.id = id; + } + + public int getProgress(Client client) + { + return client.getVarbitValue(id); + } +} diff --git a/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/quests/QuestData.java b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/quests/QuestData.java new file mode 100644 index 00000000..10f2aa19 --- /dev/null +++ b/tasks-tracker-plugin-master/src/main/java/net/reldo/taskstracker/quests/QuestData.java @@ -0,0 +1,17 @@ +package net.reldo.taskstracker.quests; + +import java.util.HashMap; +import net.runelite.api.Client; +import net.runelite.api.Quest; +import net.runelite.api.QuestState; + +public class QuestData extends HashMap +{ + public QuestData(Client client) + { + for (Quest quest : Quest.values()) + { + this.put(quest.getId(), quest.getState(client)); + } + } +} diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/eye-cross-grey.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/eye-cross-grey.png new file mode 100644 index 00000000..7e42040b Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/eye-cross-grey.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/eye-red.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/eye-red.png new file mode 100644 index 00000000..b4cea10e Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/eye-red.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/eye.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/eye.png new file mode 100644 index 00000000..55c00226 Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/eye.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/minus.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/minus.png new file mode 100644 index 00000000..b44010a2 Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/minus.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/collapsed.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/collapsed.png new file mode 100644 index 00000000..a637936b Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/collapsed.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/complete_button/complete_and_incomplete_icon.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/complete_button/complete_and_incomplete_icon.png new file mode 100644 index 00000000..60c67c8c Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/complete_button/complete_and_incomplete_icon.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/complete_button/complete_only_icon.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/complete_button/complete_only_icon.png new file mode 100644 index 00000000..ae0d44b0 Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/complete_button/complete_only_icon.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/complete_button/incomplete_only_icon.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/complete_button/incomplete_only_icon.png new file mode 100644 index 00000000..9a0ba091 Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/complete_button/incomplete_only_icon.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/expanded.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/expanded.png new file mode 100644 index 00000000..35e4f4ed Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/expanded.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/filter_buttons_collapsed.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/filter_buttons_collapsed.png new file mode 100644 index 00000000..d23684c4 Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/filter_buttons_collapsed.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/filter_buttons_expanded.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/filter_buttons_expanded.png new file mode 100644 index 00000000..2ac69454 Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/filter_buttons_expanded.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/filter_menu_collapsed.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/filter_menu_collapsed.png new file mode 100644 index 00000000..8440d0aa Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/filter_menu_collapsed.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/filter_menu_expanded.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/filter_menu_expanded.png new file mode 100644 index 00000000..0981525c Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/filter_menu_expanded.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/ignored_button/invisible_icon.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/ignored_button/invisible_icon.png new file mode 100644 index 00000000..c43883bf Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/ignored_button/invisible_icon.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/ignored_button/semivisible_icon.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/ignored_button/semivisible_icon.png new file mode 100644 index 00000000..a52cbfe0 Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/ignored_button/semivisible_icon.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/ignored_button/visible_icon.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/ignored_button/visible_icon.png new file mode 100644 index 00000000..5c4232c8 Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/ignored_button/visible_icon.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/no_skill.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/no_skill.png new file mode 100644 index 00000000..12b88401 Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/no_skill.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/search.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/search.png new file mode 100644 index 00000000..3e723753 Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/search.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/sort_button/ascending_icon.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/sort_button/ascending_icon.png new file mode 100644 index 00000000..e097ecbe Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/sort_button/ascending_icon.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/sort_button/descending_icon.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/sort_button/descending_icon.png new file mode 100644 index 00000000..f78e4893 Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/sort_button/descending_icon.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_areas/league4/asgarnia.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_areas/league4/asgarnia.png new file mode 100644 index 00000000..28ccfc09 Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_areas/league4/asgarnia.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_areas/league4/desert.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_areas/league4/desert.png new file mode 100644 index 00000000..6d061e70 Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_areas/league4/desert.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_areas/league4/fremennik.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_areas/league4/fremennik.png new file mode 100644 index 00000000..38992722 Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_areas/league4/fremennik.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_areas/league4/global.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_areas/league4/global.png new file mode 100644 index 00000000..12130775 Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_areas/league4/global.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_areas/league4/kandarin.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_areas/league4/kandarin.png new file mode 100644 index 00000000..8a151405 Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_areas/league4/kandarin.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_areas/league4/karamja.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_areas/league4/karamja.png new file mode 100644 index 00000000..e4093667 Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_areas/league4/karamja.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_areas/league4/kourend.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_areas/league4/kourend.png new file mode 100644 index 00000000..f526569c Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_areas/league4/kourend.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_areas/league4/misthalin.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_areas/league4/misthalin.png new file mode 100644 index 00000000..2a3ae7b8 Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_areas/league4/misthalin.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_areas/league4/morytania.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_areas/league4/morytania.png new file mode 100644 index 00000000..85dc110d Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_areas/league4/morytania.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_areas/league4/tirannwn.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_areas/league4/tirannwn.png new file mode 100644 index 00000000..4b7e9919 Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_areas/league4/tirannwn.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_areas/league4/wilderness.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_areas/league4/wilderness.png new file mode 100644 index 00000000..aee8fec3 Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_areas/league4/wilderness.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_categories/league4/achievement.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_categories/league4/achievement.png new file mode 100644 index 00000000..9c3f0171 Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_categories/league4/achievement.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_categories/league4/combat.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_categories/league4/combat.png new file mode 100644 index 00000000..a4dcf5c2 Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_categories/league4/combat.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_categories/league4/minigame.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_categories/league4/minigame.png new file mode 100644 index 00000000..86b2b94a Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_categories/league4/minigame.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_categories/league4/other.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_categories/league4/other.png new file mode 100644 index 00000000..76f8c331 Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_categories/league4/other.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_categories/league4/quest.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_categories/league4/quest.png new file mode 100644 index 00000000..f8904e06 Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_categories/league4/quest.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_categories/league4/skill.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_categories/league4/skill.png new file mode 100644 index 00000000..5e30ce50 Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_categories/league4/skill.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_tiers/combat/easy.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_tiers/combat/easy.png new file mode 100644 index 00000000..58073170 Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_tiers/combat/easy.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_tiers/combat/elite.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_tiers/combat/elite.png new file mode 100644 index 00000000..7506bc9a Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_tiers/combat/elite.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_tiers/combat/grandmaster.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_tiers/combat/grandmaster.png new file mode 100644 index 00000000..97abbc30 Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_tiers/combat/grandmaster.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_tiers/combat/hard.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_tiers/combat/hard.png new file mode 100644 index 00000000..9b2645a2 Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_tiers/combat/hard.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_tiers/combat/master.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_tiers/combat/master.png new file mode 100644 index 00000000..c7f4eaed Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_tiers/combat/master.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_tiers/combat/medium.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_tiers/combat/medium.png new file mode 100644 index 00000000..b868bcc4 Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_tiers/combat/medium.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_tiers/league3/easy.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_tiers/league3/easy.png new file mode 100644 index 00000000..40ede02a Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_tiers/league3/easy.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_tiers/league3/elite.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_tiers/league3/elite.png new file mode 100644 index 00000000..15021616 Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_tiers/league3/elite.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_tiers/league3/hard.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_tiers/league3/hard.png new file mode 100644 index 00000000..adf50ef8 Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_tiers/league3/hard.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_tiers/league3/master.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_tiers/league3/master.png new file mode 100644 index 00000000..2219b9fc Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_tiers/league3/master.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_tiers/league3/medium.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_tiers/league3/medium.png new file mode 100644 index 00000000..b157c3c3 Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/task_tiers/league3/medium.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/tracked_button/tracked_and_untracked_icon.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/tracked_button/tracked_and_untracked_icon.png new file mode 100644 index 00000000..8dad9aad Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/tracked_button/tracked_and_untracked_icon.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/tracked_button/tracked_icon.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/tracked_button/tracked_icon.png new file mode 100644 index 00000000..4915f067 Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/tracked_button/tracked_icon.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/tracked_button/untracked_icon.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/tracked_button/untracked_icon.png new file mode 100644 index 00000000..bc705cc0 Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel/components/tracked_button/untracked_icon.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel_icon.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel_icon.png new file mode 100644 index 00000000..7dc707fe Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/panel_icon.png differ diff --git a/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/plus.png b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/plus.png new file mode 100644 index 00000000..50c22bb0 Binary files /dev/null and b/tasks-tracker-plugin-master/src/main/resources/net/reldo/taskstracker/plus.png differ diff --git a/tasks-tracker-plugin-master/src/test/java/net/reldo/taskstracker/TasksTrackerPluginTest.java b/tasks-tracker-plugin-master/src/test/java/net/reldo/taskstracker/TasksTrackerPluginTest.java new file mode 100644 index 00000000..abca993c --- /dev/null +++ b/tasks-tracker-plugin-master/src/test/java/net/reldo/taskstracker/TasksTrackerPluginTest.java @@ -0,0 +1,13 @@ +package net.reldo.taskstracker; + +import net.runelite.client.RuneLite; +import net.runelite.client.externalplugins.ExternalPluginManager; + +public class TasksTrackerPluginTest +{ + public static void main(String[] args) throws Exception + { + ExternalPluginManager.loadBuiltin(TasksTrackerPlugin.class); + RuneLite.main(args); + } +} \ No newline at end of file