From eff53837a305dbb2a26a63db302ebdcbccf5fd54 Mon Sep 17 00:00:00 2001 From: idylls Date: Fri, 16 Dec 2022 15:46:20 -0500 Subject: [PATCH] Render tick tock overlay near mouse --- .gitignore | 5 + build.gradle.kts | 56 ++++ gradlew | 240 ++++++++++++++++++ gradlew.bat | 91 +++++++ runelite-plugin.properties | 5 + settings.gradle.kts | 10 + src/main/kotlin/net/idylls/tickle/Overlay.kt | 58 +++++ src/main/kotlin/net/idylls/tickle/Panel.kt | 40 +++ .../kotlin/net/idylls/tickle/TicklePlugin.kt | 70 +++++ .../resources/net/idylls/tickle/icon_temp.png | Bin 0 -> 7810 bytes 10 files changed, 575 insertions(+) create mode 100644 .gitignore create mode 100644 build.gradle.kts create mode 100755 gradlew create mode 100644 gradlew.bat create mode 100644 runelite-plugin.properties create mode 100644 settings.gradle.kts create mode 100644 src/main/kotlin/net/idylls/tickle/Overlay.kt create mode 100644 src/main/kotlin/net/idylls/tickle/Panel.kt create mode 100644 src/main/kotlin/net/idylls/tickle/TicklePlugin.kt create mode 100644 src/main/resources/net/idylls/tickle/icon_temp.png diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1b6985c --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +# Ignore Gradle project-specific cache directory +.gradle + +# Ignore Gradle build output directory +build diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..4824b18 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,56 @@ +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar + +plugins { + kotlin("jvm") version "1.7.21" + id("com.github.johnrengelman.shadow") version "7.1.0" +} + +repositories { + mavenCentral() + mavenLocal() + maven { + url = uri("https://repo.runelite.net") + } +} + +val runeLiteVersion = "latest.release" + +dependencies { + compileOnly(group = "net.runelite", name = "client", version = runeLiteVersion) + + compileOnly("javax.inject:javax.inject:1") + compileOnly("org.projectlombok:lombok:1.18.20") + annotationProcessor("org.projectlombok:lombok:1.18.20") +} + +tasks { + named("shadowJar") { + exclude("module-info.class") + exclude("META-INF/") + exclude("*META-INF*") + exclude("META-INF*") + exclude("*META-INF") + exclude("META-INF") + } +} + +tasks { + build { + dependsOn(shadowJar) + } +} + +tasks { + compileKotlin { + kotlinOptions { + jvmTarget = "1.8" + freeCompilerArgs = listOf("-Xjvm-default=all") + } + } + compileTestKotlin { + kotlinOptions { + jvmTarget = "1.8" + freeCompilerArgs = listOf("-Xjvm-default=all") + } + } +} diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..a69d9cb --- /dev/null +++ b/gradlew @@ -0,0 +1,240 @@ +#!/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. +# + +############################################################################## +# +# 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/master/subprojects/plugins/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 + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# 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"' + +# 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 + which java >/dev/null 2>&1 || 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 + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + 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 + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +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/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..53a6b23 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,91 @@ +@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 + +@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=. +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. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +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/runelite-plugin.properties b/runelite-plugin.properties new file mode 100644 index 0000000..d704051 --- /dev/null +++ b/runelite-plugin.properties @@ -0,0 +1,5 @@ +displayName=Tickle +author=Idylls +description=Tick Tracking Utilities +tags=tick,ticks +plugins=net.idylls.TickleKt diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..9a71819 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,10 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * The settings file is used to specify which projects to include in your build. + * + * Detailed information about configuring a multi-project build in Gradle can be found + * in the user manual at https://docs.gradle.org/7.5.1/userguide/multi_project_builds.html + */ + +rootProject.name = "tickle" diff --git a/src/main/kotlin/net/idylls/tickle/Overlay.kt b/src/main/kotlin/net/idylls/tickle/Overlay.kt new file mode 100644 index 0000000..bce2611 --- /dev/null +++ b/src/main/kotlin/net/idylls/tickle/Overlay.kt @@ -0,0 +1,58 @@ +package net.idylls.tickle + +import org.slf4j.LoggerFactory + +import java.awt.Graphics2D +import java.awt.Dimension +import java.awt.Rectangle +import java.awt.Color +import java.awt.BasicStroke + +import javax.inject.Inject + +import net.runelite.api.Client +import net.runelite.client.ui.overlay.OverlayLayer +import net.runelite.client.ui.overlay.OverlayPosition +import net.runelite.client.ui.overlay.OverlayPriority +import net.runelite.client.ui.overlay.OverlayUtil +import net.runelite.api.Point + +class Overlay +@Inject constructor( + val client: Client, + val plugin: TicklePlugin, +): net.runelite.client.ui.overlay.Overlay() { + val log = LoggerFactory.getLogger(TicklePlugin::class.java) + + init { + this.position = OverlayPosition.DYNAMIC + this.layer = OverlayLayer.ALWAYS_ON_TOP + this.priority = OverlayPriority.MED + } + + override public fun render(gfx: Graphics2D): Dimension? { + val mousePos = client.getMouseCanvasPosition() + val mousePos_ = Point(mousePos.getX() + 10, mousePos.getY() + 10) + + renderTickTock(gfx, mousePos_) + + return null + } + + fun renderTickTock(gfx: Graphics2D, mousePos: Point) { + val color = when (plugin.ticksSinceLogin % 2) { + 0 -> Color(255, 0, 0) + else -> Color(0, 255, 0) + } + + val shape = Rectangle(mousePos.getX(), mousePos.getY(), 10, 10) + + OverlayUtil.renderPolygon( + gfx, + shape, + null, + color, + BasicStroke(0.0f), + ) + } +} diff --git a/src/main/kotlin/net/idylls/tickle/Panel.kt b/src/main/kotlin/net/idylls/tickle/Panel.kt new file mode 100644 index 0000000..84cc834 --- /dev/null +++ b/src/main/kotlin/net/idylls/tickle/Panel.kt @@ -0,0 +1,40 @@ +package net.idylls.tickle + +import org.slf4j.LoggerFactory + +import java.awt.GridBagLayout +import java.awt.BorderLayout +import java.awt.event.ActionEvent +import java.awt.event.FocusEvent +import java.awt.event.FocusListener +import java.awt.Color + +import javax.swing.AbstractAction +import javax.swing.BorderFactory +import javax.swing.JPanel +import javax.swing.JTextArea +import javax.swing.KeyStroke +import javax.swing.border.EmptyBorder +import javax.swing.text.BadLocationException +import javax.swing.text.Document +import javax.swing.undo.CannotUndoException +import javax.swing.undo.UndoManager + +import net.runelite.client.ui.ColorScheme +import net.runelite.client.ui.PluginPanel + +class Panel : PluginPanel() { + val log = LoggerFactory.getLogger(Panel::class.java) + + init { + this.setLayout(BorderLayout()) + this.setBorder(BorderFactory.createEmptyBorder(10, 10, 0, 10)) + this.setBackground(ColorScheme.DARK_GRAY_COLOR) + + this.log.info("Initialized Tickle panel") + + val panel = JPanel() + + this.add(panel) + } +} diff --git a/src/main/kotlin/net/idylls/tickle/TicklePlugin.kt b/src/main/kotlin/net/idylls/tickle/TicklePlugin.kt new file mode 100644 index 0000000..0a4904d --- /dev/null +++ b/src/main/kotlin/net/idylls/tickle/TicklePlugin.kt @@ -0,0 +1,70 @@ +package net.idylls.tickle + +import org.slf4j.LoggerFactory + +import com.google.inject.Provides +import javax.inject.Inject + +import net.runelite.api.Client +import net.runelite.client.plugins.Plugin +import net.runelite.client.plugins.PluginDescriptor +import net.runelite.client.config.ConfigManager +import net.runelite.client.ui.overlay.OverlayManager +import net.runelite.client.ui.ClientToolbar +import net.runelite.client.ui.NavigationButton +import net.runelite.client.eventbus.Subscribe +import net.runelite.api.events.GameTick; +import net.runelite.client.util.ImageUtil + +@PluginDescriptor( + name = "Tickle" +) +public class TicklePlugin : Plugin() { + val log = LoggerFactory.getLogger(TicklePlugin::class.java) + + @Inject + lateinit var clientToolbar: ClientToolbar + + @Inject + lateinit var client: Client + + @Inject + lateinit final var overlayManager: OverlayManager + + @Inject + lateinit var panel: Panel + + @Inject + lateinit var overlay: Overlay + + lateinit var navButton: NavigationButton; + + var ticksSinceLogin: Int = 0 + + override fun startUp() { + log.info("Tickle started") + + val icon = ImageUtil.loadImageResource(this::class.java, "icon_temp.png"); + + this.navButton = NavigationButton.builder() + .tooltip("Tickle") + .icon(icon) + .priority(8) + .panel(panel) + .build() + + this.clientToolbar.addNavigation(this.navButton) + + this.overlayManager.add(this.overlay) + } + + override fun shutDown() { + this.clientToolbar.removeNavigation(this.navButton) + this.overlayManager.remove(this.overlay) + } + + @Subscribe + fun onGameTick(tick: GameTick) { + ticksSinceLogin++ + } +} diff --git a/src/main/resources/net/idylls/tickle/icon_temp.png b/src/main/resources/net/idylls/tickle/icon_temp.png new file mode 100644 index 0000000000000000000000000000000000000000..ed7ee84ec7ec7bbbe315a85f27adece3861dff83 GIT binary patch literal 7810 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