diff --git a/cache-client/pom.xml b/cache-client/pom.xml
index f4381ce9ae..9ad16f216c 100644
--- a/cache-client/pom.xml
+++ b/cache-client/pom.xml
@@ -29,7 +29,7 @@
net.runelite
runelite-parent
- 1.5.12-SNAPSHOT
+ 1.5.14-SNAPSHOT
cache-client
diff --git a/cache-updater/pom.xml b/cache-updater/pom.xml
index 63450786f6..f6d5cc5ffa 100644
--- a/cache-updater/pom.xml
+++ b/cache-updater/pom.xml
@@ -28,7 +28,7 @@
net.runelite
runelite-parent
- 1.5.12-SNAPSHOT
+ 1.5.14-SNAPSHOT
Cache Updater
diff --git a/cache/pom.xml b/cache/pom.xml
index 947dd7b667..072a7e4767 100644
--- a/cache/pom.xml
+++ b/cache/pom.xml
@@ -29,7 +29,7 @@
net.runelite
runelite-parent
- 1.5.12-SNAPSHOT
+ 1.5.14-SNAPSHOT
cache
diff --git a/http-api/pom.xml b/http-api/pom.xml
index 5ab155acba..910cacdbd6 100644
--- a/http-api/pom.xml
+++ b/http-api/pom.xml
@@ -28,7 +28,7 @@
net.runelite
runelite-parent
- 1.5.12-SNAPSHOT
+ 1.5.14-SNAPSHOT
Web API
diff --git a/http-api/src/main/java/net/runelite/http/api/ge/GrandExchangeClient.java b/http-api/src/main/java/net/runelite/http/api/ge/GrandExchangeClient.java
new file mode 100644
index 0000000000..a1b97bc390
--- /dev/null
+++ b/http-api/src/main/java/net/runelite/http/api/ge/GrandExchangeClient.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2019, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package net.runelite.http.api.ge;
+
+import com.google.gson.Gson;
+import java.io.IOException;
+import java.util.UUID;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import net.runelite.http.api.RuneLiteAPI;
+import okhttp3.Call;
+import okhttp3.Callback;
+import okhttp3.HttpUrl;
+import okhttp3.MediaType;
+import okhttp3.Request;
+import okhttp3.RequestBody;
+import okhttp3.Response;
+
+@Slf4j
+@AllArgsConstructor
+public class GrandExchangeClient
+{
+ private static final MediaType JSON = MediaType.parse("application/json");
+ private static final Gson GSON = RuneLiteAPI.GSON;
+
+ private final UUID uuid;
+
+ public void submit(GrandExchangeTrade grandExchangeTrade)
+ {
+ final HttpUrl url = RuneLiteAPI.getApiBase().newBuilder()
+ .addPathSegment("ge")
+ .build();
+
+ Request request = new Request.Builder()
+ .header(RuneLiteAPI.RUNELITE_AUTH, uuid.toString())
+ .post(RequestBody.create(JSON, GSON.toJson(grandExchangeTrade)))
+ .url(url)
+ .build();
+
+ RuneLiteAPI.CLIENT.newCall(request).enqueue(new Callback()
+ {
+ @Override
+ public void onFailure(Call call, IOException e)
+ {
+ log.debug("unable to submit trade", e);
+ }
+
+ @Override
+ public void onResponse(Call call, Response response)
+ {
+ log.debug("Submitted trade");
+ response.close();
+ }
+ });
+ }
+}
diff --git a/http-api/src/main/java/net/runelite/http/api/ge/GrandExchangeTrade.java b/http-api/src/main/java/net/runelite/http/api/ge/GrandExchangeTrade.java
new file mode 100644
index 0000000000..b5d0012b16
--- /dev/null
+++ b/http-api/src/main/java/net/runelite/http/api/ge/GrandExchangeTrade.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2019, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package net.runelite.http.api.ge;
+
+import java.time.Instant;
+import lombok.Data;
+
+@Data
+public class GrandExchangeTrade
+{
+ private boolean buy;
+ private int itemId;
+ private int quantity;
+ private int price;
+ private Instant time;
+}
diff --git a/http-api/src/main/java/net/runelite/http/api/loottracker/LootRecord.java b/http-api/src/main/java/net/runelite/http/api/loottracker/LootRecord.java
index fb43a1605a..fc945220f1 100644
--- a/http-api/src/main/java/net/runelite/http/api/loottracker/LootRecord.java
+++ b/http-api/src/main/java/net/runelite/http/api/loottracker/LootRecord.java
@@ -24,6 +24,7 @@
*/
package net.runelite.http.api.loottracker;
+import java.time.Instant;
import java.util.Collection;
import lombok.AllArgsConstructor;
import lombok.Data;
@@ -37,4 +38,5 @@ public class LootRecord
private String eventId;
private LootRecordType type;
private Collection drops;
+ private Instant time;
}
diff --git a/http-api/src/main/java/net/runelite/http/api/osbuddy/GrandExchangeClient.java b/http-api/src/main/java/net/runelite/http/api/osbuddy/OSBGrandExchangeClient.java
similarity index 92%
rename from http-api/src/main/java/net/runelite/http/api/osbuddy/GrandExchangeClient.java
rename to http-api/src/main/java/net/runelite/http/api/osbuddy/OSBGrandExchangeClient.java
index d80a739d29..ffdfad2035 100644
--- a/http-api/src/main/java/net/runelite/http/api/osbuddy/GrandExchangeClient.java
+++ b/http-api/src/main/java/net/runelite/http/api/osbuddy/OSBGrandExchangeClient.java
@@ -35,9 +35,9 @@ import okhttp3.Request;
import okhttp3.Response;
@Slf4j
-public class GrandExchangeClient
+public class OSBGrandExchangeClient
{
- public GrandExchangeResult lookupItem(int itemId) throws IOException
+ public OSBGrandExchangeResult lookupItem(int itemId) throws IOException
{
final HttpUrl url = RuneLiteAPI.getApiBase().newBuilder()
.addPathSegment("osb")
@@ -59,7 +59,7 @@ public class GrandExchangeClient
}
final InputStream in = response.body().byteStream();
- return RuneLiteAPI.GSON.fromJson(new InputStreamReader(in), GrandExchangeResult.class);
+ return RuneLiteAPI.GSON.fromJson(new InputStreamReader(in), OSBGrandExchangeResult.class);
}
catch (JsonParseException ex)
{
diff --git a/http-api/src/main/java/net/runelite/http/api/osbuddy/GrandExchangeResult.java b/http-api/src/main/java/net/runelite/http/api/osbuddy/OSBGrandExchangeResult.java
similarity index 97%
rename from http-api/src/main/java/net/runelite/http/api/osbuddy/GrandExchangeResult.java
rename to http-api/src/main/java/net/runelite/http/api/osbuddy/OSBGrandExchangeResult.java
index 993ee0d567..13e5f5e8e4 100644
--- a/http-api/src/main/java/net/runelite/http/api/osbuddy/GrandExchangeResult.java
+++ b/http-api/src/main/java/net/runelite/http/api/osbuddy/OSBGrandExchangeResult.java
@@ -28,7 +28,7 @@ import java.time.Instant;
import lombok.Data;
@Data
-public class GrandExchangeResult
+public class OSBGrandExchangeResult
{
private int item_id;
private int buy_average;
diff --git a/http-api/src/main/java/net/runelite/http/api/ws/messages/party/Join.java b/http-api/src/main/java/net/runelite/http/api/ws/messages/party/Join.java
index 1c4cf2f73a..21aed0f653 100644
--- a/http-api/src/main/java/net/runelite/http/api/ws/messages/party/Join.java
+++ b/http-api/src/main/java/net/runelite/http/api/ws/messages/party/Join.java
@@ -25,10 +25,12 @@
package net.runelite.http.api.ws.messages.party;
import java.util.UUID;
+import lombok.EqualsAndHashCode;
import lombok.Value;
import net.runelite.http.api.ws.WebsocketMessage;
@Value
+@EqualsAndHashCode(callSuper = true)
public class Join extends WebsocketMessage
{
private final UUID partyId;
diff --git a/http-api/src/main/java/net/runelite/http/api/ws/messages/party/UserJoin.java b/http-api/src/main/java/net/runelite/http/api/ws/messages/party/UserJoin.java
index 05d27b6790..81abe76827 100644
--- a/http-api/src/main/java/net/runelite/http/api/ws/messages/party/UserJoin.java
+++ b/http-api/src/main/java/net/runelite/http/api/ws/messages/party/UserJoin.java
@@ -25,10 +25,12 @@
package net.runelite.http.api.ws.messages.party;
import java.util.UUID;
+import lombok.EqualsAndHashCode;
import lombok.Value;
import net.runelite.http.api.ws.WebsocketMessage;
@Value
+@EqualsAndHashCode(callSuper = true)
public class UserJoin extends WebsocketMessage
{
private final UUID memberId;
diff --git a/http-api/src/main/java/net/runelite/http/api/ws/messages/party/UserPart.java b/http-api/src/main/java/net/runelite/http/api/ws/messages/party/UserPart.java
index e3efbe4a9c..e80c6002bd 100644
--- a/http-api/src/main/java/net/runelite/http/api/ws/messages/party/UserPart.java
+++ b/http-api/src/main/java/net/runelite/http/api/ws/messages/party/UserPart.java
@@ -25,10 +25,12 @@
package net.runelite.http.api.ws.messages.party;
import java.util.UUID;
+import lombok.EqualsAndHashCode;
import lombok.Value;
import net.runelite.http.api.ws.WebsocketMessage;
@Value
+@EqualsAndHashCode(callSuper = true)
public class UserPart extends WebsocketMessage
{
private final UUID memberId;
diff --git a/http-api/src/main/java/net/runelite/http/api/ws/messages/party/UserSync.java b/http-api/src/main/java/net/runelite/http/api/ws/messages/party/UserSync.java
index eca9844845..c95038c9fa 100644
--- a/http-api/src/main/java/net/runelite/http/api/ws/messages/party/UserSync.java
+++ b/http-api/src/main/java/net/runelite/http/api/ws/messages/party/UserSync.java
@@ -24,9 +24,11 @@
*/
package net.runelite.http.api.ws.messages.party;
+import lombok.EqualsAndHashCode;
import lombok.Value;
@Value
+@EqualsAndHashCode(callSuper = true)
public class UserSync extends PartyMemberMessage
{
}
diff --git a/http-service/pom.xml b/http-service/pom.xml
index 081195944c..d1217f0ced 100644
--- a/http-service/pom.xml
+++ b/http-service/pom.xml
@@ -28,7 +28,7 @@
net.runelite
runelite-parent
- 1.5.12-SNAPSHOT
+ 1.5.14-SNAPSHOT
Web Service
@@ -55,6 +55,10 @@
spring-boot-devtools
true
+
+ org.springframework
+ spring-jdbc
+
org.mapstruct
@@ -130,6 +134,11 @@
3.7.0
test
+
+ com.h2database
+ h2
+ test
+
diff --git a/http-service/src/main/java/net/runelite/http/service/SpringBootWebApplication.java b/http-service/src/main/java/net/runelite/http/service/SpringBootWebApplication.java
index 562d93445a..bfdac0a254 100644
--- a/http-service/src/main/java/net/runelite/http/service/SpringBootWebApplication.java
+++ b/http-service/src/main/java/net/runelite/http/service/SpringBootWebApplication.java
@@ -25,13 +25,11 @@
package net.runelite.http.service;
import ch.qos.logback.classic.LoggerContext;
+import com.google.common.base.Strings;
import java.io.IOException;
import java.time.Instant;
import java.util.HashMap;
import java.util.Map;
-import javax.naming.Context;
-import javax.naming.InitialContext;
-import javax.naming.NamingException;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
@@ -44,11 +42,17 @@ import okhttp3.Cache;
import okhttp3.OkHttpClient;
import org.slf4j.ILoggerFactory;
import org.slf4j.impl.StaticLoggerBinder;
+import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.support.SpringBootServletInitializer;
import org.springframework.context.annotation.Bean;
+import org.springframework.jdbc.datasource.lookup.JndiDataSourceLookup;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.sql2o.Sql2o;
import org.sql2o.converters.Converter;
@@ -56,6 +60,7 @@ import org.sql2o.quirks.NoQuirks;
@SpringBootApplication
@EnableScheduling
+@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class})
@Slf4j
public class SpringBootWebApplication extends SpringBootServletInitializer
{
@@ -96,35 +101,80 @@ public class SpringBootWebApplication extends SpringBootServletInitializer
};
}
- private Context getContext() throws NamingException
+ @ConfigurationProperties(prefix = "datasource.runelite")
+ @Bean("dataSourceRuneLite")
+ public DataSourceProperties dataSourceProperties()
{
- Context initCtx = new InitialContext();
- return (Context) initCtx.lookup("java:comp/env");
+ return new DataSourceProperties();
+ }
+
+ @ConfigurationProperties(prefix = "datasource.runelite-cache")
+ @Bean("dataSourceRuneLiteCache")
+ public DataSourceProperties dataSourcePropertiesCache()
+ {
+ return new DataSourceProperties();
+ }
+
+ @ConfigurationProperties(prefix = "datasource.runelite-tracker")
+ @Bean("dataSourceRuneLiteTracker")
+ public DataSourceProperties dataSourcePropertiesTracker()
+ {
+ return new DataSourceProperties();
+ }
+
+ @Bean(value = "runelite", destroyMethod = "")
+ public DataSource runeliteDataSource(@Qualifier("dataSourceRuneLite") DataSourceProperties dataSourceProperties)
+ {
+ return getDataSource(dataSourceProperties);
+ }
+
+ @Bean(value = "runelite-cache", destroyMethod = "")
+ public DataSource runeliteCache2DataSource(@Qualifier("dataSourceRuneLiteCache") DataSourceProperties dataSourceProperties)
+ {
+ return getDataSource(dataSourceProperties);
+ }
+
+ @Bean(value = "runelite-tracker", destroyMethod = "")
+ public DataSource runeliteTrackerDataSource(@Qualifier("dataSourceRuneLiteTracker") DataSourceProperties dataSourceProperties)
+ {
+ return getDataSource(dataSourceProperties);
}
@Bean("Runelite SQL2O")
- Sql2o sql2o() throws NamingException
+ public Sql2o sql2o(@Qualifier("runelite") DataSource dataSource)
{
- DataSource dataSource = (DataSource) getContext().lookup("jdbc/runelite");
- Map converters = new HashMap<>();
- converters.put(Instant.class, new InstantConverter());
- return new Sql2o(dataSource, new NoQuirks(converters));
+ return createSql2oFromDataSource(dataSource);
}
@Bean("Runelite Cache SQL2O")
- Sql2o cacheSql2o() throws NamingException
+ public Sql2o cacheSql2o(@Qualifier("runelite-cache") DataSource dataSource)
{
- DataSource dataSource = (DataSource) getContext().lookup("jdbc/runelite-cache2");
- Map converters = new HashMap<>();
- converters.put(Instant.class, new InstantConverter());
- return new Sql2o(dataSource, new NoQuirks(converters));
+ return createSql2oFromDataSource(dataSource);
}
@Bean("Runelite XP Tracker SQL2O")
- Sql2o trackerSql2o() throws NamingException
+ public Sql2o trackerSql2o(@Qualifier("runelite-tracker") DataSource dataSource)
{
- DataSource dataSource = (DataSource) getContext().lookup("jdbc/runelite-tracker");
- Map converters = new HashMap<>();
+ return createSql2oFromDataSource(dataSource);
+ }
+
+ private static DataSource getDataSource(DataSourceProperties dataSourceProperties)
+ {
+ if (!Strings.isNullOrEmpty(dataSourceProperties.getJndiName()))
+ {
+ // Use JNDI provided datasource, which is already configured with pooling
+ JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
+ return dataSourceLookup.getDataSource(dataSourceProperties.getJndiName());
+ }
+ else
+ {
+ return dataSourceProperties.initializeDataSourceBuilder().build();
+ }
+ }
+
+ private static Sql2o createSql2oFromDataSource(final DataSource dataSource)
+ {
+ final Map converters = new HashMap<>();
converters.put(Instant.class, new InstantConverter());
return new Sql2o(dataSource, new NoQuirks(converters));
}
diff --git a/http-service/src/main/java/net/runelite/http/service/SpringContentNegotiationConfigurer.java b/http-service/src/main/java/net/runelite/http/service/SpringWebMvcConfigurer.java
similarity index 67%
rename from http-service/src/main/java/net/runelite/http/service/SpringContentNegotiationConfigurer.java
rename to http-service/src/main/java/net/runelite/http/service/SpringWebMvcConfigurer.java
index ed055d0bdf..704e4f9cb2 100644
--- a/http-service/src/main/java/net/runelite/http/service/SpringContentNegotiationConfigurer.java
+++ b/http-service/src/main/java/net/runelite/http/service/SpringWebMvcConfigurer.java
@@ -24,22 +24,42 @@
*/
package net.runelite.http.service;
+import java.util.List;
+import net.runelite.http.api.RuneLiteAPI;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.converter.json.GsonHttpMessageConverter;
+import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
-/**
- * Configure .js as application/json to trick Cloudflare into caching json responses
- */
@Configuration
@EnableWebMvc
-public class SpringContentNegotiationConfigurer extends WebMvcConfigurerAdapter
+public class SpringWebMvcConfigurer extends WebMvcConfigurerAdapter
{
+ /**
+ * Configure .js as application/json to trick Cloudflare into caching json responses
+ */
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer)
{
configurer.mediaType("js", MediaType.APPLICATION_JSON);
}
+
+ /**
+ * Use GSON instead of Jackson for JSON serialization
+ * @param converters
+ */
+ @Override
+ public void extendMessageConverters(List> converters)
+ {
+ // Could not figure out a better way to force GSON
+ converters.removeIf(MappingJackson2HttpMessageConverter.class::isInstance);
+
+ GsonHttpMessageConverter gsonHttpMessageConverter = new GsonHttpMessageConverter();
+ gsonHttpMessageConverter.setGson(RuneLiteAPI.GSON);
+ converters.add(gsonHttpMessageConverter);
+ }
}
diff --git a/http-service/src/main/java/net/runelite/http/service/account/AccountService.java b/http-service/src/main/java/net/runelite/http/service/account/AccountService.java
index 3ef3a98659..c78a25b9ff 100644
--- a/http-service/src/main/java/net/runelite/http/service/account/AccountService.java
+++ b/http-service/src/main/java/net/runelite/http/service/account/AccountService.java
@@ -45,8 +45,6 @@ import net.runelite.http.api.ws.messages.LoginResponse;
import net.runelite.http.service.account.beans.SessionEntry;
import net.runelite.http.service.account.beans.UserEntry;
import net.runelite.http.service.util.redis.RedisPool;
-import net.runelite.http.service.ws.SessionManager;
-import net.runelite.http.service.ws.WSService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -241,12 +239,6 @@ public class AccountService
LoginResponse response = new LoginResponse();
response.setUsername(username);
- WSService service = SessionManager.findSession(uuid);
- if (service != null)
- {
- service.send(response);
- }
-
try (Jedis jedis = jedisPool.getResource())
{
jedis.publish("session." + uuid, websocketGson.toJson(response, WebsocketMessage.class));
@@ -276,10 +268,4 @@ public class AccountService
{
auth.handle(request, response);
}
-
- @RequestMapping("/wscount")
- public int wscount()
- {
- return SessionManager.getCount();
- }
}
diff --git a/http-service/src/main/java/net/runelite/http/service/cache/CacheService.java b/http-service/src/main/java/net/runelite/http/service/cache/CacheService.java
index 882e90c460..e0fca4ac21 100644
--- a/http-service/src/main/java/net/runelite/http/service/cache/CacheService.java
+++ b/http-service/src/main/java/net/runelite/http/service/cache/CacheService.java
@@ -41,6 +41,7 @@ import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import net.runelite.cache.ConfigType;
@@ -233,6 +234,11 @@ public class CacheService
public List getItems() throws IOException
{
CacheEntry cache = findMostRecent();
+ if (cache == null)
+ {
+ return Collections.emptyList();
+ }
+
IndexEntry indexEntry = findIndexForCache(cache, IndexType.CONFIGS.getNumber());
ArchiveEntry archiveEntry = findArchiveForIndex(indexEntry, ConfigType.ITEM.getId());
ArchiveFiles archiveFiles = getArchiveFiles(archiveEntry);
diff --git a/http-service/src/main/java/net/runelite/http/service/config/ConfigController.java b/http-service/src/main/java/net/runelite/http/service/config/ConfigController.java
new file mode 100644
index 0000000000..966d49cda2
--- /dev/null
+++ b/http-service/src/main/java/net/runelite/http/service/config/ConfigController.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2019, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package net.runelite.http.service.config;
+
+import java.io.IOException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import net.runelite.http.api.config.Configuration;
+import net.runelite.http.service.account.AuthFilter;
+import net.runelite.http.service.account.beans.SessionEntry;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import static org.springframework.web.bind.annotation.RequestMethod.DELETE;
+import static org.springframework.web.bind.annotation.RequestMethod.PUT;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/config")
+public class ConfigController
+{
+ private final ConfigService configService;
+ private final AuthFilter authFilter;
+
+ @Autowired
+ public ConfigController(ConfigService configService, AuthFilter authFilter)
+ {
+ this.configService = configService;
+ this.authFilter = authFilter;
+ }
+
+ @RequestMapping
+ public Configuration get(HttpServletRequest request, HttpServletResponse response) throws IOException
+ {
+ SessionEntry session = authFilter.handle(request, response);
+
+ if (session == null)
+ {
+ return null;
+ }
+
+ return configService.get(session.getUser());
+ }
+
+ @RequestMapping(path = "/{key:.+}", method = PUT)
+ public void setKey(
+ HttpServletRequest request,
+ HttpServletResponse response,
+ @PathVariable String key,
+ @RequestBody(required = false) String value
+ ) throws IOException
+ {
+ SessionEntry session = authFilter.handle(request, response);
+
+ if (session == null)
+ {
+ return;
+ }
+
+ configService.setKey(session.getUser(), key, value);
+ }
+
+ @RequestMapping(path = "/{key:.+}", method = DELETE)
+ public void unsetKey(
+ HttpServletRequest request,
+ HttpServletResponse response,
+ @PathVariable String key
+ ) throws IOException
+ {
+ SessionEntry session = authFilter.handle(request, response);
+
+ if (session == null)
+ {
+ return;
+ }
+
+ configService.unsetKey(session.getUser(), key);
+ }
+}
diff --git a/http-service/src/main/java/net/runelite/http/service/config/ConfigService.java b/http-service/src/main/java/net/runelite/http/service/config/ConfigService.java
index 102e2f9c7a..ed96f36cf4 100644
--- a/http-service/src/main/java/net/runelite/http/service/config/ConfigService.java
+++ b/http-service/src/main/java/net/runelite/http/service/config/ConfigService.java
@@ -24,28 +24,18 @@
*/
package net.runelite.http.service.config;
-import java.io.IOException;
import java.util.List;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import javax.annotation.Nullable;
import net.runelite.http.api.config.ConfigEntry;
import net.runelite.http.api.config.Configuration;
-import net.runelite.http.service.account.AuthFilter;
-import net.runelite.http.service.account.beans.SessionEntry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import static org.springframework.web.bind.annotation.RequestMethod.DELETE;
-import static org.springframework.web.bind.annotation.RequestMethod.PUT;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.stereotype.Service;
import org.sql2o.Connection;
import org.sql2o.Sql2o;
import org.sql2o.Sql2oException;
-@RestController
-@RequestMapping("/config")
+@Service
public class ConfigService
{
private static final String CREATE_CONFIG = "CREATE TABLE IF NOT EXISTS `config` (\n"
@@ -59,16 +49,13 @@ public class ConfigService
+ " ADD CONSTRAINT `user_fk` FOREIGN KEY (`user`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;";
private final Sql2o sql2o;
- private final AuthFilter auth;
@Autowired
public ConfigService(
- @Qualifier("Runelite SQL2O") Sql2o sql2o,
- AuthFilter auth
+ @Qualifier("Runelite SQL2O") Sql2o sql2o
)
{
this.sql2o = sql2o;
- this.auth = auth;
try (Connection con = sql2o.open())
{
@@ -87,71 +74,45 @@ public class ConfigService
}
}
- @RequestMapping
- public Configuration get(HttpServletRequest request, HttpServletResponse response) throws IOException
+ public Configuration get(int userId)
{
- SessionEntry session = auth.handle(request, response);
-
- if (session == null)
- {
- return null;
- }
-
List config;
try (Connection con = sql2o.open())
{
config = con.createQuery("select `key`, value from config where user = :user")
- .addParameter("user", session.getUser())
+ .addParameter("user", userId)
.executeAndFetch(ConfigEntry.class);
}
return new Configuration(config);
}
- @RequestMapping(path = "/{key:.+}", method = PUT)
public void setKey(
- HttpServletRequest request,
- HttpServletResponse response,
- @PathVariable String key,
- @RequestBody(required = false) String value
- ) throws IOException
+ int userId,
+ String key,
+ @Nullable String value
+ )
{
- SessionEntry session = auth.handle(request, response);
-
- if (session == null)
- {
- return;
- }
-
try (Connection con = sql2o.open())
{
con.createQuery("insert into config (user, `key`, value) values (:user, :key, :value) on duplicate key update `key` = :key, value = :value")
- .addParameter("user", session.getUser())
+ .addParameter("user", userId)
.addParameter("key", key)
.addParameter("value", value != null ? value : "")
.executeUpdate();
}
}
- @RequestMapping(path = "/{key:.+}", method = DELETE)
public void unsetKey(
- HttpServletRequest request,
- HttpServletResponse response,
- @PathVariable String key
- ) throws IOException
+ int userId,
+ String key
+ )
{
- SessionEntry session = auth.handle(request, response);
-
- if (session == null)
- {
- return;
- }
-
try (Connection con = sql2o.open())
{
con.createQuery("delete from config where user = :user and `key` = :key")
- .addParameter("user", session.getUser())
+ .addParameter("user", userId)
.addParameter("key", key)
.executeUpdate();
}
diff --git a/http-service/src/main/java/net/runelite/http/service/feed/FeedController.java b/http-service/src/main/java/net/runelite/http/service/feed/FeedController.java
index 8b55d1b93a..fa641d3714 100644
--- a/http-service/src/main/java/net/runelite/http/service/feed/FeedController.java
+++ b/http-service/src/main/java/net/runelite/http/service/feed/FeedController.java
@@ -71,7 +71,7 @@ public class FeedController
}
catch (IOException e)
{
- log.warn(null, e);
+ log.warn(e.getMessage());
}
try
@@ -80,7 +80,7 @@ public class FeedController
}
catch (IOException e)
{
- log.warn(null, e);
+ log.warn(e.getMessage());
}
try
@@ -89,7 +89,7 @@ public class FeedController
}
catch (IOException e)
{
- log.warn(null, e);
+ log.warn(e.getMessage());
}
feedResult = new FeedResult(items);
diff --git a/http-service/src/main/java/net/runelite/http/service/ge/GrandExchangeController.java b/http-service/src/main/java/net/runelite/http/service/ge/GrandExchangeController.java
new file mode 100644
index 0000000000..94c759b130
--- /dev/null
+++ b/http-service/src/main/java/net/runelite/http/service/ge/GrandExchangeController.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2019, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package net.runelite.http.service.ge;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.stream.Collectors;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import net.runelite.http.api.ge.GrandExchangeTrade;
+import net.runelite.http.service.account.AuthFilter;
+import net.runelite.http.service.account.beans.SessionEntry;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/ge")
+public class GrandExchangeController
+{
+ private final GrandExchangeService grandExchangeService;
+ private final AuthFilter authFilter;
+
+ @Autowired
+ public GrandExchangeController(GrandExchangeService grandExchangeService, AuthFilter authFilter)
+ {
+ this.grandExchangeService = grandExchangeService;
+ this.authFilter = authFilter;
+ }
+
+ @PostMapping
+ public void submit(HttpServletRequest request, HttpServletResponse response, @RequestBody GrandExchangeTrade grandExchangeTrade) throws IOException
+ {
+ SessionEntry session = authFilter.handle(request, response);
+
+ if (session == null)
+ {
+ return;
+ }
+
+ grandExchangeService.add(session.getUser(), grandExchangeTrade);
+ }
+
+ @GetMapping
+ public Collection get(HttpServletRequest request, HttpServletResponse response,
+ @RequestParam(required = false, defaultValue = "1024") int limit,
+ @RequestParam(required = false, defaultValue = "0") int offset) throws IOException
+ {
+ SessionEntry session = authFilter.handle(request, response);
+
+ if (session == null)
+ {
+ return null;
+ }
+
+ return grandExchangeService.get(session.getUser(), limit, offset).stream()
+ .map(GrandExchangeController::convert)
+ .collect(Collectors.toList());
+ }
+
+ private static GrandExchangeTrade convert(TradeEntry tradeEntry)
+ {
+ GrandExchangeTrade grandExchangeTrade = new GrandExchangeTrade();
+ grandExchangeTrade.setBuy(tradeEntry.getAction() == TradeAction.BUY);
+ grandExchangeTrade.setItemId(tradeEntry.getItem());
+ grandExchangeTrade.setQuantity(tradeEntry.getQuantity());
+ grandExchangeTrade.setPrice(tradeEntry.getPrice());
+ grandExchangeTrade.setTime(tradeEntry.getTime());
+ return grandExchangeTrade;
+ }
+
+ @DeleteMapping
+ public void delete(HttpServletRequest request, HttpServletResponse response) throws IOException
+ {
+ SessionEntry session = authFilter.handle(request, response);
+
+ if (session == null)
+ {
+ return;
+ }
+
+ grandExchangeService.delete(session.getUser());
+ }
+}
diff --git a/http-service/src/main/java/net/runelite/http/service/ge/GrandExchangeService.java b/http-service/src/main/java/net/runelite/http/service/ge/GrandExchangeService.java
new file mode 100644
index 0000000000..6456beb85f
--- /dev/null
+++ b/http-service/src/main/java/net/runelite/http/service/ge/GrandExchangeService.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2019, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package net.runelite.http.service.ge;
+
+import java.util.Collection;
+import net.runelite.http.api.ge.GrandExchangeTrade;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Service;
+import org.sql2o.Connection;
+import org.sql2o.Sql2o;
+
+@Service
+public class GrandExchangeService
+{
+ private static final String CREATE_TABLE = "CREATE TABLE IF NOT EXISTS `ge_trades` (\n" +
+ " `id` int(11) NOT NULL AUTO_INCREMENT,\n" +
+ " `user` int(11) NOT NULL,\n" +
+ " `action` enum('BUY','SELL') NOT NULL,\n" +
+ " `item` int(11) NOT NULL,\n" +
+ " `quantity` int(11) NOT NULL,\n" +
+ " `price` int(11) NOT NULL,\n" +
+ " `time` timestamp NOT NULL DEFAULT current_timestamp(),\n" +
+ " PRIMARY KEY (`id`),\n" +
+ " KEY `user_time` (`user`, `time`),\n" +
+ " KEY `time` (`time`),\n" +
+ " CONSTRAINT `ge_trades_ibfk_1` FOREIGN KEY (`user`) REFERENCES `users` (`id`)\n" +
+ ") ENGINE=InnoDB;";
+
+ private final Sql2o sql2o;
+
+ @Autowired
+ public GrandExchangeService(@Qualifier("Runelite SQL2O") Sql2o sql2o)
+ {
+ this.sql2o = sql2o;
+
+ // Ensure necessary tables exist
+ try (Connection con = sql2o.open())
+ {
+ con.createQuery(CREATE_TABLE).executeUpdate();
+ }
+ }
+
+ public void add(int userId, GrandExchangeTrade grandExchangeTrade)
+ {
+ try (Connection con = sql2o.open())
+ {
+ con.createQuery("insert into ge_trades (user, action, item, quantity, price) values (:user," +
+ " :action, :item, :quantity, :price)")
+ .addParameter("user", userId)
+ .addParameter("action", grandExchangeTrade.isBuy() ? "BUY" : "SELL")
+ .addParameter("item", grandExchangeTrade.getItemId())
+ .addParameter("quantity", grandExchangeTrade.getQuantity())
+ .addParameter("price", grandExchangeTrade.getPrice())
+ .executeUpdate();
+ }
+ }
+
+ public Collection get(int userId, int limit, int offset)
+ {
+ try (Connection con = sql2o.open())
+ {
+ return con.createQuery("select id, user, action, item, quantity, price, time from ge_trades where user = :user limit :limit offset :offset")
+ .addParameter("user", userId)
+ .addParameter("limit", limit)
+ .addParameter("offset", offset)
+ .executeAndFetch(TradeEntry.class);
+ }
+ }
+
+ public void delete(int userId)
+ {
+ try (Connection con = sql2o.open())
+ {
+ con.createQuery("delete from ge_trades where user = :user")
+ .addParameter("user", userId)
+ .executeUpdate();
+ }
+ }
+
+ @Scheduled(fixedDelay = 60 * 60 * 1000)
+ public void expire()
+ {
+ try (Connection con = sql2o.open())
+ {
+ con.createQuery("delete from ge_trades where time < current_timestamp - interval 1 month")
+ .executeUpdate();
+ }
+ }
+}
diff --git a/http-service/src/main/java/net/runelite/http/service/ge/TradeAction.java b/http-service/src/main/java/net/runelite/http/service/ge/TradeAction.java
new file mode 100644
index 0000000000..fcc96d615f
--- /dev/null
+++ b/http-service/src/main/java/net/runelite/http/service/ge/TradeAction.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2019, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package net.runelite.http.service.ge;
+
+enum TradeAction
+{
+ BUY,
+ SELL;
+}
diff --git a/http-service/src/main/java/net/runelite/http/service/ge/TradeEntry.java b/http-service/src/main/java/net/runelite/http/service/ge/TradeEntry.java
new file mode 100644
index 0000000000..bca3869811
--- /dev/null
+++ b/http-service/src/main/java/net/runelite/http/service/ge/TradeEntry.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2019, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package net.runelite.http.service.ge;
+
+import java.time.Instant;
+import lombok.Data;
+
+@Data
+class TradeEntry
+{
+ private int id;
+ private int user;
+ private TradeAction action;
+ private int item;
+ private int quantity;
+ private int price;
+ private Instant time;
+}
diff --git a/http-service/src/main/java/net/runelite/http/service/item/ItemService.java b/http-service/src/main/java/net/runelite/http/service/item/ItemService.java
index 2da7a50028..32569b0517 100644
--- a/http-service/src/main/java/net/runelite/http/service/item/ItemService.java
+++ b/http-service/src/main/java/net/runelite/http/service/item/ItemService.java
@@ -489,10 +489,16 @@ public class ItemService
public void reloadItems() throws IOException
{
List items = cacheService.getItems();
+ if (items.isEmpty())
+ {
+ log.warn("Failed to load any items from cache, item price updating will be disabled");
+ }
+
tradeableItems = items.stream()
.filter(item -> item.isTradeable)
.mapToInt(item -> item.id)
.toArray();
+
log.debug("Loaded {} tradeable items", tradeableItems.length);
}
diff --git a/http-service/src/main/java/net/runelite/http/service/loottracker/LootTrackerController.java b/http-service/src/main/java/net/runelite/http/service/loottracker/LootTrackerController.java
index 36e0a2333d..d5a09f44d2 100644
--- a/http-service/src/main/java/net/runelite/http/service/loottracker/LootTrackerController.java
+++ b/http-service/src/main/java/net/runelite/http/service/loottracker/LootTrackerController.java
@@ -66,7 +66,7 @@ public class LootTrackerController
}
@RequestMapping
- public Collection getLootRecords(HttpServletRequest request, HttpServletResponse response, @RequestParam(value = "count", defaultValue = "1024") int count) throws IOException
+ public Collection getLootRecords(HttpServletRequest request, HttpServletResponse response, @RequestParam(value = "count", defaultValue = "1024") int count, @RequestParam(value = "start", defaultValue = "0") int start) throws IOException
{
SessionEntry e = auth.handle(request, response);
if (e == null)
@@ -75,7 +75,7 @@ public class LootTrackerController
return null;
}
- return service.get(e.getUser(), count);
+ return service.get(e.getUser(), count, start);
}
@DeleteMapping
diff --git a/http-service/src/main/java/net/runelite/http/service/loottracker/LootTrackerService.java b/http-service/src/main/java/net/runelite/http/service/loottracker/LootTrackerService.java
index d39e159228..b999f4eabb 100644
--- a/http-service/src/main/java/net/runelite/http/service/loottracker/LootTrackerService.java
+++ b/http-service/src/main/java/net/runelite/http/service/loottracker/LootTrackerService.java
@@ -66,7 +66,7 @@ public class LootTrackerService
private static final String INSERT_KILL_QUERY = "INSERT INTO kills (accountId, type, eventId) VALUES (:accountId, :type, :eventId)";
private static final String INSERT_DROP_QUERY = "INSERT INTO drops (killId, itemId, itemQuantity) VALUES (LAST_INSERT_ID(), :itemId, :itemQuantity)";
- private static final String SELECT_LOOT_QUERY = "SELECT killId,time,type,eventId,itemId,itemQuantity FROM kills JOIN drops ON drops.killId = kills.id WHERE accountId = :accountId ORDER BY TIME DESC LIMIT :limit";
+ private static final String SELECT_LOOT_QUERY = "SELECT killId,time,type,eventId,itemId,itemQuantity FROM kills JOIN drops ON drops.killId = kills.id WHERE accountId = :accountId ORDER BY TIME DESC LIMIT :limit OFFSET :offset";
private static final String DELETE_LOOT_ACCOUNT = "DELETE FROM kills WHERE accountId = :accountId";
private static final String DELETE_LOOT_ACCOUNT_EVENTID = "DELETE FROM kills WHERE accountId = :accountId AND eventId = :eventId";
@@ -119,7 +119,7 @@ public class LootTrackerService
}
}
- public Collection get(int accountId, int limit)
+ public Collection get(int accountId, int limit, int offset)
{
List lootResults;
@@ -128,6 +128,7 @@ public class LootTrackerService
lootResults = con.createQuery(SELECT_LOOT_QUERY)
.addParameter("accountId", accountId)
.addParameter("limit", limit)
+ .addParameter("offset", offset)
.executeAndFetch(LootResult.class);
}
@@ -141,7 +142,7 @@ public class LootTrackerService
{
if (!gameItems.isEmpty())
{
- LootRecord lootRecord = new LootRecord(current.getEventId(), current.getType(), gameItems);
+ LootRecord lootRecord = new LootRecord(current.getEventId(), current.getType(), gameItems, current.getTime());
lootRecords.add(lootRecord);
gameItems = new ArrayList<>();
@@ -156,7 +157,7 @@ public class LootTrackerService
if (!gameItems.isEmpty())
{
- LootRecord lootRecord = new LootRecord(current.getEventId(), current.getType(), gameItems);
+ LootRecord lootRecord = new LootRecord(current.getEventId(), current.getType(), gameItems, current.getTime());
lootRecords.add(lootRecord);
}
diff --git a/http-service/src/main/java/net/runelite/http/service/osbuddy/GrandExchangeController.java b/http-service/src/main/java/net/runelite/http/service/osbuddy/OSBGrandExchangeController.java
similarity index 92%
rename from http-service/src/main/java/net/runelite/http/service/osbuddy/GrandExchangeController.java
rename to http-service/src/main/java/net/runelite/http/service/osbuddy/OSBGrandExchangeController.java
index 92ba9093e7..847c415772 100644
--- a/http-service/src/main/java/net/runelite/http/service/osbuddy/GrandExchangeController.java
+++ b/http-service/src/main/java/net/runelite/http/service/osbuddy/OSBGrandExchangeController.java
@@ -35,12 +35,12 @@ import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/osb/ge")
-public class GrandExchangeController
+public class OSBGrandExchangeController
{
- private final GrandExchangeService grandExchangeService;
+ private final OSBGrandExchangeService grandExchangeService;
@Autowired
- public GrandExchangeController(GrandExchangeService grandExchangeService)
+ public OSBGrandExchangeController(OSBGrandExchangeService grandExchangeService)
{
this.grandExchangeService = grandExchangeService;
}
diff --git a/http-service/src/main/java/net/runelite/http/service/osbuddy/GrandExchangeService.java b/http-service/src/main/java/net/runelite/http/service/osbuddy/OSBGrandExchangeService.java
similarity index 97%
rename from http-service/src/main/java/net/runelite/http/service/osbuddy/GrandExchangeService.java
rename to http-service/src/main/java/net/runelite/http/service/osbuddy/OSBGrandExchangeService.java
index f7addee899..797d7e76e2 100644
--- a/http-service/src/main/java/net/runelite/http/service/osbuddy/GrandExchangeService.java
+++ b/http-service/src/main/java/net/runelite/http/service/osbuddy/OSBGrandExchangeService.java
@@ -40,7 +40,7 @@ import org.sql2o.Sql2o;
@Service
@Slf4j
-public class GrandExchangeService
+public class OSBGrandExchangeService
{
private static final String CREATE_GRAND_EXCHANGE_PRICES = "CREATE TABLE IF NOT EXISTS `osb_ge` (\n"
+ " `item_id` int(11) NOT NULL,\n"
@@ -56,7 +56,7 @@ public class GrandExchangeService
private final Sql2o sql2o;
@Autowired
- public GrandExchangeService(@Qualifier("Runelite SQL2O") Sql2o sql2o)
+ public OSBGrandExchangeService(@Qualifier("Runelite SQL2O") Sql2o sql2o)
{
this.sql2o = sql2o;
diff --git a/http-service/src/main/java/net/runelite/http/service/ws/WSService.java b/http-service/src/main/java/net/runelite/http/service/ws/WSService.java
deleted file mode 100644
index 05489f2b87..0000000000
--- a/http-service/src/main/java/net/runelite/http/service/ws/WSService.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (c) 2017, Adam
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package net.runelite.http.service.ws;
-
-import com.google.gson.Gson;
-import java.util.UUID;
-import javax.websocket.CloseReason;
-import javax.websocket.EndpointConfig;
-import javax.websocket.OnClose;
-import javax.websocket.OnError;
-import javax.websocket.OnMessage;
-import javax.websocket.OnOpen;
-import javax.websocket.Session;
-import javax.websocket.server.ServerEndpoint;
-import lombok.AccessLevel;
-import lombok.Getter;
-import lombok.Setter;
-import net.runelite.http.api.ws.WebsocketGsonFactory;
-import net.runelite.http.api.ws.WebsocketMessage;
-import net.runelite.http.api.ws.messages.Handshake;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-@ServerEndpoint("/ws")
-public class WSService
-{
- private static final Logger logger = LoggerFactory.getLogger(WSService.class);
-
- private static final Gson gson = WebsocketGsonFactory.build();
-
- private Session session;
- @Getter(AccessLevel.PACKAGE)
- @Setter(AccessLevel.PACKAGE)
- private UUID uuid;
-
- public void send(WebsocketMessage message)
- {
- String json = gson.toJson(message, WebsocketMessage.class);
-
- logger.debug("Sending {}", json);
-
- session.getAsyncRemote().sendText(json);
- }
-
- @OnOpen
- public void onOpen(Session session, EndpointConfig config)
- {
- this.session = session;
- logger.debug("New session {}", session);
- }
-
- @OnClose
- public void onClose(Session session, CloseReason resaon)
- {
- SessionManager.remove(this);
- logger.debug("Close session {}", session);
- }
-
- @OnError
- public void onError(Session session, Throwable ex)
- {
- SessionManager.remove(this);
- logger.debug("Error in session {}", session, ex);
- }
-
- @OnMessage
- public void onMessage(Session session, String text)
- {
- WebsocketMessage message = gson.fromJson(text, WebsocketMessage.class);
- logger.debug("Got message: {}", message);
-
- if (message instanceof Handshake)
- {
- Handshake hs = (Handshake) message;
- SessionManager.changeSessionUID(this, hs.getSession());
- }
- }
-}
diff --git a/http-service/src/main/java/net/runelite/http/service/xp/XpMapper.java b/http-service/src/main/java/net/runelite/http/service/xp/XpMapper.java
index ad72f43c47..068c6e5438 100644
--- a/http-service/src/main/java/net/runelite/http/service/xp/XpMapper.java
+++ b/http-service/src/main/java/net/runelite/http/service/xp/XpMapper.java
@@ -38,6 +38,8 @@ public interface XpMapper
XpData xpEntityToXpData(XpEntity xpEntity);
+ @Mapping(target = "time", ignore = true)
+
@Mapping(source = "attack.experience", target = "attack_xp")
@Mapping(source = "defence.experience", target = "defence_xp")
@Mapping(source = "strength.experience", target = "strength_xp")
diff --git a/http-service/src/main/java/net/runelite/http/service/xp/XpTrackerService.java b/http-service/src/main/java/net/runelite/http/service/xp/XpTrackerService.java
index 391d37d1bf..9e8e0a5e06 100644
--- a/http-service/src/main/java/net/runelite/http/service/xp/XpTrackerService.java
+++ b/http-service/src/main/java/net/runelite/http/service/xp/XpTrackerService.java
@@ -29,8 +29,8 @@ import com.google.common.hash.Funnels;
import java.nio.charset.Charset;
import java.time.Duration;
import java.time.Instant;
+import java.util.ArrayDeque;
import java.util.Queue;
-import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.ExecutionException;
import lombok.extern.slf4j.Slf4j;
import net.runelite.http.api.hiscore.HiscoreEndpoint;
@@ -50,8 +50,8 @@ import org.sql2o.Sql2o;
@Slf4j
public class XpTrackerService
{
- private static final int QUEUE_LIMIT = 100_000;
- private static final Duration UPDATE_TIME = Duration.ofMinutes(5);
+ private static final int QUEUE_LIMIT = 32768;
+ private static final int BLOOMFILTER_EXPECTED_INSERTIONS = 100_000;
@Autowired
@Qualifier("Runelite XP Tracker SQL2O")
@@ -60,7 +60,7 @@ public class XpTrackerService
@Autowired
private HiscoreService hiscoreService;
- private final Queue usernameUpdateQueue = new ConcurrentLinkedDeque<>();
+ private final Queue usernameUpdateQueue = new ArrayDeque<>();
private BloomFilter usernameFilter = createFilter();
public void update(String username) throws ExecutionException
@@ -76,13 +76,31 @@ public class XpTrackerService
return;
}
- if (usernameUpdateQueue.size() >= QUEUE_LIMIT)
+ try (Connection con = sql2o.open())
{
- log.warn("Username update queue is full ({})", QUEUE_LIMIT);
- return;
+ PlayerEntity playerEntity = findOrCreatePlayer(con, username);
+ Duration frequency = updateFrequency(playerEntity);
+ Instant now = Instant.now();
+ Duration timeSinceLastUpdate = Duration.between(playerEntity.getLast_updated(), now);
+ if (timeSinceLastUpdate.toMillis() < frequency.toMillis())
+ {
+ log.debug("User {} updated too recently", username);
+ usernameFilter.put(username);
+ return;
+ }
+
+ synchronized (usernameUpdateQueue)
+ {
+ if (usernameUpdateQueue.size() >= QUEUE_LIMIT)
+ {
+ log.warn("Username update queue is full ({})", QUEUE_LIMIT);
+ return;
+ }
+
+ usernameUpdateQueue.add(username);
+ }
}
- usernameUpdateQueue.add(username);
usernameFilter.put(username);
}
@@ -104,13 +122,6 @@ public class XpTrackerService
log.debug("Hiscore for {} already up to date", username);
return;
}
-
- Duration difference = Duration.between(currentXp.getTime(), now);
- if (difference.compareTo(UPDATE_TIME) <= 0)
- {
- log.debug("Updated {} too recently", username);
- return;
- }
}
con.createQuery("insert into xp (player,attack_xp,defence_xp,strength_xp,hitpoints_xp,ranged_xp,prayer_xp,magic_xp,cooking_xp,woodcutting_xp,"
@@ -172,6 +183,11 @@ public class XpTrackerService
.addParameter("construction_rank", hiscoreResult.getConstruction().getRank())
.addParameter("overall_rank", hiscoreResult.getOverall().getRank())
.executeUpdate();
+
+ con.createQuery("update player set rank = :rank, last_updated = CURRENT_TIMESTAMP where id = :id")
+ .addParameter("id", playerEntity.getId())
+ .addParameter("rank", hiscoreResult.getOverall().getRank())
+ .executeUpdate();
}
}
@@ -197,6 +213,7 @@ public class XpTrackerService
playerEntity.setId(id);
playerEntity.setName(username);
playerEntity.setTracked_since(now);
+ playerEntity.setLast_updated(now);
return playerEntity;
}
@@ -220,18 +237,21 @@ public class XpTrackerService
@Scheduled(fixedDelay = 1000)
public void update() throws ExecutionException
{
- String next = usernameUpdateQueue.poll();
+ String next;
+ synchronized (usernameUpdateQueue)
+ {
+ next = usernameUpdateQueue.poll();
+ }
if (next == null)
{
return;
}
- HiscoreResult hiscoreResult = hiscoreService.lookupUsername(next, HiscoreEndpoint.NORMAL);
- update(next, hiscoreResult);
+ update(next);
}
- @Scheduled(fixedDelay = 3 * 60 * 60 * 1000) // 3 hours
+ @Scheduled(fixedDelay = 6 * 60 * 60 * 1000) // 6 hours
public void clearFilter()
{
usernameFilter = createFilter();
@@ -241,14 +261,47 @@ public class XpTrackerService
{
final BloomFilter filter = BloomFilter.create(
Funnels.stringFunnel(Charset.defaultCharset()),
- 100_000
+ BLOOMFILTER_EXPECTED_INSERTIONS
);
- for (String toUpdate : usernameUpdateQueue)
+ synchronized (usernameUpdateQueue)
{
- filter.put(toUpdate);
+ for (String toUpdate : usernameUpdateQueue)
+ {
+ filter.put(toUpdate);
+ }
}
return filter;
}
+
+ /**
+ * scale how often to check hiscore updates for players based on their rank
+ * @param playerEntity
+ * @return
+ */
+ private static Duration updateFrequency(PlayerEntity playerEntity)
+ {
+ Integer rank = playerEntity.getRank();
+ if (rank == null || rank == -1)
+ {
+ return Duration.ofDays(7);
+ }
+ else if (rank < 10_000)
+ {
+ return Duration.ofHours(6);
+ }
+ else if (rank < 50_000)
+ {
+ return Duration.ofDays(2);
+ }
+ else if (rank < 100_000)
+ {
+ return Duration.ofDays(5);
+ }
+ else
+ {
+ return Duration.ofDays(7);
+ }
+ }
}
diff --git a/http-service/src/main/java/net/runelite/http/service/xp/beans/PlayerEntity.java b/http-service/src/main/java/net/runelite/http/service/xp/beans/PlayerEntity.java
index d07d1d4640..11bec532d7 100644
--- a/http-service/src/main/java/net/runelite/http/service/xp/beans/PlayerEntity.java
+++ b/http-service/src/main/java/net/runelite/http/service/xp/beans/PlayerEntity.java
@@ -33,4 +33,6 @@ public class PlayerEntity
private Integer id;
private String name;
private Instant tracked_since;
+ private Instant last_updated;
+ private Integer rank;
}
diff --git a/http-service/src/main/resources/application.yaml b/http-service/src/main/resources/application.yaml
new file mode 100644
index 0000000000..9aa02c58ac
--- /dev/null
+++ b/http-service/src/main/resources/application.yaml
@@ -0,0 +1,12 @@
+datasource:
+ runelite:
+ jndiName: java:comp/env/jdbc/runelite
+ runelite-cache:
+ jndiName: java:comp/env/jdbc/runelite-cache2
+ runelite-tracker:
+ jndiName: java:comp/env/jdbc/runelite-tracker
+# By default Spring tries to register the datasource as an MXBean,
+# so if multiple apis are delpoyed on one web container with
+# shared datasource it tries to register it multiples times and
+# fails when starting the 2nd api
+spring.jmx.enabled: false
\ No newline at end of file
diff --git a/http-service/src/main/resources/net/runelite/http/service/xp/schema.sql b/http-service/src/main/resources/net/runelite/http/service/xp/schema.sql
index ba4c395d62..70f83a30fd 100644
--- a/http-service/src/main/resources/net/runelite/http/service/xp/schema.sql
+++ b/http-service/src/main/resources/net/runelite/http/service/xp/schema.sql
@@ -1,8 +1,8 @@
--- MySQL dump 10.16 Distrib 10.2.9-MariaDB, for Linux (x86_64)
+-- MySQL dump 10.16 Distrib 10.2.18-MariaDB, for Linux (x86_64)
--
-- Host: localhost Database: xptracker
-- ------------------------------------------------------
--- Server version 10.2.9-MariaDB
+-- Server version 10.2.18-MariaDB
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
@@ -26,6 +26,8 @@ CREATE TABLE `player` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(32) NOT NULL,
`tracked_since` timestamp NOT NULL DEFAULT current_timestamp(),
+ `last_updated` timestamp NOT NULL DEFAULT current_timestamp(),
+ `rank` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
@@ -116,7 +118,7 @@ CREATE TABLE `xp` (
`overall_rank` int(11) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `player_time` (`player`,`time`),
- INDEX `idx_time` (`time`),
+ KEY `idx_time` (`time`),
CONSTRAINT `fk_player` FOREIGN KEY (`player`) REFERENCES `player` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
@@ -130,4 +132,4 @@ CREATE TABLE `xp` (
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
--- Dump completed on 2018-01-20 18:37:09
+-- Dump completed on 2019-02-15 21:01:17
\ No newline at end of file
diff --git a/http-service/src/test/java/net/runelite/http/service/SpringBootWebApplicationTest.java b/http-service/src/test/java/net/runelite/http/service/SpringBootWebApplicationTest.java
index 0503c075d8..d2d488e1e6 100644
--- a/http-service/src/test/java/net/runelite/http/service/SpringBootWebApplicationTest.java
+++ b/http-service/src/test/java/net/runelite/http/service/SpringBootWebApplicationTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, Adam
+ * Copyright (c) 2019, Adam
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -24,57 +24,23 @@
*/
package net.runelite.http.service;
-import java.time.Instant;
-import java.util.HashMap;
-import java.util.Map;
-import javax.naming.NamingException;
-import net.runelite.http.service.util.InstantConverter;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
-import org.springframework.context.annotation.Bean;
-import org.springframework.scheduling.annotation.EnableScheduling;
-import org.sql2o.Sql2o;
-import org.sql2o.converters.Converter;
-import org.sql2o.quirks.NoQuirks;
-@SpringBootApplication
-@EnableScheduling
public class SpringBootWebApplicationTest
{
- @Bean("Runelite SQL2O")
- Sql2o sql2o()
- {
- Map converters = new HashMap<>();
- converters.put(Instant.class, new InstantConverter());
- return new Sql2o("jdbc:mysql://192.168.1.2/runelite", "runelite", "runelite", new NoQuirks(converters));
- }
-
- @Bean("Runelite Cache SQL2O")
- Sql2o cacheSql2o() throws NamingException
- {
- Map converters = new HashMap<>();
- converters.put(Instant.class, new InstantConverter());
- return new Sql2o("jdbc:mysql://192.168.1.2/cache", "runelite", "runelite", new NoQuirks(converters));
- }
-
- @Bean("Runelite XP Tracker SQL2O")
- Sql2o xpSql2o() throws NamingException
- {
- Map converters = new HashMap<>();
- converters.put(Instant.class, new InstantConverter());
- return new Sql2o("jdbc:mysql://192.168.1.2/xptracker", "runelite", "runelite", new NoQuirks(converters));
- }
-
@Test
@Ignore
- public void test() throws InterruptedException
+ public void run() throws InterruptedException
{
- SpringApplication.run(SpringBootWebApplicationTest.class, new String[0]);
+ String[] args = new String[]{
+ "--spring.config.location=classpath:/application.yaml,classpath:/dev.yaml"
+ };
+ SpringApplication.run(SpringBootWebApplication.class, args);
for (;;)
{
Thread.sleep(100L);
}
}
-}
+}
\ No newline at end of file
diff --git a/http-service/src/test/java/net/runelite/http/service/config/ConfigControllerTest.java b/http-service/src/test/java/net/runelite/http/service/config/ConfigControllerTest.java
new file mode 100644
index 0000000000..5ba857ef0a
--- /dev/null
+++ b/http-service/src/test/java/net/runelite/http/service/config/ConfigControllerTest.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2019, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package net.runelite.http.service.config;
+
+import java.io.IOException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import lombok.extern.slf4j.Slf4j;
+import net.runelite.http.service.account.AuthFilter;
+import net.runelite.http.service.account.beans.SessionEntry;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.http.MediaType;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.servlet.MockMvc;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+@RunWith(SpringRunner.class)
+@WebMvcTest(ConfigController.class)
+@Slf4j
+public class ConfigControllerTest
+{
+ @Autowired
+ private MockMvc mockMvc;
+
+ @MockBean
+ private ConfigService configService;
+
+ @MockBean
+ private AuthFilter authFilter;
+
+ @Before
+ public void before() throws IOException
+ {
+ when(authFilter.handle(any(HttpServletRequest.class), any(HttpServletResponse.class)))
+ .thenReturn(mock(SessionEntry.class));
+ }
+
+ @Test
+ public void testSetKey() throws Exception
+ {
+ mockMvc.perform(put("/config/key")
+ .content("value")
+ .contentType(MediaType.TEXT_PLAIN))
+ .andExpect(status().isOk());
+
+ verify(configService).setKey(anyInt(), eq("key"), eq("value"));
+ }
+}
\ No newline at end of file
diff --git a/http-service/src/test/java/net/runelite/http/service/loottracker/LootTrackerControllerTest.java b/http-service/src/test/java/net/runelite/http/service/loottracker/LootTrackerControllerTest.java
new file mode 100644
index 0000000000..e708de0c29
--- /dev/null
+++ b/http-service/src/test/java/net/runelite/http/service/loottracker/LootTrackerControllerTest.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2019, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package net.runelite.http.service.loottracker;
+
+import java.io.IOException;
+import java.time.Instant;
+import java.util.Collections;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import lombok.extern.slf4j.Slf4j;
+import net.runelite.http.api.RuneLiteAPI;
+import net.runelite.http.api.loottracker.GameItem;
+import net.runelite.http.api.loottracker.LootRecord;
+import net.runelite.http.api.loottracker.LootRecordType;
+import net.runelite.http.service.account.AuthFilter;
+import net.runelite.http.service.account.beans.SessionEntry;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.http.MediaType;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.servlet.MockMvc;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+@RunWith(SpringRunner.class)
+@WebMvcTest(LootTrackerController.class)
+@Slf4j
+public class LootTrackerControllerTest
+{
+ @Autowired
+ private MockMvc mockMvc;
+
+ @MockBean
+ private LootTrackerService lootTrackerService;
+
+ @MockBean
+ private AuthFilter authFilter;
+
+ @Before
+ public void before() throws IOException
+ {
+ when(authFilter.handle(any(HttpServletRequest.class), any(HttpServletResponse.class)))
+ .thenReturn(mock(SessionEntry.class));
+ }
+
+ @Test
+ public void storeLootRecord() throws Exception
+ {
+ LootRecord lootRecord = new LootRecord();
+ lootRecord.setType(LootRecordType.NPC);
+ lootRecord.setTime(Instant.now());
+ lootRecord.setDrops(Collections.singletonList(new GameItem(4151, 1)));
+
+ String data = RuneLiteAPI.GSON.toJson(lootRecord);
+ mockMvc.perform(post("/loottracker").content(data).contentType(MediaType.APPLICATION_JSON))
+ .andExpect(status().isOk());
+
+ verify(lootTrackerService).store(eq(lootRecord), anyInt());
+ }
+}
\ No newline at end of file
diff --git a/http-service/src/test/resources/application.properties b/http-service/src/test/resources/application.properties
deleted file mode 100644
index cb3dcc0507..0000000000
--- a/http-service/src/test/resources/application.properties
+++ /dev/null
@@ -1,11 +0,0 @@
-oauth.client-id=moo
-oauth.client-secret=cow
-minio.endpoint=http://10.96.22.171:9000
-minio.accesskey=AM54M27O4WZK65N6F8IP
-minio.secretkey=/PZCxzmsJzwCHYlogcymuprniGCaaLUOET2n6yMP
-minio.bucket=runelite
-runelite.twitter.consumerkey=moo
-runelite.twitter.secretkey=cow
-runelite.twitter.listid=968949795153948673
-logging.level.net.runelite=DEBUG
-spring.jackson.serialization.indent_output=true
\ No newline at end of file
diff --git a/http-service/src/test/resources/application.yaml b/http-service/src/test/resources/application.yaml
new file mode 100644
index 0000000000..128e64d8ef
--- /dev/null
+++ b/http-service/src/test/resources/application.yaml
@@ -0,0 +1,35 @@
+oauth:
+ client-id: moo
+ client-secret: cow
+
+minio:
+ endpoint: http://10.96.22.171:9000
+ accesskey: AM54M27O4WZK65N6F8IP
+ secretkey: /PZCxzmsJzwCHYlogcymuprniGCaaLUOET2n6yMP
+ bucket: runelite
+
+runelite:
+ twitter:
+ consumerkey: moo
+ secretkey: cow
+ listid: 968949795153948673
+
+logging:
+ level:
+ net:
+ runelite:
+ DEBUG
+
+datasource:
+ runelite:
+ driverClassName: org.h2.Driver
+ type: org.h2.jdbcx.JdbcDataSource
+ url: jdbc:h2:mem:runelite
+ runelite-cache:
+ driverClassName: org.h2.Driver
+ type: org.h2.jdbcx.JdbcDataSource
+ url: jdbc:h2:mem:cache
+ runelite-tracker:
+ driverClassName: org.h2.Driver
+ type: org.h2.jdbcx.JdbcDataSource
+ url: jdbc:h2:mem:xptracker
diff --git a/http-service/src/test/resources/dev.yaml b/http-service/src/test/resources/dev.yaml
new file mode 100644
index 0000000000..e4967ccee4
--- /dev/null
+++ b/http-service/src/test/resources/dev.yaml
@@ -0,0 +1,19 @@
+datasource:
+ runelite:
+ driverClassName: com.mysql.jdbc.Driver
+ type: com.mysql.jdbc.jdbc2.optional.MysqlDataSource
+ url: jdbc:mysql://192.168.1.2/runelite
+ username: runelite
+ password: runelite
+ runelite-cache:
+ driverClassName: com.mysql.jdbc.Driver
+ type: com.mysql.jdbc.jdbc2.optional.MysqlDataSource
+ url: jdbc:mysql://192.168.1.2/cache
+ username: runelite
+ password: runelite
+ runelite-tracker:
+ driverClassName: com.mysql.jdbc.Driver
+ type: com.mysql.jdbc.jdbc2.optional.MysqlDataSource
+ url: jdbc:mysql://192.168.1.2/xptracker
+ username: runelite
+ password: runelite
diff --git a/pom.xml b/pom.xml
index 0c1f89dc1a..a5187a029c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -28,7 +28,7 @@
net.runelite
runelite-parent
- 1.5.12-SNAPSHOT
+ 1.5.14-SNAPSHOT
pom
RuneLite
diff --git a/protocol-api/pom.xml b/protocol-api/pom.xml
index 551b441ed7..df42e1e53a 100644
--- a/protocol-api/pom.xml
+++ b/protocol-api/pom.xml
@@ -29,7 +29,7 @@
net.runelite
runelite-parent
- 1.5.12-SNAPSHOT
+ 1.5.14-SNAPSHOT
protocol-api
diff --git a/protocol-api/src/main/java/net/runelite/protocol/api/handshake/LoginHandshakePacket.java b/protocol-api/src/main/java/net/runelite/protocol/api/handshake/LoginHandshakePacket.java
index 4a6090504e..16ccb85694 100644
--- a/protocol-api/src/main/java/net/runelite/protocol/api/handshake/LoginHandshakePacket.java
+++ b/protocol-api/src/main/java/net/runelite/protocol/api/handshake/LoginHandshakePacket.java
@@ -25,8 +25,10 @@
package net.runelite.protocol.api.handshake;
import lombok.Data;
+import lombok.EqualsAndHashCode;
@Data
+@EqualsAndHashCode(callSuper = true)
public class LoginHandshakePacket extends HandshakePacket
{
diff --git a/protocol-api/src/main/java/net/runelite/protocol/api/handshake/UpdateHandshakePacket.java b/protocol-api/src/main/java/net/runelite/protocol/api/handshake/UpdateHandshakePacket.java
index 9d1b20c8f2..86b39d41e8 100644
--- a/protocol-api/src/main/java/net/runelite/protocol/api/handshake/UpdateHandshakePacket.java
+++ b/protocol-api/src/main/java/net/runelite/protocol/api/handshake/UpdateHandshakePacket.java
@@ -26,9 +26,11 @@ package net.runelite.protocol.api.handshake;
import lombok.AllArgsConstructor;
import lombok.Data;
+import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@Data
+@EqualsAndHashCode(callSuper = true)
@NoArgsConstructor
@AllArgsConstructor
public class UpdateHandshakePacket extends HandshakePacket
diff --git a/protocol/pom.xml b/protocol/pom.xml
index 5255e7bacb..490f2eb372 100644
--- a/protocol/pom.xml
+++ b/protocol/pom.xml
@@ -29,7 +29,7 @@
net.runelite
runelite-parent
- 1.5.12-SNAPSHOT
+ 1.5.14-SNAPSHOT
protocol
diff --git a/runelite-api/pom.xml b/runelite-api/pom.xml
index 903322a106..f9b875d6fa 100644
--- a/runelite-api/pom.xml
+++ b/runelite-api/pom.xml
@@ -29,7 +29,7 @@
net.runelite
runelite-parent
- 1.5.12-SNAPSHOT
+ 1.5.14-SNAPSHOT
runelite-api
diff --git a/runelite-api/src/main/java/net/runelite/api/AnimationID.java b/runelite-api/src/main/java/net/runelite/api/AnimationID.java
index ee1a50d7ed..1f96e9c898 100644
--- a/runelite-api/src/main/java/net/runelite/api/AnimationID.java
+++ b/runelite-api/src/main/java/net/runelite/api/AnimationID.java
@@ -123,6 +123,7 @@ public final class AnimationID
public static final int HERBLORE_POTIONMAKING = 363; //used for both herb and secondary
public static final int MAGIC_CHARGING_ORBS = 726;
public static final int MAGIC_MAKE_TABLET = 4068;
+ public static final int MAGIC_ENCHANTING_JEWELRY = 931;
public static final int BURYING_BONES = 827;
public static final int USING_GILDED_ALTAR = 3705;
public static final int LOOKING_INTO = 832;
diff --git a/runelite-api/src/main/java/net/runelite/api/Client.java b/runelite-api/src/main/java/net/runelite/api/Client.java
index ca51dc74a9..4c44c7e3ef 100644
--- a/runelite-api/src/main/java/net/runelite/api/Client.java
+++ b/runelite-api/src/main/java/net/runelite/api/Client.java
@@ -1569,4 +1569,16 @@ public interface Client extends GameEngine
int getRasterizer3D_clipMidY2();
void checkClickbox(Model model, int orientation, int pitchSin, int pitchCos, int yawSin, int yawCos, int x, int y, int z, long hash);
+
+ /**
+ * Sets if a widget is in target mode
+ */
+ void setSpellSelected(boolean selected);
+
+ /**
+ * Returns client item composition cache
+ */
+ NodeCache getItemCompositionCache();
+
+ EnumComposition getEnum(int id);
}
diff --git a/runelite-api/src/main/java/net/runelite/api/DecorativeObject.java b/runelite-api/src/main/java/net/runelite/api/DecorativeObject.java
index c2bbd14cf3..145531fa5d 100644
--- a/runelite-api/src/main/java/net/runelite/api/DecorativeObject.java
+++ b/runelite-api/src/main/java/net/runelite/api/DecorativeObject.java
@@ -38,6 +38,7 @@ public interface DecorativeObject extends TileObject
* @see net.runelite.api.model.Jarvis
*/
Polygon getConvexHull();
+ Polygon getConvexHull2();
Renderable getRenderable();
Renderable getRenderable2();
diff --git a/runelite-api/src/main/java/net/runelite/api/EnumComposition.java b/runelite-api/src/main/java/net/runelite/api/EnumComposition.java
new file mode 100644
index 0000000000..7bbfaa1693
--- /dev/null
+++ b/runelite-api/src/main/java/net/runelite/api/EnumComposition.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2019, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package net.runelite.api;
+
+public interface EnumComposition
+{
+ int[] getIntVals();
+
+ String[] getStringVals();
+
+ int getIntValue(int key);
+
+ String getStringValue(int key);
+}
diff --git a/runelite-api/src/main/java/net/runelite/api/ScriptID.java b/runelite-api/src/main/java/net/runelite/api/ScriptID.java
index 778b271036..05c057a38d 100644
--- a/runelite-api/src/main/java/net/runelite/api/ScriptID.java
+++ b/runelite-api/src/main/java/net/runelite/api/ScriptID.java
@@ -45,6 +45,24 @@ public final class ScriptID
*/
public static final int CHATBOX_INPUT = 96;
+ /**
+ * Opens the Private Message chat interface
+ *
+ * Jagex refers to this script as {@code meslayer_mode6}
+ *
+ * - String Player to send private message to
+ *
+ */
+ public static final int OPEN_PRIVATE_MESSAGE_INTERFACE = 107;
+
+ /**
+ * Rebuilds the text input widget inside the chat interface
+ *
+ * - String Message Prefix. Only used inside the GE search interfaces
+ *
+ */
+ public static final int CHAT_TEXT_INPUT_REBUILD = 222;
+
/**
* Layouts the bank widgets
*
diff --git a/runelite-api/src/main/java/net/runelite/api/SpriteID.java b/runelite-api/src/main/java/net/runelite/api/SpriteID.java
index 2c4ac4f878..e707854c12 100644
--- a/runelite-api/src/main/java/net/runelite/api/SpriteID.java
+++ b/runelite-api/src/main/java/net/runelite/api/SpriteID.java
@@ -1172,7 +1172,7 @@ public final class SpriteID
public static final int MINIMAP_ORB_XP_ACTIVATED = 1197;
public static final int MINIMAP_ORB_XP_HOVERED = 1198;
public static final int MINIMAP_ORB_XP_ACTIVATED_HOVERED = 1199;
- public static final int UNKNOWN_BLACK_BLOBS = 1200;
+ public static final int MINIMAP_CLICK_MASK = 1200;
public static final int OPTIONS_ZOOM_SLIDER_THUMB = 1201;
public static final int EMOTE_SIT_UP = 1202;
public static final int EMOTE_STAR_JUMP = 1203;
diff --git a/runelite-api/src/main/java/net/runelite/api/VarClientStr.java b/runelite-api/src/main/java/net/runelite/api/VarClientStr.java
index b3bf1b09b6..72f4ee0303 100644
--- a/runelite-api/src/main/java/net/runelite/api/VarClientStr.java
+++ b/runelite-api/src/main/java/net/runelite/api/VarClientStr.java
@@ -36,6 +36,7 @@ public enum VarClientStr
{
CHATBOX_TYPED_TEXT(1),
INPUT_TEXT(22),
+ PRIVATE_MESSAGE_TARGET(23),
RECENT_CLAN_CHAT(129);
private final int index;
diff --git a/runelite-api/src/main/java/net/runelite/api/VarPlayer.java b/runelite-api/src/main/java/net/runelite/api/VarPlayer.java
index 37b71aaf66..c3183074b0 100644
--- a/runelite-api/src/main/java/net/runelite/api/VarPlayer.java
+++ b/runelite-api/src/main/java/net/runelite/api/VarPlayer.java
@@ -37,6 +37,10 @@ public enum VarPlayer
ATTACK_STYLE(43),
QUEST_POINTS(101),
IS_POISONED(102),
+ /**
+ * Seems to start at 50(10 splash) and goes down by 1 every 30 seconds
+ */
+ DISEASE_VALUE(456),
BANK_TAB(115),
diff --git a/runelite-api/src/main/java/net/runelite/api/coords/WorldPoint.java b/runelite-api/src/main/java/net/runelite/api/coords/WorldPoint.java
index 3f164c1845..a0a23e6674 100644
--- a/runelite-api/src/main/java/net/runelite/api/coords/WorldPoint.java
+++ b/runelite-api/src/main/java/net/runelite/api/coords/WorldPoint.java
@@ -25,6 +25,10 @@
*/
package net.runelite.api.coords;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
import lombok.Value;
import net.runelite.api.Client;
import static net.runelite.api.Constants.CHUNK_SIZE;
@@ -150,7 +154,8 @@ public class WorldPoint
}
/**
- * Gets the coordinate of the tile that contains the passed local point.
+ * Gets the coordinate of the tile that contains the passed local point,
+ * accounting for instances.
*
* @param client the client
* @param localPoint the local coordinate
@@ -190,6 +195,46 @@ public class WorldPoint
}
}
+ /**
+ * Get occurrences of a tile on the scene, accounting for instances. There may be
+ * more than one if the same template chunk occurs more than once on the scene.
+ * @param client
+ * @param worldPoint
+ * @return
+ */
+ public static Collection toLocalInstance(Client client, WorldPoint worldPoint)
+ {
+ if (!client.isInInstancedRegion())
+ {
+ return Collections.singleton(worldPoint);
+ }
+
+ // find instance chunks using the template point. there might be more than one.
+ List worldPoints = new ArrayList<>();
+ final int z = client.getPlane();
+ int[][][] instanceTemplateChunks = client.getInstanceTemplateChunks();
+ for (int x = 0; x < instanceTemplateChunks[z].length; ++x)
+ {
+ for (int y = 0; y < instanceTemplateChunks[z][x].length; ++y)
+ {
+ int chunkData = instanceTemplateChunks[z][x][y];
+ int rotation = chunkData >> 1 & 0x3;
+ int templateChunkY = (chunkData >> 3 & 0x7FF) * CHUNK_SIZE;
+ int templateChunkX = (chunkData >> 14 & 0x3FF) * CHUNK_SIZE;
+ if (worldPoint.getX() >= templateChunkX && worldPoint.getX() < templateChunkX + CHUNK_SIZE
+ && worldPoint.getY() >= templateChunkY && worldPoint.getY() < templateChunkY + CHUNK_SIZE)
+ {
+ WorldPoint p = new WorldPoint(client.getBaseX() + x * CHUNK_SIZE + (worldPoint.getX() & (CHUNK_SIZE - 1)),
+ client.getBaseY() + y * CHUNK_SIZE + (worldPoint.getY() & (CHUNK_SIZE - 1)),
+ worldPoint.getPlane());
+ p = rotate(p, rotation);
+ worldPoints.add(p);
+ }
+ }
+ }
+ return worldPoints;
+ }
+
/**
* Rotate the coordinates in the chunk according to chunk rotation
*
diff --git a/runelite-api/src/main/java/net/runelite/api/vars/InputType.java b/runelite-api/src/main/java/net/runelite/api/vars/InputType.java
index db1301b281..c94ccd79ef 100644
--- a/runelite-api/src/main/java/net/runelite/api/vars/InputType.java
+++ b/runelite-api/src/main/java/net/runelite/api/vars/InputType.java
@@ -38,6 +38,7 @@ public enum InputType
RUNELITE_CHATBOX_PANEL(-3),
RUNELITE(-2),
NONE(0),
+ PRIVATE_MESSAGE(6),
SEARCH(11);
private final int type;
diff --git a/runelite-api/src/main/java/net/runelite/api/widgets/Widget.java b/runelite-api/src/main/java/net/runelite/api/widgets/Widget.java
index 3aa6e23f13..ed4277a2a3 100644
--- a/runelite-api/src/main/java/net/runelite/api/widgets/Widget.java
+++ b/runelite-api/src/main/java/net/runelite/api/widgets/Widget.java
@@ -79,6 +79,7 @@ public interface Widget
/**
* Gets the current click configuration of the widget.
+ * @see WidgetConfig
*
* @see WidgetConfig
*/
@@ -551,6 +552,13 @@ public interface Widget
*/
void setOnMouseOverListener(Object... args);
+ /**
+ * Sets a script to be ran every frame when the mouse is in the widget bounds
+ *
+ * @param args A ScriptID, then the args for the script
+ */
+ void setOnMouseRepeatListener(Object... args);
+
/**
* Sets a script to be ran when the mouse leaves the widget bounds
*
@@ -565,6 +573,20 @@ public interface Widget
*/
void setOnTimerListener(Object... args);
+ /**
+ * Sets a script to be ran when the target mode has been activated for this widget
+ *
+ * @param args A ScriptID, then the args for the script
+ */
+ void setOnTargetEnterListener(Object... args);
+
+ /**
+ * Sets a script to be ran when the target mode has been deactivated for this widget
+ *
+ * @param args A ScriptID, then the args for the script
+ */
+ void setOnTargetLeaveListener(Object... args);
+
/**
* If this widget has any listeners on it
*/
@@ -769,4 +791,34 @@ public interface Widget
* Sets if the rectangle is filled or just stroked
*/
void setFilled(boolean filled);
+
+ /**
+ * Verb for spell targets
+ */
+ String getTargetVerb();
+
+ /**
+ * Verb for spell targets
+ */
+ void setTargetVerb(String targetVerb);
+
+ /**
+ * Can widgets under this widgets be clicked in this widgets bounding box
+ */
+ boolean getNoClickThrough();
+
+ /**
+ * Can widgets under this widgets be clicked in this widgets bounding box
+ */
+ void setNoClickThrough(boolean noClickThrough);
+
+ /**
+ * Can widgets under this widgets be scrolled in this widgets bounding box
+ */
+ boolean getNoScrollThrough();
+
+ /**
+ * Can widgets under this widgets be scrolled in this widgets bounding box
+ */
+ void setNoScrollThrough(boolean noScrollThrough);
}
diff --git a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetConfig.java b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetConfig.java
index 0b8f6a587d..31829b5417 100644
--- a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetConfig.java
+++ b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetConfig.java
@@ -24,6 +24,8 @@
*/
package net.runelite.api.widgets;
+import net.runelite.api.MenuAction;
+
/**
* Utility class used for defining options to be used on the click mask
* of a {@link Widget}.
@@ -36,12 +38,61 @@ public class WidgetConfig
* Enables displaying a ninth option on a menu.
*/
public static final int SHOW_MENU_OPTION_NINE = 1 << 9;
+
+ /**
+ * Can this widget be used on a item on the floor
+ */
+ public static final int USE_GROUND_ITEM = 1 << 11;
+
+ /**
+ * Can this widget be used on a NPC
+ */
+ public static final int USE_NPC = 2 << 11;
+
+ /**
+ * Can this widget be used on a game object
+ */
+ public static final int USE_OBJECT = 4 << 11;
+
+ /**
+ * Can this widget be used on a player
+ */
+ public static final int USE_PLAYER = 8 << 11;
+
+ /**
+ * Can this widget be used on a item in your inventory
+ */
+ public static final int USE_ITEM = 16 << 11;
+
+ /**
+ * Can this widget be used on a widget with the WIDGET_USE_TARGET flag
+ */
+ public static final int USE_WIDGET = 32 << 11;
+
/**
* Controls whether or not a widget can have another dragged onto it.
*/
public static final int DRAG_ON = 1 << 17;
+
/**
* Controls whether or not a widget can be dragged around.
*/
public static final int DRAG = 1 << 20;
+
+ /**
+ * Can widgets with USE_WIDGET be used on this widget
+ */
+ public static final int WIDGET_USE_TARGET = 1 << 21;
+
+ /**
+ * Is the widget an (inventory?) item
+ */
+ public static final int ITEM = 1 << 30;
+
+ /**
+ * Add a USE option
+ *
+ * @see MenuAction#ITEM_USE
+ */
+ public static final int ITEM_USE_OP = 1 << 31;
}
diff --git a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java
index 9f07f9ba37..be7e595dd7 100644
--- a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java
+++ b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java
@@ -125,6 +125,8 @@ public class WidgetID
public static final int SKOTIZO_GROUP_ID = 308;
public static final int ENTERING_HOUSE_GROUP_ID = 71;
public static final int FULLSCREEN_MAP_GROUP_ID = 165;
+ public static final int QUESTLIST_GROUP_ID = 399;
+ public static final int SKILLS_GROUP_ID = 320;
static class WorldMap
{
@@ -292,6 +294,7 @@ public class WidgetID
static final int TOGGLE_RUN_ORB = 22; // Has the "Toggle run" name
static final int RUN_ORB_TEXT = 23;
static final int SPEC_ORB = 28;
+ static final int WORLDMAP_ORB = 40;
}
static class LoginClickToPlayScreen
@@ -747,4 +750,11 @@ public class WidgetID
{
static final int ROOT = 25;
}
+
+ static class QuestList
+ {
+ static final int FREE_CONTAINER = 9;
+ static final int MEMBERS_CONTAINER = 10;
+ static final int MINIQUEST_CONTAINER = 11;
+ }
}
diff --git a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java
index 556dc57bf5..7808e06cba 100644
--- a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java
+++ b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java
@@ -159,6 +159,7 @@ public enum WidgetInfo
MINIMAP_RUN_ORB_TEXT(WidgetID.MINIMAP_GROUP_ID, WidgetID.Minimap.RUN_ORB_TEXT),
MINIMAP_HEALTH_ORB(WidgetID.MINIMAP_GROUP_ID, WidgetID.Minimap.HEALTH_ORB),
MINIMAP_SPEC_ORB(WidgetID.MINIMAP_GROUP_ID, WidgetID.Minimap.SPEC_ORB),
+ MINIMAP_WORLDMAP_ORB(WidgetID.MINIMAP_GROUP_ID, WidgetID.Minimap.WORLDMAP_ORB),
LOGIN_CLICK_TO_PLAY_SCREEN(WidgetID.LOGIN_CLICK_TO_PLAY_GROUP_ID, 0),
LOGIN_CLICK_TO_PLAY_SCREEN_MESSAGE_OF_THE_DAY(WidgetID.LOGIN_CLICK_TO_PLAY_GROUP_ID, WidgetID.LoginClickToPlayScreen.MESSAGE_OF_THE_DAY),
@@ -463,7 +464,11 @@ public enum WidgetInfo
SKOTIZO_CONTAINER(WidgetID.SKOTIZO_GROUP_ID, WidgetID.Skotizo.CONTAINER),
- FULLSCREEN_MAP_ROOT(WidgetID.FULLSCREEN_MAP_GROUP_ID, WidgetID.FullScreenMap.ROOT);
+ FULLSCREEN_MAP_ROOT(WidgetID.FULLSCREEN_MAP_GROUP_ID, WidgetID.FullScreenMap.ROOT),
+
+ QUESTLIST_FREE_CONTAINER(WidgetID.QUESTLIST_GROUP_ID, WidgetID.QuestList.FREE_CONTAINER),
+ QUESTLIST_MEMBERS_CONTAINER(WidgetID.QUESTLIST_GROUP_ID, WidgetID.QuestList.MEMBERS_CONTAINER),
+ QUESTLIST_MINIQUEST_CONTAINER(WidgetID.QUESTLIST_GROUP_ID, WidgetID.QuestList.MINIQUEST_CONTAINER);
private final int groupId;
private final int childId;
diff --git a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetType.java b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetType.java
index da76a7580f..9d140c3cd0 100644
--- a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetType.java
+++ b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetType.java
@@ -27,9 +27,12 @@ package net.runelite.api.widgets;
public final class WidgetType
{
public static final int LAYER = 0;
+ public static final int INVENTORY = 2;
public static final int RECTANGLE = 3;
public static final int TEXT = 4;
public static final int GRAPHIC = 5;
public static final int MODEL = 6;
+ public static final int TEXT_INVENTORY = 7;
+ public static final int IF1_TOOLTIP = 8;
public static final int LINE = 9;
}
diff --git a/runelite-client/pom.xml b/runelite-client/pom.xml
index 6306dc3763..e026f85423 100644
--- a/runelite-client/pom.xml
+++ b/runelite-client/pom.xml
@@ -29,7 +29,7 @@
net.runelite
runelite-parent
- 1.5.12-SNAPSHOT
+ 1.5.14-SNAPSHOT
client
diff --git a/runelite-client/src/main/java/net/runelite/client/RuneLite.java b/runelite-client/src/main/java/net/runelite/client/RuneLite.java
index b7fae34e2c..ecdda28103 100644
--- a/runelite-client/src/main/java/net/runelite/client/RuneLite.java
+++ b/runelite-client/src/main/java/net/runelite/client/RuneLite.java
@@ -183,9 +183,9 @@ public class RuneLite
System.exit(0);
}
- final boolean developerMode = options.has("developer-mode");
+ final boolean developerMode = options.has("developer-mode") && RuneLiteProperties.getLauncherVersion() == null;
- if (developerMode && RuneLiteProperties.getLauncherVersion() == null)
+ if (developerMode)
{
boolean assertions = false;
assert assertions = true;
diff --git a/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java b/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java
index 2cde4900ad..b24c9ccb96 100644
--- a/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java
+++ b/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java
@@ -43,8 +43,11 @@ import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.nio.channels.FileLock;
import java.nio.charset.Charset;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
import java.time.Instant;
import java.util.Arrays;
+import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -70,6 +73,7 @@ import net.runelite.http.api.config.Configuration;
public class ConfigManager
{
private static final String SETTINGS_FILE_NAME = "settings.properties";
+ private static final DateFormat TIME_FORMAT = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss");
@Inject
EventBus eventBus;
@@ -111,12 +115,17 @@ public class ConfigManager
load(); // load profile specific config
}
+ private File getLocalPropertiesFile()
+ {
+ return new File(RuneLite.RUNELITE_DIR, SETTINGS_FILE_NAME);
+ }
+
private File getPropertiesFile()
{
// Sessions that aren't logged in have no username
if (session == null || session.getUsername() == null)
{
- return new File(RuneLite.RUNELITE_DIR, SETTINGS_FILE_NAME);
+ return getLocalPropertiesFile();
}
else
{
@@ -158,7 +167,13 @@ public class ConfigManager
for (ConfigEntry entry : configuration.getConfig())
{
log.debug("Loading configuration value from client {}: {}", entry.getKey(), entry.getValue());
- final String[] split = entry.getKey().split("\\.");
+ final String[] split = entry.getKey().split("\\.", 2);
+
+ if (split.length != 2)
+ {
+ continue;
+ }
+
final String groupName = split[0];
final String key = split[1];
final String value = entry.getValue();
@@ -174,7 +189,7 @@ public class ConfigManager
try
{
- saveToFile();
+ saveToFile(propertiesFile);
log.debug("Updated configuration on disk with the latest version");
}
@@ -184,6 +199,75 @@ public class ConfigManager
}
}
+ private synchronized void syncPropertiesFromFile(File propertiesFile)
+ {
+ final Properties properties = new Properties();
+ try (FileInputStream in = new FileInputStream(propertiesFile))
+ {
+ properties.load(new InputStreamReader(in, Charset.forName("UTF-8")));
+ }
+ catch (Exception e)
+ {
+ log.debug("Malformed properties, skipping update");
+ return;
+ }
+
+ final Map copy = (Map) ImmutableMap.copyOf(this.properties);
+ copy.forEach((groupAndKey, value) ->
+ {
+ if (!properties.containsKey(groupAndKey))
+ {
+ final String[] split = groupAndKey.split("\\.", 2);
+ if (split.length != 2)
+ {
+ return;
+ }
+
+ final String groupName = split[0];
+ final String key = split[1];
+ unsetConfiguration(groupName, key);
+ }
+ });
+
+ properties.forEach((objGroupAndKey, objValue) ->
+ {
+ final String groupAndKey = String.valueOf(objGroupAndKey);
+ final String[] split = groupAndKey.split("\\.", 2);
+ if (split.length != 2)
+ {
+ return;
+ }
+
+ final String groupName = split[0];
+ final String key = split[1];
+ final String value = String.valueOf(objValue);
+ setConfiguration(groupName, key, value);
+ });
+ }
+
+ public void importLocal()
+ {
+ if (session == null)
+ {
+ // No session, no import
+ return;
+ }
+
+ final File file = new File(propertiesFile.getParent(), propertiesFile.getName() + "." + TIME_FORMAT.format(new Date()));
+
+ try
+ {
+ saveToFile(file);
+ }
+ catch (IOException e)
+ {
+ log.warn("Backup failed, skipping import", e);
+ return;
+ }
+
+ syncPropertiesFromFile(getLocalPropertiesFile());
+ }
+
private synchronized void loadFromFile()
{
properties.clear();
@@ -231,7 +315,7 @@ public class ConfigManager
}
}
- private synchronized void saveToFile() throws IOException
+ private synchronized void saveToFile(final File propertiesFile) throws IOException
{
propertiesFile.getParentFile().mkdirs();
@@ -294,8 +378,6 @@ public class ConfigManager
public void setConfiguration(String groupName, String key, String value)
{
- log.debug("Setting configuration value for {}.{} to {}", groupName, key, value);
-
String oldValue = (String) properties.setProperty(groupName + "." + key, value);
if (Objects.equals(oldValue, value))
@@ -303,6 +385,8 @@ public class ConfigManager
return;
}
+ log.debug("Setting configuration value for {}.{} to {}", groupName, key, value);
+
synchronized (pendingChanges)
{
pendingChanges.put(groupName + "." + key, value);
@@ -312,7 +396,7 @@ public class ConfigManager
{
try
{
- saveToFile();
+ saveToFile(propertiesFile);
}
catch (IOException ex)
{
@@ -337,8 +421,6 @@ public class ConfigManager
public void unsetConfiguration(String groupName, String key)
{
- log.debug("Unsetting configuration value for {}.{}", groupName, key);
-
String oldValue = (String) properties.remove(groupName + "." + key);
if (oldValue == null)
@@ -346,6 +428,8 @@ public class ConfigManager
return;
}
+ log.debug("Unsetting configuration value for {}.{}", groupName, key);
+
synchronized (pendingChanges)
{
pendingChanges.put(groupName + "." + key, null);
@@ -355,7 +439,7 @@ public class ConfigManager
{
try
{
- saveToFile();
+ saveToFile(propertiesFile);
}
catch (IOException ex)
{
diff --git a/runelite-client/src/main/java/net/runelite/client/discord/DiscordService.java b/runelite-client/src/main/java/net/runelite/client/discord/DiscordService.java
index 0c2739b8a2..091c479ac6 100644
--- a/runelite-client/src/main/java/net/runelite/client/discord/DiscordService.java
+++ b/runelite-client/src/main/java/net/runelite/client/discord/DiscordService.java
@@ -78,7 +78,7 @@ public class DiscordService implements AutoCloseable
discordRPC = DiscordRPC.INSTANCE;
discordEventHandlers = new DiscordEventHandlers();
}
- catch (UnsatisfiedLinkError e)
+ catch (Error e)
{
log.warn("Failed to load Discord library, Discord support will be disabled.");
}
@@ -150,9 +150,12 @@ public class DiscordService implements AutoCloseable
? "default"
: discordPresence.getLargeImageKey();
discordRichPresence.largeImageText = discordPresence.getLargeImageText();
- discordRichPresence.smallImageKey = Strings.isNullOrEmpty(discordPresence.getSmallImageKey())
- ? "default"
- : discordPresence.getSmallImageKey();
+
+ if (!Strings.isNullOrEmpty(discordPresence.getSmallImageKey()))
+ {
+ discordRichPresence.smallImageKey = discordPresence.getSmallImageKey();
+ }
+
discordRichPresence.smallImageText = discordPresence.getSmallImageText();
discordRichPresence.partyId = discordPresence.getPartyId();
discordRichPresence.partySize = discordPresence.getPartySize();
diff --git a/runelite-client/src/main/java/net/runelite/client/events/ChatboxInput.java b/runelite-client/src/main/java/net/runelite/client/events/ChatboxInput.java
index 6388c8b433..cad7d38077 100644
--- a/runelite-client/src/main/java/net/runelite/client/events/ChatboxInput.java
+++ b/runelite-client/src/main/java/net/runelite/client/events/ChatboxInput.java
@@ -25,8 +25,10 @@
package net.runelite.client.events;
import lombok.Data;
+import lombok.EqualsAndHashCode;
@Data
+@EqualsAndHashCode(callSuper = true)
public abstract class ChatboxInput extends ChatInput
{
private final String value;
diff --git a/runelite-client/src/main/java/net/runelite/client/events/PrivateMessageInput.java b/runelite-client/src/main/java/net/runelite/client/events/PrivateMessageInput.java
index fc31fae7f1..80a189f273 100644
--- a/runelite-client/src/main/java/net/runelite/client/events/PrivateMessageInput.java
+++ b/runelite-client/src/main/java/net/runelite/client/events/PrivateMessageInput.java
@@ -25,8 +25,10 @@
package net.runelite.client.events;
import lombok.Data;
+import lombok.EqualsAndHashCode;
@Data
+@EqualsAndHashCode(callSuper = true)
public abstract class PrivateMessageInput extends ChatInput
{
private final String target;
diff --git a/runelite-client/src/main/java/net/runelite/client/game/AgilityShortcut.java b/runelite-client/src/main/java/net/runelite/client/game/AgilityShortcut.java
new file mode 100644
index 0000000000..c376bd34f1
--- /dev/null
+++ b/runelite-client/src/main/java/net/runelite/client/game/AgilityShortcut.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 2018, SomeoneWithAnInternetConnection
+ * Copyright (c) 2019, MrGroggle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package net.runelite.client.game;
+
+import lombok.Getter;
+import static net.runelite.api.NullObjectID.*;
+import static net.runelite.api.ObjectID.*;
+import net.runelite.api.coords.WorldPoint;
+
+@Getter
+public enum AgilityShortcut
+{
+ GENERIC_SHORTCUT(1, "Shortcut", null,
+ // Trollheim
+ ROCKS_3790, ROCKS_3791,
+ // Fremennik Slayer Cave
+ STEPS_29993,
+ // Fossil Island
+ LADDER_30938, LADDER_30939, LADDER_30940, LADDER_30941, RUBBER_CAP_MUSHROOM,
+ // Brimhaven dungeon
+ CREVICE_30198,
+ // Lumbridge
+ STILE_12982,
+ // Gu'Tanoth Bridge
+ GAP, GAP_2831,
+ // Lumbridge Swamp Caves
+ STEPPING_STONE_5948, STEPPING_STONE_5949, ROCKS_6673,
+ // Morytania Pirate Ship
+ ROCK_16115,
+ // Lumber Yard
+ BROKEN_FENCE_2618,
+ // McGrubor's Wood
+ LOOSE_RAILING,
+ // Underwater Area Fossil Island
+ TUNNEL_30959, HOLE_30966, OBSTACLE, OBSTACLE_30767, OBSTACLE_30964, OBSTACLE_30962,
+ // Tree Gnome Village
+ LOOSE_RAILING_2186,
+ // Burgh de Rott
+ LOW_FENCE,
+ // Taverley
+ STILE,
+ // Asgarnian Ice Dungeon
+ STEPS,
+ // Fossil Island Wyvern Cave
+ STAIRS_31485),
+ BRIMHAVEN_DUNGEON_MEDIUM_PIPE_RETURN(1, "Pipe Squeeze", null, new WorldPoint(2698, 9491, 0), PIPE_21727),
+ BRIMHAVEN_DUNGEON_PIPE_RETURN(1, "Pipe Squeeze", null, new WorldPoint(2655, 9573, 0), PIPE_21728),
+ BRIMHAVEN_DUNGEON_STEPPING_STONES_RETURN(1, "Pipe Squeeze", null, STEPPING_STONE_21739),
+ BRIMHAVEN_DUNGEON_LOG_BALANCE_RETURN(1, "Log Balance", null, LOG_BALANCE_20884),
+ AGILITY_PYRAMID_ROCKS_WEST(1, "Rocks", null, CLIMBING_ROCKS_11948),
+ CAIRN_ISLE_CLIMBING_ROCKS(1, "Rocks", null, CLIMBING_ROCKS),
+ KARAMJA_GLIDER_LOG(1, "Log Balance", new WorldPoint(2906, 3050, 0), A_WOODEN_LOG ),
+ FALADOR_CRUMBLING_WALL(5, "Crumbling Wall", new WorldPoint(2936, 3357, 0), CRUMBLING_WALL_24222 ),
+ RIVER_LUM_GRAPPLE_WEST(8, "Grapple Broken Raft", new WorldPoint(3245, 3179, 0), BROKEN_RAFT),
+ RIVER_LUM_GRAPPLE_EAST(8, "Grapple Broken Raft", new WorldPoint(3258, 3179, 0), BROKEN_RAFT),
+ CORSAIR_COVE_ROCKS(10, "Rocks", new WorldPoint(2545, 2871, 0), ROCKS_31757),
+ KARAMJA_MOSS_GIANT_SWING(10, "Rope", null, ROPESWING_23568, ROPESWING_23569),
+ FALADOR_GRAPPLE_WALL(11, "Grapple Wall", new WorldPoint(3031, 3391, 0), WALL_17049, WALL_17050),
+ BRIMHAVEN_DUNGEON_STEPPING_STONES(12, "Stepping Stones", null, STEPPING_STONE_21738),
+ VARROCK_SOUTH_FENCE(13, "Fence", new WorldPoint(3239, 3334, 0), FENCE_16518),
+ GOBLIN_VILLAGE_WALL(14, "Wall", new WorldPoint(2925, 3523, 0), TIGHTGAP),
+ CORSAIR_COVE_DUNGEON_PILLAR(15, "Pillar Jump", new WorldPoint(1980, 8996, 0), PILLAR_31809),
+ EDGEVILLE_DUNGEON_MONKEYBARS(15, "Monkey Bars", null, MONKEYBARS_23566),
+ TROLLHEIM_ROCKS(15, "Rocks", null, new WorldPoint(2838, 3614, 0), ROCKS_3748), // No fixed world map location, but rocks near death plateau have a requirement of 15
+ YANILLE_UNDERWALL_TUNNEL(16, "Underwall Tunnel", new WorldPoint(2574, 3109, 0), HOLE_16520, WALL_17047),
+ YANILLE_WATCHTOWER_TRELLIS(18, "Trellis", null, TRELLIS_20056),
+ COAL_TRUCKS_LOG_BALANCE(20, "Log Balance", new WorldPoint(2598, 3475, 0), LOG_BALANCE_23274),
+ GRAND_EXCHANGE_UNDERWALL_TUNNEL(21, "Underwall Tunnel", new WorldPoint(3139, 3515, 0), UNDERWALL_TUNNEL_16529, UNDERWALL_TUNNEL_16530),
+ BRIMHAVEN_DUNGEON_PIPE(22, "Pipe Squeeze", new WorldPoint(2654, 9569, 0), PIPE_21728),
+ OBSERVATORY_SCALE_CLIFF(23, "Grapple Rocks", new WorldPoint(2447, 3155, 0), NULL_31849),
+ EAGLES_PEAK_ROCK_CLIMB(25, "Rock Climb", new WorldPoint(2320, 3499, 0), ROCKS_19849),
+ FALADOR_UNDERWALL_TUNNEL(26, "Underwall Tunnel", new WorldPoint(2947, 3313, 0), UNDERWALL_TUNNEL, UNDERWALL_TUNNEL_16528),
+ MOUNT_KARUULM_LOWER(29, "Rocks", new WorldPoint(1324, 3782, 0), ROCKS_34397),
+ CORSAIR_COVE_RESOURCE_ROCKS(30, "Rocks", new WorldPoint(2486, 2898, 0), ROCKS_31758, ROCKS_31759),
+ SOUTHEAST_KARAJMA_STEPPING_STONES(30, "Stepping Stones", new WorldPoint(2924, 2946, 0), STEPPING_STONES, STEPPING_STONES_23646, STEPPING_STONES_23647),
+ BRIMHAVEN_DUNGEON_LOG_BALANCE(30, "Log Balance", null, LOG_BALANCE_20882),
+ AGILITY_PYRAMID_ROCKS_EAST(30, "Rocks", null, CLIMBING_ROCKS_11949),
+ DRAYNOR_MANOR_STEPPING_STONES(31, "Stepping Stones", new WorldPoint(3150, 3362, 0), STEPPING_STONE_16533),
+ CATHERBY_CLIFFSIDE_GRAPPLE(32, "Grapple Rock", new WorldPoint(2868, 3429, 0), ROCKS_17042),
+ CAIRN_ISLE_ROCKS(32, "Rocks", null, ROCKS_2231),
+ ARDOUGNE_LOG_BALANCE(33, "Log Balance", new WorldPoint(2602, 3336, 0), LOG_BALANCE_16546, LOG_BALANCE_16547, LOG_BALANCE_16548),
+ BRIMHAVEN_DUNGEON_MEDIUM_PIPE(34, "Pipe Squeeze", null, new WorldPoint(2698, 9501, 0), PIPE_21727),
+ CATHERBY_OBELISK_GRAPPLE(36, "Grapple Rock", new WorldPoint(2841, 3434, 0), CROSSBOW_TREE_17062),
+ GNOME_STRONGHOLD_ROCKS(37, "Rocks", new WorldPoint(2485, 3515, 0), ROCKS_16534, ROCKS_16535),
+ AL_KHARID_MINING_PITCLIFF_SCRAMBLE(38, "Rocks", new WorldPoint(3305, 3315, 0), ROCKS_16549, ROCKS_16550),
+ YANILLE_WALL_GRAPPLE(39, "Grapple Wall", new WorldPoint(2552, 3072, 0), CASTLE_WALL),
+ NEITIZNOT_BRIDGE_REPAIR(40, "Bridge Repair - Quest", new WorldPoint(2315, 3828, 0), ROPE_BRIDGE_21306, ROPE_BRIDGE_21307),
+ KOUREND_LAKE_JUMP_EAST(40, "Stepping Stones", new WorldPoint(1612, 3570, 0), STEPPING_STONE_29729, STEPPING_STONE_29730),
+ KOUREND_LAKE_JUMP_WEST(40, "Stepping Stones", new WorldPoint(1604, 3572, 0), STEPPING_STONE_29729, STEPPING_STONE_29730),
+ YANILLE_DUNGEON_BALANCE(40, "Balancing Ledge", null, BALANCING_LEDGE_23548),
+ TROLLHEIM_EASY_CLIFF_SCRAMBLE(41, "Rocks", new WorldPoint(2869, 3670, 0), ROCKS_16521),
+ DWARVEN_MINE_NARROW_CREVICE(42, "Narrow Crevice", new WorldPoint(3034, 9806, 0), CREVICE_16543),
+ DRAYNOR_UNDERWALL_TUNNEL(42, "Underwall Tunnel", new WorldPoint(3068, 3261, 0), UNDERWALL_TUNNEL_19032, UNDERWALL_TUNNEL_19036),
+ TROLLHEIM_MEDIUM_CLIFF_SCRAMBLE_NORTH(43, "Rocks", new WorldPoint(2886, 3684, 0), ROCKS_3803, ROCKS_3804, ROCKS_16522),
+ TROLLHEIM_MEDIUM_CLIFF_SCRAMBLE_SOUTH(43, "Rocks", new WorldPoint(2876, 3666, 0), ROCKS_3803, ROCKS_3804, ROCKS_16522),
+ TROLLHEIM_ADVANCED_CLIFF_SCRAMBLE(44, "Rocks", new WorldPoint(2907, 3686, 0), ROCKS_16523, ROCKS_3748),
+ KOUREND_RIVER_STEPPING_STONES(45, "Stepping Stones", new WorldPoint(1721, 3509, 0), STEPPING_STONE_29728),
+ TIRANNWN_LOG_BALANCE(45, "Log Balance", null, LOG_BALANCE_3933, LOG_BALANCE_3931, LOG_BALANCE_3930, LOG_BALANCE_3929, LOG_BALANCE_3932),
+ COSMIC_ALTAR_MEDIUM_WALKWAY(46, "Narrow Walkway", new WorldPoint(2399, 4403, 0), JUTTING_WALL_17002),
+ DEEP_WILDERNESS_DUNGEON_CREVICE_NORTH(46, "Narrow Crevice", new WorldPoint(3047, 10335, 0), CREVICE_19043),
+ DEEP_WILDERNESS_DUNGEON_CREVICE_SOUTH(46, "Narrow Crevice", new WorldPoint(3045, 10327, 0), CREVICE_19043),
+ TROLLHEIM_HARD_CLIFF_SCRAMBLE(47, "Rocks", new WorldPoint(2902, 3680, 0), ROCKS_16524),
+ FREMENNIK_LOG_BALANCE(48, "Log Balance", new WorldPoint(2721, 3591, 0), LOG_BALANCE_16540, LOG_BALANCE_16541, LOG_BALANCE_16542),
+ YANILLE_DUNGEON_PIPE_SQUEEZE(49, "Pipe Squeeze", null, OBSTACLE_PIPE_23140),
+ ARCEUUS_ESSENCE_MINE_BOULDER(49, "Boulder", new WorldPoint(1774, 3888, 0), BOULDER_27990),
+ MORYTANIA_STEPPING_STONE(50, "Stepping Stone", new WorldPoint(3418, 3326, 0), STEPPING_STONE_13504),
+ VARROCK_SEWERS_PIPE_SQUEEZE(51, "Pipe Squeeze", new WorldPoint(3152, 9905, 0), OBSTACLE_PIPE_16511),
+ ARCEUUS_ESSENCE_MINE_EAST_SCRAMBLE(52, "Rock Climb", new WorldPoint(1770, 3851, 0), ROCKS_27987, ROCKS_27988),
+ KARAMJA_VOLCANO_GRAPPLE_NORTH(53, "Grapple Rock", new WorldPoint(2873, 3143, 0), STRONG_TREE_17074),
+ KARAMJA_VOLCANO_GRAPPLE_SOUTH(53, "Grapple Rock", new WorldPoint(2874, 3128, 0), STRONG_TREE_17074),
+ MOTHERLODE_MINE_WALL_EAST(54, "Wall", new WorldPoint(3124, 9703, 0), DARK_TUNNEL_10047),
+ MOTHERLODE_MINE_WALL_WEST(54, "Wall", new WorldPoint(3118, 9702, 0), DARK_TUNNEL_10047),
+ MISCELLANIA_DOCK_STEPPING_STONE(55, "Stepping Stone", new WorldPoint(2572, 3862, 0), STEPPING_STONE_11768),
+ ISAFDAR_FOREST_OBSTACLES(56, "Trap", null, DENSE_FOREST_3938, DENSE_FOREST_3939, DENSE_FOREST_3998, DENSE_FOREST_3999, DENSE_FOREST, LEAVES, LEAVES_3924, LEAVES_3925, STICKS, TRIPWIRE),
+ RELEKKA_EAST_FENCE(57, "Fence", new WorldPoint(2688, 3697, 0), BROKEN_FENCE),
+ YANILLE_DUNGEON_MONKEY_BARS(57, "Monkey Bars", null, MONKEYBARS_23567),
+ PHASMATYS_ECTOPOOL_SHORTCUT(58, "Weathered Wall", null , WEATHERED_WALL, WEATHERED_WALL_16526),
+ ELVEN_OVERPASS_CLIFF_SCRAMBLE(59, "Rocks", new WorldPoint(2345, 3300, 0), ROCKS_16514, ROCKS_16515),
+ WILDERNESS_GWD_CLIMB_EAST(60, "Rocks", new WorldPoint(2943, 3770, 0), ROCKY_HANDHOLDS_26400, ROCKY_HANDHOLDS_26401, ROCKY_HANDHOLDS_26402, ROCKY_HANDHOLDS_26404, ROCKY_HANDHOLDS_26405, ROCKY_HANDHOLDS_26406),
+ WILDERNESS_GWD_CLIMB_WEST(60, "Rocks", new WorldPoint(2928, 3760, 0), ROCKY_HANDHOLDS_26400, ROCKY_HANDHOLDS_26401, ROCKY_HANDHOLDS_26402, ROCKY_HANDHOLDS_26404, ROCKY_HANDHOLDS_26405, ROCKY_HANDHOLDS_26406),
+ MOS_LEHARMLESS_STEPPING_STONE(60, "Stepping Stone", new WorldPoint(3710, 2970, 0), STEPPING_STONE_19042),
+ WINTERTODT_GAP(60, "Gap", new WorldPoint(1629, 4023, 0), GAP_29326),
+ UNGAEL_ICE(60, "Ice Chunks", null, NULL_25337, NULL_29868, NULL_29869, NULL_29870, ICE_CHUNKS_31822, NULL_31823, ICE_CHUNKS_31990),
+ SLAYER_TOWER_MEDIUM_CHAIN_FIRST(61, "Spiked Chain (Floor 1)", new WorldPoint(3421, 3550, 0), SPIKEY_CHAIN),
+ SLAYER_TOWER_MEDIUM_CHAIN_SECOND(61, "Spiked Chain (Floor 2)", new WorldPoint(3420, 3551, 0), SPIKEY_CHAIN_16538),
+ SLAYER_DUNGEON_CREVICE(62, "Narrow Crevice", new WorldPoint(2729, 10008, 0), CREVICE_16539),
+ MOUNT_KARUULM_UPPER(62, "Rocks", new WorldPoint(1322, 3791, 0), ROCKS_34396),
+ TAVERLEY_DUNGEON_RAILING(63, "Loose Railing", new WorldPoint(2935, 9811, 0), LOOSE_RAILING_28849),
+ TROLLHEIM_WILDERNESS_ROCKS_EAST(64, "Rocks", new WorldPoint(2945, 3678, 0), ROCKS_16545),
+ TROLLHEIM_WILDERNESS_ROCKS_WEST(64, "Rocks", new WorldPoint(2917, 3672, 0), ROCKS_16545),
+ FOSSIL_ISLAND_VOLCANO(64, "Rope", new WorldPoint(3780, 3822, 0), ROPE_ANCHOR, ROPE_ANCHOR_30917),
+ MORYTANIA_TEMPLE(65, "Loose Railing", new WorldPoint(3422, 3476, 0), ROCKS_16998, ROCKS_16999, ORNATE_RAILING, ORNATE_RAILING_17000),
+ REVENANT_CAVES_GREEN_DRAGONS(65, "Jump", new WorldPoint(3220, 10086, 0), PILLAR_31561),
+ COSMIC_ALTAR_ADVANCED_WALKWAY(66, "Narrow Walkway", new WorldPoint(2408, 4401, 0), JUTTING_WALL_17002),
+ LUMBRIDGE_DESERT_STEPPING_STONE(66, "Stepping Stone", new WorldPoint(3210, 3135, 0), STEPPING_STONE_16513),
+ HEROES_GUILD_TUNNEL_EAST(67, "Crevice", new WorldPoint(2898, 9901, 0), CREVICE_9739, CREVICE_9740),
+ HEROES_GUILD_TUNNEL_WEST(67, "Crevice", new WorldPoint(2913, 9895, 0), CREVICE_9739, CREVICE_9740),
+ YANILLE_DUNGEON_RUBBLE_CLIMB(67, "Pile of Rubble", null, PILE_OF_RUBBLE_23563, PILE_OF_RUBBLE_23564),
+ ELVEN_OVERPASS_MEDIUM_CLIFF(68, "Rocks", new WorldPoint(2337, 3288, 0), ROCKS_16514, ROCKS_16515),
+ WEISS_OBSTACLES(68, "Shortcut", null, LITTLE_BOULDER, ROCKSLIDE_33184, ROCKSLIDE_33185, NULL_33327, NULL_33328, LEDGE_33190, ROCKSLIDE_33191, FALLEN_TREE_33192),
+ ARCEUUS_ESSENSE_NORTH(69, "Rock Climb", new WorldPoint(1759, 3873, 0), ROCKS_27984, ROCKS_27985),
+ TAVERLEY_DUNGEON_PIPE_BLUE_DRAGON(70, "Pipe Squeeze", new WorldPoint(2886, 9798, 0), OBSTACLE_PIPE_16509),
+ TAVERLEY_DUNGEON_ROCKS_NORTH(70, "Rocks", new WorldPoint(2887, 9823, 0), ROCKS, ROCKS_14106),
+ TAVERLEY_DUNGEON_ROCKS_SOUTH(70, "Rocks", new WorldPoint(2887, 9631, 0), ROCKS, ROCKS_14106),
+ FOSSIL_ISLAND_HARDWOOD_NORTH(70, "Hole" , new WorldPoint(3713, 3827, 0), HOLE_31481, HOLE_31482),
+ FOSSIL_ISLAND_HARDWOOD_SOUTH(70, "Hole" , new WorldPoint(3715, 3817, 0), HOLE_31481, HOLE_31482),
+ AL_KHARID_WINDOW(70, "Window", new WorldPoint(3293, 3158, 0), BROKEN_WALL_33344, BIG_WINDOW),
+ GWD_SARADOMIN_ROPE_NORTH(70, "Rope Descent", new WorldPoint(2912, 5300, 0), NULL_26371),
+ GWD_SARADOMIN_ROPE_SOUTH(70, "Rope Descent", new WorldPoint(2951, 5267, 0), NULL_26375),
+ SLAYER_TOWER_ADVANCED_CHAIN_FIRST(71, "Spiked Chain (Floor 2)", new WorldPoint(3447, 3578, 0), SPIKEY_CHAIN ),
+ SLAYER_TOWER_ADVANCED_CHAIN_SECOND(71, "Spiked Chain (Floor 3)", new WorldPoint(3446, 3576, 0), SPIKEY_CHAIN_16538),
+ STRONGHOLD_SLAYER_CAVE_TUNNEL(72, "Tunnel", new WorldPoint(2431, 9806, 0), TUNNEL_30174, TUNNEL_30175),
+ TROLL_STRONGHOLD_WALL_CLIMB(73, "Rocks", new WorldPoint(2841, 3694, 0), ROCKS_16464),
+ ARCEUUS_ESSENSE_MINE_WEST(73, "Rock Climb", new WorldPoint(1742, 3853, 0), ROCKS_27984, ROCKS_27985 ),
+ LAVA_DRAGON_ISLE_JUMP(74, "Stepping Stone", new WorldPoint(3200, 3807, 0), STEPPING_STONE_14918),
+ REVENANT_CAVES_DEMONS_JUMP(75, "Jump", new WorldPoint(3199, 10135, 0), PILLAR_31561),
+ REVENANT_CAVES_ANKOU_EAST(75, "Jump", new WorldPoint(3201, 10195, 0), PILLAR_31561),
+ REVENANT_CAVES_ANKOU_NORTH(75, "Jump", new WorldPoint(3180, 10209, 0), PILLAR_31561),
+ ZUL_ANDRA_ISLAND_CROSSING(76, "Stepping Stone", new WorldPoint(2156, 3073, 0), STEPPING_STONE_10663),
+ SHILO_VILLAGE_STEPPING_STONES( 77, "Stepping Stones", new WorldPoint(2863, 2974, 0), STEPPING_STONE_16466),
+ KHARAZI_JUNGLE_VINE_CLIMB(79, "Vine", new WorldPoint(2897, 2939, 0), NULL_26884, NULL_26886),
+ TAVERLEY_DUNGEON_SPIKED_BLADES(80, "Strange Floor", new WorldPoint(2877, 9813, 0), STRANGE_FLOOR),
+ SLAYER_DUNGEON_CHASM_JUMP(81, "Spiked Blades", new WorldPoint(2770, 10003, 0), STRANGE_FLOOR_16544),
+ LAVA_MAZE_NORTH_JUMP(82, "Stepping Stone", new WorldPoint(3092, 3880, 0), STEPPING_STONE_14917),
+ BRIMHAVEN_DUNGEON_EAST_STEPPING_STONES_NORTH(83, "Stepping Stones", new WorldPoint(2685, 9547, 0), STEPPING_STONE_19040),
+ BRIMHAVEN_DUNGEON_EAST_STEPPING_STONES_SOUTH(83, "Stepping Stones", new WorldPoint(2693, 9529, 0), STEPPING_STONE_19040),
+ ELVEN_ADVANCED_CLIFF_SCRAMBLE(85, "Rocks", new WorldPoint(2337, 3253, 0), ROCKS_16514, ROCKS_16514),
+ KALPHITE_WALL(86, "Crevice", new WorldPoint(3214, 9508, 0), CREVICE_16465),
+ BRIMHAVEN_DUNGEON_VINE_EAST(87, "Vine", new WorldPoint(2672, 9582, 0), VINE_26880, VINE_26882),
+ BRIMHAVEN_DUNGEON_VINE_WEST(87, "Vine", new WorldPoint(2606, 9584, 0), VINE_26880, VINE_26882),
+ REVENANT_CAVES_CHAMBER_JUMP(89, "Jump", new WorldPoint(3240, 10144, 0), PILLAR_31561);
+
+ /**
+ * The agility level required to pass the shortcut
+ */
+ @Getter
+ private final int level;
+ /**
+ * Brief description of the shortcut (e.g. 'Rocks', 'Stepping Stones', 'Jump')
+ */
+ @Getter
+ private final String description;
+ /**
+ * The location of the Shortcut icon on the world map (null if there is no icon)
+ */
+ @Getter
+ private final WorldPoint worldMapLocation;
+ /**
+ * An optional location in case the location of the shortcut icon is either
+ * null or isn't close enough to the obstacle
+ */
+ @Getter
+ private final WorldPoint worldLocation;
+ /**
+ * Array of obstacles, null objects, decorations etc. that this shortcut uses.
+ * Typically an ObjectID/NullObjectID
+ */
+ @Getter
+ private final int[] obstacleIds;
+
+ AgilityShortcut(int level, String description, WorldPoint mapLocation, WorldPoint worldLocation, int... obstacleIds)
+ {
+ this.level = level;
+ this.description = description;
+ this.worldMapLocation = mapLocation;
+ this.worldLocation = worldLocation;
+ this.obstacleIds = obstacleIds;
+ }
+
+ AgilityShortcut(int level, String description, WorldPoint location, int... obstacleIds)
+ {
+ this(level, description, location, location, obstacleIds);
+ }
+
+ public String getTooltip()
+ {
+ return description + " - Level " + level;
+ }
+}
diff --git a/runelite-client/src/main/java/net/runelite/client/game/ItemManager.java b/runelite-client/src/main/java/net/runelite/client/game/ItemManager.java
index ad06024a7d..c91de9262c 100644
--- a/runelite-client/src/main/java/net/runelite/client/game/ItemManager.java
+++ b/runelite-client/src/main/java/net/runelite/client/game/ItemManager.java
@@ -256,6 +256,15 @@ public class ItemManager
itemCompositions.put(event.getItemComposition().getId(), event.getItemComposition());
}
+ /**
+ * Invalidates internal item manager item composition cache (but not client item composition cache)
+ * @see Client#getItemCompositionCache()
+ */
+ public void invalidateItemCompositionCache()
+ {
+ itemCompositions.invalidateAll();
+ }
+
/**
* Look up an item's price
*
diff --git a/runelite-client/src/main/java/net/runelite/client/game/SpriteManager.java b/runelite-client/src/main/java/net/runelite/client/game/SpriteManager.java
index f0df8286cd..f177377818 100644
--- a/runelite-client/src/main/java/net/runelite/client/game/SpriteManager.java
+++ b/runelite-client/src/main/java/net/runelite/client/game/SpriteManager.java
@@ -28,6 +28,7 @@ import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.inject.Inject;
import java.awt.image.BufferedImage;
+import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import javax.annotation.Nullable;
@@ -36,11 +37,14 @@ import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
+import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
import net.runelite.api.GameState;
import net.runelite.api.SpritePixels;
import net.runelite.client.callback.ClientThread;
+import net.runelite.client.util.ImageUtil;
+@Slf4j
@Singleton
public class SpriteManager
{
@@ -127,4 +131,36 @@ public class SpriteManager
});
});
}
+
+ public void addSpriteOverrides(SpriteOverride[] add)
+ {
+ if (add.length <= 0)
+ {
+ return;
+ }
+
+ clientThread.invokeLater(() ->
+ {
+ Map overrides = client.getSpriteOverrides();
+ Class> owner = add[0].getClass();
+ for (SpriteOverride o : add)
+ {
+ BufferedImage image = ImageUtil.getResourceStreamFromClass(owner, o.getFileName());
+ SpritePixels sp = ImageUtil.getImageSpritePixels(image, client);
+ overrides.put(o.getSpriteId(), sp);
+ }
+ });
+ }
+
+ public void removeSpriteOverrides(SpriteOverride[] remove)
+ {
+ clientThread.invokeLater(() ->
+ {
+ Map overrides = client.getSpriteOverrides();
+ for (SpriteOverride o : remove)
+ {
+ overrides.remove(o.getSpriteId());
+ }
+ });
+ }
}
diff --git a/runelite-client/src/main/java/net/runelite/client/game/SpriteOverride.java b/runelite-client/src/main/java/net/runelite/client/game/SpriteOverride.java
new file mode 100644
index 0000000000..a4f894d5c0
--- /dev/null
+++ b/runelite-client/src/main/java/net/runelite/client/game/SpriteOverride.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2018 Abex
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package net.runelite.client.game;
+
+import net.runelite.api.SpriteID;
+
+public interface SpriteOverride
+{
+ /**
+ * An ID for a sprite. Negative numbers are used by RuneLite specific sprites
+ *
+ * @see SpriteID
+ */
+ int getSpriteId();
+
+ /**
+ * The file name for the resource to be loaded, relative to the implementing class
+ */
+ String getFileName();
+}
diff --git a/runelite-client/src/main/java/net/runelite/client/game/chatbox/ChatboxTextInput.java b/runelite-client/src/main/java/net/runelite/client/game/chatbox/ChatboxTextInput.java
index e340027d1a..e59e47059d 100644
--- a/runelite-client/src/main/java/net/runelite/client/game/chatbox/ChatboxTextInput.java
+++ b/runelite-client/src/main/java/net/runelite/client/game/chatbox/ChatboxTextInput.java
@@ -24,7 +24,10 @@
*/
package net.runelite.client.game.chatbox;
+import com.google.common.base.Strings;
+import com.google.common.primitives.Ints;
import com.google.inject.Inject;
+import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.datatransfer.DataFlavor;
@@ -33,21 +36,25 @@ import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
import java.util.function.Consumer;
import java.util.function.IntPredicate;
import java.util.function.Predicate;
import java.util.function.ToIntFunction;
+import java.util.regex.Pattern;
import javax.swing.SwingUtilities;
+import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
-import net.runelite.api.FontTypeFace;
import net.runelite.api.FontID;
-import net.runelite.api.widgets.WidgetType;
+import net.runelite.api.FontTypeFace;
import net.runelite.api.widgets.JavaScriptCallback;
import net.runelite.api.widgets.Widget;
import net.runelite.api.widgets.WidgetPositionMode;
import net.runelite.api.widgets.WidgetSizeMode;
import net.runelite.api.widgets.WidgetTextAlignment;
+import net.runelite.api.widgets.WidgetType;
import net.runelite.client.callback.ClientThread;
import net.runelite.client.input.KeyListener;
import net.runelite.client.input.MouseListener;
@@ -57,6 +64,7 @@ import net.runelite.client.util.Text;
public class ChatboxTextInput extends ChatboxInput implements KeyListener, MouseListener
{
private static final int CURSOR_FLASH_RATE_MILLIS = 1000;
+ private static final Pattern BREAK_MATCHER = Pattern.compile("[^a-zA-Z0-9']");
private final ChatboxPanelManager chatboxPanelManager;
private final ClientThread clientThread;
@@ -66,13 +74,24 @@ public class ChatboxTextInput extends ChatboxInput implements KeyListener, Mouse
return i -> i >= 32 && i < 127;
}
+ @AllArgsConstructor
+ private static class Line
+ {
+ private final int start;
+ private final int end;
+ private final String text;
+ }
+
@Getter
private String prompt;
+ @Getter
+ private int lines;
+
private StringBuffer value = new StringBuffer();
@Getter
- private int cursor = 0;
+ private int cursorStart = 0;
@Getter
private int cursorEnd = 0;
@@ -95,12 +114,14 @@ public class ChatboxTextInput extends ChatboxInput implements KeyListener, Mouse
@Getter
private int fontID = FontID.QUILL_8;
- // This is a lambda so I can have atomic updates for it's captures
- private ToIntFunction getCharOffset = null;
- private Predicate isInBounds = null;
-
+ @Getter
private boolean built = false;
+ // These are lambdas for atomic updates
+ private Predicate isInBounds = null;
+ private ToIntFunction getLineOffset = null;
+ private ToIntFunction getPointCharOffset = null;
+
@Inject
protected ChatboxTextInput(ChatboxPanelManager chatboxPanelManager, ClientThread clientThread)
{
@@ -108,6 +129,16 @@ public class ChatboxTextInput extends ChatboxInput implements KeyListener, Mouse
this.clientThread = clientThread;
}
+ public ChatboxTextInput lines(int lines)
+ {
+ this.lines = lines;
+ if (built)
+ {
+ clientThread.invoke(this::update);
+ }
+ return this;
+ }
+
public ChatboxTextInput prompt(String prompt)
{
this.prompt = prompt;
@@ -157,7 +188,7 @@ public class ChatboxTextInput extends ChatboxInput implements KeyListener, Mouse
end = v;
}
- this.cursor = start;
+ this.cursorStart = start;
this.cursorEnd = end;
if (built)
@@ -209,7 +240,6 @@ public class ChatboxTextInput extends ChatboxInput implements KeyListener, Mouse
protected void update()
{
- this.built = true;
Widget container = chatboxPanelManager.getContainerWidget();
container.deleteAllChildren();
@@ -232,103 +262,209 @@ public class ChatboxTextInput extends ChatboxInput implements KeyListener, Mouse
protected void buildEdit(int x, int y, int w, int h)
{
+ final List editLines = new ArrayList<>();
+
Widget container = chatboxPanelManager.getContainerWidget();
- String lt = Text.escapeJagex(value.substring(0, this.cursor));
- String mt = Text.escapeJagex(value.substring(this.cursor, this.cursorEnd));
- String rt = Text.escapeJagex(value.substring(this.cursorEnd));
-
- Widget leftText = container.createChild(-1, WidgetType.TEXT);
- Widget cursor = container.createChild(-1, WidgetType.RECTANGLE);
- Widget middleText = container.createChild(-1, WidgetType.TEXT);
- Widget rightText = container.createChild(-1, WidgetType.TEXT);
-
- leftText.setFontId(fontID);
- FontTypeFace font = leftText.getFont();
+ final Widget cursor = container.createChild(-1, WidgetType.RECTANGLE);
+ long start = System.currentTimeMillis();
+ cursor.setOnTimerListener((JavaScriptCallback) ev ->
+ {
+ boolean on = (System.currentTimeMillis() - start) % CURSOR_FLASH_RATE_MILLIS > (CURSOR_FLASH_RATE_MILLIS / 2);
+ cursor.setOpacity(on ? 255 : 0);
+ });
+ cursor.setTextColor(0xFFFFFF);
+ cursor.setHasListener(true);
+ cursor.setFilled(true);
+ cursor.setFontId(fontID);
+ FontTypeFace font = cursor.getFont();
if (h <= 0)
{
h = font.getBaseline();
}
- int ltw = font.getTextWidth(lt);
- int mtw = font.getTextWidth(mt);
- int rtw = font.getTextWidth(rt);
+ final int oy = y;
+ final int ox = x;
+ final int oh = h;
- int fullWidth = ltw + mtw + rtw;
-
- int ox = x;
- if (w > 0)
+ int breakIndex = -1;
+ final StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < value.length(); i++)
{
- x += (w - fullWidth) / 2;
- }
-
- int ltx = x;
- int mtx = ltx + ltw;
- int rtx = mtx + mtw;
-
- leftText.setText(lt);
- leftText.setOriginalX(ltx);
- leftText.setOriginalY(y);
- leftText.setOriginalWidth(ltw);
- leftText.setOriginalHeight(h);
- leftText.revalidate();
-
- if (!mt.isEmpty())
- {
- cursor.setTextColor(0x113399);
- }
- else
- {
- cursor.setTextColor(0xFFFFFF);
- long start = System.currentTimeMillis();
- cursor.setOnTimerListener((JavaScriptCallback) ev ->
+ int count = i - sb.length();
+ final String c = value.charAt(i) + "";
+ sb.append(c);
+ if (BREAK_MATCHER.matcher(c).matches())
{
- boolean on = (System.currentTimeMillis() - start) % CURSOR_FLASH_RATE_MILLIS > (CURSOR_FLASH_RATE_MILLIS / 2);
- cursor.setOpacity(on ? 255 : 0);
- });
- cursor.setHasListener(true);
+ breakIndex = sb.length();
+ }
+
+ if (i == value.length() - 1)
+ {
+ Line line = new Line(count, count + sb.length() - 1, sb.toString());
+ editLines.add(line);
+ break;
+ }
+
+ if (font.getTextWidth(sb.toString() + value.charAt(i + 1)) < w)
+ {
+ continue;
+ }
+
+ if (editLines.size() < this.lines - 1 || this.lines == 0)
+ {
+ if (breakIndex > 1)
+ {
+ String str = sb.substring(0, breakIndex);
+ Line line = new Line(count, count + str.length() - 1, str);
+ editLines.add(line);
+
+ sb.replace(0, breakIndex, "");
+ breakIndex = -1;
+ continue;
+ }
+
+ Line line = new Line(count, count + sb.length() - 1, sb.toString());
+ editLines.add(line);
+ sb.replace(0, sb.length(), "");
+ }
}
- cursor.setFilled(true);
- cursor.setOriginalX(mtx - 1);
- cursor.setOriginalY(y);
- cursor.setOriginalWidth(2 + mtw);
- cursor.setOriginalHeight(h);
- cursor.revalidate();
- middleText.setText(mt);
- middleText.setFontId(fontID);
- middleText.setOriginalX(mtx);
- middleText.setOriginalY(y);
- middleText.setOriginalWidth(mtw);
- middleText.setOriginalHeight(h);
- middleText.setTextColor(0xFFFFFF);
- middleText.revalidate();
+ Rectangle bounds = new Rectangle(container.getCanvasLocation().getX() + container.getWidth(), y, 0, editLines.size() * oh);
+ for (int i = 0; i < editLines.size() || i == 0; i++)
+ {
+ final Line line = editLines.size() > 0 ? editLines.get(i) : new Line(0, 0, "");
+ final String text = line.text;
+ final int len = text.length();
- rightText.setText(rt);
- rightText.setFontId(fontID);
- rightText.setOriginalX(rtx);
- rightText.setOriginalY(y);
- rightText.setOriginalWidth(rtw);
- rightText.setOriginalHeight(h);
- rightText.revalidate();
+ String lt = Text.escapeJagex(text);
+ String mt = "";
+ String rt = "";
+
+ final boolean isStartLine = cursorOnLine(cursorStart, line.start, line.end)
+ || (cursorOnLine(cursorStart, line.start, line.end + 1) && i == editLines.size() - 1);
+
+ final boolean isEndLine = cursorOnLine(cursorEnd, line.start, line.end);
+
+ if (isStartLine || isEndLine || (cursorEnd > line.end && cursorStart < line.start))
+ {
+ final int cIdx = Ints.constrainToRange(cursorStart - line.start, 0, len);
+ final int ceIdx = Ints.constrainToRange(cursorEnd - line.start, 0, len);
+
+ lt = Text.escapeJagex(text.substring(0, cIdx));
+ mt = Text.escapeJagex(text.substring(cIdx, ceIdx));
+ rt = Text.escapeJagex(text.substring(ceIdx));
+ }
+
+ final int ltw = font.getTextWidth(lt);
+ final int mtw = font.getTextWidth(mt);
+ final int rtw = font.getTextWidth(rt);
+ final int fullWidth = ltw + mtw + rtw;
+
+ int ltx = ox;
+ if (w > 0)
+ {
+ ltx += (w - fullWidth) / 2;
+ }
+
+ final int mtx = ltx + ltw;
+ final int rtx = mtx + mtw;
+
+ if (ltx < bounds.x)
+ {
+ bounds.setLocation(ltx, bounds.y);
+ }
+
+ if (fullWidth > bounds.width)
+ {
+ bounds.setSize(fullWidth, bounds.height);
+ }
+
+ if (editLines.size() == 0 || isStartLine)
+ {
+ cursor.setOriginalX(mtx - 1);
+ cursor.setOriginalY(y);
+ cursor.setOriginalWidth(2);
+ cursor.setOriginalHeight(h);
+ cursor.revalidate();
+ }
+
+ if (!Strings.isNullOrEmpty(lt))
+ {
+ final Widget leftText = container.createChild(-1, WidgetType.TEXT);
+ leftText.setFontId(fontID);
+ leftText.setText(lt);
+ leftText.setOriginalX(ltx);
+ leftText.setOriginalY(y);
+ leftText.setOriginalWidth(ltw);
+ leftText.setOriginalHeight(h);
+ leftText.revalidate();
+ }
+
+ if (!Strings.isNullOrEmpty(mt))
+ {
+ final Widget background = container.createChild(-1, WidgetType.RECTANGLE);
+ background.setTextColor(0x113399);
+ background.setFilled(true);
+ background.setOriginalX(mtx - 1);
+ background.setOriginalY(y);
+ background.setOriginalWidth(2 + mtw);
+ background.setOriginalHeight(h);
+ background.revalidate();
+
+ final Widget middleText = container.createChild(-1, WidgetType.TEXT);
+ middleText.setText(mt);
+ middleText.setFontId(fontID);
+ middleText.setOriginalX(mtx);
+ middleText.setOriginalY(y);
+ middleText.setOriginalWidth(mtw);
+ middleText.setOriginalHeight(h);
+ middleText.setTextColor(0xFFFFFF);
+ middleText.revalidate();
+ }
+
+ if (!Strings.isNullOrEmpty(rt))
+ {
+ final Widget rightText = container.createChild(-1, WidgetType.TEXT);
+ rightText.setText(rt);
+ rightText.setFontId(fontID);
+ rightText.setOriginalX(rtx);
+ rightText.setOriginalY(y);
+ rightText.setOriginalWidth(rtw);
+ rightText.setOriginalHeight(h);
+ rightText.revalidate();
+ }
+
+ y += h;
+ }
net.runelite.api.Point ccl = container.getCanvasLocation();
- int canvasX = ltx + ccl.getX();
- Rectangle bounds = new Rectangle(ccl.getX() + ox, ccl.getY() + y, w > 0 ? w : fullWidth, h);
- String tsValue = value.toString();
- isInBounds = ev -> bounds.contains(ev.getPoint());
- getCharOffset = ev ->
+ isInBounds = ev -> bounds.contains(new Point(ev.getX() - ccl.getX(), ev.getY() - ccl.getY()));
+ getPointCharOffset = p ->
{
- if (fullWidth <= 0)
+ if (bounds.width <= 0)
{
return 0;
}
- int cx = ev.getX() - canvasX;
+ int cx = p.x - ccl.getX() - ox;
+ int cy = p.y - ccl.getY() - oy;
- int charIndex = (tsValue.length() * cx) / fullWidth;
+ int currentLine = Ints.constrainToRange(cy / oh, 0, editLines.size() - 1);
+
+ final Line line = editLines.get(currentLine);
+ final String tsValue = line.text;
+ int charIndex = tsValue.length();
+ int fullWidth = font.getTextWidth(tsValue);
+
+ int tx = ox;
+ if (w > 0)
+ {
+ tx += (w - fullWidth) / 2;
+ }
+ cx -= tx;
// `i` is used to track max execution time incase there is a font with ligature width data that causes this to fail
for (int i = tsValue.length(); i >= 0 && charIndex >= 0 && charIndex <= tsValue.length(); i--)
@@ -353,22 +489,72 @@ public class ChatboxTextInput extends ChatboxInput implements KeyListener, Mouse
break;
}
- if (charIndex < 0)
+ charIndex = Ints.constrainToRange(charIndex, 0, tsValue.length());
+ return line.start + charIndex;
+ };
+
+ getLineOffset = code ->
+ {
+ if (editLines.size() < 2)
{
- charIndex = 0;
- }
- if (charIndex > tsValue.length())
- {
- charIndex = tsValue.length();
+ return cursorStart;
}
- return charIndex;
+ int currentLine = -1;
+ for (int i = 0; i < editLines.size(); i++)
+ {
+ Line l = editLines.get(i);
+ if (cursorOnLine(cursorStart, l.start, l.end)
+ || (cursorOnLine(cursorStart, l.start, l.end + 1) && i == editLines.size() - 1))
+ {
+ currentLine = i;
+ break;
+ }
+ }
+
+ if (currentLine == -1
+ || (code == KeyEvent.VK_UP && currentLine == 0)
+ || (code == KeyEvent.VK_DOWN && currentLine == editLines.size() - 1))
+ {
+ return cursorStart;
+ }
+
+ final Line line = editLines.get(currentLine);
+ final int direction = code == KeyEvent.VK_UP ? -1 : 1;
+ final Point dest = new Point(cursor.getCanvasLocation().getX(), cursor.getCanvasLocation().getY() + (direction * oh));
+ final int charOffset = getPointCharOffset.applyAsInt(dest);
+
+ // Place cursor on right line if whitespace keep it on the same line or skip a line
+ final Line nextLine = editLines.get(currentLine + direction);
+ if ((direction == -1 && charOffset >= line.start)
+ || (direction == 1 && (charOffset > nextLine.end && (currentLine + direction != editLines.size() - 1))))
+ {
+ return nextLine.end;
+ }
+
+ return charOffset;
};
}
+ private boolean cursorOnLine(final int cursor, final int start, final int end)
+ {
+ return (cursor >= start) && (cursor <= end);
+ }
+
+ private int getCharOffset(MouseEvent ev)
+ {
+ if (getPointCharOffset == null)
+ {
+ return 0;
+ }
+
+ return getPointCharOffset.applyAsInt(ev.getPoint());
+ }
+
@Override
protected void open()
{
+ this.built = true;
update();
}
@@ -398,12 +584,12 @@ public class ChatboxTextInput extends ChatboxInput implements KeyListener, Mouse
char c = e.getKeyChar();
if (charValidator.test(c))
{
- if (cursor != cursorEnd)
+ if (cursorStart != cursorEnd)
{
- value.delete(cursor, cursorEnd);
+ value.delete(cursorStart, cursorEnd);
}
- value.insert(cursor, c);
- cursorAt(cursor + 1);
+ value.insert(cursorStart, c);
+ cursorAt(cursorStart + 1);
if (onChanged != null)
{
onChanged.accept(getValue());
@@ -421,13 +607,13 @@ public class ChatboxTextInput extends ChatboxInput implements KeyListener, Mouse
{
case KeyEvent.VK_X:
case KeyEvent.VK_C:
- if (cursor != cursorEnd)
+ if (cursorStart != cursorEnd)
{
- String s = value.substring(cursor, cursorEnd);
+ String s = value.substring(cursorStart, cursorEnd);
if (code == KeyEvent.VK_X)
{
- value.delete(cursor, cursorEnd);
- cursorAt(cursor);
+ value.delete(cursorStart, cursorEnd);
+ cursorAt(cursorStart);
}
Toolkit.getDefaultToolkit()
.getSystemClipboard()
@@ -441,20 +627,20 @@ public class ChatboxTextInput extends ChatboxInput implements KeyListener, Mouse
.getSystemClipboard()
.getData(DataFlavor.stringFlavor)
.toString();
- if (cursor != cursorEnd)
+ if (cursorStart != cursorEnd)
{
- value.delete(cursor, cursorEnd);
+ value.delete(cursorStart, cursorEnd);
}
for (int i = 0; i < s.length(); i++)
{
char ch = s.charAt(i);
if (charValidator.test(ch))
{
- value.insert(cursor, ch);
- cursor++;
+ value.insert(cursorStart, ch);
+ cursorStart++;
}
}
- cursorAt(cursor);
+ cursorAt(cursorStart);
if (onChanged != null)
{
onChanged.accept(getValue());
@@ -468,13 +654,13 @@ public class ChatboxTextInput extends ChatboxInput implements KeyListener, Mouse
}
return;
}
- int newPos = cursor;
+ int newPos = cursorStart;
if (ev.isShiftDown())
{
if (selectionEnd == -1 || selectionStart == -1)
{
- selectionStart = cursor;
- selectionEnd = cursor;
+ selectionStart = cursorStart;
+ selectionEnd = cursorStart;
}
newPos = selectionEnd;
}
@@ -486,20 +672,20 @@ public class ChatboxTextInput extends ChatboxInput implements KeyListener, Mouse
switch (code)
{
case KeyEvent.VK_DELETE:
- if (cursor != cursorEnd)
+ if (cursorStart != cursorEnd)
{
- value.delete(cursor, cursorEnd);
- cursorAt(cursor);
+ value.delete(cursorStart, cursorEnd);
+ cursorAt(cursorStart);
if (onChanged != null)
{
onChanged.accept(getValue());
}
return;
}
- if (cursor < value.length())
+ if (cursorStart < value.length())
{
- value.deleteCharAt(cursor);
- cursorAt(cursor);
+ value.deleteCharAt(cursorStart);
+ cursorAt(cursorStart);
if (onChanged != null)
{
onChanged.accept(getValue());
@@ -507,20 +693,20 @@ public class ChatboxTextInput extends ChatboxInput implements KeyListener, Mouse
}
return;
case KeyEvent.VK_BACK_SPACE:
- if (cursor != cursorEnd)
+ if (cursorStart != cursorEnd)
{
- value.delete(cursor, cursorEnd);
- cursorAt(cursor);
+ value.delete(cursorStart, cursorEnd);
+ cursorAt(cursorStart);
if (onChanged != null)
{
onChanged.accept(getValue());
}
return;
}
- if (cursor > 0)
+ if (cursorStart > 0)
{
- value.deleteCharAt(cursor - 1);
- cursorAt(cursor - 1);
+ value.deleteCharAt(cursorStart - 1);
+ cursorAt(cursorStart - 1);
if (onChanged != null)
{
onChanged.accept(getValue());
@@ -535,6 +721,14 @@ public class ChatboxTextInput extends ChatboxInput implements KeyListener, Mouse
ev.consume();
newPos++;
break;
+ case KeyEvent.VK_UP:
+ ev.consume();
+ newPos = getLineOffset.applyAsInt(code);
+ break;
+ case KeyEvent.VK_DOWN:
+ ev.consume();
+ newPos = getLineOffset.applyAsInt(code);
+ break;
case KeyEvent.VK_HOME:
ev.consume();
newPos = 0;
@@ -553,9 +747,9 @@ public class ChatboxTextInput extends ChatboxInput implements KeyListener, Mouse
return;
case KeyEvent.VK_ESCAPE:
ev.consume();
- if (cursor != cursorEnd)
+ if (cursorStart != cursorEnd)
{
- cursorAt(cursor);
+ cursorAt(cursorStart);
return;
}
chatboxPanelManager.close();
@@ -602,16 +796,16 @@ public class ChatboxTextInput extends ChatboxInput implements KeyListener, Mouse
}
if (isInBounds == null || !isInBounds.test(mouseEvent))
{
- if (cursor != cursorEnd)
+ if (cursorStart != cursorEnd)
{
selectionStart = -1;
selectionEnd = -1;
- cursorAt(getCharOffset.applyAsInt(mouseEvent));
+ cursorAt(getCharOffset(mouseEvent));
}
return mouseEvent;
}
- int nco = getCharOffset.applyAsInt(mouseEvent);
+ int nco = getCharOffset(mouseEvent);
if (mouseEvent.isShiftDown() && selectionEnd != -1)
{
@@ -653,7 +847,7 @@ public class ChatboxTextInput extends ChatboxInput implements KeyListener, Mouse
return mouseEvent;
}
- int nco = getCharOffset.applyAsInt(mouseEvent);
+ int nco = getCharOffset(mouseEvent);
if (selectionStart != -1)
{
selectionEnd = nco;
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/agility/AgilityOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/agility/AgilityOverlay.java
index bc6e953f6d..e9c205f64f 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/agility/AgilityOverlay.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/agility/AgilityOverlay.java
@@ -36,6 +36,7 @@ import net.runelite.api.Client;
import net.runelite.api.Point;
import net.runelite.api.Tile;
import net.runelite.api.coords.LocalPoint;
+import net.runelite.client.game.AgilityShortcut;
import net.runelite.client.ui.overlay.Overlay;
import net.runelite.client.ui.overlay.OverlayLayer;
import net.runelite.client.ui.overlay.OverlayPosition;
@@ -44,6 +45,7 @@ import net.runelite.client.ui.overlay.OverlayUtil;
class AgilityOverlay extends Overlay
{
private static final int MAX_DISTANCE = 2350;
+ private static final Color SHORTCUT_HIGH_LEVEL_COLOR = Color.ORANGE;
private final Client client;
private final AgilityPlugin plugin;
@@ -66,14 +68,15 @@ class AgilityOverlay extends Overlay
LocalPoint playerLocation = client.getLocalPlayer().getLocalLocation();
Point mousePosition = client.getMouseCanvasPosition();
final List marksOfGrace = plugin.getMarksOfGrace();
- plugin.getObstacles().forEach((object, tile) ->
+ plugin.getObstacles().forEach((object, obstacle) ->
{
- if (Obstacles.SHORTCUT_OBSTACLE_IDS.contains(object.getId()) && !config.highlightShortcuts() ||
+ if (Obstacles.SHORTCUT_OBSTACLE_IDS.containsKey(object.getId()) && !config.highlightShortcuts() ||
Obstacles.TRAP_OBSTACLE_IDS.contains(object.getId()) && !config.showTrapOverlay())
{
return;
}
+ Tile tile = obstacle.getTile();
if (tile.getPlane() == client.getPlane()
&& object.getLocalLocation().distanceTo(playerLocation) < MAX_DISTANCE)
{
@@ -87,11 +90,11 @@ class AgilityOverlay extends Overlay
}
return;
}
-
Area objectClickbox = object.getClickbox();
if (objectClickbox != null)
{
- Color configColor = config.getOverlayColor();
+ AgilityShortcut agilityShortcut = obstacle.getShortcut();
+ Color configColor = agilityShortcut == null || agilityShortcut.getLevel() <= plugin.getAgilityLevel() ? config.getOverlayColor() : SHORTCUT_HIGH_LEVEL_COLOR;
if (config.highlightMarks() && !marksOfGrace.isEmpty())
{
configColor = config.getMarkColor();
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/agility/AgilityPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/agility/AgilityPlugin.java
index cd60c6b940..c895a07980 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/agility/AgilityPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/agility/AgilityPlugin.java
@@ -38,10 +38,12 @@ import net.runelite.api.Item;
import net.runelite.api.ItemID;
import static net.runelite.api.ItemID.AGILITY_ARENA_TICKET;
import net.runelite.api.Player;
+import net.runelite.api.Skill;
import static net.runelite.api.Skill.AGILITY;
import net.runelite.api.Tile;
import net.runelite.api.TileObject;
import net.runelite.api.coords.WorldPoint;
+import net.runelite.api.events.BoostedLevelChanged;
import net.runelite.api.events.ConfigChanged;
import net.runelite.api.events.DecorativeObjectChanged;
import net.runelite.api.events.DecorativeObjectDespawned;
@@ -63,6 +65,7 @@ import net.runelite.api.events.WallObjectSpawned;
import net.runelite.client.Notifier;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.Subscribe;
+import net.runelite.client.game.AgilityShortcut;
import net.runelite.client.game.ItemManager;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
@@ -80,7 +83,7 @@ public class AgilityPlugin extends Plugin
private static final int AGILITY_ARENA_REGION_ID = 11157;
@Getter
- private final Map obstacles = new HashMap<>();
+ private final Map obstacles = new HashMap<>();
@Getter
private final List marksOfGrace = new ArrayList<>();
@@ -115,6 +118,9 @@ public class AgilityPlugin extends Plugin
private int lastAgilityXp;
private WorldPoint lastArenaTicketPosition;
+ @Getter
+ private int agilityLevel;
+
@Provides
AgilityConfig getConfig(ConfigManager configManager)
{
@@ -126,6 +132,7 @@ public class AgilityPlugin extends Plugin
{
overlayManager.add(agilityOverlay);
overlayManager.add(lapCounterOverlay);
+ agilityLevel = client.getBoostedSkillLevel(Skill.AGILITY);
}
@Override
@@ -136,6 +143,7 @@ public class AgilityPlugin extends Plugin
marksOfGrace.clear();
obstacles.clear();
session = null;
+ agilityLevel = 0;
}
@Subscribe
@@ -208,6 +216,17 @@ public class AgilityPlugin extends Plugin
}
}
+
+ @Subscribe
+ public void onBoostedLevelChanged(BoostedLevelChanged boostedLevelChanged)
+ {
+ Skill skill = boostedLevelChanged.getSkill();
+ if (skill == AGILITY)
+ {
+ agilityLevel = client.getBoostedSkillLevel(skill);
+ }
+ }
+
@Subscribe
public void onItemSpawned(ItemSpawned itemSpawned)
{
@@ -366,11 +385,40 @@ public class AgilityPlugin extends Plugin
}
if (Obstacles.COURSE_OBSTACLE_IDS.contains(newObject.getId()) ||
- Obstacles.SHORTCUT_OBSTACLE_IDS.contains(newObject.getId()) ||
(Obstacles.TRAP_OBSTACLE_IDS.contains(newObject.getId())
&& Obstacles.TRAP_OBSTACLE_REGIONS.contains(newObject.getWorldLocation().getRegionID())))
{
- obstacles.put(newObject, tile);
+ obstacles.put(newObject, new Obstacle(tile, null));
+ }
+
+ if (Obstacles.SHORTCUT_OBSTACLE_IDS.containsKey(newObject.getId()))
+ {
+ AgilityShortcut closestShortcut = null;
+ int distance = -1;
+
+ // Find the closest shortcut to this object
+ for (AgilityShortcut shortcut : Obstacles.SHORTCUT_OBSTACLE_IDS.get(newObject.getId()))
+ {
+ if (shortcut.getWorldLocation() == null)
+ {
+ closestShortcut = shortcut;
+ break;
+ }
+ else
+ {
+ int newDistance = shortcut.getWorldLocation().distanceTo2D(newObject.getWorldLocation());
+ if (closestShortcut == null || newDistance < distance)
+ {
+ closestShortcut = shortcut;
+ distance = newDistance;
+ }
+ }
+ }
+
+ if (closestShortcut != null)
+ {
+ obstacles.put(newObject, new Obstacle(tile, closestShortcut));
+ }
}
}
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/agility/Obstacle.java b/runelite-client/src/main/java/net/runelite/client/plugins/agility/Obstacle.java
new file mode 100644
index 0000000000..6038de468b
--- /dev/null
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/agility/Obstacle.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2019, MrGroggle
+ * 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.
+ */
+package net.runelite.client.plugins.agility;
+
+import javax.annotation.Nullable;
+import lombok.AllArgsConstructor;
+import lombok.Value;
+import net.runelite.api.Tile;
+import net.runelite.client.game.AgilityShortcut;
+
+@Value
+@AllArgsConstructor
+class Obstacle
+{
+ private final Tile tile;
+ @Nullable
+ private final AgilityShortcut shortcut;
+}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/agility/Obstacles.java b/runelite-client/src/main/java/net/runelite/client/plugins/agility/Obstacles.java
index 43a96bd151..92df605a36 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/agility/Obstacles.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/agility/Obstacles.java
@@ -25,11 +25,27 @@
package net.runelite.client.plugins.agility;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Multimap;
import java.util.List;
import java.util.Set;
-import static net.runelite.api.NullObjectID.*;
+import static net.runelite.api.NullObjectID.NULL_10872;
+import static net.runelite.api.NullObjectID.NULL_10873;
+import static net.runelite.api.NullObjectID.NULL_12945;
+import static net.runelite.api.NullObjectID.NULL_18083;
+import static net.runelite.api.NullObjectID.NULL_18116;
+import static net.runelite.api.NullObjectID.NULL_18122;
+import static net.runelite.api.NullObjectID.NULL_18124;
+import static net.runelite.api.NullObjectID.NULL_18129;
+import static net.runelite.api.NullObjectID.NULL_18130;
+import static net.runelite.api.NullObjectID.NULL_18132;
+import static net.runelite.api.NullObjectID.NULL_18133;
+import static net.runelite.api.NullObjectID.NULL_18135;
+import static net.runelite.api.NullObjectID.NULL_18136;
+import static net.runelite.api.NullObjectID.NULL_3550;
import static net.runelite.api.ObjectID.*;
+import net.runelite.client.game.AgilityShortcut;
class Obstacles
{
@@ -62,7 +78,7 @@ class Obstacles
STEPPING_STONE_15412, TROPICAL_TREE_15414, MONKEYBARS_15417, SKULL_SLOPE_15483, ROPE_15487, TROPICAL_TREE_16062,
// Falador
ROUGH_WALL_10833, TIGHTROPE_10834, HAND_HOLDS_10836, GAP_11161, GAP_11360, TIGHTROPE_11361,
- TIGHTROPE_11364, GAP_11365, LEDGE_11366, LEDGE_11367, LEDGE_11368, LEDGE_11370, EDGE_11371,
+ TIGHTROPE_11364, GAP_11365, LEDGE_11366, LEDGE_11367, LEDGE_11369, LEDGE_11370, EDGE_11371,
// Wilderness
OBSTACLE_PIPE_23137, ROPESWING_23132, STEPPING_STONE_23556, LOG_BALANCE_23542, ROCKS_23640,
// Seers
@@ -91,144 +107,7 @@ class Obstacles
ZIP_LINE_11645, ZIP_LINE_11646
);
- static final Set SHORTCUT_OBSTACLE_IDS = ImmutableSet.of(
- // Grand Exchange
- UNDERWALL_TUNNEL_16529, UNDERWALL_TUNNEL_16530,
- // South Varrock
- STEPPING_STONE_16533, FENCE_16518, ROCKS_16549, ROCKS_16550,
- // Falador
- WALL_17049, WALL_17050, CRUMBLING_WALL_24222, UNDERWALL_TUNNEL, UNDERWALL_TUNNEL_16528, CREVICE_16543,
- // Draynor
- UNDERWALL_TUNNEL_19032, UNDERWALL_TUNNEL_19036,
- // South Lumbridge
- BROKEN_RAFT, STEPPING_STONE_16513,
- // Trollheim
- ROCKS_3790, ROCKS_3791, ROCKS_3803, ROCKS_3804, ROCKS_16523, ROCKS_16524, ROCKS_3748, ROCKS_16545, ROCKS_16521,
- ROCKS_16522, ROCKS_16464,
- // North Camelot
- LOG_BALANCE_16540, LOG_BALANCE_16541, LOG_BALANCE_16542,
- // Rellekka
- BROKEN_FENCE,
- // Ardougne
- LOG_BALANCE_16546, LOG_BALANCE_16547, LOG_BALANCE_16548,
- // Yanille
- CASTLE_WALL, HOLE_16520, WALL_17047,
- // Observatory
- NULL_31849,
- // Gnome Stronghold
- ROCKS_16534, ROCKS_16535,
- // Karamja Volcano
- STRONG_TREE_17074,
- // Shilo Village
- STEPPING_STONE_16466,
- // Vine east of Shilo Village
- NULL_26884, NULL_26886,
- // Stepping stones east of Shilo Village
- STEPPING_STONES, STEPPING_STONES_23646, STEPPING_STONES_23647,
- // Middle of Karamja
- A_WOODEN_LOG,
- // Slayer Tower
- SPIKEY_CHAIN, SPIKEY_CHAIN_16538,
- // Fremennik Slayer Cave
- STRANGE_FLOOR_16544, CREVICE_16539, STEPS_29993,
- // Wilderness
- STEPPING_STONE_14918, STEPPING_STONE_14917, ROCKY_HANDHOLDS_26404, ROCKY_HANDHOLDS_26405, ROCKY_HANDHOLDS_26406,
- // Godwars
- ROCKY_HANDHOLDS_26400, ROCKY_HANDHOLDS_26401, ROCKY_HANDHOLDS_26402,
- // Seers' Village Coal Mine
- LOG_BALANCE_23274,
- // Arceuus Essence Mine
- ROCKS_27984, ROCKS_27985, BOULDER_27990, ROCKS_27987, ROCKS_27988,
- // Wintertodt
- GAP_29326,
- // Gnome Stronghold Slayer Underground
- TUNNEL_30174, TUNNEL_30175,
- // Taverley Underground
- OBSTACLE_PIPE_16509, STRANGE_FLOOR, ROCKS, ROCKS_14106, LOOSE_RAILING_28849,
- // Heroes Guild
- CREVICE_9739, CREVICE_9740,
- // Fossil Island
- HOLE_31481, HOLE_31482, LADDER_30938, LADDER_30939, LADDER_30940, LADDER_30941, ROPE_ANCHOR, ROPE_ANCHOR_30917,
- RUBBER_CAP_MUSHROOM,
- ROCKS_31757, ROCKS_31758, ROCKS_31759, PILLAR_31809,
- // West Brimhaven
- ROPESWING_23568, ROPESWING_23569,
- // Brimhaven Dungeon
- VINE_26880, VINE_26882, PIPE_21728, STEPPING_STONE_19040, PIPE_21727, LOG_BALANCE_20882, LOG_BALANCE_20884,
- STEPPING_STONE_21738, STEPPING_STONE_21739, TIGHTGAP,
- // Lumbridge
- STILE_12982,
- // Edgeville Dungeon
- MONKEYBARS_23566, OBSTACLE_PIPE_16511,
- // Miscellania
- STEPPING_STONE_11768,
- // Kalphite
- CREVICE_16465,
- // Eagles' Peak
- ROCKS_19849,
- // Catherby
- CROSSBOW_TREE_17062, ROCKS_17042,
- // McGrubor's Woods
- LOOSE_RAILING,
- // Cairn Isle
- ROCKS_2231,
- // South Kourend
- STEPPING_STONE_29728, STEPPING_STONE_29729, STEPPING_STONE_29730,
- // Cosmic Temple
- JUTTING_WALL_17002,
- // Arandar
- ROCKS_16514, ROCKS_16515, LOG_BALANCE_3933,
- // South River Salve
- STEPPING_STONE_13504,
- DARK_TUNNEL_10047,
- // Ectofuntus
- WEATHERED_WALL, WEATHERED_WALL_16526,
- // Mos Le'Harmless
- STEPPING_STONE_19042,
- // North River Salve
- ROCKS_16998, ROCKS_16999, ORNATE_RAILING, ORNATE_RAILING_17000,
- // West Zul-Andra
- STEPPING_STONE_10663,
- // Yanille Agility Dungeon
- BALANCING_LEDGE_23548, OBSTACLE_PIPE_23140, MONKEYBARS_23567, PILE_OF_RUBBLE_23563, PILE_OF_RUBBLE_23564,
- // High Level Wilderness Dungeon
- CREVICE_19043,
- // Revenant Caves
- PILLAR_31561,
- // Elf Camp Isafdar Tirranwn
- LOG_BALANCE_3931, LOG_BALANCE_3930, LOG_BALANCE_3929, LOG_BALANCE_3932, DENSE_FOREST_3938, DENSE_FOREST_3939,
- DENSE_FOREST_3998, DENSE_FOREST_3999, DENSE_FOREST, LEAVES, LEAVES_3924, LEAVES_3925, STICKS, TRIPWIRE,
- // Gu'Tanoth bridge
- GAP, GAP_2831,
- // Lumbridge Swamp Caves
- STEPPING_STONE_5948, STEPPING_STONE_5949, ROCKS_6673,
- // Morytania Pirate Ship
- ROCK_16115,
- // Agility Pyramid Entrance
- CLIMBING_ROCKS_11948, CLIMBING_ROCKS_11949,
- // Lumber Yard
- BROKEN_FENCE_2618,
- // Ungael and Vorkath crater
- NULL_25337, NULL_29868, NULL_29869, NULL_29870, ICE_CHUNKS_31822, NULL_31823, ICE_CHUNKS_31990,
- // Underwater Area Fossil Island
- TUNNEL_30959, HOLE_30966, OBSTACLE, OBSTACLE_30767, OBSTACLE_30964, OBSTACLE_30962,
- // Tree Gnome Village
- LOOSE_RAILING_2186,
- // Weiss
- LITTLE_BOULDER, ROCKSLIDE_33184, ROCKSLIDE_33185, NULL_33327, NULL_33328, LEDGE_33190, ROCKSLIDE_33191, FALLEN_TREE_33192,
- // Al-Kharid
- BROKEN_WALL_33344, BIG_WINDOW,
- // Burgh de Rott
- LOW_FENCE,
- // Taverley
- STILE,
- // Asgarnian Ice Dungeon
- STEPS,
- // Fossil Island Wyvern Cave
- STAIRS_31485,
- // Mount Karuulm
- ROCKS_34397, ROCKS_34396
- );
+ static final Multimap SHORTCUT_OBSTACLE_IDS;
static final Set TRAP_OBSTACLE_IDS = ImmutableSet.of(
// Agility pyramid
@@ -236,4 +115,17 @@ class Obstacles
);
static final List TRAP_OBSTACLE_REGIONS = ImmutableList.of(12105, 13356);
+
+ static
+ {
+ final ImmutableMultimap.Builder builder = ImmutableMultimap.builder();
+ for (final AgilityShortcut item : AgilityShortcut.values())
+ {
+ for (int obstacle : item.getObstacleIds())
+ {
+ builder.put(obstacle, item);
+ }
+ }
+ SHORTCUT_OBSTACLE_IDS = builder.build();
+ }
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/banktags/BankTagsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/banktags/BankTagsPlugin.java
index 620cc586be..6a3a7f3416 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/banktags/BankTagsPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/banktags/BankTagsPlugin.java
@@ -60,6 +60,7 @@ import net.runelite.client.callback.ClientThread;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.game.ItemManager;
+import net.runelite.client.game.SpriteManager;
import net.runelite.client.game.chatbox.ChatboxPanelManager;
import net.runelite.client.input.KeyListener;
import net.runelite.client.input.KeyManager;
@@ -125,6 +126,9 @@ public class BankTagsPlugin extends Plugin implements MouseWheelListener, KeyLis
@Inject
private KeyManager keyManager;
+ @Inject
+ private SpriteManager spriteManager;
+
private boolean shiftPressed = false;
@Provides
@@ -139,7 +143,7 @@ public class BankTagsPlugin extends Plugin implements MouseWheelListener, KeyLis
keyManager.registerKeyListener(this);
mouseManager.registerMouseWheelListener(this);
clientThread.invokeLater(tabInterface::init);
- client.getSpriteOverrides().putAll(TabSprites.toMap(client));
+ spriteManager.addSpriteOverrides(TabSprites.values());
}
@Override
@@ -148,11 +152,7 @@ public class BankTagsPlugin extends Plugin implements MouseWheelListener, KeyLis
keyManager.unregisterKeyListener(this);
mouseManager.unregisterMouseWheelListener(this);
clientThread.invokeLater(tabInterface::destroy);
-
- for (TabSprites value : TabSprites.values())
- {
- client.getSpriteOverrides().remove(value.getSpriteId());
- }
+ spriteManager.removeSpriteOverrides(TabSprites.values());
shiftPressed = false;
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/banktags/TagManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/banktags/TagManager.java
index c20974758d..c1d5ff8c11 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/banktags/TagManager.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/banktags/TagManager.java
@@ -167,6 +167,20 @@ public class TagManager
}
}
+ public void renameTag(String oldTag, String newTag)
+ {
+ List items = getItemsForTag(Text.standardize(oldTag));
+ items.forEach(id ->
+ {
+ Collection tags = getTags(id, id < 0);
+
+ tags.remove(Text.standardize(oldTag));
+ tags.add(Text.standardize(newTag));
+
+ setTags(id, tags, id < 0);
+ });
+ }
+
private int getItemId(int itemId, boolean variation)
{
itemId = Math.abs(itemId);
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/banktags/tabs/MenuIndexes.java b/runelite-client/src/main/java/net/runelite/client/plugins/banktags/tabs/MenuIndexes.java
index afa8f6e276..51e179b143 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/banktags/tabs/MenuIndexes.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/banktags/tabs/MenuIndexes.java
@@ -39,5 +39,6 @@ class MenuIndexes
static final int CHANGE_ICON = 3;
static final int DELETE_TAB = 4;
static final int EXPORT_TAB = 5;
+ static final int RENAME_TAB = 6;
}
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/banktags/tabs/TabInterface.java b/runelite-client/src/main/java/net/runelite/client/plugins/banktags/tabs/TabInterface.java
index 148b965043..2daaf0f5c6 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/banktags/tabs/TabInterface.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/banktags/tabs/TabInterface.java
@@ -64,7 +64,6 @@ import net.runelite.api.SpriteID;
import net.runelite.api.VarClientInt;
import net.runelite.api.VarClientStr;
import net.runelite.api.Varbits;
-import net.runelite.api.widgets.WidgetType;
import net.runelite.api.events.MenuEntryAdded;
import net.runelite.api.events.MenuOptionClicked;
import net.runelite.api.vars.InputType;
@@ -73,15 +72,14 @@ import net.runelite.api.widgets.JavaScriptCallback;
import net.runelite.api.widgets.Widget;
import net.runelite.api.widgets.WidgetConfig;
import net.runelite.api.widgets.WidgetInfo;
+import net.runelite.api.widgets.WidgetSizeMode;
+import net.runelite.api.widgets.WidgetType;
import net.runelite.client.Notifier;
import net.runelite.client.callback.ClientThread;
-import net.runelite.client.config.ConfigManager;
import net.runelite.client.game.ItemManager;
import net.runelite.client.game.chatbox.ChatboxPanelManager;
import net.runelite.client.plugins.banktags.BankTagsConfig;
import net.runelite.client.plugins.banktags.BankTagsPlugin;
-import static net.runelite.client.plugins.banktags.BankTagsPlugin.CONFIG_GROUP;
-import static net.runelite.client.plugins.banktags.BankTagsPlugin.ICON_SEARCH;
import static net.runelite.client.plugins.banktags.BankTagsPlugin.TAG_SEARCH;
import static net.runelite.client.plugins.banktags.BankTagsPlugin.VAR_TAG_SUFFIX;
import net.runelite.client.plugins.banktags.TagManager;
@@ -102,6 +100,7 @@ public class TabInterface
private static final String EXPORT_TAB = "Export tag tab";
private static final String IMPORT_TAB = "Import tag tab";
private static final String VIEW_TAB = "View tag tab";
+ private static final String RENAME_TAB = "Rename tag tab";
private static final String CHANGE_ICON = "Change icon";
private static final String REMOVE_TAG = "Remove-tag";
private static final String TAG_GEAR = "Tag-equipment";
@@ -111,11 +110,12 @@ public class TabInterface
private static final int BUTTON_HEIGHT = 20;
private static final int MARGIN = 1;
private static final int SCROLL_TICK = 500;
+ private static final int INCINERATOR_WIDTH = 48;
+ private static final int INCINERATOR_HEIGHT = 39;
private final Client client;
private final ClientThread clientThread;
private final ItemManager itemManager;
- private final ConfigManager configManager;
private final TagManager tagManager;
private final TabManager tabManager;
private final ChatboxPanelManager chatboxPanelManager;
@@ -148,7 +148,6 @@ public class TabInterface
final Client client,
final ClientThread clientThread,
final ItemManager itemManager,
- final ConfigManager configManager,
final TagManager tagManager,
final TabManager tabManager,
final ChatboxPanelManager chatboxPanelManager,
@@ -159,7 +158,6 @@ public class TabInterface
this.client = client;
this.clientThread = clientThread;
this.itemManager = itemManager;
- this.configManager = configManager;
this.tagManager = tagManager;
this.tabManager = tabManager;
this.chatboxPanelManager = chatboxPanelManager;
@@ -211,7 +209,7 @@ public class TabInterface
if (config.rememberTab() && !Strings.isNullOrEmpty(config.tab()))
{
- openTag(TAG_SEARCH + config.tab());
+ openTag(config.tab());
}
}
@@ -239,7 +237,7 @@ public class TabInterface
tagManager.addTag(item, activeTab.getTag(), false);
}
- openTag(TAG_SEARCH + activeTab.getTag());
+ openTag(activeTab.getTag());
}
return;
@@ -292,7 +290,7 @@ public class TabInterface
final Iterator dataIter = Text.fromCSV(dataString).iterator();
final String name = dataIter.next();
final String icon = dataIter.next();
- configManager.setConfiguration(CONFIG_GROUP, ICON_SEARCH + name, icon);
+ tabManager.setIcon(name, icon);
while (dataIter.hasNext())
{
@@ -306,7 +304,7 @@ public class TabInterface
if (activeTab != null && name.equals(activeTab.getTag()))
{
- openTag(TAG_SEARCH + activeTab.getTag());
+ openTag(activeTab.getTag());
}
notifier.notify("Tag tab " + name + " has been imported from your clipboard!");
@@ -336,7 +334,7 @@ public class TabInterface
}
else
{
- openTag(TAG_SEARCH + Text.removeTags(clicked.getName()));
+ openTag(Text.removeTags(clicked.getName()));
}
client.playSoundEffect(SoundEffectID.UI_BOOP);
@@ -373,6 +371,10 @@ public class TabInterface
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(stringSelection, null);
notifier.notify("Tag tab " + tagTab.getTag() + " has been copied to your clipboard!");
break;
+ case Tab.RENAME_TAB:
+ String renameTarget = Text.standardize(event.getOpbase());
+ renameTab(renameTarget);
+ break;
}
}
@@ -550,7 +552,7 @@ public class TabInterface
int itemId = itemManager.canonicalize(item.getId());
iconToSet.setIconItemId(itemId);
iconToSet.getIcon().setItemId(itemId);
- configManager.setConfiguration(CONFIG_GROUP, ICON_SEARCH + iconToSet.getTag(), itemId + "");
+ tabManager.setIcon(iconToSet.getTag(), itemId + "");
event.consume();
}
@@ -597,7 +599,7 @@ public class TabInterface
{
if (activeTab != null && tags.contains(activeTab.getTag()))
{
- openTag(TAG_SEARCH + activeTab.getTag());
+ openTag(activeTab.getTag());
}
}
@@ -683,6 +685,7 @@ public class TabInterface
btn.setAction(2, CHANGE_ICON);
btn.setAction(3, REMOVE_TAB);
btn.setAction(4, EXPORT_TAB);
+ btn.setAction(5, RENAME_TAB);
btn.setOnOpListener((JavaScriptCallback) this::handleTagTab);
tagTab.setBackground(btn);
}
@@ -712,13 +715,66 @@ public class TabInterface
}
tabManager.remove(tag);
- configManager.unsetConfiguration(CONFIG_GROUP, ICON_SEARCH + tag);
tabManager.save();
updateBounds();
scrollTab(0);
}
+ private void renameTab(String oldTag)
+ {
+ chatboxPanelManager.openTextInput("Enter new tag name for tag \"" + oldTag + "\":")
+ .onDone((newTag) -> clientThread.invoke(() ->
+ {
+ if (!Strings.isNullOrEmpty(newTag) && !newTag.equalsIgnoreCase(oldTag))
+ {
+ if (tabManager.find(newTag) == null)
+ {
+ TagTab tagTab = tabManager.find(oldTag);
+ tagTab.setTag(newTag);
+
+ final String coloredName = ColorUtil.wrapWithColorTag(newTag, HILIGHT_COLOR);
+ tagTab.getIcon().setName(coloredName);
+ tagTab.getBackground().setName(coloredName);
+
+ tabManager.removeIcon(oldTag);
+ tabManager.setIcon(newTag, tagTab.getIconItemId() + "");
+
+ tabManager.save();
+ tagManager.renameTag(oldTag, newTag);
+
+ if (activeTab != null && activeTab.equals(tagTab))
+ {
+ openTag(newTag);
+ }
+ }
+ else
+ {
+ chatboxPanelManager.openTextMenuInput("The specified bank tag already exists.")
+ .option("1. Merge into existing tag \"" + newTag + "\".", () ->
+ clientThread.invoke(() ->
+ {
+ tagManager.renameTag(oldTag, newTag);
+ final String activeTag = activeTab != null ? activeTab.getTag() : "";
+ deleteTab(oldTag);
+
+ if (activeTag.equals(oldTag))
+ {
+ openTag(newTag);
+ }
+ })
+ )
+ .option("2. Choose a different name.", () ->
+ clientThread.invoke(() ->
+ renameTab(oldTag))
+ )
+ .build();
+ }
+ }
+ }))
+ .build();
+ }
+
private void scrollTick(int direction)
{
// This ensures that dragging on scroll buttons do not scrolls too fast
@@ -805,17 +861,18 @@ public class TabInterface
if (incinerator != null && !incinerator.isHidden())
{
- // This is the required way to move incinerator, don't change it!
- incinerator.setOriginalHeight(39);
- incinerator.setOriginalWidth(48);
- incinerator.setRelativeY(itemContainer.getHeight());
- incinerator.revalidate();
+ incinerator.setOriginalHeight(INCINERATOR_HEIGHT);
+ incinerator.setOriginalWidth(INCINERATOR_WIDTH);
+ incinerator.setOriginalY(INCINERATOR_HEIGHT);
Widget child = incinerator.getDynamicChildren()[0];
- child.setHeight(39);
- child.setWidth(48);
+ child.setOriginalHeight(INCINERATOR_HEIGHT);
+ child.setOriginalWidth(INCINERATOR_WIDTH);
+ child.setWidthMode(WidgetSizeMode.ABSOLUTE);
+ child.setHeightMode(WidgetSizeMode.ABSOLUTE);
child.setType(WidgetType.GRAPHIC);
child.setSpriteId(TabSprites.INCINERATOR.getSpriteId());
+ incinerator.revalidate();
bounds.setSize(TAB_WIDTH + MARGIN * 2, height - incinerator.getHeight());
}
@@ -900,7 +957,6 @@ public class TabInterface
private void updateWidget(Widget t, int y)
{
t.setOriginalY(y);
- t.setRelativeY(y);
t.setHidden(y < (bounds.y + BUTTON_HEIGHT + MARGIN) || y > (bounds.y + bounds.height - TAB_HEIGHT - MARGIN - BUTTON_HEIGHT));
t.revalidate();
}
@@ -913,10 +969,10 @@ public class TabInterface
return itemManager.getItemComposition(item.getId());
}
- private void openTag(String tag)
+ private void openTag(final String tag)
{
- bankSearch.search(InputType.SEARCH, tag, true);
- activateTab(tabManager.find(tag.substring(TAG_SEARCH.length())));
+ bankSearch.search(InputType.SEARCH, TAG_SEARCH + tag, true);
+ activateTab(tabManager.find(tag));
// When tab is selected with search window open, the search window closes but the search button
// stays highlighted, this solves that issue
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/banktags/tabs/TabManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/banktags/tabs/TabManager.java
index 3f1430dd7c..e1995a5095 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/banktags/tabs/TabManager.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/banktags/tabs/TabManager.java
@@ -115,6 +115,7 @@ class TabManager
{
tagTab.setHidden(true);
tabs.remove(tagTab);
+ removeIcon(tag);
}
}
@@ -124,6 +125,16 @@ class TabManager
configManager.setConfiguration(CONFIG_GROUP, TAG_TABS_CONFIG, tags);
}
+ void removeIcon(final String tag)
+ {
+ configManager.unsetConfiguration(CONFIG_GROUP, ICON_SEARCH + Text.standardize(tag));
+ }
+
+ void setIcon(final String tag, final String icon)
+ {
+ configManager.setConfiguration(CONFIG_GROUP, ICON_SEARCH + Text.standardize(tag), icon);
+ }
+
int size()
{
return tabs.size();
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/banktags/tabs/TabSprites.java b/runelite-client/src/main/java/net/runelite/client/plugins/banktags/tabs/TabSprites.java
index e7706679b0..20f9d0dfb6 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/banktags/tabs/TabSprites.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/banktags/tabs/TabSprites.java
@@ -25,17 +25,12 @@
*/
package net.runelite.client.plugins.banktags.tabs;
-import java.awt.image.BufferedImage;
-import java.util.HashMap;
-import java.util.Map;
import lombok.Getter;
-import lombok.extern.slf4j.Slf4j;
-import net.runelite.api.Client;
-import net.runelite.api.SpritePixels;
-import net.runelite.client.util.ImageUtil;
+import lombok.RequiredArgsConstructor;
+import net.runelite.client.game.SpriteOverride;
-@Slf4j
-public enum TabSprites
+@RequiredArgsConstructor
+public enum TabSprites implements SpriteOverride
{
INCINERATOR(-200, "incinerator.png"),
TAB_BACKGROUND(-201, "tag-tab.png"),
@@ -46,23 +41,7 @@ public enum TabSprites
@Getter
private final int spriteId;
- private final BufferedImage image;
- TabSprites(final int spriteId, final String imageName)
- {
- this.spriteId = spriteId;
- this.image = ImageUtil.getResourceStreamFromClass(this.getClass(), imageName);
- }
-
- public static Map toMap(Client client)
- {
- final Map map = new HashMap<>();
-
- for (TabSprites value : values())
- {
- map.put(value.spriteId, ImageUtil.getImageSpritePixels(value.image, client));
- }
-
- return map;
- }
+ @Getter
+ private final String fileName;
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/banktags/tabs/TagTab.java b/runelite-client/src/main/java/net/runelite/client/plugins/banktags/tabs/TagTab.java
index d3d417f465..004e5f45ed 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/banktags/tabs/TagTab.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/banktags/tabs/TagTab.java
@@ -33,7 +33,7 @@ import net.runelite.api.widgets.Widget;
@EqualsAndHashCode(of = "tag")
class TagTab
{
- private final String tag;
+ private String tag;
private int iconItemId;
private Widget background;
private Widget icon;
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonCounter.java b/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonCounter.java
index 2db9d6f3e0..bd13e194be 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonCounter.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonCounter.java
@@ -26,15 +26,15 @@ package net.runelite.client.plugins.cannon;
import java.awt.Color;
import java.awt.image.BufferedImage;
-import net.runelite.client.ui.overlay.infobox.Counter;
+import net.runelite.client.ui.overlay.infobox.InfoBox;
-public class CannonCounter extends Counter
+public class CannonCounter extends InfoBox
{
private final CannonPlugin plugin;
- public CannonCounter(BufferedImage img, CannonPlugin plugin)
+ CannonCounter(BufferedImage img, CannonPlugin plugin)
{
- super(img, plugin, String.valueOf(plugin.getCballsLeft()));
+ super(img, plugin);
this.plugin = plugin;
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cerberus/CerberusPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/cerberus/CerberusPlugin.java
index 322e8c38cd..47f1adbec4 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/cerberus/CerberusPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/cerberus/CerberusPlugin.java
@@ -75,7 +75,8 @@ public class CerberusPlugin extends Plugin
@Subscribe
public void onGameStateChanged(GameStateChanged event)
{
- if (event.getGameState() == GameState.LOADING)
+ GameState gameState = event.getGameState();
+ if (gameState == GameState.LOGIN_SCREEN || gameState == GameState.HOPPING || gameState == GameState.CONNECTION_LOST)
{
ghosts.clear();
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsPlugin.java
index 42a3a9baba..7295577e34 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsPlugin.java
@@ -299,7 +299,7 @@ public class ChatCommandsPlugin extends Plugin
Widget boss = bossChildren[i];
Widget kill = killsChildren[i];
- String bossName = boss.getText();
+ String bossName = boss.getText().replace(":", "");
int kc = Integer.parseInt(kill.getText().replace(",", ""));
if (kc != getKc(bossName))
{
@@ -1089,6 +1089,7 @@ public class ChatCommandsPlugin extends Plugin
case "barrows":
return "Barrows Chests";
+ // cox
case "cox":
case "xeric":
case "chambers":
@@ -1096,6 +1097,15 @@ public class ChatCommandsPlugin extends Plugin
case "raids":
return "Chambers of Xeric";
+ // cox cm
+ case "cox cm":
+ case "xeric cm":
+ case "chambers cm":
+ case "olm cm":
+ case "raids cm":
+ return "Chambers of Xeric Challenge Mode";
+
+ // tob
case "tob":
case "theatre":
case "verzik":
diff --git a/http-service/src/main/java/net/runelite/http/service/ws/SessionManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/chathistory/ChatHistoryConfig.java
similarity index 61%
rename from http-service/src/main/java/net/runelite/http/service/ws/SessionManager.java
rename to runelite-client/src/main/java/net/runelite/client/plugins/chathistory/ChatHistoryConfig.java
index 815ab911e8..e30629c742 100644
--- a/http-service/src/main/java/net/runelite/http/service/ws/SessionManager.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/chathistory/ChatHistoryConfig.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, Adam
+ * Copyright (c) 2018, TheStonedTurtle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -22,46 +22,34 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package net.runelite.http.service.ws;
+package net.runelite.client.plugins.chathistory;
-import java.util.UUID;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
+import net.runelite.client.config.Config;
+import net.runelite.client.config.ConfigGroup;
+import net.runelite.client.config.ConfigItem;
-public class SessionManager
+@ConfigGroup("chathistory")
+public interface ChatHistoryConfig extends Config
{
- private static final ConcurrentMap sessions = new ConcurrentHashMap<>();
-
- public static void changeSessionUID(WSService service, UUID uuid)
+ @ConfigItem(
+ keyName = "retainChatHistory",
+ name = "Retain Chat History",
+ description = "Retains chat history when logging in/out or world hopping",
+ position = 0
+ )
+ default boolean retainChatHistory()
{
- synchronized (service)
- {
- remove(service);
- service.setUuid(uuid);
- sessions.put(uuid, service);
- }
+ return true;
}
- static void remove(WSService service)
+ @ConfigItem(
+ keyName = "pmTargetCycling",
+ name = "PM Target Cycling",
+ description = "Pressing Tab while sending a PM will cycle the target username based on PM history",
+ position = 1
+ )
+ default boolean pmTargetCycling()
{
- synchronized (service)
- {
- UUID current = service.getUuid();
- if (current != null)
- {
- sessions.remove(current);
- service.setUuid(null);
- }
- }
- }
-
- public static WSService findSession(UUID uuid)
- {
- return sessions.get(uuid);
- }
-
- public static int getCount()
- {
- return sessions.size();
+ return true;
}
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/chathistory/ChatHistoryPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/chathistory/ChatHistoryPlugin.java
index 6ac89bf605..43919214ea 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/chathistory/ChatHistoryPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/chathistory/ChatHistoryPlugin.java
@@ -25,47 +25,74 @@
package net.runelite.client.plugins.chathistory;
import com.google.common.collect.EvictingQueue;
-import com.google.common.collect.Sets;
+import com.google.inject.Provides;
+import java.awt.event.KeyEvent;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.Iterator;
import java.util.Queue;
-import java.util.Set;
import javax.inject.Inject;
import net.runelite.api.ChatMessageType;
+import net.runelite.api.Client;
+import net.runelite.api.ScriptID;
+import net.runelite.api.VarClientInt;
+import net.runelite.api.VarClientStr;
import net.runelite.api.events.ChatMessage;
import net.runelite.api.events.MenuOptionClicked;
+import net.runelite.api.vars.InputType;
+import net.runelite.client.callback.ClientThread;
import net.runelite.client.chat.ChatMessageManager;
import net.runelite.client.chat.QueuedMessage;
+import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.Subscribe;
+import net.runelite.client.input.KeyListener;
+import net.runelite.client.input.KeyManager;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
+import net.runelite.client.util.Text;
@PluginDescriptor(
name = "Chat History",
- description = "Retain your chat history when logging in/out or world hopping"
+ description = "Retain your chat history when logging in/out or world hopping",
+ tags = {"chat", "history", "retain", "cycle", "pm"}
)
-public class ChatHistoryPlugin extends Plugin
+public class ChatHistoryPlugin extends Plugin implements KeyListener
{
private static final String WELCOME_MESSAGE = "Welcome to Old School RuneScape.";
private static final String CLEAR_HISTORY = "Clear history";
private static final String CLEAR_PRIVATE = "Private:";
- private static final Set ALLOWED_HISTORY = Sets.newHashSet(
- ChatMessageType.PUBLIC,
- ChatMessageType.PUBLIC_MOD,
- ChatMessageType.CLANCHAT,
- ChatMessageType.PRIVATE_MESSAGE_RECEIVED,
- ChatMessageType.PRIVATE_MESSAGE_SENT,
- ChatMessageType.PRIVATE_MESSAGE_RECEIVED_MOD,
- ChatMessageType.GAME
- );
+ private static final int CYCLE_HOTKEY = KeyEvent.VK_TAB;
private Queue messageQueue;
+ private Deque friends;
+
+ @Inject
+ private Client client;
+
+ @Inject
+ private ClientThread clientThread;
+
+ @Inject
+ private ChatHistoryConfig config;
+
+ @Inject
+ private KeyManager keyManager;
@Inject
private ChatMessageManager chatMessageManager;
+
+ @Provides
+ ChatHistoryConfig getConfig(ConfigManager configManager)
+ {
+ return configManager.getConfig(ChatHistoryConfig.class);
+ }
@Override
protected void startUp()
{
messageQueue = EvictingQueue.create(100);
+ friends = new ArrayDeque<>(5);
+ keyManager.registerKeyListener(this);
}
@Override
@@ -73,6 +100,9 @@ public class ChatHistoryPlugin extends Plugin
{
messageQueue.clear();
messageQueue = null;
+ friends.clear();
+ friends = null;
+ keyManager.unregisterKeyListener(this);
}
@Subscribe
@@ -82,6 +112,11 @@ public class ChatHistoryPlugin extends Plugin
// of information that chat history was reset
if (chatMessage.getMessage().equals(WELCOME_MESSAGE))
{
+ if (!config.retainChatHistory())
+ {
+ return;
+ }
+
QueuedMessage queuedMessage;
while ((queuedMessage = messageQueue.poll()) != null)
@@ -92,21 +127,33 @@ public class ChatHistoryPlugin extends Plugin
return;
}
- if (ALLOWED_HISTORY.contains(chatMessage.getType()))
+ switch (chatMessage.getType())
{
- final QueuedMessage queuedMessage = QueuedMessage.builder()
- .type(chatMessage.getType())
- .name(chatMessage.getName())
- .sender(chatMessage.getSender())
- .value(nbsp(chatMessage.getMessage()))
- .runeLiteFormattedMessage(nbsp(chatMessage.getMessageNode().getRuneLiteFormatMessage()))
- .timestamp(chatMessage.getTimestamp())
- .build();
+ case PRIVATE_MESSAGE_SENT:
+ case PRIVATE_MESSAGE_RECEIVED:
+ case PRIVATE_MESSAGE_RECEIVED_MOD:
+ final String name = Text.removeTags(chatMessage.getName());
+ // Remove to ensure uniqueness & its place in history
+ friends.remove(name);
+ friends.add(name);
+ // intentional fall-through
+ case PUBLIC:
+ case PUBLIC_MOD:
+ case CLANCHAT:
+ case GAME:
+ final QueuedMessage queuedMessage = QueuedMessage.builder()
+ .type(chatMessage.getType())
+ .name(chatMessage.getName())
+ .sender(chatMessage.getSender())
+ .value(nbsp(chatMessage.getMessage()))
+ .runeLiteFormattedMessage(nbsp(chatMessage.getMessageNode().getRuneLiteFormatMessage()))
+ .timestamp(chatMessage.getTimestamp())
+ .build();
- if (!messageQueue.contains(queuedMessage))
- {
- messageQueue.offer(queuedMessage);
- }
+ if (!messageQueue.contains(queuedMessage))
+ {
+ messageQueue.offer(queuedMessage);
+ }
}
}
@@ -143,4 +190,64 @@ public class ChatHistoryPlugin extends Plugin
return null;
}
+
+ @Override
+ public void keyPressed(KeyEvent e)
+ {
+ if (e.getKeyCode() != CYCLE_HOTKEY || !config.pmTargetCycling())
+ {
+ return;
+ }
+
+ if (client.getVar(VarClientInt.INPUT_TYPE) != InputType.PRIVATE_MESSAGE.getType())
+ {
+ return;
+ }
+
+ clientThread.invoke(() ->
+ {
+ final String target = findPreviousFriend();
+ if (target == null)
+ {
+ return;
+ }
+
+ final String currentMessage = client.getVar(VarClientStr.INPUT_TEXT);
+
+ client.runScript(ScriptID.OPEN_PRIVATE_MESSAGE_INTERFACE, target);
+
+ client.setVar(VarClientStr.INPUT_TEXT, currentMessage);
+ client.runScript(ScriptID.CHAT_TEXT_INPUT_REBUILD, "");
+ });
+ }
+
+ @Override
+ public void keyTyped(KeyEvent e)
+ {
+ }
+
+ @Override
+ public void keyReleased(KeyEvent e)
+ {
+ }
+
+ private String findPreviousFriend()
+ {
+ final String currentTarget = client.getVar(VarClientStr.PRIVATE_MESSAGE_TARGET);
+ if (currentTarget == null || friends.isEmpty())
+ {
+ return null;
+ }
+
+ for (Iterator it = friends.descendingIterator(); it.hasNext(); )
+ {
+ String friend = it.next();
+ if (friend.equals(currentTarget))
+ {
+ return it.hasNext() ? it.next() : friends.getLast();
+ }
+ }
+
+ return friends.getLast();
+ }
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/EmoteClue.java b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/EmoteClue.java
index 30a33d7e50..d3123ce2da 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/EmoteClue.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/EmoteClue.java
@@ -133,8 +133,8 @@ public class EmoteClue extends ClueScroll implements TextClueScroll, LocationClu
new EmoteClue("Panic by the pilot on White Wolf Mountain. Beware of double agents! Equip mithril platelegs, a ring of life and a rune axe.", GNOME_GLIDER_ON_WHITE_WOLF_MOUNTAIN, new WorldPoint(2847, 3499, 0), PANIC, item(MITHRIL_PLATELEGS), item(RING_OF_LIFE), item(RUNE_AXE)),
new EmoteClue("Panic by the big egg where no one dare goes and the ground is burnt. Beware of double agents! Equip a dragon med helm, a TokTz-Ket-Xil, a brine sabre, rune platebody and an uncharged amulet of glory.", SOUTHEAST_CORNER_OF_LAVA_DRAGON_ISLE, new WorldPoint(3227, 3831, 0), PANIC, item(DRAGON_MED_HELM), item(TOKTZKETXIL), item(BRINE_SABRE), item(RUNE_PLATEBODY), item(AMULET_OF_GLORY)),
new EmoteClue("Panic at the area flowers meet snow. Equip Blue D'hide vambs, a dragon spear and a rune plateskirt.", HALFWAY_DOWN_TROLLWEISS_MOUNTAIN, new WorldPoint(2776, 3781, 0), PANIC, item(BLUE_DHIDE_VAMB), item(DRAGON_SPEAR), item(RUNE_PLATESKIRT), item(SLED_4084)),
- new EmoteClue("Do a push up at the bank of the Warrior's guild. Beware of double agents! Equip a dragon battleaxe, a dragon defender and a slayer helm of any kind.", WARRIORS_GUILD_BANK_29047, new WorldPoint(2843, 3543, 0), PUSH_UP, item(DRAGON_BATTLEAXE), item(DRAGON_DEFENDER), any("Any slayer helmet", item(SLAYER_HELMET), item(BLACK_SLAYER_HELMET), item(GREEN_SLAYER_HELMET), item(PURPLE_SLAYER_HELMET), item(RED_SLAYER_HELMET), item(TURQUOISE_SLAYER_HELMET), item(SLAYER_HELMET_I), item(BLACK_SLAYER_HELMET_I), item(GREEN_SLAYER_HELMET_I), item(PURPLE_SLAYER_HELMET_I), item(RED_SLAYER_HELMET_I), item(TURQUOISE_SLAYER_HELMET_I))),
- new EmoteClue("Blow a raspberry at the bank of the Warrior's guild. Beware of double agents! Equip a dragon battleaxe, a dragon defender and a slayer helm of any kind.", WARRIORS_GUILD_BANK_29047, new WorldPoint(2843, 3543, 0), RASPBERRY, item(DRAGON_BATTLEAXE), item(DRAGON_DEFENDER), any("Any slayer helmet", item(SLAYER_HELMET), item(BLACK_SLAYER_HELMET), item(GREEN_SLAYER_HELMET), item(PURPLE_SLAYER_HELMET), item(RED_SLAYER_HELMET), item(TURQUOISE_SLAYER_HELMET), item(SLAYER_HELMET_I), item(BLACK_SLAYER_HELMET_I), item(GREEN_SLAYER_HELMET_I), item(PURPLE_SLAYER_HELMET_I), item(RED_SLAYER_HELMET_I), item(TURQUOISE_SLAYER_HELMET_I))),
+ new EmoteClue("Do a push up at the bank of the Warrior's guild. Beware of double agents! Equip a dragon battleaxe, a dragon defender and a slayer helm of any kind.", WARRIORS_GUILD_BANK_29047, new WorldPoint(2843, 3543, 0), PUSH_UP, item(DRAGON_BATTLEAXE), item(DRAGON_DEFENDER), any("Any slayer helmet", item(SLAYER_HELMET), item(BLACK_SLAYER_HELMET), item(GREEN_SLAYER_HELMET), item(PURPLE_SLAYER_HELMET), item(RED_SLAYER_HELMET), item(TURQUOISE_SLAYER_HELMET), item(SLAYER_HELMET_I), item(BLACK_SLAYER_HELMET_I), item(GREEN_SLAYER_HELMET_I), item(PURPLE_SLAYER_HELMET_I), item(RED_SLAYER_HELMET_I), item(TURQUOISE_SLAYER_HELMET_I), item(HYDRA_SLAYER_HELMET), item(HYDRA_SLAYER_HELMET_I))),
+ new EmoteClue("Blow a raspberry at the bank of the Warrior's guild. Beware of double agents! Equip a dragon battleaxe, a dragon defender and a slayer helm of any kind.", WARRIORS_GUILD_BANK_29047, new WorldPoint(2843, 3543, 0), RASPBERRY, item(DRAGON_BATTLEAXE), item(DRAGON_DEFENDER), any("Any slayer helmet", item(SLAYER_HELMET), item(BLACK_SLAYER_HELMET), item(GREEN_SLAYER_HELMET), item(PURPLE_SLAYER_HELMET), item(RED_SLAYER_HELMET), item(TURQUOISE_SLAYER_HELMET), item(SLAYER_HELMET_I), item(BLACK_SLAYER_HELMET_I), item(GREEN_SLAYER_HELMET_I), item(PURPLE_SLAYER_HELMET_I), item(RED_SLAYER_HELMET_I), item(TURQUOISE_SLAYER_HELMET_I), item(HYDRA_SLAYER_HELMET), item(HYDRA_SLAYER_HELMET_I))),
new EmoteClue("Blow a raspberry at the monkey cage in Ardougne Zoo. Equip a studded leather body, bronze platelegs and a normal staff with no orb.", NEAR_THE_PARROTS_IN_ARDOUGNE_ZOO, new WorldPoint(2607, 3282, 0), RASPBERRY, item(STUDDED_BODY), item(BRONZE_PLATELEGS), item(STAFF)),
new EmoteClue("Blow raspberries outside the entrance to Keep Le Faye. Equip a coif, an iron platebody and leather gloves.", OUTSIDE_KEEP_LE_FAYE, new WorldPoint(2757, 3401, 0), RASPBERRY, item(COIF), item(IRON_PLATEBODY), item(LEATHER_GLOVES)),
new EmoteClue("Blow a raspberry in the Fishing Guild bank. Beware of double agents! Equip an elemental shield, blue dragonhide chaps and a rune warhammer.", FISHING_GUILD_BANK, new WorldPoint(2588, 3419, 0), RASPBERRY, item(ELEMENTAL_SHIELD), item(BLUE_DHIDE_CHAPS), item(RUNE_WARHAMMER)),
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cooking/CookingOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/cooking/CookingOverlay.java
index af0a5a8b62..7e25f3ad31 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/cooking/CookingOverlay.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/cooking/CookingOverlay.java
@@ -78,7 +78,6 @@ class CookingOverlay extends Overlay
return null;
}
- panelComponent.setPreferredSize(new Dimension(145, 0));
panelComponent.getChildren().clear();
if (isCooking() || Duration.between(session.getLastCookingAction(), Instant.now()).getSeconds() < COOK_TIMEOUT)
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsOverlay.java
index 0b9be07b7e..fd96de011c 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsOverlay.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsOverlay.java
@@ -347,6 +347,12 @@ class DevToolsOverlay extends Overlay
{
graphics.drawPolygon(p);
}
+
+ p = decorObject.getConvexHull2();
+ if (p != null)
+ {
+ graphics.drawPolygon(p);
+ }
}
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/WidgetInfoTableModel.java b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/WidgetInfoTableModel.java
index 79bb99bc5a..acd8851c0b 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/WidgetInfoTableModel.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/WidgetInfoTableModel.java
@@ -183,6 +183,9 @@ public class WidgetInfoTableModel extends AbstractTableModel
out.add(new WidgetField<>("ScrollHeight", Widget::getScrollHeight, Widget::setScrollHeight, Integer.class));
out.add(new WidgetField<>("DragDeadZone", Widget::getDragDeadZone, Widget::setDragDeadZone, Integer.class));
out.add(new WidgetField<>("DragDeadTime", Widget::getDragDeadTime, Widget::setDragDeadTime, Integer.class));
+ out.add(new WidgetField<>("NoClickThrough", Widget::getNoClickThrough, Widget::setNoClickThrough, Boolean.class));
+ out.add(new WidgetField<>("NoScrollThrough", Widget::getNoScrollThrough, Widget::setNoScrollThrough, Boolean.class));
+ out.add(new WidgetField<>("TargetVerb", Widget::getTargetVerb, Widget::setTargetVerb, String.class));
return out;
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordPlugin.java
index 06e0369535..9598b7eff5 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordPlugin.java
@@ -130,6 +130,7 @@ public class DiscordPlugin extends Plugin
clientToolbar.addNavigation(discordButton);
checkForGameStateUpdate();
+ checkForAreaUpdate();
if (discordService.getCurrentUser() != null)
{
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordState.java b/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordState.java
index 625aed1f08..0b57424f85 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordState.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordState.java
@@ -34,6 +34,7 @@ import java.util.Optional;
import java.util.UUID;
import javax.inject.Inject;
import lombok.Data;
+import net.runelite.client.RuneLiteProperties;
import net.runelite.client.discord.DiscordPresence;
import net.runelite.client.discord.DiscordService;
import net.runelite.client.ws.PartyService;
@@ -57,14 +58,16 @@ class DiscordState
private final DiscordService discordService;
private final DiscordConfig config;
private PartyService party;
+ private final RuneLiteProperties properties;
private DiscordPresence lastPresence;
@Inject
- private DiscordState(final DiscordService discordService, final DiscordConfig config, final PartyService party)
+ private DiscordState(final DiscordService discordService, final DiscordConfig config, final PartyService party, final RuneLiteProperties properties)
{
this.discordService = discordService;
this.config = config;
this.party = party;
+ this.properties = properties;
}
/**
@@ -90,6 +93,7 @@ class DiscordState
final DiscordPresence.DiscordPresenceBuilder presenceBuilder = DiscordPresence.builder()
.state(lastPresence.getState())
.details(lastPresence.getDetails())
+ .largeImageText(lastPresence.getLargeImageText())
.startTimestamp(lastPresence.getStartTimestamp())
.smallImageKey(lastPresence.getSmallImageKey())
.partyMax(lastPresence.getPartyMax())
@@ -168,11 +172,15 @@ class DiscordState
}
}
+ // Replace snapshot with + to make tooltip shorter (so it will span only 1 line)
+ final String versionShortHand = properties.getVersion().replace("-SNAPSHOT", "+");
+
final DiscordPresence.DiscordPresenceBuilder presenceBuilder = DiscordPresence.builder()
.state(MoreObjects.firstNonNull(state, ""))
.details(MoreObjects.firstNonNull(details, ""))
+ .largeImageText(properties.getTitle() + " v" + versionShortHand)
.startTimestamp(event.getStart())
- .smallImageKey(MoreObjects.firstNonNull(imageKey, "default"))
+ .smallImageKey(imageKey)
.partyMax(PARTY_MAX)
.partySize(party.getMembers().size());
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordUserInfo.java b/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordUserInfo.java
index b358a7d01e..360d58652e 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordUserInfo.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordUserInfo.java
@@ -24,10 +24,12 @@
*/
package net.runelite.client.plugins.discord;
+import lombok.EqualsAndHashCode;
import lombok.Value;
import net.runelite.http.api.ws.messages.party.PartyMemberMessage;
@Value
+@EqualsAndHashCode(callSuper = true)
class DiscordUserInfo extends PartyMemberMessage
{
private final String userId;
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingPlugin.java
index 07977334e4..8da48d7cdc 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingPlugin.java
@@ -147,7 +147,8 @@ public class FishingPlugin extends Plugin
@Subscribe
public void onGameStateChanged(GameStateChanged gameStateChanged)
{
- if (gameStateChanged.getGameState() == GameState.LOADING)
+ GameState gameState = gameStateChanged.getGameState();
+ if (gameState == GameState.CONNECTION_LOST || gameState == GameState.LOGIN_SCREEN || gameState == GameState.HOPPING)
{
fishingSpots.clear();
minnowSpots.clear();
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java
index a7871f95da..93d8a522d9 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java
@@ -1151,8 +1151,6 @@ public class GpuPlugin extends Plugin implements DrawCallbacks
gl.glTexSubImage2D(gl.GL_TEXTURE_2D, 0, 0, 0, width, height, gl.GL_BGRA, gl.GL_UNSIGNED_INT_8_8_8_8_REV, interfaceBuffer);
}
- gl.glBindTexture(gl.GL_TEXTURE_2D, interfaceTexture);
-
if (client.isStretchedEnabled())
{
Dimension dim = client.getStretchedDimensions();
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangePlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangePlugin.java
index cd4f3f050e..5a59ede074 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangePlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangePlugin.java
@@ -1,5 +1,5 @@
/*
- *
+ * Copyright (c) 2019, Adam
* Copyright (c) 2017, Robbie
* Copyright (c) 2018, SomeoneWithAnInternetConnection
* All rights reserved.
@@ -46,6 +46,7 @@ import net.runelite.api.ChatMessageType;
import net.runelite.api.Client;
import net.runelite.api.GameState;
import net.runelite.api.GrandExchangeOffer;
+import net.runelite.api.GrandExchangeOfferState;
import net.runelite.api.ItemComposition;
import net.runelite.api.MenuAction;
import net.runelite.api.MenuEntry;
@@ -56,11 +57,15 @@ import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.GameTick;
import net.runelite.api.events.GrandExchangeOfferChanged;
import net.runelite.api.events.MenuEntryAdded;
+import net.runelite.api.events.SessionClose;
+import net.runelite.api.events.SessionOpen;
import net.runelite.api.events.WidgetLoaded;
import net.runelite.api.widgets.Widget;
import net.runelite.api.widgets.WidgetID;
import net.runelite.api.widgets.WidgetInfo;
import net.runelite.client.Notifier;
+import net.runelite.client.account.AccountSession;
+import net.runelite.client.account.SessionManager;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.game.ItemManager;
@@ -73,8 +78,10 @@ import net.runelite.client.ui.NavigationButton;
import net.runelite.client.util.ImageUtil;
import net.runelite.client.util.StackFormatter;
import net.runelite.client.util.Text;
-import net.runelite.http.api.osbuddy.GrandExchangeClient;
-import net.runelite.http.api.osbuddy.GrandExchangeResult;
+import net.runelite.http.api.ge.GrandExchangeClient;
+import net.runelite.http.api.ge.GrandExchangeTrade;
+import net.runelite.http.api.osbuddy.OSBGrandExchangeClient;
+import net.runelite.http.api.osbuddy.OSBGrandExchangeResult;
@PluginDescriptor(
name = "Grand Exchange",
@@ -86,7 +93,7 @@ public class GrandExchangePlugin extends Plugin
{
private static final int OFFER_CONTAINER_ITEM = 21;
private static final int OFFER_DEFAULT_ITEM_ID = 6512;
- private static final GrandExchangeClient CLIENT = new GrandExchangeClient();
+ private static final OSBGrandExchangeClient CLIENT = new OSBGrandExchangeClient();
private static final String OSB_GE_TEXT = "
OSBuddy Actively traded price: ";
private static final String BUY_LIMIT_GE_TEXT = "
Buy limit: ";
@@ -134,10 +141,38 @@ public class GrandExchangePlugin extends Plugin
@Inject
private ScheduledExecutorService executorService;
+ @Inject
+ private SessionManager sessionManager;
+
+ @Inject
+ private ConfigManager configManager;
+
private Widget grandExchangeText;
private Widget grandExchangeItem;
private Map itemGELimits;
+ private GrandExchangeClient grandExchangeClient;
+
+ private SavedOffer getOffer(int slot)
+ {
+ String offer = configManager.getConfiguration("geoffer." + client.getUsername().toLowerCase(), Integer.toString(slot));
+ if (offer == null)
+ {
+ return null;
+ }
+ return GSON.fromJson(offer, SavedOffer.class);
+ }
+
+ private void setOffer(int slot, SavedOffer offer)
+ {
+ configManager.setConfiguration("geoffer." + client.getUsername().toLowerCase(), Integer.toString(slot), GSON.toJson(offer));
+ }
+
+ private void deleteOffer(int slot)
+ {
+ configManager.unsetConfiguration("geoffer." + client.getUsername().toLowerCase(), Integer.toString(slot));
+ }
+
@Provides
GrandExchangeConfig provideConfig(ConfigManager configManager)
{
@@ -167,6 +202,12 @@ public class GrandExchangePlugin extends Plugin
mouseManager.registerMouseListener(inputListener);
keyManager.registerKeyListener(inputListener);
}
+
+ AccountSession accountSession = sessionManager.getAccountSession();
+ if (accountSession != null)
+ {
+ grandExchangeClient = new GrandExchangeClient(accountSession.getUuid());
+ }
}
@Override
@@ -178,6 +219,27 @@ public class GrandExchangePlugin extends Plugin
grandExchangeText = null;
grandExchangeItem = null;
itemGELimits = null;
+ grandExchangeClient = null;
+ }
+
+ @Subscribe
+ public void onSessionOpen(SessionOpen sessionOpen)
+ {
+ AccountSession accountSession = sessionManager.getAccountSession();
+ if (accountSession.getUuid() != null)
+ {
+ grandExchangeClient = new GrandExchangeClient(accountSession.getUuid());
+ }
+ else
+ {
+ grandExchangeClient = null;
+ }
+ }
+
+ @Subscribe
+ public void onSessionClose(SessionClose sessionClose)
+ {
+ grandExchangeClient = null;
}
@Subscribe
@@ -204,11 +266,79 @@ public class GrandExchangePlugin extends Plugin
@Subscribe
public void onGrandExchangeOfferChanged(GrandExchangeOfferChanged offerEvent)
{
- GrandExchangeOffer offer = offerEvent.getOffer();
+ final int slot = offerEvent.getSlot();
+ final GrandExchangeOffer offer = offerEvent.getOffer();
+
ItemComposition offerItem = itemManager.getItemComposition(offer.getItemId());
boolean shouldStack = offerItem.isStackable() || offer.getTotalQuantity() > 1;
BufferedImage itemImage = itemManager.getImage(offer.getItemId(), offer.getTotalQuantity(), shouldStack);
- SwingUtilities.invokeLater(() -> panel.getOffersPanel().updateOffer(offerItem, itemImage, offerEvent.getOffer(), offerEvent.getSlot()));
+ SwingUtilities.invokeLater(() -> panel.getOffersPanel().updateOffer(offerItem, itemImage, offer, slot));
+
+ submitTrades(slot, offer);
+
+ updateConfig(slot, offer);
+ }
+
+ private void submitTrades(int slot, GrandExchangeOffer offer)
+ {
+ if (grandExchangeClient == null)
+ {
+ return;
+ }
+
+ // Only interested in offers which are fully bought/sold
+ if (offer.getState() != GrandExchangeOfferState.BOUGHT && offer.getState() != GrandExchangeOfferState.SOLD)
+ {
+ return;
+ }
+
+ SavedOffer savedOffer = getOffer(slot);
+ if (!shouldUpdate(savedOffer, offer))
+ {
+ return;
+ }
+
+ // getPrice() is the price of the offer, not necessarily what the item bought at
+ int priceEach = offer.getSpent() / offer.getTotalQuantity();
+
+ GrandExchangeTrade grandExchangeTrade = new GrandExchangeTrade();
+ grandExchangeTrade.setBuy(offer.getState() == GrandExchangeOfferState.BOUGHT);
+ grandExchangeTrade.setItemId(offer.getItemId());
+ grandExchangeTrade.setQuantity(offer.getTotalQuantity());
+ grandExchangeTrade.setPrice(priceEach);
+
+ log.debug("Submitting trade: {}", grandExchangeTrade);
+ grandExchangeClient.submit(grandExchangeTrade);
+ }
+
+ private void updateConfig(int slot, GrandExchangeOffer offer)
+ {
+ if (offer.getState() == GrandExchangeOfferState.EMPTY)
+ {
+ deleteOffer(slot);
+ }
+ else
+ {
+ SavedOffer savedOffer = new SavedOffer();
+ savedOffer.setItemId(offer.getItemId());
+ savedOffer.setQuantitySold(offer.getQuantitySold());
+ savedOffer.setTotalQuantity(offer.getTotalQuantity());
+ savedOffer.setPrice(offer.getPrice());
+ savedOffer.setSpent(offer.getSpent());
+ savedOffer.setState(offer.getState());
+ setOffer(slot, savedOffer);
+ }
+ }
+
+ private boolean shouldUpdate(SavedOffer savedOffer, GrandExchangeOffer grandExchangeOffer)
+ {
+ if (savedOffer == null)
+ {
+ return false;
+ }
+
+ // Only update offer if state has changed
+ return savedOffer.getState() != grandExchangeOffer.getState();
}
@Subscribe
@@ -346,7 +476,7 @@ public class GrandExchangePlugin extends Plugin
try
{
- final GrandExchangeResult result = CLIENT.lookupItem(itemId);
+ final OSBGrandExchangeResult result = CLIENT.lookupItem(itemId);
final String text = geText.getText() + OSB_GE_TEXT + StackFormatter.formatNumber(result.getOverall_average());
geText.setText(text);
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/SavedOffer.java b/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/SavedOffer.java
new file mode 100644
index 0000000000..58a4055fed
--- /dev/null
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/SavedOffer.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2019, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package net.runelite.client.plugins.grandexchange;
+
+import lombok.Data;
+import net.runelite.api.GrandExchangeOfferState;
+
+@Data
+class SavedOffer
+{
+ private int itemId;
+ private int quantitySold;
+ private int totalQuantity;
+ private int price;
+ private int spent;
+ private GrandExchangeOfferState state;
+}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerPlugin.java
index 12ceda382a..2cb7ebf1c2 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerPlugin.java
@@ -34,13 +34,13 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.stream.Collectors;
import javax.inject.Inject;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
-import static net.runelite.api.Constants.CHUNK_SIZE;
import net.runelite.api.GameState;
import net.runelite.api.MenuAction;
import net.runelite.api.MenuEntry;
@@ -155,87 +155,23 @@ public class GroundMarkerPlugin extends Plugin
return Collections.EMPTY_LIST;
}
- List worldPoints = new ArrayList<>();
- for (GroundMarkerPoint point : points)
- {
- int regionId = point.getRegionId();
- int regionX = point.getRegionX();
- int regionY = point.getRegionY();
- int z = point.getZ();
-
- // world point of the tile marker
- WorldPoint worldPoint = new WorldPoint(
- ((regionId >>> 8) << 6) + regionX,
- ((regionId & 0xff) << 6) + regionY,
- z
- );
-
- if (!client.isInInstancedRegion())
+ return points.stream()
+ .map(point ->
{
- worldPoints.add(worldPoint);
- continue;
- }
+ int regionId = point.getRegionId();
+ int regionX = point.getRegionX();
+ int regionY = point.getRegionY();
+ int z = point.getZ();
- // find instance chunks using the template point. there might be more than one.
- int[][][] instanceTemplateChunks = client.getInstanceTemplateChunks();
- for (int x = 0; x < instanceTemplateChunks[z].length; ++x)
- {
- for (int y = 0; y < instanceTemplateChunks[z][x].length; ++y)
- {
- int chunkData = instanceTemplateChunks[z][x][y];
- int rotation = chunkData >> 1 & 0x3;
- int templateChunkY = (chunkData >> 3 & 0x7FF) * CHUNK_SIZE;
- int templateChunkX = (chunkData >> 14 & 0x3FF) * CHUNK_SIZE;
- if (worldPoint.getX() >= templateChunkX && worldPoint.getX() < templateChunkX + CHUNK_SIZE
- && worldPoint.getY() >= templateChunkY && worldPoint.getY() < templateChunkY + CHUNK_SIZE)
- {
- WorldPoint p = new WorldPoint(client.getBaseX() + x * CHUNK_SIZE + (worldPoint.getX() & (CHUNK_SIZE - 1)),
- client.getBaseY() + y * CHUNK_SIZE + (worldPoint.getY() & (CHUNK_SIZE - 1)),
- worldPoint.getPlane());
- p = rotate(p, rotation);
- worldPoints.add(p);
- }
- }
- }
- }
- return worldPoints;
- }
-
- /**
- * Rotate the chunk containing the given point to rotation 0
- *
- * @param point point
- * @param rotation rotation
- * @return world point
- */
- private static WorldPoint rotateInverse(WorldPoint point, int rotation)
- {
- return rotate(point, 4 - rotation);
- }
-
- /**
- * Rotate the coordinates in the chunk according to chunk rotation
- *
- * @param point point
- * @param rotation rotation
- * @return world point
- */
- private static WorldPoint rotate(WorldPoint point, int rotation)
- {
- int chunkX = point.getX() & ~(CHUNK_SIZE - 1);
- int chunkY = point.getY() & ~(CHUNK_SIZE - 1);
- int x = point.getX() & (CHUNK_SIZE - 1);
- int y = point.getY() & (CHUNK_SIZE - 1);
- switch (rotation)
- {
- case 1:
- return new WorldPoint(chunkX + y, chunkY + (CHUNK_SIZE - 1 - x), point.getPlane());
- case 2:
- return new WorldPoint(chunkX + (CHUNK_SIZE - 1 - x), chunkY + (CHUNK_SIZE - 1 - y), point.getPlane());
- case 3:
- return new WorldPoint(chunkX + (CHUNK_SIZE - 1 - y), chunkY + x, point.getPlane());
- }
- return point;
+ // world point of the tile marker
+ return new WorldPoint(
+ ((regionId >>> 8) << 6) + regionX,
+ ((regionId & 0xff) << 6) + regionY,
+ z
+ );
+ })
+ .flatMap(wp -> WorldPoint.toLocalInstance(client, wp).stream())
+ .collect(Collectors.toList());
}
@Subscribe
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPlugin.java
index 9a47e57de0..8a23af905f 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPlugin.java
@@ -202,6 +202,7 @@ public class IdleNotifierPlugin extends Plugin
case MAGIC_CHARGING_ORBS:
case MAGIC_LUNAR_STRING_JEWELRY:
case MAGIC_MAKE_TABLET:
+ case MAGIC_ENCHANTING_JEWELRY:
/* Prayer */
case USING_GILDED_ALTAR:
/* Farming */
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPanel.java
index 2edb250135..203b688b4e 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPanel.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPanel.java
@@ -40,6 +40,7 @@ import javax.inject.Singleton;
import javax.swing.Box;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
+import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.event.HyperlinkEvent;
@@ -48,6 +49,7 @@ import net.runelite.api.events.SessionClose;
import net.runelite.api.events.SessionOpen;
import net.runelite.client.RuneLiteProperties;
import net.runelite.client.account.SessionManager;
+import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.EventBus;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.ui.ColorScheme;
@@ -66,9 +68,12 @@ public class InfoPanel extends PluginPanel
private static final ImageIcon DISCORD_ICON;
private static final ImageIcon PATREON_ICON;
private static final ImageIcon WIKI_ICON;
+ private static final ImageIcon IMPORT_ICON;
private final JLabel loggedLabel = new JLabel();
private final JRichTextPane emailLabel = new JRichTextPane();
+ private JPanel syncPanel;
+ private JPanel actionsContainer;
@Inject
@Nullable
@@ -86,6 +91,9 @@ public class InfoPanel extends PluginPanel
@Inject
private ScheduledExecutorService executor;
+ @Inject
+ private ConfigManager configManager;
+
static
{
ARROW_RIGHT_ICON = new ImageIcon(ImageUtil.getResourceStreamFromClass(InfoPanel.class, "/util/arrow_right.png"));
@@ -93,6 +101,7 @@ public class InfoPanel extends PluginPanel
DISCORD_ICON = new ImageIcon(ImageUtil.getResourceStreamFromClass(InfoPanel.class, "discord_icon.png"));
PATREON_ICON = new ImageIcon(ImageUtil.getResourceStreamFromClass(InfoPanel.class, "patreon_icon.png"));
WIKI_ICON = new ImageIcon(ImageUtil.getResourceStreamFromClass(InfoPanel.class, "wiki_icon.png"));
+ IMPORT_ICON = new ImageIcon(ImageUtil.getResourceStreamFromClass(InfoPanel.class, "import_icon.png"));
}
void init()
@@ -150,11 +159,22 @@ public class InfoPanel extends PluginPanel
versionPanel.add(loggedLabel);
versionPanel.add(emailLabel);
- updateLoggedIn();
-
- JPanel actionsContainer = new JPanel();
+ actionsContainer = new JPanel();
actionsContainer.setBorder(new EmptyBorder(10, 0, 0, 0));
- actionsContainer.setLayout(new GridLayout(4, 1, 0, 10));
+ actionsContainer.setLayout(new GridLayout(0, 1, 0, 10));
+
+ syncPanel = buildLinkPanel(IMPORT_ICON, "Import local settings", "to remote RuneLite account", () ->
+ {
+ final int result = JOptionPane.showOptionDialog(syncPanel,
+ "This will replace your current RuneLite account settings with settings from your local profile.",
+ "Are you sure?", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE,
+ null, new String[]{"Yes", "No"}, "No");
+
+ if (result == JOptionPane.YES_OPTION)
+ {
+ configManager.importLocal();
+ }
+ });
actionsContainer.add(buildLinkPanel(GITHUB_ICON, "Report an issue or", "make a suggestion", runeLiteProperties.getGithubLink()));
actionsContainer.add(buildLinkPanel(DISCORD_ICON, "Talk to us on our", "discord server", runeLiteProperties.getDiscordInvite()));
@@ -164,6 +184,7 @@ public class InfoPanel extends PluginPanel
add(versionPanel, BorderLayout.NORTH);
add(actionsContainer, BorderLayout.CENTER);
+ updateLoggedIn();
eventBus.register(this);
}
@@ -171,6 +192,14 @@ public class InfoPanel extends PluginPanel
* Builds a link panel with a given icon, text and url to redirect to.
*/
private static JPanel buildLinkPanel(ImageIcon icon, String topText, String bottomText, String url)
+ {
+ return buildLinkPanel(icon, topText, bottomText, () -> LinkBrowser.browse(url));
+ }
+
+ /**
+ * Builds a link panel with a given icon, text and callable to call.
+ */
+ private static JPanel buildLinkPanel(ImageIcon icon, String topText, String bottomText, Runnable callback)
{
JPanel container = new JPanel();
container.setBackground(ColorScheme.DARKER_GRAY_COLOR);
@@ -193,7 +222,6 @@ public class InfoPanel extends PluginPanel
@Override
public void mousePressed(MouseEvent mouseEvent)
{
- LinkBrowser.browse(url);
container.setBackground(pressedColor);
textContainer.setBackground(pressedColor);
}
@@ -201,6 +229,7 @@ public class InfoPanel extends PluginPanel
@Override
public void mouseReleased(MouseEvent e)
{
+ callback.run();
container.setBackground(hoverColor);
textContainer.setBackground(hoverColor);
}
@@ -252,12 +281,14 @@ public class InfoPanel extends PluginPanel
emailLabel.setContentType("text/plain");
emailLabel.setText(name);
loggedLabel.setText("Logged in as");
+ actionsContainer.add(syncPanel, 0);
}
else
{
emailLabel.setContentType("text/html");
emailLabel.setText("Login to sync settings to the cloud.");
loggedLabel.setText("Not logged in");
+ actionsContainer.remove(syncPanel);
}
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemcharges/ItemChargeConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemcharges/ItemChargeConfig.java
index 40488c5137..28c503b20a 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/itemcharges/ItemChargeConfig.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemcharges/ItemChargeConfig.java
@@ -203,4 +203,15 @@ public interface ItemChargeConfig extends Config
{
return false;
}
+
+ @ConfigItem(
+ keyName = "showInfoboxes",
+ name = "Show Infoboxes",
+ description = "Configures whether to show an infobox equipped charge items",
+ position = 15
+ )
+ default boolean showInfoboxes()
+ {
+ return false;
+ }
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemcharges/ItemChargeInfobox.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemcharges/ItemChargeInfobox.java
new file mode 100644
index 0000000000..7ee70d44b5
--- /dev/null
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemcharges/ItemChargeInfobox.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2018, Hydrox6
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package net.runelite.client.plugins.itemcharges;
+
+import java.awt.Color;
+import java.awt.image.BufferedImage;
+import lombok.Getter;
+import net.runelite.api.EquipmentInventorySlot;
+import net.runelite.client.ui.overlay.infobox.Counter;
+
+@Getter
+class ItemChargeInfobox extends Counter
+{
+ private final ItemChargePlugin plugin;
+ private final ItemWithSlot item;
+ private final EquipmentInventorySlot slot;
+
+ ItemChargeInfobox(
+ ItemChargePlugin plugin,
+ BufferedImage image,
+ String name,
+ int charges,
+ ItemWithSlot item,
+ EquipmentInventorySlot slot)
+ {
+ super(image, plugin, charges);
+ setTooltip(name);
+ this.plugin = plugin;
+ this.item = item;
+ this.slot = slot;
+ }
+
+ @Override
+ public Color getTextColor()
+ {
+ return getPlugin().getColor(getCount());
+ }
+}
\ No newline at end of file
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemcharges/ItemChargeOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemcharges/ItemChargeOverlay.java
index 7730d46ffd..27bb35b21d 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/itemcharges/ItemChargeOverlay.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemcharges/ItemChargeOverlay.java
@@ -24,7 +24,6 @@
*/
package net.runelite.client.plugins.itemcharges;
-import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Point;
@@ -83,7 +82,7 @@ class ItemChargeOverlay extends Overlay
continue;
}
- charges = itemChargePlugin.getDodgyCharges();
+ charges = config.dodgyNecklace();
}
else
{
@@ -112,7 +111,7 @@ class ItemChargeOverlay extends Overlay
final TextComponent textComponent = new TextComponent();
textComponent.setPosition(new Point(bounds.x, bounds.y + 16));
textComponent.setText(charges < 0 ? "?" : String.valueOf(charges));
- textComponent.setColor(getColor(charges));
+ textComponent.setColor(itemChargePlugin.getColor(charges));
textComponent.render(graphics);
}
return null;
@@ -137,20 +136,6 @@ class ItemChargeOverlay extends Overlay
return jewellery;
}
- private Color getColor(int charges)
- {
- Color color = Color.WHITE;
- if (charges <= config.veryLowWarning())
- {
- color = config.veryLowWarningColor();
- }
- else if (charges <= config.lowWarning())
- {
- color = config.lowWarningolor();
- }
- return color;
- }
-
private boolean displayOverlay()
{
return config.showTeleportCharges() || config.showDodgyCount() || config.showFungicideCharges()
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemcharges/ItemChargePlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemcharges/ItemChargePlugin.java
index c545e9a5bb..e295efd9e5 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/itemcharges/ItemChargePlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemcharges/ItemChargePlugin.java
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2017, Seth
+ * Copyright (c) 2018, Hydrox6
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -25,19 +26,29 @@
package net.runelite.client.plugins.itemcharges;
import com.google.inject.Provides;
+import java.awt.Color;
+import java.awt.image.BufferedImage;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.inject.Inject;
-import lombok.AccessLevel;
-import lombok.Getter;
import net.runelite.api.ChatMessageType;
+import net.runelite.api.Client;
+import net.runelite.api.EquipmentInventorySlot;
+import net.runelite.api.InventoryID;
+import net.runelite.api.Item;
+import net.runelite.api.ItemContainer;
+import net.runelite.api.ItemID;
import net.runelite.api.events.ChatMessage;
+import net.runelite.api.events.ConfigChanged;
+import net.runelite.api.events.ItemContainerChanged;
import net.runelite.client.Notifier;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.Subscribe;
+import net.runelite.client.game.ItemManager;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.ui.overlay.OverlayManager;
+import net.runelite.client.ui.overlay.infobox.InfoBoxManager;
@PluginDescriptor(
name = "Item Charges",
@@ -56,21 +67,27 @@ public class ItemChargePlugin extends Plugin
private static final int MAX_DODGY_CHARGES = 10;
+ @Inject
+ private Client client;
+
@Inject
private OverlayManager overlayManager;
@Inject
private ItemChargeOverlay overlay;
+ @Inject
+ private ItemManager itemManager;
+
+ @Inject
+ private InfoBoxManager infoBoxManager;
+
@Inject
private Notifier notifier;
@Inject
private ItemChargeConfig config;
- @Getter(AccessLevel.PACKAGE)
- private int dodgyCharges;
-
@Provides
ItemChargeConfig getConfig(ConfigManager configManager)
{
@@ -81,13 +98,43 @@ public class ItemChargePlugin extends Plugin
protected void startUp()
{
overlayManager.add(overlay);
- dodgyCharges = config.dodgyNecklace();
}
@Override
protected void shutDown() throws Exception
{
overlayManager.remove(overlay);
+ infoBoxManager.removeIf(ItemChargeInfobox.class::isInstance);
+ }
+
+ @Subscribe
+ public void onConfigChanged(ConfigChanged event)
+ {
+ if (!event.getGroup().equals("itemCharge"))
+ {
+ return;
+ }
+
+ if (!config.showInfoboxes())
+ {
+ infoBoxManager.removeIf(ItemChargeInfobox.class::isInstance);
+ return;
+ }
+
+ if (!config.showTeleportCharges())
+ {
+ removeInfobox(ItemWithSlot.TELEPORT);
+ }
+
+ if (!config.showAbyssalBraceletCharges())
+ {
+ removeInfobox(ItemWithSlot.ABYSSAL_BRACELET);
+ }
+
+ if (!config.showDodgyCount())
+ {
+ removeInfobox(ItemWithSlot.DODGY_NECKLACE);
+ }
}
@Subscribe
@@ -110,22 +157,141 @@ public class ItemChargePlugin extends Plugin
notifier.notify("Your dodgy necklace has crumbled to dust.");
}
- setDodgyCharges(MAX_DODGY_CHARGES);
+ updateDodgyNecklaceCharges(MAX_DODGY_CHARGES);
}
else if (dodgyCheckMatcher.find())
{
- setDodgyCharges(Integer.parseInt(dodgyCheckMatcher.group(1)));
+ updateDodgyNecklaceCharges(Integer.parseInt(dodgyCheckMatcher.group(1)));
}
else if (dodgyProtectMatcher.find())
{
- setDodgyCharges(Integer.parseInt(dodgyProtectMatcher.group(1)));
+ updateDodgyNecklaceCharges(Integer.parseInt(dodgyProtectMatcher.group(1)));
}
}
}
- private void setDodgyCharges(int dodgyCharges)
+ @Subscribe
+ public void onItemContainerChanged(ItemContainerChanged event)
{
- this.dodgyCharges = dodgyCharges;
- config.dodgyNecklace(dodgyCharges);
+ if (event.getItemContainer() != client.getItemContainer(InventoryID.EQUIPMENT) || !config.showInfoboxes())
+ {
+ return;
+ }
+
+ final Item[] items = event.getItemContainer().getItems();
+
+ if (config.showTeleportCharges())
+ {
+ updateJewelleryInfobox(ItemWithSlot.TELEPORT, items);
+ }
+
+ if (config.showDodgyCount())
+ {
+ updateJewelleryInfobox(ItemWithSlot.DODGY_NECKLACE, items);
+ }
+
+ if (config.showAbyssalBraceletCharges())
+ {
+ updateJewelleryInfobox(ItemWithSlot.ABYSSAL_BRACELET, items);
+ }
+ }
+
+ private void updateDodgyNecklaceCharges(final int value)
+ {
+ config.dodgyNecklace(value);
+
+ if (config.showInfoboxes() && config.showDodgyCount())
+ {
+ final ItemContainer itemContainer = client.getItemContainer(InventoryID.EQUIPMENT);
+
+ if (itemContainer == null)
+ {
+ return;
+ }
+
+ updateJewelleryInfobox(ItemWithSlot.DODGY_NECKLACE, itemContainer.getItems());
+ }
+ }
+
+ private void updateJewelleryInfobox(ItemWithSlot item, Item[] items)
+ {
+ for (final EquipmentInventorySlot equipmentInventorySlot : item.getSlots())
+ {
+ updateJewelleryInfobox(item, items, equipmentInventorySlot);
+ }
+ }
+
+ private void updateJewelleryInfobox(ItemWithSlot type, Item[] items, EquipmentInventorySlot slot)
+ {
+ removeInfobox(type, slot);
+
+ if (slot.getSlotIdx() >= items.length)
+ {
+ return;
+ }
+
+ final int id = items[slot.getSlotIdx()].getId();
+ if (id < 0)
+ {
+ return;
+ }
+
+ final ItemWithCharge itemWithCharge = ItemWithCharge.findItem(id);
+ int charges = -1;
+
+ if (itemWithCharge == null)
+ {
+ if (id == ItemID.DODGY_NECKLACE && type == ItemWithSlot.DODGY_NECKLACE)
+ {
+ charges = config.dodgyNecklace();
+ }
+ }
+ else if (itemWithCharge.getType() == type.getType())
+ {
+ charges = itemWithCharge.getCharges();
+ }
+
+ if (charges <= 0)
+ {
+ return;
+ }
+
+ final String name = itemManager.getItemComposition(id).getName();
+ final BufferedImage image = itemManager.getImage(id);
+ final ItemChargeInfobox infobox = new ItemChargeInfobox(this, image, name, charges, type, slot);
+ infoBoxManager.addInfoBox(infobox);
+ }
+
+ private void removeInfobox(final ItemWithSlot item)
+ {
+ infoBoxManager.removeIf(t -> t instanceof ItemChargeInfobox && ((ItemChargeInfobox) t).getItem() == item);
+ }
+
+ private void removeInfobox(final ItemWithSlot item, final EquipmentInventorySlot slot)
+ {
+ infoBoxManager.removeIf(t ->
+ {
+ if (!(t instanceof ItemChargeInfobox))
+ {
+ return false;
+ }
+
+ final ItemChargeInfobox i = (ItemChargeInfobox)t;
+ return i.getItem() == item && i.getSlot() == slot;
+ });
+ }
+
+ Color getColor(int charges)
+ {
+ Color color = Color.WHITE;
+ if (charges <= config.veryLowWarning())
+ {
+ color = config.veryLowWarningColor();
+ }
+ else if (charges <= config.lowWarning())
+ {
+ color = config.lowWarningolor();
+ }
+ return color;
}
}
\ No newline at end of file
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemcharges/ItemChargeType.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemcharges/ItemChargeType.java
index 2f25c12164..cbe5d20ff3 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/itemcharges/ItemChargeType.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemcharges/ItemChargeType.java
@@ -32,5 +32,6 @@ enum ItemChargeType
IMPBOX,
TELEPORT,
WATERCAN,
- WATERSKIN
+ WATERSKIN,
+ DODGY_NECKLACE
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemcharges/ItemWithSlot.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemcharges/ItemWithSlot.java
new file mode 100644
index 0000000000..3fbd2bac66
--- /dev/null
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemcharges/ItemWithSlot.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2019, Tomas Slusny
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package net.runelite.client.plugins.itemcharges;
+
+import com.google.common.collect.Sets;
+import java.util.Set;
+import lombok.Getter;
+import net.runelite.api.EquipmentInventorySlot;
+
+@Getter
+enum ItemWithSlot
+{
+ ABYSSAL_BRACELET(ItemChargeType.ABYSSAL_BRACELET, EquipmentInventorySlot.GLOVES),
+ DODGY_NECKLACE(ItemChargeType.DODGY_NECKLACE, EquipmentInventorySlot.AMULET),
+ TELEPORT(ItemChargeType.TELEPORT, EquipmentInventorySlot.WEAPON, EquipmentInventorySlot.AMULET, EquipmentInventorySlot.GLOVES, EquipmentInventorySlot.RING);
+
+ private final ItemChargeType type;
+ private final Set slots;
+
+ ItemWithSlot(final ItemChargeType type, final EquipmentInventorySlot... slots)
+ {
+ this.type = type;
+ this.slots = Sets.newHashSet(slots);
+ }
+}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemprices/ItemPricesConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemprices/ItemPricesConfig.java
index ed69408341..a7b62e52b3 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/itemprices/ItemPricesConfig.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemprices/ItemPricesConfig.java
@@ -74,4 +74,16 @@ public interface ItemPricesConfig extends Config
{
return true;
}
+
+ @ConfigItem(
+ keyName = "showAlchProfit",
+ name = "Show High Alchemy Profit",
+ description = "Show the profit from casting high alchemy on items",
+ position = 5
+ )
+ default boolean showAlchProfit()
+ {
+ return false;
+ }
+
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemprices/ItemPricesOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemprices/ItemPricesOverlay.java
index 7e170db214..7c748a7702 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/itemprices/ItemPricesOverlay.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemprices/ItemPricesOverlay.java
@@ -196,6 +196,7 @@ class ItemPricesOverlay extends Overlay
int gePrice = 0;
int haPrice = 0;
+ int haProfit = 0;
if (config.showGEPrice())
{
@@ -205,16 +206,20 @@ class ItemPricesOverlay extends Overlay
{
haPrice = Math.round(itemDef.getPrice() * HIGH_ALCHEMY_CONSTANT);
}
+ if (gePrice > 0 && haPrice > 0 && config.showAlchProfit())
+ {
+ haProfit = calculateHAProfit(haPrice, gePrice);
+ }
if (gePrice > 0 || haPrice > 0)
{
- return stackValueText(qty, gePrice, haPrice);
+ return stackValueText(qty, gePrice, haPrice, haProfit);
}
return null;
}
- private String stackValueText(int qty, int gePrice, int haValue)
+ private String stackValueText(int qty, int gePrice, int haValue, int haProfit)
{
if (gePrice > 0)
{
@@ -246,9 +251,36 @@ class ItemPricesOverlay extends Overlay
}
}
+ if (haProfit != 0)
+ {
+ Color haColor = haProfitColor(haProfit);
+
+ itemStringBuilder.append("");
+ itemStringBuilder.append("HA Profit: ")
+ .append(ColorUtil.wrapWithColorTag(String.valueOf(haProfit * qty), haColor))
+ .append(" gp");
+ if (config.showEA() && qty > 1)
+ {
+ itemStringBuilder.append(" (")
+ .append(ColorUtil.wrapWithColorTag(String.valueOf(haProfit), haColor))
+ .append(" ea)");
+ }
+ }
+
// Build string and reset builder
final String text = itemStringBuilder.toString();
itemStringBuilder.setLength(0);
return text;
}
+
+ private int calculateHAProfit(int haPrice, int gePrice)
+ {
+ int natureRunePrice = itemManager.getItemPrice(ItemID.NATURE_RUNE);
+ return haPrice - gePrice - natureRunePrice;
+ }
+
+ private static Color haProfitColor(int haProfit)
+ {
+ return haProfit >= 0 ? Color.GREEN : Color.RED;
+ }
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/ItemStatOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/ItemStatOverlay.java
index 986321e966..73113bd0ac 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/ItemStatOverlay.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/ItemStatOverlay.java
@@ -258,7 +258,7 @@ public class ItemStatOverlay extends Overlay
if (config.relative())
{
- b.append(c.getRelative());
+ b.append(c.getFormattedRelative());
}
if (config.theoretical())
@@ -267,7 +267,7 @@ public class ItemStatOverlay extends Overlay
{
b.append("/");
}
- b.append(c.getTheoretical());
+ b.append(c.getFormattedTheoretical());
}
if (config.absolute() && (config.relative() || config.theoretical()))
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/RangeStatBoost.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/RangeStatBoost.java
index abe47cea1c..13011b9f6a 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/RangeStatBoost.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/RangeStatBoost.java
@@ -42,33 +42,21 @@ public class RangeStatBoost extends SingleEffect
@Override
public StatChange effect(Client client)
{
- final StatChange a = this.a.effect(client);
- final StatChange b = this.b.effect(client);
+ final StatChange changeA = this.a.effect(client);
+ final StatChange changeB = this.b.effect(client);
- final StatChange r = new StatChange();
- r.setAbsolute(concat(a.getAbsolute(), b.getAbsolute()));
- r.setRelative(concat(a.getRelative(), b.getRelative()));
- r.setTheoretical(concat(a.getTheoretical(), b.getTheoretical()));
- r.setStat(a.getStat());
+ final RangeStatChange r = new RangeStatChange();
+ r.setMinAbsolute(Math.min(changeA.getAbsolute(), changeB.getAbsolute()));
+ r.setAbsolute(Math.max(changeA.getAbsolute(), changeB.getAbsolute()));
+ r.setMinRelative(Math.min(changeA.getRelative(), changeB.getRelative()));
+ r.setRelative(Math.max(changeA.getRelative(), changeB.getRelative()));
+ r.setMinTheoretical(Math.min(changeA.getTheoretical(), changeB.getTheoretical()));
+ r.setTheoretical(Math.max(changeA.getTheoretical(), changeB.getTheoretical()));
+ r.setStat(changeA.getStat());
- final int avg = (a.getPositivity().ordinal() + b.getPositivity().ordinal()) / 2;
+ final int avg = (changeA.getPositivity().ordinal() + changeB.getPositivity().ordinal()) / 2;
r.setPositivity(Positivity.values()[avg]);
return r;
}
-
- private String concat(String a, String b)
- {
- // If they share a operator, strip b's duplicate
- if (a.length() > 1 && b.length() > 1)
- {
- final char a0 = a.charAt(0);
- if ((a0 == '+' || a0 == '-' || a0 == '±') && b.charAt(0) == a0)
- {
- b = b.substring(1);
- }
- }
-
- return a + "~" + b;
- }
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/RangeStatChange.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/RangeStatChange.java
new file mode 100644
index 0000000000..90b614bf10
--- /dev/null
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/RangeStatChange.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2016-2019, Jordan Atwood
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package net.runelite.client.plugins.itemstats;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * A stat change which can result in different magnitudes of change to the stat
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class RangeStatChange extends StatChange
+{
+ /**
+ * Minimum relative change that will occur if the stat boost is applied now.
+ * In this class, {@code relative} is representative of the maximum relative change that will
+ * occur.
+ */
+ private int minRelative;
+
+ /**
+ * Minimum theoretical change that can occur before boost cap is enforced.
+ * In this class, {@code theoretical} is representative of the maximum theoretical change that
+ * will occur.
+ */
+ private int minTheoretical;
+
+ /**
+ * Minimum absolute total of the stat after applying the boost.
+ * In this class, {@code absolute} is representative of the maximum absolute change that will
+ * occur.
+ */
+ private int minAbsolute;
+
+ /**
+ * Returns a human-readable formatted relative boost.
+ * Should be the boost range in the format "±N" (for minimum -N and maximum +N values),
+ * "+MIN~MAX" (for minimum and maximum values of the same sign),
+ * "-MIN~+MAX" (for negative minimum and positive maximum values), or
+ * "+MAX" (for equal minimum and maximum values).
+ *
+ * @return The formatted relative boost amount
+ */
+ @Override
+ public String getFormattedRelative()
+ {
+ return concat(minRelative, getRelative());
+ }
+
+ /**
+ * Returns a human-readable formatted theoretical boost.
+ * Should be the boost range in the format "±N" (for minimum -N and maximum +N values),
+ * "+MIN~MAX" (for minimum and maximum values of the same sign),
+ * "-MIN~+MAX" (for negative minimum and positive maximum values), or
+ * "+MAX" (for equal minimum and maximum values).
+ *
+ * @return The formatted theoretical boost amount
+ */
+ @Override
+ public String getFormattedTheoretical()
+ {
+ return concat(minTheoretical, getTheoretical());
+ }
+
+ private static String concat(int changeA, int changeB)
+ {
+ if (changeA == changeB)
+ {
+ return formatBoost(changeA);
+ }
+ else if (changeA * -1 == changeB)
+ {
+ return "±" + Math.abs(changeA);
+ }
+
+ final StringBuilder sb = new StringBuilder();
+
+ sb.append(String.format("%+d", changeA));
+ sb.append('~');
+
+ // If they share a operator, strip b's duplicate
+ if (changeA < 0 && changeB < 0
+ || changeA >= 0 && changeB >= 0)
+ {
+ sb.append(Math.abs(changeB));
+ }
+ else
+ {
+ sb.append(String.format("%+d", changeB));
+ }
+
+ return sb.toString();
+ }
+}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/StatBoost.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/StatBoost.java
index 1bc59d7412..cc5f7797f2 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/StatBoost.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/StatBoost.java
@@ -88,10 +88,9 @@ public abstract class StatBoost extends SingleEffect
{
out.setPositivity(Positivity.WORSE);
}
- out.setAbsolute(Integer.toString(newValue));
- out.setRelative(String.format("%+d", delta));
- out.setTheoretical(String.format("%+d", calcedDelta));
+ out.setAbsolute(newValue);
+ out.setRelative(delta);
+ out.setTheoretical(calcedDelta);
return out;
}
-
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/StatChange.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/StatChange.java
index 3157748083..e9453984f4 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/StatChange.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/StatChange.java
@@ -40,23 +40,48 @@ public class StatChange
/**
* Relative change that will occur if the stat boost is applied now.
- * Should be a number prefixed by "+" or "-".
*/
- private String relative;
+ private int relative;
/**
* Theoretical change that can occur before boost cap is enforced.
- * Should be a number prefixed by "+" or "-".
*/
- private String theoretical;
+ private int theoretical;
/**
* Absolute total of the stat after applying the boost.
*/
- private String absolute;
+ private int absolute;
/**
* How beneficial this stat boost will be to the player.
*/
private Positivity positivity;
+
+ /**
+ * Returns a human-readable formatted relative boost.
+ * Should be the boost amount prefixed by "+" or "-".
+ *
+ * @return The formatted relative boost amount
+ */
+ public String getFormattedRelative()
+ {
+ return formatBoost(relative);
+ }
+
+ /**
+ * Returns a human-readable formatted theoretical boost.
+ * Should be the boost amount prefixed by "+" or "-".
+ *
+ * @return The formatted theoretical boost amount
+ */
+ public String getFormattedTheoretical()
+ {
+ return formatBoost(theoretical);
+ }
+
+ static String formatBoost(int boost)
+ {
+ return String.format("%+d", boost);
+ }
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/special/SpicyStew.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/special/SpicyStew.java
index 8dab7fd601..7eee548751 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/special/SpicyStew.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/special/SpicyStew.java
@@ -132,11 +132,14 @@ public class SpicyStew implements Effect
int currentBoost = currentValue - currentBase; // Can be negative
int spiceBoostCapped = (currentBoost <= 0) ? spiceBoost : Math.max(0, spiceBoost - currentBoost);
- StatChange change = new StatChange();
+ final RangeStatChange change = new RangeStatChange();
change.setStat(stat);
- change.setRelative("±" + spiceBoostCapped);
- change.setTheoretical("±" + spiceBoost);
- change.setAbsolute(String.valueOf(stat.getValue(client) + spiceBoostCapped));
+ change.setMinRelative(-spiceBoost);
+ change.setRelative(spiceBoostCapped);
+ change.setMinTheoretical(-spiceBoost);
+ change.setTheoretical(spiceBoost);
+ change.setMinAbsolute(Math.max(-spiceBoost, -currentValue));
+ change.setAbsolute(stat.getValue(client) + spiceBoostCapped);
Positivity positivity;
if (spiceBoostCapped == 0)
@@ -155,5 +158,4 @@ public class SpicyStew implements Effect
return change;
}
-
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/kingdomofmiscellania/KingdomCounter.java b/runelite-client/src/main/java/net/runelite/client/plugins/kingdomofmiscellania/KingdomCounter.java
index 7dc6aabf35..8a2871125f 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/kingdomofmiscellania/KingdomCounter.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/kingdomofmiscellania/KingdomCounter.java
@@ -32,9 +32,9 @@ public class KingdomCounter extends Counter
{
private final KingdomPlugin plugin;
- public KingdomCounter(BufferedImage image, KingdomPlugin plugin)
+ KingdomCounter(BufferedImage image, KingdomPlugin plugin)
{
- super(image, plugin, String.valueOf(plugin.getFavor()));
+ super(image, plugin, plugin.getFavor());
this.plugin = plugin;
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryConfig.java
index b61a24c139..6ec40a00fd 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryConfig.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryConfig.java
@@ -42,4 +42,14 @@ public interface KourendLibraryConfig extends Config
{
return true;
}
+
+ @ConfigItem(
+ keyName = "hideDuplicateBook",
+ name = "Hide duplicate book",
+ description = "Don't show the duplicate book locations in the library"
+ )
+ default boolean hideDuplicateBook()
+ {
+ return true;
+ }
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryOverlay.java
index 3c6863af3a..5cf7910ee6 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryOverlay.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryOverlay.java
@@ -35,6 +35,7 @@ import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
+import javax.annotation.Nullable;
import lombok.AccessLevel;
import lombok.Setter;
import net.runelite.api.Client;
@@ -54,15 +55,19 @@ class KourendLibraryOverlay extends Overlay
private final static int MAXIMUM_DISTANCE = 24;
private final Library library;
private final Client client;
+ private final KourendLibraryConfig config;
+ private final KourendLibraryPlugin plugin;
@Setter(AccessLevel.PACKAGE)
private boolean hidden;
@Inject
- private KourendLibraryOverlay(Library library, Client client)
+ private KourendLibraryOverlay(Library library, Client client, KourendLibraryConfig config, KourendLibraryPlugin plugin)
{
this.library = library;
this.client = client;
+ this.config = config;
+ this.plugin = plugin;
setPosition(OverlayPosition.DYNAMIC);
setLayer(OverlayLayer.ABOVE_SCENE);
@@ -133,7 +138,7 @@ class KourendLibraryOverlay extends Overlay
Color color = bookIsKnown ? Color.ORANGE : Color.WHITE;
// Render the poly on the floor
- if (!(bookIsKnown && book == null) && (library.getState() == SolvedState.NO_DATA || book != null || !possible.isEmpty()))
+ if (!(bookIsKnown && book == null) && (library.getState() == SolvedState.NO_DATA || book != null || !possible.isEmpty()) && !shouldHideOverlayIfDuplicateBook(book))
{
Polygon poly = getCanvasTilePoly(client, localBookcase);
if (poly != null)
@@ -146,7 +151,7 @@ class KourendLibraryOverlay extends Overlay
// If the book is singled out, render the text and the book's icon
if (bookIsKnown)
{
- if (book != null)
+ if (book != null && !shouldHideOverlayIfDuplicateBook(book))
{
FontMetrics fm = g.getFontMetrics();
Rectangle2D bounds = fm.getStringBounds(book.getShortName(), g);
@@ -216,9 +221,10 @@ class KourendLibraryOverlay extends Overlay
.forEach(n ->
{
Book b = library.getCustomerBook();
+ boolean doesPlayerContainBook = b != null && plugin.doesPlayerContainBook(b);
LocalPoint local = n.getLocalLocation();
Polygon poly = getCanvasTilePoly(client, local);
- OverlayUtil.renderPolygon(g, poly, Color.WHITE);
+ OverlayUtil.renderPolygon(g, poly, doesPlayerContainBook ? Color.GREEN : Color.WHITE);
Point screen = Perspective.localToCanvas(client, local, client.getPlane(), n.getLogicalHeight());
if (screen != null)
{
@@ -229,4 +235,12 @@ class KourendLibraryOverlay extends Overlay
return null;
}
+
+ private boolean shouldHideOverlayIfDuplicateBook(@Nullable Book book)
+ {
+ return config.hideDuplicateBook()
+ && book != null
+ && !book.isDarkManuscript()
+ && plugin.doesPlayerContainBook(book);
+ }
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPlugin.java
index 73602299f4..649189fc47 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPlugin.java
@@ -26,6 +26,7 @@ package net.runelite.client.plugins.kourendlibrary;
import com.google.inject.Provides;
import java.awt.image.BufferedImage;
+import java.util.EnumSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.inject.Inject;
@@ -34,6 +35,9 @@ import lombok.extern.slf4j.Slf4j;
import net.runelite.api.AnimationID;
import net.runelite.api.ChatMessageType;
import net.runelite.api.Client;
+import net.runelite.api.InventoryID;
+import net.runelite.api.Item;
+import net.runelite.api.ItemContainer;
import net.runelite.api.MenuAction;
import net.runelite.api.Player;
import net.runelite.api.coords.WorldPoint;
@@ -41,6 +45,7 @@ import net.runelite.api.events.AnimationChanged;
import net.runelite.api.events.ChatMessage;
import net.runelite.api.events.ConfigChanged;
import net.runelite.api.events.GameTick;
+import net.runelite.api.events.ItemContainerChanged;
import net.runelite.api.events.MenuOptionClicked;
import net.runelite.api.widgets.Widget;
import net.runelite.api.widgets.WidgetInfo;
@@ -94,6 +99,7 @@ public class KourendLibraryPlugin extends Plugin
private boolean buttonAttached = false;
private WorldPoint lastBookcaseClick = null;
private WorldPoint lastBookcaseAnimatedOn = null;
+ private EnumSet playerBooks = null;
@Provides
KourendLibraryConfig provideConfig(ConfigManager configManager)
@@ -120,6 +126,8 @@ public class KourendLibraryPlugin extends Plugin
overlayManager.add(overlay);
+ updatePlayerBooks();
+
if (!config.hideButton())
{
clientToolbar.addNavigation(navButton);
@@ -135,6 +143,7 @@ public class KourendLibraryPlugin extends Plugin
buttonAttached = false;
lastBookcaseClick = null;
lastBookcaseAnimatedOn = null;
+ playerBooks = null;
}
@Subscribe
@@ -271,4 +280,37 @@ public class KourendLibraryPlugin extends Plugin
}
}
}
+
+ @Subscribe
+ public void onItemContainerChanged(ItemContainerChanged itemContainerChangedEvent)
+ {
+ updatePlayerBooks();
+ }
+
+ boolean doesPlayerContainBook(Book book)
+ {
+ return playerBooks.contains(book);
+ }
+
+ private void updatePlayerBooks()
+ {
+ ItemContainer itemContainer = client.getItemContainer(InventoryID.INVENTORY);
+
+ if (itemContainer != null)
+ {
+ EnumSet books = EnumSet.noneOf(Book.class);
+
+ for (Item item : itemContainer.getItems())
+ {
+ Book book = Book.byId(item.getId());
+
+ if (book != null)
+ {
+ books.add(book);
+ }
+ }
+
+ playerBooks = books;
+ }
+ }
}
\ No newline at end of file
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerConfig.java
index 98b7053117..bba5994d89 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerConfig.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerConfig.java
@@ -51,11 +51,23 @@ public interface LootTrackerConfig extends Config
@ConfigItem(
keyName = "saveLoot",
- name = "Save loot",
- description = "Save loot between client sessions (requires being logged in)"
+ name = "Submit loot tracker data",
+ description = "Submit loot tracker data (requires being logged in)"
)
default boolean saveLoot()
{
return true;
}
+
+ @ConfigItem(
+ keyName = "syncPanel",
+ name = "Synchronize panel contents",
+ description = "Synchronize you local loot tracker with your online (requires being logged in). This means" +
+ " that panel is filled with portion of your remote data on startup and deleting data in panel deletes them" +
+ " also on server."
+ )
+ default boolean syncPanel()
+ {
+ return true;
+ }
}
\ No newline at end of file
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPanel.java
index 4aaa7e0c7a..ed850cdcbb 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPanel.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPanel.java
@@ -99,6 +99,7 @@ class LootTrackerPanel extends PluginPanel
private final ItemManager itemManager;
private final LootTrackerPlugin plugin;
+ private final LootTrackerConfig config;
private boolean groupLoot;
private boolean hideIgnoredItems;
@@ -130,10 +131,11 @@ class LootTrackerPanel extends PluginPanel
INVISIBLE_ICON_HOVER = new ImageIcon(ImageUtil.alphaOffset(invisibleImg, -220));
}
- LootTrackerPanel(final LootTrackerPlugin plugin, final ItemManager itemManager)
+ LootTrackerPanel(final LootTrackerPlugin plugin, final ItemManager itemManager, final LootTrackerConfig config)
{
this.itemManager = itemManager;
this.plugin = plugin;
+ this.config = config;
this.hideIgnoredItems = true;
setBorder(new EmptyBorder(6, 6, 6, 6));
@@ -297,7 +299,7 @@ class LootTrackerPanel extends PluginPanel
// Delete all loot, or loot matching the current view
LootTrackerClient client = plugin.getLootTrackerClient();
- if (client != null)
+ if (client != null && config.syncPanel())
{
client.delete(currentView);
}
@@ -472,7 +474,7 @@ class LootTrackerPanel extends PluginPanel
LootTrackerClient client = plugin.getLootTrackerClient();
// Without loot being grouped we have no way to identify single kills to be deleted
- if (client != null && groupLoot)
+ if (client != null && groupLoot && config.syncPanel())
{
client.delete(box.getId());
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java
index acb725db64..055c2cc3f4 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java
@@ -28,6 +28,7 @@ package net.runelite.client.plugins.loottracker;
import com.google.inject.Provides;
import java.awt.image.BufferedImage;
import java.io.IOException;
+import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -194,7 +195,7 @@ public class LootTrackerPlugin extends Plugin
protected void startUp() throws Exception
{
ignoredItems = Text.fromCSV(config.getIgnoredItems());
- panel = new LootTrackerPanel(this, itemManager);
+ panel = new LootTrackerPanel(this, itemManager, config);
spriteManager.getSpriteAsync(SpriteID.TAB_INVENTORY, 0, panel::loadHeaderIcon);
final BufferedImage icon = ImageUtil.getResourceStreamFromClass(getClass(), "panel_icon.png");
@@ -226,9 +227,8 @@ public class LootTrackerPlugin extends Plugin
{
Collection lootRecords;
- if (!config.saveLoot())
+ if (!config.syncPanel())
{
- // don't load loot if we're not saving loot
return;
}
@@ -274,7 +274,7 @@ public class LootTrackerPlugin extends Plugin
if (lootTrackerClient != null && config.saveLoot())
{
- LootRecord lootRecord = new LootRecord(name, LootRecordType.NPC, toGameItems(items));
+ LootRecord lootRecord = new LootRecord(name, LootRecordType.NPC, toGameItems(items), Instant.now());
lootTrackerClient.submit(lootRecord);
}
}
@@ -291,7 +291,7 @@ public class LootTrackerPlugin extends Plugin
if (lootTrackerClient != null && config.saveLoot())
{
- LootRecord lootRecord = new LootRecord(name, LootRecordType.PLAYER, toGameItems(items));
+ LootRecord lootRecord = new LootRecord(name, LootRecordType.PLAYER, toGameItems(items), Instant.now());
lootTrackerClient.submit(lootRecord);
}
}
@@ -350,7 +350,7 @@ public class LootTrackerPlugin extends Plugin
if (lootTrackerClient != null && config.saveLoot())
{
- LootRecord lootRecord = new LootRecord(eventType, LootRecordType.EVENT, toGameItems(items));
+ LootRecord lootRecord = new LootRecord(eventType, LootRecordType.EVENT, toGameItems(items), Instant.now());
lootTrackerClient.submit(lootRecord);
}
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPlugin.java
index e6420bdc99..ca62eca65e 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPlugin.java
@@ -45,8 +45,10 @@ import net.runelite.api.events.MenuOptionClicked;
import net.runelite.api.events.PostItemComposition;
import net.runelite.api.events.WidgetMenuOptionClicked;
import net.runelite.api.widgets.WidgetInfo;
+import net.runelite.client.callback.ClientThread;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.Subscribe;
+import net.runelite.client.game.ItemManager;
import net.runelite.client.game.ItemVariationMapping;
import net.runelite.client.input.KeyManager;
import net.runelite.client.menus.MenuManager;
@@ -101,6 +103,9 @@ public class MenuEntrySwapperPlugin extends Plugin
@Inject
private Client client;
+ @Inject
+ private ClientThread clientThread;
+
@Inject
private MenuEntrySwapperConfig config;
@@ -116,6 +121,9 @@ public class MenuEntrySwapperPlugin extends Plugin
@Inject
private MenuManager menuManager;
+ @Inject
+ private ItemManager itemManager;
+
@Getter
private boolean configuringShiftClick = false;
@@ -146,6 +154,11 @@ public class MenuEntrySwapperPlugin extends Plugin
@Subscribe
public void onConfigChanged(ConfigChanged event)
{
+ if (!CONFIG_GROUP.equals(event.getGroup()))
+ {
+ return;
+ }
+
if (event.getKey().equals("shiftClickCustomization"))
{
if (config.shiftClickCustomization())
@@ -157,6 +170,16 @@ public class MenuEntrySwapperPlugin extends Plugin
disableCustomization();
}
}
+ else if (event.getKey().startsWith(ITEM_KEY_PREFIX))
+ {
+ clientThread.invoke(this::resetItemCompositionCache);
+ }
+ }
+
+ private void resetItemCompositionCache()
+ {
+ itemManager.invalidateItemCompositionCache();
+ client.getItemCompositionCache().reset();
}
private Integer getSwapConfig(int itemId)
@@ -179,6 +202,7 @@ public class MenuEntrySwapperPlugin extends Plugin
private void unsetSwapConfig(int itemId)
{
+ itemId = ItemVariationMapping.map(itemId);
configManager.unsetConfiguration(CONFIG_GROUP, ITEM_KEY_PREFIX + itemId);
}
@@ -186,6 +210,7 @@ public class MenuEntrySwapperPlugin extends Plugin
{
keyManager.registerKeyListener(inputListener);
refreshShiftClickCustomizationMenus();
+ clientThread.invoke(this::resetItemCompositionCache);
}
private void disableCustomization()
@@ -193,6 +218,7 @@ public class MenuEntrySwapperPlugin extends Plugin
keyManager.unregisterKeyListener(inputListener);
removeShiftClickCustomizationMenus();
configuringShiftClick = false;
+ clientThread.invoke(this::resetItemCompositionCache);
}
@Subscribe
@@ -290,7 +316,6 @@ public class MenuEntrySwapperPlugin extends Plugin
if (option.equals(RESET) && target.equals(MENU_TARGET))
{
unsetSwapConfig(itemId);
- itemComposition.resetShiftClickActionIndex();
return;
}
@@ -323,7 +348,6 @@ public class MenuEntrySwapperPlugin extends Plugin
if (valid)
{
setSwapConfig(itemId, index);
- itemComposition.setShiftClickActionIndex(index);
}
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/mta/graveyard/GraveyardCounter.java b/runelite-client/src/main/java/net/runelite/client/plugins/mta/graveyard/GraveyardCounter.java
index 83e74cc6d6..ecfbafd544 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/mta/graveyard/GraveyardCounter.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/mta/graveyard/GraveyardCounter.java
@@ -31,22 +31,15 @@ import net.runelite.client.ui.overlay.infobox.Counter;
public class GraveyardCounter extends Counter
{
- private int count;
-
- public GraveyardCounter(BufferedImage image, Plugin plugin)
+ GraveyardCounter(BufferedImage image, Plugin plugin)
{
- super(image, plugin, "0");
- }
-
- public void setCount(int count)
- {
- this.count = count;
- this.setText(String.valueOf(count));
+ super(image, plugin, 0);
}
@Override
public Color getTextColor()
{
+ int count = getCount();
if (count >= GraveyardRoom.MIN_SCORE)
{
return Color.GREEN;
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/nightmarezone/AbsorptionCounter.java b/runelite-client/src/main/java/net/runelite/client/plugins/nightmarezone/AbsorptionCounter.java
index 05d1498312..4671edde12 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/nightmarezone/AbsorptionCounter.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/nightmarezone/AbsorptionCounter.java
@@ -26,43 +26,27 @@ package net.runelite.client.plugins.nightmarezone;
import java.awt.Color;
import java.awt.image.BufferedImage;
-import lombok.Getter;
import lombok.Setter;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.ui.overlay.infobox.Counter;
+@Setter
public class AbsorptionCounter extends Counter
{
- private int absorption;
-
- @Getter
- @Setter
private int threshold;
-
- @Getter
- @Setter
private Color aboveThresholdColor = Color.GREEN;
-
- @Getter
- @Setter
private Color belowThresholdColor = Color.RED;
- public AbsorptionCounter(BufferedImage image, Plugin plugin, int absorption, int threshold)
+ AbsorptionCounter(BufferedImage image, Plugin plugin, int absorption, int threshold)
{
- super(image, plugin, "");
+ super(image, plugin, absorption);
this.threshold = threshold;
- setAbsorption(absorption);
- }
-
- public void setAbsorption(int absorption)
- {
- this.absorption = absorption;
- setText(String.valueOf(absorption));
}
@Override
public Color getTextColor()
{
+ int absorption = getCount();
if (absorption >= threshold)
{
return aboveThresholdColor;
@@ -76,6 +60,7 @@ public class AbsorptionCounter extends Counter
@Override
public String getTooltip()
{
+ int absorption = getCount();
return "Absorption: " + absorption;
}
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/nightmarezone/NightmareZoneOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/nightmarezone/NightmareZoneOverlay.java
index e2a123b1af..71ae196d3e 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/nightmarezone/NightmareZoneOverlay.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/nightmarezone/NightmareZoneOverlay.java
@@ -129,7 +129,7 @@ class NightmareZoneOverlay extends Overlay
}
else
{
- absorptionCounter.setAbsorption(absorptionPoints);
+ absorptionCounter.setCount(absorptionPoints);
}
}
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/objectindicators/ObjectIndicatorsOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/objectindicators/ObjectIndicatorsOverlay.java
index af9431038e..f111a6f94f 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/objectindicators/ObjectIndicatorsOverlay.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/objectindicators/ObjectIndicatorsOverlay.java
@@ -29,6 +29,7 @@ import java.awt.Graphics2D;
import java.awt.Polygon;
import javax.inject.Inject;
import net.runelite.api.Client;
+import net.runelite.api.DecorativeObject;
import net.runelite.api.GameObject;
import net.runelite.api.TileObject;
import net.runelite.client.ui.overlay.Overlay;
@@ -65,22 +66,31 @@ class ObjectIndicatorsOverlay extends Overlay
}
final Polygon polygon;
+ Polygon polygon2 = null;
if (object instanceof GameObject)
{
polygon = ((GameObject) object).getConvexHull();
}
+ else if (object instanceof DecorativeObject)
+ {
+ polygon = ((DecorativeObject) object).getConvexHull();
+ polygon2 = ((DecorativeObject) object).getConvexHull2();
+ }
else
{
polygon = object.getCanvasTilePoly();
}
- if (polygon == null)
+ if (polygon != null)
{
- continue;
+ OverlayUtil.renderPolygon(graphics, polygon, config.markerColor());
}
- OverlayUtil.renderPolygon(graphics, polygon, config.markerColor());
+ if (polygon2 != null)
+ {
+ OverlayUtil.renderPolygon(graphics, polygon2, config.markerColor());
+ }
}
return null;
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/objectindicators/ObjectIndicatorsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/objectindicators/ObjectIndicatorsPlugin.java
index a352d728db..05fd84d0d8 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/objectindicators/ObjectIndicatorsPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/objectindicators/ObjectIndicatorsPlugin.java
@@ -43,6 +43,7 @@ import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
import static net.runelite.api.Constants.REGION_SIZE;
+import net.runelite.api.DecorativeObject;
import net.runelite.api.GameObject;
import net.runelite.api.GameState;
import net.runelite.api.MenuAction;
@@ -58,6 +59,8 @@ import net.runelite.api.events.GameObjectSpawned;
import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.MenuEntryAdded;
import net.runelite.api.events.MenuOptionClicked;
+import net.runelite.api.events.DecorativeObjectSpawned;
+import net.runelite.api.events.DecorativeObjectDespawned;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.input.KeyListener;
@@ -158,26 +161,15 @@ public class ObjectIndicatorsPlugin extends Plugin implements KeyListener
@Subscribe
public void onGameObjectSpawned(GameObjectSpawned event)
{
- final WorldPoint worldPoint = WorldPoint.fromLocalInstance(client, event.getGameObject().getLocalLocation());
- final Set objectPoints = points.get(worldPoint.getRegionID());
+ final GameObject eventObject = event.getGameObject();
+ checkObjectPoints(eventObject);
+ }
- if (objectPoints == null)
- {
- return;
- }
-
- for (ObjectPoint objectPoint : objectPoints)
- {
- if ((worldPoint.getX() & (REGION_SIZE - 1)) == objectPoint.getRegionX()
- && (worldPoint.getY() & (REGION_SIZE - 1)) == objectPoint.getRegionY())
- {
- if (objectPoint.getName().equals(client.getObjectDefinition(event.getGameObject().getId()).getName()))
- {
- objects.add(event.getGameObject());
- break;
- }
- }
- }
+ @Subscribe
+ public void onDecorativeObjectSpawned(DecorativeObjectSpawned event)
+ {
+ final DecorativeObject eventObject = event.getDecorativeObject();
+ checkObjectPoints(eventObject);
}
@Subscribe
@@ -186,6 +178,12 @@ public class ObjectIndicatorsPlugin extends Plugin implements KeyListener
objects.remove(event.getGameObject());
}
+ @Subscribe
+ public void onDecorativeObjectDespawned(DecorativeObjectDespawned event)
+ {
+ objects.remove(event.getDecorativeObject());
+ }
+
@Subscribe
public void onGameStateChanged(GameStateChanged gameStateChanged)
{
@@ -263,6 +261,30 @@ public class ObjectIndicatorsPlugin extends Plugin implements KeyListener
markObject(name, object);
}
+ private void checkObjectPoints(TileObject object)
+ {
+ final WorldPoint worldPoint = WorldPoint.fromLocalInstance(client, object.getLocalLocation());
+ final Set objectPoints = points.get(worldPoint.getRegionID());
+
+ if (objectPoints == null)
+ {
+ return;
+ }
+
+ for (ObjectPoint objectPoint : objectPoints)
+ {
+ if ((worldPoint.getX() & (REGION_SIZE - 1)) == objectPoint.getRegionX()
+ && (worldPoint.getY() & (REGION_SIZE - 1)) == objectPoint.getRegionY())
+ {
+ if (objectPoint.getName().equals(client.getObjectDefinition(object.getId()).getName()))
+ {
+ objects.add(object);
+ break;
+ }
+ }
+ }
+ }
+
private TileObject findTileObject(Tile tile, int id)
{
if (tile == null)
@@ -271,6 +293,12 @@ public class ObjectIndicatorsPlugin extends Plugin implements KeyListener
}
final GameObject[] tileGameObjects = tile.getGameObjects();
+ final DecorativeObject tileDecorativeObject = tile.getDecorativeObject();
+
+ if (tileDecorativeObject != null && tileDecorativeObject.getId() == id)
+ {
+ return tileDecorativeObject;
+ }
for (GameObject object : tileGameObjects)
{
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/party/PartyPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/party/PartyPlugin.java
index 73abcdb172..0e910ff338 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/party/PartyPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/party/PartyPlugin.java
@@ -191,7 +191,7 @@ public class PartyPlugin extends Plugin implements KeyListener
.build();
chatMessageManager.queue(QueuedMessage.builder()
- .type(ChatMessageType.GAME)
+ .type(ChatMessageType.CLANCHAT_INFO)
.runeLiteFormattedMessage(leaveMessage)
.build());
}
@@ -381,7 +381,7 @@ public class PartyPlugin extends Plugin implements KeyListener
.build();
chatMessageManager.queue(QueuedMessage.builder()
- .type(ChatMessageType.GAME)
+ .type(ChatMessageType.CLANCHAT_INFO)
.runeLiteFormattedMessage(joinMessage)
.build());
@@ -430,7 +430,7 @@ public class PartyPlugin extends Plugin implements KeyListener
.build();
chatMessageManager.queue(QueuedMessage.builder()
- .type(ChatMessageType.GAME)
+ .type(ChatMessageType.CLANCHAT_INFO)
.runeLiteFormattedMessage(joinMessage)
.build());
}
@@ -519,7 +519,7 @@ public class PartyPlugin extends Plugin implements KeyListener
.build();
chatMessageManager.queue(QueuedMessage.builder()
- .type(ChatMessageType.GAME)
+ .type(ChatMessageType.CLANCHAT_INFO)
.runeLiteFormattedMessage(helpMessage)
.build());
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/party/PartyStatsOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/party/PartyStatsOverlay.java
index 2b9e1820f3..693b23cb89 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/party/PartyStatsOverlay.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/party/PartyStatsOverlay.java
@@ -27,6 +27,8 @@ package net.runelite.client.plugins.party;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.Rectangle;
import java.util.Map;
import java.util.UUID;
import javax.inject.Inject;
@@ -34,6 +36,7 @@ import net.runelite.api.MenuAction;
import net.runelite.client.plugins.party.data.PartyData;
import net.runelite.client.ui.overlay.Overlay;
import net.runelite.client.ui.overlay.OverlayMenuEntry;
+import net.runelite.client.ui.overlay.components.ComponentConstants;
import net.runelite.client.ui.overlay.components.PanelComponent;
import net.runelite.client.ui.overlay.components.ProgressBarComponent;
import net.runelite.client.ui.overlay.components.TitleComponent;
@@ -58,6 +61,8 @@ public class PartyStatsOverlay extends Overlay
this.plugin = plugin;
this.party = party;
this.config = config;
+ body.setBorder(new Rectangle());
+ body.setGap(new Point(0, ComponentConstants.STANDARD_BORDER / 2));
getMenuEntries().add(new OverlayMenuEntry(MenuAction.RUNELITE_OVERLAY, "Leave", "Party"));
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/party/messages/LocationUpdate.java b/runelite-client/src/main/java/net/runelite/client/plugins/party/messages/LocationUpdate.java
index 0ff6569379..f5bf7131ce 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/party/messages/LocationUpdate.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/party/messages/LocationUpdate.java
@@ -24,11 +24,13 @@
*/
package net.runelite.client.plugins.party.messages;
+import lombok.EqualsAndHashCode;
import lombok.Value;
import net.runelite.api.coords.WorldPoint;
import net.runelite.http.api.ws.messages.party.PartyMemberMessage;
@Value
+@EqualsAndHashCode(callSuper = true)
public class LocationUpdate extends PartyMemberMessage
{
private final WorldPoint worldPoint;
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/party/messages/TilePing.java b/runelite-client/src/main/java/net/runelite/client/plugins/party/messages/TilePing.java
index 1378403333..fb4f812a81 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/party/messages/TilePing.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/party/messages/TilePing.java
@@ -24,11 +24,13 @@
*/
package net.runelite.client.plugins.party.messages;
+import lombok.EqualsAndHashCode;
import lombok.Value;
import net.runelite.api.coords.WorldPoint;
import net.runelite.http.api.ws.messages.party.PartyMemberMessage;
@Value
+@EqualsAndHashCode(callSuper = true)
public class TilePing extends PartyMemberMessage
{
private final WorldPoint point;
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/PestControlPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/PestControlPlugin.java
index 360e56f40d..7e711e66a0 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/PestControlPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/PestControlPlugin.java
@@ -92,7 +92,8 @@ public class PestControlPlugin extends Plugin
@Subscribe
public void onGameStateChanged(GameStateChanged event)
{
- if (event.getGameState() == GameState.LOADING)
+ GameState gameState = event.getGameState();
+ if (gameState == GameState.CONNECTION_LOST || gameState == GameState.LOGIN_SCREEN || gameState == GameState.HOPPING)
{
spinners.clear();
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/poison/PoisonConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/poison/PoisonConfig.java
index 2595009205..6c723d4f71 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/poison/PoisonConfig.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/poison/PoisonConfig.java
@@ -42,4 +42,14 @@ public interface PoisonConfig extends Config
{
return false;
}
+
+ @ConfigItem(
+ keyName = "changeHealthIcon",
+ name = "Change HP Orb Icon",
+ description = "Configures whether the hp orb icon should change color to match poison/disease"
+ )
+ default boolean changeHealthIcon()
+ {
+ return true;
+ }
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/poison/PoisonPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/poison/PoisonPlugin.java
index 68b9889a2a..84a4c5e3d6 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/poison/PoisonPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/poison/PoisonPlugin.java
@@ -35,12 +35,13 @@ import java.time.Instant;
import java.time.temporal.ChronoUnit;
import javax.inject.Inject;
import lombok.Getter;
-import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
+import net.runelite.api.GameState;
import net.runelite.api.SpriteID;
import net.runelite.api.VarPlayer;
import net.runelite.api.events.ConfigChanged;
import net.runelite.api.events.VarbitChanged;
+import net.runelite.client.callback.ClientThread;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.game.SpriteManager;
@@ -50,22 +51,36 @@ import net.runelite.client.ui.FontManager;
import net.runelite.client.ui.overlay.OverlayManager;
import net.runelite.client.ui.overlay.infobox.InfoBoxManager;
import net.runelite.client.util.ColorUtil;
+import net.runelite.client.util.ImageUtil;
@PluginDescriptor(
name = "Poison",
description = "Tracks current damage values for Poison and Venom",
- tags = {"combat", "poison", "venom"}
+ tags = {"combat", "poison", "venom", "heart", "hp"}
)
-@Slf4j
public class PoisonPlugin extends Plugin
{
static final int POISON_TICK_MILLIS = 18200;
private static final int VENOM_THRESHOLD = 1000000;
private static final int VENOM_MAXIUMUM_DAMAGE = 20;
+ private static final BufferedImage HEART_DISEASE;
+ private static final BufferedImage HEART_POISON;
+ private static final BufferedImage HEART_VENOM;
+
+ static
+ {
+ HEART_DISEASE = ImageUtil.resizeCanvas(ImageUtil.getResourceStreamFromClass(PoisonPlugin.class, "1067-DISEASE.png"), 26, 26);
+ HEART_POISON = ImageUtil.resizeCanvas(ImageUtil.getResourceStreamFromClass(PoisonPlugin.class, "1067-POISON.png"), 26, 26);
+ HEART_VENOM = ImageUtil.resizeCanvas(ImageUtil.getResourceStreamFromClass(PoisonPlugin.class, "1067-VENOM.png"), 26, 26);
+ }
+
@Inject
private Client client;
+ @Inject
+ private ClientThread clientThread;
+
@Inject
private PoisonOverlay poisonOverlay;
@@ -88,6 +103,8 @@ public class PoisonPlugin extends Plugin
private Instant poisonNaturalCure;
private Instant nextPoisonTick;
private int lastValue = -1;
+ private int lastDiseaseValue = -1;
+ private BufferedImage heart;
@Provides
PoisonConfig getConfig(ConfigManager configManager)
@@ -99,6 +116,11 @@ public class PoisonPlugin extends Plugin
protected void startUp() throws Exception
{
overlayManager.add(poisonOverlay);
+
+ if (client.getGameState() == GameState.LOGGED_IN)
+ {
+ clientThread.invoke(this::checkHealthIcon);
+ }
}
@Override
@@ -117,64 +139,87 @@ public class PoisonPlugin extends Plugin
poisonNaturalCure = null;
nextPoisonTick = null;
lastValue = -1;
+ lastDiseaseValue = -1;
+ clientThread.invoke(this::resetHealthIcon);
}
@Subscribe
public void onVarbitChanged(VarbitChanged event)
{
final int poisonValue = client.getVar(VarPlayer.POISON);
- if (poisonValue == lastValue)
+ if (poisonValue != lastValue)
{
- return;
- }
+ lastValue = poisonValue;
+ nextPoisonTick = Instant.now().plus(Duration.of(POISON_TICK_MILLIS, ChronoUnit.MILLIS));
- lastValue = poisonValue;
- nextPoisonTick = Instant.now().plus(Duration.of(POISON_TICK_MILLIS, ChronoUnit.MILLIS));
+ final int damage = nextDamage(poisonValue);
+ this.lastDamage = damage;
- final int damage = nextDamage(poisonValue);
- this.lastDamage = damage;
+ envenomed = poisonValue >= VENOM_THRESHOLD;
- envenomed = poisonValue >= VENOM_THRESHOLD;
-
- if (poisonValue < VENOM_THRESHOLD)
- {
- poisonNaturalCure = Instant.now().plus(Duration.of(POISON_TICK_MILLIS * poisonValue, ChronoUnit.MILLIS));
- }
- else
- {
- poisonNaturalCure = null;
- }
-
- if (config.showInfoboxes())
- {
- if (infobox != null)
+ if (poisonValue < VENOM_THRESHOLD)
{
- infoBoxManager.removeInfoBox(infobox);
- infobox = null;
+ poisonNaturalCure = Instant.now().plus(Duration.of(POISON_TICK_MILLIS * poisonValue, ChronoUnit.MILLIS));
+ }
+ else
+ {
+ poisonNaturalCure = null;
}
- if (damage > 0)
+ if (config.showInfoboxes())
{
- final BufferedImage image = getSplat(envenomed ? SpriteID.HITSPLAT_DARK_GREEN_VENOM : SpriteID.HITSPLAT_GREEN_POISON, damage);
-
- if (image != null)
+ if (infobox != null)
{
- infobox = new PoisonInfobox(image, this);
- infoBoxManager.addInfoBox(infobox);
+ infoBoxManager.removeInfoBox(infobox);
+ infobox = null;
+ }
+
+ if (damage > 0)
+ {
+ final BufferedImage image = getSplat(envenomed ? SpriteID.HITSPLAT_DARK_GREEN_VENOM : SpriteID.HITSPLAT_GREEN_POISON, damage);
+
+ if (image != null)
+ {
+ infobox = new PoisonInfobox(image, this);
+ infoBoxManager.addInfoBox(infobox);
+ }
}
}
+
+ checkHealthIcon();
+ }
+
+ final int diseaseValue = client.getVar(VarPlayer.DISEASE_VALUE);
+ if (diseaseValue != lastDiseaseValue)
+ {
+ lastDiseaseValue = diseaseValue;
+ checkHealthIcon();
}
}
@Subscribe
public void onConfigChanged(ConfigChanged event)
{
- if (event.getGroup().equals(PoisonConfig.GROUP) && !config.showInfoboxes() && infobox != null)
+ if (!event.getGroup().equals(PoisonConfig.GROUP))
+ {
+ return;
+ }
+
+ if (!config.showInfoboxes() && infobox != null)
{
infoBoxManager.removeInfoBox(infobox);
infobox = null;
}
+
+ if (config.changeHealthIcon())
+ {
+ clientThread.invoke(this::checkHealthIcon);
+ }
+ else
+ {
+ clientThread.invoke(this::resetHealthIcon);
+ }
}
private static int nextDamage(int poisonValue)
@@ -250,4 +295,53 @@ public class PoisonPlugin extends Plugin
return line1 + line2;
}
+
+ private void checkHealthIcon()
+ {
+ if (!config.changeHealthIcon())
+ {
+ return;
+ }
+
+ final BufferedImage newHeart;
+ final int poison = client.getVar(VarPlayer.IS_POISONED);
+
+ if (poison >= VENOM_THRESHOLD)
+ {
+ newHeart = HEART_VENOM;
+ }
+ else if (poison > 0)
+ {
+ newHeart = HEART_POISON;
+ }
+ else if (client.getVar(VarPlayer.DISEASE_VALUE) > 0)
+ {
+ newHeart = HEART_DISEASE;
+ }
+ else
+ {
+ resetHealthIcon();
+ return;
+ }
+
+ // Only update sprites when the heart icon actually changes
+ if (newHeart != heart)
+ {
+ heart = newHeart;
+ client.getWidgetSpriteCache().reset();
+ client.getSpriteOverrides().put(SpriteID.MINIMAP_ORB_HITPOINTS_ICON, ImageUtil.getImageSpritePixels(heart, client));
+ }
+ }
+
+ private void resetHealthIcon()
+ {
+ if (heart == null)
+ {
+ return;
+ }
+
+ client.getWidgetSpriteCache().reset();
+ client.getSpriteOverrides().remove(SpriteID.MINIMAP_ORB_HITPOINTS_ICON);
+ heart = null;
+ }
}
\ No newline at end of file
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/prayer/PrayerCounter.java b/runelite-client/src/main/java/net/runelite/client/plugins/prayer/PrayerCounter.java
index 546c9f98a8..c510dab82e 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/prayer/PrayerCounter.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/prayer/PrayerCounter.java
@@ -24,25 +24,32 @@
*/
package net.runelite.client.plugins.prayer;
+import java.awt.Color;
import lombok.Getter;
import net.runelite.client.plugins.Plugin;
-import net.runelite.client.ui.overlay.infobox.Counter;
+import net.runelite.client.ui.overlay.infobox.InfoBox;
-class PrayerCounter extends Counter
+class PrayerCounter extends InfoBox
{
@Getter
private final PrayerType prayerType;
PrayerCounter(Plugin plugin, PrayerType prayerType)
{
- super(null, plugin, "");
+ super(null, plugin);
this.prayerType = prayerType;
}
@Override
- public String toString()
+ public String getText()
{
- return "Counter{" + "prayer=" + prayerType.getName() + '}';
+ return null;
+ }
+
+ @Override
+ public Color getTextColor()
+ {
+ return null;
}
@Override
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsPlugin.java
index 72a386f5c7..d0c0d7c16c 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsPlugin.java
@@ -236,7 +236,7 @@ public class RaidsPlugin extends Plugin
{
if (timer != null)
{
- timer.timeFloor();
+ timer.timeOlm();
timer.setStopped(true);
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsTimer.java b/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsTimer.java
index 8b40b09e10..8df3087054 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsTimer.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsTimer.java
@@ -41,6 +41,7 @@ public class RaidsTimer extends InfoBox
private LocalTime time;
private LocalTime firstFloorTime;
private LocalTime secondFloorTime;
+ private LocalTime thirdFloorTime;
private LocalTime olmTime;
@Setter
@@ -66,14 +67,20 @@ public class RaidsTimer extends InfoBox
{
secondFloorTime = LocalTime.ofSecondOfDay(elapsed.getSeconds());
}
- else if (olmTime == null)
+ else if (thirdFloorTime == null)
{
- olmTime = LocalTime.ofSecondOfDay(elapsed.getSeconds());
+ thirdFloorTime = LocalTime.ofSecondOfDay(elapsed.getSeconds());
}
floorTime = Instant.now();
}
+ public void timeOlm()
+ {
+ Duration elapsed = Duration.between(floorTime, Instant.now());
+ olmTime = LocalTime.ofSecondOfDay(elapsed.getSeconds());
+ }
+
@Override
public String getText()
{
@@ -126,6 +133,12 @@ public class RaidsTimer extends InfoBox
builder.append(secondFloorTime.format(DateTimeFormatter.ofPattern("mm:ss")));
}
+ if (thirdFloorTime != null)
+ {
+ builder.append("Third floor: ");
+ builder.append(thirdFloorTime.format(DateTimeFormatter.ofPattern("mm:ss")));
+ }
+
if (olmTime != null)
{
builder.append("Olm: ");
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/runecraft/RunecraftPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/runecraft/RunecraftPlugin.java
index 14b53f9995..4662b95d5a 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/runecraft/RunecraftPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/runecraft/RunecraftPlugin.java
@@ -229,10 +229,17 @@ public class RunecraftPlugin extends Plugin
@Subscribe
public void onGameStateChanged(GameStateChanged event)
{
- if (event.getGameState() == GameState.LOADING)
+ GameState gameState = event.getGameState();
+ switch (gameState)
{
- abyssObjects.clear();
- darkMage = null;
+ case LOADING:
+ abyssObjects.clear();
+ break;
+ case CONNECTION_LOST:
+ case HOPPING:
+ case LOGIN_SCREEN:
+ darkMage = null;
+ break;
}
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ScreenMarkerRenderable.java b/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ScreenMarkerRenderable.java
index 0d44e01345..95b2da1207 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ScreenMarkerRenderable.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ScreenMarkerRenderable.java
@@ -28,6 +28,7 @@ import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Point;
+import java.awt.Rectangle;
import java.awt.Stroke;
import lombok.AccessLevel;
import lombok.Getter;
@@ -55,6 +56,9 @@ public class ScreenMarkerRenderable implements LayoutableRenderableEntity
@Setter(AccessLevel.PACKAGE)
private Stroke stroke;
+ @Getter
+ private final Rectangle bounds = new Rectangle();
+
@Override
public Dimension render(Graphics2D graphics)
{
@@ -72,6 +76,7 @@ public class ScreenMarkerRenderable implements LayoutableRenderableEntity
graphics.setColor(color);
graphics.setStroke(stroke);
graphics.drawRect(offset, offset, width - thickness, height - thickness);
+ bounds.setSize(preferredSize);
return preferredSize;
}
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotPlugin.java
index e4c6fd2ec9..ee78f3cc22 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotPlugin.java
@@ -145,6 +145,8 @@ public class ScreenshotPlugin extends Plugin
private Integer chambersOfXericNumber;
+ private Integer chambersOfXericChallengeNumber;
+
private Integer theatreOfBloodNumber;
private boolean shouldTakeScreenshot;
@@ -347,6 +349,16 @@ public class ScreenshotPlugin extends Plugin
}
}
+ if (chatMessage.startsWith("Your completed Chambers of Xeric Challenge Mode count is:"))
+ {
+ Matcher m = NUMBER_PATTERN.matcher(Text.removeTags(chatMessage));
+ if (m.find())
+ {
+ chambersOfXericChallengeNumber = Integer.valueOf(m.group());
+ return;
+ }
+ }
+
if (chatMessage.startsWith("Your completed Theatre of Blood count is:"))
{
Matcher m = NUMBER_PATTERN.matcher(Text.removeTags(chatMessage));
@@ -453,14 +465,22 @@ public class ScreenshotPlugin extends Plugin
}
case CHAMBERS_OF_XERIC_REWARD_GROUP_ID:
{
- if (chambersOfXericNumber == null)
+ if (chambersOfXericNumber != null)
+ {
+ fileName = "Chambers of Xeric(" + chambersOfXericNumber + ")";
+ chambersOfXericNumber = null;
+ break;
+ }
+ else if (chambersOfXericChallengeNumber != null)
+ {
+ fileName = "Chambers of Xeric Challenge Mode(" + chambersOfXericChallengeNumber + ")";
+ chambersOfXericChallengeNumber = null;
+ break;
+ }
+ else
{
return;
}
-
- fileName = "Chambers of Xeric(" + chambersOfXericNumber + ")";
- chambersOfXericNumber = null;
- break;
}
case THEATRE_OF_BLOOD_REWARD_GROUP_ID:
{
@@ -720,6 +740,12 @@ public class ScreenshotPlugin extends Plugin
return chambersOfXericNumber;
}
+ @VisibleForTesting
+ int getChambersOfXericChallengeNumber()
+ {
+ return chambersOfXericChallengeNumber;
+ }
+
@VisibleForTesting
int gettheatreOfBloodNumber()
{
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerPlugin.java
index d257fbd995..96a585e605 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerPlugin.java
@@ -40,7 +40,6 @@ import java.util.concurrent.ScheduledExecutorService;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.inject.Inject;
-import javax.swing.SwingUtilities;
import joptsimple.internal.Strings;
import lombok.AccessLevel;
import lombok.Getter;
@@ -48,7 +47,6 @@ import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.ChatMessageType;
import net.runelite.api.Client;
-import net.runelite.api.GameState;
import net.runelite.api.ItemID;
import net.runelite.api.MessageNode;
import net.runelite.api.NPC;
@@ -623,7 +621,7 @@ public class SlayerPlugin extends Plugin
// add and update counter, set timer
addCounter();
- counter.setText(String.valueOf(currentTask.getAmount()));
+ counter.setCount(currentTask.getAmount());
infoTimer = Instant.now();
}
@@ -696,7 +694,7 @@ public class SlayerPlugin extends Plugin
private void setTask(String name, int amt, int initAmt, boolean isNewAssignment)
{
- setTask(name, amt, initAmt, isNewAssignment,null);
+ setTask(name, amt, initAmt, isNewAssignment, null);
}
private void setTask(String name, int amt, int initAmt, boolean isNewAssignment, String location)
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerTaskPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerTaskPanel.java
index 7d73343bf7..1c3de05230 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerTaskPanel.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerTaskPanel.java
@@ -6,7 +6,6 @@ import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
-import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import javax.swing.Box;
@@ -15,7 +14,6 @@ import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
-import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.FontManager;
@@ -104,7 +102,8 @@ public class SlayerTaskPanel extends PluginPanel
playBtn.setIcon(PLAY);
playBtn.setToolTipText("Resume the current slayer task");
- playBtn.addMouseListener(new MouseAdapter() {
+ playBtn.addMouseListener(new MouseAdapter()
+ {
@Override
public void mousePressed(MouseEvent mouseEvent)
{
@@ -139,7 +138,8 @@ public class SlayerTaskPanel extends PluginPanel
pauseBtn.setIcon(PAUSE);
pauseBtn.setToolTipText("Pause the current slayer task");
- pauseBtn.addMouseListener(new MouseAdapter() {
+ pauseBtn.addMouseListener(new MouseAdapter()
+ {
@Override
public void mousePressed(MouseEvent mouseEvent)
{
@@ -300,7 +300,8 @@ public class SlayerTaskPanel extends PluginPanel
if (tasks.isEmpty() || isNewAssignment)
{
// new task so append it to the front of the list
- SwingUtilities.invokeLater(() -> {
+ SwingUtilities.invokeLater(() ->
+ {
TaskBox newBox = buildBox(slayerPlugin, tasksContainer, newData);
newBox.update(true, newData.isPaused(), newData);
});
@@ -321,7 +322,8 @@ public class SlayerTaskPanel extends PluginPanel
// so this previous task is invalid so delete it then add in the new actually
// correct task
- SwingUtilities.invokeLater(() -> {
+ SwingUtilities.invokeLater(() ->
+ {
tasksContainer.remove(tasks.get(0));
tasks.remove(0);
TaskBox newBox = buildBox(slayerPlugin, tasksContainer, newData);
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/TargetWeaknessOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/TargetWeaknessOverlay.java
index 1b8973fbce..4f60949a89 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/TargetWeaknessOverlay.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/TargetWeaknessOverlay.java
@@ -59,7 +59,7 @@ class TargetWeaknessOverlay extends Overlay
this.itemManager = itemManager;
this.npcManager = npcManager;
setPosition(OverlayPosition.DYNAMIC);
- setLayer(OverlayLayer.ABOVE_SCENE);
+ setLayer(OverlayLayer.UNDER_WIDGETS);
}
@Override
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/TaskBox.java b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/TaskBox.java
index 79d44b2d53..443dccb453 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/TaskBox.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/TaskBox.java
@@ -14,7 +14,6 @@ import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder;
import lombok.AccessLevel;
import lombok.Getter;
-import net.runelite.client.game.AsyncBufferedImage;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.DynamicGridLayout;
import net.runelite.client.ui.FontManager;
@@ -78,7 +77,8 @@ public class TaskBox extends JPanel
container.setLayout(new BorderLayout());
container.setBackground(ColorScheme.DARKER_GRAY_COLOR);
- SwingUtilities.invokeLater(() -> {
+ SwingUtilities.invokeLater(() ->
+ {
BufferedImage taskImg = slayerPlugin.getImageForTask(Task.getTask(taskData.getTaskName()));
JLabel taskIcon = new JLabel(new ImageIcon(taskImg));
taskIcon.setHorizontalAlignment(SwingConstants.CENTER);
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/TaskCounter.java b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/TaskCounter.java
index f4ac0045b9..0d4274c063 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/TaskCounter.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/TaskCounter.java
@@ -29,10 +29,10 @@ import net.runelite.client.ui.overlay.infobox.Counter;
import java.awt.image.BufferedImage;
-public class TaskCounter extends Counter
+class TaskCounter extends Counter
{
- public TaskCounter(BufferedImage img, Plugin plugin, int amount)
+ TaskCounter(BufferedImage img, Plugin plugin, int amount)
{
- super(img, plugin, String.valueOf(amount));
+ super(img, plugin, amount);
}
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/TaskData.java b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/TaskData.java
index 17e43d8be3..0e3784e687 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/TaskData.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/TaskData.java
@@ -6,7 +6,7 @@ import lombok.Data;
@Data
@AllArgsConstructor
-@Builder(toBuilder=true)
+@Builder(toBuilder = true)
public class TaskData
{
private long elapsedTime;
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounter.java b/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounter.java
index 083ef5bba6..3c7b0f2135 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounter.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounter.java
@@ -29,30 +29,24 @@ import net.runelite.client.ui.overlay.infobox.Counter;
class SpecialCounter extends Counter
{
- private int hitValue;
private SpecialWeapon weapon;
SpecialCounter(BufferedImage image, SpecialCounterPlugin plugin, int hitValue, SpecialWeapon weapon)
{
- super(image, plugin, null);
+ super(image, plugin, hitValue);
this.weapon = weapon;
- this.hitValue = hitValue;
}
void addHits(double hit)
{
- this.hitValue += hit;
- }
-
- @Override
- public String getText()
- {
- return Integer.toString(hitValue);
+ int count = getCount();
+ setCount(count + (int) hit);
}
@Override
public String getTooltip()
{
+ int hitValue = getCount();
if (!weapon.isDamage())
{
if (hitValue == 1)
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/statusbars/StatusBarsOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/statusbars/StatusBarsOverlay.java
index 0deb55c2ab..ada42169ad 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/statusbars/StatusBarsOverlay.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/statusbars/StatusBarsOverlay.java
@@ -24,8 +24,6 @@
*/
package net.runelite.client.plugins.statusbars;
-import com.google.common.base.Strings;
-import com.google.common.primitives.Ints;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
@@ -196,28 +194,16 @@ class StatusBarsOverlay extends Overlay
for (final StatChange c : statsChanges.getStatChanges())
{
- final String strVar = c.getTheoretical();
-
- if (Strings.isNullOrEmpty(strVar))
- {
- continue;
- }
-
- final Integer value = Ints.tryParse(strVar.startsWith("+") ? strVar.substring(1) : strVar);
-
- if (value == null)
- {
- continue;
- }
+ final int theoreticalBoost = c.getTheoretical();
if (c.getStat().getName().equals(Skill.HITPOINTS.getName()))
{
- foodHealValue = value;
+ foodHealValue = theoreticalBoost;
}
if (c.getStat().getName().equals(Skill.PRAYER.getName()))
{
- prayerHealValue = value;
+ prayerHealValue = theoreticalBoost;
}
if (foodHealValue != 0 && prayerHealValue != 0)
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/tileindicators/TileIndicatorsConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/tileindicators/TileIndicatorsConfig.java
index 9d9cd96e69..9a5e0bad33 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/tileindicators/TileIndicatorsConfig.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/tileindicators/TileIndicatorsConfig.java
@@ -54,6 +54,17 @@ public interface TileIndicatorsConfig extends Config
return true;
}
+ @Alpha
+ @ConfigItem(
+ keyName = "highlightHoveredColor",
+ name = "Color of current hovered highlighting",
+ description = "Configures the highlight color of hovered tile"
+ )
+ default Color highlightHoveredColor()
+ {
+ return new Color(0, 0, 0, 0);
+ }
+
@ConfigItem(
keyName = "highlightHoveredTile",
name = "Highlight hovered tile",
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/tileindicators/TileIndicatorsOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/tileindicators/TileIndicatorsOverlay.java
index f92a76f6de..a9e983656d 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/tileindicators/TileIndicatorsOverlay.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/tileindicators/TileIndicatorsOverlay.java
@@ -41,7 +41,6 @@ import net.runelite.client.ui.overlay.OverlayUtil;
public class TileIndicatorsOverlay extends Overlay
{
- private static final Color EMPTY = new Color(0, 0, 0, 0);
private final Client client;
private final TileIndicatorsConfig config;
@@ -75,7 +74,7 @@ public class TileIndicatorsOverlay extends Overlay
// If we have tile "selected" render it
if (client.getSelectedSceneTile() != null)
{
- renderTile(graphics, client.getSelectedSceneTile().getLocalLocation(), EMPTY);
+ renderTile(graphics, client.getSelectedSceneTile().getLocalLocation(), config.highlightHoveredColor());
}
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/wiki/WikiPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/wiki/WikiPlugin.java
new file mode 100644
index 0000000000..44146c4cb5
--- /dev/null
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/wiki/WikiPlugin.java
@@ -0,0 +1,314 @@
+/*
+ * Copyright (c) 2018 Abex
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package net.runelite.client.plugins.wiki;
+
+import com.google.common.primitives.Ints;
+import java.net.URLEncoder;
+import java.util.Arrays;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.inject.Inject;
+import javax.inject.Provider;
+import lombok.extern.slf4j.Slf4j;
+import net.runelite.api.Client;
+import net.runelite.api.MenuAction;
+import net.runelite.api.MenuEntry;
+import net.runelite.api.NPC;
+import net.runelite.api.NPCComposition;
+import net.runelite.api.events.MenuEntryAdded;
+import net.runelite.api.events.MenuOptionClicked;
+import net.runelite.api.events.WidgetLoaded;
+import net.runelite.api.widgets.JavaScriptCallback;
+import net.runelite.api.widgets.Widget;
+import net.runelite.api.widgets.WidgetConfig;
+import net.runelite.api.widgets.WidgetID;
+import net.runelite.api.widgets.WidgetInfo;
+import net.runelite.api.widgets.WidgetPositionMode;
+import net.runelite.api.widgets.WidgetType;
+import net.runelite.client.callback.ClientThread;
+import net.runelite.client.eventbus.Subscribe;
+import net.runelite.client.game.ItemManager;
+import net.runelite.client.game.SpriteManager;
+import net.runelite.client.game.chatbox.ChatboxPanelManager;
+import net.runelite.client.plugins.Plugin;
+import net.runelite.client.plugins.PluginDescriptor;
+import net.runelite.client.util.LinkBrowser;
+import net.runelite.client.util.Text;
+import okhttp3.HttpUrl;
+
+@Slf4j
+@PluginDescriptor(
+ name = "Wiki",
+ description = "Adds a Wiki button that takes you to the OSRS Wiki"
+)
+public class WikiPlugin extends Plugin
+{
+ private static final int[] QUESTLIST_WIDGET_IDS = new int[]
+ {
+ WidgetInfo.QUESTLIST_FREE_CONTAINER.getId(),
+ WidgetInfo.QUESTLIST_MEMBERS_CONTAINER.getId(),
+ WidgetInfo.QUESTLIST_MINIQUEST_CONTAINER.getId(),
+ };
+
+ static final String WIKI_BASE = "https://oldschool.runescape.wiki";
+ static final HttpUrl WIKI_RSLOOKUP = HttpUrl.parse(WIKI_BASE + "/w/Special:Lookup");
+ static final HttpUrl WIKI_API = HttpUrl.parse(WIKI_BASE + "/api.php");
+ static final String UTM_SORUCE_KEY = "utm_source";
+ static final String UTM_SORUCE_VALUE = "runelite";
+ static final String UTM_PARAMS = UTM_SORUCE_KEY + "=" + UTM_SORUCE_VALUE;
+
+ private static final String MENUOP_GUIDE = "Guide";
+ private static final String MENUOP_QUICKGUIDE = "Quick Guide";
+ private static final String MENUOP_WIKI_SKILL = "Wiki";
+
+ private static final Pattern SKILL_REGEX = Pattern.compile("([A-Za-z]+) guide");
+
+ @Inject
+ private SpriteManager spriteManager;
+
+ @Inject
+ private ClientThread clientThread;
+
+ @Inject
+ private Client client;
+
+ @Inject
+ private ChatboxPanelManager chatboxPanelManager;
+
+ @Inject
+ private ItemManager itemManager;
+
+ @Inject
+ private Provider wikiSearchChatboxTextInputProvider;
+
+ private Widget icon;
+
+ private boolean wikiSelected = false;
+
+ @Override
+ public void startUp()
+ {
+ spriteManager.addSpriteOverrides(WikiSprite.values());
+ clientThread.invokeLater(this::addWidgets);
+ }
+
+ @Override
+ public void shutDown()
+ {
+ spriteManager.removeSpriteOverrides(WikiSprite.values());
+ clientThread.invokeLater(() ->
+ {
+ Widget minimapOrbs = client.getWidget(WidgetInfo.MINIMAP_ORBS);
+ if (minimapOrbs == null)
+ {
+ return;
+ }
+ Widget[] children = minimapOrbs.getChildren();
+ if (children == null || children.length < 1)
+ {
+ return;
+ }
+ children[0] = null;
+ });
+ }
+
+ @Subscribe
+ private void onWidgetLoaded(WidgetLoaded l)
+ {
+ if (l.getGroupId() == WidgetID.MINIMAP_GROUP_ID)
+ {
+ addWidgets();
+ }
+ }
+
+ private void addWidgets()
+ {
+ Widget minimapOrbs = client.getWidget(WidgetInfo.MINIMAP_ORBS);
+ if (minimapOrbs == null)
+ {
+ return;
+ }
+
+ icon = minimapOrbs.createChild(0, WidgetType.GRAPHIC);
+ icon.setSpriteId(WikiSprite.WIKI_ICON.getSpriteId());
+ icon.setOriginalX(0);
+ icon.setOriginalY(2);
+ icon.setXPositionMode(WidgetPositionMode.ABSOLUTE_RIGHT);
+ icon.setYPositionMode(WidgetPositionMode.ABSOLUTE_BOTTOM);
+ icon.setOriginalWidth(42);
+ icon.setOriginalHeight(16);
+ icon.setTargetVerb("Lookup");
+ icon.setName("Wiki");
+ icon.setClickMask(WidgetConfig.USE_GROUND_ITEM | WidgetConfig.USE_ITEM | WidgetConfig.USE_NPC);
+ icon.setNoClickThrough(true);
+ icon.setOnTargetEnterListener((JavaScriptCallback) ev ->
+ {
+ wikiSelected = true;
+ icon.setSpriteId(WikiSprite.WIKI_SELECTED_ICON.getSpriteId());
+ });
+ icon.setAction(5, "Search"); // Start at option 5 so the target op is ontop
+ icon.setOnOpListener((JavaScriptCallback) ev ->
+ {
+ switch (ev.getOp())
+ {
+ case 6:
+ openSearchInput();
+ break;
+ }
+ });
+ // This doesn't always run because we cancel the menuop
+ icon.setOnTargetLeaveListener((JavaScriptCallback) ev -> onDeselect());
+ icon.revalidate();
+ }
+
+ private void onDeselect()
+ {
+ wikiSelected = false;
+ icon.setSpriteId(WikiSprite.WIKI_ICON.getSpriteId());
+ }
+
+ @Subscribe
+ private void onMenuOptionClicked(MenuOptionClicked ev)
+ {
+ if (wikiSelected)
+ {
+ onDeselect();
+ client.setSpellSelected(false);
+ ev.consume();
+
+ String type;
+ int id;
+ String name;
+ switch (ev.getMenuAction())
+ {
+ case CANCEL:
+ return;
+ case ITEM_USE_ON_WIDGET:
+ case SPELL_CAST_ON_GROUND_ITEM:
+ {
+ type = "item";
+ id = itemManager.canonicalize(ev.getId());
+ name = itemManager.getItemComposition(id).getName();
+ break;
+ }
+ case SPELL_CAST_ON_NPC:
+ {
+ type = "npc";
+ NPC npc = client.getCachedNPCs()[ev.getId()];
+ NPCComposition nc = npc.getTransformedComposition();
+ id = nc.getId();
+ name = nc.getName();
+ break;
+ }
+ default:
+ log.info("Unknown menu option: {} {} {}", ev, ev.getMenuAction(), ev.getMenuAction() == MenuAction.CANCEL);
+ return;
+ }
+
+ HttpUrl url = WIKI_RSLOOKUP.newBuilder()
+ .addQueryParameter("type", type)
+ .addQueryParameter("id", "" + id)
+ .addQueryParameter("name", name)
+ .addQueryParameter(UTM_SORUCE_KEY, UTM_SORUCE_VALUE)
+ .build();
+
+ LinkBrowser.browse(url.toString());
+ return;
+ }
+
+ if (ev.getMenuAction() == MenuAction.RUNELITE)
+ {
+ String quickguide = "";
+ switch (ev.getMenuOption())
+ {
+ case MENUOP_QUICKGUIDE:
+ quickguide = "/Quick_guide";
+ //fallthrough;
+ case MENUOP_GUIDE:
+ ev.consume();
+ String quest = Text.removeTags(ev.getMenuTarget());
+ LinkBrowser.browse(WIKI_BASE + "/w/" + URLEncoder.encode(quest.replace(' ', '_')) + quickguide + "?" + UTM_PARAMS);
+ break;
+ case MENUOP_WIKI_SKILL:
+ Matcher skillRegex = WikiPlugin.SKILL_REGEX.matcher(Text.removeTags(ev.getMenuTarget()));
+
+ if (skillRegex.find())
+ {
+ LinkBrowser.browse(WIKI_BASE + "/w/" + URLEncoder.encode(skillRegex.group(1)) + "?" + UTM_PARAMS);
+ }
+ }
+ }
+ }
+
+ private void openSearchInput()
+ {
+ wikiSearchChatboxTextInputProvider.get()
+ .build();
+ }
+
+ @Subscribe
+ public void onMenuEntryAdded(MenuEntryAdded event)
+ {
+ int widgetIndex = event.getActionParam0();
+ int widgetID = event.getActionParam1();
+ MenuEntry[] menuEntries = client.getMenuEntries();
+
+ if (Ints.contains(QUESTLIST_WIDGET_IDS, widgetID) && "Read Journal:".equals(event.getOption()))
+ {
+ menuEntries = Arrays.copyOf(menuEntries, menuEntries.length + 2);
+
+ MenuEntry menuEntry = menuEntries[menuEntries.length - 1] = new MenuEntry();
+ menuEntry.setTarget(event.getTarget());
+ menuEntry.setOption(MENUOP_GUIDE);
+ menuEntry.setParam0(widgetIndex);
+ menuEntry.setParam1(widgetID);
+ menuEntry.setType(MenuAction.RUNELITE.getId());
+
+ menuEntry = menuEntries[menuEntries.length - 2] = new MenuEntry();
+ menuEntry.setTarget(event.getTarget());
+ menuEntry.setOption(MENUOP_QUICKGUIDE);
+ menuEntry.setParam0(widgetIndex);
+ menuEntry.setParam1(widgetID);
+ menuEntry.setType(MenuAction.RUNELITE.getId());
+
+ client.setMenuEntries(menuEntries);
+ }
+
+ if ((WidgetInfo.TO_GROUP(widgetID) == WidgetID.SKILLS_GROUP_ID) && event.getOption().startsWith("View"))
+ {
+ menuEntries = Arrays.copyOf(menuEntries, menuEntries.length + 1);
+
+ MenuEntry menuEntry = menuEntries[menuEntries.length - 1] = new MenuEntry();
+ menuEntry.setTarget(event.getOption().replace("View ", ""));
+ menuEntry.setOption(MENUOP_WIKI_SKILL);
+ menuEntry.setParam0(widgetIndex);
+ menuEntry.setParam1(widgetID);
+ menuEntry.setIdentifier(event.getIdentifier());
+ menuEntry.setType(MenuAction.RUNELITE.getId());
+
+ client.setMenuEntries(menuEntries);
+ }
+ }
+}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/wiki/WikiSearchChatboxTextInput.java b/runelite-client/src/main/java/net/runelite/client/plugins/wiki/WikiSearchChatboxTextInput.java
new file mode 100644
index 0000000000..cc68aef20b
--- /dev/null
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/wiki/WikiSearchChatboxTextInput.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright (c) 2018 Abex
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package net.runelite.client.plugins.wiki;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.reflect.TypeToken;
+import com.google.gson.Gson;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonParseException;
+import com.google.gson.JsonParser;
+import com.google.inject.Inject;
+import java.awt.event.KeyEvent;
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.util.List;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import javax.inject.Named;
+import lombok.extern.slf4j.Slf4j;
+import net.runelite.api.widgets.JavaScriptCallback;
+import net.runelite.api.widgets.Widget;
+import net.runelite.api.widgets.WidgetPositionMode;
+import net.runelite.api.widgets.WidgetSizeMode;
+import net.runelite.api.widgets.WidgetTextAlignment;
+import net.runelite.api.widgets.WidgetType;
+import net.runelite.client.callback.ClientThread;
+import net.runelite.client.game.chatbox.ChatboxPanelManager;
+import net.runelite.client.game.chatbox.ChatboxTextInput;
+import net.runelite.client.util.LinkBrowser;
+import net.runelite.http.api.RuneLiteAPI;
+import okhttp3.Call;
+import okhttp3.Callback;
+import okhttp3.HttpUrl;
+import okhttp3.Request;
+import okhttp3.Response;
+
+@Slf4j
+public class WikiSearchChatboxTextInput extends ChatboxTextInput
+{
+ private static final int LINE_HEIGHT = 20;
+ private static final int CHATBOX_HEIGHT = 120;
+ private static final int MAX_NUM_PREDICTIONS = (CHATBOX_HEIGHT / LINE_HEIGHT) - 2; // 1 title, 1 edit
+
+ private static final int PREDICTION_DEBOUNCE_DELAY_MS = 200;
+
+ private final ChatboxPanelManager chatboxPanelManager;
+ private final Gson gson = new Gson();
+
+ private Future> runningRequest = null;
+ private List predictions = ImmutableList.of();
+
+ private int selectedPrediction = -1;
+ private String offPrediction = null;
+
+ @Inject
+ public WikiSearchChatboxTextInput(ChatboxPanelManager chatboxPanelManager, ClientThread clientThread,
+ ScheduledExecutorService scheduledExecutorService, @Named("developerMode") final boolean developerMode)
+ {
+ super(chatboxPanelManager, clientThread);
+ this.chatboxPanelManager = chatboxPanelManager;
+
+ lines(1);
+ prompt("OSRS Wiki Search");
+ onDone(string ->
+ {
+ if (string != null && string.length() > 0)
+ {
+ search(string);
+ }
+ });
+ onChanged(searchString ->
+ {
+ selectedPrediction = -1;
+ Future> rr = runningRequest;
+ if (rr != null)
+ {
+ rr.cancel(false);
+ }
+ if (searchString.length() <= 1)
+ {
+ runningRequest = null;
+ clientThread.invokeLater(() ->
+ {
+ predictions = ImmutableList.of();
+ update();
+ });
+ return;
+ }
+ runningRequest = scheduledExecutorService.schedule(() ->
+ {
+ HttpUrl url = WikiPlugin.WIKI_API.newBuilder()
+ .addQueryParameter("action", "opensearch")
+ .addQueryParameter("search", searchString)
+ .addQueryParameter("redirects", "resolve")
+ .addQueryParameter("format", "json")
+ .addQueryParameter("warningsaserror", Boolean.toString(developerMode))
+ .build();
+
+ Request req = new Request.Builder()
+ .url(url)
+ .build();
+
+ RuneLiteAPI.CLIENT.newCall(req).enqueue(new Callback()
+ {
+ @Override
+ public void onFailure(Call call, IOException e)
+ {
+ log.warn("error searching wiki", e);
+ }
+
+ @Override
+ public void onResponse(Call call, Response response) throws IOException
+ {
+ String body = response.body().string();
+ try
+ {
+ JsonArray jar = new JsonParser().parse(body).getAsJsonArray();
+ List apredictions = gson.fromJson(jar.get(1), new TypeToken>()
+ {
+ }.getType());
+
+ if (apredictions.size() > MAX_NUM_PREDICTIONS)
+ {
+ apredictions = apredictions.subList(0, MAX_NUM_PREDICTIONS);
+ }
+
+ final List bpredictions = apredictions;
+
+ clientThread.invokeLater(() ->
+ {
+ predictions = bpredictions;
+ update();
+ });
+ }
+ catch (JsonParseException | IllegalStateException | IndexOutOfBoundsException e)
+ {
+ log.warn("error parsing wiki response {}", body, e);
+ }
+ finally
+ {
+ response.close();
+ }
+ }
+ });
+
+ runningRequest = null;
+ }, PREDICTION_DEBOUNCE_DELAY_MS, TimeUnit.MILLISECONDS);
+ });
+ }
+
+ @Override
+ protected void update()
+ {
+ Widget container = chatboxPanelManager.getContainerWidget();
+ container.deleteAllChildren();
+
+ Widget promptWidget = container.createChild(-1, WidgetType.TEXT);
+ promptWidget.setText(getPrompt());
+ promptWidget.setTextColor(0x800000);
+ promptWidget.setFontId(getFontID());
+ promptWidget.setXPositionMode(WidgetPositionMode.ABSOLUTE_CENTER);
+ promptWidget.setOriginalX(0);
+ promptWidget.setYPositionMode(WidgetPositionMode.ABSOLUTE_TOP);
+ promptWidget.setOriginalY(5);
+ promptWidget.setOriginalHeight(LINE_HEIGHT);
+ promptWidget.setXTextAlignment(WidgetTextAlignment.CENTER);
+ promptWidget.setYTextAlignment(WidgetTextAlignment.CENTER);
+ promptWidget.setWidthMode(WidgetSizeMode.MINUS);
+ promptWidget.revalidate();
+
+ buildEdit(0, 5 + LINE_HEIGHT, container.getWidth(), LINE_HEIGHT);
+
+ Widget separator = container.createChild(-1, WidgetType.LINE);
+ separator.setXPositionMode(WidgetPositionMode.ABSOLUTE_CENTER);
+ separator.setOriginalX(0);
+ separator.setYPositionMode(WidgetPositionMode.ABSOLUTE_TOP);
+ separator.setOriginalY(4 + (LINE_HEIGHT * 2));
+ separator.setOriginalHeight(0);
+ separator.setOriginalWidth(16);
+ separator.setWidthMode(WidgetSizeMode.MINUS);
+ separator.revalidate();
+
+ for (int i = 0; i < predictions.size(); i++)
+ {
+ String pred = predictions.get(i);
+ int y = 6 + (LINE_HEIGHT * (2 + i));
+
+ Widget bg = container.createChild(-1, WidgetType.RECTANGLE);
+ bg.setTextColor(0x4444DD);
+ bg.setFilled(true);
+ bg.setXPositionMode(WidgetPositionMode.ABSOLUTE_CENTER);
+ bg.setOriginalX(1);
+ bg.setYPositionMode(WidgetPositionMode.ABSOLUTE_TOP);
+ bg.setOriginalY(y);
+ bg.setOriginalHeight(LINE_HEIGHT);
+ bg.setOriginalWidth(16);
+ bg.setWidthMode(WidgetSizeMode.MINUS);
+ bg.revalidate();
+ bg.setName("" + pred);
+ bg.setAction(0, "Open");
+ bg.setHasListener(true);
+ bg.setOnOpListener((JavaScriptCallback) ev -> search(pred));
+
+ Widget text = container.createChild(-1, WidgetType.TEXT);
+ text.setText(pred);
+ text.setFontId(getFontID());
+ text.setXPositionMode(WidgetPositionMode.ABSOLUTE_CENTER);
+ text.setOriginalX(0);
+ text.setYPositionMode(WidgetPositionMode.ABSOLUTE_TOP);
+ text.setOriginalY(y);
+ text.setOriginalHeight(LINE_HEIGHT);
+ text.setXTextAlignment(WidgetTextAlignment.CENTER);
+ text.setYTextAlignment(WidgetTextAlignment.CENTER);
+ text.setWidthMode(WidgetSizeMode.MINUS);
+ text.revalidate();
+
+ if (i == selectedPrediction)
+ {
+ text.setTextColor(0xFFFFFF);
+ }
+ else
+ {
+ bg.setOpacity(255);
+ text.setTextColor(0x000000);
+ bg.setOnMouseRepeatListener((JavaScriptCallback) ev -> text.setTextColor(0xFFFFFF));
+ bg.setOnMouseLeaveListener((JavaScriptCallback) ev -> text.setTextColor(0x000000));
+ }
+ }
+ }
+
+ @Override
+ public void keyPressed(KeyEvent ev)
+ {
+ switch (ev.getKeyCode())
+ {
+ case KeyEvent.VK_UP:
+ ev.consume();
+ if (selectedPrediction > -1)
+ {
+ selectedPrediction--;
+ if (selectedPrediction == -1)
+ {
+ value(offPrediction);
+ }
+ else
+ {
+ value(predictions.get(selectedPrediction));
+ }
+ }
+ break;
+ case KeyEvent.VK_DOWN:
+ ev.consume();
+
+ if (selectedPrediction == -1)
+ {
+ offPrediction = getValue();
+ }
+
+ selectedPrediction++;
+ if (selectedPrediction >= predictions.size())
+ {
+ selectedPrediction = predictions.size() - 1;
+ }
+
+ if (selectedPrediction != -1)
+ {
+ value(predictions.get(selectedPrediction));
+ }
+ break;
+ default:
+ super.keyPressed(ev);
+ }
+ }
+
+ private void search(String search)
+ {
+ LinkBrowser.browse(WikiPlugin.WIKI_BASE + "?search=" + URLEncoder.encode(search) + "&" + WikiPlugin.UTM_PARAMS);
+ chatboxPanelManager.close();
+ }
+}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/wiki/WikiSprite.java b/runelite-client/src/main/java/net/runelite/client/plugins/wiki/WikiSprite.java
new file mode 100644
index 0000000000..188d54837b
--- /dev/null
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/wiki/WikiSprite.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2018 Abex
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package net.runelite.client.plugins.wiki;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import net.runelite.api.SpriteID;
+import net.runelite.client.game.SpriteOverride;
+
+@RequiredArgsConstructor
+public enum WikiSprite implements SpriteOverride
+{
+ WIKI_ICON(-300, "wiki.png"),
+ WIKI_SELECTED_ICON(-301, "wiki_selected.png"),
+ FIXED_MODE_MINIMAP_CLICKMASK(SpriteID.MINIMAP_CLICK_MASK, "fixed_mode_minimap_clickmask.png");
+
+ @Getter
+ private final int spriteId;
+
+ @Getter
+ private final String fileName;
+}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/AgilityShortcutLocation.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/AgilityShortcutLocation.java
deleted file mode 100644
index dd2e3771b6..0000000000
--- a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/AgilityShortcutLocation.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright (c) 2018, Morgan Lewis
- * 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.
- */
-package net.runelite.client.plugins.worldmap;
-
-import lombok.Getter;
-import net.runelite.api.coords.WorldPoint;
-
-@Getter
-enum AgilityShortcutLocation
-{
- KARAMJA_GLIDER_LOG("Log Balance", 1, new WorldPoint(2906, 3050, 0)),
- FALADOR_CRUMBLING_WALL("Crumbling Wall", 5, new WorldPoint(2936, 3357, 0)),
- RIVER_LUM_GRAPPLE_WEST("Grapple Broken Raft", 8, new WorldPoint(3245, 3179, 0)),
- RIVER_LUM_GRAPPLE_EAST("Grapple Broken Raft", 8, new WorldPoint(3258, 3179, 0)),
- CORSAIR_COVE_ROCKS("Rocks", 10, new WorldPoint(2545, 2871, 0)),
- FALADOR_GRAPPLE_WALL("Grapple Wall", 11, new WorldPoint(3031, 3391, 0)),
- VARROCK_SOUTH_FENCE("Fence", 13, new WorldPoint(3239, 3334, 0)),
- GOBLIN_VILLAGE_WALL("Wall", 14, new WorldPoint(2925, 3523, 0)),
- CORSAIR_COVE_DUNGEON_PILLAR("Pillar Jump", 15, new WorldPoint(1980, 8996, 0)),
- YANILLE_UNDERWALL_TUNNEL("Underwall Tunnel", 16, new WorldPoint(2574, 3109, 0)),
- COAL_TRUCKS_LOG_BALANCE("Log Balance", 20, new WorldPoint(2598, 3475, 0)),
- GRAND_EXCHANGE_UNDERWALL_TUNNEL("Underwall Tunnel", 21, new WorldPoint(3139, 3515, 0)),
- BRIMHAVEN_DUNGEON_PIPE("Pipe Squeeze", 22, new WorldPoint(2654, 9569, 0)),
- OBSERVATORY_SCALE_CLIFF("Grapple Rocks", 23, new WorldPoint(2447, 3155, 0)),
- EAGLES_PEAK_ROCK_CLIMB("Rock Climb", 25, new WorldPoint(2320, 3499, 0)),
- FALADOR_UNDERWALL_TUNNEL("Underwall Tunnel", 26, new WorldPoint(2947, 3313, 0)),
- MOUNT_KARUULM_LOWER("Rocks", 29, new WorldPoint(1324, 3782, 0)),
- CORSAIR_COVE_RESOURCE_ROCKS("Rocks", 30, new WorldPoint(2545, 2871, 0)),
- SOUTHEAST_KARAJMA_STEPPING_STONES("Stepping Stones", 30, new WorldPoint(2924, 2946, 0)),
- DRAYNOR_MANOR_STEPPING_STONES("Stepping Stones", 31, new WorldPoint(3150, 3362, 0)),
- CATHERBY_CLIFFSIDE_GRAPPLE("Grapple Rock", 32, new WorldPoint(2868, 3429, 0)),
- ARDOUGNE_LOG_BALANCE("Log Balance", 33, new WorldPoint(2602, 3336, 0)),
- GNOME_STRONGHOLD_ROCKS("Rocks", 37, new WorldPoint(2485, 3515, 0)),
- AL_KHARID_MINING_PITCLIFF_SCRAMBLE("Rocks", 38, new WorldPoint(3305, 3315, 0)),
- YANILLE_WALL_GRAPPLE("Grapple Wall", 39, new WorldPoint(2552, 3072, 0)),
- NEITIZNOT_BRIDGE_REPAIR("Bridge Repair - Quest", 40, new WorldPoint(2315, 3828, 0)),
- KOUREND_LAKE_JUMP_WEST("Stepping Stones", 40, new WorldPoint(1604, 3572, 0)),
- KOUREND_LAKE_JUMP_EAST("Stepping Stones", 40, new WorldPoint(1612, 3570, 0)),
- TROLLHEIM_EASY_CLIFF_SCRAMBLE("Rocks", 41, new WorldPoint(2869, 3670, 0)),
- DWARVEN_MINE_NARROW_CREVICE("Narrow Crevice", 42, new WorldPoint(3034, 9806, 0)),
- DRAYNOR_UNDERWALL_TUNNEL("Underwall Tunnel", 42, new WorldPoint(3068, 3261, 0)),
- TROLLHEIM_MEDIUM_CLIFF_SCRAMBLE_NORTH("Rocks", 43, new WorldPoint(2886, 3684, 0)),
- TROLLHEIM_MEDIUM_CLIFF_SCRAMBLE_SOUTH("Rocks", 43, new WorldPoint(2876, 3666, 0)),
- TROLLHEIM_ADVANCED_CLIFF_SCRAMBLE("Rocks", 44, new WorldPoint(2907, 3686, 0)),
- KOUREND_RIVER_STEPPING_STONES("Stepping Stones", 45, new WorldPoint(1721, 3509, 0)),
- COSMIC_ALTAR_MEDIUM_WALKWAY("Narrow Walkway", 46, new WorldPoint(2399, 4403, 0)),
- DEEP_WILDERNESS_DUNGEON_CREVICE_NORTH("Narrow Crevice", 46, new WorldPoint(3047, 10335, 0)),
- DEEP_WILDERNESS_DUNGEON_CREVICE_SOUTH("Narrow Crevice", 46, new WorldPoint(3045, 10327, 0)),
- TROLLHEIM_HARD_CLIFF_SCRAMBLE("Rocks", 47, new WorldPoint(2902, 3680, 0)),
- FREMENNIK_LOG_BALANCE("Log Balance", 48, new WorldPoint(2721, 3591, 0)),
- ARCEUUS_ESSENCE_MINE_BOULDER("Boulder", 49, new WorldPoint(1774, 3888, 0)),
- MORYTANIA_STEPPING_STONE("Stepping Stone", 50, new WorldPoint(3418, 3326, 0)),
- VARROCK_SEWERS_PIPE_SQUEEZE("Pipe Squeeze", 51, new WorldPoint(3152, 9905, 0)),
- ARCEUUS_ESSENCE_MINE_EAST_SCRAMBLE("Rock Climb", 52, new WorldPoint(1770, 3851, 0)),
- KARAMJA_VOLCANO_GRAPPLE_NORTH("Grapple Rock", 53, new WorldPoint(2873, 3143, 0)),
- KARAMJA_VOLCANO_GRAPPLE_SOUTH("Grapple Rock", 53, new WorldPoint(2874, 3128, 0)),
- MOTHERLODE_MINE_WALL_WEST("Wall", 54, new WorldPoint(3118, 9702, 0)),
- MOTHERLODE_MINE_WALL_EAST("Wall", 54, new WorldPoint(3124, 9703, 0)),
- MISCELLANIA_DOCK_STEPPING_STONE("Stepping Stone", 55, new WorldPoint(2572, 3862, 0)),
- RELEKKA_EAST_FENCE("Fence", 57, new WorldPoint(2688, 3697, 0)),
- ELVEN_OVERPASS_CLIFF_SCRAMBLE("Rocks", 59, new WorldPoint(2345, 3300, 0)),
- WILDERNESS_GWD_CLIMB_WEST("Rocks", 60, new WorldPoint(2928, 3760, 0)),
- WILDERNESS_GWD_CLIMB_EAST("Rocks", 60, new WorldPoint(2943, 3770, 0)),
- MOS_LEHARMLESS_STEPPING_STONE("Stepping Stone", 60, new WorldPoint(3710, 2970, 0)),
- WINTERTODT_GAP("Gap", 60, new WorldPoint(1629, 4023, 0)),
- SLAYER_TOWER_MEDIUM_CHAIN_FIRST("Spiked Chain (Floor 1)", 61, new WorldPoint(3421, 3550, 0)),
- SLAYER_TOWER_MEDIUM_CHAIN_SECOND("Spiked Chain (Floor 2)", 61, new WorldPoint(3420, 3551, 0)),
- SLAYER_DUNGEON_CREVICE("Narrow Crevice", 62, new WorldPoint(2729, 10008, 0)),
- MOUNT_KARUULM_UPPER("Rocks", 62, new WorldPoint(1322, 3791, 0)),
- TAVERLEY_DUNGEON_RAILING("Loose Railing", 63, new WorldPoint(2935, 9811, 0)),
- TROLLHEIM_WILDERNESS_ROCKS("Rocks", 64, new WorldPoint(2945, 3678, 0)),
- FOSSIL_ISLAND_VOLCANO("Rope", 64, new WorldPoint(3780, 3822, 0)),
- MORYTANIA_TEMPLE("Loose Railing", 65, new WorldPoint(3422, 3476, 0)),
- REVENANT_CAVES_GREEN_DRAGONS("Jump", 65, new WorldPoint(3220, 10086, 0)),
- COSMIC_ALTAR_ADVANCED_WALKWAY("Narrow Walkway", 66, new WorldPoint(2408, 4401, 0)),
- LUMBRIDGE_DESERT_STEPPING_STONE("Stepping Stone", 66, new WorldPoint(3210, 3135, 0)),
- HEROS_GUILD_TUNNEL_WEST("Crevice", 67, new WorldPoint(2898, 9901, 0)),
- HEROS_GUILD_TUNNEL_EAST("Crevice", 67, new WorldPoint(2913, 9895, 0)),
- ELVEN_OVERPASS_MEDIUM_CLIFF("Rocks", 68, new WorldPoint(2337, 3288, 0)),
- ARCEUUS_ESSENSE_NORTH("Rock Climb", 69, new WorldPoint(1759, 3873, 0)),
- TAVERLEY_DUNGEON_PIPE_BLUE_DRAGON("Pipe Squeeze", 70, new WorldPoint(2886, 9798, 0)),
- FOSSIL_ISLAND_HARDWOOD("Hole", 70, new WorldPoint(3663, 3810, 0)),
- GWD_SARADOMIN_ROPE_FIRST("Rope Descent", 70, new WorldPoint(2912, 5300, 0)),
- GWD_SARADOMIN_ROPE_SECOND("Rope Descent", 70, new WorldPoint(2951, 5267, 0)),
- SLAYER_TOWER_ADVANCED_CHAIN_FIRST("Spiked Chain (Floor 2)", 71, new WorldPoint(3447, 3578, 0)),
- SLAYER_TOWER_ADVANCED_CHAIN_SECOND("Spiked Chain (Floor 3)", 71, new WorldPoint(3446, 3576, 0)),
- SLAYER_CAVE_WALL_CLIMB("Tunnel", 72, new WorldPoint(2431, 9806, 0)),
- TROLL_STRONGHOLD_WALL_CLIMB("Rocks", 73, new WorldPoint(2841, 3694, 0)),
- ARCEUUS_ESSENSE_MINE_WEST("Rock Climb", 73, new WorldPoint(1742, 3853, 0)),
- LAVA_DRAGON_ISLE_JUMP("Stepping Stone", 74, new WorldPoint(3200, 3807, 0)),
- REVENANT_CAVES_DEMONS_JUMP("Jump", 75, new WorldPoint(3199, 10135, 0)),
- REVENANT_CAVES_ANKOU_EAST("Jump", 75, new WorldPoint(3201, 10195, 0)),
- REVENANT_CAVES_ANKOU_NORTH("Jump", 75, new WorldPoint(3180, 10209, 0)),
- ZUL_ANDRA_ISLAND_CROSSING("Stepping Stone", 76, new WorldPoint(2156, 3073, 0)),
- SHILO_VILLAGE_STEPPING_STONES("Stepping Stones", 77, new WorldPoint(2863, 2974, 0)),
- KHARAZI_JUNGLE_VINE_CLIMB("Vine", 79, new WorldPoint(2897, 2939, 0)),
- TAVERLEY_DUNGEON_SPIKED_BLADES("Strange Floor", 80, new WorldPoint(2877, 9813, 0)),
- SLAYER_DUNGEON_CHASM_JUMP("Spiked Blades", 81, new WorldPoint(2770, 10003, 0)),
- LAVA_MAZE_NORTH_JUMP("Stepping Stone", 82, new WorldPoint(3092, 3880, 0)),
- BRIMHAVEN_DUNGEON_EAST_STEPPING_NORTH("Stepping Stones", 83, new WorldPoint(2685, 9547, 0)),
- BRIMHAVEN_DUNGEON_EAST_STEPPING_SOUTH("Stepping Stones", 83, new WorldPoint(2693, 9529, 0)),
- ELVEN_ADVANCED_CLIFF_SCRAMBLE("Rocks", 85, new WorldPoint(2337, 3253, 0)),
- KALPHITE_WALL("Crevice", 86, new WorldPoint(3214, 9508, 0)),
- BRIMHAVEN_DUNGEON_VINE_EAST("Vine", 87, new WorldPoint(2672, 9582, 0)),
- BRIMHAVEN_DUNGEON_VINE_WEST("Vine", 87, new WorldPoint(2606, 9584, 0)),
- RENEVANT_CAVES("Jump", 89, new WorldPoint(3240, 10144, 0));
-
- private final String tooltip;
- private final WorldPoint location;
- private final int levelReq;
-
- AgilityShortcutLocation(String description, int level, WorldPoint location)
- {
- this.tooltip = description + " - Level " + level;
- this.location = location;
- this.levelReq = level;
- }
-}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/AgilityShortcutPoint.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/AgilityShortcutPoint.java
index 74f4673c85..c99979f103 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/AgilityShortcutPoint.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/AgilityShortcutPoint.java
@@ -27,12 +27,13 @@ package net.runelite.client.plugins.worldmap;
import java.awt.image.BufferedImage;
import net.runelite.client.ui.overlay.worldmap.WorldMapPoint;
+import net.runelite.client.game.AgilityShortcut;
class AgilityShortcutPoint extends WorldMapPoint
{
- AgilityShortcutPoint(AgilityShortcutLocation data, BufferedImage icon, boolean showTooltip)
+ AgilityShortcutPoint(AgilityShortcut data, BufferedImage icon, boolean showTooltip)
{
- super(data.getLocation(), icon);
+ super(data.getWorldMapLocation(), icon);
if (showTooltip)
{
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/QuestStartLocation.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/QuestStartLocation.java
index 66dde6666b..c1d9e9fb88 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/QuestStartLocation.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/QuestStartLocation.java
@@ -30,6 +30,7 @@ import net.runelite.api.coords.WorldPoint;
enum QuestStartLocation
{
+ //Free Quests
COOKS_ASSISTANT_RFD("Cook's Assistant", new WorldPoint(3211, 3216, 0)),
THE_CORSAIR_CURSE("The Corsair Curse", new WorldPoint(3029, 3273, 0)),
DEMON_SLAYER("Demon Slayer", new WorldPoint(3204, 3424, 0)),
@@ -48,8 +49,12 @@ enum QuestStartLocation
SHIELD_OF_ARRAV("Shield of Arrav", new WorldPoint(3208, 3495, 0)),
VAMPIRE_SLAYER("Vampire Slayer", new WorldPoint(3096, 3266, 0)),
WITCHS_POTION("Witch's Potion", new WorldPoint(2967, 3203, 0)),
+ X_MARKS_THE_SPOT("X Marks the Spot", new WorldPoint(3227, 3242, 0)),
+
+ //Members' Quests
ANIMAL_MAGNETISM("Animal Magnetism", new WorldPoint(3094, 3360, 0)),
ANOTHER_SLICE_OF_HAM("Another Slice of H.A.M.", new WorldPoint(2799, 5428, 0)),
+ THE_ASCENT_OF_ARCEUUS("The Ascent of Arceuus", new WorldPoint(1700, 3742, 0)),
BETWEEN_A_ROCK("Between a Rock...", new WorldPoint(2823, 10168, 0)),
BIG_CHOMPY_BIRD_HUNTING("Big Chompy Bird Hunting", new WorldPoint(2629, 2981, 0)),
BIOHAZARD("Biohazard", new WorldPoint(2591, 3335, 0)),
@@ -84,6 +89,7 @@ enum QuestStartLocation
FISHING_CONTEST_1("Fishing Contest", new WorldPoint(2875, 3483, 0)),
FISHING_CONTEST_2("Fishing Contest", new WorldPoint(2820, 3487, 0)),
FORGETTABLE_TALE("Forgettable Tale...", new WorldPoint(2826, 10215, 0)),
+ THE_FORSAKEN_TOWER("The Forsaken Tower", new WorldPoint(1484, 3747, 0)),
THE_FREMENNIK_ISLES("The Fremennik Isles", new WorldPoint(2645, 3711, 0)),
THE_FREMENNIK_TRIALS("The Fremennik Trials", new WorldPoint(2657, 3669, 0)),
GARDEN_OF_TRANQUILLITY("Garden of Tranquillity", new WorldPoint(3227, 3477, 0)),
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/RareTreeLocation.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/RareTreeLocation.java
index ca30b23271..d32dee4c32 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/RareTreeLocation.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/RareTreeLocation.java
@@ -140,7 +140,7 @@ enum RareTreeLocation
new WorldPoint(3018, 3316, 0),
new WorldPoint(3041, 3320, 0),
new WorldPoint(3052, 3272, 0),
- new WorldPoint(2933, 3234, 0),
+ new WorldPoint(2931, 3231, 0),
// Misthalin
new WorldPoint(3085, 3481, 0),
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/WorldMapPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/WorldMapPlugin.java
index fea186927b..276e2083e4 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/WorldMapPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/WorldMapPlugin.java
@@ -36,6 +36,7 @@ import net.runelite.api.events.ConfigChanged;
import net.runelite.api.events.ExperienceChanged;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.Subscribe;
+import net.runelite.client.game.AgilityShortcut;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.ui.overlay.worldmap.WorldMapPointManager;
@@ -167,9 +168,10 @@ public class WorldMapPlugin extends Plugin
if (config.agilityShortcutLevelIcon() || config.agilityShortcutTooltips())
{
- Arrays.stream(AgilityShortcutLocation.values())
+ Arrays.stream(AgilityShortcut.values())
+ .filter(value -> value.getWorldMapLocation() != null)
.map(value -> new AgilityShortcutPoint(value,
- agilityLevel > 0 && config.agilityShortcutLevelIcon() && value.getLevelReq() > agilityLevel ? NOPE_ICON : BLANK_ICON,
+ agilityLevel > 0 && config.agilityShortcutLevelIcon() && value.getLevel() > agilityLevel ? NOPE_ICON : BLANK_ICON,
config.agilityShortcutTooltips()))
.forEach(worldMapPointManager::add);
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerPlugin.java
index 54dff5a299..bb43ffdd7a 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerPlugin.java
@@ -73,7 +73,7 @@ public class XpTrackerPlugin extends Plugin
/**
* Amount of EXP that must be gained for an update to be submitted.
*/
- private static final int XP_THRESHOLD = 1000;
+ private static final int XP_THRESHOLD = 10_000;
static final List COMBAT = ImmutableList.of(
Skill.ATTACK,
diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/ImageComponent.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/ImageComponent.java
index 05ddc35395..39ec431697 100644
--- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/ImageComponent.java
+++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/ImageComponent.java
@@ -27,7 +27,9 @@ package net.runelite.client.ui.overlay.components;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Point;
+import java.awt.Rectangle;
import java.awt.image.BufferedImage;
+import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
@@ -36,6 +38,10 @@ import lombok.Setter;
public class ImageComponent implements LayoutableRenderableEntity
{
private final BufferedImage image;
+
+ @Getter
+ private final Rectangle bounds = new Rectangle();
+
private Point preferredLocation = new Point();
@Override
@@ -47,7 +53,10 @@ public class ImageComponent implements LayoutableRenderableEntity
}
graphics.drawImage(image, preferredLocation.x, preferredLocation.y, null);
- return new Dimension(image.getWidth(), image.getHeight());
+ final Dimension dimension = new Dimension(image.getWidth(), image.getHeight());
+ bounds.setLocation(preferredLocation);
+ bounds.setSize(dimension);
+ return dimension;
}
@Override
diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/InfoBoxComponent.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/InfoBoxComponent.java
index ca6c55d3be..d8f7841099 100644
--- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/InfoBoxComponent.java
+++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/InfoBoxComponent.java
@@ -24,6 +24,7 @@
*/
package net.runelite.client.ui.overlay.components;
+import com.google.common.base.Strings;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FontMetrics;
@@ -45,11 +46,10 @@ public class InfoBoxComponent implements LayoutableRenderableEntity
private String tooltip;
@Getter
+ private final Rectangle bounds = new Rectangle();
+
private Point preferredLocation = new Point();
-
- @Setter
private Dimension preferredSize = new Dimension(DEFAULT_SIZE, DEFAULT_SIZE);
-
private String text;
private Color color = Color.WHITE;
private Color backgroundColor = ComponentConstants.STANDARD_BACKGROUND_COLOR;
@@ -64,12 +64,14 @@ public class InfoBoxComponent implements LayoutableRenderableEntity
}
graphics.setFont(getSize() < DEFAULT_SIZE ? FontManager.getRunescapeSmallFont() : FontManager.getRunescapeFont());
- graphics.translate(preferredLocation.x, preferredLocation.y);
+
+ final int baseX = preferredLocation.x;
+ final int baseY = preferredLocation.y;
// Calculate dimensions
final FontMetrics metrics = graphics.getFontMetrics();
final int size = getSize();
- final Rectangle bounds = new Rectangle(size, size);
+ final Rectangle bounds = new Rectangle(baseX, baseY, size, size);
// Render background
final BackgroundComponent backgroundComponent = new BackgroundComponent();
@@ -80,26 +82,24 @@ public class InfoBoxComponent implements LayoutableRenderableEntity
// Render image
graphics.drawImage(
image,
- (size - image.getWidth(null)) / 2,
- (size - image.getHeight(null)) / 2,
+ baseX + (size - image.getWidth(null)) / 2,
+ baseY + (size - image.getHeight(null)) / 2,
null);
// Render caption
- final TextComponent textComponent = new TextComponent();
- textComponent.setColor(color);
- textComponent.setText(text);
- textComponent.setPosition(new Point(((size - metrics.stringWidth(text)) / 2), size - SEPARATOR));
- textComponent.render(graphics);
+ if (!Strings.isNullOrEmpty(text))
+ {
+ final TextComponent textComponent = new TextComponent();
+ textComponent.setColor(color);
+ textComponent.setText(text);
+ textComponent.setPosition(new Point(baseX + ((size - metrics.stringWidth(text)) / 2), baseY + size - SEPARATOR));
+ textComponent.render(graphics);
+ }
- graphics.translate(-preferredLocation.x, -preferredLocation.y);
+ this.bounds.setBounds(bounds);
return bounds.getSize();
}
- public Dimension getPreferredSize()
- {
- return new Dimension(getSize(), getSize());
- }
-
private int getSize()
{
return Math.max(preferredSize.width, preferredSize.height);
diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/LayoutableRenderableEntity.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/LayoutableRenderableEntity.java
index ba7f1e7717..7a736686d0 100644
--- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/LayoutableRenderableEntity.java
+++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/LayoutableRenderableEntity.java
@@ -26,10 +26,12 @@ package net.runelite.client.ui.overlay.components;
import java.awt.Dimension;
import java.awt.Point;
+import java.awt.Rectangle;
import net.runelite.client.ui.overlay.RenderableEntity;
public interface LayoutableRenderableEntity extends RenderableEntity
{
+ Rectangle getBounds();
void setPreferredLocation(Point position);
void setPreferredSize(Dimension dimension);
}
diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/LineComponent.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/LineComponent.java
index efe7f407a6..f6c2f5e77c 100644
--- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/LineComponent.java
+++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/LineComponent.java
@@ -31,7 +31,9 @@ import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.Point;
+import java.awt.Rectangle;
import lombok.Builder;
+import lombok.Getter;
import lombok.Setter;
@Setter
@@ -53,17 +55,22 @@ public class LineComponent implements LayoutableRenderableEntity
@Builder.Default
private Dimension preferredSize = new Dimension(ComponentConstants.STANDARD_WIDTH, 0);
+ @Builder.Default
+ @Getter
+ private final Rectangle bounds = new Rectangle();
+
@Override
public Dimension render(Graphics2D graphics)
{
- graphics.translate(preferredLocation.x, preferredLocation.y);
// Prevent NPEs
final String left = MoreObjects.firstNonNull(this.left, "");
final String right = MoreObjects.firstNonNull(this.right, "");
final FontMetrics metrics = graphics.getFontMetrics();
- int x = 0;
- int y = metrics.getHeight();
+ final int baseX = preferredLocation.x;
+ final int baseY = preferredLocation.y + metrics.getHeight();
+ int x = baseX;
+ int y = baseY;
final int leftFullWidth = getLineWidth(left, metrics);
final int rightFullWidth = getLineWidth(right, metrics);
@@ -112,8 +119,10 @@ public class LineComponent implements LayoutableRenderableEntity
y += metrics.getHeight();
}
- graphics.translate(-preferredLocation.x, -preferredLocation.y);
- return new Dimension(preferredSize.width, y - metrics.getHeight());
+ final Dimension dimension = new Dimension(preferredSize.width, y - baseY);
+ bounds.setLocation(preferredLocation);
+ bounds.setSize(dimension);
+ return dimension;
}
final TextComponent leftLineComponent = new TextComponent();
@@ -129,8 +138,10 @@ public class LineComponent implements LayoutableRenderableEntity
rightLineComponent.render(graphics);
y += metrics.getHeight();
- graphics.translate(-preferredLocation.x, -preferredLocation.y);
- return new Dimension(preferredSize.width, y - metrics.getHeight());
+ final Dimension dimension = new Dimension(preferredSize.width, y - baseY);
+ bounds.setLocation(preferredLocation);
+ bounds.setSize(dimension);
+ return dimension;
}
private static int getLineWidth(final String line, final FontMetrics metrics)
diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/PanelComponent.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/PanelComponent.java
index 974ec37240..5ea81f2006 100644
--- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/PanelComponent.java
+++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/PanelComponent.java
@@ -43,9 +43,8 @@ public class PanelComponent implements LayoutableRenderableEntity
VERTICAL;
}
- @Setter
- @Nullable
- private Color backgroundColor = ComponentConstants.STANDARD_BACKGROUND_COLOR;
+ @Getter
+ private final Rectangle bounds = new Rectangle();
@Setter
private Point preferredLocation = new Point();
@@ -53,8 +52,12 @@ public class PanelComponent implements LayoutableRenderableEntity
@Setter
private Dimension preferredSize = new Dimension(ComponentConstants.STANDARD_WIDTH, 0);
+ @Setter
+ @Nullable
+ private Color backgroundColor = ComponentConstants.STANDARD_BACKGROUND_COLOR;
+
@Getter
- private List children = new ArrayList<>();
+ private final List children = new ArrayList<>();
@Setter
private Orientation orientation = Orientation.VERTICAL;
@@ -82,8 +85,6 @@ public class PanelComponent implements LayoutableRenderableEntity
return null;
}
- graphics.translate(preferredLocation.x, preferredLocation.y);
-
// Calculate panel dimension
final Dimension dimension = new Dimension(
border.x + childDimensions.width + border.width,
@@ -93,14 +94,14 @@ public class PanelComponent implements LayoutableRenderableEntity
if (backgroundColor != null)
{
final BackgroundComponent backgroundComponent = new BackgroundComponent();
- backgroundComponent.setRectangle(new Rectangle(dimension));
+ backgroundComponent.setRectangle(new Rectangle(preferredLocation, dimension));
backgroundComponent.setBackgroundColor(backgroundColor);
backgroundComponent.render(graphics);
}
// Offset children
- final int baseX = border.x;
- final int baseY = border.y;
+ final int baseX = preferredLocation.x + border.x;
+ final int baseY = preferredLocation.y + border.y;
int width = 0;
int height = 0;
int x = baseX;
@@ -174,7 +175,9 @@ public class PanelComponent implements LayoutableRenderableEntity
// Cache children bounds
childDimensions.setSize(totalWidth, totalHeight);
- graphics.translate(-preferredLocation.x, -preferredLocation.y);
+ // Cache bounds
+ bounds.setLocation(preferredLocation);
+ bounds.setSize(dimension);
return dimension;
}
}
diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/ProgressBarComponent.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/ProgressBarComponent.java
index 0db182739a..ba4634deec 100644
--- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/ProgressBarComponent.java
+++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/ProgressBarComponent.java
@@ -29,7 +29,9 @@ import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.Point;
+import java.awt.Rectangle;
import java.text.DecimalFormat;
+import lombok.Getter;
import lombok.Setter;
@Setter
@@ -43,6 +45,7 @@ public class ProgressBarComponent implements LayoutableRenderableEntity
private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("0.0");
private static final DecimalFormat DECIMAL_FORMAT_ABS = new DecimalFormat("#0");
+
private long minimum;
private long maximum = 100;
private double value;
@@ -53,14 +56,16 @@ public class ProgressBarComponent implements LayoutableRenderableEntity
private Point preferredLocation = new Point();
private Dimension preferredSize = new Dimension(ComponentConstants.STANDARD_WIDTH, 16);
+ @Getter
+ private final Rectangle bounds = new Rectangle();
+
@Override
public Dimension render(Graphics2D graphics)
{
- graphics.translate(preferredLocation.x, preferredLocation.y);
final FontMetrics metrics = graphics.getFontMetrics();
- final int barX = 0;
- final int barY = 0;
+ final int barX = preferredLocation.x;
+ final int barY = preferredLocation.y;
final long span = maximum - minimum;
final double currentValue = value - minimum;
@@ -82,7 +87,7 @@ public class ProgressBarComponent implements LayoutableRenderableEntity
final int progressTextY = barY + ((height - metrics.getHeight()) / 2) + metrics.getHeight();
final int progressFill = (int) (width * Math.min(1, pc));
- //Draw bar
+ // Draw bar
graphics.setColor(backgroundColor);
graphics.fillRect(barX, barY, width, height);
graphics.setColor(foregroundColor);
@@ -94,7 +99,9 @@ public class ProgressBarComponent implements LayoutableRenderableEntity
textComponent.setText(textToWrite);
textComponent.render(graphics);
- graphics.translate(-preferredLocation.x, -preferredLocation.y);
- return new Dimension(width, height);
+ final Dimension dimension = new Dimension(width, height);
+ bounds.setLocation(preferredLocation);
+ bounds.setSize(dimension);
+ return dimension;
}
}
diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/TitleComponent.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/TitleComponent.java
index 093d596fe9..fa4c9bc3bb 100644
--- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/TitleComponent.java
+++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/TitleComponent.java
@@ -29,7 +29,9 @@ import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.Point;
+import java.awt.Rectangle;
import lombok.Builder;
+import lombok.Getter;
import lombok.Setter;
@Setter
@@ -47,17 +49,26 @@ public class TitleComponent implements LayoutableRenderableEntity
@Builder.Default
private Dimension preferredSize = new Dimension(ComponentConstants.STANDARD_WIDTH, 0);
+ @Builder.Default
+ @Getter
+ private final Rectangle bounds = new Rectangle();
+
@Override
public Dimension render(Graphics2D graphics)
{
- graphics.translate(preferredLocation.x, preferredLocation.y);
+ final int baseX = preferredLocation.x;
+ final int baseY = preferredLocation.y;
final FontMetrics metrics = graphics.getFontMetrics();
final TextComponent titleComponent = new TextComponent();
titleComponent.setText(text);
titleComponent.setColor(color);
- titleComponent.setPosition(new Point((preferredSize.width - metrics.stringWidth(text)) / 2, metrics.getHeight()));
- final Dimension dimension = titleComponent.render(graphics);
- graphics.translate(-preferredLocation.x, -preferredLocation.y);
- return new Dimension(preferredSize.width, dimension.height);
+ titleComponent.setPosition(new Point(
+ baseX + ((preferredSize.width - metrics.stringWidth(text)) / 2),
+ baseY + metrics.getHeight()));
+ final Dimension rendered = titleComponent.render(graphics);
+ final Dimension dimension = new Dimension(preferredSize.width, rendered.height);
+ bounds.setLocation(preferredLocation);
+ bounds.setSize(dimension);
+ return dimension;
}
}
diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/Counter.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/Counter.java
index 599a178838..49614e9b61 100644
--- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/Counter.java
+++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/Counter.java
@@ -26,33 +26,28 @@ package net.runelite.client.ui.overlay.infobox;
import java.awt.Color;
import java.awt.image.BufferedImage;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
import net.runelite.client.plugins.Plugin;
+@ToString
public class Counter extends InfoBox
{
- private String text;
+ @Getter
+ @Setter
+ private int count;
- public Counter(BufferedImage image, Plugin plugin, String text)
+ public Counter(BufferedImage image, Plugin plugin, int count)
{
super(image, plugin);
- this.text = text;
- }
-
- @Override
- public String toString()
- {
- return "Counter{" + "text=" + text + '}';
+ this.count = count;
}
@Override
public String getText()
{
- return text;
- }
-
- public void setText(String text)
- {
- this.text = text;
+ return Integer.toString(count);
}
@Override
diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBox.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBox.java
index fc498d3635..8c2889e8e6 100644
--- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBox.java
+++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBox.java
@@ -26,24 +26,25 @@ package net.runelite.client.ui.overlay.infobox;
import java.awt.Color;
import java.awt.Image;
+import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import net.runelite.client.plugins.Plugin;
public abstract class InfoBox
{
- @Getter
+ @Getter(AccessLevel.PACKAGE)
private final Plugin plugin;
@Getter
@Setter
private Image image;
- @Getter
- @Setter
+ @Getter(AccessLevel.PACKAGE)
+ @Setter(AccessLevel.PACKAGE)
private Image scaledImage;
- @Getter
+ @Getter(AccessLevel.PACKAGE)
@Setter
private InfoBoxPriority priority;
diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBoxManager.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBoxManager.java
index 8cea4cc94e..4fd1c1ca11 100644
--- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBoxManager.java
+++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBoxManager.java
@@ -76,18 +76,18 @@ public class InfoBoxManager
public void removeInfoBox(InfoBox infoBox)
{
- log.debug("Removing InfoBox {}", infoBox);
if (infoBoxes.remove(infoBox))
{
+ log.debug("Removed InfoBox {}", infoBox);
refreshInfoBoxes();
}
}
public void removeIf(Predicate filter)
{
- log.debug("Removing InfoBoxes for filter {}", filter);
if (infoBoxes.removeIf(filter))
{
+ log.debug("Removed InfoBoxes for filter {}", filter);
refreshInfoBoxes();
}
}
diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBoxOverlay.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBoxOverlay.java
index 15165a4edb..58f20aae63 100644
--- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBoxOverlay.java
+++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBoxOverlay.java
@@ -26,6 +26,7 @@
package net.runelite.client.ui.overlay.infobox;
import com.google.common.base.Strings;
+import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Point;
@@ -37,7 +38,6 @@ import net.runelite.api.Client;
import net.runelite.client.config.RuneLiteConfig;
import net.runelite.client.ui.overlay.Overlay;
import net.runelite.client.ui.overlay.OverlayPosition;
-import net.runelite.client.ui.overlay.OverlayUtil;
import net.runelite.client.ui.overlay.components.InfoBoxComponent;
import net.runelite.client.ui.overlay.components.LayoutableRenderableEntity;
import net.runelite.client.ui.overlay.components.PanelComponent;
@@ -95,10 +95,16 @@ public class InfoBoxOverlay extends Overlay
continue;
}
+ final String text = box.getText();
+ final Color color = box.getTextColor();
+
final InfoBoxComponent infoBoxComponent = new InfoBoxComponent();
- infoBoxComponent.setColor(box.getTextColor());
+ infoBoxComponent.setText(text);
+ if (color != null)
+ {
+ infoBoxComponent.setColor(color);
+ }
infoBoxComponent.setImage(box.getScaledImage());
- infoBoxComponent.setText(box.getText());
infoBoxComponent.setTooltip(box.getTooltip());
panelComponent.getChildren().add(infoBoxComponent);
}
@@ -116,15 +122,10 @@ public class InfoBoxOverlay extends Overlay
if (!Strings.isNullOrEmpty(component.getTooltip()))
{
- final Rectangle intersectionRectangle = new Rectangle(component.getPreferredLocation(), component.getPreferredSize());
-
- // Move the intersection based on overlay position
+ // Create intersection rectangle
+ final Rectangle intersectionRectangle = new Rectangle(component.getBounds());
intersectionRectangle.translate(getBounds().x, getBounds().y);
- // Move the intersection based on overlay "orientation"
- final Point transformed = OverlayUtil.transformPosition(getPosition(), intersectionRectangle.getSize());
- intersectionRectangle.translate(transformed.x, transformed.y);
-
if (intersectionRectangle.contains(mouse))
{
tooltipManager.add(new Tooltip(component.getTooltip()));
diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/Timer.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/Timer.java
index 3219b487b2..4d6c2b7be4 100644
--- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/Timer.java
+++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/Timer.java
@@ -31,9 +31,11 @@ import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import lombok.Getter;
+import lombok.ToString;
import net.runelite.client.plugins.Plugin;
@Getter
+@ToString
public class Timer extends InfoBox
{
private final Instant startTime;
@@ -51,12 +53,6 @@ public class Timer extends InfoBox
endTime = startTime.plus(duration);
}
- @Override
- public String toString()
- {
- return "Timer{" + "startTime=" + startTime + ", endTime=" + endTime + ", duration=" + duration + '}';
- }
-
@Override
public String getText()
{
diff --git a/runelite-client/src/main/java/net/runelite/client/util/ImageUtil.java b/runelite-client/src/main/java/net/runelite/client/util/ImageUtil.java
index 1fa63d815e..16a6b1c78a 100644
--- a/runelite-client/src/main/java/net/runelite/client/util/ImageUtil.java
+++ b/runelite-client/src/main/java/net/runelite/client/util/ImageUtil.java
@@ -30,6 +30,7 @@ import java.awt.Image;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
+import java.awt.image.DirectColorModel;
import java.awt.image.PixelGrabber;
import java.awt.image.RescaleOp;
import java.io.IOException;
@@ -426,8 +427,19 @@ public class ImageUtil
try
{
- new PixelGrabber(image, 0, 0, image.getWidth(), image.getHeight(), pixels, 0, image.getWidth())
- .grabPixels();
+ PixelGrabber g = new PixelGrabber(image, 0, 0, image.getWidth(), image.getHeight(), pixels, 0, image.getWidth());
+ g.setColorModel(new DirectColorModel(32, 0xff0000, 0xff00, 0xff, 0xff000000));
+ g.grabPixels();
+
+ // Make any fully transparent pixels fully black, because the sprite draw routines
+ // check for == 0, not actual transparency
+ for (int i = 0; i < pixels.length; i++)
+ {
+ if ((pixels[i] & 0xFF000000) == 0)
+ {
+ pixels[i] = 0;
+ }
+ }
}
catch (InterruptedException ex)
{
diff --git a/runelite-client/src/main/java/net/runelite/client/util/LinkBrowser.java b/runelite-client/src/main/java/net/runelite/client/util/LinkBrowser.java
index 173a383bbe..194b1974b2 100644
--- a/runelite-client/src/main/java/net/runelite/client/util/LinkBrowser.java
+++ b/runelite-client/src/main/java/net/runelite/client/util/LinkBrowser.java
@@ -43,6 +43,8 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j
public class LinkBrowser
{
+ private static boolean shouldAttemptXdg = OSType.getOSType() == OSType.Linux;
+
/**
* Tries to navigate to specified URL in browser. In case operation fails, displays message box with message
* and copies link to clipboard to navigate to.
@@ -56,9 +58,55 @@ public class LinkBrowser
return false;
}
+ if (attemptDesktopBrowse(url))
+ {
+ log.debug("Opened browser through Desktop#browse to {}", url);
+ return true;
+ }
+
+ if (shouldAttemptXdg && attemptXdgOpen(url))
+ {
+ log.debug("Opened browser through xdg-open to {}", url);
+ return true;
+ }
+
+ showMessageBox("Unable to open link. Press 'OK' and link will be copied to your clipboard.", url);
+ return false;
+ }
+
+ private static boolean attemptXdgOpen(String url)
+ {
+ try
+ {
+ final Process exec = Runtime.getRuntime().exec(new String[]{"xdg-open", url});
+ exec.waitFor();
+
+ final int ret = exec.exitValue();
+ if (ret == 0)
+ {
+ return true;
+ }
+
+ log.warn("xdg-open {} returned with error code {}", url, ret);
+ return false;
+ }
+ catch (IOException ex)
+ {
+ // xdg-open not found
+ shouldAttemptXdg = false;
+ return false;
+ }
+ catch (InterruptedException ex)
+ {
+ log.warn("Interrupted while waiting for xdg-open {} to execute", url);
+ return false;
+ }
+ }
+
+ private static boolean attemptDesktopBrowse(String url)
+ {
if (!Desktop.isDesktopSupported())
{
- showMessageBox("Desktop is not supported. Press 'OK' and link will be copied to your clipboard.", url);
return false;
}
@@ -66,20 +114,17 @@ public class LinkBrowser
if (!desktop.isSupported(Desktop.Action.BROWSE))
{
- showMessageBox("Desktop browser is not supported. Press 'OK' and link will be copied to your clipboard.", url);
return false;
}
try
{
desktop.browse(new URI(url));
- log.debug("Opened browser to {}", url);
return true;
}
catch (IOException | URISyntaxException ex)
{
- log.warn("Unable to open URL {}. Error: {}", url, ex);
- showMessageBox("Unable to open a URL. Press 'OK' and link will be copied to your clipboard.", url);
+ log.warn("Failed to open Desktop#browser {}", url, ex);
return false;
}
}
diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/info/import_icon.png b/runelite-client/src/main/resources/net/runelite/client/plugins/info/import_icon.png
new file mode 100644
index 0000000000..32263bf159
Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/info/import_icon.png differ
diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/poison/1067-DISEASE.png b/runelite-client/src/main/resources/net/runelite/client/plugins/poison/1067-DISEASE.png
new file mode 100644
index 0000000000..f2ff5ac911
Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/poison/1067-DISEASE.png differ
diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/poison/1067-POISON.png b/runelite-client/src/main/resources/net/runelite/client/plugins/poison/1067-POISON.png
new file mode 100644
index 0000000000..a4b05c1ca1
Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/poison/1067-POISON.png differ
diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/poison/1067-VENOM.png b/runelite-client/src/main/resources/net/runelite/client/plugins/poison/1067-VENOM.png
new file mode 100644
index 0000000000..df6816bb74
Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/poison/1067-VENOM.png differ
diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/skillcalculator/skill_magic.json b/runelite-client/src/main/resources/net/runelite/client/plugins/skillcalculator/skill_magic.json
index 38719b3d00..15905e0a0c 100644
--- a/runelite-client/src/main/resources/net/runelite/client/plugins/skillcalculator/skill_magic.json
+++ b/runelite-client/src/main/resources/net/runelite/client/plugins/skillcalculator/skill_magic.json
@@ -238,7 +238,7 @@
"level": 37,
"sprite": 33,
"name": "Falador Teleport",
- "xp": 47
+ "xp": 48
},
{
"level": 39,
diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/wiki/fixed_mode_minimap_clickmask.png b/runelite-client/src/main/resources/net/runelite/client/plugins/wiki/fixed_mode_minimap_clickmask.png
new file mode 100644
index 0000000000..e4f5fcbe48
Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/wiki/fixed_mode_minimap_clickmask.png differ
diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/wiki/wiki.png b/runelite-client/src/main/resources/net/runelite/client/plugins/wiki/wiki.png
new file mode 100644
index 0000000000..ef2ff20ff0
Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/wiki/wiki.png differ
diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/wiki/wiki_selected.png b/runelite-client/src/main/resources/net/runelite/client/plugins/wiki/wiki_selected.png
new file mode 100644
index 0000000000..80838fb5c4
Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/wiki/wiki_selected.png differ
diff --git a/runelite-client/src/main/scripts/BankSearchFilter.rs2asm b/runelite-client/src/main/scripts/BankSearchFilter.rs2asm
index 60b9f12aa6..80a0db4021 100644
--- a/runelite-client/src/main/scripts/BankSearchFilter.rs2asm
+++ b/runelite-client/src/main/scripts/BankSearchFilter.rs2asm
@@ -67,4 +67,4 @@ LABEL34:
load_int 1
return
load_int -1
- return
\ No newline at end of file
+ return
diff --git a/runelite-client/src/main/scripts/SendPrivateMessage.rs2asm b/runelite-client/src/main/scripts/SendPrivateMessage.rs2asm
index f2102c1328..3e1e3a8d47 100644
--- a/runelite-client/src/main/scripts/SendPrivateMessage.rs2asm
+++ b/runelite-client/src/main/scripts/SendPrivateMessage.rs2asm
@@ -31,4 +31,4 @@
sload 0
sload 1
privmsg
- return
\ No newline at end of file
+ return
diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/itemcharges/ItemChargePluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/itemcharges/ItemChargePluginTest.java
index b8fc81a16a..165c7d50a7 100644
--- a/runelite-client/src/test/java/net/runelite/client/plugins/itemcharges/ItemChargePluginTest.java
+++ b/runelite-client/src/test/java/net/runelite/client/plugins/itemcharges/ItemChargePluginTest.java
@@ -28,16 +28,20 @@ import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.testing.fieldbinder.Bind;
import com.google.inject.testing.fieldbinder.BoundFieldModule;
+import java.util.concurrent.ScheduledExecutorService;
import net.runelite.api.ChatMessageType;
import net.runelite.api.Client;
import net.runelite.api.events.ChatMessage;
import net.runelite.client.Notifier;
+import net.runelite.client.config.RuneLiteConfig;
import net.runelite.client.ui.overlay.OverlayManager;
-import static org.junit.Assert.assertEquals;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import static org.mockito.Matchers.eq;
import org.mockito.Mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
import org.mockito.runners.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
@@ -52,6 +56,14 @@ public class ItemChargePluginTest
@Bind
private Client client;
+ @Mock
+ @Bind
+ private ScheduledExecutorService scheduledExecutorService;
+
+ @Mock
+ @Bind
+ private RuneLiteConfig runeLiteConfig;
+
@Mock
@Bind
private OverlayManager overlayManager;
@@ -78,18 +90,22 @@ public class ItemChargePluginTest
{
ChatMessage chatMessage = new ChatMessage(null, ChatMessageType.SERVER, "", CHECK, "", 0);
itemChargePlugin.onChatMessage(chatMessage);
- assertEquals(10, itemChargePlugin.getDodgyCharges());
+ verify(config).dodgyNecklace(eq(10));
+ reset(config);
chatMessage = new ChatMessage(null, ChatMessageType.SERVER, "", PROTECT, "", 0);
itemChargePlugin.onChatMessage(chatMessage);
- assertEquals(9, itemChargePlugin.getDodgyCharges());
+ verify(config).dodgyNecklace(eq(9));
+ reset(config);
chatMessage = new ChatMessage(null, ChatMessageType.SERVER, "", PROTECT_1, "", 0);
itemChargePlugin.onChatMessage(chatMessage);
- assertEquals(1, itemChargePlugin.getDodgyCharges());
+ verify(config).dodgyNecklace(eq(1));
+ reset(config);
chatMessage = new ChatMessage(null, ChatMessageType.SERVER, "", BREAK, "", 0);
itemChargePlugin.onChatMessage(chatMessage);
- assertEquals(10, itemChargePlugin.getDodgyCharges());
+ verify(config).dodgyNecklace(eq(10));
+ reset(config);
}
}
\ No newline at end of file
diff --git a/runelite-mixins/pom.xml b/runelite-mixins/pom.xml
index 2bbd0bb214..8b90c2bec0 100644
--- a/runelite-mixins/pom.xml
+++ b/runelite-mixins/pom.xml
@@ -29,7 +29,7 @@
net.runelite
runelite-parent
- 1.5.12-SNAPSHOT
+ 1.5.14-SNAPSHOT
mixins
diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/ProcessClientErrorMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/ProcessClientErrorMixin.java
index a4498ea814..eb3e971a3a 100644
--- a/runelite-mixins/src/main/java/net/runelite/mixins/ProcessClientErrorMixin.java
+++ b/runelite-mixins/src/main/java/net/runelite/mixins/ProcessClientErrorMixin.java
@@ -55,7 +55,7 @@ public abstract class ProcessClientErrorMixin implements RSClient
throwableToScan = ((RSRunException) throwable).getParent();
}
- client.getLogger().error("Game crash", throwableToScan);
+ client.getLogger().error("Game crash: {}", string, throwableToScan);
StackTraceElement[] stackTrace = throwableToScan.getStackTrace();
diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSClientMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSClientMixin.java
index b67eea80ab..d162081476 100644
--- a/runelite-mixins/src/main/java/net/runelite/mixins/RSClientMixin.java
+++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSClientMixin.java
@@ -24,6 +24,8 @@
*/
package net.runelite.mixins;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
@@ -32,6 +34,7 @@ import javax.annotation.Nullable;
import javax.inject.Named;
import net.runelite.api.ChatMessageType;
import net.runelite.api.ClanMember;
+import net.runelite.api.EnumComposition;
import net.runelite.api.Friend;
import net.runelite.api.GameState;
import net.runelite.api.GrandExchangeOffer;
@@ -106,6 +109,7 @@ import net.runelite.rs.api.RSChatLineBuffer;
import net.runelite.rs.api.RSClanMemberManager;
import net.runelite.rs.api.RSClient;
import net.runelite.rs.api.RSDeque;
+import net.runelite.rs.api.RSEnum;
import net.runelite.rs.api.RSFriendContainer;
import net.runelite.rs.api.RSFriendManager;
import net.runelite.rs.api.RSHashTable;
@@ -176,6 +180,11 @@ public abstract class RSClientMixin implements RSClient
@Inject
static int skyboxColor;
+ @Inject
+ private final Cache enumCache = CacheBuilder.newBuilder()
+ .maximumSize(64)
+ .build();
+
@Inject
@Override
public Callbacks getCallbacks()
@@ -1265,9 +1274,9 @@ public abstract class RSClientMixin implements RSClient
callbacks.clientMainLoop();
}
- @MethodHook("gameDraw")
+ @MethodHook("renderWidgetLayer")
@Inject
- public static void gameDraw(Widget[] widgets, int parentId, int var2, int var3, int var4, int var5, int x, int y, int var8)
+ public static void renderWidgetLayer(Widget[] widgets, int parentId, int var2, int var3, int var4, int var5, int x, int y, int var8)
{
for (Widget rlWidget : widgets)
{
@@ -1442,4 +1451,21 @@ public abstract class RSClientMixin implements RSClient
return false;
}
+
+ @Inject
+ @Override
+ public EnumComposition getEnum(int id)
+ {
+ assert isClientThread() : "getEnum must be called on client thread";
+
+ RSEnum rsEnum = enumCache.getIfPresent(id);
+ if (rsEnum != null)
+ {
+ return rsEnum;
+ }
+
+ rsEnum = getRsEnum(id);
+ enumCache.put(id, rsEnum);
+ return rsEnum;
+ }
}
\ No newline at end of file
diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSDecorativeObjectMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSDecorativeObjectMixin.java
index f278609c9b..7ae4685ef3 100644
--- a/runelite-mixins/src/main/java/net/runelite/mixins/RSDecorativeObjectMixin.java
+++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSDecorativeObjectMixin.java
@@ -82,6 +82,29 @@ public abstract class RSDecorativeObjectMixin implements RSDecorativeObject
return model;
}
+ @Inject
+ private RSModel getModel2()
+ {
+ RSRenderable renderable = getRenderable2();
+ if (renderable == null)
+ {
+ return null;
+ }
+
+ RSModel model;
+
+ if (renderable instanceof Model)
+ {
+ model = (RSModel) renderable;
+ }
+ else
+ {
+ model = renderable.getModel();
+ }
+
+ return model;
+ }
+
@Inject
@Override
public Area getClickbox()
@@ -100,6 +123,20 @@ public abstract class RSDecorativeObjectMixin implements RSDecorativeObject
return null;
}
- return model.getConvexHull(getX(), getY(), getOrientation());
+ return model.getConvexHull(getX() + getXOffset(), getY() + getYOffset(), 0);
+ }
+
+ @Inject
+ @Override
+ public Polygon getConvexHull2()
+ {
+ RSModel model = getModel2();
+
+ if (model == null)
+ {
+ return null;
+ }
+
+ return model.getConvexHull(getX(), getY(), 0);
}
}
diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSEnumMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSEnumMixin.java
new file mode 100644
index 0000000000..06dd3ab2e8
--- /dev/null
+++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSEnumMixin.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2019, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package net.runelite.mixins;
+
+import net.runelite.api.mixins.Inject;
+import net.runelite.api.mixins.Mixin;
+import net.runelite.rs.api.RSEnum;
+
+@Mixin(RSEnum.class)
+public abstract class RSEnumMixin implements RSEnum
+{
+ @Inject
+ @Override
+ public int getIntValue(int key)
+ {
+ final int[] keys = getKeys();
+ if (keys == null)
+ {
+ return getDefaultInt();
+ }
+
+ for (int i = 0; i < keys.length; ++i)
+ {
+ if (keys[i] == key)
+ {
+ final int[] values = getIntVals();
+ return values[i];
+ }
+ }
+ return getDefaultInt();
+ }
+
+ @Inject
+ @Override
+ public String getStringValue(int key)
+ {
+ final int[] keys = getKeys();
+ if (keys == null)
+ {
+ return getDefaultString();
+ }
+
+ for (int i = 0; i < keys.length; ++i)
+ {
+ if (keys[i] == key)
+ {
+ final String[] values = getStringVals();
+ return values[i];
+ }
+ }
+ return getDefaultString();
+ }
+}
diff --git a/runelite-script-assembler-plugin/pom.xml b/runelite-script-assembler-plugin/pom.xml
index 5ce6ad3a0a..57ce1ecb31 100644
--- a/runelite-script-assembler-plugin/pom.xml
+++ b/runelite-script-assembler-plugin/pom.xml
@@ -29,7 +29,7 @@
net.runelite
runelite-parent
- 1.5.12-SNAPSHOT
+ 1.5.14-SNAPSHOT
script-assembler-plugin
diff --git a/runescape-api/pom.xml b/runescape-api/pom.xml
index 10f3c0e190..4986a1d5fa 100644
--- a/runescape-api/pom.xml
+++ b/runescape-api/pom.xml
@@ -29,7 +29,7 @@
net.runelite
runelite-parent
- 1.5.12-SNAPSHOT
+ 1.5.14-SNAPSHOT
net.runelite.rs
diff --git a/runescape-api/src/main/java/net/runelite/rs/api/RSClient.java b/runescape-api/src/main/java/net/runelite/rs/api/RSClient.java
index 04ac90d398..654e0048ea 100644
--- a/runescape-api/src/main/java/net/runelite/rs/api/RSClient.java
+++ b/runescape-api/src/main/java/net/runelite/rs/api/RSClient.java
@@ -785,6 +785,10 @@ public interface RSClient extends RSGameEngine, Client
@Override
RSNodeCache getWidgetSpriteCache();
+ @Import("items")
+ @Override
+ RSNodeCache getItemCompositionCache();
+
@Import("oculusOrbState")
@Override
int getOculusOrbState();
@@ -921,4 +925,11 @@ public interface RSClient extends RSGameEngine, Client
@Import("endY")
int getEndY();
+
+ @Import("spellSelected")
+ @Override
+ void setSpellSelected(boolean selected);
+
+ @Import("getEnum")
+ RSEnum getRsEnum(int id);
}
diff --git a/runescape-api/src/main/java/net/runelite/rs/api/RSDecorativeObject.java b/runescape-api/src/main/java/net/runelite/rs/api/RSDecorativeObject.java
index 1a53a49cb9..910c5acc1b 100644
--- a/runescape-api/src/main/java/net/runelite/rs/api/RSDecorativeObject.java
+++ b/runescape-api/src/main/java/net/runelite/rs/api/RSDecorativeObject.java
@@ -39,6 +39,12 @@ public interface RSDecorativeObject extends DecorativeObject
@Import("y")
int getY();
+ @Import("offsetX")
+ int getXOffset();
+
+ @Import("offsetY")
+ int getYOffset();
+
@Import("rotation")
int getOrientation();
diff --git a/runescape-api/src/main/java/net/runelite/rs/api/RSEnum.java b/runescape-api/src/main/java/net/runelite/rs/api/RSEnum.java
new file mode 100644
index 0000000000..d1a5273e5b
--- /dev/null
+++ b/runescape-api/src/main/java/net/runelite/rs/api/RSEnum.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2019, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package net.runelite.rs.api;
+
+import net.runelite.api.EnumComposition;
+import net.runelite.mapping.Import;
+
+public interface RSEnum extends EnumComposition, RSCacheableNode
+{
+ @Import("keys")
+ int[] getKeys();
+
+ @Import("intVals")
+ @Override
+ int[] getIntVals();
+
+ @Import("stringVals")
+ @Override
+ String[] getStringVals();
+
+ @Import("defaultInt")
+ int getDefaultInt();
+
+ @Import("defaultString")
+ String getDefaultString();
+}
diff --git a/runescape-api/src/main/java/net/runelite/rs/api/RSWidget.java b/runescape-api/src/main/java/net/runelite/rs/api/RSWidget.java
index 75315b0514..0dd5b24d4f 100644
--- a/runescape-api/src/main/java/net/runelite/rs/api/RSWidget.java
+++ b/runescape-api/src/main/java/net/runelite/rs/api/RSWidget.java
@@ -328,6 +328,10 @@ public interface RSWidget extends Widget
@Override
void setOnMouseOverListener(Object... args);
+ @Import("onMouseRepeatListener")
+ @Override
+ void setOnMouseRepeatListener(Object... args);
+
@Import("onMouseLeaveListener")
@Override
void setOnMouseLeaveListener(Object... args);
@@ -336,6 +340,14 @@ public interface RSWidget extends Widget
@Override
void setOnTimerListener(Object... args);
+ @Import("onTargetEnterListener")
+ @Override
+ void setOnTargetEnterListener(Object... args);
+
+ @Import("onTargetLeaveListener")
+ @Override
+ void setOnTargetLeaveListener(Object... args);
+
@Import("fontId")
@Override
int getFontId();
@@ -435,4 +447,28 @@ public interface RSWidget extends Widget
@Import("filled")
@Override
void setFilled(boolean filled);
+
+ @Import("targetVerb")
+ @Override
+ String getTargetVerb();
+
+ @Import("targetVerb")
+ @Override
+ void setTargetVerb(String targetVerb);
+
+ @Import("noClickThrough")
+ @Override
+ boolean getNoClickThrough();
+
+ @Import("noClickThrough")
+ @Override
+ void setNoClickThrough(boolean noClickThrough);
+
+ @Import("noScrollThrough")
+ @Override
+ boolean getNoScrollThrough();
+
+ @Import("noScrollThrough")
+ @Override
+ void setNoScrollThrough(boolean noScrollThrough);
}